class Arrow: def __init__(self, path, closed = 0): self.path = CreatePath() self.head = Point(0,0) if type(path) in (ListType, TupleType): oldseg = None for segment in path: if len(segment) == 2: if oldseg and oldseg[-2:] == segment: self.head = Point(segment) apply(self.path.AppendLine, segment) else: apply(self.path.AppendBezier, segment) oldseg = segment else: self.path = path if closed: self.path.load_close() def BoundingRect(self, pos, dir, width): try: angle = atan2(dir.y, dir.x) except ValueError: angle = 0 if width < 1.0: width = 1.0 s = width * sin(angle) c = width * cos(angle) trafo = Trafo(c, s, -s, c, pos.x, pos.y) return self.path.accurate_rect(trafo) def Draw(self, device, rect = None): if self.path.closed: device.FillBezierPath(self.path, rect) else: device.DrawBezierPath(self.path, rect) def Paths(self): return (self.path,) def IsFilled(self): return self.path.closed def Head(self): return self.head def SaveRepr(self): path = map(lambda t: t[:-1], self.path.get_save()) return (path, self.path.closed) def __hash__(self): return hash(id(self.path)) def __cmp__(self, other): if __debug__: pdebug(None, 'Arrow.__cmp__, %s', other) if isinstance(other, self.__class__): return cmp(self.path, other.path) return cmp(id(self), id(other))
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 split_path_at(path, at): index = int(at) t = at - index if path.closed: path1 = path2 = CreatePath() result = [path1] else: path1 = CreatePath() path2 = CreatePath() result = [path1, path2] copy_path(path1, path, 0, 0, copy_selection = 0) type, control, node, cont = path.Segment(index + 1) if type == Line: q = (1 - t) * path.Node(index) + t * node path2.AppendLine(q) path2.AppendLine(node) path2.select_segment(0) function = path1.AppendLine args = (q,) else: p1, p2 = control p1, p2, q, p3, p4 = subdivide(path.Node(index), p1, p2, node, t) path2.AppendLine(q) path2.AppendBezier(p3, p4, node, cont) path2.select_segment(0) function = path1.AppendBezier args = (p1, p2, q, ContSymmetrical) copy_path(path2, path, index + 2, copy_selection = 0) copy_path(path1, path, 1, index, copy_selection = 0) apply(function, args) return result
def read_polyline(self, line): readline = self.readline tokenize = skread.tokenize_line args = tokenize(line) if len(args) != 15: raise SketchLoadError("Invalid PolyLine specification") sub_type, line_style, thickness, pen_color, fill_color, depth, pen_style, area_fill, style, join, cap, radius, forward_arrow, backward_arrow, npoints = ( args ) self.fill(fill_color, area_fill) self.line(pen_color, thickness, join, cap, line_style, style) if forward_arrow: readline() # XXX: implement this if backward_arrow: readline() # XXX: implement this if sub_type == 5: readline() # imported picture path = CreatePath() ncoords = npoints * 2 pts = self.read_tokens(ncoords) if not pts: raise SketchLoadError("Missing points for polyline") if len(pts) > ncoords: del pts[ncoords:] map(path.AppendLine, coords_to_points(pts, self.trafo)) if sub_type in (2, 3, 4): path.load_close(1) self.bezier(paths=path) self.set_depth(depth)
def read_polyline(self, line): readline = self.readline tokenize = skread.tokenize_line args = tokenize(line) if len(args) != 15: raise SketchLoadError('Invalid PolyLine specification') sub_type, line_style, thickness, pen_color, fill_color, depth, \ pen_style, area_fill, style, join, cap, \ radius, forward_arrow, backward_arrow, npoints = args self.fill(fill_color, area_fill) self.line(pen_color, thickness, join, cap, line_style, style) if forward_arrow: readline() # XXX: implement this if backward_arrow: readline() # XXX: implement this if sub_type == 5: readline() # imported picture path = CreatePath() ncoords = npoints * 2 pts = self.read_tokens(ncoords) if not pts: raise SketchLoadError('Missing points for polyline') if len(pts) > ncoords: del pts[ncoords:] map(path.AppendLine, coords_to_points(pts, self.trafo)) if sub_type in (2, 3, 4): path.load_close(1) self.bezier(paths=path) self.set_depth(depth)
def insert_node_at(path, at): index = int(at) t = at - index newpath = CreatePath() copy_path(newpath, path, 0, index) type, control, node, cont = path.Segment(index + 1) if type == Line: newpath.AppendLine((1 - t) * path.Node(index) + t * node) newpath.select_segment(-1) newpath.AppendLine(node) else: if newpath.Continuity(-1) == ContSymmetrical: newpath.SetContinuity(-1, ContSmooth) p1, p2 = control p1, p2, q, p3, p4 = subdivide(newpath.Node(-1), p1, p2, node, t) newpath.AppendBezier(p1, p2, q, ContSmooth) newpath.select_segment(-1) if cont == ContSymmetrical: cont = ContSmooth newpath.AppendBezier(p3, p4, node, cont) copy_path(newpath, path, index + 2) if path.closed: newpath.ClosePath() newpath.SetContinuity(-1, path.Continuity(-1)) return newpath
def bezier(self): if self.guess_continuity: self.path.guess_continuity() if self.path.len > 0: if self.compound_path is not None: self.compound_path.append(self.path) else: GenericLoader.bezier(self, paths=(self.path, )) self.path = CreatePath()
def DISJTLINE(self, size): path = () for i in range(size / (4 * reff.vdc.size)): subpath = CreatePath() P = self.Pnt() subpath.AppendLine(self.trafo(P)) P = self.Pnt() subpath.AppendLine(self.trafo(P)) path = path + (subpath, ) self.setlinestyle() self.bezier(path)
def Polygon(self): points = self.read_points(self.get_int16()) if points: path = CreatePath() map(path.AppendLine, points) if path.Node(-1) != path.Node(0): #print 'correct polygon' path.AppendLine(path.Node(0)) path.load_close() self.prop_stack.AddStyle(self.curstyle.Duplicate()) self.bezier((path,))
def LineTo(self): y, x = self.get_struct('<hh') p = self.trafo(x, y) self.prop_stack.AddStyle(self.curstyle.Duplicate()) self.prop_stack.SetProperty(fill_pattern=EmptyPattern) path = CreatePath() path.AppendLine(self.curpoint) path.AppendLine(p) self.bezier((path, )) self.curpoint = p self._print('->', self.curpoint)
class Arrow: def __init__(self, path, closed = 0): self.path = CreatePath() if type(path) in (ListType, TupleType): for segment in path: if len(segment) == 2: apply(self.path.AppendLine, segment) else: apply(self.path.AppendBezier, segment) else: self.path = path if closed: self.path.load_close() def BoundingRect(self, pos, dir, width): try: angle = atan2(dir.y, dir.x) except ValueError: angle = 0 if width < 1.0: width = 1.0 s = width * sin(angle) c = width * cos(angle) trafo = Trafo(c, s, -s, c, pos.x, pos.y) return self.path.accurate_rect(trafo) def Draw(self, device, rect = None): if self.path.closed: device.FillBezierPath(self.path, rect) else: device.DrawBezierPath(self.path, rect) def Paths(self): return (self.path,) def IsFilled(self): return self.path.closed def SaveRepr(self): path = map(lambda t: t[:-1], self.path.get_save()) return (path, self.path.closed) def __hash__(self): return hash(id(self.path)) def __cmp__(self, other): if __debug__: pdebug(None, 'Arrow.__cmp__, %s', other) if isinstance(other, self.__class__): return cmp(self.path, other.path) return cmp(id(self), id(other))
def recompute(self): path = CreatePath() vertices = self.vertices radius = self.radius twopi = 2 * pi halfpi = pi / 2 for i in range(vertices + 1): path.AppendLine(Polar(radius, (twopi * i) / vertices + halfpi), ContAngle) path.ClosePath() path.Transform(self.trafo) if self.objects: self.objects[0].SetPaths((path,)) else: self.set_objects([PolyBezier((path,))])
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 Polyline(self): points = self.read_points(self.get_int16()) if points: path = CreatePath() map(path.AppendLine, points) self.prop_stack.AddStyle(self.curstyle.Duplicate()) self.prop_stack.SetProperty(fill_pattern=EmptyPattern) self.bezier((path, ))
def __init__(self, file, filename, match, treat_toplevel_groups_as_layers=1, flatten_groups=1): GenericLoader.__init__(self, file, filename, match) self.line_color = StandardColors.black self.fill_color = StandardColors.black self.line_width = 0.0 self.line_join = const.JoinMiter self.line_cap = const.CapButt self.line_dashes = () self.cur_x = self.cur_y = 0.0 self.treat_toplevel_groups_as_layers = treat_toplevel_groups_as_layers self.flatten_groups = flatten_groups self.guess_continuity = 1 self.path = CreatePath() self.compound_path = None # If compound_path is None, we're # outside of a compound path, # otherwise it's a possibly empty list # of paths self.compound_render = '' self.stack = [] self.gradients = {} self.in_gradient_instance = 0 self.gradient_geo = None # set to a true value after Bg, and set # to false by make_gradient_pattern self.gradient_rect = None self.in_palette = 0 self.in_text = 0 self.ignore_fill = 0 self.text_type = 0 # 0: point text, 1: area text, 2 = path text self.text_render = 0 # filled self.text_font = None self.text_size = 12 # Test alignment. Possible values: 0: left, 1: center, 2:right, # 3: justified, 4: justified including last line self.text_align = 0 self.text_string = [] self.standard_encoding = encoding.adobe_standard self.font_map = {} self.guides = [] self.format_version = 0.0
def __init__(self, path, closed = 0): self.path = CreatePath() self.head = Point(0,0) if type(path) in (ListType, TupleType): oldseg = None for segment in path: if len(segment) == 2: if oldseg and oldseg[-2:] == segment: self.head = Point(segment) apply(self.path.AppendLine, segment) else: apply(self.path.AppendBezier, segment) oldseg = segment else: self.path = path if closed: self.path.load_close()
def polyline(self, attrs): if self.in_defs: id = attrs.get('id', '') if id: self.named_objects[id] = ('object', 'polyline', attrs) return points = as_latin1(attrs['points']) points = string.translate(points, commatospace) points = split(points) path = CreatePath() point = self.point for i in range(0, len(points), 2): path.AppendLine(point(points[i], points[i + 1])) style = attrs.get('style', '') if style: self.parse_style(style) self.set_loader_style() self.loader.bezier(paths=(path, ))
def PolyPolygon(self): nr_of_polygons = self.get_int16() nr_of_points = [] for i in range(nr_of_polygons): nr_of_points.append(self.get_int16()) path = () for i in nr_of_points: points = self.read_points(i) if points: subpath = CreatePath() map(subpath.AppendLine, points) if subpath.Node(-1) != subpath.Node(0): subpath.AppendLine(subpath.Node(0)) subpath.load_close() path = path + (subpath,) if path: self.prop_stack.AddStyle(self.curstyle.Duplicate()) self.bezier(path)
def bezier(self): if self.guess_continuity: self.path.guess_continuity() if self.path.len > 0: if self.compound_path is not None: self.compound_path.append(self.path) else: GenericLoader.bezier(self, paths=(self.path,)) self.path = CreatePath()
def convert_outline(outline): paths = [] trafo = Scale(0.001) for closed, sub in outline: if closed: sub.append(sub[0]) path = CreatePath() paths.append(path) for item in sub: if len(item) == 2: apply(path.AppendLine, item) else: apply(path.AppendBezier, item) if closed: path.load_close() for path in paths: path.Transform(trafo) return tuple(paths)
def Polygon(self): points = self.read_points(self.get_int16()) if points: path = CreatePath() map(path.AppendLine, points) if path.Node(-1) != path.Node(0): #print 'correct polygon' path.AppendLine(path.Node(0)) path.load_close() self.prop_stack.AddStyle(self.curstyle.Duplicate()) self.bezier((path, ))
def __init__(self, path, closed = 0): self.path = CreatePath() if type(path) in (ListType, TupleType): for segment in path: if len(segment) == 2: apply(self.path.AppendLine, segment) else: apply(self.path.AppendBezier, segment) else: self.path = path if closed: self.path.load_close()
def create_spiral_path(rotation, radius): r = unit.convert(radius) rate = r / (rotation * 2 * pi) def tangent(phi, a=0.55197 * rate): return a * Point(cos(phi) - phi * sin(phi), sin(phi) + phi * cos(phi)) pi2 = pi / 2.0 angle = 0 tang = tangent(0) path = CreatePath() p = Point(0, 0) path.AppendLine(p) for i in range(rotation * 4): p1 = p + tang angle = pi2 * (i + 1) p = Polar(rate * angle, angle) tang = tangent(angle) p2 = p - tang path.AppendBezier(p1, p2, p, ContSymmetrical) return path
def recompute(self): paths = [] trafo = self.trafo(Scale(self.size / float(char_scale))) width = 0 for char in self.text: segs = char_segs.get(char) if segs is not None: for seg in segs: path = CreatePath() map(path.AppendLine, segments[seg]) path.ClosePath() path.Translate(width, 0) path.Transform(trafo) paths.append(path) width = width + char_width paths = tuple(paths) if self.objects: self.objects[0].SetPaths(paths) else: self.set_objects([PolyBezier(paths)])
def create_star_path(corners, outer_radius, inner_radius): outer_radius = unit.convert(outer_radius) inner_radius = unit.convert(inner_radius) path = CreatePath() angle = math.pi * 2 / corners for i in range(corners): path.AppendLine(Polar(outer_radius, angle * i)) path.AppendLine(Polar(inner_radius, angle * i + angle / 2)) path.AppendLine(path.Node(0)) path.ClosePath() return path
def PolyPolygon(self): nr_of_polygons = self.get_int16() nr_of_points = [] for i in range(nr_of_polygons): nr_of_points.append(self.get_int16()) path = () for i in nr_of_points: points = self.read_points(i) if points: subpath = CreatePath() map(subpath.AppendLine, points) if subpath.Node(-1) != subpath.Node(0): subpath.AppendLine(subpath.Node(0)) subpath.load_close() path = path + (subpath, ) if path: self.prop_stack.AddStyle(self.curstyle.Duplicate()) self.bezier(path)
def bezier_load(self, line): bezier = self.object while 1: try: bezier.paths[-1].append_from_string(line) line = bezier.paths[-1].append_from_file(self.file) except: warn(INTERNAL, _("Error reading line %s"), ` line `) line = self.file.readline() if line[:2] == 'bC': bezier.paths[-1].load_close() line = self.file.readline() if line[:2] == 'bn': bezier.paths = bezier.paths + (CreatePath(), ) line = self.file.readline() else: break if line[:2] not in ('bs', 'bc'): break return line
def __init__(self, file, filename, match, treat_toplevel_groups_as_layers=1, flatten_groups=1): GenericLoader.__init__(self, file, filename, match) self.line_color = StandardColors.black self.fill_color = StandardColors.black self.line_width = 0.0 self.line_join = const.JoinMiter self.line_cap = const.CapButt self.line_dashes = () self.cur_x = self.cur_y = 0.0 self.treat_toplevel_groups_as_layers = treat_toplevel_groups_as_layers self.flatten_groups = flatten_groups self.guess_continuity = 1 self.path = CreatePath() self.compound_path = None # If compound_path is None, we're # outside of a compound path, # otherwise it's a possibly empty list # of paths self.compound_render = "" self.stack = [] self.gradients = {} self.in_gradient_instance = 0 self.gradient_geo = None # set to a true value after Bg, and set # to false by make_gradient_pattern self.gradient_rect = None self.in_palette = 0 self.in_text = 0 self.ignore_fill = 0 self.text_type = 0 # 0: point text, 1: area text, 2 = path text self.text_render = 0 # filled self.text_font = None self.text_size = 12 # Test alignment. Possible values: 0: left, 1: center, 2:right, # 3: justified, 4: justified including last line self.text_align = 0 self.text_string = [] self.standard_encoding = encoding.adobe_standard self.font_map = {} self.guides = [] self.format_version = 0.0
def POLYGONSET(self, size): path = () subpath = CreatePath() for i in range(size / (2 * reff.vdc.size + 2)): P = self.Pnt() F = self.Enum() subpath.AppendLine(self.trafo(P)) if F in (2,3): if subpath.Node(-1) != subpath.Node(0): subpath.AppendLine(subpath.Node(0)) subpath.load_close() path = path + (subpath,) subpath = CreatePath() if subpath.len != 0: if subpath.Node(-1) != subpath.Node(0): subpath.AppendLine(subpath.Node(0)) subpath.load_close() path = path + (subpath,) self.setfillstyle() self.bezier(path)
def read_spline(self, line): readline = self.readline tokenize = skread.tokenize_line args = tokenize(line) if len(args) != 13: raise SketchLoadError("Invalid Spline specification") sub_type, line_style, thickness, pen_color, fill_color, depth, pen_style, area_fill, style, cap, forward_arrow, backward_arrow, npoints = ( args ) closed = sub_type & 1 if forward_arrow: readline() if backward_arrow: readline() # in 3.2 all splines are stored as x-splines... if self.format_version == 3.2: if sub_type in (0, 2): sub_type = 4 else: sub_type = 5 self.fill(fill_color, area_fill) self.line(pen_color, thickness, 0, cap, line_style, style) ncoords = npoints * 2 pts = self.read_tokens(ncoords) if not pts: raise SketchLoadError("Missing points for spline") if len(pts) > ncoords: del pts[ncoords:] pts = coords_to_points(pts, self.trafo) path = CreatePath() if sub_type in (2, 3): # interpolated spline, read 2 control points for each node ncontrols = 4 * npoints controls = self.read_tokens(ncontrols) if not controls: raise SketchLoadError("Missing control points for spline") if len(controls) > ncontrols: del controls[ncontrols:] controls = coords_to_points(controls[2:-2], self.trafo) path.AppendLine(pts[0]) ncontrols = 2 * (npoints - 1) controls = [controls] * (npoints - 1) map( path.AppendBezier, map(getitem, controls, range(0, ncontrols, 2)), map(getitem, controls, range(1, ncontrols, 2)), pts[1:], ) elif sub_type in (0, 1): # approximated spline f13 = 1.0 / 3.0 f23 = 2.0 / 3.0 curve = path.AppendBezier straight = path.AppendLine last = pts[0] cur = pts[1] start = node = (last + cur) / 2 if closed: straight(node) else: straight(last) straight(node) last = cur for cur in pts[2:]: c1 = f13 * node + f23 * last node = (last + cur) / 2 c2 = f13 * node + f23 * last curve(c1, c2, node) last = cur if closed: curve(f13 * node + f23 * last, f13 * start + f23 * last, start) else: straight(last) elif sub_type in (4, 5): # An X-spline. Treat it like a polyline for now. # read and discard the control info self.read_tokens(npoints) self.add_message(_("X-Spline treated as PolyLine")) map(path.AppendLine, pts) if closed: path.AppendLine(path.Node(0)) if closed: path.load_close(1) self.bezier(paths=path) self.set_depth(depth)
def read_spline(self, line): readline = self.readline tokenize = skread.tokenize_line args = tokenize(line) if len(args) != 13: raise SketchLoadError('Invalid Spline specification') sub_type, line_style, thickness, pen_color, fill_color, depth, \ pen_style, area_fill, style, cap, \ forward_arrow, backward_arrow, npoints = args closed = sub_type & 1 if forward_arrow: readline() if backward_arrow: readline() # in 3.2 all splines are stored as x-splines... if self.format_version == 3.2: if sub_type in (0, 2): sub_type = 4 else: sub_type = 5 self.fill(fill_color, area_fill) self.line(pen_color, thickness, 0, cap, line_style, style) ncoords = npoints * 2 pts = self.read_tokens(ncoords) if not pts: raise SketchLoadError('Missing points for spline') if len(pts) > ncoords: del pts[ncoords:] pts = coords_to_points(pts, self.trafo) path = CreatePath() if sub_type in (2, 3): # interpolated spline, read 2 control points for each node ncontrols = 4 * npoints controls = self.read_tokens(ncontrols) if not controls: raise SketchLoadError('Missing control points for spline') if len(controls) > ncontrols: del controls[ncontrols:] controls = coords_to_points(controls[2:-2], self.trafo) path.AppendLine(pts[0]) ncontrols = 2 * (npoints - 1) controls = [controls] * (npoints - 1) map(path.AppendBezier, map(getitem, controls, range(0, ncontrols, 2)), map(getitem, controls, range(1, ncontrols, 2)), pts[1:]) elif sub_type in (0, 1): # approximated spline f13 = 1.0 / 3.0 f23 = 2.0 / 3.0 curve = path.AppendBezier straight = path.AppendLine last = pts[0] cur = pts[1] start = node = (last + cur) / 2 if closed: straight(node) else: straight(last) straight(node) last = cur for cur in pts[2:]: c1 = f13 * node + f23 * last node = (last + cur) / 2 c2 = f13 * node + f23 * last curve(c1, c2, node) last = cur if closed: curve(f13 * node + f23 * last, f13 * start + f23 * last, start) else: straight(last) elif sub_type in (4, 5): # An X-spline. Treat it like a polyline for now. # read and discard the control info self.read_tokens(npoints) self.add_message(_("X-Spline treated as PolyLine")) map(path.AppendLine, pts) if closed: path.AppendLine(path.Node(0)) if closed: path.load_close(1) self.bezier(paths=path) self.set_depth(depth)
def average_points(context): # find a bezier polygon selected selection = [] for object in context.document.SelectedObjects(): if not object.is_Bezier: continue selection.append(object) if len(selection) != 1: context.application.MessageBox(title="Average Points", message="Select one polygon.") return None # count selected points object = selection[0] object_paths = object.Paths() npoints = 0 for path in object_paths: for i in range(path.len): if path.SegmentSelected(i): npoints = npoints + 1 if npoints == 0: context.application.MessageBox(title="Average Points", message="Select two or more points.") return None # inquiry parameters which = AverageDialog(context.application.root).RunDialog() if which is None: return None # compute average coordinates of the selected points ax = 0 ay = 0 modified_paths = [] for path in object_paths: modified_paths.append([]) for i in range(path.len): type, controls, point, cont = path.Segment(i) modified_paths[-1].append([type, list(controls), point, cont]) if path.SegmentSelected(i): ax = ax + point.x ay = ay + point.y ax = float(ax) / npoints ay = float(ay) / npoints # translate the selected points for i in range(len(object_paths)): path = object_paths[i] new_path = modified_paths[i] for j in range(path.len): if path.SegmentSelected(j): point = new_path[j][2] if which == AVERAGE_X: new_point = Point(ax, point.y) elif which == AVERAGE_Y: new_point = Point(point.x, ay) else: new_point = Point(ax, ay) new_path[j][2] = new_point offset = point - new_point if len(new_path[j][1]) == 2: new_path[j][1][1] = new_path[j][1][1] - offset if j < path.len - 1 and len(new_path[j + 1][1]) == 2: new_path[j + 1][1][0] = new_path[j + 1][1][0] - offset # create new paths new_paths = [] for i in range(len(object_paths)): path = object_paths[i] new_path = CreatePath() for type, controls, point, cont in modified_paths[i]: new_path.AppendSegment(type, tuple(controls), point, cont) if path.closed: new_path.AppendLine(new_path.Node(0)) new_path.ClosePath() new_paths.append(new_path) # set the new paths undo = object.SetPaths(new_paths) # return Undo info return undo
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
class PolyBezierCreator(Creator): creation_text = _("Create Curve") def __init__(self, start): self.path = CreatePath() Creator.__init__(self, start) def apply_constraints(self, p, state): if self.path.len > 0: node = self.path.Node(-1) elif self.dragging: node = self.drag_start else: return p if state & ConstraintMask: radius, angle = (p - node).polar() pi12 = pi / 12 angle = pi12 * floor(angle / pi12 + 0.5) p = node + Polar(radius, angle) return p def ButtonDown(self, p, button, state): p = self.apply_constraints(p, state) if self.path.len == 0: self.path.AppendLine(p) else: self.path.AppendBezier(self.drag_cur, p, p) return self.DragStart(p) def MouseMove(self, p, state): if not (state & Button1Mask): return self.DragMove(self.apply_constraints(p, state)) def ButtonUp(self, p, button, state): if not (state & Button1Mask): return p = self.apply_constraints(p, state) self.DragStop(p) if self.path.len > 1: type, (p1, p2), p, cont = self.path.Segment(-1) p2 = adjust_control_point(p2, p, self.drag_cur, ContSymmetrical) self.path.SetBezier(-1, p1, p2, p, ContSymmetrical) def EndCreation(self): return self.path.len > 1 def AppendInteractive(self, p): return self def ContinueCreation(self): return self.AppendInteractive def DrawDragged(self, device, partially): if not partially: self.path.draw_not_last(device.Bezier, device.Line) device.DrawHandleLine(self.path.Node(-1), self.drag_cur) device.DrawSmallRectHandle(self.drag_cur) if self.path.len > 1: type, (p1, p2), p, cont = self.path.Segment(-1) p2 = adjust_control_point(p2, p, self.drag_cur, ContSymmetrical) device.Bezier(self.path.Node(-2), p1, p2, p) device.DrawHandleLine(p, p2) device.DrawSmallRectHandle(p2) def CreatedObject(self): return PolyBezier(paths = (self.path,), properties = DefaultGraphicsProperties())
def Path(self, size): path = CreatePath() for i in range(size / (2 * reff.vdc.size)): path.AppendLine(self.trafo(self.Pnt())) return path
def __init__(self, start): self.path = CreatePath() Creator.__init__(self, start)
class AILoader(GenericLoader): format_name = format_name functions = { "C": "curveto", "c": "curveto_smooth", "V": "curveto_v", "v": "curveto_v_smooth", "Y": "curveto_y", "y": "curveto_y_smooth", "m": "moveto", "l": "lineto", "L": "lineto", "w": "set_line_width", "j": "set_line_join", "J": "set_line_cap", "d": "set_line_dash", "G": "set_line_gray", "K": "set_line_cmyk", "XA": "set_line_rgb", "X": "set_line_cmyk_custom", "XX": "set_line_generic_custom", "P": "set_line_pattern", "g": "set_fill_gray", "k": "set_fill_cmyk", "Xa": "set_fill_rgb", "x": "set_fill_cmyk_custom", "Xx": "set_fill_generic_custom", "p": "set_fill_pattern", "F": "fill", "f": "fill_close", "S": "stroke", "s": "stroke_close", "B": "fill_stroke", "b": "fill_stroke_close", "N": "invisible", # an invisible open path "n": "invisible_close", # a invisible closed path "u": "begin_group", "U": "end_group", "*u": "begin_compound_path", "*U": "end_compound_path", "*": "guide", "[": "mark", "]": "make_array", "@": "ignore_operator", "&": "ignore_operator", "Bd": "begin_gradient", "Bs": "gradient_stop", "BS": "dummy_gradient_stop", "Br": "gradient_ramps", "BD": "end_gradient", "Bb": "begin_gradient_instance", "Bg": "gradient_geometry", "BB": "end_gradient_instance", "Lb": "begin_ai_layer", "Ln": "name_layer", "LB": "end_ai_layer", "Pb": "begin_palette", "PB": "end_palette", "TE": "set_standard_encoding", "TZ": "reencode_font", "To": "begin_text", "TO": "end_text", "Tr": "set_text_render", "Tf": "set_text_font", "Ta": "set_text_align", "Tp": "begin_text_path", "TP": "end_text_path", "Tx": "render_text", "TX": "render_text_inv", "XI": "raster_image", } def __init__(self, file, filename, match, treat_toplevel_groups_as_layers=1, flatten_groups=1): GenericLoader.__init__(self, file, filename, match) self.line_color = StandardColors.black self.fill_color = StandardColors.black self.line_width = 0.0 self.line_join = const.JoinMiter self.line_cap = const.CapButt self.line_dashes = () self.cur_x = self.cur_y = 0.0 self.treat_toplevel_groups_as_layers = treat_toplevel_groups_as_layers self.flatten_groups = flatten_groups self.guess_continuity = 1 self.path = CreatePath() self.compound_path = None # If compound_path is None, we're # outside of a compound path, # otherwise it's a possibly empty list # of paths self.compound_render = "" self.stack = [] self.gradients = {} self.in_gradient_instance = 0 self.gradient_geo = None # set to a true value after Bg, and set # to false by make_gradient_pattern self.gradient_rect = None self.in_palette = 0 self.in_text = 0 self.ignore_fill = 0 self.text_type = 0 # 0: point text, 1: area text, 2 = path text self.text_render = 0 # filled self.text_font = None self.text_size = 12 # Test alignment. Possible values: 0: left, 1: center, 2:right, # 3: justified, 4: justified including last line self.text_align = 0 self.text_string = [] self.standard_encoding = encoding.adobe_standard self.font_map = {} self.guides = [] self.format_version = 0.0 def __del__(self): pass def warn(self, level, *args, **kw): message = apply(warn, (level,) + args, kw) self.add_message(message) def get_compiled(self): funclist = {} for char, name in self.functions.items(): method = getattr(self, name) argc = method.im_func.func_code.co_argcount - 1 funclist[char] = (method, argc) return funclist def pop(self): value = self.stack[-1] del self.stack[-1] return value def pop_multi(self, num): value = self.stack[-num:] del self.stack[-num:] return value def pop_to_mark(self): s = self.stack[:] s.reverse() try: idx = s.index(None) if idx: array = self.stack[-idx:] del self.stack[-idx - 1 :] else: array = [] del self.stack[-1] return array except: raise RuntimeError, "No mark on stack" def ignore_operator(self): pass def mark(self): self.stack.append(None) def make_array(self): array = self.pop_to_mark() self.stack.append(array) def convert_color(self, color_spec): c = apply(CreateRGBColor, color_spec) return c def set_line_join(self, join): self.line_join = _ai_join[join] def set_line_cap(self, cap): self.line_cap = _ai_cap[cap] def set_line_width(self, w): self.line_width = w def set_line_dash(self, array, phase): self.line_dashes = tuple(array) def set_line_gray(self, k): self.line_color = CreateRGBColor(k, k, k) def set_line_cmyk(self, c, m, y, k): self.line_color = CreateCMYKColor(c, m, y, k) def set_line_rgb(self, r, g, b): self.line_color = CreateRGBColor(r, g, b) def set_line_cmyk_custom(self, c, m, y, k, name, tint): self.line_color = cmyk_custom_color(c, m, y, k, tint) def set_line_generic_custom(self, name, tint, type): if type == 0: # cmyk c, m, y, k = self.pop_multi(4) self.line_color = cmyk_custom_color(c, m, y, k, tint) else: # rgb r, g, b = self.pop_multi(3) self.line_color = rgb_custom_color(r, g, b, tint) def set_line_pattern(self, name, px, py, sx, sy, angle, rf, r, k, ka, matrix): if not self.in_palette: self.add_message(_("Vector patterns not supported. Using black")) self.line_color = StandardColors.black def set_fill_gray(self, k): self.fill_color = CreateRGBColor(k, k, k) def set_fill_cmyk(self, c, m, y, k): self.fill_color = CreateCMYKColor(c, m, y, k) def set_fill_rgb(self, r, g, b): self.fill_color = CreateRGBColor(r, g, b) def set_fill_cmyk_custom(self, c, m, y, k, name, tint): self.fill_color = cmyk_custom_color(c, m, y, k, tint) def set_fill_generic_custom(self, name, tint, type): if type == 0: # cmyk c, m, y, k = self.pop_multi(4) self.fill_color = cmyk_custom_color(c, m, y, k, tint) else: # rgb r, g, b = self.pop_multi(3) self.fill_color = rgb_custom_color(r, g, b, tint) def set_fill_pattern(self, name, px, py, sx, sy, angle, rf, r, k, ka, matrix): if not self.in_palette: self.add_message(_("Vector patterns not supported. Using black")) self.fill_color = StandardColors.black 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 lsnone(self): self.style.line_pattern = EmptyPattern def fs(self): if self.gradient_geo: pattern = self.make_gradient_pattern() else: pattern = SolidPattern(self.fill_color) self.style.fill_pattern = pattern def fsnone(self): self.style.fill_pattern = EmptyPattern def stroke(self): if self.compound_path is not None: self.compound_render = "stroke" else: self.ls() self.fsnone() self.bezier() def stroke_close(self): self.bezier_close() self.stroke() def fill(self): if self.ignore_fill: return if self.compound_path is not None: self.compound_render = "fill" else: self.lsnone() self.fs() self.bezier() def fill_close(self): self.bezier_close() self.fill() def fill_stroke(self): if self.compound_path is not None: self.compound_render = "fill_stroke" else: self.ls() self.fs() self.bezier() def fill_stroke_close(self): self.bezier_close() self.fill_stroke() def invisible(self): if self.compound_path is not None: self.compound_render = "invisible" else: self.lsnone() self.fsnone() self.bezier() def invisible_close(self): self.bezier_close() self.invisible() # Gradient functions def begin_gradient(self, name, type, ncolors): self.gradient_info = name, type, ncolors def gradient_stop(self, color_style, mid_point, ramp_point): if color_style == 0: # gray scale k = self.pop() color = CreateRGBColor(k, k, k) elif color_style == 1: # CMYK color = apply(CreateCMYKColor, tuple(self.pop_multi(4))) elif color_style == 2: # RGB Color args = tuple(self.pop_multi(7)) # The cmyk and rgb values usually differ slightly because AI # does some color correction. Which values should we choose # here? color = apply(CreateRGBColor, args[-3:]) color = apply(CreateCMYKColor, args[:4]) elif color_style == 3: # CMYK Custom Color args = self.pop_multi(6) color = apply(CreateCMYKColor, tuple(args[:4])) else: self.add_message(_("Gradient ColorStyle %d not yet supported." "substituted black") % color_style) if color_style == 4: n = 10 else: self.add_message(_("Unknown ColorStyle %d") % color_style) self.pop_multi(n) color = StandardColors.black # XXX # color = apply(CreateRGBColor, color) self.stack.append((ramp_point / 100.0, color)) def dummy_gradient_stop(self, color_style, mid_point, ramp_point): # same as gradient_stop but ignore all arguments. Illustrator 8 # seems to introduce this one for printing (i.e. Illustrator 8 # files with printing info contain the gradient stops *twice* in # exactly the same format but once with the Bs operator and once # with BS. I guess this has something to do with support for # PostScript Level 3 and backwards compatibility with older # Illustrator versions. if color_style == 0: # gray scale k = self.pop() elif color_style == 1: # CMYK self.pop_multi(4) elif color_style == 2: # RGB Color self.pop_multi(7) elif color_style == 3: # CMYK Custom Color self.pop_multi(6) elif color_style == 4: self.pop_multi(10) else: self.add_message(_("Unknown ColorStyle %d") % color_style) def gradient_ramps(self, ramp_type): # defines the ramp colors with a bunch of strings for printing. # Here we just pop all the strings off the stack num = (1, 4, 5, 6, 7, 8, 9)[ramp_type] self.pop_multi(num) def end_gradient(self): self.make_array() array = self.pop() if len(array) < 2: self.add_message(_("less than two color stops in gradient")) else: # sometimes the ramp_point values are increasing, sometimes # decreasing... what's going on here? The docs say they are # increasing. if array[0][0] > array[-1][0]: array.reverse() name, type, ncolors = self.gradient_info self.gradients[name] = (type, array) del self.stack[:] # self.pop_to_mark() def begin_gradient_instance(self): self.in_gradient_instance = 1 self.ignore_fill = 1 def gradient_geometry(self, flag, name, xorig, yorig, angle, length, a, b, c, d, tx, ty): trafo = Trafo(a, b, c, d, tx, ty) trafo = artboard_trafo_inv(trafo(artboard_trafo)) start = Point(xorig, yorig) end = start + Polar(length, (pi * angle) / 180.0) self.gradient_geo = (name, trafo, start, end) def make_gradient_pattern(self): name, trafo, start, end = self.gradient_geo self.gradient_geo = None type, array = self.gradients[name] array = array[:] if type == 0: # linear (axial) gradient origdir = end - start start = trafo(start) end = trafo(end) dir = end - start try: # adjust endpoint to accomodate trafo v = trafo.DTransform(origdir.y, -origdir.x).normalized() v = Point(v.y, -v.x) # rotate 90 degrees end = start + (v * dir) * v dir = end - start except ZeroDivisionError: pass trafo2 = Trafo(dir.x, dir.y, dir.y, -dir.x, start.x, start.y) trafo2 = trafo2.inverse() left, bottom, right, top = trafo2(self.current_bounding_rect()) if right > left: factor = 1 / (right - left) offset = -left * factor else: factor = 1 offset = 0 array = fix_gradient(array, factor, offset) pattern = LinearGradient(MultiGradient(array), (start - end).normalized()) elif type == 1: # radial gradient start = trafo(start) end = trafo(end) left, bottom, right, top = self.current_bounding_rect() if left == right or top == bottom: # an empty coord_rect???? center = Point(0, 0) else: center = Point((start.x - left) / (right - left), (start.y - bottom) / (top - bottom)) radius = max( hypot(left - start.x, top - start.y), hypot(right - start.x, top - start.y), hypot(right - start.x, bottom - start.y), hypot(left - start.x, bottom - start.y), ) if radius: factor = -abs(start - end) / radius array = fix_gradient(array, factor, 1) pattern = RadialGradient(MultiGradient(array), center) else: self.add_message(_("Unknown gradient type %d"), type) pattern = EmptyPattern return pattern def current_bounding_rect(self): if self.gradient_rect is not None: rect = self.gradient_rect else: rect = self.path.accurate_rect() if not self.style.line_pattern.is_Empty: rect = fix_bounding_rect(rect, self.style) return rect def end_gradient_instance(self, flag): self.ignore_fill = 0 if flag == 2: self.fill_stroke_close() elif flag == 1: self.fill_stroke() else: self.fill() self.in_gradient_instance = 0 # Path construction def moveto(self, x, y): self.cur_x = x self.cur_y = y self.path.AppendLine(x, y) def lineto(self, x, y): self.cur_x = x self.cur_y = y self.path.AppendLine(x, y) def curveto(self, x1, y1, x2, y2, x3, y3): self.path.AppendBezier(x1, y1, x2, y2, x3, y3) self.cur_x = x3 self.cur_y = y3 def curveto_smooth(self, x1, y1, x2, y2, x3, y3): self.path.AppendBezier(x1, y1, x2, y2, x3, y3, ContSmooth) self.cur_x = x3 self.cur_y = y3 def curveto_v(self, x2, y2, x3, y3): # current point and first control point are identical self.path.AppendBezier(self.cur_x, self.cur_y, x2, y2, x3, y3) self.cur_x = x3 self.cur_y = y3 def curveto_v_smooth(self, x2, y2, x3, y3): # current point and first control point are identical self.path.AppendBezier(self.cur_x, self.cur_y, x2, y2, x3, y3, ContSmooth) self.cur_x = x3 self.cur_y = y3 def curveto_y(self, x1, y1, x3, y3): # endpoint and last controlpoint are identical self.path.AppendBezier(x1, y1, x3, y3, x3, y3) self.cur_x = x3 self.cur_y = y3 def curveto_y_smooth(self, x1, y1, x3, y3): # endpoint and last controlpoint are identical self.path.AppendBezier(x1, y1, x3, y3, x3, y3, ContSmooth) self.cur_x = x3 self.cur_y = y3 def bezier_close(self): if self.path.len > 1: self.path.AppendLine(self.path.Node(0)) self.path.load_close(1) def bezier(self): if self.guess_continuity: self.path.guess_continuity() if self.path.len > 0: if self.compound_path is not None: self.compound_path.append(self.path) else: GenericLoader.bezier(self, paths=(self.path,)) self.path = CreatePath() # compound paths def begin_compound_path(self): self.compound_path = [] def end_compound_path(self): paths = tuple(self.compound_path) self.compound_path = None if paths: # XXX ugly if self.gradient_geo: rect = paths[0].accurate_rect() for path in paths[1:]: rect = UnionRects(rect, path.accurate_rect()) self.gradient_rect = rect else: self.gradient_rect = None getattr(self, self.compound_render)() GenericLoader.bezier(self, paths=paths) # Groups def begin_group(self): if self.compound_path is None: # a normal group if self.treat_toplevel_groups_as_layers: if self.composite_class == Document: self.begin_layer() return GenericLoader.begin_group(self) else: # a `compound group'. Ignored since Sketch doesn't have this. pass def end_group(self): if self.compound_path is None: # a normal group if self.composite_class == Layer: self.end_composite() else: try: GenericLoader.end_group(self) if self.flatten_groups: if self.object.NumObjects() == 1: obj = self.object.GetObjects()[0] del self.composite_items[-1] self.append_object(obj) except EmptyCompositeError: pass else: # a `compound group'. Ignored since Sketch doesn't have this. pass # Layers def begin_layer(self): self.layer(_("Layer %d") % (len(self.composite_items) + 1)) def begin_ai_layer(self): if self.format_version >= 4.0: visible, preview, enabled, printing, dimmed, unused, has_mlm, color, red, green, blue, unused, unused = self.pop_multi( 13 ) else: visible, preview, enabled, printing, dimmed, has_mlm, color, red, green, blue = self.pop_multi(10) color = CreateRGBColor(red / 255.0, green / 255.0, blue / 255.0) self.layer_kw_args = {"printable": printing, "visible": visible, "locked": not enabled, "outline_color": color} def end_ai_layer(self): self.end_layer() def name_layer(self, name): apply(self.layer, (name,), self.layer_kw_args) # Guides def guide(self, op): # print 'guide', op method = getattr(self, self.functions[op]) method() guide = self.pop_last() self.guides.append(guide) # Palette def begin_palette(self): self.in_palette = 1 def end_palette(self): self.in_palette = 0 # Text def set_standard_encoding(self): encoding = list(self.standard_encoding) pos = 0 defs = self.pop_to_mark() for item in defs: if type(item) == IntType: pos = item elif type(item) == StringType: encoding[pos] = item pos = pos + 1 else: self.add_message("unknown item %s in encoding" % ` item `) self.standard_encoding = tuple(encoding) def define_font(self, psname, newname, encoding=None): if encoding is None: encoding = self.standard_encoding[:] self.font_map[newname] = FontInfo(psname, newname, encoding) def reencode_font(self): args = self.pop_to_mark() if type(args[-1]) == ListType: self.add_message(_("Multiple Master fonts not supported. " "Using Times Roman")) newname = args[-6] self.define_font("Times Roman", newname) else: newname, psname, direction, script, usedefault = args[-5:] if len(args) > 5: self.add_message(_("Additional encoding ignored")) self.define_font(psname, newname) def begin_text(self, text_type): self.in_text = 1 self.text_type = text_type self.text_string = [] if text_type == 1: self.add_message(_("Area text not supported")) if text_type == 2: GenericLoader.begin_group(self) def end_text(self): # we don't support area text (text_type 1) at all. Return # immediately in that case. if self.text_type == 1: return # first, turn the text accumulated in the list text_string into # a single string and unify line endings to newline characters. text = string.join(self.text_string, "") text = string.replace(text, "\r\n", "\n") text = string.replace(text, "\r", "\n") # remove a trailing newline. Many Illustrator files contain a # trailing newline as 'overflow' text, there's probably a better # way to deal with this... if text[-1:] == "\n": text = text[:-1] # Re-encode to Latin1 text = self.text_font.Reencode(text) if not string.strip(text): if self.text_type == 2: self.end_composite() del self.composite_items[-1] if len(self.composite_items) > 0: self.object = self.composite_items[-1] return # first create a simple text object self.fs() self.style.font = GetFont(self.text_font.psname) self.style.font_size = self.text_size self.simple_text(text, self.text_trafo, halign=_ai_text_align[self.text_align]) # if we're actually supposed to create a path-text object, turn # the text object just created into a path-text object if self.text_type == 2: GenericLoader.end_group(self) group = self.pop_last() objects = group.GetObjects() if len(objects) == 2: path, text = objects self.append_object(PathText(text, path, start_pos=self.text_start_pos)) # self.composite_items[-1] = self.object # we've finished the text object self.in_text = 0 def set_text_render(self, render): self.text_render = render def set_text_align(self, align): self.text_align = align def set_text_font(self): # In Illustrator < 7, the operator has two arguments, new # fontname and size. In Illustrator >= 7, there are two # additional arguments, ascent and descent. args = self.pop_multi(2) if type(args[0]) != StringType: newname, size = self.pop_multi(2) else: newname, size = args if self.font_map.has_key(newname): self.text_font = self.font_map[newname] elif newname[0] == "_": # special case for ai files generated by ps2ai. They don't # use the TZ operator to reencode the fonts and define the _ # names. self.define_font(newname[1:], newname) self.text_font = self.font_map[newname] else: self.add_message(_("No font %s.") % newname) self.text_size = size def begin_text_path(self, a, b, c, d, tx, ty, start_pos): self.text_trafo = Trafo(a, b, c, d, tx, ty) self.text_start_pos = start_pos def end_text_path(self): pass def render_text(self, text): if self.text_type != 2: # in a path text only the invisible render operators count self.text_string.append(text) def render_text_inv(self, text): self.text_string.append(text) # Raster Image def raster_image(self, trafo, llx, lly, urx, ury, width, height, bits, mode, alpha, reserved, encoding, mask): if bits != 8 or mode not in (1, 3): self.add_message(_("Only images with 1 or 3 components " "and 8 bits/component supported")) self.skip_to_dsc("AI5_EndRaster") return decode = streamfilter.SubFileDecode(self.tokenizer.source, "%AI5_EndRaster") if encoding == 0: decode = streamfilter.HexDecode(decode) data_length = mode * width * height data = decode.read(data_length) # f = open("/tmp/dump.ppm", "w") # if mode == 1: # f.write("P5\n%d %d\n255\n" % (width, height)) # else: # f.write("P6\n%d %d\n255\n" % (width, height)) # f.write(data) # f.close() if mode == 1: mode = "L" elif mode == 3: mode = "RGB" elif mode == 4: mode == "CMYK" image = Image.fromstring(mode, (width, height), data, "raw", mode) self.image(image, apply(Trafo, tuple(trafo))) # def append_object(self, object): if self.composite_class == Document and object.__class__ != Layer: self.begin_layer() self.composite_items.append(object) self.object = object # # def skip_to_dsc(self, *endcomments): next_dsc = self.tokenizer.next_dsc split = string.split while 1: value = next_dsc() if not value: return if ":" in value: keyword, value = split(value, ":", 1) else: keyword = value if keyword in endcomments: return def read_prolog(self): next = self.tokenizer.next DSC = DSC_COMMENT split = string.split while 1: token, value = next() if token == DSC: if ":" in value: keyword, value = split(value, ":", 1) else: keyword = value if keyword in ("EndProlog", "BeginSetup"): return keyword if keyword[:14] == "AI5_FileFormat": self.format_version = string.atof(keyword[14:]) elif keyword == "BeginProcSet": # some ai files exported by corel draw don't have an # EndProcSet comment after a BeginProcSet... self.skip_to_dsc("EndProcSet", "EndProlog") elif keyword == "BeginResource": self.skip_to_dsc("EndResource", "EndProlog") elif keyword == "Creator": # try to determine whether the file really is an # illustrator file as opposed to some other EPS # file. It seems that Illustrator itself only # accepts EPS files as illustrator files if they # contain "Adobe Illustrator" in their Create # DSC-comment if string.find(value, "Adobe Illustrator") == -1: self.add_message("This is probably not an" " Illustrator file." " Try embedding it as EPS") if token == END: return def Load(self): funclist = self.get_compiled() # binding frequently used functions to local variables speeds up # the process considerably... a = apply t = tuple DSC = DSC_COMMENT MAX = MAX_DATA_TOKEN split = string.split stack = self.stack push = self.stack.append unknown_operator = (None, None) decoder = streamfilter.StringDecode(self.match.string, self.file) self.tokenizer = PSTokenizer(decoder) self.tokenizer.ai_pseudo_comments = 1 self.tokenizer.ai_dsc = 1 next = self.tokenizer.next self.document() value = self.read_prolog() while 1: token, value = next() if token <= MAX: push(value) elif token == DSC: if ":" in value: keyword, value = split(value, ":", 1) else: keyword = value if keyword in ("PageTrailer", "Trailer"): break elif keyword == "AI5_BeginPalette": self.skip_to_dsc("AI5_EndPalette", "EndSetup") elif keyword == "AI8_BeginBrushPattern": self.skip_to_dsc("AI8_EndBrushPattern", "EndSetup") elif token == END: break elif token == OPERATOR: method, argc = funclist.get(value, unknown_operator) # if method is not None: # name = method.__name__ # else: # name = `method` if method is None: del stack[:] else: try: if argc: args = t(stack[-argc:]) del stack[-argc:] a(method, args) else: method() except: warn_tb(INTERNAL, "AILoader: error") self.end_all() self.object.load_Completed() for obj in self.guides: self.object.guide_layer.Insert(obj, None) return self.object
def parse_path(self, str): paths = self.paths path = self.path trafo = self.trafo str = strip(string.translate(as_latin1(str), commatospace)) last_quad = None last_cmd = cmd = None f13 = 1.0 / 3.0 f23 = 2.0 / 3.0 #print '*', str while 1: match = rx_command.match(str) #print match if match: last_cmd = cmd cmd = str[0] str = str[match.end():] #print '*', str points = match.group(1) #print '**', points if points: # use tokenize_line to parse the arguments so that # we deal with signed numbers following another # number without intervening whitespace other # characters properls. # FIXME: tokenize_line works but is not the best way # to do it because it accepts input that wouldn't be # valid here. points = filter(operator.isNumberType, skread.tokenize_line(points)) #print cmd, points if cmd in 'mM': path = CreatePath() paths.append(path) if cmd == 'M' or len(paths) == 1: path.AppendLine(trafo(points[0], points[1])) else: p = trafo.DTransform(points[0], points[1]) path.AppendLine(paths[-2].Node(-1) + p) if len(points) > 2: if cmd == 'm': for i in range(2, len(points), 2): p = trafo.DTransform(points[i], points[i + 1]) path.AppendLine(path.Node(-1) + p) else: for i in range(2, len(points), 2): path.AppendLine(trafo(points[i], points[i + 1])) elif cmd == 'l': for i in range(0, len(points), 2): p = trafo.DTransform(points[i], points[i + 1]) path.AppendLine(path.Node(-1) + p) elif cmd == 'L': for i in range(0, len(points), 2): path.AppendLine(trafo(points[i], points[i + 1])) elif cmd == 'H': for num in points: path.AppendLine(Point(num, path.Node(-1).y)) elif cmd == 'h': for num in points: x, y = path.Node(-1) dx, dy = trafo.DTransform(num, 0) path.AppendLine(Point(x + dx, y + dy)) elif cmd == 'V': for num in points: path.AppendLine(Point(path.Node(-1).x, num)) elif cmd == 'v': for num in points: x, y = path.Node(-1) dx, dy = trafo.DTransform(0, num) path.AppendLine(Point(x + dx, y + dy)) elif cmd == 'C': if len(points) % 6 != 0: self.loader.add_message("number of parameters of 'C'"\ "must be multiple of 6") else: for i in range(0, len(points), 6): p1 = trafo(points[i], points[i + 1]) p2 = trafo(points[i + 2], points[i + 3]) p3 = trafo(points[i + 4], points[i + 5]) path.AppendBezier(p1, p2, p3) elif cmd == 'c': if len(points) % 6 != 0: self.loader.add_message("number of parameters of 'c'"\ "must be multiple of 6") else: for i in range(0, len(points), 6): p = path.Node(-1) p1 = p + trafo.DTransform(points[i], points[i + 1]) p2 = p + trafo.DTransform(points[i + 2], points[i + 3]) p3 = p + trafo.DTransform(points[i + 4], points[i + 5]) path.AppendBezier(p1, p2, p3) elif cmd == 'S': if len(points) % 4 != 0: self.loader.add_message("number of parameters of 'S'"\ "must be multiple of 4") else: for i in range(0, len(points), 4): type, controls, p, cont = path.Segment(-1) if type == Bezier: q = controls[1] else: q = p p1 = 2 * p - q p2 = trafo(points[i], points[i + 1]) p3 = trafo(points[i + 2], points[i + 3]) path.AppendBezier(p1, p2, p3) elif cmd == 's': if len(points) % 4 != 0: self.loader.add_message("number of parameters of 's'"\ "must be multiple of 4") else: for i in range(0, len(points), 4): type, controls, p, cont = path.Segment(-1) if type == Bezier: q = controls[1] else: q = p p1 = 2 * p - q p2 = p + trafo.DTransform(points[i], points[i + 1]) p3 = p + trafo.DTransform(points[i + 2], points[i + 3]) path.AppendBezier(p1, p2, p3) elif cmd == 'Q': if len(points) % 4 != 0: self.loader.add_message("number of parameters of 'Q'"\ "must be multiple of 4") else: for i in range(0, len(points), 4): q = trafo(points[i], points[i + 1]) p3 = trafo(points[i + 2], points[i + 3]) p1 = f13 * path.Node(-1) + f23 * q p2 = f13 * p3 + f23 * q path.AppendBezier(p1, p2, p3) last_quad = q elif cmd == 'q': if len(points) % 4 != 0: self.loader.add_message("number of parameters of 'q'"\ "must be multiple of 4") else: for i in range(0, len(points), 4): p = path.Node(-1) q = p + trafo.DTransform(points[i], points[i + 1]) p3 = p + trafo.DTransform(points[i + 2], points[i + 3]) p1 = f13 * p + f23 * q p2 = f13 * p3 + f23 * q path.AppendBezier(p1, p2, p3) last_quad = q elif cmd == 'T': if len(points) % 2 != 0: self.loader.add_message("number of parameters of 'T'"\ "must be multiple of 4") else: if last_cmd not in 'QqTt' or last_quad is None: last_quad = path.Node(-1) for i in range(0, len(points), 2): p = path.Node(-1) q = 2 * p - last_quad p3 = trafo(points[i], points[i + 1]) p1 = f13 * p + f23 * q p2 = f13 * p3 + f23 * q path.AppendBezier(p1, p2, p3) last_quad = q elif cmd == 't': if len(points) % 2 != 0: self.loader.add_message("number of parameters of 't'"\ "must be multiple of 4") else: if last_cmd not in 'QqTt' or last_quad is None: last_quad = path.Node(-1) for i in range(0, len(points), 2): p = path.Node(-1) q = 2 * p - last_quad p3 = p + trafo.DTransform(points[i], points[i + 1]) p1 = f13 * p + f23 * q p2 = f13 * p3 + f23 * q path.AppendBezier(p1, p2, p3) last_quad = q elif cmd in 'zZ': path.AppendLine(path.Node(0)) path.ClosePath() else: break self.path = path
def read_path(filename): path = CreatePath() paths = [path] points = [] file = open(filename) closed = 0 for line in file.readlines(): try: key, rest = split(line, ':', 1) except: continue if key == 'TYPE': rest = lstrip(rest) match = rx_point.match(rest) if match is not None: type = int(match.group('type')) p = Point(float(match.group('x')), float(match.group('y'))) if type == BEZIER_MOVE: if closed and points: path.AppendBezier(points[0], points[1], path.Node(0)) path.ClosePath() points = [] path = CreatePath() paths.append(path) path.AppendLine(p) elif type == BEZIER_ANCHOR: if path.len == 0: path.AppendLine(p) else: if path.Node(-1) == points[0] and points[1] == p: path.AppendLine(p) else: path.AppendBezier(points[0], points[1], p) points = [] elif type == BEZIER_CONTROL: points.append(p) elif key == 'CLOSED': closed = int(rest) if closed and points: if path.Node(-1) == points[0] and points[1] == path.Node(0): path.AppendLine(path.Node(0)) else: path.AppendBezier(points[0], points[1], path.Node(0)) path.ClosePath() return tuple(paths)
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)
def POLYGONSET(self, size): path = () subpath = CreatePath() for i in range(size / (2 * reff.vdc.size + 2)): P = self.Pnt() F = self.Enum() subpath.AppendLine(self.trafo(P)) if F in (2, 3): if subpath.Node(-1) != subpath.Node(0): subpath.AppendLine(subpath.Node(0)) subpath.load_close() path = path + (subpath, ) subpath = CreatePath() if subpath.len != 0: if subpath.Node(-1) != subpath.Node(0): subpath.AppendLine(subpath.Node(0)) subpath.load_close() path = path + (subpath, ) self.setfillstyle() self.bezier(path)
def insert_segments(path): newpath = CreatePath() newpath.AppendLine(path.Node(0), path.Continuity(0)) newpath.select_segment(0, path.SegmentSelected(0)) for i in range(1, path.len): type, p12, p, cont = path.Segment(i) if path.SegmentSelected(i) and path.SegmentSelected(i - 1): if type == Line: node = 0.5 * path.Node(i - 1) + 0.5 * path.Node(i) newpath.AppendLine(node) newpath.select_segment(-1) newpath.AppendLine(path.Node(i)) newpath.select_segment(-1) else: if newpath.Continuity(-1) == ContSymmetrical: newpath.SetContinuity(-1, ContSmooth) p1, p2 = p12 p1, p2, node, p3, p4 = subdivide(path.Node(i - 1), p1, p2, p) newpath.AppendBezier(p1, p2, node, ContSymmetrical) newpath.select_segment(-1) if cont == ContSymmetrical: cont = ContSmooth newpath.AppendBezier(p3, p4, p, cont) newpath.select_segment(-1) else: newpath.AppendSegment(type, p12, p, cont) newpath.select_segment(-1, path.SegmentSelected(i)) if path.closed: newpath.ClosePath() newpath.SetContinuity(-1, path.Continuity(-1)) return newpath
class AILoader(GenericLoader): format_name = format_name functions = { "C": 'curveto', "c": 'curveto_smooth', "V": 'curveto_v', "v": 'curveto_v_smooth', "Y": 'curveto_y', "y": 'curveto_y_smooth', "m": 'moveto', "l": 'lineto', "L": 'lineto', "w": 'set_line_width', "j": 'set_line_join', "J": 'set_line_cap', "d": 'set_line_dash', "G": 'set_line_gray', "K": 'set_line_cmyk', "XA": 'set_line_rgb', "X": 'set_line_cmyk_custom', "XX": 'set_line_generic_custom', "P": 'set_line_pattern', "g": 'set_fill_gray', "k": 'set_fill_cmyk', "Xa": 'set_fill_rgb', "x": 'set_fill_cmyk_custom', "Xx": 'set_fill_generic_custom', "p": 'set_fill_pattern', "F": 'fill', "f": 'fill_close', "S": 'stroke', "s": 'stroke_close', "B": 'fill_stroke', "b": 'fill_stroke_close', "N": 'invisible', # an invisible open path "n": 'invisible_close', # a invisible closed path "u": 'begin_group', "U": 'end_group', "*u": 'begin_compound_path', "*U": 'end_compound_path', "*": 'guide', "[": 'mark', "]": 'make_array', "@": 'ignore_operator', "&": 'ignore_operator', "Bd": 'begin_gradient', "Bs": 'gradient_stop', "BS": 'dummy_gradient_stop', "Br": 'gradient_ramps', "BD": 'end_gradient', "Bb": 'begin_gradient_instance', "Bg": 'gradient_geometry', "BB": 'end_gradient_instance', "Lb": 'begin_ai_layer', "Ln": 'name_layer', "LB": 'end_ai_layer', "Pb": 'begin_palette', "PB": 'end_palette', "TE": 'set_standard_encoding', "TZ": 'reencode_font', "To": 'begin_text', "TO": 'end_text', "Tr": 'set_text_render', "Tf": 'set_text_font', "Ta": 'set_text_align', "Tp": 'begin_text_path', "TP": 'end_text_path', "Tx": 'render_text', "TX": 'render_text_inv', "XI": 'raster_image', } def __init__(self, file, filename, match, treat_toplevel_groups_as_layers=1, flatten_groups=1): GenericLoader.__init__(self, file, filename, match) self.line_color = StandardColors.black self.fill_color = StandardColors.black self.line_width = 0.0 self.line_join = const.JoinMiter self.line_cap = const.CapButt self.line_dashes = () self.cur_x = self.cur_y = 0.0 self.treat_toplevel_groups_as_layers = treat_toplevel_groups_as_layers self.flatten_groups = flatten_groups self.guess_continuity = 1 self.path = CreatePath() self.compound_path = None # If compound_path is None, we're # outside of a compound path, # otherwise it's a possibly empty list # of paths self.compound_render = '' self.stack = [] self.gradients = {} self.in_gradient_instance = 0 self.gradient_geo = None # set to a true value after Bg, and set # to false by make_gradient_pattern self.gradient_rect = None self.in_palette = 0 self.in_text = 0 self.ignore_fill = 0 self.text_type = 0 # 0: point text, 1: area text, 2 = path text self.text_render = 0 # filled self.text_font = None self.text_size = 12 # Test alignment. Possible values: 0: left, 1: center, 2:right, # 3: justified, 4: justified including last line self.text_align = 0 self.text_string = [] self.standard_encoding = encoding.adobe_standard self.font_map = {} self.guides = [] self.format_version = 0.0 def __del__(self): pass def warn(self, level, *args, **kw): message = apply(warn, (level, ) + args, kw) self.add_message(message) def get_compiled(self): funclist = {} for char, name in self.functions.items(): method = getattr(self, name) argc = method.im_func.func_code.co_argcount - 1 funclist[char] = (method, argc) return funclist def pop(self): value = self.stack[-1] del self.stack[-1] return value def pop_multi(self, num): value = self.stack[-num:] del self.stack[-num:] return value def pop_to_mark(self): s = self.stack[:] s.reverse() try: idx = s.index(None) if idx: array = self.stack[-idx:] del self.stack[-idx - 1:] else: array = [] del self.stack[-1] return array except: raise RuntimeError, 'No mark on stack' def ignore_operator(self): pass def mark(self): self.stack.append(None) def make_array(self): array = self.pop_to_mark() self.stack.append(array) def convert_color(self, color_spec): c = apply(CreateRGBColor, color_spec) return c def set_line_join(self, join): self.line_join = _ai_join[join] def set_line_cap(self, cap): self.line_cap = _ai_cap[cap] def set_line_width(self, w): self.line_width = w def set_line_dash(self, array, phase): self.line_dashes = tuple(array) def set_line_gray(self, k): self.line_color = CreateRGBColor(k, k, k) def set_line_cmyk(self, c, m, y, k): self.line_color = CreateCMYKColor(c, m, y, k) def set_line_rgb(self, r, g, b): self.line_color = CreateRGBColor(r, g, b) def set_line_cmyk_custom(self, c, m, y, k, name, tint): self.line_color = cmyk_custom_color(c, m, y, k, tint) def set_line_generic_custom(self, name, tint, type): if type == 0: # cmyk c, m, y, k = self.pop_multi(4) self.line_color = cmyk_custom_color(c, m, y, k, tint) else: # rgb r, g, b = self.pop_multi(3) self.line_color = rgb_custom_color(r, g, b, tint) def set_line_pattern(self, name, px, py, sx, sy, angle, rf, r, k, ka, matrix): if not self.in_palette: self.add_message(_("Vector patterns not supported. Using black")) self.line_color = StandardColors.black def set_fill_gray(self, k): self.fill_color = CreateRGBColor(k, k, k) def set_fill_cmyk(self, c, m, y, k): self.fill_color = CreateCMYKColor(c, m, y, k) def set_fill_rgb(self, r, g, b): self.fill_color = CreateRGBColor(r, g, b) def set_fill_cmyk_custom(self, c, m, y, k, name, tint): self.fill_color = cmyk_custom_color(c, m, y, k, tint) def set_fill_generic_custom(self, name, tint, type): if type == 0: # cmyk c, m, y, k = self.pop_multi(4) self.fill_color = cmyk_custom_color(c, m, y, k, tint) else: # rgb r, g, b = self.pop_multi(3) self.fill_color = rgb_custom_color(r, g, b, tint) def set_fill_pattern(self, name, px, py, sx, sy, angle, rf, r, k, ka, matrix): if not self.in_palette: self.add_message(_("Vector patterns not supported. Using black")) self.fill_color = StandardColors.black 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 lsnone(self): self.style.line_pattern = EmptyPattern def fs(self): if self.gradient_geo: pattern = self.make_gradient_pattern() else: pattern = SolidPattern(self.fill_color) self.style.fill_pattern = pattern def fsnone(self): self.style.fill_pattern = EmptyPattern def stroke(self): if self.compound_path is not None: self.compound_render = 'stroke' else: self.ls() self.fsnone() self.bezier() def stroke_close(self): self.bezier_close() self.stroke() def fill(self): if self.ignore_fill: return if self.compound_path is not None: self.compound_render = 'fill' else: self.lsnone() self.fs() self.bezier() def fill_close(self): self.bezier_close() self.fill() def fill_stroke(self): if self.compound_path is not None: self.compound_render = 'fill_stroke' else: self.ls() self.fs() self.bezier() def fill_stroke_close(self): self.bezier_close() self.fill_stroke() def invisible(self): if self.compound_path is not None: self.compound_render = 'invisible' else: self.lsnone() self.fsnone() self.bezier() def invisible_close(self): self.bezier_close() self.invisible() # Gradient functions def begin_gradient(self, name, type, ncolors): self.gradient_info = name, type, ncolors def gradient_stop(self, color_style, mid_point, ramp_point): if color_style == 0: # gray scale k = self.pop() color = CreateRGBColor(k, k, k) elif color_style == 1: # CMYK color = apply(CreateCMYKColor, tuple(self.pop_multi(4))) elif color_style == 2: # RGB Color args = tuple(self.pop_multi(7)) # The cmyk and rgb values usually differ slightly because AI # does some color correction. Which values should we choose # here? color = apply(CreateRGBColor, args[-3:]) color = apply(CreateCMYKColor, args[:4]) elif color_style == 3: # CMYK Custom Color args = self.pop_multi(6) color = apply(CreateCMYKColor, tuple(args[:4])) else: self.add_message( _("Gradient ColorStyle %d not yet supported." "substituted black") % color_style) if color_style == 4: n = 10 else: self.add_message(_("Unknown ColorStyle %d") % color_style) self.pop_multi(n) color = StandardColors.black # XXX #color = apply(CreateRGBColor, color) self.stack.append((ramp_point / 100.0, color)) def dummy_gradient_stop(self, color_style, mid_point, ramp_point): # same as gradient_stop but ignore all arguments. Illustrator 8 # seems to introduce this one for printing (i.e. Illustrator 8 # files with printing info contain the gradient stops *twice* in # exactly the same format but once with the Bs operator and once # with BS. I guess this has something to do with support for # PostScript Level 3 and backwards compatibility with older # Illustrator versions. if color_style == 0: # gray scale k = self.pop() elif color_style == 1: # CMYK self.pop_multi(4) elif color_style == 2: # RGB Color self.pop_multi(7) elif color_style == 3: # CMYK Custom Color self.pop_multi(6) elif color_style == 4: self.pop_multi(10) else: self.add_message(_("Unknown ColorStyle %d") % color_style) def gradient_ramps(self, ramp_type): # defines the ramp colors with a bunch of strings for printing. # Here we just pop all the strings off the stack num = (1, 4, 5, 6, 7, 8, 9)[ramp_type] self.pop_multi(num) def end_gradient(self): self.make_array() array = self.pop() if len(array) < 2: self.add_message(_("less than two color stops in gradient")) else: # sometimes the ramp_point values are increasing, sometimes # decreasing... what's going on here? The docs say they are # increasing. if array[0][0] > array[-1][0]: array.reverse() name, type, ncolors = self.gradient_info self.gradients[name] = (type, array) del self.stack[:] #self.pop_to_mark() def begin_gradient_instance(self): self.in_gradient_instance = 1 self.ignore_fill = 1 def gradient_geometry(self, flag, name, xorig, yorig, angle, length, a, b, c, d, tx, ty): trafo = Trafo(a, b, c, d, tx, ty) trafo = artboard_trafo_inv(trafo(artboard_trafo)) start = Point(xorig, yorig) end = start + Polar(length, (pi * angle) / 180.0) self.gradient_geo = (name, trafo, start, end) def make_gradient_pattern(self): name, trafo, start, end = self.gradient_geo self.gradient_geo = None type, array = self.gradients[name] array = array[:] if type == 0: # linear (axial) gradient origdir = end - start start = trafo(start) end = trafo(end) dir = end - start try: # adjust endpoint to accomodate trafo v = trafo.DTransform(origdir.y, -origdir.x).normalized() v = Point(v.y, -v.x) # rotate 90 degrees end = start + (v * dir) * v dir = end - start except ZeroDivisionError: pass trafo2 = Trafo(dir.x, dir.y, dir.y, -dir.x, start.x, start.y) trafo2 = trafo2.inverse() left, bottom, right, top = trafo2(self.current_bounding_rect()) if right > left: factor = 1 / (right - left) offset = -left * factor else: factor = 1 offset = 0 array = fix_gradient(array, factor, offset) pattern = LinearGradient(MultiGradient(array), (start - end).normalized()) elif type == 1: # radial gradient start = trafo(start) end = trafo(end) left, bottom, right, top = self.current_bounding_rect() if left == right or top == bottom: # an empty coord_rect???? center = Point(0, 0) else: center = Point((start.x - left) / (right - left), (start.y - bottom) / (top - bottom)) radius = max(hypot(left - start.x, top - start.y), hypot(right - start.x, top - start.y), hypot(right - start.x, bottom - start.y), hypot(left - start.x, bottom - start.y)) if radius: factor = -abs(start - end) / radius array = fix_gradient(array, factor, 1) pattern = RadialGradient(MultiGradient(array), center) else: self.add_message(_("Unknown gradient type %d"), type) pattern = EmptyPattern return pattern def current_bounding_rect(self): if self.gradient_rect is not None: rect = self.gradient_rect else: rect = self.path.accurate_rect() if not self.style.line_pattern.is_Empty: rect = fix_bounding_rect(rect, self.style) return rect def end_gradient_instance(self, flag): self.ignore_fill = 0 if flag == 2: self.fill_stroke_close() elif flag == 1: self.fill_stroke() else: self.fill() self.in_gradient_instance = 0 # Path construction def moveto(self, x, y): self.cur_x = x self.cur_y = y self.path.AppendLine(x, y) def lineto(self, x, y): self.cur_x = x self.cur_y = y self.path.AppendLine(x, y) def curveto(self, x1, y1, x2, y2, x3, y3): self.path.AppendBezier(x1, y1, x2, y2, x3, y3) self.cur_x = x3 self.cur_y = y3 def curveto_smooth(self, x1, y1, x2, y2, x3, y3): self.path.AppendBezier(x1, y1, x2, y2, x3, y3, ContSmooth) self.cur_x = x3 self.cur_y = y3 def curveto_v(self, x2, y2, x3, y3): # current point and first control point are identical self.path.AppendBezier(self.cur_x, self.cur_y, x2, y2, x3, y3) self.cur_x = x3 self.cur_y = y3 def curveto_v_smooth(self, x2, y2, x3, y3): # current point and first control point are identical self.path.AppendBezier(self.cur_x, self.cur_y, x2, y2, x3, y3, ContSmooth) self.cur_x = x3 self.cur_y = y3 def curveto_y(self, x1, y1, x3, y3): # endpoint and last controlpoint are identical self.path.AppendBezier(x1, y1, x3, y3, x3, y3) self.cur_x = x3 self.cur_y = y3 def curveto_y_smooth(self, x1, y1, x3, y3): # endpoint and last controlpoint are identical self.path.AppendBezier(x1, y1, x3, y3, x3, y3, ContSmooth) self.cur_x = x3 self.cur_y = y3 def bezier_close(self): if self.path.len > 1: self.path.AppendLine(self.path.Node(0)) self.path.load_close(1) def bezier(self): if self.guess_continuity: self.path.guess_continuity() if self.path.len > 0: if self.compound_path is not None: self.compound_path.append(self.path) else: GenericLoader.bezier(self, paths=(self.path, )) self.path = CreatePath() # compound paths def begin_compound_path(self): self.compound_path = [] def end_compound_path(self): paths = tuple(self.compound_path) self.compound_path = None if paths: # XXX ugly if self.gradient_geo: rect = paths[0].accurate_rect() for path in paths[1:]: rect = UnionRects(rect, path.accurate_rect()) self.gradient_rect = rect else: self.gradient_rect = None getattr(self, self.compound_render)() GenericLoader.bezier(self, paths=paths) # Groups def begin_group(self): if self.compound_path is None: # a normal group if self.treat_toplevel_groups_as_layers: if self.composite_class == Document: self.begin_layer() return GenericLoader.begin_group(self) else: # a `compound group'. Ignored since Sketch doesn't have this. pass def end_group(self): if self.compound_path is None: # a normal group if self.composite_class == Layer: self.end_composite() else: try: GenericLoader.end_group(self) if self.flatten_groups: if self.object.NumObjects() == 1: obj = self.object.GetObjects()[0] del self.composite_items[-1] self.append_object(obj) except EmptyCompositeError: pass else: # a `compound group'. Ignored since Sketch doesn't have this. pass # Layers def begin_layer(self): self.layer(_("Layer %d") % (len(self.composite_items) + 1)) def begin_ai_layer(self): if self.format_version >= 4.0: visible, preview, enabled, printing, dimmed, unused, has_mlm,\ color, red, green, blue, unused, unused = self.pop_multi(13) else: visible, preview, enabled, printing, dimmed, has_mlm, \ color, red, green, blue = self.pop_multi(10) color = CreateRGBColor(red / 255.0, green / 255.0, blue / 255.0) self.layer_kw_args = { 'printable': printing, 'visible': visible, 'locked': not enabled, 'outline_color': color } def end_ai_layer(self): self.end_layer() def name_layer(self, name): apply(self.layer, (name, ), self.layer_kw_args) # Guides def guide(self, op): #print 'guide', op method = getattr(self, self.functions[op]) method() guide = self.pop_last() self.guides.append(guide) # Palette def begin_palette(self): self.in_palette = 1 def end_palette(self): self.in_palette = 0 # Text def set_standard_encoding(self): encoding = list(self.standard_encoding) pos = 0 defs = self.pop_to_mark() for item in defs: if type(item) == IntType: pos = item elif type(item) == StringType: encoding[pos] = item pos = pos + 1 else: self.add_message('unknown item %s in encoding' % ` item `) self.standard_encoding = tuple(encoding) def define_font(self, psname, newname, encoding=None): if encoding is None: encoding = self.standard_encoding[:] self.font_map[newname] = FontInfo(psname, newname, encoding) def reencode_font(self): args = self.pop_to_mark() if type(args[-1]) == ListType: self.add_message( _("Multiple Master fonts not supported. " "Using Times Roman")) newname = args[-6] self.define_font('Times Roman', newname) else: newname, psname, direction, script, usedefault = args[-5:] if len(args) > 5: self.add_message(_("Additional encoding ignored")) self.define_font(psname, newname) def begin_text(self, text_type): self.in_text = 1 self.text_type = text_type self.text_string = [] if text_type == 1: self.add_message(_("Area text not supported")) if text_type == 2: GenericLoader.begin_group(self) def end_text(self): # we don't support area text (text_type 1) at all. Return # immediately in that case. if self.text_type == 1: return # first, turn the text accumulated in the list text_string into # a single string and unify line endings to newline characters. text = string.join(self.text_string, '') text = string.replace(text, '\r\n', '\n') text = string.replace(text, '\r', '\n') # remove a trailing newline. Many Illustrator files contain a # trailing newline as 'overflow' text, there's probably a better # way to deal with this... if text[-1:] == "\n": text = text[:-1] # Re-encode to Latin1 text = self.text_font.Reencode(text) if not string.strip(text): if self.text_type == 2: self.end_composite() del self.composite_items[-1] if len(self.composite_items) > 0: self.object = self.composite_items[-1] return # first create a simple text object self.fs() self.style.font = GetFont(self.text_font.psname) self.style.font_size = self.text_size self.simple_text(text, self.text_trafo, halign=_ai_text_align[self.text_align]) # if we're actually supposed to create a path-text object, turn # the text object just created into a path-text object if self.text_type == 2: GenericLoader.end_group(self) group = self.pop_last() objects = group.GetObjects() if len(objects) == 2: path, text = objects self.append_object( PathText(text, path, start_pos=self.text_start_pos)) #self.composite_items[-1] = self.object # we've finished the text object self.in_text = 0 def set_text_render(self, render): self.text_render = render def set_text_align(self, align): self.text_align = align def set_text_font(self): # In Illustrator < 7, the operator has two arguments, new # fontname and size. In Illustrator >= 7, there are two # additional arguments, ascent and descent. args = self.pop_multi(2) if type(args[0]) != StringType: newname, size = self.pop_multi(2) else: newname, size = args if self.font_map.has_key(newname): self.text_font = self.font_map[newname] elif newname[0] == '_': # special case for ai files generated by ps2ai. They don't # use the TZ operator to reencode the fonts and define the _ # names. self.define_font(newname[1:], newname) self.text_font = self.font_map[newname] else: self.add_message(_("No font %s.") % newname) self.text_size = size def begin_text_path(self, a, b, c, d, tx, ty, start_pos): self.text_trafo = Trafo(a, b, c, d, tx, ty) self.text_start_pos = start_pos def end_text_path(self): pass def render_text(self, text): if self.text_type != 2: # in a path text only the invisible render operators count self.text_string.append(text) def render_text_inv(self, text): self.text_string.append(text) # Raster Image def raster_image(self, trafo, llx, lly, urx, ury, width, height, bits, mode, alpha, reserved, encoding, mask): if bits != 8 or mode not in (1, 3): self.add_message( _("Only images with 1 or 3 components " "and 8 bits/component supported")) self.skip_to_dsc("AI5_EndRaster") return decode = streamfilter.SubFileDecode(self.tokenizer.source, '%AI5_EndRaster') if encoding == 0: decode = streamfilter.HexDecode(decode) data_length = mode * width * height data = decode.read(data_length) #f = open("/tmp/dump.ppm", "w") #if mode == 1: # f.write("P5\n%d %d\n255\n" % (width, height)) #else: # f.write("P6\n%d %d\n255\n" % (width, height)) #f.write(data) #f.close() if mode == 1: mode = 'L' elif mode == 3: mode = 'RGB' elif mode == 4: mode == 'CMYK' image = Image.fromstring(mode, (width, height), data, 'raw', mode) self.image(image, apply(Trafo, tuple(trafo))) # def append_object(self, object): if self.composite_class == Document \ and object.__class__ != Layer: self.begin_layer() self.composite_items.append(object) self.object = object # # def skip_to_dsc(self, *endcomments): next_dsc = self.tokenizer.next_dsc split = string.split while 1: value = next_dsc() if not value: return if ':' in value: keyword, value = split(value, ':', 1) else: keyword = value if keyword in endcomments: return def read_prolog(self): next = self.tokenizer.next DSC = DSC_COMMENT split = string.split while 1: token, value = next() if token == DSC: if ':' in value: keyword, value = split(value, ':', 1) else: keyword = value if keyword in ('EndProlog', 'BeginSetup'): return keyword if keyword[:14] == "AI5_FileFormat": self.format_version = string.atof(keyword[14:]) elif keyword == 'BeginProcSet': # some ai files exported by corel draw don't have an # EndProcSet comment after a BeginProcSet... self.skip_to_dsc('EndProcSet', 'EndProlog') elif keyword == 'BeginResource': self.skip_to_dsc('EndResource', 'EndProlog') elif keyword == 'Creator': # try to determine whether the file really is an # illustrator file as opposed to some other EPS # file. It seems that Illustrator itself only # accepts EPS files as illustrator files if they # contain "Adobe Illustrator" in their Create # DSC-comment if string.find(value, "Adobe Illustrator") == -1: self.add_message("This is probably not an" " Illustrator file." " Try embedding it as EPS") if token == END: return def Load(self): funclist = self.get_compiled() # binding frequently used functions to local variables speeds up # the process considerably... a = apply t = tuple DSC = DSC_COMMENT MAX = MAX_DATA_TOKEN split = string.split stack = self.stack push = self.stack.append unknown_operator = (None, None) decoder = streamfilter.StringDecode(self.match.string, self.file) self.tokenizer = PSTokenizer(decoder) self.tokenizer.ai_pseudo_comments = 1 self.tokenizer.ai_dsc = 1 next = self.tokenizer.next self.document() value = self.read_prolog() while 1: token, value = next() if token <= MAX: push(value) elif token == DSC: if ':' in value: keyword, value = split(value, ':', 1) else: keyword = value if keyword in ('PageTrailer', 'Trailer'): break elif keyword == 'AI5_BeginPalette': self.skip_to_dsc('AI5_EndPalette', 'EndSetup') elif keyword == "AI8_BeginBrushPattern": self.skip_to_dsc('AI8_EndBrushPattern', 'EndSetup') elif token == END: break elif token == OPERATOR: method, argc = funclist.get(value, unknown_operator) #if method is not None: # name = method.__name__ #else: # name = `method` if method is None: del stack[:] else: try: if argc: args = t(stack[-argc:]) del stack[-argc:] a(method, args) else: method() except: warn_tb(INTERNAL, 'AILoader: error') self.end_all() self.object.load_Completed() for obj in self.guides: self.object.guide_layer.Insert(obj, None) return self.object