class SKLoader(GenericLoader):

    format_name = format_name

    base_style = base_style

    functions = [
        'document', 'layer', 'masterlayer', 'page', ('bezier', 'b'),
        ('rectangle', 'r'), ('ellipse', 'e'), 'group', ('group', 'G'),
        'endgroup', ('endgroup', 'G_'), 'guess_cont'
    ]

    def __init__(self, file, filename, match):
        GenericLoader.__init__(self, file, filename, match)
        if atoi(match.group('minor')) > 2:
            self.add_message(
                _("The file was created by a newer version"
                  " of sK1, there might be inaccuracies."))
        if self.filename:
            self.directory = os.path.split(filename)[0]
        else:
            self.directory = ''
        self.style_dict = {}
        self.id_dict = {}
        self.page_layout = None
        self.pattern = None
        self.gradient = None
        self.arrow1 = None
        self.arrow2 = None
        self.font = None
        self.font_size = 1.0
        self.color_cache = {}

    def __del__(self):
        pass

    def warn(self, level, *args, **kw):
        message = apply(warn, (level, ) + args, kw)
        self.add_message(message)

    def get_func_dict(self):
        func_dict = {}
        for name in self.functions:
            if type(name) == StringType:
                func_dict[name] = getattr(self, name)
            else:
                func_dict[name[1]] = getattr(self, name[0])
        return func_dict

    functions.append('layout')

    def layout(self, format, orientation):
        if type(format) == StringType:
            if format not in papersizes:
                # The format is given by name but it's not one of the
                # standard papersizes. The file may be corrupted.
                self.add_message(
                    _("Unknown paper format '%s', "
                      "using A4 instead") % format)
                format = "A4"
            layout = pagelayout.PageLayout(format, orientation=orientation)
        else:
            w, h = format
            layout = pagelayout.PageLayout(width=w,
                                           height=h,
                                           orientation=orientation)
        self.page_layout = layout

    functions.append('grid')

    def grid(self, geometry, visible=0, color=None, name=None):
        if name is None:
            name = _("Grid")
        self.begin_layer_class(
            GridLayer, (geometry, visible, self.convert_color(color), name))
        self.end_composite()

    def convert_color(self, color_spec):
        try:
            c = self.color_cache.get(color_spec)
            if c:
                return c
            if color_spec[0] == 'RGB' and len(color_spec) == 4:
                c = CreateRGBColor(color_spec[1], color_spec[2], color_spec[3])
            elif color_spec[0] == 'RGB' and len(color_spec) == 5:
                c = CreateRGBAColor(color_spec[1], color_spec[2],
                                    color_spec[3], color_spec[4])
            elif color_spec[0] == 'CMYK' and len(color_spec) == 5:
                c = CreateCMYKColor(color_spec[1], color_spec[2],
                                    color_spec[3], color_spec[4])
            elif color_spec[0] == 'CMYK' and len(color_spec) == 6:
                c = CreateCMYKAColor(color_spec[1], color_spec[2],
                                     color_spec[3], color_spec[4],
                                     color_spec[5])
            elif color_spec[0] == 'SPOT' and len(color_spec) == 10:
                c = CreateSPOTColor(color_spec[3], color_spec[4],
                                    color_spec[5], color_spec[6],
                                    color_spec[7], color_spec[8],
                                    color_spec[9], color_spec[2],
                                    color_spec[1])
            elif color_spec[0] == 'SPOT' and len(color_spec) == 11:
                c = CreateSPOTAColor(color_spec[3], color_spec[4],
                                     color_spec[5], color_spec[6],
                                     color_spec[7], color_spec[8],
                                     color_spec[9], color_spec[10],
                                     color_spec[2], color_spec[1])
            else:
                c = apply(ParseSketchColor, color_spec)
            self.color_cache[color_spec] = c
        except:
            # This should only happen if the color_spec is invalid
            type, value = sys.exc_info()[:2]
            warn(INTERNAL, 'Color allocation failed: %s: %s', type, value)
            c = StandardColors.black
        return c

    functions.append('gl')

    def gl(self, colors):
        c = []
        for pos, color in colors:
            c.append((pos, self.convert_color(color)))
        self.gradient = MultiGradient(c)

    functions.append('pe')

    def pe(self):
        self.pattern = EmptyPattern

    functions.append('ps')

    def ps(self, color):
        self.pattern = SolidPattern(self.convert_color(color))

    functions.append('pgl')

    def pgl(self, dx, dy, border=0):
        if not self.gradient:
            raise SketchLoadError(_("No gradient for gradient pattern"))
        self.pattern = LinearGradient(self.gradient, Point(dx, dy), border)

    functions.append('pgr')

    def pgr(self, dx, dy, border=0):
        if not self.gradient:
            raise SketchLoadError(_("No gradient for gradient pattern"))
        self.pattern = RadialGradient(self.gradient, Point(dx, dy), border)

    functions.append('pgc')

    def pgc(self, cx, cy, dx, dy):
        if not self.gradient:
            raise SketchLoadError(_("No gradient for gradient pattern"))
        self.pattern = ConicalGradient(self.gradient, Point(cx, cy),
                                       Point(dx, dy))

    functions.append('phs')

    def phs(self, color, background, dx, dy, dist, width):
        self.pattern = HatchingPattern(self.convert_color(color),
                                       self.convert_color(background),
                                       Point(dx, dy), dist, width)

    functions.append('pit')

    def pit(self, id, trafo):
        trafo = apply(Trafo, trafo)
        self.pattern = ImageTilePattern(self.id_dict[id], trafo)

    functions.append('fp')

    def fp(self, color=None):
        if color is None:
            self.style.fill_pattern = self.pattern
        else:
            self.style.fill_pattern = SolidPattern(self.convert_color(color))

    functions.append('fe')

    def fe(self):
        self.style.fill_pattern = EmptyPattern

    functions.append('ft')

    def ft(self, bool):
        self.style.fill_transform = bool

    functions.append('lp')

    def lp(self, color=None):
        if color is None:
            self.style.line_pattern = self.pattern
        else:
            self.style.line_pattern = SolidPattern(self.convert_color(color))

    functions.append('le')

    def le(self):
        self.style.line_pattern = EmptyPattern

    functions.append('lw')

    def lw(self, width):
        self.style.line_width = width

    functions.append('lj')

    def lj(self, join):
        self.style.line_join = join

    functions.append('lc')

    def lc(self, cap):
        if not 1 <= cap <= 3:
            self.add_message('line cap corrected from %d to 1' % cap)
            cap = 1
        self.style.line_cap = cap

    functions.append('ld')

    def ld(self, dashes):
        self.style.line_dashes = dashes

    functions.append('la1')

    def la1(self, args=None):
        if args is not None:
            self.style.line_arrow1 = apply(Arrow, args)
        else:
            self.style.line_arrow1 = None

    functions.append('la2')

    def la2(self, args=None):
        if args is not None:
            self.style.line_arrow2 = apply(Arrow, args)
        else:
            self.style.line_arrow2 = None

    functions.append('dstyle')

    def dstyle(self, name=''):
        if not name:
            raise SketchLoadError(_("unnamed style"))
        style = self.style.AsDynamicStyle()
        style.SetName(name)
        self.style_dict[name] = style
        self.style = Style()

    functions.append(('use_style', 'style'))

    def use_style(self, name=''):
        if not name:
            raise SketchLoadError(_("unnamed style"))
        if not self.style.IsEmpty():
            self.prop_stack.load_AddStyle(self.style)
            self.style = Style()
        style = self.style_dict[name]
        self.prop_stack.load_AddStyle(style)

    functions.append('Fn')

    def Fn(self, name):
        self.style.font = GetFont(name)

    functions.append('Fs')

    def Fs(self, size):
        self.style.font_size = size

    functions.append('guide')

    def guide(self, pos, horizontal):
        if horizontal:
            p = Point(0, pos)
        else:
            p = Point(pos, 0)
        self.append_object(GuideLine(p, horizontal))

    functions.append('guidelayer')

    def guidelayer(self, *args, **kw):
        self.begin_layer_class(GuideLayer, args, kw)

    def bezier_load(self, line):
        bezier = self.object
        while 1:
            try:
                bezier.paths[-1].append_from_string(line)
                line = bezier.paths[-1].append_from_file(self.file)
            except:
                warn(INTERNAL, _("Error reading line %s"), ` line `)
                line = self.file.readline()
            if line[:2] == 'bC':
                bezier.paths[-1].load_close()
                line = self.file.readline()
            if line[:2] == 'bn':
                bezier.paths = bezier.paths + (CreatePath(), )
                line = self.file.readline()
            else:
                break
            if line[:2] not in ('bs', 'bc'):
                break
        return line

    functions.append('txt')

    def txt(self,
            thetext,
            trafo,
            halign=text.ALIGN_LEFT,
            valign=text.ALIGN_BASE,
            chargap=1.0,
            wordgap=1.0,
            linegap=1.0):
        thetext = self.unicode_decoder(thetext)
        if len(trafo) == 2:
            trafo = Translation(trafo)
        else:
            trafo = apply(Trafo, trafo)
        object = text.SimpleText(text=thetext,
                                 trafo=trafo,
                                 halign=halign,
                                 valign=valign,
                                 properties=self.get_prop_stack())

        object.properties.SetProperty(align=halign,
                                      valign=valign,
                                      chargap=chargap,
                                      wordgap=wordgap,
                                      linegap=linegap)
        self.append_object(object)

    def unicode_decoder(self, text):
        output = ''
        for word in text.split('\u')[1:]:
            num = int(word, 16)
            if num > 256:
                output += ('\u' + word).decode('raw_unicode_escape')
            else:
                output += chr(int(num)).decode('latin1')
        return output

    functions.append('im')

    def im(self, trafo, id):
        if len(trafo) == 2:
            trafo = Translation(trafo)
        else:
            trafo = apply(Trafo, trafo)
        self.append_object(image.Image(self.id_dict[id], trafo=trafo))

    functions.append('bm')

    def bm(self, id, filename=None):
        if filename is None:
            from streamfilter import Base64Decode, SubFileDecode
            decoder = Base64Decode(SubFileDecode(self.file, '-'))
            data = image.load_image(decoder)
        else:
            data = image.load_image(os.path.join(self.directory, filename))
        self.id_dict[id] = data

    functions.append('eps')

    def eps(self, trafo, filename):
        if len(trafo) == 2:
            trafo = Translation(trafo)
        else:
            trafo = apply(Trafo, trafo)
        if not os.path.isabs(filename):
            if self.directory:
                filename = os.path.join(self.directory, filename)
            else:
                filename = os.path.join(os.getcwd(), filename)
        self.append_object(eps.EpsImage(filename=filename, trafo=trafo))

    functions.append('B')

    def B(self, *args, **kw):
        self.begin_composite(blendgroup.BlendGroup, args, kw)

    functions.append('B_')

    def B_(self):
        self.end_composite()

    functions.append('Bi')

    def Bi(self, *args, **kw):
        self.begin_composite(blendgroup.BlendInterpolation, args, kw)
        self.end_composite()

    group = GenericLoader.begin_group
    endgroup = GenericLoader.end_group

    functions.append('M')

    def M(self, *args, **kw):
        from app.Graphics import maskgroup
        self.begin_composite(maskgroup.MaskGroup, args, kw)

    functions.append('M_')

    def M_(self):
        self.end_composite()

    functions.append('PT')

    def PT(self, *args, **kw):
        self.begin_composite(text.PathText, args, kw)

    functions.append('pt')

    def pt(self, thetext, *args):
        matrix = ()
        model = text.PATHTEXT_ROTATE
        start_pos = 0.0
        if args:
            if type(args[0]) == TupleType:
                matrix = args[0]
                args = args[1:]
            if args:
                model = args[0]
                if len(args) > 1:
                    start_pos = args[1]

        if matrix:
            trafo = apply(Trafo, matrix)
        else:
            trafo = None

        self.append_object(
            text.InternalPathText(thetext,
                                  trafo=trafo,
                                  model=model,
                                  start_pos=start_pos,
                                  properties=self.get_prop_stack()))

    functions.append('PT_')

    def PT_(self):
        self.end_composite()

    functions.append('PC')

    def PC(self, class_name, *args, **kw):
        kw['loading'] = 1
        info = filters.find_object_plugin(class_name)
        if info is not None:
            try:
                theclass = info.Constructor()
                self.begin_composite(theclass, args, kw)
                return
            except SketchError:
                pass
        # constructing the plugin object failed. Use an UnknownPlugin
        # object.
        self.add_message(_("Unknown Plugin: %s") % class_name)
        self.begin_composite(plugobj.UnknownPlugin, (class_name, ) + args, kw)

    functions.append('PC_')

    def PC_(self):
        self.end_composite()

    #
    #	The loader driver
    #

    def Load(self):
        file = self.file
        if type(file) == StringType:
            file = open(file, 'r')
        dict = self.get_func_dict()
        from app import skread
        parse = skread.parse_sk_line2
        readline = file.readline
        bezier_load = self.bezier_load
        num = 1
        line = '#'
        fileinfo = os.stat(self.filename)
        totalsize = fileinfo[6]
        interval = int((totalsize / 200) / 10) + 1
        interval_count = 0
        if __debug__:
            import time
            start_time = time.clock()
        try:
            line = readline()
            parsed = int(file.tell() * 100 / totalsize)
            app.updateInfo(inf2='%u' % parsed + '% of file is parsed...',
                           inf3=parsed)
            while line:
                num = num + 1
                if line[0] == 'b' and line[1] in 'sc':
                    line = bezier_load(line)
                    continue
                #parse(line, dict)
                funcname, args, kwargs = parse(line)
                if funcname is not None:
                    function = dict.get(funcname)
                    if function is not None:
                        try:
                            apply(function, args, kwargs)
                        except TypeError:
                            tb = sys.exc_info()[2]
                            try:
                                if tb.tb_next is None:
                                    # the exception was raised by apply
                                    # and not within the function. Try to
                                    # invoke the function with fewer
                                    # arguments
                                    if call_function(function, args, kwargs):
                                        message = _("Omitted some arguments "
                                                    "for function %s")
                                    else:
                                        message = _("Cannot call function %s")
                                    self.add_message(message %
                                                     function.__name__)

                                else:
                                    raise
                            finally:
                                del tb
                    else:
                        self.add_message(_("Unknown function %s") % funcname)

                line = readline()
                interval_count += 1
                if interval_count > interval:
                    interval_count = 0
                    parsed = int(file.tell() * 100 / totalsize)
                    app.updateInfo(inf2='%u' % parsed +
                                   '% of file is parsed...',
                                   inf3=parsed)

        except (SketchLoadError, SyntaxError), value:
            # a loader specific error occurred
            warn_tb(INTERNAL, 'error in line %d', num)
            if load._dont_handle_exceptions:
                raise
            else:
                raise SketchLoadError('%d:%s' % (num, value))
        except:
class GenericLoader(LoaderWithComposites):

    format_name = ''

    base_style = None

    def __init__(self, file, filename, match):
        LoaderWithComposites.__init__(self)
        self.file = file
        self.filename = filename
        self.match = match
        self.style = Style()
        if self.base_style is not None:
            self.prop_stack = PropertyStack(base=self.base_style.Duplicate())
        else:
            self.prop_stack = PropertyStack()
        self.messages = {}

    def get_prop_stack(self):
        stack = self.prop_stack
        if not self.style.IsEmpty():
            stack.load_AddStyle(self.style)
        stack.condense()
        if self.base_style is not None:
            self.prop_stack = PropertyStack(base=self.base_style.Duplicate())
        else:
            self.prop_stack = PropertyStack()
        self.style = Style()
        return stack

    def set_prop_stack(self, stack):
        self.prop_stack = stack

    def document(self, *args, **kw):
        self.begin_composite(doc_class, args, kw)

    def layer(self, *args, **kw):
        self.begin_layer_class(layer.Layer, args, kw)

    def masterlayer(self, *args, **kw):
        kw['is_MasterLayer'] = 1
        self.begin_layer_class(layer.Layer, args, kw)

    def page(self, *args, **kw):
        kw['is_Page'] = 1
        self.begin_layer_class(layer.Layer, args, kw)

    def end_layer(self):
        self.end_composite()

    def begin_layer_class(self, layer_class, args, kw=None):
        if issubclass(self.composite_class, layer.Layer):
            self.end_composite()
        if issubclass(self.composite_class, doc_class):
            self.begin_composite(layer_class, args, kw)
        else:
            raise SketchLoadError('self.composite_class is %s, not a document',
                                  self.composite_class)

    def bezier(self, paths=None):
        self.append_object(
            PolyBezier(paths=paths, properties=self.get_prop_stack()))

    def rectangle(self, m11, m21, m12, m22, v1, v2, radius1=0, radius2=0):
        trafo = Trafo(m11, m21, m12, m22, v1, v2)
        self.append_object(
            Rectangle(trafo,
                      radius1=radius1,
                      radius2=radius2,
                      properties=self.get_prop_stack()))

    def ellipse(self,
                m11,
                m21,
                m12,
                m22,
                v1,
                v2,
                start_angle=0.0,
                end_angle=0.0,
                arc_type=ArcPieSlice):
        self.append_object(
            Ellipse(Trafo(m11, m21, m12, m22, v1, v2),
                    start_angle,
                    end_angle,
                    arc_type,
                    properties=self.get_prop_stack()))

    def simple_text(self,
                    str,
                    trafo=None,
                    valign=text.ALIGN_BASE,
                    halign=text.ALIGN_LEFT):
        if type(trafo) == TupleType:
            if len(trafo) == 2:
                trafo = apply(Translation, trafo)
            else:
                raise TypeError, "trafo must be a Trafo-object or a 2-tuple"
        self.append_object(
            text.SimpleText(text=str,
                            trafo=trafo,
                            valign=valign,
                            halign=halign,
                            properties=self.get_prop_stack()))

    def image(self, image, trafo):
        if type(trafo) == TupleType:
            if len(trafo) == 2:
                trafo = apply(Translation, trafo)
            else:
                raise TypeError, "trafo must be a Trafo-object or a 2-tuple"
        image = ImageData(image)
        self.append_object(Image(image, trafo=trafo))

    def begin_group(self, *args, **kw):
        self.begin_composite(group.Group, args, kw)

    def end_group(self):
        self.end_composite()

    def guess_cont(self):
        self.guess_continuity = 1

    def end_composite(self):
        isdoc = self.composite_class is doc_class
        LoaderWithComposites.end_composite(self)
        if isdoc:
            self.add_meta(self.object)

    def add_meta(self, doc):
        doc.meta.fullpathname = self.filename
        dir, name = os.path.split(self.filename)
        doc.meta.directory = dir
        doc.meta.filename = name
        doc.meta.native_format = 0
        doc.meta.format_name = self.format_name

    def add_message(self, message):
        pdebug(('load', 'echo_messages'), message)
        self.messages[message] = self.messages.get(message, 0) + 1

    def Messages(self):
        messages = self.messages.items()
        list = []
        for message, count in messages:
            if count > 1:
                list.append(_("%(message)s (%(count)d times)") % locals())
            else:
                list.append(message)
        list.sort()
        return string.join(list, '\n')