% Copyright (C) 2001-2018 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., 1305 Grant Avenue - Suite 200, Novato, % CA 94945, U.S.A., +1(415)492-9861, for further information. % % ProcSet for implementing CMap resources. % When this is run, systemdict is still writable. % NOTE: Rearranged fonts are not implemented yet. [ /CMERGE_DEBUG /USE_CIDCHAR_AS_RANGE ] {dup where {pop pop} { currentdict exch //false def pop } ifelse} forall % ---------------- Public operators ---------------- % /.rewriteTempMapsNotDef { % % Before building .CodeMapData from .TempMaps, % we need to replace dst type codes in the notdef map with the value 3, % which corresponds to CODE_VALUE_NOTDEF, see gxfcmap.h . % CMAPDEBUG { (rewriting TempMapsNotDef\n) print flush } if .TempMaps 2 get dup length 0 gt { 0 get CMAPDEBUG { (...original...\n) print flush } if 1 5 2 index length 1 sub { { 1 index exch get 2 3 put } stopped { CMAPDEBUG { (cannot rewrite\n) print flush } if } { CMAPDEBUG { (rewrite\n) print flush } if } ifelse } for } if pop CMAPDEBUG { (...FINISHED...\n) print } if } bind def /.composefontdict { % composefont 10 dict begin /CMap 2 index dup type /dicttype ne { /CMap findresource } if def /FDepVector 1 index cvlit def % temporarily /Encoding [ 0 1 FDepVector length 1 sub { } for ] def /FontInfo 1 dict def % for .processToUnicode in pdf_font.ps . /FDepVector [ 0 1 FDepVector length 1 sub { % Stack: name cmap[name] fonts /FDepVector [ fonts... i FDepVector 1 index get dup type /dicttype ne { dup /CIDFont resourcestatus { pop pop /CIDFont } { /Font } ifelse findresource } if exch CMap /FontMatrices get dup length 2 index gt { exch get dup //null eq { pop } { makefont } ifelse } { pop pop } ifelse } for ] readonly def /FMapType 9 def /FontMatrix matrix def /FontName 3 index def CMap /WMode .knownget { /WMode exch def } if /FontType 0 def pop pop currentdict end } bind odef % composefont doesn't appear in CMap files -- it's documented in % the "PostScript Language Reference Manual Supplement". /composefont { % composefont .composefontdict /Font defineresource } bind def % ---------------- CMap operators ---------------- % 40 dict begin % Our internal .CodeMapData structure closely mirrors the structures % defined in gxfcmap.h (q.v.). () indicate a string, [] indicate an array, % ? indicates a Boolean, # indicates an integer, {} for grouping. % [[[(first) (last) ...]+] % code space ranges % [[(prefix) (key_size,?is_range,value_type,value_size) (keys...) % {(values...) | [value ...]} #font_index ]+] % code mappings % ...] % <> % notdef mappings % ] % FontMatrices is the array of matrices defined by begin/endusematrix. % All of the arrays and strings are read-only after they have been built. % % Note that the code in zfcmap.c that constructs the C structures from % the PostScript structures has intimate knowledge of the above format. % ****** NOTE: The code currently only handles "well-behaved" CMaps: % - CID values only (no bfchars), 16-bit % - Entries (both code space and map) must be sorted % - Only the last byte must vary in each map range, except for % the identity mapping % ------ Font-level operators ------ % /begincmap { % - begincmap - /.CodeMapData [[[]] [[]] [[]]] def /FontMatrices [] def /.FontIndex 0 def /.TempMaps [20 dict 50 dict 50 dict] def /CodeMap //null def % for .buildcmap } bind def /endcmap { % - endcmap - .rewriteTempMapsNotDef CMAPDEBUG { 2 (*** undefined charmap ***) 1 (*** defined charmap ***) 0 (*** code space ranges ***) 3 { = .TempMaps exch get 0 1 2 index length 1 sub { dup //== exec (\t) print 1 index exch get //== exec } for pop } repeat } if /.CodeMapData dup load [ exch .TempMaps aload pop begin begin begin { [ exch aload pop 0 1 currentdict length 1 sub { currentdict exch get } for ] end } forall ] .endmap def CMAPDEBUG { (*** Content of .CodeMapData ***) = 0 .CodeMapData { exch dup //== exec 1 add exch (\t) print //== exec } forall pop } if currentdict /.TempMaps undef /FontMatrices FontMatrices .endmap def } bind def /.endmap { % .endmap dup type /arraytype eq { % This might be a shared read-only array inherited via usecmap. % Don't try to update its elements if this is the case. dup wcheck { 0 1 2 index length 1 sub { 2 copy 2 copy get .endmap put pop } for readonly } if } { dup type /stringtype eq { readonly } if } ifelse } bind def /.appendmap { % -mark- ... .appendmap - .TempMaps exch get counttomark 1 add 1 roll ] 1 index length exch put } bind def /begincodespacerange { % begincodespacerange - pop mark } bind def /endcodespacerange { % ... endcodespacerange - 0 .appendmap } bind def /usecmap { % usecmap - /CMap findresource dup % Copy the top level of .CodeMapData /.CodeMapData exch /.CodeMapData get copyarray def /FontMatrices exch /FontMatrices get copyarray def } bind def /usefont { % usefont - /.FontIndex exch def } bind def /beginusematrix { % beginusematrix - FontMatrices wcheck not FontMatrices length 2 index le or { FontMatrices length 1 index 1 add .max array dup 0 FontMatrices putinterval /FontMatrices exch def } if } bind def /endusematrix { % endusematrix - FontMatrices 3 1 roll put } bind def % ------ Rearranged font operators ------ % /beginrearrangedfont { % beginrearrangedfont - 10 dict begin /.FontNames exch def /.FontName exch def begincmap } bind def /endrearrangedfont { % - endrearrangedfont - (REARRANGED FONTS NOT IMPLEMENTED YET.) = flush .FontName .FontNames 0 get findfont end definefont pop } bind def % ------ Character name/code selector operators ------ % /beginbfchar { % beginbfchar - pop mark } bind def /endbfchar { % ... endbfchar counttomark 2 idiv { counttomark -2 roll % process in correct order .addbfchar } repeat 1 .appendmap } bind def /beginbfrange { % beginbfrange - pop mark } bind def /endbfrange { % ... % endbfrange - counttomark 3 idiv { counttomark -3 roll % process in correct order dup type dup /arraytype eq exch /packedarraytype eq or { % Array value, split up. exch pop { % Stack: code to_code|charname 1 index exch .addbfchar % Increment the code. As noted above, we require % that only the last byte vary, but we still must % mask it after incrementing, in case the last % value was 0xff. % Stack: code prefix params key value fontindex 6 -1 roll dup length string copy dup dup length 1 sub 2 copy get 1 add 255 and put } forall pop } { % Single value, handle directly. .addbfrange } ifelse } repeat 1 .appendmap } bind def /.addbfchar { % .addbfchar % 1 index exch .addbfrange } bind def /.addbfrange { % % .addbfrange <> 4 string dup 3 3 index type /nametype eq { 2 index 2 1 put % dst = CODE_VALUE_GLYPH, see gxfcmap.h . 4 -1 roll 1 array astore 4 1 roll 4 } { 2 index 2 2 put % dst = CODE_VALUE_CHARS, see gxfcmap.h . 3 index length } ifelse put % Stack: code_lo code_hi value params 3 index 3 index eq { % Single value. 3 -1 roll pop exch () exch } { % Range. dup 0 4 index length put dup 1 1 put 4 2 roll 1 index dup length 1 sub 0 exch getinterval 5 1 roll % prefix % Stack: prefix value params code_lo code_hi concatstrings 3 -1 roll } ifelse .FontIndex } bind def % ------ CID selector operators ------ % /begincidchar { % begincidchar - pop mark } bind def /endcidchar { % ... endcidchar - 1 .endmapchars } bind def /begincidrange { % begincidrange - pop mark } bind def /endcidrange { % ... endcidrange - 1 .endmapranges } bind def /.endmapchars { % -mark- ... .endmapchars - counttomark 1 add 1 roll counttomark 2 idiv { counttomark -2 roll % process in correct order exch % % Construct prefix, params, key, value, font_index dup length 1 eq { % 1-byte <00 00 00 02> () % } { % N-byte dup 0 1 getinterval exch % make 1-byte prefix 4 string dup 0 USE_CIDCHAR_AS_RANGE { <00 01 00 02> % skelton for param } { <00 00 00 02> % skelton for param } ifelse putinterval exch % dup length % N 1 sub % N-1 dup % N-1 N-1 3 index % N-1 N-1 exch % N-1 N-1 0 exch % N-1 0 N-1 put % N-1 1 exch % 1 N-1 getinterval % USE_CIDCHAR_AS_RANGE { dup length 2 mul string % dup % 2 index % 0 exch putinterval % dup % 3 -1 roll % dup length % N-1 exch putinterval % } if } ifelse 4 -1 roll % .endmapvalue % % prefix params keys value fontindex counttomark 5 gt { % 2 (or more) ranges (1 range = 5 item) 4 index 10 index eq % compare prefix 4 index 10 index eq and % compare params 1 index 7 index eq and % compare fontindex { CMAPDEBUG { (merge! char\n) print } if pop 4 2 roll pop pop % prefix params keys value fontindex keys2 value2 5 -1 roll 3 -1 roll concatstrings % prefix params value fontindex value2 keys' 4 -1 roll 3 -1 roll concatstrings % prefix params fontindex keys' values' 3 -1 roll } if } if % end of 2 (or more) ranges CMERGE_DEBUG { ( prefix:) print 4 index =only ( param:) print 3 index =only ( key:) print 2 index =only ( hex_cid:) print 1 index =only ( font_idx:) print 0 index //== exec flush } if } repeat counttomark 2 add -1 roll .appendmap } bind def /.endmapranges { % -mark- ... % .endmapranges - counttomark 1 add 1 roll counttomark 3 idiv { counttomark -3 roll % process in correct order % Construct prefix, params, key_lo, key_hi, value, font_index 3 1 roll % % prefix key % 1-byte code: () . % 1-byte range: () . % N-byte code: . (*) % N-byte range: (*) (*) dup 2 index eq { % == % 0: prefix_len for 1-byte code % 1: prefix_len for N-byte code dup length 1 eq { 0 } { 1 } ifelse } { % != % calculate prefix_len for *-byte range dup length 1 sub % 0 % initial value for N { % (code_len-1) N dup 2 index ge { exit } if % if (N >= len - 1) exit 3 index 1 index get % N-th byte of code_lo 3 index 2 index get % N-th byte of code_hi eq { 1 add } { exit } ifelse } loop exch pop % discard } ifelse % cid_base code_lo code_hi prefix_len % Althogh Adobe CPSI with native CID/CMap support accept % multi-dimensional range specification in notdef & cidrange % (and CID is calculated as relative position in multi-dimensional % range), but older CPSI & ATM cannot handle it. % % GS accepts such specification, but it's recommended to keep % from using this feature for notdef & cidrange. % Following is a disabler of this feature. % ------------------------------------------------------------- % counttomark 1 add index % get map# % 0 ne { % if not codespacerange % 1 index length % get code length % 1 index % get prefix length % sub % calculate key length % 1 gt { % if (key_len > 1), % (.endmapranges error) = flush % (multi-dimensional range specification is used out of codespacerange) % = flush % (/) =only % CMapName CMapName length string cvs =only % (: <) =only % 2 index (%stdout) (w) file exch writehexstring % (> <) =only % 1 index (%stdout) (w) file exch writehexstring % (>\n) =only flush % quit % } if % } if % ------------------------------------------------------------- 1 index exch 0 exch getinterval % cid_base code_lo code_hi prefix dup length 3 index length exch sub % cid_base code_lo code_hi prefix range_len dup 255 gt { (too long coderange specification for current GS\n) print /.endmapranges cvx /rangecheck signalerror } if <00 01 00 02> 4 string copy % create initialized param dup 0 4 -1 roll put % put range_len into param % get key_hi 3 -1 roll dup length 3 index length dup 3 1 roll sub getinterval % get key_lo 4 -1 roll dup length 4 index length dup 3 1 roll sub getinterval % make "keys" (concatenated key_lo + key_hi) exch concatstrings % 4 -1 roll .endmapvalue % See if we can merge with the previous value. % The prefix, params, and font index must match. % prefix params keys value fontindex counttomark 5 gt { % 2 (or more) ranges (1 range = 5 item) 4 index 10 index eq % compare prefix 4 index 10 index eq and % compare params 1 index 7 index eq and % compare fontindex { CMAPDEBUG { (merge!\n) print } if pop 4 2 roll pop pop % prefix params keys value fontindex keys2 value2 5 -1 roll 3 -1 roll concatstrings % prefix params value fontindex value2 keys' 4 -1 roll 3 -1 roll concatstrings % prefix params fontindex keys' values' 3 -1 roll } if } if % end of 2 (or more) ranges } repeat counttomark 2 add -1 roll .appendmap } bind def /.endmapvalue { % .endmapvalue (hi,lo) .FontIndex 2 string dup 0 3 index -8 bitshift put % value dup 1 4 -1 roll 255 and put .FontIndex % font_index } bind def % ------ notdef operators ------ % /beginnotdefchar { % beginnotdefchar - pop mark } bind def /endnotdefchar { % ... endnotdefchar - 2 .endmapchars } bind def /beginnotdefrange { % beginnotdefrange - pop mark } bind def /endnotdefrange { % ... endnotdefrange - 2 .endmapranges } bind def % ---------------- Resource category definition ---------------- % currentdict end languagelevel exch 2 .setlanguagelevel /CMap /Generic /Category findresource dup length dict .copydict dup /InstanceType /dicttype put dup /DefineResource { % The AdobePS5 Windows driver emits code that attempts to % create CMaps without the required CMapName entry. % Work around this here. dup /CMapName known not { dup wcheck not { .currentglobal exch dup wcheck .setglobal dup length dict .copydict exch .setglobal } if dup gcheck 2 index gcheck not and { exch .currentglobal exch //true .setglobal dup length string copy exch .setglobal exch } if dup /CMapName 3 index put } if % Adobe PS CET 23-25 and others define an almost empty CMap. Tolerate this. % With the above, we can actually tolerate: /name << >> defineresource dup /CIDSystemInfo known not { dup wcheck not { .currentglobal exch dup wcheck .setglobal dup length dict .copydict exch .setglobal } if dup /CIDSystemInfo [ //null ] put } if dup /CodeMap .knownget { //null eq { .buildcmap } if } if /Generic /Category findresource /DefineResource get exec } bind executeonly put /Category defineresource pop % We might have loaded CID font support already. /CIDInit /ProcSet 2 copy { findresource } .internalstopped % An interior `stopped' might have reset VM allocation to local. //true .setglobal { pop pop 3 -1 roll } { dup length 4 index length add dict .copydict 4 -1 roll exch .copydict } ifelse exch defineresource pop .setlanguagelevel