plan9fox/sys/lib/ghostscript/gs_ttf.ps
2011-03-30 19:35:09 +03:00

1456 lines
45 KiB
PostScript

% Copyright (C) 1996-2003 artofcode LLC. 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.
%
% For more information about licensing, please refer to
% http://www.ghostscript.com/licensing/. For information on
% commercial licensing, go to http://www.artifex.com/licensing/ or
% contact Artifex Software, Inc., 101 Lucas Valley Road #110,
% San Rafael, CA 94903, U.S.A., +1(415)492-9861.
% $Id: gs_ttf.ps,v 1.48 2005/09/22 16:11:37 ray Exp $
% Support code for direct use of TrueType fonts.
% (Not needed for Type 42 fonts.)
% Note that if you want to use this file without including the ttfont.dev
% option when you built Ghostscript, you will need to load the following
% files before this one:
% lib/gs_mgl_e.ps
% lib/gs_mro_e.ps
% lib/gs_wan_e.ps
% Thanks to B. Jackowski and GUST (the Polish TeX Users' Group) for
% the glyf-splitting code.
% ---------------- Font loading machinery ---------------- %
% Augment the FONTPATH machinery so it recognizes TrueType fonts.
/.scanfontheaders where {
pop /.scanfontheaders [
.scanfontheaders aload pop (\000\001\000\000*) (true*)
] def
} if
% <file> <key> .findfontvalue <value> true
% <file> <key> .findfontvalue false
% Closes the file in either case.
/.findnonttfontvalue /.findfontvalue load def
/.findfontvalue {
1 index read {
2 index 1 index unread
% beginning with binary 0 or 't' (TrueType), or 'O' (OpenType)
dup 0 eq 1 index (O) 0 get eq or exch (t) 0 get eq or {
% If this is a font at all, it's a TrueType font.
dup /FontType eq {
pop closefile 42 true
} {
dup /FontName eq { pop .findttfontname } { pop closefile false } ifelse
} ifelse
} {
% Not a TrueType font.
.findnonttfontvalue
} ifelse
} { pop closefile false } ifelse
} bind def
% <file> .findttfontname <fname> true
% <file> .findttfontname false
% Closes the file in either case.
/.findttfontname {
//true 0 .loadttfonttables
tabdict /name .knownget {
dup 8 getu32 f exch setfileposition
12 getu32 string f exch readstring pop
6 findname
} {
false
} ifelse
f closefile end end
} bind def
% Load a font file that might be a TrueType font.
% <file> .loadfontfile -
/.loadnonttfontfile /.loadfontfile load def
/.loadfontfile {
dup read pop 2 copy unread 0 eq {
% If this is a font at all, it's a TrueType font.
.loadttfont pop
} {
% Not a TrueType font.
.loadnonttfontfile
} ifelse
} bind def
% ---------------- Automatic Type 42 generation ---------------- %
% Load a TrueType font from a file as a Type 42 PostScript font.
% The thing that makes this really messy is the handling of encodings.
% There are 2 interacting tables that affect the encoding:
% 'cmap' provides multiple maps from character codes to glyph indices
% 'post' maps glyph indices to glyph names (if present)
% What we need to get out of this is:
% Encoding mapping character codes to glyph names
% (the composition of cmap and post)
% CharStrings mapping glyph names to glyph indices
% (the inverse of post)
% If the post table is missing, we have to take a guess based on the cmap
% table.
/.loadttfontdict 50 dict dup begin
/orgXUID AladdinEnterprisesXUID def
/maxstring 32764 def % half the maximum length of a PostScript string,
% must be a multiple of 4 (for hmtx / loca / vmtx)
/.invert_encoding % <array> invert_encoding <dict>
{ dup 256 dict exch
0 exch 1 exch length 1 sub { % [] <> i
dup 3 index exch get % [] <> i v
dup /.notdef ne {
exch 2 index 2 index .knownget {
dup type /arraytype eq {
[ exch aload pop counttomark 2 add -1 roll ]
} {
exch 2 array astore
} ifelse
} if 2 index 3 1 roll put
} {
pop pop
} ifelse
} for
exch pop
} bind def
% Define the Macintosh standard mapping from characters to glyph indices.
/MacRomanEncoding dup .findencoding def
/MacGlyphEncoding dup .findencoding def
% Invert the MacRomanEncoding.
/.romanmacdict MacRomanEncoding .invert_encoding def
% Define remapping for misnamed glyphs in TrueType 'post' tables.
% There are probably a lot more than this!
/postremap mark
/Cdot /Cdotaccent
/Edot /Edotaccent
/Eoverdot /Edotaccent
/Gdot /Gdotaccent
/Ldot /Ldotaccent
/Zdot /Zdotaccent
/cdot /cdotaccent
/edot /edotaccent
/eoverdot /edotaccent
/gdot /gdotaccent
/ldot /ldotaccent
/zdot /zdotaccent
.dicttomark readonly def
% Array used for fast pre-filling of cmap array
/.array1024z [ 1024 { 0 } repeat ] def
% ---- Utilities ---- %
% Define a serial number for creating unique XUIDs for TrueType fonts.
% We used to use the checkSumAdjustment value from the font, but this is
% not reliable, since some fonts don't set it correctly.
% Note that we must do this in a string to make it immune to save/restore.
/xuidstring <80000000> def
/curxuid { % - curxuid <int>
0 xuidstring { exch 8 bitshift exch add } forall
} bind def
/nextxuid { % - nextxuid -
3 -1 0 {
xuidstring 1 index 2 copy get dup 255 ne {
1 add put pop exit
} if pop 0 put pop
} for
} bind def
% <string> <index> getu16 <integer>
/getu16 {
2 copy get 8 bitshift 3 1 roll 1 add get add
} bind def
% <string> <index> gets16 <integer>
/gets16 {
getu16 16#8000 xor 16#8000 sub
} bind def
% <string> <index> getu32 <integer>
/getu32 {
2 copy getu16 16 bitshift 3 1 roll 2 add getu16 add
} bind def
% <string> <index> gets32 <integer>
/gets32 {
2 copy gets16 16 bitshift 3 1 roll 2 add getu16 add
} bind def
16#ffffffff 0 gt { % 64-bit sign extension
{ /curxuid /gets32 } {
mark 1 index load aload pop { 16#80000000 xor 16#80000000 sub } aload pop
.packtomark cvx def
} bind forall
} if
% <string> <index> <integer> putu16 -
/putu16 {
3 copy -8 bitshift put
exch 1 add exch 16#ff and put
} bind def
% <string> <index> <integer> putu32 -
/putu32 {
3 copy -16 bitshift putu16
exch 2 add exch 16#ffff and putu16
} bind def
% <nametable> <nameid> findname <string> true
% <nametable> <nameid> findname false
/findname {
TTFDEBUG { (findname: ) print dup =only } if
false 3 1 roll
1 index length 0 gt { % check for zero length name table
0 1 3 index 2 getu16 1 sub {
% Stack: false table id index
12 mul 6 add 2 index exch 12 getinterval
dup 6 getu16 2 index eq {
% We found the name we want.
exch pop
% Stack: false table record
dup 10 getu16 2 index 4 getu16 add
1 index 8 getu16 4 -1 roll 3 1 roll
3 copy add 1 index length
le {
pop
getinterval exch
% Stack: false string record
% Check for 8- vs. 16-bit characters.
is2byte { string2to1 } if true null 4 -1 roll exit
} {
pop pop pop pop
false
exit
} ifelse
} if pop
} for
} if
pop pop
TTFDEBUG {
dup { ( = ) print 1 index == } { ( not found) = } ifelse
} if
} bind def
% <namerecord> is2byte <bool>
/is2byte {
dup 0 getu16 {
{ pop true } % Apple Unicode
{ pop false } % Macintosh Script manager
{ 1 getu16 1 eq } % ISO
{ 1 getu16 1 eq } % Microsoft
} exch get exec
} bind def
% <string2> string2to1 <string>
/string2to1 {
dup length 2 idiv string dup
0 1 3 index length 1 sub {
3 index 1 index 2 mul 1 add get put dup
} for pop exch pop
} bind def
% Each procedure in this dictionary is called as follows:
% <encodingtable> proc <glypharray>
/cmapformats mark
0 { % Apple standard 1-to-1 mapping.
6 256 getinterval { } forall 256 packedarray
} bind
2 { % Apple 16bit CJK (ShiftJIS etc)
% /sHK_sz subHeaderKey_size % 1 * uint16
% /sH_sz subHeader_size % 4 * uint16
% /sH_len subHeader_length
% /cmapf2_tblen total table length
% /cmapf2_lang language code (not used)
% /sHKs subHeaderKeys
/sHK_sz 2 def
/sH_sz 8 def
dup 2 getu16 /cmapf2_tblen exch def
dup 4 getu16 /cmapf2_lang exch def
dup 6 256 sHK_sz mul getinterval /sHKs exch def
0 % initialization value for /sH_len
0 1 255 {
sHKs exch
2 mul getu16
1 index % get current max
1 index % get current subHeaderKey
lt {exch} if pop
} for
/sH_len exch def
dup 6 256 sHK_sz mul add
cmapf2_tblen 1 index sub getinterval
/sH_gIA exch def
/cmapf2_glyph_array 65535 array def
/.cmapf2_putGID {
/cmapf2_ch cmapf2_ch_hi 8 bitshift cmapf2_ch_lo add def
firstCode cmapf2_ch_lo le
cmapf2_ch_lo firstCode entryCount add lt
and { % true: j is inside
sH_offset idRangeOffset add % offset to gI
cmapf2_ch_lo firstCode sub 2 mul % rel. pos. in range
add 6 add % offset in sH_gIA
sH_gIA exch getu16
dup 0 gt { %
idDelta add
cmapf2_glyph_array exch cmapf2_ch exch put
} {
pop
% cmapf2_glyph_array cmapf2_ch 0 put
} ifelse
} { % false: j is outside
% cmapf2_glyph_array cmapf2_ch 0 put
} ifelse
} def
16#00 1 16#ff { % hi_byte scan
/cmapf2_ch_hi exch def
sHKs cmapf2_ch_hi sHK_sz mul getu16
/sH_offset exch def
sH_gIA sH_offset sH_sz getinterval
dup 0 getu16 /firstCode exch def
dup 2 getu16 /entryCount exch def
dup 4 gets16 /idDelta exch def
dup 6 getu16 /idRangeOffset exch def
pop
sH_offset 0 eq {
/cmapf2_ch_lo cmapf2_ch_hi def
/cmapf2_ch_hi 0 def
.cmapf2_putGID
} {
16#00 1 16#ff { % lo_byte scan
/cmapf2_ch_lo exch def
.cmapf2_putGID
} for
} ifelse
} for
pop
0 1 cmapf2_glyph_array length 1 sub { % rewrite null -> 0.
dup cmapf2_glyph_array exch get
null eq { cmapf2_glyph_array exch 0 put } {pop} ifelse
} for
cmapf2_glyph_array
} bind
4 { % Microsoft/Adobe segmented mapping.
/etab exch def
/nseg2 etab 6 getu16 def
14 /endc etab 2 index nseg2 getinterval def
% The Apple TrueType documentation omits the 2-byte
% 'reserved pad' that follows the endCount vector!
2 add
nseg2 add /startc etab 2 index nseg2 getinterval def
nseg2 add /iddelta etab 2 index nseg2 getinterval def
nseg2 add /idroff etab 2 index nseg2 getinterval def
% The following hack allows us to properly handle
% idiosyncratic fonts that start at 0xf000:
pop
/firstcode startc 0 getu16 16#ff00 and dup 16#f000 ne { pop 0 } if def
/putglyph {
glyphs code 3 -1 roll put /code code 1 add def
} bind def
% Do a first pass to compute the size of the glyphs array.
/numcodes 0 def /glyphs 0 0 2 nseg2 3 sub {
% Stack: /glyphs numglyphs i2
/i2 exch def
/scode startc i2 getu16 def
/ecode endc i2 getu16 def
numcodes scode firstcode sub
% Hack for fonts that have only 0x0000 and 0xf000 ranges
%dup 16#e000 ge { 255 and } if
% the previous line is obstructive to CJK fonts, so it was removed
exch sub 0 .max ecode scode sub 1 add add
exch 1 index add exch
numcodes add /numcodes exch def
} for array def
% prefill the array with 0's faster than a { 0 putglyph } repeat
glyphs length 1024 ge {
.array1024z 0 1024 glyphs length 1023 sub { glyphs exch 2 index putinterval } for
glyphs dup length 1024 sub 3 -1 roll
putinterval
} {
0 1 glyphs length 1 sub { glyphs exch 0 put } for
} ifelse
% Now fill in the array.
/numcodes 0 def /code 0 def
0 2 nseg2 3 sub {
/i2 exch def
/scode startc i2 getu16 def
/ecode endc i2 getu16 def
numcodes scode firstcode sub
% Hack for fonts that have only 0x0000 and 0xf000 ranges
%dup 16#e000 ge { 255 and } if
% the previous line is obstructive to CJK fonts, so it was removed
exch sub 0 .max dup /code exch code exch add def
ecode scode sub 1 add add numcodes add /numcodes exch def
/delta iddelta i2 gets16 def
TTFDEBUG {
(scode=) print scode =only
( ecode=) print ecode =only
( delta=) print delta =only
( droff=) print idroff i2 getu16 =
} if
idroff i2 getu16 dup 0 eq {
pop scode delta add 65535 and 1 ecode delta add 65535 and
{ putglyph } for
} { % The +2 is for the 'reserved pad'.
/gloff exch 14 nseg2 3 mul add 2 add i2 add add def
0 1 ecode scode sub {
2 mul gloff add etab exch getu16
dup 0 ne { delta add 65535 and } if putglyph
} for
} ifelse
} for glyphs /glyphs null def % for GC
} bind
6 { % Single interval lookup.
dup 6 getu16 /firstcode exch def dup 8 getu16 /ng exch def
firstcode ng add array
% Stack: tab array
% Fill elements 0 .. firstcode-1 with 0
0 1 firstcode 1 sub { 2 copy 0 put pop } for
dup firstcode ng getinterval
% Stack: tab array subarray
% Fill elements firstcode .. firstcode+nvalue-1 with glyph values
0 1 ng 1 sub {
dup 2 mul 10 add 4 index exch getu16 3 copy put pop pop
} for pop exch pop
} bind
.dicttomark readonly def % cmapformats
% <cmaptab> cmaparray <glypharray>
/cmaparray {
dup 0 getu16 cmapformats exch .knownget {
TTFDEBUG {
(cmap: format ) print 1 index 0 getu16 = flush
} if exec
} {
(Can't handle format ) print 0 getu16 = flush
0 1 255 { } for 256 packedarray
} ifelse
TTFDEBUG {
(cmap: length=) print dup length = dup ==
} if
} bind def
/get_from_stringarray % <array|string> <offset> get_from_stringarray <int>
{ 1 index type /stringtype eq {
get
} {
exch { % o ()
2 copy length gt {
length sub
} {
exch get exit
} ifelse
} forall
} ifelse
} bind def
/getinterval_from_stringarray % <array|string> <offset> <length> getinterval_from_stringarray <string>
{ % May allocate a string in VM.
2 index type /stringtype eq {
getinterval
} {
string exch 0 % [] s o p
4 3 roll { % s o p Si
dup length % s o p Si lSi
dup 4 index lt {
3 index exch sub % s o p Si o'
exch pop 3 1 roll exch pop % s o' p
} { % s o p Si lSi
dup 3 1 roll % s o p lSi Si lSi
4 index sub % s o p lSi Si lSi-o
5 index length 4 index sub % s o p lSi Si lSi-o ls-p
2 copy gt { exch } if pop % s o p lSi Si minl
dup 3 1 roll % s o p lSi minl Si minl
5 index exch getinterval % s o p lSi minl from
5 index 4 index 3 index % s o p lSi minl from s p minl
getinterval % s o p lSi minl from to
copy pop % s o p lSi minl
3 1 roll % s o minl p lSi
sub % s o minl p'
3 1 roll add % s p' o'
dup 3 index length ge {
exch exit % s o p'
} if
exch % s o' p'
} ifelse
} forall
pop pop % s
} ifelse
} bind def
/string_array_size % <array|string> string_array_size <int>
{ dup type /stringtype eq {
length
} {
0 exch { length add } forall
} ifelse
} bind def
% Each procedure in this dictionary is called as follows:
% posttable <<proc>> glyphencoding
/postformats mark
16#00010000 { % 258 standard Macintosh glyphs.
pop MacGlyphEncoding
}
16#00020000 { % Detailed map, required by Microsoft fonts.
dup dup type /arraytype eq { 0 get } if length 36 lt {
TTFDEBUG { (post format 2.0 invalid.) = flush } if
pop [ ]
} {
/postglyphs exch def
/post_first postglyphs dup type /arraytype eq { 0 get } if def
post_first 32 getu16 /numglyphs exch def
/glyphnames numglyphs 2 mul 34 add def
% Build names array in the order they occur in the 'post' table
/postpos glyphnames def
/total_length postglyphs //string_array_size exec def
[ numglyphs 1 sub {
postpos total_length ge { exit } if
% No name available, /postnames will be defined as an empty
% array and the glyph won't get a name attached.
postglyphs postpos //get_from_stringarray exec
postglyphs postpos 1 add 2 index //getinterval_from_stringarray exec cvn
exch postpos add 1 add /postpos exch def
} repeat
] /postnames exch def
[ 0 1 numglyphs 1 sub {
2 mul 34 add post_first exch getu16
dup 258 lt {
MacGlyphEncoding exch get
} {
dup 32768 ge {
% According to the published TrueType spec, such values are
% "reserved for future use", but at least some PDF files
% produced by the Adobe PDF library contain entries with a
% value of 16#ffff.
pop /.notdef
} {
% Get the name for this glyph
258 sub dup postnames length ge {
TTFDEBUG { ( *** warning: glyph index past end of 'post' table) = flush } if
exit
} if
postnames exch get
% At least some of Microsoft's TrueType fonts use incorrect
% (Adobe-incompatible) names for some glyphs.
% Correct for this here.
postremap 1 index .knownget { exch pop } if
} ifelse
} ifelse
} for ]
}
ifelse
} bind
16#00030000 { % No map.
pop [ ]
} bind
.dicttomark readonly def % postformats
/call.readtable
{ .readtable
} bind def
/call.readbigtable
{ .readbigtable
} bind def
% Each procedure in this dictionary is called as follows:
% <file> <length> -proc- <string|array_of_strings>
% Note that each table must have an even length, because of a strange
% Adobe requirement that each sfnts entry have even length.
/readtables mark
% Ordinary tables
(cmap) //call.readtable
(head) 1 index
(hhea) 1 index
(maxp) 1 index
(name) 1 index
(OS/2) 1 index
(post) //call.readbigtable
(vhea) //call.readtable
% Big tables
(glyf) //call.readbigtable
(loca) 1 index
(hmtx) 1 index
(vmtx) 1 index
% Tables only needed for embedding in PDF files
(cvt ) //call.readtable
(fpgm) 1 index
(prep) 1 index
.dicttomark
% Normally there would be a 'readonly' here, but the ttf2pf utility wants
% to include the 'kern' table as well, so we leave the readtables dictionary
% writable.
def % readtables
/readtables_stripped readtables dup length dict copy
dup (loca) { .skiptable } put
dup (glyf) { .skiptable } put
def
% Read a table as a single string.
% <file> <length> .skiptable <string>
/.skiptable {
pop pop ()
} bind def
% Read a table as a single string.
% <file> <length> .readtable <string>
/.readtable {
dup dup 1 and add string
% Stack: f len str
dup 0 4 -1 roll getinterval
% Stack: f str str1
% Because of the absurd PostScript specification that gives an
% error for reading into an empty string, we have to check for
% this explicitly here.
3 -1 roll exch
dup () ne { readstring } if pop pop
} bind def
% Read a big table (one that may exceed 64K).
% <file> <length> .readbigtable <string[s]>
/.readbigtable {
dup 65400 lt {
.readtable
} {
currentuserparams /VMReclaim get -2 vmreclaim
[ 4 2 roll {
% Stack: mark ... f left
dup maxstring le { exit } if
1 index maxstring string readstring pop 3 1 roll maxstring sub
} loop .readtable ]
exch vmreclaim
} ifelse
} bind def
end readonly def % .loadttfontdict
% <tab> .printtab -
/.printtab {
dup 0 4 getinterval print ( ) print
dup 8 getu32 =only ( ) print
12 getu32 =
} bind def
% <file> <bool> <SubfontID> .loadttfonttables -
% Pushes .loadttfontdict & scratch dict on d-stack.
% Defines f, offsets, tables, tabdict, tabs.
% Skips loca and glyf if <bool> is true.
/.loadttfonttables {
.loadttfontdict begin
40 dict begin
/SubfontID exch def
/load_stripped exch def
/f exch def
/offsets f 12 string readstring pop def
load_stripped { readtables_stripped } { readtables } ifelse /readtables_ exch def
offsets 0 4 getinterval (ttcf) eq {
% We need to handle TT collections with disk fonts only.
% Therefore the file is a disk file and it can be positioned.
offsets 8 getu32 /num_fonts exch def
SubfontID num_fonts ge {
QUIET not { (True Type collection contains insufficient fonts.) = } if
/.loadttfonttables cvx /invalidfont signalerror
} if
SubfontID 4 mul 12 add f exch setfileposition
f 4 string readstring pop 0
getu32 /ttc_offset exch def
f ttc_offset setfileposition
/offsets f 12 string readstring pop def
} {
SubfontID 0 gt {
QUIET not { (SubfontID > 0 with a True Type file which is not a collection.) = } if
/.loadttfonttables cvx /invalidfont signalerror
} if
/ttc_offset 0 def
} ifelse
/tables f offsets 4 getu16 16 mul string readstring pop def
/tabdict tables length 16 idiv dict def
% tabs = tables we want to keep, sorted by file position.
/tabs [ 0 16 tables length 1 sub {
tables exch 16 getinterval
TTFDEBUG { dup .printtab } if
dup 0 4 getinterval readtables_ 1 index known {
% put all 0 length tables at 0 to avoid overlap
1 index 12 getu32 0 eq { 1 index 8 0 putu32 } if
tabdict exch 2 index put
} {
pop pop
} ifelse
} for ] {
exch 8 getu32 exch 8 getu32 lt
} .sort def
% In certain malformed TrueType fonts, tables overlap.
% Truncate tables if necessary.
0 1 tabs length 2 sub {
dup tabs exch get exch 1 add tabs exch get
1 index 8 getu32 2 index 12 getu32 add
1 index 8 getu32 gt {
(**** Warning: ) print 1 index 0 4 getinterval print
( overlaps ) print dup 0 4 getinterval print
(, truncating.) = flush
dup 8 getu32 2 index 8 getu32 sub
2 index 12 3 -1 roll putu32
} if pop pop
} for
} bind def
/.file_table_pos_names
mark
/glyf 0
/loca 0
.dicttomark readonly def
% - .readttdata -
% Read data. Updates offsets, tabs; stores data in tabdict.
/.readttdata {
/file_table_pos 10 dict def
/fpos offsets length tables length add ttc_offset add def
/sfpos offsets length tabs length 16 mul add def
offsets 4 tabs length putu16
tabs {
dup 0 4 getinterval /tname exch def
dup 8 getu32 /tpos exch def
dup 12 getu32 /tlen exch def
load_stripped //.file_table_pos_names tname known and {
pop
file_table_pos tname [tpos tlen] put
tabdict tname () put
} {
8 sfpos putu32
% Skip data between the end of the previous table and
% the beginning of this one, if any.
tpos fpos gt {
load_stripped {
% 'setfileposition' is faster for skipping a big data.
f tpos setfileposition
} {
f tpos fpos sub () /SubFileDecode filter dup flushfile closefile
/fpos tpos def
} ifelse
} if
f tlen readtables_ tname get exec
tabdict tname 3 -1 roll put
% Round up the table length to an even value.
/sfpos sfpos tlen dup 1 and add add def
} ifelse
/fpos fpos tlen add def
} forall
} bind def
% Find the string in a list of strings that includes a given index.
% <strings> <index> .findseg <string> <index'>
/.findseg {
exch {
dup length 2 index gt { exch exit } if
length sub
} forall
} bind def
% - .makesfnts -
% Defines checksum, getloca, head, locatable, numloca, post, sfnts, upem
% Note that the 'loca' table may be out of order. This is handled when
% needed in .dividesfnts
/.makesfnts {
.readttdata
/head tabdict /head get def
/post tabdict /post .knownget {
dup 0 get /post_first_part exch def
} {
null
} ifelse def
load_stripped not {
/locatable tabdict /loca get def
/numloca
locatable dup type /stringtype eq
{ length }
{ 0 exch { length add } forall }
ifelse % no def yet
locatable type /stringtype eq {
/.indexloca {} def
} {
/.indexloca /.findseg load def
} ifelse
head 50 getu16 0 ne {
/getloca {
2 bitshift locatable exch .indexloca getu32
} def
4 idiv 1 sub
} {
/getloca {
dup add locatable exch .indexloca getu16 dup add
} def
2 idiv 1 sub
} ifelse def % numloca
% If necessary, re-partition the glyfs.
tabdict /glyf get dup type /stringtype ne {
.dividesfnts tabdict /glyf 3 -1 roll put
} {
pop
} ifelse
} {
% We did not load loca, take the number of glyphs from maxp.
/numloca tabdict /maxp get 4 getu16 def
} ifelse
/sfnts [
offsets tabs { concatstrings } forall
tabs {
0 4 getinterval tabdict exch get
dup type /stringtype ne { aload pop } if
} forall
] def
} bind def
% <glyfs> .dividesfnts <glyfs'>
/.dividesfnts {
/glyfs exch def
/len1 0 glyfs { length add } forall def
% Determine where to split the glyfs by scanning the sorted locatable
% The very last entry in loca may be bogus.
% Note that some loca entries may be odd, but we can only
% split at even positions.
%
% Construct splitarray, the array of final lengths of
% the sfnts entries covering the glyfs (i.e., all but
% the first and last sfnts entries).
/prevsplit 0 def
/prevboundary 0 def
% sort the locatable in case it is out of order
% Note the 'loca' table remains unchanged
/needsort false def
numloca array % the array of 'loca' entries (may be out of order)
-1 % initial values for in order check
0 1 numloca 1 sub {
dup getloca dup
4 -1 roll lt { /needsort true def } if
3 copy put exch pop
} for pop % discard inorder check value
needsort {
{ lt } bind .sort % stack: locatable_array
} if
/splitarray [
3 -1 roll 0 1 numloca 1 sub {
% stack: /splitarray -mark- { splitlen splitlen ... } locatable_array index
1 index exch get dup prevsplit maxstring add gt {
prevboundary prevsplit sub exch
/prevsplit prevboundary def
} if
dup 1 and 0 eq { /prevboundary exch def } { pop } ifelse
dup type /arraytype ne { exch } if % keep locatable_array on top
} for
len1 prevsplit sub
exch pop % discard locatable_array
] def
currentuserparams /VMReclaim get -2 vmreclaim
[
% Re-split the sfnts glyfs strings according to splitarray.
% We do this by iterating over the final segments defined
% by splitarray, and constructing them from pieces of the
% current glyfs strings. We recycle the current strings
% when possible, to avoid stressing the allocator.
/sfnt_idx 0 def
/strpos 0 def
/avail () def
splitarray {
/seglen exch def
/segpos 0 def
avail length seglen ge
{ avail 0 seglen getinterval /avail () def } { seglen string }
ifelse
{
/str glyfs sfnt_idx get def
/strlen str length def
/strleft strlen strpos sub def
seglen segpos sub strleft lt { exit } if
% Copy the (rest of the) string into the new segment.
% We know strleft <= segleft.
dup segpos str strpos strleft getinterval putinterval
/segpos segpos strleft add def
/avail str def
/sfnt_idx sfnt_idx 1 add def
/strpos 0 def
segpos seglen eq { exit } if
} loop
% Fill up the segment with an initial piece of the next
% existing glyfs string. We know strleft > segleft.
/segleft seglen segpos sub def
dup segpos str strpos segleft getinterval putinterval
/strpos strpos segleft add def
} forall
]
exch vmreclaim
} bind def
/first_post_string % - first_post_string <string>
{
post dup type /arraytype eq { 0 get } if
} bind def
% - .getpost -
% Uses post, defines glyphencoding
/.getpost {
/glyphencoding post null eq {
TTFDEBUG { (post missing) = flush } if [ ]
} {
postformats first_post_string 0 getu32 .knownget {
TTFDEBUG {
(post: format ) print
first_post_string
dup 0 getu16 =only (,) print 2 getu16 = flush
} if
post exch exec
} {
TTFDEBUG { (post: unknown format ) print post 0 getu32 = flush } if [ ]
} ifelse
} ifelse
TTFDEBUG { (post=) print dup == } if
def
} bind def
% - .ttkeys <key> <value> ...
/.ttkeys {
count /ttkeycount exch def
/upem head 18 getu16 def
/FontMatrix matrix
/FontBBox [ 36 2 42 { head exch gets16 upem div } for ]
nextxuid
tabdict /name .knownget {
% Find the names from the 'name' table.
/names exch def
/FontName names 6 findname not { curxuid 16 8 string cvrs } if
/fontname 1 index def
/FontInfo mark
names 0 findname { /Notice exch } if
names 1 findname { /FamilyName exch } if
names 4 findname { /FullName exch } if
names 5 findname { /Version exch } if
} {
% No name table, fabricate a FontName.
/FontName curxuid 16 8 string cvrs
/fontname 1 index def
/FontInfo mark
} ifelse
% Stack: ... /FontInfo mark key1 value1 ...
post null ne {
/ItalicAngle first_post_string 4 gets32 65536.0 div
/isFixedPitch first_post_string 12 getu32 0 ne
/UnderlinePosition first_post_string 8 gets16 upem div
/UnderlineThickness first_post_string 10 gets16 upem div
} if
counttomark 0 ne { .dicttomark } { pop pop } ifelse
/XUID [orgXUID 42 curxuid]
TTFDEBUG {
tabs { .printtab } forall
[ sfnts { length } forall ] ==
count ttkeycount sub array astore dup { == } forall aload pop
} if
/sfnts sfnts
} bind def
% ---------------- Standard TrueType font loading ---------------- %
% - .pickcmap_with_no_xlatmap -
% Defines cmapsub, cmaptab
/.pickcmap_with_no_xlatmap {
tabdict /cmap get
% The Apple cmap format is no help in determining the encoding.
% Look for a Microsoft table. If we can't find one,
% just use the first table, whatever it is.
dup 4 8 getinterval exch % the default
0 1 2 index 2 getu16 1 sub {
8 mul 4 add 1 index exch 8 getinterval
TTFDEBUG {
(cmap: platform ) print dup 0 getu16 =only
( encoding ) print dup 2 getu16 = flush
} if
dup 0 getu16 3 eq { exch 3 -1 roll pop exit } if pop
} for
% Stack: subentry table
/cmapsub 2 index def
exch 4 getu32 1 index length 1 index sub getinterval
/cmaptab exch def
} bind def
% - .pickcmap_with_xlatmap -
% Defines cmapsub, cmaptab
/.pickcmap_with_xlatmap {
.xlatmap_dict /TrueType known not {
(Emulating a CID font with a True Type file, ) print
(the file gs/lib/xlatmap must contain /TrueType key.) =
/.pickcmap_with_xlatmap cvx /configurationerror signalerror
} if
false
.xlatmap_dict /TrueType get
dup length 2 sub 0 exch 2 exch { % bool [] i
2 copy get % bool [] i ()
(.) search { % bool [] i post match pre
cvi exch pop exch cvi % bool [] i PlatID SpecID
} {
(gs/lib/xlatmap containg a record with an invalid (PlatformID.SpecificID)) =
/.pickcmap_with_xlatmap cvx /configurationerror signalerror
} ifelse
TTFDEBUG {
(Seeking a cmap for platform=) print 1 index =only ( encoding=) print dup =
} if
tabdict /cmap get % bool [] i PlatID SpecID (cmap)
dup /cmaptab exch def % temporary
0 1 2 index 2 getu16 1 sub { % bool [] i PlatID SpecID (cmap) j
8 mul 4 add 1 index exch 8 getinterval % bool [] i PlatID SpecID (cmap) (cmapsub)
TTFDEBUG {
(cmap: platform ) print dup 0 getu16 =only
( encoding ) print dup 2 getu16 = flush
} if
dup 0 getu16 4 index eq {
dup 2 getu16 3 index eq { % bool [] i PlatID SpecID (cmap) (cmapsub)
TTFDEBUG {
(Choosen a cmap for platform=) print 3 index =only
( encoding=) print 2 index =
} if
/cmapsub 1 index def
dup 4 getu32 % bool [] i PlatID SpecID (cmap) (cmapsub) p
cmaptab length 1 index sub % bool [] i PlatID SpecID (cmap) (cmapsub) p l
cmaptab 3 1 roll getinterval
/cmaptab exch def % bool [] i PlatID SpecID (cmap) (cmapsub)
5 index 5 index 1 add get % bool [] i PlatID SpecID (cmap) (cmapsub) /Decoding
/Decoding exch def % bool [] i PlatID SpecID (cmap) (cmapsub)
7 -1 roll pop true 7 1 roll % true [] i PlatID SpecID (cmap) (cmapsub)
} if
} if
pop % true [] i PlatID SpecID (cmap)
5 index { exit } if
} for % bool [] i PlatID SpecID (cmap)
pop pop pop pop % bool []
1 index { exit } if
} for % bool []
pop % bool
not {
QUIET not { (True Type font doesn't contain a charset listed in gs/lib/xlatmap.) = } if
/.pickcmap_with_xlatmap cvx /invalidfont signalerror
} if %
} bind def
% - .pickcmap -
% Defines cmapsub, cmaptab
/.pickcmap {
% Currently we use xlatmap only for emulation CIDFontType 2 with
% a disk True Type font files, and use load_stripped
% to check this regime. We would like to do so
% while emulating a Type 42, but first the old code
% about handling them to be changed
% with adding a handling of a Decoding.
% fixme : A correct way to fix this is to implenent
% the Type 42 emulation with gs_fntem.ps .
% Also note that PDF embedded fonts probably don't need a xlatmap -
% see PDF spec, "Encodings for True Type fonts".
load_stripped {
//.pickcmap_with_xlatmap exec
} {
//.pickcmap_with_no_xlatmap exec
} ifelse
} bind def
% <glyph> .nname <_name>
/.nname {
=string cvs (_) exch concatstrings cvn
} bind def
% - .charkeys /CharStrings <charstrings> /Encoding <encoding>
% Resets glyphencoding
/.charkeys {
TTFDEBUG {
(glyphencoding: length=) print glyphencoding dup length = === flush
} if
% Hack: if there is no usable post table but the cmap uses
% the Microsoft Unicode encoding, use ISOLatin1Encoding.
glyphencoding length 0 eq {
cmapsub 0 4 getinterval <00030001> eq {
PDFDEBUG { (No post but have cmap 3.1, so use ISOLatin1Encoding) } if
/glyphencoding ISOLatin1Encoding dup length array copy def
} {
PDFDEBUG { (No encoding info, use .GS_extended_SymbolEncoding) } if
/glyphencoding /.GS_extended_SymbolEncoding findencoding dup length array copy def
} ifelse
} if
% If necessary, fabricate additional glyphencoding entries
% to cover all of loca, or truncate glyphencoding.
glyphencoding length numloca lt {
/glyphencoding numloca array
glyphencoding length dup 1 sub 0 1 3 2 roll {
dup glyphencoding exch get
3 index 3 1 roll put
} for
% /glyphencoding <newarray> <glyphencoding length>
1 numloca 1 sub {
1 index exch dup .nname put
} for
def
} {
/glyphencoding glyphencoding 0 numloca getinterval def
} ifelse
% Some badly designed Chinese fonts have a post table
% in which all glyphs other than 0 are named .null.
% Use CharStrings to keep track of the reverse map from
% names to glyphs, and don't let any name be used for
% more than one glyph.
/CharStrings glyphencoding dup length 1 add dict % +1 for .notdef
0 1 3 index length 1 sub {
% Stack: glyphencoding dict index
2 index 1 index get 2 index 1 index known {
% The same name maps to more than one glyph.
% Change the name.
pop dup .nname 3 index 2 index 2 index put
} if
2 index exch 3 -1 roll put
} for exch pop
% If there is no .notdef entry, map it to glyph 0.
dup /.notdef known not { dup /.notdef 0 put } if
readonly
/Encoding
[ cmaptab cmaparray dup length 256 gt { 0 256 getinterval } if
{ glyphencoding exch get } forall
counttomark 256 exch sub { /.notdef } repeat ]
TTFDEBUG { (Encoding: ) print dup === flush } if
} bind def
% -mark- <key> <value> ... .definettfont <font>
/.definettfont {
/FontType 42
/PaintType 0
TTFDEBUG {
(numloca=) print numloca =
} if
.dicttomark
end end dup /FontName get exch definefont
} bind def
% <file> .loadttfont <type42font>
/.loadttfont {
//false 0 .loadttfonttables
.makesfnts
.getpost
.pickcmap
mark
.charkeys
.ttkeys
.definettfont
} bind def
% ---------------- CIDFontType 2 font loading ---------------- %
% Fill a string with sequential CIDs starting from the initial value.
% <string> <value> .fill_identity_cmap <string>
/.fill_identity_cmap { % () v
1 index length 2 sub % () v n-2
0 2 3 2 roll { % () v 0 2 n-1
3 copy exch % () v i () i v
-8 bitshift % () v i () i v>>8
put % () v i
3 copy 1 add % () v i () v i+1
exch 255 and % () v i () i+1 v&255
put % () v i
pop 1 add % () v+1
} for
pop
} bind def
% -mark- <key> <value> ... .definettcidfont <font>
/.definettcidfont {
/CIDFontName fontname
/CIDFontType 2
/CIDSystemInfo mark
/Registry (Adobe)
/Ordering (Japan1) % adhoc
/Supplement 0
.dicttomark
/CharStrings mark /.notdef 0 .dicttomark
% The cmap isn't of any use even if it is present.
% Just construct an identity CIDMap covering all the glyphs.
/CIDCount numloca % Wrong if a CIDFontType2 embedded into PDF with a non-Identity CIDToGIDMap.
% processCIDToGIDMap may replace.
/CIDMap numloca maxstring le {
% Use a single string.
numloca 2 mul string 0 .fill_identity_cmap
} {
% We must use 2 strings.
maxstring 2 mul string 0 .fill_identity_cmap
numloca maxstring sub 2 mul string maxstring .fill_identity_cmap
2 array astore
} ifelse
/GDBytes 2
.dicttomark
end end dup /CIDFontName get exch /CIDFont defineresource
} bind def
% <file> .loadttcidfont <cidtype2font>
/.loadttcidfont {
//false 0 .loadttfonttables
.makesfnts
% CIDFontType2 fonts don't have a cmap: they are indexed by CID.
mark
.ttkeys
.definettcidfont
} bind def
% <file> <SubfontID> .load_tt_font_stripped <font_data>
% The font_data includes sfnts, NumGlyphs, TT_cmap, file_table_pos, Decoding.
% CIDMap to be created later from TT_cmap.
/.load_tt_font_stripped {
//true exch .loadttfonttables
.makesfnts
.pickcmap
mark
.ttkeys
/NumGlyphs numloca
/TT_cmap cmaptab cmaparray
/file_table_pos file_table_pos
/Decoding Decoding
.dicttomark
end end
} bind def
% ---------------- PDF TrueType font loading ---------------- %
% Strictly speaking, this code should be loaded only if we have a PDF
% interpreter, but it's so closely tied to the rest of the code in this
% file that we always include it.
% <plat+enc> .findcmap <subtable> true
% <plat+enc> .findcmap false
/.findcmap {
false exch tabdict /cmap get
% Some fonts have multiple cmaps with the same platform and
% encoding. Use the first one we find.
0 1 2 index 2 getu16 1 sub {
% Stack: false plat+enc cmap index
8 mul 4 add 1 index exch 8 getinterval
dup 0 4 getinterval 3 index eq {
4 getu32 1 index exch 1 index length 1 index sub getinterval
4 -1 roll not 4 2 roll exit
} if pop
} for
% Stack: false plat+enc cmap || subtable true plat+enc cmap
pop pop
} bind def
% Build .symbol_list for .pdfcharkeys .
% It is a dictionary containing all SymbolEncoding glyph names
% and random names for filling gaps in the character code range.
/.symbol_list 256 dict def
{
=string 0 (x) 0 get put
/SymbolEncoding .findencoding
0 1 255 {
dup 2 index exch get
dup /.notdef eq {
pop dup
=string 1 3 getinterval cvs length 1 add
=string exch 0 exch getinterval cvn
} if
exch //.symbol_list 3 1 roll put
} for
pop
} bind exec
% Create .GS_extended_SymbolEncoding as inverse of .symbol_list .
{
/.GS_extended_SymbolEncoding 256 array
//.symbol_list {
exch 2 index 3 1 roll put
} forall
.defineencoding
} bind exec
/.addglyph % <name> <glyph#> .addglyph <name> <glyph#>
% <name> <glyph#> .addglyph -
{
dup cmapencoding length lt {
cmapencoding exch get dup 0 eq {
pop pop
} if
} {
pop pop
} ifelse
} bind def
% <subcmap> <chartoglyphmap> .pdfmapchars
% /CharStrings <charstrings>
/.pdfmapchars {
exch cmaparray /cmapencoding exch def
/CharStrings mark
% Add glyphs of <chartoglyphmap>*<subcmap> :
3 2 roll {
dup type /arraytype eq {
exch /.name exch def
{ .name exch //.addglyph exec
} forall
currentdict /.name undef
} {
//.addglyph exec
} ifelse
} forall
% stack: /CharStrings mark /name1 glyph#1 /name2 glyph#2 ... /namen glyph#n
% Stack depth is restricted with AdobeGlyphList size.
% Add glyphs of 'post' (with lower priority, see .dicttomark) :
0 1 glyphencoding length 1 sub {
dup glyphencoding exch get exch
dup 0 eq {
pop pop
} if
} for
/.notdef 0
.dicttomark
} bind def
% - .pdfcharkeys /CharStrings <charstrings> /Encoding <encoding>
/.pdfcharkeys {
% The following algorithms are per the PDF Reference, Second Edition
% (PDF 1.3 reference manual).
is_symbolic {
<00030001> .findcmap {
%
% Adobe PDF spec says that symbolic fonts should contain exactly one
% cmap subtable for Platform=1, Encoding=0.
% Perhaps "Adobe Acrobat PDFWriter 4.0 for Windows" sometimes embeds
% fonts with both subtables 3.1 and 1.0 (see comparefiles/159.pdf,
% the font "Arial,Bold" with the character "registered"),
% and both Acrobat Reader 4.0 and 5.0 choose 3.1.
% Therefore we try 3.1 first.
%
( **** Warning: Embedded symbolic TT fonts should not contain a cmap for Platform=3 Encoding=1.\n)
pdfformaterror
TTFDEBUG { (Using cmap 3.1) = } if
AdobeGlyphList .pdfmapchars
/Encoding /WinAnsiEncoding .findencoding
} {
%
% Adobe PDF spec says that in this case PDF interpreter should
% map character codes directly to glyphs using
% the cmap <00010000>. But we use PS interpreter to emulate
% a PDF interpreter. Therefore we need to construct
% a type 42 font, which requires an Encoding and a Charstrings.
% We construct them with symbol_list, which
% includes all glyphs from SymbolEncoding and additional
% random names for 1-to-1 mapping.
%
% A real TT font may use a different characters than
% the Symbol charaster set. Perhaps our code
% will give a correct printing, because glyph names are not
% important for symbolic fonts in PDF.
%
<00010000> .findcmap {
prebuilt_encoding null ne {
TTFDEBUG { (Using cmap 1.0 with prebuilt_encoding.) = } if
prebuilt_encoding .invert_encoding .pdfmapchars
/Encoding prebuilt_encoding
} {
% This is a case, in which an obsolete software could stupidly specify Macintosh Roman
% for a random encoding. Particulatrly GPL Ghostscript 7.06 does so.
% Trying to recover with 'post'.
pop % The table itself doesn't contain useful data.
TTFDEBUG { (Using cmap 1.0 with post or .GS_extended_SymbolEncoding) = } if
.charkeys
} ifelse
} {
% This is illegal with PDF spec.
( **** Warning: Embedded symbolic TT fonts must contain a cmap for Platform=1 Encoding=0.\n)
pdfformaterror
% Try to map Unicode to SymbolEncoding
<00030001> .findcmap {
TTFDEBUG { (Using cmap 3.1) = } if
AdobeGlyphList .pdfmapchars
/Encoding /SymbolEncoding .findencoding
} {
% Apply the default algorithm. Hopely it has 'post'.
.charkeys
% check if the CharStrings dict contains glyphs needed by the
% prebuilt_encoding otherwise try the 3,0 cmap.
prebuilt_encoding null ne {
false prebuilt_encoding { % false means no missing glyphs
4 index exch known not { pop true exit } if
} forall
{
( **** Warning: Encoding derived from 'post' is incomplete.\n)
pdfformaterror
% Try another cmap format 3,0 -- Adobe doesn't mention it, but does
% use it apparently (empirically determined).
<00030000> .findcmap {
TTFDEBUG { (Adding cmap 3.0) = } if
5 1 roll pop pop pop pop
prebuilt_encoding null ne {
prebuilt_encoding .invert_encoding .pdfmapchars
/Encoding prebuilt_encoding
} {
AdobeGlyphList .pdfmapchars
/Encoding /SymbolEncoding .findencoding
} ifelse
} if
}if
} if
} ifelse
} ifelse
} ifelse
} {
<00030001> .findcmap {
TTFDEBUG { (Using cmap 3.1 for non-symbolic.) = } if
AdobeGlyphList .pdfmapchars
/Encoding /WinAnsiEncoding .findencoding
% WinAnsiEncoding is just a stub here.
% It will be replaced with one from font resource,
% because PDF spec requires it.
} {
<00010000> .findcmap {
TTFDEBUG { (Using cmap 1.0 for non-symbolic.) = } if
.romanmacdict .pdfmapchars
/Encoding /MacRomanEncoding .findencoding
} {
% Apply the default algorithm for using the 'post'.
.charkeys
} ifelse
} ifelse
} ifelse
} bind def
% <file> <is_symbolic> <Encoding|null> .loadpdfttfont <type42font>
/.loadpdfttfont {
/prebuilt_encoding exch def % for .pdfcharkeys
/is_symbolic exch def
//false 0 .loadttfonttables
.makesfnts
.getpost
.pickcmap
mark
.pdfcharkeys
.ttkeys
.definettfont
} bind def