1 # -*- coding: ISO-8859-1 -*-
4 # Copyright (C) 2005-2006 André Wobst <wobsta@users.sourceforge.net>
5 # Copyright (C) 2006 Jörg Lehmann <joergl@users.sourceforge.net>
7 # This file is part of PyX (http://pyx.sourceforge.net/).
9 # PyX is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2 of the License, or
12 # (at your option) any later version.
14 # PyX is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with PyX; if not, write to the Free Software
21 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 from __future__
import nested_scopes
25 import array
, binascii
, re
35 # fallback implementation for Python 2.2 and below
37 return zip(xrange(len(list)), list)
40 from pyx
.path
import path
, moveto_pt
, lineto_pt
, curveto_pt
, closepath
51 def __init__(self
, t1font
):
52 """context for T1cmd evaluation"""
64 ######################################################################
66 # Note, that the T1 commands are variable-free except for plain number,
67 # which are stored as integers. All other T1 commands exist as a single
75 def __init__(self
, code
, subcmd
=0):
79 T1subcmds
[code
] = self
84 """returns a string representation of the T1 command"""
85 raise NotImplementedError
87 def updatepath(self
, path
, trafo
, context
):
88 """update path instance applying trafo to the points"""
89 raise NotImplementedError
91 def gathercalls(self
, seacglyphs
, subrs
, othersubrs
, context
):
92 """gather dependancy information
94 subrs is the "called-subrs" dictionary. gathercalls will insert the
95 subr number as key having the value 1, i.e. subrs.keys() will become the
96 numbers of used subrs. Similar seacglyphs will contain all glyphs in
97 composite characters (subrs and othersubrs for those glyphs will also
98 already be included) and othersubrs the othersubrs called.
100 This method might will not properly update all information in the
101 context (especially consuming values from the stack) and will also skip
102 various tests for performance reasons. For most T1 commands it just
103 doesn't need to do anything.
108 # commands for starting and finishing
110 class _T1endchar(T1cmd
):
113 T1cmd
.__init
__(self
, 14)
118 def updatepath(self
, path
, trafo
, context
):
121 T1endchar
= _T1endchar()
124 class _T1hsbw(T1cmd
):
127 T1cmd
.__init
__(self
, 13)
132 def updatepath(self
, path
, trafo
, context
):
133 sbx
= context
.t1stack
.pop(0)
134 wx
= context
.t1stack
.pop(0)
135 path
.append(moveto_pt(*trafo
.apply_pt(sbx
, 0)))
144 class _T1seac(T1cmd
):
147 T1cmd
.__init
__(self
, 6, subcmd
=1)
152 def updatepath(self
, path
, atrafo
, context
):
153 sab
= context
.t1stack
.pop(0)
154 adx
= context
.t1stack
.pop(0)
155 ady
= context
.t1stack
.pop(0)
156 bchar
= context
.t1stack
.pop(0)
157 achar
= context
.t1stack
.pop(0)
158 aglyph
= encoding
.adobestandardencoding
.decode(achar
)
159 bglyph
= encoding
.adobestandardencoding
.decode(bchar
)
160 context
.t1font
.updateglyphpath(bglyph
, path
, atrafo
, context
)
161 atrafo
= atrafo
* trafo
.translate_pt(adx
-sab
, ady
)
162 context
.t1font
.updateglyphpath(aglyph
, path
, atrafo
, context
)
164 def gathercalls(self
, seacglyphs
, subrs
, othersubrs
, context
):
165 bchar
= context
.t1stack
.pop()
166 achar
= context
.t1stack
.pop()
167 aglyph
= encoding
.adobestandardencoding
.decode(achar
)
168 bglyph
= encoding
.adobestandardencoding
.decode(bchar
)
169 seacglyphs
[aglyph
] = 1
170 seacglyphs
[bglyph
] = 1
171 context
.t1font
.gatherglyphcalls(bglyph
, seacglyphs
, subrs
, othersubrs
, context
)
172 context
.t1font
.gatherglyphcalls(aglyph
, seacglyphs
, subrs
, othersubrs
, context
)
180 T1cmd
.__init
__(self
, 7, subcmd
=1)
185 def updatepath(self
, path
, trafo
, context
):
186 sbx
= context
.t1stack
.pop(0)
187 sby
= context
.t1stack
.pop(0)
188 wx
= context
.t1stack
.pop(0)
189 wy
= context
.t1stack
.pop(0)
190 path
.append(moveto_pt(*trafo
.apply_pt(sbx
, sby
)))
199 # path construction commands
201 class _T1closepath(T1cmd
):
204 T1cmd
.__init
__(self
, 9)
209 def updatepath(self
, path
, trafo
, context
):
210 path
.append(closepath())
211 # The closepath in T1 is different from PostScripts in that it does
212 # *not* modify the current position; hence we need to add an additional
214 path
.append(moveto_pt(*trafo
.apply_pt(context
.x
, context
.y
)))
216 T1closepath
= _T1closepath()
219 class _T1hlineto(T1cmd
):
222 T1cmd
.__init
__(self
, 6)
227 def updatepath(self
, path
, trafo
, context
):
228 dx
= context
.t1stack
.pop(0)
229 path
.append(lineto_pt(*trafo
.apply_pt(context
.x
+ dx
, context
.y
)))
232 T1hlineto
= _T1hlineto()
235 class _T1hmoveto(T1cmd
):
238 T1cmd
.__init
__(self
, 22)
243 def updatepath(self
, path
, trafo
, context
):
244 dx
= context
.t1stack
.pop(0)
245 path
.append(moveto_pt(*trafo
.apply_pt(context
.x
+ dx
, context
.y
)))
248 T1hmoveto
= _T1hmoveto()
251 class _T1hvcurveto(T1cmd
):
254 T1cmd
.__init
__(self
, 31)
259 def updatepath(self
, path
, trafo
, context
):
260 dx1
= context
.t1stack
.pop(0)
261 dx2
= context
.t1stack
.pop(0)
262 dy2
= context
.t1stack
.pop(0)
263 dy3
= context
.t1stack
.pop(0)
264 path
.append(curveto_pt(*(trafo
.apply_pt(context
.x
+ dx1
, context
.y
) +
265 trafo
.apply_pt(context
.x
+ dx1
+ dx2
, context
.y
+ dy2
) +
266 trafo
.apply_pt(context
.x
+ dx1
+ dx2
, context
.y
+ dy2
+ dy3
))))
270 T1hvcurveto
= _T1hvcurveto()
273 class _T1rlineto(T1cmd
):
276 T1cmd
.__init
__(self
, 5)
281 def updatepath(self
, path
, trafo
, context
):
282 dx
= context
.t1stack
.pop(0)
283 dy
= context
.t1stack
.pop(0)
284 path
.append(lineto_pt(*trafo
.apply_pt(context
.x
+ dx
, context
.y
+ dy
)))
288 T1rlineto
= _T1rlineto()
291 class _T1rmoveto(T1cmd
):
294 T1cmd
.__init
__(self
, 21)
299 def updatepath(self
, path
, trafo
, context
):
300 dx
= context
.t1stack
.pop(0)
301 dy
= context
.t1stack
.pop(0)
302 path
.append(moveto_pt(*trafo
.apply_pt(context
.x
+ dx
, context
.y
+ dy
)))
306 T1rmoveto
= _T1rmoveto()
309 class _T1rrcurveto(T1cmd
):
312 T1cmd
.__init
__(self
, 8)
317 def updatepath(self
, path
, trafo
, context
):
318 dx1
= context
.t1stack
.pop(0)
319 dy1
= context
.t1stack
.pop(0)
320 dx2
= context
.t1stack
.pop(0)
321 dy2
= context
.t1stack
.pop(0)
322 dx3
= context
.t1stack
.pop(0)
323 dy3
= context
.t1stack
.pop(0)
324 path
.append(curveto_pt(*(trafo
.apply_pt(context
.x
+ dx1
, context
.y
+ dy1
) +
325 trafo
.apply_pt(context
.x
+ dx1
+ dx2
, context
.y
+ dy1
+ dy2
) +
326 trafo
.apply_pt(context
.x
+ dx1
+ dx2
+ dx3
, context
.y
+ dy1
+ dy2
+ dy3
))))
327 context
.x
+= dx1
+dx2
+dx3
328 context
.y
+= dy1
+dy2
+dy3
330 T1rrcurveto
= _T1rrcurveto()
333 class _T1vlineto(T1cmd
):
336 T1cmd
.__init
__(self
, 7)
341 def updatepath(self
, path
, trafo
, context
):
342 dy
= context
.t1stack
.pop(0)
343 path
.append(lineto_pt(*trafo
.apply_pt(context
.x
, context
.y
+ dy
)))
346 T1vlineto
= _T1vlineto()
349 class _T1vmoveto(T1cmd
):
352 T1cmd
.__init
__(self
, 4)
357 def updatepath(self
, path
, trafo
, context
):
358 dy
= context
.t1stack
.pop(0)
359 path
.append(moveto_pt(*trafo
.apply_pt(context
.x
, context
.y
+ dy
)))
362 T1vmoveto
= _T1vmoveto()
365 class _T1vhcurveto(T1cmd
):
368 T1cmd
.__init
__(self
, 30)
373 def updatepath(self
, path
, trafo
, context
):
374 dy1
= context
.t1stack
.pop(0)
375 dx2
= context
.t1stack
.pop(0)
376 dy2
= context
.t1stack
.pop(0)
377 dx3
= context
.t1stack
.pop(0)
378 path
.append(curveto_pt(*(trafo
.apply_pt(context
.x
, context
.y
+ dy1
) +
379 trafo
.apply_pt(context
.x
+ dx2
, context
.y
+ dy1
+ dy2
) +
380 trafo
.apply_pt(context
.x
+ dx2
+ dx3
, context
.y
+ dy1
+ dy2
))))
384 T1vhcurveto
= _T1vhcurveto()
389 class _T1dotsection(T1cmd
):
392 T1cmd
.__init
__(self
, 0, subcmd
=1)
397 def updatepath(self
, path
, trafo
, context
):
400 T1dotsection
= _T1dotsection()
403 class _T1hstem(T1cmd
):
406 T1cmd
.__init
__(self
, 1)
411 def updatepath(self
, path
, trafo
, context
):
412 y
= context
.t1stack
.pop(0)
413 dy
= context
.t1stack
.pop(0)
418 class _T1hstem3(T1cmd
):
421 T1cmd
.__init
__(self
, 2, subcmd
=1)
426 def updatepath(self
, path
, trafo
, context
):
427 y0
= context
.t1stack
.pop(0)
428 dy0
= context
.t1stack
.pop(0)
429 y1
= context
.t1stack
.pop(0)
430 dy1
= context
.t1stack
.pop(0)
431 y2
= context
.t1stack
.pop(0)
432 dy2
= context
.t1stack
.pop(0)
434 T1hstem3
= _T1hstem3()
437 class _T1vstem(T1cmd
):
440 T1cmd
.__init
__(self
, 3)
445 def updatepath(self
, path
, trafo
, context
):
446 x
= context
.t1stack
.pop(0)
447 dx
= context
.t1stack
.pop(0)
452 class _T1vstem3(T1cmd
):
455 T1cmd
.__init
__(self
, 1, subcmd
=1)
460 def updatepath(self
, path
, trafo
, context
):
461 self
.x0
= context
.t1stack
.pop(0)
462 self
.dx0
= context
.t1stack
.pop(0)
463 self
.x1
= context
.t1stack
.pop(0)
464 self
.dx1
= context
.t1stack
.pop(0)
465 self
.x2
= context
.t1stack
.pop(0)
466 self
.dx2
= context
.t1stack
.pop(0)
468 T1vstem3
= _T1vstem3()
476 T1cmd
.__init
__(self
, 12, subcmd
=1)
481 def updatepath(self
, path
, trafo
, context
):
482 num2
= context
.t1stack
.pop()
483 num1
= context
.t1stack
.pop()
484 context
.t1stack
.append(divmod(num1
, num2
)[0])
486 def gathercalls(self
, seacglyphs
, subrs
, othersubrs
, context
):
487 num2
= context
.t1stack
.pop()
488 num1
= context
.t1stack
.pop()
489 context
.t1stack
.append(divmod(num1
, num2
)[0])
494 # subroutine commands
496 class _T1callothersubr(T1cmd
):
499 T1cmd
.__init
__(self
, 16, subcmd
=1)
502 return "callothersubr"
504 def updatepath(self
, path
, trafo
, context
):
505 othersubrnumber
= context
.t1stack
.pop()
506 n
= context
.t1stack
.pop()
508 context
.psstack
.append(context
.t1stack
.pop())
510 def gathercalls(self
, seacglyphs
, subrs
, othersubrs
, context
):
511 othersubrnumber
= context
.t1stack
.pop()
512 othersubrs
[othersubrnumber
] = 1
513 n
= context
.t1stack
.pop()
515 context
.psstack
.append(context
.t1stack
.pop())
517 T1callothersubr
= _T1callothersubr()
520 class _T1callsubr(T1cmd
):
523 T1cmd
.__init
__(self
, 10)
528 def updatepath(self
, path
, trafo
, context
):
529 subr
= context
.t1stack
.pop()
530 context
.t1font
.updatesubrpath(subr
, path
, trafo
, context
)
532 def gathercalls(self
, seacglyphs
, subrs
, othersubrs
, context
):
533 subr
= context
.t1stack
.pop()
535 context
.t1font
.gathersubrcalls(subr
, seacglyphs
, subrs
, othersubrs
, context
)
537 T1callsubr
= _T1callsubr()
543 T1cmd
.__init
__(self
, 17, subcmd
=1)
548 def updatepath(self
, path
, trafo
, context
):
549 context
.t1stack
.append(context
.psstack
.pop())
551 def gathercalls(self
, seacglyphs
, subrs
, othersubrs
, context
):
552 context
.t1stack
.append(context
.psstack
.pop())
557 class _T1return(T1cmd
):
560 T1cmd
.__init
__(self
, 11)
565 def updatepath(self
, path
, trafo
, context
):
568 T1return
= _T1return()
571 class _T1setcurrentpoint(T1cmd
):
574 T1cmd
.__init
__(self
, 33, subcmd
=1)
577 return "setcurrentpoint" % self
.x
, self
.y
579 def updatepath(self
, path
, trafo
, context
):
580 x
= context
.t1stack
.pop(0)
581 y
= context
.t1stack
.pop(0)
582 path
.append(moveto_pt(*trafo
.apply_pt(x
, y
)))
586 T1setcurrentpoint
= _T1setcurrentpoint()
589 ######################################################################
592 """cursor to read a string token by token"""
594 def __init__(self
, data
, startstring
, eattokensep
=1, tokenseps
=" \t\r\n", tokenstarts
="()<>[]{}/%"):
595 """creates a cursor for the string data
597 startstring is a string at which the cursor should start at. The first
598 ocurance of startstring is used. When startstring is not in data, an
599 exception is raised, otherwise the cursor is set to the position right
600 after the startstring. When eattokenseps is set, startstring must be
601 followed by a tokensep and this first tokensep is also consumed.
602 tokenseps is a string containing characters to be used as token
603 separators. tokenstarts is a string containing characters which
604 directly (even without intermediate token separator) start a new token.
607 self
.pos
= self
.data
.index(startstring
) + len(startstring
)
608 self
.tokenseps
= tokenseps
609 self
.tokenstarts
= tokenstarts
611 if self
.data
[self
.pos
] not in self
.tokenstarts
:
612 if self
.data
[self
.pos
] not in self
.tokenseps
:
613 raise ValueError("cursor initialization string is not followed by a token separator")
617 """get the next token
619 Leading token separators and comments are silently consumed. The first token
620 separator after the token is also silently consumed."""
621 while self
.data
[self
.pos
] in self
.tokenseps
:
623 # ignore comments including subsequent whitespace characters
624 while self
.data
[self
.pos
] == "%":
625 while self
.data
[self
.pos
] not in "\r\n":
627 while self
.data
[self
.pos
] in self
.tokenseps
:
630 while self
.data
[self
.pos
] not in self
.tokenseps
:
631 # any character in self.tokenstarts ends the token
632 if self
.pos
>startpos
and self
.data
[self
.pos
] in self
.tokenstarts
:
635 result
= self
.data
[startpos
:self
.pos
]
636 if self
.data
[self
.pos
] in self
.tokenseps
:
637 self
.pos
+= 1 # consume a single tokensep
641 """get the next token as an integer"""
642 return int(self
.gettoken())
644 def getbytes(self
, count
):
645 """get the next count bytes"""
648 return self
.data
[startpos
: self
.pos
]
656 def __init__(self
, data1
, data2eexec
, data3
):
657 """initializes a t1font instance
659 data1 and data3 are the two clear text data parts and data2 is
660 the binary data part"""
662 self
._data
2eexec
= data2eexec
665 # marker and value for decoded data
667 # note that data2eexec is set to none by setsubrcmds and setglyphcmds
668 # this *also* denotes, that data2 is out-of-date; hence they are both
669 # marked by an _ and getdata2 and getdata2eexec will properly resolve
670 # the current state of decoding ...
672 # marker and value for standard encoding check
675 def _eexecdecode(self
, code
):
676 """eexec decoding of code"""
677 return decoder(code
, self
.eexecr
, 4)
679 def _charstringdecode(self
, code
):
680 """charstring decoding of code"""
681 return decoder(code
, self
.charstringr
, self
.lenIV
)
683 def _eexecencode(self
, data
):
684 """eexec encoding of data"""
685 return encoder(data
, self
.eexecr
, "PyX!")
687 def _charstringencode(self
, data
):
688 """eexec encoding of data"""
689 return encoder(data
, self
.charstringr
, "PyX!"[:self
.lenIV
])
691 lenIVpattern
= re
.compile("/lenIV\s+(\d+)\s+def\s+")
692 flexhintsubrs
= [[3, 0, T1callothersubr
, T1pop
, T1pop
, T1setcurrentpoint
, T1return
],
693 [0, 1, T1callothersubr
, T1return
],
694 [0, 2, T1callothersubr
, T1return
],
698 """helper method to lookup the encoding in the font"""
699 c
= cursor(self
.data1
, "/Encoding")
700 token1
= c
.gettoken()
701 token2
= c
.gettoken()
702 if token1
== "StandardEncoding" and token2
== "def":
703 self
.encoding
= encoding
.adobestandardencoding
705 encvector
= [None]*256
707 self
.encodingstart
= c
.pos
708 if c
.gettoken() == "dup":
714 encvector
[i
] = glyph
[1:]
715 token
= c
.gettoken(); assert token
== "put"
716 self
.encodingend
= c
.pos
718 if token
== "readonly" or token
== "def":
720 assert token
== "dup"
721 self
.encoding
= encoding
.encoding(encvector
)
723 def _data2decode(self
):
724 """decodes data2eexec to the data2 string and the subr and glyphs dictionary
726 It doesn't make sense to call this method twice -- check the content of
727 data2 before calling. The method also keeps the subrs and charstrings
728 start and end positions for later use."""
729 self
._data
2 = self
._eexecdecode
(self
._data
2eexec
)
731 m
= self
.lenIVpattern
.search(self
._data
2)
733 self
.lenIV
= int(m
.group(1))
736 self
.emptysubr
= self
._charstringencode
(chr(11))
739 c
= cursor(self
._data
2, "/Subrs")
740 self
.subrsstart
= c
.pos
741 arraycount
= c
.getint()
742 token
= c
.gettoken(); assert token
== "array"
744 for i
in range(arraycount
):
745 token
= c
.gettoken(); assert token
== "dup"
746 token
= c
.getint(); assert token
== i
749 self
.subrrdtoken
= c
.gettoken()
751 token
= c
.gettoken(); assert token
== self
.subrrdtoken
752 self
.subrs
.append(c
.getbytes(size
))
754 if token
== "noaccess":
755 token
= "%s %s" % (token
, c
.gettoken())
757 self
.subrnptoken
= token
759 assert token
== self
.subrnptoken
760 self
.subrsend
= c
.pos
762 # hasflexhintsubrs is a boolean indicating that the font uses flex or
763 # hint replacement subrs as specified by Adobe (tm). When it does, the
764 # first 4 subrs should all be copied except when none of them are used
765 # in the stripped version of the font since we than get a font not
766 # using flex or hint replacement subrs at all.
767 self
.hasflexhintsubrs
= (arraycount
>= len(self
.flexhintsubrs
) and
769 for i
in range(len(self
.flexhintsubrs
))] == self
.flexhintsubrs
)
773 self
.glyphlist
= [] # we want to keep the order of the glyph names
774 c
= cursor(self
._data
2, "/CharStrings")
775 self
.charstringsstart
= c
.pos
777 token
= c
.gettoken(); assert token
== "dict"
778 token
= c
.gettoken(); assert token
== "dup"
779 token
= c
.gettoken(); assert token
== "begin"
782 chartoken
= c
.gettoken()
783 if chartoken
== "end":
785 assert chartoken
[0] == "/"
788 self
.glyphrdtoken
= c
.gettoken()
790 token
= c
.gettoken(); assert token
== self
.glyphrdtoken
791 self
.glyphlist
.append(chartoken
[1:])
792 self
.glyphs
[chartoken
[1:]] = c
.getbytes(size
)
794 self
.glyphndtoken
= c
.gettoken()
796 token
= c
.gettoken(); assert token
== self
.glyphndtoken
798 self
.charstringsend
= c
.pos
799 assert not self
.subrs
or self
.subrrdtoken
== self
.glyphrdtoken
801 def _cmds(self
, code
):
802 """return a list of T1cmd's for encoded charstring data in code"""
803 code
= array
.array("B", self
._charstringdecode
(code
))
807 if x
== 12: # this starts an escaped cmd
808 cmds
.append(T1subcmds
[code
.pop(0)])
809 elif 0 <= x
< 32: # those are cmd's
810 cmds
.append(T1cmds
[x
])
811 elif 32 <= x
<= 246: # short ints
813 elif 247 <= x
<= 250: # mid size ints
814 cmds
.append(((x
- 247)*256) + code
.pop(0) + 108)
815 elif 251 <= x
<= 254: # mid size ints
816 cmds
.append(-((x
- 251)*256) - code
.pop(0) - 108)
817 else: # x = 255, i.e. full size ints
818 y
= ((code
.pop(0)*256l+code
.pop(0))*256+code
.pop(0))*256+code
.pop(0)
820 cmds
.append(y
- (1l << 32))
825 def _code(self
, cmds
):
826 """return an encoded charstring data for list of T1cmd's in cmds"""
827 code
= array
.array("B")
832 code
.append(cmd
.code
)
833 except AttributeError:
834 if -107 <= cmd
<= 107:
836 elif 108 <= cmd
<= 1131:
837 a
, b
= divmod(cmd
-108, 256)
840 elif -1131 <= cmd
<= -108:
841 a
, b
= divmod(-cmd
-108, 256)
847 cmd
, x4
= divmod(cmd
, 256)
848 cmd
, x3
= divmod(cmd
, 256)
849 x1
, x2
= divmod(cmd
, 256)
855 return self
._charstringencode
(code
.tostring())
857 def getsubrcmds(self
, subr
):
858 """return a list of T1cmd's for subr subr"""
861 return self
._cmds
(self
.subrs
[subr
])
863 def getglyphcmds(self
, glyph
):
864 """return a list of T1cmd's for glyph glyph"""
867 return self
._cmds
(self
.glyphs
[glyph
])
869 def setsubrcmds(self
, subr
, cmds
):
870 """replaces the T1cmd's by the list cmds for subr subr"""
873 self
._data
2eexec
= None
874 self
.subrs
[subr
] = self
._code
(cmds
)
876 def setglyphcmds(self
, glyph
, cmds
):
877 """replaces the T1cmd's by the list cmds for glyph glyph"""
880 self
._data
2eexec
= None
881 self
.glyphs
[glyph
] = self
._code
(cmds
)
883 def updatepath(self
, cmds
, path
, trafo
, context
):
885 if isinstance(cmd
, T1cmd
):
886 cmd
.updatepath(path
, trafo
, context
)
888 context
.t1stack
.append(cmd
)
890 def updatesubrpath(self
, subr
, path
, trafo
, context
):
891 self
.updatepath(self
.getsubrcmds(subr
), path
, trafo
, context
)
893 def updateglyphpath(self
, glyph
, path
, trafo
, context
):
894 self
.updatepath(self
.getglyphcmds(glyph
), path
, trafo
, context
)
896 def gathercalls(self
, cmds
, seacglyphs
, subrs
, othersubrs
, context
):
898 if isinstance(cmd
, T1cmd
):
899 cmd
.gathercalls(seacglyphs
, subrs
, othersubrs
, context
)
901 context
.t1stack
.append(cmd
)
903 def gathersubrcalls(self
, subr
, seacglyphs
, subrs
, othersubrs
, context
):
904 self
.gathercalls(self
.getsubrcmds(subr
), seacglyphs
, subrs
, othersubrs
, context
)
906 def gatherglyphcalls(self
, glyph
, seacglyphs
, subrs
, othersubrs
, context
):
907 self
.gathercalls(self
.getglyphcmds(glyph
), seacglyphs
, subrs
, othersubrs
, context
)
909 fontmatrixpattern
= re
.compile("/FontMatrix\s*\[\s*(-?[0-9.]+)\s+(-?[0-9.]+)\s+(-?[0-9.]+)\s+(-?[0-9.]+)\s+(-?[0-9.]+)\s+(-?[0-9.]+)\s*\]\s*(readonly\s+)?def")
911 def getglyphpathwxwy_pt(self
, glyph
, size
):
912 m
= self
.fontmatrixpattern
.search(self
.data1
)
913 m11
, m12
, m21
, m22
, v1
, v2
= map(float, m
.groups()[:6])
914 t
= trafo
.trafo_pt(matrix
=((m11
, m12
), (m21
, m22
)), vector
=(v1
, v2
)).scaled(size
)
915 context
= T1context(self
)
917 self
.updateglyphpath(glyph
, p
, t
, context
)
918 wx
, wy
= t
.apply_pt(context
.wx
, context
.wy
)
921 def getglyphpath(self
, glyph
, size
):
922 """return a PyX path for glyph named glyph"""
923 return self
.getglyphpathwxwy_pt(glyph
, size
)[0]
925 def getglyphwxwy_pt(self
, glyph
, size
):
926 return self
.getglyphpathwxwy_pt(glyph
, size
)[1:]
928 def getdata2(self
, subrs
=None, glyphs
=None):
929 """makes a data2 string
931 subrs is a dict containing those subrs numbers as keys,
932 which are to be contained in the subrsstring to be created.
933 If subrs is None, all subrs in self.subrs will be used.
934 The subrs dict might be modified *in place*.
936 glyphs is a dict containing those glyph names as keys,
937 which are to be contained in the charstringsstring to be created.
938 If glyphs is None, all glyphs in self.glyphs will be used."""
939 def addsubrs(subrs
, result
):
940 if subrs
is not None:
941 # some adjustments to the subrs dict
943 subrsindices
= subrs
.keys()
944 subrsmin
= min(subrsindices
)
945 subrsmax
= max(subrsindices
)
946 if self
.hasflexhintsubrs
and subrsmin
< len(self
.flexhintsubrs
):
947 # According to the spec we need to keep all the flex and hint subrs
948 # as long as any of it is used.
949 for subr
in range(len(self
.flexhintsubrs
)):
954 # build a new subrs dict containing all subrs
955 subrs
= dict([(subr
, 1) for subr
in range(len(self
.subrs
))])
956 subrsmax
= len(self
.subrs
) - 1
958 # build the string from all selected subrs
959 result
.append("%d array\n" % (subrsmax
+ 1))
960 for subr
in range(subrsmax
+1):
961 if subrs
.has_key(subr
):
962 code
= self
.subrs
[subr
]
964 code
= self
.emptysubr
965 result
.append("dup %d %d %s %s %s\n" % (subr
, len(code
), self
.subrrdtoken
, code
, self
.subrnptoken
))
967 def addcharstrings(glyphs
, result
):
968 result
.append("%d dict dup begin\n" % (glyphs
is None and len(self
.glyphlist
) or len(glyphs
)))
969 for glyph
in self
.glyphlist
:
970 if glyphs
is None or glyphs
.has_key(glyph
):
971 result
.append("/%s %d %s %s %s\n" % (glyph
, len(self
.glyphs
[glyph
]), self
.glyphrdtoken
, self
.glyphs
[glyph
], self
.glyphndtoken
))
972 result
.append("end\n")
974 if self
.subrsstart
< self
.charstringsstart
:
975 result
= [self
._data
2[:self
.subrsstart
]]
976 addsubrs(subrs
, result
)
977 result
.append(self
._data
2[self
.subrsend
:self
.charstringsstart
])
978 addcharstrings(glyphs
, result
)
979 result
.append(self
._data
2[self
.charstringsend
:])
981 result
= [self
._data
2[:self
.charstringsstart
]]
982 addcharstrings(glyphs
, result
)
983 result
.append(self
._data
2[self
.charstringsend
:self
.subrsstart
])
984 addsubrs(subrs
, result
)
985 result
.append(self
._data
2[self
.subrsend
:])
986 return "".join(result
)
988 def getdata2eexec(self
):
990 return self
._data
2eexec
991 # note that self._data2 is out-of-date here too, hence we need to call getdata2
992 return self
._eexecencode
(self
.getdata2())
994 newlinepattern
= re
.compile("\s*[\r\n]\s*")
995 uniqueidpattern
= re
.compile("/UniqueID\s+\d+\s+def\s+")
997 def getstrippedfont(self
, glyphs
):
998 """create a T1file instance containing only certain glyphs
1000 glyphs is a dict having the glyph names to be contained as keys.
1001 The glyphs dict might be modified *in place*.
1003 # TODO: we could also strip othersubrs to those actually used
1005 # collect information about used glyphs and subrs
1009 for glyph
in glyphs
.keys():
1010 self
.gatherglyphcalls(glyph
, seacglyphs
, subrs
, othersubrs
, T1context(self
))
1011 # while we have gathered all subrs for the seacglyphs alreadys, we
1012 # might have missed the glyphs themself (when they are not used stand-alone)
1013 glyphs
.update(seacglyphs
)
1014 glyphs
[".notdef"] = 1
1017 if not self
.encoding
:
1019 if self
.encoding
is encoding
.adobestandardencoding
:
1022 encodingstrings
= []
1023 for char
, glyph
in enumerate(self
.encoding
.encvector
):
1024 if glyph
in glyphs
.keys():
1025 encodingstrings
.append("dup %i /%s put\n" % (char
, glyph
))
1026 data1
= self
.data1
[:self
.encodingstart
] + "".join(encodingstrings
) + self
.data1
[self
.encodingend
:]
1027 data1
= self
.newlinepattern
.subn("\n", data1
)[0]
1028 data1
= self
.uniqueidpattern
.subn("", data1
)[0]
1031 data2
= self
.uniqueidpattern
.subn("", self
.getdata2(subrs
, glyphs
))[0]
1034 data3
= self
.newlinepattern
.subn("\n", self
.data3
)[0]
1036 # create and return the new font instance
1037 return T1file(data1
.rstrip() + "\n", self
._eexecencode
(data2
), data3
.rstrip() + "\n")
1040 # As a simple heuristics we assume non-symbolic fonts if and only
1041 # if the Adobe standard encoding is used. All other font flags are
1042 # not specified here.
1043 if not self
.encoding
:
1045 if self
.encoding
is encoding
.adobestandardencoding
:
1049 def outputPFA(self
, file):
1050 """output the T1file in PFA format"""
1051 file.write(self
.data1
)
1052 data2eexechex
= binascii
.b2a_hex(self
.getdata2eexec())
1054 for i
in range((len(data2eexechex
)-1)/linelength
+ 1):
1055 file.write(data2eexechex
[i
*linelength
: i
*linelength
+linelength
])
1057 file.write(self
.data3
)
1059 def outputPFB(self
, file):
1060 """output the T1file in PFB format"""
1061 data2eexec
= self
.getdata2eexec()
1062 def pfblength(data
):
1064 l
, x1
= divmod(l
, 256)
1065 l
, x2
= divmod(l
, 256)
1066 x4
, x3
= divmod(l
, 256)
1067 return chr(x1
) + chr(x2
) + chr(x3
) + chr(x4
)
1068 file.write("\200\1")
1069 file.write(pfblength(self
.data1
))
1070 file.write(self
.data1
)
1071 file.write("\200\2")
1072 file.write(pfblength(data2eexec
))
1073 file.write(data2eexec
)
1074 file.write("\200\1")
1075 file.write(pfblength(self
.data3
))
1076 file.write(self
.data3
)
1077 file.write("\200\3")
1079 def outputPS(self
, file, writer
):
1080 """output the PostScript code for the T1file to the file file"""
1081 self
.outputPFA(file)
1083 def outputPDF(self
, file, writer
):
1084 data2eexec
= self
.getdata2eexec()
1086 # we might be allowed to skip the third part ...
1087 if (data3
.replace("\n", "")
1090 .replace(" ", "")) == "0"*512 + "cleartomark":
1093 data
= self
.data1
+ data2eexec
+ data3
1094 if writer
.compress
and haszlib
:
1095 data
= zlib
.compress(data
)
1101 "/Length3 %d\n" % (len(data
), len(self
.data1
), len(data2eexec
), len(data3
)))
1102 if writer
.compress
and haszlib
:
1103 file.write("/Filter /FlateDecode\n")
1111 class PFAfile(T1file
):
1113 """create a T1file instance from a pfa font file"""
1115 def __init__(self
, filename
):
1116 d
= open(filename
, "rb").read()
1117 # hey, that's quick'n'dirty
1118 m1
= d
.index("eexec") + 6
1119 m2
= d
.index("0"*40)
1121 data2
= binascii
.a2b_hex(d
[m1
: m2
].replace(" ", "").replace("\r", "").replace("\n", ""))
1123 T1file
.__init
__(self
, data1
, data2
, data3
)
1126 class PFBfile(T1file
):
1128 """create a T1file instance from a pfb font file"""
1130 def __init__(self
, filename
):
1133 raise ValueError("invalid string length")
1137 ord(s
[3])*256*256*256)
1138 f
= open(filename
, "rb")
1139 mark
= f
.read(2); assert mark
== "\200\1"
1140 data1
= f
.read(pfblength(f
.read(4)))
1141 mark
= f
.read(2); assert mark
== "\200\2"
1143 while mark
== "\200\2":
1144 data2
= data2
+ f
.read(pfblength(f
.read(4)))
1146 assert mark
== "\200\1"
1147 data3
= f
.read(pfblength(f
.read(4)))
1148 mark
= f
.read(2); assert mark
== "\200\3"
1149 assert not f
.read(1)
1150 T1file
.__init
__(self
, data1
, data2
, data3
)