def shadow(self, eShadow): """Set the graphics state for shadow if parameters are set.""" if eShadow is not None and eShadow.offset is not None: if eShadow.color.isCmyk: self.b.shadow( upt(eShadow.offset), # Convert units to values blur=upt(eShadow.blur), color=color(eShadow.color).cmyk) else: self.b.shadow(upt(eShadow.offset), blur=upt(eShadow.blur), color=color(eShadow.color).rgb)
def setShadow(self, eShadow): """Sets the InDesign graphics state for shadow if all parameters are set.""" if eShadow is not None and eShadow.offset is not None: if eShadow.color.isCmyk: self.b.shadow(upt(eShadow.offset), blur=upt(eShadow.blur), color=eShadow.color.cmyk) else: self.b.shadow(upt(eShadow.offset), blur=upt(eShadow.blur), color=eShadow.color.rgb)
def _get_h(self): u = None if not self._h: # Width is undefined iwpt = upt(self.iw) if self._w and iwpt: u = self.ih * upt(self._w / iwpt) # Width is lead, calculate height. else: u = self.ih # Undefined and without parent, answer original image width. else: base = dict(base=self.parentH, em=self.em) # In case relative units, use the right kind of base. u = units(self._h, base=base) # Height is lead and defined as not 0 or None. return u
def line(self, p1, p2): """Draw a line from p1 to p2. This method is using the core BezierPath as path to draw on. For a more rich ennvironment use PageBotPath(context). >>> from pagebot.contexts.drawbotcontext import DrawBotContext >>> context = DrawBotContext() >>> context.line(pt(100, 100), pt(200, 200)) >>> context.line((100, 100), (200, 200)) """ p1pt = upt(point2D(p1)) p2pt = upt(point2D(p2)) self.b.line(p1pt, p2pt) # Render tuple of units point
def line(self, p1, p2): """Draw a line from p1 to p2. This method is using the BezierPath as path to draw on. >>> from pagebot import getContext >>> context = getContext() >>> context.newPage(420, 420) >>> context.line(pt(100, 100), pt(200, 200)) >>> context.line((100, 100), (200, 200)) """ p1pt = upt(point2D(p1)) p2pt = upt(point2D(p2)) self.b.line(p1pt, p2pt) # Render tuple of units point
def gradient(self, gradient, origin, w, h): """Define the gradient call to match the size of element e., Gradient position is from the origin of the page, so we need the current origin of e.""" b = self.b start = origin[0] + gradient.start[0] * w, origin[1] + gradient.start[1] * h end = origin[0] + gradient.end[0] * w, origin[1] + gradient.end[1] * h if gradient.linear: if (gradient.colors[0]).isCmyk: colors = [color(c).cmyk for c in gradient.colors] b.cmykLinearGradient(startPoint=upt(start), endPoint=upt(end), colors=colors, locations=gradient.locations) else: colors = [color(c).rgb for c in gradient.colors] b.linearGradient(startPoint=upt(start), endPoint=upt(end), colors=colors, locations=gradient.locations) else: # Gradient must be radial. if color(gradient.colors[0]).isCmyk: colors = [color(c).cmyk for c in gradient.colors] b.cmykRadialGradient(startPoint=upt(start), endPoint=upt(end), colors=colors, locations=gradient.locations, startRadius=gradient.startRadius, endRadius=gradient.endRadius) else: colors = [color(c).rgb for c in gradient.colors] b.radialGradient(startPoint=upt(start), endPoint=upt(end), colors=colors, locations=gradient.locations, startRadius=gradient.startRadius, endRadius=gradient.endRadius)
def textBox(self, sOrBs, r): """Draw the sOrBs text string, can be a str or BabelString, including a DrawBot FormattedString in rectangle r.""" if not isinstance(sOrBs, str): sOrBs = sOrBs.s # Assume here is's a BabelString with a FormattedString inside. x, y, w, h = r t = self._drawing.text(sOrBs, insert=point2D(upt(x, y)), stroke=self._svgStroke, stroke_width=upt(self._strokeWidth), fill=self._svgFill, font_size=upt(self._fontSize), font_family=self._font) self._drawing.add(t)
def translatePoint(self, p): """Simpler function to translate a point based on origin coordinates (`self._ox` and `self._oy`). TODO: merge with getTransformed. """ x, y = point2D(upt(p)) x = self._ox + x '''Because the origin is at the bottom, like in DrawBot and as opposed to Flat, we need to subtract all vertical coordinates from the page height before an object gets placed. In case of (bounding) boxes, we also need to subtract the box height.''' y = self.height - (self._oy + y) return upt(x, y)
def circle(self, x, y, r): """Draw an circle in square, with radius r and (x,y) as middle.""" xpt, ypt, rpt = upt(x, y, r) shape = self._getShape() if shape is not None: self.ensure_page() self.page.place(shape.circle(xpt, ypt, rpt))
def text(self, sOrBs, p): """Draw the sOrBs text string, can be a str or BabelString, including a DrawBot FormattedString at position p.""" if not isinstance(sOrBs, str): sOrBs = sOrBs.s # Assume here is's a BabelString with a FormattedString inside. ppt = point2D(upt(p)) self.b.text(sOrBs, ppt) # Render point units to value tuple
def font(self, font, fontSize=None): """Set the current font, in case it is not defined in a formatted string. Font can be a Font instance, or a full font file path, or an abbreveation that can be found by family or by findFont. >>> from pagebot.fonttoolbox.objects.font import findFont >>> from pagebot.fonttoolbox.fontpaths import * >>> from pagebot.paths import DEFAULT_FONT_PATH >>> pbFonts = getPageBotFontPaths() >>> print(len(pbFonts)) 57 >>> font = findFont('Roboto-Regular') >>> print(font) <Font Roboto-Regular> >>> context = FlatContext() >>> print(context) <FlatContext> >>> context.font(font.path) >>> context._font.endswith('/Roboto-Regular.ttf') True >>> context.font('OtherFont', 12) # Font does not exists, font path is set to DEFAULT_FONT_PATH >>> context._font == DEFAULT_FONT_PATH True >>> context._fontSize # Renders to pt-unit 12 """ from pagebot.fonttoolbox.fontpaths import getFontPathOfFont self._font = getFontPathOfFont(font) # Convert name or path to font path. if fontSize is not None: self._fontSize = upt(fontSize)
def roundedRect(self, x, y, w, h, offset=25): """Draw a rectangle in the canvas. This method is using the Bézier path as path to draw on. TODO: move to elements. >>> from pagebot import getContext >>> context = getContext() >>> context.roundedRect(pt(0), pt(0), pt(100), pt(100)) >>> context.roundedRect(0, 0, 100, 100) """ xPt, yPt, wPt, hPt, offsetPt = upt(x, y, w, h, offset) path = self.newPath() path.moveTo((xPt + offsetPt, yPt)) path.lineTo((xPt + wPt - offsetPt, yPt)) path.curveTo((xPt + wPt, yPt), (xPt + wPt, yPt), (xPt + wPt, yPt + offsetPt)) path.lineTo((xPt + wPt, yPt + hPt - offsetPt)) path.curveTo((xPt + wPt, yPt + hPt), (xPt + wPt, yPt + hPt), (xPt + wPt - offsetPt, yPt + hPt)) path.lineTo((xPt + offsetPt, yPt + hPt)) path.curveTo((xPt, yPt + hPt), (xPt, yPt + hPt), (xPt, yPt + hPt - offsetPt)) path.lineTo((xPt, yPt + offsetPt)) path.curveTo((xPt, yPt), (xPt, yPt), (xPt + offsetPt, yPt)) self.closePath() self.drawPath(path)
def _get_w(self): """Get the intended width and calculate the new scale, validating the width to the image minimum width and the height to the image minimum height. If not self._h is defined, then the proportion is recalculated, depending on the ratio of the image.""" u = None if not self._w: # Width is undefined ihpt = upt(self.ih) if self._h and ihpt: u = self.iw * upt(self._h / ihpt) # Height is lead, calculate width. else: u = self.iw # Undefined and without parent, answer original image width. else: base = dict(base=self.parentW, em=self.em) # In case relative units, use the right kind of base. u = units(self._w, base=base) # Width is lead and defined as not 0 or None. return u
def saveScaledCache(self, view): """If the self.saveScaled is True and the reduction scale is inside the range, then create a new cached image file, if it does not already exist. Scaling images in the DrawBot context is a fast operation, so always worthwhile to creating PNG from large export PDF files. In case the source is a PDF, then use self.index to request for the page. """ if self.path is None or not self.saveScaled: return if not self.iw or not self.ih: # Make sure not zero, to avoid division print('Image.saveScaledCache: %dx%d zero image size' % (self.iw, self.ih)) return extension = path2Extension(self.path) resolutionFactor = self.resolutionFactors.get(extension, 1) # Translate the extension to the related type of output. exportExtension = CACHE_EXTENSIONS.get(extension, extension) resW = self.w * resolutionFactor resH = self.h * resolutionFactor sx, sy = upt(resW / self.iw, resH / self.ih) if not self.saveScaled and 0.8 <= sx and 0.8 <= sy: # If no real scale reduction, then skip. Never enlarge. return # Scale the image the cache does not exist already. # A new path is answers for the scaled image file. Reset the (self.iw, self.ih) self.path = self.context.scaleImage( path=self.path, w=resW, h=resH, index=self.index, showImageLoresMarker=self.showImageLoresMarker or view.showImageLoresMarker, exportExtension=exportExtension )
def textBox(self, sOrBs, r=None, clipPath=None, align=None): """Draw the sOrBs text string, can be a str or BabelString, including a DrawBot FormattedString in rectangle r. NOTE: signature differs from DrawBot. >>> from pagebot.toolbox.units import pt >>> from pagebot.contexts.drawbotcontext import DrawBotContext >>> context = DrawBotContext() >>> context.textBox('ABC', (10, 10, 200, 200)) """ if hasattr(sOrBs, 's'): sOrBs = sOrBs.s # Assume here is's a BabelString with a FormattedString inside. else: sOrBs = str(sOrBs) # Otherwise convert to string if it is not already if clipPath is not None: box = clipPath.bp self.b.textBox(sOrBs, clipPath.bp) # Render rectangle units to value tuple elif isinstance(r, (tuple, list)): # Render rectangle units to value tuple box = upt(r) else: raise ValueError('%s.textBox has no box or clipPath defined' % self.__class__.__name__) self.b.textBox(sOrBs, box, align=None)
def getBaselines(self, bs, w=None, h=None): """Answers the dictionary of baseline positions, relative to the firstline. If @h is defined, the clip on the height. See also doctests/string*.txt. """ if h is None: h = XXXL baselines = {} if w is not None: lines = self.getTextLines(bs, w, h=h) else: lines = bs.lines if lines: y = lines[0].y for lineInfo in lines: if lineInfo.y - y > h: break baselines[upt(lineInfo.y - y)] = lineInfo return baselines
def text(self, bs, p): """Place the babelstring instance at position p. The position can be any 2D or 3D points tuple. Currently the z-axis is ignored. The FlatContext version of the BabelString should contain Flat.text. NOTE:in the Flat model the position is an attribute of the string, therefore strings cannot be reused to be displayed on multiple positions. >>> context = FlatContext() >>> style = dict(font='Roboto-Regular', fontSize=pt(12)) >>> bs = context.newString('ABC', style=style) >>> bs.__class__.__name__ 'FlatString' >>> context.newDocument(1000, 1000) >>> context.newPage() >>> context.text(bs, (100, 100)) """ if not isinstance(bs, FlatString): if isinstance(bs, str): bs = self.newString(bs) else: print('wrong type %s' % type(s)) # TODO: raise error. assert self.page is not None, 'FlatString.text: self.page is not set.' placedText = self.page.place(bs.s) xpt, ypt = point2D(upt(p)) placedText.position(xpt, ypt) # Render unit tuple to value tuple
def roundedRect(self, x, y, w, h, offset=25): """Draw a rectangle in the canvas. This method is using the core BezierPath as path to draw on. For a more rich environment use PageBotPath(context) instead. TODO: move to elements. >>> context = PdfLibContext() >>> context.roundedRect(pt(0), pt(0), pt(100), pt(100)) >>> context.roundedRect(0, 0, 100, 100) """ if 0: xPt, yPt, wPt, hPt, offsetPt = upt(x, y, w, h, offset) path = self.newPath() path.moveTo((xPt+offsetPt, yPt)) path.lineTo((xPt+wPt-offsetPt, yPt)) path.curveTo((xPt+wPt, yPt), (xPt+wPt, yPt), (xPt+wPt, yPt+offsetPt)) path.lineTo((xPt+wPt, yPt+hPt-offsetPt)) path.curveTo((xPt+wPt, yPt+hPt), (xPt+wPt, yPt+hPt), (xPt+wPt-offsetPt, yPt+hPt)) path.lineTo((xPt+offsetPt, yPt+hPt)) path.curveTo((xPt, yPt+hPt), (xPt, yPt+hPt), (xPt, yPt+hPt-offsetPt)) path.lineTo((xPt, yPt+offsetPt)) path.curveTo((xPt, yPt), (xPt, yPt), (xPt+offsetPt, yPt)) self.closePath() self.drawPath(path)
def text(self, bs, p): """Place the babelstring instance at position p. The position can be any 2D or 3D points tuple. Currently the z-axis is ignored. The FlatContext version of the BabelString is supposed to contain Flat.text. Note that in the Flat model, the positions is an attribute of the string, so strings cannot be reused to show on multiple positions. >>> context = FlatContext() >>> style = dict(font='Roboto-Regular', fontSize=pt(12)) >>> bs = context.newString('ABC', style=style) >>> bs.__class__.__name__ 'FlatString' >>> context.newDocument(1000, 1000) >>> context.newPage() >>> context.text(bs, (100, 100)) """ assert isinstance( bs, FlatString ), 'FlatString.text: bs not of type %s' % FlatString.__name__ assert self.page is not None, 'FlatString.text: self.page is not set.' placedText = self.page.place(bs.s) xpt, ypt = point2D(upt(p)) placedText.position(xpt, ypt) # Render unit tuple to value tuple
def addPoint(self, point, segmentType=None, smooth=False, name=None, identifier=None, **kwargs): msg = '%s.addPoint: Point "%s" is not a tuple or a Point' % ( self.__class__.__name__, point) assert isinstance(point, (list, tuple, BaseBezierPoint)), msg if isinstance(point, (list, tuple)): px, py = upt(point2D(point)) else: px = point.x py = point.y segmentType = segmentType or point.segmentType smooth = smooth or point.smooth identifier = identifier or point.identifier self.bp.addPoint((px, py), segmentType=segmentType, smooth=smooth, name=name, identifier=identifier, **kwargs)
def line(self, p1, p2): """Draw a line from p1 to p2. >>> path = '~/SvgContext_line.svg' >>> context = SvgContext() >>> context.stroke((1, 0, 0.5), 30) >>> context.line((0, 100), (300, 300)) >>> context.stroke((0.6, 0.1, 0.5), 20) >>> context.line((300, 150), (200, 100)) >>> context.saveDocument(path) >>> #r = os.system('open %s' % path) """ line = self._drawing.line(upt((self._ox+p1[0]), (self._oy+p1[1])), upt((self._ox+p2[0]), (self._oy+p2[1])), stroke_width=upt(self._strokeWidth), stroke=color(self._stroke).css) self._drawing.add(line)
def build(self, view, origin=ORIGIN, **kwargs): """Default drawing method just drawing the frame. Probably will be redefined by inheriting element classes.""" p = pointOffset(self.origin, origin) p = self._applyScale(view, p) px, py, _ = p = self._applyAlignment(p) # Ignore z-axis for now. pt, pr, pb, pl = self.padding self._applyRotation(view, p) self.buildFrame(view, p) # Draw optional frame or borders. # Let the view draw frame info for debugging, in case view.showFrame == True # and self.isPage or if self.showFrame. Mark that we are drawing background here. view.drawPageMetaInfoBackground(self, p) if self.clipPath is not None: # If there is a clipPath defined, use it. clipPath = self.clipPath else: # Otherwise use self.box as clipRect when drawing the child elements. clipPath = self.context.newPath() # move to a point clipPath.moveTo(upt(px+pl, py+pb)) # line to points of the clip rect. clipPath.lineTo(upt(px+pl, py+pb+self.ph)) clipPath.lineTo(upt(px+pl+self.pw, py+pr+self.ph)) clipPath.lineTo(upt(px+pl+self.pw, py+pb)) clipPath.lineTo(upt(px+pl, py+pb)) # close the path clipPath.closePath() #self.context.fill((0, 1, 0)) #self.context.drawPath(clipPath) self.context.save() # set the path as a clipping path #self.context.clipPath(clipPath) # Build the child elements. Default this is the ImageData instance, but there # may be other elemnents added too in any particular order. self.buildChildElements(view, p) self.context.restore() # Let the view draw frame info for debugging, in case view.showFrame == True # and self.isPage or if self.showFrame. Mark that we are drawing foreground here. view.drawPageMetaInfo(self, p) self._restoreRotation(view, p) self._restoreScale(view) view.drawElementInfo(self, origin) # Depends on flag 'view.showElementInfo'
def stroke(self, c, strokeWidth=None): c = color(c) if c is noColor: self._fill = 'none' else: r, g, b = c.rgb self._fill = self.b.rgb(100*r, 100*g, 100*b, '%') self._strokeWidth = upt(strokeWidth or pt(1))
def textBox(self, bs, r): """ ... FIXME: Not using width and height here?""" xpt, ypt, _, _ = upt(r) placedText = self.page.place(bs.s) placedText.position(xpt, ypt)
def font(self, fontName, fontSize=None): # FIXME: fontSize? self.b.font(fontName) # Also renders fontSize unit to value. if fontSize is not None: fspt = upt(fontSize) self.b.fontSize(fspt)
def arcTo(self, pt1, pt2, radius): """Arc from one point to an other point with a given radius. >>> from pagebot.toolbox.units import p >>> from pagebot.contexts import getContext >>> context = getContext() >>> path = PageBotPath(context=context) >>> path.moveTo((0, 0)) >>> p1 = pt(100), p(6) >>> p2 = p(10), pt(200) >>> r = pt(300) >>> path.arcTo(p1, p2, r) >>> path.closePath() """ pt1 = upt(point2D(pt1)) pt2 = upt(point2D(pt2)) ptRadius = upt(radius or DEFAULT_WIDTH / 2) self.bp.arcTo(pt1, pt2, ptRadius)
def circle(self, x, y, r): """Circle draws a DrawBot oval with (x,y) as middle point and radius r. >>> from pagebot.toolbox.color import color, blackColor >>> path = '~/SvgContext_circle.svg' >>> context = SvgContext() >>> context.fill(color(r=1, g=0, b=0.5)) >>> context.circle(pt(0), pt(100), pt(300)) >>> context.stroke(blackColor, pt(20)) >>> context.fill(color(r=0.6, g=0.1, b=0.5)) >>> context.circle(pt(300), pt(150), pt(200)) >>> context.saveDocument(path) >>> #r = os.system('open %s' % path) """ circle = self._drawing.circle(center=upt((self._ox+x+r), (self._oy+y+r)), r=upt(r), stroke_width=upt(self._strokeWidth), stroke=color(self._stroke).css, fill=color(self._fill).css) self._drawing.add(circle)
def circle(self, x, y, r): u"""Circle draws a DrawBot oval with (x,y) as middle point and radius r. >>> context = DrawBotContext() >>> context.circle(pt(100), pt(200), pt(50)) >>> context.circle(100, 200, 50) """ xpt, ypt, rpt = upt(x, y, r) self.b.oval(xpt - rpt, ypt - rpt, rpt * 2, rpt * 2) # Render the unit values
def rect(self, x, y, w, h): """Draw a rectangle in the canvas. >>> context = DrawBotContext() >>> context.rect(pt(0), pt(0), pt(100), pt(100)) >>> context.rect(0, 0, 100, 100) """ xpt, ypt, wpt, hpt = upt(x, y, w, h) self.b.rect(xpt, ypt, wpt, hpt) # Render units to points for DrawBot.
def fontSize(self, fontSize): """Set the font size in the context. >>> from pagebot.toolbox.units import pt >>> context = DrawBotContext() >>> context.fontSize(pt(12)) """ fspt = upt(fontSize) self.b.fontSize(fspt) # Render fontSize unit to value