Exemplo n.º 1
0
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)
Exemplo n.º 2
0
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
Exemplo n.º 3
0
    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
Exemplo n.º 4
0
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
Exemplo n.º 5
0
    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