2 # -*- coding: ISO-8859-1 -*-
5 # Copyright (C) 2005 Jörg Lehmann <joergl@users.sourceforge.net>
6 # Copyright (C) 2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 import prolog
, pykpathsea
, unit
, style
26 from t1strip
import fullfont
36 def __init__(self
, writer
, refno
):
39 def outputPDFobject(self
, file):
40 self
.filepos
= file.tell()
41 file.write("%i 0 obj\n" % self
.refno
)
43 file.write("endobj\n")
45 def outputPDF(self
, file):
46 raise NotImplementedError("outputPDF method has to be provided by PDFobject subclass")
49 class PDFcatalog(PDFobject
):
51 def __init__(self
, writer
, refno
, document
):
52 PDFobject
.__init
__(self
, writer
, refno
)
53 self
.pages
= writer
.addobject(PDFpages
, document
)
55 def outputPDF(self
, file):
59 ">>\n" % self
.pages
.refno
)
62 class PDFpages(PDFobject
):
64 def __init__(self
, writer
, refno
, document
):
65 PDFobject
.__init
__(self
, writer
, refno
)
67 for page
in document
.pages
:
68 self
.pages
.append(writer
.addobject(PDFpage
, self
.refno
, page
))
70 def outputPDF(self
, file):
75 ">>\n" % (" ".join(["%i 0 R" % page
.refno
76 for page
in self
.pages
]),
80 class PDFpage(PDFobject
):
82 def __init__(self
, writer
, refno
, pagesrefno
, page
):
83 PDFobject
.__init
__(self
, writer
, refno
)
84 self
.pagesrefno
= pagesrefno
86 self
.bbox
= page
.canvas
.bbox()
87 self
.pagetrafo
= page
.pagetrafo(self
.bbox
)
89 self
.bbox
.transform(self
.pagetrafo
)
90 self
.content
= writer
.addobject(PDFcontent
, page
.canvas
, self
.pagetrafo
)
92 def outputPDF(self
, file):
95 "/Parent %i 0 R\n" % self
.pagesrefno
)
96 paperformat
= self
.page
.paperformat
97 file.write("/MediaBox [0 0 %d %d]\n" % (unit
.topt(paperformat
.width
), unit
.topt(paperformat
.height
)))
98 file.write("/CropBox " )
99 self
.bbox
.outputPDF(file)
100 file.write("/Resources << /ProcSet [ /PDF ] >>\n")
101 file.write("/Contents %i 0 R\n"
102 ">>\n" % (self
.content
.refno
))
105 class PDFcontent(PDFobject
):
107 def __init__(self
, writer
, refno
, canvas
, pagetrafo
):
108 PDFobject
.__init
__(self
, writer
, refno
)
111 self
.pagetrafo
= pagetrafo
112 self
.contentlength
= writer
.addobject(PDFcontentlength
)
114 def outputPDF(self
, file):
116 "/Length %i 0 R\n" % (self
.refno
+ 1))
118 # self.write("/Filter /FlateDecode\n")
120 file.write("stream\n")
121 beginstreampos
= file.tell()
124 # if self.compressstream is not None:
125 # raise RuntimeError("compression within compression")
126 # self.compressstream = zlib.compressobj(self.compresslevel)
128 # apply a possible global transformation
131 self
.pagetrafo
.outputPDF(file)
132 style
.linewidth
.normal
.outputPDF(file)
133 self
.canvas
.outputPDF(file)
135 # if self.compressstream is not None:
136 # self.file.write(self.compressstream.flush())
137 # self.compressstream = None
139 self
.contentlength
.contentlength
= file.tell() - beginstreampos
140 file.write("endstream\n")
143 class PDFcontentlength(PDFobject
):
145 def __init__(self
, writer
, refno
):
146 PDFobject
.__init
__(self
, writer
, refno
)
147 # initially we do not know about the content length, we
148 # has to be written into the instance later on
149 self
.contentlength
= None
151 def outputPDF(self
, file):
152 file.write("%d\n" % self
.contentlength
)
155 class PDFfont(PDFobject
):
156 def __init__(self
, writer
, refno
, font
):
157 PDFobject
.__init
__(self
, writer
, refno
)
159 self
.fontwidths
= writer
.addobject(PDFfontwidths
, font
)
160 self
.fontdescriptor
= writer
.addobject(PDFfontdescriptor
, font
)
162 def outputPDF(self
, file):
171 "/FontDescriptor %d 0 R\n"
172 "/Encoding /StandardEncoding\n" # FIXME
173 ">>\n" % (self
.font
.getpsname(), self
.font
.getbasepsname(),
174 self
.fontwidths
.refno
, self
.fontdescriptor
.refno
))
176 class PDFfontwidths(PDFobject
):
177 def __init__(self
, writer
, refno
, font
):
178 PDFobject
.__init
__(self
, writer
, refno
)
181 def outputPDF(self
, file):
185 width
= self
.font
.getwidth_pt(i
)*1000/self
.font
.getsize_pt()
188 self
.write("%f\n" % width
)
192 class PDFfontdescriptor(PDFobject
):
194 def __init__(self
, writer
, refno
, font
):
195 PDFobject
.__init
__(self
, writer
, refno
)
197 path
= pykpathsea
.find_file(font
.filename
, pykpathsea
.kpse_type1_format
)
198 self
.fontfile
= writer
.addobject(PDFfontfile
, path
)
200 def outputPDF(self
, file):
202 "/Type /FontDescriptor\n"
205 "/FontBBox [-10 -10 1000 1000]\n" # FIXME
206 "/ItalicAngle 0\n" # FIXME
207 "/Ascent 20\n" # FIXME
208 "/Descent -5\n" # FIXME
209 "/CapHeight 15\n" # FIXME
211 "/FontFile %d 0 R\n" # FIXME
212 # "/CharSet \n" # fill in when stripping
213 ">>\n" % (self
.font
.getbasepsname(), self
.fontfile
.refno
))
215 class PDFfontfile(PDFobject
):
217 def __init__(self
, writer
, refno
, path
):
218 PDFobject
.__init
__(self
, writer
, refno
)
221 def outputPDF(self
, file):
222 fontfile
= open(self
.path
)
223 fontdata
= fontfile
.read()
225 if fontdata
[0:2] != fullfont
._PFB
_ASCII
:
226 raise RuntimeError("PFB_ASCII mark expected")
227 length1
= fullfont
.pfblength(fontdata
[2:6])
228 if fontdata
[6+length1
:8+length1
] != fullfont
._PFB
_BIN
:
229 raise RuntimeError("PFB_BIN mark expected")
230 length2
= fullfont
.pfblength(fontdata
[8+length1
:12+length1
])
231 if fontdata
[12+length1
+length2
:14+length1
+length2
] != fullfont
._PFB
_ASCII
:
232 raise RuntimeError("PFB_ASCII mark expected")
233 length3
= fullfont
.pfblength(fontdata
[14+length1
+length2
:18+length1
+length2
])
234 if fontdata
[18+length1
+length2
+length3
:20+length1
+length2
+length3
] != fullfont
._PFB
_DONE
:
235 raise RuntimeError("PFB_DONE mark expected")
236 if len(fontdata
) != 20 + length1
+ length2
+ length3
:
237 raise RuntimeError("end of pfb file expected")
239 # we might be allowed to skip the third part ...
240 if fontdata
[18+length1
+length2
:18+length1
+length2
+length3
].replace("\n", "").replace("\r", "").replace("\t", "").replace(" ", "") == "0"*512 + "cleartomark":
243 uncompresseddata
= fontdata
[6:6+length1
] + fontdata
[12+length1
:12+length1
+length2
] + fontdata
[18+length1
+length2
:18+length1
+length2
+length3
]
244 compresseddata
= zlib
.compress(uncompresseddata
)
251 "/Filter /FlateDecode\n"
253 "stream\n" % (len(compresseddata
), length1
, length2
, length3
))
254 #file.write(fontdata[6:6+length1])
255 #file.write(fontdata[12+length1:12+length1+length2])
256 #file.write(fontdata[18+length1+length2:18+length1+length2+length3])
257 self
.write(compresseddata
)
258 self
.write("endstream\n")
263 def __init__(self
, document
, filename
, compress
=1, compresslevel
=6):
264 sys
.stderr
.write("*** PyX Warning: writePDFfile is experimental and supports only a subset of PyX's features\n")
266 if filename
[-4:] != ".pdf":
267 filename
= filename
+ ".pdf"
269 self
.file = open(filename
, "wb")
271 raise IOError("cannot open output file")
273 if compress
and not haszlib
:
275 sys
.stderr
.write("*** PyX Warning: compression disabled due to missing zlib module\n")
276 self
.compress
= compress
277 self
.compresslevel
= compresslevel
280 self
.pdfobjectcount
= 0
282 self
.file.write("%%PDF-1.4\n%%%s%s%s%s\n" % (chr(195), chr(182), chr(195), chr(169)))
284 # the PDFcatalog class automatically builds up the pdfobjects from a document
285 catalog
= self
.addobject(PDFcatalog
, document
)
287 # by recursively appending to the list of pdfobjects, we create this list in reverse
288 # order and thus need to reverse it before writing it in the output file
289 self
.pdfobjects
.reverse()
290 for pdfobject
in self
.pdfobjects
:
291 pdfobject
.outputPDFobject(self
.file)
294 xrefpos
= self
.file.tell()
295 self
.file.write("xref\n"
297 "0000000000 65535 f \n" % (len(self
.pdfobjects
)+1))
298 for pdfobject
in self
.pdfobjects
:
299 self
.file.write("%010i 00000 n \n" % pdfobject
.filepos
)
302 self
.file.write("trailer\n"
309 "%%%%EOF\n" % (len(self
.pdfobjects
)+1, catalog
.refno
, xrefpos
))
312 def addobject(self
, pdfobjectclass
, *args
, **kwargs
):
313 """ create an instance of pdfobjectclass, append it to the list of pdfobjects
314 and return it to the caller.
316 The constructor of pdfobjectclass gets as arguments the PDFwriter and the
317 refno of the new object well as further arguments *args and **kwargs."""
319 self
.pdfobjectcount
+= 1
320 pdfobject
= pdfobjectclass(self
, self
.pdfobjectcount
, *args
, **kwargs
)
321 self
.pdfobjects
.append(pdfobject
)
323 # # During the creating of object other objects may already have been added.
324 # # We have to make sure that we add the object at the right place in the
326 # i = len(self.pdfobjects)
327 # while i > 0 and self.pdfobjects[i-1].refno > pdfobject.refno:
329 # self.pdfobjects.insert(i, pdfobject)
333 def page(self
, abbox
, canvas
, mergedprolog
, ctrafo
):
335 for pritem
in mergedprolog
:
336 if isinstance(pritem
, prolog
.fontdefinition
):
342 self
.beginobj(PDFresources
% self
.pages
)
344 if len([pritem
for pritem
in mergedprolog
if isinstance(pritem
, prolog
.fontdefinition
)]):
347 for pritem
in mergedprolog
:
348 if isinstance(pritem
, prolog
.fontdefinition
):
349 self
.write("/%s %d 0 R\n" % (pritem
.font
.getpsname(),
350 self
.refdict
[PDFfont
% pritem
.font
.getpsname()]))
361 self
.colorspace
= None
362 self
.linewidth
= None
368 self
.context
= context()
371 self
.stack
.append(copy
.copy(self
.context
))
374 self
.context
= self
.stack
.pop()