def __init__(self, surface, *al, **ad): self._renderer = self._dwg = surface self._bounds = Extents() self._padding = PADDING self._stack = [] self._m = Affine.translation(0, 0) self._xy = (0, 0) self._mxy = self._m * self._xy self._lw = 0 self._rgb = (0, 0, 0) self._ff = "sans-serif" self._fs = 10 self._last_path = None
def _adjust_coordinates(self): extents = self.extents() extents.xmin -= PADDING extents.ymin -= PADDING extents.xmax += PADDING extents.ymax += PADDING m = Affine.translation(-extents.xmin, -extents.ymin) if self.invert_y: m = Affine.scale(self.scale, -self.scale) * m m = Affine.translation(0, self.scale*extents.height) * m else: m = Affine.scale(self.scale, self.scale) * m self.transform(m, self.invert_y) return Extents(0, 0, extents.width * self.scale, extents.height * self.scale)
def extents(self): e = Extents() for p in self.path: e.add(*p[1:3]) if p[0] == 'T': m, text, params = p[3:] h = params['fs'] l = len(text) * h * 0.7 align = params.get('align', 'left') start, end = { 'left': (0, 1), 'middle': (-0.5, 0.5), 'end': (-1, 0), }[align] for x in (start * l, end * l): for y in (0, h): x_, y_ = m * (x, y) e.add(x_, y_) return e
def extents(self): if not self.parts: return Extents() return sum([p.extents() for p in self.parts])
class Context: def __init__(self, surface, *al, **ad): self._renderer = self._dwg = surface self._bounds = Extents() self._padding = PADDING self._stack = [] self._m = Affine.translation(0, 0) self._xy = (0, 0) self._mxy = self._m * self._xy self._lw = 0 self._rgb = (0, 0, 0) self._ff = "sans-serif" self._fs = 10 self._last_path = None def _update_bounds_(self, mx, my): self._bounds.update(mx, my) def save(self): self._stack.append((self._m, self._xy, self._lw, self._rgb, self._mxy, self._last_path)) self._xy = (0, 0) def restore(self): ( self._m, self._xy, self._lw, self._rgb, self._mxy, self._last_path, ) = self._stack.pop() ## transformations def translate(self, x, y): self._m *= Affine.translation(x, y) self._xy = (0, 0) def scale(self, sx, sy): self._m *= Affine.scale(sx, sy) def rotate(self, r): self._m *= Affine.rotation(180 * r / math.pi) def set_line_width(self, lw): self._lw = lw def set_source_rgb(self, r, g, b): self._rgb = (r, g, b) ## path methods def _line_to(self, x, y): self._add_move() x1, y1 = self._mxy self._xy = x, y x2, y2 = self._mxy = self._m * self._xy if not points_equal(x1, y1, x2, y2): self._dwg.append("L", x2, y2) def _add_move(self): self._dwg.move_to(*self._mxy) def move_to(self, x, y): self._xy = (x, y) self._mxy = self._m * self._xy def line_to(self, x, y): self._line_to(x, y) def _arc(self, xc, yc, radius, angle1, angle2, direction): if abs(angle1 - angle2) < EPS or radius < EPS: return x1, y1 = radius * math.cos(angle1) + xc, radius * math.sin(angle1) + yc x4, y4 = radius * math.cos(angle2) + xc, radius * math.sin(angle2) + yc # XXX direction seems not needed for small arcs ax = x1 - xc ay = y1 - yc bx = x4 - xc by = y4 - yc q1 = ax * ax + ay * ay q2 = q1 + ax * bx + ay * by k2 = 4 / 3 * ((2 * q1 * q2)**0.5 - q2) / (ax * by - ay * bx) x2 = xc + ax - k2 * ay y2 = yc + ay + k2 * ax x3 = xc + bx + k2 * by y3 = yc + by - k2 * bx mx1, my1 = self._m * (x1, y1) mx2, my2 = self._m * (x2, y2) mx3, my3 = self._m * (x3, y3) mx4, my4 = self._m * (x4, y4) mxc, myc = self._m * (xc, yc) self._add_move() self._dwg.append("C", mx4, my4, mx2, my2, mx3, my3) self._xy = (x4, y4) self._mxy = (mx4, my4) def arc(self, xc, yc, radius, angle1, angle2): self._arc(xc, yc, radius, angle1, angle2, 1) def arc_negative(self, xc, yc, radius, angle1, angle2): self._arc(xc, yc, radius, angle1, angle2, -1) def curve_to(self, x1, y1, x2, y2, x3, y3): # mx0,my0 = self._m*self._xy mx1, my1 = self._m * (x1, y1) mx2, my2 = self._m * (x2, y2) mx3, my3 = self._m * (x3, y3) self._add_move() self._dwg.append("C", mx3, my3, mx1, my1, mx2, my2) # destination first! self._xy = (x3, y3) def stroke(self): # print('stroke stack-level=',len(self._stack),'lastpath=',self._last_path,) self._last_path = self._dwg.stroke(rgb=self._rgb, lw=self._lw) self._xy = (0, 0) def fill(self): self._xy = (0, 0) raise NotImplementedError() def set_font(self, style, bold=False, italic=False): if style not in ("serif", "sans-serif", "monospaced"): raise ValueError("Unknown font style") self._ff = (style, bold, italic) def set_font_size(self, fs): self._fs = fs def show_text(self, text, **args): params = { "ff": self._ff, "fs": self._fs, "lw": self._lw, "rgb": self._rgb } params.update(args) mx0, my0 = self._m * self._xy m = self._m self._dwg.append("T", mx0, my0, m, text, params) def text_extents(self, text): fs = self._fs # XXX ugly hack! Fix Boxes.text() ! return (0, 0, 0.6 * fs * len(text), 0.65 * fs, fs * 0.1, 0) def rectangle(self, x, y, width, height): # todo: better check for empty path? self.stroke() self.move_to(x, y) self.line_to(x + width, y) self.line_to(x + width, y + height) self.line_to(x, y + height) self.line_to(x, y) self.stroke() def get_current_point(self): return self._xy def flush(self): pass # todo: check, if needed # self.stroke() ## additional methods def new_part(self): self._dwg.new_part()