1 # -*- encoding: utf-8 -*-
4 # Copyright (C) 2005-2011 Jörg Lehmann <joergl@users.sourceforge.net>
5 # Copyright (C) 2006-2011 Michael Schindler <m-schindler@users.sourceforge.net>
6 # Copyright (C) 2005-2011 André Wobst <wobsta@users.sourceforge.net>
8 # This file is part of PyX (http://pyx.sourceforge.net/).
10 # PyX is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 2 of the License, or
13 # (at your option) any later version.
15 # PyX is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License
21 # along with PyX; if not, write to the Free Software
22 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25 from pyx
import bbox
, baseclasses
, deco
, path
, pswriter
, pdfwriter
, svgwriter
, trafo
, unit
26 from . import t1file
, afmfile
28 logger
= logging
.getLogger("pyx")
30 ##############################################################################
32 ##############################################################################
34 class PST1file(pswriter
.PSresource
):
36 """ PostScript font definition included in the prolog """
38 def __init__(self
, t1file
, glyphnames
, charcodes
):
39 """ include type 1 font t1file stripped to the given glyphnames"""
43 self
.glyphnames
= set(glyphnames
)
44 self
.charcodes
= set(charcodes
)
46 def merge(self
, other
):
47 self
.glyphnames
.update(other
.glyphnames
)
48 self
.charcodes
.update(other
.charcodes
)
50 def output(self
, file, writer
, registry
):
51 file.write("%%%%BeginFont: %s\n" % self
.t1file
.name
)
52 if writer
.strip_fonts
:
54 file.write("%%Included glyphs: %s\n" % " ".join(self
.glyphnames
))
56 file.write("%%Included charcodes: %s\n" % " ".join([str(charcode
) for charcode
in self
.charcodes
]))
57 self
.t1file
.getstrippedfont(self
.glyphnames
, self
.charcodes
).outputPS(file, writer
)
59 self
.t1file
.outputPS(file, writer
)
60 file.write("\n%%EndFont\n")
63 _ReEncodeFont
= pswriter
.PSdefinition("ReEncodeFont", b
"""{
68 /basefontname exch def
69 /basefontdict basefontname findfont def
70 /newfontdict basefontdict maxlength dict def
72 exch dup dup /FID ne exch /Encoding ne and
73 { exch newfontdict 3 1 roll put }
77 newfontdict /FontName newfontname put
78 newfontdict /Encoding newencoding put
79 newfontname newfontdict definefont pop
84 class PSreencodefont(pswriter
.PSresource
):
86 """ reencoded PostScript font"""
88 def __init__(self
, basefontname
, newfontname
, encoding
):
89 """ reencode the font """
91 self
.type = "reencodefont"
92 self
.basefontname
= basefontname
93 self
.id = self
.newfontname
= newfontname
94 self
.encoding
= encoding
96 def output(self
, file, writer
, registry
):
97 file.write("%%%%BeginResource: %s\n" % self
.newfontname
)
98 file.write("/%s /%s\n[" % (self
.basefontname
, self
.newfontname
))
99 vector
= [None] * len(self
.encoding
)
100 for glyphname
, charcode
in list(self
.encoding
.items()):
101 vector
[charcode
] = glyphname
102 for i
, glyphname
in enumerate(vector
):
108 file.write("/%s" % glyphname
)
110 file.write("ReEncodeFont\n")
111 file.write("%%EndResource\n")
114 _ChangeFontMatrix
= pswriter
.PSdefinition("ChangeFontMatrix", b
"""{
117 /newfontmatrix exch def
118 /newfontname exch def
119 /basefontname exch def
120 /basefontdict basefontname findfont def
121 /newfontdict basefontdict maxlength dict def
123 exch dup dup /FID ne exch /FontMatrix ne and
124 { exch newfontdict 3 1 roll put }
128 newfontdict /FontName newfontname put
129 newfontdict /FontMatrix newfontmatrix readonly put
130 newfontname newfontdict definefont pop
135 class PSchangefontmatrix(pswriter
.PSresource
):
137 """ change font matrix of a PostScript font"""
139 def __init__(self
, basefontname
, newfontname
, newfontmatrix
):
140 """ change the font matrix """
142 self
.type = "changefontmatrix"
143 self
.basefontname
= basefontname
144 self
.id = self
.newfontname
= newfontname
145 self
.newfontmatrix
= newfontmatrix
147 def output(self
, file, writer
, registry
):
148 file.write("%%%%BeginResource: %s\n" % self
.newfontname
)
149 file.write("/%s /%s\n" % (self
.basefontname
, self
.newfontname
))
150 file.write(str(self
.newfontmatrix
))
151 file.write("\nChangeFontMatrix\n")
152 file.write("%%EndResource\n")
155 ##############################################################################
157 ##############################################################################
159 class PDFfont(pdfwriter
.PDFobject
):
161 def __init__(self
, fontname
, basefontname
, charcodes
, fontdescriptor
, encoding
, metric
):
162 pdfwriter
.PDFobject
.__init
__(self
, "font", fontname
)
164 self
.fontname
= fontname
165 self
.basefontname
= basefontname
166 self
.charcodes
= set(charcodes
)
167 self
.fontdescriptor
= fontdescriptor
168 self
.encoding
= encoding
171 def merge(self
, other
):
172 self
.charcodes
.update(other
.charcodes
)
174 def write(self
, file, writer
, registry
):
178 file.write("/Name /%s\n" % self
.fontname
)
179 file.write("/BaseFont /%s\n" % self
.basefontname
)
180 firstchar
= min(self
.charcodes
)
181 lastchar
= max(self
.charcodes
)
182 file.write("/FirstChar %d\n" % firstchar
)
183 file.write("/LastChar %d\n" % lastchar
)
184 file.write("/Widths\n"
187 encoding
= self
.encoding
.getvector()
189 if self
.fontdescriptor
.fontfile
.t1file
.encoding
is None:
190 self
.fontdescriptor
.fontfile
.t1file
._encoding
()
191 encoding
= self
.fontdescriptor
.fontfile
.t1file
.encoding
192 for i
in range(firstchar
, lastchar
+1):
198 if i
in self
.charcodes
:
199 if self
.metric
is not None:
200 file.write("%i" % self
.metric
.width_ds(encoding
[i
]))
202 file.write("%i" % self
.fontdescriptor
.fontfile
.t1file
.getglyphinfo(encoding
[i
])[0])
206 file.write("/FontDescriptor %d 0 R\n" % registry
.getrefno(self
.fontdescriptor
))
208 file.write("/Encoding %d 0 R\n" % registry
.getrefno(self
.encoding
))
212 class PDFstdfont(pdfwriter
.PDFobject
):
214 def __init__(self
, basename
):
215 pdfwriter
.PDFobject
.__init
__(self
, "font", "stdfont-%s" % basename
)
216 self
.name
= basename
# name is ignored by acroread
217 self
.basename
= basename
219 def write(self
, file, writer
, registry
):
220 file.write("<</BaseFont /%s\n" % self
.basename
)
221 file.write("/Name /%s\n" % self
.name
)
222 file.write("/Type /Font\n")
223 file.write("/Subtype /Type1\n")
226 # the 14 standard fonts that are always available in PDF
227 PDFTimesRoman
= PDFstdfont("Times-Roman")
228 PDFTimesBold
= PDFstdfont("Times-Bold")
229 PDFTimesItalic
= PDFstdfont("Times-Italic")
230 PDFTimesBoldItalic
= PDFstdfont("Times-BoldItalic")
231 PDFHelvetica
= PDFstdfont("Helvetica")
232 PDFHelveticaBold
= PDFstdfont("Helvetica-Bold")
233 PDFHelveticaOblique
= PDFstdfont("Helvetica-Oblique")
234 PDFHelveticaBoldOblique
= PDFstdfont("Helvetica-BoldOblique")
235 PDFCourier
= PDFstdfont("Courier")
236 PDFCourierBold
= PDFstdfont("Courier-Bold")
237 PDFCourierOblique
= PDFstdfont("Courier-Oblique")
238 PDFCourierBoldOblique
= PDFstdfont("Courier-BoldOblique")
239 PDFSymbol
= PDFstdfont("Symbol")
240 PDFZapfDingbats
= PDFstdfont("ZapfDingbats")
243 class PDFfontdescriptor(pdfwriter
.PDFobject
):
245 def __init__(self
, fontname
, fontfile
, metric
):
246 pdfwriter
.PDFobject
.__init
__(self
, "fontdescriptor", fontname
)
247 self
.fontname
= fontname
248 self
.fontfile
= fontfile
251 def write(self
, file, writer
, registry
):
253 "/Type /FontDescriptor\n"
254 "/FontName /%s\n" % self
.fontname
)
255 if self
.metric
is not None:
256 self
.metric
.writePDFfontinfo(file)
258 self
.fontfile
.t1file
.writePDFfontinfo(file)
259 if self
.fontfile
is not None:
260 file.write("/FontFile %d 0 R\n" % registry
.getrefno(self
.fontfile
))
264 class PDFfontfile(pdfwriter
.PDFobject
):
266 def __init__(self
, t1file
, glyphnames
, charcodes
):
267 pdfwriter
.PDFobject
.__init
__(self
, "fontfile", t1file
.name
)
269 self
.glyphnames
= set(glyphnames
)
270 self
.charcodes
= set(charcodes
)
272 def merge(self
, other
):
273 self
.glyphnames
.update(other
.glyphnames
)
274 self
.charcodes
.update(other
.charcodes
)
276 def write(self
, file, writer
, registry
):
277 if writer
.strip_fonts
:
278 self
.t1file
.getstrippedfont(self
.glyphnames
, self
.charcodes
).outputPDF(file, writer
)
280 self
.t1file
.outputPDF(file, writer
)
283 class PDFencoding(pdfwriter
.PDFobject
):
285 def __init__(self
, encoding
, name
):
286 pdfwriter
.PDFobject
.__init
__(self
, "encoding", name
)
287 self
.encoding
= encoding
290 # As self.encoding might be appended after the constructur has set it,
291 # we need to defer the calculation until the whole content was constructed.
292 vector
= [None] * len(self
.encoding
)
293 for glyphname
, charcode
in list(self
.encoding
.items()):
294 vector
[charcode
] = glyphname
297 def write(self
, file, writer
, registry
):
302 for i
, glyphname
in enumerate(self
.getvector()):
308 file.write("/%s" % glyphname
)
313 ##############################################################################
315 ##############################################################################
318 _glyphnames
= {glyphname
: str for str, glyphname
in afmfile
.unicodestring
.items()}
319 _charcodes
= {i
: chr(i
) for i
in range(32, 127)} # 0x20 (space) to 0x7e (tilde)
324 def __init__(self
, glyphnames
, charcodes
):
325 # glyphnames and charcodes are not stored as sets, but are dicts
326 # mapping the values to unicode characters. If the glyphnames and
327 # charcodes are contained in _glyphnames and _charcodes, use those
328 # values, otherwise use the private use areas A and B.
329 self
.private_glyphname
= 0xf0000
330 self
.private_charcode
= 0x100000
333 self
.merge_glyphnames(glyphnames
)
334 self
.merge_charcodes(charcodes
)
336 def merge_glyphnames(self
, glyphnames
):
337 for glyphname
in glyphnames
:
338 if glyphname
not in self
.glyphnames
:
339 if glyphname
in _glyphnames
:
340 self
.glyphnames
[glyphname
] = _glyphnames
[glyphname
]
342 self
.glyphnames
[glyphname
] = chr(self
.private_glyphname
)
343 self
.private_glyphname
+= 1
345 def merge_charcodes(self
, charcodes
):
346 for charcode
in charcodes
:
347 if charcode
not in self
.charcodes
:
348 if charcode
in _charcodes
:
349 self
.charcodes
[charcode
] = _charcodes
[charcode
]
351 self
.charcodes
[charcode
] = chr(self
.private_charcode
)
352 self
.private_charcode
+= 1
355 class SVGT1file(svgwriter
.SVGresource
, SVGT1mapping
):
357 """ PostScript font definition included in the prolog """
359 def __init__(self
, t1file
, glyphnames
, charcodes
):
360 """ include type 1 font t1file stripped to the given glyphnames"""
362 svgwriter
.SVGresource
.__init
__(self
, "t1file", t1file
.name
)
363 SVGT1mapping
.__init
__(self
, glyphnames
, charcodes
)
365 def merge(self
, other
):
366 # Note that merging the glyphnames and charcodes does not alter
367 # any existing mapping to the private use areas for self (but for
368 # other). If you merge before use, the mapping by self.glyphnames
369 # and self.charcodes is already updated and also copied to "other".
370 self
.merge_glyphnames(other
.glyphnames
.keys())
371 self
.merge_charcodes(other
.charcodes
.keys())
372 other
.glyphnames
= self
.glyphnames
373 other
.charcodes
= self
.charcodes
375 def output(self
, xml
, writer
, registry
):
376 xml
.startSVGElement("font", {})
377 xml
.startSVGElement("font-face", {"font-family": self
.t1file
.name
})
378 xml
.endSVGElement("font-face")
379 for glyphname
in self
.glyphnames
:
380 glyphpath
= self
.t1file
.getglyphpath_pt(0, 0, glyphname
, 1000, convertcharcode
=False)
381 attrs
= {"unicode": self
.glyphnames
[glyphname
],
382 "horiz-adv-x": "%f" % glyphpath
.wx_pt
,
383 "d": glyphpath
.path
.returnSVGdata(inverse_y
=False)}
384 xml
.startSVGElement("glyph", attrs
)
385 xml
.endSVGElement("glyph")
386 for charcode
in self
.charcodes
:
387 glyphpath
= self
.t1file
.getglyphpath_pt(0, 0, charcode
, 1000, convertcharcode
=True)
388 attrs
= {"unicode": self
.charcodes
[charcode
],
389 "horiz-adv-x": "%f" % glyphpath
.wx_pt
,
390 "d": glyphpath
.path
.returnSVGdata(inverse_y
=False)}
391 xml
.startSVGElement("glyph", attrs
)
392 xml
.endSVGElement("glyph")
393 xml
.endSVGElement("font")
396 ##############################################################################
397 # basic PyX text output
398 ##############################################################################
402 def text(self
, x
, y
, charcodes
, size_pt
, **kwargs
):
403 return self
.text_pt(unit
.topt(x
), unit
.topt(y
), charcodes
, size_pt
, **kwargs
)
408 def __init__(self
, t1file
, metric
=None):
410 self
.name
= t1file
.name
413 def text_pt(self
, x
, y
, charcodes
, size_pt
, **kwargs
):
414 return T1text_pt(self
, x
, y
, charcodes
, size_pt
, **kwargs
)
417 class T1builtinfont(T1font
):
419 def __init__(self
, name
, metric
):
427 def __init__(self
, name
, size_pt
):
429 self
.size_pt
= size_pt
431 def __ne__(self
, other
):
432 return self
.name
!= other
.name
or self
.size_pt
!= other
.size_pt
434 def outputPS(self
, file, writer
):
435 file.write("/%s %f selectfont\n" % (self
.name
, self
.size_pt
))
437 def outputPDF(self
, file, writer
):
438 file.write("/%s %f Tf\n" % (self
.name
, self
.size_pt
))
441 class text_pt(baseclasses
.canvasitem
):
443 def requiretextregion(self
):
447 class T1text_pt(text_pt
):
449 def __init__(self
, font
, x_pt
, y_pt
, charcodes
, size_pt
, decoding
=afmfile
.unicodestring
, slant
=None, ignorebbox
=False, kerning
=False, ligatures
=False, spaced_pt
=0):
450 if decoding
is not None:
451 self
.glyphnames
= [decoding
[character
] for character
in charcodes
]
454 self
.charcodes
= charcodes
459 self
.size_pt
= size_pt
461 self
.ignorebbox
= ignorebbox
462 self
.kerning
= kerning
463 self
.ligatures
= ligatures
464 self
.spaced_pt
= spaced_pt
465 self
._textpath
= None
467 if self
.kerning
and not self
.decode
:
468 raise ValueError("decoding required for font metric access (kerning)")
469 if self
.ligatures
and not self
.decode
:
470 raise ValueError("decoding required for font metric access (ligatures)")
472 self
.glyphnames
= self
.font
.metric
.resolveligatures(self
.glyphnames
)
475 if self
.font
.metric
is None:
476 logger
.warning("We are about to extract the bounding box from the path of the text. This is slow and differs from the font metric information. You should provide an afm file whenever possible.")
477 return self
.textpath().bbox()
479 raise ValueError("decoding required for font metric access (bbox)")
481 kerning_correction
= sum(value
or 0 for i
, value
in enumerate(self
.font
.metric
.resolvekernings(self
.glyphnames
, self
.size_pt
)) if i
%2)
483 kerning_correction
= 0
484 return bbox
.bbox_pt(self
.x_pt
,
485 self
.y_pt
+self
.font
.metric
.depth_pt(self
.glyphnames
, self
.size_pt
),
486 self
.x_pt
+self
.font
.metric
.width_pt(self
.glyphnames
, self
.size_pt
) + (len(self
.glyphnames
)-1)*self
.spaced_pt
+ kerning_correction
,
487 self
.y_pt
+self
.font
.metric
.height_pt(self
.glyphnames
, self
.size_pt
))
489 def getencodingname(self
, encodings
):
490 """returns the name of the encoding (in encodings) mapping self.glyphnames to codepoints
491 If no such encoding can be found or extended, a new encoding is added to encodings
493 glyphnames
= set(self
.glyphnames
)
494 if len(glyphnames
) > 256:
495 raise ValueError("glyphs do not fit into one single encoding")
496 for encodingname
, encoding
in list(encodings
.items()):
498 for glyphname
in glyphnames
:
499 if glyphname
not in list(encoding
.keys()):
500 glyphsmissing
.append(glyphname
)
502 if len(glyphsmissing
) + len(encoding
) < 256:
503 # new glyphs fit in existing encoding which will thus be extended
504 for glyphname
in glyphsmissing
:
505 encoding
[glyphname
] = len(encoding
)
507 # create a new encoding for the glyphnames
508 encodingname
= "encoding%d" % len(encodings
)
509 encodings
[encodingname
] = dict([(glyphname
, i
) for i
, glyphname
in enumerate(glyphnames
)])
513 if self
._textpath
is None:
516 data
= self
.font
.metric
.resolvekernings(self
.glyphnames
, self
.size_pt
)
518 data
= self
.glyphnames
520 data
= self
.charcodes
521 self
._textpath
= path
.path()
524 for i
, value
in enumerate(data
):
525 if self
.kerning
and i
% 2:
526 if value
is not None:
530 x_pt
+= self
.spaced_pt
531 glyphpath
= self
.font
.t1file
.getglyphpath_pt(x_pt
, y_pt
, value
, self
.size_pt
, convertcharcode
=not self
.decode
)
532 self
._textpath
+= glyphpath
.path
533 x_pt
+= glyphpath
.wx_pt
534 y_pt
+= glyphpath
.wy_pt
535 return self
._textpath
537 def processPS(self
, file, writer
, context
, registry
, bbox
):
538 if not self
.ignorebbox
:
541 if writer
.text_as_path
and not self
.font
.t1file
:
542 logger
.warning("Cannot output text as path when font not given by a font file (like for builtin fonts).")
543 if writer
.text_as_path
and self
.font
.t1file
:
544 deco
.decoratedpath(self
.textpath(), fillstyles
=[]).processPS(file, writer
, context
, registry
, bbox
)
547 if self
.font
.t1file
is not None:
549 registry
.add(PST1file(self
.font
.t1file
, self
.glyphnames
, []))
551 registry
.add(PST1file(self
.font
.t1file
, [], self
.charcodes
))
553 fontname
= self
.font
.name
555 encodingname
= self
.getencodingname(writer
.encodings
.setdefault(self
.font
.name
, {}))
556 encoding
= writer
.encodings
[self
.font
.name
][encodingname
]
557 newfontname
= "%s-%s" % (fontname
, encodingname
)
558 registry
.add(_ReEncodeFont
)
559 registry
.add(PSreencodefont(fontname
, newfontname
, encoding
))
560 fontname
= newfontname
563 newfontmatrix
= trafo
.trafo_pt(matrix
=((1, self
.slant
), (0, 1)))
564 if self
.font
.t1file
is not None:
565 newfontmatrix
= newfontmatrix
* self
.font
.t1file
.fontmatrix
566 newfontname
= "%s-slant%f" % (fontname
, self
.slant
)
567 registry
.add(_ChangeFontMatrix
)
568 registry
.add(PSchangefontmatrix(fontname
, newfontname
, newfontmatrix
))
569 fontname
= newfontname
571 # select font if necessary
572 sf
= selectedfont(fontname
, self
.size_pt
)
573 if context
.selectedfont
is None or sf
!= context
.selectedfont
:
574 context
.selectedfont
= sf
575 sf
.outputPS(file, writer
)
577 file.write("%f %f moveto (" % (self
.x_pt
, self
.y_pt
))
580 data
= self
.font
.metric
.resolvekernings(self
.glyphnames
, self
.size_pt
)
582 data
= self
.glyphnames
584 data
= self
.charcodes
585 for i
, value
in enumerate(data
):
586 if self
.kerning
and i
% 2:
587 if value
is not None:
588 file.write(") show\n%f 0 rmoveto (" % (value
+self
.spaced_pt
))
590 file.write(") show\n%f 0 rmoveto (" % self
.spaced_pt
)
592 if i
and not self
.kerning
and self
.spaced_pt
:
593 file.write(") show\n%f 0 rmoveto (" % self
.spaced_pt
)
595 value
= encoding
[value
]
596 if 32 < value
< 127 and chr(value
) not in "()[]<>\\":
597 file.write("%s" % chr(value
))
599 file.write("\\%03o" % value
)
600 file.write(") show\n")
602 def processPDF(self
, file, writer
, context
, registry
, bbox
):
603 if not self
.ignorebbox
:
606 if writer
.text_as_path
and not self
.font
.t1file
:
607 logger
.warning("Cannot output text as path when font not given by a font file (like for builtin fonts).")
608 if writer
.text_as_path
and self
.font
.t1file
:
609 deco
.decoratedpath(self
.textpath(), fillstyles
=[]).processPDF(file, writer
, context
, registry
, bbox
)
612 encodingname
= self
.getencodingname(writer
.encodings
.setdefault(self
.font
.name
, {}))
613 encoding
= writer
.encodings
[self
.font
.name
][encodingname
]
614 charcodes
= [encoding
[glyphname
] for glyphname
in self
.glyphnames
]
616 charcodes
= self
.charcodes
619 fontname
= self
.font
.name
621 newfontname
= "%s-%s" % (fontname
, encodingname
)
622 _encoding
= PDFencoding(encoding
, newfontname
)
623 fontname
= newfontname
626 if self
.font
.t1file
is not None:
628 fontfile
= PDFfontfile(self
.font
.t1file
, self
.glyphnames
, [])
630 fontfile
= PDFfontfile(self
.font
.t1file
, [], self
.charcodes
)
633 fontdescriptor
= PDFfontdescriptor(self
.font
.name
, fontfile
, self
.font
.metric
)
634 font
= PDFfont(fontname
, self
.font
.name
, charcodes
, fontdescriptor
, _encoding
, self
.font
.metric
)
637 if fontfile
is not None:
638 registry
.add(fontfile
)
639 registry
.add(fontdescriptor
)
640 if _encoding
is not None:
641 registry
.add(_encoding
)
644 registry
.addresource("Font", fontname
, font
, procset
="Text")
646 if self
.slant
is None:
649 slantvalue
= self
.slant
651 # select font if necessary
652 sf
= selectedfont(fontname
, self
.size_pt
)
653 if context
.selectedfont
is None or sf
!= context
.selectedfont
:
654 context
.selectedfont
= sf
655 sf
.outputPDF(file, writer
)
657 # convert inter-character spacing to font units
658 spaced
= self
.spaced_pt
*1000/self
.size_pt
660 if self
.kerning
or spaced
:
661 file.write("1 0 %f 1 %f %f Tm [(" % (slantvalue
, self
.x_pt
, self
.y_pt
))
663 file.write("1 0 %f 1 %f %f Tm (" % (slantvalue
, self
.x_pt
, self
.y_pt
))
666 data
= self
.font
.metric
.resolvekernings(self
.glyphnames
)
668 data
= self
.glyphnames
670 data
= self
.charcodes
672 for i
, value
in enumerate(data
):
673 if self
.kerning
and i
% 2:
674 if value
is not None:
675 file.write(")%f(" % (-value
-spaced
))
677 file.write(")%f(" % (-spaced
))
679 if i
and not self
.kerning
and spaced
:
680 file.write(")%f(" % (-spaced
))
682 value
= encoding
[value
]
683 if 32 <= value
<= 127 and chr(value
) not in "()[]<>\\":
684 file.write("%s" % chr(value
))
686 file.write("\\%03o" % value
)
687 if self
.kerning
or spaced
:
688 file.write(")] TJ\n")
692 def processSVG(self
, xml
, writer
, context
, registry
, bbox
):
693 if not self
.ignorebbox
:
696 # this is too common to be warned about as text_as_path is the
697 # default for svg due to the missing font support by current browsers
699 # if writer.text_as_path and not self.font.t1file:
700 # logger.warning("Cannot output text as path when font not given by a font file (like for builtin fonts).")
702 if writer
.text_as_path
and self
.font
.t1file
:
703 deco
.decoratedpath(self
.textpath(), fillstyles
=[]).processSVG(xml
, writer
, context
, registry
, bbox
)
705 if self
.font
.t1file
is not None:
707 t1mapping
= SVGT1file(self
.font
.t1file
, self
.glyphnames
, [])
709 t1mapping
= SVGT1file(self
.font
.t1file
, [], self
.charcodes
)
710 registry
.add(t1mapping
)
713 t1mapping
= SVGT1mapping(self
.glyphnames
, [])
715 t1mapping
= SVGT1mapping([], self
.charcodes
)
717 fontname
= self
.font
.name
721 data
= self
.font
.metric
.resolvekernings(self
.glyphnames
, self
.size_pt
)
723 data
= self
.glyphnames
725 data
= self
.charcodes
726 attrs
= {"x": "%f" % self
.x_pt
,
727 "y": "%f" % -self
.y_pt
,
728 "font-size": "%f" % self
.size_pt
,
729 "font-family": fontname
,
730 "fill": context
.fillcolor
}
731 if context
.fillopacity
:
732 attrs
["opacity"] = "%f" % context
.fillopacity
734 trafo
.trafo_pt(matrix
=((1, self
.slant
), (0, 1))).outputSVGattrs(attrs
, writer
, context
, registry
)
735 xml
.startSVGElement("text", attrs
)
737 for i
, value
in enumerate(data
):
738 if self
.kerning
and i
% 2:
739 if value
is not None:
741 xml
.endSVGElement("tspan")
742 xml
.startSVGElement("tspan", {"dx": "%f" % (value
+ self
.spaced_pt
)})
746 xml
.endSVGElement("tspan")
747 xml
.startSVGElement("tspan", {"dx": "%f" % (self
.spaced_pt
)})
750 if i
and not self
.kerning
and self
.spaced_pt
:
752 xml
.endSVGElement("tspan")
753 xml
.startSVGElement("tspan", {"dx": "%f" % (self
.spaced_pt
)})
756 xml
.characters(t1mapping
.glyphnames
[value
])
758 xml
.characters(t1mapping
.charcodes
[value
])
760 xml
.endSVGElement("tspan")
761 xml
.endSVGElement("text")