def bugmark(self, P): P = P - Point(1, 1) style = basestyle.Duplicate() style.fill_pattern = SolidPattern(StandardColors.black) style.line_pattern = SolidPattern(StandardColors.black) self.prop_stack.AddStyle(style) self.rectangle(2, 0, 0, 2, P.x, P.y)
def try_add_style(self, key, val): if key == 'fill': if val == 'none': self.style.fill_pattern = EmptyPattern else: color = csscolor(val) self._print('fill', color) self.style.fill_pattern = SolidPattern(color) elif key == 'stroke': if val == 'none': self.style.line_pattern = EmptyPattern else: color = csscolor(val) self._print('stroke', color) self.style.line_pattern = SolidPattern(color) elif key == 'stroke-width': width = self.user_length(val) # Multiply the width with a value taken from the # transformation matrix because so far transforming an # object in Sketch does not affect the stroke width in any # way. Thus we have to do that explicitly here. # FIXME: using m11 is not really the best approach but in # many cases better than using the width as is. width = self.trafo.m11 * width self._print('width', width) self.style.line_width = width elif key == 'stroke-linejoin': self.style.line_join = join[val] elif key == 'stroke-linecap': self.style.line_cap = cap[val] elif key == 'font-family': try: # convert val to 8bit string. self.style.font = GetFont(str(val)) except UnicodeError: # If it's not ASCII we probably won't have the font, so # use the default one. # FIXME: Give a warning pass elif key == 'font-size': self.style.font_size = self.user_length(val) ####self.style.font_size = float(val) elif key == 'text-anchor': if val == 'start': self.halign = text.ALIGN_LEFT elif val == 'middle': self.halign = text.ALIGN_CENTER elif val == 'end': self.halign = text.ALIGN_RIGHT
def ls(self): style = self.style style.line_pattern = SolidPattern(self.line_color) style.line_width = self.line_width style.line_join = self.line_join style.line_cap = self.line_cap style.line_dashes = self.line_dashes
def getStdConnLine(fromobj, toobj, cplist=()): global tarrw1, tarrw2, tarrw3 #~ snAdct['ptfl_1'], # from (startpoint - moveto) #~ snBdct['box'], # to (endpoint) #~ # here optional arguments for 'in-between' points; #~ # (ordered) tuple of dict, where key is command: #~ # like in tikz:: '|' means along y , '-' means along x #~ # (last {'-':-10}, is not needed - endpoint specifies it #~ ({'-':10}, {'|':-20}, {'-':-30}, {'|':-40}) # for now, we expect that fromobj is always going to be a # 'pointer' tf line; and to obj is going to be a box connlineCol = SolidPattern(CreateRGBColor(0.3, 0.3, 0.3)) # retrieve start point - endpoint of fromobj ptf line # get 2nd node (=segment 1) - in there, Point() is third in list ([2]) tmpep_start = fromobj.paths[0].Segment(1)[2] # NOTE though: 'skpoint' object has only read-only attributes !! (cannot assign to .x) # retrieve end point - the center left (west) point of the box of to obj: #~ tmpep_end = toobj.bounding_rect.center() #~ tmpep_end.x = toobj.bounding_rect.left # there seems to be a 10 units padding for bounding_rect; (see below) # compensate it tobr = toobj.bounding_rect.grown(-10) tmpep_end = Point(tobr.left, tobr.center().y) # start drawing the line tpath = CreatePath() tpath.AppendLine(tmpep_start) # moveto # are there any 'in-between' connection points ? prevPoint = tmpep_start nextPoint = tmpep_start for ibcp in cplist: axiscommand = ibcp.keys()[0] moveval = ibcp[axiscommand] if axiscommand == '-': # along x #~ nextPoint.x = prevPoint.x + moveval nextPoint = Point(prevPoint.x + moveval, prevPoint.y) elif axiscommand == '|': # along y #~ nextPoint.y = prevPoint.y + moveval nextPoint = Point(prevPoint.x, prevPoint.y + moveval) tpath.AppendLine(nextPoint) # moveto prevPoint = nextPoint tpath.AppendLine(tmpep_end) # lineto tline = PolyBezier((tpath, )) #~ tline.AddStyle(tbase_style) # of Graphics.properties (also in compound, document) - seems to add a 'layer' if dynamic; else seems to 'replace' ?! tline.SetProperties(line_width=2.0, line_pattern=connlineCol, line_arrow2=tarrw2) tline.update_rects() return tline
def setfillstyle(self): style = basestyle.Duplicate() if reff.fill.type == 1: style.fill_pattern = SolidPattern( apply(CreateRGBColor, reff.fill.color)) elif reff.fill.type == 3: style.fill_pattern = HatchingPattern( apply(CreateRGBColor, reff.fill.color), StandardColors.white, Point(2.0, 1.0), 5, 1) if reff.edge.visible: style.line_pattern = SolidPattern( apply(CreateRGBColor, reff.edge.color)) style.line_width = reff.edge.width if reff.edge.widthmode == 0: style.line_width = style.line_width * self.Scale style.line_dashes = reff.edge.dashtable[reff.edge.type - 1] self.prop_stack.AddStyle(style)
def setlinestyle(self): style = basestyle.Duplicate() style.line_pattern = SolidPattern( apply(CreateRGBColor, reff.line.color)) style.line_width = reff.line.width if reff.line.widthmode == 0: style.line_width = style.line_width * self.Scale style.line_dashes = reff.line.dashtable[reff.line.type - 1] self.prop_stack.AddStyle(style)
def __init__(self, master=None, **kw): apply(PatternFrame.__init__, (self, master), kw) label = Label(self, text = _("Color")) label.pack(side = LEFT, expand = 1, anchor = E) self.color_but = ColorButton(self, width = 3, height = 1, command = self.set_color) self.color_but.pack(side = LEFT, expand = 1, anchor = W) self.SetPattern(SolidPattern(StandardColors.black))
def CreateBrushIndirect(self): style, r, g, b, hatch = self.get_struct('<hBBBxh') if style == 1: pattern = EmptyPattern else: pattern = SolidPattern( CreateRGBColor(r / 255.0, g / 255.0, b / 255.0)) self.add_gdiobject((('fill_pattern', pattern), )) self._print('->', style, r, g, b, hatch)
def ta_set_colour(self, colour): if tuple(colour) == (255, 255, 255): return EmptyPattern else: return SolidPattern( CreateRGBColor( float(colour[0]) / 255.0, float(colour[1]) / 255.0, float(colour[2]) / 255.0))
def change_color(self, object): if object.has_properties: properties = object.Properties() pattern = properties.fill_pattern if pattern.is_Solid: if pattern.Color() == self.color: pattern = SolidPattern(black) else: pattern = SolidPattern(white) undo = properties.SetProperty(fill_pattern=pattern) self.undo.append(undo) pattern = properties.line_pattern if pattern.is_Solid: if pattern.Color() == self.color: pattern = SolidPattern(black) else: pattern = SolidPattern(white) undo = properties.SetProperty(line_pattern=pattern) self.undo.append(undo)
def do_apply(self): kw = {} if not self.var_color_none.get(): color = self.color_but.Color() kw["line_pattern"] = SolidPattern(color) kw["line_width"] = self.var_width.get() kw["line_join"] = self.opt_join.GetValue() kw["line_cap"] = self.opt_cap.GetValue() kw["line_dashes"] = self.opt_dash.GetValue() kw["line_arrow1"] = self.opt_arrow1.GetValue() kw["line_arrow2"] = self.opt_arrow2.GetValue() else: kw["line_pattern"] = EmptyPattern self.set_properties(_("Set Outline"), 'line', kw)
def getQuickLine(tstart, tend): # expected tuple at input pstart = Point(tstart[0], tstart[1]) pend = Point(tend[0], tend[1]) tpath = CreatePath() # Note - apparently, the first appended point is "moveTo"; # .. the ubsequent ones being "LineTo" tpath.AppendLine(pstart) # moveto tpath.AppendLine(pend) # lineto tline = PolyBezier((tpath,)) tline.AddStyle(tbase_style) # of Graphics.properties (also in compound, document) - seems to add a 'layer' if dynamic; else seems to 'replace' ?! tline.SetProperties(line_pattern = SolidPattern(CreateRGBColor(0.7, 0.7, 0.9))) return tline
def CreatePenIndirect(self): style, widthx, widthy, r, g, b = self.get_struct('<hhhBBBx') cap = (style & 0x0F00) >> 8 join = (style & 0x00F0) >> 4 style = style & 0x000F if style == 5: pattern = EmptyPattern else: pattern = SolidPattern( CreateRGBColor(r / 255.0, g / 255.0, b / 255.0)) width = abs(widthx * self.trafo.m11) self.add_gdiobject((( 'line_pattern', pattern, ), ('line_width', width))) self._print('->', style, widthx, widthy, r, g, b, cap, join)
def get_pattern(self, color, style=None): if style == -1: return EmptyPattern rgb = self.colors[color] if style is not None: if color in (BLACK, DEFAULT_COLOR): if style > 0 and style <= 20: rgb = Blend(self.colors[WHITE], rgb, (20 - style) / 20.0) elif style == 0: rgb = self.colors[WHITE] else: if style >= 0 and style < 20: rgb = Blend(self.colors[BLACK], rgb, (20 - style) / 20.0) elif style > 20 and style <= 40: rgb = Blend(self.colors[WHITE], rgb, (style - 20) / 20.0) return SolidPattern(rgb)
def create_text(context): # Create the text 'xyzzy' at 100,100. The first parameter to the # constructor is an affine transformation. text = SimpleText(Translation(100, 100), "xyzzy") # Set the font to 36pt Times-Bold and fill with solid green. # The text object is modified by this method, but the text object is # not yet part of the document, so we don't have to deal with undo # here. text.SetProperties(fill_pattern = SolidPattern(StandardColors.green), font = GetFont('Times-Bold'), font_size = 36) # Finally, insert the text object at the top of the current layer # and select it. Like all public document methods that modify the # document, the Insert method takes care of undo information itself. context.document.Insert(text)
def __init__(self, loader): self.loader = loader self.trafo = self.basetrafo = Trafo() self.state_stack = () self.style = loader.style.Copy() self.style.line_pattern = EmptyPattern self.style.fill_pattern = SolidPattern(StandardColors.black) self.current_text = "" self.style.font = GetFont("Times-Roman") self.style.font_size = 12 self.halign = text.ALIGN_LEFT self.named_objects = {} self.in_defs = 0 self.paths = None self.path = None self.depth = 0 self.indent = ' '
def TEXT(self, size): P = self.Pnt() F = self.Enum() S = self.getstr() T = Translation(self.trafo(P)) Py = Point(reff.text.orientation[0]).normalized() Px = Point(reff.text.orientation[1]).normalized() B = transform_base(Point(0.0, 0.0), reff.text.expansion * Px, Py) self.style = basestyle.Duplicate() self.style.font = GetFont(fntlst[self.fntmap[reff.text.fontindex]]) self.style.font_size = reff.text.height * self.Scale self.style.fill_pattern = SolidPattern( apply(CreateRGBColor, reff.text.color)) O = text.SimpleText(text=S, trafo=T(B), halign=text.ALIGN_LEFT, valign=text.ALIGN_BASE, properties=self.get_prop_stack()) self.append_object(O)
def fs(self): if self.gradient_geo: pattern = self.make_gradient_pattern() else: pattern = SolidPattern(self.fill_color) self.style.fill_pattern = pattern
def main(): import Sketch global doc global tbase_style Sketch.init_lib() draw_printable = 1 draw_visible = 0 embed_fonts = 0 eps_for = util.get_real_username() eps_date = util.current_date() eps_title = None rotate = 0 #doc = load.load_drawing('') # from mainwindow.py: self.SetDocument(Document(create_layer = 1)) doc = Document(create_layer = 1) # get font info first Graphics.font.read_font_dirs() # indicate start of coord system first # coord system:: + goes upward / rightward # from RectangleCreator: trafo = Trafo(off.x, 0, 0, off.y, end.x, end.y) # actually, there 'end' seems to correspond to start (llc: lower left corner of rectangle) - and 'off' to the length extended in each direction (i.e. width, height - but can be negative) ; so instead of 'end' - calling it 'start' start_x = 5 start_y = 5 off_x = -10 off_y = -10 trec = Rectangle(trafo = Trafo(off_x, 0, 0, off_y, start_x, start_y)) trec.update_rects() doc.Insert(trec) # from create_text.py textfld = SimpleText(Translation(50, 50), "xyzzy") textfld.SetProperties(fill_pattern = SolidPattern(StandardColors.green), font = GetFont('Courier-Bold'),#('Times-Bold'), font_size = 36) #copy textfld textfld2 = textfld.Duplicate() textfld2.SetProperties(fill_pattern = SolidPattern(StandardColors.blue)) # change color only # rotate textfld angleDeg = 45 angleRad = pi*(angleDeg/180.0) # ensure float op - could use math.radians instead textfld.Transform(Rotation(angleRad)) # Rotation(angle, center) textfld.update_rects() # probably a good idea # change textfld's text with the current width (that we see) # get bounding box of text a = textfld.properties llx, lly, urx, ury = a.font.TextBoundingBox(textfld.text, a.font_size) # calculate width - its of UNTRANSFORMED text twidth = urx - llx # insert this width as text in textbox now: #~ textfld.text = str(twidth) #~ textfld.update_rects() # probably a good idea - again # get textfield as bezier textbez = textfld.AsBezier() #~ print textbez # returns Sketch.Graphics.group.Group; subclass of EditableCompound # the bounding rectangle - from Compound (type is Rect): textbez_bRect = textbez.bounding_rect # calc width now t2width = textbez_bRect.right - textbez_bRect.left # insert this width as text in textbox now: textfld.text = str(t2width) textfld.update_rects() # probably a good idea - again #~ doc.Insert(textfld) # create a line # using create_spiral.py technique below (see syntax note #(A1)) tpath = CreatePath() # Note - apparently, the first appended point is "moveTo"; # .. the ubsequent ones being "LineTo" tp = Point(textbez_bRect.left,textbez_bRect.bottom) tpath.AppendLine(tp) # moveto tp = Point(textbez_bRect.left,textbez_bRect.top) tpath.AppendLine(tp) # lineto tp = Point(textbez_bRect.right,textbez_bRect.top) tpath.AppendLine(tp) # lineto tp = Point(textbez_bRect.right,textbez_bRect.bottom) tpath.AppendLine(tp) # lineto tline = PolyBezier((tpath,)) tline.AddStyle(tbase_style) # of Graphics.properties (also in compound, document) - seems to add a 'layer' if dynamic; else seems to 'replace' ?! #~ doc.Insert(tline) # group tline and textfld ... # footprints.py has Group(foot_prints = []) tgrp = Group([textfld, textfld2, tline]) tgrp.update_rects() doc.Insert(tgrp) # add a box.. around textfld2 # use radius1, radius2 != 0 AND 1 (logarithmic) to get RoundedRectangle (best between 0.0 and 1.0) tfbr = textfld2.bounding_rect start_x = tfbr.left start_y = tfbr.bottom off_x = tfbr.right - tfbr.left off_y = tfbr.top - tfbr.bottom twid = abs(off_x - start_x) thei = abs(off_y - start_y) radfact = 1.2*twid/thei tradius = 0.05 # if we want to specify a single one, then the actual look will depend on the dimesions of the rectangle - so must 'smooth' it with radfact... trec = Rectangle(trafo = Trafo(off_x, 0, 0, off_y, start_x, start_y), radius1 = tradius, radius2 = tradius*radfact) trec.update_rects() doc.Insert(trec) # add another box - any where start_x = 100.0 start_y = 100.0 off_x = 50.0 off_y = 50.0 trec2 = Rectangle(trafo = Trafo(off_x, 0, 0, off_y, start_x, start_y)) trec2.update_rects() doc.Insert(trec2) # try change props post insert - OK trec2.SetProperties(fill_pattern = SolidPattern(StandardColors.yellow), line_width = 2.0, line_pattern = SolidPattern(CreateRGBColor(0.5, 0.5, 0.7))) # try move the group as a whole (Translate - syntax: spread.py) # say, align the right edge of tline to left edge of trec2 (x direction) # NOTE: group does not define own .AsBezier(self); # but it has tgrp.bounding_rect (although python doesn't show it in dir(tgrp)) # also there is Rectangle.bounding_rect # NOTE though - it seems bounding_rect is somehow padded, with (at least) 10 units in each direction! (also, bounding rect of line will include the arrow) xmove = (trec2.bounding_rect.left+10)-(tline.bounding_rect.right-10) #~ print xmove, trec2.bounding_rect.left, tline.bounding_rect.right tgrp.Translate(Point(xmove, 0)) tgrp.update_rects() # add temporary line to indicate bounding boxes # and effect of padding (may cover the very first trec) tmpbr = trec2.bounding_rect doc.Insert( getQuickLine( (0,0), (trec2.bounding_rect.left+10, tline.bounding_rect.top-10) ) ) # end of draw - generate output file filename = '' psfile = 'vectorout.ps' sk2ps(filename, psfile, printable= draw_printable, visible = draw_visible, For = eps_for, CreationDate = eps_date, Title = eps_title, rotate = rotate, embed_fonts = embed_fonts)
elp.trafo = Trafo(elp_radius, 0, 0, elp_radius, elp_startx, elp_starty) elpbz = elp.AsBezier() elpbz_nl = elpbz.Paths()[0].NodeList() # as list of Point(x,y) elpbz_tnl = [] # as list of tuples (x,y) for tpnt in elpbz_nl: elpbz_tnl.append( (tpnt.x, tpnt.y) ) tarrw1 = Arrow(elpbz_tnl, closed=1) #_nl or _tnl - all the same here; ellipse sends four points, which in Arrow are AppendBezier (AppendLine only for two points -- but still it looks like a diamond.. ).. # the difference is in tarrw1.Paths()[0].arc_lengths() vs elpbz.Paths()[0].arc_lengths() tarrw1.path = elpbz.Paths()[0] # and this FINALLY makes the arrow a circle! tarrw2 = Arrow(arpath2, closed=1) global tbase_style tbase_style = Style() tbase_style.fill_pattern = EmptyPattern tbase_style.fill_transform = 1 tbase_style.line_pattern = SolidPattern(StandardColors.red) tbase_style.line_width = 2.0 tbase_style.line_join = const.JoinMiter tbase_style.line_cap = const.CapButt tbase_style.line_dashes = () tbase_style.line_arrow1 = tarrw1 tbase_style.line_arrow2 = tarrw2 tbase_style.font = None tbase_style.font_size = 12.0 # from create_spiral.py from Sketch import CreatePath # from footprints.py from Sketch import Group
def DibCreatePatternBrush(self): self.add_message(_("Bitmap brushes not yet implemented. Using black")) pattern = SolidPattern(StandardColors.black) self.add_gdiobject((('fill_pattern', pattern), ))
def main(): import Sketch global doc, psfile global tbase_style, courfont Sketch.init_lib() draw_printable = 1 draw_visible = 0 embed_fonts = 0 eps_for = util.get_real_username() eps_date = util.current_date() eps_title = None rotate = 0 doc = Document(create_layer=1) ## ***************************** # start of drawing # indicate start of coord system first: + goes upward / rightward trec = getQuickRect(5, 5, -10, -10) doc.Insert(trec) # from create_text.py textfld = SimpleText(Translation(0, 0), "24pt") textfld.SetProperties(font=courfont, font_size=24) doc.Insert(textfld) ## add struct box nodes structNodeA = getStructBoxnode( "struct1", # title label ("*pnt1", "*pointer2", "*point3"), ("var1", "variable2", "Varib3")) structNodeB = getStructBoxnode( "str2", # title label ( "*pnt1", "*pnt2", "*pnt3", "*pnt4", "*pnt5", "*pnt6", ), ("var1", "var2", "var3", "var4", "var5", "var6", "var7", "var8", "var9")) structNodeC = getStructBoxnode( "str3", # title label ("*pnt1", ), ()) snAgrp = structNodeA[0] snBgrp = structNodeB[0] snCgrp = structNodeC[0] ## position nodes # place node A snAgrp.Translate(Point(100, 200)) snAgrp.update_rects() # place node B - 'below left' of node A # (i.e. find lower right corner of A group; # align upper left corner of B with that) sabr = snAgrp.bounding_rect sbbr = snBgrp.bounding_rect xtran = sabr.right - sbbr.left ytran = sabr.bottom - sbbr.top snBgrp.Translate(Point(xtran, ytran)) snBgrp.update_rects() sbbr = snBgrp.bounding_rect # place node C - below, left aligned to node B scbr = snCgrp.bounding_rect xtran = sbbr.left - scbr.left ytran = sbbr.bottom - scbr.top snCgrp.Translate(Point(xtran, ytran)) snCgrp.update_rects() scbr = snCgrp.bounding_rect # show struct nodes on drawing doc.Insert(snAgrp) doc.Insert(snBgrp) doc.Insert(snCgrp) # display the bounding boxes of struct nodes trecA = getQuickRect(sabr.left, sabr.bottom, sabr.right - sabr.left, sabr.top - sabr.bottom) trecA.SetProperties(line_pattern=SolidPattern(StandardColors.red)) trecB = getQuickRect(sbbr.left, sbbr.bottom, sbbr.right - sbbr.left, sbbr.top - sbbr.bottom) trecB.SetProperties(line_pattern=SolidPattern(StandardColors.red)) trecC = getQuickRect(scbr.left, scbr.bottom, scbr.right - scbr.left, scbr.top - scbr.bottom) trecC.SetProperties(line_pattern=SolidPattern(StandardColors.red)) doc.Insert(trecA) doc.Insert(trecB) doc.Insert(trecC) # make a connection # get dicts first snAdct = structNodeA[1] snBdct = structNodeB[1] snCdct = structNodeC[1] # put a rect around snBdct['box'] # since grown(-10) aligns to box; shows that theres 10 units padding! tbr = snBdct['box'].bounding_rect.grown(-10) trecC = getQuickRect(tbr.left, tbr.bottom, tbr.right - tbr.left, tbr.top - tbr.bottom) trecC.SetProperties(line_pattern=SolidPattern(StandardColors.green)) doc.Insert(trecC) # for easier calc after: yjump = abs(snAdct['ptfl_1'].bounding_rect.center().y - snBdct['box'].bounding_rect.center().y) / 2 # 'standard' connection: # start from 2nd [1] pointer of structNodeA - end in 'input' (left) of box of structNodeB # so, from dict we need:: structNodeA:'ptfl_1'; and structNodeB:'box' tconnline = getStdConnLine( snAdct['ptfl_1'], # from (startpoint - moveto) snBdct['box'], # to (endpoint) # here optional arguments for 'in-between' points; # (ordered) tuple of dict, where key is command: # like in tikz:: '|' means along y , '-' means along x # (last {'-':-10}, is not needed - endpoint specifies it ({ '-': 30 }, { '|': -yjump }, { '-': -40 }, { '|': -yjump })) doc.Insert(tconnline) # for easier calc after - # we won't use the "half-the-distance" directly, so we don't divide /2 here yjumpAC = abs(snAdct['ptfl_2'].bounding_rect.center().y - snCdct['box'].bounding_rect.center().y) tconnlineAC = getStdConnLine( snAdct['ptfl_2'], # from (startpoint - moveto) snCdct['box'], # to (endpoint) ({ '-': 20 }, { '|': -(yjump - 30) }, { '-': -40 }, { '|': -(yjumpAC - (yjump - 30)) })) doc.Insert(tconnlineAC) ## ***************************** # end of drawing - generate output file filename = '' # was .sk filename to convert sk2ps(filename, thisfilename, printable=draw_printable, visible=draw_visible, For=eps_for, CreationDate=eps_date, Title=eps_title, rotate=rotate, embed_fonts=embed_fonts)
def lp(self, color=None): if color is None: self.style.line_pattern = self.pattern else: self.style.line_pattern = SolidPattern(self.convert_color(color))
def ps(self, color): self.pattern = SolidPattern(self.convert_color(color))
def set_color(self): self.pattern = SolidPattern(self.__get_color()) self.issue(CHANGED)
def DropAt(self, x, y, what, data): if what == DROP_COLOR: self.init_from_pattern(SolidPattern(data))
def recompute(self): chunker = Chunker(rstrip(self.text), self.styling, '\t') objects = [] options = {} x = 0 y = 0 maxy = 0 line = [] while not chunker.eof(): text, styles = chunker.get() family = styles['family'].options color = styles['color'].options fill = SolidPattern(getattr(StandardColors, color)) # XXX should be done in advance size = styles['size'].options bold = styles['bold'].options italic = styles['italic'].options if bold and italic: attr = [ 'Bold Italic', 'Bold Oblique', 'Demi Oblique', 'Demi Italic', 'Medium Italic', 'Demi Bold Italic' ] elif bold: attr = ['Bold', 'Demi', 'Medium', 'Demi Bold'] elif italic: attr = [ 'Italic', 'Oblique', 'Book Oblique', 'Light Italic', 'Regular Oblique', 'Regular Italic' ] else: attr = ['Roman', 'Book', 'Light', 'Regular'] supersub = styles['supersub'].options if len(text) > 0: height = 1.2 * size # XXX is that ok ? if maxy < height: maxy = height if supersub != 'normal': if supersub == 'superscript': offset = size * 0.5 else: offset = -size * 0.15 size = 0.5 * size else: offset = 0 textObj = SimpleText(Translation(x, offset), text) objects.append(textObj) line.append(textObj) psfont = FONTS.ps(family=family, attr=attr) font = GetFont(psfont) textObj.SetProperties(font=font, font_size=size, fill_pattern=fill) left, bottom, right, top = textObj.coord_rect width = right - left else: width = 0 reason = chunker.reason() if reason == '\n' or chunker.eof(): if width > 0: y = y - maxy else: y = y - size maxy = 0 x = 0 for obj in line: obj.Transform(self.trafo(Translation(0, y))) line = [] elif reason == '\t': x = x + width + size # # XXX this is very provisional x = ceil(x / 50.) * 50. else: x = x + width # XXX this is a problem: what happens, if the MultilineText onbject # is empty ?? #~ if len(objects)>0: self.set_objects(objects)
def read_objects(self, objects): n_objects = 0 # Traverse the list of drawfile object for object in objects: if isinstance(object, drawfile.group): # Start a group object in the document self.begin_group() # Descend into the group n_objects_lower = self.read_objects(object.objects) # If the group was empty then don't try to end it if n_objects_lower == 0: # self.__pop() (self.composite_class, self.composite_args, self.composite_items, self.composite_stack) = self.composite_stack else: # End group object self.end_group() n_objects = n_objects + 1 elif isinstance(object, drawfile.tagged): # Tagged object n_objects_lower = self.read_objects([object.object]) if n_objects_lower != 0: n_objects = n_objects + 1 elif isinstance(object, drawfile.path): # Path object n_objects = n_objects + 1 # Set the path style self.style.line_width = object.width / scale if object.style['join'] == 'mitred': self.style.line_join = const.JoinMiter if object.style['start cap'] == 'butt': self.style.line_cap = const.CapButt elif object.style['start cap'] == 'round': if object.width > 0: width = 0.5 length = 0.5 else: width = 0.0 length = 0.0 # Draw arrow path = [(0.0, width), (0.5 * length, width, length, 0.5 * width, length, 0.0), (length, -0.5 * width, 0.5 * length, -width, 0.0, -width), (0.0, width)] self.style.line_arrow1 = Arrow(path, 1) elif object.style['start cap'] == 'square': if object.width > 0: width = 0.5 length = 0.5 else: width = 0.0 length = 0.0 # Draw arrow path = [(0.0, width), (length, width), (length, -width), (0.0, -width), (0.0, width)] self.style.line_arrow1 = Arrow(path, 1) elif object.style['start cap'] == 'triangular': if object.width > 0: width = object.style['triangle cap width'] / 16.0 length = object.style['triangle cap length'] / 16.0 else: width = 0.0 length = 0.0 # Draw arrow path = [(0.0, width), (length, 0.0), (0.0, -width), (0.0, width)] self.style.line_arrow1 = Arrow(path, 1) if (object.width / scale) < 1.0: self.style.line_arrow1.path.Transform( Scale(object.width / scale, object.width / scale)) if object.style['end cap'] == 'butt': self.style.line_cap = const.CapButt elif object.style['end cap'] == 'round': if object.width > 0: width = 0.5 length = 0.5 else: width = 0.0 length = 0.0 # Draw arrow path = [(0.0, width), (0.5 * length, width, length, 0.5 * width, length, 0.0), (length, -0.5 * width, 0.5 * length, -width, 0.0, -width), (0.0, width)] self.style.line_arrow2 = Arrow(path, 1) elif object.style['end cap'] == 'square': if object.width > 0: width = 0.5 length = 0.5 else: width = 0.0 length = 0.0 # Draw arrow path = [(0.0, width), (length, width), (length, -width), (0.0, -width), (0.0, width)] self.style.line_arrow2 = Arrow(path, 1) elif object.style['end cap'] == 'triangular': if object.width > 0: width = object.style['triangle cap width'] / 16.0 length = object.style['triangle cap length'] / 16.0 else: width = 0.0 length = 0.0 # Draw arrow path = [(0.0, width), (length, 0.0), (0.0, -width), (0.0, width)] self.style.line_arrow2 = Arrow(path, 1) if (object.width / scale) < 1.0: self.style.line_arrow2.path.Transform( Scale(object.width / scale, object.width / scale)) # Outline colour if object.outline == [255, 255, 255, 255]: self.style.line_pattern = EmptyPattern else: self.style.line_pattern = SolidPattern( CreateRGBColor( float(object.outline[1]) / 255.0, float(object.outline[2]) / 255.0, float(object.outline[3]) / 255.0)) # Fill colour if object.fill == [255, 255, 255, 255]: self.style.fill_pattern = EmptyPattern else: self.style.fill_pattern = SolidPattern( CreateRGBColor( float(object.fill[1]) / 255.0, float(object.fill[2]) / 255.0, float(object.fill[3]) / 255.0)) # Dash pattern if object.style['dash pattern'] == 'present': line_dashes = [] for n in object.pattern: line_dashes.append(int(n / scale)) self.style.line_dashes = tuple(line_dashes) # Create a list of path objects in the document paths = [] path = None # Examine the path elements for element in object.path: if element[0] == 'move': x, y = self.relative(element[1][0], element[1][1]) # Add any previous path to the list if path != None: # path.load_close() paths.append(path) path = CreatePath() path.AppendLine(x, y) elif element[0] == 'draw': x, y = self.relative(element[1][0], element[1][1]) path.AppendLine(x, y) elif element[0] == 'bezier': x1, y1 = self.relative(element[1][0], element[1][1]) x2, y2 = self.relative(element[2][0], element[2][1]) x, y = self.relative(element[3][0], element[3][1]) path.AppendBezier(x1, y1, x2, y2, x, y) elif element[0] == 'close': path.ClosePath() elif element[0] == 'end': # Should be the last object in the path # path.load_close() paths.append(path) break # Create a bezier object if paths != []: self.bezier(tuple(paths)) elif isinstance(object, drawfile.font_table): # Font table n_objects = n_objects + 1 # Set object level instance self.font_table = object.font_table elif isinstance(object, drawfile.text): # Text object n_objects = n_objects + 1 # Determine the font if self.font_table.has_key(object.style): self.style.font = RISCOSFont(self.font_table[object.style]) else: self.style.font = GetFont('Times Roman') # The size self.style.font_size = object.size[0] / scale # Outline colour if object.background == [255, 255, 255, 255]: self.style.line_pattern = EmptyPattern else: self.style.line_pattern = SolidPattern( CreateRGBColor( float(object.background[1]) / 255.0, float(object.background[2]) / 255.0, float(object.background[3]) / 255.0)) # Fill colour if object.foreground == [255, 255, 255, 255]: self.style.fill_pattern = EmptyPattern else: self.style.fill_pattern = SolidPattern( CreateRGBColor( float(object.foreground[1]) / 255.0, float(object.foreground[2]) / 255.0, float(object.foreground[3]) / 255.0)) # Transformation if hasattr(object, 'transform'): x, y = object.transform[4] / scale, object.transform[ 5] / scale ox, oy = self.relative(object.baseline[0], object.baseline[1]) transform = Trafo(object.transform[0] / 65536.0, object.transform[1] / 65536.0, object.transform[2] / 65536.0, object.transform[3] / 65536.0, ox + x, oy + y) else: transform = Translation( self.relative(object.baseline[0], object.baseline[1])) # Write the text self.simple_text(object.text, transform) elif isinstance(object, drawfile.jpeg): # JPEG object n_objects = n_objects + 1 # Transformation matrix x, y = self.relative(object.transform[4], object.transform[5]) # Scale the object using the dpi information available, noting # that unlike Draw which uses 90 dpi, Sketch uses 72 dpi. # (I assume this since 90 dpi Drawfile JPEG objects appear 1.25 # times larger in Sketch if no scaling is performed here.) scale_x = (object.transform[0] / 65536.0) * (72.0 / object.dpi_x) scale_y = (object.transform[3] / 65536.0) * (72.0 / object.dpi_y) transform = Trafo(scale_x, object.transform[1] / 65536.0, object.transform[2] / 65536.0, scale_y, x, y) # Decode the JPEG image image = Image.open(StringIO.StringIO(object.image)) # # Read dimensions of images in pixels # width, height = image.size # # # Divide these by the dpi values to obtain the size of the # # image in inches # width, height = width/float(object.dpi_x), \ # height/float(object.dpi_y) # image.load() self.image(image, transform) elif isinstance(object, drawfile.sprite): # Sprite object n_objects = n_objects + 1 # Transformation matrix if hasattr(object, 'transform'): x, y = self.relative(object.transform[4], object.transform[5]) # Multiply the scale factor by that in the transformation matrix scale_x = (object.transform[0]/65536.0) * \ (72.0 / object.sprite['dpi x']) scale_y = (object.transform[3]/65536.0) * \ (72.0 / object.sprite['dpi y']) transform = Trafo( scale_x, (object.transform[1]/65536.0) * \ (72.0 / object.sprite['dpi y']), (object.transform[2]/65536.0) * \ (72.0 / object.sprite['dpi x']), scale_y, x, y ) else: x, y = self.relative(object.x1, object.y1) # Draw scales the Sprite to fit in the object's # bounding box. To do the same, we need to know the # actual size of the Sprite # In points: # size_x = 72.0 * float(object.sprite['width']) / \ # object.sprite['dpi x'] # size_y = 72.0 * float(object.sprite['height']) / \ # object.sprite['dpi y'] # # # Bounding box dimensions in points: # bbox_width = (object.x2 - object.x1)/scale # bbox_height = (object.y2 - object.y1)/scale # # # Scale factors # scale_x = (bbox_width / size_x) * \ # (72.0 / object.sprite['dpi x']) # scale_y = (bbox_height / size_y) * \ # (72.0 / object.sprite['dpi y']) scale_x = (object.x2 - object.x1) / \ (scale * object.sprite['width']) scale_y = (object.y2 - object.y1) / \ (scale * object.sprite['height']) transform = Trafo(scale_x, 0.0, 0.0, scale_y, x, y) # Create an Image object image = Image.fromstring( object.sprite['mode'], (object.sprite['width'], object.sprite['height']), object.sprite['image']) self.image(image, transform) elif isinstance(object, drawfile.options): # Options object n_objects = n_objects + 1 # Read page size paper_size = object.options['paper size'] orientation = object.options['paper limits'] if paper_size in papersizes: if orientation == 'landscape': self.page_layout = pagelayout.PageLayout( object.options['paper size'], orientation=pagelayout.Landscape) else: self.page_layout = pagelayout.PageLayout( object.options['paper size'], orientation=pagelayout.Portrait) if object.options['grid locking'] == 'on': spacing = object.options['grid spacing'] if object.options['grid units'] == 'in': spacing = spacing * 72.0 else: spacing = spacing * 72.0 / 2.54 if object.options['grid shown'] == 'on': visible = 1 else: visible = 0 # self.begin_layer_class( GridLayer, # ( # (0, 0, int(spacing), int(spacing)), # visible, # CreateRGBColor(0.0, 0.0, 0.0), # _("Grid") # ) ) # self.end_composite() elif isinstance(object, drawfile.text_area): # Text area n_objects = n_objects + 1 # The text area object contains a number of columns. self.columns = len(object.columns) # Start in the first column and move to subsequent # columns as required, unless the number is overidden # by details in the text area. self.column = 0 # The cursor position is initially undefined. cursor = [None, None] # The column margins self.margin_offsets = [1.0, 1.0] self.margins = [ (object.columns[self.column].x1 / scale) + \ self.margin_offsets[0], (object.columns[self.column].x2 / scale) - \ self.margin_offsets[1] ] # The column base self.column_base = object.columns[self.column].y1 / scale # Line and paragraph spacing self.linespacing = 0.0 paragraph = 10.0 # Current font name and dimensions font_name = '' font_size = 0.0 font_width = 0.0 # Text colours background = (255, 255, 255) foreground = (0, 0, 0) # Build lines (lists of words) until the column width # is reached then write the line to the page. line = [] width = 0.0 # Current text alignment align = 'L' # Last command to be executed last_command = '' # Execute the commands in the text area: for command, args in object.commands: if command == '!': # Version number # print 'Version number', args pass elif command == 'A': # print 'Align:', args # Write current line self.ta_write_line(align, cursor, line, 0) # Empty the line list line = [] # Set the line width width = 0.0 # Align text align = args # Start new line cursor = self.ta_new_line(cursor, object, self.linespacing) elif command == 'B': # print 'Background:', args # Background colour background = args elif command == 'C': # print 'Foreground:', args # Foreground colour foreground = args elif command == 'D': # print 'Columns:', args # Number of columns if self.column == 0 and cursor == [None, None]: # Nothing rendered yet, so change number of columns self.columns = args elif command == 'F': # print 'Define font:', args # Define font (already defined in object.font_table) pass elif command == 'L': # print 'Line spacing:', args # Set line spacing self.linespacing = args elif command == 'M': # print 'Margins:', args # Change margins self.margin_offsets = [args[0], args[1]] self.margins = [ (object.columns[self.column].x1 / scale) + args[0], (object.columns[self.column].x2 / scale) - args[1] ] elif command == 'P': # print 'Paragraph spacing:', args # Change paragraph spacing paragraph = args elif command == 'U': # print 'Underlining' # Underlining pass elif command == 'V': # print 'Vertical displacement' # Vertical displacement pass elif command == '-': # print 'Hyphen' # Hyphen pass elif command == 'newl': # print 'New line' # New line # Write current line self.ta_write_line(align, cursor, line, 0) # Start new line cursor = self.ta_new_line(cursor, object, self.linespacing) # Can't position cursor? if cursor == [None, None]: break # Empty the line list line = [] # Set the line width width = 0.0 elif command == 'para': # print 'New paragraph' # New paragraph # Write current line self.ta_write_line(align, cursor, line, 0) # Start new line if last_command != 'newl': cursor = self.ta_new_line( cursor, object, paragraph + self.linespacing) else: cursor = self.ta_new_line(cursor, object, paragraph) # Can't position cursor? if cursor == [None, None]: break # Empty the line list line = [] # Set the line width width = 0.0 elif command == ';': # print 'Comment:', args # Comment pass elif command == 'font': # print 'Use font:', args # Font change font_name, \ font_size, \ font_width = object.font_table[args] # Select font use_font = RISCOSFont(font_name) # Move cursor to start of a line if the cursor is # undefined if cursor == [None, None]: cursor[0] = self.margins[0] cursor[1] = (object.columns[self.column].y2 / scale) - font_size # Set line spacing self.linespacing = font_size elif command == 'text': # print args # Text. Add it to the line, checking that the line # remains within the margins. text, space = self.make_safe(args[0]), args[1] # Add the width of the text to the current total width width = width + \ use_font.TextCoordBox(text, font_size)[2] # print width, margins[1] - margins[0] # Compare current total width with column width while width > (self.margins[1] - self.margins[0]): # First write any text on this line if line != []: # Width will exceed column width # print 'Width will exceed column width' # Write current line self.ta_write_line(align, cursor, line, 1) # Start new line cursor = self.ta_new_line( cursor, object, self.linespacing) # Can't position cursor? if cursor == [None, None]: break # Clear the list line = [] # Reset the width width = 0.0 # Now attempt to fit this word on the next line width = use_font.TextCoordBox(text, font_size)[2] br = len(text) # Continue to try until the word fits, or none of it fits while width > (self.margins[1] - self.margins[0]) \ and br > 0: # Keep checking the size of the word width = use_font.TextCoordBox( text[:br], font_size)[2] br = br - 1 if br == 0: # Word couldn't fit in the column at all, so # break out of this loop break elif br < len(text): # Write the subword to the line self.ta_write_line( align, cursor, [(text[:br], font_name, font_size, font_width, self.ta_set_colour(foreground), self.ta_set_colour(background))], 0) # Start new line cursor = self.ta_new_line( cursor, object, self.linespacing) # Can't position cursor? if cursor == [None, None]: break # keep the remaining text text = text[br:] # The width is just the width of this text width = use_font.TextCoordBox(text, font_size)[2] # If the whole string fit onto the line then # control will flow to the else clause which will # append the text to the line list for next time. else: # The text fits within the margins so add the text # to the line line.append( (text, font_name, font_size, font_width, self.ta_set_colour(foreground), self.ta_set_colour(background))) # Also append any trailing space if space != '': line.append( (space, font_name, font_size, font_width, self.ta_set_colour(foreground), self.ta_set_colour(background))) width = width + \ use_font.TextCoordBox( space, font_size)[2] # Can't position cursor? if cursor == [None, None]: break # Remember this command last_command = command # Render any remaining text if line != [] and cursor != [None, None]: # Write current line self.ta_write_line(align, cursor, line, 0) else: pass # Return the number of recognised objects return n_objects
def getStructBoxnode(inlabel, plist, vlist): global courfont global tarrw1, tarrw2, tarrw3 pvfnsz = 12 # font size for pointers/vars # here we'll also generate a dict of all graphic objects # global 'id' is not needed - it will be a part of a tuple, along with master group sbdict = {} ## pointers - horizontal yoff = 0 ypad = 2 lastcharpad = pvfnsz # so the arrow dont overlap with last char xoutermost = 0 # to get the longest arrow (so we use it to make arrows same length) point_tflist = [] # list of actual textfield objects tcount = 0 for iptxt in plist: textfld = SimpleText(Translation(0, yoff), iptxt) textfld.SetProperties(font=courfont, font_size=pvfnsz) textfld.update_rects() sbdict['ptf_' + str(tcount)] = textfld # add connector line tfbr = textfld.bounding_rect tfline = getQuickLine((tfbr.left, tfbr.bottom), (tfbr.right + lastcharpad, tfbr.bottom)) tfline.SetProperties(line_width=2.0, line_arrow1=tarrw1, line_arrow2=tarrw2) tfline.update_rects() sbdict['ptfl_' + str(tcount)] = tfline if tfbr.right + lastcharpad > xoutermost: xoutermost = tfbr.right + lastcharpad # group line and text tgrp = Group([tfline, textfld]) tgrp.update_rects() sbdict['ptfg_' + str(tcount)] = tgrp # add the group - not just textfld - to list here point_tflist.append(tgrp) # get height - calc next offset; yoff will go negative, but nvm (will group and move) # don't use tgrp's BB, its too high llx, lly, urx, ury = textfld.bounding_rect # /tgrp. /getTfBB(textfld) tfHeight = ury - lly yoff -= tfHeight + ypad tcount += 1 # done - now resize all the arrows according to xoutermost, # so they are same length # SetLine(i, x, y[, cont]) "Replace the ith segment by a line segment" # where the i-th segment is the node.. for tgrp in point_tflist: # loop (in a list) tfline = tgrp.objects[ 0] # access Group (sub)object - tfline is first [0] # get 2nd node (=segment 1) - in there, Point() is third in list ([2]) tmpep = tfline.paths[0].Segment(1)[2] # change 2nd node (=segment 1) of the (first) path (paths[0]) tfline.paths[0].SetLine(1, (xoutermost, tmpep.y)) tfline.update_rects() tgrp.update_rects() # finally, group all these point_group = Group(point_tflist) # accepts list directly point_group.update_rects() sbdict['ptg'] = point_group ## variables - vertical (so, rotate group afterwards) yoff = 0 ypad = 2 lastcharpad = 1 # pvfnsz /so the arrow dont overlap with last char xoutermost = 0 # to get the longest arrow (so we use it to make arrows same length) varbl_tflist = [] # list of actual textfield objects tcount = 0 havevars = len(vlist) # could be zero! for ivtxt in vlist: textfld = SimpleText(Translation(0, yoff), ivtxt) textfld.SetProperties(font=courfont, font_size=pvfnsz) textfld.update_rects() sbdict['vtf_' + str(tcount)] = textfld # add connector line tfbr = textfld.bounding_rect tfline = getQuickLine((tfbr.left, tfbr.bottom), (tfbr.right + lastcharpad, tfbr.bottom)) tfline.SetProperties(line_width=2.0, line_arrow1=tarrw1, line_arrow2=tarrw3) tfline.update_rects() sbdict['vtfl_' + str(tcount)] = tfline if tfbr.right + lastcharpad > xoutermost: xoutermost = tfbr.right + lastcharpad # group line and text tgrp = Group([tfline, textfld]) tgrp.update_rects() sbdict['vtfg_' + str(tcount)] = tgrp # add the group - not just textfld - to list here varbl_tflist.append(tgrp) # get height - calc next offset; yoff will go negative, but nvm (will group and move) llx, lly, urx, ury = textfld.bounding_rect # getTfBB(textfld) tfHeight = ury - lly yoff -= tfHeight + 2 tcount += 1 # done - now resize all the arrows according to xoutermost, # so they are same length # SetLine(i, x, y[, cont]) "Replace the ith segment by a line segment" # where the i-th segment is the node.. for tgrp in varbl_tflist: # loop (in a list) tfline = tgrp.objects[ 0] # access Group (sub)object - tfline is first [0] # get 2nd node (=segment 1) - in there, Point() is third in list ([2]) tmpep = tfline.paths[0].Segment(1)[2] # change 2nd node (=segment 1) of the (first) path (paths[0]) tfline.paths[0].SetLine(1, (xoutermost, tmpep.y)) tfline.update_rects() tgrp.update_rects() # finally, group all these varbl_group = Group(varbl_tflist) # accepts list directly varbl_group.update_rects() sbdict['vtg'] = varbl_group # rotate variable group # for repositioning - easiest to rotate around # upper-left corner (instead of center point) vgbr = varbl_group.bounding_rect varbl_g_cp = vgbr.center() # centerpoint varbl_g_ul = Point(vgbr.left, vgbr.top) # top (upper) left varbl_group.Transform(Rotation(radians(-90), varbl_g_ul)) varbl_group.update_rects() # must reassign variable for .bounding_rect # after transform and update_rects()!!: vgbr = varbl_group.bounding_rect vgbr_w = vgbr.right - vgbr.left vgbr_h = vgbr.top - vgbr.bottom # also, below needed for box & move calc: #~ point_group.update_rects() pgbr = point_group.bounding_rect pgbr_w = pgbr.right - pgbr.left pgbr_h = pgbr.top - pgbr.bottom # and note - groups seem to add some sort of padding, so align to intern lines instead (instead of group BB edges) # get first (moveto) Point of first tf's line (align to that, instead of pgbr) - needed for 'boxing' below: tfline = point_group[0].objects[0] tmpep = tfline.paths[0].Segment(0)[2] tmpep_v = Point(0, 0) if havevars != 0: tmpep_v = varbl_group[0].objects[0].paths[0].Segment(0)[2] xmoveadj = tmpep.x - pgbr.left # adjustment due to group padding ymoveadj = -(tmpep_v.y - vgbr.top) # only for moving varbl_group # move rotated varbl_group below left of point_group xmove = 0 # -vgbr_w # looks ok like this xmove += xmoveadj # adjust due to group padding ymove = -pgbr_h ymove += ymoveadj # adjust due to group padding varbl_group.Translate(Point(xmove, ymove)) varbl_group.update_rects() vgbr = varbl_group.bounding_rect ## add the box label lxpad = 10 # box label x padding tfld_label = SimpleText(Translation(0, 0), inlabel) tfld_label.SetProperties(font=courfont, font_size=16) tfld_label.update_rects() # reposition # on y: center align tfld_label with center of point_group cp_pg = pgbr.center() tlbr = tfld_label.bounding_rect cp_tl = tlbr.center() ymove = -(cp_tl.y - cp_pg.y) # on x: <NO>align right edge of tfld_label + pad with left edge of point_group</NO> # center in respect to expected boxwidth instead # calc expected boxwidth first tlbr = tfld_label.bounding_rect tlbr_w = tlbr.right - tlbr.left boxwidth = lxpad + tlbr_w + lxpad # only title text label width + padding varbl_width = vgbr.right - vgbr.left if boxwidth < lxpad + varbl_width + lxpad: boxwidth = lxpad + varbl_width + lxpad #~ xmove = -(tlbr.right + lxpad - pgbr.left) # title text left aligned w/ right edge xmove = -((tlbr.center().x - pgbr.left) + boxwidth / 2) xmove += xmoveadj # adjust due to group padding tfld_label.Translate(Point(xmove, ymove)) tfld_label.update_rects() tlbr = tfld_label.bounding_rect # must reassign variable (though we won't need it anymore) ## create a box for point/varbl_group # start at upper left point of point_group BB; # go downleft, to <NO>upper left point of varbl_group BB</NO> # .. note - groups seem to add some sort of padding, so align to intern lines: # go downleft, to upper edge of varbl_group, and ([left edge of tfld_label] - pad) # get first (moveto) Point of first tf's line (align to that, instead of pgbr) tfline = point_group[0].objects[0] tmpep = tfline.paths[0].Segment(0)[2] start_x = tmpep.x # pgbr.left start_y = pgbr.top off_x = -(boxwidth) # -vgbr_w off_y = -pgbr_h trec = Rectangle(trafo=Trafo(off_x, 0, 0, off_y, start_x, start_y)) trec.SetProperties( line_width=2.0, fill_pattern=SolidPattern(CreateRGBColor(0.7, 0.7, 0.9)), ) trec.update_rects() sbdict['box'] = trec # now group all these # grouping goes: 1st on bottom, 2nd on top of it, etc.. retgrp = Group([trec, tfld_label, point_group, varbl_group]) retgrp.update_rects() # move lower left corner of retgrp to 0,0 rgbr = retgrp.bounding_rect retgrp.Translate(Point(-rgbr.left, -rgbr.bottom)) retgrp.update_rects() return (retgrp, sbdict)