2 # -*- coding: ISO-8859-1 -*-
5 # Copyright (C) 2002-2004 Jörg Lehmann <joergl@users.sourceforge.net>
6 # Copyright (C) 2002-2004 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
24 # XXX what are the correct base classes of clip and pattern
26 """The canvas module provides a PostScript canvas class and related classes
28 A canvas holds a collection of all elements that should be displayed together
29 with their attributes.
32 import sys
, cStringIO
, time
33 import attr
, base
, bbox
, deco
, deformer
, unit
, prolog
, style
, trafo
, version
35 # temporarily needed for pdf fonts
37 from t1strip
import fullfont
45 # fallback implementation for Python 2.2. and below
47 return zip(xrange(len(list)), list)
49 # known paperformats as tuple (width, height)
51 _paperformats
= { "A4" : (210 * unit
.t_mm
, 297 * unit
.t_mm
),
52 "A3" : (297 * unit
.t_mm
, 420 * unit
.t_mm
),
53 "A2" : (420 * unit
.t_mm
, 594 * unit
.t_mm
),
54 "A1" : (594 * unit
.t_mm
, 840 * unit
.t_mm
),
55 "A0" : (840 * unit
.t_mm
, 1188 * unit
.t_mm
),
56 "A0b" : (910 * unit
.t_mm
, 1370 * unit
.t_mm
),
57 "Letter" : (8.5 * unit
.t_inch
, 11 * unit
.t_inch
),
58 "Legal" : (8.5 * unit
.t_inch
, 14 * unit
.t_inch
)}
64 class clip(base
.canvasitem
):
66 """class for use in canvas constructor which clips to a path"""
68 def __init__(self
, path
):
69 """construct a clip instance for a given path"""
73 # as a canvasitem a clipping path has NO influence on the bbox...
77 # ... but for clipping, we nevertheless need the bbox
78 return self
.path
.bbox()
80 def outputPS(self
, file):
81 file.write("newpath\n")
82 self
.path
.outputPS(file)
85 def outputPDF(self
, file):
86 self
.path
.outputPDF(file)
90 # general canvas class
93 class _canvas(base
.canvasitem
):
95 """a canvas holds a collection of canvasitems"""
97 def __init__(self
, attrs
=[], texrunner
=None):
101 The canvas can be modfied by supplying args, which have
102 to be instances of one of the following classes:
103 - trafo.trafo (leading to a global transformation of the canvas)
104 - canvas.clip (clips the canvas)
105 - base.PathStyle (sets some global attributes of the canvas)
107 Note that, while the first two properties are fixed for the
108 whole canvas, the last one can be changed via canvas.set().
110 The texrunner instance used for the text method can be specified
111 using the texrunner argument. It defaults to text.defaulttexrunner
116 self
.trafo
= trafo
.trafo()
118 if texrunner
is not None:
119 self
.texrunner
= texrunner
121 # prevent cyclic imports
123 self
.texrunner
= text
.defaulttexrunner
126 if isinstance(attr
, trafo
.trafo_pt
):
127 self
.trafo
= self
.trafo
*attr
128 self
.items
.append(attr
)
129 elif isinstance(attr
, clip
):
130 if self
.clipbbox
is None:
131 self
.clipbbox
= attr
.clipbbox().transformed(self
.trafo
)
133 self
.clippbox
*= attr
.clipbbox().transformed(self
.trafo
)
134 self
.items
.append(attr
)
139 """returns bounding box of canvas"""
141 for cmd
in self
.items
:
145 elif abbox
is not None:
148 # transform according to our global transformation and
149 # intersect with clipping bounding box (which have already been
150 # transformed in canvas.__init__())
151 if obbox
is not None and self
.clipbbox
is not None:
152 return obbox
.transformed(self
.trafo
)*self
.clipbbox
153 elif obbox
is not None:
154 return obbox
.transformed(self
.trafo
)
160 for cmd
in self
.items
:
161 result
.extend(cmd
.prolog())
164 def outputPS(self
, file):
166 file.write("gsave\n")
167 for cmd
in self
.items
:
169 file.write("grestore\n")
171 def outputPDF(self
, file):
173 file.write("q\n") # gsave
174 for cmd
in self
.items
:
176 file.write("Q\n") # grestore
178 def insert(self
, item
, attrs
=None):
179 """insert item in the canvas.
181 If attrs are passed, a canvas containing the item is inserted applying attrs.
187 if not isinstance(item
, base
.canvasitem
):
188 raise RuntimeError("only instances of base.canvasitem can be inserted into a canvas")
193 self
.items
.append(sc
)
195 self
.items
.append(item
)
199 def set(self
, attrs
):
200 """sets styles args globally for the rest of the canvas
203 attr
.checkattrs(attrs
, [style
.strokestyle
, style
.fillstyle
])
207 def draw(self
, path
, attrs
):
208 """draw path on canvas using the style given by args
210 The argument attrs consists of PathStyles, which modify
211 the appearance of the path, PathDecos, which add some new
212 visual elements to the path, or trafos, which are applied
213 before drawing the path.
217 attrs
= attr
.mergeattrs(attrs
)
218 attr
.checkattrs(attrs
, [deco
.deco
, deformer
.deformer
, style
.fillstyle
, style
.strokestyle
])
220 for adeformer
in attr
.getattrs(attrs
, [deformer
.deformer
]):
221 path
= adeformer
.deform(path
)
223 styles
= attr
.getattrs(attrs
, [style
.fillstyle
, style
.strokestyle
])
224 dp
= deco
.decoratedpath(path
, styles
=styles
)
226 # add path decorations and modify path accordingly
227 for adeco
in attr
.getattrs(attrs
, [deco
.deco
]):
228 dp
= adeco
.decorate(dp
)
232 def stroke(self
, path
, attrs
=[]):
233 """stroke path on canvas using the style given by args
235 The argument attrs consists of PathStyles, which modify
236 the appearance of the path, PathDecos, which add some new
237 visual elements to the path, or trafos, which are applied
238 before drawing the path.
242 self
.draw(path
, [deco
.stroked
]+list(attrs
))
244 def fill(self
, path
, attrs
=[]):
245 """fill path on canvas using the style given by args
247 The argument attrs consists of PathStyles, which modify
248 the appearance of the path, PathDecos, which add some new
249 visual elements to the path, or trafos, which are applied
250 before drawing the path.
254 self
.draw(path
, [deco
.filled
]+list(attrs
))
256 def settexrunner(self
, texrunner
):
257 """sets the texrunner to be used to within the text and text_pt methods"""
259 self
.texrunner
= texrunner
261 def text(self
, x
, y
, atext
, *args
, **kwargs
):
262 """insert a text into the canvas
264 inserts a textbox created by self.texrunner.text into the canvas
266 returns the inserted textbox"""
268 return self
.insert(self
.texrunner
.text(x
, y
, atext
, *args
, **kwargs
))
271 def text_pt(self
, x
, y
, atext
, *args
):
272 """insert a text into the canvas
274 inserts a textbox created by self.texrunner.text_pt into the canvas
276 returns the inserted textbox"""
278 return self
.insert(self
.texrunner
.text_pt(x
, y
, atext
, *args
))
281 # canvas for patterns
284 class pattern(_canvas
, attr
.exclusiveattr
, style
.fillstyle
):
286 def __init__(self
, painttype
=1, tilingtype
=1, xstep
=None, ystep
=None, bbox
=None, trafo
=None):
287 attr
.exclusiveattr
.__init
__(self
, pattern
)
288 _canvas
.__init
__(self
)
289 attr
.exclusiveattr
.__init
__(self
, pattern
)
290 self
.id = "pattern%d" % id(self
)
291 if painttype
not in (1,2):
292 raise ValueError("painttype must be 1 or 2")
293 self
.painttype
= painttype
294 if tilingtype
not in (1,2,3):
295 raise ValueError("tilingtype must be 1, 2 or 3")
296 self
.tilingtype
= tilingtype
299 self
.patternbbox
= bbox
300 self
.patterntrafo
= trafo
305 def outputPS(self
, file):
306 file.write("%s setpattern\n" % self
.id)
309 realpatternbbox
= _canvas
.bbox(self
)
310 if self
.xstep
is None:
311 xstep
= unit
.topt(realpatternbbox
.width())
313 xstep
= unit
.topt(self
.xstep
)
314 if self
.ystep
is None:
315 ystep
= unit
.topt(realpatternbbox
.height())
317 ystep
= unit
.topt(self
.ystep
)
319 raise ValueError("xstep in pattern cannot be zero")
321 raise ValueError("ystep in pattern cannot be zero")
322 patternbbox
= self
.patternbbox
or realpatternbbox
.enlarged(5*unit
.pt
)
324 patternprefix
= "\n".join(("<<",
326 "/PaintType %d" % self
.painttype
,
327 "/TilingType %d" % self
.tilingtype
,
328 "/BBox[%s]" % str(patternbbox
),
331 "/PaintProc {\nbegin\n"))
332 stringfile
= cStringIO
.StringIO()
333 _canvas
.outputPS(self
, stringfile
)
334 patternproc
= stringfile
.getvalue()
336 patterntrafostring
= self
.patterntrafo
is None and "matrix" or str(self
.patterntrafo
)
337 patternsuffix
= "end\n} bind\n>>\n%s\nmakepattern" % patterntrafostring
339 pr
= _canvas
.prolog(self
)
340 pr
.append(prolog
.definition(self
.id, "".join((patternprefix
, patternproc
, patternsuffix
))))
343 pattern
.clear
= attr
.clearclass(pattern
)
347 def calctrafo(abbox
, paperformat
, margin
, rotated
, fittosize
):
348 """ calculate a trafo which rotates and fits a canvas with
349 bounding box abbox on the given paperformat with a margin on all
351 if not isinstance(margin
, unit
.length
):
352 margin
= unit
.length(margin
)
353 atrafo
= None # global transformation of canvas
356 atrafo
= trafo
.rotate(90, *abbox
.center())
359 # center (optionally rotated) output on page
361 paperwidth
, paperheight
= _paperformats
[paperformat
.capitalize()]
363 raise KeyError, "unknown paperformat '%s'" % paperformat
365 paperwidth
-= 2*margin
366 paperheight
-= 2*margin
368 if not atrafo
: atrafo
= trafo
.trafo()
370 atrafo
= atrafo
.translated(margin
+ 0.5*(paperwidth
- abbox
.width()) - abbox
.left(),
371 margin
+ 0.5*(paperheight
- abbox
.height()) - abbox
.bottom())
374 # scale output to pagesize - margins
375 if 2*margin
> min(paperwidth
, paperheight
):
376 raise RuntimeError("Margins too broad for selected paperformat. Aborting.")
379 sfactor
= min(unit
.topt(paperheight
)/unit
.topt(abbox
.width()),
380 unit
.topt(paperwidth
)/unit
.topt(abbox
.height()))
382 sfactor
= min(unit
.topt(paperwidth
)/unit
.topt(abbox
.width()),
383 unit
.topt(paperheight
)/unit
.topt(abbox
.height()))
385 atrafo
= atrafo
.scaled(sfactor
, sfactor
, margin
+ 0.5*paperwidth
, margin
+ 0.5*paperheight
)
387 raise ValueError("must specify paper size for fittosize")
392 # The main canvas class
395 class canvas(_canvas
):
397 """a canvas holds a collection of canvasitems"""
399 def writeEPSfile(self
, filename
, paperformat
=None, rotated
=0, fittosize
=0, margin
=1 * unit
.t_cm
,
400 bbox
=None, bboxenlarge
=1 * unit
.t_pt
):
401 """write canvas to EPS file
403 If paperformat is set to a known paperformat, the output will be centered on
406 If rotated is set, the output will first be rotated by 90 degrees.
408 If fittosize is set, then the output is scaled to the size of the
409 page (minus margin). In that case, the paperformat the specification
410 of the paperformat is obligatory.
412 The bbox parameter overrides the automatic bounding box determination.
413 bboxenlarge may be used to enlarge the bbox of the canvas (or the
414 manually specified bbox).
417 if filename
[-4:]!=".eps":
418 filename
= filename
+ ".eps"
421 file = open(filename
, "w")
423 raise IOError("cannot open output file")
425 abbox
= bbox
is not None and bbox
or self
.bbox()
426 abbox
.enlarge(bboxenlarge
)
427 ctrafo
= calctrafo(abbox
, paperformat
, margin
, rotated
, fittosize
)
429 # if there has been a global transformation, adjust the bounding box
431 if ctrafo
: abbox
.transform(ctrafo
)
433 file.write("%!PS-Adobe-3.0 EPSF 3.0\n")
435 file.write("%%%%Creator: PyX %s\n" % version
.version
)
436 file.write("%%%%Title: %s\n" % filename
)
437 file.write("%%%%CreationDate: %s\n" %
438 time
.asctime(time
.localtime(time
.time())))
439 file.write("%%EndComments\n")
441 file.write("%%BeginProlog\n")
445 for pritem
in self
.prolog():
446 for mpritem
in mergedprolog
:
447 if mpritem
.merge(pritem
) is None: break
449 mergedprolog
.append(pritem
)
451 for pritem
in mergedprolog
:
452 pritem
.outputPS(file)
454 file.write("%%EndProlog\n")
456 # apply a possible global transformation
457 if ctrafo
: ctrafo
.outputPS(file)
459 file.write("%f setlinewidth\n" % unit
.topt(style
.linewidth
.normal
))
461 # here comes the actual content
464 file.write("showpage\n")
465 file.write("%%Trailer\n")
466 file.write("%%EOF\n")
468 def writePDFfile(self
, filename
, paperformat
=None, rotated
=0, fittosize
=0, margin
=1 * unit
.t_cm
,
469 bbox
=None, bboxenlarge
=1 * unit
.t_pt
):
470 sys
.stderr
.write("*** PyX Warning: writePDFfile is experimental and supports only a subset of PyX's features\n")
472 if filename
[-4:]!=".pdf":
473 filename
= filename
+ ".pdf"
476 file = open(filename
, "wb")
478 raise IOError("cannot open output file")
480 abbox
= bbox
is not None and bbox
or self
.bbox()
481 abbox
.enlarge(bboxenlarge
)
483 ctrafo
= calctrafo(abbox
, paperformat
, margin
, rotated
, fittosize
)
485 # if there has been a global transformation, adjust the bounding box
487 if ctrafo
: abbox
.transform(ctrafo
)
491 for pritem
in self
.prolog():
492 for mpritem
in mergedprolog
:
493 if mpritem
.merge(pritem
) is None: break
495 mergedprolog
.append(pritem
)
497 file.write("%%PDF-1.4\n%%%s%s%s%s\n" % (chr(195), chr(182), chr(195), chr(169)))
498 reflist
= [file.tell()]
499 file.write("1 0 obj\n"
505 reflist
.append(file.tell())
506 file.write("2 0 obj\n"
513 reflist
.append(file.tell())
514 file.write("3 0 obj\n"
519 abbox
.outputPDF(file)
520 file.write("/Contents 4 0 R\n"
525 if len([pritem
for pritem
in mergedprolog
if isinstance(pritem
, prolog
.fontdefinition
)]):
528 for pritem
in mergedprolog
:
529 if isinstance(pritem
, prolog
.fontdefinition
):
531 file.write("/%s %d 0 R\n" % (pritem
.font
.getpsname(), fontnr
+fontstartref
))
532 fontnr
+= 3 # further objects due to a font
538 reflist
.append(file.tell())
539 file.write("4 0 obj\n"
540 "<< /Length 5 0 R >>\n"
542 streamstartpos
= file.tell()
544 # apply a possible global transformation
545 if ctrafo
: ctrafo
.outputPDF(file)
546 style
.linewidth
.normal
.outputPDF(file)
549 streamendpos
= file.tell()
550 file.write("endstream\n"
552 reflist
.append(file.tell())
553 file.write("5 0 obj\n"
555 "endobj\n" % (streamendpos
- streamstartpos
))
558 for pritem
in mergedprolog
:
559 if isinstance(pritem
, prolog
.fontdefinition
):
561 reflist
.append(file.tell())
562 file.write("%d 0 obj\n"
571 "/FontDescriptor %d 0 R\n"
572 "/Encoding /StandardEncoding\n" # FIXME
574 "endobj\n" % (fontnr
+fontstartref
, pritem
.font
.getpsname(), pritem
.font
.getbasepsname(),
575 fontnr
+fontstartref
+1, fontnr
+fontstartref
+2))
577 reflist
.append(file.tell())
578 file.write("%d 0 obj\n"
579 "[\n" % (fontnr
+fontstartref
))
582 width
= pritem
.font
.getwidth_pt(i
)*1000/pritem
.font
.getsize_pt()
585 file.write("%f\n" % width
)
590 reflist
.append(file.tell())
591 file.write("%d 0 obj\n"
593 "/Type /FontDescriptor\n"
596 "/FontBBox [-10 -10 1000 1000]\n" # FIXME
597 "/ItalicAngle 0\n" # FIXME
598 "/Ascent 20\n" # FIXME
599 "/Descent -5\n" # FIXME
600 "/CapHeight 15\n" # FIXME
602 "/FontFile %d 0 R\n" # FIXME
603 # "/CharSet \n" # fill in when stripping
605 "endobj\n" % (fontnr
+fontstartref
, pritem
.font
.getbasepsname(),
606 fontnr
+fontstartref
+1))
609 reflist
.append(file.tell())
611 fontdata
= open(pykpathsea
.find_file(pritem
.filename
, pykpathsea
.kpse_type1_format
)).read()
612 if fontdata
[0:2] != fullfont
._PFB
_ASCII
:
613 raise RuntimeError("PFB_ASCII mark expected")
614 length1
= fullfont
.pfblength(fontdata
[2:6])
615 if fontdata
[6+length1
:8+length1
] != fullfont
._PFB
_BIN
:
616 raise RuntimeError("PFB_BIN mark expected")
617 length2
= fullfont
.pfblength(fontdata
[8+length1
:12+length1
])
618 if fontdata
[12+length1
+length2
:14+length1
+length2
] != fullfont
._PFB
_ASCII
:
619 raise RuntimeError("PFB_ASCII mark expected")
620 length3
= fullfont
.pfblength(fontdata
[14+length1
+length2
:18+length1
+length2
])
621 if fontdata
[18+length1
+length2
+length3
:20+length1
+length2
+length3
] != fullfont
._PFB
_DONE
:
622 raise RuntimeError("PFB_DONE mark expected")
623 if len(fontdata
) != 20 + length1
+ length2
+ length3
:
624 raise RuntimeError("end of pfb file expected")
626 # we might be allowed to skip the third part ...
627 if fontdata
[18+length1
+length2
:18+length1
+length2
+length3
].replace("\n", "").replace("\r", "").replace("\t", "").replace(" ", "") == "0"*512 + "cleartomark":
630 uncompresseddata
= fontdata
[6:6+length1
] + fontdata
[12+length1
:12+length1
+length2
] + fontdata
[18+length1
+length2
:18+length1
+length2
+length3
]
631 compresseddata
= zlib
.compress(uncompresseddata
)
633 file.write("%d 0 obj\n"
639 "/Filter /FlateDecode\n"
641 "stream\n" % (fontnr
+fontstartref
, len(compresseddata
),
645 #file.write(fontdata[6:6+length1])
646 #file.write(fontdata[12+length1:12+length1+length2])
647 #file.write(fontdata[18+length1+length2:18+length1+length2+length3])
648 file.write(compresseddata
)
649 file.write("endstream\n"
654 xrefpos
= file.tell()
656 "0 %d\n" % (len(reflist
)+1))
657 file.write("0000000000 65535 f \n")
659 file.write("%010i 00000 n \n" % ref
)
660 file.write("trailer\n"
667 "%%%%EOF\n" % xrefpos
)
669 def writePDFfile_new(self
, filename
, paperformat
=None, rotated
=0, fittosize
=0, margin
=1 * unit
.t_cm
,
670 bbox
=None, bboxenlarge
=1 * unit
.t_pt
):
671 sys
.stderr
.write("*** PyX Warning: writePDFfile is experimental and supports only a subset of PyX's features\n")
673 if filename
[-4:]!=".pdf":
674 filename
= filename
+ ".pdf"
677 writer
= pdfwriter
.pdfwriter(filename
)
679 raise IOError("cannot open output file")
681 abbox
= bbox
is not None and bbox
or self
.bbox()
682 abbox
.enlarge(bboxenlarge
)
684 ctrafo
= calctrafo(abbox
, paperformat
, margin
, rotated
, fittosize
)
686 # if there has been a global transformation, adjust the bounding box
688 if ctrafo
: abbox
.transform(ctrafo
)
692 for pritem
in self
.prolog():
693 for mpritem
in mergedprolog
:
694 if mpritem
.merge(pritem
) is None: break
696 mergedprolog
.append(pritem
)
697 writer
.page(abbox
, self
, mergedprolog
, ctrafo
)
700 def writetofile(self
, filename
, *args
, **kwargs
):
701 if filename
[-4:] == ".eps":
702 self
.writeEPSfile(filename
, *args
, **kwargs
)
703 elif filename
[-4:] == ".pdf":
704 self
.writePDFfile(filename
, *args
, **kwargs
)
706 sys
.stderr
.write("*** PyX Warning: deprecated usage of writetofile -- writetofile needs a filename extension or use an explicit call to writeEPSfile or the like\n")
707 self
.writeEPSfile(filename
, *args
, **kwargs
)
711 def __init__(self
, attrs
=[], texrunner
=None, pagename
=None, paperformat
="a4", rotated
=0, fittosize
=0,
712 margin
=1 * unit
.t_cm
, bboxenlarge
=1 * unit
.t_pt
):
713 canvas
.__init
__(self
, attrs
, texrunner
)
714 self
.pagename
= pagename
715 self
.paperformat
= paperformat
.capitalize()
716 self
.rotated
= rotated
717 self
.fittosize
= fittosize
719 self
.bboxenlarge
= bboxenlarge
722 # the bounding box of a page is fixed by its format and an optional rotation
723 pbbox
= bbox
.bbox(0, 0, *_paperformats
[self
.paperformat
])
724 pbbox
.enlarge(self
.bboxenlarge
)
726 pbbox
.transform(trafo
.rotate(90, *pbbox
.center()))
729 def outputPS(self
, file):
730 file.write("%%%%PageMedia: %s\n" % self
.paperformat
)
731 file.write("%%%%PageOrientation: %s\n" % (self
.rotated
and "Landscape" or "Portrait"))
732 # file.write("%%%%PageBoundingBox: %d %d %d %d\n" % (math.floor(pbbox.llx_pt), math.floor(pbbox.lly_pt),
733 # math.ceil(pbbox.urx_pt), math.ceil(pbbox.ury_pt)))
736 file.write("%%BeginPageSetup\n")
737 file.write("/pgsave save def\n")
738 # for scaling, we need the real bounding box of the page contents
739 pbbox
= canvas
.bbox(self
)
740 pbbox
.enlarge(self
.bboxenlarge
)
741 ptrafo
= calctrafo(pbbox
, self
.paperformat
, self
.margin
, self
.rotated
, self
.fittosize
)
743 ptrafo
.outputPS(file)
744 file.write("%f setlinewidth\n" % unit
.topt(style
.linewidth
.normal
))
745 file.write("%%EndPageSetup\n")
747 # here comes the actual content
748 canvas
.outputPS(self
, file)
749 file.write("pgsave restore\n")
750 file.write("showpage\n")
751 # file.write("%%PageTrailer\n")
756 """holds a collection of page instances which are output as pages of a document"""
758 def __init__(self
, pages
=[]):
761 def append(self
, page
):
762 self
.pages
.append(page
)
764 def writePSfile(self
, filename
):
765 """write pages to PS file """
767 if filename
[-3:]!=".ps":
768 filename
= filename
+ ".ps"
771 file = open(filename
, "w")
773 raise IOError("cannot open output file")
776 for apage
in self
.pages
:
784 file.write("%!PS-Adobe-3.0\n")
785 docbbox
.outputPS(file)
786 file.write("%%%%Creator: PyX %s\n" % version
.version
)
787 file.write("%%%%Title: %s\n" % filename
)
788 file.write("%%%%CreationDate: %s\n" %
789 time
.asctime(time
.localtime(time
.time())))
790 # required paper formats
792 for apage
in self
.pages
:
793 if isinstance(apage
, page
):
794 paperformats
[apage
.paperformat
] = _paperformats
[apage
.paperformat
]
796 for paperformat
, size
in paperformats
.items():
798 file.write("%%DocumentMedia: ")
802 file.write("%s %d %d 75 white ()\n" % (paperformat
, unit
.topt(size
[0]), unit
.topt(size
[1])))
804 file.write("%%%%Pages: %d\n" % len(self
.pages
))
805 file.write("%%PageOrder: Ascend\n")
806 file.write("%%EndComments\n")
808 # document default section
809 #file.write("%%BeginDefaults\n")
811 # file.write("%%%%PageMedia: %s\n" % paperformat)
812 #file.write("%%%%PageOrientation: %s\n" % (rotated and "Landscape" or "Portrait"))
813 #file.write("%%EndDefaults\n")
815 # document prolog section
816 file.write("%%BeginProlog\n")
818 for apage
in self
.pages
:
819 for pritem
in apage
.prolog():
820 for mpritem
in mergedprolog
:
821 if mpritem
.merge(pritem
) is None: break
823 mergedprolog
.append(pritem
)
824 for pritem
in mergedprolog
:
825 pritem
.outputPS(file)
826 file.write("%%EndProlog\n")
828 # document setup section
829 #file.write("%%BeginSetup\n")
830 #file.write("%%EndSetup\n")
833 for nr
, apage
in enumerate(self
.pages
):
834 file.write("%%%%Page: %s %d\n" % (apage
.pagename
is None and str(nr
) or apage
.pagename
, nr
+1))
837 file.write("%%Trailer\n")
838 file.write("%%EOF\n")