Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- __author__ = 'Gary Williams, Grin Technologies'
- '''
- The PDF class below adds convenience functions and enumerations to Reportlab's Canvas class.
- '''
- import colorsys
- import math
- from enum import Enum # https://pypi.python.org/pypi/enum34
- from reportlab.graphics.charts.textlabels import _text2Path
- from reportlab.pdfgen.canvas import Canvas
- from reportlab.pdfbase import pdfmetrics
- from reportlab.pdfbase.ttfonts import TTFont
- import reportlab.lib.colors
- import reportlab.lib.pagesizes
- import reportlab.lib.units
- colors = reportlab.lib.colors
- page_sizes = reportlab.lib.pagesizes
- units = reportlab.lib.units
- class HSVColor(colors.Color):
- def __init__(self, h, s, v, a = 1):
- r, g, b = colorsys.hsv_to_rgb(h, s, v)
- colors.Color.__init__(self, r, g, b, a)
- # self.luminance = 0.299 * r + 0.587 * g + 0.114 * b
- self.luminance = math.sqrt(0.299 * r**2 + 0.587 * g**2 + 0.114 * b**2)
- class PageOrientation(Enum):
- Portrait = 0
- Landscape = 1
- class Direction(Enum):
- Right = 0
- Up = 90
- Left = 180
- Down = 270
- PageOrientation.Portrait.orient = page_sizes.portrait
- PageOrientation.Landscape.orient = page_sizes.landscape
- class PDF(Canvas):
- # shared class-level object
- font_metrics = {}
- x = 0
- y = 0
- def __init__(self, filename, page_orientation = PageOrientation.Portrait, page_size = page_sizes.letter):
- Canvas.__init__(self, filename = filename, pagesize = page_orientation.orient(page_size))
- def register_truetype_font(self, friendlyname, filename):
- pdfmetrics.registerFont(TTFont(friendlyname, filename))
- def get_page_width(self):
- return self._pagesize[0]
- def get_page_height(self):
- return self._pagesize[1]
- def get_font_size(self):
- return self._fontsize
- def set_font_size(self, value):
- self.setFont(self.get_font_name(), value)
- def get_font_name(self):
- return self._fontname
- def set_font_name(self, value):
- self.setFont(value, self.get_font_size())
- def get_text_bounds(self, text):
- # this function is based on a calculation found in source code of unknown provenance found at
- # http://reportlab-users.reportlab.narkive.com/20CtH5lY/font-size-baseline_height-and-typography-stuff
- return _text2Path(text, fontName = self.get_font_name(), fontSize = self.get_font_size()).getBounds()
- '''
- key = (self.get_font_name(), VAlign.ascender)
- if key not in self.font_metrics:
- self.font_metrics[key] =
- return self.get_font_size() * self.font_metrics[key]
- '''
- def get_font_ascender_height(self):
- key = (self.get_font_name(), VAlign.ascender)
- if key not in self.font_metrics:
- self.font_metrics[key] = pdfmetrics.getAscent(self.get_font_name()) / 1000.0
- return self.get_font_size() * self.font_metrics[key]
- def get_font_arrow_center_height(self):
- key = (self.get_font_name(), VAlign.arrow_center)
- if key not in self.font_metrics:
- x0, y0, x1, y1 = self.get_text_bounds('→')
- self.font_metrics[key] = 0.5 * (y0 + y1) / self.get_font_size()
- return self.get_font_size() * self.font_metrics[key]
- def get_font_x_height(self):
- key = (self.get_font_name(), VAlign.x_height)
- if key not in self.font_metrics:
- x0, y0, x1, y1 = self.get_text_bounds('x')
- self.font_metrics[key] = float(y1) / self.get_font_size()
- return self.get_font_size() * self.font_metrics[key]
- def get_font_cap_height(self):
- key = (self.get_font_name(), VAlign.cap)
- if key not in self.font_metrics:
- x0, y0, x1, y1 = self.get_text_bounds('H')
- self.font_metrics[key] = float(y1) / self.get_font_size()
- return self.get_font_size() * self.font_metrics[key]
- def get_font_half_cap_height(self):
- return 0.5 * self.get_font_cap_height()
- def get_font_baseline_height(self):
- return 0
- def get_font_descender_height(self):
- key = (self.get_font_name(), VAlign.descender)
- if key not in self.font_metrics:
- self.font_metrics[key] = pdfmetrics.getDescent(self.get_font_name()) / 1000.0
- return self.get_font_size() * self.font_metrics[key]
- def get_font_leading(self):
- return self._leading
- # draws an optionally-filled rectangle while preserving existing line width, stroke color, and fill color
- # TODO rename this to something more intuitive
- def erase(self, x, y, width, height, fill_color = None, border_color = None, border_width = None):
- self.saveState()
- try:
- if border_width is not None:
- self.setLineWidth(border_width)
- if border_color is not None:
- self.setStrokeColor(border_color)
- if fill_color is not None:
- self.setFillColor(fill_color)
- self.rect(x, y, width, height, fill = fill_color is not None, stroke = border_color is not None)
- finally:
- self.restoreState()
- def draw_crosshairs(self, x, y):
- # plot crosshairs to check text positioning
- size = 5 # points
- self.saveState()
- try:
- self.translate(x, y)
- self.setLineWidth(0)
- self.line(-size, 0, size, 0)
- self.line(0, -size, 0, size)
- finally:
- self.restoreState()
- def get_text_width(self, text):
- assert isinstance(text, basestring)
- return self.stringWidth(text, self.get_font_name(), self.get_font_size())
- def draw_dot(self, x, y):
- self.circle(x, y, 0.1)
- def draw_text(self, x, y, text, halign, valign, angle = 0, outline = False, border = False):
- assert isinstance(text, basestring)
- x0, y0, x1, y1 = self.get_text_bounds(text)
- width = x1
- dx = -halign.dx * width
- dy = -valign.dy(self)
- if angle == 0 and not(outline):
- self.drawString(x + dx, y + dy, text)
- if border:
- self.rect(x + dx + x0, y + dy + y0, x1 - x0, y1 - y0)
- else:
- self.saveState()
- try:
- if outline:
- t = self.beginText()
- t.setTextRenderMode(2)
- self._code.append(t.getCode())
- self.translate(x, y)
- self.rotate(angle)
- self.drawString(dx, dy, text)
- if border:
- self.rect(dx + x0, dy + y0, x1 - x0, y1 - y0)
- finally:
- self.restoreState()
- #self.draw_crosshairs(x, y)
- # TODO add optional max_width and wordwrap parameters
- def draw_text_block(self, x, y, lines, halign, valign2, fill_color = None, border_color = None, border_width = None):
- assert isinstance(lines, list)
- for line in lines:
- assert isinstance(line, basestring)
- width = max([self.get_text_width(line) for line in lines])
- dx = -halign.dx * width
- height = len(lines) * self._leading
- y += valign2.dy * height # y is now the top of the rectangle
- if (fill_color is not None) or (border_color is not None):
- self.erase(x + dx, y - height, width, height, fill_color, border_color, border_width)
- for line in lines:
- self.draw_text(x, y, line, halign, VAlign.ascender)
- y -= self._leading
- def draw_polyline(self, xlist, ylist):
- assert len(xlist) == len(ylist)
- path = self.beginPath()
- path.moveTo(xlist[0], ylist[0])
- for i in range(1, len(xlist)):
- path.lineTo(xlist[i], ylist[i])
- self.drawPath(path)
- def draw_arc(self, xc, yc, r, start_angle, extent):
- self.arc(xc - r, yc - r, xc + r, yc + r, start_angle, extent)
- # Turtle-like functions
- def set_xy(self, x, y):
- self.x = x
- self.y = y
- def set_dir(self, dir):
- self.dir = dir
- def forward(self, dist, mark = True):
- x2 = self.x + dist * math.cos(math.radians(self.dir))
- y2 = self.y + dist * math.sin(math.radians(self.dir))
- if mark:
- self.line(self.x, self.y, x2, y2)
- self.set_xy(x2, y2)
- def turn_left(self, angle = 90):
- self.set_dir(self.dir + angle)
- def turn_right(self, angle = 90):
- self.set_dir(self.dir - angle)
- def curve_left(self, radius, angle = 90):
- curve_right(radius, -angle)
- return
- def curve_right(self, radius, angle = 90):
- cx = self.x + radius * math.cos(math.radians(self.dir - 90))
- cy = self.y + radius * math.sin(math.radians(self.dir - 90))
- #self.draw_crosshairs(cx, cy)
- self.draw_arc(cx, cy, radius, self.dir + 90, -angle)
- end_angle = math.radians(self.dir - angle)
- self.set_xy(cx - radius * math.sin(end_angle),
- cy + radius * math.cos(end_angle))
- self.set_dir(self.dir - angle)
- class HAlign(Enum):
- left = 0
- center = 1
- right = 2
- HAlign.left.dx = 0
- HAlign.center.dx = 0.5
- HAlign.right.dx = 1
- class VAlign(Enum):
- ascender = 0
- cap = 1
- half_cap = 2
- x_height = 3
- baseline = 4
- descender = 5
- arrow_center = 6
- VAlign.ascender.dy = PDF.get_font_ascender_height
- VAlign.cap.dy = PDF.get_font_cap_height
- VAlign.half_cap.dy = PDF.get_font_half_cap_height
- VAlign.x_height.dy = PDF.get_font_x_height
- VAlign.baseline.dy = PDF.get_font_baseline_height
- VAlign.descender.dy = PDF.get_font_descender_height
- VAlign.arrow_center.dy = PDF.get_font_arrow_center_height
- class VAlign2(Enum):
- top = 0
- center = 1
- bottom = 2
- VAlign2.top.dy = 0
- VAlign2.center.dy = 0.5
- VAlign2.bottom.dy = 1
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement