def build(self, view, origin=ORIGIN, **kwargs): """Draws all elements of this page in DrawBot. Note that this method is only used in case pages are drawn as element on another page. In normal usage, pages get drawn by PageView.build""" p = pointOffset(self.origin, origin) # Ignore z-axis for now. view.drawPageMetaInfo(self, p, background=True) # If there are child elements, draw them over the text. Build child # elements, depending in context build implementations. self.buildChildElements(view, p, **kwargs) # Draw addition page info, such as crop-mark, registration crosses, # etc. if parameters are set. view.drawPageMetaInfo(self, p, background=False)
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 build(self, view, origin=ORIGIN, **kwargs): """Default drawing method just drawing the frame. Probably will be redefined by inheriting element classes.""" c = self.context p = pointOffset(self.origin, origin) p = self._applyScale(view, p) p = self._applyAlignment(p) # Ignore z-axis for now. self.buildFrame(view, p) # Draw optional background fill, frame or borders. # Let the view draw frame info for debugging, in case view.showFrame == True view.drawElementFrame(self, p) # Draw that actual content of the element by stacked specimen rectangles. self.drawStacked(view, p, **kwargs) self._restoreScale(view) view.drawElementInfo(self, origin) # Depends on flag 'view.showElementInfo'
def build_html(self, view, origin=None, drawElements=True): p = pointOffset(self.origin, origin) p = self._applyScale(p) px, py, _ = p = self._applyAlignment(p) # Ignore z-axis for now. if self.drawBefore is not None: # Call if defined self.drawBefore(self, view, p) if drawElements: self.buildChildElements(view, p) if self.drawAfter is not None: # Call if defined self.drawAfter(self, view, p) self._restoreScale(view) view.drawElementInfo(self, origin) # Depends on css flag 'showElementInfo'
def drawMissingElementRect(self, e, origin): """When designing templates and pages, this will draw a filled rectangle on the element bounding box (if self.css('missingElementFill' is defined) and a cross, indicating that this element has missing content (as in unused image frames). Only draw if the list self.showGrid contains proper types of grid names. >>> from pagebot import getContext >>> context = getContext() >>> from pagebot.elements.element import Element >>> from pagebot.style import getRootStyle >>> from pagebot.elements.views.pageview import PageView >>> style = getRootStyle() # Get default values >>> e = Element(style=style) # Works on generic elements as well as pages. >>> view = PageView(context=context, style=style) >>> view.showMissingElement = True >>> view.drawMissingElementRect(e, (0, 0)) """ if (self.showMissingElement and e.isPage) or e.showMissingElement: context = self.context p = pointOffset(e.origin, origin) p = self._applyScale(e, p) px, py, _ = e._applyAlignment(p) # Ignore z-axis for now. context.saveGraphicState() context.setShadow(self.shadow) sMissingElementFill = self.css('viewMissingElementFill', noColor) if sMissingElementFill is not noColor: context.fill(sMissingElementFill) context.stroke(noColor) context.rect(px, py, self.w, self.h) # Draw crossed rectangle. context.fill(noColor) context.stroke(blackColor, pt(0.5)) context.rect(px, py, self.w, self.h) context.newPath() context.moveTo((px, py)) context.lineTo((px + self.w, py + self.h)) context.moveTo((px + self.w, py)) context.lineTo((px, py + self.h)) context.drawPath() context.restoreGraphicState() e._restoreScale(self)
def draw(self, view, origin): c = self.doc.context p = pointOffset(self.origin, origin) p = self._applyScale(view, p) px, py, _ = self._applyAlignment(p) # Ignore z-axis for now. if self.drawBefore is not None: # Call if defined self.drawBefore(self, view, p) fillColor = self.style.get('fill') if fillColor is not None: c.fill(fillColor) c.stroke(None) stepX = self.w / (self.sizeX+1) stepY = self.h / (self.sizeY+1) """Add more parametric layout behavior here.""" for indexX in range(self.sizeX+1): for indexY in range(self.sizeY+1): ox = 30 oy = 25 ppx = ox + px + indexX * stepX ppy = oy + py + indexY * stepY if self.locations is not None: location = choice(self.locations) else: location = self.getRandomLocation() glyphPathScale = self.fontSize/self.font.info.unitsPerEm fillColor = self.style.get('textFill') or (0, 0, 0) c.drawGlyphPath(self.font.ttFont, self.glyphNames[0], px, py, location, s=glyphPathScale, fillColor=fillColor) if self.recipeAxes: recipe = self.location2Recipe(location) bs = c.newString(recipe, fontSize=4, fill=blackColor) w, h = bs.size() c.text(bs, ppx - stepX/4, ppy - 24) # Bit of hack, we need the width of the glyph here. if len(self.recipeAxes) > 3: recipe = self.location2Recipe(location, 3, 6) bs = c.newString(recipe, fontSize=4, fill=blackColor) w, h = bs.size() c.text(bs, point=(ppx - stepX/4 + 30, ppy - 24)) # Bit of hack, we need the width of the glyph here. if self.drawAfter is not None: # Call if defined self.drawAfter(self, view, p)
def build(self, view, origin=ORIGIN, **kwargs): c = self.context p = pointOffset(self.origin, origin) p = self._applyScale(view, p) px, py, _ = self._applyAlignment(p) # Ignore z-axis for now. view.drawElementFrame(self, p, **kwargs) fillColor = self.style.get('fill') #fillColor = (0, 0, 0) #if fillColor is not None: # c = self.doc.context # c.setFillColor(fillColor) # c.setStrokeColor(None) #else: # fillColor = (0, 0, 0) glyphPathScale = self.fontSize / self.font.info.unitsPerEm context.drawGlyphPath(c, self.font.ttFont, self.glyphNames[0], px, py, self.location, glyphPathScale, fillColor) self.buildChildElements(view, p)
def build(self, view, origin=ORIGIN, **kwargs): """Draws the circle info-graphic, showing most info about the variable font as can be interpreted from the file.""" p = pointOffset(self.origin, origin) p = self._applyScale(view, p) px, py, _ = self._applyAlignment(p) # Ignore z-axis for now. # Let the view draw frame info for debugging, in case view.showFrame == True view.drawElementFrame(self, p) # Draw actual circle self._drawFontCircle(px, py) for e in self.elements: e.build(view, p, **kwargs) self._restoreScale(view) view.drawElementInfo(self, origin) # Depends on css flag 'showElementInfo'
def build_drawBot(self, view, origin): """This method is called if the view is used as a placable element inside another element, such as a Page or Template. """ p = pointOffset(self.origin, origin) p = self._applyScale(view, p) px, py, _ = p = self._applyAlignment(p) # Ignore z-axis for now. if self.drawBefore is not None: # Call if defined self.drawBefore(self, view, p) self.drawElementFrame(view, p) # In case the view itself is used on a page. for page in self.elements: page.build(view, p) if self.drawAfter is not None: # Call if defined self.drawAfter(self, view, p) self._restoreScale(view)
def build(self, view, origin=ORIGIN, drawElements=True): """Draw the image in the calculated scale. Since we need to use the image by scale transform, all other measure (position, lineWidth) are scaled back to their original proportions. If stroke is defined, then use that to draw a frame around the image. Note that the (sx, sy) is already scaled to fit the padding position and size.""" context = self.context # Get current context and builder. p = pointOffset(self.origin, origin) p = self._applyScale(view, p) px, py, _ = p = self._applyAlignment(p) # Ignore z-axis for now. self._applyRotation(view, p) if self.path is None or not os.path.exists(self.path) or not self.iw or not self.ih: # TODO: Also show error, in case the image does not exist, to differ from empty box. print('Cannot display image %s' % self) _, _, pb, pl = self.padding context.stroke(0.5, 0.5) context.fill(0.8) context.rect(px+pl, py+pb, self.pw, self.ph) else: #sx = self.w / self.iw #sy = self.h / self.ih #context.scale(sx.rv, sy.rv) if self.imo is not None: self.imo.image(self.path, (0, 0), alpha=self._getAlpha()) context.image(self.imo, p=(px, py), w=self.w, h=self.h, alpha=self._getAlpha()) else: context.image(self.path, p=(px, py), w=self.w, h=self.h, alpha=self._getAlpha()) # TODO: Draw optional (transparant) forground color? if drawElements: self.buildChildElements(view, p) self._restoreRotation(view, p) self._restoreScale(view) view.drawElementInfo(self, origin)
def build(self, view, origin, drawElements=True): """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. self._applyRotation(view, p) # No automatic frame drawing on Paths elements #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.drawPageMetaInfo(self, p, background=True) if self.drawBefore is not None: # Call if defined self.drawBefore(self, view, p) for path in self.paths: # Get the style file/stroke if defined in the path. Otherwise use the settings of self. context = self.context context.fill(path.style.get('fill', self.fill)) context.stroke(path.style.get('stroke', self.stroke), path.style.get('strokeWidth', self.strokeWidth)) context.drawPath(path, p=p, sx=self.scaleX, sy=self.scaleY) if drawElements: # If there are child elements, recursively draw them over the pixel image. self.buildChildElements(view, p) if self.drawAfter is not None: # Call if defined self.drawAfter(self, view, p) # 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, background=False) self._restoreRotation(view, p) self._restoreScale(view) view.drawElementInfo(self, origin) # Depends on flag 'view.showElementInfo'
def build(self, view, origin=ORIGIN, drawElements=True): """Like "rolled pasteboard" galleys can draw themselves, if the Composer decides to keep them in tact, instead of select, pick & choose elements, until the are all part of a page. In that case the w/h must have been set by the Composer to fit the containing page.""" p = pointOffset(self.origin, origin) p = self._applyScale(view, p) px, py, _ = self._applyAlignment(p) # Ignore z-axis for now. # Let the view draw frame info for debugging, in case # view.showFrame == True view.drawElementFrame(self, p) if self.drawBefore is not None: self.drawBefore(self, view, p) self.context.fill(self.css('fill')) # Find old-paper background color gw, gh = self.getSize() self.context.rect(px, py, gw, gh) if drawElements: hook = 'build_' + self.context.b.PB_ID # Don't call self.buildElements, we want to track the vertical # positions. gy = 0 for e in self.elements: if not e.show: continue # @@@ Find space and do more composition if hasattr(e, hook): getattr(e, hook)(view, (px, py + gy)) else: # No implementation for this context, call default building # method for this element. e.build(view, (px, py + gy)) gy += e.h if self.drawAfter is not None: self.drawAfter(self, view, p) self._restoreScale(view) view.drawElementInfo(self, origin)
def build(self, view, origin, drawElements=True): """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. self.draw(view, p) if self.drawBefore is not None: # Call if defined self.drawBefore(self, view, p) if drawElements: # If there are child elements, recursively draw them over the pixel image. self.buildChildElements(view, p) if self.drawAfter is not None: # Call if defined self.drawAfter(self, view, p) self._restoreScale(view) view.drawElementInfo(self, origin) # Depends on flag 'view.showElementInfo'
def build(self, view, origin=ORIGIN, drawElements=True): p = pointOffset(self.origin, origin) p = self._applyScale(view, p) px, py, _ = p = self._applyAlignment(p) # Ignore z-axis for now. self.drawFrame(view, p) # Draw optional frame or borders. # Let the view draw frame info for debugging, in case view.showFrame == True view.drawElementFrame(self, p) if self.drawBefore is not None: # Call if defined self.drawBefore(self, view, p) if drawElements: self.buildChildElements(view, p) if self.drawAfter is not None: # Call if defined self.drawAfter(self, view, p) self._restoreScale(view) view.drawElementInfo(self, origin) # Depends on css flag 'showElementInfo'
def build(self, view, origin, drawElements=True): """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. self.buildFrame(view, p) # Draw optional frame or borders. # Let the view draw frame info for debugging, in case view.showFrame == True view.drawElementFrame(self, p) if self.drawBefore is not None: # Call if defined self.drawBefore(self, view, p) self.drawMatrix(view, p) if self.drawAfter is not None: # Call if defined self.drawAfter(self, view, p) self._restoreScale(view) view.drawElementInfo(self, origin) # Depends on flag 'view.showElementInfo'
def drawFlowConnections(self, e, origin): """If rootStyle.showFlowConnections is True, then draw the flow connections on the page, using their stroke/width settings of the style.""" context = self.context p = pointOffset(e.origin, origin) p = e._applyScale(self, p) px, py, _ = p = e._applyAlignment(p) # Ignore z-axis for now. if (self.showFlowConnections and e.isPage) or e.showFlowConnections: fmf = 0.15 #self.css('viewFlowCurvatureFactor', 0.15) for startE in e.elements: nextE = startE.next if nextE is not None: # For all the flow sequences found in the page, draw flow arrows at offset (ox, oy) # This offset is defined by optional sx = startE.right sy = startE.bottom nx = nextE.left ny = nextE.top xm = (nx + sx) / 2 ym = (ny + sy) / 2 xb1 = xm * upt(ny - sy) * fmf yb1 = ym * upt(nx - sx) * fmf xb2 = xm * upt(ny - sy) * fmf yb2 = ym * upt(nx - sx) * fmf context.fill(noColor) context.stroke(color(0), 1) context.newPath() context.moveTo((sx, sy)) context.curveTo( (xb1, yb1), (xb2, yb2), (nx, ny) ) #((ax1+ax2)/2, (ay1+ay2)/2)) # End in middle of arrow head. context.drawPath()
def build(self, view, origin=ORIGIN, **kwargs): context = self.context # Get current context and builder. b = context.b # This is a bit more efficient than self.b once we got context p = pointOffset(self.origin, origin) p = self._applyScale(view, p) px, py, _ = p = self._applyAlignment(p) # Ignore z-axis for now. self._applyRotation(view, p) doDraw = False if self.fill not in (None, noColor): context.fill(self.fill) doDraw = True if self.stroke not in (None, noColor) and self.strokeWidth: context.stroke(self.stroke) context.strokeWidth(self.strokeWidth) doDraw = True if doDraw: context.rect(px, py, self.w, self.h) if self.drawAfter is not None: # Call if defined self.drawAfter(self, view, p) # 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) # Supposedly drawing outside rotation/scaling mode, so the origin of # the element is visible. view.drawElementOrigin(self, origin) self._restoreRotation(view, p) self._restoreScale(view) view.drawElementInfo(self, origin) # Depends on flag 'view.showElementInfo'
def drawElementOrigin(self, e, origin): if not (self.showOrigin or e.showOrigin): return context = self.context px, py, _ = pointOffset(e.origin, origin) S = e.css('viewInfoOriginMarkerSize', pt(5)) # Draw origin of the element fill = e.css('viewInfoOriginMarkerFill', noColor) stroke = e.css('viewInfoOriginMarkerStroke', blackColor) width = e.css('viewInfoOriginMarkerStrokeWidth', pt(0.25)) context.fill(fill) # Transparant fill, so we can see the marker on dark backgrounds. context.stroke(stroke, width) context.oval(px-S, py-S, 2*S, 2*S) context.line((px-S, py), (px+S, py)) context.line((px, py-S), (px, py+S)) if (self.showDimensions and e.isPage) or e.showDimensions: bs = context.newString(e.xy, style=dict(font=self.css('viewInfoFont'), fontSize=self.css('viewInfoFontSize'), leading=self.css('viewInfoLeading'), textFill=color(0.1))) w, h = bs.size context.text(bs, (px - w/2, py + S*1.5))
def build(self, view, origin=ORIGIN, drawElements=True): """Draw the oval in the current context canvas. >>> e = Oval(x=0, y=20, w=500, h=3) >>> e.xy (0pt, 20pt) >>> e.size (500pt, 3pt) """ context = self.context # Get current context and builder. p = pointOffset(self.origin, origin) p = self._applyScale(view, p) px, py, _ = p = self._applyAlignment(p) # Ignore z-axis for now. #self.buildFrame(view, p) # Draw optional frame or borders. # Let the view draw frame info for debugging, in case # view.showFrame == True view.drawElementFrame(self, p) if self.drawBefore is not None: # Call if defined self.drawBefore(self, view, p) context.fill(self.css('fill', noColor)) context.stroke(self.css('stroke', noColor), self.css('strokeWidth')) context.oval(px, py, self.w, self.h) if drawElements: self.buildChildElements(view, p) if self.drawAfter is not None: # Call if defined self.drawAfter(self, view, p) self._restoreScale(view) view.drawElementInfo(self, origin)
def draw(self, view, origin): c = self.doc.context p = pointOffset(self.origin, origin) p = self._applyScale(view, p) px, py, _ = self._applyAlignment(p) # Ignore z-axis for now. fillColor = self.style.get('fill') if fillColor is not None: c.fill(fillColor) c.stroke((0.8, 0.8, 0.8), 0.5) c.rect(px, py, self.w, self.h) if len(self.dimensions) == 1: raise ValueError('Not supporting 1 axis now') if len(self.dimensions) > 2: raise ValueError('Not supporting >2 axis now') axisNames = sorted(self.dimensions.keys()) axisX = axisNames[0] sizeX = self.dimensions[axisX] axisY = axisNames[1] sizeY = self.dimensions[axisY] stepX = self.w / (sizeX + 1) stepY = self.h / (sizeY + 1) """Add more parametric layout behavior here.""" RANGE = 1000 for indexX in range(sizeX + 1): for indexY in range(sizeY + 1): ox = 30 oy = 25 ppx = ox + px + indexX * stepX ppy = oy + py + indexY * stepY self.location[axisX] = indexX * RANGE / sizeX self.location[axisY] = indexY * RANGE / sizeY glyphPathScale = self.fontSize / self.font.info.unitsPerEm c.drawGlyphPath(c, self.font.ttFont, self.glyphNames[0], ppx, ppy, self.location, s=glyphPathScale, fillColor=(0, 0, 0)) bs = c.newString('%s %d\n%s %d' % (axisX, indexX * RANGE / sizeX, axisY, indexY * RANGE / sizeY), fontSize=6, fill=blackColor) w, h = bs.size() c.text(bs, ppx - stepX / 4, ppy - 16) # Bit of hack, we need the width of the glyph here. bs = c.newString('Other axes: %s' % self.location, fontSize=6, fill=blackColor) w, h = bs.size() c.text(bs, px, py - 16)
def build(self, view, origin, drawElements=True): """Draw the text on position (x, y). Draw background rectangle and/or frame if fill and/or stroke are defined.""" context = view.context # Get current context p = pointOffset(self.origin, origin) p = self._applyScale(view, p) px, py, _ = p = self._applyAlignment(p) # Ignore z-axis for now. self._applyRotation(view, p) # Let the view draw frame info for debugging, in case view.showFrame == True view.drawElementFrame(self, p) self.buildFrame(view, p) # Draw optional background, frame or borders. if self.drawBefore is not None: # Call if defined self.drawBefore(self, view, p) # self has its own baseline drawing, derived from the text, instace of self.baselineGrid. self.drawBaselines( view, px, py, background=True) # In case there is baseline at the back # Draw the text with horizontal and vertical alignment tw, th = self.bs.size xOffset = yOffset = 0 if self.css('yTextAlign') == MIDDLE: yOffset = (self.h - self.pb - self.pt - th) / 2 elif self.css('yTextAlign') == BOTTOM: yOffset = self.h - self.pb - self.pt - th if self.css('xTextAlign') == CENTER: xOffset = (self.w - self.pl - self.pr - tw) / 2 elif self.css('xTextAlign') == RIGHT: xOffset = self.w - self.pl - self.pr - tw textShadow = self.textShadow if textShadow: context.saveGraphicState() context.setShadow(textShadow) # Set the hyphenation flag from style, as in DrawBot this is set by a global function, # not as FormattedString attribute. context.language(self.css('language', DEFAULT_LANGUAGE)) context.hyphenation(bool(self.css('hyphenation'))) context.textBox( self.bs, (px + self.pl + xOffset, py + self.pb - yOffset, self.pw, self.ph)) if textShadow: context.restoreGraphicState() if drawElements: # If there are child elements, recursively draw them over the pixel image. self.buildChildElements(view, origin) # self has its own baseline drawing, derived from the text, instace of self.baselineGrid. self.drawBaselines( view, px, py, background=False) # In case there is baseline at the front if view.showTextOverflowMarker and self.isOverflow(): # TODO: Make this work for FlatContext too self._drawOverflowMarker_drawBot(view, px, py) if self.drawAfter is not None: # Call if defined self.drawAfter(self, view, p) self._restoreRotation(view, p) self._restoreScale(view) view.drawElementInfo(self, origin) # Depends on css flag 'showElementInfo'
def build(self, view, origin=ORIGIN, **kwargs): """Draws the text on position (x, y). Draws a background rectangle and / or frame if fill and / or stroke are defined. >>> from pagebot.document import Document >>> from pagebot.elements import * >>> from pagebot.contexts import getContext >>> from pagebot.toolbox.units import pt, em >>> context = getContext() >>> W, H = 500, 400 >>> doc = Document(w=W, h=H, context=context) >>> page = doc[1] >>> fontSize = pt(100) >>> style = dict(font='PageBot-Regular', fontSize=fontSize, leading=fontSize, textFill=(1, 0, 0), xAlign=CENTER) >>> bs = context.newString('Hkpx\\nHkpx', style) >>> t = Text(bs, x=W/2, y=H/2, parent=page, showOrigin=True, fill=0.9, yAlign=MIDDLE) >>> l = Line(x=t.x-t.w/2, y=t.y, w=t.w, h=0, stroke=(0, 0, 0.5), parent=page) >>> l = Line(x=t.x-t.w/2, y=t.y+t.bs.capHeight, w=t.w, h=0, stroke=(0, 0, 0.5), parent=page) >>> l = Line(x=t.x-t.w/2, y=t.y+t.bs.ascender, w=t.w, h=0, stroke=(0, 0, 0.5), parent=page) >>> l = Line(x=t.x-t.w/2, y=t.y+t.bs.xHeight, w=t.w, h=0, stroke=(0, 0, 0.5), parent=page) >>> l = Line(x=t.x-t.w/2, y=t.y+t.bs.descender, w=t.w, h=0, stroke=(0, 0, 0.5), parent=page) >>> doc.export('_export/Text-build1.pdf') >>> W, H = 700, 100 >>> doc = Document(w=W, h=H, context=context) >>> view = doc.view >>> view.showOrigin = True >>> view.padding = pt(30) >>> view.showCropMarks = True >>> view.showFrame = True >>> style = dict(font='PageBot-Regular', leading=em(1), fontSize=pt(18), textFill=(0, 0, 0.5), xAlign=CENTER) >>> txt = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit valim mecto trambor.' >>> bs = context.newString(txt, style) # Creates a BabelString with context reference. >>> bs.context is context True >>> t = Text(bs, x=W/2, y=H/2, parent=doc[1], yAlign=MIDDLE_X) >>> t.context is context True >>> doc.export('_export/Text-build2.pdf') """ if not self.bs: return context = view.context # Get current context # Stores scaled x-value first; text is already aligned. p = pointOffset(self.origin, origin) x, _, _, = self._applyScale(view, p) # Determines frame position. p = self.getPosition(view, origin) px, py, _ = p # TODO: Add Element clipping stuff here. # FIXME: needs some restructuring, text box width and BabelString width # can be different now. # Let the view draw frame info for debugging, in case view.showFrame == # True. view.drawElementFrame(self, p, **kwargs) assert self.w, self.h context.fill(self.css('fill', noColor)) context.stroke(self.css('stroke', noColor), self.css('strokeWidth')) context.rect(px, py, self.w, self.h) px1 = px + self.pl py1 = py - self.pt pw1 = self.w - self.pr ph1 = self.h - self.pb context.drawText(self.bs, (px1, py1, pw1, ph1)) self.buildFrame(view, (px, py, self.w, self.h)) if self.showMargin: view.drawMargin(self, (px, py)) if self.showPadding: view.drawPadding(self, (px, py)) self._restoreRotation(view, p) self._restoreScale(view) self.drawMeta(view, origin)
def build(self, view, origin=ORIGIN, drawElements=True): """Draw a line on the current context canvas. >>> from pagebot.toolbox.units import pt >>> from pagebot.contexts.drawbotcontext import DrawBotContext >>> from pagebot.document import Document >>> c = DrawBotContext() >>> w, h = pt(300, 400) >>> doc = Document(w=w, h=h, autoPages=1, padding=30, originTop=False, context=c) >>> page = doc[1] >>> e = Line(parent=page, x=0, y=20, w=page.w, h=0) >>> e.x, e.y, e.w, e.h (0pt, 20pt, 300pt, 0pt) >>> e.build(doc.getView(), pt(0, 0)) >>> e.xy (0pt, 20pt) >>> e.size (300pt, 0pt) >>> view = doc.getView() >>> e.build(view, pt(0, 0)) >>> from pagebot.contexts.flatcontext import FlatContext >>> from pagebot.document import Document >>> c = FlatContext() >>> doc = Document(w=w, h=h, autoPages=1, padding=30, originTop=False, context=c) >>> page = doc[1] >>> e = Line(parent=page, x=0, y=20, w=page.w, h=3) >>> # Allow the context to create a new document and page canvas. Normally view does it. >>> c.newPage(w, h) >>> e.build(doc.getView(), (0, 0)) >>> e.xy (0pt, 20pt) >>> e.size3D (300pt, 3pt, 100pt) """ context = self.context # Get current context and builder. p = pointOffset(self.origin, origin) p = self._applyScale(view, p) px, py, _ = p = self._applyAlignment(p) # Ignore z-axis for now. s = self.css('stroke', noColor) w = self.css('strokeWidth') context.stroke(s, w) context.newPath() context.moveTo((px, py)) context.lineTo((px + self.w, py + self.h)) context.closePath() context.drawPath() # Let the view draw frame info for debugging, in case view.showFrame == True view.drawElementFrame(self, p) if self.drawBefore is not None: # Call if defined self.drawBefore(self, view, p) if drawElements: # If there are child elements, recursively draw them over the pixel image. self.buildChildElements(view, p) if self.drawAfter is not None: # Call if defined self.drawAfter(self, view, p) self._restoreScale(view) view.drawElementInfo(self, origin)
def drawGrid(self, e, origin, background=False): """Draw grid of lines and/or rectangles if colors are set in the style. Normally origin is ORIGIN pt(0, 0, 0), but it's possible to give the grid a fixed offset. If types self.showGrid is set, display the type of grid in forground for (GRID_COL, GRID_ROW, GRID_SQR) and draw in background for (GRID_COL_BG, GRID_ROW_BG, GRID_SQR_BG) >>> from pagebot.contexts.platform import getContext >>> context = getContext() >>> from pagebot.elements.element import Element >>> from pagebot.style import getRootStyle >>> style = getRootStyle() # Get default values >>> e = Element(style=style) # Works on generic elements as well as pages. >>> view = PageView(context=context, style=style) >>> view.showGrid = [GRID_COL, GRID_ROW] >>> view.drawGrid(e, (0, 0)) >>> view.showGrid = [GRID_COL_BG] >>> view.drawGrid(e, (0, 0), background=True) """ if (self.showGrid and e.isPage): showGrid = self.showGrid elif e.showGrid: showGrid = e.showGrid else: return context = self.context p = pointOffset(e.origin, origin) p = self._applyScale(e, p) px, py, _ = e._applyAlignment(p) # Ignore z-axis for now. # Drawing the grid as vertical lines. Check on foreground/background flags. if (background and GRID_COL_BG in showGrid) or (not background and GRID_COL in showGrid): # Set color for vertical grid lines context.fill(noColor) gridStrokeColor = e.css('viewGridStrokeY', noColor) gridStrokeWidth = e.css('viewGridStrokeWidthY', blackColor) context.stroke(gridStrokeColor, gridStrokeWidth) gridX = e.gridX if gridX: x = px + e.pl # Position on left padding of page/e y1 = py + e.pb y2 = y1 + e.ph for cw in gridX: if isinstance(cw, (tuple, list)): cw, gx = cw else: gx = 0 context.line((x, y1), (x, y2)) if gx: context.line((x + cw, y1), (x + cw, y2)) x += cw + gx # Drawing the grid as horizontal lines. Check on foreground/background flags. if (background and GRID_ROW_BG in showGrid) or (not background and GRID_ROW in showGrid): # Set color for vertical grid lines context.fill(noColor) gridStrokeColor = e.css('viewGridStrokeX', noColor) gridStrokeWidth = e.css('viewGridStrokeWidthX', blackColor) context.stroke(gridStrokeColor, gridStrokeWidth) gridY = e.gridY if gridY: x1 = px + e.pl x2 = x1 + e.pw y = py + e.pb # Position on bottom padding of page/e for ch in gridY: if isinstance(ch, (tuple, list)): ch, gy = ch else: gy = 0 context.line((x1, y), (x2, y)) if gy: context.line((x1, y + ch), (x2, y + ch)) y += ch + gy # Drawing the grid as rectangles. Check on foreground/background flags. if (background and GRID_SQR_BG in showGrid) or (not background and GRID_SQR in showGrid): # Set color for grid rectangles context.fill(e.css('viewGridFill', noColor)) context.stroke(e.css('viewGridStroke', noColor)) gridX = e.gridX gridY = e.gridY if gridX and gridY: x = e.pl # Position on right padding of page/e for cw in gridX: if isinstance(cw, (tuple, list)): cw, gx = cw else: gx = 0 y = e.pb # Position on bottom padding of page/e for ch in gridY: if isinstance(ch, (tuple, list)): ch, gy = ch else: gy = 0 context.rect(px + x, py + y, cw, ch) y += ch + gy x += cw + gx
def _drawElementsNeedingInfo(self, e): b = self.b context = self.context for e, origin in self.elementsNeedingInfo.values(): p = pointOffset(e.origin, origin) p = e._applyScale(self, p) px, py, _ = e._applyAlignment(p) # Ignore z-axis for now. if (self.showElementInfo or e.isPage) or e.showElementInfo: # Draw box with element info. bs = context.newString( e.getElementInfoString(), style=dict(font=self.css('viewInfoFont'), fontSize=self.css('viewInfoFontSize'), leading=self.css('viewInfoLeading'), textFill=color(0.1))) tw, th = bs.size Pd = 4 # Padding in box and shadow offset. tpx = px - Pd / 2 # Make info box outdent the element. Keeping shadow on the element top left corner. tpy = py + e.h - th - Pd # Tiny shadow context.fill(color(0.3, 0.3, 0.3, 0.5)) context.stroke(noColor) context.rect(tpx + Pd / 2, tpy, tw + 2 * Pd, th + 1.5 * Pd) # Frame context.fill(self.css('viewInfoFill')) context.stroke(color(0.3), w=0.25) context.rect(tpx, tpy, tw + 2.5 * Pd, th + 1.5 * Pd) context.text(bs, (tpx + Pd, tpy + th)) if (self.showDimensions or e.isPage) or e.showDimensions: # TODO: Make separate arrow functio and better positions # Draw width and height measures context.fill(noColor) context.stroke(blackColor, w=pt(0.25)) S = self.css('viewInfoOriginMarkerSize', pt(5)) x1, y1, x2, y2 = px + e.left, py + e.bottom, e.right, e.top # Horizontal measure context.line((x1, y1 - 0.5 * S), (x1, y1 - 3.5 * S)) context.line((x2, y1 - 0.5 * S), (x2, y1 - 3.5 * S)) context.line((x1, y1 - 2 * S), (x2, y1 - 2 * S)) # Arrow heads context.line((x1, y1 - 2 * S), (x1 + S, y1 - 1.5 * S)) context.line((x1, y1 - 2 * S), (x1 + S, y1 - 2.5 * S)) context.line((x2, y1 - 2 * S), (x2 - S, y1 - 1.5 * S)) context.line((x2, y1 - 2 * S), (x2 - S, y1 - 2.5 * S)) bs = context.newString( asFormatted(x2 - x1), style=dict(font=self.css('viewInfoFont'), fontSize=self.css('viewInfoFontSize'), leading=self.css('viewInfoLeading'), textFill=color(0.1))) tw, th = bs.size context.text(bs, ((x2 + x1) / 2 - tw / 2, y1 - 1.5 * S)) # Vertical measure context.line((x2 + 0.5 * S, y1), (x2 + 3.5 * S, y1)) context.line((x2 + 0.5 * S, y2), (x2 + 3.5 * S, y2)) context.line((x2 + 2 * S, y1), (x2 + 2 * S, y2)) # Arrow heads context.line((x2 + 2 * S, y2), (x2 + 2.5 * S, y2 - S)) context.line((x2 + 2 * S, y2), (x2 + 1.5 * S, y2 - S)) context.line((x2 + 2 * S, y1), (x2 + 2.5 * S, y1 + S)) context.line((x2 + 2 * S, y1), (x2 + 1.5 * S, y1 + S)) bs = context.newString( asFormatted(y2 - y1), style=dict(font=self.css('viewInfoFont'), fontSize=self.css('viewInfoFontSize'), leading=self.css('viewInfoLeading'), textFill=0.1)) tw, th = bs.size context.text(bs, (x2 + 2 * S - tw / 2, (y2 + y1) / 2)) e._restoreScale(self)
def build(self, view, origin, drawElements=True): """Draw the text on position (x, y). Draw background rectangle and/or frame if fill and/or stroke are defined.""" context = view.context # Get current context p = pointOffset(self.origin, origin) p = self._applyScale(view, p) px, py, _ = p = self._applyAlignment(p) # Ignore z-axis for now. self._applyRotation(view, p) # Let the view draw frame info for debugging, in case view.showFrame == # True. view.drawElementFrame(self, p) self.buildFrame(view, p) # Draw optional background, frame or borders. if self.drawBefore is not None: # Call if defined self.drawBefore(self, view, p) # self has its own baseline drawing, derived from the text, instance of # self.baselineGrid. self.drawBaselines( view, px, py, background=True) # In case there is baseline at the back # Draw the text with horizontal and vertical alignment. tw, th = self.bs.size xOffset = yOffset = 0 if self.css('yTextAlign') == MIDDLE: yOffset = (self.h - self.pb - self.pt - th) / 2 elif self.css('yTextAlign') == BOTTOM: yOffset = self.h - self.pb - self.pt - th if self.css('xTextAlign') == CENTER: xOffset = (self.w - self.pl - self.pr - tw) / 2 elif self.css('xTextAlign') == RIGHT: xOffset = self.w - self.pl - self.pr - tw textShadow = self.textShadow if textShadow: context.saveGraphicState() context.setShadow(textShadow) # Set the hyphenation flag from style, as in DrawBot this is set by a # global function, not as FormattedString attribute. NOTE: textBox # needs it's own style dictionary FIXME: take from babel string? context.language(self.css('language', DEFAULT_LANGUAGE)) h = bool(self.css('hyphenation')) context.hyphenation(h) box = clipPath = None if self.clipPath is not None: # Use the elements as clip path: clipPath = self.clipPath clipPath.translate((px, py)) context.textBox(self.bs, clipPath=clipPath, align=self.css('xTextAlign')) elif clipPath is None: if self.elements: # If there are child elements, then these are used as layout for the clipping path. clipPath = self.childClipPath # Construct the clip path, so we don't need to restore translate. if clipPath is not None: clipPath.translate((self.pl, self.pb)) clipPath.translate((self.pl, self.pb)) context.textBox(self.bs, clipPath=clipPath, align=self.css('xTextAlign')) else: box = px + self.pl, py + self.pb, self.pw, self.ph # One of box or clipPath are now defined. context.textBox(self.bs, r=box, align=self.css('xTextAlign')) if textShadow: context.restoreGraphicState() if drawElements: # If there are child elements, recursively draw them over the pixel # image. self.buildChildElements(view, p) # self has its own baseline drawing, derived from the text, instace of # self.baselineGrid. self.drawBaselines( view, px, py, background=False) # In case there is baseline at the front if view.showTextOverflowMarker and self.isOverflow(): # TODO: Make this work for FlatContext too self._drawOverflowMarker_drawBot(view, px, py) if self.drawAfter is not None: # Call if defined self.drawAfter(self, view, p) self._restoreRotation(view, p) self._restoreScale(view) view.drawElementInfo(self, origin) # Depends on css flag 'showElementInfo'
def drawBaselines(self, e, origin, background=False): """Draw baseline grid if self.showBaselines is True and there is a baseline defined > 0. Use the color from style values viewGridStrokeX and viewGridStrokeWidthX to make a difference with the baselines drawn by TextBox with style values baselineColor and baselineWidth. In this method is called by an element, instead of self, the show attribute is a way to overwrite the setting of self.showBaselines >>> from pagebot.contexts.platform import getContext >>> context = getContext() >>> from pagebot.elements.element import Element >>> from pagebot.style import getRootStyle >>> style = getRootStyle() # Get default values >>> e = Element(style=style) # Works on generic elements as well as pages. >>> view = PageView(context=context, style=style) >>> view.showBaselines = [BASE_LINE, BASE_INDEX_LEFT, BASE_Y_LEFT] >>> view.drawBaselines(e, pt(0, 0)) """ show = e.showBaselines or self.showBaselines # Sets the default, in case not drawing or show is True if not show: return context = self.context p = pointOffset(e.origin, origin) p = self._applyScale(e, p) px, py, _ = e._applyAlignment(p) # Ignore z-axis for now. baselineGrid = e.baselineGrid # Get the baseline grid of this element. indexFontSize = max(9, min(16, baselineGrid * 0.5)) # Index size depends on baseline. indexGutter = baselineGrid / 4 # Gutter between index marker and element padding startY = e.baselineGridStart if startY is None: startY = e.pt # Otherwise use the top padding as start Y. oy = e.h - startY # Assumes origin at bottom for context drawing. line = 0 # Line index baselineColor = e.css('baselineColor', color(0, 7)) baselineWidth = e.css('baselineWidth', 0.5) # Format of line numbers. style = dict(font=e.css('viewMarkerFont'), xTextAlign=RIGHT, fontSize=indexFontSize, stroke=noColor, textFill=baselineColor) context.fill(noColor) context.stroke(baselineColor, baselineWidth) while oy > e.pb: # Run until the padding of the element is reached. tl = tr = None if not background: if BASE_INDEX_LEFT in show: # Shows line baseline index tl = repr(line) elif BASE_Y_LEFT in show: # Show vertical position marker tl = repr(e.h - oy) if BASE_INDEX_RIGHT in show: # Shows line baseline index tr = repr(line) elif BASE_Y_RIGHT in show: # Show vertical position marker tr = repr(e.h - oy) bsl = context.newString(tl, style=style) bsr = context.newString(tr, style=style) twl, thl = bsl.size twr, thr = bsr.size if BASE_INSIDE in show: if tl: context.text(bsl, (px + e.pl + indexGutter, py + oy - thl / 5)) if tr: context.text(bsr, (px + e.pl + e.pw - twr - indexGutter, py + oy - thr / 5)) if (background and BASE_LINE_BG in show) or (not background and BASE_LINE in show): context.line((px + e.pl + 2 * indexGutter + twl, py + oy), (px + e.pw - 2 * indexGutter - twr, py + oy)) else: if tl: context.text( bsl, (px + e.pl - twl - indexGutter, py + oy - thl / 5)) if tr: context.text( bsr, (px + e.pl + e.pw + indexGutter, py + oy - thr / 5)) if (background and BASE_LINE_BG in show) or (not background and BASE_LINE in show): context.line((px + e.pl, py + oy), (px + e.w - e.pr, py + oy)) line += 1 # Increment line index. oy -= baselineGrid # Next vertical line position of baseline grid.
def build_drawBot(self, view, origin=ORIGIN, drawElements=True): """Draw the image in the calculated scale. Since we need to use the image by scale transform, all other measure (position, lineWidth) are scaled back to their original proportions. If stroke is defined, then use that to draw a frame around the image. Note that the (sx, sy) is already scaled to fit the padding position and size.""" context = self.context # Get current context and builder. b = context.b # This is a bit more efficient than self.b once we got context p = pointOffset(self.origin, origin) p = self._applyScale(view, p) px, py, _ = p = self._applyAlignment(p) # Ignore z-axis for now. self._applyRotation(view, p) if self.path is None or not os.path.exists(self.path) or not self.iw or not self.ih: # TODO: Also show error, in case the image does not exist, to differ from empty box. print('Cannot display image %s' % self) # Draw missing element as cross xpt, ypt, wpt, hpt = upt(px, py, self.w, self.h) b.stroke(0.5) b.strokeWidth(0.5) b.fill(None) b.rect(xpt, ypt, wpt, hpt) else: context.save() sx = self.w / self.iw sy = self.h / self.ih context.scale(sx, sy) # If there is a clipRect defined, create the bezier path if self.clipRect is not None: clipRect = context.newPath() clX, clY, clW, clH = upt(self.clipRect) sclX = clX/sx sclY = clY/sx sclW = clW/sx sclH = clH/sy # move to a point clipRect.moveTo((sclX, sclY)) # line to a point clipRect.lineTo((sclX, sclY+sclH)) clipRect.lineTo((sclX+sclW, sclY+sclH)) clipRect.lineTo((sclX+sclW, sclY)) # close the path clipRect.closePath() # set the path as a clipping path b.clipPath(clipRect) # the image will be clipped inside the path #b.fill(0, 0, 0.5, 0.5) #b.drawPath(clipRect) elif self.clipPath is not None: #Otherwise if there is a clipPath, then use it. b.clipPath(self.clipPath) if self.imo is not None: with self.imo: b.image(self.path, (0, 0), pageNumber=1, alpha=self._getAlpha()) b.image(self.imo, upt(px/sx, py/sy), pageNumber=self.index, alpha=self._getAlpha()) else: b.image(self.path, upt(px/sx, py/sy), pageNumber=self.index, alpha=self._getAlpha()) # TODO: Draw optional (transparant) forground color? b.clipPath(None) context.restore() if drawElements: self.buildChildElements(view, p) self._restoreRotation(view, p) self._restoreScale(view) view.drawElementInfo(self, origin)