From 66f8db3b334706e55d732bf6d54e44578f7dc78b Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=B6rg=20Lehmann?= Date: Sun, 21 Oct 2007 14:41:48 +0000 Subject: [PATCH] start of PDF support resurrection git-svn-id: https://pyx.svn.sourceforge.net/svnroot/pyx/trunk/pyx@2926 069f4177-920e-0410-937b-c2a4a81bcd90 --- pyx/bbox.py | 3 +- pyx/dvi/texfont.py | 7 +- pyx/font/encoding.py | 70 --------------- pyx/font/font.py | 240 +++++++++++++++++++++++++++++++++++++++++++++++++-- pyx/pdfwriter.py | 172 +++--------------------------------- pyx/pswriter.py | 12 +-- 6 files changed, 259 insertions(+), 245 deletions(-) delete mode 100644 pyx/font/encoding.py diff --git a/pyx/bbox.py b/pyx/bbox.py index 12a1d7be..d458c8bd 100644 --- a/pyx/bbox.py +++ b/pyx/bbox.py @@ -21,7 +21,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA import math -import unit +import path, unit # # classes representing bounding boxes @@ -246,7 +246,6 @@ class bbox_pt: def rect(self): """return rectangle corresponding to bbox""" - import path if self.llx_pt is None: raise ValueError("Cannot return path for empty bbox") return path.rect_pt(self.llx_pt, self.lly_pt, self.urx_pt-self.llx_pt, self.ury_pt-self.lly_pt) diff --git a/pyx/dvi/texfont.py b/pyx/dvi/texfont.py index 43dee6f8..c0324c0b 100644 --- a/pyx/dvi/texfont.py +++ b/pyx/dvi/texfont.py @@ -207,5 +207,10 @@ class TeXtext_pt(canvasitem.canvasitem): text.processPS(file, writer, context, registry, bbox) def processPDF(self, file, writer, context, registry, bbox): - pass + bbox += self.bbox() + + mapline = self.font.getMAPline(writer.getfontmap()) + font = mapline.getfont() + text = font.text_pt(self.x_pt, self.y_pt, self.charcodes, self.size_pt, decoding=mapline.getencoding(), slant=mapline.slant, ignorebbox=True) + text.processPDF(file, writer, context, registry, bbox) diff --git a/pyx/font/encoding.py b/pyx/font/encoding.py deleted file mode 100644 index 7d7060e4..00000000 --- a/pyx/font/encoding.py +++ /dev/null @@ -1,70 +0,0 @@ -# -*- coding: ISO-8859-1 -*- -# -# -# Copyright (C) 2005-2006 Jörg Lehmann -# Copyright (C) 2005-2006 André Wobst -# -# This file is part of PyX (http://pyx.sourceforge.net/). -# -# PyX is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# PyX is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with PyX; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - -class encoding: - - def outputPDF(self, file, writer): - file.write("<<\n" - "/Type /Encoding\n" - "/Differences\n" - "[ 0") - for i, glyphname in enumerate(self.encvector): - if i and not (i % 8): - file.write("\n") - else: - file.write(" ") - file.write(glyphname) - file.write(" ]\n" - ">>\n") - -# adobestandardencoding = encoding([None, None, None, None, None, None, None, None, -# None, None, None, None, None, None, None, None, -# None, None, None, None, None, None, None, None, -# None, None, None, None, None, None, None, None, -# "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quoteright", -# "parenleft", "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", -# "zero", "one", "two", "three", "four", "five", "six", "seven", -# "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", -# "at", "A", "B", "C", "D", "E", "F", "G", -# "H", "I", "J", "K", "L", "M", "N", "O", -# "P", "Q", "R", "S", "T", "U", "V", "W", -# "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum", "underscore", -# "quoteleft", "a", "b", "c", "d", "e", "f", "g", -# "h", "i", "j", "k", "l", "m", "n", "o", -# "p", "q", "r", "s", "t", "u", "v", "w", -# "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", None, -# None, None, None, None, None, None, None, None, -# None, None, None, None, None, None, None, None, -# None, None, None, None, None, None, None, None, -# None, None, None, None, None, None, None, None, -# None, "exclamdown", "cent", "sterling", "fraction", "yen", "florin", "section", -# "currency", "quotesingle", "quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi", "fl", -# None, "endash", "dagger", "daggerdbl", "periodcentered", None, "paragraph", "bullet", -# "quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", "ellipsis", "perthousand", None, "questiondown", -# None, "grave", "acute", "circumflex", "tilde", "macron", "breve", "dotaccent", -# "dieresis", None, "ring", "cedilla", None, "hungarumlaut", "ogonek", "caron", -# "emdash", None, None, None, None, None, None, None, -# None, None, None, None, None, None, None, None, -# None, "AE", None, "ordfeminine", None, None, None, None, -# "Lslash", "Oslash", "OE", "ordmasculine", None, None, None, None, -# None, "ae", None, None, None, "dotlessi", None, None, -# "lslash", "oslash", "oe", "germandbls", None, None, None, None]) diff --git a/pyx/font/font.py b/pyx/font/font.py index 4de61f9b..69b4e6f1 100644 --- a/pyx/font/font.py +++ b/pyx/font/font.py @@ -20,7 +20,7 @@ # along with PyX; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA -from pyx import bbox, canvasitem, pswriter, trafo, unit +from pyx import bbox, canvasitem, pswriter, pdfwriter, trafo, unit import t1file try: @@ -29,10 +29,10 @@ except NameError: # Python 2.3 from sets import Set as set -# -# PSresources -# +############################################################################## +# PS resources +############################################################################## class PST1file(pswriter.PSresource): @@ -156,6 +156,183 @@ class PSchangefontmatrix(pswriter.PSresource): file.write("%%EndResource\n") +############################################################################## +# PDF resources +############################################################################## + +class PDFfont(pdfwriter.PDFobject): + + def __init__(self, font, chars, writer, registry): + PDFobject.__init__(self, "font", font.name) + registry.addresource("Font", font.name, self, procset="Text") + + self.fontdescriptor = PDFfontdescriptor(font, chars, writer, registry) + registry.add(self.fontdescriptor) + + if font.encoding: + self.encoding = PDFencoding(font.encoding, writer, registry) + registry.add(self.encoding) + else: + self.encoding = None + + self.name = font.name + self.basefontname = font.basefontname + self.metric = font.metric + + def write(self, file, writer, registry): + file.write("<<\n" + "/Type /Font\n" + "/Subtype /Type1\n") + file.write("/Name /%s\n" % self.name) + file.write("/BaseFont /%s\n" % self.basefontname) + if self.fontdescriptor.fontfile is not None and self.fontdescriptor.fontfile.usedchars is not None: + usedchars = self.fontdescriptor.fontfile.usedchars + firstchar = min(usedchars.keys()) + lastchar = max(usedchars.keys()) + file.write("/FirstChar %d\n" % firstchar) + file.write("/LastChar %d\n" % lastchar) + file.write("/Widths\n" + "[") + for i in range(firstchar, lastchar+1): + if i and not (i % 8): + file.write("\n") + else: + file.write(" ") + if usedchars.has_key(i): + file.write("%f" % self.metric.getwidth_ds(i)) + else: + file.write("0") + file.write(" ]\n") + else: + file.write("/FirstChar 0\n" + "/LastChar 255\n" + "/Widths\n" + "[") + for i in range(256): + if i and not (i % 8): + file.write("\n") + else: + file.write(" ") + try: + width = self.metric.getwidth_ds(i) + except (IndexError, AttributeError): + width = 0 + file.write("%f" % width) + file.write(" ]\n") + file.write("/FontDescriptor %d 0 R\n" % registry.getrefno(self.fontdescriptor)) + if self.encoding: + file.write("/Encoding %d 0 R\n" % registry.getrefno(self.encoding)) + file.write(">>\n") + + +class PDFfontdescriptor(pdfwriter.PDFobject): + + def __init__(self, font, chars, writer, registry): + PDFobject.__init__(self, "fontdescriptor", font.basefontname) + + if font.filename is None: + self.fontfile = None + else: + self.fontfile = PDFfontfile(font.basefontname, font.filename, font.encoding, chars, writer, registry) + registry.add(self.fontfile) + + self.name = font.basefontname + self.fontinfo = font.metric.fontinfo() + + def write(self, file, writer, registry): + file.write("<<\n" + "/Type /FontDescriptor\n" + "/FontName /%s\n" % self.name) + if self.fontfile is None: + file.write("/Flags 32\n") + else: + file.write("/Flags %d\n" % self.fontfile.getflags()) + file.write("/FontBBox [%d %d %d %d]\n" % self.fontinfo.fontbbox) + file.write("/ItalicAngle %d\n" % self.fontinfo.italicangle) + file.write("/Ascent %d\n" % self.fontinfo.ascent) + file.write("/Descent %d\n" % self.fontinfo.descent) + file.write("/CapHeight %d\n" % self.fontinfo.capheight) + file.write("/StemV %d\n" % self.fontinfo.vstem) + if self.fontfile is not None: + file.write("/FontFile %d 0 R\n" % registry.getrefno(self.fontfile)) + file.write(">>\n") + + +class PDFfontfile(pdfwriter.PDFobject): + + def __init__(self, name, filename, encoding, chars, writer, registry): + PDFobject.__init__(self, "fontfile", filename) + self.name = name + self.filename = filename + if encoding is None: + self.encodingfilename = None + else: + self.encodingfilename = encoding.filename + self.usedchars = {} + for char in chars: + self.usedchars[char] = 1 + + self.strip = 1 + self.font = None + + def merge(self, other): + if self.encodingfilename == other.encodingfilename: + self.usedchars.update(other.usedchars) + else: + # TODO: need to resolve the encoding when several encodings are in the play + self.strip = 0 + + def mkfontfile(self): + import font.t1font + self.font = font.t1font.T1pfbfont(self.filename) + + def getflags(self): + if self.font is None: + self.mkfontfile() + return self.font.getflags() + + def write(self, file, writer, registry): + if self.font is None: + self.mkfontfile() + if self.strip: + # XXX: access to the encoding file + if self.encodingfilename: + encodingfile = type1font.encodingfile(self.encodingfilename, self.encodingfilename) + usedglyphs = dict([(encodingfile.decode(char)[1:], 1) for char in self.usedchars.keys()]) + else: + self.font._encoding() + usedglyphs = dict([(self.font.encoding.decode(char), 1) for char in self.usedchars.keys()]) + strippedfont = self.font.getstrippedfont(usedglyphs) + else: + strippedfont = self.font + strippedfont.outputPDF(file, writer) + + +class PDFencoding(pdfwriter.PDFobject): + + def __init__(self, encoding, writer, registry): + PDFobject.__init__(self, "encoding", encoding.name) + self.encoding = encoding + + def write(self, file, writer, registry): + file.write("<<\n" + "/Type /Encoding\n" + "/Differences\n" + "[ 0") + for i, glyphname in enumerate(self.encvector): + if i and not (i % 8): + file.write("\n") + else: + file.write(" ") + file.write(glyphname) + file.write(" ]\n" + ">>\n") + + +############################################################################## +# basic PyX text output +############################################################################## + class font: def text(self, x, y, charcodes, size_pt, **kwargs): @@ -187,12 +364,16 @@ class selectedfont: self.name = name self.size_pt = size_pt - def __eq__(self, other): - return self.name == other.name and self.size_pt == other.size_pt + def __ne__(self, other): + print self.name != other.name or self.size_pt != other.size_pt + return self.name != other.name or self.size_pt != other.size_pt def outputPS(self, file, writer): file.write("/%s %f selectfont\n" % (self.name, self.size_pt)) + def outputPDF(self, file, writer): + file.write("/%s %f Tf\n" % (self.name, self.size_pt)) + class text_pt(canvasitem.canvasitem): @@ -219,6 +400,8 @@ class T1text_pt(text_pt): def bbox(self): if self.font.metric is None: raise NotImplementedError("we don't yet have access to the metric") + if not self.reencode: + raise NotImplementedError("can only handle glyphname based font metrics") return bbox.bbox_pt(self.x_pt, self.y_pt-self.font.metric.depth_pt(self.glyphnames, self.size_pt), self.x_pt+self.font.metric.width_pt(self.glyphnames, self.size_pt), @@ -292,3 +475,48 @@ class T1text_pt(text_pt): else: file.write("\\%03o" % charcode) file.write(") show\n") + + def processPDF(self, file, writer, context, registry, bbox): + if not self.ignorebbox: + bbox += self.bbox() + + # register resources + if self.font.t1file is not None: + if self.reencode: + registry.add(PST1file(self.font.t1file, self.glyphnames, [])) + else: + registry.add(PST1file(self.font.t1file, [], self.charcodes)) + + registry.add(PDFfont(self.font, self.chars, writer, registry)) + +# fontname = self.font.name +# if self.reencode: +# encodingname = self.getencodingname(context.encodings.setdefault(self.font.name, {})) +# encoding = context.encodings[self.font.name][encodingname] +# newfontname = "%s-%s" % (fontname, encodingname) +# registry.add(_ReEncodeFont) +# registry.add(PSreencodefont(fontname, newfontname, encoding)) +# fontname = newfontname + + # select font if necessary + sf = selectedfont(fontname, self.size_pt) + if context.selectedfont is None or sf != context.selectedfont: + context.selectedfont = sf + sf.outputPDF(file, writer) + + if self.slant is None: + slantvalue = 0 + else: + slantvalue = self.slant + + file.write("1 0 %f 1 %f %f Tm (" % (slantvalue, self.x_pt, self.y_pt)) + if self.reencode: + charcodes = [encoding[glyphname] for glyphname in self.glyphnames] + else: + charcodes = self.charcodes + for charcode in charcodes: + if 32 <= charcode <= 127 and chr(charcode) not in "()[]<>\\": + file.write("%s" % chr(charcode)) + else: + file.write("\\%03o" % charcode) + file.write(") Tj\n") diff --git a/pyx/pdfwriter.py b/pyx/pdfwriter.py index baa03f35..2efb0b4c 100644 --- a/pyx/pdfwriter.py +++ b/pyx/pdfwriter.py @@ -267,170 +267,13 @@ class PDFcontent(PDFobject): file.write("endstream\n") -class PDFfont(PDFobject): - - def __init__(self, font, chars, writer, registry): - PDFobject.__init__(self, "font", font.name) - registry.addresource("Font", font.name, self, procset="Text") - - self.fontdescriptor = PDFfontdescriptor(font, chars, writer, registry) - registry.add(self.fontdescriptor) - - if font.encoding: - self.encoding = PDFencoding(font.encoding, writer, registry) - registry.add(self.encoding) - else: - self.encoding = None - - self.name = font.name - self.basefontname = font.basefontname - self.metric = font.metric - - def write(self, file, writer, registry): - file.write("<<\n" - "/Type /Font\n" - "/Subtype /Type1\n") - file.write("/Name /%s\n" % self.name) - file.write("/BaseFont /%s\n" % self.basefontname) - if self.fontdescriptor.fontfile is not None and self.fontdescriptor.fontfile.usedchars is not None: - usedchars = self.fontdescriptor.fontfile.usedchars - firstchar = min(usedchars.keys()) - lastchar = max(usedchars.keys()) - file.write("/FirstChar %d\n" % firstchar) - file.write("/LastChar %d\n" % lastchar) - file.write("/Widths\n" - "[") - for i in range(firstchar, lastchar+1): - if i and not (i % 8): - file.write("\n") - else: - file.write(" ") - if usedchars.has_key(i): - file.write("%f" % self.metric.getwidth_ds(i)) - else: - file.write("0") - file.write(" ]\n") - else: - file.write("/FirstChar 0\n" - "/LastChar 255\n" - "/Widths\n" - "[") - for i in range(256): - if i and not (i % 8): - file.write("\n") - else: - file.write(" ") - try: - width = self.metric.getwidth_ds(i) - except (IndexError, AttributeError): - width = 0 - file.write("%f" % width) - file.write(" ]\n") - file.write("/FontDescriptor %d 0 R\n" % registry.getrefno(self.fontdescriptor)) - if self.encoding: - file.write("/Encoding %d 0 R\n" % registry.getrefno(self.encoding)) - file.write(">>\n") - - -class PDFfontdescriptor(PDFobject): - - def __init__(self, font, chars, writer, registry): - PDFobject.__init__(self, "fontdescriptor", font.basefontname) - - if font.filename is None: - self.fontfile = None - else: - self.fontfile = PDFfontfile(font.basefontname, font.filename, font.encoding, chars, writer, registry) - registry.add(self.fontfile) - - self.name = font.basefontname - self.fontinfo = font.metric.fontinfo() - - def write(self, file, writer, registry): - file.write("<<\n" - "/Type /FontDescriptor\n" - "/FontName /%s\n" % self.name) - if self.fontfile is None: - file.write("/Flags 32\n") - else: - file.write("/Flags %d\n" % self.fontfile.getflags()) - file.write("/FontBBox [%d %d %d %d]\n" % self.fontinfo.fontbbox) - file.write("/ItalicAngle %d\n" % self.fontinfo.italicangle) - file.write("/Ascent %d\n" % self.fontinfo.ascent) - file.write("/Descent %d\n" % self.fontinfo.descent) - file.write("/CapHeight %d\n" % self.fontinfo.capheight) - file.write("/StemV %d\n" % self.fontinfo.vstem) - if self.fontfile is not None: - file.write("/FontFile %d 0 R\n" % registry.getrefno(self.fontfile)) - file.write(">>\n") - - -class PDFfontfile(PDFobject): - - def __init__(self, name, filename, encoding, chars, writer, registry): - PDFobject.__init__(self, "fontfile", filename) - self.name = name - self.filename = filename - if encoding is None: - self.encodingfilename = None - else: - self.encodingfilename = encoding.filename - self.usedchars = {} - for char in chars: - self.usedchars[char] = 1 - - self.strip = 1 - self.font = None - - def merge(self, other): - if self.encodingfilename == other.encodingfilename: - self.usedchars.update(other.usedchars) - else: - # TODO: need to resolve the encoding when several encodings are in the play - self.strip = 0 - - def mkfontfile(self): - import font.t1font - self.font = font.t1font.T1pfbfont(self.filename) - - def getflags(self): - if self.font is None: - self.mkfontfile() - return self.font.getflags() - - def write(self, file, writer, registry): - if self.font is None: - self.mkfontfile() - if self.strip: - # XXX: access to the encoding file - if self.encodingfilename: - encodingfile = type1font.encodingfile(self.encodingfilename, self.encodingfilename) - usedglyphs = dict([(encodingfile.decode(char)[1:], 1) for char in self.usedchars.keys()]) - else: - self.font._encoding() - usedglyphs = dict([(self.font.encoding.decode(char), 1) for char in self.usedchars.keys()]) - strippedfont = self.font.getstrippedfont(usedglyphs) - else: - strippedfont = self.font - strippedfont.outputPDF(file, writer) - - -class PDFencoding(PDFobject): - - def __init__(self, encoding, writer, registry): - PDFobject.__init__(self, "encoding", encoding.name) - self.encoding = encoding - - def write(self, file, writer, registry): - encodingfile = type1font.encodingfile(self.encoding.name, self.encoding.filename) - encodingfile.outputPDF(file, writer) - - class PDFwriter: def __init__(self, document, file, title=None, author=None, subject=None, keywords=None, fullscreen=0, writebbox=0, compress=1, compresslevel=6): + self._fontmap = None + self.title = title self.author = author self.subject = subject @@ -452,6 +295,15 @@ class PDFwriter: registry.write(file, self, catalog) file.close() + def getfontmap(self): + if self._fontmap is None: + # late import due to cyclic dependency + from pyx.dvi import mapfile + self._fontmap = mapfile.readfontmap(["pdftex.map"]) + # config.get("text", "fontmaps", "psfonts.map") + # self.fontmap = dvifile.readfontmap(self.fontmaps.split()) + return self._fontmap + class context: @@ -461,7 +313,7 @@ class context: self.colorspace = None self.strokeattr = 1 self.fillattr = 1 - self.font = None + self.selectedfont = None self.textregion = 0 def __call__(self, **kwargs): diff --git a/pyx/pswriter.py b/pyx/pswriter.py index 476c2ea8..ba1ec0c1 100644 --- a/pyx/pswriter.py +++ b/pyx/pswriter.py @@ -151,10 +151,10 @@ class epswriter: def getfontmap(self): if self._fontmap is None: - # late import due to cyclic dependency - from pyx.dvi import mapfile + # late import due to cyclic dependency + from pyx.dvi import mapfile self._fontmap = mapfile.readfontmap(["psfonts.map"]) - # config.get("text", "fontmaps", "psfonts.map") + # config.get("text", "fontmaps", "psfonts.map") # self.fontmap = dvifile.readfontmap(self.fontmaps.split()) return self._fontmap @@ -256,9 +256,9 @@ class context: self.linewidth_pt = None self.colorspace = None self.selectedfont = None - # dictionary mapping font names to dictionaries mapping encoding names to encodings - # encodings themselves are mappings from glyphnames to codepoints - self.encodings = {} + # dictionary mapping font names to dictionaries mapping encoding names to encodings + # encodings themselves are mappings from glyphnames to codepoints + self.encodings = {} def __call__(self, **kwargs): newcontext = copy.copy(self) -- 2.11.4.GIT