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
, 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
, style
.fillstyle
, style
.strokestyle
, trafo
.trafo_pt
])
220 for t
in attr
.getattrs(attrs
, [trafo
.trafo_pt
]):
221 path
= path
.transformed(t
)
223 dp
= deco
.decoratedpath(path
)
226 dp
.styles
= attr
.getattrs(attrs
, [style
.fillstyle
, style
.strokestyle
])
228 # add path decorations and modify path accordingly
229 for adeco
in attr
.getattrs(attrs
, [deco
.deco
]):
230 dp
= adeco
.decorate(dp
)
234 def stroke(self
, path
, attrs
=[]):
235 """stroke path on canvas using the style given by args
237 The argument attrs consists of PathStyles, which modify
238 the appearance of the path, PathDecos, which add some new
239 visual elements to the path, or trafos, which are applied
240 before drawing the path.
244 self
.draw(path
, [deco
.stroked
]+list(attrs
))
246 def fill(self
, path
, attrs
=[]):
247 """fill path on canvas using the style given by args
249 The argument attrs consists of PathStyles, which modify
250 the appearance of the path, PathDecos, which add some new
251 visual elements to the path, or trafos, which are applied
252 before drawing the path.
256 self
.draw(path
, [deco
.filled
]+list(attrs
))
258 def settexrunner(self
, texrunner
):
259 """sets the texrunner to be used to within the text and text_pt methods"""
261 self
.texrunner
= texrunner
263 def text(self
, x
, y
, atext
, *args
, **kwargs
):
264 """insert a text into the canvas
266 inserts a textbox created by self.texrunner.text into the canvas
268 returns the inserted textbox"""
270 return self
.insert(self
.texrunner
.text(x
, y
, atext
, *args
, **kwargs
))
273 def text_pt(self
, x
, y
, atext
, *args
):
274 """insert a text into the canvas
276 inserts a textbox created by self.texrunner.text_pt into the canvas
278 returns the inserted textbox"""
280 return self
.insert(self
.texrunner
.text_pt(x
, y
, atext
, *args
))
283 # canvas for patterns
286 class pattern(_canvas
, attr
.exclusiveattr
, style
.fillstyle
):
288 def __init__(self
, painttype
=1, tilingtype
=1, xstep
=None, ystep
=None, bbox
=None, trafo
=None):
289 attr
.exclusiveattr
.__init
__(self
, pattern
)
290 _canvas
.__init
__(self
)
291 attr
.exclusiveattr
.__init
__(self
, pattern
)
292 self
.id = "pattern%d" % id(self
)
293 if painttype
not in (1,2):
294 raise ValueError("painttype must be 1 or 2")
295 self
.painttype
= painttype
296 if tilingtype
not in (1,2,3):
297 raise ValueError("tilingtype must be 1, 2 or 3")
298 self
.tilingtype
= tilingtype
301 self
.patternbbox
= bbox
302 self
.patterntrafo
= trafo
307 def outputPS(self
, file):
308 file.write("%s setpattern\n" % self
.id)
311 realpatternbbox
= _canvas
.bbox(self
)
312 if self
.xstep
is None:
313 xstep
= unit
.topt(realpatternbbox
.width())
315 xstep
= unit
.topt(self
.xstep
)
316 if self
.ystep
is None:
317 ystep
= unit
.topt(realpatternbbox
.height())
319 ystep
= unit
.topt(self
.ystep
)
321 raise ValueError("xstep in pattern cannot be zero")
323 raise ValueError("ystep in pattern cannot be zero")
324 patternbbox
= self
.patternbbox
or realpatternbbox
.enlarged(5*unit
.pt
)
326 patternprefix
= "\n".join(("<<",
328 "/PaintType %d" % self
.painttype
,
329 "/TilingType %d" % self
.tilingtype
,
330 "/BBox[%s]" % str(patternbbox
),
333 "/PaintProc {\nbegin\n"))
334 stringfile
= cStringIO
.StringIO()
335 _canvas
.outputPS(self
, stringfile
)
336 patternproc
= stringfile
.getvalue()
338 patterntrafostring
= self
.patterntrafo
is None and "matrix" or str(self
.patterntrafo
)
339 patternsuffix
= "end\n} bind\n>>\n%s\nmakepattern" % patterntrafostring
341 pr
= _canvas
.prolog(self
)
342 pr
.append(prolog
.definition(self
.id, "".join((patternprefix
, patternproc
, patternsuffix
))))
345 pattern
.clear
= attr
.clearclass(pattern
)
349 def calctrafo(abbox
, paperformat
, margin
, rotated
, fittosize
):
350 """ calculate a trafo which rotates and fits a canvas with
351 bounding box abbox on the given paperformat with a margin on all
353 if not isinstance(margin
, unit
.length
):
354 margin
= unit
.length(margin
)
355 atrafo
= None # global transformation of canvas
358 atrafo
= trafo
.rotate(90, *abbox
.center())
361 # center (optionally rotated) output on page
363 paperwidth
, paperheight
= _paperformats
[paperformat
.capitalize()]
365 raise KeyError, "unknown paperformat '%s'" % paperformat
367 paperwidth
-= 2*margin
368 paperheight
-= 2*margin
370 if not atrafo
: atrafo
= trafo
.trafo()
372 atrafo
= atrafo
.translated(margin
+ 0.5*(paperwidth
- abbox
.width()) - abbox
.left(),
373 margin
+ 0.5*(paperheight
- abbox
.height()) - abbox
.bottom())
376 # scale output to pagesize - margins
377 if 2*margin
> min(paperwidth
, paperheight
):
378 raise RuntimeError("Margins too broad for selected paperformat. Aborting.")
381 sfactor
= min(unit
.topt(paperheight
)/unit
.topt(abbox
.width()),
382 unit
.topt(paperwidth
)/unit
.topt(abbox
.height()))
384 sfactor
= min(unit
.topt(paperwidth
)/unit
.topt(abbox
.width()),
385 unit
.topt(paperheight
)/unit
.topt(abbox
.height()))
387 atrafo
= atrafo
.scaled(sfactor
, sfactor
, margin
+ 0.5*paperwidth
, margin
+ 0.5*paperheight
)
389 raise ValueError("must specify paper size for fittosize")
394 # The main canvas class
397 class canvas(_canvas
):
399 """a canvas holds a collection of canvasitems"""
401 def writeEPSfile(self
, filename
, paperformat
=None, rotated
=0, fittosize
=0, margin
=1 * unit
.t_cm
,
402 bbox
=None, bboxenlarge
=1 * unit
.t_pt
):
403 """write canvas to EPS file
405 If paperformat is set to a known paperformat, the output will be centered on
408 If rotated is set, the output will first be rotated by 90 degrees.
410 If fittosize is set, then the output is scaled to the size of the
411 page (minus margin). In that case, the paperformat the specification
412 of the paperformat is obligatory.
414 The bbox parameter overrides the automatic bounding box determination.
415 bboxenlarge may be used to enlarge the bbox of the canvas (or the
416 manually specified bbox).
419 if filename
[-4:]!=".eps":
420 filename
= filename
+ ".eps"
423 file = open(filename
, "w")
425 raise IOError("cannot open output file")
427 abbox
= bbox
is not None and bbox
or self
.bbox()
428 abbox
.enlarge(bboxenlarge
)
429 ctrafo
= calctrafo(abbox
, paperformat
, margin
, rotated
, fittosize
)
431 # if there has been a global transformation, adjust the bounding box
433 if ctrafo
: abbox
.transform(ctrafo
)
435 file.write("%!PS-Adobe-3.0 EPSF 3.0\n")
437 file.write("%%%%Creator: PyX %s\n" % version
.version
)
438 file.write("%%%%Title: %s\n" % filename
)
439 file.write("%%%%CreationDate: %s\n" %
440 time
.asctime(time
.localtime(time
.time())))
441 file.write("%%EndComments\n")
443 file.write("%%BeginProlog\n")
447 for pritem
in self
.prolog():
448 for mpritem
in mergedprolog
:
449 if mpritem
.merge(pritem
) is None: break
451 mergedprolog
.append(pritem
)
453 for pritem
in mergedprolog
:
454 pritem
.outputPS(file)
456 file.write("%%EndProlog\n")
458 # apply a possible global transformation
459 if ctrafo
: ctrafo
.outputPS(file)
461 file.write("%f setlinewidth\n" % unit
.topt(style
.linewidth
.normal
))
463 # here comes the actual content
466 file.write("showpage\n")
467 file.write("%%Trailer\n")
468 file.write("%%EOF\n")
470 def writePDFfile(self
, filename
, paperformat
=None, rotated
=0, fittosize
=0, margin
=1 * unit
.t_cm
,
471 bbox
=None, bboxenlarge
=1 * unit
.t_pt
):
472 sys
.stderr
.write("*** PyX Warning: writePDFfile is experimental and supports only a subset of PyX's features\n")
474 if filename
[-4:]!=".pdf":
475 filename
= filename
+ ".pdf"
478 file = open(filename
, "wb")
480 raise IOError("cannot open output file")
482 abbox
= bbox
is not None and bbox
or self
.bbox()
483 abbox
.enlarge(bboxenlarge
)
485 ctrafo
= calctrafo(abbox
, paperformat
, margin
, rotated
, fittosize
)
487 # if there has been a global transformation, adjust the bounding box
489 if ctrafo
: abbox
.transform(ctrafo
)
493 for pritem
in self
.prolog():
494 for mpritem
in mergedprolog
:
495 if mpritem
.merge(pritem
) is None: break
497 mergedprolog
.append(pritem
)
499 file.write("%%PDF-1.4\n%%%s%s%s%s\n" % (chr(195), chr(182), chr(195), chr(169)))
500 reflist
= [file.tell()]
501 file.write("1 0 obj\n"
507 reflist
.append(file.tell())
508 file.write("2 0 obj\n"
515 reflist
.append(file.tell())
516 file.write("3 0 obj\n"
521 abbox
.outputPDF(file)
522 file.write("/Contents 4 0 R\n"
527 if len([pritem
for pritem
in mergedprolog
if isinstance(pritem
, prolog
.fontdefinition
)]):
530 for pritem
in mergedprolog
:
531 if isinstance(pritem
, prolog
.fontdefinition
):
533 file.write("/%s %d 0 R\n" % (pritem
.font
.getpsname(), fontnr
+fontstartref
))
534 fontnr
+= 3 # further objects due to a font
540 reflist
.append(file.tell())
541 file.write("4 0 obj\n"
542 "<< /Length 5 0 R >>\n"
544 streamstartpos
= file.tell()
546 # apply a possible global transformation
547 if ctrafo
: ctrafo
.outputPDF(file)
548 style
.linewidth
.normal
.outputPDF(file)
551 streamendpos
= file.tell()
552 file.write("endstream\n"
554 reflist
.append(file.tell())
555 file.write("5 0 obj\n"
557 "endobj\n" % (streamendpos
- streamstartpos
))
560 for pritem
in mergedprolog
:
561 if isinstance(pritem
, prolog
.fontdefinition
):
563 reflist
.append(file.tell())
564 file.write("%d 0 obj\n"
573 "/FontDescriptor %d 0 R\n"
574 "/Encoding /StandardEncoding\n" # FIXME
576 "endobj\n" % (fontnr
+fontstartref
, pritem
.font
.getpsname(), pritem
.font
.getbasepsname(),
577 fontnr
+fontstartref
+1, fontnr
+fontstartref
+2))
579 reflist
.append(file.tell())
580 file.write("%d 0 obj\n"
581 "[\n" % (fontnr
+fontstartref
))
584 width
= pritem
.font
.getwidth_pt(i
)*1000/pritem
.font
.getsize_pt()
587 file.write("%f\n" % width
)
592 reflist
.append(file.tell())
593 file.write("%d 0 obj\n"
595 "/Type /FontDescriptor\n"
598 "/FontBBox [-10 -10 1000 1000]\n" # FIXME
599 "/ItalicAngle 0\n" # FIXME
600 "/Ascent 20\n" # FIXME
601 "/Descent -5\n" # FIXME
602 "/CapHeight 15\n" # FIXME
604 "/FontFile %d 0 R\n" # FIXME
605 # "/CharSet \n" # fill in when stripping
607 "endobj\n" % (fontnr
+fontstartref
, pritem
.font
.getbasepsname(),
608 fontnr
+fontstartref
+1))
611 reflist
.append(file.tell())
613 fontdata
= open(pykpathsea
.find_file(pritem
.filename
, pykpathsea
.kpse_type1_format
)).read()
614 if fontdata
[0:2] != fullfont
._PFB
_ASCII
:
615 raise RuntimeError("PFB_ASCII mark expected")
616 length1
= fullfont
.pfblength(fontdata
[2:6])
617 if fontdata
[6+length1
:8+length1
] != fullfont
._PFB
_BIN
:
618 raise RuntimeError("PFB_BIN mark expected")
619 length2
= fullfont
.pfblength(fontdata
[8+length1
:12+length1
])
620 if fontdata
[12+length1
+length2
:14+length1
+length2
] != fullfont
._PFB
_ASCII
:
621 raise RuntimeError("PFB_ASCII mark expected")
622 length3
= fullfont
.pfblength(fontdata
[14+length1
+length2
:18+length1
+length2
])
623 if fontdata
[18+length1
+length2
+length3
:20+length1
+length2
+length3
] != fullfont
._PFB
_DONE
:
624 raise RuntimeError("PFB_DONE mark expected")
625 if len(fontdata
) != 20 + length1
+ length2
+ length3
:
626 raise RuntimeError("end of pfb file expected")
628 # we might be allowed to skip the third part ...
629 if fontdata
[18+length1
+length2
:18+length1
+length2
+length3
].replace("\n", "").replace("\r", "").replace("\t", "").replace(" ", "") == "0"*512 + "cleartomark":
632 uncompresseddata
= fontdata
[6:6+length1
] + fontdata
[12+length1
:12+length1
+length2
] + fontdata
[18+length1
+length2
:18+length1
+length2
+length3
]
633 compresseddata
= zlib
.compress(uncompresseddata
)
635 file.write("%d 0 obj\n"
641 "/Filter /FlateDecode\n"
643 "stream\n" % (fontnr
+fontstartref
, len(compresseddata
),
647 #file.write(fontdata[6:6+length1])
648 #file.write(fontdata[12+length1:12+length1+length2])
649 #file.write(fontdata[18+length1+length2:18+length1+length2+length3])
650 file.write(compresseddata
)
651 file.write("endstream\n"
656 xrefpos
= file.tell()
658 "0 %d\n" % (len(reflist
)+1))
659 file.write("0000000000 65535 f \n")
661 file.write("%010i 00000 n \n" % ref
)
662 file.write("trailer\n"
669 "%%%%EOF\n" % xrefpos
)
671 def writePDFfile_new(self
, filename
, paperformat
=None, rotated
=0, fittosize
=0, margin
=1 * unit
.t_cm
,
672 bbox
=None, bboxenlarge
=1 * unit
.t_pt
):
673 sys
.stderr
.write("*** PyX Warning: writePDFfile is experimental and supports only a subset of PyX's features\n")
675 if filename
[-4:]!=".pdf":
676 filename
= filename
+ ".pdf"
679 writer
= pdfwriter
.pdfwriter(filename
)
681 raise IOError("cannot open output file")
683 abbox
= bbox
is not None and bbox
or self
.bbox()
684 abbox
.enlarge(bboxenlarge
)
686 ctrafo
= calctrafo(abbox
, paperformat
, margin
, rotated
, fittosize
)
688 # if there has been a global transformation, adjust the bounding box
690 if ctrafo
: abbox
.transform(ctrafo
)
694 for pritem
in self
.prolog():
695 for mpritem
in mergedprolog
:
696 if mpritem
.merge(pritem
) is None: break
698 mergedprolog
.append(pritem
)
699 writer
.page(abbox
, self
, mergedprolog
, ctrafo
)
702 def writetofile(self
, filename
, *args
, **kwargs
):
703 if filename
[-4:] == ".eps":
704 self
.writeEPSfile(filename
, *args
, **kwargs
)
705 elif filename
[-4:] == ".pdf":
706 self
.writePDFfile(filename
, *args
, **kwargs
)
708 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")
709 self
.writeEPSfile(filename
, *args
, **kwargs
)
713 def __init__(self
, attrs
=[], texrunner
=None, pagename
=None, paperformat
="a4", rotated
=0, fittosize
=0,
714 margin
=1 * unit
.t_cm
, bboxenlarge
=1 * unit
.t_pt
):
715 canvas
.__init
__(self
, attrs
, texrunner
)
716 self
.pagename
= pagename
717 self
.paperformat
= paperformat
.capitalize()
718 self
.rotated
= rotated
719 self
.fittosize
= fittosize
721 self
.bboxenlarge
= bboxenlarge
724 # the bounding box of a page is fixed by its format and an optional rotation
725 pbbox
= bbox
.bbox(0, 0, *_paperformats
[self
.paperformat
])
726 pbbox
.enlarge(self
.bboxenlarge
)
728 pbbox
.transform(trafo
.rotate(90, *pbbox
.center()))
731 def outputPS(self
, file):
732 file.write("%%%%PageMedia: %s\n" % self
.paperformat
)
733 file.write("%%%%PageOrientation: %s\n" % (self
.rotated
and "Landscape" or "Portrait"))
734 # file.write("%%%%PageBoundingBox: %d %d %d %d\n" % (math.floor(pbbox.llx_pt), math.floor(pbbox.lly_pt),
735 # math.ceil(pbbox.urx_pt), math.ceil(pbbox.ury_pt)))
738 file.write("%%BeginPageSetup\n")
739 file.write("/pgsave save def\n")
740 # for scaling, we need the real bounding box of the page contents
741 pbbox
= canvas
.bbox(self
)
742 pbbox
.enlarge(self
.bboxenlarge
)
743 ptrafo
= calctrafo(pbbox
, self
.paperformat
, self
.margin
, self
.rotated
, self
.fittosize
)
745 ptrafo
.outputPS(file)
746 file.write("%f setlinewidth\n" % unit
.topt(style
.linewidth
.normal
))
747 file.write("%%EndPageSetup\n")
749 # here comes the actual content
750 canvas
.outputPS(self
, file)
751 file.write("pgsave restore\n")
752 file.write("showpage\n")
753 # file.write("%%PageTrailer\n")
758 """holds a collection of page instances which are output as pages of a document"""
760 def __init__(self
, pages
=[]):
763 def append(self
, page
):
764 self
.pages
.append(page
)
766 def writePSfile(self
, filename
):
767 """write pages to PS file """
769 if filename
[-3:]!=".ps":
770 filename
= filename
+ ".ps"
773 file = open(filename
, "w")
775 raise IOError("cannot open output file")
778 for apage
in self
.pages
:
786 file.write("%!PS-Adobe-3.0\n")
787 docbbox
.outputPS(file)
788 file.write("%%%%Creator: PyX %s\n" % version
.version
)
789 file.write("%%%%Title: %s\n" % filename
)
790 file.write("%%%%CreationDate: %s\n" %
791 time
.asctime(time
.localtime(time
.time())))
792 # required paper formats
794 for apage
in self
.pages
:
795 if isinstance(apage
, page
):
796 paperformats
[apage
.paperformat
] = _paperformats
[apage
.paperformat
]
798 for paperformat
, size
in paperformats
.items():
800 file.write("%%DocumentMedia: ")
804 file.write("%s %d %d 75 white ()\n" % (paperformat
, unit
.topt(size
[0]), unit
.topt(size
[1])))
806 file.write("%%%%Pages: %d\n" % len(self
.pages
))
807 file.write("%%PageOrder: Ascend\n")
808 file.write("%%EndComments\n")
810 # document default section
811 #file.write("%%BeginDefaults\n")
813 # file.write("%%%%PageMedia: %s\n" % paperformat)
814 #file.write("%%%%PageOrientation: %s\n" % (rotated and "Landscape" or "Portrait"))
815 #file.write("%%EndDefaults\n")
817 # document prolog section
818 file.write("%%BeginProlog\n")
820 for apage
in self
.pages
:
821 for pritem
in apage
.prolog():
822 for mpritem
in mergedprolog
:
823 if mpritem
.merge(pritem
) is None: break
825 mergedprolog
.append(pritem
)
826 for pritem
in mergedprolog
:
827 pritem
.outputPS(file)
828 file.write("%%EndProlog\n")
830 # document setup section
831 #file.write("%%BeginSetup\n")
832 #file.write("%%EndSetup\n")
835 for nr
, apage
in enumerate(self
.pages
):
836 file.write("%%%%Page: %s %d\n" % (apage
.pagename
is None and str(nr
) or apage
.pagename
, nr
+1))
839 file.write("%%Trailer\n")
840 file.write("%%EOF\n")