def draw_star(self, inner_length=1 * inch, outer_length=2 * inch, points=5, origin=None): canvas = self.canv canvas.setLineWidth(0) if not origin: canvas.translate(self.size * 0.5, self.size * 0.5) else: canvas.translate(*origin) canvas.setFillColor(self.fillcolor) canvas.setStrokeColor(self.strokecolor) p = canvas.beginPath() inner = False # Start on top is_origin = True #print 'Drawing star with radius',outer_length,'(moving origin ',origin,')' for theta in range(0, 360, 360 / (points * 2)): if inner: r = inner_length else: r = outer_length x = (math.sin(math.radians(theta)) * r) y = (math.cos(math.radians(theta)) * r) #print 'POINT:',x,y if is_origin: p.moveTo(x, y) is_origin = False else: p.lineTo(x, y) inner = not inner p.close() canvas.drawPath(p, fill=1)
def confidential(self, canvas): canvas.saveState() canvas.translate(18.5*cm, 27.4*cm) canvas.setLineWidth(3) canvas.setFillColorRGB(1, 0, 0) canvas.setStrokeGray(0.5) p = canvas.beginPath() p.moveTo(10, 0) p.lineTo(20, 10) p.lineTo(30, 0) p.lineTo(40, 10) p.lineTo(30, 20) p.lineTo(40, 30) p.lineTo(30, 40) p.lineTo(20, 30) p.lineTo(10, 40) p.lineTo(0, 30) p.lineTo(10, 20) p.lineTo(0, 10) canvas.drawPath(p, fill=1, stroke=0) canvas.restoreState()
def _draw_single_background(self, canvas, x, margins, width, height): canvas.saveState() clipping_mask = canvas.beginPath() clipping_mask.roundRect( x + margins[Border.LEFT], margins[Border.BOTTOM], width - margins[Border.RIGHT] - margins[Border.LEFT], height - margins[Border.TOP] - margins[Border.BOTTOM], self.BACKGROUND_CORNER_DIAMETER) canvas.clipPath(clipping_mask, stroke=0, fill=0) # get optimum background orientation background_orientation = best_orientation(self.background_image_path, width, height) if background_orientation == Orientation.TURN90: canvas.rotate(90) canvas.translate(0, -self.WIDTH * 2) canvas.drawImage(self.background_image_path, 0, 0, width=height, height=width, mask=None) else: canvas.drawImage(self.background_image_path, x, 0, width=width, height=height, mask=None) canvas.restoreState()
def draw_half_star (self, inner_length=1*inch, outer_length=2*inch, points=5, origin=None): canvas = self.canv canvas.setLineWidth(0) if not origin: canvas.translate(self.size*0.5,self.size*0.5) else: canvas.translate(*origin) canvas.setFillColor(self.fillcolor) canvas.setStrokeColor(self.strokecolor) p = canvas.beginPath() inner = False # Start on top is_origin = True #print 'Drawing star with radius',outer_length,'(moving origin ',origin,')' for theta in range(0,360,360/(points*2)): if 0 < theta < 180: continue if inner: r = inner_length else: r = outer_length x = (math.sin(math.radians(theta)) * r) y = (math.cos(math.radians(theta)) * r) #print 'POINT:',x,y if is_origin: p.moveTo(x,y) is_origin = False else: p.lineTo(x,y) inner = not inner p.close() canvas.drawPath(p,fill=1)
def confidential(self, canvas): canvas.saveState() canvas.translate(18.5 * cm, 27.4 * cm) canvas.setLineWidth(3) canvas.setFillColorRGB(1, 0, 0) canvas.setStrokeGray(0.5) p = canvas.beginPath() p.moveTo(10, 0) p.lineTo(20, 10) p.lineTo(30, 0) p.lineTo(40, 10) p.lineTo(30, 20) p.lineTo(40, 30) p.lineTo(30, 40) p.lineTo(20, 30) p.lineTo(10, 40) p.lineTo(0, 30) p.lineTo(10, 20) p.lineTo(0, 10) canvas.drawPath(p, fill=1, stroke=0) canvas.restoreState()
def _draw_front(self, canvas): canvas.saveState() # Set card orientation if self.front_orientation == Orientation.TURN90: canvas.rotate(90) canvas.translate(0, -self.WIDTH) width = self.HEIGHT height = self.WIDTH else: width = self.WIDTH height = self.HEIGHT # Draw red border self._draw_single_border(canvas, 0, width, height) # Parchment background self._draw_single_background(canvas, 0, self.BORDER_FRONT, width, height) # D&D logo dnd_logo = svg2rlg("logo.svg") if dnd_logo is not None: factor = self.LOGO_WIDTH/dnd_logo.width dnd_logo.width *= factor dnd_logo.height *= factor dnd_logo.scale(factor, factor) logo_margin = (self.BORDER_FRONT[Border.TOP]-dnd_logo.height)/2 renderPDF.draw(dnd_logo, canvas, (width-self.LOGO_WIDTH)/2, height - self.BORDER_FRONT[Border.TOP] + logo_margin, ) # Titles canvas.setFillColor("black") title_height = self.fonts.set_font(canvas, "title") canvas.drawCentredString(width * 0.5, self.FRONT_MARGINS[Border.BOTTOM], self.title.upper()) # Artist if self.artist: canvas.setFillColor("white") artist_font_height = self.fonts.set_font(canvas, "artist") canvas.drawCentredString(width/2, self.BORDER_FRONT[Border.BOTTOM] - artist_font_height - 1*mm, "Artist: {}".format(self.artist)) # Image image_bottom = self.FRONT_MARGINS[Border.BOTTOM] + title_height + 1*mm canvas.drawImage(self.front_image_path, self.FRONT_MARGINS[Border.LEFT], image_bottom, width=width \ - self.FRONT_MARGINS[Border.LEFT] \ - self.FRONT_MARGINS[Border.RIGHT], height=height \ - image_bottom \ - self.FRONT_MARGINS[Border.TOP], preserveAspectRatio=True, mask='auto') canvas.restoreState()
def draw_vertical_text(canvas, font_name, font_size, xto, ymid, text): tw = stringWidth(text, font_name, font_size); canvas.setFont(font_name, font_size); canvas.saveState(); canvas.translate(xto, ymid - tw/2); canvas.rotate(90); canvas.drawString(0, 0, text); canvas.restoreState();
def _draw_front(self, canvas): canvas.saveState() # Draw red border self._draw_single_border(canvas, 0, self.width, self.height) # Parchment background self._draw_single_background( canvas, 0, self.border_front, self.width, self.height, self.front_orientation, ) # Set card orientation if self.front_orientation == Orientation.TURN90: canvas.rotate(90) canvas.translate(0, -self.width) width = self.height height = self.width else: width = self.width height = self.height # D&D logo dnd_logo = svg2rlg(ASSET_DIR / "logo.svg") if dnd_logo is not None: factor = self.LOGO_WIDTH / dnd_logo.width dnd_logo.width *= factor dnd_logo.height *= factor dnd_logo.scale(factor, factor) logo_margin = (self.border_front[Border.TOP] - self.bleed - dnd_logo.height) / 2 renderPDF.draw( dnd_logo, canvas, (width - self.LOGO_WIDTH) / 2, height - self.border_front[Border.TOP] + logo_margin, ) self._draw_front_frame(canvas, width, height) # Artist if self.artist: canvas.setFillColor("white") artist_font_height = self.fonts.set_font(canvas, "artist") canvas.drawCentredString( width / 2, self.border_front[Border.BOTTOM] - artist_font_height - 1 * mm, "Artist: {}".format(self.artist), ) canvas.restoreState()
def draw_codebox(canvas, x, y, code): if 0: assert isinstance(canvas, reportlab.pdfgen.canvas.Canvas) size = defs.codebox_step * mm length = defs.codebox_length # 2 Bytes canvas.saveState() canvas.translate(x, y) canvas.rect(0, 0, defs.codebox_width * mm, defs.codebox_height * mm) for i in range(length): if code & (1 << i): canvas.rect((length - i - 1) * size, 0, size, defs.codebox_height * mm, stroke=1, fill=1) canvas.restoreState()
def draw_main_doc_header(canvas, pdf): """ Draws the invoice header """ canvas.saveState() canvas.translate(0, 29.7 * cm) canvas.setFont('Helvetica', 10) canvas.setStrokeColorRGB(0.9, 0.5, 0.2) canvas.setFillColorRGB(0.2, 0.2, 0.2) canvas.setFont('Helvetica', 16) canvas.drawString(18 * cm, -1 * cm, 'Invoice') canvas.drawInlineImage(INV_LOGO, 1 * cm, -1 * cm, 25, 25) canvas.setLineWidth(4) canvas.line(0, -1.25 * cm, 21.7 * cm, -1.25 * cm) canvas.restoreState()
def onMyFirstPage(self, canvas, doc): # If the left_footer attribute is not None, then add it to the page canvas.saveState() if self.left_footer is not None: # canvas.setFont('Helvetica', 8) # canvas.drawString(1 * cm, 1 * cm, self.left_footer) canvas.setFont("Times-Roman", 150) canvas.setStrokeColorRGB(0.74, 0.74, 0.74) canvas.setFillColorRGB(0.74, 0.74, 0.74) canvas.translate(A4[0] / 2, A4[1] / 2) canvas.rotate(45) canvas.drawCentredString(20, 0, self.left_footer) canvas.restoreState()
def scaletranslate(canvas): from reportlab.lib.units import inch canvas.setFont("Courier-BoldOblique", 12) # save the state canvas.saveState() # scale then translate canvas.scale(0.3, 0.5) canvas.translate(2.4*inch, 1.5*inch) canvas.drawString(0, 2.7*inch, "Scale then translate") coords(canvas) # forget the scale and translate... canvas.restoreState() # translate then scale canvas.translate(2.4*inch, 1.5*inch) canvas.scale(0.3, 0.5) canvas.drawString(0, 2.7*inch, "Translate then scale") coords(canvas)
def drawOn(self, canvas): canvas.saveState() canvas.setFont('Helvetica-Bold', 24) canvas.drawString(600, 100, 'A Custom Shape') canvas.translate(self.x, self.y) canvas.scale(self.scale, self.scale) self.drawBounds(canvas) self.drawCentre(canvas) self.drawTopLeft(canvas) self.drawBottomLeft(canvas) self.drawBottomRight(canvas) self.drawTopRight(canvas) canvas.restoreState()
def draw(self, pmlOp, pageNr, canvas, pe): # we need to adjust y position since PDF uses baseline of text as # the y pos, but pml uses top of the text as y pos. The Adobe # standard Courier family font metrics give 157 units in 1/1000 # point units as the Descender value, thus giving (1000 - 157) = # 843 units from baseline to top of text. # http://partners.adobe.com/asn/tech/type/ftechnotes.jsp contains # the "Font Metrics for PDF Core 14 Fonts" document. x = pe.x(pmlOp.x) y = pe.y(pmlOp.y) - 0.843 * pmlOp.size if pe.doc.tocs and pmlOp.toc: key = "id%d" % id(pmlOp) canvas.bookmarkPage(key, fit="XYZ", left=pe.x(pmlOp.x), top=pe.y(pmlOp.y)) canvas.addOutlineEntry(pmlOp.toc.text, key) newFont = (pe.getFontNr(pmlOp.flags), pmlOp.size) if newFont != pe.currentFont: canvas.setFont(*newFont) pe.currentFont = newFont if pmlOp.angle is None or pmlOp.angle == 0.0: canvas.drawString(x, y, pmlOp.text) else: canvas.saveState() canvas.translate(x, y) canvas.rotate(pmlOp.angle) canvas.drawString(0, 0, pmlOp.text) canvas.restoreState() if pmlOp.flags & pml.UNDERLINED: undLen = fontinfo.getMetrics(pmlOp.flags).getTextWidth( pmlOp.text, pmlOp.size) # all standard PDF fonts have the underline line 100 units # below baseline with a thickness of 50 undY = y - 0.1 * pmlOp.size canvas.setLineWidth(0.05 * pmlOp.size) canvas.line(x, undY, x + undLen, undY)
def drawOn(self, canvas): canvas.saveState() canvas.setFont('Helvetica-Bold',24) canvas.drawString(600, 100, 'A Custom Shape') canvas.translate(self.x, self.y) canvas.scale(self.scale, self.scale) self.drawBounds(canvas) self.drawCentre(canvas) self.drawTopLeft(canvas) self.drawBottomLeft(canvas) self.drawBottomRight(canvas) self.drawTopRight(canvas) canvas.restoreState()
def drawOn(self, canvas, x, y, _sW=0): "Tell it to draw itself on the canvas. Do not override" if _sW and hasattr(self,'hAlign'): a = self.hAlign if a in ['CENTER','CENTRE']: x = x + 0.5*_sW elif a == 'RIGHT': x = x + _sW elif a != 'LEFT': raise ValueError, "Bad hAlign value "+str(a) canvas.saveState() canvas.translate(x, y) self._drawOn(canvas) if hasattr(self, '_showBoundary') and self._showBoundary: #diagnostic tool support canvas.setStrokeColor(gray) canvas.rect(0,0,self.width, self.height) canvas.restoreState()
def draw(self, pmlOp, pageNr, canvas, pe): # we need to adjust y position since PDF uses baseline of text as # the y pos, but pml uses top of the text as y pos. The Adobe # standard Courier family font metrics give 157 units in 1/1000 # point units as the Descender value, thus giving (1000 - 157) = # 843 units from baseline to top of text. # http://partners.adobe.com/asn/tech/type/ftechnotes.jsp contains # the "Font Metrics for PDF Core 14 Fonts" document. x = pe.x(pmlOp.x) y = pe.y(pmlOp.y) - 0.843 * pmlOp.size if pe.doc.tocs and pmlOp.toc: key = "id%d" % id(pmlOp) canvas.bookmarkPage(key,fit="XYZ",left=pe.x(pmlOp.x),top=pe.y(pmlOp.y)) canvas.addOutlineEntry(pmlOp.toc.text,key) newFont = (pe.getFontNr(pmlOp.flags), pmlOp.size) if newFont != pe.currentFont: canvas.setFont(*newFont) pe.currentFont = newFont if pmlOp.angle is None or pmlOp.angle == 0.0: canvas.drawString(x,y,pmlOp.text) else: canvas.saveState() canvas.translate(x,y) canvas.rotate(pmlOp.angle) canvas.drawString(0,0,pmlOp.text) canvas.restoreState() if pmlOp.flags & pml.UNDERLINED: undLen = fontinfo.getMetrics(pmlOp.flags).getTextWidth( pmlOp.text, pmlOp.size) # all standard PDF fonts have the underline line 100 units # below baseline with a thickness of 50 undY = y - 0.1 * pmlOp.size canvas.setLineWidth(0.05 * pmlOp.size) canvas.line(x, undY, x + undLen, undY)
def draw(self, canvas, x, y): #specifies top left corner canvas.saveState() height = self._getHeight() canvas.rect(x, y - height, 6 * inch, height) #first draw the text canvas.setTextOrigin(x + 3 * inch, y - 12) canvas.setFont('Times-Roman', 10) canvas.textLines(self.comment1) drawCode(canvas, self.code) canvas.textLines(self.comment2) #now a box for the drawing, slightly within rect canvas.rect(x + 9, y - height + 9, 198, height - 18) #boundary: self.namespace = {'canvas': canvas, 'cm': cm, 'inch': inch} canvas.translate(x + 9, y - height + 9) codeObj = compile(self.code, '<sample>', 'exec') exec(codeObj, self.namespace) canvas.restoreState()
def draw(self, canvas, x, y): #specifies top left corner canvas.saveState() height = self._getHeight() canvas.rect(x, y-height, 6*inch, height) #first draw the text canvas.setTextOrigin(x + 3 * inch, y - 12) canvas.setFont('Times-Roman',10) canvas.textLines(self.comment1) drawCode(canvas, self.code) canvas.textLines(self.comment2) #now a box for the drawing, slightly within rect canvas.rect(x + 9, y - height + 9, 198, height - 18) #boundary: self.namespace = {'canvas':canvas,'cm': cm,'inch':inch} canvas.translate(x+9, y - height + 9) codeObj = compile(self.code, '<sample>','exec') exec(codeObj, self.namespace) canvas.restoreState()
def draw(self, canvas, x, y): # specifies top left corner canvas.saveState() height = self._getHeight() canvas.rect(x, y - height, 6 * inch, height) # first draw the text canvas.setTextOrigin(x + 3 * inch, y - 12) canvas.setFont("Times-Roman", 10) canvas.textLines(self.comment1) drawCode(canvas, self.code) canvas.textLines(self.comment2) # now a box for the drawing, slightly within rect canvas.rect(x + 9, y - height + 9, 198, height - 18) # boundary: self.namespace = {"canvas": canvas, "cm": cm, "inch": inch} canvas.translate(x + 9, y - height + 9) codeObj = compile(self.code, "<sample>", "exec") exec codeObj in self.namespace canvas.restoreState()
def draw(self): canvas = self.canv canvas.setLineWidth(0.1 * cm) canvas.setFillColor(self.fillcolor) canvas.setStrokeColor(self.strokecolor) canvas.translate((1 - self.scale) * self.size / 2, (1 - self.scale) * self.size / 2) canvas.scale(self.scale, self.scale) canvas.setFillColor(self.fillcolor) canvas.setLineJoin(2) ident = self.ident shapes = [[(0, 0), (1, 0), (0.5, 1)], [(0, 0), (1, 0), (0, 1), (1, 1)], [(0, 0), (1, 0), (1, 1), (0, 1)], [(0, 0.5), (0.5, 1), (1, 0.5), (0.5, 0)], [(0, 1), (1, 1), (0.5, 0)]] if self.ident % 2 == 1: canvas.setDash(3, 2) p = canvas.beginPath() ident = ident // 2 if self.ident % 2 == 0: p.moveTo(0.5 * self.size, 0) p.lineTo(0.5 * self.size, 1 * self.size) ident = ident // 2 sndx = (ident) % (len(shapes) + 1) if sndx < len(shapes): d = shapes[sndx] p.moveTo(d[0][0] * self.size, d[0][1] * self.size) for ndx in range(len(d) - 1): p.lineTo(d[ndx + 1][0] * self.size, d[ndx + 1][1] * self.size) else: p.ellipse(0, 0, 1 * self.size, 1 * self.size) p.close() canvas.drawPath(p)
def write_title_and_credits(canvas, opts, pagesize): canvas.saveState() if pagesize[0] > pagesize[1]: width = pagesize[0] height = pagesize[1] else: width = pagesize[1] height = pagesize[0] canvas.rotate(90) canvas.translate(0, -pagesize[0]) t = canvas.beginText() t.setFont("Times-Roman", 10) l = stringWidth(__COPYRIGHT__, "Times-Roman", 10) t.setTextOrigin(width - l - 5*mm, height+5*mm) t.textOut(__COPYRIGHT__) canvas.setFillColorRGB(0, 0, 0, 0.4) canvas.drawText(t) canvas.setFillColorRGB(0, 0, 0, 1) if opts.title: t = canvas.beginText() t.setTextOrigin(5*mm, height+5*mm) t.setFont("Times-Italic", 20) t.textOut(opts.title) canvas.drawText(t) t = canvas.beginText() t.setTextOrigin(5*mm, -5*mm) t.setFont("Times-Italic", 8) t.textOut(opts.subject) canvas.drawText(t) canvas.restoreState()
def create_badges(data): # canvas.translate(0, section_height) for batch in make_batches(data, 2): # fold & cut helpers canvas.setDash(6, 3) canvas.line(width / 2.0, 0, width / 2.0, height) # vertical line canvas.setDash(1, 0) canvas.line(0, height / 2.0, width, height / 2.0) # horizontal canvas.setDash(1, 0) draw_margins() canvas.translate(0, height / 2.0 + margin) for pos, (ticket_index, attendee) in enumerate(batch, 1): write_qr_code(attendee, ticket_index) canvas.translate(width / 2.0 + margin, 0) write_badge(attendee) canvas.translate(-width / 2.0 - margin, -height / 2.0 - margin) canvas.showPage( ) # finish the page, next statements should go next page canvas.save()
def render_element(root, element, canvas, styles, text_state=None): canvas.saveState() current_style = {} if len(styles): current_style.update(styles[-1]) for declaration in element.get("style", "").split(";"): if declaration == "": continue key, value = declaration.split(":") if key == "dominant-baseline" and value == "inherit": continue current_style[key] = value styles.append(current_style) if "stroke-width" in current_style: canvas.setLineWidth(float(current_style["stroke-width"])) if "stroke-dasharray" in current_style: canvas.setDash([float(length) for length in current_style["stroke-dasharray"].split(",")]) if current_style.get("visibility") != "hidden": if "transform" in element.attrib: for transformation in element.get("transform").split(")")[::1]: if transformation: type, arguments = transformation.split("(") arguments = arguments.split(",") if type.strip() == "translate": if len(arguments) == 2: canvas.translate(float(arguments[0]), float(arguments[1])) elif type.strip() == "rotate": if len(arguments) == 1: canvas.rotate(float(arguments[0])) if len(arguments) == 3: canvas.translate(float(arguments[1]), float(arguments[2])) canvas.rotate(float(arguments[0])) canvas.translate(-float(arguments[1]), -float(arguments[2])) if element.tag == "svg": if "background-color" in current_style: set_fill_color(canvas, toyplot.color.css(current_style["background-color"])) canvas.rect( 0, 0, float(element.get("width")[:-2]), float(element.get("height")[:-2]), stroke=0, fill=1 ) for child in element: render_element(root, child, canvas, styles) elif element.tag == "g": if element.get("clip-path", None) is not None: clip_id = element.get("clip-path")[5:-1] clip_path = root.find(".//*[@id='%s']" % clip_id) for child in clip_path: if child.tag == "rect": x = float(child.get("x")) y = float(child.get("y")) width = float(child.get("width")) height = float(child.get("height")) path = canvas.beginPath() path.moveTo(x, y) path.lineTo(x + width, y) path.lineTo(x + width, y + height) path.lineTo(x, y + height) path.close() canvas.clipPath(path, stroke=0, fill=1) else: toyplot.log.error("Unhandled clip tag: %s" % child.tag) # pragma: no cover for child in element: render_element(root, child, canvas, styles) elif element.tag == "clipPath": pass elif element.tag == "line": stroke = get_stroke(current_style) if stroke is not None: set_stroke_color(canvas, stroke) canvas.line( float(element.get("x1")), float(element.get("y1")), float(element.get("x2")), float(element.get("y2")), ) elif element.tag == "path": stroke = get_stroke(current_style) if stroke is not None: set_stroke_color(canvas, stroke) path = canvas.beginPath() commands = element.get("d").split() while len(commands): command = commands.pop(0) if command == "L": path.lineTo(float(commands.pop(0)), float(commands.pop(0))) elif command == "M": path.moveTo(float(commands.pop(0)), float(commands.pop(0))) canvas.drawPath(path) elif element.tag == "polygon": fill, fill_gradient = get_fill(root, current_style) if fill_gradient is not None: raise NotImplementedError("Gradient <polygon> not implemented.") # pragma: no cover if fill is not None: set_fill_color(canvas, fill) stroke = get_stroke(current_style) if stroke is not None: set_stroke_color(canvas, stroke) points = [point.split(",") for point in element.get("points").split()] path = canvas.beginPath() for point in points[:1]: path.moveTo(float(point[0]), float(point[1])) for point in points[1:]: path.lineTo(float(point[0]), float(point[1])) path.close() canvas.drawPath(path, stroke=stroke is not None, fill=fill is not None) elif element.tag == "rect": fill, fill_gradient = get_fill(root, current_style) if fill is not None: set_fill_color(canvas, fill) stroke = get_stroke(current_style) if stroke is not None: set_stroke_color(canvas, stroke) x = float(element.get("x")) y = float(element.get("y")) width = float(element.get("width")) height = float(element.get("height")) path = canvas.beginPath() path.moveTo(x, y) path.lineTo(x + width, y) path.lineTo(x + width, y + height) path.lineTo(x, y + height) path.close() if fill_gradient is not None: pdf_colors = [] pdf_offsets = [] for stop in fill_gradient: offset = float(stop.get("offset")) color = toyplot.color.css(stop.get("stop-color")) opacity = float(stop.get("stop-opacity")) pdf_colors.append(reportlab.lib.colors.Color(color["r"], color["g"], color["b"], color["a"])) pdf_offsets.append(offset) canvas.saveState() canvas.clipPath(path, stroke=0, fill=1) canvas.setFillAlpha(1) canvas.linearGradient( float(fill_gradient.get("x1")), float(fill_gradient.get("y1")), float(fill_gradient.get("x2")), float(fill_gradient.get("y2")), pdf_colors, pdf_offsets, ) canvas.restoreState() canvas.drawPath(path, stroke=stroke is not None, fill=fill is not None) elif element.tag == "circle": fill, fill_gradient = get_fill(root, current_style) if fill_gradient is not None: raise NotImplementedError("Gradient <circle> not implemented.") # pragma: no cover if fill is not None: set_fill_color(canvas, fill) stroke = get_stroke(current_style) if stroke is not None: set_stroke_color(canvas, stroke) cx = float(element.get("cx")) cy = float(element.get("cy")) r = float(element.get("r")) canvas.circle(cx, cy, r, stroke=stroke is not None, fill=fill is not None) elif element.tag == "text": text_state = {"x": 0, "y": 0, "chunks": [[]]} for child in element: render_element(root, child, canvas, styles, text_state) for chunk in text_state["chunks"]: width = sum([span[7] for span in chunk]) dx = 0 text_anchor = current_style.get("text-anchor", "start") if text_anchor == "middle": dx = -width * 0.5 elif text_anchor == "end": dx = -width for x, y, fill, stroke, font_family, font_size, text, width in chunk: canvas.saveState() canvas.setFont(font_family, font_size) if fill is not None: set_fill_color(canvas, fill) if stroke is not None: set_stroke_color(canvas, stroke) canvas.translate(x + dx, y) canvas.scale(1, -1) canvas.drawString(0, 0, text) canvas.restoreState() elif element.tag == "tspan": # if "font-weight" in current_style: # font_description.set_weight( # pango.WEIGHT_BOLD if current_style["font-weight"] == "bold" else pango.WEIGHT_NORMAL) font_family = get_font_family(current_style) font_size = toyplot.units.convert(current_style["font-size"].strip(), "px") string_width = reportlab.pdfbase.pdfmetrics.stringWidth(element.text, font_family, font_size) ascent, descent = reportlab.pdfbase.pdfmetrics.getAscentDescent(font_family, font_size) if "x" in element.attrib: text_state["x"] = float(element.get("x")) text_state["chunks"].append([]) if "dy" in element.attrib: text_state["y"] += float(element.get("dy")) x = text_state["x"] y = text_state["y"] alignment_baseline = current_style.get("alignment-baseline", "middle") if alignment_baseline == "hanging": y += ascent elif alignment_baseline == "central": y += ascent * 0.5 elif alignment_baseline == "middle": y += (ascent + descent) * 0.5 elif alignment_baseline == "alphabetic": pass else: raise ValueError("Unsupported alignment-baseline: %s" % alignment_baseline) # pragma: no cover baseline_shift = current_style.get("baseline-shift", "0").strip() baseline_shift = toyplot.units.convert(baseline_shift, "px", "px", ascent - descent) y -= baseline_shift fill, fill_gradient = get_fill(root, current_style) stroke = get_stroke(current_style) text_state["chunks"][-1].append( (x, y, fill, stroke, font_family, font_size, element.text, string_width) ) text_state["x"] += string_width elif element.tag in ["defs", "title"]: pass else: raise Exception("unhandled tag: %s" % element.tag) # pragma: no cover styles.pop() canvas.restoreState()
def translate(canvas): from reportlab.lib.units import cm canvas.translate(2.3*cm, 0.3*cm) coords(canvas)
def printcards(self): drawCard = self.loadPythonFunc("drawCard", self.session.query(Data).get("card.py").data) page = request.GET.get("page", "card") type = request.GET.get("type", "blank") query = ( self.session.query(Driver, Car, Registration) .join("cars", "registration") .filter(Registration.eventid == self.eventid) ) if type == "blank": registered = [(None, None, None)] elif type == "lastname": registered = query.order_by(func.lower(Driver.lastname), func.lower(Driver.firstname)).all() elif type == "classnumber": registered = query.order_by(Car.classcode, Car.number).all() if page == "csv": # CSV data, just use a template and return objects = list() for (dr, car, reg) in registered: o = Registered() o.__dict__.update(dr.__dict__) o.__dict__.update(reg.__dict__) o.__dict__.update(car.__dict__) # car is last so id = car.id objects.append(o) titles = [ "lastname", "firstname", "email", "address", "city", "state", "zip", "phone", "sponsor", "brag", "id", "year", "make", "model", "color", "number", "classcode", "indexcode", ] return self.csv("cards", titles, objects) # Otherwise we are are PDF try: from reportlab.pdfgen import canvas from reportlab.lib.units import inch from reportlab.lib.utils import ImageReader except: c.text = "<h4>PDFGen not installed, can't create timing card PDF files from this system</h4>" return render_mako("/admin/simple.mako") try: from PIL import Image except ImportError: try: import Image except: c.text = "<h4>Python Image not installed, can't create timing card PDF files from this system</h4>" return render_mako("/admin/simple.mako") if page == "letter": # Letter has an additional 72 points Y to space out size = (8 * inch, 11 * inch) else: size = (8 * inch, 5 * inch) if page == "letter" and len(registered) % 2 != 0: registered.append((None, None, None)) # Pages are always two cards per so make it divisible by 2 buffer = cStringIO.StringIO() canvas = canvas.Canvas(buffer, pagesize=size, pageCompression=1) carddata = self.session.query(Data).get("cardimage") if carddata is None: cardimage = Image.new("RGB", (1, 1)) else: cardimage = Image.open(cStringIO.StringIO(carddata.data)) cardimage = ImageReader(cardimage) while len(registered) > 0: if page == "letter": canvas.translate(0, 18) # 72/4, bottom margin for letter page (driver, car, reg) = registered.pop(0) drawCard(canvas, c.event, driver, car, cardimage) canvas.translate(0, 396) # 360+72/2 card size plus 2 middle margins (driver, car, reg) = registered.pop(0) drawCard(canvas, c.event, driver, car, cardimage) else: (driver, car, reg) = registered.pop(0) drawCard(canvas, c.event, driver, car, cardimage) canvas.showPage() canvas.save() response.headers["Content-type"] = "application/octet-stream" response.headers["Content-Disposition"] = "attachment;filename=cards.pdf" return buffer.getvalue()
def mirror(canvas): from reportlab.lib.units import inch canvas.translate(5.5*inch, 0) canvas.scale(-1.0, 1.0) coords(canvas)
def printcards(self): drawCard = self.loadPythonFunc( 'drawCard', self.session.query(Data).get('card.py').data) page = request.GET.get('page', 'card') type = request.GET.get('type', 'blank') query = self.session.query(Driver, Car, Registration).join( 'cars', 'registration').filter(Registration.eventid == self.eventid) if type == 'blank': registered = [(None, None, None)] elif type == 'lastname': registered = query.order_by(func.lower(Driver.lastname), func.lower(Driver.firstname)).all() elif type == 'classnumber': registered = query.order_by(Car.classcode, Car.number).all() if page == 'csv': # CSV data, just use a template and return objects = list() for (dr, car, reg) in registered: o = Registered() o.__dict__.update(dr.__dict__) o.__dict__.update(reg.__dict__) o.__dict__.update(car.__dict__) # car is last so id = car.id objects.append(o) titles = [ 'lastname', 'firstname', 'email', 'address', 'city', 'state', 'zip', 'phone', 'sponsor', 'brag', 'id', 'year', 'make', 'model', 'color', 'number', 'classcode', 'indexcode' ] return self.csv("cards", titles, objects) # Otherwise we are are PDF try: from reportlab.pdfgen import canvas from reportlab.lib.units import inch from reportlab.lib.utils import ImageReader except: c.text = "<h4>PDFGen not installed, can't create timing card PDF files from this system</h4>" return render_mako("/admin/simple.mako") try: from PIL import Image except ImportError: try: import Image except: c.text = "<h4>Python Image not installed, can't create timing card PDF files from this system</h4>" return render_mako("/admin/simple.mako") if page == 'letter': # Letter has an additional 72 points Y to space out size = (8 * inch, 11 * inch) else: size = (8 * inch, 5 * inch) if page == 'letter' and len(registered) % 2 != 0: registered.append( (None, None, None )) # Pages are always two cards per so make it divisible by 2 buffer = cStringIO.StringIO() canvas = canvas.Canvas(buffer, pagesize=size, pageCompression=1) carddata = self.session.query(Data).get('cardimage') if carddata is None: cardimage = Image.new('RGB', (1, 1)) else: cardimage = Image.open(cStringIO.StringIO(carddata.data)) cardimage = ImageReader(cardimage) while len(registered) > 0: if page == 'letter': canvas.translate(0, 18) # 72/4, bottom margin for letter page (driver, car, reg) = registered.pop(0) drawCard(canvas, c.event, driver, car, cardimage) canvas.translate( 0, 396) # 360+72/2 card size plus 2 middle margins (driver, car, reg) = registered.pop(0) drawCard(canvas, c.event, driver, car, cardimage) else: (driver, car, reg) = registered.pop(0) drawCard(canvas, c.event, driver, car, cardimage) canvas.showPage() canvas.save() response.headers['Content-type'] = "application/octet-stream" response.headers[ 'Content-Disposition'] = 'attachment;filename=cards.pdf' return buffer.getvalue()
def genkitlabel(canvas,xa,ya,xw,yh,contentlist,test=0,paper_size_no=0): """ """ v('[------genkitlabel()') x = xa y = ya m = .05*inch h = yh # calc height h -= m*2 # height - margin w = xw # calc width w -= m*2 # width - margin v('genkitlabel() ------') v('genkitlabel() xa,ya,xw,yh: %f %f %f %f' % (xa,ya,xw,yh)) v('genkitlabel() m h w : %f %f %f' % ( m, h, w)) xo = yo = m # origin canvas.setLineWidth(.01*inch) canvas.setStrokeColorRGB(.75,.75,.75) global xwidth if (not (xwidth/inch <= 3.50)): cutmark(canvas,x,y,1,1) cutmark(canvas,x+xw,y+yh,-1,-1) cutmark(canvas,x,y+yh,1,-1) cutmark(canvas,x+xw,y,-1,1) didlogo = False yloc = y + h image_size = 1.2*inch logo_yloc = yloc-image_size+.2*inch yrel = 0 for line in contentlist: if (line == '..' or line == '.'): flip = False break v('genkitlabel(): line:%s' %(line)) token = line.split() if len(token) <= 0: continue dowhat = token[0].upper() #--- global flip if (dowhat == 'FLIP'): flip = True elif (dowhat == 'LOGO'): v('LOGO') if (len(token) == 1): # no arg print logo if (paper_size_no == 3): image_size = 1*inch canvas.drawImage('logo512x512.png', x+m-.75*inch,logo_yloc, image_size, image_size, preserveAspectRatio=True) else: canvas.drawImage('logo512x512.png', x+m+.1*inch+2.4*inch,logo_yloc, image_size, image_size, preserveAspectRatio=True) else: # print arg arg = token[1] if (len(arg) == 1): # character. make it big if flip: # They said 'LOGO X', so we draw big fat X where the logo should be canvas.saveState() canvas.translate(x+m+.3*inch,logo_yloc+1*inch) canvas.scale(-1,-1) canvas.setFont('Helvetica-Bold',70) canvas.drawString(0,0,token[1]) canvas.restoreState() else: # They said 'LOGO X', so we draw big fat X where the logo should be canvas.setFont('Helvetica-Bold',70) canvas.drawString(x+m-.45*inch,logo_yloc+.2*inch,token[1]) else: # Multiple characters if flip: # They said 'LOGO X', so we draw big fat X where the logo should be canvas.saveState() canvas.translate(x+m+.3*inch,logo_yloc+1*inch) canvas.scale(-1,-1) canvas.setFont('Helvetica-Bold',20) canvas.drawString(.5*inch,.55*inch,arg[0]) canvas.drawString(.5*inch,.30*inch,arg[1]) canvas.drawString(.5*inch,.05*inch,arg[2]) canvas.restoreState() else: # They said 'LOGO X', so we draw big fat X where the logo should be canvas.setFont('Helvetica-Bold',20) canvas.drawString(x+m-.45*inch,logo_yloc+.80*inch,arg[0]) canvas.drawString(x+m-.45*inch,logo_yloc+.55*inch,arg[1]) canvas.drawString(x+m-.45*inch,logo_yloc+.30*inch,arg[2]) #--- elif (dowhat == 'BARCODE'): yloc = render_barcode(canvas, x,yloc, token[1], ' '.join(token[2:])) v('genkitlabel(): yloc now: %f' % (yloc)) #--- elif (dowhat == 'KEYVAL'): v('KEYVAL Width : ') yloc = render_key_and_value(canvas, x+m+.350*inch, yloc, token[1], ' '.join(token[2:])) #yinc = .150*inch ## ## v('genkitlabel() --- line:' + line) return line
def render_key_and_value(canvas, x, y, lhs, rhs, wraplen=40): """ render a keyword:value pair. Keyword will be in bold """ global xwidth global yheight yinc = .125 * inch fontsize = 10 v('render_key_and_value(): xwidth:%f yheight:%f ' % (xwidth, yheight)) # little labels get special treatment if (xwidth / inch <= 3.50): v('render_key_and_value(): fixing wraplen for small labels') wraplen = 25 fontsize = 9 if flip: yinc = -.105 * inch else: yinc = .105 * inch lhs += ': ' width = stringWidth(lhs, 'Helvetica-Bold', fontsize) width *= 1.2 if (width < .75 * inch): width = .75 * inch # draw keyword canvas.setFont('Helvetica-Bold', fontsize) if flip: canvas.saveState() canvas.translate(x + 2.45 * inch, y + .25 * inch) canvas.scale(-1, -1) canvas.drawString(0, 0, lhs.upper()) canvas.restoreState() else: canvas.drawString(x, y, lhs.upper()) # draw value canvas.setFont('Helvetica', fontsize) yrel = 0 lines = 0 v('render_key_and_value(): y+yrel: %f' % (y + yrel)) text_line_list = textwrap.wrap(rhs, wraplen) for line in text_line_list: if flip: canvas.saveState() canvas.translate(x + width + 1.00 * inch, y + yrel + .25 * inch) canvas.scale(-1, -1) canvas.drawString(0, 0, line) canvas.restoreState() else: canvas.drawString(x + width, y + yrel, line) yrel -= yinc lines += 1 if ((yheight / inch <= 1.25) and (lines == 3)): break yrel -= yinc / 2 return y + yrel
markup = "The answer is: $E=\\frac{m_1v^2}{2}$" markup_tex = """ \documentclass{standalone} \\begin{document} %s \end{document} """ % markup pdflatex = subprocess.Popen(["pdflatex"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = pdflatex.communicate(markup_tex) if pdflatex.returncode: print stdout, stderr canvas = reportlab.pdfgen.canvas.Canvas("latex-math-pdf.pdf") for page in pdfrw.PdfReader("texput.pdf").pages: page = pdfrw.buildxobj.pagexobj(page) canvas.saveState() canvas.translate(100, 100) canvas.doForm(pdfrw.toreportlab.makerl(canvas, page)) canvas.restoreState() canvas.showPage() canvas.save()
markup = "The answer is: $E=\\frac{m_1v^2}{2}$" markup_tex = ( """ \documentclass{standalone} \\begin{document} %s \end{document} """ % markup ) pdflatex = subprocess.Popen(["pdflatex"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = pdflatex.communicate(markup_tex) if pdflatex.returncode: print stdout, stderr canvas = reportlab.pdfgen.canvas.Canvas("latex-math-pdf.pdf") for page in pdfrw.PdfReader("texput.pdf").pages: page = pdfrw.buildxobj.pagexobj(page) canvas.saveState() canvas.translate(100, 100) canvas.doForm(pdfrw.toreportlab.makerl(canvas, page)) canvas.restoreState() canvas.showPage() canvas.save()
def render_element(root, element, canvas, styles, text_state=None): canvas.saveState() current_style = {} if len(styles): current_style.update(styles[-1]) for declaration in element.get("style", "").split(";"): if declaration == "": continue key, value = declaration.split(":") if key == "dominant-baseline" and value == "inherit": continue current_style[key] = value styles.append(current_style) if "stroke-width" in current_style: canvas.setLineWidth(float(current_style["stroke-width"])) if "stroke-dasharray" in current_style: canvas.setDash([ float(length) for length in current_style["stroke-dasharray"].split(",") ]) if current_style.get("visibility") != "hidden": if "transform" in element.attrib: for transformation in element.get("transform").split(")")[::1]: if transformation: type, arguments = transformation.split("(") arguments = arguments.split(",") if type.strip() == "translate": if len(arguments) == 2: canvas.translate(float(arguments[0]), float(arguments[1])) elif type.strip() == "rotate": if len(arguments) == 1: canvas.rotate(float(arguments[0])) if len(arguments) == 3: canvas.translate(float(arguments[1]), float(arguments[2])) canvas.rotate(float(arguments[0])) canvas.translate(-float(arguments[1]), -float(arguments[2])) if element.tag == "svg": if "background-color" in current_style: set_fill_color( canvas, toyplot.color.css(current_style["background-color"])) canvas.rect( 0, 0, float(element.get("width")[:-2]), float(element.get("height")[:-2]), stroke=0, fill=1, ) for child in element: render_element(root, child, canvas, styles) elif element.tag == "g": if element.get("clip-path", None) is not None: clip_id = element.get("clip-path")[5:-1] clip_path = root.find(".//*[@id='%s']" % clip_id) for child in clip_path: if child.tag == "rect": x = float(child.get("x")) y = float(child.get("y")) width = float(child.get("width")) height = float(child.get("height")) path = canvas.beginPath() path.moveTo(x, y) path.lineTo(x + width, y) path.lineTo(x + width, y + height) path.lineTo(x, y + height) path.close() canvas.clipPath(path, stroke=0, fill=1) else: toyplot.log.error("Unhandled clip tag: %s" % child.tag) # pragma: no cover for child in element: render_element(root, child, canvas, styles) elif element.tag == "clipPath": pass elif element.tag == "line": stroke = get_stroke(current_style) if stroke is not None: set_stroke_color(canvas, stroke) canvas.line( float(element.get("x1")), float(element.get("y1")), float(element.get("x2")), float(element.get("y2")), ) elif element.tag == "path": stroke = get_stroke(current_style) if stroke is not None: set_stroke_color(canvas, stroke) path = canvas.beginPath() commands = element.get("d").split() while len(commands): command = commands.pop(0) if command == "L": path.lineTo(float(commands.pop(0)), float(commands.pop(0))) elif command == "M": path.moveTo(float(commands.pop(0)), float(commands.pop(0))) canvas.drawPath(path) elif element.tag == "polygon": fill, fill_gradient = get_fill(root, current_style) if fill_gradient is not None: raise NotImplementedError( "Gradient <polygon> not implemented." ) # pragma: no cover if fill is not None: set_fill_color(canvas, fill) stroke = get_stroke(current_style) if stroke is not None: set_stroke_color(canvas, stroke) points = [ point.split(",") for point in element.get("points").split() ] path = canvas.beginPath() for point in points[:1]: path.moveTo(float(point[0]), float(point[1])) for point in points[1:]: path.lineTo(float(point[0]), float(point[1])) path.close() canvas.drawPath(path, stroke=stroke is not None, fill=fill is not None) elif element.tag == "rect": fill, fill_gradient = get_fill(root, current_style) if fill is not None: set_fill_color(canvas, fill) stroke = get_stroke(current_style) if stroke is not None: set_stroke_color(canvas, stroke) x = float(element.get("x")) y = float(element.get("y")) width = float(element.get("width")) height = float(element.get("height")) path = canvas.beginPath() path.moveTo(x, y) path.lineTo(x + width, y) path.lineTo(x + width, y + height) path.lineTo(x, y + height) path.close() if fill_gradient is not None: pdf_colors = [] pdf_offsets = [] for stop in fill_gradient: offset = float(stop.get("offset")) color = toyplot.color.css(stop.get("stop-color")) opacity = float(stop.get("stop-opacity")) pdf_colors.append( reportlab.lib.colors.Color(color["r"], color["g"], color["b"], color["a"])) pdf_offsets.append(offset) canvas.saveState() canvas.clipPath(path, stroke=0, fill=1) canvas.setFillAlpha(1) canvas.linearGradient( float(fill_gradient.get("x1")), float(fill_gradient.get("y1")), float(fill_gradient.get("x2")), float(fill_gradient.get("y2")), pdf_colors, pdf_offsets, ) canvas.restoreState() canvas.drawPath(path, stroke=stroke is not None, fill=fill is not None) elif element.tag == "circle": fill, fill_gradient = get_fill(root, current_style) if fill_gradient is not None: raise NotImplementedError( "Gradient <circle> not implemented." ) # pragma: no cover if fill is not None: set_fill_color(canvas, fill) stroke = get_stroke(current_style) if stroke is not None: set_stroke_color(canvas, stroke) cx = float(element.get("cx")) cy = float(element.get("cy")) r = float(element.get("r")) canvas.circle(cx, cy, r, stroke=stroke is not None, fill=fill is not None) elif element.tag == "text": text_state = {"x": 0, "y": 0, "chunks": [[]]} for child in element: render_element(root, child, canvas, styles, text_state) for chunk in text_state["chunks"]: width = sum([span[7] for span in chunk]) dx = 0 text_anchor = current_style.get("text-anchor", "start") if text_anchor == "middle": dx = -width * 0.5 elif text_anchor == "end": dx = -width for x, y, fill, stroke, font_family, font_size, text, width in chunk: canvas.saveState() canvas.setFont(font_family, font_size) if fill is not None: set_fill_color(canvas, fill) if stroke is not None: set_stroke_color(canvas, stroke) canvas.translate(x + dx, y) canvas.scale(1, -1) canvas.drawString(0, 0, text) canvas.restoreState() elif element.tag == "tspan": # if "font-weight" in current_style: # font_description.set_weight( # pango.WEIGHT_BOLD if current_style["font-weight"] == "bold" else pango.WEIGHT_NORMAL) font_family = get_font_family(current_style) font_size = toyplot.units.convert( current_style["font-size"].strip(), "px") string_width = reportlab.pdfbase.pdfmetrics.stringWidth( element.text, font_family, font_size) ascent, descent = reportlab.pdfbase.pdfmetrics.getAscentDescent( font_family, font_size) if "x" in element.attrib: text_state["x"] = float(element.get("x")) text_state["chunks"].append([]) if "dy" in element.attrib: text_state["y"] += float(element.get("dy")) x = text_state["x"] y = text_state["y"] alignment_baseline = current_style.get("alignment-baseline", "middle") if alignment_baseline == "hanging": y += ascent elif alignment_baseline == "central": y += ascent * 0.5 elif alignment_baseline == "middle": y += (ascent + descent) * 0.5 elif alignment_baseline == "alphabetic": pass else: raise ValueError("Unsupported alignment-baseline: %s" % alignment_baseline) # pragma: no cover baseline_shift = current_style.get("baseline-shift", "0").strip() baseline_shift = toyplot.units.convert(baseline_shift, "px", "px", ascent - descent) y -= baseline_shift fill, fill_gradient = get_fill(root, current_style) stroke = get_stroke(current_style) text_state["chunks"][-1].append( (x, y, fill, stroke, font_family, font_size, element.text, string_width)) text_state["x"] += string_width elif element.tag in ["defs", "title"]: pass else: raise Exception("unhandled tag: %s" % element.tag) # pragma: no cover styles.pop() canvas.restoreState()
def render_element(root, element, canvas, styles): canvas.saveState() current_style = {} if len(styles): current_style.update(styles[-1]) for declaration in element.get("style", "").split(";"): if declaration == "": continue key, value = declaration.split(":") current_style[key] = value styles.append(current_style) if "stroke-width" in current_style: canvas.setLineWidth(float(current_style["stroke-width"])) if "stroke-dasharray" in current_style: canvas.setDash([ float(length) for length in current_style["stroke-dasharray"].split(",") ]) if current_style.get("visibility") != "hidden": if "transform" in element.attrib: for transformation in element.get("transform").split(")")[::1]: if transformation: transform, arguments = transformation.split("(") arguments = arguments.split(",") if transform.strip() == "translate": if len(arguments) == 2: canvas.translate(float(arguments[0]), float(arguments[1])) elif transform.strip() == "rotate": if len(arguments) == 1: canvas.rotate(float(arguments[0])) if len(arguments) == 3: canvas.translate(float(arguments[1]), float(arguments[2])) canvas.rotate(float(arguments[0])) canvas.translate(-float(arguments[1]), -float(arguments[2])) if element.tag == "svg": if "background-color" in current_style: set_fill_color( canvas, toyplot.color.css(current_style["background-color"])) canvas.rect( 0, 0, float(element.get("width")[:-2]), float(element.get("height")[:-2]), stroke=0, fill=1, ) for child in element: render_element(root, child, canvas, styles) elif element.tag == "g": if element.get("clip-path", None) is not None: clip_id = element.get("clip-path")[5:-1] clip_path = root.find(".//*[@id='%s']" % clip_id) for child in clip_path: if child.tag == "rect": x = float(child.get("x")) y = float(child.get("y")) width = float(child.get("width")) height = float(child.get("height")) path = canvas.beginPath() path.moveTo(x, y) path.lineTo(x + width, y) path.lineTo(x + width, y + height) path.lineTo(x, y + height) path.close() canvas.clipPath(path, stroke=0, fill=1) else: toyplot.log.error("Unhandled clip tag: %s", child.tag) # pragma: no cover for child in element: render_element(root, child, canvas, styles) elif element.tag == "clipPath": pass elif element.tag == "line": stroke = get_stroke(current_style) if stroke is not None: set_stroke_color(canvas, stroke) canvas.setLineCap(get_line_cap(current_style)) canvas.line( float(element.get("x1", 0)), float(element.get("y1", 0)), float(element.get("x2", 0)), float(element.get("y2", 0)), ) elif element.tag == "path": stroke = get_stroke(current_style) if stroke is not None: set_stroke_color(canvas, stroke) canvas.setLineCap(get_line_cap(current_style)) path = canvas.beginPath() commands = element.get("d").split() while len(commands): command = commands.pop(0) if command == "L": path.lineTo(float(commands.pop(0)), float(commands.pop(0))) elif command == "M": path.moveTo(float(commands.pop(0)), float(commands.pop(0))) canvas.drawPath(path) elif element.tag == "polygon": fill, fill_gradient = get_fill(root, current_style) if fill_gradient is not None: raise NotImplementedError( "Gradient <polygon> not implemented." ) # pragma: no cover if fill is not None: set_fill_color(canvas, fill) stroke = get_stroke(current_style) if stroke is not None: set_stroke_color(canvas, stroke) points = [ point.split(",") for point in element.get("points").split() ] path = canvas.beginPath() for point in points[:1]: path.moveTo(float(point[0]), float(point[1])) for point in points[1:]: path.lineTo(float(point[0]), float(point[1])) path.close() canvas.drawPath(path, stroke=stroke is not None, fill=fill is not None) elif element.tag == "rect": fill, fill_gradient = get_fill(root, current_style) if fill is not None: set_fill_color(canvas, fill) stroke = get_stroke(current_style) if stroke is not None: set_stroke_color(canvas, stroke) x = float(element.get("x", 0)) y = float(element.get("y", 0)) width = float(element.get("width")) height = float(element.get("height")) path = canvas.beginPath() path.moveTo(x, y) path.lineTo(x + width, y) path.lineTo(x + width, y + height) path.lineTo(x, y + height) path.close() if fill_gradient is not None: pdf_colors = [] pdf_offsets = [] for stop in fill_gradient: offset = float(stop.get("offset")) color = toyplot.color.css(stop.get("stop-color")) opacity = float(stop.get("stop-opacity")) pdf_colors.append( reportlab.lib.colors.Color(color["r"], color["g"], color["b"], color["a"] * opacity)) pdf_offsets.append(offset) canvas.saveState() canvas.clipPath(path, stroke=0, fill=1) canvas.setFillAlpha(1) canvas.linearGradient( float(fill_gradient.get("x1")), float(fill_gradient.get("y1")), float(fill_gradient.get("x2")), float(fill_gradient.get("y2")), pdf_colors, pdf_offsets, ) canvas.restoreState() canvas.drawPath(path, stroke=stroke is not None, fill=fill is not None) elif element.tag == "circle": fill, fill_gradient = get_fill(root, current_style) if fill_gradient is not None: raise NotImplementedError( "Gradient <circle> not implemented." ) # pragma: no cover if fill is not None: set_fill_color(canvas, fill) stroke = get_stroke(current_style) if stroke is not None: set_stroke_color(canvas, stroke) cx = float(element.get("cx", 0)) cy = float(element.get("cy", 0)) r = float(element.get("r")) canvas.circle(cx, cy, r, stroke=stroke is not None, fill=fill is not None) elif element.tag == "text": x = float(element.get("x", 0)) y = float(element.get("y", 0)) fill, fill_gradient = get_fill(element, current_style) stroke = get_stroke(current_style) font_family = get_font_family(current_style) font_size = toyplot.units.convert(current_style["font-size"], target="px") text = element.text canvas.saveState() canvas.setFont(font_family, font_size) if fill is not None: set_fill_color(canvas, fill) if stroke is not None: set_stroke_color(canvas, stroke) canvas.translate(x, y) canvas.scale(1, -1) canvas.drawString(0, 0, text) canvas.restoreState() elif element.tag == "image": # pylint: disable=redefined-variable-type import PIL.Image image = element.get("xlink:href") if not image.startswith("data:image/png;base64,"): raise ValueError( "Unsupported image type.") # pragma: no cover image = base64.standard_b64decode(image[22:]) image = io.BytesIO(image) image = PIL.Image.open(image) image = reportlab.lib.utils.ImageReader(image) x = float(element.get("x", 0)) y = float(element.get("y", 0)) width = float(element.get("width")) height = float(element.get("height")) canvas.saveState() path = canvas.beginPath() set_fill_color(canvas, toyplot.color.rgb(1, 1, 1)) canvas.rect(x, y, width, height, stroke=0, fill=1) canvas.translate(x, y + height) canvas.scale(1, -1) canvas.drawImage(image=image, x=0, y=0, width=width, height=height, mask=None) canvas.restoreState() elif element.tag in ["defs", "title"]: pass else: raise Exception("unhandled tag: %s" % element.tag) # pragma: no cover styles.pop() canvas.restoreState()
def draw_pdf(buffer, invoice): """ Draws the invoice """ canvas = Canvas(buffer, pagesize=A4) canvas.translate(0, 29.7 * cm) canvas.setFont('Helvetica', 10) canvas.saveState() header_func(canvas) canvas.restoreState() canvas.saveState() footer_func(canvas, invoice.footer) canvas.restoreState() canvas.saveState() address_func(canvas, invoice.address) canvas.restoreState() # Client address textobject = canvas.beginText(1.5 * cm, -2.5 * cm) for line in invoice.client_business_details: textobject.textLine(line) # if invoice.address.contact_name: # textobject.textLine(invoice.address.contact_name) # textobject.textLine(invoice.address.address_one) # if invoice.address.address_two: # textobject.textLine(invoice.address.address_two) # textobject.textLine(invoice.address.town) # if invoice.address.county: # textobject.textLine(invoice.address.county) # textobject.textLine(invoice.address.postcode) # textobject.textLine(invoice.address.country.name) canvas.drawText(textobject) # Info textobject = canvas.beginText(1.5 * cm, -6.75 * cm) textobject.textLine('Invoice ID: %s' % invoice.invoice_id) textobject.textLine('Invoice Date: %s' % invoice.invoice_date.strftime('%d %b %Y')) textobject.textLine('Client: %s' % invoice.client) for line in invoice.body_text: textobject.textLine(line) canvas.drawText(textobject) # Items data = [ ['Quantity', 'Description', 'Amount', 'Total'], ] for item in invoice.items: data.append([ item.quantity, item.description, format_currency(item.unit_price, invoice.currency), format_currency(item.total(), invoice.currency) ]) data.append( ['', '', 'Total:', format_currency(invoice.total(), invoice.currency)]) table = Table(data, colWidths=[2 * cm, 11 * cm, 3 * cm, 3 * cm]) table.setStyle([ ('FONT', (0, 0), (-1, -1), 'Helvetica'), ('FONTSIZE', (0, 0), (-1, -1), 10), ('TEXTCOLOR', (0, 0), (-1, -1), (0.2, 0.2, 0.2)), ('GRID', (0, 0), (-1, -2), 1, (0.7, 0.7, 0.7)), ('GRID', (-2, -1), (-1, -1), 1, (0.7, 0.7, 0.7)), ('ALIGN', (-2, 0), (-1, -1), 'RIGHT'), ('BACKGROUND', (0, 0), (-1, 0), (0.8, 0.8, 0.8)), ]) tw, th, = table.wrapOn(canvas, 15 * cm, 19 * cm) table.drawOn(canvas, 1 * cm, -10 * cm - th) canvas.showPage() canvas.save()
def draw_pdf(buffer, invoice): """ Draws the invoice """ canvas = Canvas(buffer, pagesize=A4) canvas.translate(0, 29.7 * cm) canvas.setFont('Helvetica', 10) canvas.saveState() header_func(canvas) canvas.restoreState() canvas.saveState() footer_func(canvas, invoice.footer) canvas.restoreState() canvas.saveState() address_func(canvas) canvas.restoreState() # Client address textobject = canvas.beginText(1.5 * cm, -2.5 * cm) for line in invoice.client_business_details: textobject.textLine(line) # if invoice.address.contact_name: # textobject.textLine(invoice.address.contact_name) # textobject.textLine(invoice.address.address_one) # if invoice.address.address_two: # textobject.textLine(invoice.address.address_two) # textobject.textLine(invoice.address.town) # if invoice.address.county: # textobject.textLine(invoice.address.county) # textobject.textLine(invoice.address.postcode) # textobject.textLine(invoice.address.country.name) canvas.drawText(textobject) # Info textobject = canvas.beginText(1.5 * cm, -6.75 * cm) textobject.textLine(u'Invoice ID: %s' % invoice.invoice_id) textobject.textLine(u'Invoice Date: %s' % invoice.invoice_date.strftime('%d %b %Y')) textobject.textLine(u'Client: %s' % invoice.client) for line in invoice.body_text: textobject.textLine(line) canvas.drawText(textobject) # Items data = [[u'Quantity', u'Description', u'Amount', u'Total'], ] for item in invoice.items: data.append([ item.quantity, item.description, format_currency(item.unit_price, invoice.currency), format_currency(item.total(), invoice.currency) ]) data.append([u'', u'', u'Total:', format_currency(invoice.total(), invoice.currency)]) table = Table(data, colWidths=[2 * cm, 11 * cm, 3 * cm, 3 * cm]) table.setStyle([ ('FONT', (0, 0), (-1, -1), 'Helvetica'), ('FONTSIZE', (0, 0), (-1, -1), 10), ('TEXTCOLOR', (0, 0), (-1, -1), (0.2, 0.2, 0.2)), ('GRID', (0, 0), (-1, -2), 1, (0.7, 0.7, 0.7)), ('GRID', (-2, -1), (-1, -1), 1, (0.7, 0.7, 0.7)), ('ALIGN', (-2, 0), (-1, -1), 'RIGHT'), ('BACKGROUND', (0, 0), (-1, 0), (0.8, 0.8, 0.8)), ]) tw, th, = table.wrapOn(canvas, 15 * cm, 19 * cm) table.drawOn(canvas, 1 * cm, -10 * cm - th) canvas.showPage() canvas.save()
def generatePage(words, canvas: canvas.Canvas, page: PageSettings, title): """ :param words: matrix of words (rows * columns) :param canvas: PDF canvas :param meta: other information (e.g page title) :return: """ if page.landscape: (marginR, marginT, marginL, marginB) = page.margins (height, width) = page.pagesize titleX = marginT titleY = width - marginR else: (marginT, marginL, marginB, marginR) = page.margins (width, height) = page.pagesize titleX = marginL titleY = height - marginT # if page.title: # canvas.setFont("HatWordFont", page.titleFontSize) # canvas.drawString(titleX + page.titleIndent, titleY - page.titleHeight / 2, title) if page.landscape: canvas.rotate(90) canvas.translate(0, -height) gwidth = width - marginL - marginR gheight = height - marginT - marginB goriginx = marginL goriginy = marginB # if page.title: # if page.landscape: # gwidth -= page.titleHeight # else: # gheight -= page.titleHeight if page.cutGrid: canvas.setStrokeColor(black) # Large bold rectangle canvas.setLineWidth(0.4 * mm) canvas.rect(goriginx, goriginy, gwidth, gheight) # outer cutting lines: canvas.setLineWidth(0.3 * mm) canvas.line(0, goriginy, width, goriginy) canvas.line(0, goriginy + gheight, width, goriginy + gheight) canvas.line(goriginx, 0, goriginx, height) canvas.line(goriginx + gwidth, 0, goriginx + gwidth, height) # grid cellWidth = gwidth / page.columns cellHeight = gheight / page.rows canvas.setLineWidth(0.2 * mm) canvas.grid([goriginx + i * cellWidth for i in range(page.columns + 1)], [goriginy + j * cellHeight for j in range(page.rows + 1)]) # add words canvas.setFont("HatWordFont", page.fontSize) # As y starts at the end of the page, adjust for it and start from the top # (so that empty cells will placed be at bottom). yoffset = goriginy + cellHeight / 2 + cellHeight * (page.rows - 1) for row in words: xoffset = goriginx + cellWidth / 2 for word in row: # scale down font size for long words numlines = word.count(";") + 1 fontSize = page.fontSize while fontSize > 0 and max( canvas.stringWidth(w, fontSize=fontSize) for w in word.split(";")) >= cellWidth - 2 * page.wordMargin - 0.5: fontSize -= 1 canvas.setFontSize(fontSize) # Somewhat cheap guess on string height : fontsize / 2 yoff = yoffset + fontSize * numlines * 0.65 - fontSize / 2 # print("99999999", word) flag = False for i in word.split(";"): print(i) if (flag): yoff -= fontSize * 1.1 if (i[:5] == "Место"): flag = True else: flag = False if (i == word.split(";")[0]): canvas.setFont("HatWordFontBold", page.titleFontSize) canvas.setFontSize(fontSize) canvas.drawCentredString(xoffset, yoff, i) yoff -= fontSize * 1.1 else: canvas.drawString(xoffset - cellWidth / 2 + 10, yoff, i) if (i == word.split(";")[0]): canvas.setFont("HatWordFont", page.titleFontSize) canvas.setFontSize(fontSize) yoff -= fontSize * 1.1 xoffset += cellWidth yoffset -= cellHeight
def genkitlabel(canvas, xa, ya, xw, yh, contentlist, test=0, paper_size_no=0): """ """ v('[------genkitlabel()') x = xa y = ya m = .05 * inch h = yh # calc height h -= m * 2 # height - margin w = xw # calc width w -= m * 2 # width - margin v('genkitlabel() ------') v('genkitlabel() xa,ya,xw,yh: %f %f %f %f' % (xa, ya, xw, yh)) v('genkitlabel() m h w : %f %f %f' % (m, h, w)) xo = yo = m # origin canvas.setLineWidth(.01 * inch) canvas.setStrokeColorRGB(.75, .75, .75) global xwidth if (not (xwidth / inch <= 3.50)): cutmark(canvas, x, y, 1, 1) cutmark(canvas, x + xw, y + yh, -1, -1) cutmark(canvas, x, y + yh, 1, -1) cutmark(canvas, x + xw, y, -1, 1) didlogo = False yloc = y + h image_size = 1.2 * inch logo_yloc = yloc - image_size + .2 * inch yrel = 0 for line in contentlist: if (line == '..' or line == '.'): flip = False break v('genkitlabel(): line:%s' % (line)) token = line.split() if len(token) <= 0: continue dowhat = token[0].upper() #--- global flip if (dowhat == 'FLIP'): flip = True elif (dowhat == 'LOGO'): v('LOGO') if (len(token) == 1): # no arg print logo if (paper_size_no == 3): image_size = 1 * inch canvas.drawImage('logo512x512.png', x + m - .75 * inch, logo_yloc, image_size, image_size, preserveAspectRatio=True) else: canvas.drawImage('logo512x512.png', x + m + .1 * inch + 2.4 * inch, logo_yloc, image_size, image_size, preserveAspectRatio=True) else: # print arg arg = token[1] if (len(arg) == 1): # character. make it big if flip: # They said 'LOGO X', so we draw big fat X where the logo should be canvas.saveState() canvas.translate(x + m + .3 * inch, logo_yloc + 1 * inch) canvas.scale(-1, -1) canvas.setFont('Helvetica-Bold', 70) canvas.drawString(0, 0, token[1]) canvas.restoreState() else: # They said 'LOGO X', so we draw big fat X where the logo should be canvas.setFont('Helvetica-Bold', 70) canvas.drawString(x + m - .45 * inch, logo_yloc + .2 * inch, token[1]) else: # Multiple characters if flip: # They said 'LOGO X', so we draw big fat X where the logo should be canvas.saveState() canvas.translate(x + m + .3 * inch, logo_yloc + 1 * inch) canvas.scale(-1, -1) canvas.setFont('Helvetica-Bold', 20) canvas.drawString(.5 * inch, .55 * inch, arg[0]) canvas.drawString(.5 * inch, .30 * inch, arg[1]) canvas.drawString(.5 * inch, .05 * inch, arg[2]) canvas.restoreState() else: # They said 'LOGO X', so we draw big fat X where the logo should be canvas.setFont('Helvetica-Bold', 20) canvas.drawString(x + m - .45 * inch, logo_yloc + .80 * inch, arg[0]) canvas.drawString(x + m - .45 * inch, logo_yloc + .55 * inch, arg[1]) canvas.drawString(x + m - .45 * inch, logo_yloc + .30 * inch, arg[2]) #--- elif (dowhat == 'BARCODE'): yloc = render_barcode(canvas, x, yloc, token[1], ' '.join(token[2:])) v('genkitlabel(): yloc now: %f' % (yloc)) #--- elif (dowhat == 'KEYVAL'): v('KEYVAL Width : ') yloc = render_key_and_value(canvas, x + m + .350 * inch, yloc, token[1], ' '.join(token[2:])) #yinc = .150*inch ## ## v('genkitlabel() --- line:' + line) return line
def printcards(self): drawCard = self.loadPythonFunc("drawCard", self.session.query(Data).get("card.py").data) page = request.GET.get("page", "card") type = request.GET.get("type", "blank") query = ( self.session.query(Driver, Car, Registration) .join("cars", "registration") .filter(Registration.eventid == self.eventid) ) if type == "blank": registered = [(None, None, None)] elif type == "lastname": registered = query.order_by(func.lower(Driver.lastname), func.lower(Driver.firstname)).all() elif type == "classnumber": registered = query.order_by(Car.classcode, Car.number).all() if page == "csv": # CSV data, just use a template and return c.registered = registered response.headers["Content-type"] = "application/octet-stream" response.headers["Content-Disposition"] = "attachment;filename=cards.csv" response.charset = "utf8" return render_mako("/admin/csv.mako") # Otherwise we are are PDF try: from reportlab.pdfgen import canvas from reportlab.lib.units import inch except: c.text = "<h4>PDFGen not installed, can't create timing card PDF files from this system</h4>" return render_mako("/admin/simple.mako") if page == "letter": # Letter has an additional 72 points Y to space out size = (8 * inch, 11 * inch) else: size = (8 * inch, 5 * inch) if page == "letter" and len(registered) % 2 != 0: registered.append((None, None, None)) # Pages are always two cards per so make it divisible by 2 buffer = cStringIO.StringIO() canvas = canvas.Canvas(buffer, pagesize=size, pageCompression=1) while len(registered) > 0: if page == "letter": canvas.translate(0, 18) # 72/4, bottom margin for letter page (driver, car, reg) = registered.pop(0) drawCard(canvas, c.event, driver, car) canvas.translate(0, 396) # 360+72/2 card size plus 2 middle margins (driver, car, reg) = registered.pop(0) drawCard(canvas, c.event, driver, car) else: (driver, car, reg) = registered.pop(0) drawCard(canvas, c.event, driver, car) canvas.showPage() canvas.save() response.headers["Content-type"] = "application/octet-stream" response.headers["Content-Disposition"] = "attachment;filename=cards.pdf" return buffer.getvalue()
def generate_list(list_id, names, pre_cross_dict={}, title="Coffee list", canvas=None): """ Generate a PDF for a coffee list Parameters: list_id: A (preferably unique) ID for this list. Will be embedded as a QR code into the URL names: A list of names for this list pre_cross_dict: A dictionary mapping names to a number of crosses to pre-draw onto the list title: A heading for the list. Could e.g. include a date. canvas: If set, draw to this canvas. Returns: A StringIO instance with the PDF file, or None if canvas is given. """ assert len(names) <= COFFEE_COUNT_PER_PAGE # Prepare QR code qr_code = tempfile.NamedTemporaryFile(suffix=".png") qr_data = "%s?id=%d" % (COFFEE_HOMEPAGE, list_id) qrcode.make(qr_data, border=0).save(qr_code.name) # Start page, prepare units had_canvas = canvas is not None if not had_canvas: outfile = StringIO.StringIO() canvas = reportlab.pdfgen.canvas.Canvas(outfile, pagesize=COFFEE_SHEET_PAGE_FORMAT) width, height = COFFEE_SHEET_PAGE_FORMAT cm_unit = reportlab.lib.units.cm qr_size = 2 * cm_unit canvas.translate(1.5 * cm_unit, 1.5 * cm_unit) width -= 3 * cm_unit height -= 3 * cm_unit # Draw orientation markers path = canvas.beginPath() path.moveTo(cm_unit, height) path.lineTo(0, height) path.lineTo(0, height - cm_unit) canvas.setLineWidth(5) canvas.setLineJoin(0) canvas.drawPath(path) path = canvas.beginPath() path.moveTo(width, height - cm_unit) path.lineTo(width, height) path.lineTo(width - cm_unit, height) canvas.drawPath(path) path = canvas.beginPath() path.moveTo(width, cm_unit) path.lineTo(width, 0) path.lineTo(width - cm_unit, 0) canvas.drawPath(path) path = canvas.beginPath() path.moveTo(0, cm_unit) path.lineTo(0, 0) path.lineTo(cm_unit, 0) canvas.drawPath(path) canvas.setLineWidth(1) # Draw title canvas.setFont("Helvetica", 16) canvas.drawString(.5 * cm_unit, height - 1 * cm_unit, title) # Draw the QR code and ID canvas.drawImage(qr_code.name, .5 * cm_unit, .5 * cm_unit, qr_size, qr_size) canvas.setFont("Helvetica", 8) canvas.drawString(.5 * cm_unit, .2 * cm_unit, "#%d" % list_id) # Draw bottom text canvas.setFont("Helvetica", 9) ypos = -.2 COFFEE_SHEET_BOTTOM_TEXT = getattr(config, "COFFEE_SHEET_BOTTOM_TEXT", "") for text in COFFEE_SHEET_BOTTOM_TEXT.split("\n"): text = text.strip() canvas.drawString(qr_size + 1. * cm_unit, qr_size - ypos * cm_unit, text) ypos += .5 # Draw grid grid_y = height - 2*cm_unit canvas.line(0, grid_y, width, grid_y) for name in names: new_y = grid_y - COFFEE_LINE_HEIGHT * cm_unit canvas.line(0, grid_y, 0, new_y) canvas.line(width, grid_y, width, new_y) box_start = COFFEE_NAME_FIELD_WIDTH * cm_unit box_width = (width - box_start) / COFFEE_BOXES_PER_LINE pre_draw_crosses = pre_cross_dict.get(name, 0) for i in range(int((width - box_start) / box_width)): canvas.line(box_start, grid_y, box_start, new_y) if pre_draw_crosses > 0: pre_draw_crosses -= 1 cross_margin = 2 canvas.line(box_start + cross_margin, grid_y - cross_margin, box_start + box_width - cross_margin, new_y + cross_margin) canvas.line(box_start + cross_margin, new_y + cross_margin, box_start + box_width - cross_margin, grid_y - cross_margin) box_start += box_width canvas.drawString(.2 * cm_unit, grid_y - (COFFEE_LINE_HEIGHT - .1) * cm_unit, name) grid_y = new_y canvas.line(0, grid_y, width, grid_y) canvas.showPage() if not had_canvas: canvas.save() return outfile
def render_key_and_value(canvas,x,y,lhs,rhs,wraplen=40): """ render a keyword:value pair. Keyword will be in bold """ global xwidth global yheight yinc = .125*inch fontsize = 10 v('render_key_and_value(): xwidth:%f yheight:%f ' % (xwidth,yheight)) # little labels get special treatment if (xwidth/inch <= 3.50): v('render_key_and_value(): fixing wraplen for small labels') wraplen = 25 fontsize = 9 if flip: yinc = -.105*inch else: yinc = .105*inch lhs += ': ' width = stringWidth(lhs,'Helvetica-Bold',fontsize) width *= 1.2 if (width < .75*inch): width = .75*inch # draw keyword canvas.setFont('Helvetica-Bold',fontsize) if flip: canvas.saveState() canvas.translate(x+2.45*inch,y+.25*inch) canvas.scale(-1,-1) canvas.drawString(0, 0, lhs.upper()) canvas.restoreState() else: canvas.drawString(x, y, lhs.upper()) # draw value canvas.setFont('Helvetica',fontsize) yrel = 0 lines = 0 v('render_key_and_value(): y+yrel: %f' %(y+ yrel)) text_line_list = textwrap.wrap(rhs,wraplen) for line in text_line_list: if flip: canvas.saveState() canvas.translate(x+width+1.00*inch, y+yrel+.25*inch) canvas.scale(-1,-1) canvas.drawString(0,0, line) canvas.restoreState() else: canvas.drawString(x+width, y+yrel, line) yrel -= yinc lines += 1 if ((yheight/inch <= 1.25) and (lines == 3)): break yrel -= yinc/2 return y+yrel