class SVGHandler(saxlib.HandlerBase): dispatch_start = { 'svg': 'initsvg', 'g': 'begin_group', 'circle': 'circle', 'ellipse': 'ellipse', 'rect': 'rect', 'polyline': 'polyline', 'polygon': 'polygon', 'path': 'begin_path', 'text': 'begin_text', 'image': 'image', 'data': 'data', 'use': 'use', 'defs': 'begin_defs', } dispatch_end = { 'g': 'end_group', 'path': 'end_path', 'defs': 'end_defs', 'text': 'end_text' } def __init__(self, loader): self.loader = loader self.trafo = self.basetrafo = Trafo() self.state_stack = () self.style = loader.style.Copy() self.style.line_pattern = EmptyPattern self.style.fill_pattern = SolidPattern(StandardColors.black) self.current_text = "" self.style.font = GetFont("Times-Roman") self.style.font_size = 12 self.halign = text.ALIGN_LEFT self.named_objects = {} self.in_defs = 0 self.paths = None self.path = None self.depth = 0 self.indent = ' ' def _print(self, *args): return if args: print self.depth * self.indent + args[0], for s in args[1:]: print s, print def parse_transform(self, trafo_string): trafo = self.trafo #print trafo trafo_string = as_latin1(trafo_string) while trafo_string: #print trafo_string match = rx_trafo.match(trafo_string) if match: function = match.group(1) args = string.translate(match.group(2), commatospace) args = map(float, split(args)) trafo_string = trafo_string[match.end(0):] if function == 'matrix': trafo = trafo(apply(Trafo, tuple(args))) elif function == 'scale': trafo = trafo(Scale(args[0])) elif function == 'translate': dx, dy = args trafo = trafo(Translation(dx, dy)) elif function == 'rotate': trafo = trafo(Rotation(args[0] * degrees)) elif function == 'skewX': trafo = trafo(Trafo(1, 0, tan(args[0] * degrees), 1, 0, 0)) elif function == 'skewY': trafo = trafo(Trafo(1, tan(args[0] * degrees), 0, 1, 0, 0)) else: trafo_string = '' #print trafo self.trafo = trafo def startElement(self, name, attrs): self._print('(', name) for key, value in attrs.items(): self._print(' -', key, ` value `) self.depth = self.depth + 1 self.push_state() if attrs.has_key('transform'): self.parse_transform(attrs['transform']) self._print("applied transormation", self.trafo) method = self.dispatch_start.get(name) if method is not None: getattr(self, method)(attrs) def endElement(self, name): self.depth = self.depth - 1 self._print(')', name) method = self.dispatch_end.get(name) if method is not None: getattr(self, method)() self.pop_state() def characters(self, ch, start, length): self.current_text = self.current_text + as_latin1(ch) def error(self, exception): print 'error', exception def fatalError(self, exception): print 'fatalError', exception def warning(self, exception): print 'warning', exception def initsvg(self, attrs): width = self.user_length(attrs.get('width', '100%')) height = self.user_length(attrs.get('height', '100%')) self._print('initsvg', width, height) self.trafo = Trafo(1, 0, 0, -1, 0, height) self.basetrafo = self.trafo # evaluate viewBox # FIXME: Handle preserveAspectRatio as well viewbox = attrs.get("viewBox", "") if viewbox: vx, vy, vwidth, vheight = map(float, split(viewbox)) t = Scale(width / vwidth, height / vheight) t = t(Translation(-vx, -vy)) self.trafo = self.trafo(t) self._print("basetrafo", self.basetrafo) def parse_style(self, style): parts = filter(None, map(strip, split(style, ';'))) for part in parts: key, val = map(strip, split(part, ':', 1)) self._print('style', key, val) # only try to parse the value if it's not empty if val: self.try_add_style(key, val) else: # FIXME: we should probably print a message or something pass def try_add_style(self, key, val): if key == 'fill': if val == 'none': self.style.fill_pattern = EmptyPattern else: color = csscolor(val) self._print('fill', color) self.style.fill_pattern = SolidPattern(color) elif key == 'stroke': if val == 'none': self.style.line_pattern = EmptyPattern else: color = csscolor(val) self._print('stroke', color) self.style.line_pattern = SolidPattern(color) elif key == 'stroke-width': width = self.user_length(val) # Multiply the width with a value taken from the # transformation matrix because so far transforming an # object in Sketch does not affect the stroke width in any # way. Thus we have to do that explicitly here. # FIXME: using m11 is not really the best approach but in # many cases better than using the width as is. width = self.trafo.m11 * width self._print('width', width) self.style.line_width = width elif key == 'stroke-linejoin': self.style.line_join = join[val] elif key == 'stroke-linecap': self.style.line_cap = cap[val] elif key == 'font-family': try: # convert val to 8bit string. self.style.font = GetFont(str(val)) except UnicodeError: # If it's not ASCII we probably won't have the font, so # use the default one. # FIXME: Give a warning pass elif key == 'font-size': self.style.font_size = self.user_length(val) ####self.style.font_size = float(val) elif key == 'text-anchor': if val == 'start': self.halign = text.ALIGN_LEFT elif val == 'middle': self.halign = text.ALIGN_CENTER elif val == 'end': self.halign = text.ALIGN_RIGHT def set_loader_style(self, allow_font=0): # Copy self.style to loader. # If allow_font is false (the default) do not copy the font # properties. property_types = properties.property_types style = self.style.Copy() if not allow_font: for name in style.__dict__.keys(): if property_types.get(name) == properties.FontProperty: delattr(style, name) self.loader.style = style def push_state(self): self.state_stack = self.style, self.trafo, self.state_stack self.style = self.style.Copy() def pop_state(self): self.style, self.trafo, self.state_stack = self.state_stack def user_length(self, str): # interpret string as a length and return the appropriate value # user coordinates str = strip(str) factor = factors.get(str[-2:]) if factor is not None: str = str[:-2] elif str[-1] == '%': # FIXME: this case depends on the width/height attrs of the # SVG element str = str[:-1] factor = 1.0 else: factor = 1.0 return float(str) * factor def user_point(self, x, y): # Return the point described by the SVG coordinates x and y as # an SKPoint object in user coordinates. x and y are expected to # be strings. x = strip(x) y = strip(y) # extract the units from the coordinate values if any and # determine the appropriate factor to convert those units to # user space units. xunit = x[-2:] factor = factors.get(xunit) if factor is not None: x = x[:-2] elif x[-1] == '%': # XXX this is wrong x = x[:-1] xunit = '%' factor = 1 else: xunit = '' factor = 1.0 x = float(x) * factor yunit = y[-2:] factor = factors.get(yunit) if factor is not None: y = y[:-2] elif y[-1] == '%': y = y[:-1] yunit = '%' factor = 1.0 else: yunit = '' factor = 1.0 y = float(y) * factor return Point(x, y) def point(self, x, y, relative=0): # Return the point described by the SVG coordinates x and y as # an SKPoint object in absolute, i.e. document coordinates. x # and y are expected to be strings. If relative is true, they're # relative coordinates. x, y = self.user_point(x, y) if relative: p = self.trafo.DTransform(x, y) else: p = self.trafo(x, y) return p def circle(self, attrs): if self.in_defs: id = attrs.get('id', '') if id: self.named_objects[id] = ('object', 'circle', attrs) return if attrs.has_key('cx'): x = attrs['cx'] else: x = '0' if attrs.has_key('cy'): y = attrs['cy'] else: y = '0' x, y = self.point(x, y) r = self.point(attrs['r'], '0', relative=1).x t = Trafo(r, 0, 0, r, x, y) self._print('circle', t) style = attrs.get('style', '') if style: self.parse_style(style) self.set_loader_style() apply(self.loader.ellipse, t.coeff()) def ellipse(self, attrs): if self.in_defs: id = attrs.get('id', '') if id: self.named_objects[id] = ('object', 'ellipse', attrs) return if attrs.has_key('cx'): x = attrs['cx'] else: x = '0' if attrs.has_key('cy'): y = attrs['cy'] else: y = '0' x, y = self.point(x, y) rx, ry = self.point(attrs['rx'], attrs['ry'], relative=1) t = Trafo(rx, 0, 0, ry, x, y) self._print('ellipse', t) style = attrs.get('style', '') if style: self.parse_style(style) self.set_loader_style() apply(self.loader.ellipse, t.coeff()) def rect(self, attrs): #print 'rect', attrs.map if self.in_defs: id = attrs.get('id', '') if id: self.named_objects[id] = ('object', 'rect', attrs) return if attrs.has_key('x'): x = attrs['x'] else: x = '0' if attrs.has_key('y'): y = attrs['y'] else: y = '0' x, y = self.point(x, y) wx, wy = self.point(attrs['width'], "0", relative=1) hx, hy = self.point("0", attrs['height'], relative=1) t = Trafo(wx, wy, hx, hy, x, y) self._print('rect', t) style = attrs.get('style', '') if style: self.parse_style(style) self.set_loader_style() apply(self.loader.rectangle, t.coeff()) 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 polygon(self, attrs): if self.in_defs: id = attrs.get('id', '') if id: self.named_objects[id] = ('object', 'polygon', 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])) path.AppendLine(path.Node(0)) path.ClosePath() style = attrs.get('style', '') if style: self.parse_style(style) self.set_loader_style() self.loader.bezier(paths=(path, )) 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 begin_path(self, attrs): if self.in_defs: id = attrs.get('id', '') if id: self.named_objects[id] = ('object', 'path', attrs) return self.paths = [] self.path = None self.parse_path(attrs['d']) style = attrs.get('style', '') if style: self.parse_style(style) self.set_loader_style() def end_path(self): if self.in_defs: return self.loader.bezier(paths=tuple(self.paths)) self.paths = None def image(self, attrs): if self.in_defs: id = attrs.get('id', '') if id: self.named_objects[id] = ('object', 'image', attrs) return href = as_latin1(attrs['xlink:href']) image = load_image(os.path.join(self.loader.directory, href)).image if attrs.has_key('x'): x = attrs['x'] else: x = '0' if attrs.has_key('y'): y = attrs['y'] else: y = '0' x, y = self.user_point(x, y) width = self.user_length(attrs['width']) scalex = width / image.size[0] height = self.user_length(attrs['height']) scaley = -height / image.size[1] style = attrs.get('style', '') if style: self.parse_style(style) self.set_loader_style() t = self.trafo(Trafo(scalex, 0, 0, scaley, x, y + height)) self._print('image', t) self.loader.image(image, t) def begin_text(self, attrs): if self.in_defs: id = attrs.get('id', '') if id: self.named_objects[id] = ('object', 'text', attrs) return # parse the presentation attributes if any. # FIXME: this has to be implemented for the other elements that # can have presentation attributes as well. for key, value in attrs.items(): self.try_add_style(key, value) if attrs.has_key('x'): x = attrs['x'] else: x = '0' if attrs.has_key('y'): y = attrs['y'] else: y = '0' x, y = self.user_point(x, y) self.text_trafo = self.trafo(Trafo(1, 0, 0, -1, x, y)) self._print('text', self.text_trafo) style = attrs.get('style', '') if style: self.parse_style(style) self.set_loader_style(allow_font=1) self.current_text = '' def end_text(self): self.loader.simple_text(strip(self.current_text), self.text_trafo, halign=self.halign) def data(self, attrs): pass def begin_group(self, attrs): style = attrs.get('style', '') if style: self.parse_style(style) self.loader.begin_group() def end_group(self): try: self.loader.end_group() except EmptyCompositeError: pass def use(self, attrs): #print 'use', attrs.map if attrs.has_key('xlink:href'): name = attrs['xlink:href'] else: name = attrs.get('href', '<none>') if name: data = self.named_objects.get(name[1:]) if data[0] == 'object': if attrs.has_key('style'): self.parse_style(attrs['style']) self.startElement(data[1], data[2]) self.endElement(data[1]) def begin_defs(self, attrs): self.in_defs = 1 def end_defs(self): self.in_defs = 0
class XFigLoader(SimplifiedLoader): format_name = format_name functions = [('define_color', 0), ('read_ellipse', 1), ('read_polyline', 2), ('read_spline', 3), ('read_text', 4), ('read_arc', 5), ('begin_compound', 6), ('end_compound', -6)] def __init__(self, file, filename, match): SimplifiedLoader.__init__(self, file, filename, match) self.layout = None self.format_version = atof(match.group('version')) self.trafo = Trafo(1.0, 0.0, 0.0, -1.0, 0.0, 800) self.colors = std_colors + [StandardColors.black] * 512 self.depths = {} # map object ids to depth self.guess_cont() def readline(self): line = SimplifiedLoader.readline(self) while line[:1] == '#': line = SimplifiedLoader.readline(self) return line def get_compiled(self): funclist = {} for name, rx in self.functions: funclist[rx] = getattr(self, name) return funclist def set_depth(self, depth): self.depths[id(self.object)] = -depth def define_color(self, line): idx, color = split(line, None, 1) self.colors[atoi(idx)] = XRGBColor(color) def get_pattern(self, color, style=None): if style == -1: return EmptyPattern rgb = self.colors[color] if style is not None: if color in (BLACK, DEFAULT_COLOR): if style > 0 and style <= 20: rgb = Blend(self.colors[WHITE], rgb, (20 - style) / 20.0) elif style == 0: rgb = self.colors[WHITE] else: if style >= 0 and style < 20: rgb = Blend(self.colors[BLACK], rgb, (20 - style) / 20.0) elif style > 20 and style <= 40: rgb = Blend(self.colors[WHITE], rgb, (style - 20) / 20.0) return SolidPattern(rgb) def line(self, color, width, join, cap, style=0, style_val=0): if width: val = style_val / width width = width * 72.0 / 80.0 pattern = self.get_pattern(color) dashes = () if style == 1: # dashed dashes = (val, val) elif style == 2: # dotted dashes = (1 / width, val) elif style == 3: # dash-dot dashes = (val, 0.5 * val, 1 / width, 0.5 * val) elif style == 4: # dash-dot-dot dashes = (val, 0.45 * val, 1 / width, 0.333 * val, 1 / width, 0.45 * val) elif style == 5: # dash-dot-dot-dot dashes = (val, 0.4 * val, 1 / width, 0.333 * val, 1 / width, 0.333 * val, 1 / width, 0.4 * val) try: self.set_properties(line_pattern=pattern, line_width=width, line_join=xfig_join[join], line_cap=xfig_cap[cap], line_dashes=dashes) except: raise SketchLoadError("can't assign line style: %s:%s" % sys.exc_info()[:2]) else: self.empty_line() def fill(self, color, style): pattern = self.get_pattern(color, style) try: self.set_properties(fill_pattern=pattern) except: raise SketchLoadError("can't assign fill style: %s:%s" % sys.exc_info()[:2]) def font(self, font, size, flags): if flags & 4: # A PostScript font name = psfonts[font] else: # A TeX font. map to psfont name = texfonts[font] self.add_message( _("PostScript font `%(ps)s' substituted for " "TeX-font `%(tex)s'") % { 'ps': name, 'tex': tex_font_names[font] }) self.set_properties(font=GetFont(name), font_size=size) def read_tokens(self, num): # read NUM tokens from the input file. return an empty list if # eof met before num items are read readline = self.readline tokenize = skread.tokenize_line tokens = [] while len(tokens) < num: line = readline() if not line: return [] tokens = tokens + tokenize(line) return tokens def read_arc(self, line): readline = self.readline tokenize = skread.tokenize_line args = tokenize(line) if len(args) != 21: raise SketchLoadError('Invalid Arc specification') sub_type, line_style, thickness, pen_color, fill_color, depth, \ pen_style, area_fill, style, cap, direction, \ forward_arrow, backward_arrow, \ cx, cy, x1, y1, x2, y2, x3, y3 = args self.fill(fill_color, area_fill) self.line(pen_color, thickness, const.JoinMiter, cap, line_style, style) if forward_arrow: readline() # XXX: implement this if backward_arrow: readline() # XXX: implement this trafo = self.trafo center = trafo(cx, cy) start = trafo(x1, y1) end = trafo(x3, y3) radius = abs(start - center) start_angle = atan2(start.y - center.y, start.x - center.x) end_angle = atan2(end.y - center.y, end.x - center.x) if direction == 0: start_angle, end_angle = end_angle, start_angle if sub_type == 1: sub_type = const.ArcArc else: sub_type = const.ArcPieSlice self.ellipse(radius, 0, 0, radius, center.x, center.y, start_angle, end_angle, sub_type) self.set_depth(depth) def read_ellipse(self, line): readline = self.readline tokenize = skread.tokenize_line args = tokenize(line) if len(args) != 19: raise SketchLoadError('Invalid Ellipse specification') sub_type, line_style, thickness, pen_color, fill_color, depth, \ pen_style, area_fill, style, direction, angle, \ cx, cy, rx, ry, sx, sy, ex, ey = args self.fill(fill_color, area_fill) self.line(pen_color, thickness, const.JoinMiter, const.CapButt, line_style, style) center = self.trafo(cx, cy) radius = self.trafo.DTransform(rx, ry) trafo = Trafo(radius.x, 0, 0, radius.y) trafo = Rotation(angle)(trafo) trafo = Translation(center)(trafo) apply(self.ellipse, trafo.coeff()) 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 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_text(self, line): args = tokenize(line, 12) # don't tokenize the text itself if len(args) != 13: # including the unparsed rest of the line raise SketchLoadError('Invalid text specification') sub_type, color, depth, pen_style, font, size, angle, flags, \ height, length, x, y, rest = args self.fill(color, None) self.font(font, size * 0.9, flags) if len(rest) > 2: #at least a space and a newline # read the actual text. This implementation may fail in # certain cases! string = rest[1:] while string[-5:] != '\\001\n': line = self.readline() if not line: raise SketchLoadError('Premature end of string') string = string + line globals = {'__builtins__': {}} try: # using eval here might be a security hole! string = eval('"""' + string[:-5] + '"""', globals) except: string = eval("'''" + string[:-5] + "'''", globals) else: raise SketchLoadError('Invalid text string') trafo = Translation(self.trafo(x, y))(Rotation(angle)) self.simple_text(string, trafo=trafo, halign=align[sub_type]) self.set_depth(depth) def begin_compound(self, line): self.begin_group() def end_compound(self, line): try: self.end_group() except EmptyCompositeError: pass def end_composite(self): # sort composite_items by their depth items = self.composite_items depths = map(self.depths.get, map(id, items), [-10000] * len(items)) depths = map(None, depths, range(len(items)), items) depths.sort() self.composite_items = map(getitem, depths, [2] * len(items)) SimplifiedLoader.end_composite(self) def read_header(self): format = orientation = None if self.format_version >= 3.0: line = strip(self.readline()) if line: # portrait/landscape if lower(line) == 'landscape': orientation = pagelayout.Landscape else: orientation = pagelayout.Portrait else: raise SketchLoadError('No format specification') line = strip(self.readline()) if line: # centering line = lower(line) if line == 'center' or line == 'flush left': line = lower(strip(self.readline())) if not line: raise SketchLoadError( 'No Center/Flushleft or Units specification') if line == 'metric': # ignore for now pass if self.format_version >= 3.2: self.readline() # papersize self.readline() # magnification self.readline() # pages self.readline() # transparent color line = strip(self.readline()) if line: try: ppi, coord = map(atoi, split(line)) except: raise SketchLoadError('Invalid Resolution specification') self.trafo = self.trafo(Scale(72.0 / ppi)) def Load(self): file = self.file funclist = self.get_compiled() # binding frequently used functions to local variables speeds up # the process considerably... readline = file.readline tokenize = skread.tokenize_line self.document() self.layer(_("Layer 1")) try: self.read_header() line = self.readline() while line: tokens = tokenize(line, 1) if len(tokens) > 1: function, rest = tokens else: function = tokens[0] rest = '' if type(function) == type(0): function = funclist.get(function) if function: function(rest) line = self.readline() except SketchLoadError, value: warn_tb(INTERNAL) raise SketchLoadError('%d:%s' % (self.lineno, str(value))), None,\ sys.exc_traceback except: