|
|
% Copyright (C) 2001-2023 Artifex Software, Inc.
% All Rights Reserved.
%
% This software is provided AS-IS with no warranty, either express or
% implied.
%
% This software is distributed under license and may not be copied,
% modified or distributed except as expressly authorized under the terms
% of the license contained in the file LICENSE in this distribution.
%
% Refer to licensing information at http://www.artifex.com or contact
% Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
% CA 94129, USA, for further information.
%
% Extending Font resource category with CIDFont-CMap fonts.
languagelevel 2 .setlanguagelevel currentglobal //true setglobal
% In the comments below, 'CSI' is an abbreviation/acronym for CIDSystemInfo.
% We pre-scan resource files to retrieve the CSI from them.
% First we define a hidden procset .prs_dict containing
% necessary variables and procedures.
% Then we redefine the old /Font category using this procset.
% We maintain internal caches for the CSI values retrieved from
% resource files. This supposes that document doesn't uninstall
% resource files. To disable caching, set enable_cache to false.
% We assume that names starting with '.prs' do not appear in resource files.
% If this causes any problem, this prefix should be systematically changed
% in this file. ('prs' is an abbreviation for 'prescan'.)
25 dict begin
% Define local variables :
/.prs_dict currentdict def % self-reference (constant)
/.prs_empty 0 dict readonly def/path_buffer 8192 string def/name_buffer 1024 string def/minus (-) 0 get def % character code constant for '-'
/period (.) 0 get def % character code constant for '.'
/CMap 10 dict def % CSI cache for CMaps
/CIDFont 10 dict def % CSI cache for CIDFonts
/enable_cache //true def % set false to disable cache
% The folloving variables are just placeholders for ones to be set
% dynamically :
/.prsFile 0 def % file to prescan
/.prsResult 0 def % result of prescan
/.prsDictCount 0 def % save the dictionary stack depth
% Define a dummy CIDInit procset to use while pre-scanning :
/DummyCIDInit 15 dictbegin
/begincmap {} def /usecmap {pop} .internalbind def
{stop} .internalbind [ /begincodespacerange /endcodespacerange /beginnotdefchar /endnotdefchar /beginnotdefrange /endnotdefrange /begincidchar /endcidchar /begincidrange /endcidrange /endcmap /usefont /StartData ] { 1 index def } .internalbind forall pop
currentdict end def
% Define a local 'findresource' for pre-scanning :
% (it returns the dummy CIDInit instead of the regular CIDInit ProcSet)
/findresource { % <InstName> <CatName> findresource <inst>
2 copy /ProcSet eq exch % /InstName /CatName bool /InstName
/CIDInit eq and { pop pop //DummyCIDInit } { //findresource exec } ifelse} .internalbind def
% Define procedures for pre-scanning :
/StopIfCSIDefined { % - StopIfCSIDefined -
% Check if the dictionary stack contains a dictionary containing /CIDSystemInfo.
% The search is limited to the top .prsDictCount dictionaries in the stack.
% If so, retrieve the CSI, and execute stop to terminate the pre-scanning of the file.
% Otherwise, do nothing, so the pre-scanning continues.
countdictstack //.prs_dict /.prsDictCount get sub dup { currentdict /CIDSystemInfo .knownget { //.prs_dict exch /.prsResult exch put stop } if currentdict exch end } repeat { begin } repeat} .internalbind def
/PrescanFile { % - PrescanFile -
{ //.prs_dict /.prsFile get token { dup type % token type
dup /nametype eq exch /operatortype eq or { dup xcheck { exec //StopIfCSIDefined exec } if } if } { stop } ifelse } loop} .internalbind odef
/GetCIDSystemInfoFromFile { % <file> GetCIDSystemInfoFromFile <CSI>
% This procedure reads resource files with 'token',
% executing the tokens untill /CIDSystemInfo appears to be defined.
% Normally the resource file creates a new dictionary on
% dictionary stack and defines /CIDSystemInfo in it.
%
% Returns an empty dictionary if no CIDSystemInfo is found.
RESMPDEBUG { (cidcm GetCIDSystemInfoFromFile beg) = } if //.prs_dict begin /.prsFile exch def /.prsResult //.prs_empty def /.prsDictCount countdictstack def RESMPDEBUG { (cidcm GetCIDSystemInfoFromFile will PrescanFile.) = } if { //PrescanFile } stopped pop //.prs_dict /.prsResult get end RESMPDEBUG { (cidcm GetCIDSystemInfoFromFile end) = } if} .internalbind def
/GetCIDSystemInfo { % <InstName> <CatName> GetCIDSystemInfo <CSI>
% Retrieve CSI, using caches.
RESMPDEBUG { (cidcm GetCIDSystemInfo beg) = } if /Category findresource begin % /InstName
dup ResourceStatus { pop 2 lt { FindResource /CIDSystemInfo .knownget not { //.prs_empty } if % CSI
} { % /InstName
currentdict /GetCIDSystemInfoFromMap .knownget { exec } if dup type /nametype eq { RESMPDEBUG { (cidcm GetCIDSystemInfo got a name.) = } if //.prs_dict Category get % /InstName CSIs
dup 2 index known //enable_cache and { RESMPDEBUG { (cidcm GetCIDSystemInfo from cache.) = } if exch get % CSI
} { RESMPDEBUG { (cidcm GetCIDSystemInfo from file.) = } if exch % CSIs /InstName
dup //path_buffer ResourceFileName % CSIs /InstName (path)
RESMPDEBUG { (cidcm GetCIDSystemInfo from file ) print dup = } if currentglobal exch //true setglobal % CSIs /InstName g (path)
mark exch % CSIs /InstName g [ (path)
{ (r) file } stopped { cleartomark //.prs_empty } { exch 1 index % CSIs /InstName g file [ file
//GetCIDSystemInfoFromFile stopped { cleartomark closefile //.prs_empty } { exch pop exch closefile } ifelse } ifelse % CSIs /InstName g CSI
exch setglobal % CSIs /InstName CSI
dup 4 1 roll % CSI CSIs /InstName CSI
put % CSI
RESMPDEBUG { (cidcm GetCIDSystemInfo got from file : <<) print dup { exch //=string cvs print ( ) print //=string cvs print ( ) print } forall (>>) = } if } ifelse } if } ifelse } { pop //.prs_empty } ifelse end RESMPDEBUG { (cidcm GetCIDSystemInfo end) = } if} .internalbind def
/IsCompatibleCSI { % <CSI-M> <CSI-F> IsCompatibleCSI <bool>
% The CSI in a CIDFont may be an array, a dict, or null.
% If it is an array, it must be of 1 element, which is a dict.
% In this case the dict is used for testing the compatibility.
% Two dicts are compatible iff they contain same /Ordering and /Registry.
% Identity CMap is compatible with any CIDFont.
exch % CSI-F CSI-M
{ dup type /arraytype eq { dup length 1 ne { pop pop //false exit } if 0 get } if % CSI-F CSI-M
dup type /dicttype ne { pop pop //false exit } if % CSI-F <<CSI-M>>
exch % <<CSI-M>> CSI-F
dup type /dicttype ne { pop pop //false exit } if % <<CSI-M>> <<CSI-F>>
dup /Ordering .knownget { /Identity eq { pop pop //true exit } if } if //true % <<CSI-M>> <<CSI-F>> bEQ
[/Registry /Ordering] { 2 index 1 index .knownget not { 1234567 } if % <<CSI-M>> <<CSI-F>> bEQ /key vF
exch % <<CSI-M>> <<CSI-F>> bEQ vF /key
4 index exch .knownget not { 7654321 } if % <<CSI-M>> <<CSI-F>> bEQ vF vM
eq and % <<CSI-M>> <<CSI-F>> bEQ
} forall exch pop exch pop % bEQ
exit } loop} .internalbind def
/IsWellComposed { % <CIDFontName> <CMapName> IsWellComposed <bool>
% Check if the given CIDFont and CMap have compatible CSIs.
exch % /CMapName /CIDFontName
/CIDFont //GetCIDSystemInfo exec % /CMapName CSI-F
dup type /dicttype eq { dup length 0 ne { exch % CSI-F /CMapName
/CMap //GetCIDSystemInfo exec % CSI-F CSI-M
//IsCompatibleCSI exec % bool
} { pop pop //false } ifelse } { pop pop //false } ifelse} .internalbind def
/IsComposedFont { % <FontName> IsComposedFont <CIDFontName> <CMapName> true
% <FontName> IsComposedFont false
% Check if the given font name may be decomposed into CIDFont.CMap, CIDFont-CMap
% or into CIDFont--CMap, such that CIDFont and CMap have compatible CSIs.
% FontName
dup type /stringtype ne { //name_buffer cvs } if % (FontName)
{ dup length 2 sub -1 1 { % (FontName) i
2 copy get dup //minus eq exch //period eq or { 2 copy 2 copy % (FontName) i (FontName) i (FontName) i
2 copy get //minus eq { 2 copy 1 sub get //minus eq { 1 sub } if } if % (FontName) i (FontName) i (FontName) i0
0 exch getinterval cvn % (FontName) i (FontName) i /CIDFontName
3 1 roll % (FontName) i /CIDFontName (FontName) i
1 add dup % (FontName) i /CIDFontName (FontName) i1 i1
5 index length % (FontName) i /CIDFontName (FontName) i1 i1 l
exch sub getinterval cvn % (FontName) i /CIDFontName /CMapName
2 copy //IsWellComposed exec { % (FontName) i /CIDFontName /CMapName
4 2 roll pop pop % /CIDFontName /CMapName
stop } if pop pop pop } { pop } ifelse % (FontName)
} for pop } stopped} .internalbind def
/ComposeName { % <CIDFont> <CMap> <scr> ComposeName <CIDFont-CMap>
dup dup 5 2 roll % (scr) (scr) /CIDFont /CMap (scr)
3 2 roll exch cvs length dup % (scr) (scr) /CMap l0 l0
4 -1 roll exch //minus put % (scr) /CMap l0
1 add dup % (scr) /CMap l1 l1
3 index dup length % (scr) /CMap l1 l1 (scr) L
2 index sub % (scr) /CMap l1 l1 (scr) LT
3 2 roll % (scr) /CMap l1 (scr) LT l1
exch getinterval % (scr) /CMap l1 (scrT)
3 2 roll exch cvs length % (scr) l1 l2
add 0 exch getinterval % (CIDFont-CMap)
} .internalbind def
% Redefine the /Font category with CIDFont-CMap construction :
% The following code supposes that the following names are not
% defined in the old /Font category dictionary :
% /IsComposedFont, /IsWellComposed .
/Font /Category findresource dup length dict copy begin
/FindResource { % <InstName> FindResource <inst>
dup //ResourceStatus exec { pop pop //FindResource exec } { dup //IsComposedFont exec { % /FontName /CIDFontName /CMapName
exch [ exch ] composefont % inst
} { //FindResource exec } ifelse } ifelse} .internalbind def
/ResourceStatus { % <InstName> ResourceStatus <nStatus> <nSize> true
% <InstName> ResourceStatus false
dup //ResourceStatus exec { 3 2 roll pop //true % nStatus nSize true
} { //IsComposedFont exec { % /CIDFontName /CMapName
/CMap resourcestatus { % /CIDFontName nStatusM nSizeM
exch pop exch % nSizeM /CIDFontName
/CIDFont resourcestatus { % nSizeM nStatusF nSizeF
exch pop % nSizeF nSizeM
dup 0 ge { exch dup 0 ge { add } { exch pop } ifelse } { pop } ifelse % nSize
2 exch //true % nStatus nSize true
} { pop pop pop //false % work around buggy resource file
} ifelse } { pop pop pop //false % work around buggy resource file
} ifelse } { //false } ifelse } ifelse} .internalbind def
/ResourceForAll { % <template> <proc> <scratch> ResourceForAll -
% We suppose that the resourceforall procedure does not
% define or install new fonts, CMaps, and/or CIDFonts.
% First we create 3 temporary dictionaries to store temporary data
% about fonts, CMaps and CIDFonts.
% These dictionaries must be created dynamically, to allow for a possible
% recursive call to resourceforall from the resourceforall procedure.
currentglobal //false setglobal 20 dict 20 dict 20 dict % (templ) proc (scr) g <<CIDFont>> <<CMap>> <<Fonts>>
% Store resource identifiers into local dictionaries
% A resource instance can have a key that is not a name or a string. In this
% case, resourceforall passes the key directly to proc instead of copying it
% into the scratch string. This case can arise only for a resource instance
% defined in virtual memory by a previous defineresource
% Discard non-string keys of CIDFont and CMap because <CIDFontName>-<CMapName>
% is only defined for names.
{ /.DisableResourceOrdering pop % gs_resmp accesses this through execstack - don't remove !
6 index [ 2 index {exch //null put} aload pop ] cvx bind 6 index //ResourceForAll exec
(*) [ 3 index {exch dup type /stringtype eq { cvn dup put } { pop pop } ifelse } aload pop ] cvx bind 6 index /CMap resourceforall
(*) [ 4 index {exch dup type /stringtype eq { cvn dup put } { pop pop } ifelse } aload pop ] cvx bind 6 index /CIDFont resourceforall
exit } loop % This loop is a pattern for execstack_lookup - don't remove !
4 -1 roll setglobal % (templ) proc (scr) <<CIDFont>> <<CMap>> <<Fonts>>
%% Make the list of fonts in the form (/Name status) :
% (templ) proc (scr) <<CIDFont>> <<CMap>> <<Fonts>>
dup { pop dup //ResourceStatus exec { pop 2 index % (templ) proc (scr) <<CIDFont>> <<CMap>> <<Fonts>> /Name nStatus <<Font>>
3 1 roll put % (templ) proc (scr) <<CIDFont>> <<CMap>> <<Fonts>>
} { pop } ifelse } forall % (templ) proc (scr) <<CIDFont>> <<CMap>> <<Fonts>>
%% Add CIDFont-CMap to it (filtering duplicates) :
3 2 roll { 3 index { 3 1 roll % (templ) proc (scr) <<CMap>> <<Font>> /CIDFont /CMap /CIDFont /CMap
6 index //ComposeName exec % (templ) proc (scr) <<CMap>> <<Font>> /CIDFont /CMap (Font)
dup 8 index .stringmatch { cvn % (templ) proc (scr) <<CMap>> <<Font>> /CIDFont /CMap /Font
dup 4 index exch known { pop pop } { 2 index % (templ) proc (scr) <<CMap>> <<Font>> /CIDFont /CMap /Font /CIDFont
4 2 roll % (templ) proc (scr) <<CMap>> <<Font>> /Font /CIDFont /CIDFont /CMap
//IsWellComposed exec { exch 2 index exch 2 put % (templ) proc (scr) <<CMap>> <<Font>> /CIDFont
} { exch pop } ifelse } ifelse } { pop pop } ifelse dup % (templ) proc (scr) <<CMap>> <<Font>> /CIDFont /CIDFont
} forall pop pop % (templ) proc (scr) <<CMap>> <<Font>>
} forall % (templ) proc (scr) <<CMap>> <<Font>>
exch pop % (templ) proc (scr) <<Font>>
4 3 roll pop % proc (scr) <<Font>>
% Make the enumerator and apply it :
/MappedCategoryRedefiner /ProcSet findresource /MakeResourceEnumerator get exec exec} .internalbind def
currentdict end /Font exch /Category defineresource popendsetglobal .setlanguagelevel
|