def draw(self): slices = self.slices _3d_angle = self.angle_3d _3dva = self._3dva = _360(_3d_angle+90) a0 = _2rad(_3dva) self._xdepth_3d = cos(a0)*self.depth_3d self._ydepth_3d = sin(a0)*self.depth_3d self._cx = self.x+self.width/2.0 self._cy = self.y+(self.height - self._ydepth_3d)/2.0 radius = self._radius = self._cx-self.x self._radiusx = radiusx = radius self._radiusy = radiusy = (1.0 - self.perspective/100.0)*radius data = self.normalizeData() sum = self._sum CX = self.CX CY = self.CY OX = self.OX OY = self.OY rad_dist = self.rad_dist _fillSide = self._fillSide n = len(data) _sl3d = self._sl3d = [] g = Group() last = _360(self.startAngle) a0 = self.direction=='clockwise' and -1 or 1 for v in data: v *= a0 angle1, angle0 = last, v+last last = angle0 if a0>0: angle0, angle1 = angle1, angle0 _sl3d.append(_SL3D(angle0,angle1)) #print '%d: %.2f %.2f --> %s' %(len(_sl3d)-1,angle0,angle1,_sl3d[-1]) labels = _fixLabels(self.labels,n) a0 = _3d_angle a1 = _3d_angle+180 T = [] S = [] L = [] class WedgeLabel3d(WedgeLabel): def _checkDXY(self,ba): if ba[0]=='n': if not hasattr(self,'_ody'): self._ody = self.dy self.dy = -self._ody + self._ydepth_3d WedgeLabel3d._ydepth_3d = self._ydepth_3d for i in xrange(n): style = slices[i] if not style.visible: continue sl = _sl3d[i] lo = angle0 = sl.lo hi = angle1 = sl.hi if abs(hi-lo)<=1e-7: continue fillColor = _getShaded(style.fillColor,style.fillColorShaded,style.shading) strokeColor = _getShaded(style.strokeColor,style.strokeColorShaded,style.shading) or fillColor strokeWidth = style.strokeWidth cx0 = CX(i,0) cy0 = CY(i,0) cx1 = CX(i,1) cy1 = CY(i,1) #background shaded pie bottom g.add(Wedge(cx1,cy1,radiusx, lo, hi,yradius=radiusy, strokeColor=strokeColor,strokeWidth=strokeWidth,fillColor=fillColor, strokeLineJoin=1)) #connect to top if lo < a0 < hi: angle0 = a0 if lo < a1 < hi: angle1 = a1 if 1: p = ArcPath(strokeColor=strokeColor, fillColor=fillColor,strokeWidth=strokeWidth,strokeLineJoin=1) p.addArc(cx1,cy1,radiusx,angle0,angle1,yradius=radiusy,moveTo=1) p.lineTo(OX(i,angle1,0),OY(i,angle1,0)) p.addArc(cx0,cy0,radiusx,angle0,angle1,yradius=radiusy,reverse=1) p.closePath() if angle0<=_3dva and angle1>=_3dva: rd = 0 else: rd = min(rad_dist(angle0),rad_dist(angle1)) S.append((rd,p)) _fillSide(S,i,lo,strokeColor,strokeWidth,fillColor) _fillSide(S,i,hi,strokeColor,strokeWidth,fillColor) #bright shaded top fillColor = style.fillColor strokeColor = style.strokeColor or fillColor T.append(Wedge(cx0,cy0,radiusx,lo,hi,yradius=radiusy, strokeColor=strokeColor,strokeWidth=strokeWidth,fillColor=fillColor,strokeLineJoin=1)) text = labels[i] if text: rat = style.labelRadius self._radiusx *= rat self._radiusy *= rat mid = sl.mid _addWedgeLabel(self,text,L.append,mid,OX(i,mid,0),OY(i,mid,0),style,labelClass=WedgeLabel3d) self._radiusx = radiusx self._radiusy = radiusy S.sort(lambda a,b: -cmp(a[0],b[0])) map(g.add,map(lambda x:x[1],S)+T+L) return g
def draw(self): slices = self.slices _3d_angle = self.angle_3d _3dva = self._3dva = _360(_3d_angle+90) a0 = _2rad(_3dva) self._xdepth_3d = cos(a0)*self.depth_3d self._ydepth_3d = sin(a0)*self.depth_3d self._cx = self.x+self.width/2.0 self._cy = self.y+(self.height - self._ydepth_3d)/2.0 radiusx = radiusy = self._cx-self.x if self.xradius: radiusx = self.xradius if self.yradius: radiusy = self.yradius self._radiusx = radiusx self._radiusy = radiusy = (1.0 - self.perspective/100.0)*radiusy data = self.normalizeData() sum = self._sum CX = self.CX CY = self.CY OX = self.OX OY = self.OY rad_dist = self.rad_dist _fillSide = self._fillSide self._seriesCount = n = len(data) _sl3d = self._sl3d = [] g = Group() last = _360(self.startAngle) a0 = self.direction=='clockwise' and -1 or 1 for v in data: v *= a0 angle1, angle0 = last, v+last last = angle0 if a0>0: angle0, angle1 = angle1, angle0 _sl3d.append(_SL3D(angle0,angle1)) labels = _fixLabels(self.labels,n) a0 = _3d_angle a1 = _3d_angle+180 T = [] S = [] L = [] class WedgeLabel3d(WedgeLabel): _ydepth_3d = self._ydepth_3d def _checkDXY(self,ba): if ba[0]=='n': if not hasattr(self,'_ody'): self._ody = self.dy self.dy = -self._ody + self._ydepth_3d checkLabelOverlap = self.checkLabelOverlap for i in xrange(n): style = slices[i] if not style.visible: continue sl = _sl3d[i] lo = angle0 = sl.lo hi = angle1 = sl.hi if abs(hi-lo)<=1e-7: continue fillColor = _getShaded(style.fillColor,style.fillColorShaded,style.shading) strokeColor = _getShaded(style.strokeColor,style.strokeColorShaded,style.shading) or fillColor strokeWidth = style.strokeWidth cx0 = CX(i,0) cy0 = CY(i,0) cx1 = CX(i,1) cy1 = CY(i,1) #background shaded pie bottom g.add(Wedge(cx1,cy1,radiusx, lo, hi,yradius=radiusy, strokeColor=strokeColor,strokeWidth=strokeWidth,fillColor=fillColor, strokeLineJoin=1)) #connect to top if lo < a0 < hi: angle0 = a0 if lo < a1 < hi: angle1 = a1 if 1: p = ArcPath(strokeColor=strokeColor, fillColor=fillColor,strokeWidth=strokeWidth,strokeLineJoin=1) p.addArc(cx1,cy1,radiusx,angle0,angle1,yradius=radiusy,moveTo=1) p.lineTo(OX(i,angle1,0),OY(i,angle1,0)) p.addArc(cx0,cy0,radiusx,angle0,angle1,yradius=radiusy,reverse=1) p.closePath() if angle0<=_3dva and angle1>=_3dva: rd = 0 else: rd = min(rad_dist(angle0),rad_dist(angle1)) S.append((rd,p)) _fillSide(S,i,lo,strokeColor,strokeWidth,fillColor) _fillSide(S,i,hi,strokeColor,strokeWidth,fillColor) #bright shaded top fillColor = style.fillColor strokeColor = style.strokeColor or fillColor T.append(Wedge(cx0,cy0,radiusx,lo,hi,yradius=radiusy, strokeColor=strokeColor,strokeWidth=strokeWidth,fillColor=fillColor,strokeLineJoin=1)) text = labels[i] if style.label_visible and text: rat = style.labelRadius self._radiusx *= rat self._radiusy *= rat mid = sl.mid labelX = OX(i,mid,0) labelY = OY(i,mid,0) _addWedgeLabel(self,text,L.append,mid,labelX,labelY,style,labelClass=WedgeLabel3d) if checkLabelOverlap: l = L[-1] l._origdata = { 'x': labelX, 'y':labelY, 'angle': mid, 'rx': self._radiusx, 'ry':self._radiusy, 'cx':CX(i,0), 'cy':CY(i,0), 'bounds': l.getBounds(), } self._radiusx = radiusx self._radiusy = radiusy S.sort(lambda a,b: -cmp(a[0],b[0])) if checkLabelOverlap and L: fixLabelOverlaps(L) map(g.add,map(lambda x:x[1],S)+T+L) return g
def _draw_arc(self, inner_radius, outer_radius, startangle, endangle, color, border=None, colour=None, **kwargs): """ draw_arc(self, inner_radius, outer_radius, startangle, endangle, color) -> Group o inner_radius Float distance of inside of arc from drawing center o outer_radius Float distance of outside of arc from drawing center o startangle Float angle subtended by start of arc at drawing center (in radians) o endangle Float angle subtended by end of arc at drawing center (in radians) o color colors.Color object for arc (overridden by backwards compatible argument with UK spelling, colour). Returns a closed path object describing an arced box corresponding to the passed values. For very small angles, a simple four sided polygon is used. """ #Let the UK spelling (colour) override the USA spelling (color) if colour is not None: color = colour if border is None: border = color if color is None: color = colour if color == colors.white and border is None: # Force black border on strokecolor = colors.black # white boxes with elif border is None: # undefined border, else strokecolor = color # use fill colour elif border is not None: strokecolor = border if abs(float(endangle - startangle))>.01: # Wide arc, must use full curves p = ArcPath(strokeColor=strokecolor, fillColor=color, strokewidth=0) #Note reportlab counts angles anti-clockwise from the horizontal #(as in mathematics, e.g. complex numbers and polar coordinates) #but we use clockwise from the vertical. Also reportlab uses #degrees, but we use radians. p.addArc(self.xcenter, self.ycenter, inner_radius, 90 - (endangle * 180 / pi), 90 - (startangle * 180 / pi), moveTo=True) p.addArc(self.xcenter, self.ycenter, outer_radius, 90 - (endangle * 180 / pi), 90 - (startangle * 180 / pi), reverse=True) p.closePath() return p else: #Cheat and just use a four sided polygon. # Calculate trig values for angle and coordinates startcos, startsin = cos(startangle), sin(startangle) endcos, endsin = cos(endangle), sin(endangle) x0,y0 = self.xcenter, self.ycenter # origin of the circle x1,y1 = (x0+inner_radius*startsin, y0+inner_radius*startcos) x2,y2 = (x0+inner_radius*endsin, y0+inner_radius*endcos) x3,y3 = (x0+outer_radius*endsin, y0+outer_radius*endcos) x4,y4 = (x0+outer_radius*startsin, y0+outer_radius*startcos) return draw_polygon([(x1,y1),(x2,y2),(x3,y3),(x4,y4)], color, border)
def _draw_arc_arrow(self, inner_radius, outer_radius, startangle, endangle, color, border=None, shaft_height_ratio=0.4, head_length_ratio=0.5, orientation='right', colour=None, **kwargs): """Draw an arrow along an arc.""" #Let the UK spelling (colour) override the USA spelling (color) if colour is not None: color = colour if border is None: border = color if color is None: color = colour if color == colors.white and border is None: # Force black border on strokecolor = colors.black # white boxes with elif border is None: # undefined border, else strokecolor = color # use fill colour elif border is not None: strokecolor = border #if orientation == 'right': # startangle, endangle = min(startangle, endangle), max(startangle, endangle) #elif orientation == 'left': # startangle, endangle = max(startangle, endangle), min(startangle, endangle) #else: startangle, endangle = min(startangle, endangle), max(startangle, endangle) if orientation <> "left" and orientation <> "right": raise ValueError("Invalid orientation %s, should be 'left' or 'right'" \ % repr(orientation)) angle = float(endangle - startangle) # angle subtended by arc middle_radius = 0.5*(inner_radius+outer_radius) boxheight = outer_radius - inner_radius shaft_height = boxheight*shaft_height_ratio shaft_inner_radius = middle_radius - 0.5*shaft_height shaft_outer_radius = middle_radius + 0.5*shaft_height headangle_delta = max(0.0,min(abs(boxheight)*head_length_ratio/middle_radius, abs(angle))) if angle < 0: headangle_delta *= -1 #reverse it if orientation=="right": headangle = endangle-headangle_delta else: headangle = startangle+headangle_delta if startangle <= endangle: headangle = max(min(headangle, endangle), startangle) else: headangle = max(min(headangle, startangle), endangle) assert startangle <= headangle <= endangle \ or endangle <= headangle <= startangle, \ (startangle, headangle, endangle, angle) # Calculate trig values for angle and coordinates startcos, startsin = cos(startangle), sin(startangle) headcos, headsin = cos(headangle), sin(headangle) endcos, endsin = cos(endangle), sin(endangle) x0,y0 = self.xcenter, self.ycenter # origin of the circle if 0.5 >= abs(angle) and abs(headangle_delta) >= abs(angle): #If the angle is small, and the arrow is all head, #cheat and just use a triangle. if orientation=="right": x1,y1 = (x0+inner_radius*startsin, y0+inner_radius*startcos) x2,y2 = (x0+outer_radius*startsin, y0+outer_radius*startcos) x3,y3 = (x0+middle_radius*endsin, y0+middle_radius*endcos) else: x1,y1 = (x0+inner_radius*endsin, y0+inner_radius*endcos) x2,y2 = (x0+outer_radius*endsin, y0+outer_radius*endcos) x3,y3 = (x0+middle_radius*startsin, y0+middle_radius*startcos) #return draw_polygon([(x1,y1),(x2,y2),(x3,y3)], color, border, # stroke_line_join=1) return Polygon([x1,y1,x2,y2,x3,y3], strokeColor=border or color, fillColor=color, strokeLineJoin=1, #1=round, not mitre! strokewidth=0) elif orientation=="right": p = ArcPath(strokeColor=strokecolor, fillColor=color, #default is mitre/miter which can stick out too much: strokeLineJoin=1, #1=round strokewidth=0, **kwargs) #Note reportlab counts angles anti-clockwise from the horizontal #(as in mathematics, e.g. complex numbers and polar coordinates) #but we use clockwise from the vertical. Also reportlab uses #degrees, but we use radians. p.addArc(self.xcenter, self.ycenter, shaft_inner_radius, 90 - (headangle * 180 / pi), 90 - (startangle * 180 / pi), moveTo=True) p.addArc(self.xcenter, self.ycenter, shaft_outer_radius, 90 - (headangle * 180 / pi), 90 - (startangle * 180 / pi), reverse=True) p.lineTo(x0+outer_radius*headsin, y0+outer_radius*headcos) if abs(angle) < 0.5: p.lineTo(x0+middle_radius*endsin, y0+middle_radius*endcos) p.lineTo(x0+inner_radius*headsin, y0+inner_radius*headcos) else: dx = min(0.1, abs(angle)/50.0) #auto-scale number of steps x = dx while x < 1: r = outer_radius - x*(outer_radius-middle_radius) a = headangle + x*(endangle-headangle) p.lineTo(x0+r*sin(a), y0+r*cos(a)) x += dx p.lineTo(x0+middle_radius*endsin, y0+middle_radius*endcos) x = dx while x < 1: r = middle_radius - x*(middle_radius-inner_radius) a = headangle + (1-x)*(endangle-headangle) p.lineTo(x0+r*sin(a), y0+r*cos(a)) x += dx p.lineTo(x0+inner_radius*headsin, y0+inner_radius*headcos) p.closePath() return p else: p = ArcPath(strokeColor=strokecolor, fillColor=color, #default is mitre/miter which can stick out too much: strokeLineJoin=1, #1=round strokewidth=0, **kwargs) #Note reportlab counts angles anti-clockwise from the horizontal #(as in mathematics, e.g. complex numbers and polar coordinates) #but we use clockwise from the vertical. Also reportlab uses #degrees, but we use radians. p.addArc(self.xcenter, self.ycenter, shaft_inner_radius, 90 - (endangle * 180 / pi), 90 - (headangle * 180 / pi), moveTo=True, reverse=True) p.addArc(self.xcenter, self.ycenter, shaft_outer_radius, 90 - (endangle * 180 / pi), 90 - (headangle * 180 / pi), reverse=False) p.lineTo(x0+outer_radius*headsin, y0+outer_radius*headcos) #TODO - two staight lines is only a good approximation for small #head angle, in general will need to curved lines here: if abs(angle) < 0.5: p.lineTo(x0+middle_radius*startsin, y0+middle_radius*startcos) p.lineTo(x0+inner_radius*headsin, y0+inner_radius*headcos) else: dx = min(0.1, abs(angle)/50.0) #auto-scale number of steps x = dx while x < 1: r = outer_radius - x*(outer_radius-middle_radius) a = headangle + x*(startangle-headangle) p.lineTo(x0+r*sin(a), y0+r*cos(a)) x += dx p.lineTo(x0+middle_radius*startsin, y0+middle_radius*startcos) x = dx while x < 1: r = middle_radius - x*(middle_radius-inner_radius) a = headangle + (1-x)*(startangle-headangle) p.lineTo(x0+r*sin(a), y0+r*cos(a)) x += dx p.lineTo(x0+inner_radius*headsin, y0+inner_radius*headcos) p.closePath() return p
def __init__(self, specification, drawing_callable, pages_to_draw=None, border=False, shade_missing=False, direction="across"): """ Parameters ---------- specification: labels.Specification instance The sizes etc of the label sheets. drawing_callable: callable A function (or other callable object) to call to draw an individual label. It will be given four parameters specifying the label. In order, these are a `reportlab.graphics.shapes.Drawing` instance to draw the label on, the width of the label, the height of the label, and the object to draw. The dimensions will be in points, the unit of choice for ReportLab. pages_to_draw: list of positive integers, default None The list pages to actually draw labels on. This is intended to be used with the preview methods to avoid drawing labels that will never be displayed. A value of None means draw all pages. border: Boolean, default False Whether or not to draw a border around each label. shade_missing: Boolean or ReportLab colour, default False Whether or not to shade missing labels (those specified through the partial_pages method). False means leave the labels unshaded. If a ReportLab colour is given, the labels will be shaded in that colour. A value of True will result in the missing labels being shaded in the hex colour 0xBBBBBB (a medium-light grey). direction: String, either "across" or "down" to indication the order labels will be assigned. Default is across. Notes ----- If you specify a pages_to_draw list, pages not in that list will be blank since the drawing function will not be called on that page. This could have a side-affect if you rely on the drawing function modifying some global values. For example, in the nametags.py and preview.py demo scripts, the colours for each label are picked by a pseduo-random number generator. However, in the preview script, this generator is not advanced and so the colours on the last page differ between the preview and the actual output. """ # Save our arguments. specification._calculate() self.specs = deepcopy(specification) self.drawing_callable = drawing_callable self.pages_to_draw = pages_to_draw self.border = border if shade_missing == True: self.shade_missing = colors.HexColor(0xBBBBBB) else: self.shade_missing = shade_missing # Set up some internal variables. self._lw = self.specs.label_width * mm self._lh = self.specs.label_height * mm self._cr = self.specs.corner_radius * mm self._dw = (self.specs.label_width - self.specs.left_padding - self.specs.right_padding) * mm self._dh = (self.specs.label_height - self.specs.top_padding - self.specs.bottom_padding) * mm self._lp = self.specs.left_padding * mm self._bp = self.specs.bottom_padding * mm self._pr = self.specs.padding_radius * mm self._used = {} self._pages = [] self._current_page = None # Page information. self._pagesize = (float(self.specs.sheet_width*mm), float(self.specs.sheet_height*mm)) self._numlabels = (self.specs.rows, self.specs.columns) self._position = None self.label_count = 0 self.page_count = 0 if direction == "vertical": self._positionIteratorFunc = specification.verticalPosIterator else: self._positionIteratorFunc = specification.horizontalPosIterator # Background image. if self.specs.background_image: self._bgimage = deepcopy(self.specs.background_image) # Different classes are scaled in different ways... if isinstance(self._bgimage, Image): self._bgimage.x = 0 self._bgimage.y = 0 self._bgimage.width = self._pagesize[0] self._bgimage.height = self._pagesize[1] elif isinstance(self._bgimage, Drawing): self._bgimage.shift(0, 0) self._bgimage.scale(self._pagesize[0]/self._bgimage.width, self._pagesize[1]/self._bgimage.height) else: raise ValueError("Unhandled background type.") # Background from a filename. elif self.specs.background_filename: self._bgimage = Image(0, 0, self._pagesize[0], self._pagesize[1], self.specs.background_filename) # No background. else: self._bgimage = None # Borders and clipping paths. We need two clipping paths; one for the # label as a whole (which is identical to the border), and one for the # available drawing area (i.e., after taking the padding into account). # This is necessary because sometimes the drawing area can extend # outside the border at the corners, e.g., if there is left padding # only and no padding radius, then the 'available' area corners will be # square and go outside the label corners if they are rounded. # Copy some properties to a local scope. h, w, r = float(self._lh), float(self._lw), float(self._cr) # Create the border from a path. If the corners are not rounded, skip # adding the arcs. border = ArcPath() if r: border.moveTo(w - r, 0) border.addArc(w - r, r, r, -90, 0) border.lineTo(w, h - r) border.addArc(w - r, h - r, r, 0, 90) border.lineTo(r, h) border.addArc(r, h - r, r, 90, 180) border.lineTo(0, r) border.addArc(r, r, r, 180, 270) border.closePath() else: border.moveTo(0, 0) border.lineTo(w, 0) border.lineTo(w, h) border.lineTo(0, h) border.closePath() # Set the properties and store. border.isClipPath = 0 border.strokeWidth = 1 border.strokeColor = colors.black border.fillColor = None self._border = border # Clip path for the label is the same as the border. self._clip_label = deepcopy(border) self._clip_label.isClipPath = 1 self._clip_label.strokeColor = None self._clip_label.fillColor = None # If there is no padding (i.e., the drawable area is the same as the # label area) then we can just use the label clip path for the drawing # clip path. if (self._dw == self._lw) and (self._dh == self._lh): self._clip_drawing = self._clip_label # Otherwise we have to generate a separate path. else: h, w, r = float(self._dh), float(self._dw), float(self._pr) clip = ArcPath() if r: clip.moveTo(w - r, 0) clip.addArc(w - r, r, r, -90, 0) clip.lineTo(w, h - r) clip.addArc(w - r, h - r, r, 0, 90) clip.lineTo(r, h) clip.addArc(r, h - r, r, 90, 180) clip.lineTo(0, r) clip.addArc(r, r, r, 180, 270) clip.closePath() else: clip.moveTo(0, 0) clip.lineTo(w, 0) clip.lineTo(w, h) clip.lineTo(0, h) clip.closePath() # Set the clipping properties. clip.isClipPath = 1 clip.strokeColor = None clip.fillColor = None self._clip_drawing = clip
def draw(self): slices = self.slices _3d_angle = self.angle_3d _3dva = self._3dva = _360(_3d_angle+90) a0 = _2rad(_3dva) self._xdepth_3d = cos(a0)*self.depth_3d self._ydepth_3d = sin(a0)*self.depth_3d self._cx = self.x+self.width/2.0 self._cy = self.y+(self.height - self._ydepth_3d)/2.0 radiusx = radiusy = self._cx-self.x if self.xradius: radiusx = self.xradius if self.yradius: radiusy = self.yradius self._radiusx = radiusx self._radiusy = radiusy = (1.0 - self.perspective/100.0)*radiusy data = self.normalizeData() sum = self._sum CX = self.CX CY = self.CY OX = self.OX OY = self.OY rad_dist = self.rad_dist _fillSide = self._fillSide self._seriesCount = n = len(data) _sl3d = self._sl3d = [] g = Group() last = _360(self.startAngle) a0 = self.direction=='clockwise' and -1 or 1 for v in data: v *= a0 angle1, angle0 = last, v+last last = angle0 if a0>0: angle0, angle1 = angle1, angle0 _sl3d.append(_SL3D(angle0,angle1)) labels = _fixLabels(self.labels,n) a0 = _3d_angle a1 = _3d_angle+180 T = [] S = [] L = [] class WedgeLabel3d(WedgeLabel): _ydepth_3d = self._ydepth_3d def _checkDXY(self,ba): if ba[0]=='n': if not hasattr(self,'_ody'): self._ody = self.dy self.dy = -self._ody + self._ydepth_3d checkLabelOverlap = self.checkLabelOverlap for i in xrange(n): style = slices[i] if not style.visible: continue sl = _sl3d[i] lo = angle0 = sl.lo hi = angle1 = sl.hi if abs(hi-lo)<=1e-7: continue fillColor = _getShaded(style.fillColor,style.fillColorShaded,style.shading) strokeColor = _getShaded(style.strokeColor,style.strokeColorShaded,style.shading) or fillColor strokeWidth = style.strokeWidth cx0 = CX(i,0) cy0 = CY(i,0) cx1 = CX(i,1) cy1 = CY(i,1) #background shaded pie bottom g.add(Wedge(cx1,cy1,radiusx, lo, hi,yradius=radiusy, strokeColor=strokeColor,strokeWidth=strokeWidth,fillColor=fillColor, strokeLineJoin=1)) #connect to top if lo < a0 < hi: angle0 = a0 if lo < a1 < hi: angle1 = a1 if 1: p = ArcPath(strokeColor=strokeColor, fillColor=fillColor,strokeWidth=strokeWidth,strokeLineJoin=1) p.addArc(cx1,cy1,radiusx,angle0,angle1,yradius=radiusy,moveTo=1) p.lineTo(OX(i,angle1,0),OY(i,angle1,0)) p.addArc(cx0,cy0,radiusx,angle0,angle1,yradius=radiusy,reverse=1) p.closePath() if angle0<=_3dva and angle1>=_3dva: rd = 0 else: rd = min(rad_dist(angle0),rad_dist(angle1)) S.append((rd,p)) _fillSide(S,i,lo,strokeColor,strokeWidth,fillColor) _fillSide(S,i,hi,strokeColor,strokeWidth,fillColor) #bright shaded top fillColor = style.fillColor strokeColor = style.strokeColor or fillColor T.append(Wedge(cx0,cy0,radiusx,lo,hi,yradius=radiusy, strokeColor=strokeColor,strokeWidth=strokeWidth,fillColor=fillColor,strokeLineJoin=1)) text = labels[i] if style.label_visible and text: rat = style.labelRadius self._radiusx *= rat self._radiusy *= rat mid = sl.mid labelX = OX(i,mid,0) labelY = OY(i,mid,0) l=_addWedgeLabel(self,text,mid,labelX,labelY,style,labelClass=WedgeLabel3d) L.append(l) if checkLabelOverlap: l._origdata = { 'x': labelX, 'y':labelY, 'angle': mid, 'rx': self._radiusx, 'ry':self._radiusy, 'cx':CX(i,0), 'cy':CY(i,0), 'bounds': l.getBounds(), } self._radiusx = radiusx self._radiusy = radiusy S.sort(lambda a,b: -cmp(a[0],b[0])) if checkLabelOverlap and L: fixLabelOverlaps(L) for x in ([s[1] for s in S]+T+L): g.add(x) return g
def _draw_arc(self, inner_radius, outer_radius, startangle, endangle, color, border=None, colour=None, **kwargs): """ draw_arc(self, inner_radius, outer_radius, startangle, endangle, color) -> Group o inner_radius Float distance of inside of arc from drawing center o outer_radius Float distance of outside of arc from drawing center o startangle Float angle subtended by start of arc at drawing center (in radians) o endangle Float angle subtended by end of arc at drawing center (in radians) o color colors.Color object for arc (overridden by backwards compatible argument with UK spelling, colour). Returns a closed path object describing an arced box corresponding to the passed values. For very small angles, a simple four sided polygon is used. """ #Let the UK spelling (colour) override the USA spelling (color) if colour is not None: color = colour if border is None: border = color if color is None: color = colour if color == colors.white and border is None: # Force black border on strokecolor = colors.black # white boxes with elif border is None: # undefined border, else strokecolor = color # use fill colour elif border is not None: strokecolor = border angle = float(endangle - startangle) # angle subtended by arc if angle > .01: # Wide arc, represent with multiple boxes p = ArcPath(strokeColor=strokecolor, fillColor=color, strokewidth=0) #Note reportlab counts angles anti-clockwise from the horizontal #(as in mathematics, e.g. complex numbers and polar coordinates) #but we use clockwise from the vertical. Also reportlab uses #degrees, but we use radians. p.addArc(self.xcenter, self.ycenter, inner_radius, 90 - (endangle * 180 / pi), 90 - (startangle * 180 / pi), moveTo=True) p.addArc(self.xcenter, self.ycenter, outer_radius, 90 - (endangle * 180 / pi), 90 - (startangle * 180 / pi), reverse=True) p.closePath() return p else: #Cheat and just use a four sided polygon. # Calculate trig values for angle and coordinates startcos, startsin = cos(startangle), sin(startangle) endcos, endsin = cos(endangle), sin(endangle) boxes = Group() # Holds arc elements x0, y0 = self.xcenter, self.ycenter # origin of the circle x1, y1 = (x0 + inner_radius * startsin, y0 + inner_radius * startcos) x2, y2 = (x0 + inner_radius * endsin, y0 + inner_radius * endcos) x3, y3 = (x0 + outer_radius * endsin, y0 + outer_radius * endcos) x4, y4 = (x0 + outer_radius * startsin, y0 + outer_radius * startcos) return draw_polygon([(x1, y1), (x2, y2), (x3, y3), (x4, y4)], color, border)
def _draw_arc_arrow(self, inner_radius, outer_radius, startangle, endangle, color, border=None, shaft_height_ratio=0.4, head_length_ratio=1.0, orientation='right', colour=None, **kwargs): """Draw an arrow along an arc.""" #Let the UK spelling (colour) override the USA spelling (color) if colour is not None: color = colour if border is None: border = color if color is None: color = colour if color == colors.white and border is None: # Force black border on strokecolor = colors.black # white boxes with elif border is None: # undefined border, else strokecolor = color # use fill colour elif border is not None: strokecolor = border #if orientation == 'right': # startangle, endangle = min(startangle, endangle), max(startangle, endangle) #elif orientation == 'left': # startangle, endangle = max(startangle, endangle), min(startangle, endangle) #else : startangle, endangle = min(startangle, endangle), max(startangle, endangle) if orientation <> "left" and orientation <> "right": raise ValueError("Invalid orientation %s, should be 'left' or 'right'" \ % repr(orientation)) angle = float(endangle - startangle) # angle subtended by arc middle_radius = 0.5 * (inner_radius + outer_radius) boxheight = outer_radius - inner_radius shaft_height = boxheight * shaft_height_ratio shaft_inner_radius = middle_radius - 0.5 * shaft_height shaft_outer_radius = middle_radius + 0.5 * shaft_height headangle_delta = min( abs(asin(boxheight / middle_radius) * head_length_ratio), abs(angle)) if angle < 0: headangle_delta *= -1 #reverse it if orientation == "right": headangle = endangle - headangle_delta else: headangle = startangle + headangle_delta if startangle <= endangle: headangle = max(min(headangle, endangle), startangle) else: headangle = max(min(headangle, startangle), endangle) assert startangle <= headangle <= endangle \ or endangle <= headangle <= startangle # Calculate trig values for angle and coordinates startcos, startsin = cos(startangle), sin(startangle) headcos, headsin = cos(headangle), sin(headangle) endcos, endsin = cos(endangle), sin(endangle) x0, y0 = self.xcenter, self.ycenter # origin of the circle if abs(headangle_delta) >= abs(angle): #Cheat and just use a triangle. boxes = Group() # Holds arc elements if orientation == "right": x1, y1 = (x0 + inner_radius * startsin, y0 + inner_radius * startcos) x2, y2 = (x0 + outer_radius * startsin, y0 + outer_radius * startcos) x3, y3 = (x0 + middle_radius * endsin, y0 + middle_radius * endcos) else: x1, y1 = (x0 + inner_radius * endsin, y0 + inner_radius * endcos) x2, y2 = (x0 + outer_radius * endsin, y0 + outer_radius * endcos) x3, y3 = (x0 + middle_radius * startsin, y0 + middle_radius * startcos) return draw_polygon([(x1, y1), (x2, y2), (x3, y3)], color, border) elif orientation == "right": p = ArcPath(strokeColor=strokecolor, fillColor=color, strokewidth=0) #Note reportlab counts angles anti-clockwise from the horizontal #(as in mathematics, e.g. complex numbers and polar coordinates) #but we use clockwise from the vertical. Also reportlab uses #degrees, but we use radians. p.addArc(self.xcenter, self.ycenter, shaft_inner_radius, 90 - (headangle * 180 / pi), 90 - (startangle * 180 / pi), moveTo=True) p.addArc(self.xcenter, self.ycenter, shaft_outer_radius, 90 - (headangle * 180 / pi), 90 - (startangle * 180 / pi), reverse=True) p.lineTo(x0 + outer_radius * headsin, y0 + outer_radius * headcos) p.lineTo(x0 + middle_radius * endsin, y0 + middle_radius * endcos) p.lineTo(x0 + inner_radius * headsin, y0 + inner_radius * headcos) p.closePath() return p else: p = ArcPath(strokeColor=strokecolor, fillColor=color, strokewidth=0) #Note reportlab counts angles anti-clockwise from the horizontal #(as in mathematics, e.g. complex numbers and polar coordinates) #but we use clockwise from the vertical. Also reportlab uses #degrees, but we use radians. p.addArc(self.xcenter, self.ycenter, shaft_inner_radius, 90 - (endangle * 180 / pi), 90 - (headangle * 180 / pi), moveTo=True, reverse=True) p.addArc(self.xcenter, self.ycenter, shaft_outer_radius, 90 - (endangle * 180 / pi), 90 - (headangle * 180 / pi), reverse=False) p.lineTo(x0 + outer_radius * headsin, y0 + outer_radius * headcos) p.lineTo(x0 + middle_radius * startsin, y0 + middle_radius * startcos) p.lineTo(x0 + inner_radius * headsin, y0 + inner_radius * headcos) p.closePath() return p
def _draw_arc_arrow(self, inner_radius, outer_radius, startangle, endangle, color, border=None, shaft_height_ratio=0.4, head_length_ratio=1.0, orientation='right', colour=None, **kwargs): """Draw an arrow along an arc.""" #Let the UK spelling (colour) override the USA spelling (color) if colour is not None: color = colour if border is None: border = color if color is None: color = colour if color == colors.white and border is None: # Force black border on strokecolor = colors.black # white boxes with elif border is None: # undefined border, else strokecolor = color # use fill colour elif border is not None: strokecolor = border #if orientation == 'right': # startangle, endangle = min(startangle, endangle), max(startangle, endangle) #elif orientation == 'left': # startangle, endangle = max(startangle, endangle), min(startangle, endangle) #else : startangle, endangle = min(startangle, endangle), max(startangle, endangle) if orientation <> "left" and orientation <> "right" : raise ValueError("Invalid orientation %s, should be 'left' or 'right'" \ % repr(orientation)) angle = float(endangle - startangle) # angle subtended by arc middle_radius = 0.5*(inner_radius+outer_radius) boxheight = outer_radius - inner_radius shaft_height = boxheight*shaft_height_ratio shaft_inner_radius = middle_radius - 0.5*shaft_height shaft_outer_radius = middle_radius + 0.5*shaft_height headangle_delta = min(abs(asin(boxheight/middle_radius)*head_length_ratio), abs(angle)) if angle < 0 : headangle_delta *= -1 #reverse it if orientation=="right" : headangle = endangle-headangle_delta else : headangle = startangle+headangle_delta if startangle <= endangle : headangle = max(min(headangle, endangle), startangle) else : headangle = max(min(headangle, startangle), endangle) assert startangle <= headangle <= endangle \ or endangle <= headangle <= startangle # Calculate trig values for angle and coordinates startcos, startsin = cos(startangle), sin(startangle) headcos, headsin = cos(headangle), sin(headangle) endcos, endsin = cos(endangle), sin(endangle) x0,y0 = self.xcenter, self.ycenter # origin of the circle if abs(headangle_delta) >= abs(angle) : #Cheat and just use a triangle. if orientation=="right" : x1,y1 = (x0+inner_radius*startsin, y0+inner_radius*startcos) x2,y2 = (x0+outer_radius*startsin, y0+outer_radius*startcos) x3,y3 = (x0+middle_radius*endsin, y0+middle_radius*endcos) else : x1,y1 = (x0+inner_radius*endsin, y0+inner_radius*endcos) x2,y2 = (x0+outer_radius*endsin, y0+outer_radius*endcos) x3,y3 = (x0+middle_radius*startsin, y0+middle_radius*startcos) return draw_polygon([(x1,y1),(x2,y2),(x3,y3)], color, border) elif orientation=="right" : p = ArcPath(strokeColor=strokecolor, fillColor=color, strokewidth=0) #Note reportlab counts angles anti-clockwise from the horizontal #(as in mathematics, e.g. complex numbers and polar coordinates) #but we use clockwise from the vertical. Also reportlab uses #degrees, but we use radians. p.addArc(self.xcenter, self.ycenter, shaft_inner_radius, 90 - (headangle * 180 / pi), 90 - (startangle * 180 / pi), moveTo=True) p.addArc(self.xcenter, self.ycenter, shaft_outer_radius, 90 - (headangle * 180 / pi), 90 - (startangle * 180 / pi), reverse=True) p.lineTo(x0+outer_radius*headsin, y0+outer_radius*headcos) p.lineTo(x0+middle_radius*endsin, y0+middle_radius*endcos) p.lineTo(x0+inner_radius*headsin, y0+inner_radius*headcos) p.closePath() return p else : p = ArcPath(strokeColor=strokecolor, fillColor=color, strokewidth=0) #Note reportlab counts angles anti-clockwise from the horizontal #(as in mathematics, e.g. complex numbers and polar coordinates) #but we use clockwise from the vertical. Also reportlab uses #degrees, but we use radians. p.addArc(self.xcenter, self.ycenter, shaft_inner_radius, 90 - (endangle * 180 / pi), 90 - (headangle * 180 / pi), moveTo=True, reverse=True) p.addArc(self.xcenter, self.ycenter, shaft_outer_radius, 90 - (endangle * 180 / pi), 90 - (headangle * 180 / pi), reverse=False) p.lineTo(x0+outer_radius*headsin, y0+outer_radius*headcos) p.lineTo(x0+middle_radius*startsin, y0+middle_radius*startcos) p.lineTo(x0+inner_radius*headsin, y0+inner_radius*headcos) p.closePath() return p
def __init__(self, specification, drawing_callable, pages_to_draw=None, border=False, shade_missing=False): """ Parameters ---------- specification: labels.Specification instance The sizes etc of the label sheets. drawing_callable: callable A function (or other callable object) to call to draw an individual label. It will be given four parameters specifying the label. In order, these are a `reportlab.graphics.shapes.Drawing` instance to draw the label on, the width of the label, the height of the label, and the object to draw. The dimensions will be in points, the unit of choice for ReportLab. pages_to_draw: list of positive integers, default None The list pages to actually draw labels on. This is intended to be used with the preview methods to avoid drawing labels that will never be displayed. A value of None means draw all pages. border: Boolean, default False Whether or not to draw a border around each label. shade_missing: Boolean or ReportLab colour, default False Whether or not to shade missing labels (those specified through the partial_pages method). False means leave the labels unshaded. If a ReportLab colour is given, the labels will be shaded in that colour. A value of True will result in the missing labels being shaded in the hex colour 0xBBBBBB (a medium-light grey). Notes ----- If you specify a pages_to_draw list, pages not in that list will be blank since the drawing function will not be called on that page. This could have a side-affect if you rely on the drawing function modifying some global values. For example, in the nametags.py and preview.py demo scripts, the colours for each label are picked by a pseduo-random number generator. However, in the preview script, this generator is not advanced and so the colours on the last page differ between the preview and the actual output. """ # Save our arguments. specification._calculate() self.specs = deepcopy(specification) self.drawing_callable = drawing_callable self.pages_to_draw = pages_to_draw self.border = border if shade_missing == True: self.shade_missing = colors.HexColor(0xBBBBBB) else: self.shade_missing = shade_missing # Set up some internal variables. self._lw = self.specs.label_width * mm self._lh = self.specs.label_height * mm self._cr = self.specs.corner_radius * mm self._dw = (self.specs.label_width - self.specs.left_padding - self.specs.right_padding) * mm self._dh = (self.specs.label_height - self.specs.top_padding - self.specs.bottom_padding) * mm self._lp = self.specs.left_padding * mm self._bp = self.specs.bottom_padding * mm self._pr = self.specs.padding_radius * mm self._used = {} self._pages = [] self._current_page = None # Page information. self._pagesize = (float(self.specs.sheet_width * mm), float(self.specs.sheet_height * mm)) self._numlabels = [self.specs.rows, self.specs.columns] self._position = [1, 0] self.label_count = 0 self.page_count = 0 # Background image. if self.specs.background_image: self._bgimage = deepcopy(self.specs.background_image) # Different classes are scaled in different ways... if isinstance(self._bgimage, Image): self._bgimage.x = 0 self._bgimage.y = 0 self._bgimage.width = self._pagesize[0] self._bgimage.height = self._pagesize[1] elif isinstance(self._bgimage, Drawing): self._bgimage.shift(0, 0) self._bgimage.scale(self._pagesize[0] / self._bgimage.width, self._pagesize[1] / self._bgimage.height) else: raise ValueError("Unhandled background type.") # Background from a filename. elif self.specs.background_filename: self._bgimage = Image(0, 0, self._pagesize[0], self._pagesize[1], self.specs.background_filename) # No background. else: self._bgimage = None # Borders and clipping paths. We need two clipping paths; one for the # label as a whole (which is identical to the border), and one for the # available drawing area (i.e., after taking the padding into account). # This is necessary because sometimes the drawing area can extend # outside the border at the corners, e.g., if there is left padding # only and no padding radius, then the 'available' area corners will be # square and go outside the label corners if they are rounded. # Copy some properties to a local scope. h, w, r = float(self._lh), float(self._lw), float(self._cr) # Create the border from a path. If the corners are not rounded, skip # adding the arcs. border = ArcPath() if r: border.moveTo(w - r, 0) border.addArc(w - r, r, r, -90, 0) border.lineTo(w, h - r) border.addArc(w - r, h - r, r, 0, 90) border.lineTo(r, h) border.addArc(r, h - r, r, 90, 180) border.lineTo(0, r) border.addArc(r, r, r, 180, 270) border.closePath() else: border.moveTo(0, 0) border.lineTo(w, 0) border.lineTo(w, h) border.lineTo(0, h) border.closePath() # Set the properties and store. border.isClipPath = 0 border.strokeWidth = 1 border.strokeColor = colors.black border.fillColor = None self._border = border # Clip path for the label is the same as the border. self._clip_label = deepcopy(border) self._clip_label.isClipPath = 1 self._clip_label.strokeColor = None self._clip_label.fillColor = None # If there is no padding (i.e., the drawable area is the same as the # label area) then we can just use the label clip path for the drawing # clip path. if (self._dw == self._lw) and (self._dh == self._lh): self._clip_drawing = self._clip_label # Otherwise we have to generate a separate path. else: h, w, r = float(self._dh), float(self._dw), float(self._pr) clip = ArcPath() if r: clip.moveTo(w - r, 0) clip.addArc(w - r, r, r, -90, 0) clip.lineTo(w, h - r) clip.addArc(w - r, h - r, r, 0, 90) clip.lineTo(r, h) clip.addArc(r, h - r, r, 90, 180) clip.lineTo(0, r) clip.addArc(r, r, r, 180, 270) clip.closePath() else: clip.moveTo(0, 0) clip.lineTo(w, 0) clip.lineTo(w, h) clip.lineTo(0, h) clip.closePath() # Set the clipping properties. clip.isClipPath = 1 clip.strokeColor = None clip.fillColor = None self._clip_drawing = clip
def __init__(self, specification, drawing_callable, pages_to_draw=None, border=False, shade_missing=False): """ Parameters ---------- specification: labels.Specification instance The sizes etc of the label sheets. drawing_callable: callable A function (or other callable object) to call to draw an individual label. It will be given four parameters specifying the label. In order, these are a `reportlab.graphics.shapes.Drawing` instance to draw the label on, the width of the label, the height of the label, and the object to draw. The dimensions will be in points, the unit of choice for ReportLab. pages_to_draw: list of positive integers, default None The list pages to actually draw labels on. This is intended to be used with the preview methods to avoid drawing labels that will never be displayed. A value of None means draw all pages. border: Boolean, default False Whether or not to draw a border around each label. shade_missing: Boolean or ReportLab colour, default False Whether or not to shade missing labels (those specified through the partial_pages method). False means leave the labels unshaded. If a ReportLab colour is given, the labels will be shaded in that colour. A value of True will result in the missing labels being shaded in the hex colour 0xBBBBBB (a medium-light grey). Notes ----- If you specify a pages_to_draw list, pages not in that list will be blank since the drawing function will not be called on that page. This could have a side-affect if you rely on the drawing function modifying some global values. For example, in the nametags.py and preview.py demo scripts, the colours for each label are picked by a pseduo-random number generator. However, in the preview script, this generator is not advanced and so the colours on the last page differ between the preview and the actual output. """ # Save our arguments. specification._calculate() self.specs = deepcopy(specification) self.drawing_callable = drawing_callable self.pages_to_draw = pages_to_draw self.border = border if shade_missing == True: self.shade_missing = colors.HexColor(0xBBBBBB) else: self.shade_missing = shade_missing # Set up some internal variables. self._lw = self.specs.label_width * mm self._lh = self.specs.label_height * mm self._cr = self.specs.corner_radius * mm self._used = {} self._pages = [] self._current_page = None # Page information. self._pagesize = (float(self.specs.sheet_width*mm), float(self.specs.sheet_height*mm)) self._numlabels = [self.specs.rows, self.specs.columns] self._position = [1, 0] self.label_count = 0 self.page_count = 0 # Have to create the border from a path so we can use it as a clip path. border = ArcPath() # Copy some properties to a local scope. h, w, cr = float(self._lh), float(self._lw), float(self._cr) # If the border has rounded corners. if self._cr: border.moveTo(w - cr, 0) border.addArc(w - cr, cr, cr, -90, 0) border.lineTo(w, h - cr) border.addArc(w - cr, h - cr, cr, 0, 90) border.lineTo(cr, h) border.addArc(cr, h - cr, cr, 90, 180) border.lineTo(0, cr) border.addArc(cr, cr, cr, 180, 270) border.closePath() # No rounded corners. else: border.moveTo(0, 0) border.lineTo(w, 0) border.lineTo(w, h) border.lineTo(0, h) border.closePath() # Use it as a clip path. border.isClipPath = 1 border.strokeColor = None border.fillColor = None # And done. self._border = border # The border doesn't show up if its part of a clipping path when # outputting to an image. If its needed, make a copy and turn the # clipping path off. if self.border: from copy import copy self._border_visible = copy(self._border) self._border_visible.isClipPath = 0 self._border_visible.strokeWidth = 1 self._border_visible.strokeColor = colors.black else: self._border_visible = None