take into account kerning and inter-character spacing in bounding box
[PyX.git] / pyx / style.py
blob3931b52abaccc0ccb6a54f8d359779fd342e998e
1 # -*- encoding: utf-8 -*-
4 # Copyright (C) 2002-2012 Jörg Lehmann <joergl@users.sourceforge.net>
5 # Copyright (C) 2003-2006 Michael Schindler <m-schindler@users.sourceforge.net>
6 # Copyright (C) 2002-2012 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 import math
25 from . import attr, unit
28 # base classes for stroke and fill styles
31 class style:
32 pass
34 class strokestyle(style):
35 pass
37 class fillstyle(style):
38 pass
41 # common stroke styles
45 class linecap(attr.exclusiveattr, strokestyle):
47 """linecap of paths"""
49 def __init__(self, value=0):
50 attr.exclusiveattr.__init__(self, linecap)
51 self.value = value
53 def processPS(self, file, writer, context, registry):
54 file.write("%d setlinecap\n" % self.value)
56 def processPDF(self, file, writer, context, registry):
57 file.write("%d J\n" % self.value)
59 linecap.butt = linecap(0)
60 linecap.round = linecap(1)
61 linecap.square = linecap(2)
62 linecap.clear = attr.clearclass(linecap)
65 class linejoin(attr.exclusiveattr, strokestyle):
67 """linejoin of paths"""
69 def __init__(self, value=0):
70 attr.exclusiveattr.__init__(self, linejoin)
71 self.value = value
73 def processPS(self, file, writer, context, registry):
74 file.write("%d setlinejoin\n" % self.value)
76 def processPDF(self, file, writer, context, registry):
77 file.write("%d j\n" % self.value)
79 linejoin.miter = linejoin(0)
80 linejoin.round = linejoin(1)
81 linejoin.bevel = linejoin(2)
82 linejoin.clear = attr.clearclass(linejoin)
85 class miterlimit(attr.exclusiveattr, strokestyle):
87 """miterlimit of paths"""
89 def __init__(self, value=10.0):
90 attr.exclusiveattr.__init__(self, miterlimit)
91 self.value = value
93 def processPS(self, file, writer, context, registry):
94 file.write("%f setmiterlimit\n" % self.value)
96 def processPDF(self, file, writer, context, registry):
97 file.write("%f M\n" % self.value)
99 miterlimit.lessthan180deg = miterlimit(1/math.sin(math.pi*180/360))
100 miterlimit.lessthan90deg = miterlimit(1/math.sin(math.pi*90/360))
101 miterlimit.lessthan60deg = miterlimit(1/math.sin(math.pi*60/360))
102 miterlimit.lessthan45deg = miterlimit(1/math.sin(math.pi*45/360))
103 miterlimit.lessthan11deg = miterlimit(10) # the default, approximately 11.4783 degress
104 miterlimit.clear = attr.clearclass(miterlimit)
107 _defaultlinewidth = 0.02 * unit.w_cm
108 _defaultlinewidth_pt = unit.topt(_defaultlinewidth)
110 class dash(attr.exclusiveattr, strokestyle):
112 """dash of paths"""
114 def __init__(self, pattern=[], offset=0, rellengths=1):
115 """set pattern with offset.
117 If rellengths is True, interpret all dash lengths relative to current linewidth.
119 attr.exclusiveattr.__init__(self, dash)
120 self.pattern = pattern
121 self.offset = offset
122 self.rellengths = rellengths
124 def processPS(self, file, writer, context, registry):
125 if self.rellengths:
126 patternstring = " ".join(["%f" % (element * context.linewidth_pt/_defaultlinewidth_pt) for element in self.pattern])
127 else:
128 patternstring = " ".join(["%f" % element for element in self.pattern])
129 file.write("[%s] %d setdash\n" % (patternstring, self.offset))
131 def processPDF(self, file, writer, context, registry):
132 if self.rellengths:
133 patternstring = " ".join(["%f" % (element * context.linewidth_pt/_defaultlinewidth_pt) for element in self.pattern])
134 else:
135 patternstring = " ".join(["%f" % element for element in self.pattern])
136 file.write("[%s] %d d\n" % (patternstring, self.offset))
138 dash.clear = attr.clearclass(dash)
141 class linestyle(attr.exclusiveattr, strokestyle):
143 """linestyle (linecap together with dash) of paths"""
145 def __init__(self, c=linecap.butt, d=dash([])):
146 # XXX better, but at the moment not supported by attr.exlusiveattr would be:
147 # XXX attr.exclusiveattr.__init__(self, [linestyle, linecap, dash])
148 attr.exclusiveattr.__init__(self, linestyle)
149 self.c = c
150 self.d = d
152 def processPS(self, file, writer, context, registry):
153 self.c.processPS(file, writer, context, registry)
154 self.d.processPS(file, writer, context, registry)
156 def processPDF(self, file, writer, context, registry):
157 self.c.processPDF(file, writer, context, registry)
158 self.d.processPDF(file, writer, context, registry)
160 linestyle.solid = linestyle(linecap.butt, dash([]))
161 linestyle.dashed = linestyle(linecap.butt, dash([2]))
162 linestyle.dotted = linestyle(linecap.round, dash([0, 2]))
163 linestyle.dashdotted = linestyle(linecap.round, dash([0, 2, 2, 2]))
164 linestyle.clear = attr.clearclass(linestyle)
167 class linewidth(attr.sortbeforeexclusiveattr, strokestyle):
169 """linewidth of paths"""
171 def __init__(self, width):
172 attr.sortbeforeexclusiveattr.__init__(self, linewidth, [dash, linestyle])
173 self.width = width
175 def processPS(self, file, writer, context, registry):
176 file.write("%f setlinewidth\n" % unit.topt(self.width))
177 context.linewidth_pt = unit.topt(self.width)
179 def processPDF(self, file, writer, context, registry):
180 file.write("%f w\n" % unit.topt(self.width))
181 context.linewidth_pt = unit.topt(self.width)
183 linewidth.THIN = linewidth(_defaultlinewidth/math.sqrt(32))
184 linewidth.THIn = linewidth(_defaultlinewidth/math.sqrt(16))
185 linewidth.THin = linewidth(_defaultlinewidth/math.sqrt(8))
186 linewidth.Thin = linewidth(_defaultlinewidth/math.sqrt(4))
187 linewidth.thin = linewidth(_defaultlinewidth/math.sqrt(2))
188 linewidth.normal = linewidth(_defaultlinewidth)
189 linewidth.thick = linewidth(_defaultlinewidth*math.sqrt(2))
190 linewidth.Thick = linewidth(_defaultlinewidth*math.sqrt(4))
191 linewidth.THick = linewidth(_defaultlinewidth*math.sqrt(8))
192 linewidth.THIck = linewidth(_defaultlinewidth*math.sqrt(16))
193 linewidth.THICk = linewidth(_defaultlinewidth*math.sqrt(32))
194 linewidth.THICK = linewidth(_defaultlinewidth*math.sqrt(64))
195 linewidth.clear = attr.clearclass(linewidth)
198 class fillrule(attr.exclusiveattr, fillstyle):
200 """defines the fill rule to be used"""
202 def __init__(self, even_odd):
203 attr.exclusiveattr.__init__(self, fillrule)
204 self.even_odd = even_odd
206 def processPS(self, file, writer, context, registry):
207 context.fillrule = self.even_odd
209 def processPDF(self, file, writer, context, registry):
210 context.fillrule = self.even_odd
212 fillrule.nonzero_winding = fillrule(0)
213 fillrule.even_odd = fillrule(1)
214 fillrule.clear = attr.clearclass(fillrule)