add svg option to writetofile
[PyX.git] / pyx / document.py
blobbc91c41408822ca1be0b6c59754549f6df35fc77
1 # -*- encoding: utf-8 -*-
4 # Copyright (C) 2005-2011 Jörg Lehmann <joergl@users.sourceforge.net>
5 # Copyright (C) 2005-2011 André Wobst <wobsta@users.sourceforge.net>
7 # This file is part of PyX (http://pyx.sourceforge.net/).
9 # PyX is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2 of the License, or
12 # (at your option) any later version.
14 # PyX is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with PyX; if not, write to the Free Software
21 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 import logging, sys
24 from . import bbox, pswriter, pdfwriter, svgwriter, trafo, style, unit
26 logger = logging.getLogger("pyx")
28 class paperformat:
30 def __init__(self, width, height, name=None):
31 self.width = width
32 self.height = height
33 self.name = name
35 paperformat.A5 = paperformat(148.5 * unit.t_mm, 210 * unit.t_mm, "A5")
36 paperformat.A4 = paperformat(210 * unit.t_mm, 297 * unit.t_mm, "A4")
37 paperformat.A3 = paperformat(297 * unit.t_mm, 420 * unit.t_mm, "A3")
38 paperformat.A2 = paperformat(420 * unit.t_mm, 594 * unit.t_mm, "A2")
39 paperformat.A1 = paperformat(594 * unit.t_mm, 840 * unit.t_mm, "A1")
40 paperformat.A0 = paperformat(840 * unit.t_mm, 1188 * unit.t_mm, "A0")
41 paperformat.A0b = paperformat(910 * unit.t_mm, 1370 * unit.t_mm, None) # dedicated to our friends in Augsburg
42 paperformat.Letter = paperformat(8.5 * unit.t_inch, 11 * unit.t_inch, "Letter")
43 paperformat.Legal = paperformat(8.5 * unit.t_inch, 14 * unit.t_inch, "Legal")
45 def _paperformatfromstring(name):
46 return getattr(paperformat, name.capitalize())
49 class page:
51 def __init__(self, canvas, pagename=None, paperformat=None, rotated=0, centered=1, fittosize=0,
52 margin=1*unit.t_cm, bboxenlarge=1*unit.t_pt, bbox=None):
53 self.canvas = canvas
54 self.pagename = pagename
55 # support for deprecated string specification of paper formats
56 try:
57 paperformat + ""
58 except:
59 self.paperformat = paperformat
60 else:
61 self.paperformat = _paperformatfromstring(paperformat)
62 logger.warning("specification of paperformat by string is deprecated, use document.paperformat.%s instead" % paperformat.capitalize())
64 self.rotated = rotated
65 self.centered = centered
66 self.fittosize = fittosize
67 self.margin = margin
68 self.bboxenlarge = bboxenlarge
69 self.pagebbox = bbox
71 def _process(self, processMethod, contentfile, writer, context, registry, bbox):
72 # usually, it is the bbox of the canvas enlarged by self.bboxenlarge, but
73 # it might be a different bbox as specified in the page constructor
74 assert not bbox
75 if self.pagebbox:
76 bbox.set(self.pagebbox)
77 else:
78 bbox.set(self.canvas.bbox()) # this bbox is not accurate
79 bbox.enlarge(self.bboxenlarge)
81 # check whether we expect a page trafo and use a temporary canvas to insert the
82 # page canvas
83 if self.paperformat and (self.rotated or self.centered or self.fittosize) and bbox:
84 # calculate the pagetrafo
85 paperwidth, paperheight = self.paperformat.width, self.paperformat.height
87 # center (optionally rotated) output on page
88 if self.rotated:
89 pagetrafo = trafo.rotate(90).translated(paperwidth, 0)
90 if self.centered or self.fittosize:
91 if not self.fittosize and (bbox.height() > paperwidth or bbox.width() > paperheight):
92 logger.warning("content exceeds the papersize")
93 pagetrafo = pagetrafo.translated(-0.5*(paperwidth - bbox.height()) + bbox.bottom(),
94 0.5*(paperheight - bbox.width()) - bbox.left())
95 else:
96 if not self.fittosize and (bbox.width() > paperwidth or bbox.height() > paperheight):
97 logger.warning("content exceeds the papersize")
98 pagetrafo = trafo.translate(0.5*(paperwidth - bbox.width()) - bbox.left(),
99 0.5*(paperheight - bbox.height()) - bbox.bottom())
101 if self.fittosize:
103 if 2*self.margin > paperwidth or 2*self.margin > paperheight:
104 raise ValueError("Margins too broad for selected paperformat. Aborting.")
106 paperwidth -= 2 * self.margin
107 paperheight -= 2 * self.margin
109 # scale output to pagesize - margins
110 if self.rotated:
111 sfactor = min(unit.topt(paperheight)/bbox.width_pt(), unit.topt(paperwidth)/bbox.height_pt())
112 else:
113 sfactor = min(unit.topt(paperwidth)/bbox.width_pt(), unit.topt(paperheight)/bbox.height_pt())
115 pagetrafo = pagetrafo.scaled(sfactor, sfactor, self.margin + 0.5*paperwidth, self.margin + 0.5*paperheight)
117 bbox.transform(pagetrafo)
118 from . import canvas as canvasmodule
119 cc = canvasmodule.canvas()
120 cc.insert(self.canvas, [pagetrafo])
121 else:
122 cc = self.canvas
124 if processMethod != "processSVG":
125 # for SVG we write the pyx defaults as part of the svg node attributes in the writer
126 getattr(style.linewidth.normal, processMethod)(contentfile, writer, context, registry)
127 if self.pagebbox:
128 bbox = bbox.copy() # don't alter the bbox provided to the constructor -> use a copy
129 getattr(cc, processMethod)(contentfile, writer, context, registry, bbox)
131 def processPS(self, *args):
132 self._process("processPS", *args)
134 def processPDF(self, *args):
135 self._process("processPDF", *args)
137 def processSVG(self, *args):
138 self._process("processSVG", *args)
141 class _noclose:
143 def __init__(self, f):
144 self.f = f
146 def __enter__(self):
147 return self.f
149 def __exit__(self, type, value, tb):
150 pass
153 def _outputstream(file, suffix):
154 if file is None:
155 if not sys.argv[0].endswith(".py"):
156 raise RuntimeError("could not auto-guess filename")
157 return open("%s.%s" % (sys.argv[0][:-3], suffix), "wb")
158 if file == "-":
159 return _noclose(sys.stdout.buffer)
160 try:
161 file.write(b"")
162 except:
163 if not file.endswith(".%s" % suffix):
164 return open("%s.%s" % (file, suffix), "wb")
165 return open(file, "wb")
166 else:
167 return _noclose(file)
170 class document:
172 """holds a collection of page instances which are output as pages of a document"""
174 def __init__(self, pages=None):
175 if pages is None:
176 self.pages = []
177 else:
178 self.pages = pages
180 def append(self, page):
181 self.pages.append(page)
183 def writeEPSfile(self, file=None, **kwargs):
184 with _outputstream(file, "eps") as f:
185 pswriter.EPSwriter(self, f, **kwargs)
187 def writePSfile(self, file=None, **kwargs):
188 with _outputstream(file, "ps") as f:
189 pswriter.PSwriter(self, f, **kwargs)
191 def writePDFfile(self, file=None, **kwargs):
192 with _outputstream(file, "pdf") as f:
193 pdfwriter.PDFwriter(self, f, **kwargs)
195 def writeSVGfile(self, file=None, **kwargs):
196 with _outputstream(file, "svg") as f:
197 svgwriter.SVGwriter(self, f, **kwargs)
199 def writetofile(self, filename, **kwargs):
200 for suffix, method in [("eps", pswriter.EPSwriter),
201 ("ps", pswriter.PSwriter),
202 ("pdf", pdfwriter.PDFwriter),
203 ("svg", svgwriter.SVGwriter)]:
204 if filename.endswith(".{}".format(suffix)):
205 with open(filename, "wb") as f:
206 method(self, f, **kwargs)
207 return
208 raise ValueError("unknown file extension")