def drawMissingElementRect(self, e, origin): u"""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 self.css('showGrid') is True.""" if self.showMissingElementRect: p = pointOffset(e.point, origin) p = e._applyOrigin(p) p = e._applyScale(p) px, py, _ = e._applyAlignment(p) # Ignore z-axis for now. self.setShadow() sMissingElementFill = self.css('viewMissingElementFill', NO_COLOR) if sMissingElementFill is not NO_COLOR: setFillColor(sMissingElementFill) setStrokeColor(None) rect(px, py, self.w, self.h) # Draw crossed rectangle. setFillColor(None) setStrokeColor(0, 0.5) rect(px, py, self.w, self.h) newPath() moveTo((px, py)) lineTo((px + self.w, py + self.h)) moveTo((px + self.w, py)) lineTo((px, py + self.h)) drawPath() self.resetShadow() e._restoreScale()
def draw(self, origin, view): u"""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.oPoint, origin) p = self._applyScale(p) px, py, _ = self._applyAlignment(p) # Ignore z-axis for now. if self.drawBefore is not None: # Call if defined self.drawBefore(self, p, view) setFillColor(self.OLD_PAPER_COLOR) # Color of old paper: #F8ECC2 gw, gh = self.getSize() rect(px, py, gw, gh) gy = 0 for element in self.elements: # @@@ Find space and do more composition element.draw((px, py + gy), view) gy += element.h if self.drawAfter is not None: # Call if defined self.drawAfter(self, p, view) self._restoreScale() view.drawElementMetaInfo(self, origin)
def draw(self, origin, view): p = pointOffset(self.oPoint, origin) p = self._applyScale(p) px, py, _ = p = self._applyAlignment(p) # Ignore z-axis for now. save() sh = 1.0 * self.h / self.ih transform((1, 0, 0, 1, px, py)) scale(sh) if self.pathFilter is not None: self.pathFilter(self, self.glyph.path) if self.css('fill') != NO_COLOR or self.css('stroke') != NO_COLOR: setFillColor(self.css('fill')) print(self.css('strokeWidth') or 1), sh setStrokeColor(self.css('stroke', NO_COLOR), (self.css('strokeWidth') or 20)) fill(0) stroke(1, 0, 0) strokeWidth(20) drawPath(self.glyph.path) restore() # If there are child elements, draw them over the polygon. self._drawElements(p, view) # Draw optional bounding box. #self.drawFrame(origin, view) self._restoreScale() view.drawElementMetaInfo( self, origin) # Depends on css flag 'showElementInfo'
def drawGlyphPath(font, glyphName, x, y, s=0.1, fillColor=0, strokeColor=None, strokeWidth=0): glyph = font[glyphName] save() setFillColor(fillColor) setStrokeColor(strokeColor, strokeWidth) transform((1, 0, 0, 1, x - glyph.width/2*s, y)) scale(s) drawPath(glyph.path) restore()
def drawPages(self, pageSelection=None): u"""Draw the selected pages. pageSelection is an optional set of y-pageNumbers to draw.""" doc = self.parent w, h, _ = doc.getMaxPageSizes(pageSelection) w2 = 2 * w # Make spread width for pn, pages in doc.getSortedPages(): #if pageSelection is not None and not page.y in pageSelection: # continue # Create a new DrawBot viewport page to draw template + page, if not already done. # In case the document is oversized, then make all pages the size of the document, so the # pages can draw their crop-marks. Otherwise make DrawBot pages of the size of each page. # Size depends on the size of the larges pages + optional decument padding. page = pages[ 0] # TODO: Make it work if there as multiple pages on the same page number. pw, ph = w, h # Copy from main (w, h), since they may be altered. if self.pl > self.MIN_PADDING and self.pt > self.MIN_PADDING and self.pb > self.MIN_PADDING and self.pr > self.MIN_PADDING: pw += self.pl + self.pr ph += self.pt + self.pb if self.originTop: origin = self.pl, self.pt, 0 else: origin = self.pl, self.pb, 0 else: pw = page.w # No padding defined, follow the size of the page. ph = page.h origin = (0, 0, 0) pw2 = 2 * pw if (pn % 2 == 0): # Is even? newPage( pw2, ph ) # Make page in DrawBot of self size, actual page may be smaller if showing cropmarks. # View may have defined a background if self.style.get('fill') is not None: setFillColor(self.style['fill']) rect(0, 0, pw2, ph) else: # Odd, shift origin to right origin = origin[0] + pw, origin[1], origin[2] if self.drawBefore is not None: # Call if defined self.drawBefore(page, origin, self) self.drawPageFrame(page, origin) # Use the (docW, docH) as offset, in case cropmarks need to be displayed. page.draw(origin, self) if self.drawAfter is not None: # Call if defined self.drawAfter(page, origin, self) # Self.infoElements now may have collected elements needed info to be drawn, after all drawing is done. # So the info boxes don't get covered by regular page content. for e in self.elementsNeedingInfo.values(): self._drawElementsNeedingInfo()
def drawGrid(self, e, origin): u"""Draw grid of lines and/or rectangles if colors are set in the style. Normally px and py will be 0, but it's possible to give them a fixed offset.""" # Drawing the grid as squares. if not self.showGrid: return #if not self.showGridColumns or not self.showGrid: # return p = pointOffset(e.oPoint, origin) p = self._applyScale(p) px, py, _ = e._applyAlignment(p) # Ignore z-axis for now. sGridFill = e.css('viewGridFill', NO_COLOR) gutterW = e.gw # Gutter width gutterH = e.gh # Gutter height columnWidth = e.cw # Column width columnHeight = e.ch # Column height padL = e.pl # Padding left padT = e.pt # Padding top padR = e.pr # padding right padB = e.pb # padding bottom padW = e.pw # Padding width padH = e.ph # Padding height w = e.w h = e.h if e.isRight(): ox = px + padR else: ox = px + padL oy = py + padB if self.showGrid and self.css('viewGridStroke', NO_COLOR) is not NO_COLOR: setFillColor(None) setStrokeColor(self.css('viewGridStroke', NO_COLOR), self.css('viewGridStrokeWidth')) newPath() for cx, cw in e.getGridColumns(): moveTo((ox + cx, oy)) lineTo((ox + cx, oy + padH)) moveTo((ox + cx + cw, oy)) lineTo((ox + cx + cw, oy + padH)) for cy, ch in e.getGridRows(): moveTo((ox, oy + cy)) lineTo((ox + padW, oy + cy)) moveTo((ox, oy + cy + ch)) lineTo((ox + padW, oy + cy + ch)) drawPath() #text(fs+repr(index), (ox + M * 0.3, oy + M / 4)) """
def drawElementOrigin(self, e, origin): px, py, _ = pointOffset(e.oPoint, origin) S = self.css('viewInfoOriginMarkerSize', 4) if self.showElementOrigin: # Draw origin of the element setFillColor( (0.5, 0.5, 0.5, 0.1) ) # Transparant fill, so we can see the marker on dark backgrounds. setStrokeColor(0, 0.25) oval(px - S, py - S, 2 * S, 2 * S) line((px - S, py), (px + S, py)) line((px, py - S), (px, py + S)) if self.showElementDimensions: fs = newFS(point2S(e.point3D), style=dict(font=self.css('viewInfoFont'), fontSize=self.css('viewInfoFontSize'), leading=self.css('viewInfoLeading'), textFill=0.1)) w, h = textSize(fs) text(fs, (px - w / 2, py + S * 1.5))
def draw(self, origin, view): p = pointOffset(self.oPoint, origin) p = self._applyScale(p) px, py, _ = p = self._applyAlignment(p) # Ignore z-axis for now. self.drawFrame(p, view) # Draw optional frame or borders. if self.drawBefore is not None: # Call if defined self.drawBefore(self, p, view) setFillColor(self.css('fill', NO_COLOR)) setStrokeColor(self.css('stroke', NO_COLOR), self.css('strokeWidth')) oval(px, py, self.w, self.h) # If there are child elements, draw them over the text. self._drawElements(p, view) if self.drawAfter is not None: # Call if defined self.drawAfter(self, p, view) self._restoreScale() view.drawElementMetaInfo(self, origin)
def drawBaselineGrid(self, e, origin): u"""Draw baseline grid if line color is set in the style. TODO: Make fixed values part of calculation or part of grid style. Normally px and py will be 0, but it's possible to give them a fixed offset.""" if not self.showBaselineGrid: return p = pointOffset(self.oPoint, origin) p = self._applyScale(p) px, py, _ = self._applyAlignment(p) # Ignore z-axis for now. M = 16 startY = e.css('baselineGridStart') if startY is None: startY = e.pt # Otherwise use the top padding as start Y. oy = e.h - startY #- py line = 0 # Format of line numbers. # TODO: DrawBot align and fill don't work properly now. fs = newFS( '', self, dict(font=e.css('fallbackFont', 'Verdana'), xTextAlign=RIGHT, fontSize=M / 2, stroke=None, textFill=e.css('gridStroke'))) while oy > e.pb or 0: setFillColor(None) setStrokeColor(e.css('baselineGridStroke', NO_COLOR), e.css('gridStrokeWidth')) newPath() moveTo((px + e.pl, py + oy)) lineTo((px + e.w - e.pr, py + oy)) drawPath() text(fs + repr(line), (px + e.pl - 2, py + oy - e.pl * 0.6)) text(fs + repr(line), (px + e.w - e.pr - 8, py + oy - e.pr * 0.6)) line += 1 # Increment line index. oy -= e.css('baselineGrid' ) # Next vertical line position of baseline grid.
def draw(self, origin, view): p = pointOffset(self.oPoint, origin) p = self._applyScale(p) px, py, _ = self._applyAlignment(p) # Ignore z-axis for now. sIndent = self.css('indent') sTailIndent = self.css('tailIndent') w = self.w - sIndent - sTailIndent if self.drawBefore is not None: # Call if defined self.drawBefore(self, p, view) setFillColor(None) setStrokeColor(self.css('stroke', NO_COLOR), self.css('strokeWidth')) line((px + sIndent, py), (px + w, py)) # If there are child elements, draw them over the text. self._drawElements(origin) if self.drawAfter is not None: # Call if defined self.drawAfter(self, p, view) self._restoreScale() view.drawElementMetaInfo(self, origin)
def _drawElementsNeedingInfo(self): for e, origin in self.elementsNeedingInfo.values(): p = pointOffset(e.oPoint, origin) p = e._applyScale(p) px, py, _ = e._applyAlignment(p) # Ignore z-axis for now. if self.showElementInfo: # Draw box with element info. fs = newFS(e.getElementInfoString(), style=dict(font=self.css('viewInfoFont'), fontSize=self.css('viewInfoFontSize'), leading=self.css('viewInfoLeading'), textFill=0.1)) tw, th = textSize(fs) 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 setFillColor((0.3, 0.3, 0.3, 0.5)) setStrokeColor(None) rect(tpx + Pd / 2, tpy, tw + 2 * Pd, th + 1.5 * Pd) # Frame setFillColor(self.css('viewInfoFill')) setStrokeColor(0.3, 0.25) rect(tpx, tpy, tw + 2.5 * Pd, th + 1.5 * Pd) text(fs, (tpx + Pd, tpy + th)) e._restoreScale() if self.showElementDimensions: # TODO: Make separate arrow functio and better positions # Draw width and height measures setFillColor(None) setStrokeColor(0, 0.25) S = self.css('viewInfoOriginMarkerSize', 4) x1, y1, x2, y2 = px + e.left, py + e.bottom, e.right, e.top # Horizontal measure line((x1, y1 - 0.5 * S), (x1, y1 - 3.5 * S)) line((x2, y1 - 0.5 * S), (x2, y1 - 3.5 * S)) line((x1, y1 - 2 * S), (x2, y1 - 2 * S)) # Arrow heads line((x1, y1 - 2 * S), (x1 + S, y1 - 1.5 * S)) line((x1, y1 - 2 * S), (x1 + S, y1 - 2.5 * S)) line((x2, y1 - 2 * S), (x2 - S, y1 - 1.5 * S)) line((x2, y1 - 2 * S), (x2 - S, y1 - 2.5 * S)) fs = newFS(asFormatted(x2 - x1), style=dict(font=self.css('viewInfoFont'), fontSize=self.css('viewInfoFontSize'), leading=self.css('viewInfoLeading'), textFill=0.1)) tw, th = textSize(fs) text(fs, ((x2 + x1) / 2 - tw / 2, y1 - 1.5 * S)) # Vertical measure line((x2 + 0.5 * S, y1), (x2 + 3.5 * S, y1)) line((x2 + 0.5 * S, y2), (x2 + 3.5 * S, y2)) line((x2 + 2 * S, y1), (x2 + 2 * S, y2)) # Arrow heads line((x2 + 2 * S, y2), (x2 + 2.5 * S, y2 - S)) line((x2 + 2 * S, y2), (x2 + 1.5 * S, y2 - S)) line((x2 + 2 * S, y1), (x2 + 2.5 * S, y1 + S)) line((x2 + 2 * S, y1), (x2 + 1.5 * S, y1 + S)) fs = newFS(asFormatted(y2 - y1), style=dict(font=self.css('viewInfoFont'), fontSize=self.css('viewInfoFontSize'), leading=self.css('viewInfoLeading'), textFill=0.1)) tw, th = textSize(fs) text(fs, (x2 + 2 * S - tw / 2, (y2 + y1) / 2))
def drawArrow(self, e, xs, ys, xt, yt, onText=1, startMarker=False, endMarker=False, fms=None, fmf=None, fill=None, stroke=None, strokeWidth=None): u"""Draw curved arrow marker between the two points. TODO: Add drawing of real arrow-heads, rotated in the right direction.""" if fms is None: fms = self.css('viewFlowMarkerSize') if fmf is None: fmf or self.css('viewFlowCurvatureFactor') if stroke is None: if onText == 1: stroke = self.css('viewFlowConnectionStroke2', NO_COLOR) else: stroke = self.css('viewFlowConnectionStroke1', NO_COLOR) if strokeWidth is None: strokeWidth = self.css('viewFlowConnectionStrokeWidth', 0.5) setStrokeColor(stroke, strokeWidth) if startMarker: if fill is None: fill = self.css('viewFlowMarkerFill', NO_COLOR) setFillColor(fill) oval(xs - fms, ys - fms, 2 * fms, 2 * fms) xm = (xt + xs) / 2 ym = (yt + ys) / 2 xb1 = xm + onText * (yt - ys) * fmf yb1 = ym - onText * (xt - xs) * fmf xb2 = xm - onText * (yt - ys) * fmf yb2 = ym + onText * (xt - xs) * fmf # Arrow head position arrowSize = 12 arrowAngle = 0.4 angle = atan2(xt - xb2, yt - yb2) hookedAngle = radians(degrees(angle) - 90) ax1 = xt - cos(hookedAngle + arrowAngle) * arrowSize ay1 = yt + sin(hookedAngle + arrowAngle) * arrowSize ax2 = xt - cos(hookedAngle - arrowAngle) * arrowSize ay2 = yt + sin(hookedAngle - arrowAngle) * arrowSize newPath() setFillColor(None) moveTo((xs, ys)) curveTo( (xb1, yb1), (xb2, yb2), ((ax1 + ax2) / 2, (ay1 + ay2) / 2)) # End in middle of arrow head. drawPath() # Draw the arrow head. newPath() setFillColor(stroke) setStrokeColor(None) moveTo((xt, yt)) lineTo((ax1, ay1)) lineTo((ax2, ay2)) closePath() drawPath() if endMarker: setFillColor(self.css('viewFlowMarkerFill', NO_COLOR)) oval(xt - fms, yt - fms, 2 * fms, 2 * fms)