UniConvertor-1.1.4/0000755000076400007640000000000011211455344012644 5ustar igorigorUniConvertor-1.1.4/src/0000755000076400007640000000000011211455344013433 5ustar igorigorUniConvertor-1.1.4/src/app/0000755000076400007640000000000011211455344014213 5ustar igorigorUniConvertor-1.1.4/src/app/io/0000755000076400007640000000000011211455344014622 5ustar igorigorUniConvertor-1.1.4/src/app/io/load.py0000775000076400007640000002374011060500603016115 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2003-2006 by Igor E. Novikov # Copyright (C) 1997, 1998, 1999, 2000, 2001, 2003 by Bernhard Herzog # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. # This file contains various classes for reading a drawing from (ASCII) # files. # # Classes: # # LoaderWithComposites # GenericLoader(LoaderWithComposites) # # Two classes that provide common functions for the format # specific classes. # # Functions # # load_drawing(filename) # # Determines the type of the file (by reading a few lines) and # invokes the appropriate import filter. Return the newly created # document. # from types import StringType, TupleType import os, string import time from app import config from app.plugins import plugins from app.utils import locale_utils from app.events.warn import warn, INTERNAL, pdebug from app.conf.const import ArcPieSlice from app.Graphics import document, layer, group, text from app import _, PolyBezier, Rectangle, Ellipse, Trafo, Translation, \ Style, PropertyStack, EmptyPattern, Document, SketchLoadError, \ ImageData, Image doc_class = Document class EmptyCompositeError(SketchLoadError): pass # The loaders usually intercept exceptions raised while reading and # raise a SketchLoadError instead. Setting the following variable to # true will prohibit this. (useful only for debugging a loader) # # XXX this is better done by using warn_tb(INTERNAL,...) before # reraising the exception as a SketchLoadError. _dont_handle_exceptions = 0 class LoaderWithComposites: guess_continuity = 0 def __init__(self): self.composite_stack = None self.composite_items = [] self.composite_class = None self.composite_args = None self.object = None def __push(self): self.composite_stack = (self.composite_class, self.composite_args, self.composite_items, self.composite_stack) def __pop(self): (self.composite_class, self.composite_args, self.composite_items, self.composite_stack) = self.composite_stack def begin_composite(self, composite_class, args = (), kw = None): self.__push() self.composite_class = composite_class self.composite_args = (args, kw) self.composite_items = [] self.object = None def end_composite(self): if self.composite_class: args, kw = self.composite_args if not kw: kw = {} composite = apply(self.composite_class, args, kw) # We treat Plugins specially here and in check_object so # that we can be a bit more lenient with plugin objects. # They should not be empty (after all they'd be invisible # then) but they might. If they're empty they're simply # ignored if self.composite_items or composite.can_be_empty \ or composite.is_Plugin: append = composite.load_AppendObject for item in self.composite_items: if self.check_object(item): append(item) composite.load_Done() self.__pop() self.append_object(composite) else: self.__pop() raise EmptyCompositeError else: raise SketchLoadError('no composite to end') def end_all(self): while self.composite_stack: self.end_composite() def check_object(self, object): # Return true if object is OK. Currently this just checks the # following things: # # - whether a bezier object has at least one path and all paths # have at least length 1. # - for bezier objects, guess the continuity if # self.guess_continuity is true. # - Whether a plugin object is not empty result = 1 if object.is_Bezier: paths = object.Paths() if len(paths) >= 1: for path in paths: if path.len == 0: result = 0 break else: result = 0 if result and self.guess_continuity: object.guess_continuity() elif object.is_Plugin: result = len(object.GetObjects()) return result def append_object(self, object): self.composite_items.append(object) self.object = object def pop_last(self): # remove the last object in self.composite_items and return it object = None if self.composite_items: object = self.composite_items[-1] del self.composite_items[-1] if self.composite_items: self.object = self.composite_items[-1] else: self.object = None return object 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 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') class SimplifiedLoader(GenericLoader): def __init__(self, file, filename, match): GenericLoader.__init__(self, file, filename, match) self.lineno = 1 # first line has been read def readline(self): line = self.file.readline() self.lineno = self.lineno + 1 return line def set_properties(self, **kw): style = self.style for key, value in kw.items(): setattr(style, key, value) def empty_line(self): self.style.line_pattern = EmptyPattern def empty_fill(self): self.style.fill_pattern = EmptyPattern do_profile = 0 def load_drawing_from_file(file, filename = '', doc_class = None): # Note: the doc_class argument is only here for plugin interface # compatibility with 0.7 (especiall e.g. gziploader) line = file.readline() # XXX ugly hack for riff-based files, e.g. Corel's CMX. The length # might contain newline characters. if line[:4] == 'RIFF' and len(line) < 12: line = line + file.read(12 - len(line)) #print line for info in plugins.import_plugins: match = info.rx_magic.match(line) if match: loader = info(file, filename, match) try: if do_profile: import profile warn(INTERNAL, 'profiling...') prof = profile.Profile() prof.runctx('loader.Load()', globals(), locals()) prof.dump_stats(os.path.join(info.dir, info.module_name + '.prof')) warn(INTERNAL, 'profiling... (done)') doc = loader.object else: #t = time.clock() doc = loader.Load() #print 'load in', time.clock() - t, 'sec.' messages = loader.Messages() if messages: doc.meta.load_messages = messages return doc finally: info.UnloadPlugin() else: raise SketchLoadError(_("unrecognised file type")) def load_drawing(filename): if type(filename) == StringType: name=locale_utils.utf_to_locale(filename) #name=locale_utils.strip_line(name) try: file = open(name, 'rb') except IOError, value: message = value.strerror raise SketchLoadError(_("Cannot open %(filename)s:\n%(message)s") % locals()) else: # assume a file object. This does not happen at the moment and # SKLoder requires the filename for external objects. file = filename filename = '' return load_drawing_from_file(file, filename) UniConvertor-1.1.4/src/app/io/loadres.py0000775000076400007640000000151511060500603016623 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2003-2006 by Igor E. Novikov # Copyright (C) 1997, 1998 by Bernhard Herzog # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. # # Read a Sketch resource file (dashes, arrows...) # from app.events.skexceptions import SketchError from app.events.warn import warn_tb, USER def read_resource_file(filename, magic, errmsg, functions): file = open(filename, 'r') try: line = file.readline() if line[:len(magic)] != magic: raise SketchError(errmsg % filename) from app.skread import parse_sk_line linenr = 1 while 1: line = file.readline() if not line: break linenr = linenr + 1 try: parse_sk_line(line, functions) except: warn_tb(USER, '%s:%d', filename, linenr) finally: file.close() UniConvertor-1.1.4/src/app/io/__init__.py0000775000076400007640000000000011060500603016715 0ustar igorigorUniConvertor-1.1.4/src/app/Lib/0000755000076400007640000000000011211455344014721 5ustar igorigorUniConvertor-1.1.4/src/app/Lib/dscparser.py0000775000076400007640000001614311060500603017262 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2003-2006 by Igor E. Novikov # Copyright (C) 1998, 2000 by Bernhard Herzog # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. # A simple parser for PostScript files that conform to the Document # Structuring Conventions (DSC). # # In its current form this is mainly intended for parsing EPS files and # extract the information necessary for Sketch (BoundingBox and resource # dependencies) # import re, string from string import split, strip, atof import streamfilter try: from app.events.warn import warn, INTERNAL except ImportError: def warn(*args): pass INTERNAL = None # match a line containing a DSC-comment. rx_dsccomment = re.compile('^%%([a-zA-Z+]+):?') # match the beginning of an EPS file. rx_eps_magic = re.compile('^%!.*EPSF') endcommentchars = string.maketrans('','')[33:127] ATEND = '(atend)' class DSCError(Exception): pass # # Class EpsInfo # # The instance variables of this class are the key/value pairs extracted # from the header comments of an EPS file. # # BoundingBox: # # The bounding box of the document as a 4-tuple of floats. The DSC # say that the BoundingBox should be given in UINTs but since some # programs (incorrectly) use floats here we also use float here. # # DocumentNeededResources: # # A dictionary describing the resources needed by the document. # The information is stored in the *keys* of the dictionary. # # A key has the form (TYPE, VALUE) where TYPE is a string giving # the resource type (such as 'font') and value is a string # describing the resource (such as 'Times-Roman') # # DocumentSuppliedResources: # # The resources supplied by the document in the same format as # DocumentNeededResources. # # atend: # # True, if any comment in the header had a value of `(atend)'. # (Used internally by the parsing functions) class EpsInfo: def __init__(self): self.DocumentSuppliedResources = {} self.DocumentNeededResources = {} self.BoundingBox = None self.atend = 0 def NeedResources(self, type, resources): for res in resources: self.DocumentNeededResources[(type, res)] = 1 def SupplyResources(self, type, resources): for res in resources: self.DocumentSuppliedResources[(type, res)] = 1 def print_info(self): # print the contents of self in a readable manner. (for debugging) print 'BoundingBox:\t%s' % `self.BoundingBox` print 'DocumentNeededResources: [', for res in self.DocumentNeededResources.keys(): print res, print ']' print 'DocumentSuppliedResources: [', for res in self.DocumentSuppliedResources.keys(): print res, print ']' for key, value in self.__dict__.items(): if key not in ('BoundingBox', 'DocumentNeededResources', 'DocumentSuppliedResources', 'atend'): print '%s\t%s' % (key, value) def IsEpsFileStart(data): # return true if data might be the beginning of an Encapsulated # PostScript file. return rx_eps_magic.match(data) def parse_header(file, info): # Parse the header section of FILE and store the information found # in the INFO object which is assumed to be an instance of EpsInfo. # # This works for the %%Trailer section as well so that parsing the # beginning (until %%EndComments) and end (from %%Trailer) if # necessary with the same INFO object should get all information # available. line = file.readline() last_key = '' while line: match = rx_dsccomment.match(line) if match: key = match.group(1) value = strip(line[match.end(0):]) if key == 'EndComments' or key == 'EOF': break if key == '+': key = last_key else: last_key = '' if key == 'BoundingBox': if value != ATEND: # the bounding box should be given in UINTs # but may also (incorrectly) be float. info.BoundingBox = tuple(map(atof, split(value))) else: info.atend = 1 elif key == 'DocumentNeededResources': if value != ATEND: if value: [type, value] = split(value, None, 1) if type == 'font': info.NeedResources(type, split(value)) else: # XXX: might occasionally be interesting for the # user warn(INTERNAL, 'needed resource %s %s ignored', type, value) else: info.atend = 1 elif key == 'DocumentNeededFonts': if value != ATEND: info.NeedResources('font', split(value)) else: info.atend = 1 elif key == 'DocumentSuppliedResources': if value != ATEND: if value: [type, value] = split(value, None, 1) if type == 'font': info.NeedResources(type, split(value)) else: # XXX: might occasionally be interesting for the # user warn(INTERNAL, 'supplied resource %s %s ignored', type, value) else: info.atend = 1 else: setattr(info, key, value) # last_key = key else: # the header comments end at a line not beginning with %X, # where X is a printable character not in SPACE, TAB, NL # XXX: It is probably wrong to do this in the %%Trailer if line[0] != '%': break if len(line) == 1 or line[1] not in endcommentchars: break line = file.readline() def skip_to_comment(file, comment): # Read lines from FILE until a line with a DSC comment COMMENT is # found. Handles (it should at least) (binary) data and embedded # documents correctly (i.e. isn't confused by embedded documents # containing COMMENT as well, if they are enclosed in # Begin/EndDocument comments). # # The file is positioned right after the line containing the # comment. Raise a DSCError if the comment is not found line = file.readline() while line: match = rx_dsccomment.match(line) if match: key = match.group(1) if key == comment: return elif key == 'BeginDocument': # skip embedded document skip_to_comment(file, 'EndDocument') elif key == 'BeginData': value = split(strip(line[match.end(0):])) lines = 0 if len(value) >= 1: count = atoi(value) if len(value) == 3: lines = value[2] == 'Lines' else: # should never happen in a conforming document... count = 0 if count > 0: if lines: for i in range(count): file.readline() else: blocksize = 4000 while count: if count > blocksize: count = count - len(file.read(blocksize)) else: count = count - len(file.read(count)) line = file.readline() else: raise DSCError('DSC-Comment %s not found' % comment) def parse_eps_file(filename): # Extract information from the EPS file FILENAME. Return an instance # of EpsInfo with the appropriate parameters. Raise a DSCError, if # the file is not an EPS file. file = streamfilter.LineDecode(open(filename, 'r')) line = file.readline() info = EpsInfo() if IsEpsFileStart(line): parse_header(file, info) if info.atend: skip_to_comment(file, 'Trailer') parse_header(file, info) else: raise DSCError('%s is not an EPS file' % filename) file.close() return info # # # if __name__ == '__main__': import sys file = open(sys.argv[1], 'r') info = EpsInfo() parse_header(file, info) if info.atend: skip_to_comment(file, 'Trailer') parse_header(file, info) file.close() info.print_info() UniConvertor-1.1.4/src/app/Lib/psmisc.py0000775000076400007640000000171711060500602016572 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2003-2006 by Igor E. Novikov # Copyright (C) 1998, 1999 by Bernhard Herzog # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. # # Miscellaneous functions for PostScript creation # from string import join import operator def make_ps_quote_table(): table = [''] * 256 quote = (ord('('), ord(')'), ord('\\')) for i in range(128): if i in quote: table[i] = '\\' + chr(i) else: table[i] = chr(i) for i in range(128, 256): table[i] = '\\' + oct(i)[1:] return table quote_table = make_ps_quote_table() def quote_ps_string(text): return join(map(operator.getitem, [quote_table]*len(text), map(ord, text)), '') def make_textline(text): # return text unchanged if no character needs to be quoted, as a # PS-string (with enclosing parens) otherwise. quoted = quote_ps_string(text) if quoted == text: return text return "(%s)" % quoted UniConvertor-1.1.4/src/app/Lib/units.py0000775000076400007640000000122211060500603016426 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2003-2006 by Igor E. Novikov # Copyright (C) 1997, 1998 by Bernhard Herzog # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. # some factors to convert between different length units and the base # unit of sketch, PostScript points in_to_pt = 72.0 cm_to_pt = in_to_pt / 2.54 mm_to_pt = cm_to_pt / 10 m_to_pt = 100 * cm_to_pt pt_to_in = 1.0 / 72.0 pt_to_cm = 2.54 * pt_to_in pt_to_mm = pt_to_cm * 10 pt_to_m = pt_to_cm / 100 unit_dict = {'pt': 1.0, 'in': in_to_pt, 'cm': cm_to_pt, 'mm': mm_to_pt} unit_names = ['pt', 'in', 'cm', 'mm'] UniConvertor-1.1.4/src/app/Lib/type1.py0000775000076400007640000002455311060500603016342 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2003-2006 by Igor E. Novikov # Copyright (C) 1998, 1999 by Bernhard Herzog # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. # # Extract character outlines from Type1 font files... # import sys from cStringIO import StringIO from types import StringType from string import atoi, split, find, strip import streamfilter from app import Point from app._type1 import decode, hexdecode from app.pstokenize import PSTokenizer, OPERATOR, NAME, INT, END def read_type1_file(filename): data = StringIO() file = open(filename, 'rb') head = file.read(6) if head[:2] == '%!': line = file.readline() while line: line = file.readline() pos = find(line, 'eexec') if pos >= 0: data.write(line[:pos + 5] + '\n') line = line[pos + 5:] break data.write(line) try: buf = line + file.read(200) buf, extra = hexdecode(buf) buf, r = decode(buf, 55665) data.write(buf[4:]) buf = extra + file.read(200) while buf: buf, extra = hexdecode(buf) buf, r = decode(buf, r) data.write(buf) buf = extra + file.read(200) except: pass else: while 1: if ord(head[0]) != 128: raise TypeError, 'not a pfb file' data_type = ord(head[1]) if data_type == 3: # EOF break data_length = ord(head[2]) + 256 * ord(head[3]) \ + 65536 * ord(head[4]) + 16777216 * ord(head[5]) if data_type == 1: # ASCII data.write(file.read(data_length)) elif data_type == 2: #Binary # decode and discard the first 4 bytes buf = file.read(4) if len(buf) < 4: raise IOError, "insufficient data" buf, r = decode(buf, 55665) data_length = data_length - 4 if data_length < 0: raise IOError, "invalid data" while data_length: buf = file.read(min(1000, data_length)) if not buf: raise IOError, "insufficient data" buf, r = decode(buf, r) data.write(buf) data_length = data_length - len(buf) else: raise RuntimeError, "Invalid data type" head = file.read(6) data = data.getvalue() return data def parse_type1_file(data): subrs = char_strings = None tokenizer = PSTokenizer(data) next = tokenizer.next while 1: token, value = next() if token == NAME: if value == 'Subrs': token, value = next() if token == INT: subrs = read_subrs(tokenizer, value) elif value == 'CharStrings': char_strings = read_char_strings(tokenizer) if subrs is not None: break elif token == END: break return subrs, char_strings def read_subrs(tokenizer, num): subrs = [''] * num next = tokenizer.next read = tokenizer.read while not subrs[-1]: token, value = next() if token == OPERATOR and value == 'dup': token, index = next() token, length = next() next() # discard RD operator read(1) # discard space character data = read(length) subrs[index] = decode(data)[0][4:] elif token == END: break return subrs def read_char_strings(tokenizer): char_strings = {} next = tokenizer.next read = tokenizer.read while 1: token, value = next() if token == NAME: token, length = next() next() # discard RD operator read(1) # discard space character data = read(length) char_strings[value] = decode(data)[0][4:] elif token == END or (token == OPERATOR and value == 'end'): break return char_strings class SubrReturn(Exception): pass class CharStringInterpreter: commands = {} def __init__(self, subrs): self.subrs = subrs self.reset() def print_path(self): for closed, path in self.paths: if closed: print 'closed:' else: print 'open:' for part in path: print part def reset(self): self.stack = [] self.ps_stack = [] self.paths = () self.path = [] self.closed = 0 self.in_flex = 0 self.flex = [] self.cur = Point(0, 0) def execute(self, cs): stack = self.stack cs = map(ord, cs) try: while cs: code = cs[0]; del cs[0] if code >= 32: if code <= 246: stack.append(code - 139) elif code <= 250: stack.append((code - 247) * 256 + cs[0] + 108) del cs[0] elif code <= 254: stack.append(-(code - 251) * 256 - cs[0] - 108) del cs[0] else: stack.append(cs[0] * 0x01000000 + cs[1] * 0x10000 + cs[2] * 0x100 + cs[3]) del cs[:4] else: if code == 12: code = 32 + cs[0] del cs[0] cmd = self.commands[code] if cmd: cmd(self) except SubrReturn: return def new_path(self): if self.path: self.paths = self.paths + ((self.closed, self.path),) self.path = [] self.closed = 0 def flush_stack(self, *rest): del self.stack[:] commands[32 + 0] = flush_stack # dotsection commands[1] = flush_stack # hstem commands[32 + 2] = flush_stack # hstem3 commands[3] = flush_stack # vstem commands[32 + 1] = flush_stack # vstem3 def pop(self, n): result = self.stack[-n:] del self.stack[-n:] if n == 1: return result[0] return result def pop_all(self): result = self.stack[:] del self.stack[:] if len(result) == 1: return result[0] return result def endchar(self): self.new_path() self.flush_stack() commands[14] = endchar def hsbw(self): # horizontal sidebearing and width sbx, wx = self.pop_all() self.cur = Point(sbx, 0) commands[13] = hsbw def seac(self): # standard encoding accented character asb, adx, ady, bchar, achar = self.pop_all() commands[32 + 6] = seac def sbw(self): # sidebearing and width sbx, sby, wx, wy = self.pop_all() self.cur = Point(sbx, sby) commands[32 + 7] = sbw def closepath(self): self.pop_all() self.closed = 1 commands[9] = closepath def rlineto(self): dx, dy = self.pop_all() self.cur = self.cur + Point(dx, dy) self.path.append(tuple(self.cur)) commands[5] = rlineto def hlineto(self): dx = self.pop_all() self.cur = self.cur + Point(dx, 0) self.path.append(tuple(self.cur)) commands[6] = hlineto def vlineto(self): dy = self.pop_all() self.cur = self.cur + Point(0, dy) self.path.append(tuple(self.cur)) commands[7] = vlineto def rmoveto(self): dx, dy = self.pop_all() self.cur = self.cur + Point(dx, dy) if self.in_flex: self.flex.append(self.cur) else: self.new_path() self.path.append(tuple(self.cur)) commands[21] = rmoveto def hmoveto(self): dx = self.pop_all() self.cur = self.cur + Point(dx, 0) self.new_path() self.path.append(tuple(self.cur)) commands[22] = hmoveto def vmoveto(self): dy = self.pop_all() self.cur = self.cur + Point(0, dy) self.new_path() self.path.append(tuple(self.cur)) commands[4] = vmoveto def rrcurveto(self): dx1, dy1, dx2, dy2, dx3, dy3 = self.pop_all() d1 = self.cur + Point(dx1, dy1) d2 = d1 + Point(dx2, dy2) d3 = d2 + Point(dx3, dy3) self.cur = d3 self.path.append(tuple(d1) +tuple(d2) + tuple(d3)) commands[8] = rrcurveto def hvcurveto(self): dx1, dx2, dy2, dy3 = self.pop_all() d1 = self.cur + Point(dx1, 0) d2 = d1 + Point(dx2, dy2) d3 = d2 + Point(0, dy3) self.cur = d3 self.path.append(tuple(d1) + tuple(d2) + tuple(d3)) commands[31] = hvcurveto def vhcurveto(self): dy1, dx2, dy2, dx3 = self.pop_all() d1 = self.cur + Point(0, dy1) d2 = d1 + Point(dx2, dy2) d3 = d2 + Point(dx3, 0) self.cur = d3 self.path.append(tuple(d1) + tuple(d2) + tuple(d3)) commands[30] = vhcurveto def start_flex(self): print 'start_flex' self.in_flex = 1 self.flex = [] def end_flex(self): print 'end_flex' size, x, y = self.pop_all() d1, d2, d3 = self.flex[1:4] self.path.append(tuple(d1) + tuple(d2) + tuple(d3)) d1, d2, d3 = self.flex[4:7] self.path.append(tuple(d1) + tuple(d2) + tuple(d3)) def div(self): num1, num2 = self.pop(2) self.stack.append(float(num1) / num2) commands[32 + 12] = div def callothersubr(self): n, sn = self.pop(2) if n: self.ps_stack = self.pop(n) if sn == 3: self.ps_stack = [3] commands[32 + 16] = callothersubr def callsubr(self): num = self.pop(1) if num == 0: self.end_flex() elif num == 1: self.start_flex() elif 2 <= num <= 3: return self.execute(self.subrs[num]) commands[10] = callsubr def pop_ps(self): value = self.ps_stack[-1] del self.ps_stack[-1] self.stack.append(value) commands[32 + 17] = pop_ps def subr_return(self): raise SubrReturn commands[11] = subr_return def setcurrentpoint(self): x, y = self.pop_all() self.cur = Point(x, y) commands[32 + 33] = setcurrentpoint # # read_outlines(FILENAME) # # Return the outlines of the glyphs in the Type1 font stored in the file # FILENAME as a tuple (CHAR_STRINGS, INTERPRETER). CHAR_STRINGS is a # dictionary mapping glyph names to strings containing the outline # description, INTERPRETER is an instance of CharStringInterpreter # initialized with the appropriate Subrs. def read_outlines(filename): data = read_type1_file(filename) data = streamfilter.StringDecode(data, None) subrs, char_strings = parse_type1_file(data) interpreter = CharStringInterpreter(subrs) return char_strings, interpreter def embed_type1_file(fontfile, outfile): if type(fontfile) == StringType: file = open(filename, 'rb') else: file = fontfile head = file.read(6) if head[:2] == '%!': # PFA outfile.write(head) data = file.read(4000) while data: outfile.write(data) data = file.read(4000) else: # Probably PFB while 1: # loop over all chunks if ord(head[0]) != 128: raise TypeError, 'not a pfb file' data_type = ord(head[1]) if data_type == 3: # EOF break data_length = ord(head[2]) + 256 * ord(head[3]) \ + 65536 * ord(head[4]) + 16777216 * ord(head[5]) if data_type == 1: # ASCII outfile.write(file.read(data_length)) elif data_type == 2: #Binary # Hex encode data encoder = streamfilter.HexEncode(outfile) while data_length: if data_length > 4000: length = 4000 else: length = data_length data = file.read(length) encoder.write(data) data_length = data_length - length encoder.close() head = file.read(6) # # some test functions... # def test(): filename = sys.argv[1] data = read_type1_file(filename) data = streamfilter.StringDecode(data, None) subrs, char_strings = parse_type1_file(data) items = char_strings.items() items.sort() interpreter = CharStringInterpreter(subrs) for name, code in items: print name, `code` interpreter.execute(code) interpreter.print_path() interpreter.reset() if __name__ == '__main__': test() UniConvertor-1.1.4/src/app/Lib/skcompleter.py0000775000076400007640000000311111060500602017612 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2003-2006 by Igor E. Novikov # Copyright (C) 1997, 1998, 1999, 2003 by Bernhard Herzog # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. # This is a more general completer than the one of python 1.5 on which this # one is based... import readline import __builtin__ import __main__ class Completer: def __init__(self, global_dict = None, local_dict = None): if global_dict is None: global_dict = __main__.__dict__ self.global_dict = global_dict if local_dict is None: local_dict = global_dict self.local_dict = local_dict def complete(self, text, state): if state == 0: if "." in text: self.matches = self.attr_matches(text) else: self.matches = self.global_matches(text) return self.matches[state] def global_matches(self, text): import keyword matches = [] n = len(text) for list in [keyword.kwlist, self.local_dict.keys(), self.global_dict.keys(), __builtin__.__dict__.keys()]: for word in list: if word[:n] == text: matches.append(word) return matches def attr_matches(self, text): import re m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text) if not m: return expr, attr = m.group(1, 3) words = dir(eval(expr, self.global_dict, self.local_dict)) matches = [] n = len(attr) for word in words: if word[:n] == attr: matches.append("%s.%s" % (expr, word)) return matches def install(global_dict = None, local_dict = None): readline.set_completer(Completer(global_dict, local_dict).complete) UniConvertor-1.1.4/src/app/Lib/encoding.py0000775000076400007640000001355411060500603017065 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2003-2006 by Igor E. Novikov # Copyright (C) 1998, 1999 by Bernhard Herzog # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. import operator, string notdef = '.notdef' iso_latin_1 = (notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright', 'asterisk', 'plus', 'comma', 'minus', 'period', 'slash', 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde', notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, 'dotlessi', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve', 'dotaccent', 'dieresis', notdef, 'ring', 'cedilla', notdef, 'hungarumlaut', 'ogonek', 'caron', 'space', 'exclamdown', 'cent', 'sterling', 'currency', 'yen', 'brokenbar', 'section', 'dieresis', 'copyright', 'ordfeminine', 'guillemotleft', 'logicalnot', 'hyphen', 'registered', 'macron', 'degree', 'plusminus', 'twosuperior', 'threesuperior', 'acute', 'mu', 'paragraph', 'periodcentered', 'cedilla', 'onesuperior', 'ordmasculine', 'guillemotright', 'onequarter', 'onehalf', 'threequarters', 'questiondown', 'Agrave', 'Aacute', 'Acircumflex', 'Atilde', 'Adieresis', 'Aring', 'AE', 'Ccedilla', 'Egrave', 'Eacute', 'Ecircumflex', 'Edieresis', 'Igrave', 'Iacute', 'Icircumflex', 'Idieresis', 'Eth', 'Ntilde', 'Ograve', 'Oacute', 'Ocircumflex', 'Otilde', 'Odieresis', 'multiply', 'Oslash', 'Ugrave', 'Uacute', 'Ucircumflex', 'Udieresis', 'Yacute', 'Thorn', 'germandbls', 'agrave', 'aacute', 'acircumflex', 'atilde', 'adieresis', 'aring', 'ae', 'ccedilla', 'egrave', 'eacute', 'ecircumflex', 'edieresis', 'igrave', 'iacute', 'icircumflex', 'idieresis', 'eth', 'ntilde', 'ograve', 'oacute', 'ocircumflex', 'otilde', 'odieresis', 'divide', 'oslash', 'ugrave', 'uacute', 'ucircumflex', 'udieresis', 'yacute', 'thorn', 'ydieresis') adobe_standard = (notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright', 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde', notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin', 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft', 'guilsinglleft', 'guilsinglright', 'fi', 'fl', notdef, 'endash', 'dagger', 'daggerdbl', 'periodcentered', notdef, 'paragraph', 'bullet', 'quotesinglbase', 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand', notdef, 'questiondown', notdef, 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve', 'dotaccent', 'dieresis', notdef, 'ring', 'cedilla', notdef, 'hungarumlaut', 'ogonek', 'caron', 'emdash', notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef, 'AE', notdef, 'ordfeminine', notdef, notdef, notdef, notdef, 'Lslash', 'Oslash', 'OE', 'ordmasculine', notdef, notdef, notdef, notdef, notdef, 'ae', notdef, notdef, notdef, 'dotlessi', notdef, notdef, 'lslash', 'oslash', 'oe', 'germandbls', notdef, notdef, notdef, notdef) class Reencoder: def __init__(self, source, dest, notdef = None): self.source = source self.dest = dest if notdef is None: try: notdef = list(self.source).index('.notdef') except: notdef = 0 self.notdef = notdef self.build_mapping() def build_mapping(self): dest = self.dest; source = self.source; notdef = self.notdef dict = {} length = len(dest) map(operator.setitem, [dict] * length, dest, range(length)) dict[notdef] = self.notdef mapping = range(256) for i in mapping: if source[i] != dest[i]: if source[i] == notdef: mapping[i] = notdef else: mapping[i] = dict.get(source[i], notdef) if mapping == range(256): self.mapping = '' else: self.mapping = string.join(map(chr, mapping), '') def __call__(self, text): if self.mapping: return string.translate(text, self.mapping) else: return text UniConvertor-1.1.4/src/app/Lib/__init__.py0000775000076400007640000000000011060500603017014 0ustar igorigorUniConvertor-1.1.4/src/app/conf/0000755000076400007640000000000011211455344015140 5ustar igorigorUniConvertor-1.1.4/src/app/conf/const.py0000775000076400007640000001067411060500601016642 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2003-2006 by Igor E. Novikov # Copyright (C) 1997, 1998, 1999 by Bernhard Herzog # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. # # Constants... # # # Types of handles # # physical # for rect handles: filled == handle_id & 1 Handle_OpenRect = 0 Handle_FilledRect = 1 Handle_SmallOpenRect = 2 Handle_SmallFilledRect = 3 Handle_OpenCircle = 4 Handle_FilledCircle = 5 Handle_SmallOpenCircle = 6 Handle_SmallFilledCircle = 7 Handle_SmallOpenRectList = 8 Handle_Line = 9 Handle_Pixmap = 10 Handle_Caret = 11 Handle_PathText = 12 # logical XXX should these be moved to config.py? Handle = Handle_FilledRect HandleNode = Handle_OpenRect HandleSelectedNode = Handle_FilledRect HandleControlPoint = Handle_SmallFilledRect HandleLine = Handle_Line HandleCurvePoint = Handle_FilledCircle # # # # The corners of the unit rectangle corners = [(0, 0), (1, 0), (1, 1), (0, 1)] # # Standard channel names # # common CHANGED = 'CHANGED' DOCUMENT = 'DOCUMENT' MODE = 'MODE' SELECTION = 'SELECTION' # dialogs CLOSED = 'CLOSED' # TKExt COMMAND = 'COMMAND' # also uses SELECTION # APPLICATION CLIPBOARD = 'CLIPBOARD' ADD_TO_SPECIAL_MENU = 'ADD_TO_SPECIAL_MENU' # Global INITIALIZE = 'INITIALIZE' APP_INITIALIZED = 'APP_INITIALIZED' INIT_READLINE = 'INIT_READLINE' MOVING = 0 # CANVAS STATE = 'STATE' UNDO = 'UNDO' VIEW = 'VIEW' POSITION = 'POSITION' CURRENTINFO = 'CURRENTINFO' # DOCUMENT EDITED = 'EDITED' GRID = 'GRID' LAYER = 'LAYER' LAYER_STATE = 'LAYER_STATE'; LAYER_ORDER = 'LAYER_ORDER' LAYER_COLOR = 'LAYER_COLOR'; LAYER_ACTIVE = 'LAYER_ACTIVE' LAYOUT = 'LAYOUT' REDRAW = 'REDRAW' STYLE = 'STYLE' UNDO = 'UNDO' GUIDE_LINES = 'GUIDE_LINES' PAGE = 'PAGE' # graphics object #TRANSFORMED = 'TRANSFORMED' # command UPDATE = 'update' # palette COLOR1 = 'color1' COLOR2 = 'color2' # Drop types DROP_COLOR = 'COLOR' # # Scripting Access # SCRIPT_UNDO = 'SCRIPT_UNDO' SCRIPT_GET = 'SCRIPT_GET' SCRIPT_OBJECT = 'SCRIPT_OBJECT' SCRIPT_OBJECTLIST = 'SCRIPT_OBJECTLIST' # # constants for selections # # the same as in curveobject.c SelectSet = 0 SelectAdd = 1 SelectSubtract = 2 SelectSubobjects = 3 SelectDrag = 4 SelectGuide = 5 # Arc modes. bezier_obj.approx_arc uses these ArcArc = 0 ArcChord = 1 ArcPieSlice = 2 # # X specific stuff # #from app.X11 import X ShiftMask = (1<<0) LockMask = (1<<1) ControlMask = (1<<2) Mod1Mask = (1<<3) Mod2Mask = (1<<4) Mod3Mask = (1<<5) Mod4Mask = (1<<6) Mod5Mask = (1<<7) #ShiftMask = X.ShiftMask #LockMask = X.LockMask #ControlMask = X.ControlMask #Mod1Mask = X.Mod1Mask #Mod2Mask = X.Mod2Mask #Mod3Mask = X.Mod3Mask #Mod4Mask = X.Mod4Mask #Mod5Mask = X.Mod5Mask MetaMask = Mod1Mask #Button1Mask = X.Button1Mask #Button2Mask = X.Button2Mask #Button3Mask = X.Button3Mask #Button4Mask = X.Button4Mask #Button5Mask = X.Button5Mask Button1Mask = (1<<8) Button2Mask = (1<<9) Button3Mask = (1<<10) Button4Mask = (1<<11) Button5Mask = (1<<12) AllButtonsMask = Button1Mask | Button2Mask | Button3Mask #Button1 = X.Button1 #Button2 = X.Button2 #Button3 = X.Button3 #Button4 = X.Button4 #Button5 = X.Button5 #ContextButton = Button3 #ContextButtonMask = Button3Mask AllowedModifierMask = ShiftMask | ControlMask | MetaMask ConstraintMask = ControlMask AlternateMask = ShiftMask LineSolid = 0 LineOnOffDash = 1 LineDoubleDash = 2 #AddSelectionMask = ShiftMask #SubtractSelectionMask = MetaMask #SubobjectSelectionMask = ControlMask ## ## Line Styles ## #JoinMiter = X.JoinMiter #JoinRound = X.JoinRound #JoinBevel = X.JoinBevel #CapButt = X.CapButt #CapRound = X.CapRound #CapProjecting = X.CapProjecting JoinMiter = 0 JoinRound = 1 JoinBevel = 2 CapButt = 1 CapRound = 2 CapProjecting = 3 # cursors CurStd = 'top_left_arrow' CurStd1 = 'top_left_arrow' CurHandle = 'crosshair' CurTurn = 'exchange' CurPick = 'hand2' CurCreate = 'crosshair'#'pencil' CurPlace = 'crosshair' CurDragColor = 'spraycan' CurHGuide = 'sb_v_double_arrow' CurVGuide = 'sb_h_double_arrow' CurZoom = 'plus' # is replaced by bitmap specification in CurUp ='based_arrow_up' CurUpDown = 'sb_v_double_arrow' CurDown ='based_arrow_down' CurEdit = 'left_ptr'#'xterm' # unused as yet CurHelp = 'question_arrow' CurWait = 'watch' CurMove = 'top_left_arrow'#'fleur' # # Text Alignment # ALIGN_BASE = 0 ALIGN_CENTER = 1 ALIGN_TOP = 2 ALIGN_BOTTOM = 3 ALIGN_LEFT = 0 ALIGN_CENTER = 1 ALIGN_RIGHT = 2UniConvertor-1.1.4/src/app/conf/__init__.py0000775000076400007640000000000011060500601017231 0ustar igorigorUniConvertor-1.1.4/src/app/conf/configurator.py0000775000076400007640000003507011060500601020213 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2006 by Igor E. Novikov # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. import os, sys from xml.sax import handler from app.events import connector from const import CHANGED from app.utils.os_utils import gethome, create_directory from app.utils import output from app import Point, PointType class Configurator: """Configuration class configs sK1 and loads preferences at start up.""" def __init__(self, master=None, base_dir='~', cnf={}, **kw): self.name = 'UniConvertor' self.sk_command = 'uniconvertor' self.sk_dir = base_dir self.sk_share_dir = os.path.join(self.sk_dir,'share') self.sk_icc = os.path.join(self.sk_share_dir,'icc') self.sk_fonts = os.path.join(self.sk_share_dir,'fonts') self.sk_ps = os.path.join(self.sk_share_dir,'ps_templates') self.user_home_dir = gethome() self.user_config_dir = os.path.join(self.user_home_dir,'.uniconvertor') self.restore_theme = 0 self.user_icc = os.path.join(self.user_config_dir,'icc') self.user_fonts = os.path.join(self.user_config_dir,'fonts') self.user_ps = os.path.join(self.user_config_dir,'ps_templates') self.plugin_path = [] # Directories where sK1 searches for plugins. The expanded plugin_dir is appended to this self.filters_dir = os.path.join(self.sk_dir,'app/plugins/Filters/') # Subdirectory for i/o filters #self.plugins_dir = os.path.join(self.sk_dir,'app/plugins/Objects/') # Subdirectory for plugins #self.plugin_path.append(self.plugins_dir) self.plugin_path.append(self.filters_dir) self.user_preferences_file = os.path.join(self.user_config_dir, 'preferences.xml') #print 'Self testing\n==========================================' #self.check_sk_dir() #self.check_user_config() self.preferences = Preferences() #self.preferences.load(self.user_preferences_file) #print '==========================================' if self.restore_theme: self.preferences.active_theme = 'Plastik' #===============DEPRECATED VARIABLES=============== self.font_path = [self.user_config_dir] # Directories where pfa/pfb files are located. The expanded fontmetric_dir is appended to this. self.sketch_dir = base_dir # The directory where sketch and its modules are found. Set automagically from __init__.py of the Sketch package self.fontmetric_dir = self.user_fonts # Subdirectory for the font metrics. On startup it is expanded to an absolute pathname. self.postscript_prolog = os.path.join(self.sk_dir, 'share/ps_templates/sk1-proc.ps') # PostScript Prolog. self.small_font = '-*-dejavu sans-medium-r-*-*-8-*-*-*-*-*-*' self.normal_font = '-*-dejavu sans-medium-r-*-*-10-*-*-*-*-*-*' #============================================ def save_user_preferences(self): self.preferences.save(self.user_preferences_file) def add_options(self, root): root.option_readfile(self.tk_defaults, 'startupFile') #def add_program_default(self,key, value): #setattr(configurato.ProgramDefaults, key, value) def get_preference(self,key, default): if hasattr(self.preferences, key): return getattr(self.preferences, key) return default def add_mru_file(self, filename): if not filename: return mru_list = self.preferences.mru_files if filename in mru_list: mru_list.remove(filename) mru_list.insert(0, filename) self.preferences.mru_files = mru_list[:4] def remove_mru_file(self, filename): if not filename: return mru_list = self.preferences.mru_files if filename in mru_list: mru_list.remove(filename) if len(mru_list) < 4: mru_list = mru_list + ['', '', '', ''] self.preferences.mru_files = mru_list[:4] def check_sk_dir(self): #print 'sK1 directories test... ', result = True dirs = (self.sk_share_dir, self.sk_icc, self.sk_ps) for dir in dirs: if not os.path.isdir(dir): result = False if not result: #print '[',output.red('CRASH'),']' print output.yellow('sK1 installation is corrupted. Please check sK1 directories or reinstall sK1!') sys.exit(1) #else: #print '[',output.green('OK'),']' def check_user_config(self): #print 'sK1 user config test... ', result = True if not os.path.isdir(self.user_config_dir): result = False try: os.mkdir(self.user_config_dir, 0777) except (IOError, os.error), value: #print '[',output.red('CRASH'),']' sys.stderr('cannot write preferences into %s.' % user_config_dir) sys.exit(1) if not os.path.isdir(self.user_icc): result = False os.system("cp -r "+self.sk_icc+" "+self.user_icc) if not os.path.isdir(self.user_fonts): result = False os.system("cp -r "+self.sk_fonts+" "+self.user_fonts) if not os.path.isdir(self.user_ps): result = False os.system("cp -r "+self.sk_ps+" "+self.user_ps) #if not result: #print '[',output.yellow('FIXED'),']' #else: #print '[',output.green('OK'),']' class Preferences(connector.Publisher): def __setattr__(self, attr, value): if not hasattr(self, attr) or getattr(self, attr) != value: self.__dict__[attr] = value self.issue(CHANGED, attr, value) def load(self, filename=None): import xml.sax from xml.sax.xmlreader import InputSource content_handler = XMLPrefReader(pref=self) error_handler = ErrorHandler() entity_resolver = EntityResolver() dtd_handler = DTDHandler() try: input = open(filename, "r") input_source = InputSource() input_source.setByteStream(input) xml_reader = xml.sax.make_parser() xml_reader.setContentHandler(content_handler) xml_reader.setErrorHandler(error_handler) xml_reader.setEntityResolver(entity_resolver) xml_reader.setDTDHandler(dtd_handler) xml_reader.parse(input_source) input.close except: pass def save(self, filename=None): if len(self.__dict__) == 0 or filename == None: return from xml.sax.saxutils import XMLGenerator try: file = open(filename, 'w') except (IOError, os.error), value: import sys sys.stderr('cannot write preferences into %s: %s'% (`filename`, value[1])) return writer = XMLGenerator(out=file,encoding=self.system_encoding) writer.startDocument() defaults = Preferences.__dict__ items = self.__dict__.items() items.sort() writer.startElement('preferences',{}) writer.characters('\n') for key, value in items: if defaults.has_key(key) and defaults[key] == value: continue writer.characters(' ') writer.startElement('%s' % key,{}) if type(value) == PointType: to_write= '(%g, %g)' % tuple(value) writer.characters('Point%s' % to_write) else: writer.characters('%s' % `value`) writer.endElement('%s' % key) writer.characters('\n') writer.endElement('preferences') writer.endDocument() file.close #============== sK1 PREFERENCES =================== undo_limit = None #how many undo steps sketch remembers. None means unlimited. system_encoding = 'utf-8' # default encoding for sK1 (GUI uses utf-8 only) #The initial grid geometry for a new document. It must be a tuple of the form (ORIG_X, ORIG_Y, WIDTH_X, WIDTH_Y). WIDTH_X and WIDTH_Y are #the horizontal and the vertical distance between points of the grid, (ORIG_X, ORIG_X) is one point of the grid. These coordinates are given in Point grid_geometry = (0, 0, 2.83465, 2.83465) #If the grid should be visible in a new document, set grid_visible to a true value grid_visible = 0 #The grid color of a new document as a tuple of RGB values in the range 0..1. E.g. (0, 0, 1) for blue grid_color = ('RGB', 0, 0, 1) #The outline color of a new GuideLayer as a tuple of RGB values in the range 0..1. guide_color = ('RGB', 0, 0.3, 1) layer_color = ('RGB', 0.196, 0.314, 0.635) #When objects are duplicated, the new copies are translated by duplicate_offset, given in document coordiates duplicate_offset = (10, 10) #The default unit used in various places. Supported values: 'pt', 'in', 'cm', 'mm' default_unit = 'mm' poslabel_sets_default_unit = 1 #How many steps to draw in a gradient pattern gradient_steps_editor = 30 gradient_steps_print = 50 #If the text on the screen becomes smaller than greek_threshold, don't render a font, but draw little lines instead. XXX see comments in graphics.py greek_threshold = 5 #If the metrics file for a font can't be found or if a requested font is not known at all, the (metrics of) fallback_font is used fallback_font = 'Slim' #When snapping is active, coordinates specified with the mouse are snapped to the nearest `special' point (e.g. a grid point) if that is nearer than #max_snap_distance pixels. (Thus, this length is given in window (pixel-) coordinates). max_snap_distance = 30 #If true and snapping is active, the current position displayed in the status bar is the position the mouse position would be snapped to. snap_current_pos = 1 #If true, change the cursor when above a selected object or a guide line active_cursor = 1 #Icons color_icons = 1 #List of most recently used files. mru_files = ['', '', '', '', ''] viewport_ring_length = 10 #The standard palette. If this is a relative pathname it is #interpreted relative to std_res_dir. palette = 'standard.spl' unipalette = 'CMYK standart.skp' arrows = 'standard.arrow' dashes = 'standard.dashes' pattern = 'pattern.ppm' pattern_dir = '' image_dir = '' set_default_properties = 1 # whether the apply button in the property dialogs sets the default properties for new objects. ( 1 - do it, but ask; 0 - don't) sample_text = 'Text' # Font dialog sample text. Can be changed by simply editing it in the font dialog. default_paper_format = 'A4' #Default paper format for new documents and documents read from a files that don't specify a paper format. This should be one of the formats defined in papersize.py. default_page_orientation = 0 #Default page orientation. Portrait = 0, Landscape = 1. Other values are silenty ignored. #Screen resolution in pixel per point. Used by the canvas to convert document coordinates to screen coordinates for a zoom factor of 100% #None means to compute it from information obtained from the X-Server (ScreenWidth and ScreenMMWidth). 1.0 means 72 pixels per inch. screen_resolution = 1.0 #If true, switch to selection mode after drawing an object. Stay in creation mode otherwise. creation_is_temporary = 0 autoscroll_interval = 1 # ms, 0 disables auto scrolling autoscroll_amount = .5 # no. of scroll units #Ask user for confirmation if the memory size of an image is larger than huge_image_size (measured in bytes) (unused at the moment) huge_image_size = 1 << 20 #Default resolution in pixels/inch for a new raster image that doesn't specify it itself. (not implemented yet) default_image_resolution = 72 #The resoulution in pixel/inch of the preview image sK1 renders for preview. (using gs). Leave this at 72 for now. eps_preview_resolution = 72 #Whether to print internal warning messages. Useful for debugging. print_internal_warnings = 1 #print additional messages. these are usually only interesting for development purposes. print_debug_messages = 0 #Howto report warnings to the user: 'dialog' popup a dialog box; 'stderr' write the message to stderr warn_method = 'dialog' #whether to show the special menu. The special menu contains some commands that provide access to sketch internals and new, experimental features. show_special_menu = 0 show_advanced_snap_commands = 0 #whether to show advanced snapping options. activate_tooltips = 1 #Use Tooltips. tooltip_delay = 500 #Delay for tooltips in milliseconds window_title_template ='%(appname)s - [%(docname)s]' panel_use_coordinates = 1 # If true, use the saved coordinates when opening a panel panel_correct_wm = 1 # If true, try to compensate for the coordinate changes the window manager introduces by reparenting. blend_panel_default_steps = 10 print_destination = 'printer' # Default print destination. 'file' for file, 'printer' for printer print_directory = '~' # default directory for printing to file menu_tearoff_fix = 1 # Menus style = 'Plastik' color_theme = 'System' #color_theme = 'UbuntuLooks' icons='CrystalSVG' small_font='Arial 8' normal_font='Arial 10' large_font='Arial 15' #---------Color managment--------- default_rgb_profile='sRGB.icm' default_cmyk_profile='GenericCMYK.icm' default_monitor_profile='sRGB.icm' user_rgb_profile=0 user_cmyk_profile=0 user_monitor_profile=0 printer_intent=0 monitor_intent=0 use_cms=0 simulate_printer=0 #------------------------------------ forceCMYK=0 forceRGB=0 #------------------------------------ #RULER data ruler_min_tick_step = 4 ruler_min_text_step = 33 ruler_max_text_step = 100 ruler_text_color = 'black' ruler_tick_color = 'black' ruler_color = '#F9F9FC' #Rulers background color color_cube = (6, 6, 6, 20) # For PseudoColor displays. reduce_color_flashing = 1 # Screen Gamma. (leave this at 1.0 for now) #screen_gamma = 1.0 # # Cairo data # cairo_enabled=0 alpha_channel_enabled=0 # # Bezier Objects # # Whether the first click-drag-release in the PolyLine creator # defines the start and end of the first line segment or just the # start point. polyline_create_line_with_first_cklick = 1 topmost_is_mask = 1 # Mask Group default_font = 'Slim' # The name of the font used for new text-objects # If true, try to unload some of the import filter modules after # use. Only filters marked as unloadable in their config file are # affected. unload_import_filters = 1 handle_jump= 1 #Handle jump to manipulate objects by keyboard arrows (mm) editor_line_width = 1 # The line width for the outlines during a drag. # Load these standard scripts at runtime in interactive mode. This # is really just a list of module names that are passed to # __import__, but don't count on it. standard_scripts = ["app.scripts.export_raster", "app.scripts.simple_separation", "app.scripts.spread", "app.scripts.reload_image", "app.scripts.create_star", "app.scripts.create_star_outline", "app.scripts.create_spiral", "app.scripts.read_gimp_path",] class XMLPrefReader(handler.ContentHandler): """Handler for xml file reading""" def __init__(self, pref=None): self.key = None self.value = None self.pref = pref def startElement(self, name, attrs): self.key = name def endElement(self, name): if name!='preferences': code=compile('self.value='+self.value,'','exec') exec code self.pref.__dict__[self.key] = self.value def characters(self, data): self.value = data class ErrorHandler(handler.ErrorHandler): pass class EntityResolver(handler.EntityResolver): pass class DTDHandler(handler.DTDHandler): pass UniConvertor-1.1.4/src/app/utils/0000755000076400007640000000000011211455344015353 5ustar igorigorUniConvertor-1.1.4/src/app/utils/locale_utils.py0000775000076400007640000000224111060500570020402 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2003-2006 by Igor E. Novikov # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. import os from popen2 import popen2 from app import config UTF_CODEC='utf-8' def locale_to_utf(string=''): if string=='' : return string locale=get_locale() if locale==UTF_CODEC: return string return strip_line(cmd_iconv(locale, UTF_CODEC, string)) def utf_to_locale(string=''): if string=='' : return string locale=get_locale() if locale==UTF_CODEC: return string return strip_line(cmd_iconv(UTF_CODEC, locale, string)) def get_locale(): return config.preferences.system_encoding def cmd_iconv(from_codec='', to_codec='', string=''): if from_codec=='' or to_codec=='' or string=='' : return string from_bash = os.popen('echo "'+string+'" |iconv -f '+from_codec+' -t '+to_codec) # to_bash.write(string) result=from_bash.read() # to_bash.close() from_bash.close() return result def strip_line(string=''): if string=='' : return string return string[0:len(string)-1] def getshell_var(s): if os.confstr(s): return os.environ[s] return None UniConvertor-1.1.4/src/app/utils/os_utils.py0000775000076400007640000001631211060500570017570 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2003-2006 by Igor E. Novikov # Copyright (C) 1997, 1998, 1999, 2002, 2003 by Bernhard Herzog # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. # # Some useful functions used in various places... # import os, string, re, stat # Return the value of the environment variable S if present, None # otherwise. In Python 1.5 one might use os.environ.get(S) instead... def getenv(s): if os.environ.has_key(s): return os.environ[s] return None #Return directory list for provided path def get_dirs(path='.'): list=[] if path: if os.path.isdir(path): try: names = os.listdir(path) except os.error: return [] names.sort() for name in names: if os.path.isdir(os.path.join(path, name)): list.append(name) return list def get_dirs_withpath(path='.'): list=[] names=[] if os.path.isdir(path): try: names = os.listdir(path) except os.error: return names names.sort() for name in names: if os.path.isdir(os.path.join(path, name)) and not name=='.svn': list.append(os.path.join(path, name)) return list #Return file list for provided path def get_files(path='.', ext='*'): list=[] if path: if os.path.isdir(path): try: names = os.listdir(path) except os.error: return [] names.sort() for name in names: if not os.path.isdir(os.path.join(path, name)): if ext=='*': list.append(name) elif '.'+ext==name[-1*(len(ext)+1):]: list.append(name) return list #Return full file names list for provided path def get_files_withpath(path='.', ext='*'): import glob list = glob.glob(os.path.join(path, "*."+ext)) list.sort() return list #Return recursive directories list for provided path def get_dirs_tree(path='.'): tree=get_dirs_withpath(path) for node in tree: subtree=get_dirs_tree(node) tree+=subtree return tree #Return recursive files list for provided path def get_files_tree(path='.', ext='*'): tree=[] dirs=[path,] dirs+=get_dirs_tree(path) for dir in dirs: list = get_files_withpath(dir,ext) list.sort() tree+=list return tree # Return the pwd entry for the current user in the format of # pwd.getpwuid. def get_pwd(): import pwd user = getenv("USER") or getenv("LOGNAME") if not user: return pwd.getpwuid(os.getuid()) else: return pwd.getpwnam(user) # Return the user's home directory. If it can't be determined from the # environment or from the password database, return the current # directory. def gethome(): try: home = getenv("HOME") if not home: home = get_pwd()[5] return home except (KeyError, ImportError): return os.curdir # Return the real user name (the gecos field of passwd) def get_real_username(): try: return get_pwd()[4] except: return None # Return the hostname def gethostname(): name = getenv('HOSTNAME') if not name: try: import socket name = socket.gethostname() except: pass return name # sh_quote taken almost verbatim from the python standard library with # the only difference that it doesn't prepend a space def sh_quote(x): """Return a unix shell quoted version of the string x. The result is of a form that can be inserted into an argument to os.system so that it looks like a single word to the shell. """ # Two strategies: enclose in single quotes if it contains none; # otherwise, enclose in double quotes and prefix quotable characters # with backslash. if '\'' not in x: return '\'' + x + '\'' s = '"' for c in x: if c in '\\$"`': s = s + '\\' s = s + c s = s + '"' return s # # Filename manipulation # # return the longest common prefix of path1 and path2 that is a # directory. def commonbasedir(path1, path2): if path1[-1] != os.sep: path1 = path1 + os.sep return os.path.split(os.path.commonprefix([path1, path2]))[0] # return the absolute path PATH2 as a path relative to the directory # PATH1. PATH1 must be an absoulte filename. If commonbasedir(PATH1, # PATH2) is '/', return PATH2. Doesn't take symbolic links into # account... def relpath(path1, path2): if not os.path.isabs(path2): return path2 basedir = commonbasedir(path1, path2) if basedir == os.sep: return path2 path2 = path2[len(basedir) + 1 : ] curbase = path1 while curbase != basedir: curbase = os.path.split(curbase)[0] path2 = os.pardir + os.sep + path2 return path2 # find a file FILE in one of the directories listed in PATHS. If a file # is found, return its full name, None otherwise. def find_in_path(paths, file): for path in paths: fullname = os.path.join(path, file) if os.path.isfile(fullname): return fullname # find one of the files listed in FILES in one of the directories in # PATHS. Return the name of the first one found, None if no file is # found. def find_files_in_path(paths, files): for path in paths: for file in files: fullname = os.path.join(path, file) if os.path.isfile(fullname): return fullname # Create the directory dir and its parent dirs when necessary def create_directory(dir): if os.path.isdir(dir): return parent, base = os.path.split(dir) create_directory(parent) os.mkdir(dir, 0777) # Make a backup of FILENAME if it exists by renaming it to its # backupname (a ~ appended) class BackupError(Exception): def __init__(self, errno, strerror, filename = ''): self.errno = errno self.strerror = strerror self.filename = filename def make_backup(filename): if os.path.isfile(filename): backupname = filename + '~' try: os.rename(filename, backupname) except os.error, value: raise BackupError(value[0], value[1], backupname) # # # # Return the current local date and time as a string. The optional # parameter format is used as the format parameter of time.strftime and # defaults to '%c'. # Currently this is used for the CreationTime comment in a PostScript # file. def current_date(format = '%c'): import time return time.strftime(format, time.localtime(time.time())) # # # #An empty class... class Empty: def __init__(self, **kw): for key, value in kw.items(): setattr(self, key, value) # # List Manipulation # from types import ListType def flatten(list): result = [] for item in list: if type(item) == ListType: result = result + flatten(item) else: result.append(item) return result # # String Manipulation # rx_format = re.compile(r'%\((?P[a-zA-Z_0-9]+)\)' r'\[(?P[a-zA-Z_]+)\]') def format(template, converters, dict): result = [] pos = 0 while pos < len(template): match = rx_format.search(template, pos) if match: result.append(template[pos:match.start()] % dict) converter = converters[match.group('converter')] item = dict[match.group('item')] result.append(converter(item)) pos = match.end() else: result.append(template[pos:] % dict) pos = len(template) return string.join(result, '') # convert a bitmap to a string containing an xbm file. The linedlg uses # this for instance to convert bitmap objects to Tk bitmap images. def xbm_string(bitmap): import string width, height = bitmap.GetGeometry()[3:5] lines = ['#define sketch_width %d' % width, '#define sketch_height %d' % height, 'static unsigned char sketch_bits[] = {'] lines = lines + bitmap.GetXbmStrings() + ['}', ''] return string.join(lines, '\n') UniConvertor-1.1.4/src/app/utils/output.py0000775000076400007640000000427611060500570017275 0ustar igorigor# Copyright 1998-2003 Daniel Robbins, Gentoo Technologies, Inc. # Distributed under the GNU Public License v2 # $Header: /home/cvsroot/gentoo-src/portage/pym/output.py,v 1.16 2003/05/29 08:34:55 carpaski Exp $ import os,sys havecolor=1 dotitles=1 codes={} codes["reset"]="\x1b[0m" codes["bold"]="\x1b[01m" codes["teal"]="\x1b[36;06m" codes["turquoise"]="\x1b[36;01m" codes["fuscia"]="\x1b[35;01m" codes["purple"]="\x1b[35;06m" codes["blue"]="\x1b[34;01m" codes["darkblue"]="\x1b[34;06m" codes["green"]="\x1b[32;01m" codes["darkgreen"]="\x1b[32;06m" codes["yellow"]="\x1b[33;01m" codes["brown"]="\x1b[33;06m" codes["red"]="\x1b[31;01m" codes["darkred"]="\x1b[31;06m" def xtermTitle(mystr): if havecolor and dotitles and os.environ.has_key("TERM"): myt=os.environ["TERM"] if myt in ["xterm","Eterm","aterm","rxvt"]: sys.stderr.write("\x1b]1;\x07\x1b]2;"+str(mystr)+"\x07") sys.stderr.flush() def xtermTitleReset(): if havecolor and dotitles and os.environ.has_key("TERM"): myt=os.environ["TERM"] xtermTitle(os.environ["TERM"]) def notitles(): "turn off title setting" dotitles=0 def nocolor(): "turn off colorization" havecolor=0 for x in codes.keys(): codes[x]="" def resetColor(): return codes["reset"] def ctext(color,text): return codes[ctext]+text+codes["reset"] def bold(text): return codes["bold"]+text+codes["reset"] def white(text): return bold(text) def teal(text): return codes["teal"]+text+codes["reset"] def turquoise(text): return codes["turquoise"]+text+codes["reset"] def darkteal(text): return turquoise(text) def fuscia(text): return codes["fuscia"]+text+codes["reset"] def purple(text): return codes["purple"]+text+codes["reset"] def blue(text): return codes["blue"]+text+codes["reset"] def darkblue(text): return codes["darkblue"]+text+codes["reset"] def green(text): return codes["green"]+text+codes["reset"] def darkgreen(text): return codes["darkgreen"]+text+codes["reset"] def yellow(text): return codes["yellow"]+text+codes["reset"] def brown(text): return codes["brown"]+text+codes["reset"] def darkyellow(text): return brown(text) def red(text): return codes["red"]+text+codes["reset"] def darkred(text): return codes["darkred"]+text+codes["reset"] UniConvertor-1.1.4/src/app/utils/__init__.py0000775000076400007640000000000011060500570017451 0ustar igorigorUniConvertor-1.1.4/src/app/scripts/0000755000076400007640000000000011211455344015702 5ustar igorigorUniConvertor-1.1.4/src/app/scripts/save_selection.py0000775000076400007640000000441611060500604021261 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 2000 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import os from app import UI, Document, PostScriptDevice import app.Scripting def selection_as_document(document): # Get a copy of the currently selected objects as a group # If no object is selected the method returns None selection = document.CopyForClipboard() if selection is not None: # create a new document seldoc = Document(create_layer = 1) # and insert the group seldoc.Insert(selection) # The group is now the selected object in the document. Ungroup # it. seldoc.UngroupSelected() return seldoc return None def get_ps_filename(context): dir = context.document.meta.directory if not dir: dir = os.getcwd() name = context.document.meta.filename name, ext = os.path.splitext(name) name = name + '.eps' app = context.application filename = app.GetSaveFilename(title = "Save Selection As PostScript", filetypes = UI.skapp.psfiletypes, initialdir = dir, initialfile = name) return filename def save_selection_as_ps(context): seldoc = selection_as_document(context.document) if seldoc is not None: filename = get_ps_filename(context) if filename: bbox = seldoc.BoundingRect(visible = 0, printable = 1) ps_dev = PostScriptDevice(filename, as_eps = 1, bounding_box = tuple(bbox), document = seldoc) seldoc.Draw(ps_dev) ps_dev.Close() app.Scripting.AddFunction('save_selection_as_ps', 'Save Selection As EPS', save_selection_as_ps, script_type = app.Scripting.AdvancedScript) UniConvertor-1.1.4/src/app/scripts/create_star_outline.py0000775000076400007640000000574111060500604022313 0ustar igorigor# # create_star.py - create star-like objects # Tamito KAJIYAMA <26 March 2000> # Copyright (C) 2000 by Tamito KAJIYAMA # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # from app import _, PolyBezier, CreatePath, Polar from app.UI.sketchdlg import SKModal from Tkinter import * import math, unit class CreateStarDlg(SKModal): title = _("Create Star") def build_dlg(self): self.var_corners = IntVar(self.top) self.var_corners.set(10) label = Label(self.top, text=_("Corners")) label.grid(column=0, row=0, sticky=E) entry = Entry(self.top, width=15, textvariable=self.var_corners) entry.grid(column=1, row=0) self.var_outer_radius = StringVar(self.top) self.var_outer_radius.set("100pt") label = Label(self.top, text=_("Outer Radius")) label.grid(column=0, row=1, sticky=E) entry = Entry(self.top, width=15, textvariable=self.var_outer_radius) entry.grid(column=1, row=1) self.var_inner_radius = StringVar(self.top) self.var_inner_radius.set("75pt") label = Label(self.top, text=_("Inner Radius")) label.grid(column=0, row=2, sticky=E) entry = Entry(self.top, width=15, textvariable=self.var_inner_radius) entry.grid(column=1, row=2) button = Button(self.top, text=_("OK"), command=self.ok) button.grid(column=0, row=3, sticky=W) button = Button(self.top, text=_("Cancel"), command=self.cancel) button.grid(column=1, row=3, sticky=E) def ok(self): self.close_dlg((self.var_corners.get(), self.var_outer_radius.get(), self.var_inner_radius.get())) def create_star_outline(context): args = CreateStarDlg(context.application.root).RunDialog() if args is None: return path = apply(create_star_path, args) bezier = PolyBezier((path,)) context.main_window.PlaceObject(bezier) def create_star_path(corners, outer_radius, inner_radius): outer_radius = unit.convert(outer_radius) inner_radius = unit.convert(inner_radius) path = CreatePath() angle = math.pi * 2 / corners for i in range(corners): path.AppendLine(Polar(outer_radius, angle * i)) path.AppendLine(Polar(inner_radius, angle * i + angle / 2)) path.AppendLine(path.Node(0)) path.ClosePath() return path import app.Scripting app.Scripting.AddFunction('create_star_outline', _("Star Outline"), create_star_outline, menu = _("Create Objects"), script_type = app.Scripting.AdvancedScript) UniConvertor-1.1.4/src/app/scripts/create_spiral.py0000775000076400007640000000557511060500604021102 0ustar igorigor# # create_spiral.py - create spiral lines # Tamito KAJIYAMA <26 March 2000> # # Copyright (C) 2000 by Tamito KAJIYAMA # Copyright (C) 2000, 2002 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from math import pi, cos, sin from app import _, PolyBezier, CreatePath, Polar, Point, \ ContAngle, ContSmooth, ContSymmetrical from app.UI.sketchdlg import SKModal from Tkinter import * import unit class CreateStarDlg(SKModal): title = _("Create Spiral") def build_dlg(self): self.var_rotation = IntVar(self.top) self.var_rotation.set(4) label = Label(self.top, text=_("Rotations")) label.grid(column=0, row=0, sticky=E) entry = Entry(self.top, width=15, textvariable=self.var_rotation) entry.grid(column=1, row=0) self.var_radius = StringVar(self.top) self.var_radius.set("100pt") label = Label(self.top, text=_("Radius")) label.grid(column=0, row=1, sticky=E) entry = Entry(self.top, width=15, textvariable=self.var_radius) entry.grid(column=1, row=1) button = Button(self.top, text=_("OK"), command=self.ok) button.grid(column=0, row=2, sticky=W) button = Button(self.top, text=_("Cancel"), command=self.cancel) button.grid(column=1, row=2, sticky=E) def ok(self): self.close_dlg((self.var_rotation.get(), self.var_radius.get())) def create_spiral(context): args = CreateStarDlg(context.application.root).RunDialog() if args is None: return path = apply(create_spiral_path, args) bezier = PolyBezier((path,)) context.main_window.PlaceObject(bezier) 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 import app.Scripting app.Scripting.AddFunction('create_spiral', _("Spiral"), create_spiral, menu = _("Create Objects"), script_type = app.Scripting.AdvancedScript) UniConvertor-1.1.4/src/app/scripts/read_gimp_path.py0000775000076400007640000000635111060500604021221 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1999 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Read a path exported by GIMP's Layers & Channels dialog # # A new command "Read Gimp Path" for the Script menu that reads a path # from a file created with "Export Path" in Gimp's Layers & Channels # dialog and insert it into the document at a fixed position. # # The path is inserted at a fixed position so that if you import several # paths from the same image these paths have the correct relative # position. # # This you might want to change are the position at which the path is # inserted, or scaling it, or placing it interactively. from string import split, lstrip import re import app from app import CreatePath, Point, PolyBezier, Trafo BEZIER_ANCHOR = 1 BEZIER_CONTROL = 2 BEZIER_MOVE = 3 rx_point = re.compile('\ (?P[123])\s+X:\s*(?P[-+]?[0-9]+)\s+Y:\s*(?P[-+]?[0-9]+)') def read_path(filename): path = CreatePath() paths = [path] points = [] file = open(filename) closed = 0 for line in file.readlines(): try: key, rest = split(line, ':', 1) except: continue if key == 'TYPE': rest = lstrip(rest) match = rx_point.match(rest) if match is not None: type = int(match.group('type')) p = Point(float(match.group('x')), float(match.group('y'))) if type == BEZIER_MOVE: if closed and points: path.AppendBezier(points[0], points[1], path.Node(0)) path.ClosePath() points = [] path = CreatePath() paths.append(path) path.AppendLine(p) elif type == BEZIER_ANCHOR: if path.len == 0: path.AppendLine(p) else: if path.Node(-1) == points[0] and points[1] == p: path.AppendLine(p) else: path.AppendBezier(points[0], points[1], p) points = [] elif type == BEZIER_CONTROL: points.append(p) elif key == 'CLOSED': closed = int(rest) if closed and points: if path.Node(-1) == points[0] and points[1] == path.Node(0): path.AppendLine(path.Node(0)) else: path.AppendBezier(points[0], points[1], path.Node(0)) path.ClosePath() return tuple(paths) def read_gimp_path(context, filename = ''): if not filename: filename = context.application.GetOpenFilename() if not filename: return paths = read_path(filename) object = PolyBezier(paths) object.Transform(Trafo(1, 0, 0, -1, 0, 800)) #context.main_window.PlaceObject(object) context.document.Insert(object) app.Scripting.AddFunction('read_gimp_path', 'Read Gimp Path', read_gimp_path, script_type = app.Scripting.AdvancedScript) UniConvertor-1.1.4/src/app/scripts/footprints.py0000775000076400007640000001100311060500604020453 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 2002 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """Demo Script for GNU/LinuxTag and EuroPython 2002. Repeat foot-prints along a path. Usage: Select two objects, one to be used as the left foot-print and another one as the path to place the footprints on. Make sure that the path is in front of the foot print. The script will place copies of the foot print along the path as if someone had walked along the path. """ import app.Scripting from app.Graphics.text import coord_sys_at, PATHTEXT_ROTATE from app import Group, Translation, Scale def foot_prints_along_path(context): doc = context.document objects = doc.SelectedObjects() # The requirement for this script is, as described in the module # doc-string, that exactly two objects are selected and that the # top-most of these is the path. if len(objects) == 2 and objects[1].is_curve: # First, we take the foot print. We copy the foot print since we # have to modify it and we don't want the original to be # affected. foot_print = objects[0].Duplicate() # The rest of the script is easier to write if we have a foot # print located at the origin of the coordinate system that we # can use as a stencil, so we move the copy accordingly. The # Transform method modifies the object in place. That's why it # was important to copy it. r = foot_print.coord_rect foot_length = r.right - r.left foot_print.Transform(Translation(-r.right + foot_length/2, -r.bottom)) # Now the path. The Paths() method of an object returns a tuple # of path-objects if the object can be represented as paths. All # objects whose is_curve attribute is true can be represented as # Paths. In this example script we only look at the first path. path = objects[1].Paths()[0] # arc_lengths returns a list of (LENGTH, POINT) pairs where # POINT is a point on the curve and LENGTH is the arc length # from the start of the curve to that point. The points are # placed so that the curve between successive points can be seen # as a straight line. We'll be using this list to determine the # positions and orientations of the individual foot prints. arc_lengths = path.arc_lengths() # In the loop below, we'll be positioning the foot prints one # after the other from the start of the curve to the end, # alternating between the left and right foot prints. # Total length of the path so that we know when we're done. total_length = arc_lengths[-1][0] # Distance along the path we've already covered. distance = 0 # Count the number of foot prints so that we can mirror produce # left and right footprints. count = 0 # List we put all the copies into. foot_prints = [] # Now loop until we've walked along the whole path. while total_length - distance > foot_length: # Determine the transformation that turns the stencil into a # foot print at the right place with the right orientation. # We can borrow this functionality from the path-text code. # Placing letters along the path is practically the same as # placing foot prints. trafo = coord_sys_at(arc_lengths, distance, PATHTEXT_ROTATE) # Right feet are created by mirroring the left foot which # serves as stencil if count % 2: trafo = trafo(Scale(1, -1)) # Create a transformed copy of the stencil. foot = foot_print.Duplicate() foot.Transform(trafo) foot_prints.append(foot) # Update the length and the counter distance = distance + foot_length count = count + 1 # As the last step, insert the foot prints into the document as # a group. The Insert method takes care of undo handling. if foot_prints: doc.Insert(Group(foot_prints)) app.Scripting.AddFunction('foot_prints_along_path', 'Foot Prints', foot_prints_along_path, script_type = app.Scripting.AdvancedScript) UniConvertor-1.1.4/src/app/scripts/simple_separation.py0000775000076400007640000002040211060500604021765 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 2001, 2002 by Intevation GmbH # Author: Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """Save a document in several EPS files, one for each color used. The resulting set of EPS files can be considered a simple form of color separations. Limitations: - Not all fill types are supported. At the moment, only solid fills and empty fills are implemented. If a drawing contains unsupported fills, an appropriate message is displayed. - Raster images and EPS objects are not supported. """ import os from app import _, SolidPattern, StandardColors, CreateListUndo, Undo, \ PostScriptDevice from app.utils import os_utils from app.UI.sketchdlg import SKModal from Tkinter import StringVar, Frame, Label, Button, Entry, E, W, X, TOP, \ BOTTOM, LEFT, BOTH black = StandardColors.black white = StandardColors.white # exception and messages used for unsupported features class UnsupportedFeature(Exception): pass unsupported_pattern = _("""\ The drawing contains unsupported fill or line patterns. Only solid colors are supported. """) unsupported_type_raster = _("""\ The drawing contains raster images, which are not supported for separation """) unsupported_type_raster = _("""\ The drawing contains embedded EPS files which are not supported for separation """) class ColorExtractor: """Helper class to extract determine the colors used in a drawing. This is a class because the document object's WalkHierarchy method doesn't provide a way to pass an additional parameter through to the callback, so we use a bound method. """ def __init__(self): self.colors = {} def extract_colors(self, object): """Extract the colors of the solid fill and line patterns of object. If the object has non-empty non-solid patterns raise UnsupportedFeature exception """ if object.has_properties: properties = object.Properties() pattern = properties.fill_pattern if pattern.is_Solid: self.colors[pattern.Color()] = 1 elif not pattern.is_Empty: raise UnsupportedFeature(unsupported_pattern) pattern = properties.line_pattern if pattern.is_Solid: self.colors[pattern.Color()] = 1 elif not pattern.is_Empty: raise UnsupportedFeature(unsupported_pattern) else: if object.is_Image: raise UnsupportedFeature(unsupported_type_raster) elif object.is_Eps: raise UnsupportedFeature(unsupported_type_eps) class CreateSeparation: """Helperclass to create a separation. This is a class so that we can use a method as the callback function for the document's WalkHierarchy method. """ def __init__(self, color): """Initialze separator. color is the color to make black.""" self.color = color self.undo = [] def change_color(self, object): if object.has_properties: properties = object.Properties() pattern = properties.fill_pattern if pattern.is_Solid: if pattern.Color() == self.color: pattern = SolidPattern(black) else: pattern = SolidPattern(white) undo = properties.SetProperty(fill_pattern = pattern) self.undo.append(undo) pattern = properties.line_pattern if pattern.is_Solid: if pattern.Color() == self.color: pattern = SolidPattern(black) else: pattern = SolidPattern(white) undo = properties.SetProperty(line_pattern = pattern) self.undo.append(undo) def undo_changes(self): Undo(CreateListUndo(self.undo)) filename_dialog_text = _("""The drawing has %d unique colors. Please choose a basename for the separation files. There will be one file for each color with a name of the form basename-XXXXXX.ps where XXXXXX is the hexadecimal color value. """) class SimpleSeparationDialog(SKModal): title = "Simple Separation" def __init__(self, master, num_colors, basename): self.num_colors = num_colors self.basename = basename SKModal.__init__(self, master) def build_dlg(self): self.var_name = StringVar(self.top) self.var_name.set(self.basename) frame = Frame(self.top) frame.pack(side = TOP, fill = BOTH, expand = 1) text = filename_dialog_text % self.num_colors label = Label(frame, text = text, justify = "left") label.grid(column = 0, row = 0, sticky = E, columnspan = 2) label = Label(frame, text = _("Basename:")) label.grid(column = 0, row = 1, sticky = E) entry = Entry(frame, width = 15, textvariable = self.var_name) entry.grid(column = 1, row = 1) frame = Frame(self.top) frame.pack(side = BOTTOM, fill = X, expand = 1) button = Button(frame, text = _("OK"), command = self.ok) button.pack(side = LEFT, expand = 1) button = Button(frame, text = _("Cancel"), command = self.cancel) button.pack(side = LEFT, expand = 1) def ok(self): self.close_dlg(self.var_name.get()) def hexcolor(color): """Return the color in hexadecimal form""" return "%02x%02x%02x" \ % (255 * color.red, 255 * color.green, 255 * color.blue) def draw_alignment_marks(psdevice, bbox, length, distance): """Draw alignment marks onto the postscript device""" llx, lly, urx, ury = bbox # the marks should be black psdevice.SetLineColor(StandardColors.black) psdevice.SetLineAttributes(1) # lower left corner psdevice.DrawLine((llx - distance, lly), (llx - distance - length, lly)) psdevice.DrawLine((llx, lly - distance), (llx, lly - distance - length)) # lower right corner psdevice.DrawLine((urx + distance, lly), (urx + distance + length, lly)) psdevice.DrawLine((urx, lly - distance), (urx, lly - distance - length)) # upper right corner psdevice.DrawLine((urx + distance, ury), (urx + distance + length, ury)) psdevice.DrawLine((urx, ury + distance), (urx, ury + distance + length)) # upper left corner psdevice.DrawLine((llx - distance, ury), (llx - distance - length, ury)) psdevice.DrawLine((llx, ury + distance), (llx, ury + distance + length)) def simple_separation(context): doc = context.document # first determine the number of unique colors in the document color_extractor = ColorExtractor() try: doc.WalkHierarchy(color_extractor.extract_colors, all = 1) except UnsupportedFeature, value: context.application.MessageBox(_("Simple Separation"), value) return colors = color_extractor.colors.keys() doc_bbox = doc.BoundingRect() filename = doc.meta.fullpathname if filename is None: filename = "unnamed" basename = os.path.splitext(filename)[0] # ask the user for a filename dlg = SimpleSeparationDialog(context.application.root, len(colors), basename) basename = dlg.RunDialog() if basename is None: # the dialog was cancelled return # the EPS bbox is larger than the doc's bbox because of the # alignment marks align_distance = 6 align_length = 12 grow = align_length + align_distance llx, lly, urx, ury = doc_bbox ps_bbox = (llx - grow, lly - grow, urx + grow, ury + grow) for color in colors: separator = CreateSeparation(color) try: # do this in a try-finall to make sure the document colors # get restored even if something goes wrong doc.WalkHierarchy(separator.change_color) filename = basename + '-' + hexcolor(color) + '.ps' ps_dev = PostScriptDevice(filename, as_eps = 1, bounding_box = ps_bbox, For = os_utils.get_real_username(), CreationDate = os_utils.current_date(), Title = os.path.basename(filename), document = doc) doc.Draw(ps_dev) draw_alignment_marks(ps_dev, doc_bbox, align_length, align_distance) ps_dev.Close() finally: separator.undo_changes() import app.Scripting app.Scripting.AddFunction('simple_separation', _("Simple Separation"), simple_separation, script_type = app.Scripting.AdvancedScript) UniConvertor-1.1.4/src/app/scripts/abut_horizontal.py0000775000076400007640000000645011060500604021462 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1999 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # This is a reimplementation of the Arrange| Abut Horizontal command as # a safe script. # # This script uses these methods and concepts: # # SelectedObjects(): # # The document method SelectedObjects returns a list of all # currently selected objects. If no objects are selected the list # is empty. # # coord_rect: # # The coord_rect attribute of a graphics object is an axis-aligned # rectangle that contains the entire object. This is not exactly # the same as a bounding rectangle, as explained in more detail in # the developer's guide (Doc/devguide.html). # # The coord_rect is a rect object and has four read only # attributes, left, bottom, right, and top, that contain the # coordinates of the rectangle. # # Point(x, y): # # Point is a function that returns a point object with the # coordinates x and y. point objects are immutable objects that # overload the usual arithmetic operators such as +, -, * among # others. * is the inner product or multiplication with a scalar # depending on the operand types. Thy are interpreted as points or # vectors depending on the context in which they are used. # # Point objects are described in more detail in the developer's # guide. # # Translate(offset): # # The translate method of a graphics object translates (i.e. # moves) the object by offset, which has to be a point object. # # This method modifies the object involved and an advanced script # would have to deal with undo when using this method. # # First, we need the Sketch specific function Point. Point creates # point-objects, that represent a 2D-point or vector. from app import Point # define the abut_horizontal function. As stated above, it has to accept # a single argument, customarily called 'context'. def abut_horizontal(context): # We have to refer to the document frequently, so save some typing # by binding it to the local variable doc. doc = context.document pos = [] for obj in doc.SelectedObjects(): rect = obj.coord_rect pos.append((rect.left, rect.top, rect.right - rect.left, obj)) if pos: # pos is empty (= false) if no object is selected pos.sort() start, top, width, ob = pos[0] next = start + width for left, top, width, obj in pos[1:]: obj.Translate(Point(next - left, 0)) next = next + width # register script import app.Scripting app.Scripting.AddFunction('abut_horizontal', 'Abut Horizontal', abut_horizontal, menu = 'Arrange') UniConvertor-1.1.4/src/app/scripts/unit.py0000775000076400007640000000253311060500604017233 0ustar igorigor# # unit.py - a module for unit conversion # Tamito KAJIYAMA <26 March 2000> # Copyright (C) 2000 by Tamito KAJIYAMA # Copyright (C) 2000 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # import re import string from app.Lib.units import unit_dict def convert(s): "Convert S (representing a value and unit) into a value in point." match = re.search('[^0-9]+$', s) if match: value, unit = s[:match.start()], s[match.start():] value = string.atof(value) unit = string.strip(unit) if unit_dict.has_key(unit): value = value * unit_dict[unit] elif unit: raise ValueError, "unsupported unit: " + unit else: value = string.atof(s) return value UniConvertor-1.1.4/src/app/scripts/export_raster.py0000775000076400007640000001752511060500604021164 0ustar igorigor# Sketch - A Python-based interactive drawing program # export_raster script # Copyright (C) 1999, 2000, 2002, 2003 by Bernhard Herzog # 6.12.2000 improved by Bernhard Reiter with Help form Bernhard # (used create_star.py by Tamito KAJIYAMA as an example to # build the dialog) # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Export Sketch drawings as raster images using ghostscript to render # them # # This script adds the following to the Script menu: # # Export Raster Renders the drawing on a white background # # You can add more with fixed parameters, if you need them frequently # see the comments below, one example is enabled. # # Both commands prompt for a filename. The output file format is # determined by the filename extension and should be something your PIL # installation can handle. For the alpha version this should be PNG or # some other format that can handle an alpha channel. # import os, tempfile import PIL.Image, PIL.ImageChops import app.Scripting from app import _, PostScriptDevice # for parameter dialogs from app.UI.sketchdlg import SKModal from Tkinter import * class CreateRasterParametersDlg(SKModal): "Create Tk Dialog to ask for raster parameters." title = _("Choose Raster Parameters") def build_dlg(self): self.var_ppi = IntVar(self.top) self.var_ppi.set(72) label = Label(self.top, text=_("ppi")) label.grid(column=0, row=0, sticky=E) entry = Entry(self.top, width=15, textvariable=self.var_ppi) entry.grid(column=1, row=0) self.var_alpha = BooleanVar(self.top) self.var_alpha.set(1) label = Label(self.top, text=_("w. Transparency")) label.grid(column=0, row=1, sticky=E) entry = Checkbutton(self.top, variable=self.var_alpha) entry.grid(column=1, row=1) self.var_use_bbox = BooleanVar(self.top) self.var_use_bbox.set(0) label = Label(self.top, text=_("use BB information")) label.grid(column=0, row=2, sticky=E) entry = Checkbutton(self.top, variable=self.var_use_bbox) entry.grid(column=1, row=2) button = Button(self.top, text=_("OK"), command=self.ok) button.grid(column=0, row=3, sticky=W) button = Button(self.top, text=_("Cancel"), command=self.cancel) button.grid(column=1, row=3, sticky=E) def ok(self): self.close_dlg((self.var_ppi.get(), self.var_alpha.get(),self.var_use_bbox.get())) def export_raster_more_interactive(context, alpha = 0, use_bbox = 0, render_ppi=72): "Get Parameter per dialog and run export_raster_interactive()" parms = CreateRasterParametersDlg(context.application.root).RunDialog() if parms is None: return else: render_ppi=parms[0] alpha=parms[1] use_bbox=parms[2] return export_raster_interactive(context,alpha,use_bbox,render_ppi) def make_ps(document): file = tempfile.mktemp('.ps') device = PostScriptDevice(file, as_eps = 0, document = document) document.Draw(device) device.Close() return file def render_ps(filename, resolution, width, height, orig_x = 0, orig_y = 0, prolog = '', antialias = '', gsdevice = 'ppmraw'): if prolog: prolog = '-c ' + '"' + prolog + '"' if antialias: antialias = ("-dTextAlphaBits=%d -dGraphicsAlphaBits=%d" % (antialias, antialias)) else: antialias = "" orig_x = -orig_x orig_y = -orig_y temp = tempfile.mktemp() try: gs_cmd = ('gs -dNOPAUSE -g%(width)dx%(height)d -r%(resolution)d ' '-sOutputFile=%(temp)s %(antialias)s ' '-sDEVICE=%(gsdevice)s -q %(prolog)s ' '-c %(orig_x)f %(orig_y)f translate ' '-f%(filename)s -c quit') gs_cmd = gs_cmd % locals() os.system(gs_cmd) image = PIL.Image.open(temp) image.load() return image finally: try: os.unlink(temp) except: pass def export_raster(context, filename, resolution, use_bbox, format = None, antialias = None): # instead of the page size one could also use the bounding box # (returned by the BoundingRect method). if use_bbox: left, bottom, right, top = context.document.BoundingRect() width = right - left height = top - bottom x = left; y = bottom else: width, height = context.document.PageSize() x = y = 0 width = round(width * resolution / 72.0) height = round(height * resolution / 72.0) temp = make_ps(context.document) try: image = render_ps(temp, resolution, width, height, orig_x = x, orig_y = y, antialias = antialias) finally: os.unlink(temp) image.save(filename, format = format) alpha_prolog = "/setrgbcolor {pop pop pop 0 0 0 setrgbcolor} bind def \ /setgray { pop 0 setgray} bind def \ /setcmykcolor { pop pop pop pop 0 0 0 1.0 setcmykcolor} bind def " def export_alpha(context, filename, resolution, use_bbox = 0): if use_bbox: left, bottom, right, top = context.document.BoundingRect() width = right - left height = top - bottom x = left; y = bottom else: width, height = context.document.PageSize() x = y = 0 ps = make_ps(context.document) width = round(width * resolution / 72.0) height = round(height * resolution / 72.0) rgb = render_ps(ps, resolution, width, height, orig_x = x, orig_y = y, antialias = 2) alpha = render_ps(ps, resolution, width, height, orig_x = x, orig_y = y, antialias = 2, prolog = alpha_prolog, gsdevice = 'pgmraw') alpha = PIL.ImageChops.invert(alpha) rgb = rgb.convert('RGBA') rgb.putalpha(alpha) rgb.save(filename) filelist = [(_("Portable Pixmap"), '.ppm'), (_("Portable Graymap"), '.pgm'), (_("Jpeg"), '.jpg'), (_("Portable Network Graphics"), '.png')] def export_raster_interactive(context, alpha = 0, use_bbox = 0, render_ppi=72): # popup a filedialog and export the document doc = context.document # construct the tk filetypes list extensions = {} for text, ext in filelist: extensions[ext] = 1 # determine a default filename basename = os.path.splitext(doc.meta.filename)[0] if alpha: default_ext = '.png' # shift png up in filetypes so it is displayed accordingly filetypes=tuple(filelist[-1:]+filelist[1:-1]) else: default_ext = '.ppm' filetypes=tuple(filelist) filename = context.application.GetSaveFilename( title = _("Export Raster"), filetypes = filetypes, initialdir = doc.meta.directory, initialfile = basename + default_ext) if filename: ext = os.path.splitext(filename)[1] if extensions.has_key(ext): if alpha: export_alpha(context, filename, render_ppi, use_bbox) else: export_raster(context, filename, render_ppi, use_bbox) else: message = _("unknown extension %s") % ext context.application.MessageBox(title = _("Export Raster"), message = message) app.Scripting.AddFunction('export_raster', _("Export Raster"), export_raster_more_interactive, script_type = app.Scripting.AdvancedScript) #app.Scripting.AddFunction('export_raster', 'Export Raster Alpha (Default)', # export_raster_interactive, args = (1,0), # script_type = app.Scripting.AdvancedScript) app.Scripting.AddFunction('export_raster', _("Export Raster Alpha (100ppi)"), export_raster_interactive, args = (1,0,100), script_type = app.Scripting.AdvancedScript) #app.Scripting.AddFunction('export_raster', 'Export Raster Alpha (120ppi)', # export_raster_interactive, args = (1,0,120), # script_type = app.Scripting.AdvancedScript) UniConvertor-1.1.4/src/app/scripts/spread.py0000775000076400007640000001306511060500604017534 0ustar igorigor# -*- coding: utf-8 -*- # Sketch script for spreading selected objects ("distribute" in XFig) # (c) 2000 Michael Loßin # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # these are based on abut_*.py from app import _, Point import app.Scripting # spread objects horizontally (cascade left) def spread_h_casc_l(context): pos = [] for obj in context.document.SelectedObjects(): pos.append((obj.coord_rect.left, obj)) l = len(pos) - 1 if l > 1: pos.sort() left1, ob = pos[0] left2, ob = pos[-1] skip = (left2 - left1) / l next = left1 + skip for left, obj in pos[1:-1]: obj.Translate(Point(next - left, 0)) next = next + skip app.Scripting.AddFunction('spread_h_casc_l', _("Spread Horizontal (cascade left)"), spread_h_casc_l, menu = _("Arrange")) # spread objects horizontally (cascade right) def spread_h_casc_r(context): pos = [] for obj in context.document.SelectedObjects(): pos.append((obj.coord_rect.right, obj)) l = len(pos) - 1 if l > 1: pos.sort() right1, ob = pos[0] right2, ob = pos[-1] skip = (right2 - right1) / l next = right1 + skip for right, obj in pos[1:-1]: obj.Translate(Point(next - right, 0)) next = next + skip app.Scripting.AddFunction('spread_h_casc_r', _("Spread Horizontal (cascade right)"), spread_h_casc_r, menu = _("Arrange")) # spread objects horizontally (equidistant centers) def spread_h_center(context): pos = [] for obj in context.document.SelectedObjects(): rect = obj.coord_rect pos.append(((rect.left + rect.right) / 2, obj)) l = len(pos) - 1 if l > 1: pos.sort() center1, ob = pos[0] center2, ob = pos[-1] gap = (center2 - center1) / l next = center1 + gap for center, obj in pos[1:-1]: obj.Translate(Point(next - center, 0)) next = next + gap app.Scripting.AddFunction('spread_h_center', _("Spread Horizontal (center)"), spread_h_center, menu = _("Arrange")) # spread objects horizontally (gaps/overlaps of equal width) def spread_h_bbox(context): pos = [] sum = 0 for obj in context.document.SelectedObjects(): rect = obj.coord_rect width = rect.right - rect.left pos.append((rect.left, width, obj)) sum = sum + width l = len(pos) - 1 if l > 1: pos.sort() start, width1, ob = pos[0] end, width2, ob = pos[-1] gap = (end + width2 - start - sum) / l next = start + width1 + gap for left, width, obj in pos[1:-1]: obj.Translate(Point(next - left ,0)) next = next + width + gap app.Scripting.AddFunction('spread_h_bbox', _("Spread Horizontal (bbox)"), spread_h_bbox, menu = _("Arrange")) # spread objects vertically (cascade bottom) def spread_v_casc_b(context): pos = [] for obj in context.document.SelectedObjects(): pos.append((obj.coord_rect.bottom, obj)) l = len(pos) - 1 if l > 1: pos.sort() pos.reverse() bottom1, ob = pos[0] bottom2, ob = pos[-1] skip = (bottom1 - bottom2) / l next = bottom1 - skip for bottom, obj in pos[1:-1]: obj.Translate(Point(0, next - bottom)) next = next - skip app.Scripting.AddFunction('spread_v_casc_b', _("Spread Vertical (cascade bottom)"), spread_v_casc_b, menu = _("Arrange")) # spread objects vertically (cascade top) def spread_v_casc_t(context): pos = [] for obj in context.document.SelectedObjects(): pos.append((obj.coord_rect.top, obj)) l = len(pos) - 1 if l > 1: pos.sort() pos.reverse() top1, ob = pos[0] top2, ob = pos[-1] skip = (top1 - top2) / l next = top1 - skip for top, obj in pos[1:-1]: obj.Translate(Point(0, next - top)) next = next - skip app.Scripting.AddFunction('spread_v_casc_t', _("Spread Vertical (cascade top)"), spread_v_casc_t, menu = _("Arrange")) # spread objects vertically (equidistant centers) def spread_v_center(context): pos = [] for obj in context.document.SelectedObjects(): rect = obj.coord_rect pos.append(((rect.top + rect.bottom) / 2, obj)) l = len(pos) - 1 if l > 1: pos.sort() pos.reverse() center1, ob = pos[0] center2, ob = pos[-1] gap = (center1 - center2) / l next = center1 - gap for center, obj in pos[1:-1]: obj.Translate(Point(0, next - center)) next = next - gap app.Scripting.AddFunction('spread_v_center', _("Spread Vertical (center)"), spread_v_center, menu = _("Arrange")) # spread objects vertically (gaps/overlaps of equal height) def spread_v_bbox(context): pos = [] sum = 0 for obj in context.document.SelectedObjects(): rect = obj.coord_rect height = rect.top - rect.bottom pos.append((rect.top, height, obj)) sum = sum + height l = len(pos) - 1 if l > 1: pos.sort() pos.reverse() start, height1, ob = pos[0] end, height2, ob = pos[-1] gap = (start - end + height2 - sum) / l next = start - height1 - gap for top, height, obj in pos[1:-1]: obj.Translate(Point(0, next - top)) next = next - height - gap app.Scripting.AddFunction('spread_v_bbox', _("Spread Vertical (bbox)"), spread_v_bbox, menu = _("Arrange")) UniConvertor-1.1.4/src/app/scripts/inspect_beziers.py0000775000076400007640000000625511060500604021451 0ustar igorigor# # inspect_beziers - examine bezier polygons # Tamito KAJIYAMA <26 March 2000> # Copyright (C) 2000 by Tamito KAJIYAMA # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # from app import Bezier, Line, ContAngle, ContSmooth, ContSymmetrical from Tkinter import * class Viewer(Toplevel): type_names = {Line: "Line", Bezier: "Bezier"} cont_names = {ContAngle: "ContAngle", ContSmooth: "ContSmooth", ContSymmetrical: "ContSymmetrical"} def __init__(self, context): Toplevel.__init__(self, context.application.root) self.title("Inspect Beziers") self.document = context.document frame = Frame(self) frame.pack(side=BOTTOM, fill=X) button = Button(frame, text="Update", command=self.update) button.pack(side=LEFT) button = Button(frame, text="Dismiss", command=self.destroy) button.pack(side=RIGHT) sb = Scrollbar(self) sb.pack(side=RIGHT, fill=Y) self.text = Text(self, width=85, height=25, yscrollcommand=sb.set) self.text.pack(side=LEFT, fill=BOTH, expand=1) sb.config(command=self.text.yview) self.update() def update(self): self.text.delete("1.0", END) n = 0 for obj in self.document.SelectedObjects(): if not obj.is_Bezier: continue self.text.insert(END, "Bezier #%d\n" % (n + 1)) paths = obj.Paths() for i in range(len(paths)): self.text.insert(END, " path #%d\n" % (i + 1)) for j in range(paths[i].len): s = apply(self.format_segment, paths[i].Segment(j)) self.text.insert(END, " #%d %s\n" % ((j + 1), s)) self.text.insert(END, "\n") n = n + 1 if n == 0: self.text.insert(END, "No bezier polygons selected.") def format_segment(self, type, controls, point, cont): if type == Line: controls = "" elif controls: controls = " ((%.2f, %.2f), (%.2f, %.2f))" % \ (controls[0].x, controls[0].y, controls[1].x, controls[1].y) else: controls = " ()" point = " (%.2f, %.2f) " % (point.x, point.y) type = self.type_names[type] cont = self.cont_names[cont] return type + controls + point + cont import app.Scripting app.Scripting.AddFunction('inspect_beziers', 'Inspect Beziers', Viewer, script_type=app.Scripting.AdvancedScript) UniConvertor-1.1.4/src/app/scripts/create_text.py0000775000076400007640000000343211060500604020562 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1999, 2000 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import app.Scripting from app import SimpleText, Translation, SolidPattern, StandardColors, \ GetFont def create_text(context): # Create the text 'xyzzy' at 100,100. The first parameter to the # constructor is an affine transformation. text = SimpleText(Translation(100, 100), "xyzzy") # Set the font to 36pt Times-Bold and fill with solid green. # The text object is modified by this method, but the text object is # not yet part of the document, so we don't have to deal with undo # here. text.SetProperties(fill_pattern = SolidPattern(StandardColors.green), font = GetFont('Times-Bold'), font_size = 36) # Finally, insert the text object at the top of the current layer # and select it. Like all public document methods that modify the # document, the Insert method takes care of undo information itself. context.document.Insert(text) app.Scripting.AddFunction('create_text', 'Create Text', create_text, script_type = app.Scripting.AdvancedScript) UniConvertor-1.1.4/src/app/scripts/select_same_fill_color.py0000775000076400007640000000630011060500604022740 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1999 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Select all objects in the current layer with the same fill color as # the currently selected object. This is implemented as an advanced # script. It doesn't have to deal with undo because it only changes the # set of currently selected objects and not the objects themselves. # # Conceps and Methods: # # CurrentProperties(): # # This document method returns the properties of the currently # selected object. If more than one objects are selected or no # object is selected or the selected object doesn't have # properties, a special property object EmptyProperties is # returned. # # Now, what does this mean? Objects like rectangles, text and # curves have graphics properties like fill or line patters, line # width or font whatever is applicable for that particular type. # Some obejcts have no graphics properties at all, e.g. groups, # while others can only have some properties, e.g. text objects # currently can't have a line color (this is really a limitation # in X11, PostScript wouldn't have problems with that). # # All of the properties are stored in a properties object, and # that is what the CurrentProperties() method returns. Such a # properties object has three methods that indicate whether the # fill-, line- or text properties are valid: HasFill(), HasLine() # and HasFont(). Only if one of those methods returns true, can # you safely access the respective properties. The properties are # publicly readable attributes of the properties object. For the # EmptyProperties object that may be returned by # CurrentProperties(), all of these methods return false. # import time def select_same_fill_color(context): doc = context.document select = [] properties = doc.CurrentProperties() if properties.HasFill(): color = properties.fill_pattern.Color() layer = doc.ActiveLayer() doc.SelectNone() for obj in layer.GetObjects(): if obj.has_fill: prop = obj.Properties() if prop.HasFill() and prop.fill_pattern.is_Solid \ and color == prop.fill_pattern.Color(): select.append(obj) doc.SelectObject(select, Sketch.const.SelectAdd) # register script import Sketch.Scripting Sketch.Scripting.AddFunction('select_same_fill_color', 'Select Same Fill Color', select_same_fill_color, script_type = Sketch.Scripting.AdvancedScript) UniConvertor-1.1.4/src/app/scripts/create_star.py0000775000076400007640000001222511060500604020547 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1999, 2000, 2002 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from math import pi from Tkinter import IntVar, DoubleVar, Entry, Label, Button, Frame import app.Scripting from app import _, SolidPattern, StandardColors, PolyBezier, CreatePath, \ Point, Polar from app.UI.sketchdlg import SKModal # # # def create_star_path(corners, step, radius): # create a star-like polygon. center = Point(300, 400) radius = 100 angle = step * 2 * pi / corners # create an empty path and append the line segments path = CreatePath() for i in range(corners): p = Polar(radius, angle * i + pi / 2) path.AppendLine(p) # close the path. path.AppendLine(path.Node(0)) path.ClosePath() return path # # A modal dialog that asks for the parameters # # SKModal is the baseclass Sketch uses for modal dialogs. It provides # some standard functionality for all modal dialogs. # # The intended use of a sub-class of SKModal is to instantiate it and # call its RunDialog method. # # RunDialog pops up the dialog and returns when the user either cancels # the dialog or presses the OK button. Its return value is None if the # dialog was canceled or whatever object was passed to the close_dlg # method to close the dialog in response to the click on the OK-button. # See the method ok below. # class CreateStarDlg(SKModal): title = _("Create Star") def __init__(self, master, **kw): # This constructor is here just for illustration purposes; it's # not really needed here, as it simply passes all parameters on # to the base class' constructor. # # The parameter master is the window this dialog belongs to. It # should normally be the top-level application window. apply(SKModal.__init__, (self, master), kw) def build_dlg(self): # The SKModal constructor automatically calls this method to # create the widgets in the dialog. # # self.top is the top-level window of the dialog. All widgets of # the dialog must contained in it. top = self.top # The rest is normal Tkinter code. self.var_corners = IntVar(top) self.var_corners.set(5) label = Label(top, text = _("Corners"), anchor = 'e') label.grid(column = 0, row = 0, sticky = 'ew') entry = Entry(top, textvariable = self.var_corners, width = 15) entry.grid(column = 1, row = 0, sticky = 'ew') self.var_steps = IntVar(top) self.var_steps.set(2) label = Label(top, text = _("Steps"), anchor = 'e') label.grid(column = 0, row = 1, sticky = 'ew') entry = Entry(top, textvariable = self.var_steps, width = 15) entry.grid(column = 1, row = 1, sticky = 'ew') self.var_radius = DoubleVar(top) self.var_radius.set(100) label = Label(top, text = _("Radius"), anchor = 'e') label.grid(column = 0, row = 2, sticky = 'ew') entry = Entry(top, textvariable = self.var_radius, width = 15) entry.grid(column = 1, row = 2, sticky = 'ew') but_frame = Frame(top) but_frame.grid(column = 0, row = 3, columnspan = 2) button = Button(but_frame, text = _("OK"), command = self.ok) button.pack(side = 'left', expand = 1) # The self.cancel method is provided by the base class and # cancels the dialog. button = Button(but_frame, text = _("Cancel"), command = self.cancel) button.pack(side = 'right', expand = 1) def ok(self, *args): # This method is bound to the OK-button. Its purpose is to # collect the values of the various edit fields and pass them as # one parameter to the close_dlg method. # # close_dlg() saves its parameter and closes the dialog. corners = self.var_corners.get() steps = self.var_steps.get() radius = self.var_radius.get() self.close_dlg((corners, steps, radius)) def create_star(context): # Instantiate the modal dialog... dlg = CreateStarDlg(context.application.root) # ... and run it. result = dlg.RunDialog() if result is not None: # if the result is not None, the user pressed OK. Now constuct # the star-path... corners, steps, radius = result path = create_star_path(corners, steps, radius) # ... and create the bezier object. The parameter to the # constructor must be a tuple of paths bezier = PolyBezier((path,)) # Set the line color to blue, the line width to 4pt bezier.SetProperties(line_pattern = SolidPattern(StandardColors.blue), line_width = 4) # and insert it into the document context.main_window.PlaceObject(bezier) app.Scripting.AddFunction('create_star', _("Create Star"), create_star, script_type = app.Scripting.AdvancedScript) UniConvertor-1.1.4/src/app/scripts/usersguide.py0000775000076400007640000000045511060500604020434 0ustar igorigor# # Scripts from the users guide # def hello_world(context): context.application.MessageBox(title = "My Script", message = "Hello World!") # register scripts import Sketch.Scripting Sketch.Scripting.AddFunction('ug_hello_world', 'Hello World', hello_world, menu = "User's Guide") UniConvertor-1.1.4/src/app/scripts/reload_image.py0000775000076400007640000000533311060500604020665 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 2002 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """User Script that reloads the image data of an external image or updates the preview of an EPS file. """ import app from app import _ import app.Scripting from app.Graphics import external, eps def reload_image(context): image = context.document.CurrentObject() if image is not None and isinstance(image, external.ExternalGraphics): # Don't try this at home :) It pokes around in the internals of # Sketch! olddata = image.data filename = olddata.Filename() oldrect = image.bounding_rect # first, remove the old object from the cache. if olddata.stored_in_cache \ and external.instance_cache.has_key(filename): del external.instance_cache[filename] olddata.stored_in_cache = 0 # now we can load the data again the normal way because it's not # in the cache anymore. if image.is_Eps: data = eps.load_eps(filename) else: data = app.load_image(filename) # replace the old data object with the new one. Normally we # would have to handle the undo info returned. Here we just # discard it so that the reload won't be in the history. image.SetData(data) # some house keeping tasks that are necessary because the sort # of thing we're doing here, i.e. modifying an object without # undo information etc., wasn't anticipated: # to make sure that the bboxes get recomputed etc, call the # _changed method. SetData should probably do that # automatically, but currently it doesn't image._changed() # make sure the object itself is properly redrawn context.document.AddClearRect(oldrect) context.document.AddClearRect(image.bounding_rect) # make sure the selection's idea of the bounding rect is updated # too and have the canvas update the handles context.document.selection.ResetRectangle() context.main_window.canvas.update_handles() app.Scripting.AddFunction('reload_image', _("Reload Image"), reload_image, script_type = app.Scripting.AdvancedScript) UniConvertor-1.1.4/src/app/scripts/__init__.py0000775000076400007640000000000011060500604017776 0ustar igorigorUniConvertor-1.1.4/src/app/scripts/average_points.py0000775000076400007640000001060711060500604021263 0ustar igorigor# # average_points.py - average coordinates of selected points # Tamito KAJIYAMA <24 March 2000> # Copyright (C) 2000 by Tamito KAJIYAMA # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from app import CreatePath, Point, _ from app.UI.sketchdlg import SKModal from Tkinter import * AVERAGE_BOTH = 0 AVERAGE_X = 1 AVERAGE_Y = 2 class AverageDialog(SKModal): title = "Average Points" def build_dlg(self): self.var_which = IntVar(self.top) self.var_which.set(AVERAGE_X) label = Label(self.top, text="Average", anchor=W) label.pack(fill=X) button = Radiobutton(self.top, text="X Coordinates", variable=self.var_which, value=AVERAGE_X, anchor=W) button.pack(fill=X) button = Radiobutton(self.top, text="Y Coordinates", variable=self.var_which, value=AVERAGE_Y, anchor=W) button.pack(fill=X) button = Radiobutton(self.top, text="Both Coordinates", variable=self.var_which, value=AVERAGE_BOTH, anchor=W) button.pack(fill=X) button = Button(self.top, text="OK", command=self.ok) button.pack(side=LEFT) button = Button(self.top, text="Cancel", command=self.cancel) button.pack(side=RIGHT) def ok(self, *args): self.close_dlg(self.var_which.get()) def average_points(context): # find a bezier polygon selected selection = [] for object in context.document.SelectedObjects(): if not object.is_Bezier: continue selection.append(object) if len(selection) != 1: context.application.MessageBox(title="Average Points", message="Select one polygon.") return None # count selected points object = selection[0] object_paths = object.Paths() npoints = 0 for path in object_paths: for i in range(path.len): if path.SegmentSelected(i): npoints = npoints + 1 if npoints == 0: context.application.MessageBox(title="Average Points", message="Select two or more points.") return None # inquiry parameters which = AverageDialog(context.application.root).RunDialog() if which is None: return None # compute average coordinates of the selected points ax = 0 ay = 0 modified_paths = [] for path in object_paths: modified_paths.append([]) for i in range(path.len): type, controls, point, cont = path.Segment(i) modified_paths[-1].append([type, list(controls), point, cont]) if path.SegmentSelected(i): ax = ax + point.x ay = ay + point.y ax = float(ax) / npoints ay = float(ay) / npoints # translate the selected points for i in range(len(object_paths)): path = object_paths[i] new_path = modified_paths[i] for j in range(path.len): if path.SegmentSelected(j): point = new_path[j][2] if which == AVERAGE_X: new_point = Point(ax, point.y) elif which == AVERAGE_Y: new_point = Point(point.x, ay) else: new_point = Point(ax, ay) new_path[j][2] = new_point offset = point - new_point if len(new_path[j][1]) == 2: new_path[j ][1][1] = new_path[j ][1][1] - offset if j < path.len - 1 and len(new_path[j+1][1]) == 2: new_path[j+1][1][0] = new_path[j+1][1][0] - offset # create new paths new_paths = [] for i in range(len(object_paths)): path = object_paths[i] new_path = CreatePath() for type, controls, point, cont in modified_paths[i]: new_path.AppendSegment(type, tuple(controls), point, cont) if path.closed: new_path.AppendLine(new_path.Node(0)) new_path.ClosePath() new_paths.append(new_path) # set the new paths undo = object.SetPaths(new_paths) # return Undo info return undo def run(context): document = context.document undo = average_points(context) if undo is not None: document.AddUndo(undo) import app.Scripting app.Scripting.AddFunction('average_points', 'Average Points', run, script_type = app.Scripting.AdvancedScript) UniConvertor-1.1.4/src/app/scripts/select_same_line_color.py0000775000076400007640000000621711060500604022750 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1999 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Select all objects in the current layer with the same line color as # the currently selected object. This is implemented as an advanced # script. It doesn't have to deal with undo because it only changes the # set of currently selected objects and not the objects themselves. # # Conceps and Methods: # # CurrentProperties(): # # This document method returns the properties of the currently # selected object. If more than one objects are selected or no # object is selected or the selected object doesn't have # properties, a special property object EmptyProperties is # returned. # # Now, what does this mean? Objects like rectangles, text and # curves have graphics properties like fill or line patters, line # width or font whatever is applicable for that particular type. # Some obejcts have no graphics properties at all, e.g. groups, # while others can only have some properties, e.g. text objects # currently can't have a line color (this is really a limitation # in X11, PostScript wouldn't have problems with that). # # All of the properties are stored in a properties object, and # that is what the CurrentProperties() method returns. Such a # properties object has three methods that indicate whether the # fill-, line- or text properties are valid: HasFill(), HasLine() # and HasFont(). Only if one of those methods returns true, can # you safely access the respective properties. The properties are # publicly readable attributes of the properties object. For the # EmptyProperties object that may be returned by # CurrentProperties(), all of these methods return false. # def select_same_line_color(context): doc = context.document select = [] properties = doc.CurrentProperties() if properties.HasLine(): color = properties.line_pattern.Color() layer = doc.ActiveLayer() doc.SelectNone() for obj in layer.GetObjects(): if obj.has_line: prop = obj.Properties() if prop.HasLine() and color == prop.line_pattern.Color(): select.append(obj) doc.SelectObject(select, Sketch.const.SelectAdd) # register script import Sketch.Scripting Sketch.Scripting.AddFunction('select_same_line_color', 'Select Same Line Color', select_same_line_color, script_type = Sketch.Scripting.AdvancedScript) UniConvertor-1.1.4/src/app/scripts/abut_vertical.py0000775000076400007640000000605511060500604021103 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1999 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # This is a reimplementation of the Arrange| Abut Vertical command. It # is of course very similar to the abut_horizontal script, but this # script is implemented as an advanced script that has to take care of # undo information. # The only new concept introduced here, compared to what is described in # abut_horizontal.py, is how to handle undo information in an advanced # script. # # In Sketch, every method that modifies an object returns an undo # information object. The structure of this object is irrelevant here, # the only important thing to know is that it stores all information # necessary to undo the changes that the method performed. # # In most cases, an advanced script simply passes any undo information # it receives from a method _immediately_ to the document method # AddUndo(). Passing it to the document object _immediately_ is # importan, because if an error occurs and an exception is thrown, the # changes already performed have to be undone to make sure that the # document is in a consistent state again. # # For more infomation about undo handling in Sketch, see the developer's # manual and the Sketch sources, in particular the plugin objects in # Plugins/Objects. # # I don't think that you'll ever have to do anything with undo info # other than passing it immediately to the document, but under some # circumstances it might be necessary to influence the order in which # undo info is executed when the user selects the undo command or even # discard the undo info you get from the methods you called and replace # it with completely new undo info that still undoes the changes but at # a higher level. # from app import Point def abut_vertical(context): doc = context.document pos = [] for obj in doc.SelectedObjects(): rect = obj.coord_rect pos.append((rect.top, -rect.left, rect.top - rect.bottom, obj)) if pos: pos.sort() pos.reverse() start, left, height, ob = pos[0] next = start - height for top, left, height, obj in pos[1:]: off = Point(0, next - top) doc.AddUndo(obj.Translate(off)) next = next - height # register script import app.Scripting import app.Scripting app.Scripting.AddFunction('abut_vertical', 'Abut Vertical', abut_vertical, menu = 'Arrange', script_type = app.Scripting.AdvancedScript) UniConvertor-1.1.4/src/app/managers/0000755000076400007640000000000011211455344016010 5ustar igorigorUniConvertor-1.1.4/src/app/managers/colormanager.py0000775000076400007640000001030211060500604021024 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2007 by Igor E. Novikov # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. import os, app try: from lcms import cmsOpenProfileFromFile,cmsCreateTransform,cmsDoTransform, \ cmsDeleteTransform,cmsCloseProfile,TYPE_RGB_8,TYPE_CMYK_8, \ INTENT_PERCEPTUAL,cmsFLAGS_NOTPRECALC,COLORB, INTENT_RELATIVE_COLORIMETRIC except: app.config.preferences.use_cms=0 class ColorManager: rgb_monitor=None cmyk_rgb=None rgb_cmyk=None cmyk_monitor=None hRGB=None hCMYK=None hMONITOR=None def __init__(self): if app.config.preferences.use_cms: self.refresh_profiles() def refresh_profiles(self): if app.config.preferences.user_rgb_profile and os.path.isfile(app.config.preferences.user_rgb_profile): rgb_file=app.config.user_rgb_profile else: rgb_file=os.path.join(app.config.sk_icc, app.config.preferences.default_rgb_profile) if app.config.preferences.user_cmyk_profile and os.path.isfile(app.config.preferences.user_cmyk_profile): cmyk_file=app.config.preferences.user_cmyk_profile else: cmyk_file=os.path.join(app.config.sk_icc, app.config.preferences.default_cmyk_profile) if app.config.preferences.user_monitor_profile and os.path.isfile(app.config.preferences.user_monitor_profile): monitor_file=app.config.preferences.user_monitor_profile else: monitor_file=os.path.join(app.config.sk_icc, app.config.preferences.default_monitor_profile) self.hRGB = cmsOpenProfileFromFile(rgb_file, "r") self.hCMYK = cmsOpenProfileFromFile(cmyk_file, "r") self.hMONITOR = cmsOpenProfileFromFile(cmyk_file, "r") self.cmyk_rgb = cmsCreateTransform(self.hCMYK, TYPE_CMYK_8, self.hRGB, TYPE_RGB_8, INTENT_RELATIVE_COLORIMETRIC, #INTENT_PERCEPTUAL, cmsFLAGS_NOTPRECALC) self.rgb_cmyk = cmsCreateTransform(self.hRGB, TYPE_RGB_8, self.hCMYK, TYPE_CMYK_8, INTENT_PERCEPTUAL, cmsFLAGS_NOTPRECALC) self.rgb_monitor = cmsCreateTransform(self.hRGB, TYPE_RGB_8, self.hRGB, TYPE_RGB_8, INTENT_PERCEPTUAL, 0) self.cmyk_monitor = cmsCreateTransform(self.hCMYK, TYPE_CMYK_8, self.hRGB, TYPE_RGB_8, INTENT_PERCEPTUAL, cmsFLAGS_NOTPRECALC) def processCMYK(self,c,m,y,k): CMYK = COLORB() CMYK[0] = int(round(c, 3)*255) CMYK[1] = int(round(m, 3)*255) CMYK[2] = int(round(y, 3)*255) CMYK[3] = int(round(k, 3)*255) outRGB = COLORB() outRGB[0] = 0 outRGB[1] = 0 outRGB[2] = 0 cmsDoTransform(self.cmyk_rgb, CMYK, outRGB, 1) return round(outRGB[0]/255.0, 3), round(outRGB[1]/255.0, 3), round(outRGB[2]/255.0, 3) def processRGB(self,r,g,b): RGB = COLORB() RGB[0] = int(round(r, 3)*255) RGB[1] = int(round(g, 3)*255) RGB[2] = int(round(b, 3)*255) outRGB = COLORB() outRGB[0] = 0 outRGB[1] = 0 outRGB[2] = 0 cmsDoTransform(self.rgb_monitor, RGB, outRGB, 1) return round(outRGB[0]/255.0, 3), round(outRGB[1]/255.0, 3), round(outRGB[2]/255.0, 3) def convertRGB(self,r,g,b): RGB = COLORB() RGB[0] = int(round(r, 3)*255) RGB[1] = int(round(g, 3)*255) RGB[2] = int(round(b, 3)*255) CMYK = COLORB() CMYK[0] = 0 CMYK[1] = 0 CMYK[2] = 0 CMYK[3] = 0 cmsDoTransform(self.rgb_cmyk, RGB, CMYK, 1) return round(CMYK[0]/255.0, 3), round(CMYK[1]/255.0, 3), round(CMYK[2]/255.0, 3), round(CMYK[3]/255.0, 3) def convertCMYK(self,c,m,y,k): CMYK = COLORB() CMYK[0] = int(round(c, 3)*255) CMYK[1] = int(round(m, 3)*255) CMYK[2] = int(round(y, 3)*255) CMYK[3] = int(round(k, 3)*255) outRGB = COLORB() outRGB[0] = 0 outRGB[1] = 0 outRGB[2] = 0 cmsDoTransform(self.cmyk_rgb, CMYK, outRGB, 1) return round(outRGB[0]/255.0, 3), round(outRGB[1]/255.0, 3), round(outRGB[2]/255.0, 3) def terminate(self): cmsDeleteTransform(self.cmyk_rgb) cmsDeleteTransform(self.rgb_cmyk) cmsDeleteTransform(self.rgb_monitor) cmsDeleteTransform(self.cmyk_monitor) cmsCloseProfile(self.hCMYK) cmsCloseProfile(self.hRGB) cmsCloseProfile(self.hMONITOR) UniConvertor-1.1.4/src/app/managers/__init__.py0000775000076400007640000000000011060500604020104 0ustar igorigorUniConvertor-1.1.4/src/app/modules/0000755000076400007640000000000011211455344015663 5ustar igorigorUniConvertor-1.1.4/src/app/modules/descr.txt0000664000076400007640000000006611060500604017520 0ustar igorigorAfter build this folder will contains native modules. UniConvertor-1.1.4/src/app/events/0000755000076400007640000000000011211455344015517 5ustar igorigorUniConvertor-1.1.4/src/app/events/skexceptions.py0000775000076400007640000000105611060500601020604 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2003-2006 by Igor E. Novikov # Copyright (C) 1998 by Bernhard Herzog # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. # Sketch specific exceptions class SketchError(Exception): pass class SketchInternalError(SketchError): pass class SketchLoadError(SketchError): pass class SketchIOError(SketchError): def __init__(self, errno, strerror, filename = ''): self.errno = errno self.strerror = strerror self.filename = filename UniConvertor-1.1.4/src/app/events/undodict.py0000775000076400007640000000302511060500601017674 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2003-2006 by Igor E. Novikov # Copyright (C) 1997, 1998 by Bernhard Herzog # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. # # A dictionary with undo capability. This is used for the styles, so # this dictionary assumes, that objects stored in it have SetName() and # Name() methods. # from app.events.undo import NullUndo class UndoDict: def __init__(self): self.dict = {} def __getitem__(self, key): return self.dict[key] def __len__(self): return len(self.dict) def keys(self): return self.dict.keys() def items(self): return self.dict.items() def values(self): return self.dict.values() def has_key(self, key): return self.dict.has_key(key) def SetItem(self, key, item): # Add ITEM to self using KEY. # # Two cases: # # 1. ITEM is stored in self under item.Name(): Store it under # KEY, rename ITEM and remove the old entry. # # 2. ITEM is not stored in self: Store it under KEY and rename # ITEM. if self.dict.has_key(key): if self.dict[key] is item: return NullUndo # name conflict raise ValueError, '%s already used' % key oldname = item.Name() if self.dict.has_key(oldname): del self.dict[oldname] undo = (self.SetItem, oldname, item) else: undo = (self.DelItem, key) item.SetName(key) self.dict[key] = item return undo def DelItem(self, key): item = self.dict[key] del self.dict[key] return (self.SetItem, key, item) UniConvertor-1.1.4/src/app/events/undo.py0000775000076400007640000001321611060500601017033 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2003-2006 by Igor E. Novikov # Copyright (C) 1997, 1998 by Bernhard Herzogg # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. # # Sketch's undo handler # # For a description of the representation and generation of undo # information, see the Developer's Guide in the Doc directory. # from types import StringType, TupleType from app import config preferences = config.preferences from sys import maxint from app.events.warn import warn, INTERNAL, warn_tb from app import _ def Undo(info): # execute a single undoinfo func = info[0] if type(func) == StringType: text = func func = info[1] args = info[2:] else: args = info[1:] text = None try: redo = apply(func, args) if text is not None and callable(redo[0]): return (text,) + redo else: return redo except: warn(INTERNAL, 'Exception in undo:\ninfo: %s\nfunc: %s\nargs: %s', info, func, args) warn_tb(INTERNAL) def UndoList(infos): undoinfo = map(Undo, infos) undoinfo.reverse() return (UndoList, undoinfo) def _get_callable(info): if type(info[0]) == StringType: return info[1] return info[0] def CreateListUndo(infos): infos.reverse() undolist = [] for info in infos: if info is NullUndo: continue if info[0] is UndoList: undolist[len(undolist):] = list(info[-1]) else: undolist.append(info) if undolist: if len(undolist) == 1: return undolist[0] return (UndoList, undolist) return NullUndo def CreateMultiUndo(*infos): if len(infos) > 1: return CreateListUndo(list(infos)) return infos[0] def UndoAfter(undo_info, after_info): return (UndoAfter, Undo(undo_info), Undo(after_info)) # NullUndo: undoinfo that does nothing. Useful when undoinfo has to be # returned but nothing has really changed. def _NullUndo(*ignore): return NullUndo NullUndo = (_NullUndo,) class UndoTypeError(Exception): pass def check_info(info): # Check whether INFO has the correct format for undoinfo. Raise # UndoTypeError if the format is invalid. if type(info) != TupleType: raise UndoTypeError("undo info is not a tuple (%s, type %s)" % (info, type(info))) if len(info) < 1: raise UndoTypeError("undo info is empty tuple") f = info[0] if type(f) == StringType: if len(info) > 1: f = info[1] if not callable(f): raise UndoTypeError("undo info has no callable item") def check_info_silently(info): # Return true if INFO is valid undo information, false otherwise. try: check_info(info) return 1 except UndoTypeError: return 0 class UndoRedo: # A Class that manages lists of of undo and redo information # # It also manages the undo count. This is the number of operations # performed on the document since the last save operation. It # increased by adding undo info, that is, by editing the document or # by Redo, and is decreased by undoing something. The undo count can # be used to determine whether the document was changed since the # last save or not. undo_count = 0 def __init__(self): self.undoinfo = [] self.redoinfo = [] self.SetUndoLimit(preferences.undo_limit) if not self.undo_count: self.undo_count = 0 def SetUndoLimit(self, undo_limit): if undo_limit is None: # unlimited undo. approximate by choosing a very large number undo_limit = maxint if undo_limit >= 1: self.max_undo = undo_limit else: self.max_undo = 1 def CanUndo(self): # Return true, iff an undo operation can be performed. return len(self.undoinfo) > 0 def CanRedo(self): # Return true, iff a redo operation can be performed. return len(self.redoinfo) > 0 def Undo(self): # If undo info is available, perform a single undo and add the # redo info to the redo list. Also, decrement the undo count. if len(self.undoinfo) > 0: self.add_redo(Undo(self.undoinfo[0])) del self.undoinfo[0] self.undo_count = self.undo_count - 1 def AddUndo(self, info, clear_redo = 1): # Add the undo info INFO to the undo list. If the undo list is # longer than self.max_undo, discard the excessive undo info. # Also increment the undo count and discard all redo info. # # The flag CLEAR_REDO is used for internal purposes and inhibits # clearing the redo info if it is false. This flag is only used # by the Redo method. Code outside of this class should not use # this parameter. check_info(info) if info: self.undoinfo.insert(0, info) self.undo_count = self.undo_count + 1 if len(self.undoinfo) > self.max_undo: del self.undoinfo[self.max_undo:] if clear_redo: self.redoinfo = [] def Redo(self): # If redo info is available, perform a single redo and add the # undo info to the undo list. The undo count is taken care of by # the AddUndo method. if len(self.redoinfo) > 0: self.AddUndo(Undo(self.redoinfo[0]), 0) del self.redoinfo[0] def add_redo(self, info): # Internal method: add a single redo info check_info(info) self.redoinfo.insert(0, info) def UndoText(self): # Return a string to describe the operation that would be undone # next, in a format suitable for a menu entry. if self.undoinfo: undolabel = self.undoinfo[0][0] if type(undolabel) == StringType: return _("Undo %s") % undolabel return _("Undo") def RedoText(self): # Return a string to describe the operation that would be redone # next, in a format suitable for a menu entry. if self.redoinfo: redolabel = self.redoinfo[0][0] if type(redolabel) == StringType: return _("Redo %s") % redolabel return _("Redo") def Reset(self): # Forget all undo/redo information self.__init__() def ResetUndoCount(self): self.undo_count = 0 def UndoCount(self): return self.undo_count UniConvertor-1.1.4/src/app/events/warn.py0000775000076400007640000000467111060500601017042 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2003-2006 by Igor E. Novikov # Copyright (C) 1998, 1999, 2001 by Bernhard Herzog # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. import sys, string from types import StringType, DictionaryType import traceback # import config from app import _ INTERNAL = 'INTERNAL' USER = 'USER' #TEMPORAL CONSTANTS TO DO REFACTORING WARN_METHOD = 'dialog' PRINT_INTERNAL_WARNINGS =1 PRINT_DEBUG_MESSAGES = 1 def write_error(message): sys.stderr.write(message) if message and message[-1] != '\n': sys.stderr.write('\n') def flexible_format(format, args, kw): try: if args: text = format % args elif kw: text = format % kw else: text = format except TypeError: if args: text = string.join([format] + map(str, args)) elif kw: text = string.join([format] + map(str, kw.items())) else: text = format return text def warn(_level, _message, *args, **kw): _message = flexible_format(_message, args, kw) if _level == INTERNAL: #TODO: reverse to preferences after refactoring if PRINT_INTERNAL_WARNINGS: # if config.preferences.print_internal_warnings: write_error(_message) else: write_error(_message) return _message def warn_tb(_level, _message = '', *args, **kw): _message = flexible_format(_message, args, kw) if _level == INTERNAL: #TODO: reverse to preferences after refactoring if PRINT_INTERNAL_WARNINGS: # if config.preferences.print_internal_warnings: write_error(_message) traceback.print_exc() else: write_error(_message) traceback.print_exc() return _message def Dict(**kw): return kw _levels = Dict(default = 1, __del__ = 0, Graphics = 1, properties = 0, DND = 1, context_menu = 0, Load = Dict(default = 1, PSK = 1, AI = 1, echo_messages = 1), PS = 1, bezier = 1, styles = 1, tkext = 0, handles = 0, timing = 0) def pdebug(level, message, *args, **kw): #TODO: reverse to preferences after refactoring # if not config.preferences.print_debug_messages: if not PRINT_DEBUG_MESSAGES: return if level: if type(level) == StringType: level = (level,) enabled = _levels for item in level: try: enabled = enabled[item] except: break if type(enabled) == DictionaryType: enabled = enabled['default'] if not enabled: return message = flexible_format(message, args, kw) write_error(message) UniConvertor-1.1.4/src/app/events/connector.py0000775000076400007640000001063111060500601020056 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2003-2006 by Igor E. Novikov # Copyright (C) 1997, 1998 by Bernhard Herzog # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. # # The Connector # from types import MethodType from app.events.skexceptions import SketchInternalError from warn import warn_tb, INTERNAL class ConnectorError(SketchInternalError): pass class Connector: def __init__(self): self.connections = {} def Connect(self, object, channel, function, args): idx = id(object) if self.connections.has_key(idx): channels = self.connections[idx] else: channels = self.connections[idx] = {} if channels.has_key(channel): receivers = channels[channel] else: receivers = channels[channel] = [] info = (function, args) try: receivers.remove(info) except ValueError: pass receivers.append(info) def Disconnect(self, object, channel, function, args): try: receivers = self.connections[id(object)][channel] except KeyError: raise ConnectorError, \ 'no receivers for channel %s of %s' % (channel, object) try: receivers.remove((function, args)) except ValueError: raise ConnectorError,\ 'receiver %s%s is not connected to channel %s of %s' \ % (function, args, channel, object) if not receivers: # the list of receivers is empty now, remove the channel channels = self.connections[id(object)] del channels[channel] if not channels: # the object has no more channels del self.connections[id(object)] def Issue(self, object, channel, *args): #print object, channel, args try: receivers = self.connections[id(object)][channel] except KeyError: return for func, fargs in receivers: try: apply(func, args + fargs) except: warn_tb(INTERNAL, "%s.%s: %s%s", object, channel, func, fargs) def RemovePublisher(self, object): i = id(object) if self.connections.has_key(i): del self.connections[i] # don't use try: del ... ; except KeyError here. That would create a # new reference of object in a traceback object and this method should # be callable from a __del__ method (at least for versions prior # Python 1.5) def HasSubscribers(self, object): return self.connections.has_key(id(object)) def print_connections(self): # for debugging for id, channels in self.connections.items(): for name, subscribers in channels.items(): print id, name for func, args in subscribers: if type(func) == MethodType: print '\tmethod %s of %s' % (func.im_func.func_name, func.im_self) else: print '\t', func _the_connector = Connector() Connect = _the_connector.Connect Issue = _the_connector.Issue RemovePublisher = _the_connector.RemovePublisher Disconnect = _the_connector.Disconnect def Subscribe(channel, function, *args): return Connect(None, channel, function, args) class Publisher: ignore_issue = 0 def __del__(self): # the new finalization code in 1.5.1 might bind RemovePublisher # to None before all objects derived from Publisher are deleted... if RemovePublisher is not None: RemovePublisher(self) def Subscribe(self, channel, func, *args): Connect(self, channel, func, args) def Unsubscribe(self, channel, func, *args): Disconnect(self, channel, func, args) def issue(self, channel, *args): if not self.ignore_issue: apply(Issue, (self, channel,) + args) def Destroy(self): RemovePublisher(self) class QueueingPublisher(Publisher): def __init__(self): self.clear_message_queue() def queue_message(self, channel, *args): # Put message in the queue. If it is already queued put it at # the end. This is done to make certain that no channel gets # called twice between two calls to flush_message_queue. If the # order of channel invocation is important two or more queues # should be used. message = (channel, args) if message not in self.message_queue: self.message_queue.append(message) def flush_message_queue(self): # Issue all queued messages and make the queue empty # # Issueing messages might result in new messages being queued. # This does not happen in sketch yet (Jul 1997) but let's hope # that we don't get infinite loops here... while self.message_queue: queue = self.message_queue self.message_queue = [] for channel, args in queue: apply(Issue, (self, channel) + args) def clear_message_queue(self): self.message_queue = [] UniConvertor-1.1.4/src/app/events/__init__.py0000775000076400007640000000000011060500601017610 0ustar igorigorUniConvertor-1.1.4/src/app/Scripting/0000755000076400007640000000000011211455344016155 5ustar igorigorUniConvertor-1.1.4/src/app/Scripting/wrapper.py0000775000076400007640000000660111060500601020204 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1998, 1999, 2000, 2002 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA from types import ListType from app import _ from app.conf.const import SCRIPT_UNDO, SCRIPT_GET, SCRIPT_OBJECT, \ SCRIPT_OBJECTLIST, SelectSet from app.events.warn import warn, USER class UndoMethodWrapper: def __init__(self, method, document): self.method = method self.document = document def __call__(self, *args, **kw): self.document.AddUndo(apply(self.method, args, kw)) class ObjectMethodWrapper: def __init__(self, method, document): self.method = method self.document = document def __call__(self, *args, **kw): return ObjectWrapper(apply(self.method, args, kw), self.document) class ObjectListMethodWrapper: def __init__(self, method, document): self.method = method self.document = document def __call__(self, *args, **kw): objects = apply(self.method, args, kw) return map(ObjectWrapper, objects, (self.document,) * len(objects)) # special methods (__*__) which can be called safely safe_special_methods = { "__eq__": 1, "__lt__": 1, "__gt__": 1, "__cmp__": 1, "__repr__": 1, "__str__": 1, "__coerce__": 1, } class Wrapper: def __init__(self, object, document): self._object = object self._document = document def __getattr__(self, attr): try: access = self._object.script_access[attr] except KeyError: if not safe_special_methods.get(attr): warn(USER,'Cant access attribute %s of %s in safe user script', attr, self._object) raise AttributeError, attr if access == SCRIPT_UNDO: return UndoMethodWrapper(getattr(self._object, attr), self._document) elif access == SCRIPT_GET: return getattr(self._object, attr) elif access == SCRIPT_OBJECT: return ObjectMethodWrapper(getattr(self._object, attr), self._document) elif access == SCRIPT_OBJECTLIST: return ObjectListMethodWrapper(getattr(self._object, attr), self._document) else: raise AttributeError, attr def __cmp__(self, other): return cmp(self._object, strip_wrapper(other)) def strip_wrapper(wrapped): if isinstance(wrapped, Wrapper): return wrapped._object else: return object class DocumentWrapper(Wrapper): def __init__(self, document): Wrapper.__init__(self, document, document) def SelectObject(self, objects, mode = SelectSet): if type(objects) == ListType: objects = map(strip_wrapper, objects) else: objects = strip_wrapper(objects) self._document.SelectObject(objects, mode) def DeselectObject(self, object): self._document.DeselectObject(strip_wrapper(object)) def Insert(self, object, undo_text = _("Create Object")): self._document(strip_wrapper(object), undo_text) # ObjectWrapper = Wrapper UniConvertor-1.1.4/src/app/Scripting/registry.py0000775000076400007640000000362711060500601020401 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1998, 1999 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA from types import DictType, StringType import operator from app.events.warn import warn, USER from script import SafeScript class ScriptRegistry: def __init__(self): self.registry = {} self.menu = {} def Add(self, script, menu = ()): if type(menu) == StringType: menu = (menu,) self.registry[script.name] = script submenu = self.menu for item in menu: if submenu.has_key(item): if type(submenu[item]) != DictType: warn(USER, 'Replacing menu entry "%s" with a submenu', item) submenu[item] = {} else: submenu[item] = {} submenu = submenu[item] submenu[script.Title()] = script def AddFunction(self, name, title, function, args = (), menu = (), sensitive = None, script_type = SafeScript): self.Add(script_type(name, title, function, args = args, sensitive = sensitive), menu = menu) def MenuTree(self): return make_menu_tree(self.menu) def make_menu_tree(dict): result = [] for key, value in dict.items(): if type(value) == DictType: value = make_menu_tree(value) result.append((key, value)) result.sort() return result UniConvertor-1.1.4/src/app/Scripting/__init__.py0000775000076400007640000000174211060500601020264 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1998, 1999 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA from registry import ScriptRegistry from script import SafeScript, AdvancedScript Registry = ScriptRegistry() AddFunction = Registry.AddFunction Add = Registry.Add UniConvertor-1.1.4/src/app/Scripting/script.py0000775000076400007640000000414011060500601020024 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1998, 1999 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA from app.events.warn import warn_tb, USER import app from wrapper import DocumentWrapper class Context: def __init__(self): self.application = app.main.application self.main_window = self.application.main_window self.document = self.main_window.document class Script: def __init__(self, name, title, function, args = (), kwargs = None, sensitive = None): self.name = name self.title = title self.function = function self.args = args self.kwargs = kwargs self.sensitive = sensitive def Title(self): return self.title def execute(self, context, *args, **kw): document = context.main_window.document apply(document.BeginTransaction, args, kw) try: try: kw = self.kwargs if kw is None: kw = {} apply(self.function, (context,) + self.args, kw) except: warn_tb(USER, 'Error in user script "%s"', self.name) document.AbortTransaction() finally: document.EndTransaction() class SafeScript(Script): def Execute(self): context = Context() context.document = DocumentWrapper(context.document) self.execute(context, self.Title()) #class SelectionScript(Script): # # def Execute(self): # self.execute(Context(), clear_selection_rect = 0) class AdvancedScript(Script): def Execute(self): self.execute(Context(), self.Title()) UniConvertor-1.1.4/src/app/VERSION0000664000076400007640000000000611211362553015260 0ustar igorigor1.1.4 UniConvertor-1.1.4/src/app/Graphics/0000755000076400007640000000000011211455344015753 5ustar igorigorUniConvertor-1.1.4/src/app/Graphics/clone.py0000775000076400007640000001042611060500565017433 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2003-2006 by Igor E. Novikov # Copyright (C) 1998, 1999 by Bernhard Herzog # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. # # The Clone Object # #from traceback import print_stack from app import Translation, NullPoint, Trafo from app import PointType, Undo, NullUndo from app.conf.const import CHANGED from base import Bounded, HierarchyNode _clone_registry = {} def _register_clone(original): original = id(original) _clone_registry[original] = _clone_registry.get(original, 0) + 1 def _unregister_clone(original): original = id(original) _clone_registry[original]= _clone_registry[original] - 1 if _clone_registry[original] == 0: del _clone_registry[original] class Clone(Bounded, HierarchyNode): is_Clone = 1 def __init__(self, original = None, duplicate = None): HierarchyNode.__init__(self, duplicate = duplicate) if original is not None and original.is_Clone: duplicate = original original = None if duplicate is not None: self._original = duplicate._original self._center = duplicate._center self._offset = duplicate._offset else: self._original = original self._center = self._original.coord_rect.center() self._offset = NullPoint self.register() def register(self): _register_clone(self._original) self._original.Subscribe(CHANGED, self.orig_changed) def unregister(self): self._original.Unsubscribe(CHANGED, self.orig_changed) _unregister_clone(self._original) def __getattr__(self, attr): if self._lazy_attrs.has_key(attr): return Bounded.__getattr__(self, attr) #print 'Clone.__getattr__: from original:', attr #if attr in ('__nonzero__', 'document'): # print_stack() return getattr(self._original, attr) def update_rects(self): off = self._offset self.bounding_rect = self._original.bounding_rect.translated(off) self.coord_rect = self._original.coord_rect.translated(off) def Translate(self, offset): self._offset = self._offset + offset self.del_lazy_attrs() return self.Translate, -offset def _set_offset(self, offset): undo = (self._set_offset, self._offset) self._offset = offset self.del_lazy_attrs() return undo def offset_center(self, offset): undo = (self.offset_center, -offset) self._center = self._center + offset return undo def Transform(self, trafo): center = self._center + self._offset offset = trafo(center) - center if self.document is not None: self.document.AddAfterHandler(_transform, (self._original, trafo.matrix()), -1) return self.offset_center(offset) def orig_changed(self, *args): if self.document is not None: self.document.AddClearRect(self.bounding_rect) self.del_lazy_attrs() center = self._center self._center = self._original.coord_rect.center() self._offset = self._offset + center - self._center if self.document is not None: self.document.AddClearRect(self.bounding_rect) def DrawShape(self, device, rect = None): device.PushTrafo() try: device.Translate(self._offset) self._original.DrawShape(device, rect) finally: device.PopTrafo() def Hit(self, p, rect, device): off = -self._offset return self._original.Hit(p + off, rect.translated(off), device) def Info(self): return 'Clone of ' + self._original.Info() # overwrite Selectable methods def SelectSubobject(self, p, rect, device, path = None, *rest): return self def GetObjectHandle(self, multiple): trafo = Translation(self._offset) handle = self._original.GetObjectHandle(multiple) if type(handle) == PointType: return trafo(handle) else: return map(trafo, handle) # overwrite Bounded methods def LayoutPoint(self): return self._original.LayoutPoint() + self._offset def GetSnapPoints(self): return map(Translation(self._offset), self._original.GetSnapPoints()) def _transform(original, matrix): trafo = apply(Trafo, matrix) undo = original.Transform(trafo) doc = original.document doc.AddUndo((_undo, doc, [undo])) def _after_handler(undo, list): list[0] = Undo(undo) def _undo(doc, undo): list = [NullUndo] doc.AddAfterHandler(_after_handler, (undo[0], list), -1) return (_undo, doc, list) def CreateClone(object): clone = Clone(object) undo = object.parent.ReplaceChild(object, clone) return clone.Duplicate(), undo UniConvertor-1.1.4/src/app/Graphics/selinfo.py0000775000076400007640000001706011060500565017773 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1996, 1997, 1998 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Functions to manipulate selection info # # Representation # # The set of currently selected objects in Sketch is represented as a # list of tuples. Each of the tuples has the form: # # (PATH, OBJ) # # where OBJ is a selected object and PATH is a tuple of ints describing # the path through the hierarchy of objects to OBJ, usually starting # from the document at the top of the hierarchy. Each item in PATH is # the index of the next object in the path. For example, the second # object in the first layer has the PATH (0, 1) (indexes start from 0). # # This representation serves two purposes: # # 1. storing the path to the object allows fast access to the # parents of the selected object. # # 2. it allows sorting the list by path, which results in a list # with the objects lowest in the stack of objects at the front. # # A sorted list is important when changing the stacking order of # objects, since the indices, i.e. the path elements, may change # during the operation. # # Sorting the list also allows to make sure that each selected # object is listed exactly once in the list. # # This representation, if the list is sorted, is called the _standard # representation_. # # Alternative Representations: # # There are several alternative representations that are mainly useful # in the methods of compound objects that rearrange the children. In # those methods, the path is usually taken relative to self. Where # selection info has to be passed to children, to rearrange their # children, the first component of the path is stripped, so that the # path is relative to the child. # # All of the alternative representations are lists of tuples sorted at # least by the first item of the tuples. # # Tree: # # An alternative representation of the selection info is a list of # tuples of the form: # # (INDEX, LIST) # # where INDEX is just the first part of the PATH of the standard # representation and LIST is a list of selection info in standard # representation but with each PATH stripped of its first component # which is INDEX. That is, LIST is selection info in standard form # relative to the compound object given by INDEX. # # Tree2: # # Just like Tree1, but if LIST would contain just one item with an empty # PATH (an empty tuple), LIST is replaced by the object. # # Sliced Tree: # # A variant of Tree2, where consecutive items with an object (i.e. # something that is no list) are replaced by a tuple `(start, end)' # where start is the lowest INDEX and end the highest. Consecutive items # are items where the INDEX parts are consecutive integers. # # # Creating Selection Info: # # Selecting objects is done for instance by the GraphicsObject method # SelectSubobject. In a compound object, when it has determined that a # certain non compound child obj is to be selected, this method # constructs a selection info tuple by calling build_info: # # info1 = build_info(idx1, obj) # # idx is the index of obj in the compound object's list of children. # info1 will then be just a tuple: ((idx1,) obj) This info is returned # to the caller, its parent, which is often another compound object. # This parent then extends the selection info with # # info2 = prepend_idx(idx2, info) # # This results in a new tuple new_info: ((idx2, idx1), obj). idx2 is, of # course, the index of the compound object in its parent's list of # children. # # Finally, the document object receives such a selection info tuple from # one of its layers, prepends that layer's index to the info and puts it # into the list of selected objects. # from types import TupleType, ListType def build_info(idx, obj): return ((idx,), obj) def prepend_idx(idx, info): # prepend idx to the path of info. if type(info) == TupleType: return ((idx,) + info[0], info[1]) if type(info) == ListType: idx = (idx,) for i in range(len(info)): tmp = info[i] info[i] = (idx + tmp[0], tmp[1]) return info # assume info is an instance object return ((idx,), info) def select_all(objects): return map(None, map(lambda *t: t, range(len(objects))), objects) def select_range(min, objects): return map(None, map(lambda *t: t, range(min, min + len(objects))), objects) def get_parent(info): path, obj = info if len(path) > 1: parent = obj.parent if parent is not None: return (path[:-1], parent) return None def list_to_tree(infolist): # convert standard representation to Tree1 representation dict = {} for info in infolist: path, obj = info idx = path[0] info = (path[1:], obj) try: dict[idx].append(info) except KeyError: dict[idx] = [info] result = dict.items() result.sort() for idx, info in result: info.sort() return result def list_to_tree2(infolist): # convert standard representation to Tree2 representation dict = {} for info in infolist: path, obj = info idx = path[0] path = path[1:] if path: info = (path, obj) else: info = obj try: dict[idx].append(info) except KeyError: dict[idx] = [info] result = dict.items() result.sort() for i in range(len(result)): idx, info = result[i] if len(info) == 1 and type(info[0]) != TupleType: result[i] = (idx, info[0]) else: info.sort() return result def list_to_tree_sliced(infolist): # convert standard representation to sliced tree representation result = [] slice_start = slice_end = -1 last_obj = None for idx, list in list_to_tree2(infolist): if type(list) != ListType: # list is a child if idx == slice_end: slice_end = idx + 1 else: if slice_start > -1: if slice_end > slice_start + 1: result.append((slice_start, slice_end)) else: result.append((slice_start, last_obj)) slice_start = idx slice_end = idx + 1 last_obj = list else: # list is a list of children of child if slice_start > -1: if slice_end > slice_start + 1: result.append((slice_start, slice_end)) else: result.append((slice_start, last_obj)) slice_start = -1 slice_end = -1 last_obj = None result.append((idx, list)) else: if slice_start > -1: if slice_end > slice_start + 1: result.append((slice_start, slice_end)) else: result.append((slice_start, last_obj)) return result def tree_to_list(tree): result = [] for idx, info in tree: idx = (idx,) for path, obj in info: result.append((idx + path, obj)) return result def common_prefix(list): # Return the longest path that all items in LIST have in common. # LIST is in standard representation. Since that is a sorted list, # we just have to compare the first and last elements. if not list: return () if len(list) == 1: return list[0][0] first = list[0][0] last = list[-1][0] if len(first) > len(last): length = len(last) first = first[:length] else: length = len(first) last = last[:length] for i in range(length): if first[i] != last[i]: return first[:i] return first UniConvertor-1.1.4/src/app/Graphics/dashes.py0000775000076400007640000000264011060500565017601 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1998 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # dashes for sketch import os from app import config, _ from app.events.warn import warn_tb, USER from app.io.loadres import read_resource_file std_dashes = None def StandardDashes(): global std_dashes if std_dashes is None: filename = os.path.join(config.std_res_dir, config.preferences.dashes) try: std_dashes = [] read_resource_file(filename, '##Sketch Dashes 0', _("%s is not dashes file"), {'dashes': std_dashes.append}) except: warn_tb(USER, _("Error trying to read dashes from %s\n" "Using builtin defaults"), filename) std_dashes = [(), (5, 5)] return std_dashes UniConvertor-1.1.4/src/app/Graphics/compound.py0000775000076400007640000004646111060500566020170 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1997, 1998, 1999, 2002 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Class Compound # # a baseclass for objects that contain other graphics objects # (primitives or other compounds) # from types import ListType, TupleType, IntType, InstanceType import operator from app import _, SketchError, UnionRects, EmptyRect, _sketch from app import NullUndo, CreateListUndo, Undo from base import GraphicsObject, Bounded, CHANGED from blend import Blend, MismatchError from properties import EmptyProperties from selinfo import prepend_idx, select_range, build_info, list_to_tree2, \ list_to_tree_sliced class Compound(GraphicsObject): has_edit_mode = 0 is_Group = 0 is_Compound = 1 can_be_empty = 0 allow_traversal = 0 def __init__(self, objects = None, duplicate = None): GraphicsObject.__init__(self, duplicate = duplicate) if duplicate is not None: objects = [] for obj in duplicate.objects: objects.append(obj.Duplicate()) self.objects = objects elif objects: self.objects = objects else: self.objects = [] self.changing_children = 0 self.set_parent() def Destroy(self): self.destroy_objects() GraphicsObject.Destroy(self) def destroy_objects(self): for obj in self.objects: obj.Destroy() self.objects = [] def SetParent(self, parent): if parent is self.parent: return GraphicsObject.SetParent(self, parent) if parent is not None: self.set_parent() else: self.unset_parent() def set_parent(self): for child in self.objects: child.SetParent(self) def unset_parent(self): for child in self.objects: child.SetParent(None) def SelectionInfo(self, child = None): info = GraphicsObject.SelectionInfo(self) if info and child is not None: path = info[0] return (path + (_sketch.IdIndex(self.objects, child),), child) return info def ChildChanged(self, child): self.del_lazy_attrs() if self.changing_children: return self.issue_changed() def SetDocument(self, doc): for obj in self.objects: obj.SetDocument(doc) GraphicsObject.SetDocument(self, doc) self.set_parent() def disconnect_objects(self): for obj in self.objects: obj.Disconnect() def Disconnect(self): self.disconnect_objects() self.unset_parent() def connect_objects(self): for obj in self.objects: obj.Connect() def Connect(self): self.set_parent() self.connect_objects() def set_objects(self, new_objs): if self.document is not None: self.document.AddClearRect(self.bounding_rect) self.disconnect_objects() self.destroy_objects() self.objects = new_objs for obj in self.objects: if self.document is not None: obj.SetDocument(self.document) obj.SetParent(self) # XXX no connect here ? self.del_lazy_attrs() if self.document is not None: self.document.AddClearRect(self.bounding_rect) def UntieFromDocument(self): for obj in self.objects: obj.UntieFromDocument() def TieToDocument(self): for obj in self.objects: obj.TieToDocument() def load_AppendObject(self, object): self.objects.append(object) def load_Done(self): pass def __getitem__(self, idx): if type(idx) == IntType: return self.objects[idx] elif type(idx) == TupleType: if len(idx) > 1: return self.objects[idx[0]][idx[1:]] elif len(idx) == 1: return self.objects[idx[0]] raise ValueError, 'invalid index %s' % `idx` def GetObjects(self): # XXX should this return a copy of self.objects? return self.objects def del_lazy_attrs(self): Bounded.del_lazy_attrs(self) return (self.del_lazy_attrs,) def update_rects(self): # XXX: should we raise an exception here if self.objects is empty? boxes = map(lambda o: o.coord_rect, self.objects) if boxes: self.coord_rect = reduce(UnionRects, boxes, boxes[0]) else: self.coord_rect = EmptyRect boxes = map(lambda o: o.bounding_rect, self.objects) if boxes: self.bounding_rect = reduce(UnionRects, boxes, boxes[0]) else: self.bounding_rect = EmptyRect def SelectSubobject(self, p, rect, device, path = None, *rest): return self def Insert(self, obj, at): raise SketchError('Cannot insert in compound') def Remove(self, *args, **kw): raise SketchError('Cannot remove from compound') RemoveSlice = Remove RemoveObjects = Remove ReplaceChild = Remove def MoveObjectsToTop(self, infolist): raise SketchError('Cannot rearrange objects in compound') MoveObjectsToBottom = MoveObjectsToTop MoveObjectsDown = MoveObjectsToTop MoveObjectsUp = MoveObjectsToTop def move_objects_to_top(self, infolist, to_bottom = 0): return infolist, NullUndo def DuplicateObjects(self, infolist, offset): raise SketchError('Cannot duplicate objects in compound') def ForAll(self, func): self.changing_children = 1 try: return map(func, self.objects) finally: self.changing_children = 0 def WalkHierarchy(self, func): for obj in self.objects: if obj.is_Compound: obj.WalkHierarchy(func) else: func(obj) def begin_change_children(self): self.changing_children = self.changing_children + 1 return (self.end_change_children,) def end_change_children(self): self.changing_children = self.changing_children - 1 if not self.changing_children: self._changed() return (self.begin_change_children,) def ForAllUndo(self, func): if self.objects: undo = [self.begin_change_children()] undo = undo + map(func, self.objects) undo.append(self.end_change_children()) return CreateListUndo(undo) else: return NullUndo def FilterAll(self, func): return filter(func, self.GetObjects()) def NumObjects(self): return len(self.objects) def Info(self): return _("Compound with %d objects") % len(self.objects) def AddStyle(self, style): undo = self.ForAllUndo(lambda o, style = style: o.AddStyle(style)) return undo def Properties(self): return EmptyProperties def SetProperties(self, **kw): self.del_lazy_attrs() func = lambda o, kw = kw: apply(o.SetProperties, (), kw) undo = self.ForAllUndo(func) return undo def Hit(self, p, rect, device): test = rect.overlaps objects = self.objects for obj_idx in range(len(objects) - 1, -1, -1): obj = objects[obj_idx] if test(obj.bounding_rect): if obj.Hit(p, rect, device): return obj_idx + 1 return 0 def Transform(self, trafo): self.del_lazy_attrs() undo = self.ForAllUndo(lambda o, t = trafo: o.Transform(t)) return undo def Translate(self, offset): undo = self.ForAllUndo(lambda o, p = offset: o.Translate(p)) return undo def DrawShape(self, device, rect = None): if rect: test = rect.overlaps for o in self.objects: if test(o.bounding_rect): o.DrawShape(device, rect) else: for obj in self.objects: obj.DrawShape(device) def PickObject(self, point, rect, device): objects = self.objects[:] objects.reverse() test = rect.overlaps for obj in objects: if test(obj.bounding_rect): if obj.is_Compound: result = obj.PickObject(point, rect, device) if result: break elif obj.Hit(point, rect, device): result = obj break else: result = None return result def Blend(self, other, frac1, frac2): try: objs = self.objects oobjs = other.objects blended = [] for i in range(min(len(objs), len(oobjs))): blended.append(Blend(objs[i], oobjs[i], frac1, frac2)) return Compound(blended) except: raise MismatchError def GetObjectHandle(self, multiple): return self.ForAll(lambda o: o.GetObjectHandle(1)) def SelectFirstChild(self): if self.allow_traversal and self.objects: return self.objects[0] def SelectLastChild(self): if self.allow_traversal and self.objects: return self.objects[-1] def SelectNextChild(self, child, idx): if self.allow_traversal and len(self.objects) > idx + 1: return self.objects[idx + 1] def SelectPreviousChild(self, child, idx): if self.allow_traversal and len(self.objects) > idx - 1 and idx > 0: return self.objects[idx - 1] class EditableCompound(Compound): allow_traversal = 1 def SelectSubobject(self, p, rect, device, path = None, *rest): test = rect.overlaps if path is None: return self if path: path_idx = path[0] path = path[1:] else: path_idx = -1 path = None objects = self.objects for obj_idx in range(len(objects) - 1, -1, -1): obj = objects[obj_idx] if test(obj.bounding_rect) and obj.Hit(p, rect, device): if obj_idx == path_idx: result = obj.SelectSubobject(p, rect, device, path) else: result = obj.SelectSubobject(p, rect, device) return prepend_idx(obj_idx, result) return None def Insert(self, obj, at): # Insert OBJ into the object hierarchy at the position described # by AT. AT should be either an integer or a tuple of integers. # OBJ can be a graphics object of a list of such objects. # # If AT is a tuple of 2 or more ints, self's child at AT[0] has # to be a compound object and its Insert method is called with # OBJ and AT[1:] as arguments. # # If AT is an int or a singleton of one int, insert OBJ at that # position in self's children. If OBJ is a graphics object, this # works just like a list objects insert method (insert(AT, # OBJ)). If its a list of graphics objects this method # effectively assigns that list to the slice AT:AT. # # As a side effect, this method calls the following methods of # the inserted objects: # # obj.SetDocument(self.document) # obj.SetParent(self) # obj.Connect() # # Return a tuple (SELINFO, UNDO), where SELINFO is selection # info for the inserted objects at their new positions, and UNDO # is the appropriate undo info. # # If self is modified directly, issue a CHANGED message. undo_info = None try: if type(at) == TupleType and at: if len(at) == 1: at = at[0] else: child = at[0] at = at[1:] sel_info, undo_info = self.objects[child].Insert(obj, at) sel_info = prepend_idx(child, sel_info) return (sel_info, undo_info) if type(at) != IntType or at > len(self.objects): at = len(self.objects) if type(obj) == InstanceType: self.objects.insert(at, obj) obj.SetDocument(self.document) obj.SetParent(self) obj.Connect() sel_info = build_info(at, obj) undo_info = (self.Remove, obj, at) else: self.objects[at:at] = obj for o in obj: # XXX: should we have undo info for these: o.SetDocument(self.document) o.SetParent(self) o.Connect() sel_info = select_range(at, obj) undo_info = (self.RemoveSlice, at, at + len(obj)) self._changed() return (sel_info, undo_info) except: if undo_info is not None: Undo(undo_info) raise def _insert_with_undo(self, obj, at): # The same as the Insert method but return only the undo info. return self.Insert(obj, at)[1] def do_remove_child(self, idx): obj = self.objects[idx] del self.objects[idx] obj.Disconnect() obj.SetParent(None) self._changed() return (self._insert_with_undo, obj, idx) def Remove(self, obj, idx = None): if type(idx) == TupleType: if len(idx) == 1: idx = idx[0] else: return self.objects[idx[0]].Remove(obj, idx[1:]) if idx is None: idx = self.objects.index(obj) elif self.objects[idx] is not obj: raise ValueError, 'Compound.Remove(): invalid index' return self.do_remove_child(idx) def RemoveSlice(self, min, max): objs = self.objects[min:max] self.objects[min:max] = [] for obj in objs: obj.Disconnect() obj.SetParent(None) self._changed() return (self._insert_with_undo, objs, min) def RemoveObjects(self, infolist): if not infolist: return NullUndo sliced = list_to_tree_sliced(infolist) sliced.reverse() # important! undo = [self.begin_change_children()] try: for start, end in sliced: if type(end) == IntType: undo.append(self.RemoveSlice(start, end)) elif type(end) == ListType: undo.append(self.objects[start].RemoveObjects(end)) else: undo.append(self.Remove(end, start)) undo.append(self.end_change_children()) return CreateListUndo(undo) except: Undo(CreateListUndo(undo)) raise def ReplaceChild(self, child, object): # replace self's child child with object. Return undo info idx = self.objects.index(child) self.objects[idx] = object object.SetParent(self) object.SetDocument(self.document) child.SetParent(None) self._changed() return (self.ReplaceChild, object, child) def permute_objects(self, permutation): # permutation must be a list of ints and len(permutation) must be # equal to len(self.objects). permutation[i] is the index of the # object that is moved to index i in the result. objects = self.objects length = len(objects) identity = range(length) if permutation == identity: return NullUndo if len(objects) != len(permutation): raise ValueError, 'len(permutation) != len(self.objects)' result = map(operator.getitem, [objects] * length, permutation) inverse = [0] * length map(operator.setitem, [inverse] * length, permutation, identity) self.objects = result self._changed() return (self.permute_objects, inverse) def move_objects_to_top(self, infolist, to_bottom = 0): # Implement the public methods MoveToTop (if to_bottom is false) # and MoveToBottom (if to_bottom is true). sliced = list_to_tree_sliced(infolist) sliced.reverse() undo = [self.begin_change_children()] selection = [] idxs = [] permutation = range(len(self.objects)) try: for start, end in sliced: if type(end) == IntType: # a contiguous range of self's children (start:end) idxs[:0] = permutation[start:end] del permutation[start:end] elif type(end) == ListType: # children of self.objects[start] child = self.objects[start] sel, undo_info = child.move_objects_to_top(end, to_bottom) if undo_info is not NullUndo: undo.append(undo_info) selection = selection + prepend_idx(start, sel) else: # a single object (self.object[start]) idxs.insert(0, start) del permutation[start] if idxs: # direct children of self are involved: apply the # permutation if to_bottom: permutation = idxs + permutation else: permutation = permutation + idxs undo_info = self.permute_objects(permutation) if undo_info is not NullUndo: undo.append(undo_info) # finished: undo.append(self.end_change_children()) if len(undo) <= 2: # We haven't really done anything (undo has length 2), # so we just pass the selection info back unchanged selection = infolist undo = NullUndo else: # We have done something, so figure out the new # selection info undo = CreateListUndo(undo) if to_bottom: selection = selection \ + select_range(0, self.objects[:len(idxs)]) else: min = len(self.objects) - len(idxs) selection = selection \ + select_range(min, self.objects[min:]) return (selection, undo) except: # Ooops, something's gone wrong. Undo everything we've done # so far... (hmm, this currently fails to undo everything if # undo.append(undo_info) fails... (the undo_info involved # would be lost)) Undo(CreateListUndo(undo)) raise def MoveObjectsToTop(self, infolist): return self.move_objects_to_top(infolist) def MoveObjectsToBottom(self, infolist): return self.move_objects_to_top(infolist, to_bottom = 1) def MoveObjectsDown(self, infolist): sliced = list_to_tree_sliced(infolist) undo = [self.begin_change_children()] selection = [] permutation = range(len(self.objects)) objects = self.objects try: for start, end in sliced: if type(end) == IntType: if start > 0: temp = permutation[start:end] del permutation[start:end] permutation[start - 1:start - 1] = temp selection = selection +select_range(start - 1, objects[start:end]) else: selection = selection +select_range(start, objects[start:end]) elif type(end) == ListType: sel, undo_info = objects[start].MoveObjectsDown(end) if undo_info is not NullUndo: undo.append(undo_info) selection = selection + prepend_idx(start, sel) else: if start > 0: del permutation[start] permutation.insert(start - 1, start) selection.append(build_info(start - 1, objects[start])) else: selection.append(build_info(start, objects[start])) undo_info = self.permute_objects(permutation) if undo_info is not NullUndo: undo.append(undo_info) undo.append(self.end_change_children()) if len(undo) <= 2: undo = NullUndo selection = infolist else: undo = CreateListUndo(undo) return (selection, undo) except: Undo(CreateListUndo(undo)) raise def MoveObjectsUp(self, infolist): sliced = list_to_tree_sliced(infolist) sliced.reverse() undo = [self.begin_change_children()] selection = [] permutation = range(len(self.objects)) objects = self.objects max = len(objects) try: for start, end in sliced: if type(end) == IntType: if end < max: temp = permutation[start:end] del permutation[start:end] permutation[start + 1:start + 1] = temp selection = selection + select_range(start + 1, objects[start:end]) else: selection = selection + select_range(start, objects[start:end]) elif type(end) == ListType: sel, undo_info = objects[start].MoveObjectsUp(end) if undo_info is not NullUndo: undo.append(undo_info) selection = selection + prepend_idx(start, sel) else: if start < max - 1: del permutation[start] permutation.insert(start + 1, start) selection.append(build_info(start + 1, objects[start])) else: selection.append(build_info(start, objects[start])) undo_info = self.permute_objects(permutation) if undo_info is not NullUndo: undo.append(undo_info) undo.append(self.end_change_children()) if len(undo) <= 2: undo = NullUndo selection = infolist else: undo = CreateListUndo(undo) return (selection, undo) except: Undo(CreateListUndo(undo)) raise def DuplicateObjects(self, infolist, offset): infolist = list_to_tree2(infolist) objects = self.objects undo = [self.begin_change_children()] selection = [] added = 0 try: for idx, obj in infolist: idx = idx + added if type(obj) == ListType: # duplicate in subobj sel, undoinfo = objects[idx].DuplicateObjects(obj, offset) undo.append(undoinfo) selection = selection + prepend_idx(idx, sel) else: obj = obj.Duplicate() obj.Translate(offset) sel, undoinfo = self.Insert(obj, idx + 1) undo.append(undoinfo) selection.append(sel) added = added + 1 undo.append(self.end_change_children()) return (selection, CreateListUndo(undo)) except: Undo(CreateListUndo(undo)) raise UniConvertor-1.1.4/src/app/Graphics/pagelayout.py0000775000076400007640000000450011060500565020501 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1997, 1998 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Class PageLayout # # This class represents the layout of one page. This includes the # papersize, margins, etc. The papersize can be a standard paper size # like `A4' or `legal' or some user specific format like `10cm x 10cm'. # # XXX: margins are not yet implemented from papersize import Papersize from app import config Portrait = 0 Landscape = 1 class PageLayout: def __init__(self, paperformat = None, width = None, height = None, orientation = None): if width and height: self.width = width self.height = height self.paperformat = '' else: if paperformat is None: self.paperformat = config.preferences.default_paper_format else: self.paperformat = paperformat self.width, self.height = Papersize[self.paperformat] if orientation is None: self.orientation = config.preferences.default_page_orientation else: self.orientation = orientation if self.orientation not in (Portrait, Landscape): self.orientation = Portrait def Width(self): if self.orientation == Portrait: return self.width else: return self.height def Height(self): if self.orientation == Portrait: return self.height else: return self.width def Size(self): if self.orientation == Portrait: return (self.width, self.height) else: return (self.height, self.width) def FormatName(self): return self.paperformat def Orientation(self): return self.orientation def SaveToFile(self, file): file.PageLayout(self.paperformat, self.width, self.height, self.orientation) UniConvertor-1.1.4/src/app/Graphics/selection.py0000775000076400007640000007513411060500566020330 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2003-2006 by Igor E. Novikov # Copyright (C) 1997, 1998, 1999, 2000 by Bernhard Herzog # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. # Classes for handling selections. These include classes that represent # lists of selected objects and classes that represent the combined # bounding rectangle of all selected objects. The user can interact in # the usual fashion with these selection rects to transform (translate, # rotate, scale, shear) the selected objects. import operator, math from types import ListType, InstanceType, TupleType import time from app.utils.os_utils import flatten #from app.UI.skpixmaps import pixmaps from app.events.warn import pdebug, warn, INTERNAL from app.conf import const from app.conf.const import SelectSet from app import _, Point, Polar, Rect, UnionRects, RectType, Identity, \ Trafo, TrafoType, Rotation, CreateListUndo, NullUndo import handle from base import SelectAndDrag, Bounded import selinfo from math import floor, ceil class SelRectBase(SelectAndDrag, Bounded): # # Handle/selection numbers: # sx ex # 1 2 3 sy # # 8 4 # # 7 6 5 ey # # -1: whole object selTop = (1, 2, 3) selBottom = (7, 6, 5) selLeft = (1, 8, 7) selRight = (3, 4, 5) selAspect = (1, 3, 5, 7) # constrain aspect ratio for these selections handle_idx_to_sel = (7, 6, 5, 8, 4, 1, 2, 3) def __init__(self): SelectAndDrag.__init__(self) self.outline_object = None def update_rects(self): self.coord_rect = Rect(self.start, self.end) self.bounding_rect = self.coord_rect def SetOutlineObject(self, obj): # XXX: this is a hack... if obj is not None: if obj.is_Compound: objects = obj.GetObjects() if len(objects) == 1: obj = objects[0] else: return if not obj.is_Compound and not obj.is_Text: self.outline_object = obj class SelectionRectangle(SelRectBase): def __init__(self, rect, anchor = None): SelRectBase.__init__(self) if type(rect) == RectType: self.start = Point(rect.left, rect.bottom) self.end = Point(rect.right, rect.top) self.Normalize() self.anchor = anchor else: # assume type Point and interactive creation self.start = rect self.end = rect self.anchor = None self.selection = 5 def DrawDragged(self, device, partially): sel = self.selection if sel == -1: sx, sy = self.start + self.off ex, ey = self.end + self.off else: if sel in self.selTop: sy = self.drag_cur.y else: sy = self.start.y if sel in self.selBottom: ey = self.drag_cur.y else: ey = self.end.y if sel in self.selLeft: sx = self.drag_cur.x else: sx = self.start.x if sel in self.selRight: ex = self.drag_cur.x else: ex = self.end.x if sx > ex: tmp = sx; sx = ex; ex = tmp if sy < ey: tmp = sy; sy = ey; ey = tmp device.DrawRubberRect(Point(sx, sy), Point(ex, ey)) def ButtonDown(self, p, button, state): SelectAndDrag.DragStart(self, p) sel = self.selection if sel == -1: if self.anchor: #XXX shouldn't this be 'if self.anchor is not None' start = self.anchor else: start = self.start self.drag_start = self.drag_cur = start return (p - start, self.coord_rect.translated(-start)) ds_x , ds_y = (self.start + self.end) / 2 if sel in self.selLeft: ds_x = self.start.x if sel in self.selTop: ds_y = self.start.y if sel in self.selRight: ds_x = self.end.x if sel in self.selBottom: ds_y = self.end.y self.drag_cur = self.drag_start = ds = Point(ds_x, ds_y) self.init_constraint() return p - ds def init_constraint(self): pass def apply_constraint(self, p, state): return p def MouseMove(self, p, state): p = self.apply_constraint(p, state) SelectAndDrag.MouseMove(self, p, state) def compute_endpoints(self): cur = self.drag_cur start = self.start end = self.end sel = self.selection if sel in self.selTop: start = Point(start.x, cur.y) if sel in self.selBottom: end = Point(end.x, cur.y) if sel in self.selLeft: start = Point(cur.x, start.y) if sel in self.selRight: end = Point(cur.x, end.y) if sel == -1: start = start + self.off end = end + self.off return start, end def ButtonUp(self, p, button, state): p = self.apply_constraint(p, state) SelectAndDrag.DragStop(self, p) cur = self.drag_cur oldstart = self.start oldend = self.end start, end = self.compute_endpoints() self.start = start self.end = end result = self.ComputeTrafo(oldstart, oldend, start, end) self.Normalize() return result def ComputeTrafo(self, oldStart, oldEnd, start, end): pass def Normalize(self): sx, sy = self.start ex, ey = self.end if sx > ex: sx, ex = ex, sx if sy > ey: sy, ey = ey, sy self.start = Point(sx, sy) self.end = Point(ex, ey) def Hit(self, p, rect, device): pass def Select(self): self.selection = -1 def SelectPoint(self, p, rect, device, mode = SelectSet): if p: self.selection = 0 else: self.selection = -1 return self.selection def SelectHandle(self, handle, mode = SelectSet): self.selection = self.handle_idx_to_sel[handle.index] def GetHandles(self): sx = self.start.x sy = self.start.y ex = self.end.x ey = self.end.y x2 = (sx + ex) / 2 y2 = (sy + ey) / 2 return map(handle.MakeOffsetHandle, [Point(sx, ey), Point(x2, ey), Point(ex, ey), Point(sx, y2), Point(ex, y2), Point(sx, sy), Point(x2, sy), Point(ex, sy)], [(-1, 1), (0, 1), ( 1, 1), (-1, 0), ( 1, 0), (-1, -1), (0, -1), ( 1, -1)]) class Selection(Bounded): is_EditSelection = 0 _lazy_attrs = Bounded._lazy_attrs.copy() _lazy_attrs['rect'] = 'update_rectangle' def __init__(self, copy_from = None): if copy_from is not None: if type(copy_from) == ListType: self.objects = copy_from[:] else: # assume copy_from is another instance of a selection class self.objects = copy_from.objects self.coord_rect = copy_from.coord_rect self.bounding_rect = copy_from.bounding_rect self.anchor = copy_from.anchor else: self.objects = [] self.anchor = None def normalize(self): # make sure that self.objects contains no object more than once # and that no two objects have a direct or indirect parent/child # relationship. objs = self.objects changed = 0 if len(objs) > 1: objs.sort() last_info, obj = objs[-1] for idx in range(len(objs) - 2, -1, -1): info, obj = objs[idx] if info == last_info: del objs[idx] changed = 1 continue if len(info) < len(last_info): while info == last_info[:len(info)]: del objs[idx + 1] changed = 1 if idx + 1 < len(objs): last_info = objs[idx + 1][0] else: break last_info = info return changed def update_selinfo(self): objs = self.objects for i in range(len(objs)): objs[i] = objs[i][-1].SelectionInfo() def SetSelection(self, info): old_objs = self.objects if info: if type(info) == ListType: self.objects = info self.objects.sort() else: self.objects = [info] else: self.objects = [] self.del_lazy_attrs() return old_objs != self.objects def SetSelectionTree(self, info): return self.SetSelection(selinfo.tree_to_list(info)) def Add(self, info): if not info: return 0 if self.TestSubtract(info)==1: return self.Subtract(info) old_len = len(self.objects) if type(info) == ListType: self.objects = self.objects + info elif info: self.objects.append(info) changed = self.normalize() self.del_lazy_attrs() return changed or old_len != len(self.objects) def TestSubtract(self, info): result=0 old_len = len(self.objects) if type(info) != ListType: info = [info] objects = self.objects for item in info: if item in objects: result=1 return result def Subtract(self, info): if not info: return 0 old_len = len(self.objects) if type(info) != ListType: info = [info] objects = self.objects for item in info: if item in objects: objects.remove(item) self.del_lazy_attrs() return old_len != len(self.objects) def GetObjects(self): return map(operator.getitem, self.objects, [-1] * len(self.objects)) def GetInfo(self): return self.objects def GetInfoTree(self): return selinfo.list_to_tree(self.objects) def Depth(self): if self.objects: lengths = map(len, map(operator.getitem, self.objects, [0] * len(self.objects))) lmin = min(lengths) lmax = max(lengths) if lmin == lmax: return lmin return (lmin, lmax) return 0 def IsSingleDepth(self): if self.objects: return type(self.Depth()) != TupleType return 1 def GetPath(self): if len(self.objects) == 1: return self.objects[0][0] return () def for_all(self, func): return map(func, self.GetObjects()) def ForAllUndo(self, func): undoinfo = self.for_all(func) self.del_lazy_attrs() if len(undoinfo) == 1: undoinfo = undoinfo[0] if type(undoinfo) == ListType: return CreateListUndo(undoinfo) return undoinfo def ForAllUndo2(self, method, *args): t = time.clock() methods = map(getattr, self.GetObjects(), [method] * len(self.objects)) #print time.clock() - t, #undoinfo = self.for_all(func) undoinfo = map(apply, methods, [args] * len(methods)) #print time.clock() - t self.del_lazy_attrs() if len(undoinfo) == 1: undoinfo = undoinfo[0] if type(undoinfo) == ListType: return CreateListUndo(undoinfo) return undoinfo def __len__(self): return len(self.objects) __nonzero__ = __len__ def update_rects(self): objects = self.GetObjects() boxes = map(lambda o: o.coord_rect, objects) if boxes: self.coord_rect = reduce(UnionRects, boxes) else: self.coord_rect = None boxes = map(lambda o: o.bounding_rect, objects) if boxes: self.bounding_rect = reduce(UnionRects, boxes) else: self.bounding_rect = None if len(objects) == 1: self.anchor = objects[0].LayoutPoint() else: self.anchor = None def ChangeRect(self): return self.bounding_rect def ResetRectangle(self): self.del_lazy_attrs() def Hit(self, p, rect, device): test = rect.overlaps for obj in self.GetObjects(): if test(obj.bounding_rect): if obj.Hit(p, rect, device): return 1 return 0 def DragCancel(self): self.rect.DragCancel() def GetHandles(self): rect_handles = self.rect.GetHandles() multiple = len(self.objects) > 1 handles = flatten(self.for_all(lambda o, m = multiple: o.GetObjectHandle(m))) rect_handles.append(handle.MakeObjectHandleList(handles)) return rect_handles def CallObjectMethod(self, aclass, methodname, args): if len(self.objects) == 1: obj = self.objects[0][-1] if not isinstance(obj, aclass): return NullUndo try: method = getattr(obj, methodname) except AttributeError: return NullUndo undo = apply(method, args) if undo is None: undo = NullUndo return undo return NullUndo def GetObjectMethod(self, aclass, method): if len(self.objects) == 1: obj = self.objects[0][-1] if isinstance(obj, aclass): try: return getattr(obj, method) except AttributeError: pass return None def InfoText(self): # Return a string describing the selected object(s) result = _("No Selection") if self.objects: sel_info = self.objects br = self.coord_rect hor_sel=round((br.right - br.left)/.283465)/10 ver_sel=round((br.top - br.bottom)/.283465)/10 # hor_sel=ceil(floor(10**3*(br.right - br.left)/2.83465)/10)/100 # ver_sel=ceil(floor(10**3*(br.top - br.bottom)/2.83465)/10)/100 document = sel_info[0][1].document if len(sel_info) == 1: path, obj = sel_info[0] dict = {'layer': document[path[0]].Name()} info = obj.Info() if type(info) == TupleType: dict.update(info[1]) # the %% is correct here. The result has to be a # %-template itself. text = _("%s on `%%(layer)s'") % info[0]+"\n Selection size: "+str(hor_sel)+" x "+str(ver_sel) +" mm"### else: dict['object'] = info text = _("%(object)s on `%(layer)s'")+"\n Selection size: "+str(hor_sel)+" x "+str(ver_sel) +" mm" ### result = text, dict else: layer = sel_info[0][0][0] if layer == sel_info[-1][0][0]: # a single layer layer_name = document.layers[layer].Name() result = _("%(number)d objects on `%(layer)s'") \ % {'number':len(sel_info), 'layer':layer_name} result = result + "\n Selection size: "+str(hor_sel)+" x "+str(ver_sel) +" mm" else: result = _("%d objects on several layers") % len(sel_info)+"\n Selection size: "+str(hor_sel)+" x "+str(ver_sel) +" mm" ### return result def CurrentInfoText(self): return '' def _dummy(self, *args): pass Hide = _dummy DragStart = None DragMove = None DragStop = None Show = _dummy Hide = _dummy SelectPoint = _dummy SelectHandle = _dummy drag_mask = SelectAndDrag.drag_mask class SizeRectangle(SelectionRectangle): def init_constraint(self): sel = self.selection if sel == 1: self.reference = tuple(self.end) elif sel == 3: self.reference = (self.start.x, self.end.y) elif sel == 5: self.reference = tuple(self.start) elif sel == 7: self.reference = (self.end.x, self.start.y) else: return width = abs(self.start.x - self.end.x) height = abs(self.start.y - self.end.y) if width >= 1e-10: self.aspect = height / width else: self.aspect = None def apply_constraint(self, p, state): if state: if self.selection in self.selAspect: ref_x, ref_y = self.reference aspect = self.aspect if aspect is None: # width is 0 p = Point(self.drag_start.x, p.y) else: w = p.x - ref_x h = p.y - ref_y if w == 0: w = 0.00001 a = h / w if a > 0: sign = 1 else: sign = -1 if abs(a) > aspect: h = sign * w * aspect else: w = sign * h / aspect p = Point(ref_x + w, ref_y + h) # if state & const.AlternateMask: # pi4 = math.pi / 4 # off = p - self.drag_start # d = Polar(pi4 * round(math.atan2(off.y, off.x) / pi4)) # p = self.drag_start + (off * d) * d # print 'ALT' if state & const.ConstraintMask:# and self.selection == -1: pi4 = math.pi / 4 off = p - self.drag_start d = Polar(pi4 * round(math.atan2(off.y, off.x) / pi4)) p = self.drag_start + (off * d) * d return p def ButtonDown(self, p, button, state): self.trafo = Identity return SelectionRectangle.ButtonDown(self, p, button, state) def MouseMove(self, p, state): p = self.apply_constraint(p, state) SelectAndDrag.MouseMove(self, p, state) start, end = self.compute_endpoints() text, self.trafo = self.ComputeTrafo(self.start, self.end, start, end) def ComputeTrafo(self, oldStart, oldEnd, start, end): oldDelta = oldEnd - oldStart delta = end - start if self.selection == -1: # a translation. return _("Move Objects"), start - oldStart else: try: m11 = delta.x / oldDelta.x except ZeroDivisionError: m11 = 0 if __debug__: pdebug(None, 'ComputeTrafo: ZeroDivisionError') try: m22 = delta.y / oldDelta.y except ZeroDivisionError: m22 = 0 if __debug__: pdebug(None, 'ComputeTrafo: ZeroDivisionError') offx = start.x - m11 * oldStart.x offy = start.y - m22 * oldStart.y return _("Resize Objects"), Trafo(m11, 0, 0, m22, offx, offy) def DrawDragged(self, device, partial): SelectionRectangle.DrawDragged(self, device, partial) if self.outline_object is not None: trafo = self.trafo device.PushTrafo() if type(trafo) == TrafoType: device.Concat(trafo) else: device.Translate(trafo.x, trafo.y) self.outline_object.DrawShape(device) device.PopTrafo() def CurrentInfoText(self): t = self.trafo data = {} if type(t) == TrafoType: x = t.m11 y = t.m22 #if round(x, 3) == round(y, 3): # text = _("Uniform Scale %(factor)[factor]") # data['factor'] = x #else: br = self.coord_rect hor_sel=ceil(floor(10**3*x*(br.right - br.left)/2.83465)/10)/100 ver_sel=ceil(floor(10**3*y*(br.top - br.bottom)/2.83465)/10)/100 text = _("Scale %(factorx)[factor], %(factory)[factor]") text = text +"\n Changing size to: "+str(hor_sel)+" x "+str(ver_sel) +" mm" data['factorx'] = x data['factory'] = y else: text = _("Move %(x)[length], %(y)[length]") data['x'] = t.x data['y'] = t.y return text, data class SizeSelection(Selection): def __init__(self, arg = None): Selection.__init__(self, arg) def update_rectangle(self): if self: self.rect = SizeRectangle(self.coord_rect, self.anchor) else: self.rect = SizeRectangle(Rect(0, 0, 0, 0)) def ButtonDown(self, p, button, state): if len(self.objects) == 1: self.rect.SetOutlineObject(self.objects[0][-1]) return self.rect.ButtonDown(p, button, state) def MouseMove(self, p, state): self.rect.MouseMove(p, state) def ButtonUp(self, p, button, state, forget_trafo = 0): self.rect.SetOutlineObject(None) undo_text, trafo = self.rect.ButtonUp(p, button, state) if forget_trafo: return None, None t = time.clock() if type(trafo) == TrafoType: #undo = self.ForAllUndo(lambda o, t = trafo: o.Transform(t)) undo = self.ForAllUndo2('Transform', trafo) else: # trafo is point representing a translation undo = self.ForAllUndo2('Translate', trafo) #print 'transform/translate', time.clock() - t self.del_lazy_attrs() return undo_text, undo def Show(self, device, partially = 0): self.rect.Show(device, partially) def Hide(self, device, partially = 0): self.rect.Hide(device, partially) def DrawDragged(self, device, partial): self.rect.DrawDragged(device, partial) def SelectPoint(self, p, rect, device, mode = SelectSet): if not self.rect.SelectPoint(p, rect, device, mode): if self.Hit(p, rect, device): self.rect.Select() def SelectHandle(self, handle, mode = SelectSet): self.rect.SelectHandle(handle, mode) def Hit(self, p, rect, device): if self.objects: return (rect.overlaps(self.bounding_rect) and Selection.Hit(self, p, rect, device)) return 0 def CurrentInfoText(self): return self.rect.CurrentInfoText() class TrafoRectangle(SelRectBase): selTurn = [1, 3, 5, 7] selShear = [2, 4, 6, 8] selCenter = 100 def __init__(self, rect, center = None): SelRectBase.__init__(self) self.start = Point(rect.left, rect.bottom) self.end = Point(rect.right, rect.top) if center is None: self.center = rect.center() else: self.center = center def compute_trafo(self, state = 0): sel = self.selection if sel in self.selTurn: # rotation vec = self.drag_cur - self.center angle = math.atan2(vec.y, vec.x) angle = angle - self.start_angle + 2 * math.pi if state & const.ConstraintMask: pi12 = math.pi / 12 angle = pi12 * int(angle / pi12 + 0.5) self.trafo = Rotation(angle, self.center) self.trafo_desc = (1, angle) elif sel in self.selShear: if sel in (2,6): # horiz. shear height = self.drag_start.y - self.reference if height: ratio = self.off.x / height self.trafo = Trafo(1, 0, ratio, 1, - ratio * self.reference, 0) self.trafo_desc = (2, ratio) else: # vert. shear width = self.drag_start.x - self.reference if width: ratio = self.off.y / width self.trafo = Trafo(1, ratio, 0, 1, 0, - ratio * self.reference) self.trafo_desc = (3, ratio) def DrawDragged(self, device, partially): sel = self.selection if sel == self.selCenter: pass #device.DrawPixmapHandle(self.drag_cur, pixmaps.Center) else: trafo = self.trafo if trafo: device.PushTrafo() device.Concat(trafo) device.DrawRubberRect(self.start, self.end) if self.outline_object is not None: self.outline_object.DrawShape(device) device.PopTrafo() def ButtonDown(self, p, button, state): self.drag_state = state self.trafo = Identity self.trafo_desc = (0, 0) SelectAndDrag.DragStart(self, p) sel = self.selection if sel == self.selCenter: self.drag_cur = self.drag_start = self.center return p - self.center ds_x = ds_y = 0 if sel in self.selLeft: ds_x = self.start.x if sel in self.selTop: ds_y = self.start.y if sel in self.selRight: ds_x = self.end.x if sel in self.selBottom: ds_y = self.end.y self.drag_cur = self.drag_start = ds = Point(ds_x, ds_y) if sel in self.selTurn: vec = ds - self.center self.start_angle = math.atan2(vec.y, vec.x) else: if sel == 2: self.reference = self.end.y elif sel == 4: self.reference = self.start.x elif sel == 6: self.reference = self.start.y elif sel == 8: self.reference = self.end.x return p - ds def constrain_center(self, p, state): if state & const.ConstraintMask: start = self.start end = self.end if p.x < 0.75 * start.x + 0.25 * end.x: x = start.x elif p.x > 0.25 * start.x + 0.75 * end.x: x = end.x else: x = (start.x + end.x) / 2 if p.y < 0.75 * start.y + 0.25 * end.y: y = start.y elif p.y > 0.25 * start.y + 0.75 * end.y: y = end.y else: y = (start.y + end.y) / 2 return Point(x, y) return p def MouseMove(self, p, state): self.drag_state = state if self.selection == self.selCenter: p = self.constrain_center(p, state) SelectAndDrag.MouseMove(self, p, state) self.compute_trafo(state) def ButtonUp(self, p, button, state): if self.selection == self.selCenter: p = self.constrain_center(p, state) SelectAndDrag.DragStop(self, p) sel = self.selection if sel == self.selCenter: self.center = self.drag_cur return '', None else: self.compute_trafo(state) trafo = self.trafo if self.selection in self.selShear: text = _("Shear Objects") else: text = _("Rotate Objects") return text, trafo def CurrentInfoText(self): if self.selection == self.selCenter: text = _("Rotation Center at %(position)[position]") data = {'position': self.drag_cur} else: type, value = self.trafo_desc if type == 1: text = _("Rotate by %(angle)[angle]") data = {'angle': value} elif type == 2: text = _("Horizontal Shear by %(ratio)[factor]") data = {'ratio': value} elif type == 3: text = _("Vertical Shear by %(ratio)[factor]") data = {'ratio': value} else: text = _("Identity Transform") data = {} return text, data def Hit(self, p, rect, device): pass def Select(self): pass def SelectPoint(self, p, rect, device, mode = SelectSet): self.selection = 0 return self.selection def SelectHandle(self, handle, mode = SelectSet): handle = handle.index if handle == len(self.handle_idx_to_sel): self.selection = self.selCenter else: self.selection = self.handle_idx_to_sel[handle] def GetHandles(self): return None #sx = self.start.x #sy = self.start.y #ex = self.end.x #ey = self.end.y #x2 = (sx + ex) / 2 #y2 = (sy + ey) / 2 #return map(handle.MakePixmapHandle, #[Point(sx, ey), Point(x2, ey), Point(ex, ey), #Point(sx, y2), Point(ex, y2), #Point(sx, sy), Point(x2, sy), Point(ex, sy)], #[(-1, 1), (0, 1), ( 1, 1), #(-1, 0), ( 1, 0), #(-1, -1), (0, -1), ( 1, -1)], #[pixmaps.TurnTL, pixmaps.ShearLR, pixmaps.TurnTR, #pixmaps.ShearUD, pixmaps.ShearUD, #pixmaps.TurnBL, pixmaps.ShearLR, pixmaps.TurnBR], #[const.CurTurn] * 8) \ #+ [handle.MakePixmapHandle(self.center, (0, 0), pixmaps.Center)] class TrafoSelection(Selection): def __init__(self, copy_from = None): Selection.__init__(self, copy_from) self.center = None def update_rectangle(self, same_center = 1): if self: self.rect = TrafoRectangle(self.coord_rect, self.center) else: self.rect = TrafoRectangle(Rect(0, 0, 0, 0)) def ButtonDown(self, p, button, state): if len(self.objects) == 1: self.rect.SetOutlineObject(self.objects[0][-1]) return self.rect.ButtonDown(p, button, state) def MouseMove(self, p, state): self.rect.MouseMove(p, state) def ButtonUp(self, p, button, state, forget_trafo = 0): self.rect.SetOutlineObject(None) undo_text, trafo = self.rect.ButtonUp(p, button, state) self.center = self.rect.center if forget_trafo: return None, None if trafo is not None: t = time.clock() #undo = self.ForAllUndo(lambda o, t = trafo: o.Transform(t)) undo = self.ForAllUndo2('Transform', trafo) self.del_lazy_attrs() #print 'transform/translate', time.clock() - t return undo_text, undo return '', None def Show(self, device, partially = 0): self.rect.Show(device, partially) def Hide(self, device, partially = 0): self.rect.Hide(device, partially) def DrawDragged(self, device, partial): self.rect.DrawDragged(device, partial) def SelectPoint(self, p, rect, device, mode = SelectSet): if not self.rect.SelectPoint(p, rect, device, mode): if self.Hit(p, rect, device): self.rect.Select() def SelectHandle(self, handle, mode = SelectSet): self.rect.SelectHandle(handle, mode) def Hit(self, p, rect, device): if self.objects: return (rect.overlaps(self.rect.bounding_rect) and Selection.Hit(self, p, rect, device)) return 0 def CurrentInfoText(self): return self.rect.CurrentInfoText() class EditorWrapper: def __init__(self, editor): self.editor = editor def __del__(self): self.editor.Destroy() def __getattr__(self, attr): return getattr(self.editor, attr) def compatible(self, aclass): obj = self.editor return isinstance(obj, aclass) or issubclass(obj.EditedClass, aclass) class EditSelection(Selection): is_EditSelection = 1 drag_this = None editor = None def __init__(self, copy_from = None): Selection.__init__(self, copy_from) self.check_edit_mode() if type(copy_from) == InstanceType \ and copy_from.__class__ == self.__class__: self.editor = copy_from.editor else: self.get_editor() def check_edit_mode(self): # allow only one object for editing at a time if self.objects: if len(self.objects) > 1 or not self.objects[0][-1].has_edit_mode: self.objects = [] def get_editor(self): if self.objects: self.editor = EditorWrapper(self.objects[0][-1].Editor()) else: self.editor = None def GetHandles(self): if self.editor is not None: return self.editor.GetHandles() return [] def ButtonDown(self, p, button, state): if self.drag_this is not None: return self.drag_this.ButtonDown(p, button, state) else: return None def MouseMove(self, p, state): if self.drag_this is not None: self.drag_this.MouseMove(p, state) def ButtonUp(self, p, button, state, forget_trafo = 0): if self.drag_this is not None: self.del_lazy_attrs() return _("Edit Object"), self.drag_this.ButtonUp(p, button, state) return ('', None) # XXX make the undo text more general by a method of graphics objects def Show(self, device, partially = 0): if self.editor is not None: self.editor.Show(device, partially) def Hide(self, device, partially = 0): if self.editor is not None: self.editor.Hide(device, partially) def DrawDragged(self, device, partial): if self.editor is not None: self.editor.DrawDragged(device, partial) def SetSelection(self, info): old_sel = self.objects Selection.SetSelection(self, info) self.check_edit_mode() if old_sel != self.objects: self.get_editor() return 1 return 0 def SelectPoint(self, p, rect, device, mode = const.SelectSet): self.drag_this = None if self.editor is not None: if self.editor.SelectPoint(p, rect, device, mode): self.drag_this = self.editor return self.drag_this != None def SelectHandle(self, handle, mode = SelectSet): if self.editor is not None: self.editor.SelectHandle(handle, mode) self.drag_this = self.editor else: self.drag_this = None def SelectRect(self, rect, mode = SelectSet): self.drag_this = None if self.editor is not None: if self.editor.SelectRect(rect, mode): self.drag_this = self.editor return self.drag_this != None def CallObjectMethod(self, aclass, methodname, args): if len(self.objects) == 1: if self.editor is not None: obj = self.editor if not obj.compatible(aclass): warn(INTERNAL, 'EditSelection.GetObjectMethod: ' 'editor %s is not compatible with class %s', self.editor, aclass) return NullUndo else: obj = self.objects[0][-1] if not isinstance(obj, aclass): warn(INTERNAL, 'EditSelection.GetObjectMethod: ' 'object is not instance of %s', aclass) return NullUndo try: method = getattr(obj, methodname) except AttributeError: warn(INTERNAL, 'EditSelection.GetObjectMethod: ' 'no method %s for class %s', methodname, aclass) return NullUndo undo = apply(method, args) if undo is None: undo = NullUndo return undo return NullUndo def GetObjectMethod(self, aclass, method): if len(self.objects) == 1: if self.editor is not None: obj = self.editor if not obj.compatible(aclass): warn(INTERNAL, 'EditSelection.GetObjectMethod: ' 'editor is not compatible with class %s', aclass) return None else: obj = self.objects[0][-1] if not isinstance(obj, aclass): warn(INTERNAL, 'EditSelection.GetObjectMethod: ' 'object is not instance of %s', aclass) return None try: return getattr(obj, method) except AttributeError: warn(INTERNAL, 'EditSelection.GetObjectMethod: ' 'no method %s for class %s', method, aclass) pass return None def ChangeRect(self): if self.editor is not None: return self.editor.ChangeRect() return self.bounding_rect def InfoText(self): # Return a string describing the selected object(s) # XXX we shouldn't access document.layers directly if self.editor is not None: return self.editor.Info() else: return _("No Selection") def CurrentInfoText(self): if self.editor is not None: return self.editor.CurrentInfoText() else: return "" UniConvertor-1.1.4/src/app/Graphics/blend.py0000775000076400007640000000305511060500566017420 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2003-2006 by Igor E. Novikov # Copyright (C) 1997, 1998, 1999 by Bernhard Herzog # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. # # blend function # from app import Trafo, SketchError, _sketch class MismatchError(SketchError): pass def Blend(obj1, obj2, frac1, frac2 = None): if frac2 is None: frac2 = 1.0 - frac1 try: return obj1.Blend(obj2, frac1, frac2) except MismatchError: pass try: return obj2.Blend(obj1, frac2, frac1) except MismatchError: pass try: from bezier import PolyBezier if not isinstance(obj1,PolyBezier) and not isinstance(obj2,PolyBezier)\ and obj1.is_curve and obj2.is_curve: paths = BlendPaths(obj1.Paths(), obj2.Paths(), frac1, frac2) properties = Blend(obj1.Properties(), obj2.Properties(), frac1, frac2) return PolyBezier(paths = paths, properties = properties) except AttributeError, value: if str(value) != 'is_curve': raise return obj1.Duplicate() def BlendTrafo(t1, t2, frac1, frac2): return Trafo(frac1 * t1.m11 + frac2 * t2.m11, frac1 * t1.m21 + frac2 * t2.m21, frac1 * t1.m12 + frac2 * t2.m12, frac1 * t1.m22 + frac2 * t2.m22, frac1 * t1.v1 + frac2 * t2.v1, frac1 * t1.v2 + frac2 * t2.v2) def BlendPaths(paths1, paths2, frac1, frac2): length = min((len(paths1), len(paths2))) paths = [None] * length blend_paths = _sketch.blend_paths for i in range(length): paths[i] = blend_paths(paths1[i], paths2[i], frac1, frac2) return tuple(paths) UniConvertor-1.1.4/src/app/Graphics/group.py0000775000076400007640000000304011060500565017461 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1997, 1998 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from app.events.warn import warn_tb, INTERNAL from app import _ from compound import EditableCompound from blend import Blend, MismatchError class Group(EditableCompound): is_Group = 1 def Info(self): return _("Group with %d objects") % len(self.objects) def Blend(self, other, frac1, frac2): try: objs = self.objects oobjs = other.objects blended = [] for i in range(min(len(objs), len(oobjs))): blended.append(Blend(objs[i], oobjs[i], frac1, frac2)) return Group(blended) except: warn_tb(INTERNAL) raise MismatchError Ungroup = EditableCompound.GetObjects def SaveToFile(self, file): file.BeginGroup() for obj in self.objects: obj.SaveToFile(file) file.EndGroup() UniConvertor-1.1.4/src/app/Graphics/text.py0000775000076400007640000007630411060500566017327 0ustar igorigor# -*- coding: utf-8 -*- # Sketch - A Python-based interactive drawing program # Copyright (C) 1997, 1998, 1999, 2000 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # SimpleText: A graphics object representing a single line of text # # # Model: # # An instance of SimpleText is a single line of text in one particular # font. The position and orientation of the text is described by an # anchor point and an arbitrary linear transformation (a 2x2-matrix). # The transformation allows rotated, sheared, reflected and nonuniformly # scaled text. # # In addition, there are two flags that control which of several special # points of the text is located at the anchor point: horizontal and # vertical alignment. The special point chosen is the reference point. # # Horizontal alignment can be one of `left', `right' and `center', # meaning the left, right and horizontal center of the text. Vertical # alignment can be `top', `bottom', `center' and `baseline', referring # to the top, bottom, vertical center and the baseline of the text. # # The default alignment is left and baseline. # # The anchor point is also the layout point. If some form of snapping is # active, this point will be `magnetic'. # # # Representation: # # The position and orientation of a SimpleText-instance is stored in a # single affine transformation in the instance variable `trafo' (An # instance of the Trafo type (see the developer's guide)). The # translation part of the transformation (trafo.v1 and trafo.v2) is the # position of the anchor point. The matrix part (trafo.m11, ..., # trafo.m22) is the linear transformation. # # The alignment is stored in the instance variables `halign' and # `valign' and in a special internal transformation `atrafo'. # # atrafo is set up in such a way, that trafo(atrafo) (the concatenation # of both transformations) maps text coordinates to document # coordinates. Text coordinates are the natural coordinates for the text # and the given font and size. The origin is the leftmost point of the # first character projected on the baseline. X extends to the right, y # upwards. The unit is the point. # # While halign and valign are independent of font, size and the text # itself, atrafo needs to be recomputed every time some of these change. # # The definition of trafo and atrafo leads to these rules: # # 1. trafo(0, 0) is the anchor point. # # 2. If (rx, ry) is the reference point in text coordinates, then # atrafo(rx, ry) == (0, 0) # # 3. For the default alignment, atrafo is the identity transformation. # # from string import split from math import sin, cos, atan2, hypot, pi, fmod, floor from app import _, Rect, UnionRects, EmptyRect, NullPoint, Polar, \ IdentityMatrix, SingularMatrix, Identity, Trafo, Scale, Translation, \ Rotation, NullUndo, CreateMultiUndo, RegisterCommands #from app.UI.command import AddCmd from app import config from app.conf import const import handle import selinfo, codecs from base import Primitive, RectangularPrimitive, Creator, Editor from compound import Compound from group import Group from bezier import PolyBezier, CombineBeziers from blend import Blend, MismatchError, BlendTrafo from properties import PropertyStack, FactoryTextStyle, DefaultTextProperties import color, pattern, app import graphics, font font_module = font from app.Lib import encoding; iso_latin_1 = encoding.iso_latin_1 (encoder,decoder, sr,sw)=codecs.lookup('utf-8') printable = '' for n in range(len(iso_latin_1)): if iso_latin_1[n] != encoding.notdef: printable = printable + chr(n) # Alignment. Defaults are 0 ALIGN_BASE = 0 ALIGN_CENTER = 1 ALIGN_TOP = 2 ALIGN_BOTTOM = 3 ALIGN_LEFT = 0 ALIGN_CENTER = 1 ALIGN_RIGHT = 2 class CommonText: commands = [] def __init__(self, text = '', duplicate = None): if duplicate is not None: self.text = duplicate.text else: self.text = text def SetText(self, text, caret = None): if self.editor is not None: oldcaret = self.editor.Caret() else: oldcaret = 0 undo = (self.SetText, self.text, oldcaret) self.text = text if caret is not None and self.editor is not None: self.editor.SetCaret(caret) self._changed() return undo def Text(self): return self.text editor = None def set_editor(self, editor): self.editor = editor def unset_editor(self, editor): if self.editor is editor: self.editor = None def SetFont(self, font, size = None): if size is not None: undo = self.properties.SetProperty(font = font, font_size = size) else: undo = self.properties.SetProperty(font = font) return self.properties_changed(undo) def SetGap(self, char, word, line): undo = self.properties.SetProperty(chargap = char, wordgap = word, linegap = line) return self.properties_changed(undo) def SetAlign(self, align, valign): if align == const.ALIGN_CENTER: valign=const.ALIGN_CENTER else: valign=const.ALIGN_BASE undo = self.properties.SetProperty(align = align, valign = valign) return self.properties_changed(undo) def SetFontSize(self, size): undo = self.properties.SetProperty(font_size = size) return self.properties_changed(undo) def Font(self): return self.properties.font def FontSize(self): return self.properties.font_size class CommonTextEditor(Editor): EditedClass = CommonText commands = [] def __init__(self, object): Editor.__init__(self, object) self.caret = 0 object.set_editor(self) def Destroy(self): self.object.unset_editor(self) def ButtonDown(self, p, button, state): Editor.DragStart(self, p) def ButtonUp(self, p, button, state): Editor.DragStop(self, p) def update_selection(self): # a bit ugly... if self.document is not None: self.document.queue_selection() def SetCaret(self, caret): if caret > len(self.text): caret = len(self.text) self.caret = caret def Caret(self): return self.caret def InsertCharacter(self, event): # if len(char) == 1 and self.properties.font.IsPrintable(char): try: char = event.char char=char.decode('utf-8') text = self.text; caret = self.caret text = text[:caret] + char + text[caret:] return self.SetText(text, caret + 1) except: return NullUndo # AddCmd(commands, InsertCharacter, '', key_stroke = tuple(printable), invoke_with_event = 1) def InsertEOL(self): try: text = self.text; caret = self.caret text = text[:caret] + '\n' + text[caret:] return self.SetText(text, caret + 1) except: return NullUndo # AddCmd(commands, InsertEOL, '', key_stroke = ('Return','KP_Enter')) def InsertTAB(self): try: text = self.text; caret = self.caret text = text[:caret] + '\t' + text[caret:] return self.SetText(text, caret + 1) except: return NullUndo # AddCmd(commands, InsertTAB, '', key_stroke = 'Tab') def InsertTextFromClipboard(self): try: insertion = app.root.tk.call('::tk::GetSelection','.','CLIPBOARD') insertion=insertion.decode('utf-8') text = self.text; caret = self.caret text = text[:caret] + insertion + text[caret:] return self.SetText(text, caret + len(insertion)) except: return NullUndo # AddCmd(commands, InsertTextFromClipboard, '', key_stroke = ('Ctrl+v', 'Shift+Insert')) def DeleteCharBackward(self): if self.text and self.caret > 0: text = self.text; caret = self.caret text = text[:caret - 1] + text[caret:] return self.SetText(text, caret - 1) return NullUndo # AddCmd(commands, DeleteCharBackward, '', key_stroke = 'BackSpace') def DeleteCharForward(self): if self.text and self.caret < len(self.text): text = self.text; caret = self.caret text = text[:caret] + text[caret + 1:] return self.SetText(text, caret) return NullUndo # AddCmd(commands, DeleteCharForward, '', key_stroke = ('Delete', 'KP_Delete')) def MoveForwardChar(self): if self.caret < len(self.text): self.SetCaret(self.caret + 1) self.update_selection() return NullUndo # AddCmd(commands, MoveForwardChar, '', key_stroke = ('Right', 'KP_Right')) def MoveBackwardChar(self): if self.caret > 0: self.SetCaret(self.caret - 1) self.update_selection() return NullUndo # AddCmd(commands, MoveBackwardChar, '', key_stroke = ('Left', 'KP_Left')) def MoveToNextLine(self): lines=split(self.text, '\n') index, line_index = self.get_position() if line_index < len(lines)-1: if index>len(lines[line_index+1]): self.SetCaret(self.caret + len(lines[line_index+1])+len(lines[line_index])-index+2) else: self.SetCaret(self.caret + len(lines[line_index])+1) self.update_selection() return NullUndo # AddCmd(commands, MoveToNextLine, '', key_stroke = ('Down', 'KP_Down')) def MoveToPreviousLine(self): lines=split(self.text, '\n') index, line_index = self.get_position() if line_index > 0: if index>len(lines[line_index-1]): self.SetCaret(self.caret - index) else: self.SetCaret(self.caret - len(lines[line_index-1])-1) self.update_selection() return NullUndo # AddCmd(commands, MoveToPreviousLine, '', key_stroke = ('Up', 'KP_Up')) def MoveToBeginningOfLine(self): index, line_index = self.get_position() self.SetCaret(self.caret-index+1) self.update_selection() return NullUndo # AddCmd(commands, MoveToBeginningOfLine, '', key_stroke = ('Home', 'KP_Home')) def MoveToBeginningOfText(self): self.SetCaret(0) self.update_selection() return NullUndo # AddCmd(commands, MoveToBeginningOfText, '', key_stroke = ('Ctrl-Home', 'Ctrl-KP_Home')) def MoveToEndOfLine(self): lines=split(self.text, '\n') index, line_index = self.get_position() self.SetCaret(self.caret+len(lines[line_index])-index+1) self.update_selection() return NullUndo # AddCmd(commands, MoveToEndOfLine, '', key_stroke = ('End', 'KP_End')) def MoveToEndOfText(self): self.SetCaret(len(self.text)) self.update_selection() return NullUndo # AddCmd(commands, MoveToEndOfText, '', key_stroke = ('Ctrl-End', 'Ctrl-KP_End')) def get_position(self): lines=split(self.text, '\n') caret=self.caret+1 index=0 line_index=0 for line in lines: line+='\n' caret-=len(line) if caret<=0: index=len(line)+caret break line_index+=1 return (index,line_index) RegisterCommands(CommonTextEditor) class SimpleText(CommonText, RectangularPrimitive): has_edit_mode = 1 is_Text = 1 is_SimpleText = 1 is_curve = 1 is_clip = 1 has_font = 1 has_fill = 1 has_line = 0 commands = CommonText.commands + RectangularPrimitive.commands _lazy_attrs = RectangularPrimitive._lazy_attrs.copy() _lazy_attrs['atrafo'] = 'update_atrafo' def __init__(self, trafo = None, text = '', halign = const.ALIGN_LEFT, valign = const.ALIGN_BASE, properties = None, duplicate = None): CommonText.__init__(self, text, duplicate) RectangularPrimitive.__init__(self, trafo, properties = properties, duplicate = duplicate) if duplicate != None: self.halign = duplicate.halign self.valign = duplicate.valign self.atrafo = duplicate.atrafo else: self.halign = halign self.valign = valign if properties is None: self.properties = PropertyStack(base=FactoryTextStyle()) self.properties.align = self.halign self.properties.valign = self.valign self.cache = {} def Disconnect(self): self.cache = {} RectangularPrimitive.Disconnect(self) def Hit(self, p, rect, device, clip = 0): a = self.properties llx, lly, urx, ury = a.font.TextBoundingBox(self.text, a.font_size, a) trafo = self.trafo(self.atrafo) trafo = trafo(Trafo(urx - llx, 0, 0, ury - lly, llx, lly)) return device.ParallelogramHit(p, trafo, 1, 1, 1, ignore_outline_mode = 1) def GetObjectHandle(self, multiple): trafo = self.trafo(self.atrafo(Scale(self.properties.font_size))) if multiple: return trafo(NullPoint) else: pts = self.properties.font.TypesetText(self.text,self.properties) return map(trafo, pts) def SetAlignment(self, horizontal, vertical): undo = (self.SetAlignment, self.halign, self.valign) if horizontal is not None: self.halign = horizontal self.properties.align = horizontal if vertical is not None: self.valign = vertical self.properties.valign = vertical self._changed() return undo # AddCmd(commands, 'AlignLeft', _("Align Left"), SetAlignment, # args = (const.ALIGN_LEFT, None)) # AddCmd(commands, 'AlignRight', _("Align Right"), SetAlignment, # args =(const.ALIGN_RIGHT,None)) # AddCmd(commands, 'AlignHCenter', _("Align H. Center"), SetAlignment, # args = (const.ALIGN_CENTER, None)) # AddCmd(commands, 'AlignTop', _("Align Top"), SetAlignment, # args = (None, const.ALIGN_TOP)) # AddCmd(commands, 'AlignVCenter', _("Align V. Center"), SetAlignment, # args =(None, const.ALIGN_CENTER)) # AddCmd(commands, 'AlignBase', _("Align Baseline"), SetAlignment, # args = (None, const.ALIGN_BASE)) # AddCmd(commands, 'AlignBottom', _("Align Bottom"), SetAlignment, # args = (None, const.ALIGN_BOTTOM)) def Alignment(self): return self.properties.align, self.properties.valign def RemoveTransformation(self): if self.trafo.matrix() != IdentityMatrix: a = self.properties trafo = self.trafo llx, lly, urx, ury = a.font.TextCoordBox(self.text, a.font_size, a) try: undostyle = Primitive.Transform(self, trafo.inverse()) except SingularMatrix: undostyle = None undotrafo = self.set_transformation(Translation(trafo.offset())) return CreateMultiUndo(undostyle, undotrafo) return NullUndo def DrawShape(self, device, rect = None, clip = 0): RectangularPrimitive.DrawShape(self, device) base_trafo = self.trafo(self.atrafo) base_trafo = base_trafo(Scale(self.properties.font_size)) paths = self.properties.font.GetPaths(self.text, self.properties) obj = PolyBezier(paths, self.properties.Duplicate()) obj.Transform(base_trafo) device.MultiBezier(obj.paths, rect, clip) def update_atrafo(self): # a = self.properties # llx, lly, urx, ury = a.font.TextCoordBox(self.text, a.font_size, a) # hj = self.halign # if hj == ALIGN_RIGHT: # xoff = llx - urx # elif hj == ALIGN_CENTER: # xoff = (llx - urx) / 2 # else: # xoff = 0 xoff = 0 yoff=0 # vj = self.valign # if vj == ALIGN_TOP: # yoff = -ury # elif vj == ALIGN_CENTER: # yoff = (lly - ury) / 2 - lly # elif vj == ALIGN_BOTTOM: # yoff = -lly # else: # yoff = 0 self.atrafo = Translation(xoff, yoff) def update_rects(self): trafo = self.trafo(self.atrafo) a = self.properties rect = apply(Rect, a.font.TextBoundingBox(self.text, a.font_size, a)) self.bounding_rect = trafo(rect).grown(2) rect = apply(Rect, a.font.TextCoordBox(self.text, a.font_size, a)) self.coord_rect = trafo(rect) def Info(self): text=self.text.replace('\n','') text=text.replace('\r','') text=text.strip() text=text.encode('utf-8') return (_("Text `%(text)s' at %(position)[position]"), {'text':text[:10], 'position':self.trafo.offset()} ) def FullTrafo(self): # XXX perhaps the Trafo method should return # self.trafo(self.atrafo) for a SimpleText object as well. return self.trafo(self.atrafo) def SaveToFile(self, file): RectangularPrimitive.SaveToFile(self, file) file.SimpleText(self.text, self.trafo, self.properties.align, self.properties.valign, self.properties.chargap, self.properties.wordgap, self.properties.linegap) def Blend(self, other, p, q): if self.__class__ != other.__class__ \ or self.properties.font != other.properties.font \ or self.text != other.text: raise MismatchError blended = self.__class__(BlendTrafo(self.trafo, other.trafo, p, q), self.text) self.set_blended_properties(blended, other, p, q) return blended def AsBezier(self): if self.text: text = split(self.text, '\n')[0] base_trafo = self.trafo(self.atrafo) base_trafo = base_trafo(Scale(self.properties.font_size)) paths = self.properties.font.GetPaths(self.text, self.properties) obj = PolyBezier(paths, self.properties.Duplicate()) obj.Transform(base_trafo) return obj def Paths(self): # paths = [] if self.text: text = split(self.text, '\n')[0] base_trafo = self.trafo(self.atrafo) base_trafo = base_trafo(Scale(self.properties.font_size)) paths = self.properties.font.GetPaths(self.text, self.properties) obj = PolyBezier(paths, self.properties.Duplicate()) obj.Transform(base_trafo) return obj.paths # base_trafo = self.trafo(self.atrafo) # base_trafo = base_trafo(Scale(self.properties.font_size)) # pos = self.properties.font.TypesetText(self.text) # for i in range(len(self.text)): # outline = self.properties.font.GetOutline(self.text[i]) # trafo = base_trafo(Translation(pos[i])) # for path in outline: # path.Transform(trafo) # paths.append(path) # return tuple(paths) def Editor(self): return SimpleTextEditor(self) context_commands = ('AlignLeft', 'AlignRight', 'AlignHCenter', None, 'AlignTop', 'AlignVCenter', 'AlignBase', 'AlignBottom') RegisterCommands(SimpleText) class SimpleTextCreator(Creator): is_Text = 1 # XXX: ugly creation_text = _("Create Text") def __init__(self, start): Creator.__init__(self, start) def ButtonDown(self, p, button, state): Creator.DragStart(self, p) def MouseMove(self, p, state): p = self.apply_constraint(p, state) Creator.MouseMove(self, p, state) def ButtonUp(self, p, button, state): p = self.apply_constraint(p, state) Creator.DragStop(self, p) def DrawDragged(self, device, partially): device.DrawLine(self.start, self.drag_cur) def apply_constraint(self, p, state): if state & const.ConstraintMask: r, phi = (p - self.start).polar() pi12 = pi / 12 phi = pi12 * floor(phi / pi12 + 0.5) p = self.start + Polar(r, phi) return p def CreatedObject(self): trafo = Translation(self.start) r, phi = (self.drag_cur - self.start).polar() if r: trafo = trafo(Rotation(phi)) return SimpleText(trafo = trafo, properties = DefaultTextProperties()) class SimpleTextEditor(CommonTextEditor): EditedClass = SimpleText commands = CommonTextEditor.commands[:] def GetHandles(self): a = self.properties pos, up = a.font.TextCaretData(self.text, self.caret, a.font_size, a) pos = self.trafo(self.atrafo(pos)) up = self.trafo.DTransform(up) return [handle.MakeCaretHandle(pos, up)] def SelectPoint(self, p, rect, device, mode): trafo = self.trafo(self.atrafo(Scale(self.properties.font_size))) trafo = trafo.inverse() p2 = trafo(p) pts = self.properties.font.TypesetText(self.text + ' ',self.properties) dists = [] for i in range(len(pts)): dists.append((abs(pts[i].x - p2.x), i)) caret = min(dists)[-1] self.SetCaret(caret) # print "CATCHED!" return 1 def Destroy(self): CommonTextEditor.Destroy(self) self.document.AddAfterHandler(maybe_remove_text, (self.object,)) RegisterCommands(SimpleTextEditor) def maybe_remove_text(text): if text.parent is not None and not text.text: doc = text.document doc.DeselectObject(text) doc.AddUndo(text.parent.Remove(text)) doc.selection.update_selinfo() # # # PATHTEXT_ROTATE = 1 PATHTEXT_SKEW = 2 def coord_sys_at(lengths, pos, type): if len(lengths) < 2: return None for idx in range(len(lengths)): if lengths[idx][0] > pos: d2, p2 = lengths[idx] d1, p1 = lengths[idx - 1] if d2 != d1 and p1 != p2: break else: return None t = (pos - d1) / (d2 - d1) p = (1 - t) * p1 + t * p2 diff = (p2 - p1).normalized() del lengths[:idx - 1] if type == PATHTEXT_SKEW: return Trafo(diff.x, diff.y, 0, 1, p.x, p.y) else: return Trafo(diff.x, diff.y, -diff.y, diff.x, p.x, p.y) def pathtext(path, start_pos, text, font, size, type, properties): metric = font.metric lengths = path.arc_lengths(start_pos) scale = Scale(size); factor = size / 2000.0 pos = font.TypesetText(text, properties) pos = map(scale, pos) trafos = [] for idx in range(len(text)): char = text[idx] width2 = metric.char_width(ord(char)) * factor x = pos[idx].x + width2 trafo = coord_sys_at(lengths, x, type) if trafo is not None: trafos.append(trafo(Translation(-width2, 0))) else: # we've reached the end of the path. Ignore all following # characters break return trafos class InternalPathText(CommonText, Primitive): has_edit_mode = 1 is_Text = 1 is_PathTextText = 1 is_curve = 0 is_clip = 1 has_font = 1 has_fill = 1 has_line = 0 _lazy_attrs = Primitive._lazy_attrs.copy() _lazy_attrs['trafos'] = 'update_trafos' _lazy_attrs['paths'] = 'update_paths' commands = CommonText.commands + Primitive.commands def __init__(self, text = '', trafo = None, model = PATHTEXT_ROTATE, start_pos = 0.0, properties = None, duplicate = None): CommonText.__init__(self, text, duplicate = duplicate) Primitive.__init__(self, properties = properties, duplicate = duplicate) if duplicate is not None and isinstance(duplicate, self.__class__): # dont copy paths, update it from parent self.trafo = duplicate.trafo self.model = duplicate.model self.start_pos = duplicate.start_pos else: if trafo is None: self.trafo = Identity else: self.trafo = trafo self.model = model self.start_pos = start_pos self.cache = {} def update_rects(self): a = self.properties length = len(self.trafos) sizes = [a.font_size] * length boxes = map(a.font.TextBoundingBox, self.text[:length], sizes, a) rects = map(lambda *a:a, map(apply, [Rect] * length, boxes)) self.bounding_rect = reduce(UnionRects, map(apply, self.trafos, rects), EmptyRect) boxes = map(a.font.TextCoordBox, self.text[:length], sizes, a) rects = map(lambda *a:a, map(apply, [Rect] * length, boxes)) self.coord_rect = reduce(UnionRects, map(apply, self.trafos, rects), EmptyRect) def update_trafos(self): self.trafos = map(self.trafo, pathtext(self.paths[0], self.start_pos, self.text, self.properties.font, self.properties.font_size, self.model,self.properties)) def update_paths(self): paths = self.parent.get_paths() try: itrafo = self.trafo.inverse() transformed = [] for path in paths: path = path.Duplicate() path.Transform(itrafo) transformed.append(path) paths = tuple(transformed) except SingularMatrix: # XXX what do we do? pass self.paths = paths def SetText(self, text, caret = None): self.cache = {} return CommonText.SetText(self, text, caret) def PathChanged(self): self.del_lazy_attrs() def SetModel(self, model): undo = (self.SetModel, self.model) self.model = model self._changed() return undo def Model(self): return self.model def SetStartPos(self, start_pos): undo = (self.SetStartPos, self.start_pos) self.start_pos = start_pos self._changed() return undo def StartPos(self): return self.start_pos def CharacterTransformations(self): return self.trafos def DrawShape(self, device, rect = None, clip = 0): text = self.text; trafos = self.trafos font = self.properties.font; font_size = self.properties.font_size Primitive.DrawShape(self, device) device.BeginComplexText(clip, self.cache) for idx in range(len(trafos)): char = text[idx] if char not in '\r\n': # avoid control chars device.DrawComplexText(text[idx], trafos[idx], font, font_size) device.EndComplexText() def Disconnect(self): self.cache = {} Primitive.Disconnect(self) def Hit(self, p, rect, device, clip = 0): bbox = self.properties.font.TextBoundingBox font_size = self.properties.font_size text = self.text; trafos = self.trafos for idx in range(len(trafos)): llx, lly, urx, ury = bbox(text[idx], font_size, self.properties) trafo = trafos[idx](Trafo(urx - llx, 0, 0, ury - lly, llx, lly)) if device.ParallelogramHit(p, trafo, 1, 1, 1, ignore_outline_mode = 1): return 1 return 0 def Translate(self, offset): return NullUndo def Transform(self, trafo): return self.set_transformation(trafo(self.trafo)) def set_transformation(self, trafo): undo = (self.set_transformation, self.trafo) self.trafo = trafo self._changed() return undo def RemoveTransformation(self): return self.set_transformation(Identity) def Blend(self, other, p, q): if self.__class__ != other.__class__ \ or self.properties.font != other.properties.font \ or self.text != other.text: raise MismatchError trafo = BlendTrafo(self.trafo, other.trafo, p, q) start_pos = p * self.start_pos + q * other.start_pos blended = self.__class__(self.text, trafo = trafo, start_pos = start_pos, model = self.model) self.set_blended_properties(blended, other, p, q) return blended def SaveToFile(self, file): Primitive.SaveToFile(self, file) file.InternalPathText(self.text, self.trafo, self.model, self.start_pos) def Info(self): return _("Text on Path: `%(text)s'") % {'text':self.text[:10]} def Editor(self): return InternalPathTextEditor(self) class InternalPathTextEditor(CommonTextEditor): EditedClass = InternalPathText commands = CommonTextEditor.commands def GetHandles(self): a = self.properties if self.caret > 0 and self.trafos: # special case to deal with here: the characters that fall # off the end of the path are not visible. If the caret is # in this invisible area, display the caret after the last # visible character caret = 1 index = min(self.caret, len(self.text), len(self.trafos)) - 1 text = self.text[index] trafo = self.trafos[index] else: caret = 0 if self.text and self.trafos: text = self.text[0] trafo = self.trafos[0] else: # XXX fix this self.start_point = self.paths[0].point_at(self.start_pos) return [handle.MakeNodeHandle(self.start_point, 1)] pos, up = a.font.TextCaretData(text, caret, a.font_size, a) pos = trafo(pos) up = trafo.DTransform(up) self.start_point = self.trafos[0].offset() return [handle.MakeCaretHandle(pos, up), handle.MakeNodeHandle(self.start_point, 1)] selection = None def SelectHandle(self, handle, mode = const.SelectSet): self.selection = handle def SelectPoint(self, p, rect, device, mode): if self.trafos: dists = [] for i in range(len(self.trafos)): dists.append((abs(p - self.trafos[i].offset()), i)) char = self.text[len(self.trafos) - 1] width = self.properties.font.metric.char_width(ord(char)) / 1000.0 pos = self.trafos[-1](width * self.properties.font_size, 0) dists.append((abs(p - pos), len(self.trafos))) caret = min(dists)[-1] self.SetCaret(caret) def ButtonDown(self, p, button, state): self.cache = {} return p - self.start_point def nearest_start_pos(self, p): try: x, y = self.trafo.inverse()(p) t = self.paths[0].nearest_point(x, y) except SingularMatrix: # XXX t = 0.0 return t def DrawDragged(self, device, partially): text = self.text; trafos = self.trafos font = self.properties.font; font_size = self.properties.font_size t = self.nearest_start_pos(self.drag_cur) trafos = map(self.trafo, pathtext(self.paths[0], t, text, font, font_size, self.model, self.properties)) device.BeginComplexText(0, self.cache) for idx in range(len(trafos)): char = text[idx] if char not in '\n\r': device.DrawComplexText(char, trafos[idx], font, font_size) device.EndComplexText() device.ResetFontCache() def ButtonUp(self, p, button, state): CommonTextEditor.ButtonUp(self, p, button, state) return self.SetStartPos(self.nearest_start_pos(self.drag_cur)) RegisterCommands(InternalPathTextEditor) class PathText(Compound): is_PathTextGroup = 1 allow_traversal = 1 commands = Compound.commands[:] def __init__(self, text = None, path = None, model = PATHTEXT_ROTATE, start_pos = 0.0, duplicate = None, _blended_text = None): if duplicate is not None: Compound.__init__(self, duplicate = duplicate) self.text = self.objects[0] self.path = self.objects[1] else: if _blended_text is not None: self.text = _blended_text self.path = path Compound.__init__(self, [self.text, self.path]) elif text is not None: self.text = InternalPathText(text.Text(), start_pos = start_pos, model = model, duplicate = text) self.path = path Compound.__init__(self, [self.text, self.path]) else: # we're being loaded self.text = self.path = None Compound.__init__(self) def ChildChanged(self, child): if self.document is not None: self.document.AddClearRect(self.bounding_rect) Compound.ChildChanged(self, child) if child is self.path: self.text.PathChanged() if self.document is not None: self.document.AddClearRect(self.bounding_rect) def load_AppendObject(self, object): Compound.load_AppendObject(self, object) if len(self.objects) == 2: self.text, self.path = self.objects def SelectSubobject(self, p, rect, device, path = None, *rest): idx = self.Hit(p, rect, device) - 1 obj = self.objects[idx] if path: path_idx = path[0] path = path[1:] if path_idx == idx: obj = obj.SelectSubobject(p, rect, device, path) elif path == (): obj = obj.SelectSubobject(p, rect, device) else: return self return selinfo.prepend_idx(idx, obj) def ReplaceChild(self, child, object): if child is self.path and object.is_curve: undo = self.ReplaceChild, object, child self.path = self.objects[1] = object object.SetParent(self) object.SetDocument(self.document) child.SetParent(None) self.ChildChanged(object) #self._changed() return undo else: raise SketchError('Cannot replace child') def Info(self): return _("Path Text: `%(text)s'") % {'text':self.text.Text()[:10]} def SaveToFile(self, file): file.BeginPathText() self.text.SaveToFile(file) self.path.SaveToFile(file) file.EndPathText() def SelectTextObject(self): self.document.SelectObject(self.text) # AddCmd(commands, SelectTextObject, _("Select Text"), key_stroke = 't') def SelectPathObject(self): self.document.SelectObject(self.path) # AddCmd(commands, SelectPathObject, _("Select Path"), key_stroke = 'p') def get_paths(self): return self.path.Paths() def SetModel(self, model): return self.text.SetModel(model) # AddCmd(commands, 'SetModelRotate', _("Rotate Letters"), SetModel, # args = PATHTEXT_ROTATE) # AddCmd(commands, 'SetModelSkew', _("Skew Letters"), SetModel, # args = PATHTEXT_SKEW) def Model(self): return self.text.Model() def Blend(self, other, p, q): if self.__class__ != other.__class__: raise MismatchError return self.__class__(_blended_text = Blend(self.text,other.text, p,q), path = Blend(self.path, other.path, p, q)) context_commands = ('SelectTextObject', 'SelectPathObject', None, 'SetModelRotate', 'SetModelSkew') RegisterCommands(PathText) def CanCreatePathText(objects): if len(objects) == 2: if objects[0].is_Text: return objects[1].is_curve elif objects[0].is_curve: return objects[1].is_Text def CreatePathText(objects): if len(objects) == 2: if objects[0].is_Text: text, curve = objects elif objects[1].is_Text: curve, text = objects if not curve.is_curve: # XXX what do we do here? return text return PathText(text, curve) UniConvertor-1.1.4/src/app/Graphics/rectangle.py0000775000076400007640000002627011060500565020303 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1997, 1998, 1999, 2000 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Rectangle: # # The Rectangle is actually better described as a Parallelogram. When # created interactively, instances of this class are rectangles with # edges parallel to the axes of the coordinate system. The user can then # rotate and shear it interactively so that it may become a # (nonrectangular) Parallelogram. # from math import hypot from app.conf.const import corners, AlternateMask from app import _, SingularMatrix, PointsToRect, Trafo, Polar,\ RoundedRectanglePath, RectanglePath, NullUndo from app.events.warn import warn, INTERNAL #import app.UI.skpixmaps #pixmaps = app.UI.skpixmaps.PixmapTk from base import Primitive, RectangularPrimitive, RectangularCreator, Editor from bezier import PolyBezier import handle from properties import DefaultGraphicsProperties class Rectangle(RectangularPrimitive): is_Rectangle = 1 is_curve = 1 is_clip = 1 has_edit_mode = 1 _lazy_attrs = RectangularPrimitive._lazy_attrs.copy() _lazy_attrs['rect_path'] = 'update_path' def __init__(self, trafo = None, radius1 = 0, radius2 = 0, properties = None, duplicate = None): RectangularPrimitive.__init__(self, trafo, properties = properties, duplicate = duplicate) if duplicate is not None: self.radius1 = duplicate.radius1 self.radius2 = duplicate.radius2 else: self.radius1 = radius1 self.radius2 = radius2 def Radii(self): return self.radius1, self.radius2 def SetTrafoAndRadii(self, trafo, radius1, radius2): undo = self.SetTrafoAndRadii, self.trafo, self.radius1, self.radius2 self.trafo = trafo if radius1 <= 0 or radius2 <= 0: self.radius1 = 0 self.radius2 = 0 if __debug__: if radius1 > 0 or radius2 > 0: warn(INTERNAL, 'Rectangle radius corrected: r1 = %g, r2 = %g', radius1, radius2) else: self.radius1 = radius1 self.radius2 = radius2 self._changed() return undo def DrawShape(self, device, rect = None, clip = 0): Primitive.DrawShape(self, device) if self.radius1 == self.radius2 == 0: device.Rectangle(self.trafo, clip) else: device.RoundedRectangle(self.trafo, self.radius1, self.radius2, clip) def update_path(self): if self.radius1 == self.radius2 == 0: self.rect_path = (RectanglePath(self.trafo),) else: self.rect_path = (RoundedRectanglePath(self.trafo, self.radius1, self.radius2),) def Paths(self): return self.rect_path def AsBezier(self): return PolyBezier(paths = self.rect_path, properties = self.properties.Duplicate()) def Hit(self, p, rect, device, clip = 0): if self.radius1 == self.radius2 == 0: return device.ParallelogramHit(p, self.trafo, 1, 1, clip or self.Filled(), self.properties, ignore_outline_mode = clip) else: return device.MultiBezierHit(self.rect_path, p, self.properties, clip or self.Filled(), ignore_outline_mode = clip) def GetSnapPoints(self): return map(self.trafo, corners) def Snap(self, p): try: x, y = self.trafo.inverse()(p) minx = self.radius1 maxx = 1 - self.radius1 miny = self.radius2 maxy = 1 - self.radius2 if minx < x < maxx: if miny < y < maxy: ratio = hypot(self.trafo.m11, self.trafo.m21) \ / hypot(self.trafo.m12, self.trafo.m22) if x < 0.5: dx = x else: dx = 1 - x if y < 0.5: dy = y else: dy = 1 - y if dy / dx > ratio: x = round(x) else: y = round(y) elif y > maxy: y = 1 else: y = 0 elif miny < y < maxy: if x > maxx: x = 1 else: x = 0 elif minx > 0 and miny > 0: # the round corners if x < 0.5: cx = minx else: cx = maxx if y < 0.5: cy = miny else: cy = maxy trafo = Trafo(minx, 0, 0, miny, cx, cy) r, phi = trafo.inverse()(x, y).polar() x, y = trafo(Polar(1, phi)) else: # normal corners x = round(min(max(x, 0), 1)) y = round(min(max(y, 0), 1)) p2 = self.trafo(x, y) return (abs(p - p2), p2) except SingularMatrix: return (1e200, p) def update_rects(self): rect = PointsToRect(map(self.trafo, corners)) self.coord_rect = rect if self.properties.HasLine(): self.bounding_rect = rect.grown(self.properties.GrowAmount()) else: self.bounding_rect = rect def Info(self): trafo = self.trafo w = hypot(trafo.m11, trafo.m21) h = hypot(trafo.m12, trafo.m22) return _("Rectangle %(size)[size]"), {'size': (w, h)} def SaveToFile(self, file): Primitive.SaveToFile(self, file) file.Rectangle(self.trafo, self.radius1, self.radius2) def Blend(self, other, p, q): result = RectangularPrimitive.Blend(self, other, p, q) result.radius1 = p * self.radius1 + q * other.radius1 result.radius2 = p * self.radius2 + q * other.radius2 return result def Editor(self): return RectangleEditor(self) class RectangleCreator(RectangularCreator): creation_text = _("Create Rectangle") state = 0 def MouseMove(self, p, state): self.state = state RectangularCreator.MouseMove(self, p, state) def ButtonUp(self, p, button, state): if self.state & AlternateMask: p = self.apply_constraint(p, state) self.DragStop(p) off = 2 * self.off end = self.trafo.offset() - self.off self.trafo = Trafo(off.x, 0, 0, off.y, end.x, end.y) else: RectangularCreator.ButtonUp(self, p, button, state) def DrawDragged(self, device, partially): start = self.drag_start if self.state & AlternateMask: start = start - self.off device.DrawRectangle(start, self.drag_cur) def CurrentInfoText(self): start = self.drag_start if self.state & AlternateMask: start = start - self.off width, height = self.drag_cur - start return 'Rectangle: %(size)[size]', {'size': (abs(width), abs(height))} def CreatedObject(self): return Rectangle(self.trafo, properties = DefaultGraphicsProperties()) class RectangleEditor(Editor): EditedClass = Rectangle selection = None def ButtonDown(self, p, button, state): if self.selection is not None: start = self.selection.p Editor.DragStart(self, start) return p - start else: return None def ButtonUp(self, p, button, state): if self.selection is not None: trafo, radius1, radius2 = self.resize() self.selection = None return self.object.SetTrafoAndRadii(trafo, radius1, radius2) else: return NullUndo def resize(self): code = self.selection.x_code trafo = self.trafo; radius1 = self.radius1; radius2 = self.radius2 if code < 0: # a special handle that has to be treated as a normal handle # depending on the direction of the drag width = hypot(trafo.m11, trafo.m21) height = hypot(trafo.m12, trafo.m22) t = Trafo(trafo.m11 / width, trafo.m21 / width, trafo.m12 / height, trafo.m22 / height, 0, 0) dx, dy = t.inverse()(self.off) #print code, dx, dy if code > -5: # one of the corners in a rectangle with sharp corners if abs(dx) > abs(dy): code = 4 - code else: code = (12, 10, 11, 9)[code] else: # the edge handle and the round corner handles coincide if code >= -7: # horizontal edges if abs(dx) > abs(dy): if dx < 0: code = -code else: code = -code + 1 else: code = -4 - code else: # vertical edges if abs(dx) > abs(dy): code = code + 13 else: if dy < 0: code = -code else: code = -code + 1 # # code is now a normal handle # #print '->', code x, y = trafo.inverse()(self.drag_cur) width = hypot(trafo.m11, trafo.m21) height = hypot(trafo.m12, trafo.m22) if code <= 4: # drag one of the edges if code == 1: t = Trafo(1, 0, 0, 1 - y, 0, y) if y != 1: radius2 = radius2 / abs(1 - y) else: radius1 = radius2 = 0 elif code == 2: t = Trafo(x, 0, 0, 1, 0, 0) if x != 0: radius1 = radius1 / abs(x) else: radius1 = radius2 = 0 elif code == 3: t = Trafo(1, 0, 0, y, 0, 0) if y != 0: radius2 = radius2 / abs(y) else: radius1 = radius2 = 0 elif code == 4: t = Trafo(1 - x, 0, 0, 1, x, 0) if x != 1: radius1 = radius1 / abs(1 - x) else: radius1 = radius2 = 0 trafo = trafo(t) if radius1 != 0 or radius2 != 0: ratio = radius1 / radius2 if radius1 > 0.5: radius1 = 0.5 radius2 = radius1 / ratio if radius2 > 0.5: radius2 = 0.5 radius1 = radius2 * ratio else: # modify the round corners if radius1 == radius2 == 0: ratio = height / width else: ratio = radius1 / radius2 if ratio > 1: max1 = 0.5 max2 = max1 / ratio else: max2 = 0.5 max1 = max2 * ratio if code < 9: if code == 6 or code == 8: x = 1 - x radius1 = max(min(x, max1), 0) radius2 = radius1 / ratio else: if code == 10 or code == 12: y = 1 - y radius2 = max(min(y, max2), 0) radius1 = radius2 * ratio return trafo, radius1, radius2 def DrawDragged(self, device, partially): if self.selection is not None: trafo, radius1, radius2 = self.resize() device.RoundedRectangle(trafo, radius1, radius2) def GetHandles(self): trafo = self.trafo; radius1 = self.radius1; radius2 = self.radius2 handles = [] if radius1 == radius2 == 0: for x, y, code in ((0, 0, -1), (1, 0, -2), (0, 1, -3), (1, 1, -4), (0.5, 0, 1), (1.0, 0.5, 2), (0.5, 1.0, 3), ( 0, 0.5, 4)): h = handle.MakeNodeHandle(trafo(x, y)) h.x_code = code handles.append(h) else: # horizontal edges if round(radius1, 3) >= 0.5: h = handle.MakeNodeHandle(trafo(0.5, 0)) h.x_code = -5 handles.append(h) h = handle.MakeNodeHandle(trafo(0.5, 1)) h.x_code = -7 handles.append(h) else: coords = ((radius1, 0, 5), (0.5, 0, 1), (1 - radius1, 0, 6), (radius1, 1, 7), (0.5, 1, 3), (1 - radius1, 1, 8)) for x, y, code in coords: h = handle.MakeNodeHandle(trafo(x, y)) h.x_code = code handles.append(h) # vertical edges if round(radius2, 3) >= 0.5: h = handle.MakeNodeHandle(trafo(0, 0.5)) h.x_code = -9 handles.append(h) h = handle.MakeNodeHandle(trafo(1, 0.5)) h.x_code = -11 handles.append(h) else: coords = ((0, radius2, 9), (0, 0.5, 4), (0, 1 - radius2, 10), (1, radius2, 11),(1, 0.5, 2), (1, 1 - radius2, 12)) for x, y, code in coords: h = handle.MakeNodeHandle(trafo(x, y)) h.x_code = code handles.append(h) # return handles def SelectHandle(self, handle, mode): self.selection = handle def SelectPoint(self, p, rect, device, mode): return 0 UniConvertor-1.1.4/src/app/Graphics/color.py0000775000076400007640000002314611060500565017454 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2003-2006 by Igor E. Novikov # Copyright (C) 1997, 1998, 1999 by Bernhard Herzog # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. # # Color Handling # from string import atoi from app.events.warn import warn, INTERNAL, USER from app._sketch import RGBColor #, XVisual from app import config, _ import app, string skvisual = None CMYK = 'CMYK' RGB = 'RGB' def CreateRGBColor(r, g, b): return RGB_Color(round(r, 3), round(g, 3), round(b, 3)) def XRGBColor(s): # only understands the old x specification with two hex digits per # component. e.g. `#00FF00' if s[0] != '#': raise ValueError("Color %s doesn't start with a '#'" % s) r = atoi(s[1:3], 16) / 255.0 g = atoi(s[3:5], 16) / 255.0 b = atoi(s[5:7], 16) / 255.0 return CreateRGBColor(r, g, b) def CreateCMYKColor(c, m, y, k): return CMYK_Color(c, m, y, k) def CreateSPOTColor(r,g,b,c,m,y,k,name,palette): return SPOT_Color(r,g,b,c,m,y,k, alpha=0, name=name, palette=palette) def CreateSPOT_RGBColor(r,g,b,name,palette): if app.config.preferences.use_cms: c,m,y,k = app.colormanager.convertRGB(r, g, b) else: c,m,y,k = rgb_to_cmyk(r, g, b) return SPOT_Color(r,g,b,c,m,y,k, alpha=0, name=name, palette=palette) def CreateSPOT_CMYKColor(c,m,y,k,name,palette): if app.config.preferences.use_cms: r,g,b = app.colormanager.processCMYK(c,m,y,k) else: r,g,b = cmyk_to_rgb(c,m,y,k) return SPOT_Color(r,g,b,c,m,y,k, alpha=0, name=name, palette=palette) def cmyk_to_rgb(c, m, y, k): r = round(1.0 - min(1.0, c + k), 3) g = round(1.0 - min(1.0, m + k), 3) b = round(1.0 - min(1.0, y + k), 3) return r, g, b def rgb_to_tk((r, g, b)): return '#%04x%04x%04x' % (65535 * r, 65535 * g, 65535 * b) def tk_to_rgb(tk): r=int(string.atoi(tk[1:3], 0x10))/ 255.0 g=int(string.atoi(tk[3:5], 0x10))/ 255.0 b=int(string.atoi(tk[5:], 0x10))/ 255.0 return r,g,b def rgb_to_cmyk(r, g, b): c = 1.0 - r m = 1.0 - g y = 1.0 - b k = min(c, m, y) return c - k, m - k, y - k, k def ParseSketchColor(v1, v2, v3): return RGB_Color(round(v1, 3), round(v2, 3), round(v3, 3)) def ParseSKColor(model, v1, v2, v3, v4=0, v5=0): if model=='CMYK': r,g,b = cmyk_to_rgb(v1, v2, v3, v4) return CMYK_Color(v1, v2, v3, v4) if model=='RGB': return RGB_Color(round(v1, 3), round(v2, 3), round(v3, 3)) class RGB_Color: def __init__(self, r, g, b, alpha=0, name='Not defined'): self.model = 'RGB' self.red=r self.green=g self.blue=b self.alpha=alpha self.name=name def RGB(self): return self.getScreenColor() def cRGB(self): rgb=self.getScreenColor() return (rgb.red, rgb.green, rgb.blue) def cRGBA(self): rgb=self.getScreenColor() return (rgb.red, rgb.green, rgb.blue, self.alpha) def getCMYK(self): if app.config.preferences.use_cms: c,m,y,k = app.colormanager.convertRGB(self.red, self.green, self.blue) else: c,m,y,k = rgb_to_cmyk(self.red, self.green, self.blue) return c,m,y,k def getScreenColor(self): if app.config.preferences.use_cms: if app.config.preferences.simulate_printer: c,m,y,k = app.colormanager.convertRGB(self.red, self.green, self.blue) r,g,b = app.colormanager.processCMYK(c,m,y,k) return RGBColor(r, g, b) else: r,g,b = app.colormanager.processRGB(self.red, self.green, self.blue) return RGBColor(self.red, self.green, self.blue) else: return RGBColor(self.red, self.green, self.blue) def toString(self): R='R-'+str(int(round(self.red*255, 0))) G=' G-'+str(int(round(self.green*255, 0))) B=' B-'+str(int(round(self.blue*255, 0))) return R+G+B def toSave(self): R= str(round(self.red, 3))+',' G= str(round(self.green, 3))+',' B= str(round(self.blue, 3)) return '("'+self.model+'",'+R+G+B+')' class CMYK_Color: def __init__(self, c, m, y, k, alpha=0, name='Not defined'): self.model = 'CMYK' self.c=c self.m=m self.y=y self.k=k self.alpha=alpha self.name=name def getCMYK(self): return self.c, self.m, self.y, self.k def RGB(self): return self.getScreenColor() def cRGB(self): rgb=self.getScreenColor() return (rgb.red, rgb.green, rgb.blue) def cRGBA(self): rgb=self.getScreenColor() return (rgb.red, rgb.green, rgb.blue, self.alpha) def getScreenColor(self): if app.config.preferences.use_cms: r,g,b = app.colormanager.processCMYK(self.c,self.m,self.y,self.k) else: r,g,b = cmyk_to_rgb(self.c,self.m,self.y,self.k) return RGBColor(r, g, b) def toString(self): C='C-'+str(int(round(self.c, 2)*100))+'% ' M='M-'+str(int(round(self.m, 2)*100))+'% ' Y='Y-'+str(int(round(self.y, 2)*100))+'% ' K='K-'+str(int(round(self.k, 2)*100))+'%' return C+M+Y+K def toSave(self): C= str(round(self.c, 3))+',' M= str(round(self.m, 3))+',' Y= str(round(self.y, 3))+',' K= str(round(self.k, 3)) return '("'+self.model+'",'+C+M+Y+K+')' class SPOT_Color: def __init__(self, r, g, b, c, m, y, k, alpha=0, name='Not defined', palette='Unknown'): self.model = 'SPOT' self.r=r self.g=g self.b=b self.c=c self.m=m self.y=y self.k=k self.alpha=alpha self.name=name self.palette = palette def getCMYK(self): return self.c, self.m, self.y, self.k def RGB(self): return self.getScreenColor() def cRGB(self): rgb=self.getScreenColor() return (rgb.red, rgb.green, rgb.blue) def cRGBA(self): rgb=self.getScreenColor() return (rgb.red, rgb.green, rgb.blue, self.alpha) def getScreenColor(self): if app.config.preferences.use_cms: if app.config.preferences.simulate_printer: r,g,b = app.colormanager.processCMYK(c,m,y,k) return RGBColor(r, g, b) else: return RGBColor(self.r, self.g, self.b) else: return RGBColor(self.r, self.g, self.b) def toString(self): return self.name + '' def toSave(self): R= str(round(self.r, 3))+',' G= str(round(self.g, 3))+',' B= str(round(self.b, 3))+',' C= str(round(self.c, 3))+',' M= str(round(self.m, 3))+',' Y= str(round(self.y, 3))+',' K= str(round(self.k, 3)) return '("'+self.model+'","'+self.palette+'","'+self.name+'",'+R+G+B+C+M+Y+K+')' # # some standard colors. # class StandardColors: black = CreateRGBColor(0.0, 0.0, 0.0) darkgray = CreateRGBColor(0.25, 0.25, 0.25) gray = CreateRGBColor(0.5, 0.5, 0.5) lightgray = CreateRGBColor(0.75, 0.75, 0.75) white = CreateRGBColor(1.0, 1.0, 1.0) red = CreateRGBColor(1.0, 0.0, 0.0) green = CreateRGBColor(0.0, 1.0, 0.0) blue = CreateRGBColor(0.0, 0.0, 1.0) cyan = CreateRGBColor(0.0, 1.0, 1.0) magenta = CreateRGBColor(1.0, 0.0, 1.0) yellow = CreateRGBColor(1.0, 1.0, 0.0) class StandardCMYKColors: black = CreateCMYKColor(0.0, 0.0, 0.0, 1.0) darkgray = CreateCMYKColor(0.0, 0.0, 0.0, 0.75) gray = CreateCMYKColor(0.0, 0.0, 0.0, 0.5) lightgray = CreateCMYKColor(0.0, 0.0, 0.0, 0.25) white = CreateCMYKColor(0.0, 0.0, 0.0, 0.0) red = CreateCMYKColor(0.0, 1.0, 1.0, 0.0) green = CreateCMYKColor(1.0, 0.0, 1.0, 0.0) blue = CreateCMYKColor(1.0, 1.0, 0.0, 0.0) cyan = CreateCMYKColor(1.0, 0.0, 0.0, 0.0) magenta = CreateCMYKColor(0.0, 1.0, 0.0, 0.0) yellow = CreateCMYKColor(0.0, 0.0, 1.0, 0.0) # # For 8-bit displays: # def float_to_x(float): return int(int(float * 63) / 63.0 * 65535) def fill_colormap(cmap): max = 65535 colors = [] color_idx = [] failed = 0 shades_r, shades_g, shades_b, shades_gray = config.preferences.color_cube max_r = shades_r - 1 max_g = shades_g - 1 max_b = shades_b - 1 for red in range(shades_r): red = float_to_x(red / float(max_r)) for green in range(shades_g): green = float_to_x(green / float(max_g)) for blue in range(shades_b): blue = float_to_x(blue / float(max_b)) colors.append((red, green, blue)) for i in range(shades_gray): value = int((i / float(shades_gray - 1)) * max) colors.append((value, value, value)) for red, green, blue in colors: try: ret = cmap.AllocColor(red, green, blue) color_idx.append(ret[0]) except: color_idx.append(None) failed = 1 if failed: warn(USER, _("I can't alloc all needed colors. I'll use a private colormap")) warn(INTERNAL, "allocated colors without private colormap: %d", len(filter(lambda i: i is None, color_idx))) if config.preferences.reduce_color_flashing: #print 'reduce color flashing' cmap = cmap.CopyColormapAndFree() for idx in range(len(color_idx)): if color_idx[idx] is None: color_idx[idx] = apply(cmap.AllocColor, colors[idx])[0] else: #print "don't reduce color flashing" cmap = cmap.CopyColormapAndFree() cmap.FreeColors(filter(lambda i: i is not None, color_idx), 0) color_idx = [] for red, green, blue in colors: color_idx.append(cmap.AllocColor(red, green, blue)[0]) return cmap, color_idx _init_from_widget_done = 0 global_colormap = None #def InitFromWidget(tkwin, root = None): # global _init_from_widget_done, skvisual # if _init_from_widget_done: # return # if root: # visual = root.winfo_visual() # if visual == 'truecolor': # skvisual = XVisual(tkwin.c_display(), tkwin.c_visual()) # #skvisual.set_gamma(config.preferences.screen_gamma) # alloc_function = skvisual.get_pixel # if visual == 'pseudocolor' and root.winfo_depth() == 8: # global global_colormap # cmap = tkwin.colormap() # newcmap, idxs = fill_colormap(cmap) # if newcmap != cmap: # cmap = newcmap # tkwin.SetColormap(cmap) # shades_r, shades_g, shades_b, shades_gray \ # = config.preferences.color_cube # skvisual = XVisual(tkwin.c_display(), tkwin.c_visual(), # (shades_r, shades_g, shades_b, shades_gray, idxs)) # global_colormap = cmap # _init_from_widget_done = 1 UniConvertor-1.1.4/src/app/Graphics/pattern.py0000775000076400007640000004241411060500566020013 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1997, 1998, 1999, 2000, 2003 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import math from math import atan2, hypot, pi, sin, cos from app import Point, Rotation, Translation, Trafo, NullPoint, NullUndo from blend import Blend, MismatchError, BlendTrafo import color class Pattern: is_procedural = 1 is_Empty = 0 is_Solid = 0 is_Gradient = 0 is_RadialGradient = 0 is_AxialGradient = 0 is_ConicalGradient = 0 is_Hatching = 0 is_Tiled = 0 is_Image = 0 name = '' def __init__(self, duplicate = None): pass def SetName(self, name): self.name = name def Name(self): return self.name def Execute(self, device, rect = None): pass def Transform(self, trafo, rects = None): # This method is usually called by a primitives Transform method. return NullUndo def Duplicate(self): return self.__class__(duplicate = self) Copy = Duplicate class EmptyPattern_(Pattern): is_procedural = 0 is_Empty = 1 def Duplicate(self): return self Copy = Duplicate def Blend(self, *args): return self def SaveToFile(self, file): file.EmptyPattern() def __str__(self): return 'EmptyPattern' EmptyPattern = EmptyPattern_() class SolidPattern(Pattern): is_procedural = 0 is_Solid = 1 def __init__(self, color = None, duplicate = None): if duplicate is not None: self.color = duplicate.color elif color is not None: self.color = color else: raise ValueError,'SolidPattern be must created with color argument' def __cmp__(self, other): if self.__class__ == other.__class__: return cmp(self.color, other.color) else: return cmp(id(self), id(other)) def __str__(self): return 'SolidPattern(%s)' % `self.color` def Execute(self, device, rect = None): device.SetFillColor(self.color) def Blend(self, other, frac1, frac2): if other.__class__ == self.__class__: return SolidPattern(Blend(self.color.RGB(), other.color.RGB(), frac1, frac2)) else: raise MismatchError def Color(self): return self.color def SaveToFile(self, file): file.SolidPattern(self.color) class GradientPattern(Pattern): is_Gradient = 1 def __init__(self, gradient, duplicate = None): if duplicate is not None: Pattern.__init__(self, duplicate = duplicate) self.gradient = duplicate.gradient.Duplicate() elif gradient: self.gradient = gradient else: raise ValueError,\ 'GradientPattern must be created with gradient argument' def Gradient(self): return self.gradient def SetGradient(self, gradient): undo = (self.SetGradient, self.gradient) self.gradient = gradient return undo class LinearGradient(GradientPattern): is_AxialGradient = 1 def __init__(self, gradient = None, direction = Point(0, -1), border = 0, duplicate = None): GradientPattern.__init__(self, gradient, duplicate = duplicate) self.direction = direction self.border = border if duplicate is not None: if duplicate.__class__ == self.__class__: self.direction = duplicate.direction self.border = duplicate.border elif duplicate.__class__ == ConicalGradient: self.direction = duplicate.direction elif duplicate.__class__ == RadialGradient: self.border = duplicate.border def SetDirection(self, dir): undo = (self.SetDirection, self.direction) self.direction = dir return undo def Direction(self): return self.direction def Border(self): return self.border def SetBorder(self, border): undo = (self.SetBorder, self.border) self.border = border return undo def Transform(self, trafo, rects = None): dx, dy = self.direction dx, dy = trafo.DTransform(dy, -dx) dir = Point(dy, -dx).normalized() if dir * trafo.DTransform(self.direction) < 0: dir = -dir return self.SetDirection(dir) def Execute(self, device, rect): if device.has_axial_gradient: self.execute_axial_gradient(device, rect) return SetFillColor = device.SetFillColor FillRectangle = device.FillRectangle steps = device.gradient_steps colors = self.gradient.Sample(steps) SetFillColor(colors[0]) apply(device.FillRectangle, tuple(rect)) device.PushTrafo() vx, vy = self.direction angle = atan2(vy, vx) - pi / 2 center = rect.center() rot = Rotation(angle, center) left, bottom, right, top = rot(rect) device.Concat(rot) device.Translate(center) height = top - bottom miny = -height / 2 height = height * (1.0 - self.border) width = right - left dy = height / steps y = height / 2 x = width / 2 for i in range(steps): SetFillColor(colors[i]) FillRectangle(-x, y, +x, miny) y = y - dy device.PopTrafo() def execute_axial_gradient(self, device, rect): vx, vy = self.direction angle = atan2(vy, vx) - pi / 2 center = rect.center() rot = Rotation(angle, center) left, bottom, right, top = rot(rect) height = (top - bottom) * (1.0 - self.border) trafo = rot(Translation(center)) device.AxialGradient(self.gradient, trafo(0, height / 2), trafo(0, -height / 2)) def Blend(self, other, frac1, frac2): if other.__class__ == self.__class__: gradient = other.gradient dir = other.direction border = other.border elif other.__class__ == SolidPattern: gradient = other.Color() dir = self.direction border = self.border else: raise MismatchError return LinearGradient(Blend(self.gradient, gradient, frac1, frac2), frac1 * self.direction + frac2 * dir, frac1 * self.border + frac2 * border) def SaveToFile(self, file): file.LinearGradientPattern(self.gradient, self.direction, self.border) class RadialGradient(GradientPattern): is_RadialGradient = 1 def __init__(self, gradient = None, center = Point(0.5, 0.5), border = 0, duplicate = None): GradientPattern.__init__(self, gradient, duplicate = duplicate) self.center = center self.border = border if duplicate is not None: if duplicate.__class__ == self.__class__: self.center = duplicate.center self.border = duplicate.border elif duplicate.__class__ == ConicalGradient: self.center = duplicate.center elif duplicate.__class__ == LinearGradient: self.border = duplicate.border def SetCenter(self, center): undo = (self.SetCenter, self.center) self.center = center return undo def Center(self): return self.center def Border(self): return self.border def SetBorder(self, border): undo = (self.SetBorder, self.border) self.border = border return undo def Transform(self, trafo, rects = None): if rects: r1, r2 = rects left, bottom, right, top = r1 cx, cy = self.center cx = cx * right + (1 - cx) * left cy = cy * top + (1 - cy) * bottom cx, cy = trafo(cx, cy) left, bottom, right, top = r2 len = right - left if len: cx = (cx - left) / len else: cx = 0 len = top - bottom if len: cy = (cy - bottom) / len else: cy = 0 center = Point(cx, cy) else: center = self.center return self.SetCenter(center) def Execute(self, device, rect): if device.has_radial_gradient: self.execute_radial(device, rect) return steps = device.gradient_steps cx, cy = self.center cx = cx * rect.right + (1 - cx) * rect.left cy = cy * rect.top + (1 - cy) * rect.bottom radius = max(hypot(rect.left - cx, rect.top - cy), hypot(rect.right - cx, rect.top - cy), hypot(rect.right - cx, rect.bottom - cy), hypot(rect.left - cx, rect.bottom - cy)) color = self.gradient.ColorAt SetFillColor = device.SetFillColor FillCircle = device.FillCircle SetFillColor(color(0)) apply(device.FillRectangle, tuple(rect)) radius = radius * (1.0 - self.border) dr = radius / steps device.PushTrafo() device.Translate(cx, cy) center = NullPoint for i in range(steps): SetFillColor(color(float(i) / (steps - 1))) FillCircle(center, radius) radius = radius - dr device.PopTrafo() def execute_radial(self, device, rect): cx, cy = self.center cx = cx * rect.right + (1 - cx) * rect.left cy = cy * rect.top + (1 - cy) * rect.bottom radius = max(hypot(rect.left - cx, rect.top - cy), hypot(rect.right - cx, rect.top - cy), hypot(rect.right - cx, rect.bottom - cy), hypot(rect.left - cx, rect.bottom - cy)) radius = radius * (1.0 - self.border) device.RadialGradient(self.gradient, (cx, cy), radius, 0) def Blend(self, other, frac1, frac2): if other.__class__ == self.__class__: gradient = other.gradient center = other.center border = other.border elif other.__class__ == SolidPattern: gradient = other.Color() center = self.center border = self.border else: raise MismatchError return RadialGradient(Blend(self.gradient, gradient, frac1, frac2), frac1 * self.center + frac2 * center, frac1 * self.border + frac2 * border) def SaveToFile(self, file): file.RadialGradientPattern(self.gradient, self.center, self.border) class ConicalGradient(GradientPattern): is_ConicalGradient = 1 def __init__(self, gradient = None, center = Point(0.5, 0.5), direction = Point(1, 0), duplicate = None): GradientPattern.__init__(self, gradient, duplicate = duplicate) self.center = center self.direction = direction if duplicate is not None: if duplicate.__class__ == self.__class__: self.center = duplicate.center self.direction = duplicate.direction elif duplicate.__class__ == LinearGradient: self.direction = duplicate.direction elif duplicate.__class__ == RadialGradient: self.center = duplicate.center def __set_center_and_dir(self, center, dir): undo = (self.__set_center_and_dir, self.center, self.direction) self.center = center self.direction = dir return undo def Transform(self, trafo, rects = None): dir = trafo.DTransform(self.direction).normalized() if rects: r1, r2 = rects left, bottom, right, top = r1 cx, cy = self.center cx = cx * right + (1 - cx) * left cy = cy * top + (1 - cy) * bottom cx, cy = trafo(cx, cy) left, bottom, right, top = r2 len = right - left if len: cx = (cx - left) / len else: cx = 0 len = top - bottom if len: cy = (cy - bottom) / len else: cy = 0 center = Point(cx, cy) else: center = self.center return self.__set_center_and_dir(center, dir) def SetCenter(self, center): undo = (self.SetCenter, self.center) self.center = center return undo def Center(self): return self.center def SetDirection(self, dir): undo = (self.SetDirection, self.direction) self.direction = dir return undo def Direction(self): return self.direction def Execute(self, device, rect): if device.has_conical_gradient: self.execute_conical(device, rect) return steps = device.gradient_steps cx, cy = self.center left, bottom, right, top = rect cx = cx * right + (1 - cx) * left cy = cy * top + (1 - cy) * bottom vx, vy = self.direction angle = atan2(vy, vx) rot = Rotation(angle, cx, cy) radius = max(hypot(left - cx, top - cy), hypot(right - cx, top - cy), hypot(right - cx, bottom - cy), hypot(left-cx,bottom-cy)) + 10 device.PushTrafo() device.Concat(rot) device.Translate(cx, cy) device.Scale(radius) colors = self.gradient.Sample(steps) SetFillColor = device.SetFillColor FillPolygon = device.FillPolygon da = pi / steps points = [(1, 0)] for i in range(steps): a = da * (i + 1) x = cos(a); y = sin(a) points.insert(0, (x, y)) points.append((x, -y)) colors.reverse() SetFillColor(colors[0]) FillPolygon(points) points.insert(0, (0, 0)) for i in range(steps): SetFillColor(colors[i]) del points[1] del points[-1] FillPolygon(points) device.PopTrafo() def execute_conical(self, device, rect): cx, cy = self.center left, bottom, right, top = rect cx = cx * right + (1 - cx) * left cy = cy * top + (1 - cy) * bottom angle = self.direction.polar()[1] device.ConicalGradient(self.gradient, (cx, cy), angle) def Blend(self, other, frac1, frac2): if other.__class__ == self.__class__: gradient = other.gradient dir = other.direction center = other.center elif other.__class__ == SolidPattern: gradient = other.Color() dir = self.direction center = self.center else: raise MismatchError return ConicalGradient(Blend(self.gradient, gradient, frac1, frac2), frac1 * self.center + frac2 * center, frac1 * self.direction + frac2 * dir) def SaveToFile(self, file): file.ConicalGradientPattern(self.gradient, self.center, self.direction) class HatchingPattern(Pattern): is_Hatching = 1 def __init__(self, foreground = None, background = None, direction = Point(1, 0), spacing = 5.0, width = 0.5, duplicate = None): if duplicate is not None: self.foreground = duplicate.foreground self.background = duplicate.background self.spacing = duplicate.spacing self.width = duplicate.width self.direction = duplicate.direction elif foreground: self.foreground = foreground if not background: background = color.StandardColors.white self.background = background self.spacing = spacing self.width = width self.direction = direction else: raise ValueError,\ 'HatchingPattern must be created with color argument' def SetDirection(self, dir): undo = (self.SetDirection, self.direction) self.direction = dir return undo def Direction(self): return self.direction def SetSpacing(self, spacing): undo = (self.SetSpacing, self.spacing) self.spacing = spacing return undo def Spacing(self): return self.spacing def Width(self): return self.width def Transform(self, trafo, rects = None): # XXX: should spacing be transformed as well? Should the pattern be # transformed at all? dir = trafo.DTransform(self.direction).normalized() return self.SetDirection(dir) def SetForeground(self, foreground): undo = (self.SetForeground, self.foreground) self.foreground = foreground return undo def Foreground(self): return self.foreground def SetBackground(self, color): undo = (self.SetBackground, self.background) self.background = color return undo def Background(self): return self.background def Execute(self, device, rect): left, bottom, right, top = rect dy = self.spacing if dy > 0: device.SetFillColor(self.background) device.FillRectangle(left, top, right, bottom) device.PushTrafo() vx, vy = self.direction angle = atan2(vy, vx) center = rect.center() rot = Rotation(angle, center) left, bottom, right, top = rot(rect) device.Concat(rot) device.Translate(center) height = top - bottom width = right - left steps = int(height / dy + 1) y = height / 2 x = width / 2 device.SetLineColor(self.foreground) device.SetLineAttributes(self.width) drawline = device.DrawLineXY for i in range(steps): drawline(-x, y, +x, y) y = y - dy device.PopTrafo() else: device.SetFillColor(self.foreground) device.FillRectangle(left, bottom, right, top) def Blend(self, other, frac1, frac2): if other.__class__ == self.__class__: fg = other.foreground bg = other.background dir = other.direction spacing = other.spacing width = other.width elif other.__class__ == SolidPattern: fg = bg = other.Color() dir = self.direction spacing = self.spacing width = self.width else: raise MismatchError return HatchingPattern(Blend(self.foreground, fg, frac1, frac2), Blend(self.background, bg, frac1, frac2), frac1 * self.direction + frac2 * dir, frac1 * self.spacing + frac2 * spacing, frac1 * self.width + frac2 * width) def SaveToFile(self, file): file.HatchingPattern(self.foreground, self.background, self.direction, self.spacing, self.width) class ImageTilePattern(Pattern): is_Tiled = 1 is_Image = 1 data = None def __init__(self, data = None, trafo = None, duplicate = None): if duplicate is not None: data = duplicate.data self.trafo = duplicate.trafo else: if trafo is None: #width, height = data.size trafo = Trafo(1, 0, 0, -1, 0, 0) self.trafo = trafo self.data = data def set_transformation(self, trafo): undo = (self.set_transformation, self.trafo) self.trafo = trafo return undo def Transform(self, trafo, rects = None): if rects: r1, r2 = rects trafo = trafo(Translation(r1.left, r1.top)) trafo = Translation(-r2.left, -r2.top)(trafo) return self.set_transformation(trafo(self.trafo)) def Execute(self, device, rect): device.TileImage(self.data, Translation(rect.left, rect.top)(self.trafo)) def Blend(self, other, frac1, frac2): if self.__class__ == other.__class__: if self.data is other.data: return self.__class__(self.data, BlendTrafo(self.trafo, other.trafo, frac1, frac2)) raise MismatchError def SaveToFile(self, file): file.ImageTilePattern(self.data, self.trafo) UniConvertor-1.1.4/src/app/Graphics/bezier.py0000775000076400007640000011650611060500565017621 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2003-2006 by Igor E. Novikov # Copyright (C) 1997, 1998, 1999, 2000, 2002 by Bernhard Herzog # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. from math import pi, floor, atan2, ceil from traceback import print_stack from app.conf.const import SelectSet, SelectAdd, SelectSubtract, SelectDrag, \ Button1Mask, ConstraintMask,\ SCRIPT_GET, SCRIPT_OBJECT, SCRIPT_OBJECTLIST, SCRIPT_UNDO from app.events.warn import pdebug, warn, INTERNAL from app import Point, Polar, Rect, EmptyRect, UnionRects, PointsToRect from app import _, _sketch, CreatePath, config, RegisterCommands, \ CreateMultiUndo, NullUndo, Undo #from app.UI.command import AddCmd #import app.UI.skpixmaps #pixmaps = app.UI.skpixmaps.PixmapTk from app._sketch import ContAngle, ContSmooth, ContSymmetrical, \ SelNone, SelNodes, SelSegmentFirst, SelSegmentLast, Bezier, Line import handle from base import Primitive, Creator, Editor from blend import Blend, BlendPaths, MismatchError from properties import DefaultGraphicsProperties # # PolyBezier # # The most important primitive, since it can be used to draw lines, # curves and arbitrary contours (to some degree; circles for instance # can only be approximated, but that works quite well). # def undo_path(undo, path): if undo is not None: return apply(getattr(path, undo[0]), undo[1:]) else: return undo class PolyBezier(Primitive): is_Bezier = 1 has_edit_mode = 1 is_curve = 1 is_clip = 1 script_access = Primitive.script_access.copy() def __init__(self, paths = None, properties = None, duplicate = None): if duplicate is not None: if paths is None: paths = [] for path in duplicate.paths: paths.append(path.Duplicate()) self.paths = tuple(paths) else: # This special case uses the properties kwarg now, I # hope. warn(INTERNAL, 'Bezier object created with paths and duplicte') print_stack() if type(paths) != type(()): paths = (paths,) self.paths = paths elif paths is not None: if type(paths) != type(()): paths = (paths,) self.paths = paths else: self.paths = (CreatePath(),) Primitive.__init__(self, properties = properties, duplicate=duplicate) def Hit(self, p, rect, device, clip = 0): for path in self.paths: if path.hit_point(rect): return 1 return device.MultiBezierHit(self.paths, p, self.properties, clip or self.Filled(), ignore_outline_mode = clip) def do_undo(self, undo_list): undo = map(undo_path, undo_list, self.paths) self._changed() return (self.do_undo, undo) def Transform(self, trafo): undo = [] undostyle = NullUndo try: rect = self.bounding_rect for path in self.paths: undo.append(path.Transform(trafo)) self._changed() undo = (self.do_undo, undo) self.update_rects() # calling update_rects directly is a bit faster undostyle = Primitive.Transform(self, trafo, rects = (rect, self.bounding_rect)) return CreateMultiUndo(undostyle, undo) except: Undo(undostyle) if type(undo) != type(()): undo = (self.do_undo, undo) Undo(undo) raise def Translate(self, offset): for path in self.paths: path.Translate(offset) self._changed() return self.Translate, -offset def DrawShape(self, device, rect = None, clip = 0): Primitive.DrawShape(self, device) device.MultiBezier(self.paths, rect, clip) def GetObjectHandle(self, multiple): if self.paths: return self.paths[0].Node(0) return Point(0, 0) def GetSnapPoints(self): points = [] for path in self.paths: points = points + path.NodeList() return points def Snap(self, p): found = [(1e100, p)] for path in self.paths: t = path.nearest_point(p.x, p.y) if t is not None: #print p, t p2 = path.point_at(t) found.append((abs(p - p2), p2)) return min(found) def update_rects(self): if not self.paths: # this should never happen... self.coord_rect = self.bounding_rect = Rect(0, 0, 0, 0) return rect = self.paths[0].accurate_rect() for path in self.paths[1:]: rect = UnionRects(rect, path.accurate_rect()) self.coord_rect = rect if self.properties.HasLine(): rect = self.add_arrow_rects(rect) self.bounding_rect = rect.grown(self.properties.GrowAmount()) else: self.bounding_rect = rect def add_arrow_rects(self, rect): if self.properties.HasLine(): arrow1 = self.properties.line_arrow1 arrow2 = self.properties.line_arrow2 if arrow1 or arrow2: width = self.properties.line_width for path in self.paths: if not path.closed and path.len > 1: if arrow1 is not None: type, controls, p3, cont = path.Segment(1) p = path.Node(0) if type == Bezier: dir = p - controls[0] else: dir = p - p3 rect = UnionRects(rect, arrow1.BoundingRect(p,dir,width)) if arrow2 is not None: type, controls, p, cont = path.Segment(-1) if type == Bezier: dir = p - controls[1] else: dir = p - path.Node(-2) rect = UnionRects(rect, arrow2.BoundingRect(p,dir,width)) return rect def Info(self): nodes = 0 for path in self.paths: nodes = nodes + path.len if path.closed: nodes = nodes - 1 return _("PolyBezier (%(nodes)d nodes in %(paths)d paths)") \ % {'nodes':nodes, 'paths':len(self.paths)} def set_paths(self, paths): undo = (self.set_paths, self.paths) self.paths = tuple(paths) self._changed() return undo SetPaths = set_paths def Paths(self): return self.paths def PathsAsObjects(self): result = [] for path in self.paths: object = self.__class__(paths = (path.Duplicate(),), properties = self.properties.Duplicate()) result.append(object) return result script_access['PathsAsObjects'] = SCRIPT_OBJECTLIST def AsBezier(self): # is `return self' enough ? return self.Duplicate() # # # def SaveToFile(self, file): Primitive.SaveToFile(self, file) file.PolyBezier(self.paths) def load_straight(self, *args): apply(self.paths[-1].AppendLine, args) def load_curve(self, *args): apply(self.paths[-1].AppendBezier, args) def load_close(self, copy_cont_from_last = 0): self.paths[-1].load_close(copy_cont_from_last) def guess_continuity(self): for path in self.paths: path.guess_continuity() def load_IsComplete(self): if self.paths[0].len < 2: # we need at least two nodes return 0 return 1 def Blend(self, other, frac1, frac2): if self.__class__ != other.__class__: try: other = other.AsBezier() if not other: raise MismatchError except AttributeError, value: if value == 'AsBezier': raise MismatchError else: raise paths = BlendPaths(self.paths, other.paths, frac1, frac2) blended = PolyBezier(paths = paths) self.set_blended_properties(blended, other, frac1, frac2) return blended def Editor(self): return PolyBezierEditor(self) class PolyBezierCreator(Creator): creation_text = _("Create Curve") def __init__(self, start): self.path = CreatePath() Creator.__init__(self, start) def apply_constraints(self, p, state): if self.path.len > 0: node = self.path.Node(-1) elif self.dragging: node = self.drag_start else: return p if state & ConstraintMask: radius, angle = (p - node).polar() pi12 = pi / 12 angle = pi12 * floor(angle / pi12 + 0.5) p = node + Polar(radius, angle) return p def ButtonDown(self, p, button, state): p = self.apply_constraints(p, state) if self.path.len == 0: self.path.AppendLine(p) else: self.path.AppendBezier(self.drag_cur, p, p) return self.DragStart(p) def MouseMove(self, p, state): if not (state & Button1Mask): return self.DragMove(self.apply_constraints(p, state)) def ButtonUp(self, p, button, state): if not (state & Button1Mask): return p = self.apply_constraints(p, state) self.DragStop(p) if self.path.len > 1: type, (p1, p2), p, cont = self.path.Segment(-1) p2 = adjust_control_point(p2, p, self.drag_cur, ContSymmetrical) self.path.SetBezier(-1, p1, p2, p, ContSymmetrical) def EndCreation(self): return self.path.len > 1 def AppendInteractive(self, p): return self def ContinueCreation(self): return self.AppendInteractive def DrawDragged(self, device, partially): if not partially: self.path.draw_not_last(device.Bezier, device.Line) device.DrawHandleLine(self.path.Node(-1), self.drag_cur) device.DrawSmallRectHandle(self.drag_cur) if self.path.len > 1: type, (p1, p2), p, cont = self.path.Segment(-1) p2 = adjust_control_point(p2, p, self.drag_cur, ContSymmetrical) device.Bezier(self.path.Node(-2), p1, p2, p) device.DrawHandleLine(p, p2) device.DrawSmallRectHandle(p2) def CreatedObject(self): return PolyBezier(paths = (self.path,), properties = DefaultGraphicsProperties()) class PolyLineCreator(Creator): creation_text = _("Create Poly-Line") def __init__(self, start): self.path = CreatePath() self.was_dragged = 0 Creator.__init__(self, start) def apply_constraints(self, p, state): if self.path.len > 0: node = self.path.Node(-1) elif self.dragging: node = self.drag_start else: return p if state & ConstraintMask: radius, angle = (p - node).polar() pi12 = pi / 12 angle = pi12 * floor(angle / pi12 + 0.5) p = node + Polar(radius, angle) return p def ButtonDown(self, p, button, state): return self.DragStart(self.apply_constraints(p, state)) def MouseMove(self, p, state): if not (state & Button1Mask): return self.was_dragged = 1 self.DragMove(self.apply_constraints(p, state)) def ButtonUp(self, p, button, state): if not (state & Button1Mask): return self.DragStop(self.apply_constraints(p, state)) if self.was_dragged and self.path.len == 0: self.path.AppendLine(self.drag_start) self.path.AppendLine(self.drag_cur) def EndCreation(self): return self.path.len > 1 def AppendInteractive(self, p): return self def ContinueCreation(self): return self.AppendInteractive def DrawDragged(self, device, partially): if self.path.len > 1: if not partially: device.DrawBezierPath(self.path) if self.path.len >= 1: device.Line(self.path.Node(-1), self.drag_cur) else: if self.was_dragged: device.Line(self.drag_start, self.drag_cur) def CreatedObject(self): return PolyBezier(paths = (self.path,), properties = DefaultGraphicsProperties()) SelCurvePoint = -1 class PolyBezierEditor(Editor): EditedClass = PolyBezier commands = [] def __init__(self, object): self.selected_path = -1 self.selected_idx = -1 self.selection_type = SelNone self.other_segment = -1 Editor.__init__(self, object) self.Deselect() def SelectPoint(self, p, rect, device, mode): self.deselect() found = [] for i in range(len(self.paths)): path = self.paths[i] t = path.nearest_point(p.x, p.y) if t is not None: p2 = path.point_at(t) found.append((abs(p - p2), i, t, p2)) dist, i, t, p2 = min(found) self.selected_path = i self.selected_idx = t self.selection_type = SelCurvePoint return 1 def SelectHandle(self, handle, mode = SelectSet): path_idx, segment = handle.code if segment < 0: segment = -segment if segment % 2: self.selection_type = SelSegmentFirst else: self.selection_type = SelSegmentLast self.selected_idx = (segment + 1) / 2 segment = segment / 2 else: self.selection_type = SelNodes self.selected_idx = segment self.selected_path = path_idx path = self.paths[path_idx] if mode == SelectSet or mode == SelectDrag: if not path.SegmentSelected(segment): self.deselect() path.SelectSegment(segment) elif mode == SelectAdd: path.SelectSegment(segment) elif mode == SelectSubtract: path.SelectSegment(segment, 0) def SelectRect(self, rect, mode = SelectSet): selected = 0 for path in self.paths: selected = path.select_rect(rect, mode) or selected self.selection_type = SelNodes return selected def SelectAllNodes(self): for path in self.paths: for i in range(path.len): path.SelectSegment(i, 1) #AddCmd(commands, SelectAllNodes, _("Select All Nodes")) def deselect(self): for path in self.paths: path.deselect() def Deselect(self): self.deselect() self.selected_path = -1 self.selected_idx = -1 self.selection_type = SelNone def ButtonDown(self, p, button, state): if self.selected_path >= 0: path = self.paths[self.selected_path] if self.selection_type == SelNodes: start = path.Node(self.selected_idx) self.DragStart(start) return p - start elif self.selection_type == SelSegmentFirst: segment = self.selected_idx if segment > 1 \ and path.SegmentType(segment - 1) == Bezier\ and path.Continuity(segment - 1): self.other_segment = segment - 1 elif path.closed and segment == 1 \ and path.SegmentType(-1) == Bezier\ and path.Continuity(-1): self.other_segment = path.len - 1 else: self.other_segment = -1 p1 = path.Segment(segment)[1][0] self.DragStart(p1) return p - p1 elif self.selection_type == SelSegmentLast: segment = self.selected_idx self.other_segment = -1 if path.Continuity(segment): if segment < path.len - 1 \ and path.SegmentType(segment + 1) == Bezier: self.other_segment = segment + 1 elif path.closed and segment == path.len - 1 \ and path.SegmentType(1) == Bezier: self.other_segment = 1 p2 = path.Segment(segment)[1][1] self.DragStart(p2) return p - p2 elif self.selection_type == SelCurvePoint: start = path.point_at(self.selected_idx) segment = ceil(self.selected_idx) prev = next = -1 type = path.SegmentType(segment) if type == Bezier: if path.Continuity(segment): if segment < path.len - 1 \ and path.SegmentType(segment + 1) == Bezier: next = segment + 1 elif path.closed and segment == path.len - 1 \ and path.SegmentType(1) == Bezier: next = 1 if segment > 1 \ and path.SegmentType(segment - 1) == Bezier\ and path.Continuity(segment - 1): prev = segment - 1 elif path.closed and segment == 1 \ and path.SegmentType(-1) == Bezier\ and path.Continuity(-1): prev = path.len - 1 else: if segment < path.len - 1: next = segment + 1 elif path.closed and segment == path.len - 1: next = 1 if segment >= 1: prev = segment - 1 elif path.closed and segment == 1: prev = path.len - 1 self.other_segment = (prev, next) self.DragStart(start) return p - start def apply_constraints(self, p, state): if state & ConstraintMask: if self.selection_type == SelNodes: pi4 = pi / 4 off = p - self.drag_start d = Polar(pi4 * round(atan2(off.y, off.x) / pi4)) p = self.drag_start + (off * d) * d elif self.selection_type in (SelSegmentFirst, SelSegmentLast): path = self.paths[self.selected_path] if self.selection_type == SelSegmentFirst: node = path.Node(self.selected_idx - 1) else: node = path.Node(self.selected_idx) radius, angle = (p - node).polar() pi12 = pi / 12 angle = pi12 * floor(angle / pi12 + 0.5) p = node + Polar(radius, angle) return p def MouseMove(self, p, state): self.DragMove(self.apply_constraints(p, state)) def ButtonUp(self, p, button, state): p = self.apply_constraints(p, state) self.DragStop(p) type = self.selection_type if type == SelNodes: undo = [] for path in self.paths: if path.selection_count() > 0: undo.append(path.move_selected_nodes(self.off)) else: undo.append(None) if undo: self._changed() return (self.do_undo, undo) elif type in (SelSegmentFirst, SelSegmentLast): idx = self.selected_path segment = self.selected_idx path = self.paths[idx].Duplicate() paths = self.paths[:idx] + (path,) + self.paths[idx + 1:] if type == SelSegmentFirst: type, (p1, p2), node, cont = path.Segment(segment) path.SetBezier(segment, self.drag_cur, p2, node, cont) if self.other_segment >= 0: other = self.other_segment type, (p1, p2), node, cont = path.Segment(other) p2 = adjust_control_point(p2, node, self.drag_cur, cont) path.SetBezier(other, p1, p2, node, cont) path.SelectSegment(segment - 1) elif type == SelSegmentLast: type, (p1, p2), node, cont = path.Segment(segment) path.SetBezier(segment, p1, self.drag_cur, node, cont) if self.other_segment >= 0: other = self.other_segment type, (p1, p2), node2, cont2 = path.Segment(other) p1 = adjust_control_point(p1, node, self.drag_cur, cont) path.SetBezier(other, p1, p2, node2, cont2) path.SelectSegment(segment) return self.set_paths(paths) # set_paths calls _changed() elif self.selection_type == SelCurvePoint: idx = self.selected_path path = self.paths[idx].Duplicate() paths = self.paths[:idx] + (path,) + self.paths[idx + 1:] segment = int(self.selected_idx) t = self.selected_idx - segment type, control, node, cont = path.Segment(segment + 1) if type == Bezier: p1, p2 = control if t <= 0.5: alpha = ((t * 2) ** 3) / 2 else: alpha = 1 - (((1 - t) * 2) ** 3) / 2 p1 = p1 + (self.off / (3 * t * (1 - t)**2)) * (1 - alpha) p2 = p2 + (self.off / (3 * t**2 * (1 - t))) * alpha path.SetBezier(segment + 1, p1, p2, node, cont) else: path.SetLine(segment + 1, node + self.off, cont) prev, next = self.other_segment if prev >= 0: _type, _control, _node, _cont = path.Segment(prev) if _type == Bezier: _p1, _p2 = _control if type == Bezier: _p2 = adjust_control_point(_p2, _node, p1, _cont) else: _p2 = _p2 + self.off _node = _node + self.off path.SetBezier(prev, _p1, _p2, _node, _cont) else: path.SetLine(prev, _node + self.off, _cont) if next >= 0: _type, _control, _node, _cont = path.Segment(next) if _type == Bezier: _p1, _p2 = _control if type == Bezier: _p1 = adjust_control_point(_p1, node, p2, cont) else: _p1 = _p1 + self.off path.SetBezier(next, _p1, _p2, _node, _cont) return self.set_paths(paths) # set_paths calls _changed() def DrawDragged(self, device, partially): if self.selection_type == SelNodes: for path in self.paths: path.draw_dragged_nodes(self.off, partially, device.Bezier, device.Line) elif self.selection_type == SelSegmentFirst: if not partially: for path in self.paths: path.draw_unselected(device.Bezier, device.Line) path = self.paths[self.selected_path] segment = self.selected_idx node = path.Node(segment - 1) type, (p1, p2), node2, cont = path.Segment(segment) device.Bezier(node, self.drag_cur, p2, node2) device.DrawSmallRectHandle(self.drag_cur) device.DrawHandleLine(node, self.drag_cur) if self.other_segment >= 0: other = self.other_segment type, (p1, p2), node, cont = path.Segment(other) p2 = adjust_control_point(p2, node, self.drag_cur, cont) device.Bezier(path.Node(other - 1), p1, p2, node) device.DrawSmallRectHandle(p2) device.DrawHandleLine(node, p2) elif self.selection_type == SelSegmentLast: if not partially: for path in self.paths: path.draw_unselected(device.Bezier, device.Line) path = self.paths[self.selected_path] segment = self.selected_idx type, (p1, p2), node, cont = path.Segment(segment) device.Bezier(path.Node(segment - 1), p1, self.drag_cur, node) device.DrawSmallRectHandle(self.drag_cur) device.DrawHandleLine(node, self.drag_cur) if self.other_segment >= 0: other = self.other_segment type, (p1, p2), node2, cont2 = path.Segment(other) p1 = adjust_control_point(p1, node, self.drag_cur, cont) device.Bezier(node, p1, p2, node2) device.DrawSmallRectHandle(p1) device.DrawHandleLine(node, p1) elif self.selection_type == SelCurvePoint: path = self.paths[self.selected_path] segment = int(self.selected_idx) t = self.selected_idx - segment prevnode = path.Node(segment) type, control, node, cont = path.Segment(segment + 1) if type == Bezier: p1, p2 = control if t <= 0.5: alpha = ((t * 2) ** 3) / 2 else: alpha = 1 - (((1 - t) * 2) ** 3) / 2 p1 = p1 + (self.off / (3 * t * (1 - t)**2)) * (1 - alpha) p2 = p2 + (self.off / (3 * t**2 * (1 - t))) * alpha device.Bezier(prevnode, p1, p2, node) device.DrawSmallRectHandle(p1) device.DrawHandleLine(prevnode, p1) device.DrawSmallRectHandle(p2) device.DrawHandleLine(node, p2) else: device.DrawLine(prevnode + self.off, node + self.off) prev, next = self.other_segment if prev > 0: _type, _control, _node, _cont = path.Segment(prev) if _type == Bezier: _p1, _p2 = _control if type == Bezier: _p2 = adjust_control_point(_p2, _node, p1, _cont) device.Bezier(path.Node(prev - 1), _p1, _p2, _node) device.DrawSmallRectHandle(_p2) device.DrawHandleLine(_node, _p2) else: device.Bezier(path.Node(prev - 1), _p1, _p2 + self.off, _node + self.off) else: device.DrawLine(path.Node(prev - 1), _node + self.off) if next >= 0: _type, _control, _node, _cont = path.Segment(next) if _type == Bezier: _p1, _p2 = _control if type == Bezier: _p1 = adjust_control_point(_p1, node, p2, cont) device.Bezier(node, _p1, _p2, _node) device.DrawSmallRectHandle(_p1) device.DrawHandleLine(node, _p1) else: device.Bezier(node + self.off, _p1 + self.off, _p2, _node) else: device.DrawLine(node + self.off, _node) def GetHandles(self): NodeHandle = handle.MakeNodeHandle ControlHandle = handle.MakeControlHandle LineHandle = handle.MakeLineHandle handles = [] node_handles = [] append = handles.append for path_idx in range(len(self.paths)): path = self.paths[path_idx] if path.len > 0: if not path.closed: node_handles.append(NodeHandle(path.Node(0), path.SegmentSelected(0), (path_idx, 0))) for i in range(1, path.len): selected = path.SegmentSelected(i) node_handles.append(NodeHandle(path.Node(i), selected, (path_idx, i))) if (path.SegmentType(i) == Bezier and (selected or path.SegmentSelected(i - 1))): type, (p1, p2), node, cont = path.Segment(i) append(ControlHandle(p1, (path_idx, -(2 * i - 1)))) append(ControlHandle(p2, (path_idx, -(2 * i)))) append(LineHandle(path.Node(i - 1), p1)) append(LineHandle(p2, node)) if self.selection_type == SelCurvePoint: p = self.paths[self.selected_path].point_at(self.selected_idx) handles.append(handle.MakeCurveHandle(p)) return handles + node_handles def Info(self): selected = 0 idx = None paths = self.paths for i in range(len(paths)): path = paths[i] count = path.selection_count() if count > 0: selected = selected + count idx = i if selected > 1: return _("%d nodes in PolyBezier") % selected else: if idx is not None: path = paths[idx] for i in range(path.len): if path.SegmentSelected(i): break else: warn(INTERNAL, 'Strange selection count') return _("PolyBezier") if i == 0: return _("First node of PolyBezier") elif i == path.len - 1: return _("Last node of PolyBezier") else: return _("1 node of PolyBezier") else: if self.selection_type == SelCurvePoint: return _("Point on curve at position %.2f") \ % self.selected_idx else: return _("No Node of PolyBezier") # # Special poly bezier protocol: closing, continuity # def OpenNodes(self): if self.selection_type == SelCurvePoint: index = self.selected_path paths = list(self.paths) path = paths[index] paths[index:index + 1] = split_path_at(path, self.selected_idx) self.selected_idx = int(self.selected_idx) + 1 self.selection_type = SelNodes else: paths = [] for path in self.paths: if path.selection_count() >= 1: if path.closed: for i in range(path.len - 1): if path.SegmentSelected(i): start_idx = i break else: start_idx = 0 newpath = CreatePath() paths.append(newpath) p = path.Node(start_idx) newpath.AppendLine(p, ContAngle) for i in range(start_idx + 1, path.len): type, control, p, cont = path.Segment(i) if path.SegmentSelected(i): # XXX remove this ? cont = ContAngle newpath.AppendSegment(type, control, p, cont) if path.SegmentSelected(i) and i < path.len - 1: newpath = CreatePath() newpath.AppendLine(p, ContAngle) paths.append(newpath) if start_idx != 0: # the path was closed and the first node was not # selected for i in range(1, start_idx + 1): type, control, p, cont = path.Segment(i) newpath.AppendSegment(type, control, p, cont) else: paths.append(path) return self.set_paths(paths) #AddCmd(commands, OpenNodes, _("Cut Curve"), key_stroke = 'c', #bitmap = pixmaps.BezierOpenNodes) def CloseNodes(self): # find out if close is possible two = 0 one = 0 for i in range(len(self.paths)): path = self.paths[i] selected = path.selection_count() if not selected: continue if (path.closed and selected) or selected not in (1, 2): return if selected == 1: if path.SegmentSelected(0) or path.SegmentSelected(-1): one = one + 1 continue return else: if path.SegmentSelected(0) and path.SegmentSelected(-1): two = two + 1 continue return # now, close the nodes if one == 2 and two == 0: paths = [] append_to = None for path in self.paths: if path.selection_count(): if append_to: # path is the second of the paths involved end_node = append_to.Node(-1) if path.SegmentSelected(0): for i in range(1, path.len): type, p12, p, cont = path.Segment(i) if end_node is not None and type == Bezier: p12 = (p12[0] + end_node - path.Node(0), p12[1]) end_node = None append_to.AppendSegment(type, p12, p, cont) else: for i in range(path.len - 1, 0, -1): type, p12, p3, cont = path.Segment(i) if end_node is not None and type == Bezier: p12 = (p12[0], p12[1] + end_node - path.Node(-1)) end_node = None p = path.Node(i - 1) if type == Bezier: p12 = (p12[1], p12[0]) append_to.AppendSegment(type, p12, p, path.Continuity(i - 1)) continue else: # path is the first of the paths involved if path.SegmentSelected(0): # reverse the path append_to = CreatePath() p = path.Node(-1) append_to.AppendLine(p, ContAngle) for i in range(path.len - 1, 0, -1): type, p12, p3, cont = path.Segment(i) p = path.Node(i - 1) if type == Bezier: p12 = (p12[1], p12[0]) append_to.AppendSegment(type, p12, p, path.Continuity(i - 1)) path = append_to else: path = append_to = path.Duplicate() append_to.SetContinuity(-1, ContAngle) paths.append(path) undo = self.set_paths(paths) elif one == 0 and two == 1: undo_list = [] for path in self.paths: if path.selection_count(): undo_list.append(path.ClosePath()) else: undo_list.append(None) undo = (self.object.do_undo, undo_list) self._changed() else: return return undo #AddCmd(commands, CloseNodes, _("Close Nodes"), #bitmap = pixmaps.BezierCloseNodes) def SetContinuity(self, cont): new_paths = [] for path in self.paths: if path.selection_count(): new_paths.append(set_continuity(path, cont)) else: new_paths.append(path) return self.set_paths(new_paths) #AddCmd(commands, 'ContAngle', _("Angle"), SetContinuity, #args = ContAngle, bitmap = pixmaps.BezierAngle, key_stroke = 'a') #AddCmd(commands, 'ContSmooth', _("Smooth"), SetContinuity, #args = ContSmooth, bitmap = pixmaps.BezierSmooth, key_stroke = 's') #AddCmd(commands, 'ContSymmetrical', _("Symmetrical"), SetContinuity, #args = ContSymmetrical, bitmap = pixmaps.BezierSymm, key_stroke='y') def SegmentsToLines(self): if self.selection_type == SelCurvePoint: new_paths = list(self.paths) path = new_paths[self.selected_path] new_paths[self.selected_path] = segment_to_line(path, self.selected_idx) else: new_paths = [] for path in self.paths: if path.selection_count() > 1: new_paths.append(segments_to_lines(path)) else: new_paths.append(path) return self.set_paths(new_paths) #AddCmd(commands, SegmentsToLines, _("Curve->Line"), key_stroke = 'l', #bitmap = pixmaps.BezierCurveLine) def SegmentsToCurve(self): if self.selection_type == SelCurvePoint: new_paths = list(self.paths) path = new_paths[self.selected_path] new_paths[self.selected_path] = segment_to_curve(path, self.selected_idx) else: new_paths = [] for path in self.paths: if path.selection_count() > 1: new_paths.append(segments_to_beziers(path)) else: new_paths.append(path) return self.set_paths(new_paths) #AddCmd(commands, SegmentsToCurve, _("Line->Curve"), key_stroke = 'b', #bitmap = pixmaps.BezierLineCurve) def DeleteNodes(self): new_paths = [] for path in self.paths: if path.selection_count() > 0: newpath = delete_segments(path) else: newpath = path if newpath.len > 1: new_paths.append(newpath) else: # all nodes of path have been deleted if __debug__: pdebug('bezier', 'path removed') if new_paths: return self.set_paths(new_paths) else: if __debug__: pdebug('bezier', 'PolyBezier removed') self.document.DeselectObject(self.object) return self.parent.Remove(self.object) #AddCmd(commands, DeleteNodes, _("Delete Nodes"), #bitmap = pixmaps.BezierDeleteNode, key_stroke = ('-', 'Delete')) def InsertNodes(self): if self.selection_type == SelCurvePoint: new_paths = list(self.paths) path = new_paths[self.selected_path] new_paths[self.selected_path] = insert_node_at(path, self.selected_idx) self.selected_idx = int(self.selected_idx) + 1 self.selection_type = SelNodes else: new_paths = [] for path in self.paths: if path.selection_count() > 1: new_paths.append(insert_segments(path)) else: new_paths.append(path) return self.set_paths(new_paths) #AddCmd(commands, InsertNodes, _("Insert Nodes"), key_stroke = '+', #bitmap = pixmaps.BezierInsertNode) def ChangeRect(self): prop = self.properties if prop.IsAlgorithmicFill() or prop.IsAlgorithmicLine() \ or prop.line_arrow1 is not None or prop.line_arrow2 is not None \ or self.selection_type == SelCurvePoint: return self.bounding_rect filled = self.Filled() pts = [] for path in self.paths: if path.selection_count(): for i in range(1, path.len): if path.SegmentSelected(i - 1) or path.SegmentSelected(i): pts.append(path.Node(i - 1)) type, p12, p, cont = path.Segment(i) if type == Bezier: p1, p2 = p12 pts.append(p1) pts.append(p2) pts.append(p) if filled and not path.closed: if path.SegmentSelected(-1): pts.append(path.Node(0)) if path.SegmentSelected(0): pts.append(path.Node(-1)) if pts: return PointsToRect(pts).grown(prop.GrowAmount()) else: return EmptyRect context_commands = ('SelectAllNodes',) RegisterCommands(PolyBezierEditor) def adjust_control_point(p, node, control, continuity): if continuity == ContSymmetrical: return 2 * node - control elif continuity == ContSmooth: try: d = (control - node).normalized() length = abs(p - node) return node - length * d except ZeroDivisionError: # control == node return p else: return p def subdivide(p0, p1, p2, p3, t = 0.5): t2 = 1 - t r = t2 * p1 + t * p2 q1 = t2 * p0 + t * p1 q2 = t2 * q1 + t * r q5 = t2 * p2 + t * p3 q4 = t2 * r + t * q5 q3 = t2 * q2 + t * q4 return q1, q2, q3, q4, q5 def delete_segments(path): newpath = CreatePath() selected = path.selection_count() if (path.closed and selected == path.len - 1) or selected == path.len: return newpath f13 = 1.0 / 3.0; f23 = 2.0 / 3.0 i = 0 while path.SegmentSelected(i): i = i + 1 if path.closed and i > 0: if path.SegmentType(i) == Bezier: last_p2 = path.Segment(i)[1][1] last_type = Bezier else: last_p2 = f23 * path.Node(i - 1) + f13 * path.Node(i) last_type = Line else: last_p2 = None newpath.AppendLine(path.Node(i), path.Continuity(i)) seg_p1 = None; seg_type = None for i in range(i + 1, path.len): type, p12, p, cont = path.Segment(i) if type == Bezier: p1, p2 = p12 if path.SegmentSelected(i): if seg_type is None: seg_type = type if type == Bezier: seg_p1 = p1 else: seg_p1 = f23 * path.Node(i - 1) + f13 * p else: if seg_type is not None: if type == Bezier or seg_type == Bezier: if type == Line: p2 = f13 * path.Node(i - 1) + f23 * p newpath.AppendBezier(seg_p1, p2, p, cont) else: newpath.AppendLine(p, cont) seg_type = None else: newpath.AppendSegment(type, p12, p, cont) if path.closed: if last_p2 is not None: if last_type == Bezier or seg_type == Bezier: newpath.AppendBezier(seg_p1, last_p2, newpath.Node(0), newpath.Continuity(0)) else: newpath.AppendLine(newpath.Node(0), newpath.Continuity(0)) newpath.ClosePath() return newpath def insert_segments(path): newpath = CreatePath() newpath.AppendLine(path.Node(0), path.Continuity(0)) newpath.select_segment(0, path.SegmentSelected(0)) for i in range(1, path.len): type, p12, p, cont = path.Segment(i) if path.SegmentSelected(i) and path.SegmentSelected(i - 1): if type == Line: node = 0.5 * path.Node(i - 1) + 0.5 * path.Node(i) newpath.AppendLine(node) newpath.select_segment(-1) newpath.AppendLine(path.Node(i)) newpath.select_segment(-1) else: if newpath.Continuity(-1) == ContSymmetrical: newpath.SetContinuity(-1, ContSmooth) p1, p2 = p12 p1, p2, node, p3, p4 = subdivide(path.Node(i - 1), p1, p2, p) newpath.AppendBezier(p1, p2, node, ContSymmetrical) newpath.select_segment(-1) if cont == ContSymmetrical: cont = ContSmooth newpath.AppendBezier(p3, p4, p, cont) newpath.select_segment(-1) else: newpath.AppendSegment(type, p12, p, cont) newpath.select_segment(-1, path.SegmentSelected(i)) if path.closed: newpath.ClosePath() newpath.SetContinuity(-1, path.Continuity(-1)) return newpath def copy_selection(path, newpath): for i in range(path.len): newpath.select_segment(i, path.SegmentSelected(i)) def segments_to_lines(path): newpath = CreatePath() newpath.AppendLine(path.Node(0)) for i in range(1, path.len): if path.SegmentSelected(i) and path.SegmentSelected(i - 1): if path.SegmentType(i) == Bezier: cont = ContAngle newpath.SetContinuity(-1, ContAngle) else: cont = path.Continuity(i) newpath.AppendLine(path.Node(i), cont) else: apply(newpath.AppendSegment, path.Segment(i)) if path.closed: cont = newpath.Continuity(-1) newpath.ClosePath() newpath.SetContinuity(-1, cont) copy_selection(path, newpath) return newpath def segments_to_beziers(path): f13 = 1.0 / 3.0; f23 = 2.0 / 3.0 newpath = CreatePath() newpath.AppendLine(path.Node(0)) for i in range(1, path.len): type, p12, p, cont = path.Segment(i) if path.SegmentSelected(i) and path.SegmentSelected(i - 1): cont = path.Continuity(i) if type == Line: node1 = path.Node(i - 1); node2 = path.Node(i) p1 = f23 * node1 + f13 * node2 p2 = f13 * node1 + f23 * node2 cont = ContAngle else: p1, p2 = p12 newpath.AppendBezier(p1, p2, p, cont) else: newpath.AppendSegment(type, p12, p, cont) if path.closed: cont = newpath.Continuity(-1) newpath.ClosePath() newpath.SetContinuity(-1, cont) copy_selection(path, newpath) return newpath def set_continuity(path, cont): f13 = 1.0 / 3.0; f23 = 2.0 / 3.0 newpath = path.Duplicate() for i in range(1, path.len): if path.SegmentSelected(i): newpath.SetContinuity(i, cont) if cont == ContAngle: continue if newpath.SegmentType(i) != Bezier: continue if i == path.len - 1: if newpath.closed: other = 1 else: continue else: other = i + 1 if newpath.SegmentType(other) != Bezier: continue type, (p1, p2), node, oldcont = newpath.Segment(i) type, (p3, p4), other_node, other_cont = newpath.Segment(other) d = p3 - p2 if cont == ContSymmetrical: d = 0.5 * d p2 = adjust_control_point(p2, node, node + d, cont) p3 = adjust_control_point(p3, node, node - d, cont) newpath.SetBezier(i, p1, p2, node, cont) newpath.SetBezier(other, p3, p4, other_node, other_cont) return newpath def copy_path(dest, src, start = 0, end = -1, copy_selection = 1): if start < 0: start = src.len + start if end < 0: end = src.len + end for i in range(start, end + 1): type, control, node, cont = src.Segment(i) dest.AppendSegment(type, control, node, cont) if copy_selection: dest.select_segment(-1, src.SegmentSelected(i)) def insert_node_at(path, at): index = int(at) t = at - index newpath = CreatePath() copy_path(newpath, path, 0, index) type, control, node, cont = path.Segment(index + 1) if type == Line: newpath.AppendLine((1 - t) * path.Node(index) + t * node) newpath.select_segment(-1) newpath.AppendLine(node) else: if newpath.Continuity(-1) == ContSymmetrical: newpath.SetContinuity(-1, ContSmooth) p1, p2 = control p1, p2, q, p3, p4 = subdivide(newpath.Node(-1), p1, p2, node, t) newpath.AppendBezier(p1, p2, q, ContSmooth) newpath.select_segment(-1) if cont == ContSymmetrical: cont = ContSmooth newpath.AppendBezier(p3, p4, node, cont) copy_path(newpath, path, index + 2) if path.closed: newpath.ClosePath() newpath.SetContinuity(-1, path.Continuity(-1)) return newpath def split_path_at(path, at): index = int(at) t = at - index if path.closed: path1 = path2 = CreatePath() result = [path1] else: path1 = CreatePath() path2 = CreatePath() result = [path1, path2] copy_path(path1, path, 0, 0, copy_selection = 0) type, control, node, cont = path.Segment(index + 1) if type == Line: q = (1 - t) * path.Node(index) + t * node path2.AppendLine(q) path2.AppendLine(node) path2.select_segment(0) function = path1.AppendLine args = (q,) else: p1, p2 = control p1, p2, q, p3, p4 = subdivide(path.Node(index), p1, p2, node, t) path2.AppendLine(q) path2.AppendBezier(p3, p4, node, cont) path2.select_segment(0) function = path1.AppendBezier args = (p1, p2, q, ContSymmetrical) copy_path(path2, path, index + 2, copy_selection = 0) copy_path(path1, path, 1, index, copy_selection = 0) apply(function, args) return result def segment_to_line(path, at): index = int(at) if path.SegmentType(index + 1) == Bezier: newpath = CreatePath() copy_path(newpath, path, 0, index) newpath.SetContinuity(-1, ContAngle) newpath.AppendLine(path.Node(index + 1), ContAngle) copy_path(newpath, path, index + 2) if path.closed: cont = newpath.Continuity(-1) newpath.ClosePath() newpath.SetContinuity(-1, cont) else: newpath = path return newpath def segment_to_curve(path, at): index = int(at) if path.SegmentType(index + 1) == Line: newpath = CreatePath() copy_path(newpath, path, 0, index) f13 = 1.0 / 3.0; f23 = 2.0 / 3.0 node1 = path.Node(index); node2 = path.Node(index + 1) p1 = f23 * node1 + f13 * node2 p2 = f13 * node1 + f23 * node2 newpath.AppendBezier(p1, p2, node2, path.Continuity(index + 1)) copy_path(newpath, path, index + 2) if path.closed: cont = newpath.Continuity(-1) newpath.ClosePath() newpath.SetContinuity(-1, cont) else: newpath = path return newpath def CombineBeziers(beziers): combined = beziers[0].Duplicate() paths = combined.paths for bezier in beziers[1:]: paths = paths + bezier.paths combined.paths = paths return combined UniConvertor-1.1.4/src/app/Graphics/font.py0000775000076400007640000003014711060500565017303 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1997, 1998, 1999, 2000 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Font management... # import os, re, operator from string import split, strip, atoi, atof, lower, translate, maketrans import streamfilter from app import _, config, Point, TrafoType, Scale, SketchError, \ SketchInternalError, Subscribe, CreatePath, CreateFontMetric, SKCache from app.conf import const from app.Lib import encoding from app.events.warn import warn, INTERNAL, USER, pdebug from app.utils.os_utils import find_in_path, find_files_in_path minus_tilde = maketrans('-', '~') def xlfd_matrix(trafo): mat = '[%f %f %f %f]' % trafo.matrix() return translate(mat, minus_tilde) def _str(val): return strip(val) def _bb(val): return tuple(map(int, map(round, map(atof, split(strip(val)))))) def _number(val): return int(round(atof(val))) converters = { 'EncodingScheme': _str, 'Ascender': _number, 'Descender': _number, 'ItalicAngle': atof, 'FontBBox': _bb, 'StartCharMetrics': None, 'EndFontMetrics': None } StandardEncoding = 'AdobeStandardEncoding' def read_char_metrics(afm): # read the individual char metrics. Assumes that each line contains # the keys C, W, N and B. Of these keys, only C (or CH, but that's # not implemented here) is really required by the AFM specification. charmetrics = {encoding.notdef: (0, 0,0,0,0)} font_encoding = [encoding.notdef] * 256 while 1: line = afm.readline() if line == 'EndCharMetrics\n': break items = filter(None, map(strip, split(line, ';'))) if not items: continue code = name = width = bbox = None for item in items: [key, value] = split(item, None, 1) if key == 'C': code = atoi(value) elif key == 'WX': width = int(round(atof(value))) elif key == 'N': name = value elif key == 'B': bbox = tuple(map(int,map(round,map(atof,split(value))))) charmetrics[name] = (width,) + bbox font_encoding[code] = name return charmetrics, font_encoding def read_afm_file(filename): afm = streamfilter.LineDecode(open(filename, 'r')) attribs = {'ItalicAngle': 0.0} charmetrics = None font_encoding = [encoding.notdef] * 256 while 1: line = afm.readline() if not line: break try: [key, value] = split(line, None, 1) except ValueError: # this normally means that a line contained only a keyword # but no value or that the line was empty continue try: action = converters[key] except KeyError: continue if action: attribs[key] = action(value) elif key == 'StartCharMetrics': charmetrics, font_encoding = read_char_metrics(afm) break else: # EndFontMetrics break if not charmetrics: raise ValueError, \ 'AFM files without individual char metrics not yet supported.' if attribs.get('EncodingScheme', StandardEncoding) == StandardEncoding: enc = encoding.iso_latin_1 else: enc = font_encoding try: rescharmetrics = map(operator.getitem, [charmetrics] * len(enc), enc) except KeyError: # Some iso-latin-1 glyphs are not defined in the font. Try the # slower way and report missing glyphs. length = len(enc) rescharmetrics = [(0, 0,0,0,0)] * length for idx in range(length): name = enc[idx] try: rescharmetrics[idx] = charmetrics[name] except KeyError: # missing character... warn(INTERNAL, '%s: missing character %s', filename, name) # some fonts don't define ascender and descender (psyr.afm for # instance). use the values from the font bounding box instead. This # is not really a good idea, but how do we solve this? # # If psyr.afm is the only afm-file where these values are missing # (?) we could use the values from the file s050000l.afm shipped # with ghostscript (or replace psyr.afm with that file). # # This is a more general problem since many of the values Sketch # reads from afm files are only optional (including ascender and # descender). if not attribs.has_key('Ascender'): attribs['Ascender'] = attribs['FontBBox'][3] if not attribs.has_key('Descender'): attribs['Descender'] = attribs['FontBBox'][1] return (CreateFontMetric(attribs['Ascender'], attribs['Descender'], attribs['FontBBox'], attribs['ItalicAngle'], rescharmetrics), enc) _warned_about_afm = {} def read_metric(ps_name): for afm in ps_to_filename[ps_name]: afm = afm + '.afm' filename = find_in_path(config.font_path, afm) if filename: if __debug__: import time start = time.clock() metric = read_afm_file(filename) if __debug__: pdebug('timing', 'time to read afm %s: %g', afm, time.clock() - start) return metric else: if not _warned_about_afm.get(afm): warn(USER, _("I cannot find the metrics for the font %(ps_name)s.\n" "The file %(afm)s is not in the font_path.\n" "I'll use the metrics for %(fallback)s instead."), ps_name = ps_name, afm = afm, fallback = config.preferences.fallback_font) _warned_about_afm[afm] = 1 if ps_name != config.preferences.fallback_font: return read_metric(config.preferences.fallback_font) else: raise SketchError("Can't load metrics for fallback font %s", config.preferences.fallback_font) def font_file_name(ps_name): names = [] for basename in ps_to_filename[ps_name]: names.append(basename + '.pfb') names.append(basename + '.pfa') filename = find_files_in_path(config.font_path, names) return filename def read_outlines(ps_name): filename = font_file_name(ps_name) if filename: if __debug__: pdebug('font', 'read_outlines: %s', filename) import app.Lib.type1 return app.Lib.type1.read_outlines(filename) else: raise SketchInternalError('Cannot find file for font %s' % ps_name) def convert_outline(outline): paths = [] trafo = Scale(0.001) for closed, sub in outline: if closed: sub.append(sub[0]) path = CreatePath() paths.append(path) for item in sub: if len(item) == 2: apply(path.AppendLine, item) else: apply(path.AppendBezier, item) if closed: path.load_close() for path in paths: path.Transform(trafo) return tuple(paths) fontlist = [] fontmap = {} ps_to_filename = {} def _add_ps_filename(ps_name, filename): filename = (filename,) if ps_to_filename.has_key(ps_name): filename = ps_to_filename[ps_name] + filename ps_to_filename[ps_name] = filename def read_font_dirs(): #print 'read_font_dirs' if __debug__: import time start = time.clock() rx_sfd = re.compile(r'^.*\.sfd$') for directory in config.font_path: #print directory try: filenames = os.listdir(directory) except os.error, exc: warn(USER, _("Cannot list directory %s:%s\n" "ignoring it in font_path"), directory, str(exc)) continue dirfiles = filter(rx_sfd.match, filenames) for filename in dirfiles: filename = os.path.join(directory, filename) #print filename try: file = open(filename, 'r') line_nr = 0 for line in file.readlines(): line_nr = line_nr + 1 line = strip(line) if not line or line[0] == '#': continue info = map(intern, split(line, ',')) if len(info) == 6: psname = info[0] fontlist.append(tuple(info[:-1])) _add_ps_filename(psname, info[-1]) fontmap[psname] = tuple(info[1:-1]) elif len(info) == 2: psname, basename = info _add_ps_filename(psname, basename) else: warn(INTERNAL,'%s:%d: line must have exactly 6 fields', filename, line_nr) file.close() except IOError, value: warn(USER, _("Cannot load sfd file %(filename)s:%(message)s;" "ignoring it"), filename = filename, message = value.strerror) if __debug__: pdebug('timing', 'time to read font dirs: %g', time.clock() - start) def make_family_to_fonts(): families = {} for item in fontlist: family = item[1] fontname = item[0] if families.has_key(family): families[family] = families[family] + (fontname,) else: families[family] = (fontname,) return families xlfd_template = "%s--%s-*-*-*-*-*-%s" font_cache = SKCache() class Font: def __init__(self, name): self.name = name info = fontmap[name] family, font_attrs, xlfd_start, encoding_name = info self.family = family self.font_attrs = font_attrs self.xlfd_start = lower(xlfd_start) self.encoding_name = encoding_name self.metric, self.encoding = read_metric(self.PostScriptName()) self.outlines = None self.ref_count = 0 font_cache[self.name] = self def __del__(self): if font_cache.has_key(self.name): del font_cache[self.name] def __repr__(self): return "" % self.name def GetXLFD(self, size_trafo): if type(size_trafo) == TrafoType: if size_trafo.m11 == size_trafo.m22 > 0\ and size_trafo.m12 == size_trafo.m21 == 0: # a uniform scaling. Special case for better X11R5 # compatibility return xlfd_template % (self.xlfd_start, int(round(size_trafo.m11)), self.encoding_name) return xlfd_template % (self.xlfd_start, xlfd_matrix(size_trafo), self.encoding_name) return xlfd_template % (self.xlfd_start, int(round(size_trafo)), self.encoding_name) def PostScriptName(self): return self.name def TextBoundingBox(self, text, size): # Return the bounding rectangle of TEXT when set in this font # with a size of SIZE. The coordinates of the rectangle are # relative to the origin of the first character. llx, lly, urx, ury = self.metric.string_bbox(text) size = size / 1000.0 return (llx * size, lly * size, urx * size, ury * size) def TextCoordBox(self, text, size): # Return the coord rectangle of TEXT when set in this font with # a size of SIZE. The coordinates of the rectangle are relative # to the origin of the first character. metric = self.metric width = metric.string_width(text) size = size / 1000.0 return (0, metric.descender * size, width * size, metric.ascender * size) def TextCaretData(self, text, pos, size): from math import tan, pi size = size / 1000.0 x = self.metric.string_width(text, pos) * size lly = self.metric.lly * size ury = self.metric.ury * size t = tan(self.metric.italic_angle * pi / 180.0); up = ury - lly return Point(x - t * lly, lly), Point(-t * up, up) def TypesetText(self, text): return self.metric.typeset_string(text) def IsPrintable(self, char): return self.encoding[ord(char)] != encoding.notdef def GetOutline(self, char): if self.outlines is None: self.char_strings, self.cs_interp \ = read_outlines(self.PostScriptName()) self.outlines = {} char_name = self.encoding[ord(char)] outline = self.outlines.get(char_name) if outline is None: self.cs_interp.execute(self.char_strings[char_name]) outline = convert_outline(self.cs_interp.paths) self.outlines[char_name] = outline self.cs_interp.reset() copy = [] for path in outline: path = path.Duplicate() copy.append(path) return tuple(copy) def FontFileName(self): return font_file_name(self.PostScriptName()) _warned_about_font = {} def GetFont(fontname): if font_cache.has_key(fontname): return font_cache[fontname] if not fontmap.has_key(fontname): if not _warned_about_font.get(fontname): warn(USER, _("I can't find font %(fontname)s. " "I'll use %(fallback)s instead"), fontname = fontname, fallback = config.preferences.fallback_font) _warned_about_font[fontname] = 1 if fontname != config.preferences.fallback_font: return GetFont(config.preferences.fallback_font) raise ValueError, 'Cannot find font %s.' % fontname return Font(fontname) # # Initialisation on import # Subscribe(const.INITIALIZE, read_font_dirs) UniConvertor-1.1.4/src/app/Graphics/eps.py0000775000076400007640000000776511060500565017136 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1998, 1999, 2000, 2002 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # A GraphicsObject for Encapsulated Postscript Pictures # import os, math import PIL.Image from app.Lib import dscparser from app.utils import os_utils IsEpsFileStart = dscparser.IsEpsFileStart from app import _, Point, config from base import GraphicsObject from external import ExternalData, get_cached, ExternalGraphics gs_command = ('gs -sDEVICE=ppmraw -r%(resolution)d -dNOPAUSE -dSAFER -q' ' -sOutputFile=%(temp)s -g%(width)dx%(height)d' ' -c %(offx)f %(offy)f translate' ' /oldshowpage /showpage load def /showpage \'{}\' def ' ' -f %(filename)s -c oldshowpage quit') def render_preview(filename, startx, starty, width, height, resolution = None): import tempfile temp = tempfile.mktemp() try: # quote the filename so that it can have spaces and to avoid a # security hole filename = os_utils.sh_quote(filename) if resolution is None: resolution = config.preferences.eps_preview_resolution factor = resolution / 72.0 width = int(math.ceil(width * factor)) height = int(math.ceil(height * factor)) offx = -startx offy = -starty os.system(gs_command % locals()) image = PIL.Image.open(temp) image.load() return image finally: try: os.unlink(temp) except: pass class EpsData(ExternalData): def __init__(self, filename): self.info = info = dscparser.parse_eps_file(filename) self.filename = filename if info.BoundingBox: llx, lly, urx, ury = info.BoundingBox self.width = Point(urx - llx, 0) self.height = Point(0, ury - lly) self.start = (llx, lly) self.size = (urx - llx, ury - lly) self.image = None try: self.image = render_preview(filename, llx, lly, urx - llx, ury - lly) except IOError: pass else: raise TypeError, '%s has no BoundingBox' % filename ExternalData.__init__(self, filename) def Start(self): return self.start def Size(self): return self.size def WriteLines(self, file): write = file.write try: infile = open(self.filename, 'r') except IOError, val: raise IOError, (filename, val) try: readline = infile.readline line = readline() while line: if line[:15] == '%%BeginPreview:': while line[:12] != '%%EndPreview': line = readline() continue write(line) line = readline() finally: infile.close() def load_eps(filename): eps = get_cached(filename) if eps: return eps return EpsData(filename) class EpsImage(ExternalGraphics): has_edit_mode = 0 is_Eps = 1 def __init__(self, filename = '', trafo = None, duplicate = None): if duplicate is None: if not filename: raise ValueError, 'filename must be provided' data = load_eps(filename) else: data = None ExternalGraphics.__init__(self, data, trafo, duplicate = duplicate) def DrawShape(self, device, rect = None): device.DrawEps(self.data, self.trafo) def Info(self): filename = os.path.basename(self.data.Filename()) width, height = self.data.Size() x, y = self.trafo.offset() return _("EpsFile `%(filename)s' %(width)d x %(height)d " "at (%(x)d, %(y)d)") % locals() def SaveToFile(self, file): file.EpsFile(self.data, self.trafo) def PSNeededResources(self): return self.data.info.DocumentNeededResources UniConvertor-1.1.4/src/app/Graphics/plugobj.py0000775000076400007640000001345411060500565020001 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1998, 1999, 2001 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from types import TupleType from app import _, SketchError, CreateListUndo, Undo, \ Trafo, Translation, Scale, Identity, TrafoType from compound import Compound class PluginCompound(Compound): class_name = '' # has to be provided by derived classes is_Plugin = 1 def SetParameters(self, kw = None): # XXX could be extended to handle keyword arguments. undo = {} for key, value in kw.items(): undo[key] = getattr(self, key) setattr(self, key, value) return self.SetParameters, undo def SaveToFile(self, file, *args, **kw): if self.class_name: apply(file.BeginPluginCompound, (self.class_name,) + args, kw) for obj in self.objects: obj.SaveToFile(file) file.EndPluginCompound() else: raise SketchError("Plugin %s doesn't define a class name" % self.__class__) class TrafoPlugin(PluginCompound): def __init__(self, trafo = None, duplicate = None, loading = 0): PluginCompound.__init__(self, duplicate = duplicate) if duplicate is not None: self.trafo = duplicate.trafo else: if trafo is None: trafo = Identity elif type(trafo) == TupleType: trafo = apply(Trafo, trafo) elif isinstance(trafo, TrafoType): # trafo is already a trafo object pass else: # assume a number and interpret it as a scaling transformation trafo = Scale(trafo) self.trafo = trafo def recompute(self): # Implement this in the derived class to update the children. pass def SetParameters(self, kw): undo = PluginCompound.SetParameters(self, kw) try: self.recompute() except: Undo(undo) raise return undo def set_transformation(self, trafo): undo = (self.set_transformation, self.trafo) self.trafo = trafo self.recompute() return undo def Transform(self, trafo): undo = [self.begin_change_children()] try: undo.append(PluginCompound.Transform(self, trafo)) undo.append(self.set_transformation(trafo(self.trafo))) undo.append(self.end_change_children()) except: undo.reverse() map(Undo, undo) raise return CreateListUndo(undo) def Translate(self, offset): return self.Transform(Translation(offset)) def RemoveTransformation(self): return self.set_transformation(Translation(self.trafo.offset())) def LayoutPoint(self): return self.trafo.offset() def Trafo(self): return self.trafo class UnknownPlugin(PluginCompound): is_Group = 1 changed = 0 def __init__(self, class_name = '', *args, **kw): if kw.has_key('loading'): del kw['loading'] duplicate = kw.get('duplicate') if duplicate is not None: self.class_name = duplicate.class_name self.args = duplicate.args self.kw = duplicate.kw else: self.class_name = class_name self.args = args self.kw = kw PluginCompound.__init__(self, duplicate = duplicate) self.disguise() def disguise(self): # If self has only one child, try to behave like it: if len(self.objects) == 1: object = self.objects[0] if object.is_curve: self.is_curve = object.is_curve self.AsBezier = object.AsBezier self.Paths = object.Paths def _changed(self): PluginCompound._changed(self) self.changed = 1 def load_Done(self): PluginCompound.load_Done(self) self.disguise() def SaveToFile(self, file): if not self.changed: apply(PluginCompound.SaveToFile, (self, file) + self.args, self.kw) else: # XXX an alternative approach for a changed UnknownPlugin # might be to store explicitly as an 'UnknownPlugin' so that # always is represented by this class. if len(self.objects) == 1: self.objects[0].SaveToFile(file) else: # Save as ordinary group. This might not be a good idea, # since the UnknownPlugin may (in the future) have some # special behaviour, that the group doesn't have. file.BeginGroup() for obj in self.objects: obj.SaveToFile(file) file.EndGroup() def Info(self): return _("Unknown Plugin Object `%s'") % self.class_name Ungroup = PluginCompound.GetObjects # XXX this implementation of the UnknownPlugin my fail in some # situations. # # The following example is now fixed, but similar situations are # conceivable: # # If the plugin defines is_curve and AsBezier(), for instance, it can be # used as a control object in a BlendGroup with, say, a PolyBezier # object as the other control object. The UnknownPlugin does not provide # that interface, so that loading a document containing such a # BlendGroup would fail, because the UnknownPlugin instance cannot be # converted to a PolyBezier object when the interpolation is recomputed. # # A solution would be to make the UnknownPlugin behave different if it # only has one child. Or to have it examine its children and try to # support the interfaces they have in common. I.e. if all children have # is_curve == 1, it could also set its instance variable is_curve to 1, # and implement the AsBezier meghod by returning a PolyBezier object # that is the combination (`Combine Beziers') of its converted children. # This would not always be appropriate, however. # # Are there other cases where UnknownPlugin has shortcomings? UniConvertor-1.1.4/src/app/Graphics/ellipse.py0000775000076400007640000003153511060500565017774 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1997, 1998, 1999, 2000, 2001 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from math import sin, cos, atan2, hypot, pi, fmod, floor from app.conf.const import ArcArc, ArcChord, ArcPieSlice, ConstraintMask, \ AlternateMask from app import _, Point, Polar, Trafo, SingularMatrix, Rect, UnionRects, \ CreateMultiUndo, NullUndo, RegisterCommands import graphics #from app.UI.command import AddCmd #import app.UI.skpixmaps #pixmaps = app.UI.skpixmaps.PixmapTk import handle from base import Primitive, RectangularPrimitive, RectangularCreator, Creator,\ Editor from bezier import PolyBezier from blend import Blend from properties import DefaultGraphicsProperties from app import _sketch # # Ellipse # # helper function for snapping (might be useful elsewhere too): def snap_to_line(start, end, p): if start != end: result = [(abs(start - p), start), (abs(end - p), end)] v = end - start length = abs(v) r = (v * (p - start)) / (length ** 2) if 0 <= r <= 1.0: p2 = start + r * v result.append((abs(p2 - p), p2)) return min(result) else: return (abs(start - p), start) class Ellipse(RectangularPrimitive): is_Ellipse = 1 is_curve = 1 is_clip = 1 has_edit_mode = 1 commands = RectangularPrimitive.commands[:] def __init__(self, trafo = None, start_angle = 0.0, end_angle = 0.0, arc_type = ArcPieSlice, properties = None, duplicate = None): if duplicate is not None: self.start_angle = duplicate.start_angle self.end_angle = duplicate.end_angle self.arc_type = duplicate.arc_type else: self.start_angle = start_angle self.end_angle = end_angle self.arc_type = arc_type RectangularPrimitive.__init__(self, trafo, properties = properties, duplicate = duplicate) self.normalize() def DrawShape(self, device, rect = None, clip = 0): Primitive.DrawShape(self, device) device.SimpleEllipse(self.trafo, self.start_angle, self.end_angle, self.arc_type, rect, clip) def SetAngles(self, start_angle, end_angle): undo = (self.SetAngles, self.start_angle, self.end_angle) self.start_angle = start_angle self.end_angle = end_angle self.normalize() self._changed() return undo def Angles(self): return self.start_angle, self.end_angle def SetArcType(self, arc_type): if arc_type == self.arc_type: return NullUndo undo = (self.SetArcType, self.arc_type) self.arc_type = arc_type self._changed() return undo def ArcType(self): return self.arc_type #AddCmd(commands, 'EllipseArc', _("Arc"), SetArcType, args = ArcArc) #AddCmd(commands, 'EllipseChord', _("Chord"), SetArcType, args = ArcChord) #AddCmd(commands, 'EllipsePieSlice', _("Pie Slice"), SetArcType, #args = ArcPieSlice) def normalize(self): pi2 = 2 * pi self.start_angle = fmod(self.start_angle, pi2) if self.start_angle < 0: self.start_angle = self.start_angle + pi2 self.end_angle = fmod(self.end_angle, pi2) if self.end_angle < 0: self.end_angle = self.end_angle + pi2 def Paths(self): path = _sketch.approx_arc(self.start_angle, self.end_angle, self.arc_type) path.Transform(self.trafo) return (path,) def AsBezier(self): return PolyBezier(paths = self.Paths(), properties = self.properties.Duplicate()) def Hit(self, p, rect, device, clip = 0): return device.SimpleEllipseHit(p, self.trafo, self.start_angle, self.end_angle, self.arc_type, self.properties, self.Filled() or clip, ignore_outline_mode = clip) def Blend(self, other, p, q): blended = RectangularPrimitive.Blend(self, other, p, q) if self.start_angle != self.end_angle \ or other.start_angle != other.end_angle: blended.start_angle = p * self.start_angle + q * other.start_angle blended.end_angle = p * self.end_angle + q * other.end_angle if self.start_angle == self.end_angle: blended.arc_type = other.arc_type elif other.start_angle == other.end_angle: blended.arc_type = self.arc_type else: if self.arc_type == other.arc_type: blended.arc_type = self.arc_type # The rest of the arc type blends is quite arbitrary # XXX: are these rules acceptable? Maybe we should blend # the ellipses as bezier curves if the arc types differ elif self.arc_type == ArcArc or other.arc_type == ArcArc: blended.arc_type = ArcArc elif self.arc_type == ArcChord or other.arc_type == ArcChord: blended.arc_type = ArcChord else: blended.arc_type = ArcPieSlice return blended def GetSnapPoints(self): t = self.trafo start_angle = self.start_angle; end_angle = self.end_angle if self.start_angle == self.end_angle: a = Point(t.m11, t.m21) b = Point(t.m12, t.m22) c = t.offset() return [c, c + a, c - a, c + b, c - b] else: points = [t(Polar(start_angle)), t(Polar(end_angle)), t.offset()] if end_angle < start_angle: end_angle = end_angle + 2 * pi pi2 = pi / 2 angle = pi2 * (floor(start_angle / pi2) + 1) while angle < end_angle: points.append(t(Polar(1, angle))) angle = angle + pi2 return points def Snap(self, p): try: r, phi = self.trafo.inverse()(p).polar() start_angle = self.start_angle; end_angle = self.end_angle p2 = self.trafo(Polar(1, phi)) if start_angle == end_angle: result = (abs(p - p2), p2) else: result = [] if phi < 0: phi = phi + 2 * pi if start_angle < end_angle: between = start_angle <= phi <= end_angle else: between = start_angle <= phi or phi <= end_angle if between: result.append((abs(p - p2), p2)) start = self.trafo(Polar(self.start_angle)) end = self.trafo(Polar(self.end_angle)) if self.arc_type == ArcArc: result.append((abs(start - p), start)) result.append((abs(end - p), end)) elif self.arc_type == ArcChord: result.append((snap_to_line(start, end, p))) elif self.arc_type == ArcPieSlice: center = self.trafo.offset() result.append(snap_to_line(start, center, p)) result.append(snap_to_line(end, center, p)) result = min(result) return result except SingularMatrix: # XXX this case could be handled better. return (1e200, p) def update_rects(self): trafo = self.trafo start = trafo.offset() # On some systems, atan2 can raise a ValueError if both # parameters are 0. In that case, the actual value the of angle # is not important since in the computation of p below, the # coordinate depending on the angle will always be 0 because # both trafo coefficients are 0. So set the angle to 0 in case # of an exception. try: phi1 = atan2(trafo.m12, trafo.m11) except ValueError: phi1 = 0 try: phi2 = atan2(trafo.m22, trafo.m21) except ValueError: phi2 = 0 p = Point(trafo.m11 * cos(phi1) + trafo.m12 * sin(phi1), trafo.m21 * cos(phi2) + trafo.m22 * sin(phi2)) self.coord_rect = r = Rect(start + p, start - p) if self.properties.HasLine(): width = self.properties.line_width r = r.grown(width / 2 + 1) # add the bounding boxes of arrows if self.arc_type == ArcArc: pi2 = pi / 2 arrow1 = self.properties.line_arrow1 if arrow1 is not None: pos = trafo(Polar(1, self.start_angle)) dir = trafo.DTransform(Polar(1, self.start_angle - pi2)) r = UnionRects(r, arrow1.BoundingRect(pos, dir, width)) arrow2 = self.properties.line_arrow2 if arrow2 is not None: pos = trafo(Polar(1, self.end_angle)) dir = trafo.DTransform(Polar(1, self.end_angle + pi2)) r = UnionRects(r, arrow2.BoundingRect(pos, dir, width)) self.bounding_rect = r def Info(self): trafo = self.trafo w = hypot(trafo.m11, trafo.m21) h = hypot(trafo.m12, trafo.m22) dict = {'center': trafo.offset(), 'radius': w, 'axes': (w, h)} if w == h: text = _("Circle radius %(radius)[length], " "center %(center)[position]") else: text = _("Ellipse axes %(axes)[size], center %(center)[position]") return text, dict def SaveToFile(self, file): Primitive.SaveToFile(self, file) file.Ellipse(self.trafo, self.start_angle, self.end_angle, self.arc_type) def Editor(self): return EllipseEditor(self) context_commands = ('EllipseArc', 'EllipseChord', 'EllipsePieSlice') RegisterCommands(Ellipse) class EllipseCreator(RectangularCreator): creation_text = _("Create Ellipse") def compute_trafo(self, state): start = self.drag_start end = self.drag_cur if state & AlternateMask: # start is the center of the ellipse if state & ConstraintMask: # end is a point of the periphery of a *circle* centered # at start radius = abs(start - end) self.trafo = Trafo(radius, 0, 0, radius, start.x, start.y) else: # end is a corner of the bounding box d = end - start self.trafo = Trafo(d.x, 0, 0, d.y, start.x, start.y) else: # the ellipse is inscribed into the rectangle with start and # end as opposite corners. end = self.apply_constraint(self.drag_cur, state) d = (end - start) / 2 self.trafo = Trafo(d.x, 0, 0, d.y, start.x + d.x, start.y + d.y) def MouseMove(self, p, state): # Bypass RectangularCreator Creator.MouseMove(self, p, state) self.compute_trafo(state) def ButtonUp(self, p, button, state): Creator.DragStop(self, p) self.compute_trafo(state) def DrawDragged(self, device, partially): device.DrawEllipse(self.trafo(-1, -1), self.trafo(1, 1)) def CurrentInfoText(self): t = self.trafo data = {} if abs(round(t.m11, 2)) == abs(round(t.m22, 2)): text = _("Circle %(radius)[length], center %(center)[position]") data['radius'] = t.m11 else: text = _("Ellipse %(size)[size], center %(center)[position]") data['size'] = (abs(t.m11), abs(t.m22)) data['center'] = t.offset() return text, data def CreatedObject(self): return Ellipse(self.trafo, properties = DefaultGraphicsProperties()) class EllipseEditor(Editor): EditedClass = Ellipse selection = 0 def ButtonDown(self, p, button, state): if self.selection == 1: start = self.trafo(cos(self.start_angle), sin(self.start_angle)) else: start = self.trafo(cos(self.end_angle), sin(self.end_angle)) Editor.DragStart(self, start) return p - start def apply_constraint(self, p, state): if state & ConstraintMask: try: inverse = self.trafo.inverse() p2 = inverse(p) r, phi = p2.polar() pi12 = pi / 12 angle = pi12 * floor(phi / pi12 + 0.5) pi2 = 2 * pi d1 = fmod(abs(phi - angle), pi2) if self.selection == 1: selected_angle = self.end_angle else: selected_angle = self.start_angle d2 = fmod(abs(phi - selected_angle), pi2) if d2 < d1: phi = selected_angle else: phi = angle p = self.trafo(Polar(r, phi)) except SingularMatrix: pass return p def MouseMove(self, p, state): p = self.apply_constraint(p, state) Editor.MouseMove(self, p, state) def ButtonUp(self, p, button, state): p = self.apply_constraint(p, state) Editor.DragStop(self, p) start_angle, end_angle, arc_type = self.angles() return CreateMultiUndo(self.object.SetAngles(start_angle, end_angle), self.object.SetArcType(arc_type)) def angles(self): start_angle = self.start_angle; end_angle = self.end_angle if self.arc_type == ArcChord: arc_type = ArcChord else: arc_type = ArcPieSlice try: inverse = self.trafo.inverse() p = inverse(self.drag_cur) if self.selection == 1: start_angle = atan2(p.y, p.x) elif self.selection == 2: end_angle = atan2(p.y, p.x) if abs(p) > 1: arc_type = ArcArc except SingularMatrix: pass if fmod(abs(start_angle - end_angle), 2 * pi) < 0.0001: if self.selection == 1: start_angle = end_angle else: end_angle = start_angle return (start_angle, end_angle, arc_type) def DrawDragged(self, device, partially): start_angle, end_angle, arc_type = self.angles() device.SimpleEllipse(self.trafo, start_angle, end_angle, arc_type) def GetHandles(self): trafo = self.trafo start_angle = self.start_angle; end_angle = self.end_angle p1 = trafo(cos(self.start_angle), sin(self.start_angle)) if start_angle == end_angle: return [handle.MakeNodeHandle(p1)] p2 = trafo(cos(self.end_angle), sin(self.end_angle)) return [handle.MakeNodeHandle(p1), handle.MakeNodeHandle(p2)] def SelectHandle(self, handle, mode): self.selection = handle.index + 1 def SelectPoint(self, p, rect, device, mode): return 0 UniConvertor-1.1.4/src/app/Graphics/image.py0000775000076400007640000001024611060500566017416 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1997, 1998, 1999, 2000 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # A simple graphics object that represents a pixel image. It uses the # Python Image Library for file I/O. The ExternalData baseclass # maintains a cache of images to avoid having multiple copies of data in # memory. # import os from types import StringType import PIL.Image, PIL.ImageChops from app import _, RegisterCommands #from app.UI.command import AddCmd from external import ExternalData, get_cached, ExternalGraphics class ImageData(ExternalData): attributes = {'mode':0, 'size':0, 'im':0, 'info':0} def __init__(self, image, filename = '', cache = 1): # convert image to mode 'L' or 'RGB' if necessary if image.mode not in ('RGB', 'RGBA', 'L'): if image.mode == '1': mode = 'L' else: mode = 'RGB' image = image.convert(mode) else: image.load() self.image = image ExternalData.__init__(self, filename, cache) def __getattr__(self, attr): if self.attributes.has_key(attr): return getattr(self.image, attr) raise AttributeError, attr def AsEmbedded(self): if self.filename: return ImageData(self.image) else: return self def IsEmbedded(self): return not self.filename def Size(self): return self.size def Image(self): return self.image def Convert(self, mode): if mode != self.image.mode: return ImageData(self.image.convert(mode)) else: return self def Invert(self): return ImageData(PIL.ImageChops.invert(self.image)) def load_image(filename, cache = 1): if type(filename) == StringType: image = get_cached(filename) if image: return image image = PIL.Image.open(filename) if type(filename) != StringType: filename = '' return ImageData(image, filename = filename, cache = cache) class Image(ExternalGraphics): is_Image = 1 is_clip = 1 commands = ExternalGraphics.commands[:] def __init__(self, image = None, imagefile = '', trafo = None, duplicate = None): if duplicate is None: if not image: if not imagefile: raise ValueError, 'Image must be instantiated with'\ ' either image or imagefile' image = load_image(imagefile) ExternalGraphics.__init__(self, image, trafo, duplicate = duplicate) def DrawShape(self, device, rect = None, clip = 0): device.DrawImage(self.data, self.trafo, clip) def Info(self): width, height = self.data.Size() x, y = self.trafo.offset() if self.IsEmbedded(): return _("Embedded Image %(width)d x %(height)d " "at (%(x)d, %(y)d)") % locals() else: filename = os.path.basename(self.data.Filename()) return _("Linked Image `%(filename)s' %(width)d x %(height)d " "at (%(x)d, %(y)d)") % locals() def SaveToFile(self, file): file.Image(self.data, self.trafo) def IsEmbedded(self): return self.data.IsEmbedded() def CanEmbed(self): return not self.IsEmbedded() def Embed(self): return self.SetData(self.data.AsEmbedded()) #AddCmd(commands, 'EmbedImage', _("Embed Image"), Embed, #sensitive_cb = 'CanEmbed') def CallImageFunction(self, function, args = ()): if type(args) != type(()): args = (args,) data = apply(getattr(self.data, function), args) return self.SetData(data) #AddCmd(commands, 'GrayscaleImage', _("Grayscale Image"), #CallImageFunction, args = ('Convert', 'L')) #AddCmd(commands, 'InvertImage', _("Invert Image"), CallImageFunction, #args = 'Invert') #context_commands = ('EmbedImage', 'GrayscaleImage', 'InvertImage') RegisterCommands(Image) UniConvertor-1.1.4/src/app/Graphics/maskgroup.py0000775000076400007640000001141311060500565020340 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1998, 1999, 2000 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Class MaskGroup # # A special group where one object defines a clip mask for the # entire group # from app.events.warn import warn_tb, INTERNAL #from app.UI.command import AddCmd from app import _, IntersectRects, RegisterCommands from compound import EditableCompound from properties import EmptyFillStyle, EmptyLineStyle class MaskGroup(EditableCompound): is_Group = 1 is_MaskGroup = 1 _lazy_attrs = EditableCompound._lazy_attrs.copy() _lazy_attrs['mask_fill'] = 'update_mask_attrs' _lazy_attrs['mask_line'] = 'update_mask_attrs' commands = EditableCompound.commands[:] def __init__(self, objects = None, duplicate = None): EditableCompound.__init__(self, objects, duplicate = duplicate) def update_mask_attrs(self): if self.objects: mask = self.objects[0] if mask.has_properties: self.mask_fill = mask.Properties().Duplicate() self.mask_fill.AddStyle(EmptyLineStyle) if mask.has_line and mask.Properties().HasLine(): self.mask_line = mask.Properties().Duplicate() self.mask_line.AddStyle(EmptyFillStyle) else: self.mask_line = None else: self.mask_line = self.mask_fill = None def update_rects(self): if self.objects: self.bounding_rect = self.objects[0].bounding_rect self.coord_rect = self.objects[0].coord_rect def ChildChanged(self, child): if child is self.objects[0]: if self.document is not None: self.document.AddClearRect(child.bounding_rect) EditableCompound.ChildChanged(self, child) def Info(self): return _("MaskGroup with %d objects") % len(self.objects) def Hit(self, p, rect, device): if self.objects[0].Hit(p, rect, device, clip = 1): return EditableCompound.Hit(self, p, rect, device) def Blend(self, other, frac1, frac2): try: objs = self.objects oobjs = other.objects blended = [] for i in range(min(len(objs), len(oobjs))): blended.append(Blend(objs[i], oobjs[i], frac1, frac2)) return MaskGroup(blended) except: warn_tb(INTERNAL) raise MismatchError def Ungroup(self): objects = EditableCompound.GetObjects(self) # Move the mask, which is in objects[0] to the end of the # objects list, because it was on top of all other objects # before the group was created. # # Use a copy of the objects list or we're modifying the list # used by the mask group itself with unpredictable consequences # for undo. # XXX perhaps it would be better to have GetObjects return a # copy of the list. objects = objects[:] objects.append(objects[0]) del objects[0] return objects def SaveToFile(self, file): file.BeginMaskGroup() for obj in self.objects: obj.SaveToFile(file) file.EndMaskGroup() def DrawShape(self, device, rect = None): if not self.objects: return mask = self.objects[0] if mask.has_properties: attr = mask.properties mask.properties = self.mask_fill device.PushClip() clipped = 1 try: mask.DrawShape(device, rect, clip = 1) if rect: rect = IntersectRects(rect, mask.bounding_rect) test = rect.overlaps for o in self.objects[1:]: if test(o.bounding_rect): o.DrawShape(device, rect) else: for obj in self.objects[1:]: obj.DrawShape(device) if self.mask_line is not None: device.PopClip() clipped = 0 mask.properties = self.mask_line mask.DrawShape(device, rect) finally: if clipped: device.PopClip() if mask.has_properties: mask.properties = attr def permute_objects(self, permutation): # make sure the mask stays at index 0 if permutation[0] != 0: permutation = list(permutation) permutation.remove(0) permutation.insert(0, 0) return EditableCompound.permute_objects(self, permutation) def Mask(self): return self.objects[0] def MaskedObjects(self): return self.objects[1:] def SelectMask(self): if self.document is not None: self.document.SelectObject(self.objects[0]) #AddCmd(commands, SelectMask, _("Select Mask"), key_stroke = 'm') #context_commands = ('SelectMask',) RegisterCommands(MaskGroup) UniConvertor-1.1.4/src/app/Graphics/guide.py0000775000076400007640000000532611060500565017433 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1998, 1999 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from app import _, Point, PointType, InfinityRect from base import GraphicsObject, Draggable class GuideLine(GraphicsObject, Draggable): is_GuideLine = 1 def __init__(self, point, horizontal = 0): self.point = point self.horizontal = horizontal def DrawShape(self, device): device.DrawGuideLine(self.point, self.horizontal) def DrawDragged(self, device, partially): device.DrawGuideLine(self.drag_cur, self.horizontal) def Hit(self, p, rect, device): if self.horizontal: return rect.contains_point(Point(p.x, self.point.y)) else: return rect.contains_point(Point(self.point.x, p.y)) def ButtonDown(self, p, button, state): self.DragStart(p) if self.horizontal: result = Point(0, p.y - self.point.y) else: result = Point(p.x - self.point.x, 0) return result def ButtonUp(self, p, button, state): self.DragStop(p) def SetPoint(self, point): undo = (self.SetPoint, self.point) if type(point) != PointType: if type(point) == type(()): point = apply(Point, point) else: if self.horizontal: point = Point(self.point.x, point) else: point = Point(point, self.point.y) self.point = point return undo def update_rects(self): self.bounding_rect = self.coord_rect = InfinityRect def get_clear_rect(self): return (self.point, self.horizontal) def Snap(self, p): if self.horizontal: return (abs(p.y - self.point.y), (None, self.point.y)) else: return (abs(p.x - self.point.x), (self.point.x, None)) def SaveToFile(self, file): file.GuideLine(self.point, self.horizontal) def Coordinates(self): if self.horizontal: return (self.point.y, 1) else: return (self.point.x, 0) def CurrentInfoText(self): if self.horizontal: text = _("Horizontal Guide Line at %(coord)[length]") dict = {'coord': self.drag_cur.y} else: text = _("Vertical Guide Line at %(coord)[length]") dict = {'coord': self.drag_cur.x} return text, dict UniConvertor-1.1.4/src/app/Graphics/handle.py0000775000076400007640000001044011060500565017562 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1997, 1998, 1999 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Some convenience functions for specifying the coordinates and the # shape of handles. # # # A handle is described by a tuple of the form: (TYPE, P, CURSOR) # # Note: This is no longer true (Sketch 0.5.3), the handles are now # represented by instances of the Handle class below, but the # interpretation of the values hasn't changed. # # TYPE is an integer specifying the type of tuple. Constants for this # are defined in const.py and have names of the form Handle*. # # P is usually a Point object (in document coordinates) specifying the # position of the handle. An alternative form is a tuple of the form (P, # (X, Y)) where P is the position like before, and X and Y are offsets # inicating that the handle should not be shown exactly at P. X and Y # can be 0, +1 or -1 which currently means that the offset is 0 or +/- 8 # pixels. # The second form is used for instance for the handles at the corners # and sides of the current selection where the user can click and resize # the object. # # CURSOR specifies the shape of the mouse pointer when it is over the # handle. In the current Tk version this is usually a string with the # name of the cursor. None means the default cursor of the window. # # There are two handle types which have a slightly different # representation: # # HandleLine: (HandleLine, P1, P2) # This handle is not really a handle but a (dashed) line from P1 # to P2. P1 and P2 are Point objects in document coordinates. This # type is used by the PolyBezier object in edit mode. # # Handle_Pixmap: (Handle_Pixmap, P, PIXMAP, CURSOR) # PIXMAP is drawn as a handle at P. PIXMAP is a Pixmap object and # not a Tk-like pixmap specification (a string of a certain # format). The pixmap must have depth 1. P and CURSOR are used as # in the normal case. # # The functions in this module should be used to create the handle # specifications since the representation may change. More specifically, # a class hierarchy might be used for the various types of handles. # from app import Point from app.conf import const class Handle: def __init__(self, type, p, cursor = const.CurHandle, offset = None, p2 = None, list = None, pixmap = None, code = None): self.type = type self.p = p self.cursor = cursor self.p2 = p2 self.offset = offset self.list = list self.pixmap = pixmap self.index = 0 self.code = code def __str__(self): return "Handle(%d, %s)" % (self.type, self.p) def __repr__(self): return "Handle(%d, %s, index = %s)" % (self.type, self.p, self.index) def MakeHandle(type, p, cursor = const.CurHandle): return Handle(type, p, cursor) def MakeNodeHandle(p, selected = 0, code = 0): if selected: return Handle(const.HandleSelectedNode, p, code = code) return Handle(const.HandleNode, p, code = code) def MakeObjectHandleList(list): return Handle(const.Handle_SmallOpenRectList, None, list = list) def MakeControlHandle(p, code = 0): return Handle(const.HandleControlPoint, p, code = code) def MakeCurveHandle(p): return Handle(const.HandleCurvePoint, p, cursor = None) def MakeLineHandle(p1, p2): return Handle(const.HandleLine, p1, p2 = p2) def MakeOffsetHandle(p, offset, cursor = const.CurHandle): return Handle(const.Handle, p, cursor, offset = offset) def MakePixmapHandle(p, offset, pixmap, cursor = const.CurHandle): return Handle(const.Handle_Pixmap, p, cursor, offset = offset, pixmap = pixmap) def MakeCaretHandle(p, up): return Handle(const.Handle_Caret, p, p2 = up) def MakePathTextHandle(p, up): return Handle(const.Handle_PathText, p, p2 = up) UniConvertor-1.1.4/src/app/Graphics/curveop.py0000775000076400007640000000410711060500565020015 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1997, 1998, 1999, 2000, 2001 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from app import CreatePath, Rotation, Translation, Scale from app._sketch import ContAngle, ContSmooth, ContSymmetrical, Bezier, Line def arrow_vectors(path, arrow1, arrow2): # Return the directions for the arrow heads of path as a pair of # point objects. If the path is closed or consists of only one Node, # return (None, None). dir1 = dir2 = None if not path.closed and path.len > 1: if arrow1: type, controls, p3, cont = path.Segment(1) p = path.Node(0) if type == Bezier: p1, p2 = controls dir = p - p1 if not abs(dir): dir = p - p2 else: dir = p - p3 dir1 = dir if arrow2: type, controls, p, cont = path.Segment(-1) p3 = path.Node(-2) if type == Bezier: p1, p2 = controls dir = p - p2 if not abs(dir): dir = p - p1 else: dir = p - p3 dir2 = dir return dir1, dir2 def arrow_trafos(path, properties): dir1, dir2 = arrow_vectors(path, properties.line_arrow1, properties.line_arrow2) width = properties.line_width if width < 1.0: width = 1.0 scale = Scale(width) t1 = t2 = None if dir1 is not None: t1 = Translation(path.Node(0))(Rotation(dir1.polar()[1]))(scale) if dir2 is not None: t2 = Translation(path.Node(-1))(Rotation(dir2.polar()[1]))(scale) return t1, t2 UniConvertor-1.1.4/src/app/Graphics/base.py0000775000076400007640000006237411060500565017256 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2003-2006 by Igor E. Novikov # Copyright (C) 1997, 1998, 1999, 2001 by Bernhard Herzog # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. # # This file contains the root of the Sketch graphics class hierarchy. # from traceback import print_stack from app.events.warn import warn, INTERNAL from app.conf.const import CHANGED, SelectSet, Button1Mask, ConstraintMask, \ SCRIPT_GET, SCRIPT_OBJECT, SCRIPT_UNDO from app import NullUndo, CreateMultiUndo, Undo, UndoAfter from app import Point, NullPoint, UnionRects, Identity, Translation, Trafo from blend import Blend, MismatchError, BlendTrafo from properties import PropertyStack import properties # Class Draggable # # This class maintains some instance variables for a click and drag # operation on a graphics object. # # The scenario is this: The user has selected a graphics object, say a # straight line between the points A and B, for editing. As a hint for # the user where to click, the application shows two inverted rectangles # at the endpoints. These rectangles are called handles. The user clicks # on one of the handles, and, with the mouse button still pressed, drags # the mouse to the new location of the selected endpoint. As feedback to # the user, the application shows a `rubber-band' line during the drag # to indicate what the line would look like if the user released the # button. # # Two aspects of this operation are handled by the classes Draggable and # EditSelect: Keeping track of the start point, the current point, the # amount dragged, drawing the object during the drag and, in the case of # Selectable, which parts of the object the user selected. # # Keeping track of where the drag started and how far in which direction # the user has moved the mouse so far, is important, because, in the # above example the endpoint should be moved not simply to the point the # user dragged to, but by the amount the user dragged. # # To make this a little clearer: the handle is usually a few pixels # wide, so the user may not click exactly on the pixel the endpoint lies # on, but some pixels away. In that case, releasing the button without # moving the mouse would still move the endpoint which is not what the # user expected. # # Using only the offset of the drag is even more important when the # entire object is being moved. In the above example, clicking on the # middle of the line should select the entire line, i.e. both endpoints, # for the drag. During the drag and at the end of the drag we can't move # one or both endpoints to the current location of the mouse pointer, we # have to move both endpoints by the same offset. # # To achieve this, an instance of Draggable has the following instance # variables: # # dragging True, while being dragged # drag_start start point # drag_cur current point # off offset by which the pointer was moved, # i.e. drag_cur - drag_start # drawn true, if the object is visible on the screen in its # dragged form (see Hide() and Show()) # # These variables only have meaningful values during the drag, that is, # between the calls to DragStart() and DragStop(), see below. # drag_start, drag_cur and off are of type Point. (See the developer's guide) class Draggable: drawn = 0 dragging = 0 drag_start = NullPoint drag_cur = NullPoint off = NullPoint drag_mask = Button1Mask # XXX move this to some other class ? def __init__(self): # not needed here, but if some derived class wants to call the # base class constructor... pass def DragStart(self, p): # Start the drag at P. Initialize the instance variables. Set # dragging to true. # XXX: document the meaning of the return value self.drawn = 0 # the object is not visible yet self.dragging = 1 self.drag_start = p self.drag_cur = p self.off = NullPoint return self.off def DragMove(self, p): # The pointer has moved to p. Compute the new offset. self.off = p - self.drag_start self.drag_cur = p def MouseMove(self, p, state): # XXX add documentation for this if state & self.drag_mask: self.off = p - self.drag_start self.drag_cur = p def DragStop(self, p): # The drag stopped at p. Update drag_cur and off for the last # time, and set dragging to false. self.dragging = 0 self.off = p - self.drag_start self.drag_cur = p def DragCancel(self): self.dragging = 0 # The rest of Draggable's methods deal with drawing the object in # `dragged' form (usually an outline) on the screen. The output # device is assumed to be set up in such a way that drawing the same # object twice removes it again (usually using GCxor). Currently, # this will be an instance of InvertingDevice (graphics.py) # # Show() and Hide() use this assumption and the instance variable # drawn, to make certain that the object is visible or invisible, # respectively. If drawn is false Show() calls DrawDragged() to draw # the object and then sets drawn to true. This way Show() may be # called multiple times by the canvas widget if it thinks the # outline of the object should be visible, without removing the # outline accidentally. # # DrawDragged(), which obviously has to be implemented by some # derived class, has to draw the outline of the object on the output # device, using drag_cur or off to compute coordinates. The internal # state of the object, for example the endpoints of lines, should # only be changed temporarily during DrawDragged; the state of the # object should only change if the drag is completed successfully. # # The boolean parameter PARTIALLY indicates whether the object has # to be drawn completely or if it is sufficient to draw only the # parts that are changed by the drag. For instance, if a vertex of a # polygon is dragged, it might suffice to draw the two edges sharing # this vertex. It is safe to ignore this parameter and always draw # the whole object. It is especially useful for complex objects like # polygons or poly beziers, where it improves performance and # reduces flickering on the screen # # Implementation Note: Show and Hide are the methods normally used # by the canvas to show or hide the object while dragging. An # exception is the RedrawMethod of the canvas object where # DrawDragged is called directly. def DrawDragged(self, device, partially): pass def Show(self, device, partially = 0): if not self.drawn: self.DrawDragged(device, partially) self.drawn = 1 def Hide(self, device, partially = 0): if self.drawn: self.DrawDragged(device, partially) self.drawn = 0 # # Class Selectable # # This class defines the interface and default implementation for # objects that can be selected by the user with a mouse click. # class Selectable: def __init__(self): # only needed for derived classes. pass def Hit(self, p, rect, device): return None def SelectSubobject(self, p, rect, device, path = None, *rest): return self def GetObjectHandle(self, multiple): # Return a single point marking an important point of the # object. This point is highlighted by a small rectangle in the # canvas to indicate that the object is selected. Alternatively, # a list of such points can be returned to mark several points, # but that feature should only be used by compound objects. # # If multiple is false, self is the only object selected. If # it's true, there may be more than one selected object. return [] class EditSelect(Selectable): def SelectPoint(self, p, rect, device, mode = SelectSet): # Select (sub)object at P. If something is selected, return # true, false otherwise. return 0 def SelectHandle(self, handle, mode = SelectSet): pass def SelectRect(self, rect, mode = SelectSet): # select (sub-)object(s) in RECT pass def GetHandles(self): # In edit mode, this method will be called to get a list of # handles. A handle should be shown at every `hot' spot of the # object (e.g. the nodes of a PolyBezier). Handles are described # by tuples which can be easily created by the functions in # handle.py return [] class SelectAndDrag(Draggable, EditSelect): def __init__(self): Draggable.__init__(self) Selectable.__init__(self) def CurrentInfoText(self): # return a string describing the current state of the object # during a drag return '' # # Class Protocols # # Some boolean flags that describe the object's capabilities # class Protocols: is_GraphicsObject = 0 is_Primitive = 0 is_Editor = 0 is_Creator = 0 has_edit_mode = 0 # true if object has an edit mode. If true, the # Editor() method must be implemented is_curve = 0 # true, if object can be represented by and # converted to a PolyBezier object. If true, the # AsBezier() and Paths() methods must be # implemented is_clip = 0 has_fill = 0 # True, iff object can have fill properties has_line = 0 # True, iff object can have line properties has_font = 0 # True, iff object can have a font has_properties = 0 is_Bezier = 0 is_Rectangle = 0 is_Ellipse = 0 is_Text = 0 # Text objects must have a Font() method # returning a font. is_SimpleText = 0 is_PathTextGroup = 0 is_PathTextText = 0 # The text part of a path text group is_Image = 0 is_Eps = 0 is_Group = 0 is_Compound = 0 is_Layer = 0 is_Blend = 0 # The blendgroup is_BlendInterpolation = 0 # The interpolation child of a blend group is_Clone = 0 is_MaskGroup = 0 is_GuideLine = 0 is_Plugin = 0 # # Class Bounded # # Instances of this class have various kinds of bounding rectangles # These rectangles are accessible via instance variables to increase # performance (important for bounding_rect, which is used when testing # which object is selected by a click). All rectangles are given in # document coords and are aligned with the axes. The variables are: # # coord_rect # # The smallest rectangle that contains all points of the outline. # The line width, if applicable, is NOT taken into account here. # This rectangle is used to arrange objects (AlignSelected, # AbutHorizonal, ...) # # bounding_rect # # Like coord rect but takes the line width into account. It is # meant to be useful as a PostScript BoundingBox. # # Method: # # LayoutPoint() # # Return the point which should be snapped to a grid point. # class Bounded: _lazy_attrs = {'coord_rect' : 'update_rects', 'bounding_rect' : 'update_rects'} def __init__(self): pass def del_lazy_attrs(self): for key in self._lazy_attrs.keys(): try: delattr(self, key) except: pass def update_rects(self): # compute the various bounding rects and other attributes that # use `lazy evaluation'. This method MUST be implemented by # derived classes. It MUST set self.bounding_rect and # self.coord_rect and other attributes where appropriate. pass def __getattr__(self, attr): # if a lazy attribute is accessed, compute it. method = self._lazy_attrs.get(attr) if method: getattr(self, method)() # now it should work... use self.__dict__ directly to avoid # recursion if the method is buggy try: return self.__dict__[attr] except KeyError, msg: warn(INTERNAL, '%s did not compute %s for %s.', method, attr, self) if attr[:2] == attr[-2:] == '__': #if attr in ('__nonzero__', '__len__'): # print_stack() pass else: warn(INTERNAL, "%s instance doesn't have an attribute %s", self.__class__, attr) raise AttributeError, attr def LayoutPoint(self): return Point(self.coord_rect.left, self.coord_rect.bottom) def GetSnapPoints(self): return [] # # Class HierarchyNode # # This is base class for all objects that are part of the object # hierarchy of a document. It manages the parent child relationship and # the references to the document and other methods (and standard # behavior) that every object needs. No object derived from this class # should override the methods defined here except as documented. # class HierarchyNode: def __init__(self, duplicate = None): if duplicate is not None: self.document = duplicate.document if duplicate.was_untied: self.was_untied = duplicate.was_untied def __del__(self): if self.document: self.document.connector.RemovePublisher(self) def Destroy(self): # remove all circular references here... # May be extended by derived classes. self.parent = None parent = None def SetParent(self, parent): self.parent = parent def depth(self): if self.parent is not None: return self.parent.depth() + 1 return 1 def SelectionInfo(self): if self.parent is not None: return self.parent.SelectionInfo(self) document = None # the document self belongs to def SetDocument(self, doc): self.document = doc if doc is not None and self.was_untied: self.TieToDocument() del self.was_untied def UntieFromDocument(self): # this will be called when self is being stored in the clipboard # (CopyForClipboard/CutForClipboard), but before self.document # becomes None. Disconnect will not be called in this case. # May be extended by derived classes. self.was_untied = 1 def TieToDocument(self): # this will be called when self is being inserted into the # document from the clipboard, after self.document has been set. # Connect will not be called in this case. # May be extended by derived classes. pass def Subscribe(self, channel, func, *args): # XXX: what do we do if document has not been set (yet) if self.document is not None: self.document.connector.Connect(self, channel, func, args) def Unsubscribe(self, channel, func, *args): if self.document is not None: self.document.connector.Disconnect(self, channel, func, args) def Issue(self, channel, *args): if self.document is not None: apply(self.document.connector.Issue, (self, channel,) + args) def issue_changed(self): self.Issue(CHANGED, self) if self.parent is not None: self.parent.ChildChanged(self) def Connect(self): # May be extended by derived classes. pass def Disconnect(self): # May be extended by derived classes. pass def Duplicate(self): # return a duplicate of self return self.__class__(duplicate = self) # # Class GraphicsObject # # The base class for all `normal' objects that are part of the drawing # itself, like rectangles or groups (the experimental clone objects are # derived from HierarchyNode (Sep98)) # class GraphicsObject(Bounded, HierarchyNode, Selectable, Protocols): is_GraphicsObject = 1 keymap = None commands = [] context_commands = () was_untied = 0 script_access = {} def __init__(self, duplicate = None): Selectable.__init__(self) HierarchyNode.__init__(self, duplicate = duplicate) def ChildChanged(self, child): # in compound objects, this method is called by the child # whenever it changes (normally via the issue_changed method) pass def __cmp__(self, other): return cmp(id(self), id(other)) def _changed(self): self.del_lazy_attrs() self.issue_changed() return (self._changed,) def SetLowerLeftCorner(self, corner): # move self so that self's lower left corner is at CORNER. This # used when interactively placing an object rect = self.coord_rect ll = Point(rect.left, rect.bottom) return self.Translate(corner - ll) def RemoveTransformation(self): # Some objects accumulate the transformation applied by # Transform() and apply them every time the object is displayed # Restore this transformation to Identity. return NullUndo script_access['RemoveTransformation'] = SCRIPT_UNDO def AsBezier(self): # Return self as bezier if possible. See is_curve above. return None script_access['AsBezier'] = SCRIPT_OBJECT def Paths(self): # Return a tuple of curve objects describing the outline of self # if possible. The curve objects can be the ones used internally # by self. The calling code is expected not to modify the curve # objects in place. # See is_curve above. return None script_access['Paths'] = SCRIPT_GET def Blend(self, other, frac1, frac2): # Return the weighted average of SELF and OTHER. FRAC1 and FRAC2 # are the weights (if SELF and OTHER were numbers this should be # FRAC1 * SELF + FRAC2 * OTHER). # # This method is used by the function Blend() in blend.py. If # SELF and OTHER can't be blended, raise the blend.MismatchError # exception. This is also the default behaviour. raise MismatchError script_access['Blend'] = SCRIPT_OBJECT def Snap(self, p): # Determine the point Q on self's outline closest to P and # return a tuple (abs(Q - P), Q) return (1e100, p) script_access['Snap'] = SCRIPT_GET def ObjectChanged(self, obj): return 0 def ObjectRemoved(self, obj): return NullUndo # Add some inherited method's script access flags script_access['coord_rect'] = SCRIPT_GET script_access['bounding_rect'] = SCRIPT_GET script_access['LayoutPoint'] = SCRIPT_GET script_access['Duplicate'] = SCRIPT_OBJECT # and flags for standard methods script_access['Transform'] = SCRIPT_UNDO script_access['Translate'] = SCRIPT_UNDO # # # class Creator(SelectAndDrag, Protocols): is_Creator = 1 creation_text = 'Create Object' def __init__(self, start): self.start = start def EndCreation(self): # This method will be called when the object was being created # interactively using more than one click-drag-release cycle, # and the user has finished. This method is needed by the # PolyBezier primitive for instance. # # Return true if creation was successful, false otherwise. return 1 def ContinueCreation(self): # called during interactive creation when the user releases the # mouse button. Return true, if the object may need another # click-drag-release cycle, false for objects that are always # complete after one cycle. (XXX the `true' return value is # interpreted in a special way, see the PolyBezier primitive) # # XXX: Should we distinguish more cases? A rectangle for example # is always complete after one click-drag-release cycle. A # PolyBezier object needs at least two cycles but accepts any # number of additional cycles. A polygon (with straight lines) # needs at least one. We might return a value that indicates # whether the user *must* supply additional points, whether it's # optional or whether the object is complete and the user # *cannot* add points. return None class Editor(SelectAndDrag): is_Editor = 1 EditedClass = GraphicsObject context_commands = () def __init__(self, object): self.object = object def __getattr__(self, attr): return getattr(self.object, attr) def Destroy(self): # called by the edit mode selection when the editor it not # needed anymore. pass def ChangeRect(self): # ChangeRect indicates the area that is going to change during # the current click-drag-release cycle. It is safe to make this # equal to bounding_rect. This rectangle is used to determine # which parts of the window have to be redrawn. return self.bounding_rect # # Class Primitive # # The baseclass for all graphics primitives like Polygon, Rectangle, ... # but not for composite objects. Basically, this adds the management of # properties and styles to GraphicsObject class Primitive(GraphicsObject): has_fill = 1 has_line = 1 has_properties = 1 is_Primitive = 1 tie_info = None script_access = GraphicsObject.script_access.copy() def __init__(self, properties = None, duplicate = None): GraphicsObject.__init__(self, duplicate = duplicate) if duplicate is not None: self.properties = duplicate.properties.Duplicate() if duplicate.tie_info: self.tie_info = duplicate.tie_info else: if properties is not None: self.properties = properties else: self.properties = PropertyStack() def Destroy(self): GraphicsObject.Destroy(self) def UntieFromDocument(self): info = self.properties.Untie() if info: self.tie_info = info GraphicsObject.UntieFromDocument(self) def TieToDocument(self): if self.tie_info: self.properties.Tie(self.document, self.tie_info) del self.tie_info def Transform(self, trafo, rects = None): # Apply the affine transformation trafo to all coordinates and # the properties. undo = self.properties.Transform(trafo, rects) if undo is not NullUndo: return self.properties_changed(undo) return undo def Translate(self, offset): # Move all points by OFFSET. OFFSET is an SKPoint instance. return NullUndo def DrawShape(self, device): # Draw the object on device. Here we just set the properties. device.SetProperties(self.properties, self.bounding_rect) # The following functions manage the properties def set_property_stack(self, properties): self.properties = properties load_SetProperties = set_property_stack def properties_changed(self, undo): if undo is not NullUndo: return (UndoAfter, undo, self._changed()) return undo def AddStyle(self, style): return self.properties_changed(self.properties.AddStyle(style)) script_access['AddStyle'] = SCRIPT_UNDO def Filled(self): return self.properties.HasFill() script_access['Filled'] = SCRIPT_GET def Properties(self): return self.properties script_access['Properties'] = SCRIPT_OBJECT def SetProperties(self, if_type_present = 0, **kw): if if_type_present: # change properties of that type if properties of that are # already present. prop_types = properties.property_types LineProperty = properties.LineProperty FillProperty = properties.FillProperty FontProperty = properties.FontProperty types = map(prop_types.get, kw.keys()) if LineProperty in types and not self.properties.HasLine(): for key in kw.keys(): if prop_types[key] == LineProperty: del kw[key] if FillProperty in types and not self.properties.HasFill(): for key in kw.keys(): if prop_types[key] == FillProperty: del kw[key] if FontProperty in types and not self.properties.HasFont(): for key in kw.keys(): if prop_types[key] == FontProperty: del kw[key] return self.properties_changed(apply(self.properties.SetProperty, (), kw)) script_access['SetProperties'] = SCRIPT_UNDO def LineWidth(self): if self.properties.HasLine: return self.properties.line_width return 0 script_access['LineWidth'] = SCRIPT_GET def ObjectChanged(self, obj): if self.properties.ObjectChanged(obj): rect = self.bounding_rect self.del_lazy_attrs() self.document.AddClearRect(UnionRects(rect, self.bounding_rect)) self.issue_changed() return 1 return 0 def ObjectRemoved(self, obj): return self.properties.ObjectRemoved(obj) def set_blended_properties(self, blended, other, frac1, frac2): blended.set_property_stack(Blend(self.properties, other.properties, frac1, frac2)) def SaveToFile(self, file): # save object to file. Must be extended by the subclasses. Here, # we just save the properties. self.properties.SaveToFile(file) # # Class RectangularObject # # A mix-in class for graphics objects that are more or less rectangular # and store their position and orientation in a SKTrafoObject. class RectangularObject: def __init__(self, trafo = None, duplicate = None): if duplicate is not None: self.trafo = duplicate.trafo else: if not trafo: self.trafo = Identity else: self.trafo = trafo def Trafo(self): return self.trafo def LayoutPoint(self, *rest): # accept arguments to use this function as GetObjectHandle return self.trafo.offset() GetObjectHandle = LayoutPoint def Translate(self, offset): return self.Transform(Translation(offset)) def set_transformation(self, trafo): undo = (self.set_transformation, self.trafo) self.trafo = trafo self._changed() return undo def Transform(self, trafo): trafo = trafo(self.trafo) return self.set_transformation(trafo) def Blend(self, other, p, q): if other.__class__ == self.__class__: blended = self.__class__(BlendTrafo(self.trafo, other.trafo, p, q)) self.set_blended_properties(blended, other, p, q) return blended raise MismatchError class RectangularPrimitive(RectangularObject, Primitive): def __init__(self, trafo = None, properties = None, duplicate = None): RectangularObject.__init__(self, trafo, duplicate = duplicate) Primitive.__init__(self, properties = properties, duplicate = duplicate) def Transform(self, trafo, transform_properties = 1): undostyle = undo = NullUndo try: rect = self.bounding_rect undo = RectangularObject.Transform(self, trafo) if transform_properties: rects = (rect, self.bounding_rect) undostyle = Primitive.Transform(self, trafo, rects = rects) return CreateMultiUndo(undostyle, undo) except: Undo(undo) Undo(undostyle) raise def Translate(self, offset): return self.Transform(Translation(offset), transform_properties = 0) class RectangularCreator(Creator): def __init__(self, start): Creator.__init__(self, start) self.trafo = Trafo(1, 0, 0, 1, start.x, start.y) def ButtonDown(self, p, button, state): Creator.DragStart(self, p) def apply_constraint(self, p, state): if state & ConstraintMask: trafo = self.trafo w, h = p - self.drag_start if w == 0: w = 0.00001 a = h / w if a > 0: sign = 1 else: sign = -1 if abs(a) > 1.0: h = sign * w else: w = sign * h p = self.drag_start + Point(w, h) return p def MouseMove(self, p, state): p = self.apply_constraint(p, state) Creator.MouseMove(self, p, state) def ButtonUp(self, p, button, state): p = self.apply_constraint(p, state) Creator.DragStop(self, p) x, y = self.off self.trafo = Trafo(x, 0, 0, y, self.trafo.v1, self.trafo.v2) UniConvertor-1.1.4/src/app/Graphics/external.py0000775000076400007640000001013311060500565020150 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1997, 1998, 1999, 2000 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Handle external data # # # This module defines two classes to represent external graphics like # bitmapped images or encapsulated PostScript files. from app import Rect, Trafo, IdentityMatrix, NullUndo, SKCache from base import GraphicsObject, RectangularObject from properties import EmptyProperties instance_cache = SKCache() class ExternalData: # default instance variables stored_in_cache = 0 filename = '' def __init__(self, filename = '', do_cache = 1): if filename and do_cache: self.stored_in_cache = 1 instance_cache[filename] = self if filename: self.filename = filename def __del__(self): if self.stored_in_cache and instance_cache.has_key(self.filename): del instance_cache[self.filename] def Filename(self): return self.filename # to be supplied by derived classes # # Size() return the size as a tuple (width, height) def get_cached(filename): if instance_cache.has_key(filename): return instance_cache[filename] return None class ExternalGraphics(RectangularObject, GraphicsObject): has_edit_mode = 0 # by default this has no properties: has_fill = has_line = has_font = 0 data = None def __init__(self, data = None, trafo = None, duplicate = None): RectangularObject.__init__(self, trafo, duplicate = duplicate) GraphicsObject.__init__(self, duplicate = duplicate) if duplicate is not None: data = duplicate.data self.data = data def Data(self): return self.data def SetData(self, data): undo = self.SetData, self.data self.data = data # XXX issue_changed() here ? return undo def Hit(self, p, rect, device, clip = 0): width, height = self.data.Size() return device.ParallelogramHit(p, self.trafo, width, height, 1, ignore_outline_mode = clip) def SetLowerLeftCorner(self, corner): # Used by the place mode in SketchCanvas. Currently no undo # needed since this is called *before* self is a part of a # document. self.trafo = apply(Trafo, self.trafo.coeff()[:4] + tuple(corner)) self.del_lazy_attrs() def RemoveTransformation(self): if self.trafo.matrix() != IdentityMatrix: center = self.coord_rect.center() width, height = self.data.Size() trafo = Trafo(1, 0, 0, 1, center.x - width / 2, center.y - height / 2) return self.set_transformation(trafo) return NullUndo def update_rects(self): width, height = self.data.Size() rect = self.trafo(Rect(0, 0, width, height)) self.coord_rect = rect self.bounding_rect = rect.grown(2) def Info(self): # Should be overwritten by derived classes return 'ExternalGraphics' def SetProperties(self, **kw): return NullUndo def AddStyle(self, style): return NullUndo def Properties(self): return EmptyProperties def GetSnapPoints(self): width, height = self.data.Size() corners = ((0, 0), (width, 0), (width, height), (0, height)) return map(self.trafo, corners) def Snap(self, p): width, height = self.data.Size() try: x, y = self.trafo.inverse()(p) if 0 <= x <= width: if 0 <= y <= height: x = round(x / width) * width y = round(y / height) * height else: y = min(max(y, 0), height) else: x = min(max(x, 0), width) if 0 > y or y > height: y = min(max(y, 0), height) p2 = self.trafo(x, y) return (abs(p - p2), p2) except SingularMatrix: return (1e200, p) UniConvertor-1.1.4/src/app/Graphics/document.py0000775000076400007640000023023011060500565020146 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1996, 1997, 1998, 1999, 2000 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Classes: # # SketchDocument # EditDocument(SketchDocument) # # The document class represents a complete Sketch drawing. Each drawing # consists of one or more Layers, which in turn consist of zero of more # graphics objects. Graphics objects can be primitives like rectangles # or curves or composite objects like groups which consist of graphics # objects themselves. Objects may be arbitrarily nested. # # The distinction between SketchDocument and EditDocument has only # historical reasons... # from types import ListType, IntType, StringType, TupleType from string import join from app.events.warn import pdebug, warn, warn_tb, USER, INTERNAL from app import SketchInternalError from app import config, _ from app.events.connector import Issue, RemovePublisher, Connect, Disconnect, QueueingPublisher, Connector from app.events.undodict import UndoDict from app import Rect, Point, UnionRects, InfinityRect, Trafo, Rotation, Translation, Scale from app import UndoRedo, Undo, CreateListUndo, NullUndo, UndoAfter import app import color, selinfo, pagelayout from base import Protocols from layer import Layer, GuideLayer, GridLayer from group import Group from bezier import CombineBeziers from properties import EmptyProperties from pattern import SolidPattern import guide from selection import SizeSelection, EditSelection, TrafoSelection, TrafoRectangle from math import * from app.conf.const import STYLE, SELECTION, EDITED, MODE, UNDO, REDRAW, LAYOUT, PAGE from app.conf.const import LAYER, LAYER_ORDER, LAYER_ACTIVE, GUIDE_LINES, GRID from app.conf.const import SelectSet, SelectAdd,SelectSubtract,SelectSubobjects,\ SelectDrag, SelectGuide, Button1Mask from app.conf.const import SCRIPT_OBJECT, SCRIPT_OBJECTLIST, SCRIPT_GET # from text import CanCreatePathText, CreatePathText # SketchDocument is derived from Protocols for the benefit of the loader # classes class SketchDocument(Protocols): can_be_empty = 1 script_access = {} def __init__(self, create_layer = 0): self.pages = [] self.active_page=0 self.snap_grid = GridLayer() self.snap_grid.SetDocument(self) self.guide_layer = GuideLayer(_("Guide Lines")) self.guide_layer.SetDocument(self) layer=Layer(_("MasterLayer 1")) layer.SetDocument(self) layer.is_MasterLayer=1 self.master_layers=[layer] if create_layer: # a new empty document self.active_layer = Layer(_("Layer 1")) self.active_layer.SetDocument(self) self.layers = [self.snap_grid]+[self.active_layer] + self.master_layers + [self.guide_layer] self.pages.append([self.active_layer]) else: # we're being created by the load module self.active_layer = None self.layers = [] self.pages.append(self.layers) def __del__(self): if __debug__: pdebug('__del__', '__del__', self.meta.filename) def __getitem__(self, idx): if type(idx) == IntType: return self.layers[idx] elif type(idx) == TupleType: if len(idx) > 1: return self.layers[idx[0]][idx[1:]] elif len(idx) == 1: return self.layers[idx[0]] raise ValueError, 'invalid index %s' % `idx` def AppendLayer(self, layer_name = None, master=0, *args, **kw_args): try: old_layers = self.layers[:] if layer_name is None: layer_name = _("Layer %d") % (len(self.layers) + 1) else: layer_name = str(layer_name) layer = apply(Layer, (layer_name,) + args, kw_args) layer.SetDocument(self) if master: layer.is_MasterLayer=1 mlayers=self.getMasterLayers() mlayers.append(layer) self.layers = [self.snap_grid] + self.getRegularLayers() + mlayers + [self.guide_layer] else: rlayers=self.getRegularLayers() rlayers.append(layer) self.layers = [self.snap_grid] + rlayers + self.getMasterLayers() + [self.guide_layer] if not self.active_layer: self.active_layer = layer return layer except: self.layers[:] = old_layers raise script_access['AppendLayer'] = SCRIPT_OBJECT def RearrangeLayers(self): if not len(self.getMasterLayers()): layer=Layer(_("MasterLayer 1")) layer.SetDocument(self) layer.is_MasterLayer=1 self.layers.append(layer) self.layers = [self.snap_grid] + self.getRegularLayers() + self.getMasterLayers() + [self.guide_layer] def getRegularLayers(self): result=[] for layer in self.layers: if not layer.is_SpecialLayer and not layer.is_MasterLayer: result.append(layer) return result def getMasterLayers(self): result=[] for layer in self.layers: if layer.is_MasterLayer: result.append(layer) return result def insert_pages(self, number=1, index=0, is_before=0): for item in range(number): if is_before: self.pages.insert(index, self.NewPage()) self.active_page+=1 self.setActivePage(index) else: self.pages.insert(index+item+1, self.NewPage()) self.setActivePage(index+1) def setActivePage(self, index): self.pages[self.active_page]=self.getRegularLayers() self.layers=[self.snap_grid] + self.pages[index] + self.getMasterLayers() + [self.guide_layer] self.active_page=index self.active_layer=(self.pages[index])[0] def updateActivePage(self): self.pages[self.active_page]=self.getRegularLayers() def NewPage(self): page=[] new_layer=Layer(_("Layer 1")) new_layer.SetDocument(self) page.append(new_layer) return page def delete_page(self, index=0): if len(self.pages)==1: return if self.active_page and index == self.active_page: self.setActivePage(index-1) if not self.active_page and index == self.active_page: self.setActivePage(1) self.active_page=0 if self.active_page and self.active_page>index: self.active_page-=1 self.pages.remove(self.pages[index]) def delete_pages(self, number=1, index=0,is_before=0): for item in range(number): self.delete_page(index+is_before) def move_page(self, index=0, backward=0): if index==0 or index==len(self.pages)-1: return else: page=self.pages[index] self.pages.remove(page) self.pages.insert(index+1-2*backward, page) if index==self.active_page: self.active_page=self.active_page+1-2*backward def BoundingRect(self, visible = 1, printable = 0): rects = [] for layer in self.layers: if ((visible and layer.Visible()) or (printable and layer.Printable())): rect = layer.bounding_rect if rect and rect != InfinityRect: rects.append(rect) if rects: return reduce(UnionRects, rects) return None script_access['BoundingRect'] = SCRIPT_GET def augment_sel_info(self, info, layeridx): if type(layeridx) != IntType: layeridx = self.layers.index(layeridx) return selinfo.prepend_idx(layeridx, info) def insert(self, object, at = None, layer = None): undo_info = None try: if layer is None: layer = self.active_layer elif type(layer) == IntType: layer = self.layers[layer] if layer is None or layer.Locked(): raise SketchInternalError('Layer %s is locked' % layer) if type(object) == ListType: for obj in object: obj.SetDocument(self) else: object.SetDocument(self) sel_info, undo_info = layer.Insert(object, at) sel_info = self.augment_sel_info(sel_info, layer) return (sel_info, undo_info) except: if undo_info is not None: Undo(undo_info) raise def selection_from_point(self, p, hitrect, device, path = None): # iterate top down (i.e. backwards) through the list of layers if path: path_layer = path[0] path = path[1:] else: path_layer = -1 for idx in range(len(self.layers) - 1, -1, -1): if idx == path_layer: info = self.layers[idx].SelectSubobject(p, hitrect, device, path) else: info = self.layers[idx].SelectSubobject(p, hitrect, device) if info: return self.augment_sel_info(info, idx) else: return None def selection_from_rect(self, rect): info = [] for layer in self.layers: info = info + self.augment_sel_info(layer.SelectRect(rect), layer) return info def Draw(self, device, rect = None): for layer in self.layers: layer.Draw(device, rect) def Grid(self): return self.snap_grid def SnapToGrid(self, p): return self.snap_grid.Snap(p) def SnapToGuide(self, p, maxdist): return self.guide_layer.Snap(p) #, maxdist) def DocumentInfo(self): info = [] info.append('%d layers' % len(self.layers)) for idx in range(len(self.layers)): layer = self.layers[idx] info.append('%d: %s,\t%d objects' % (idx + 1, layer.name, len(layer.objects))) return join(info, '\n') def SaveToFile(self, file): self.updateActivePage() file.BeginDocument() self.page_layout.SaveToFile(file) self.write_styles(file) self.snap_grid.SaveToFile(file) pagesnum=len(self.pages) pagecount=0 interval=100/pagesnum for page in self.pages: file.Page() pagecount+=1 app.updateInfo(inf2=_('Saving page %u of %u')%(pagecount,pagesnum), inf3=interval*pagecount) layercount=0 layersnum=len(page) l_interval=interval/layersnum for layer in page: layercount+=1 app.updateInfo(inf2=_('Saving page %u of %u, layer %u of %u')% (pagecount,pagesnum,layercount,layersnum), inf3=interval*pagecount) layer.SaveToFile(file) for layer in self.getMasterLayers(): layer.SaveToFile(file) self.guide_layer.SaveToFile(file) file.EndDocument() def load_AppendObject(self, layer): self.layers.append(layer) def load_Done(self): pass def load_Completed(self): if not self.layers: self.layers = [Layer(_("Layer 1"))] if self.active_layer is None: for layer in self.layers: if layer.CanSelect(): self.active_layer = layer break add_guide_layer = add_grid_layer = 1 for layer in self.layers: layer.SetDocument(self) if isinstance(layer, GuideLayer): self.guide_layer = layer add_guide_layer = 0 if isinstance(layer, GridLayer): self.snap_grid = layer add_grid_layer = 0 if add_guide_layer: self.layers.append(self.guide_layer) if add_grid_layer: self.layers.append(self.snap_grid) self.extract_pages() self.RearrangeLayers() def extract_pages(self): layers=self.getRegularLayers() if layers[0].is_Page: self.pages=[] for layer in layers: if layer.is_Page: page=[] self.pages.append(page) else: page.append(layer) pages=[]+self.pages for page in pages: if not len(page): self.pages.remove(page) else: self.pages=[] self.pages.append(layers) self.active_page=0 self.layers=[self.snap_grid] + self.pages[0] + self.getMasterLayers() + [self.guide_layer] self.active_layer=(self.pages[0])[0] # # Class MetaInfo # # Each document has an instance of this class as the variable # meta. The application object uses this variable to store various # data about the document, such as the name of the file it was # read from, the file type, etc. See skapp.py # class MetaInfo: pass class AbortTransactionError(SketchInternalError): pass SelectionMode = 0 EditMode = 1 class EditDocument(SketchDocument, QueueingPublisher): drag_mask = Button1Mask # canvas sometimes has the doc as current # object script_access = SketchDocument.script_access.copy() def __init__(self, create_layer = 0): SketchDocument.__init__(self, create_layer) QueueingPublisher.__init__(self) self.selection = SizeSelection() self.__init_undo() self.was_dragged = 0 self.meta = MetaInfo() self.hit_cache = None self.connector = Connector() self.init_transaction() self.init_clear() self.init_styles() self.init_after_handler() self.init_layout() def Destroy(self): self.undo = None self.destroy_styles() RemovePublisher(self) for layer in self.layers: layer.Destroy() self.layers = [] self.active_layer = None self.guide_layer = None self.snap_grid = None # make self.connector empty connector to remove circular refs # and to allow object to call document.connector.RemovePublisher # in their __del__ methods self.connector = Connector() self.selection = None self.transaction_undo = [] self.transaction_sel = [] def queue_layer(self, *args): if self.transaction: apply(self.queue_message, (LAYER,) + args) return (self.queue_layer, args) else: apply(self.issue, (LAYER,) + args) def queue_selection(self): self.queue_message(SELECTION) def queue_edited(self): # An EDITED message should probably indicate the type of edit, # i.e. whether properties changed, the geometry of objects # changed, etc.; hence the additional string argument which may # hold this information in the future self.queue_message(EDITED, '') return (self.queue_edited,) def Subscribe(self, channel, func, *args): Connect(self, channel, func, args) def Unsubscribe(self, channel, func, *args): Disconnect(self, channel, func, args) def init_after_handler(self): self.after_handlers = [] def AddAfterHandler(self, handler, args = (), depth = 0): handler = (depth, handler, args) try: self.after_handlers.remove(handler) except ValueError: pass self.after_handlers.append(handler) def call_after_handlers(self): if not self.after_handlers: return 0 while self.after_handlers: handlers = self.after_handlers handlers.sort() handlers.reverse() depth = handlers[0][0] count = 0 for d, handler, args in handlers: if d == depth: count = count + 1 else: break self.after_handlers = handlers[count:] handlers = handlers[:count] for d, handler, args in handlers: try: apply(handler, args) except: warn_tb(INTERNAL, "In after handler `%s'%s", handler, args) return 1 def init_clear(self): self.clear_rects = [] self.clear_all = 0 reset_clear = init_clear def AddClearRect(self, rect): self.clear_rects.append(rect) return (self.AddClearRect, rect) def view_redraw_all(self): self.clear_all = 1 return (self.view_redraw_all,) def issue_redraw(self): try: if self.clear_all: Issue(self, REDRAW, 1) else: Issue(self, REDRAW, 0, self.clear_rects) finally: self.clear_rects = [] self.clear_all = 0 def init_transaction(self): self.reset_transaction() def reset_transaction(self): self.transaction = 0 self.transaction_name = '' self.transaction_sel = [] self.transaction_undo = [] self.transaction_sel_ignore = 0 self.transaction_clear = None self.transaction_aborted = 0 self.transaction_cleanup = [] def cleanup_transaction(self): for handler, args in self.transaction_cleanup: try: apply(handler, args) except: warn_tb(INTERNAL, "in cleanup handler %s%s", handler, `args`) self.transaction_cleanup = [] def add_cleanup_handler(self, handler, *args): handler = (handler, args) try: self.transaction_cleanup.remove(handler) except ValueError: pass self.transaction_cleanup.append(handler) def begin_transaction(self, name = '', no_selection = 0, clear_selection_rect = 1): if self.transaction_aborted: raise AbortTransactionError if self.transaction == 0: if not no_selection: selinfo = self.selection.GetInfo()[:] if selinfo != self.transaction_sel: self.transaction_sel = selinfo self.transaction_sel_mode = self.selection.__class__ self.transaction_sel_ignore = no_selection self.transaction_name = name self.transaction_undo = [] if clear_selection_rect: if self.selection: self.transaction_clear = self.selection.bounding_rect else: self.transaction_clear = None elif not self.transaction_name: self.transaction_name = name self.transaction = self.transaction + 1 def end_transaction(self, issue = (), queue_edited = 0): self.transaction = self.transaction - 1 if self.transaction_aborted: # end an aborted transaction if self.transaction == 0: # undo the changes already done... undo = self.transaction_undo undo.reverse() map(Undo, undo) self.cleanup_transaction() self.reset_transaction() self.reset_clear() else: # a normal transaction if type(issue) == StringType: self.queue_message(issue) else: for channel in issue: self.queue_message(channel) if self.transaction == 0: # the outermost end_transaction # increase transaction flag temporarily because some # after handlers might call public methods that are # themselves transactions... self.transaction = 1 if self.call_after_handlers(): self.selection.ResetRectangle() self.transaction = 0 undo = CreateListUndo(self.transaction_undo) if undo is not NullUndo: undo = [undo] if self.transaction_clear is not None: undo.append(self.AddClearRect(self.transaction_clear)) if self.selection: self.selection.ResetRectangle() rect = self.selection.bounding_rect undo.append(self.AddClearRect(rect)) if queue_edited: undo.append(self.queue_edited()) undo = CreateListUndo(undo) if self.transaction_sel_ignore: self.__real_add_undo(self.transaction_name, undo) else: self.__real_add_undo(self.transaction_name, undo, self.transaction_sel, self.transaction_sel_mode) self.flush_message_queue() self.issue_redraw() self.cleanup_transaction() self.reset_transaction() self.reset_clear() elif self.transaction < 0: raise SketchInternalError('transaction < 0') def abort_transaction(self): self.transaction_aborted = 1 warn_tb(INTERNAL, "in transaction `%s'" % self.transaction_name) raise AbortTransactionError # public versions of the transaction methods BeginTransaction = begin_transaction AbortTransaction = abort_transaction def EndTransaction(self): self.end_transaction(queue_edited = 1) def Insert(self, object, undo_text = _("Create Object")): group_flag = 0 if isinstance(object, guide.GuideLine): self.add_guide_line(object) else: self.begin_transaction(undo_text, clear_selection_rect = 0) try: try: if type(object) == ListType: gobject = Group(object) else: gobject = object selected, undo = self.insert(object) self.add_undo(undo) self.add_undo(self.AddClearRect(gobject.bounding_rect)) self.__set_selection(selected, SelectSet) self.add_undo(self.remove_selected()) extracted_select=[] for info, object in self.selection.GetInfo(): objects=[] if type(object) == ListType: objects = object else: objects.append(object) select, undo_insert = self.insert(objects, at = info[1:], layer = info[0]) extracted_select+=select self.add_undo(undo_insert) self.__set_selection(extracted_select, SelectSet) except: self.abort_transaction() finally: self.end_transaction() def SelectPoint(self, p, device, type = SelectSet): # find object at point, and modify the current selection # according to type self.begin_transaction(clear_selection_rect = 0) try: try: if type == SelectSubobjects: path = self.selection.GetPath() else: path = () rect = device.HitRectAroundPoint(p) if self.hit_cache: cp, cdevice, hit = self.hit_cache self.hit_cache = None if p is cp and device is cdevice: selected = hit else: selected = self.selection_from_point(p, rect, device, path) else: selected = self.selection_from_point(p, rect, device, path) if type == SelectGuide: if selected and selected[-1].is_GuideLine: return selected[-1] return None elif selected: path, object = selected if self.layers[path[0]] is self.guide_layer: if object.is_GuideLine: # guide lines cannot be selected in the # ordinary way, but other objects on the # guide layer can. # pass ######################################################################## ######################################################################## selected = None self.__set_selection(selected, type) if self.IsEditMode(): object = self.CurrentObject() if object is not None and object.is_Text: self.SelectPointPart(p, device, SelectSet) except: self.abort_transaction() finally: self.end_transaction() return selected def SelectRect(self, rect, mode = SelectSet): # Find all objects contained in rect and modify the current # selection according to mode self.begin_transaction(clear_selection_rect = 0) try: try: self.hit_cache = None selected = self.selection_from_rect(rect) self.__set_selection(selected, mode) except: self.abort_transaction() finally: self.end_transaction() return selected def SelectRectPart(self, rect, mode = SelectSet): # Select the part of the CSO that lies in rect. Currently this # works only in edit mode. For a PolyBezier this means that all # nodes within rect are selected. if not self.IsEditMode(): raise SketchInternalError('SelectRectPart requires edit mode') self.begin_transaction(clear_selection_rect = 0) try: try: self.hit_cache = None self.selection.SelectRect(rect, mode) self.queue_selection() except: self.abort_transaction() finally: self.end_transaction() def SelectPointPart(self, p, device, mode = SelectSet): # Select the part of the current object under the point p. # Like SelectRectPart this only works in edit mode. self.begin_transaction(clear_selection_rect = 0) try: try: self.hit_cache = None rect = device.HitRectAroundPoint(p) self.selection.SelectPoint(p, rect, device, mode) if mode != SelectDrag: self.queue_selection() except: self.abort_transaction() finally: self.end_transaction() def SelectHandle(self, handle, mode = SelectSet): # Select the handle indicated by handle. This only works in edit # mode. self.begin_transaction(clear_selection_rect = 0) try: try: self.hit_cache = None self.selection.SelectHandle(handle, mode) if mode != SelectDrag: self.queue_selection() except: self.abort_transaction() finally: self.end_transaction() def SelectAll(self): # Select all objects that can currently be selected. # XXX should the objects in the guide layer also be selected by # this method? (currently they are) self.begin_transaction(clear_selection_rect = 0) try: try: sel_info = [] for layer_idx in range(len(self.layers)): sel = self.layers[layer_idx].SelectAll() if sel: sel = self.augment_sel_info(sel, layer_idx) sel_info = sel_info + sel self.__set_selection(sel_info, SelectSet) except: self.abort_transaction() finally: self.end_transaction() script_access['SelectAll'] = SCRIPT_GET def SelectNone(self): # Deselect all objects. self.begin_transaction(clear_selection_rect = 0) try: try: self.__set_selection(None, SelectSet) except: self.abort_transaction() finally: self.end_transaction() script_access['SelectNone'] = SCRIPT_GET def SelectObject(self, objects, mode = SelectSet): # Select the objects defined by OBJECTS. OBJECTS may be a single # GraphicsObject or a list of such objects. Modify the current # selection according to MODE. self.begin_transaction(clear_selection_rect = 0) try: try: if type(objects) != ListType: objects = [objects] selinfo = [] for object in objects: selinfo.append(object.SelectionInfo()) if selinfo: self.__set_selection(selinfo, mode) else: self.__set_selection(None, SelectSet) except: self.abort_transaction() finally: self.end_transaction() #script_access['SelectObject'] = SCRIPT_GET def select_first_in_layer(self, idx = 0): for layer in self.layers[idx:]: if layer.CanSelect() and not layer.is_SpecialLayer: object = layer.SelectFirstChild() if object is not None: return object def SelectNextObject(self): # If exactly one object is selected select its next higher # sibling. If there is no next sibling and its parent is a # layer, select the first object in the next higher layer that # allows selections. # # If more than one object is currently selected, deselect all # but the the highest of them. self.begin_transaction(clear_selection_rect = 0) try: try: info = self.selection.GetInfo() if len(info) > 1: self.__set_selection(info[-1], SelectSet) elif info: path, object = info[0] parent = object.parent object = parent.SelectNextChild(object, path[-1]) if object is None and parent.is_Layer: idx = self.layers.index(parent) object = self.select_first_in_layer(idx + 1) if object is not None: self.SelectObject(object) else: object = self.select_first_in_layer() if object is not None: self.SelectObject(object) except: self.abort_transaction() finally: self.end_transaction() script_access['SelectNextObject'] = SCRIPT_GET def select_last_in_layer(self, idx): if idx < 0: return layers = self.layers[:idx + 1] layers.reverse() for layer in layers: if layer.CanSelect() and not layer.is_SpecialLayer: object = layer.SelectLastChild() if object is not None: return object def SelectPreviousObject(self): # If exactly one object is selected select its next lower # sibling. If there is no lower sibling and its parent is a # layer, select the last object in the next lower layer that # allows selections. # # If more than one object is currently selected, deselect all # but the the lowest of them. self.begin_transaction(clear_selection_rect = 0) try: try: info = self.selection.GetInfo() if len(info) > 1: self.__set_selection(info[0], SelectSet) elif info: path, object = info[0] parent = object.parent object = parent.SelectPreviousChild(object, path[-1]) if object is None and parent.is_Layer: idx = self.layers.index(parent) object = self.select_last_in_layer(idx - 1) if object is not None: self.SelectObject(object) else: object = self.select_last_in_layer(len(self.layers)) if object is not None: self.SelectObject(object) except: self.abort_transaction() finally: self.end_transaction() script_access['SelectPreviousObject'] = SCRIPT_GET def SelectFirstChild(self): # If exactly one object is selected and this object is a # compound object, select its first (lowest) child. The first # child is the object returned by the compound object's method # SelectFirstChild. If that method returns none, do nothing. self.begin_transaction(clear_selection_rect = 0) try: try: objects = self.selection.GetObjects() if len(objects) == 1: object = objects[0] if object.is_Compound: object = object.SelectFirstChild() if object is not None: self.SelectObject(object) except: self.abort_transaction() finally: self.end_transaction() script_access['SelectFirstChild'] = SCRIPT_GET def SelectParent(self): # Select the parent of the currently selected object(s). self.begin_transaction(clear_selection_rect = 0) try: try: if len(self.selection) > 1: path = selinfo.common_prefix(self.selection.GetInfo()) if len(path) > 1: object = self[path] self.SelectObject(object) elif len(self.selection) == 1: object = self.selection.GetObjects()[0].parent if not object.is_Layer: self.SelectObject(object) except: self.abort_transaction() finally: self.end_transaction() script_access['SelectParent'] = SCRIPT_GET def DeselectObject(self, object): # Deselect the object OBJECT. # XXX: for large selections this can be very slow. selected = self.selection.GetObjects() try: index = selected.index(object) except ValueError: return info = self.selection.GetInfo() del info[index] self.__set_selection(info, SelectSet) def __set_selection(self, selected, type): # Modify the current selection. SELECTED is a list of selection # info describing the new selection, TYPE indicates how the # current selection is modified: # # type Meaning # SelectSet Replace the old selection by the new one # SelectSubtract Subtract the new selection from the old one # SelectAdd Add the new selection to the old one. # SelectSubobjects like SelectSet here changed = 0 if type == SelectAdd: if selected: changed = self.selection.Add(selected) elif type == SelectSubtract: if selected: changed = self.selection.Subtract(selected) elif type == SelectGuide: if selected: pass else: # type is SelectSet or SelectSubobjects # set the selection. make a size selection if necessary if self.selection.__class__ == TrafoSelection: self.selection = SizeSelection() changed = 1 changed = self.selection.SetSelection(selected) or changed if changed: self.queue_selection() def SetMode(self, mode): self.begin_transaction(clear_selection_rect = 0) try: try: if mode == SelectionMode: self.selection = SizeSelection(self.selection) else: self.selection = EditSelection(self.selection) except: self.abort_transaction() finally: self.end_transaction(issue = (SELECTION, MODE)) def Mode(self): if self.selection.__class__ == EditSelection: return EditMode return SelectionMode script_access['Mode'] = SCRIPT_GET def IsSelectionMode(self): return self.Mode() == SelectionMode script_access['IsSelectionMode'] = SCRIPT_GET def IsEditMode(self): return self.Mode() == EditMode script_access['IsEditMode'] = SCRIPT_GET def SelectionHit(self, p, device, test_all = 1): # Return true, if the point P hits the currently selected # objects. # # If test_all is true (the default), find the object that would # be selected by SelectPoint and return true if it or one of its # ancestors is contained in the current selection and false # otherwise. # # If test_all is false, just test the currently selected objects. rect = device.HitRectAroundPoint(p) if len(self.selection) < 10 or not test_all: selection_hit = self.selection.Hit(p, rect, device) if not test_all or not selection_hit: return selection_hit if test_all: path = self.selection.GetPath() if len(path) > 2: path = path[:-1] else: path = () hit = self.selection_from_point(p, rect, device, path) self.hit_cache = (p, device, hit) while hit: if hit in self.selection.GetInfo(): return 1 hit = selinfo.get_parent(hit) #self.hit_cache = None return 0 def GetSelectionHandles(self): if self.selection: return self.selection.GetHandles() else: return [] # # Get information about the selected objects # def HasSelection(self): # Return true, if one or more objects are selected return len(self.selection) script_access['HasSelection'] = SCRIPT_GET def CountSelected(self): # Return the number of currently selected objects return len(self.selection) script_access['CountSelected'] = SCRIPT_GET def SelectionInfoText(self): # Return a string describing the selected object(s) return self.selection.InfoText() script_access['SelectionInfoText'] = SCRIPT_GET def CurrentInfoText(self): return self.selection.CurrentInfoText() def SelectionBoundingRect(self): # Return the bounding rect of the current selection return self.selection.bounding_rect script_access['SelectionBoundingRect'] = SCRIPT_GET def CurrentObject(self): # If exactly one object is selected return that, None instead. if len(self.selection) == 1: return self.selection.GetObjects()[0] return None script_access['CurrentObject'] = SCRIPT_OBJECT def SelectedObjects(self): # Return the selected objects as a list. They are listed in the # order in which they are drawn. return self.selection.GetObjects() script_access['SelectedObjects'] = SCRIPT_OBJECTLIST def CurrentProperties(self): # Return the properties of the current object if exactly one # object is selected. Return EmptyProperties otherwise. if self.selection: if len(self.selection) > 1: return EmptyProperties return self.selection.GetInfo()[0][-1].Properties() return EmptyProperties script_access['CurrentProperties'] = SCRIPT_OBJECT def CurrentFillColor(self): # Return the fill color of the current object if exactly one # object is selected and that object has a solid fill. Return # None otherwise. if len(self.selection) == 1: properties = self.selection.GetInfo()[0][-1].Properties() try: return properties.fill_pattern.Color() except AttributeError: pass return None script_access['CurrentFillColor'] = SCRIPT_GET def PickObject(self, device, point, selectable = 0): # Return the object that is hit by a click at POINT. The object # is not selected and should not be modified by the caller. # # If selectable is false, this function descends into compound # objects that are normally selected as a whole when one of # their children is hit. If selectable is true, the search is # done as for a normal selection. # # This method is intended to be used to # let the user click on the drawing and extract properties from # the indicated object. The fill and line dialogs use this # indirectly (through the canvas object's PickObject) for their # 'Update From...' button. # # XXX should this be implemented by calling WalkHierarchy # instead of requiring a special PickObject method in each # compound? Unlike the normal hit-test, this method is not that # time critical and WalkHierarchy is sufficiently fast for most # purposes (see extract_snap_points in the canvas). # WalkHierarchy would have to be able to traverse the hierarchy # top down and not just bottom up. object = None rect = device.HitRectAroundPoint(point) if not selectable: layers = self.layers[:] layers.reverse() for layer in layers: object = layer.PickObject(point, rect, device) if object is not None: break else: selected = self.selection_from_point(point, rect, device) if selected: object = selected[-1] return object def PickActiveObject(self, device, p): # return the object under point if it's selected or a guide # line. None otherwise. rect = device.HitRectAroundPoint(p) path = self.selection.GetPath() if len(path) > 2: path = path[:-1] else: path = () hit = self.selection_from_point(p, rect, device, path) #self.hit_cache = (p, device, hit) if hit: if not hit[-1].is_GuideLine: while hit: if hit in self.selection.GetInfo(): hit = hit[-1] break hit = selinfo.get_parent(hit) else: hit = hit[-1] return hit # # # def WalkHierarchy(self, func, printable = 1, visible = 1, all = 0): # XXX make the selection of layers more versatile for layer in self.layers: if (all or printable and layer.Printable() or visible and layer.Visible()): layer.WalkHierarchy(func) # # # def ButtonDown(self, p, button, state): self.was_dragged = 0 self.old_change_rect = self.selection.ChangeRect() result = self.selection.ButtonDown(p, button, state) return result def MouseMove(self, p, state): self.was_dragged = 1 self.selection.MouseMove(p, state) def ButtonUp(self, p, button, state): self.begin_transaction(clear_selection_rect = 0) try: try: if self.was_dragged: undo_text, undo_edit \ = self.selection.ButtonUp(p, button, state) if undo_edit is not None and undo_edit != NullUndo: self.add_undo(undo_text, undo_edit) uc1 = self.AddClearRect(self.old_change_rect) uc2 = self.AddClearRect(self.selection.ChangeRect()) self.add_undo(uc1, uc2) self.add_undo(self.queue_edited()) else: # the user probably just moved the rotation # center point. The canvas has to update the # handles self.queue_selection() else: self.selection.ButtonUp(p, button, state, forget_trafo = 1) self.ToggleSelectionBehaviour() except: self.abort_transaction() finally: self.end_transaction() def ToggleSelectionBehaviour(self): self.begin_transaction(clear_selection_rect = 0) try: try: if self.selection.__class__ == SizeSelection: self.selection = TrafoSelection(self.selection) elif self.selection.__class__ == TrafoSelection: self.selection = SizeSelection(self.selection) self.queue_selection() except: self.abort_transaction() finally: self.end_transaction() def DrawDragged(self, device, partially = 0): self.selection.DrawDragged(device, partially) def Hide(self, device, partially = 0): self.selection.Hide(device, partially) def Show(self, device, partially = 0): self.selection.Show(device, partially) def ChangeRect(self): return self.selection.ChangeRect() # # The undo mechanism # def __init_undo(self): self.undo = UndoRedo() def CanUndo(self): return self.undo.CanUndo() script_access['CanUndo'] = SCRIPT_GET def CanRedo(self): return self.undo.CanRedo() script_access['CanRedo'] = SCRIPT_GET def Undo(self): if self.undo.CanUndo(): self.begin_transaction(clear_selection_rect = 0) try: try: self.undo.Undo() except: self.abort_transaction() finally: self.end_transaction(issue = UNDO) script_access['Undo'] = SCRIPT_GET def add_undo(self, *infos): # Add undoinfo for the current transaction. should not be called # when not in a transaction. if infos: if type(infos[0]) == StringType: if not self.transaction_name: self.transaction_name = infos[0] infos = infos[1:] if not infos: return for info in infos: if type(info) == ListType: info = CreateListUndo(info) else: if type(info[0]) == StringType: if __debug__: pdebug(None, 'add_undo: info contains text') info = info[1:] self.transaction_undo.append(info) # public version of add_undo. to be called between calls to # BeginTransaction and EndTransaction/AbortTransaction AddUndo = add_undo def __undo_set_sel(self, selclass, selinfo, redo_class, redo_info): old_class = self.selection.__class__ if old_class != selclass: self.selection = selclass(selinfo) self.queue_message(MODE) else: # keep the same selection object to avoid creating a new # editor object in EditMode self.selection.SetSelection(selinfo) self.queue_selection() return (self.__undo_set_sel, redo_class, redo_info, selclass, selinfo) def __real_add_undo(self, text, undo, selinfo = None, selclass = None): if undo is not NullUndo: if selinfo is not None: new_class = self.selection.__class__ new_info = self.selection.GetInfo()[:] if new_info == selinfo: # make both lists identical new_info = selinfo undo_sel = (self.__undo_set_sel, selclass, selinfo, new_class, new_info) info = (text, UndoAfter, undo_sel, undo) else: info = (text, undo) self.undo.AddUndo(info) self.queue_message(UNDO) def Redo(self): if self.undo.CanRedo(): self.begin_transaction(clear_selection_rect = 0) try: try: self.undo.Redo() except: self.abort_transaction() finally: self.end_transaction(issue = UNDO) script_access['Redo'] = SCRIPT_GET def ResetUndo(self): self.begin_transaction(clear_selection_rect = 0) try: try: self.undo.Reset() except: self.abort_transaction() finally: self.end_transaction(issue = UNDO) script_access['ResetUndo'] = SCRIPT_GET def UndoMenuText(self): return self.undo.UndoText() script_access['UndoMenuText'] = SCRIPT_GET def RedoMenuText(self): return self.undo.RedoText() script_access['RedoMenuText'] = SCRIPT_GET def SetUndoLimit(self, limit): self.begin_transaction(clear_selection_rect = 0) try: try: self.undo.SetUndoLimit(limit) except: self.abort_transaction() finally: self.end_transaction(issue = UNDO) script_access['SetUndoLimit'] = SCRIPT_GET def WasEdited(self): # return true if document has changed since last save if self.undo.UndoCount(): return 1 return 0 script_access['WasEdited'] = SCRIPT_GET def ClearEdited(self): self.undo.ResetUndoCount() self.issue(UNDO) # # def apply_to_selected(self, undo_text, func): if self.selection: self.begin_transaction(undo_text) try: try: self.add_undo(self.selection.ForAllUndo(func)) self.queue_selection() except: self.abort_transaction() finally: self.end_transaction() def AddStyle(self, style): if type(style) == StringType: style = self.GetDynamicStyle(style) self.apply_to_selected(_("Add Style"), lambda o, style = style: o.AddStyle(style)) def SetLineColor(self, color): # Set the line color of the currently selected objects. # XXX this method should be removed in favour of the more # generic SetProperties. self.SetProperties(line_pattern = SolidPattern(color), if_type_present = 0) def SetProperties(self, **kw): self.apply_to_selected(_("Set Properties"), lambda o, kw=kw: apply(o.SetProperties, (), kw)) def SetStyle(self, style): if type(style) == StringType: style = self.get_dynamic_style(style) self.AddStyle(style) # # Deleting and rearranging objects... # def remove_objects(self, infolist): split = selinfo.list_to_tree(infolist) undo = [] try: for layer, infolist in split: undo.append(self.layers[layer].RemoveObjects(infolist)) return CreateListUndo(undo) except: Undo(CreateListUndo(undo)) raise def remove_selected(self): return self.remove_objects(self.selection.GetInfo()) def RemoveSelected(self): # Remove all selected objects. After successful completion, the # selection will be empty. if self.selection: self.begin_transaction(_("Delete")) try: try: self.add_undo(self.remove_selected()) self.__set_selection(None, SelectSet) self.add_undo(self.queue_edited()) except: self.abort_transaction() finally: self.end_transaction() def __call_layer_method_sel(self, undotext, methodname, *args): if not self.selection: return self.begin_transaction(undotext) try: try: split = selinfo.list_to_tree(self.selection.GetInfo()) edited = 0 selection = [] for layer, infolist in split: method = getattr(self.layers[layer], methodname) sel, undo = apply(method, (infolist,) + args) if undo is not NullUndo: self.add_undo(undo) edited = 1 selection = selection + self.augment_sel_info(sel, layer) self.__set_selection(selection, SelectSet) if edited: self.add_undo(self.queue_edited()) except: self.abort_transaction() finally: self.end_transaction() def MoveSelectedToTop(self): self.__call_layer_method_sel(_("Move To Top"), 'MoveObjectsToTop') def MoveSelectedToBottom(self): self.__call_layer_method_sel(_("Move To Bottom"),'MoveObjectsToBottom') def MoveSelectionDown(self): self.__call_layer_method_sel(_("Lower"), 'MoveObjectsDown') def MoveSelectionUp(self): self.__call_layer_method_sel(_("Raise"), 'MoveObjectsUp') def MoveSelectionToLayer(self, layer): if self.selection: self.begin_transaction(_("Move Selection to `%s'") % self.layers[layer].Name()) try: try: # remove the objects from the document... self.add_undo(self.remove_selected()) # ... and insert them a the end of the layer objects = self.selection.GetObjects() select, undo_insert = self.insert(objects, layer = layer) self.add_undo(undo_insert) self.__set_selection(select, SelectSet) self.add_undo(self.queue_edited()) except: self.abort_transaction() finally: self.end_transaction() # # Cut/Copy # def copy_objects(self, objects): copies = [] for obj in objects: copies.append(obj.Duplicate()) if len(copies) > 1: for copy in copies: copy.UntieFromDocument() copy.SetDocument(None) else: copy = copies[0] # This is ugly: Special case for internal path text objects. # If the internal path text object is the only selected # object, turn the copy into a normal simple text object. # Thsi avoids some of the problems when you "Copy" an # internal path text. import text if copy.is_PathTextText: properties = copy.Properties().Duplicate() copy = text.SimpleText(text = copy.Text(), properties = properties) copy.UntieFromDocument() copy.SetDocument(None) copies[0]=copy return copies def CopyForClipboard(self): if self.selection: return self.copy_objects(self.selection.GetObjects()) def CutForClipboard(self): result = None if self.selection: self.begin_transaction(_("Cut")) try: try: objects = self.selection.GetObjects() result = self.copy_objects(objects) self.add_undo(self.remove_selected()) self.__set_selection(None, SelectSet) self.add_undo(self.queue_edited()) except: result = None self.abort_transaction() finally: self.end_transaction() return result # # Duplicate # def ApplyToDuplicate(self): offset = Point(0,0) self.__call_layer_method_sel(_("Duplicate"), 'DuplicateObjects', offset) def DuplicateSelected(self, offset = None): if offset is None: offset = Point(config.preferences.duplicate_offset) self.__call_layer_method_sel(_("Duplicate"), 'DuplicateObjects', offset) # # Group # def group_selected(self, title, creator): self.begin_transaction(title) try: try: self.add_undo(self.remove_selected()) objects = self.selection.GetObjects() group = creator(objects) parent = selinfo.common_prefix(self.selection.GetInfo()) if parent: layer = parent[0] at = parent[1:] else: layer = None at = None select, undo_insert = self.insert(group, at = at, layer =layer) self.add_undo(undo_insert) self.__set_selection(select, SelectSet) except: self.abort_transaction() finally: self.end_transaction() def CanGroup(self): return len(self.selection) > 1 def GroupSelected(self): if self.CanGroup(): self.group_selected(_("Create Group"), Group) def CanUngroup(self): infos = self.selection.GetInfo() return len(infos) == 1 and infos[0][-1].is_Group def CanUngroupAll(self): infos = self.selection.GetInfo() isGroup=0 if len(infos) > 0: for i in range(len(infos)): isGroup+=infos[i][-1].is_Group #if len(infos) > 0: #isGroup=infos[0][-1].is_Group #for i in range(len(infos)): #if infos[i][-1].is_Group: #isGroup=infos[i][-1].is_Group return len(infos) > 0 and isGroup def UngroupSelected(self): if self.CanUngroup(): self.begin_transaction(_("Ungroup")) try: try: self.add_undo(self.remove_selected()) info, group = self.selection.GetInfo()[0] objects = group.Ungroup() select, undo_insert = self.insert(objects, at = info[1:], layer = info[0]) self.add_undo(undo_insert) self.__set_selection(select, SelectSet) except: self.abort_transaction() finally: self.end_transaction() def ExtractNonGroup(self, object): objects=[] if object.is_Group: for item in object.objects: objects+=self.ExtractNonGroup(item) else: objects.append(object) return objects def UngroupAllSelected(self): if self.CanUngroupAll(): self.begin_transaction(_("Ungroup All")) try: try: self.add_undo(self.remove_selected()) extracted_select=[] for info, object in self.selection.GetInfo(): objects = self.ExtractNonGroup(object) select, undo_insert = self.insert(objects, at = info[1:], layer = info[0]) extracted_select+=select self.add_undo(undo_insert) self.__set_selection(extracted_select, SelectSet) except: self.abort_transaction() finally: self.end_transaction() def ModifyAndCopy(self): if self.selection: copies=self.copy_objects(self.selection.GetObjects()) self.Undo() self.Insert(copies, undo_text=_("Modify&Copy")) def CanCreateMaskGroup(self): infos = self.selection.GetInfo() return len(infos) > 1 and infos[-1][-1].is_clip def CreateMaskGroup(self): if self.CanCreateMaskGroup(): self.begin_transaction(_("Create Mask Group")) try: try: import maskgroup self.add_undo(self.remove_selected()) objects = self.selection.GetObjects() if config.preferences.topmost_is_mask: mask = objects[-1] del objects[-1] objects.insert(0, mask) group = maskgroup.MaskGroup(objects) parent = selinfo.common_prefix(self.selection.GetInfo()) if parent: layer = parent[0] at = parent[1:] else: layer = None at = None select, undo_insert = self.insert(group, at = at, layer = layer) self.add_undo(undo_insert) self.__set_selection(select, SelectSet) except: self.abort_transaction() finally: self.end_transaction() # # Transform, Translate, ... # def TransformSelected(self, trafo, undo_text = _("Transform")): self.apply_to_selected(undo_text, lambda o, t = trafo: o.Transform(t)) def TranslateSelected(self, offset, undo_text = _("Translate")): self.apply_to_selected(undo_text, lambda o, v = offset: o.Translate(v)) def RemoveTransformation(self): self.apply_to_selected(_("Remove Transformation"), lambda o: o.RemoveTransformation()) # # Align, Flip, ... # # XXX These functions could be implemented outside of the document. # (Maybe by command plugins or scripts?) # def AlignSelection(self, x, y, reference = 'selection'): if self.selection and (x or y): self.begin_transaction(_("Align Objects")) try: try: add_undo = self.add_undo objects = self.selection.GetObjects() if reference == 'page': br = self.PageRect() elif reference == 'lowermost': br = objects[0].coord_rect else: br = self.selection.coord_rect for obj in objects: r = obj.coord_rect xoff = yoff = 0 if x == 1: xoff = br.left - r.left elif x == 3: xoff = br.right - r.right elif x == 2: xoff = (br.left + br.right - r.left - r.right) / 2 if y == 1: yoff = br.top - r.top elif y == 3: yoff = br.bottom - r.bottom elif y == 2: yoff = (br.top + br.bottom - r.top - r.bottom) / 2 add_undo(obj.Translate(Point(xoff, yoff))) add_undo(self.queue_edited()) except: self.abort_transaction() finally: self.end_transaction() def AbutHorizontal(self): if len(self.selection) > 1: self.begin_transaction(_("Abut Horizontal")) try: try: pos = [] for obj in self.selection.GetObjects(): rect = obj.coord_rect pos.append((rect.left, rect.top, rect.right - rect.left, obj)) pos.sort() undo = [] start, top, width, ob = pos[0] next = start + width for left, top, width, obj in pos[1:]: off = Point(next - left, 0) self.add_undo(obj.Translate(off)) next = next + width self.add_undo(self.queue_edited()) except: self.abort_transaction() finally: self.end_transaction() def AbutVertical(self): if len(self.selection) > 1: self.begin_transaction(_("Abut Vertical")) try: try: pos = [] for obj in self.selection.GetObjects(): rect = obj.coord_rect pos.append((rect.top, -rect.left, rect.top - rect.bottom, obj)) pos.sort() pos.reverse() undo = [] start, left, height, ob = pos[0] next = start - height for top, left, height, obj in pos[1:]: off = Point(0, next - top) self.add_undo(obj.Translate(off)) next = next - height self.add_undo(self.queue_edited()) except: self.abort_transaction() finally: self.end_transaction() def FlipSelected(self, horizontal = 0, vertical = 0): if self.selection and (horizontal or vertical): self.begin_transaction() try: try: rect = self.selection.coord_rect if horizontal: xoff = rect.left + rect.right factx = -1 text = _("Flip Horizontal") else: xoff = 0 factx = 1 if vertical: yoff = rect.top + rect.bottom facty = -1 text = _("Flip Vertical") else: yoff = 0 facty = 1 if horizontal and vertical: text = _("Flip Both") trafo = Trafo(factx, 0, 0, facty, xoff, yoff) self.TransformSelected(trafo, text) except: self.abort_transaction() finally: self.end_transaction() def RotateSelected(self, angle): if self.selection: self.begin_transaction() try: try: cnt = self.selection.coord_rect.center() text = _("Rotation") angle=angle*pi/180 trafo = Rotation(angle, cnt) self.TransformSelected(trafo, text) except: self.abort_transaction() finally: self.end_transaction() def HandleMoveSelected(self, h, v): val=config.preferences.handle_jump self.MoveSelected(h*val, v*val) def MoveSelected(self, h, v): if self.selection: self.begin_transaction() try: try: #cnt = self.selection.coord_rect.center() text = _("Move") #angle=angle*pi/180 trafo = Translation(h, v) self.TransformSelected(trafo, text) except: self.abort_transaction() finally: self.end_transaction() def MoveAndCopy(self, h, v, *args): if self.selection: self.begin_transaction() try: try: text = _("Move&Copy") methodname='DuplicateObjects' split = selinfo.list_to_tree(self.selection.GetInfo()) edited = 0 selection = [] for layer, infolist in split: method = getattr(self.layers[layer], methodname) sel, undo = apply(method, (infolist,) + args) if undo is not NullUndo: self.add_undo(undo) edited = 1 selection = selection + self.augment_sel_info(sel, layer) self.__set_selection(selection, SelectSet) if edited: self.add_undo(self.queue_edited()) trafo = Translation(h, v) self.TransformSelected(trafo, text) except: self.abort_transaction() finally: self.end_transaction() def ScaleSelected(self, h, v): if self.selection: self.begin_transaction() try: try: br=self.selection.coord_rect hor_sel=br.right - br.left ver_sel=br.top - br.bottom cnt_x=hor_sel/2+br.left cnt_y=ver_sel/2+br.bottom text = _("Scale") trafo = Trafo(h, 0, 0, v, cnt_x-cnt_x*h, cnt_y-cnt_y*v) self.TransformSelected(trafo, text) except: self.abort_transaction() finally: self.end_transaction() def CallObjectMethod(self, aclass, description, methodname, *args): self.begin_transaction(description) try: try: undo = self.selection.CallObjectMethod(aclass, methodname, args) if undo != NullUndo: self.add_undo(undo) self.add_undo(self.queue_edited()) # force recomputation of selections rects: self.selection.ResetRectangle() else: # in case the handles have to be updated self.queue_selection() except: self.abort_transaction() finally: self.end_transaction() def GetObjectMethod(self, aclass, method): return self.selection.GetObjectMethod(aclass, method) def CurrentObjectCompatible(self, aclass): obj = self.CurrentObject() if obj is not None: if aclass.is_Editor: return isinstance(obj, aclass.EditedClass) else: return isinstance(obj, aclass) return 0 # XXX the following methods for blend groups, path text, clones and # bezier objects should perhaps be implemented in their respective # modules (and then somehow grafted onto the document class?) def CanBlend(self): info = self.selection.GetInfo() if len(info) == 2: path1, obj1 = info[0] path2, obj2 = info[1] if len(path1) == len(path2) + 1: return obj1.parent.is_Blend and 2 if len(path1) + 1 == len(path2): return obj2.parent.is_Blend and 2 return len(path1) == len(path2) return 0 def Blend(self, steps): info = self.selection.GetInfo() path1, obj1 = info[0] path2, obj2 = info[1] if len(path1) == len(path2) + 1: if obj1.parent.is_Blend: del info[0] else: return elif len(path1) + 1 == len(path2): if obj2.parent.is_Blend: del info[1] else: return elif len(path1) != len(path2): return if steps >= 2: import blendgroup, blend self.begin_transaction(_("Blend")) try: try: self.add_undo(self.remove_objects(info)) try: blendgrp, undo = blendgroup.CreateBlendGroup(obj1,obj2, steps) self.add_undo(undo) except blend.MismatchError: warn(USER, _("I can't blend the selected objects")) # XXX: is there some other solution?: raise if len(info) == 2: select, undo_insert = self.insert(blendgrp, at = path1[1:], layer = path1[0]) self.add_undo(undo_insert) self.__set_selection(select, SelectSet) else: self.SelectObject(blendgrp) self.add_undo(self.queue_edited()) except: self.abort_transaction() finally: self.end_transaction() def CanCancelBlend(self): info = self.selection.GetInfo() return len(info) == 1 and info[0][-1].is_Blend def CancelBlend(self): if self.CanCancelBlend(): self.begin_transaction(_("Cancel Blend")) try: try: info = self.selection.GetInfo()[0] self.add_undo(self.remove_selected()) group = info[-1] objs = group.CancelEffect() info = info[0] layer = info[0] at = info[1:] select, undo_insert = self.insert(objs, at = at, layer = layer) self.add_undo(undo_insert) self.__set_selection(select, SelectSet) self.add_undo(self.queue_edited()) except: self.abort_transaction() finally: self.end_transaction() # # def CanCreatePathText(self): return CanCreatePathText(self.selection.GetObjects()) def CreatePathText(self): if self.CanCreatePathText(): self.begin_transaction(_("Create Path Text")) try: try: self.add_undo(self.remove_selected()) object = CreatePathText(self.selection.GetObjects()) select, undo_insert = self.insert(object) self.add_undo(undo_insert) self.__set_selection(select, SelectSet) self.add_undo(self.queue_edited()) except: self.abort_transaction() finally: self.end_transaction() # # Clone (under construction...) # def CanCreateClone(self): if len(self.selection) == 1: obj = self.selection.GetObjects()[0] return not obj.is_Compound return 0 def CreateClone(self): if self.CanCreateClone(): self.begin_transaction(_("Create Clone")) try: try: from clone import CreateClone object = self.selection.GetObjects()[0] clone, undo_clone = CreateClone(object) self.add_undo(undo_clone) select, undo_insert = self.insert(clone) self.add_undo(undo_insert) self.__set_selection(select, SelectSet) self.add_undo(self.queue_edited()) except: self.abort_transaction() finally: self.end_transaction() # # Bezier Curves # def CanCombineBeziers(self): if len(self.selection) > 1: can = 1 for obj in self.selection.GetObjects(): can = can and obj.is_Bezier return can return 0 def CombineBeziers(self): if self.CanCombineBeziers(): self.begin_transaction(_("Combine Beziers")) try: try: self.add_undo(self.remove_selected()) objects = self.selection.GetObjects() combined = CombineBeziers(objects) select, undo_insert = self.insert(combined) self.add_undo(undo_insert) self.__set_selection(select, SelectSet) self.add_undo(self.queue_edited()) except: self.abort_transaction() finally: self.end_transaction() def CanSplitBeziers(self): return len(self.selection) == 1 \ and self.selection.GetObjects()[0].is_Bezier def SplitBeziers(self): if self.CanSplitBeziers(): self.begin_transaction(_("Split Beziers")) try: try: self.add_undo(self.remove_selected()) info, bezier = self.selection.GetInfo()[0] objects = bezier.PathsAsObjects() select, undo_insert = self.insert(objects, at = info[1:], layer =info[0]) self.add_undo(undo_insert) self.__set_selection(select, SelectSet) self.add_undo(self.queue_edited()) except: self.abort_transaction() finally: self.end_transaction() def CanConvertToCurve(self): check_crv = 0 if len(self.selection) >0: check_crv = 1 # for a in range(len(self.selection)): # if self.selection.GetObjects()[a].is_curve: # pass # else: # check_crv = 0 return check_crv # return len(self.selection) == 1 \ #and self.selection.GetObjects()[0].is_curve def ConvertToCurve(self): if self.CanConvertToCurve(): self.begin_transaction(_("Convert To Curve")) try: try: selection = [] edited = 0 for info, object in self.selection.GetInfo(): if object.is_curve: if object.is_Bezier: selection.append((info, object)) else: bezier = object.AsBezier() parent = object.parent self.add_undo(parent.ReplaceChild(object, bezier)) selection.append((info, bezier)) edited = 1 else: pass self.__set_selection(selection, SelectSet) if edited: self.add_undo(self.queue_edited()) except: self.abort_transaction() finally: self.end_transaction() # # PAGES MANAGMENT # ############ def CanGoToPage(self): return len(self.pages)>1 def GoToPage(self, index=0): self.begin_transaction(_("Go to page"),clear_selection_rect = 0) try: try: current_page=self.active_page if self.CanUndo(): self.add_undo((self._return_to_page, current_page)) self.setActivePage(index) except: self.abort_transaction() finally: self.end_transaction() self.issue(PAGE) def _return_to_page(self, index): current_page=self.active_page self.setActivePage(index) self.issue(PAGE) return (self._return_to_page, current_page) ############ def InsertPages(self, number=1, index=0, is_before=0): if number>1: self.begin_transaction(_("Insert Pages"), clear_selection_rect = 0) else: self.begin_transaction(_("Insert Page"), clear_selection_rect = 0) try: try: for_del=abs(is_before-1) self.add_undo((self._delete_pages,number,index,for_del)) self.insert_pages(number,index,is_before) except: self.abort_transaction() finally: self.end_transaction() self.issue(PAGE) def _insert_pages(self,number,index,is_before): self.insert_pages(number,index,is_before) self.issue(PAGE) return (self._delete_pages,number,index,is_before) def _delete_pages(self, number=1, index=0,is_before=0): self.delete_pages(number,index,is_before) self.issue(PAGE) return (self._insert_pages,number,index,is_before) ############ def CanDeletePage(self,index): return 0 <= index < len(self.pages) and len(self.pages) > 1 def CanBePageDeleting(self): return len(self.pages) > 1 def DeletePage(self, index=0): if self.CanDeletePage(index): self.begin_transaction(_("Delete Page"), clear_selection_rect = 0) try: try: self.add_undo((self._insert_page,index,self.pages[index])) self.delete_page(index) except: self.abort_transaction() finally: self.end_transaction() self.issue(PAGE) def _insert_page(self,index,page): current_page = self.pages[self.active_page] self.pages.insert(index,page) self.active_page = self.pages.index(current_page) self.SelectNone() self.issue(PAGE) return (self._delete_page,index,page) def _delete_page(self,index,page): current_page = self.pages[self.active_page] self.pages.remove(page) self.active_page = self.pages.index(current_page) self.SelectNone() self.issue(PAGE) return (self._insert_page,index,page) # # LAYERS METHODS # def Layers(self): return self.layers[:] def NumLayers(self): return len(self.layers) def ActiveLayer(self): return self.active_layer def ActiveLayerIdx(self): if self.active_layer is None: return None return self.layers.index(self.active_layer) def SetActiveLayer(self, idx): if type(idx) == IntType: layer = self.layers[idx] else: layer = idx if not layer.Locked(): self.active_layer = layer self.queue_layer(LAYER_ACTIVE) def LayerIndex(self, layer): return self.layers.index(layer) def update_active_layer(self): if self.active_layer is not None and self.active_layer.CanSelect(): return self.find_active_layer() def find_active_layer(self, idx = None): if idx is not None: layer = self.layers[idx] if layer.CanSelect(): self.SetActiveLayer(idx) return for layer in self.layers: if layer.CanSelect(): self.SetActiveLayer(layer) return self.active_layer = None self.queue_layer(LAYER_ACTIVE) def deselect_layer(self, layer_idx): # Deselect all objects in layer given by layer_idx # Called when a layer is deleted or becomes locked sel = selinfo.list_to_tree(self.selection.GetInfo()) for idx, info in sel: if idx == layer_idx: self.__set_selection(selinfo.tree_to_list([(idx, info)]), SelectSubtract) def SelectLayer(self, layer_idx, mode = SelectSet): # Select all children of the layer given by layer_idx self.begin_transaction(_("Select Layer"), clear_selection_rect = 0) try: try: layer = self.layers[layer_idx] info = self.augment_sel_info(layer.SelectAll(), layer_idx) self.__set_selection(info, mode) except: self.abort_transaction() finally: self.end_transaction() def SetLayerState(self, layer_idx, visible, printable, locked, outlined): self.begin_transaction(_("Change Layer State"), clear_selection_rect = 0) try: try: layer = self.layers[layer_idx] self.add_undo(layer.SetState(visible, printable, locked, outlined)) if not layer.CanSelect(): # XXX: this depends on whether we're drawing visible or # printable layers self.deselect_layer(layer_idx) self.update_active_layer() except: self.abort_transaction() finally: self.end_transaction() def SetLayerColor(self, layer_idx, color): self.begin_transaction(_("Set Layer Outline Color"), clear_selection_rect = 0) try: try: layer = self.layers[layer_idx] self.add_undo(layer.SetOutlineColor(color)) except: self.abort_transaction() finally: self.end_transaction() def SetLayerName(self, idx, name): self.begin_transaction(_("Rename Layer"), clear_selection_rect = 0) try: try: layer = self.layers[idx] self.add_undo(layer.SetName(name)) self.add_undo(self.queue_layer()) except: self.abort_transaction() finally: self.end_transaction() def AppendLayer(self, *args, **kw_args): self.begin_transaction(_("Append Layer"),clear_selection_rect = 0) try: try: layer = apply(SketchDocument.AppendLayer, (self,) + args, kw_args) self.add_undo((self._remove_layer, len(self.layers) - 1)) self.queue_layer(LAYER_ORDER, layer) except: self.abort_transaction() finally: self.end_transaction() return layer def NewLayer(self): self.begin_transaction(_("New Layer"), clear_selection_rect = 0) try: try: self.AppendLayer() self.active_layer = self.layers[-1] except: self.abort_transaction() finally: self.end_transaction() def _move_layer_up(self, idx): # XXX: exception handling if idx < len(self.layers) - 1: # move the layer... layer = self.layers[idx] del self.layers[idx] self.layers.insert(idx + 1, layer) other = self.layers[idx] # ... and adjust the selection sel = self.selection.GetInfoTree() newsel = [] for i, info in sel: if i == idx: i = idx + 1 elif i == idx + 1: i = idx newsel.append((i, info)) self.__set_selection(selinfo.tree_to_list(newsel), SelectSet) self.queue_layer(LAYER_ORDER, layer, other) return (self._move_layer_down, idx + 1) return None def _move_layer_down(self, idx): # XXX: exception handling if idx > 0: # move the layer... layer = self.layers[idx] del self.layers[idx] self.layers.insert(idx - 1, layer) other = self.layers[idx] # ...and adjust the selection sel = self.selection.GetInfoTree() newsel = [] for i, info in sel: if i == idx: i = idx - 1 elif i == idx - 1: i = idx newsel.append((i, info)) self.__set_selection(selinfo.tree_to_list(newsel), SelectSet) self.queue_layer(LAYER_ORDER, layer, other) return (self._move_layer_up, idx - 1) return NullUndo def MoveLayerUp(self, idx): if idx < len(self.layers) - 1: self.begin_transaction(_("Move Layer Up"), clear_selection_rect=0) try: try: self.add_undo(self._move_layer_up(idx)) except: self.abort_transaction() finally: self.end_transaction() def MoveLayerDown(self, idx): if idx > 0: self.begin_transaction(_("Move Layer Down"),clear_selection_rect=0) try: try: self.add_undo(self._move_layer_down(idx)) except: self.abort_transaction() finally: self.end_transaction() def _remove_layer(self, idx): layer = self.layers[idx] del self.layers[idx] if layer is self.active_layer: if idx < len(self.layers): self.find_active_layer(idx) else: self.find_active_layer() sel = self.selection.GetInfoTree() newsel = [] for i, info in sel: if i == idx: continue elif i > idx: i = i - 1 newsel.append((i, info)) self.__set_selection(selinfo.tree_to_list(newsel), SelectSet) self.queue_layer(LAYER_ORDER, layer) return (self._insert_layer, idx, layer) def _insert_layer(self, idx, layer): self.layers.insert(idx, layer) layer.SetDocument(self) self.queue_layer(LAYER_ORDER, layer) return (self._remove_layer, idx) def CanDeleteLayer(self, idx): return (len(self.layers) > 3 and not self.layers[idx].is_SpecialLayer) def DeleteLayer(self, idx): if self.CanDeleteLayer(idx): self.begin_transaction(_("Delete Layer"), clear_selection_rect = 0) try: try: self.add_undo(self._remove_layer(idx)) except: self.abort_transaction() finally: self.end_transaction() # # Style management # def queue_style(self): self.queue_message(STYLE) return (self.queue_style,) def init_styles(self): self.styles = UndoDict() self.auto_assign_styles = 1 self.asked_about = {} def destroy_styles(self): for style in self.styles.values(): style.Destroy() self.styles = None def get_dynamic_style(self, name): return self.styles[name] def GetDynamicStyle(self, name): try: return self.styles[name] except KeyError: return None def Styles(self): return self.styles.values() def write_styles(self, file): for style in self.styles.values(): style.SaveToFile(file) def load_AddStyle(self, style): self.styles.SetItem(style.Name(), style) def add_dynamic_style(self, name, style): if style: style = style.AsDynamicStyle() self.add_undo(self.styles.SetItem(name, style)) self.add_undo(self.queue_style()) return style def update_style_dependencies(self, style): def update(obj, style = style): obj.ObjectChanged(style) self.WalkHierarchy(update) return (self.update_style_dependencies, style) def UpdateDynamicStyleSel(self): if len(self.selection) == 1: self.begin_transaction(_("Update Style"), clear_selection_rect = 0) try: try: properties = self.CurrentProperties() # XXX hack for style in properties.stack: if style.is_dynamic: break else: return undo = [] # we used to use dir(style) to get at the list of # instance variables of style. In Python 2.2 dir # returns class attributes as well. So we use # __dict__.keys() now. for attr in style.__dict__.keys(): if attr not in ('name', 'is_dynamic'): undo.append(style.SetProperty(attr, getattr(properties, attr))) undo.append(properties.AddStyle(style)) undo = (UndoAfter, CreateListUndo(undo), self.update_style_dependencies(style)) self.add_undo(undo) except: self.abort_transaction() finally: self.end_transaction() def CanCreateStyle(self): if len(self.selection) == 1: obj = self.selection.GetObjects()[0] return obj.has_fill or obj.has_line return 0 def CreateStyleFromSelection(self, name, which_properties): if self.CanCreateStyle(): properties = self.CurrentProperties() style = properties.CreateStyle(which_properties) self.begin_transaction(_("Create Style %s") % name, clear_selection_rect = 0) try: try: style = self.add_dynamic_style(name, style) self.AddStyle(style) except: self.abort_transaction() finally: self.end_transaction() def RemoveDynamicStyle(self, name): style = self.GetDynamicStyle(name) if not style: # style does not exist. XXX: raise an exception ? return self.begin_transaction(_("Remove Style %s") % name, clear_selection_rect = 0) try: try: def remove(obj, style = style, add_undo = self.add_undo): add_undo(obj.ObjectRemoved(style)) self.WalkHierarchy(remove) self.add_undo(self.styles.DelItem(name)) self.add_undo(self.queue_style()) except: self.abort_transaction() finally: self.end_transaction() def GetStyleNames(self): names = self.styles.keys() names.sort() return names # # Layout # def queue_layout(self): self.queue_message(LAYOUT) return (self.queue_layout,) def init_layout(self): self.page_layout = pagelayout.PageLayout() def Layout(self): return self.page_layout def PageSize(self): return (self.page_layout.Width(), self.page_layout.Height()) def PageRect(self): w, h = self.page_layout.Size() return Rect(0, 0, w, h) def load_SetLayout(self, layout): self.page_layout = layout def __set_page_layout(self, layout): undo = (self.__set_page_layout, self.page_layout) self.page_layout = layout self.queue_layout() return undo def SetLayout(self, layout): self.begin_transaction(clear_selection_rect = 0) try: try: undo = self.__set_page_layout(layout) self.add_undo(_("Change Page Layout"), undo) except: self.abort_transaction() finally: self.end_transaction() # # Grid Settings # def queue_grid(self): self.queue_message(GRID) return (self.queue_grid,) def SetGridGeometry(self, geometry): self.begin_transaction(_("Set Grid Geometry")) try: try: self.add_undo(self.snap_grid.SetGeometry(geometry)) self.add_undo(self.queue_grid()) except: self.abort_transaction() finally: self.end_transaction() def GridGeometry(self): return self.snap_grid.Geometry() def GridLayerChanged(self): return self.queue_grid() # # Guide Lines # def add_guide_line(self, line): self.begin_transaction(_("Add Guide Line"), clear_selection_rect = 0) try: try: sel, undo = self.guide_layer.Insert(line, 0) self.add_undo(undo) self.add_undo(self.AddClearRect(line.get_clear_rect())) except: self.abort_transaction() finally: self.end_transaction() def AddGuideLine(self, point, horizontal): self.add_guide_line(guide.GuideLine(point, horizontal)) def RemoveGuideLine(self, line): if not line.parent is self.guide_layer or not line.is_GuideLine: return self.begin_transaction(_("Delete Guide Line"), clear_selection_rect = 0) try: try: self.add_undo(self.remove_objects([line.SelectionInfo()])) self.add_undo(self.AddClearRect(line.get_clear_rect())) except: self.abort_transaction() finally: self.end_transaction() def MoveGuideLine(self, line, point): if not line.parent is self.guide_layer or not line.is_GuideLine: return self.begin_transaction(_("Move Guide Line"), clear_selection_rect = 0) try: try: self.add_undo(self.AddClearRect(line.get_clear_rect())) self.add_undo(line.SetPoint(point)) self.add_undo(self.AddClearRect(line.get_clear_rect())) self.add_undo(self.GuideLayerChanged(line.parent)) except: self.abort_transaction() finally: self.end_transaction() def GuideLayerChanged(self, layer): self.queue_message(GUIDE_LINES, layer) return (self.GuideLayerChanged, layer) def GuideLines(self): return self.guide_layer.GuideLines() # # def as_group(self): for name in self.GetStyleNames(): self.RemoveDynamicStyle(name) layers = self.layers self.layers = [] groups = [] for layer in layers: if not layer.is_SpecialLayer: layer.UntieFromDocument() objects = layer.GetObjects() layer.objects = [] if objects: groups.append(Group(objects)) else: layer.Destroy() if groups: return Group(groups) else: return None UniConvertor-1.1.4/src/app/Graphics/arrow.py0000775000076400007640000000437111060500565017467 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2003-2006 by Igor E. Novikov # Copyright (C) 1997, 1998, 1999, 2001 by Bernhard Herzog # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. # # Arrows # import os from types import TupleType, ListType from math import atan2, sin, cos from app.events.warn import warn_tb, USER, pdebug from app import config from app import _, Trafo, CreatePath from app.io.loadres import read_resource_file class Arrow: def __init__(self, path, closed = 0): self.path = CreatePath() if type(path) in (ListType, TupleType): for segment in path: if len(segment) == 2: apply(self.path.AppendLine, segment) else: apply(self.path.AppendBezier, segment) else: self.path = path if closed: self.path.load_close() def BoundingRect(self, pos, dir, width): angle = atan2(dir.y, dir.x) if width < 1.0: width = 1.0 s = width * sin(angle) c = width * cos(angle) trafo = Trafo(c, s, -s, c, pos.x, pos.y) return self.path.accurate_rect(trafo) def Draw(self, device, rect = None): if self.path.closed: device.FillBezierPath(self.path, rect) else: device.DrawBezierPath(self.path, rect) def Paths(self): return (self.path,) def IsFilled(self): return self.path.closed def SaveRepr(self): path = map(lambda t: t[:-1], self.path.get_save()) return (path, self.path.closed) def __hash__(self): return hash(id(self.path)) def __cmp__(self, other): if __debug__: pdebug(None, 'Arrow.__cmp__, %s', other) if isinstance(other, self.__class__): return cmp(self.path, other.path) return cmp(id(self), id(other)) def read_arrows(filename): arrows = [] def arrow(path, closed, list = arrows): list.append(Arrow(path, closed)) dict = {'arrow': arrow} read_resource_file(filename, '##Sketch Arrow 0', _("%s is not an arrow definition file"), dict) return arrows std_arrows = None def StandardArrows(): global std_arrows if std_arrows is None: filename = os.path.join(config.std_res_dir, config.preferences.arrows) try: std_arrows = read_arrows(filename) except: warn_tb(USER, _("Error trying to read arrows from %s\n" "Using builtin defaults"), filename) std_arrows = [] return std_arrows UniConvertor-1.1.4/src/app/Graphics/psdevice.py0000775000076400007640000005020011060500565020127 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2002, 2003 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # PostScriptDevice: # # A graphics device which outputs to a postscript file. The postscript # file can be an EPS file. # import os from types import StringType from math import pi, sqrt from string import join, strip, lstrip, split from app.utils.os_utils import Empty from app import _, config, _sketch, Scale, sKVersion from app.events.warn import pdebug, warn_tb, warn, USER, INTERNAL from app.Lib.psmisc import quote_ps_string, make_textline from app import IdentityMatrix from app.Lib import type1 from graphics import CommonDevice import color import pagelayout import font class _DummyLineStyle: def Execute(device): device.SetLineColor(color.ExtStandardColors.black) device.SetLineAttributes(1, 0, 1, 0) defaultLineStyle = _DummyLineStyle() ps_join = (0, 1, 2) ps_cap = (None, 0, 1, 2) # header comments. Currently the type of all parameters is HeaderComments = [('For', '%%%%For: %s\n'), ('CreationDate', '%%%%CreationDate: %s\n'), ('Title', '%%%%Title: %s\n')] class PostScriptDevice(CommonDevice): draw_visible = 0 draw_printable = 1 file = None # default value for file in case open fails. def __init__(self, file, as_eps = 1, bounding_box = None, document = None, printable = 1, visible = 0, rotate = 0, embed_fonts = 0, **options): if as_eps and not bounding_box: raise ValueError, 'bounding_box required for EPS' self.fill = 0; self.line = 0 self.properties = None self.line = defaultLineStyle self.as_eps = as_eps self.needed_resources = {} self.include_resources = {} self.supplied_resources = [] self.draw_visible = visible self.draw_printable = printable self.gradient_steps = config.preferences.gradient_steps_print self.init_props() if document: document.WalkHierarchy(self.add_obj_resources, visible = self.draw_visible, printable = self.draw_printable) self.needed_resources.update(self.include_resources) # take care of the case where we're writing to an EPS that's # referenced by the document. This is a bit tricky. If the file # argument is an already opened file object, all hope is lost. # If it's a filename we look at all EPS files contained in the # document and load them into memory if and only if they are the # file we're writing into. # dictionary used as a set of ids of EpsData objects. self.loaded_eps_files = {} # the original contents of the file we're writing to if it is # actually referenced. self.loaded_eps_contents = None # search through the document if file is a string with the name # of an existing file. if isinstance(file, StringType) and os.path.exists(file): self.filename = file document.WalkHierarchy(self.handle_writes_to_embedded_eps, visible = self.draw_visible, printable = self.draw_printable) if type(file) == StringType: file = open(file, 'w') self.close_file = 1 else: self.close_file = 0 self.file = file if rotate: width, height = document.PageSize() llx, lly, urx, ury = bounding_box bounding_box = (height - ury, llx, height - lly, urx) write = file.write if as_eps: write("%!PS-Adobe-3.0 EPSF-3.0\n") else: write("%!PS-Adobe-3.0\n") # header comments for name, format in HeaderComments: if options.has_key(name): value = options[name] if value: write(format % make_textline(value)) write("%%%%Creator: sK1 %s\n" % sKVersion) write("%%Pages: 1\n") # there is exactly one page if bounding_box: [llx, lly, urx, ury] = map(int, bounding_box) write('%%%%BoundingBox: %d %d %d %d\n' % (llx - 1, lly - 1, urx + 1, ury + 1)) if rotate and document.Layout().Orientation() == pagelayout.Landscape: write("%%Orientation: Landscape\n") # XXX The Extensions comment should take embedded EPS files into # account. (the colorimage operator should be optional) write("%%Extensions: CMYK\n") write("%%DocumentSuppliedResources: (atend)\n") if self.needed_resources: start_written = 0 for restype, value in self.needed_resources.keys(): if restype == 'font' and embed_fonts: continue if not start_written: write('%%%%DocumentNeededResources: %s %s\n' % (restype, value)) else: write('%%%%+ %s %s\n' % (restype, value)) write("%%EndComments\n\n") write('%%BeginProlog\n') #procfile = os.path.join(config.user_config_dir, config.postscript_prolog) procfile=config.postscript_prolog procfile = open(procfile, 'r') line = procfile.readline() self.supplied_resources.append(join(split(strip(line))[1:])) write(line) for line in map(lstrip, procfile.readlines()): if line and line[0] != '%': write(line) write('%%EndResource\n') procfile.close() write('%%EndProlog\n\n') write('%%BeginSetup\n') if self.needed_resources: for res in self.include_resources.keys(): restype, value = res if restype == 'font' and embed_fonts: fontfile = font.GetFont(value).FontFileName() if fontfile: try: fontfile = open(fontfile, 'rb') write("%%%%BeginResource: %s %s\n" % res) type1.embed_type1_file(fontfile, self.file) write("\n%%EndResource\n") except IOError, exc: warn(USER, _("Can't embed font '%s': %s") % (value, exc)) del self.needed_resources[res] self.supplied_resources.append(join(res)) continue else: warn(USER, _("Can't find file for font '%s' for embedding") % value) write('%%%%IncludeResource: %s %s\n' % res) write('\n10.433 setmiterlimit\n') # 11 degree write('%%EndSetup\n\n') write('%%Page: 1 1\n') write('SketchDict begin\n') if rotate: self.Rotate(pi/2) self.Translate(0, -height) def init_props(self): self.init_line_props() self.init_color_props() self.init_font_props() def add_obj_resources(self, obj): # append reources from OBJ to self.needed_resources if obj.has_font: res = ('font', obj.Font().PostScriptName()) self.include_resources[res] = 1 if obj.is_Eps: self.needed_resources.update(obj.PSNeededResources()) def handle_writes_to_embedded_eps(self, obj): if obj.is_Eps: data = obj.Data() # we only need to investigate this if we've not processed it yet if not self.loaded_eps_files.has_key(id(data)): filename = data.Filename() if os.path.samefile(self.filename, filename): # it's the same file we're going to write into, so load it self.loaded_eps_files[id(data)] = 1 if self.loaded_eps_contents is None: self.loaded_eps_contents = open(filename).read() trailer_written = 0 def Close(self): if self.file is not None and not self.file.closed: if not self.trailer_written: write = self.file.write write('%%PageTrailer\n') write('showpage\n') write('%%Trailer\n') write('end\n') if self.supplied_resources: write('%%%%DocumentSuppliedResources: %s\n' % self.supplied_resources[0]) for res in self.supplied_resources[1:]: write('%%%%+ %s\n' % res) write('%%EOF\n') self.trailer_written = 1 if self.close_file: self.file.close() def __del__(self): try: self.Close() except: warn_tb(INTERNAL, "In __del__ of psdevice") def PushTrafo(self): self.file.write('pusht\n') def Concat(self, trafo): self.file.write('[%g %g %g %g %g %g] concat\n' % (trafo.m11, trafo.m21, trafo.m12, trafo.m22, trafo.v1, trafo.v2)) def Translate(self, x, y = None): if y is None: x, y = x self.file.write('%g %g translate\n' % (x, y)) def Rotate(self, angle): self.file.write('%g rotate\n' % (angle * 180 / pi)) def Scale(self, scale): self.file.write('%g dup scale\n' % scale) def PopTrafo(self): self.file.write('popt\n') def PushClip(self): self.file.write('pushc\n') def PopClip(self): self.file.write('popc\n') # popc is grestore. make sure properties are set properly again # after this. self.init_props() def init_color_props(self): self.current_color = None def _set_color(self, color): if self.current_color != color: if color.model == 'CMYK': c, m, y, k = color.getCMYK() self.file.write('%g %g %g %g cmyk\n' % (round(c, 3), round(m, 3), round(y, 3), round(k, 3))) else: r, g, b = color.RGB() self.file.write('%g %g %g rgb\n' % (round(r, 3), round(g, 3), round(b, 3))) self.current_color = color SetFillColor = _set_color SetLineColor = _set_color def init_line_props(self): self.current_width = self.current_cap = self.current_join = None self.current_dash = None def SetLineAttributes(self, width, cap = 1, join = 0, dashes = ()): write = self.file.write if self.current_width != width: width_changed = 1 write('%g w\n' % width) self.current_width = width else: width_changed = 0 join = ps_join[join] if self.current_join != join: write('%d j\n' % join) self.current_join = join cap = ps_cap[cap] if self.current_cap != cap: write('%d J\n' % cap) self.current_cap = cap if self.current_dash != dashes or width_changed: # for long dashes tuples this could theoretically produce # lines longer than 255 chars, which means that the file # would not conform to the DSC if width < 1: width = 1 write('[') for dash in dashes: dash = width * dash if dash < 0.001: dash = 0.001 write('%g ' % dash) write('] 0 d\n') self.current_dash = dashes def SetLineSolid(self): self.file.write('[ ] 0 d\n') self.current_dash = () def DrawLine(self, start, end): self.file.write('%g %g m %g %g l s\n' % (tuple(start) + tuple(end))) def DrawLineXY(self, x1, y1, x2, y2): self.file.write('%g %g m %g %g l s\n' % (x1, y1, x2, y2)) def DrawRectangle(self, start, end): self.file.write('%g %g %g %g R s\n' % (tuple(start) + tuple(end))) def FillRectangle(self, left, bottom, right, top): self.file.write('%g %g %g %g R f\n' % (left, bottom, right, top)) def DrawCircle(self, center, radius): self.file.write('%g %g %g C s\n' % (center.x, center.y, radius)) def FillCircle(self, center, radius): self.file.write('%g %g %g C f\n' % (center.x, center.y, radius)) def FillPolygon(self, pts): write = self.file.write if len(pts) > 1: write('%g %g m\n' % pts[0]) for p in pts[1:]: write('%g %g l\n' % p) write('f\n') def DrawBezierPath(self, path, rect = None): self.write_path(path.get_save()) self.file.write('S\n') def FillBezierPath(self, path, rect = None): self.write_path(path.get_save()) self.file.write('F\n') def fill_path(self, clip = 0): write = self.file.write if self.proc_fill: if not clip: self.PushClip() write('eoclip newpath\n') self.properties.ExecuteFill(self, self.pattern_rect) if not clip: self.PopClip() write('newpath\n') else: self.properties.ExecuteFill(self, self.pattern_rect) if not clip: write('F\n') else: write('gsave F grestore eoclip newpath\n') def stroke_path(self): self.properties.ExecuteLine(self, self.pattern_rect) self.file.write('S\n') def fill_and_stroke(self, clip = 0): write = self.file.write if not clip and self.line: if self.fill: write('gsave\n') self.fill_path() write('grestore\n') self.init_props() self.stroke_path() else: self.stroke_path() else: if self.fill: self.fill_path(clip) elif clip: write('eoclip newpath\n') def write_path(self, list): write = self.file.write write('%g %g m\n' % list[0][:-1]) for item in list[1:]: if len(item) == 3: write('%g %g l\n' % item[:-1]) elif len(item) == 7: write('%g %g %g %g %g %g c\n' % item[:-1]) else: if __debug__: pdebug('PS', 'PostScriptDevice: invalid bezier item:',item) def MultiBezier(self, paths, rect = None, clip = 0): # XXX: try to write path only once write = self.file.write line = self.line; fill = self.fill if line or fill or clip: open = 0 for path in paths: open = open or not path.closed write('newpath\n') if fill or clip: if not open: # all subpaths are closed for path in paths: self.write_path(path.get_save()) write('closepath\n') self.fill_and_stroke(clip) line = 0 else: # some but not all sub paths are closed for path in paths: self.write_path(path.get_save()) if not path.closed: write('closepath\n') self.fill_path(clip) if line: for path in paths: self.write_path(path.get_save()) if path.closed: write('closepath\n') self.stroke_path() self.draw_arrows(paths) def Rectangle(self, trafo, clip = 0): if self.fill or self.line or clip: self.file.write('[%g %g %g %g %g %g] rect\n' % trafo.coeff()) self.fill_and_stroke(clip) def RoundedRectangle(self, trafo, radius1, radius2, clip = 0): if self.fill or self.line or clip: self.file.write('[%g %g %g %g %g %g] %g %g rect\n' % (trafo.coeff() + (radius1, radius2))) self.fill_and_stroke(clip) def SimpleEllipse(self, trafo, start_angle, end_angle, arc_type, rect = None, clip = 0): if self.fill or self.line or clip: if start_angle == end_angle: self.file.write('[%g %g %g %g %g %g] ellipse\n' % trafo.coeff()) self.fill_and_stroke(clip) else: self.file.write('[%g %g %g %g %g %g] %g %g %d ellipse\n' % (trafo.coeff() + (start_angle, end_angle, arc_type))) self.fill_and_stroke(clip) self.draw_ellipse_arrows(trafo, start_angle, end_angle, arc_type, rect) def init_font_props(self): self.current_font = None def set_font(self, font, size): spec = (font.PostScriptName(), size) if self.current_font != spec: self.file.write('/%s %g sf\n' % spec) self.current_font = spec def DrawText(self, text, trafo, clip = 0, cache = None): # XXX: should make sure that lines in eps file will not be # longer than 255 characters. font = self.properties.font if font: write = self.file.write self.set_font(font, self.properties.font_size) write('(%s)\n' % quote_ps_string(text)) if trafo.matrix() == IdentityMatrix: write('%g %g ' % tuple(trafo.offset())) else: write('[%g %g %g %g %g %g] ' % trafo.coeff()) if self.proc_fill: write('P ') write('gsave clip newpath\n') self.properties.ExecuteFill(self, self.pattern_rect) write('grestore ') if clip: write('clip ') write('newpath\n') else: self.properties.ExecuteFill(self, self.pattern_rect) if clip: write('TP clip newpath\n') else: write('T\n') complex_text = None def BeginComplexText(self, clip = 0, cache = None): # XXX clip does not work yet... self.complex_text = Empty(clip = clip, fontname = '', size = None) if self.proc_fill or clip: self.PushClip() else: self.properties.ExecuteFill(self, self.pattern_rect) def DrawComplexText(self, text, trafo, font, font_size): write = self.file.write complex_text = self.complex_text self.set_font(font, font_size) write('(%s) ' % quote_ps_string(text)) if trafo.matrix() == IdentityMatrix: write('%g %g ' % tuple(trafo.offset())) else: write('[%g %g %g %g %g %g] ' % trafo.coeff()) if self.proc_fill: write('P\n') else: write('T\n') def EndComplexText(self): if self.proc_fill: self.file.write('eoclip newpath\n') self.properties.ExecuteFill(self, self.pattern_rect) self.PopClip() self.complex_text = None def DrawImage(self, image, trafo, clip = 0): write = self.file.write w, h = image.size if len(image.mode) >= 3: # compute number of hex lines. 80 hex digits per line. 3 bytes # per pixel digits = w * h * 6 # 3 bytes per pixel, 2 digits per byte if digits <= 0: # an empty image. (it should never be < 0 ...) return lines = (digits - 1) / 80 + 1 write('%d %d ' % image.size) write('[%g %g %g %g %g %g] true\n' % trafo.coeff()) write('%%%%BeginData: %d Hex Lines\n' % (lines + 1)) write('skcimg\n') else: digits = w * h * 2 # 2 digits per byte if digits <= 0: # an empty image. (it should never be < 0 ...) return lines = (digits - 1) / 80 + 1 write('%d %d ' % image.size) write('[%g %g %g %g %g %g] true\n' % trafo.coeff()) write('%%%%BeginData: %d Hex Lines\n' % (lines + 1)) write('skgimg\n') _sketch.write_ps_hex(image.im, self.file) write('%%EndData\n') if clip: write('pusht [%g %g %g %g %g %g] concat\n' % trafo.coeff()) write('%d %d scale\n' % image.size) write('0 0 m 1 0 l 1 1 l 0 1 l closepath popt clip\n') def DrawEps(self, data, trafo): write = self.file.write write('%g %g %g %g ' % (data.Start() + data.Size())) write('[%g %g %g %g %g %g]\n' % trafo.coeff()) write('skeps\n') write('%%%%BeginDocument: %s\n' % data.Filename()) if self.loaded_eps_files.has_key(id(data)): write(self.loaded_eps_contents) else: data.WriteLines(self.file) write('\n%%EndDocument\n') write('skepsend\n') def DrawGrid(self, orig_x, orig_y, xwidth, ywidth, rect): pass def DrawGuideLine(self, *args): pass def SetProperties(self, properties, rect = None): self.properties = properties self.line = properties.HasLine() self.fill = properties.HasFill() self.pattern_rect = rect self.proc_fill = properties.IsAlgorithmicFill() self.proc_line = properties.IsAlgorithmicLine() def write_gradient(self, gradient): # self.current_color is implicitly reset because gradients are # nested in a PushClip/PopClip, so we don't have to update that write = self.file.write samples = gradient.Sample(self.gradient_steps) write('%d gradient\n' % len(samples)) last = None for color in samples: if color != last: r, g, b = last = color write('%g %g %g $\n' % (round(r, 3), round(g, 3), round(b, 3))) else: write('!\n') has_axial_gradient = 1 def AxialGradient(self, gradient, p0, p1): # must accept p0 and p1 as PointSpecs self.write_gradient(gradient) self.file.write('%g %g %g %g axial\n' % (tuple(p0) + tuple(p1))) has_radial_gradient = 1 def RadialGradient(self, gradient, p, r0, r1): # must accept p as PointSpec self.write_gradient(gradient) self.file.write('%g %g %g %g radial\n' % (tuple(p) + (r0, r1))) has_conical_gradient = 1 def ConicalGradient(self, gradient, p, angle): # must accept p as PointSpec self.write_gradient(gradient) self.file.write('%g %g %g conical\n' % (tuple(p) + (angle,))) def TileImage(self, image, trafo): width, height = image.size if image.mode == 'RGBA': # We don't support transparency in textures, so we simply # treat RGBA as as RGB images mode = 'RGB' else: mode = image.mode length = width * height * len(mode) if length > 65536: # the tile image is too large to fit into a PostScript # string. Resize it. #warn(USER, "Image data to big for tiling, resizing...") ratio = float(width) / height max_size = 65536 / len(mode) width = int(sqrt(max_size * ratio)) height = int(sqrt(max_size / ratio)) tile = image.im.resize((width, height)) length = width * height * len(mode) trafo = trafo(Scale(float(image.size[0]) / width, float(image.size[1]) / height)) else: tile = image.im write = self.file.write write('%d %d %d [%g %g %g %g %g %g]\n' % ((width, height, len(mode)) + trafo.coeff())) digits = length * 2 # 2 digits per byte lines = (digits - 1) / 80 + 1 write('%%%%BeginData: %d Hex Lines\n' % (lines + 1)) write("tileimage\n") # write_ps_hex treats RGBA like RGB _sketch.write_ps_hex(tile, self.file) write('%%EndData\n') # # Outline Mode # This will be ignored in PostScript devices # def StartOutlineMode(self, *rest): pass def EndOutlineMode(self, *rest): pass def IsOutlineActive(self, *rest): return 0 UniConvertor-1.1.4/src/app/Graphics/properties.py0000775000076400007640000003161511060500565020532 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1998, 1999, 2000 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from app.conf import const CHANGED = const.CHANGED from app.events.connector import Publisher from app import CreateListUndo, UndoAfter, NullUndo, SketchInternalError, _ from app.events.warn import pdebug, INTERNAL from pattern import SolidPattern, EmptyPattern from color import StandardColors, StandardCMYKColors from blend import Blend class Style(Publisher): is_dynamic = 0 name = '' def __init__(self, name = '', duplicate = None, **kw): if duplicate is not None: self.__dict__ = duplicate.__dict__.copy() if hasattr(self, 'fill_pattern'): self.fill_pattern = self.fill_pattern.Copy() if hasattr(self, 'line_pattern'): self.line_pattern = self.line_pattern.Copy() else: if name: self.name = name for key, value in kw.items(): setattr(self, key, value) def SetProperty(self, prop, value): dict = self.__dict__ if dict.has_key(prop): undo = (self.SetProperty, prop, dict[prop]) else: undo = (self.DelProperty, prop) if prop == 'fill_pattern' or prop == 'line_pattern': value = value.Copy() dict[prop] = value self.issue(CHANGED, self) return undo def DelProperty(self, prop): undo = (self.SetProperty, prop, getattr(self, prop)) delattr(self, prop) self.issue(CHANGED, self) return undo def Duplicate(self): if self.is_dynamic: return self return self.__class__(duplicate = self) def Copy(self): return self.__class__(duplicate = self) def Name(self): return self.name def SetName(self, name): undo = self.SetName, self.name self.name = name return undo def AsDynamicStyle(self): result = self.Copy() result.is_dynamic = 1 return result def AsUndynamicStyle(self): result = self.Copy() if self.is_dynamic: del result.is_dynamic del result.name return result def SaveToFile(self, file): if self.is_dynamic: file.DynamicStyle(self) def IsEmpty(self): return not self.__dict__ def FillStyle(pattern): return Style(fill_pattern = pattern) EmptyFillStyle = Style(fill_pattern = EmptyPattern) def LineStyle(color = None, width = 1, cap = const.CapButt, join = const.JoinMiter, dashes = None, arrow1 = None, arrow2 = None): return Style(line_pattern = SolidPattern(color), line_width = width, line_cap = cap, line_join = join, line_dashes = dashes, line_arrow1 = arrow1, line_arrow2 = arrow2) SolidLine = LineStyle EmptyLineStyle = Style(line_pattern = EmptyPattern) class PropertyStack: update_cache = 1 def __init__(self, base = None, duplicate = None): if duplicate is not None: self.stack = [] for layer in duplicate.stack: self.stack.append(layer.Duplicate()) else: if base is None: base = factory_defaults.Duplicate() self.stack = [base] def __getattr__(self, attr): if self.update_cache: cache = self.__dict__ stack = self.stack[:] stack.reverse() for layer in stack: cache.update(layer.__dict__) self.update_cache = 0 try: return self.__dict__[attr] except KeyError: raise AttributeError, attr def _clear_cache(self): self.__dict__ = {'stack' : self.stack} return (self._clear_cache,) def prop_layer(self, prop): # return property layer containing PROP for item in self.stack: if hasattr(item, prop): return item # we should never reach this... raise SketchInternalError('unknown graphics property "%s"' % prop) def set_property(self, prop, value): layer = self.prop_layer(prop) if layer.is_dynamic: if self.stack[0].is_dynamic: layer = Style() setattr(layer, prop, value) return self.add_layer(layer) else: layer = self.stack[0] return layer.SetProperty(prop, value) def SetProperty(self, **kw): stack = self.stack undo = [] append = undo.append if len(stack) == 1 and not stack[0].is_dynamic: set = stack[0].SetProperty for prop, value in kw.items(): append(set(prop, value)) else: set = self.set_property for prop, value in kw.items(): append(set(prop, value)) if len(self.stack) > 1: undo_stack = (self.set_stack, self.stack[:]) if self.delete_shadowed_layers(): undo.append(undo_stack) undo = CreateListUndo(undo) undo = (UndoAfter, undo, self._clear_cache()) return undo def set_stack(self, stack): undo = (self.set_stack, self.stack) self.stack = stack self._clear_cache() return undo def delete_shadowed_layers(self): # check if some styles are completely hidden now stack = self.stack layers = [] dict = {'name':1, 'is_dynamic':0} dict.update(stack[0].__dict__) length = len(dict) for layer in stack[1:]: dict.update(layer.__dict__) if length != len(dict): layers.append(layer) length = len(dict) length = len(stack) stack[1:] = layers return length != len(stack) def add_layer(self, layer): undo = (self.set_stack, self.stack[:]) self.stack.insert(0, layer) return undo load_AddStyle = add_layer def AddStyle(self, style): if style.is_dynamic: undo = self.add_layer(style) self.delete_shadowed_layers() self._clear_cache() return undo else: return apply(self.SetProperty, (), style.__dict__) def HasFill(self): return self.fill_pattern is not EmptyPattern def IsAlgorithmicFill(self): return self.fill_pattern.is_procedural def ExecuteFill(self, device, rect = None): self.fill_pattern.Execute(device, rect) def HasLine(self): return self.line_pattern is not EmptyPattern def IsAlgorithmicLine(self): return self.line_pattern.is_procedural def ExecuteLine(self, device, rect = None): line_pattern = self.line_pattern if line_pattern is not EmptyPattern: line_pattern.Execute(device, rect) device.SetLineAttributes(self.line_width, self.line_cap, self.line_join, self.line_dashes) def HasFont(self): return self.font is not None def ObjectChanged(self, object): if object in self.stack: self._clear_cache() return 1 return 0 def ObjectRemoved(self, object): if object in self.stack: idx = self.stack.index(object) undo = (self.set_stack, self.stack[:]) self.stack[idx] = self.stack[idx].AsUndynamicStyle() pdebug('properties', 'made style undynamic') return undo return NullUndo def Untie(self): info = [] for i in range(len(self.stack)): style = self.stack[i] if style.is_dynamic: self.stack[i] = style.AsUndynamicStyle() info.append((i, style)) self._clear_cache() return info def Tie(self, document, info): for i, style in info: s = document.GetDynamicStyle(style.Name()) if s == style: self.stack[i] = s self._clear_cache() def Duplicate(self): return self.__class__(duplicate = self) grow_join = [5.240843064, 0.5, 0.5] grow_cap = [None, 0.5, 0.5, 0.70710678] def GrowAmount(self): return self.line_width * max(self.grow_cap[self.line_cap], self.grow_join[self.line_join]) def Blend(self, other, frac1, frac2): result = {} for prop, func in blend_functions: if func: result[prop] = func(getattr(self, prop), getattr(other, prop), frac1, frac2) else: result[prop] = getattr(self, prop) return PropertyStack(apply(Style, (), result)) def Transform(self, trafo, rects): # XXX hardcoding which properties may need to be transformed is # not really a good idea, but it's significantly faster. undo = NullUndo if len(self.stack) == 1 and not self.stack[0].is_dynamic: if self.fill_transform: pattern = self.fill_pattern if pattern.is_procedural: undo = pattern.Transform(trafo, rects) elif self.fill_transform: pattern = self.fill_pattern if pattern.is_procedural: pattern = pattern.Duplicate() if pattern.Transform(trafo, rects) is not NullUndo: undo = self.set_property('fill_pattern', pattern) if undo is not NullUndo: undo = (UndoAfter, undo, self._clear_cache()) return undo def CreateStyle(self, which_properties): properties = {} for prop in which_properties: if property_types[prop] == FontProperty and not self.HasFont(): continue properties[prop] = getattr(self, prop) return apply(Style, (), properties) def DynamicStyleNames(self): names = [] for style in self.stack: if style.is_dynamic: names.append(style.Name()) return names def condense(self): stack = self.stack last = stack[0] for style in stack[1:]: if not last.is_dynamic and not style.is_dynamic: dict = style.__dict__.copy() dict.update(last.__dict__) last.__dict__ = dict last = style length = len(stack) self.delete_shadowed_layers() def SaveToFile(self, file): file.Properties(self) class _EmptyProperties: def HasFill(self): return 0 HasLine = HasFill HasFont = HasFill def DynamicStyleNames(self): return [] EmptyProperties = _EmptyProperties() # # # factory_defaults = Style() default_graphics_style = None # set below default_text_style = None # set below blend_functions = [] property_names = [] property_titles = {} property_types = {} transform_properties = [] def blend_number(n1, n2, frac1, frac2): return n1 * frac1 + n2 * frac2 LineProperty = 1 FillProperty = 2 FontProperty = 3 OtherProperty = -1 def _set_defaults(prop, title, short_title, type, value, blend = None, transform = 0): factory_defaults.SetProperty(prop, value) property_names.append(prop) property_titles[prop] = (title, short_title) property_types[prop] = type blend_functions.append((prop, blend)) if transform: transform_properties.append(prop) black = StandardCMYKColors.black # XXX the default properties should be defined by the user. _set_defaults('fill_pattern', _("Fill Pattern"), _("Pattern"), FillProperty, EmptyPattern, blend = Blend, transform = 1) _set_defaults('fill_transform', _("Fill Transform Pattern"), _("Transform pattern"), FillProperty, 1) _set_defaults('line_pattern', _("Line Pattern"), _("Pattern"), LineProperty, SolidPattern(black), blend = Blend, transform = 1) _set_defaults('line_width', _("Line Width"), _("Width"), LineProperty, .283286 , blend = blend_number) _set_defaults('line_cap', _("Line Cap"), _("Cap"), LineProperty, const.CapButt) _set_defaults('line_join', _("Line Join"), _("Join"), LineProperty, const.JoinMiter) _set_defaults('line_dashes', _("Line Dashes"), _("Dashes"), LineProperty, ()) _set_defaults('line_arrow1', _("Line Arrow 1"), _("Arrow 1"), LineProperty, None) _set_defaults('line_arrow2', _("Line Arrow 2"), _("Arrow 2"), LineProperty, None) _set_defaults('font', _("Font"), _("Font"), FontProperty, None) _set_defaults('font_size', _("Font Size"), _("Size"), FontProperty, 12, blend = blend_number) _set_defaults('linegap', _("Linegap"), _("Linegap"), FontProperty, 1.0) _set_defaults('wordgap', _("Wordgap"), _("Wordgap"), FontProperty, 1.0) _set_defaults('chargap', _("Chargap"), _("Chargap"), FontProperty, 1.0) _set_defaults('align', _("Text Alignment"), _("Alignment"), FontProperty, const.ALIGN_LEFT) _set_defaults('valign', _("Text Vertical Alignment"), _("VAlignment"), FontProperty, const.ALIGN_BASE) factory_text_style = factory_defaults.Copy() factory_text_style.fill_pattern = SolidPattern(black) factory_text_style.line_pattern = EmptyPattern factory_text_style.font = None default_graphics_style = factory_defaults.Copy() default_text_style = factory_defaults.Copy() default_text_style.fill_pattern = SolidPattern(black) default_text_style.line_pattern = EmptyPattern default_text_style.font = None def FactoryTextStyle(): import app style = factory_text_style.Copy() if style.font is None: fontname = app.config.preferences.default_font style.font = app.GetFont(fontname) return style def DefaultTextProperties(): import app if default_text_style.font is None: fontname = app.config.preferences.default_font default_text_style.font = app.GetFont(fontname) return PropertyStack(base = default_text_style.Copy()) def DefaultGraphicsProperties(): return PropertyStack(base = default_graphics_style.Copy()) def set_graphics_defaults(kw): for key, value in kw.items(): if not property_types[key] == FontProperty: if key in ('fill_pattern', 'line_pattern'): value = value.Copy() default_graphics_style.SetProperty(key, value) def set_text_defaults(kw): for key, value in kw.items(): if not property_types[key] == LineProperty: if key == 'fill_pattern': value = value.Copy() default_text_style.SetProperty(key, value) UniConvertor-1.1.4/src/app/Graphics/gradient.py0000775000076400007640000000717711060500565020141 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1997, 1998 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # color gradient classes # from blend import Blend, MismatchError class Gradient: def __init__(self, duplicate = None): pass def ColorAt(self, pos): pass def Duplicate(self): return self.__class__(duplicate = self) class MultiGradient(Gradient): def __init__(self, colors = (), duplicate = None): if duplicate is not None: self.colors = duplicate.colors[:] else: if len(colors) < 2: raise ValueError, 'at least 2 colors required' self.colors = colors def StartColor(self): return self.colors[0][-1] def SetStartColor(self, color): undo = (self.SetStartColor, self.colors[0][-1]) self.colors[0] = (0, color) return undo def EndColor(self): return self.colors[-1][-1] def SetEndColor(self, color): undo = (self.SetEndColor, self.colors[-1][-1]) self.colors[-1] = (1, color) return undo def ColorAt(self, pos): colors = self.colors for i in range(len(colors) - 1): if colors[i][0] <= pos and colors[i + 1][0] >= pos: break else: return self.EndColor() start_pos, start_color = colors[i] if i < len(colors) - 1: end_pos, end_color = colors[i + 1] else: return start_color return Blend(end_color, start_color, (pos - start_pos) / float(end_pos - start_pos)) def Sample(self, num): colors = self.colors max = num - 1.0 pos1, color1 = colors[0] pos2, color2 = colors[1] diff = float(pos2 - pos1) cur = 1 result = [] blend = color1.RGB().Blend for i in range(num): frac = i / max while frac > pos2: pos1 = pos2; color1 = color2 cur = cur + 1 pos2, color2 = colors[cur] diff = float(pos2 - pos1) blend = color1.RGB().Blend frac = (frac - pos1) / diff result.append(blend(color2.RGB(), 1 - frac, frac)) return result def Colors(self): return self.colors def SetColors(self): undo = (self.SetColors, self.colors) self.colors = colors return undo def Blend(self, other, frac1, frac2): if type(other) == type(self.colors[0][-1]): # blend a gradient with a single color sc = self.colors c = [] for i in range(len(sc)): p1, c1 = sc[i] c.append((p1, c1.Blend(other, frac1, frac2))) return self.__class__(c) elif other.__class__ == self.__class__: # blend two MultiGradient instances # XXX: improve this... length = min(len(self.colors), len(other.colors)) sc = self.colors[:length - 1] sc.append(self.colors[-1]) oc = other.colors[:length - 1] oc.append(other.colors[-1]) c = [] for i in range(length): p1, c1 = sc[i] p2, c2 = oc[i] c.append((frac1 * p1 + frac2 * p2, Blend(c1, c2, frac1, frac2))) return self.__class__(c) else: raise MismatchError def SaveToFile(self, file): file.Gradient(self.colors) # convenience function for the most common gradient type def CreateSimpleGradient(start, end): return MultiGradient([(0, start), (1, end)]) UniConvertor-1.1.4/src/app/Graphics/__init__.py0000775000076400007640000000000011060500565020055 0ustar igorigorUniConvertor-1.1.4/src/app/Graphics/blendgroup.py0000775000076400007640000002457311060500565020504 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2003-2006 by Igor E. Novikov # Copyright (C) 1997, 1998, 1999 by Bernhard Herzog # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. # # The Blend Group # from types import IntType, ListType import operator #from app.UI import command from app.conf.const import SelectAdd from app import SketchInternalError, _ from app import NullUndo, CreateListUndo, CreateMultiUndo, Undo, UndoAfter import selinfo from compound import Compound from blend import Blend from group import Group SelectStart = 0 SelectEnd = 1 class BlendInterpolation(Compound): can_be_empty = 1 is_BlendInterpolation = 1 def __init__(self, steps = 2, start = None, end = None, duplicate = None): if duplicate is not None: self.steps = duplicate.steps Compound.__init__(self, duplicate = duplicate) else: self.steps = steps if start is not None: Compound.__init__(self, self.compute_blend(start, end)) else: Compound.__init__(self) def SetParameters(self, steps): if steps < 2: raise ValueError, 'steps must be >= 2' if self.steps != steps: undo = (self.SetParameters, self.steps) self.steps = steps self.parent.RecomputeChild(self) else: undo = NullUndo return undo def Steps(self): return self.steps def Recompute(self, start, end): self.set_objects(self.compute_blend(start, end)) def compute_blend(self, start, end): steps = self.steps blended = [] for step in range(1, steps): fraction = float(step) / steps blend = Blend(start, end, fraction) blend.SetDocument(self.document) blend.SetParent(None) blended.append(blend) blended.reverse() return blended def set_objects(self, new_objs): # called by __init__ and recompute if self.document is not None: self.document.AddClearRect(self.bounding_rect) for obj in self.objects: obj.Destroy() self.objects = new_objs self.del_lazy_attrs() if self.document is not None: self.document.AddClearRect(self.bounding_rect) self.issue_changed() def SaveToFile(self, file): if file.options.full_blend: file.BeginBlendInterpolation(self.steps) for obj in self.objects: obj.SaveToFile(file) file.EndBlendInterpolation() else: file.BlendInterpolation(self.steps) def AsGroup(self): return Group(self.objects[:]) def Transform(self, trafo): if self.parent.changing_children: return Compound.Transform(self, trafo) else: return NullUndo def Translate(self, offset): if self.parent.changing_children: Compound.Translate(self, offset) return NullUndo def Info(self): return _("Interpolation with %d steps") % self.steps class BlendGroup(Compound): is_Blend = 1 is_Group = 1 allow_traversal = 1 # allow selection of subobjects via M-Down etc. def __init__(self, steps = 0, start = None, end = None, duplicate = None): # Three different ways to instantiate this class: # # 1. Duplicating a BlendGroup: duplicate is not None. # # 2. Creating a BlendGroup from two normal graphics objects: # start and end are not None. # # 3. Creating a BlendGroup from an sk file: steps is 0. if duplicate is not None: # case 1 Compound.__init__(self, duplicate = duplicate) #self.Connect() elif start is not None: # case 2 inter = BlendInterpolation(steps, start, end) Compound.__init__(self, [start, inter, end]) #self.Connect() else: # case 3 Compound.__init__(self) def load_AppendObject(self, object): Compound.load_AppendObject(self, object) length = len(self.objects) if length > 2 and length & 1: # last object was a control object if len(self.objects[-2].objects) == 0: self.objects[-2].Recompute(self.objects[-3], self.objects[-1]) def insert(self, obj, at): undo_info = None try: if type(at) != IntType or at > len(self.objects): at = len(self.objects) self.objects.insert(at, obj) obj.SetDocument(self.document) obj.SetParent(self) obj.Connect() undo_info = (self.remove, at) self._changed() return undo_info except: if undo_info is not None: Undo(undo_info) raise def remove(self, idx): obj = self.objects[idx] del self.objects[idx] obj.Disconnect() obj.SetParent(None) self._changed() return (self.insert, obj, idx) def do_remove_child(self, idx): undo = [] try: if idx % 2 == 0: # a control object if self.document is not None: undo.append(self.document.AddClearRect(self.bounding_rect)) if len(self.objects) > 3: if idx == 0: undo.append(self.remove(1)) undo.append(self.remove(0)) elif idx == len(self.objects) - 1: undo.append(self.remove(idx)) undo.append(self.remove(idx - 1)) else: steps = self.objects[idx + 1].Steps() \ + self.objects[idx - 1].Steps() u = (UndoAfter, CreateMultiUndo(self.remove(idx + 1), self.remove(idx)), self.objects[idx - 1].SetParameters(steps)) undo.append(u) else: # remove one of only two control objects -> Remove self undo.append(self.parent.Remove(self)) return CreateListUndo(undo) else: # XXX implement this case raise ValueError, 'BlendGroup: cannot remove non control child' except: Undo(CreateListUndo(undo)) raise def RemoveSlice(self, min, max): raise SketchInternalError('RemoveSlice not allowed for BlendGroup') def RemoveObjects(self, infolist): if not infolist: return NullUndo sliced = selinfo.list_to_tree_sliced(infolist) sliced.reverse() undo = [self.begin_change_children()] try: for start, end in sliced: if type(end) == IntType: # > 1 adjacent children of self. XXX implement this raise SketchInternalError('Deletion of multiple objects' ' of BlendGroups not yet' ' implemented') elif type(end) == ListType: # remove grandchildren (children of child of self) if start % 2 == 0: # control object changes. This should result in # a recompute() automatically. undo.append(self.objects[start].RemoveObjects(end)) else: pass else: # a single child. If it's one of our control # objects, remove self undo.append(self.do_remove_child(start)) undo.append(self.end_change_children()) return CreateListUndo(undo) except: Undo(CreateListUndo(undo)) raise def permute_objects(self, permutation): # for now, silently ignore this return NullUndo def DuplicateObjects(self, infolist, offset): # XXX: should allow duplication of the control objects by inserting # them into the parent. return [], NullUndo def ReplaceChild(self, child, object): idx = self.objects.index(child) if idx % 2 == 0: # the object is a control object undo = self.ReplaceChild, object, child self.objects[idx] = object object.SetParent(self) object.SetDocument(self.document) child.SetParent(None) self.ChildChanged(object) return undo else: raise SketchError('Cannot replace child') def SelectSubobject(self, p, rect, device, path = None, *rest): idx = self.Hit(p, rect, device) - 1 obj = self.objects[idx] if idx % 2 == 0: # a control object if path: path_idx = path[0] path = path[1:] obj = obj.SelectSubobject(p, rect, device, path) elif path == (): obj = obj.SelectSubobject(p, rect, device) info = selinfo.prepend_idx(idx, obj) else: # an interpolation object if path is None: info = self else: info = selinfo.prepend_idx(idx, obj) return info def ForAllUndo(self, func): # XXX: should we just change start and end and recompute? self.begin_change_children() undo = map(func, self.objects) self.end_change_children(ignore_child_changes = 1) idx = range(0, len(self.objects), 2) undo = map(operator.getitem, [undo] * len(idx), idx) return CreateListUndo(undo) def begin_change_children(self): self.child_has_changed = 0 return Compound.begin_change_children(self) def end_change_children(self, ignore_child_changes = 0): if self.child_has_changed and not ignore_child_changes: self.document.AddAfterHandler(self.recompute, (), self.depth()) self.child_has_changed = 0 return Compound.end_change_children(self) def ChildChanged(self, child): idx = self.objects.index(child) if idx % 2 == 0: if self.changing_children: self.child_has_changed = 1 else: depth = self.depth(); recompute = self.recompute if idx > 0: self.document.AddAfterHandler(recompute, (idx - 1,), depth) if idx < len(self.objects) - 1: self.document.AddAfterHandler(recompute, (idx + 1,), depth) else: Compound.ChildChanged(self, child) self.del_lazy_attrs() def recompute(self, idx): self.objects[idx].Recompute(self.objects[idx - 1], self.objects[idx+1]) def RecomputeChild(self, child): self.recompute(self.objects.index(child)) def SaveToFile(self, file): file.BeginBlendGroup() for obj in self.objects: obj.SaveToFile(file) file.EndBlendGroup() def Info(self): return _("BlendGroup with %d control objects") \ % (len(self.objects) / 2 + 1) def CancelEffect(self): self.unset_parent() idx = range(0, len(self.objects), 2) return map(operator.getitem, [self.objects] * len(idx), idx) def SelectControl(self, relative, which): idx = self.objects.index(relative) if idx % 2 == 1: # an interpolation if which == SelectStart: idx = idx - 1 else: idx = idx + 1 else: # a control if which == SelectStart: if idx > 0: idx = idx - 2 else: if idx < len(self.objects) - 1: idx = idx + 2 self.document.SelectObject(self.objects[idx]) def ExtendBlend(self, start, end, steps): idx = self.objects.index(start) if idx == 0: inter = BlendInterpolation(steps, end, start) return CreateMultiUndo(self.insert(inter, 0), self.insert(end, 0)) elif idx == len(self.objects) - 1: inter = BlendInterpolation(steps, start, end) return CreateMultiUndo(self.insert(inter, None), self.insert(end, None)) def Ungroup(self): objects = [] for obj in self.objects: if obj.__class__ is BlendInterpolation: objects.append(obj.AsGroup()) else: objects.append(obj) return objects def CreateBlendGroup(start, end, steps): if isinstance(start.parent, BlendGroup): undo = start.parent.ExtendBlend(start, end, steps) result = start.parent elif isinstance(end.parent, BlendGroup): undo = end.parent.ExtendBlend(end, start, steps) result = end.parent else: result = BlendGroup(steps, start, end) undo = NullUndo return result, undo UniConvertor-1.1.4/src/app/Graphics/layer.py0000775000076400007640000002557711060500565017464 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1996, 1997, 1998, 1999 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # The layer classes. # from types import TupleType from app.conf.const import LAYER_STATE, LAYER_COLOR from app import _, NullUndo, EmptyRect, InfinityRect, Point, config, _sketch import color import selinfo from compound import EditableCompound class Layer(EditableCompound): can_be_empty = 1 is_Layer = 1 is_SpecialLayer = 0 is_GridLayer = 0 is_GuideLayer = 0 is_MasterLayer = 0 is_Page=0 def __init__(self, name = _("New Layer"), visible = 1, printable = 1, locked = 0, outlined = 0, outline_color = config.preferences.layer_color, is_MasterLayer=0, is_Page=0): EditableCompound.__init__(self, []) self.name = name self.visible = visible self.printable = printable self.locked = locked self.outlined = outlined self.is_MasterLayer = is_MasterLayer self.is_Page = is_Page if type(outline_color) == TupleType: if len(outline_color)==3: outline_color=('RGB',outline_color[0],outline_color[1],outline_color[2]) self.outline_color = apply(color.ParseSKColor, outline_color) else: self.outline_color = outline_color def Draw(self, device, rect = None): # Draw all objects on the device device. RECT, if provided, # gives the bounding rect of the region to be drawn allowing to # optimize the redisplay by drawing only those objects that # overlap with this rect. if device.draw_visible and self.visible \ or device.draw_printable and self.printable: outlined = self.outlined or device.IsOutlineActive() if outlined: device.StartOutlineMode(self.outline_color) EditableCompound.DrawShape(self, device, rect) if outlined: device.EndOutlineMode() def SelectSubobject(self, p, rect, device, path = (), *rest): if not self.CanSelect(): return None if self.outlined: device.StartOutlineMode() try: result = EditableCompound.SelectSubobject(self, p, rect, device, path) finally: if self.outlined: device.EndOutlineMode() return result def SelectRect(self, rect): if not self.CanSelect(): return [] test = rect.contains_rect build_info = selinfo.build_info selected = [] objects = self.objects for idx in range(len(objects)): obj = objects[idx] if test(obj.bounding_rect): selected.append(build_info(idx, obj)) return selected def SelectAll(self): if self.CanSelect(): return selinfo.select_all(self.objects) else: return [] def SelectionInfo(self, child, cache = None): info = selinfo.build_info(_sketch.IdIndex(self.objects, child), child) return selinfo.prepend_idx(self.document.LayerIndex(self), info) def PickObject(self, p, rect, device): if not self.visible: return None if self.outlined: device.StartOutlineMode() result = EditableCompound.PickObject(self, p, rect, device) if self.outlined: device.EndOutlineMode() return result def SetName(self, name): undo = (self.SetName, self.name) self.name = name return undo def Name(self): return self.name def NumObjects(self): return len(self.objects) def Visible(self): return self.visible def Printable(self): return self.printable def Locked(self): return self.locked def CanSelect(self): return not self.locked and self.visible def get_state(self): return (self.visible, self.printable, self.locked, self.outlined) def SetState(self, visible, printable, locked, outlined): # set the layer state. Return undo info. # # Side Effect: # Queue a LAYER message with parameter LAYER_STATE + tuple. # The tuple has the form # # (layer, visible_changed, printable_changed, outline_changed) # # We assume here that the receiver (usually SketchCanvas or # SketchView) uses this to determine whether to repaint parts of # the screen. If the receiver shows only visible layers and # allows outline, it should use the following expression to # determine whether to redraw or not: # # layer.NumObjects() and (visible_changed or # (outlined_changed and layer.Visible())) # # If you only show printable layers: # # layer.NumObjects() and printable_changed # # (in that case outline mode should be ignored as it is only # meant for quicker or clearer display while editing) # # The bounding rect of the now invalid region is # layer.bounding_rect oldstate = self.get_state() visible_changed = self.visible != visible self.visible = visible printable_changed = self.printable != printable self.printable = printable locked_changed = self.locked != locked self.locked = locked outlined_changed = self.outlined != outlined self.outlined = outlined if oldstate != self.get_state(): undo = (self.SetState,) + oldstate visibility = (self, visible_changed, printable_changed, outlined_changed) if self.document is not None: self.document.queue_layer(LAYER_STATE, visibility) if locked_changed: self.document.update_active_layer() return undo return NullUndo def SetOutlineColor(self, color): undo = (self.SetOutlineColor, self.outline_color) self.outline_color = color if self.document is not None: self.document.queue_layer(LAYER_COLOR, self) return undo def OutlineColor(self): return self.outline_color def Outlined(self): return self.outlined def SaveToFile(self, file): if self.is_MasterLayer: file.BeginMasterLayer(self.name, self.visible, self.printable, self.locked, self.outlined, self.outline_color) else: file.BeginLayer(self.name, self.visible, self.printable, self.locked, self.outlined, self.outline_color) for obj in self.objects: obj.SaveToFile(file) file.EndLayer() class SpecialLayer(Layer): is_SpecialLayer = 1 def __none(self, *args): return None SelectSubobject = __none PickObject = __none def SelectRect(self, *rect): return [] SelectAll = SelectRect class GuideLayer(SpecialLayer): is_GuideLayer = 1 def __init__(self, name = _("Guides"), visible = 1, printable = 0, locked = 0, outlined = 1, outline_color = None): if outline_color is None: outline_color = config.preferences.guide_color SpecialLayer.__init__(self, name, visible, 0, locked, 1, outline_color) def SetState(self, visible, printable, locked, outlined): return SpecialLayer.SetState(self, visible, 0, locked, outlined) def Draw(self, device, rect = None): if device.draw_visible and self.visible \ or device.draw_printable and self.printable: device.StartOutlineMode(self.outline_color) SpecialLayer.DrawShape(self, device) device.EndOutlineMode() def SelectSubobject(self, p, rect, device, path = (), *rest): if not self.CanSelect(): return None device.StartOutlineMode() try: objects = self.objects for obj_idx in range(len(objects) - 1, -1, -1): obj = objects[obj_idx] if obj.Hit(p, rect, device): result = obj.SelectSubobject(p, rect, device) return selinfo.prepend_idx(obj_idx, result) return None finally: device.EndOutlineMode() def SelectRect(self, rect): if not self.CanSelect(): return [] test = rect.contains_rect build_info = selinfo.build_info selected = [] objects = self.objects for idx in range(len(objects)): obj = objects[idx] if not obj.is_GuideLine and test(obj.bounding_rect): selected.append(build_info(idx, obj)) return selected def SelectAll(self): return self.SelectRect(InfinityRect) def compute_rects(self): if self.objects: self.bounding_rect = self.coord_rect = InfinityRect else: self.bounding_rect = self.coord_rect = EmptyRect def SaveToFile(self, file): file.BeginGuideLayer(self.name, self.visible, self.printable, self.locked, self.outlined, self.outline_color) for obj in self.objects: obj.SaveToFile(file) file.EndGuideLayer() def Snap(self, p): default = (1e100, p) horizontal = [default] vertical = [default] result = [default] for obj in self.objects: dist, snapped = obj.Snap(p) if type(snapped) == TupleType: if snapped[0] is None: horizontal.append((dist, snapped)) else: vertical.append((dist, snapped)) else: result.append((dist, snapped)) return min(horizontal), min(vertical), min(result) def GuideLines(self): result = self.objects[:] for idx in range(len(result) - 1, -1, -1): if not result[idx].is_GuideLine: del result[idx] return result def issue_changed(self): Layer.issue_changed(self) if self.document is not None: self.document.GuideLayerChanged(self) class GridLayer(SpecialLayer): is_GridLayer = 1 geometry = (0, 0, 20, 20) def __init__(self, geometry = None, visible = None, outline_color = None, name = _("Grid")): # The grid is locked, outlined and not printable if geometry is None: geometry = config.preferences.grid_geometry if visible is None: visible = config.preferences.grid_visible if outline_color is None: outline_color = config.preferences.grid_color SpecialLayer.__init__(self, name, visible, 0, 1, 1, outline_color) if len(geometry) == 2: self.geometry = (0, 0) + geometry elif len(geometry) == 4: self.geometry = geometry else: raise ValueError, "grid tuple must have length 2 or 4" def Draw(self, device, rect = None): if device.draw_visible and self.visible \ or device.draw_printable and self.printable: device.StartOutlineMode(self.outline_color) xorg, yorg, xwidth, ywidth = self.geometry device.DrawGrid(xorg, yorg, xwidth, ywidth, rect) device.EndOutlineMode() def update_rects(self): self.bounding_rect = self.coord_rect = InfinityRect def SaveToFile(self, file): file.BeginGridLayer(self.geometry, self.visible, self.outline_color, self.name) file.EndGridLayer() def Snap(self, p): xorg, yorg, xwidth, ywidth = self.geometry sx = round((p.x - xorg) / xwidth) * xwidth + xorg sy = round((p.y - yorg) / ywidth) * ywidth + yorg result = Point(sx, sy) return (abs(result - p), result) def SetState(self, visible, printable, locked, outlined): return SpecialLayer.SetState(self, visible, 0, 1, 1) def Geometry(self): return self.geometry def SetGeometry(self, geometry): undo = (self.SetGeometry, self.geometry) self.geometry = geometry if self.document: # a hack... self.document.queue_layer(LAYER_COLOR, self) return undo UniConvertor-1.1.4/src/app/Graphics/graphics.py0000775000076400007640000015605411060500565020143 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2002, 2003 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # This file contains the classes that act as a more or less abstract # graphics device. For the purposes of this file, a graphics device # provides methods for drawing primitives like rectangles, curves or # images and for setting the colors and pattern with which they are # drawn. A graphics device is abstract in the sense that all coordinates # are given in document coordinates and code using a graphics device # need not know whether output goes to a window or a printer, etc. # import sys from types import TupleType import operator, string from math import atan2, hypot, pi, sin, cos import PIL.Image #from app.X11 import X from app.conf import const X=const #from pax import PaxRegionType, IntersectMasks, CreateRegion from app.events.warn import pdebug, warn, INTERNAL, USER from app import _, SketchError, config from app.utils.os_utils import Empty from app.conf import const from app.conf.const import ArcPieSlice, ArcChord from app import _sketch from app import Point, Polar, Rect, UnitRect, Identity, SingularMatrix, \ Trafo, Rotation, Scale, Translation, Undo, TransformRectangle from color import StandardColors import color from properties import SolidLine, PropertyStack from pattern import SolidPattern, EmptyPattern # approximate a unit circle circle_path = _sketch.approx_arc(0, 2 * pi) # Flip y coordinates FlipY = Trafo(1, 0, 0, -1, 0, 0) # Define a device dependent color (as opposed to color.RGBColor, which # is a bit device independent). Mainly interesting when drawing on a # Bitmap or inverting device where it is important which bits are # involved and not which color is actually used. The colors are # currently represented as python integers. def ScreenColor(color): return int(color) color_0 = ScreenColor(0) color_1 = ScreenColor(1) # some default properties black_line = PropertyStack() black_line.AddStyle(SolidLine(StandardColors.black)) blue_line = PropertyStack() blue_line.AddStyle(SolidLine(StandardColors.blue)) defaultLineStyle = black_line default_outline_style = black_line default_guide_style = blue_line default_grid_style = blue_line class GCDevice: # The base class for devices dealing with X graphics contexts # (GCs). Implements the methods for converting between window and # doc coordinates, clipping and outline mode outline_style = default_outline_style line = defaultLineStyle def __init__(self): self.orig_x = self.orig_y = 0.0 self.scale = 1.0 self.win_to_doc = self.doc_to_win = Identity self.init_trafo_stack() self.gc = None self.outline_mode = 0 self.font = None self.clip_region = None self.clip_stack = None self.proc_fill = 0 def init_gc(self, widget, **gcargs): self.gc = widget.CreateGC(gcargs) self.widget = widget self.visual = color.skvisual self.InitClip() #width=self.widget.winfo_reqwidth() #height=self.widget.winfo_reqheight() # # Clipping # # Ideally, the clipping mechanism works as follows: # # Before drawing anything, the canvas initializes the clipping # mechanism with InitClip(). After this, no clipping is active # (apart from clipping to the window which is always done by X) # # Subsequently, whenever clipping is needed, you are expected to # call PushClip to save the current clipping region on a stack and # use one (or more) of the methods ClipRegion, ClipRect, # ClipPolygon or ClipBitmap to establish a new clipping region. # After this, only those parts of the device that lie within the # clipping region are modified. # # If there already is an active clipping region, the new clipping # region will be the intersection of the old clipping region and # the region specified in the method invocation. # # To restore the old clipping region, finally call PopClip. There # must be a call to PopClip for every call to PushClip, etc. # # This mechanism allows arbitrarily nested clipping operations. # # The clipping regions are given in device coordinates # def InitClip(self): # reset all clipping... self.gc.SetClipMask(None) self.clip_region = None self.clip_stack = () def IsClipping(self): return self.clip_stack != () def PushClip(self): self.clip_stack = (self.clip_region, self.clip_stack) def PopClip(self): # Pop the old clip region from the clip stack and make it the # active clip region. self.clip_region, self.clip_stack = self.clip_stack self.gc.SetClipMask(self.clip_region) def ClipRegion(self, region): # Itersect the current clip region and REGION and make the # result the new clip region. REGION may be a region object or a # pixmap object of depth 1 self.clip_region = IntersectMasks(self.clip_region, region) self.gc.SetClipMask(self.clip_region) def ClipRect(self, recttuple): region = self.widget.CreateRegion() apply(region.UnionRectWithRegion, recttuple) self.ClipRegion(region) def ClipPolygon(self, pts): self.ClipRegion(self.widget.PolygonRegion(pts)) ClipBitmap = ClipRegion def create_clip_bitmap(self): width = self.widget.width height = self.widget.height bitmap = self.widget.CreatePixmap(width, height, 1) bitmap_gc = bitmap.CreateGC(foreground = 0) bitmap_gc.FillRectangle(0, 0, width, height) bitmap_gc.foreground = 1 return (bitmap, bitmap_gc) # # Convert document coordinates to window coordinates and vice versa. # def DocToWin(self, *args): # Return the point in window coords as a tuple of ints return apply(self.doc_to_win.DocToWin, args) def DocToWinPoint(self, p): # Return the point in window coords as a point object return self.doc_to_win(p) def LengthToWin(self, len): return int(len * self.scale + 0.5) def LengthToWinFloat(self, len): return len * self.scale def LengthToDoc(self, len): return len / self.scale def WinToDoc(self, *args): return apply(self.win_to_doc, args) def init_trafo_stack(self): self.trafo_stack = () self.default_trafo = (self.win_to_doc, self.doc_to_win) def SetViewportTransform(self, scale, doc_to_win, win_to_doc): self.scale = scale self.doc_to_win = doc_to_win self.win_to_doc = win_to_doc self.init_trafo_stack() def InitTrafo(): self.win_to_doc, self.doc_to_win = self.default_trafo self.trafo_stack = () def PushTrafo(self): self.trafo_stack = (self.win_to_doc, self.doc_to_win, self.trafo_stack) def Concat(self, trafo): self.doc_to_win = self.doc_to_win(trafo) try: self.win_to_doc = trafo.inverse()(self.win_to_doc) except SingularMatrix: pass def Translate(self, *args): self.Concat(apply(Translation, args)) def Scale(self, factor): self.Concat(Scale(factor)) def Rotate(self, angle): self.Concat(Rotation(angle)) def PopTrafo(self): self.win_to_doc, self.doc_to_win, self.trafo_stack = self.trafo_stack def WindowResized(self, width, height): pass # # Double Buffering # def StartDblBuffer(self): self.buffer_pixmap = self.widget.CreatePixmap() self.gc.SetDrawable(self.buffer_pixmap) def EndDblBuffer(self): self.gc.SetDrawable(self.widget) self.buffer_pixmap.CopyArea(self.widget, self.gc, 0, 0, self.widget.width, self.widget.height, 0, 0) self.buffer_pixmap = None class SimpleGC(GCDevice): # # Functions for drawing X-primitives (rectangles, lines, ...) # with solid colors # # These functions are used internally and by the Patterns # def __init__(self): GCDevice.__init__(self) self.current_color = StandardColors.black def SetFillColor(self, color): self.current_color = color try: self.gc.SetForegroundAndFill(self.visual.get_pixel(color.RGB())) except: self.gc.SetForegroundAndFill(self.visual.get_pixel(color)) def SetLineColor(self, color): self.current_color = color try: #print "Xlib outline0",color.RGB() self.gc.SetForegroundAndFill(self.visual.get_pixel(color.RGB())) except: #print "Xlib outline",color self.gc.SetForegroundAndFill(self.visual.get_pixel(color)) def SetLineAttributes(self, width, cap = X.CapButt, join = X.JoinMiter, dashes = None): if dashes: line = X.LineOnOffDash else: line = X.LineSolid self.gc.SetLineAttributes(int(round(width * self.scale)), line, cap, join) if dashes: if width < 1.0: scale = self.scale else: scale = width * self.scale dashes = map(operator.mul, dashes, [scale] * len(dashes)) dashes = map(int, map(round, dashes)) for idx in range(len(dashes)): length = dashes[idx] if length <= 0: dashes[idx] = 1 elif length > 255: dashes[idx] = 255 self.gc.SetDashes(dashes) def SetLineSolid(self): self.gc.line_style = X.LineSolid def DrawLine(self, start, end): startx, starty = self.DocToWin(start) endx, endy = self.DocToWin(end) self.gc.DrawLine(startx, starty, endx, endy) def DrawLineXY(self, x1, y1, x2, y2): startx, starty = self.DocToWin(x1, y1) endx, endy = self.DocToWin(x2, y2) self.gc.DrawLine(startx, starty, endx, endy) def DrawLines(self, pts): pts = map(self.DocToWin, pts) self.gc.DrawLines(pts, X.CoordModeOrigin) def FillPolygon(self, pts): pts = map(self.DocToWin, pts) self.gc.FillPolygon(pts, X.Complex, X.CoordModeOrigin) def DrawRectangle(self, start, end): # draw the outline of the rectangle whose opposite corners are # start and end. pts = TransformRectangle(self.doc_to_win, Rect(start, end)) if type(pts) == TupleType: apply(self.gc.DrawRectangle, pts) else: self.gc.DrawLines(pts, X.CoordModeOrigin) def FillRectangle(self, left, top, right, bottom): pts = TransformRectangle(self.doc_to_win, Rect(left, top, right, bottom)) if type(pts) == TupleType: apply(self.gc.FillRectangle, pts) else: self.gc.FillPolygon(pts, X.Convex, X.CoordModeOrigin) def DrawEllipse(self, start, end): pts = TransformRectangle(self.doc_to_win, Rect(start, end)) if type(pts) == TupleType: apply(self.gc.DrawArc, pts + (0, 360 * 64)) else: d = end - start self.PushTrafo() self.Concat(Trafo(d.x, 0, 0, d.y, start.x, start.y)) self.DrawBezierPath(circle_path) self.PopTrafo() def DrawCircle(self, center, radius): rect = Rect(center.x - radius, center.y - radius, center.x + radius, center.y + radius) pts = TransformRectangle(self.doc_to_win, rect) if type(pts) == TupleType: apply(self.gc.DrawArc, pts + (0, 360 * 64)) else: self.PushTrafo() self.Concat(Trafo(radius, 0, 0, radius, center.x, center.y)) self.DrawBezierPath(circle_path) self.PopTrafo() def FillCircle(self, center, radius): rect = Rect(center.x - radius, center.y - radius, center.x + radius, center.y + radius) pts = TransformRectangle(self.doc_to_win, rect) if type(pts) == TupleType: apply(self.gc.FillArc, pts + (0, 360 * 64)) else: self.PushTrafo() self.Concat(Trafo(radius, 0, 0, radius, center.x, center.y)) self.FillBezierPath(circle_path) self.PopTrafo() def DrawBezierPath(self, path, rect = None): path.draw_transformed(self.gc, self.doc_to_win, 1, 0, rect) def FillBezierPath(self, path, rect = None): path.draw_transformed(self.gc, self.doc_to_win, 0, 1, rect) class CommonDevice: # some methods common to GraphicsDevice and PostScriptDevice def draw_arrow(self, arrow, width, pos, dir, rect = None): self.PushTrafo() self.Translate(pos.x, pos.y) self.Rotate(dir.polar()[1]) if width >= 1.0: self.Scale(width) self.SetLineSolid() arrow.Draw(self) #, rect) self.PopTrafo() def draw_arrows(self, paths, rect = None): if self.line: arrow1 = self.properties.line_arrow1 arrow2 = self.properties.line_arrow2 if arrow1 or arrow2: width = self.properties.line_width for path in paths: if not path.closed and path.len > 1: if arrow1: type, controls, p3, cont = path.Segment(1) p = path.Node(0) if type == _sketch.Bezier: p1, p2 = controls dir = p - p1 if not abs(dir): dir = p - p2 else: dir = p - p3 self.draw_arrow(arrow1, width, p, dir, rect) if arrow2: type, controls, p, cont = path.Segment(-1) p3 = path.Node(-2) if type == _sketch.Bezier: p1, p2 = controls dir = p - p2 if not abs(dir): dir = p - p1 else: dir = p - p3 self.draw_arrow(arrow2, width, p, dir, rect) def draw_ellipse_arrows(self, trafo, start_angle, end_angle, arc_type, rect = None): if arc_type == const.ArcArc and self.line and start_angle != end_angle: pi2 = pi / 2 width = self.properties.line_width arrow1 = self.properties.line_arrow1 if arrow1 is not None: pos = trafo(Polar(1, start_angle)) dir = trafo.DTransform(Polar(1, start_angle - pi2)) self.draw_arrow(arrow1, width, pos, dir, rect) arrow2 = self.properties.line_arrow2 if arrow2 is not None: pos = trafo(Polar(1, end_angle)) dir = trafo.DTransform(Polar(1, end_angle + pi2)) self.draw_arrow(arrow2, width, pos, dir, rect) use_shm_images = 0 shm_images_supported = 0 class GraphicsDevice(SimpleGC, CommonDevice): # # Graphics device that allows complex fill and line properties # XXX This class should have a different name # ximage = None font_cache = None old_font_cache = None grid_style = default_grid_style guide_style = default_guide_style # the following flags may be used by the document and layer objects # to determine which parts to draw, depending on whether the user # marks them as visible or printable. draw_visible = 1 draw_printable = 0 def __init__(self): SimpleGC.__init__(self) self.line = 0 self.fill = 0 self.properties = PropertyStack() self.gradient_steps = config.preferences.gradient_steps_editor self.images_drawn = 0 self.unknown_fonts = {} self.failed_fonts = {} self.cairo_draw=0 def InitClip(self): SimpleGC.InitClip(self) self.images_drawn = 0 proc_fill = proc_line = 0 fill_rect = None def set_properties(self, properties): self.properties = properties if properties.HasFill(): self.fill = 1 self.proc_fill = properties.IsAlgorithmicFill() else: self.fill = 0 self.proc_fill = 0 if properties.HasLine(): self.line = 1 self.proc_line = properties.IsAlgorithmicLine() else: self.line = 0 self.proc_line = 0 def SetProperties(self, properties, rect = None): if not self.outline_mode: self.fill_rect = rect self.set_properties(properties) else: # if outline, set only font properties self.properties.SetProperty(font = properties.font, font_size = properties.font_size) # def activate_line(self): self.properties.ExecuteLine(self) def activate_fill(self): self.properties.ExecuteFill(self, self.fill_rect) # # Patterns # def get_pattern_image(self): width = self.widget.width height = self.widget.height winrect = self.doc_to_win(self.fill_rect) left, top, right, bottom = map(int, map(round, winrect)) l = max(left, 0); r = min(right, width); t = max(top, 0); b = min(bottom, height); if type(self.clip_region) == PaxRegionType: cx, cy, cw, ch = self.clip_region.ClipBox() l = max(l, cx); r = min(r, cx + cw) t = max(t, cy); b = min(b, cy + ch) if l >= r or t >= b: return None, None, None image = PIL.Image.new('RGB', (r - l, b - t), (255, 255, 255)) trafo = Translation(-l, -t)(self.doc_to_win) return image, trafo, (l, t) def draw_pattern_image(self, image, pos): if use_shm_images and self.images_drawn: # force a shmimage to be drawn if ShmPutImage requests might # be in the queue self.widget.Sync() self.create_ximage() ximage = self.ximage x, y = pos w, h = image.size _sketch.copy_image_to_ximage(self.visual, image.im, ximage, x, y, w, h) if use_shm_images: self.gc.ShmPutImage(ximage, x, y, x, y, w, h, 0) self.images_drawn = 1 else: self.gc.PutImage(ximage, x, y, x, y, w, h) has_axial_gradient = 1 def AxialGradient(self, gradient, p0, p1): if config.preferences.cairo_enabled == 1: return # p0 and p1 may be PointSpecs image, trafo, pos = self.get_pattern_image() if image is None: return x0, y0 = trafo(p0) x1, y1 = trafo(p1) #import time #_t = time.clock() print 'AXIAL\n================' print 'x0',x0, 'y0',y0 print 'x1',x1, 'y1',y1 RGBgradient = [] for position, color in gradient.Colors(): RGBgradient.append((position, tuple(color.RGB()))) _sketch.fill_axial_gradient(image.im, RGBgradient, x0,y0, x1,y1) #_t = time.clock() - _t self.draw_pattern_image(image, pos) has_radial_gradient = 1 def RadialGradient(self, gradient, p, r0, r1): if config.preferences.cairo_enabled == 1: return # p may be PointSpec image, trafo, pos = self.get_pattern_image() if image is None: return x, y = trafo.DocToWin(p) r0 = int(round(abs(trafo.DTransform(r0, 0)))) r1 = int(round(abs(trafo.DTransform(r1, 0)))) #import time #_t = time.clock() RGBgradient = [] for position, color in gradient.Colors(): RGBgradient.append((position, tuple(color.RGB()))) _sketch.fill_radial_gradient(image.im, RGBgradient, x, y, r0, r1) #_t = time.clock() - _t #print 'radial:', _t self.draw_pattern_image(image, pos) has_conical_gradient = 1 def ConicalGradient(self, gradient, p, angle): if config.preferences.cairo_enabled == 1: return # p may be PointSpec image, trafo, pos = self.get_pattern_image() if image is None: return cx, cy = trafo.DocToWin(p) #import time #_t = time.clock() RGBgradient = [] for position, color in gradient.Colors(): RGBgradient.append((position, tuple(color.RGB()))) _sketch.fill_conical_gradient(image.im, RGBgradient, cx, cy, -angle) #_t = time.clock() - _t #print 'conical:', _t self.draw_pattern_image(image, pos) use_pixmap_tile = 1 def TileImage(self, tile, trafo): # XXX this could be faster with some caching. width, height = self.doc_to_win(trafo).DTransform(tile.size) width = int(round(width)) height = int(round(height)) if self.use_pixmap_tile and trafo.m12 == 0 and trafo.m21 == 0 \ and width * height < self.widget.width * self.widget.height: # the image is only scaled. Use a tile pixmap # # there are other cases where this could be done: # horizontal/vertical shearing, rotation by 90 degrees,... # This should also be done like in DrawImage, i.e. use the # integer coordintes of the transformed tile to determine if # pixmaps can be used. This would be a little less precise, # though. # degenerate cases if width == 0: width = 1 if height == 0: height = 1 ximage = self.create_sized_ximage(abs(width), abs(height)) pixmap = self.widget.CreatePixmap(abs(width), abs(height), ximage.depth) _sketch.copy_image_to_ximage(self.visual, tile.im, ximage, 0, 0, width, height) gc = pixmap.CreateGC() gc.PutImage(ximage, 0, 0, 0, 0, abs(width), abs(height)) self.gc.SetForegroundAndFill(pixmap) x, y = trafo.DocToWin(0, 0) self.gc.SetTSOrigin(x, y) self.gc.FillRectangle(0, 0, self.widget.width, self.widget.height) else: image, temp_trafo, pos = self.get_pattern_image() if image is None: return trafo = temp_trafo(trafo) try: _sketch.fill_transformed_tile(image.im, tile.im, trafo.inverse()) self.draw_pattern_image(image, pos) except SingularMatrix: pass # # Outline Mode # # StartOutlineMode(COLOR) starts drawing everything unfilled with # a solid line of color COLOR. # # EndOutlineMode() restores the previous mode. # # As long as there are always matching calls to StartOutlineMode # and EndOutlineMode outline modes can be arbitrarily nested. This # allows activating the outline mode globally in the canvas widget # and overriding the color in indiviual layers or drawing only # some layers in outline mode in different colors. allow_outline = 1 def StartOutlineMode(self, color = None): if self.allow_outline: self.outline_mode = (self.properties, self.outline_mode) if color is None: properties = self.outline_style else: properties = PropertyStack() properties.SetProperty(fill_pattern = EmptyPattern) properties.AddStyle(SolidLine(color)) self.set_properties(properties) def EndOutlineMode(self): if self.allow_outline and self.outline_mode: properties, self.outline_mode = self.outline_mode self.set_properties(properties) def IsOutlineActive(self): return not not self.outline_mode def CairoSetFill(self): #print "SET_FILL\n================" if not self.properties.fill_pattern.is_Gradient: if config.preferences.alpha_channel_enabled <> 1: apply(self.gc.CairoSetSourceRGB, self.properties.fill_pattern.Color().cRGB()) else: apply(self.gc.CairoSetSourceRGBA, self.properties.fill_pattern.Color().cRGBA()) else: if self.properties.fill_pattern.is_AxialGradient: # Linear gradient processing for Cairo engine rect=self.fill_rect vx, vy = self.properties.fill_pattern.direction print 'direction', vx, vy angle = atan2(vy, vx) - pi / 2 print 'angle', angle center = rect.center() print 'center', center print 'rect', rect rx0, ry0, rx1, ry1 = self.fill_rect print rx0, ry0, rx1, ry1 rot = Rotation(angle, center) left, bottom, right, top = rot(rect) height = (top - bottom) * (1.0 - self.properties.fill_pattern.border) trafo = rot(Translation(center)) gradient = self.properties.fill_pattern.gradient p0 = trafo(0, height / 2) p1 = trafo(0, -height / 2) print p0, p1 #image, trafo, pos = self.get_pattern_image() #if image is None: #return x0, y0 = self.DocToWinPoint(trafo(p0)) x1, y1 = self.DocToWinPoint(trafo(p1)) x0, y0 = p0 x1, y1 = p1 print 'x0',x0, 'y0',y0 print 'x1',x1, 'y1',y1 self.gc.DrawLine(x0, y0, x1, y1) self.gc.CairoPatternCreateLinear(x0, y0, x1, y1) #self.gc.CairoPatternCreateLinear(0.25, 0.35, 100, 300) #stopcolors=[] #for position, color in gradient.Colors(): #stopcolors.append((position, color)) #stopcolors.reverse() for position, color in gradient.Colors(): if config.preferences.alpha_channel_enabled <> 1: r,g,b = color.cRGB() print r,g,b self.gc.CairoPatternAddColorStopRGB(position, r, g, b) else: r,g,b,a = color.cRGBA() self.gc.CairoPatternAddColorStopRGBA(position, r, g, b, a) elif self.properties.fill_pattern.is_RadialGradient: print 'RadialGradient' elif self.properties.fill_pattern.is_ConicalGradient: print 'ConicalGradient' def CairoSetOutline(self): if config.preferences.alpha_channel_enabled == 1 and not self.IsOutlineActive(): apply(self.gc.CairoSetSourceRGBA, self.properties.line_pattern.Color().cRGBA()) else: apply(self.gc.CairoSetSourceRGB, self.properties.line_pattern.Color().cRGB()) cWidth = self.properties.line_width * self.scale if self.IsOutlineActive(): cWidth = 1.0 cCap = self.properties.line_cap cJoin = self.properties.line_join if cCap > 0: cCap = cCap - 1 self.gc.CairoSetOutlineAttr(cWidth, cCap, cJoin) dashes = self.properties.line_dashes if dashes: if self.properties.line_width < 1.0: scale = self.scale else: scale = self.properties.line_width * self.scale dashes = map(operator.mul, dashes, [scale] * len(dashes)) dashes = map(int, map(round, dashes)) for idx in range(len(dashes)): length = dashes[idx] if length <= 0: dashes[idx] = 1 elif length > 255: dashes[idx] = 255 self.gc.CairoSetDash(dashes,0) else: self.gc.CairoSetDash([],0) # # Primitives # def Rectangle(self, trafo, clip = 0): self.PushTrafo() self.Concat(trafo) pts = TransformRectangle(self.doc_to_win, UnitRect) self.PopTrafo() if type(pts) == TupleType: if self.fill or self.proc_fill: if config.preferences.cairo_enabled == 0: if self.proc_fill: if not clip: self.PushClip() self.ClipRect(pts) self.properties.ExecuteFill(self, self.fill_rect) if not clip: self.PopClip() clip = 0 else: self.properties.ExecuteFill(self, self.fill_rect) apply(self.gc.FillRectangle, pts) else: self.CairoSetFill() apply(self.gc.CairoFillRectangle, pts) if self.line: if config.preferences.cairo_enabled == 0: self.properties.ExecuteLine(self) apply(self.gc.DrawRectangle, pts) else: try: self.CairoSetOutline() apply(self.gc.CairoDrawRectangle, pts) except: self.properties.ExecuteLine(self) apply(self.gc.DrawRectangle, pts) else: if self.proc_fill: if not clip: self.PushClip() self.ClipPolygon(pts) self.properties.ExecuteFill(self, self.fill_rect) if not clip: self.PopClip() clip = 0 elif self.fill: if config.preferences.cairo_enabled == 0: self.properties.ExecuteFill(self, self.fill_rect) self.gc.FillPolygon(pts, X.Convex, X.CoordModeOrigin) else: self.CairoSetFill() self.gc.CairoFillPolygon(pts) if self.line: if config.preferences.cairo_enabled == 0: self.properties.ExecuteLine(self) self.gc.DrawLines(pts, X.CoordModeOrigin) else: try: self.CairoSetOutline() self.gc.CairoDrawPolygon(pts) except: self.properties.ExecuteLine(self) self.gc.DrawLines(pts, X.CoordModeOrigin) if clip: if type(pts) == TupleType: self.ClipRect(pts) else: self.ClipPolygon(pts) def RoundedRectangle(self, trafo, radius1, radius2, clip = 0): path = _sketch.RoundedRectanglePath(trafo, radius1, radius2) self.MultiBezier((path,), None, clip) def SimpleEllipse(self, trafo, start_angle, end_angle, arc_type, rect = None, clip = 0): trafo2 = self.doc_to_win(trafo) if trafo2.m12 == 0.0 and trafo2.m21 == 0.0 and not self.proc_fill \ and start_angle == end_angle and not clip: x1, y1 = trafo2.DocToWin(1, 1) x2, y2 = trafo2.DocToWin(-1, -1) if x1 > x2: t = x1; x1 = x2; x2 = t if y1 > y2: t = y1; y1 = y2; y2 = t w = x2 - x1 h = y2 - y1 if self.fill: if config.preferences.cairo_enabled == 0: self.properties.ExecuteFill(self, self.fill_rect) self.gc.FillArc(x1, y1, w, h, 0, 23040) # 360 * 64 else: self.CairoSetFill() self.gc.CairoFillArc(x1+w/2, y1+h/2, w, h) if self.line: if config.preferences.cairo_enabled == 0: self.properties.ExecuteLine(self) self.gc.DrawArc(x1, y1, w, h, 0, 23040) else: try: self.CairoSetOutline() self.gc.CairoDrawArc(x1+w/2, y1+h/2, w, h) except: self.properties.ExecuteLine(self) self.gc.DrawArc(x1, y1, w, h, 0, 23040) else: if self.line: line = self.activate_line else: line = None if self.fill: fill = self.activate_fill else: fill = None if start_angle != end_angle: arc = _sketch.approx_arc(start_angle, end_angle, arc_type) else: arc = circle_path # pass rect as None, because trafo2 is not really the # viewport transformation if config.preferences.cairo_enabled == 0: _sketch.draw_multipath(self.gc, trafo2, line, fill, self.PushClip, self.PopClip, self.ClipRegion, None, (arc,), CreateRegion(), self.proc_fill, clip) self.draw_ellipse_arrows(trafo, start_angle, end_angle, arc_type, rect) else: if self.fill: self.CairoSetFill() _sketch.cairo_fill_multipath(self.gc, trafo2, line, fill, self.PushClip, self.PopClip, self.ClipRegion, None, (arc,), CreateRegion(), self.proc_fill, clip) if self.line: try: self.CairoSetOutline() _sketch.cairo_draw_multipath(self.gc, trafo2, line, fill, self.PushClip, self.PopClip, self.ClipRegion, None, (arc,), CreateRegion(), self.proc_fill, clip) except: _sketch.draw_multipath(self.gc, trafo2, line, fill, self.PushClip, self.PopClip, self.ClipRegion, None, (arc,), CreateRegion(), self.proc_fill, clip) def MultiBezier(self, paths, rect = None, clip = 0): if self.line: line = self.activate_line else: line = None if self.fill: fill = self.activate_fill else: fill = None if config.preferences.cairo_enabled == 0: _sketch.draw_multipath(self.gc, self.doc_to_win, line, fill, self.PushClip, self.PopClip, self.ClipRegion, rect, paths, CreateRegion(), self.proc_fill, clip) else: if self.fill: self.CairoSetFill() _sketch.cairo_fill_multipath(self.gc, self.doc_to_win, line, fill, self.PushClip, self.PopClip, self.ClipRegion, rect, paths, CreateRegion(), self.proc_fill, clip) if self.line: try: self.CairoSetOutline() _sketch.cairo_draw_multipath(self.gc, self.doc_to_win, line, fill, self.PushClip, self.PopClip, self.ClipRegion, rect, paths, CreateRegion(), self.proc_fill, clip) except: _sketch.draw_multipath(self.gc, self.doc_to_win, line, fill, self.PushClip, self.PopClip, self.ClipRegion, rect, paths, CreateRegion(), self.proc_fill, clip) if self.line: if config.preferences.cairo_enabled == 1: self.cairo_draw = 1 self.draw_arrows(paths, rect) self.cairo_draw = 0 def DrawBezierPath(self, path, rect = None): if self.cairo_draw == 0: path.draw_transformed(self.gc, self.doc_to_win, 1, 0, rect) else: path.cairo_draw_transformed(self.gc, self.doc_to_win, 1, 0, rect) def FillBezierPath(self, path, rect = None): if self.cairo_draw == 0: path.draw_transformed(self.gc, self.doc_to_win, 0, 1, rect) else: path.cairo_draw_transformed(self.gc, self.doc_to_win, 0, 1, rect) def draw_text_on_gc(self, gc, text, trafo, font, font_size, cache = None): self.PushTrafo() try: self.Concat(trafo) self.Scale(font_size) up = self.doc_to_win.DTransform(0, 1) if abs(up) >= config.preferences.greek_threshold: ptrafo = FlipY(self.doc_to_win) xlfd = font.GetXLFD(ptrafo) if (ptrafo and (ptrafo.m12 != 0 or ptrafo.m21 != 0 or ptrafo.m11 > 40 or ptrafo.m11 < 0 or ptrafo.m22 > 40 or ptrafo.m22 < 0)): xlfd = '%s[%s]' % (xlfd, _sketch.xlfd_char_range(text)) try: xfont = self.load_font(xlfd, cache) except RuntimeError, val: # We must be careful here when reporting this to the # user. If warn pops up a message box and the user # clicks OK, the window gets another expose event # and sketch tries to draw the text again which will # also fail. To avoid infinite loops we try to # report unknown fonts only once for a given font. if not self.unknown_fonts.has_key(font.PostScriptName()): warn(USER, _("Cannot load %(font)s:\n%(text)s"), font = `xlfd`, text = val) self.unknown_fonts[font.PostScriptName()] = 1 # Use a font that will hopefully be always available. # XXX Is there a better way to handle this situation? # We might try times roman with the same size and trafo xfont = self.load_font('fixed', None) gc.SetFont(xfont) pos = font.TypesetText(text) pos = map(self.DocToWin, pos) for i in range(len(text)): x, y = pos[i] gc.DrawString(x, y, text[i]) else: # 'greek'. XXX is this really necessary. It avoids # rendering fonts that are too small to read. pos = font.TypesetText(text) pos = map(self.DocToWin, pos) ux, uy = up lx = ux / 2; ly = uy / 2 uppercase = string.uppercase draw = gc.DrawLine # we should draw rectangles... for i in range(len(text)): x, y = pos[i] if text[i] in uppercase: draw(x, y, x + ux, y + uy) else: draw(x, y, x + lx, y + ly) finally: self.PopTrafo() def DrawText(self, text, trafo = None, clip = 0, cache = None): if text and self.properties.font: if self.fill or clip: if self.proc_fill or clip: bitmap, bitmapgc = self.create_clip_bitmap() self.draw_text_on_gc(bitmapgc, text, trafo, self.properties.font, self.properties.font_size, cache) if not clip: self.PushClip() self.ClipBitmap(bitmap) self.properties.ExecuteFill(self, self.fill_rect) if not self.proc_fill: w, h = bitmap.GetGeometry()[3:5] bitmap.CopyPlane(self.widget, self.gc, 0, 0, w, h, 0, 0, 1) if not clip: self.PopClip() else: self.properties.ExecuteFill(self) self.draw_text_on_gc(self.gc, text, trafo, self.properties.font, self.properties.font_size, cache) elif self.IsOutlineActive(): # in outline mode, draw text filled with the current # outline color, because we can't draw outlined text at # the moment. We could draw a rectangle, though. (?) self.properties.ExecuteLine(self) self.draw_text_on_gc(self.gc, text, trafo, self.properties.font, self.properties.font_size, cache) def ResetFontCache(self): self.font_cache = {} def load_font(self, xlfd, cache): font_cache = self.font_cache complex_text = self.complex_text if self.failed_fonts.has_key(xlfd): # the same xlfd failed before. use fixed as fallback # immediately to avoid delays. some servers apparantly take # very long to decide that they can't load a font. xlfd = 'fixed' if cache and cache.has_key(id(self)): old_xlfd, old_font = cache[id(self)] if old_xlfd == xlfd: font_cache[xlfd] = old_font return old_font if complex_text is not None: cache = complex_text.cache key = id(self), complex_text.idx if cache.has_key(key): old_xlfd, old_font = cache[key] if old_xlfd == xlfd: font_cache[xlfd] = old_font return old_font cache = None if font_cache is not None and font_cache.has_key(xlfd): font = font_cache[xlfd] else: #print 'load font', xlfd try: font = self.widget.LoadQueryFont(xlfd) except RuntimeError: self.failed_fonts[xlfd] = 1 raise if font_cache is not None: font_cache[xlfd] = font if cache is not None: cache[id(self)] = (xlfd, font) elif complex_text is not None: complex_text.cache[(id(self), complex_text.idx)] = (xlfd, font) return font complex_text = None def BeginComplexText(self, clip = 0, cache = None): if self.fill or clip or self.IsOutlineActive(): if cache is None: cache = {} if self.proc_fill or clip: bitmap, gc = self.create_clip_bitmap() self.complex_text = Empty(bitmap = bitmap, gc = gc, clip = clip, cache = cache, idx = 0) else: if self.fill: self.properties.ExecuteFill(self) else: # outline mode self.properties.ExecuteLine(self) self.complex_text = Empty(gc = self.gc, clip = 0, cache = cache, idx = 0) else: self.complex_text = None def DrawComplexText(self, text, trafo, font, font_size): if self.complex_text is not None: self.draw_text_on_gc(self.complex_text.gc, text, trafo, font, font_size) self.complex_text.idx = self.complex_text.idx + 1 def EndComplexText(self): if self.complex_text is not None: if self.complex_text.clip or self.proc_fill: bitmap = self.complex_text.bitmap self.PushClip() self.ClipBitmap(bitmap) self.properties.ExecuteFill(self, self.fill_rect) if not self.proc_fill: w, h = bitmap.GetGeometry()[3:5] bitmap.CopyPlane(self.widget, self.gc, 0, 0, w, h, 0, 0, 1) if not self.complex_text.clip: self.PopClip() self.complex_text = None def create_ximage(self): global use_shm_images if not self.ximage: w = self.widget if use_shm_images and not shm_images_supported: warn(INTERNAL, 'tried to use unsupported shared memory images\n') use_shm_images = 0 if use_shm_images: try: self.ximage = w.ShmCreateImage(w.depth, X.ZPixmap, None, w.width, w.height, 1) except: # Creating a shared memory image failed. Print a # message and don't use shmimages again. A likely # reason for this is that the test for shmimages in # pax succeeded but the ShmCreateImage here fails # because the limit for shm segments is too low, as # it is by default on Solaris. warn(INTERNAL, _("Can't create shared memory image: %s"), sys.exc_info()[1]) use_shm_images = 0 if not self.ximage: self.ximage = self.create_sized_ximage(w.width, w.height) def create_sized_ximage(self, width, height): w = self.widget depth = w.depth if depth > 16: bpl = 4 * width elif depth > 8: bpl = ((2 * width + 3) / 4) * 4 elif depth == 8: bpl = ((width + 3) / 4) * 4 else: raise SketchError('unsupported depth for images') return w.CreateImage(w.depth, X.ZPixmap, 0, None, width, height, 32, bpl) def DrawImage(self, image, trafo, clip = 0): w, h = image.size if self.IsOutlineActive(): self.PushTrafo() self.Concat(trafo) self.DrawRectangle(Point(0, 0), Point(w, h)) self.PopTrafo() return self.create_ximage() ximage = self.ximage if use_shm_images and self.IsClipping() and self.images_drawn: # force a shmimage to be drawn if complex clipping is done # or ShmPutImage requests might be in the queue. self.widget.Sync() self.images_drawn = 0 llx, lly = self.DocToWin(trafo.offset()) lrx, lry = self.DocToWin(trafo(w, 0)) ulx, uly = self.DocToWin(trafo(0, h)) urx, ury = self.DocToWin(trafo(w, h)) if llx == ulx and lly == lry: if llx < lrx: sx = llx; w = lrx - llx + 1 else: sx = lrx; w = lrx - llx - 1 if uly < lly: sy = uly; h = lly - uly + 1 else: sy = lly; h = lly - uly - 1 _sketch.copy_image_to_ximage(self.visual, image.im, ximage, sx, sy, w, h) if w < 0: w = -w if h < 0: h = -h if not clip: self.PushClip() self.ClipRect((sx, sy, w, h)) else: self.PushTrafo() self.Concat(trafo) self.Concat(Trafo(1, 0, 0, -1, 0, h)) inverse = self.win_to_doc dtw = self.DocToWin ulx, uly = dtw(0, 0) urx, ury = dtw(0, h) llx, lly = dtw(w, 0) lrx, lry = dtw(w, h) self.PopTrafo() sx = min(ulx, llx, urx, lrx) ex = max(ulx, llx, urx, lrx) sy = min(uly, lly, ury, lry) ey = max(uly, lly, ury, lry) if type(self.clip_region) == PaxRegionType: cx, cy, cw, ch = self.clip_region.ClipBox() cex = cx + cw; cey = cy + ch if cx >= ex or cex <= sx or cy >= ey or cey <= sy: return if cx > sx and cx < ex: sx = cx if cex < ex and cex > sx: ex = cex if cy > sy and cy < ey: sy = cy if cey < ey and cey > sy: ey = cey w = ex - sx h = ey - sy region = self.widget.CreateRegion() _sketch.transform_to_ximage(self.visual, inverse, image.im, ximage, sx, sy, w, h, region) if not clip: self.PushClip() self.ClipRegion(region) if sx+w <= 0 or sx >= ximage.width or sy+h <= 0 or sy >= ximage.height: if not clip: self.PopClip() return if sx < 0: w = w + sx sx = 0 if sx + w > ximage.width: w = ximage.width - sx if sy < 0: h = h + sy sy = 0 if sy + h > ximage.height: h = ximage.height - sy if use_shm_images: self.gc.ShmPutImage(ximage, sx, sy, sx, sy, w, h, 0) self.images_drawn = 1 else: self.gc.PutImage(ximage, sx, sy, sx, sy, w, h) if not clip: self.PopClip() def DrawEps(self, data, trafo): if not data.image or self.IsOutlineActive(): w, h = data.Size() self.PushTrafo() self.Concat(trafo) self.DrawRectangle(Point(0, 0), Point(w, h)) self.PopTrafo() else: resolution = config.preferences.eps_preview_resolution self.DrawImage(data.image, trafo(Scale(72.0 / resolution))) # # def WindowResized(self, width, height): self.ximage = None SimpleGC.WindowResized(self, width, height) # # def DrawGrid(self, orig_x, orig_y, xwidth, ywidth, rect): # Draw a grid with a horitontal width XWIDTH and vertical width # YWIDTH whose origin is at (ORIG_X, ORIG_Y). RECT gives the # region of the document for which the grid has to be drawn. # RECT is usually the parameter of the same name of the # Draw/DrawShape methods of the various graphics objects and the # document/layer Note: This functions assumes that doc_to_win is # in its initial state self.SetProperties(self.grid_style) xwinwidth = self.LengthToWinFloat(xwidth) if not xwinwidth: if __debug__: pdebug(None, 'GraphicsDevice.DrawGrid: zero winwidth') return ywinwidth = self.LengthToWinFloat(ywidth) if not ywinwidth: if __debug__: pdebug(None, 'GraphicsDevice.DrawGrid: zero winwidth') return # make the minimum distance between drawn points at least 5 # pixels XXX: should be configurable if xwinwidth < 5: xwinwidth = (int(5.0 / xwinwidth) + 1) * xwinwidth xwidth = self.LengthToDoc(xwinwidth) if ywinwidth < 5: ywinwidth = (int(5.0 / ywinwidth) + 1) * ywinwidth ywidth = self.LengthToDoc(ywinwidth) startx = int((rect.left - orig_x) / xwidth) * xwidth + orig_x starty = int((rect.top - orig_y) / ywidth) * ywidth + orig_y winx, winy = self.DocToWinPoint((startx, starty)) nx = int((rect.right - rect.left) / xwidth) + 2 ny = int((rect.top - rect.bottom) / ywidth) + 2 if self.line: self.properties.ExecuteLine(self) _sketch.DrawGrid(self.gc, winx, winy, xwinwidth, ywinwidth, nx, ny) def DrawGuideLine(self, point, horizontal): temp_scale=self.scale self.scale=1 if self.line: self.properties.ExecuteLine(self) self.gc.line_style = X.LineOnOffDash self.gc.dashes = 5 x, y = self.DocToWin(point) if horizontal: self.gc.DrawLine(0, y, self.widget.width, y) else: self.gc.DrawLine(x, 0, x, self.widget.height) self.gc.line_style = X.LineSolid self.scale=temp_scale def DrawPageOutline(self, width, height): # Draw the outline of the page whose size is given by width and # height. The page's lower left corner is at (0,0) and its upper # right corner at (width, height) in doc coords. The outline is # drawn as a rectangle with a thin shadow. self.gc.line_width = 0 self.gc.line_style = X.LineSolid left, bottom = self.DocToWin(0, 0) right, top = self.DocToWin(width, height) sw = 5 # shadow width XXX: should be configurable ? w = right - left h = bottom - top self.SetFillColor(StandardColors.gray) self.gc.FillRectangles([(left + sw, bottom, w + 1, sw + 1), (right, top + sw, sw + 1, h + 1)]) self.SetFillColor(StandardColors.black) self.gc.DrawRectangle(left, top, w, h) # # Class InvertingDevice # # Draws objects always in outline mode, regardless of the object's # properties. Also draws with function = GXxor. # # This class defines a few additional drawing methods that are used by # the primitives Rectangle and PolyBezier during interactive creation # and dragging # # DummyLineStyle is needed for the InvertingDevice and the HitTestDevice class DummyAttr(PropertyStack): def ExecuteLine(self, gc): pass dummyLineStyle = DummyAttr(SolidLine(color_0)) class InvertingDevice(GraphicsDevice): normal_line_style = X.LineSolid handle_line_style = X.LineOnOffDash caret_line_style = X.LineSolid caret_line_width = 2 def __init__(self): GraphicsDevice.__init__(self) self.handle_size = 3 self.small_handle_size = 2 self.properties = dummyLineStyle self.fill = 0 self.line = 1 self.gc = None self.font_cache = {} def init_gc(self, widget, **gcargs): self.visual = color.skvisual line_width = config.preferences.editor_line_width self.gc = widget.CreateGC(foreground = ~0, function = X.GXxor, background = 0, line_width = line_width, line_style = self.normal_line_style) self.widget = widget # make sure that the properties are not changed def SetProperties(self, properties, rect = None): pass def IsOutlineActive(self): return 1 # Bezier and Line are currently only needed for the bezier objects # during a drag def Bezier(self, p1, p2, p3, p4): dtw = self.DocToWin pts = dtw(p1) + dtw(p2) + dtw(p3) + dtw(p4) apply(_sketch.DrawBezier, (self.gc,) + pts) def Line(self, p1, p2): if self.line: startx, starty = self.DocToWin(p1) endx, endy = self.DocToWin(p2) self.gc.DrawLine(startx, starty, endx, endy) # draw a rectangular 'handle' at P. The size of the handle is given # by self.handle_size and is always in window coordinates (i.e. is # independent of scaling) def DrawRectHandle(self, p, filled = 1): x, y = self.DocToWin(p) size = self.handle_size x = x - size y = y - size if filled: self.gc.FillRectangle(x, y, 2 * size + 1, 2 * size + 1) else: self.gc.DrawRectangle(x, y, 2 * size, 2 * size) def DrawSmallRectHandle(self, p, filled = 1): x, y = self.DocToWin(p) size = self.small_handle_size x = x - size y = y - size if filled: self.gc.FillRectangle(x, y, 2 * size + 1, 2 * size + 1) else: self.gc.DrawRectangle(x, y, 2 * size, 2 * size) def DrawCircleHandle(self, p, filled = 1): x, y = self.DocToWin(p) size = self.handle_size x = x - size y = y - size if filled: # 23040 = 360 * 64 self.gc.FillArc(x, y, 2 * size + 1, 2 * size + 1, 0, 23040) else: self.gc.DrawArc(x, y, 2 * size, 2 * size, 0, 23040) def DrawSmallCircleHandle(self, p, filled = 1): x, y = self.DocToWin(p) size = self.small_handle_size x = x - size y = y - size if filled: # 23040 = 360 * 64 self.gc.FillArc(x, y, 2 * size + 1, 2 * size + 1, 0, 23040) else: self.gc.DrawArc(x, y, 2 * size, 2 * size, 0, 23040) def DrawSmallRectHandleList(self, pts, filled = 1): pts = map(self.doc_to_win.DocToWin, pts) size = self.small_handle_size size2 = 2 * size if filled: size = size + 1 rects = [] pts.sort() lx = ly = None for x, y in pts: if y != ly or x != lx: rects.append((x - size, y - size, size2, size2)) lx = x ly = y if rects: if filled: self.gc.FillRectangles(rects) else: self.gc.DrawRectangles(rects) def DrawHandleLine(self, start, end): self.gc.line_style = self.handle_line_style self.DrawLine(start, end) self.gc.line_style = self.normal_line_style def DrawRubberRect(self, start, end): self.gc.line_style = self.handle_line_style self.DrawRectangle(start, end) self.gc.line_style = self.normal_line_style def DrawPixmapHandle(self, p, pixmap): x, y = self.DocToWin(p) width, height = pixmap.GetGeometry()[3:5] x = x - width / 2 y = y - height / 2 pixmap.CopyPlane(self.widget, self.gc, 0, 0, width, height, x, y, 1) def DrawCaretHandle(self, p, up): line_width = self.gc.line_width self.gc.line_width = self.caret_line_width self.gc.line_style = self.caret_line_style self.DrawLine(p, p + up) self.gc.line_style = self.normal_line_style self.gc.line_width = line_width # # Class HitTestDevice # pixmap_width_2 = 4 pixmap_width = 2 * pixmap_width_2 + 1 hit_properties = PropertyStack() hit_properties.SetProperty(fill_pattern = SolidPattern(color_1)) hit_properties.AddStyle(SolidLine(color_1, width = 3)) class HitTestDevice(GraphicsDevice): outline_style = hit_properties def __init__(self): GraphicsDevice.__init__(self) self.properties = hit_properties self.fill = 1 self.line = 1 self.gc = None def init_gc(self, widget, **gcargs): self.pixmap = widget.CreatePixmap(pixmap_width, pixmap_width, 1) self.gc = self.pixmap.CreateGC(foreground = 1, line_width = 3) self.visual = color.skvisual # make sure that the properties are not changed def SetProperties(self, properties, rect = None): if properties.HasLine(): self.line_width = properties.line_width else: self.line_width = 0 # # def SetViewportTransform(self, scale, doc_to_win, win_to_doc): GraphicsDevice.SetViewportTransform(self, scale, doc_to_win, win_to_doc) self.hit_radius_doc = self.LengthToDoc(self.hit_radius) hit_properties.SetProperty(line_width = self.hit_radius_doc * 2) # # Detect various `hits' # hit_radius = 2 def SetHitRadius(self, radius): self.hit_radius = radius def HitRadiusDoc(self): return self.LengthToDoc(self.hit_radius) def HitRectAroundPoint(self, p): rad = self.HitRadiusDoc() return Rect(p.x - rad, p.y - rad, p.x + rad, p.y + rad) def LineHit(self, start, end, p, line_width = 0): radius = self.hit_radius / self.scale if line_width: w = line_width / 2 if radius < w: radius = w # check bounding box if p.x < min(start.x, end.x) - radius: return 0 if p.x > max(start.x, end.x) + radius: return 0 if p.y < min(start.y, end.y) - radius: return 0 if p.y > max(start.y, end.y) + radius: return 0 # check if line is hit try: d = end - start len = abs(d) if len < 1: return abs(start.x - p.x) <= radius \ and abs(start.y - p.y) <= radius off = p - start dist = abs((float(off.x) * d.y - float(off.y) * d.x) / len) linepos = (off * d) / len return dist <= radius and linepos > 0 and linepos < len except OverflowError: warn(INTERNAL, 'HitTestDevice.LineHit: start = %s end = %s p = %s', start, end, p) return 0 def ParallelogramHit(self, p, trafo, maxx, maxy, filled, properties=None, ignore_outline_mode = 0): filled = filled and (ignore_outline_mode or not self.outline_mode) if filled: try: inverse = trafo.inverse() x, y = inverse(p) return 0 <= x <= maxx and 0 <= y <= maxy except SingularMatrix: if properties is not None and properties.HasLine(): properties = defaultLineStyle return self.ParallelogramHit(p, trafo, maxx, maxy, 0, properties) if self.outline_mode or (properties is not None and properties.HasLine()): p1 = trafo.offset() p2 = trafo(0, maxy) p3 = trafo(maxx, 0) p4 = trafo(maxx, maxy) if self.outline_mode: line_width = 0 else: line_width = properties.line_width return self.LineHit(p1, p2, p, line_width)\ or self.LineHit(p1, p3, p, line_width) \ or self.LineHit(p2, p4, p, line_width) \ or self.LineHit(p3, p4, p, line_width) def SimpleEllipseHit(self, p, trafo, start_angle, end_angle, arc_type, properties, filled, ignore_outline_mode = 0): # Hmm, the ellipse is not that simple anymore, maybe we should # change the name? filled = filled and (ignore_outline_mode or not self.outline_mode) try: inverse = trafo.inverse() p2 = inverse(p) dist, phi = p2.polar() has_line = properties.HasLine() or self.outline_mode if has_line: # Check whether p is on the outline of the complete # ellipse. This check is not entirely correct, but it # works well enough for now. if not self.outline_mode: line_width = properties.line_width else: line_width = 0 d = max(line_width / 2, self.HitRadiusDoc()) d = abs(inverse.DTransform(Point(d, 0))) border_hit = abs(1.0 - dist) < d else: border_hit = 0 if start_angle == end_angle: # The most common case: a complete ellipse if filled and dist <= 1.0: # p is inside of the ellipse -> Hit! return 1 # Either ellipse is not filled or dist > 1.0. NOw it # only depends on the outline. return border_hit else: # The ellipse is not complete. Now it depends on the # arc_type. if phi < 0: phi = phi + pi + pi # map phi into the range 0 - 2*PI if start_angle < end_angle: between = start_angle <= phi <= end_angle else: between = start_angle <= phi or phi <= end_angle center = trafo.offset() start = Polar(start_angle) end = Polar(end_angle) if arc_type == ArcPieSlice: if between: # p is somewhere in the painted sector. Just # like for a full ellipse: if filled and dist <= 1: return 1 return border_hit else: # p is outside of the painted sector. It might # still be on the lines: if has_line: if (self.LineHit(center, trafo(start), p, line_width) or self.LineHit(center, trafo(end), p, line_width)): return 1 # no line was hit return 0 else: # ArcArc or ArcChord. if filled: # this is identical for both arc_types. # Strategy: p is inside of the ellipse if it is # in the intersection of the full ellipse the # half plane defined by the line through start # and end (the plane to the left of the vector # (start - end)). v = start - end d = p2 - end in_plane = (v.x * d.y - v.y * d.x) > 0 if dist <= 1 and in_plane: return 1 # if between and border_hit: return 1 if has_line and arc_type == ArcChord: return self.LineHit(trafo(start), trafo(end), p, line_width) return 0 except SingularMatrix: # ellipse degenerates into a line # XXX we should use the eigenvectors. The following code is # incorrect. start = trafo.offset() right = Point(trafo.m11, trafo.m21) up = Point(trafo.m12, trafo.m22) if abs(up) > abs(right): dir = up else: dir = right return self.LineHit(start - dir, start + dir, p, properties.line_width) def MultiBezierHit(self, paths, p, properties, filled, ignore_outline_mode = 0): x, y = self.DocToWin(p) filled = filled and (ignore_outline_mode or not self.outline_mode) result = _sketch.test_transformed(paths, self.doc_to_win, x, y, filled) if properties.HasLine(): line_width = properties.line_width else: line_width = 0 if result or self.outline_mode or self.LengthToWin(line_width) <= 1: return result odtw = self.doc_to_win self.doc_to_win = Trafo(odtw.m11, odtw.m21, odtw.m12, odtw.m22, -x + pixmap_width_2 + odtw.v1, -y + pixmap_width_2 + odtw.v2) top_left = self.WinToDoc(x - pixmap_width_2 - 1, y - pixmap_width_2 - 1) bot_right = self.WinToDoc(x + pixmap_width_2 + 1, y + pixmap_width_2 + 1) rect = Rect(top_left, bot_right) self.gc.function = X.GXclear self.gc.FillRectangle(0, 0, pixmap_width + 1, pixmap_width + 1) self.gc.function = X.GXcopy self.fill = 0 line_width = max(line_width, hit_properties.line_width) undo = self.properties.SetProperty(line_width = line_width, line_pattern = SolidPattern(color_1)) self.MultiBezier(paths, rect) self.doc_to_win = odtw Undo(undo) self.fill = 1 return _sketch.GetPixel(self.gc, pixmap_width_2, pixmap_width_2) # # Initialization that can only be done after widgets were realized # _init_from_widget_done = 0 def check_for_shm_images(widget): global shm_images_supported shm_images_supported = 0 try: img = widget.ShmCheckExtension() except RuntimeError, exc: print "Exception in ShmCheckExtension:", exc img = None if img is not None: if img.depth not in (15, 16, 24, 32, 8): # XXX: warn print 'depth =', img.depth return if img.format != X.ZPixmap: # XXX: warn print 'format =', img.format return shm_images_supported = 1 def InitFromWidget(widget): global _init_from_widget_done, use_shm_images if not _init_from_widget_done: color.InitFromWidget(widget) # make certain that color is initialized check_for_shm_images(widget) if shm_images_supported: warn(INTERNAL, 'shared memory images supported') use_shm_images = 1 else: warn(INTERNAL, 'shared memory images not supported') use_shm_images = 0 _init_from_widget_done = 1 UniConvertor-1.1.4/src/app/Graphics/papersize.py0000775000076400007640000000550611060500565020340 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2003-2006 by Igor E. Novikov # Copyright (C) 1997, 1998, 2000 by Bernhard Herzog # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. # # Define some standard Papersizes # # This module exports the following variables: # # Papersize: # # A dictionary that maps the name of paper formats such as 'letter' or # 'A4' to their widths and heights. The value of an entry is a tuple # (width, height). # # # PapersizesList: # # A list containing all sizes defined in Papersize. The list items are # tuples of the form (name, width, height) # # # All sizes are given in units of PostScript points (1/72 inch) # from app.Lib.units import m_to_pt, in_to_pt # Compute the European paper sizes. # # While this is a cute approach, it also is less accurate than simply # listing the papersizes because the standard defines the sizes in mm. # I've left the code in here for entertainment and educational value # :-). # # The definition is that A0 has an area of 1m^2 and its sides have a # ratio of sqrt(2). A1 is A0 cut in half and so on. Therefore A0's # height is 2**0.25 and its width 0.5**0.25 (both in meters) def _european_sizes(): sizes = [] width = 0.5**0.25 height = 2.0**0.25 for i in range(8): sizes.append(('A' + `i`, width * m_to_pt, height * m_to_pt)) width, height = height / 2, width return sizes[3:] # The more accurate way to specify the european sizes, contributed by # Martin Glaser: _din_sizes = [ ('A0', 0.841 * m_to_pt, 1.189 * m_to_pt), ('A1', 0.594 * m_to_pt, 0.841 * m_to_pt), ('A2', 0.420 * m_to_pt, 0.594 * m_to_pt), ('A3', 0.297 * m_to_pt, 0.420 * m_to_pt), ('A4', 0.210 * m_to_pt, 0.297 * m_to_pt), ('A5', 0.148 * m_to_pt, 0.210 * m_to_pt), ('A6', 0.105 * m_to_pt, 0.148 * m_to_pt), # ('A7', 0.074 * m_to_pt, 0.105 * m_to_pt), ('B1 (ISO)', 0.707 * m_to_pt, 1.0 * m_to_pt), ('B4 (ISO)', 0.250 * m_to_pt, 0.353 * m_to_pt), ('B5 (ISO)', 0.176 * m_to_pt, 0.250 * m_to_pt), ('C3', 0.324 * m_to_pt, 0.458 * m_to_pt), ('C4', 0.229 * m_to_pt, 0.324 * m_to_pt), ('C5', 0.162 * m_to_pt, 0.229 * m_to_pt), ('C6', 0.114 * m_to_pt, 0.162 * m_to_pt), ('Visit card #1', 0.05 * m_to_pt, 0.09 * m_to_pt), ('Visit card #2', 0.055 * m_to_pt, 0.085 * m_to_pt), ('Envelope C6', 0.114 * m_to_pt, 0.162 * m_to_pt), ('Envelope E65/DL', 0.110 * m_to_pt, 0.220 * m_to_pt), ('Envelope C5', 0.162 * m_to_pt, 0.229 * m_to_pt), ('Envelope C4', 0.229 * m_to_pt, 0.324 * m_to_pt), ] _american_sizes = [ ('Letter', 8.5 * in_to_pt, 11 * in_to_pt), ('Half Letter', 5.5 * in_to_pt, 8.5 * in_to_pt), ('Legal', 8.5 * in_to_pt, 14 * in_to_pt), ('Executive', 7.25 * in_to_pt, 10.5 * in_to_pt) ] Papersize = {} PapersizesList = _din_sizes + _american_sizes for name, width, height in PapersizesList: Papersize[name] = (width, height) UniConvertor-1.1.4/src/app/__init__.py0000775000076400007640000001242611060500604016326 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2003-2006 by Igor E. Novikov # Copyright (C) 1998, 1999, 2000, 2002 by Bernhard Herzog # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. import os, sys, string path_separator='/' if os.name=='nt': path_separator='\\' _pkgdir = __path__[0] temp=string.split(_pkgdir,path_separator) temp.remove(temp[-1]) _parentdir=string.join(temp,path_separator) sKVersion = string.strip(open(os.path.join(_pkgdir, 'VERSION')).read()) for _dir in ('modules', 'Base'): __path__.insert(0, os.path.join(_pkgdir, _dir)) dir = os.path.join(_parentdir, 'app') dir = os.path.join(dir, 'modules') if os.path.isdir(dir): sys.path.insert(1, dir) message_dir = os.path.join(sys.path[0], 'Resources') message_dir = os.path.join(message_dir, 'Messages') try: from intl import gettext, dgettext, bindtextdomain import intl, locale try: locale.setlocale(locale.LC_ALL, "") except: # if we can't set the locale we might not be able to get # properly translated messages print "Can't set locale." \ " Please check your LANG and LC_* environment variables" intl.textdomain("sketch") bindtextdomain("sketch", message_dir) except ImportError: def gettext(text): return text def dgettext(domain, text): return text def bindtextdomain(*args): pass _ = gettext ####Info variables for progress info1=None info2=None info3=None info_win=None ####################################### from conf import const from _sketch import Point, Polar, PointType NullPoint = Point(0, 0) from conf.configurator import Configurator config = Configurator(base_dir=_parentdir) from managers.colormanager import ColorManager colormanager=ColorManager() from _sketch import Rect, PointsToRect, UnionRects, IntersectRects, EmptyRect, InfinityRect, RectType UnitRect = Rect(0, 0, 1, 1) from _sketch import Trafo, Scale, Translation, Rotation, SingularMatrix, TrafoType Identity = Trafo(1, 0, 0, 1, 0, 0) IdentityMatrix = Identity.matrix() from _sketch import CreatePath, RectanglePath, RoundedRectanglePath, approx_arc, CreateFontMetric, SKCache, TransformRectangle from _sketch import ContAngle, ContSmooth, ContSymmetrical, SelNone, SelNodes, SelSegmentFirst, SelSegmentLast, Bezier, Line # import config # config.init_directories(_parentdir) from events.skexceptions import * from events.undo import Undo, UndoList, CreateListUndo, CreateMultiUndo, UndoAfter, UndoRedo, NullUndo from events.connector import Connect, Disconnect, Issue, RemovePublisher, Subscribe, Publisher, QueueingPublisher def updateInfo(inf1=None,inf2=None,inf3=None): pass # def _import_PIL(): # Import PIL and work around some bugs... # First, try to import PIL as a package try: import PIL import PIL.Image # Work around a bug in PIL 1.0 when used as a package if PIL.__path__[0] not in sys.path: sys.path.append(PIL.__path__[0]) except ImportError: # Must be an older PIL. try: import Image, ImageChops except: warn.warn(warn.USER, "Can't import the Python Imaging Library") sys.exit(1) from app.plugins import plugins plugins.create_packages('PIL') import PIL PIL.__path__.append(os.path.split(Image.__file__)[0]) PIL.Image = Image PIL.ImageChops = ImageChops sys.modules['PIL.Image'] = Image sys.modules['PIL.ImageChops'] = ImageChops _import_PIL() # command_classes = [] def RegisterCommands(aclass): for cmd in aclass.commands: cmd.SetClass(aclass) command_classes.append(aclass) # from Graphics.base import GraphicsObject, Primitive from Graphics.arrow import StandardArrows, Arrow from Graphics.properties import Style, FillStyle, EmptyFillStyle, LineStyle, EmptyLineStyle, PropertyStack, EmptyProperties from Graphics.blend import MismatchError, Blend, BlendTrafo from Graphics.blendgroup import BlendGroup, CreateBlendGroup, BlendInterpolation from Graphics.color import CreateRGBColor, XRGBColor, CreateCMYKColor, StandardColors, ParseSKColor from Graphics.compound import Compound, EditableCompound from Graphics.dashes import StandardDashes from Graphics.document import EditDocument, SelectionMode, EditMode Document = EditDocument from Graphics.font import GetFont from Graphics.gradient import MultiGradient, CreateSimpleGradient from Graphics.graphics import SimpleGC, GraphicsDevice, InvertingDevice, HitTestDevice from Graphics.group import Group from Graphics.guide import GuideLine from Graphics.image import Image, load_image, ImageData from Graphics.layer import Layer, GuideLayer, GridLayer from Graphics.maskgroup import MaskGroup from Graphics.pattern import EmptyPattern, SolidPattern, HatchingPattern, LinearGradient, RadialGradient, ConicalGradient, ImageTilePattern from Graphics.plugobj import PluginCompound, TrafoPlugin from Graphics.rectangle import Rectangle, RectangleCreator from Graphics.ellipse import Ellipse, EllipseCreator from Graphics.bezier import PolyBezier, PolyBezierCreator, PolyLineCreator, CombineBeziers, CreatePath, ContAngle, ContSmooth, ContSymmetrical from Graphics.psdevice import PostScriptDevice from Graphics.text import SimpleText, SimpleTextCreator, PathText def init_lib(): from app.plugins import plugins # config.load_user_preferences() plugins.load_plugin_configuration() Issue(None, const.INITIALIZE) def init_ui(): pass def init_modules_from_widget(root): pass UniConvertor-1.1.4/src/app/plugins/0000755000076400007640000000000011211455344015674 5ustar igorigorUniConvertor-1.1.4/src/app/plugins/plugins.py0000775000076400007640000002451111211350773017737 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2003-2006 by Igor E. Novikov # Copyright (C) 1998, 1999, 2000, 2001, 2002 by Bernhard Herzog # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. import sys, os import re, imp from string import join, split from app.events.skexceptions import SketchError from app.events.warn import warn_tb, warn, USER, INTERNAL, pdebug from app import config from app import _, dgettext, gettext, bindtextdomain, message_dir, Subscribe, config, const # All plugins are loaded as modules in the _plugin_package_name package # to keep the namespaces clean _plugin_package_name = "app.Plugins" def create_packages(package): """ Return the package's module and create it and it's parents if necessary """ names = split(package, '.') for i in range(1, len(names) + 1): name = join(names[:i], '.') if not sys.modules.has_key(name): module = imp.new_module(name) module.__path__ = [] sys.modules[name] = module return sys.modules[name] class ConfigInfo: module = None package = _plugin_package_name def __init__(self, module_name, dir, version = '1.0.0', unload = None, load_immediately = 0, standard_messages = 0): self.module_name = module_name self.dir = dir self.unload = unload self.load_immediately = load_immediately self.version = version self.plugin_list.append(self) self.standard_messages = standard_messages if not self.standard_messages: bindtextdomain(self.module_name, message_dir) if self.load_immediately: self.load_module() def load_module(self): if self.module is not None: return self.module try: file, filename, desc = imp.find_module(self.module_name, [self.dir]) except: warn_tb(INTERNAL, 'Cannot find plugin module %s', self.module_name) return None try: try: create_packages(self.package) module_name = self.package + '.' + self.module_name self.module = imp.load_module(module_name, file, filename, desc) except: warn_tb(USER, _("Cannot load plugin module %s"), self.module_name) raise finally: if file is not None: file.close() self.module._ = self.nls_function() return self.module def UnloadPlugin(self): if self.unload: module_name = self.package + '.' + self.module_name try: module = sys.modules[module_name] except KeyError: # might happen if the module wasn't even loaded return self.module = None if type(self.unload) == type(""): getattr(module, self.unload)(module) else: name = module.__name__ module.__dict__.clear() del sys.modules[name] def nls_function(self): if self.standard_messages: _ = gettext else: domain = self.module_name def _(text, domain = domain): #print domain, text result = dgettext(domain, text) #print '->', result return result return _ def gettext(self, text): if self.standard_messages: return gettext(text) else: return dgettext(self.module_name, text) NativeFormat = 'sK1' import_plugins = [] class ImportInfo(ConfigInfo): plugin_list = import_plugins def __init__(self, module_name, dir, rx_magic, class_name, format_name, tk_file_type = (), version = '1.0.0', unload = None, load_immediately = 0, standard_messages = 0): ConfigInfo.__init__(self, module_name, dir, version = version, unload = unload, load_immediately = load_immediately, standard_messages = standard_messages) self.rx_magic = re.compile(rx_magic) self.class_name = class_name self.format_name = format_name self.tk_file_type = tk_file_type self.translate() def translate(self): name, ext = self.tk_file_type name = self.gettext(name) self.tk_file_type = name, ext def __call__(self, *args, **kw): try: module = self.load_module() if module is not None: return apply(getattr(module, self.class_name), args, kw) except: warn_tb(INTERNAL, 'When importing plugin %s', self.module_name) raise SketchError('Cannot load filter %(name)s.%(message)s' % {'name':self.module_name, 'message':self.class_name}) def UnloadPlugin(self): if config.preferences.unload_import_filters: ConfigInfo.UnloadPlugin(self) export_plugins = [] export_formats = {} class ExportInfo(ConfigInfo): plugin_list = export_plugins def __init__(self, module_name, dir, format_name, tk_file_type = (), extensions = (), version = '1.0.0', unload = None, load_immediately = 0, standard_messages = 0): ConfigInfo.__init__(self, module_name, dir, version = version, unload = unload, load_immediately = load_immediately, standard_messages = standard_messages) self.format_name = format_name self.tk_file_type = tk_file_type if type(extensions) != type(()): extensions = (extensions,) self.extensions = extensions export_formats[format_name] = self def translate(self): name, ext = self.tk_file_type name = self.gettext(name) self.tk_file_type = name, ext def __call__(self, document, filename, file = None, options = None): if options is None: options = {} try: module = self.load_module() except: warn_tb(INTERNAL, 'When importing plugin %s', self.module_name) raise SketchError(_("Cannot load filter %(name)s") % {'name':self.module_name}) if file is None: file = open(filename, 'wb') close = 1 else: close = 0 if module is not None: module.save(document, file, filename, options) if close: file.close() if self.format_name == NativeFormat: document.ClearEdited() self.UnloadPlugin() def UnloadPlugin(self): if config.preferences.unload_import_filters: ConfigInfo.UnloadPlugin(self) def find_export_plugin(name): return export_formats.get(name) def guess_export_plugin(extension): for plugin in export_plugins: if extension in plugin.extensions: return plugin.format_name return '' object_plugins = {} compound_plugins = [] class PluginCompoundInfo(ConfigInfo): plugin_list = compound_plugins def __init__(self, module_name, dir, class_name, menu_text, factory = '', version = '1.0.0', parameters = (), uses_selection = 0, custom_dialog = '', load_immediately = 0, standard_messages = 0): ConfigInfo.__init__(self, module_name, dir, version = version, load_immediately = load_immediately, standard_messages = standard_messages) self.class_name = class_name self.factory = factory self.menu_text = menu_text self.parameters = parameters self.uses_selection = uses_selection self.custom_dialog = custom_dialog object_plugins[class_name] = self self.translate() def translate(self): gettext = self.gettext self.menu_text = gettext(self.menu_text) parameters = [] for parameter in self.parameters: parameters.append(parameter[:-1] + (gettext(parameter[-1]),)) self.parameters = parameters def load_module_attr(self, attr): try: module = self.load_module() if module is not None: return getattr(module, attr) except: warn_tb(INTERNAL, 'When importing plugin %s', self.module_name) raise SketchError(_("Cannot load plugin %(name)s.%(attr)s") % {'name':self.module_name, 'attr':attr}) def Constructor(self): return self.load_module_attr(self.class_name) def CallFactory(self, *args, **kw): if self.factory: attr = self.factory else: attr = self.class_name return apply(self.load_module_attr(attr), args, kw) __call__ = CallFactory def HasParameters(self): return len(self.parameters) def UsesSelection(self): return self.uses_selection def HasCustomDialog(self): return self.custom_dialog def CreateCustomDialog(self, root, mainwindow, document): dialog = self.load_module_attr(self.custom_dialog) return dialog(root, mainwindow, document) def find_object_plugin(name): return object_plugins.get(name) config_types = {'Import': ImportInfo, 'Export': ExportInfo, 'PluginCompound': PluginCompoundInfo } def extract_cfg(file): rx_start = re.compile('^###Sketch[ \t]+Config') rx_end = re.compile('^###End') file = open(file, 'r') cfg = [] for line in file.readlines(): if rx_end.match(line): break elif cfg or rx_start.match(line): if line[0] == '#': line = line[1:] if line[-2] == '\\': line = line[:-2] cfg.append(line) return join(cfg, '') def _search_dir(dir, recurse, package = 'app.Plugins'): try: files = os.listdir(dir) except os.error, value: warn(USER, _("Cannot list directory %(filename)s\n%(message)s"), filename = dir, message = value[1]) return for file in files: filename = os.path.join(dir, file) if os.path.isdir(filename): if file == "Lib": # A Lib directory on the plugin path. It's assumed to # hold library files for some plugin. Append it to the # current package's .Lib package's __path__ so that it's # modules can be imported by prefixing their names with # Lib. lib_pkg = create_packages(package + '.Lib') lib_pkg.__path__.append(filename) elif recurse: # an ordinary directory and we should recurse into it to # find more modules, so do that. _search_dir(filename, recurse - 1, package + '.' + file) elif filename[-3:] == '.py': try: module_name = os.path.splitext(os.path.basename(filename))[0] vars = {'_':_} # hack cfg = extract_cfg(filename) exec cfg in config_types, vars infoclass = vars.get('type') if infoclass is None: warn(USER, _("No plugin-type information in %(filename)s"), filename = filename) else: del vars['type'] del vars['_'] info = apply(infoclass, (module_name, dir), vars) info.package = package except: warn_tb(INTERNAL, 'In config file %s', filename) warn(USER, _("can't read configuration information from " "%(filename)s"), filename = filename) def load_plugin_configuration(): #path): if __debug__: import time start = time.clock() path = config.plugin_path for dir in path: # XXX unix specific if len(dir) >= 2 and dir[-1] == '/': if dir[-2] == '/': recurse = -1 else: recurse = 1 else: recurse = 0 _search_dir(dir, recurse) if __debug__: pdebug('timing', 'time to scan cfg files: %g', time.clock()-start) # rearrange import plugins to ensure that native format is first for loader in import_plugins: if loader.format_name == NativeFormat: import_plugins.remove(loader) import_plugins.insert(0, loader) Subscribe(const.INITIALIZE, load_plugin_configuration) UniConvertor-1.1.4/src/app/plugins/Filters/0000755000076400007640000000000011211455344017304 5ustar igorigorUniConvertor-1.1.4/src/app/plugins/Filters/Lib/0000755000076400007640000000000011211455344020012 5ustar igorigorUniConvertor-1.1.4/src/app/plugins/Filters/Lib/spritefile.py0000775000076400007640000007136611060500575022553 0ustar igorigor""" spritefile.py Read a Sprite file and store the contents in an instance of a Sprite file class. (C) David Boddie 2001-2 This module may be freely distributed and modified. """ # History # # 0.10 (Fri 24th August 2001) # # First version to allow the drawfile.py module to access sprites. # # 0.11 (Tue 09th October 2001) # # Started sprite writing support. # # 0.12 (Sun 17th February 2002) # # Modified the maximum number of dots per inch for sprites with a mode # number. version = '0.12 (Sun 17th February 2002)' import struct def str2num(size, s): """Convert the integer in the string s which has to contain size bytes. Allowed sizes are 1, 2 and 4. 1-byte and 2-byte integers as unsigned and 4-byte integers as signed. All numbers were little-endian. """ formats = (" 0: i = n % 256 s = s + chr(i) # n = n / 256 n = n >> 8 size = size - 1 return s def str2num(self, size, s): return str2num(size, s) def sprite2rgb(self, file, width, height, h_words, first_bit_used, bpp, palette): # Convert sprite to RGB values if palette != []: has_palette = 1 else: has_palette = 0 rgb = '' ptr = file.tell()*8 # bit offset for j in range(0, height): row = '' row_ptr = ptr + first_bit_used # bit offset into the image for i in range(0, width): file.seek(row_ptr >> 3, 0) # Conversion depends on bpp value if bpp == 32: red = ord(file.read(1)) green = ord(file.read(1)) blue = ord(file.read(1)) row_ptr = row_ptr + 32 elif bpp == 16: value = self.str2num(2, file.read(2)) red = int( (value & 0x1f) * scale16 ) green = int( ((value >> 5) & 0x1f) * scale16 ) blue = int( ((value >> 10) & 0x1f) * scale16 ) row_ptr = row_ptr + 16 elif bpp == 8: if has_palette == 0: # Standard VIDC 256 colours value = ord(file.read(1)) red = ((value & 0x10) >> 1) | (value & 7) green = ((value & 0x40) >> 3) | \ ((value & 0x20) >> 3) | (value & 3) blue = ((value & 0x80) >> 4) | \ ((value & 8) >> 1) | (value & 3) red = int( red * scale8 ) green = int( green * scale8 ) blue = int( blue * scale8 ) else: # 256 entry palette value = ord(file.read(1)) red, green, blue = palette[value][0] row_ptr = row_ptr + 8 elif bpp == 4: value = ( ord(file.read(1)) >> (row_ptr % 8) ) & 0xf if has_palette == 0: # Standard 16 desktop colours # Look up the value in the standard palette red, green, blue = self.palette16[value] else: # 16 entry palette red, green, blue = palette[value][0] row_ptr = row_ptr + 4 elif bpp == 2: value = (ord(file.read(1)) >> (row_ptr % 8) ) & 0x3 if has_palette == 0: # Greyscales red, green, blue = self.palette4[value] else: # 4 entry palette red, green, blue = palette[value][0] row_ptr = row_ptr + 2 elif bpp == 1: value = (ord(file.read(1)) >> (row_ptr % 8) ) & 1 if has_palette == 0: # Black and white red = green = blue = (255*(1-value)) else: # 2 entry palette red, green, blue = palette[value][0] row_ptr = row_ptr + 1 row = row + chr(red) + chr(green) + chr(blue) rgb = rgb + row ptr = ptr + (h_words * 32) return rgb def mask2byte(self, file, width, height, bpp): mask = '' ptr = file.tell()*8 # bit offset image_ptr = 0 if bpp == 32 or bpp == 16: bpp = 1 # Colour depths below 16 bpp have the same number of bpp in the mask bits = bpp * width row_size = bits >> 5 # number of words if bits % 32 != 0: row_size = row_size + 1 for j in range(0, height): row = '' row_ptr = ptr # bit offset into the image for i in range(0, width): file.seek(row_ptr >> 3, 0) # Conversion depends on bpp value if bpp == 32: value = (ord(file.read(1)) >> (row_ptr % 8)) & 1 value = value * 0xff row_ptr = row_ptr + 1 elif bpp == 16: value = (ord(file.read(1)) >> (row_ptr % 8)) & 1 value = value * 0xff row_ptr = row_ptr + 1 elif bpp == 8: value = ord(file.read(1)) row_ptr = row_ptr + 8 elif bpp == 4: value = ( ord(file.read(1)) >> (row_ptr % 8) ) & 0xf value = value | (value << 4) row_ptr = row_ptr + 4 elif bpp == 2: value = ( ord(file.read(1)) >> (row_ptr % 8) ) & 0x3 if value == 3: value = 0xff row_ptr = row_ptr + 2 elif bpp == 1: # Black and white value = (ord(file.read(1)) >> (row_ptr % 8) ) & 1 value = value * 0xff row_ptr = row_ptr + 1 row = row + chr(value) mask = mask + row ptr = ptr + (row_size * 32) return mask def mask2rgba(self, file, width, height, bpp, image): rgba = '' ptr = file.tell()*8 # bit offset image_ptr = 0 if bpp == 32 or bpp == 16: bpp = 1 # Colour depths below 16 bpp have the same number of bpp in the mask bits = bpp * width row_size = bits >> 5 # number of words if bits % 32 != 0: row_size = row_size + 1 for j in range(0, height): row = '' row_ptr = ptr # bit offset into the image for i in range(0, width): file.seek(row_ptr >> 3, 0) # Conversion depends on bpp value if bpp == 32: value = (ord(file.read(1)) >> (row_ptr % 8)) & 1 value = value * 0xff row_ptr = row_ptr + 1 elif bpp == 16: value = (ord(file.read(1)) >> (row_ptr % 8)) & 1 value = value * 0xff row_ptr = row_ptr + 1 elif bpp == 8: value = ord(file.read(1)) row_ptr = row_ptr + 8 elif bpp == 4: value = ( ord(file.read(1)) >> (row_ptr % 8) ) & 0xf value = value | (value << 4) row_ptr = row_ptr + 4 elif bpp == 2: value = ( ord(file.read(1)) >> (row_ptr % 8) ) & 0x3 if value == 3: value = 0xff row_ptr = row_ptr + 2 elif bpp == 1: # Black and white value = (ord(file.read(1)) >> (row_ptr % 8) ) & 1 value = value * 0xff row_ptr = row_ptr + 1 row = row + image[image_ptr:image_ptr+3] + chr(value) image_ptr = image_ptr + 3 rgba = rgba + row ptr = ptr + (row_size * 32) return rgba def read_details(self, file, offset): # Go to start of this sprite file.seek(offset, 0) next = self.str2num(4, file.read(4)) # We will return a data dictionary data = {} n = file.read(12) name = '' for i in n: if ord(i)>32: name = name + i else: break h_words = self.str2num(4, file.read(4)) + 1 v_lines = self.str2num(4, file.read(4)) + 1 data['h_words'] = h_words data['v_lines'] = v_lines first_bit_used = self.str2num(4, file.read(4)) last_bit_used = self.str2num(4, file.read(4)) data['first bit'] = first_bit_used data['last bit'] = last_bit_used image_ptr = offset + self.str2num(4, file.read(4)) mask_ptr = offset + self.str2num(4, file.read(4)) mode = self.str2num(4, file.read(4)) bpp = (mode >> 27) if bpp == 0: mode = mode & 0x3f # Information on commonly used modes if self.mode_info.has_key(mode): log2bpp, xscale, yscale = self.mode_info[mode] #xdpi = int(180/xscale) # was 90 #ydpi = int(180/yscale) # was 90 xdpi = int(90/xscale) # Old modes have a maximum of ydpi = int(90/yscale) # 90 dots per inch. bpp = 1 << log2bpp else: raise spritefile_error, 'Unknown mode number.' else: if bpp == 1: log2bpp = 0 elif bpp == 2: log2bpp = 1 elif bpp == 3: bpp = 4 log2bpp = 2 elif bpp == 4: bpp = 8 log2bpp = 3 elif bpp == 5: bpp = 16 log2bpp = 4 elif bpp == 6: bpp = 32 log2bpp = 5 else: return xdpi = ((mode >> 1) & 0x1fff) ydpi = ((mode >> 14) & 0x1fff) data['bpp'] = bpp data['log2bpp'] = log2bpp data['dpi x'] = xdpi data['dpi y'] = ydpi has_palette = 0 palette = [] # Read palette, if present, putting the values into a list while file.tell() < image_ptr: file.seek(1,1) # skip a byte # First entry (red, green, blue) entry1 = (ord(file.read(1)), ord(file.read(1)), ord(file.read(1))) file.seek(1,1) # skip a byte # Second entry (red, green, blue) entry2 = (ord(file.read(1)), ord(file.read(1)), ord(file.read(1))) palette.append( ( entry1, entry2 ) ) if palette != []: if bpp == 8 and len(palette) < 256: if len(palette) == 16: # Each four pairs of entries describes the variation # in a particular colour: 0-3, 4-7, 8-11, 12-15 # These sixteen colours describe the rest of the 256 # colours. for j in range(16, 256, 16): for i in range(0, 16): entry1, entry2 = palette[i] # Generate new colours using the palette # supplied for the first 16 colours red = (((j + i) & 0x10) >> 1) | (entry1[0] >> 4) green = (((j + i) & 0x40) >> 3) | \ (((j + i) & 0x20) >> 3) | (entry1[1] >> 4) blue = (((j + i) & 0x80) >> 4) | (entry1[2] >> 4) red = int( red * scale8 ) green = int( green * scale8 ) blue = int( blue * scale8 ) # Append new entries palette.append( ( (red, green, blue), (red, green, blue) ) ) elif len(palette) == 64: for j in range(64, 256, 64): for i in range(0, 64): entry1, entry2 = palette[i] red = (((j + i) & 0x10) >> 1) | (entry1[0] >> 4) green = (((j + i) & 0x40) >> 3) | \ (((j + i) & 0x20) >> 3) | (entry1[1] >> 4) blue = (((j + i) & 0x80) >> 4) | (entry1[2] >> 4) red = int( red * scale8 ) green = int( green * scale8 ) blue = int( blue * scale8 ) # Append new entries palette.append( ( (red, green, blue), (red, green, blue) ) ) data['palette'] = palette else: data['palette'] = palette width = (h_words * (32 >> log2bpp)) - (first_bit_used >> log2bpp) - \ ((31-last_bit_used) >> log2bpp) height = v_lines data['width'] = width data['height'] = height # Obtain image data file.seek(image_ptr, 0) data['image'] = self.sprite2rgb(file, width, height, h_words, first_bit_used, bpp, palette) data['mode'] = 'RGB' # Obtain mask data if mask_ptr != image_ptr: file.seek(mask_ptr, 0) data['image'] = self.mask2rgba(file, width, height, bpp, data['image']) data['mode'] = 'RGBA' return name, data, next def read(self, file): file.seek(0,2) size = file.tell() file.seek(0,0) # Examine the sprites number = self.str2num(4, file.read(4)) offset = self.str2num(4, file.read(4)) - 4 free = self.str2num(4, file.read(4)) - 4 self.sprites = {} while (offset < free): name, data, next = self.read_details(file, offset) self.sprites[name] = data offset = offset + next def rgb2sprite(self, name): data = self.sprites[name] # Number of bits per pixel in the original sprite bpp = data['bpp'] # If the sprite didn't have a palette then use a standard one if data.has_key('palette'): # Explicitly defined palette has_palette = 1 palette = data['palette'] else: # Standard palette - invert the built in palette if bpp == 4: palette = self.palette4 elif bpp == 2: palette = self.palette2 else: palette = [] # There is no explicitly defined palette has_palette = 0 # Image data image = data['image'] # Storage mode: RGB or RGBA mode = data['mode'] # Sprite and mask strings sprite = '' mask = '' # If there was either a palette specified or a standard one used # then create an inverse. if palette != []: # Create inverse palette dictionary inverse = {} for i in range(0, len(palette)): inverse[palette[i]] = i # Write the image data to the sprite and mask ptr = 0 for j in range(0, data['height']): sprite_word = 0 mask_word = 0 sprite_ptr = 0 mask_ptr = 0 for i in range(0, data['width']): # Read the red, green and blue components r = ord(image[ptr]) g = ord(image[ptr + 1]) b = ord(image[ptr + 2]) if mode == 'RGBA': a = image[ptr + 3] ptr = ptr + 4 else: # No alpha component ptr = ptr + 3 # Write the pixels to the sprite and mask if bpp == 32: # Store the components in the sprite word sprite_word = r | (g << 8) | (b << 24) sprite_ptr = 32 # Store mask data if relevant if mode == 'RGBA': mask_word = mask_word | ((a == 255) << mask_ptr) mask_ptr = mask_ptr + 1 elif bpp == 16: # Convert the components to the relevant form half_word = int(r/scale16) | \ (int(g/scale16) << 5) | \ (int(b/scale16) << 10) sprite_word = sprite_word | (half_word << sprite_ptr) sprite_ptr = sprite_ptr + 16 # Store mask data if relevant if mode == 'RGBA': mask_word = mask_word | ((a == 255) << mask_ptr) mask_ptr = mask_ptr + 1 elif bpp == 8: # If there is a palette then look up the colour index # in the inverse palette dictionary if palette != []: index = inverse[(r, g, b)] else: # Standard palette red = int(r/scale8) green = int(g/scale8) blue = int(b/scale8) index = ((red & 0x8) << 1) | (red & 0x4) | \ ((green & 0x8) << 3) | ((green & 0x4) << 3) | \ ((blue & 0x8) << 4) | ((blue & 0x4) << 1) | \ int((red + green + blue) / 15.0) # Store the contents in the sprite word sprite_word = sprite_word | (index << sprite_ptr) sprite_ptr = sprite_ptr + 8 # Store mask data if mode == 'RGBA': if a != 0xff: a = 0 mask_word = mask_word | (a << mask_ptr) mask_ptr = mask_ptr + 8 elif bpp == 4: # Look up bit state in inverse palette index = inverse[(r, g, b)] # Store the contents in the sprite word sprite_word = sprite_word | (index << sprite_ptr) sprite_ptr = sprite_ptr + 4 # Store mask data if mode == 'RGBA': if a == 0xff: a = 0xf else: a = 0 mask_word = mask_word | (a << mask_ptr) mask_ptr = mask_ptr + 4 elif bpp == 2: # Look up bit state in inverse palette index = inverse[(r, g, b)] # Store the contents in the sprite word sprite_word = sprite_word | (index << sprite_ptr) sprite_ptr = sprite_ptr + 2 # Store mask data if mode == 'RGBA': if a == 0xff: a = 0x3 else: a = 0 mask_word = mask_word | (a << mask_ptr) mask_ptr = mask_ptr + 2 elif bpp == 1: if palette != []: # Look up bit state in inverse palette bit = inverse[(r, g, b)] else: # Use red component bit = (bit == 255) # Append bit to byte sprite_word = sprite_word | (bit << sprite_ptr) sprite_ptr = sprite_ptr + 1 # Determine mask bit if present if mode == 'RGBA': mask_word = mask_word | ((a == 255) << mask_ptr) mask_ptr = mask_ptr + 1 # Write the sprite word to the sprite string if the word is # full if sprite_ptr == 32: # End of word, so reset offset, sprite_ptr = 0 # store the word in the sprite string sprite = sprite + self.number(4, sprite_word) # and reset the byte sprite_word = 0 # Do the same for the mask if mask_ptr == 32: mask_ptr = 0 mask = mask + self.number(4, mask_word) mask_word = 0 # Write any remaining sprite data to the sprite string if sprite_ptr > 0: # store the word in the sprite string sprite = sprite + self.number(4, sprite_word) # Do the same for the mask if mask_ptr > 0: mask = mask + self.number(4, mask_word) # Determine the actual number of words used per line of # the sprite width = int( (data['width'] * bpp)/32 ) excess = (data['width'] % (32/bpp)) != 0 self.sprites[name]['h_words'] = width + excess self.sprites[name]['v_lines'] = data['height'] if has_palette == 1: # Convert the palette into a string palette_string = '' for (r1, g1, b1), (r2, g2, b2) in palette: word = r1 | (g1 << 8) | (b1 << 16) palette_string = palette_string + self.number(4, word) word = r2 | (g2 << 8) | (b2 << 16) palette_string = palette_string + self.number(4, word) # Return sprite, mask and palette strings return sprite, mask, palette_string else: return sprite, mask, '' def write_details(self, name): # The details of the sprite data = self.sprites[name] # Using the bits per pixel of the image, convert the # RGB or RGBA image to an appropriate pixel format. sprite, mask, palette = self.rgb2sprite(name) # Write the sprite header minus the offset to the next sprite header = name[:12] + (12 - len(name))*'\0' + \ self.number(4, data['h_words'] - 1) + \ self.number(4, data['v_lines'] - 1) + \ self.number(4, data['first bit']) + \ self.number(4, data['last bit']) + \ self.number(4, 16 + len(palette)) if mask != '': # Offset to mask header = header + \ self.number(4, 12 + len(palette) + len(sprite)) else: # Point to sprite instead header = header + \ self.number(4, 12 + len(palette) ) # Determine the screen mode from the bpp, xdpi and ydpi if data['bpp'] == 1: log2bpp = 0 elif data['bpp'] == 2: log2bpp = 1 elif data['bpp'] == 4: log2bpp = 2 elif data['bpp'] == 8: log2bpp = 3 elif data['bpp'] == 16: log2bpp = 4 elif data['bpp'] == 32: log2bpp = 5 else: raise spritefile_error, \ 'Invalid number of bits per pixel in sprite.' mode = (log2bpp << 27) | (int(180/data['dpi x'] << 1)) | \ (int(180/data['dpi y']) << 14) header = header + self.number(4, mode) # Write the next sprite offset for this sprite # the sprite header + palette + sprite + mask file.write( self.number( 4, len(header) + len(palette) + len(sprite) + len(mask) ) ) # Write the sprite header file.write(header) # Write the palette file.write(palette) # Write the image data file.write(sprite) # Write the mask file.write(mask) # Return the amount of data written to the file return len(header) + len(palette) + len(sprite) + len(mask) def write(self, file): # Count the sprites in the area number = len(self.sprites.keys()) file.write(self.number(4, number)) # Put the sprites in the standard place offset = 16 file.write(self.number(4, offset)) # The free space offset points to after all the sprites # so we need to know how much space they take up. # Record the position of the free space pointer in the # file. free_ptr = file.tell() # Put a zero word in as a placeholder file.write(self.number(4, 0)) # The offset will start after the number, first sprite offset # and free space offset with an additional word added for when # the sprite file is imported into a sprite area. free = 16 # Write the sprites to the file for name in self.sprites.keys(): free = free + self.write_details(name) # Fill in free space pointer file.seek(free_ptr, 0) file.write(self.number(free)) UniConvertor-1.1.4/src/app/plugins/Filters/Lib/drawfile.py0000775000076400007640000012457211060500575022200 0ustar igorigor""" drawfile.py Read a Drawfile and store the elements in an instance of a Drawfile class (C) David Boddie 2001-2 This module may be freely distributed and modified. """ # Version history: # # 0.10 (Fri 24th August 2001) # # Initial release version. # # 0.11 (Fri 24th August 2001) # # Modified tagged objects so that they contain any extra data which may be # present. # # 0.12 (Wed 29th August 2001) # # Changed paragraph handling in text areas. # # 0.13 (Sat 20th October 2001) # # Changed error to a class derived from exceptions.Exception. # # 0.131 (Sat 26th January 2002) # # Tidied up some formatting errors caused by mixing tabs and spaces. import spritefile, os, string import struct version = '0.13 (Sat 20th October 2001)' try: import cStringIO StringIO = cStringIO except ImportError: import StringIO class drawfile_error(Exception): pass units_per_inch = 180*256 units_per_point = 640 # Compatibility with future versions of Python with different division # semantics: points_per_inch = int(units_per_inch/units_per_point) class drawfile_object: def __init__(self, data = None): if data != None: self.input(data) else: self.new() def number(self, size, n): # Little endian writing s = "" while size > 0: i = n % 256 s = s + chr(i) # n = n / 256 n = n >> 8 size = size - 1 return s def str2num(self, size, s): return spritefile.str2num(size, s) def decode_double(self, s): """Return a floating point number from an IEEE double encoded in a string""" # Information on decoding from the gnu-fixed library for gcc word1 = self.str2num(4, s[0:4]) word2 = self.str2num(4, s[4:8]) sign = word1 >> 31 exponent = ((word1 >> 20) & 0x7ff) - 1023 fraction = long((word1 & 0x000fffff) | 0x100000) << 32 fraction = fraction | long(word2) if exponent > 0: value = float(fraction << exponent) / float(1L << 52) elif exponent == 0: value = float(fraction) / float(1L << 52) else: value = float(fraction >> -exponent) / float(1L << 52) if sign == 1: return -value else: return value def encode_double(self, f): l = long(f) class font_table(drawfile_object): def new(self): self.font_table = {} def input(self, data): """input(self, data) Decodes a raw font table object from a Draw file and stores it internally as a dictionary of font numbers and names. """ at = 0 l = len(data) self.font_table = {} while at < l: number = ord(data[at]) if number == 0: # at the end of the object return name = '' at = at + 1 while ord(data[at]) != 0 and at < l: c = ord(data[at]) if c > 32 and c != 127: name = name + data[at] at = at + 1 at = at + 1 if name == '': # at the end of the object return self.font_table[number] = name # if (at % 4) > 0: at = at + 4 - (at % 4) def output(self): """output(self) Returns the internal dictionary of font numbers and names as raw data to be written to a Draw file. """ data = '' for key in self.font_table.keys(): # Font number data = data + chr(key) # Font name data = data + self.font_table[key] + '\000' # Pad with zeros pad = len(data) % 4 data = data + ((4-pad) % 4)*'\000' data = self.number(4, 0) + self.number(4, len(data) + 8) + data return data class text(drawfile_object): def new(self): self.x1 = self.y1 = self.x2 = self.y2 = 0.0 self.foreground = [0, 0, 0, 0] self.background = [0xff, 0xff, 0xff, 0xff] self.size = [0, 0] self.baseline = [0, 0] self.style = 0 self.text = '' def input(self, args): self.x1 = args[0] self.y1 = args[1] self.x2 = args[2] self.y2 = args[3] data = args[4] l = len(data) if len(args) == 6: # Transformed text self.transform = ( self.str2num(4, data[0:4]), self.str2num(4, data[4:8]), self.str2num(4, data[8:12]), self.str2num(4, data[12:16]), self.str2num(4, data[16:20]), self.str2num(4, data[20:24]) ) self.font_flags = self.str2num(4, data[24:28]) data = data[28:] # Standard text information self.foreground = [self.str2num(1, data[0]), self.str2num(1, data[1]), self.str2num(1, data[2]), self.str2num(1, data[3])] self.background = [self.str2num(1, data[4]), self.str2num(1, data[5]), self.str2num(1, data[6]), self.str2num(1, data[7])] self.style = self.str2num(4, data[8:12]) self.size = [self.str2num(4, data[12:16]), self.str2num(4, data[16:20])] self.baseline = [self.str2num(4, data[20:24]), self.str2num(4, data[24:28])] at = 28 while (data[at] != '\000') and (at < l): at = at + 1 self.text = data[28:at] def output(self): pad = (len(self.text)+1) % 4 if pad != 0: pad = 4 - pad data = self.number(4, 1) + \ self.number(4, len(self.text) + 1 + pad + 24 + 28) + \ self.number(4, self.x1) + \ self.number(4, self.y1) + \ self.number(4, self.x2) + \ self.number(4, self.y2) + \ self.number(1, self.foreground[0]) + \ self.number(1, self.foreground[1]) + \ self.number(1, self.foreground[2]) + \ self.number(1, self.foreground[3]) + \ self.number(1, self.background[0]) + \ self.number(1, self.background[1]) + \ self.number(1, self.background[2]) + \ self.number(1, self.background[3]) + \ self.number(4, self.style) + \ self.number(4, self.size[0]) + \ self.number(4, self.size[1]) + \ self.number(4, self.baseline[0]) + \ self.number(4, self.baseline[1]) + \ self.text + '\000' data = data + pad*'\000' return data class path(drawfile_object): join = ['mitred', 'round', 'bevelled'] # 0, 1, 2 end_cap = ['butt', 'round', 'square', 'triangular'] # 0, 4, 8, 12 start_cap = ['butt', 'round', 'square', 'triangular'] # 0, 16, 32, 48 winding = ['non-zero', 'even-odd'] # 0, 64 dashed = ['missing', 'present'] # 0, 128 tag = ['end', '', 'move', '', '', 'close', 'bezier', '', 'draw'] def new(self): self.x1 = self.y1 = self.x2 = self.y2 = 0.0 self.outline = [0, 0, 0, 0] self.fill = [0xff, 0xff, 0xff, 0xff] self.width = 0 self.style = {'join': 'mitred', 'end cap': 'butt', 'start cap': 'butt', 'winding rule': 'even-odd', 'dash pattern': 'missing', 'triangle cap width': 16, 'triangle cap length': 16} self.path = [] def input(self, args): self.x1 = args[0] self.y1 = args[1] self.x2 = args[2] self.y2 = args[3] data = args[4] l = len(data) self.fill = [self.str2num(1,data[0]), self.str2num(1,data[1]), self.str2num(1,data[2]), self.str2num(1,data[3])] self.outline = [self.str2num(1,data[4]), self.str2num(1,data[5]), self.str2num(1,data[6]), self.str2num(1,data[7])] self.width = self.str2num(4, data[8:12]) style = self.str2num(4, data[12:16]) self.style = {} try: self.style['join'] = self.join[style & 3] except: self.style['join'] = 'mitred' try: self.style['end cap'] = self.end_cap[(style >> 2) & 3] except: self.style['end cap'] = 'butt' try: self.style['start cap'] = self.start_cap[(style >> 4) & 3] except: self.style['end cap'] = 'butt' try: self.style['winding rule'] = self.winding[(style >> 6) & 1] except: self.style['winding rule'] = 'even-odd' try: self.style['dash pattern'] = self.dashed[(style >> 7) & 1] except: self.style['dash pattern'] = 'missing' self.style['triangle cap width'] = (style >> 16) & 255 self.style['triangle cap length'] = (style >> 24) & 255 if self.style['dash pattern'] == 'present': # Read the dash pattern self.pattern = [self.str2num(4, data[16:20])] number = self.str2num(4, data[20:24]) self.pattern.append(number) at = 24 for i in range(number): self.pattern.append(self.str2num(4, data[at:at+4])) at = at + 4 else: at = 16 # Read the path elements self.path = [] while at < l: tag = self.str2num(4, data[at:at+4]) if tag == 0: self.path.append(['end']) at = at + 4 elif tag == 2: self.path.append( ('move', (self.str2num(4, data[at+4:at+8]), self.str2num(4, data[at+8:at+12]) ) ) ) at = at + 12 elif tag == 5: self.path.append(['close']) at = at + 4 elif tag == 6: self.path.append( ('bezier', (self.str2num(4, data[at+4:at+8]), self.str2num(4, data[at+8:at+12]) ), (self.str2num(4, data[at+12:at+16]), self.str2num(4, data[at+16:at+20]) ), (self.str2num(4, data[at+20:at+24]), self.str2num(4, data[at+24:at+28]) ) ) ) at = at + 28 elif tag == 8: self.path.append( ('draw', (self.str2num(4, data[at+4:at+8]), self.str2num(4, data[at+8:at+12]) ) ) ) at = at + 12 else: raise drawfile_error, 'Unknown path segment found (%s)' % \ hex(tag) def output(self): # Write the colours and width data = self.number(1,self.fill[0]) + \ self.number(1,self.fill[1]) + \ self.number(1,self.fill[2]) + \ self.number(1,self.fill[3]) + \ self.number(1,self.outline[0]) + \ self.number(1,self.outline[1]) + \ self.number(1,self.outline[2]) + \ self.number(1,self.outline[3]) + \ self.number(4,self.width) if hasattr(self, 'pattern'): self.style['dash pattern'] == 'present' # Write the path style style = self.join.index(self.style['join']) | \ (self.end_cap.index(self.style['end cap']) << 2) | \ (self.start_cap.index(self.style['start cap']) << 4) | \ (self.winding.index(self.style['winding rule']) << 6) | \ (self.dashed.index(self.style['dash pattern']) << 7) | \ (self.style['triangle cap width'] << 16) | \ (self.style['triangle cap length'] << 24) data = data + self.number(4, style) if hasattr(self, 'pattern'): for n in self.pattern: data = data + self.number(4, n) # Write the path segments to the string for item in self.path: tag = item[0] data = data + self.number(4, self.tag.index(tag)) for x, y in item[1:]: self.x1 = min(self.x1, x) self.y1 = min(self.y1, y) self.x2 = max(self.x2, x) self.y2 = max(self.y2, y) data = data + self.number(4, x) + self.number(4, y) # The last segment must be an end segment if tag != 'end': data = data + self.number(4, 0) # Write the header data = self.number(4, 2) + \ self.number(4, len(data) + 24) + \ self.number(4, self.x1) + \ self.number(4, self.y1) + \ self.number(4, self.x2) + \ self.number(4, self.y2) + data return data class sprite(drawfile_object): def new(self): self.sprite = {} def input(self, args): self.x1 = args[0] self.y1 = args[1] self.x2 = args[2] self.y2 = args[3] data = args[4] if len(args) == 6: # Transformed sprite self.transform = ( self.str2num(4, data[0:4]), self.str2num(4, data[4:8]), self.str2num(4, data[8:12]), self.str2num(4, data[12:16]), self.str2num(4, data[16:20]), self.str2num(4, data[20:24]) ) data = data[24:] # Construct a reasonable sprite block from the data supplied # One sprite and offset to sprite sprdata = self.number(4, 1) + \ self.number(4, 0x10) free = self.str2num(4, data[0:4]) + 0x10 sprdata = sprdata + self.number(4, free) + data # Create a spritefile object sprites = spritefile.spritefile(StringIO.StringIO(sprdata)) # Use the first, and only, sprite self.name = sprites.sprites.keys()[0] self.sprite = sprites.sprites[self.name] def output(self): # Create a new spritefile object sprites = spritefile.spritefile() # Add the sprite sprites.sprites[self.name] = self.sprite # Write the sprite to an output stream stream = StringIO.StringIO() sprites.write(stream) # Return the text string stored in the stream return StringIO.StringIO.read() class group(drawfile_object): def new(self): self.name = '' self.x1 = self.y1 = self.x2 = self.y2 = 0.0 self.objects = '' def input(self, data): self.x1 = data[0] self.y1 = data[1] self.x2 = data[2] self.y2 = data[3] self.name = data[4] self.objects = data[5] def output(self): data = '' for obj in self.objects: data = data + obj.output() self.x1 = min(obj.x1, self.x1) self.y1 = min(obj.y1, self.y1) self.x2 = max(obj.x2, self.x2) self.y2 = max(obj.y2, self.y2) if len(self.name) < 12: self.name = self.name + (12-len(self.name))*' ' data = self.name + data data = self.number(4,6) + \ self.number(4,len(data)+24) + \ self.number(4,self.x1) + \ self.number(4,self.y1) + \ self.number(4,self.x2) + \ self.number(4,self.y2) + data return data class tagged(drawfile_object): def new(self): self.id = '' self.object = [] def input(self, args): self.x1 = args[0] self.y1 = args[1] self.x2 = args[2] self.y2 = args[3] self.id = args[4] self.object = args[5] # there is only one object passed self.data = args[6] def output(self): """output(self) Returns the tag ID and objects as data to be written to a Draw file. """ data = self.id data = data + self.objects.output() # get the object contained # to output itself data = data + self.data # add extra data data = self.number(4,7) + \ self.number(4,len(data)+24) + \ self.number(4,self.x1) + \ self.number(4,self.y1) + \ self.number(4,self.x2) + \ self.number(4,self.y2) + data return data class text_area(drawfile_object): def new(self): self.columns = [] self.text = '' def input(self, args): self.x1 = args[0] self.y1 = args[1] self.x2 = args[2] self.y2 = args[3] data = args[4] self.columns = [] l = len(data) i = 0 while i < l: n = self.str2num(4, data[i:i+4]) if n == 0: i = i + 4 break if n != 10: raise drawfile_error, 'Not a text column object.' length = self.str2num(4, data[i+4:i+8]) if length != 24: raise drawfile_error, 'Text column object has invalid length.' self.columns.append( column( ( self.str2num(4, data[i+8:i+12]), self.str2num(4, data[i+12:i+16]), self.str2num(4, data[i+16:i+20]), self.str2num(4, data[i+20:i+24]) ) ) ) i = i + 24 # Skip reserved words i = i + 8 # Initial colours self.foreground = (ord(data[i+1]), ord(data[i+2]), ord(data[i+3])) self.background = (ord(data[i+5]), ord(data[i+6]), ord(data[i+7])) i = i + 8 # Read text self.text = '' while i < l: if ord(data[i]) != 0: self.text = self.text + data[i] i = i + 1 # Parse the text, creating tuples containing command and value # information. self.align = 'L' # current alignment # self.baseline = object.y2 # current baseline # self.horizontal = object.x1 # current cursor position self.linespacing = 0.0 # line spacing for next line self.paragraph = 10.0 # spacing before this paragraph self.columns_number = len(self.columns) # number of columns to use self.in_column = 1 # the current column self.left_margin = 1.0 # left and self.right_margin = 1.0 # right margins self.font_table = {} # font and size dictionary self.font_name = '' self.font_size = 0.0 self.font_width = 0.0 self.current = '' # text buffer # Write the commands and their arguments to a list for later processing self.commands = [] # Each line/paragraph will contain the following keys: # text the textual content # left margin # right margin # paragraph i = 0 while i < len(self.text): if self.text[i] == '\\': command, args, next = self.read_escape(i+1) # Add command to the list self.commands.append((command, args)) else: # In text: add it to the buffer next = string.find(self.text, '\\', i) if next == -1: next = len(self.text) lines = string.split(self.text[i:next], '\n') # Examine all but the last line # Paragraph counter n_paragraphs = 0 for line in lines[:-1]: if line == '': # Empty line: new paragraph n_paragraphs = n_paragraphs + 1 else: # If the preceding elements were paragraph # breaks then add them all except one, with a # minimum of one if n_paragraphs > 0: for j in range(max(n_paragraphs-1, 1)): self.commands.append(('para', '')) n_paragraphs = 0 # Substitute spaces for tabs line = string.expandtabs(line, 1) words = string.split(line, ' ') # Examine the words on this line for word in words: # Add word (assuming that a space follows) self.commands.append(('text', (word, ' '))) # If the preceding elements were paragraph # breaks then add them all except one, with a # minimum of one if n_paragraphs > 0: for j in range(max(n_paragraphs-1, 1)): self.commands.append(('para', '')) # The last line doesn't end with a newline character so we # can't have a paragraph break if lines[-1] != '': # Substitute spaces for tabs line = string.expandtabs(lines[-1], 1) words = string.split(line, ' ') # Examine all but the last word for word in words[:-1]: # Add word (assuming that a space follows) self.commands.append(('text', (word, ' '))) if words[-1] != '': # Add word without following space self.commands.append(('text', (words[-1], ''))) # Go to next command i = next def read_escape(self, i): command = self.text[i] # Special case: the newline character is itself a command if command != '\n' and command != '-' and command != '\\': next = self.skip_whitespace(i + 1) else: next = i + 1 if command == '!': args, next = self.read_value(next, ['/', '\n']) elif command == 'A': args = self.text[next] if args not in 'LRCD': raise drawfile_error, \ 'Unknown alignment character '+self.align + \ ' in text area at '+hex(i) if self.text[next+1] == '/': next = next + 2 else: next = next + 1 elif command == 'B': value1, next = self.read_value(next, [' ']) next = self.skip_whitespace(next) value2, next = self.read_value(next, [' ']) next = self.skip_whitespace(next) value3, next = self.read_value(next, ['\n', '/']) try: value1, value2, value3 = int(value1), int(value2), int(value3) except ValueError: raise drawfile_error, 'Invalid colour in text area at '+hex(i) if value1 < 0 or value1 > 255: raise drawfile_error, 'Invalid colour in text area at '+hex(i) if value2 < 0 or value2 > 255: raise drawfile_error, 'Invalid colour in text area at '+hex(i) if value3 < 0 or value3 > 255: raise drawfile_error, 'Invalid colour in text area at '+hex(i) args = (value1, value2, value3) elif command == 'C': value1, next = self.read_value(next, [' ']) next = self.skip_whitespace(next) value2, next = self.read_value(next, [' ']) next = self.skip_whitespace(next) value3, next = self.read_value(next, ['\n', '/']) try: value1, value2, value3 = int(value1), int(value2), int(value3) except ValueError: raise drawfile_error, 'Invalid colour in text area at '+hex(i) if value1 < 0 or value1 > 255: raise drawfile_error, 'Invalid colour in text area at '+hex(i) if value2 < 0 or value2 > 255: raise drawfile_error, 'Invalid colour in text area at '+hex(i) if value3 < 0 or value3 > 255: raise drawfile_error, 'Invalid colour in text area at '+hex(i) args = (value1, value2, value3) elif command == 'D': args, next = self.read_value(next, ['/', '\n']) try: args = int(args) except ValueError: raise drawfile_error, \ 'Invalid number of columns in text area at '+hex(i) elif command == 'F': digits, next = self.read_number(next) try: digits = int(digits) except ValueError: raise drawfile_error, \ 'Invalid font number in text area at '+hex(i) next = self.skip_whitespace(next) name, next = self.read_value(next, [' ']) next = self.skip_whitespace(next) value2, next = self.read_value(next, [' ', '/', '\n']) if self.text[next] == ' ': value3, next = self.read_value(next, ['\n', '/']) else: value3 = value2 try: value2, value3 = int(value2), int(value3) except ValueError: raise drawfile_error, \ 'Invalid font size in text area at '+hex(i) self.font_table[digits] = (name, value2, value3) args = (digits, name, value2, value3) elif command == 'L': args, next = self.read_value(next, ['/', '\n']) try: args = int(args) except ValueError: raise drawfile_error, \ 'Invalid leading value in text area at '+hex(i) elif command == 'M': value1, next = self.read_value(next, [' ']) next = self.skip_whitespace(next) value2, next = self.read_value(next, ['/', '\n']) try: value1, value2 = int(value1), int(value2) except ValueError: raise drawfile_error, \ 'Invalid margins in text area at '+hex(i) if value1 <= 0.0 or value2 <= 0.0: raise drawfile_error, \ 'Invalid margins in text area at '+hex(i) args = (value1, value2) elif command == 'P': args, next = self.read_value(next, ['/', '\n']) try: args = int(args) except ValueError: raise drawfile_error, \ 'Invalid leading value in text area at '+hex(i) elif command == 'U': if self.text[next] != '.': value1, next = self.read_value(next, [' ']) next = self.skip_whitespace(next) value2, next = self.read_value(next, ['/', '\n']) try: value1, value2 = int(value1), int(value2) except ValueError: raise drawfile_error, \ 'Invalid value in text area at '+hex(i) args = (value1, value2) else: next = next + 1 args = '.' elif command == 'V': args = self.text[next] if self.text[next+1] == '/': next = next + 2 else: next = next + 1 try: args = int(args) except ValueError: raise drawfile_error, 'Invalid value in text area at '+hex(i) elif command == '-': args = '' elif command == '\n': command = 'newl' args = '' elif command == '\\': commands = 'text' args = ('\\', '') elif command == ';': args, next = self.read_value(next, ['\n']) elif command not in string.digits: # Unknown raise drawfile_error, \ 'Unknown command '+command+' in text area at '+hex(i) else: # Digits value, next = self.read_number(next) # The command was actually the first digit value = command+value try: value = int(value) except ValueError: raise drawfile_error, \ 'Font number was not an integer in text area at '+hex(i) if self.text[next] == '/': next = next + 1 try: font_name, font_size, font_width = self.font_table[value] except KeyError: raise drawfile_error, 'Font not found in text area at '+hex(i) command = 'font' args = value return command, args, next def skip_whitespace(self, i): while self.text[i] in string.whitespace: i = i + 1 return i def read_value(self, i, endings): ends = [] for ch in endings: end = string.find(self.text, ch, i) if end != -1: ends.append(end) if ends == []: return self.text[i:], len(self.text) return self.text[i:min(ends)], min(ends) + 1 def read_number(self, i): value = '' while self.text[i] in '0123456789': value = value + self.text[i] i = i + 1 return value, i def output(self): data = '' for c in self.columns: data = data + c.output() data = data + self.number(4, 0) + \ self.number(4, 0) + \ self.number(4, 0) + \ self.number(4, self.foreground) + \ self.number(4, self.background) + \ self.text + '\000' excess = len(data) % 4 if excess != 0: data = data + ((4 - excess)*'\000') data = self.number(4,9) + \ self.number(4,len(data)+24) + \ self.number(4,self.x1) + \ self.number(4,self.y1) + \ self.number(4,self.x2) + \ self.number(4,self.y2) + data return data class column(drawfile_object): def new(self): pass def input(self, args): self.x1 = args[0] self.y1 = args[1] self.x2 = args[2] self.y2 = args[3] def output(self): data = self.number(4,10) + \ self.number(4,24) + \ self.number(4,self.x1) + \ self.number(4,self.y1) + \ self.number(4,self.x2) + \ self.number(4,self.y2) return data class options(drawfile_object): def new(self): self.options = {} def input(self, data): # Ignore the empty bounding box data = data[16:] self.options = {} self.options['paper size'] = 'A%i' % \ ((self.str2num(4, data[0:4]) >> 8) - 1) paper = self.str2num(4, data[4:8]) if (paper & 1) != 0: self.options['show limits'] = 'on' else: self.options['show limits'] = 'off' if (paper & 0x10) != 0: self.options['paper limits'] = 'landscape' else: self.options['paper limits'] = 'portrait' if (paper & 0x100) != 0: self.options['printer limits'] = 'default' else: self.options['printer limits'] = 'unknown' self.options['grid spacing'] = self.decode_double(data[8:16]) self.options['grid subdivisions'] = self.str2num(4, data[16:20]) if self.str2num(4, data[20:24]) == 0: self.options['grid type'] = 'rectangular' else: self.options['grid type'] = 'isometric' if self.str2num(4, data[24:28]) == 0: self.options['grid auto adjustment'] = 'off' else: self.options['grid auto adjustment'] = 'on' if self.str2num(4, data[28:32]) == 0: self.options['grid shown'] = 'off' else: self.options['grid shown'] = 'on' if self.str2num(4, data[32:36]) == 0: self.options['grid locking'] = 'off' else: self.options['grid locking'] = 'on' if self.str2num(4, data[36:40]) == 0: self.options['grid units'] = 'in' else: self.options['grid units'] = 'cm' zoom_mult = self.str2num(4, data[40:44]) if zoom_mult > 8: zoom_mult = 8 if zoom_mult < 1: zoom_mult = 1 zoom_div = self.str2num(4, data[44:48]) if zoom_div > 8: zoom_div = 8 if zoom_div < 1: zoom_div = 1 self.options['zoom'] = (zoom_mult, zoom_div) if self.str2num(4, data[48:52]) == 0: self.options['zoom locking'] = 'off' else: self.options['zoom locking'] = 'on' if self.str2num(4, data[52:56]) == 0: self.options['toolbox'] = 'off' else: self.options['toolbox'] = 'on' tool = self.str2num(4, data[56:60]) if tool == 1: self.options['tool'] = 'line' elif tool == 2: self.options['tool'] = 'closed line' elif tool == 4: self.options['tool'] = 'curve' elif tool == 8: self.options['tool'] = 'closed curve' elif tool == 16: self.options['tool'] = 'rectangle' elif tool == 32: self.options['tool'] = 'ellipse' elif tool == 64: self.options['tool'] = 'text' elif tool == 128: self.options['tool'] = 'select' else: self.options['tool'] = 'unknown' self.options['undo buffer size'] = self.str2num(4, data[60:64]) class jpeg(drawfile_object): def new(self): self.image = '' self.transform = [] # Data with unknown meaning self.unknown = '' def input(self, args): self.x1 = args[0] self.y1 = args[1] self.x2 = args[2] self.y2 = args[3] data = args[4] self.unknown = data[:8] self.dpi_x = self.str2num(4, data[8:12]) self.dpi_y = self.str2num(4, data[12:16]) self.transform = [ self.str2num(4, data[16:20]), self.str2num(4, data[20:24]), self.str2num(4, data[24:28]), self.str2num(4, data[28:32]), self.str2num(4, data[32:36]), self.str2num(4, data[36:40]) ] length = self.str2num(4, data[40:44]) self.image = data[44:44+length] def output(self): data = '' data = data + self.unknown + \ self.number(4, self.dpi_x) + \ self.number(4, self.dpi_y) for value in self.transform: data = data + self.number(4, value) data = data + self.number(4, len(self.image)) + self.image return data class unknown(drawfile_object): def new(self): raise drawfile_error, 'Cannot create an unspecified unknown object.' def input(self, data): self.type = data[0] self.length = data[1] self.x1 = data[2] self.y1 = data[3] self.x2 = data[4] self.y2 = data[5] self.data = data[6] def output(self): return self.number(4,self.type) + \ self.number(4,self.length) + \ self.number(4,self.x1) + \ self.number(4,self.y1) + \ self.number(4,self.x2) + \ self.number(4,self.y2) + \ self.data class drawfile: def __init__(self, file = None): if file != None: self.read(file) else: self.new() def new(self): self.version = {'major': 201, 'minor': 0} self.creator = 'drawfile.py ' self.objects = [] self.x1 = 0x7fffffff self.y1 = 0x7fffffff # Note: self.x2 and self.y2 used to be initialized to # 0x80000000. In Python 2.4 the meaning of that literal has # changed to be always positive instead of being negative on 32 # bit platforms. To avoid problems with that the constants have # been replaced with the decimal version of the interpretation # of 0x80000000 on 32 bit machines. It's not clear whether that # really is the right solution. The intention of the value # might have been something like -sys.maxint - 1 instead. self.x2 = -2147483648 self.y2 = -2147483648 def number(self, size, n): # Little endian writing s = "" while size > 0: i = n % 256 s = s + chr(i) # n = n / 256 n = n >> 8 size = size - 1 return s def str2num(self, size, s): return spritefile.str2num(size, s) def read_group(self, f): start = f.tell() - 4 l = self.str2num(4, f.read(4)) x1 = self.str2num(4, f.read(4)) y1 = self.str2num(4, f.read(4)) x2 = self.str2num(4, f.read(4)) y2 = self.str2num(4, f.read(4)) name = f.read(12) objects = [] while f.tell() < (start + l): object = self.read_object(f) if object == '': break objects.append(object) return (6, l, x1, y1, x2, y2, name, objects) def read_object(self, f): # Read object number s = f.read(4) if not s: return '' t = self.str2num(4, s) if t == 0: # Font table object l = self.str2num(4, f.read(4)) # return (t, l, f.read(l-8)) return font_table(f.read(l-8)) elif t == 1: # Text object l = self.str2num(4, f.read(4)) x1 = self.str2num(4, f.read(4)) y1 = self.str2num(4, f.read(4)) x2 = self.str2num(4, f.read(4)) y2 = self.str2num(4, f.read(4)) return text((x1, y1, x2, y2, f.read(l-24))) elif t == 2: # Path object l = self.str2num(4, f.read(4)) x1 = self.str2num(4, f.read(4)) y1 = self.str2num(4, f.read(4)) x2 = self.str2num(4, f.read(4)) y2 = self.str2num(4, f.read(4)) return path((x1, y1, x2, y2, f.read(l-24))) elif t == 5: # Sprite object l = self.str2num(4, f.read(4)) x1 = self.str2num(4, f.read(4)) y1 = self.str2num(4, f.read(4)) x2 = self.str2num(4, f.read(4)) y2 = self.str2num(4, f.read(4)) return sprite((x1, y1, x2, y2, f.read(l-24))) elif t == 6: # Group object # return self.read_group(f) return group(self.read_group(f)[2:]) elif t == 7: # Tagged object l = self.str2num(4, f.read(4)) x1 = self.str2num(4, f.read(4)) y1 = self.str2num(4, f.read(4)) x2 = self.str2num(4, f.read(4)) y2 = self.str2num(4, f.read(4)) id = f.read(4) begin = f.tell() object = self.read_object(f) length = f.tell() - begin data = f.read(l-28-length) return tagged((x1, y1, x2, y2, id, object, data)) elif t == 9: # Text area object l = self.str2num(4, f.read(4)) x1 = self.str2num(4, f.read(4)) y1 = self.str2num(4, f.read(4)) x2 = self.str2num(4, f.read(4)) y2 = self.str2num(4, f.read(4)) return text_area((x1, y1, x2, y2, f.read(l-24))) elif t == 11: # Options object l = self.str2num(4, f.read(4)) return options(f.read(l-8)) elif t == 12: # Transformed text object l = self.str2num(4, f.read(4)) x1 = self.str2num(4, f.read(4)) y1 = self.str2num(4, f.read(4)) x2 = self.str2num(4, f.read(4)) y2 = self.str2num(4, f.read(4)) return text((x1, y1, x2, y2, f.read(l-24), 1)) elif t == 13: # Tranformed sprite object l = self.str2num(4, f.read(4)) x1 = self.str2num(4, f.read(4)) y1 = self.str2num(4, f.read(4)) x2 = self.str2num(4, f.read(4)) y2 = self.str2num(4, f.read(4)) return sprite((x1, y1, x2, y2, f.read(l-24), 1)) elif t == 16: # JPEG image l = self.str2num(4, f.read(4)) x1 = self.str2num(4, f.read(4)) y1 = self.str2num(4, f.read(4)) x2 = self.str2num(4, f.read(4)) y2 = self.str2num(4, f.read(4)) return jpeg((x1, y1, x2, y2, f.read(l-24))) else: l = self.str2num(4, f.read(4)) x1 = self.str2num(4, f.read(4)) y1 = self.str2num(4, f.read(4)) x2 = self.str2num(4, f.read(4)) y2 = self.str2num(4, f.read(4)) # return (t, l, x1, y1, x2, y2, f.read(l-24)) return unknown((t, l, x1, y1, x2, y2, f.read(l-24))) def read(self, file): self.version = {} self.creator = '' self.objects = [] file.seek(0, 0) if file.read(4) != 'Draw': raise drawfile_error, 'Not a Draw file' self.version['major'] = self.str2num(4, file.read(4)) self.version['minor'] = self.str2num(4, file.read(4)) self.creator = file.read(12) self.x1 = self.str2num(4, file.read(4)) self.y1 = self.str2num(4, file.read(4)) self.x2 = self.str2num(4, file.read(4)) self.y2 = self.str2num(4, file.read(4)) objects = [] while 1: object = self.read_object(file) if object == '': break objects.append(object) self.objects = objects def write_objects(self, file, objects): for object in objects: file.write(object.output()) # # Write the object type and length and bounding box # # if there is one # for i in range(0,len(object)-1): # file.write(self.number(4, object[i])) # # if object[0] == 6: # # Group object # self.write_objects(file, object[-1]) # else: # file.write(object[-1]) def write(self, file): # Write file header file.write('Draw') # Write version stamps file.write(self.number(4, self.version['major'])) file.write(self.number(4, self.version['minor'])) # Write creator information file.write(self.creator[:12]) # Write bounding box file.write(self.number(4, self.x1)) file.write(self.number(4, self.y1)) file.write(self.number(4, self.x2)) file.write(self.number(4, self.y2)) self.write_objects(file, self.objects) UniConvertor-1.1.4/src/app/plugins/Filters/cgmloader.py0000644000076400007640000006761410740000121021612 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 by Bernhard Herzog # This CGMloader mostly by Antoon Pardon (2002) # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # $Id: cgmloader.py,v 1.1.2.3 2003/05/17 18:22:14 bherzog Exp $ ################################################################################ ###Sketch Config #type = Import #class_name = 'CGMLoader' rx_magic = '\\x00' #tk_file_type = ('Computer Graphics Metafile', '.cgm') #format_name = 'CGM' #unload = 1 #standard_messages = 1 ###End # # Import Filter for CGM files # import sys, os, string from math import sin, cos, pi, atan2 import struct import operator import copy import types from app import _, Trafo, Scale, Translation, Point, Polar, CreatePath, \ CreateRGBColor, SolidPattern, EmptyPattern, LinearGradient, \ MultiGradient, Style, const, StandardColors, GridLayer, GetFont, \ HatchingPattern from app.events.warn import INTERNAL, warn_tb from app.io.load import GenericLoader, SketchLoadError import app.Graphics.font from app.Graphics import text basestyle = Style() basestyle.fill_pattern = EmptyPattern basestyle.fill_transform = 1 basestyle.line_pattern = EmptyPattern basestyle.line_width = 1.0 basestyle.line_join = const.JoinMiter basestyle.line_cap = const.CapButt basestyle.line_dashes = () basestyle.line_arrow1 = None basestyle.line_arrow2 = None basestyle.font = None basestyle.font_size = 12.0 CGM_ID = { 0x0020: 'BEGMF', 0x0040: 'ENDMF', 0x0060: 'BEGPIC', 0x0080: 'BEGPICBODY', 0x00A0: 'ENDPIC', 0x1020: 'mfversion', 0x1040: 'mfdesc', 0x1060: 'vdctype', 0x1080: 'integerprec', 0x10a0: 'realprec', 0x10c0: 'indexprec', 0x10e0: 'colrprec', 0x1100: 'colrindexprec', 0x1120: 'maxcolrindex', 0x1140: 'colrvalueext', 0x1160: 'mfelemlist', 0x1180: 'mfdfltrpl', 0x11a0: 'fontlist', 0x11c0: 'charsetlist', 0x11e0: 'charcoding', 0x2020: 'scalemode', 0x2040: 'colrmode', 0x2060: 'linewidthmode', 0x2080: 'markersizemode', 0x20a0: 'edgewidthmode', 0x20c0: 'vdcext', 0x20e0: 'backcolr', 0x3020: 'vdcintegerprec', 0x3040: 'vdcrealprec', 0x3060: 'auxcolr', 0x3080: 'transparency', 0x30a0: 'cliprect', 0x30c0: 'clip', 0x4020: 'LINE', 0x4040: 'DISJTLINE', 0x4060: 'MARKER', 0x4080: 'TEXT', 0x40a0: 'RESTRTEXT', 0x40c0: 'APNDTEXT', 0x40e0: 'POLYGON', 0x4100: 'POLYGONSET', 0x4120: 'CELLARRAY', 0x4140: 'GDP', 0x4160: 'RECT', 0x4180: 'CIRCLE', 0x41a0: 'ARC3PT', 0x41c0: 'ARC3PTCLOSE', 0x41e0: 'ARCCTR', 0x4200: 'ARCCTRCLOSE', 0x4220: 'ELLIPSE', 0x4240: 'ELLIPARC', 0x4260: 'ELLIPARCCLOSE', 0x5040: 'linetype', 0x5060: 'linewidth', 0x5080: 'linecolr', 0x50c0: 'markertype', 0x5100: 'markercolr', 0x5140: 'textfontindex', 0x5160: 'textprec', 0x5180: 'charexpan', 0x51a0: 'charspace', 0x51c0: 'textcolr', 0x51e0: 'charheight', 0x5200: 'charori', 0x5220: 'textpath', 0x5240: 'textalign', 0x5260: 'charsetindex', 0x52c0: 'intstyle', 0x52e0: 'fillcolr', 0x5300: 'hatchindex', 0x5320: 'patindex', 0x5360: 'edgetype', 0x5380: 'edgewidth', 0x53a0: 'edgecolr', 0x53c0: 'edgevis', 0x5440: 'colrtable', 0x5460: 'asf', 0x6020: 'ESCAPE', } cp = copy.deepcopy fntlst = map(lambda l: l[0], app.Graphics.font.fontlist) #print fntlst fntalias = { 'AvantGarde' : () , 'Bookman' : ('Brooklyn',) , 'Courier' : ('Fixed',) , 'Helvetica' : ('Arial' , 'Swiss' , 'Switzerland' , 'Monospace') , 'NewCenturySchlbk' : ('NewBrunswick' , 'NewCenturion') , 'Palatino' : ('PalmSprings' , 'Zapf Calligraphic') , 'Times' : ('Dutch' , 'Times New Roman') , 'Symbol' : ('GreekMathSYmbols',) , 'ZapfChancery' : ('ZurichCalligraphic',) , 'ZapfDingbats' : ('Dixieland',) , 'URWGothicL' : ('Block',) , 'CenturySchL' : ('NewBrunswick' , 'WordPerfect' , 'Centurion') , 'URWBookmanL' : () , 'Dingbats' : () , 'NimbusSanL' : () , 'NimbusRomNo9L' : () , 'NimbusMonL' : () , 'URWPalladioL' : () , 'StandardSymL' : () , 'URWChanceryL' : () , 'Utopia' : ('Univers' , ) , 'CharterBT' : ('Bernhard Modern BT' , 'Blackletter' , 'Brush' , 'GeometricSlabSerif' , 'Humanist' , 'Onyx') } class cgminfo: def __init__(self): pass def sign(num): return num/abs(num) def cr(P1, P2): return P1.x * P2.y - P1.y * P2.x def Angle(V): x, y = V return (atan2(y,x) % (2 * pi)) def Angle2(V1, V2): return Angle((V1*V2 , cr(V1,V2))) def cr3(P1, P2, P3): return cr(P1, P2) + cr(P2, P3) + cr(P3, P1) def Cnt3Pnt(P1, P2, P3): Q1 = Point(P1*P1 , P1.y) Q2 = Point(P2*P2 , P2.y) Q3 = Point(P3*P3 , P3.y) R1 = Point(P1.x , P1*P1) R2 = Point(P2.x , P2*P2) R3 = Point(P3.x , P3*P3) N = 2 * cr3(P1, P2, P3) Nx = cr3(Q1, Q2, Q3) Ny = cr3(R1, R2, R3) return Point(Nx/N , Ny/N) def transform_base(po, px, py): return apply(Trafo, tuple(px - po) + tuple(py - po) + tuple(po)) def CreateColorTable(sz): Max = 1 bs = 0 while Max < sz: Max = Max * 2 bs = bs + 1 cb = bs / 3 tb = bs % 3 mc = (1 << (cb + tb)) - 1.0 Table = Max * [(0.0, 0.0, 0.0)] for i in range(Max): j = i + Max - 1 j = j % Max red, grn, blu = 0, 0, 0 for k in range(cb): red = (red << 1) + j % 2 j = j >> 1 grn = (grn << 1) + j % 2 j = j >> 1 blu = (blu << 1) + j % 2 j = j >> 1 tint = j red = (red << tb) + tint grn = (grn << tb) + tint blu = (blu << tb) + tint Table[i] = (red / mc, grn / mc, blu / mc) return Table def strmatch(s1, s2): s1 = list(s1.lower()) s2 = list(s2.lower()) len1 = len(s1) len2 = len(s2) for i in range(len1): if not s1[i].isalnum(): s1[i] = ' ' for i in range(len2): if not s2[i].isalnum(): s2[i] = ' ' mat = (len1 + 1) * [0] for i in range(len1 + 1): mat[i] = (len2 + 1) * [0] for i in range(len1 + 1): mat[i][0] = i for i in range(len2 + 1): mat[0][i] = i for i in range(1, len1 + 1): for j in range(1 , len2 + 1): t = min(mat[i - 1][j] , mat[i][j - 1]) + 1 if s1[i - 1] == s2[j - 1]: t = min(t , mat[i - 1][j - 1]) mat[i][j] = t x = len1 y = len2 dx = 0 dy = 0 while x * y != 0: if s1[x - 1] == s2[y - 1]: x = x - 1 y = y - 1 else: if mat[x - 1][y] < mat[x][y - 1]: x = x - 1 dx = dx + 1 else: y = y - 1 dy = dy + 1 dx = x + dx dy = y + dy return dx + dy init = cgminfo() dflt = cgminfo() curr = cgminfo() reff = dflt init.intprec = 1 # 16 bits init.intsize = 2 init.inxprec = 1 # 16 bits init.inxsize = 2 init.realprec = 0 # 32 bits fixed point init.realsize = 4 init.color = cgminfo() init.color.absstruct = "!BBB" init.color.inxstruct = "!B" init.color.mode = 0 init.color.maxindex = 63 init.color.table = CreateColorTable(64) init.color.offset = (0.0, 0.0, 0.0) init.color.scale = (255.0, 255.0, 255.0) init.vdc = cgminfo() init.vdc.type = 0 # integers init.vdc.realprec = 0 # 32 bits fixed point init.vdc.realsize = 4 init.vdc.intprec = 1 # 16 bits init.vdc.intsize = 2 init.vdc.prec = None # integers , 16 bit init.vdc.size = None init.vdc.intextend = ((0,0),(32767,32767)) init.vdc.realextend = ((0.0,0.0),(1.0,1.0)) init.vdc.extend = None #((0,0),(32767,32767)) init.fill = cgminfo() init.fill.type = 1 init.fill.color = (0.0, 0.0, 0.0) init.line = cgminfo() init.line.type = 1 init.line.color = (0.0, 0.0, 0.0) init.line.widthmode = 0 init.line.width = None init.line.dashtable = ((), (4,4) , (1,1) , (4,1,1,1) , (4,1,1,1,1,1)) init.edge = cgminfo() init.edge.type = 1 init.edge.color = (0.0, 0.0, 0.0) init.edge.widthmode = 0 init.edge.width = None init.edge.dashtable = init.line.dashtable init.edge.visible = 0 init.text = cgminfo() init.text.fontindex = fntlst.index("Times-Roman") init.text.height = None init.text.expansion = 1.0 init.text.spacing = 0.0 init.text.orientation = ((0.0 , 1.0),(1.0, 0.0)) # Up , Base vector init.text.path = 0 # right init.text.alignment = 0 # Dont understand this yet. init.text.color = (0.0, 0.0, 0.0) init.marker = cgminfo() init.marker.sizemode = 0 init.marker.type = 3 init.marker.size = None init.clip = cgminfo() init.clip.mode = 1 init.clip.rect = init.vdc.extend init.scale = cgminfo() init.scale.mode = 0 # abstract init.scale.metric = 0.0 class CGMLoader(GenericLoader): def __init__(self, file, filename, match): GenericLoader.__init__(self, file, filename, match) self.file = file self.verbosity = 15 self.IntF = (self.i8, self.i16, self.i24, self.i32) self.CardF = (self.u8, self.u16, self.u24, self.u32) self.FloatF = (self.flp32, self.flp64) self.FixedF = (self.fip32, self.fip64) self.RealF = self.FixedF + self.FloatF self.VDCF = (self.IntF, self.RealF) def _print(self, pl , format, *args, **kw): if pl <= self.verbosity: try: if kw: text = format % kw elif args: text = format % args else: text = format except: text = string.join([format] + map(str, args)) if text[-1] != '\n': text = text + '\n' sys.stdout.write(text) def unpack(self, format): size = struct.calcsize(format) return struct.unpack(format, self.file.read(size)) def u8(self): return self.unpack("!B")[0] def u16(self): return self.unpack("!H")[0] def u24(self): t = self.unpack("!BH") return (t[0] << 16) | t[1] def u32(self): return self.unpack("!I")[0] def i8(self): return self.unpack("!b")[0] def i16(self): return self.unpack("!h")[0] def i24(self): t = self.unpack("!bH") return (t[0] << 16) | t[1] def i32(self): return self.unpack("!i")[0] def fip32(self): t = self.unpack("!hH") return t[0] + t[1] / 65536.0 def fip64(self): t = self.unpack("!hH") return t[0] + t[1] / (65536.0 * 65536.0) def flp32(self): return self.unpack("!f")[0] def flp64(self): return self.unpack("!d")[0] def Int(self): return self.IntF[reff.intprec]() def Real(self): return self.RealF[reff.realprec]() def Inx(self): return self.IntF[reff.inxprec]() def Enum(self): return self.unpack("!h")[0] def VDC(self): return self.VDCF[reff.vdc.type][reff.vdc.prec]() def Pnt(self): return (self.VDC() , self.VDC()) def getstr(self): lng = self.u8() return self.unpack("!" + `lng` + "s")[0] def getcol(self): if reff.color.mode == 1: cgmcol = self.unpack(reff.color.absstruct) cgmcol = map(operator.sub , cgmcol , reff.color.offset) cgmcol = map(operator.div , cgmcol , reff.color.scale) return cgmcol else: cgmcol = self.unpack(reff.color.inxstruct)[0] return reff.color.table[cgmcol % reff.color.maxindex] # 0x0020: def BEGMF(self, size): self._print(10 , '======= 0.1 =======') global dflt, reff dflt = cp(init) reff = dflt self.document() self.fntmap = range(78) self.fntmap.insert(0,0) # 0x0040: def ENDMF(self, size): pass # 0x0060: def BEGPIC(self, size): global curr, reff curr = cp(dflt) reff = curr if reff.vdc.extend == None: if reff.vdc.type == 0: reff.vdc.extend = reff.vdc.intextend reff.vdc.size = reff.vdc.intsize reff.vdc.prec = reff.vdc.intprec else: reff.vdc.extend = reff.vdc.realextend reff.vdc.size = reff.vdc.realsize reff.vdc.prec = reff.vdc.realprec if reff.vdc.prec == None: if reff.vdc.type == 0: reff.vdc.size = reff.vdc.intsize reff.vdc.prec = reff.vdc.intprec else: reff.vdc.size = reff.vdc.realsize reff.vdc.prec = reff.vdc.realprec Hgt = reff.vdc.extend[1][1] - reff.vdc.extend[0][1] Wdt = reff.vdc.extend[1][0] - reff.vdc.extend[0][0] LS = max(abs(Hgt),abs(Wdt)) if reff.clip.rect == None: reff.clip.rect = reff.vdc.extend if reff.marker.size == None: if reff.marker.sizemode == 0: reff.marker.size = LS / 100.0 else: reff.marker.size = 3 if reff.text.height == None: reff.text.height = LS / 100.0 if reff.edge.width == None: if reff.edge.widthmode == 0: reff.edge.width = LS / 1000.0 else: reff.edge.width = 1 if reff.line.width == None: if reff.line.widthmode == 0: reff.line.width = LS / 1000.0 else: reff.line.width = 1 ln = self.getstr() self.layer(name = ln) # 0x0080: def BEGPICBODY(self, size): self.mktrafo(reff.vdc.extend) # 0x00A0: def ENDPIC(self, size): pass # 0x1020: def mfversion(self, size): if self.u16() != 1: raise SketchLoadError("Can only load CGM version 1") # 0x1040: def mfdesc(self,size): pass # 0x1060: def vdctype(self,size): reff.vdc.type = self.Enum() if reff.vdc.type == 0: reff.vdc.size = reff.vdc.intsize reff.vdc.prec = reff.vdc.intprec reff.vdc.extend = reff.vdc.intextend else: reff.vdc.size = reff.vdc.realsize reff.vdc.prec = reff.vdc.realprec reff.vdc.extend = reff.vdc.realextend # 0x1080: def integerprec(self, size): bits = self.Int() if bits in (8,16,24,32): reff.intsize = (bits / 8) reff.intprec = reff.intsize - 1 else: raise SketchLoadError("This implementation can't work with %d bit integers" % (bits,)) # 0x10a0: def realprec(self, size): type = self.Enum() prec = (self.Int(), self.Int()) if type == 1: if prec == (16, 16): reff.realprec = 0 # 32 bit fixed precision elif prec == (32, 32): reff.realprec = 1 # 64 bit fixed precision else: raise SketchLoadError("This implementation can't work with %d,%d bit fixed points" % prec) else: if prec == (9, 23): reff.realprec = 2 # 32 bit floating point elif prec == (12, 52): reff.realprec = 3 # 64 bit floating point else: raise SketchLoadError("This implementation can't work with %d,%d bit floatingpoints" % prec) # 0x10c0: 'indexprec', def indexprec(self, size): bits = self.Int() if bits in (8,16,24,32): reff.inxsize = (bits / 8) reff.inxprec = reff.inxsize - 1 else: raise SketchLoadError("This implementation can't work with %d bit indices" % (bits,)) # 0x10e0: def colrprec(self, size): bits = self.Int() if bits == 8: reff.color.absstruct = "!BBB" elif bits == 16: reff.color.absstruct = "!HHH" elif bits == 32: reff.color.absstruct = "!III" else: raise SketchLoadError("This implementation can't work with %d bit color components" % (bits,)) # 0x1100: def colrindexprec(self, size): bits = self.Int() if bits == 8: reff.color.inxstruct = "!B" elif bits == 16: reff.color.inxstruct = "!H" elif bits == 32: reff.color.inxstruct = "!I" else: raise SketchLoadError("This implementation can't work with %d bit color indices" % (bits,)) # 0x1120: 'maxcolrindex', def maxcolrindex(self, size): reff.color.maxindex = self.unpack(reff.color.inxstruct)[0] reff.color.table = CreateColorTable(reff.color.maxindex) # 0x1140: def colrvalueext(self, size): bottom = self.unpack(reff.color.absstruct) top = self.unpack(reff.color.absstruct) reff.color.offset = map(operator.mul , bottom , (1.0, 1.0, 1.0)) reff.color.scale = map(operator.sub, top , reff.color.offset) # 0x1160: def mfelemlist(self, size): pass # 0x1180: def mfdfltrpl(self, size): self.interpret(size) # 0x11a0: def fontlist(self, size): tot = 0 fntinx = 1 while tot < size: fontname = self.getstr() bsteval = 100 bstinx = 0 for inx in range(len(fntlst)): fntname = fntlst[inx] baseinx = fntname.find('-') if baseinx == -1: baseinx = len(fntname) basename = fntname[0:baseinx] postfix = fntname[baseinx:] self._print(20 , "fontname = %s; basename = %s; postfix = %s\n" , fntname , basename , postfix) self._print(30 , "base = %s; baselst = %s\n" , basename , fntalias.get(basename , ())) baselst = (basename,) + fntalias.get(basename , ()) for suffix in ["" , "-Roman" , "-Regular" , "-Book"]: for base in baselst: score = strmatch(fontname + suffix , base + postfix) if score < bsteval: bsteval = score bstinx = inx self.fntmap[fntinx] = bstinx tot = tot + len(fontname) + 1 self._print(10 , 'font[%d]: %s => %s = %s\n' , fntinx , fontname , fntlst[bstinx] , fntlst[self.fntmap[fntinx]]) fntinx = fntinx + 1 # 0x2020: 'scalemode', def scalemode(self,size): reff.scale.mode = self.Enum() if reff.realprec in (2,3): # floatingpoint precision reff.scale.metric = self.Real() else: reff.scale.metric = self.flp32() if reff.scale.mode == 1 and reff.scale.metric == 0: self._print(10 , "Scale metric set to zero; mode set back to absolute") reff.scale.mode = 0 # 0x2040: def colrmode(self, size): reff.color.mode = self.Enum() # 0x2060: def linewidthmode(self, size): reff.line.widthmode = self.Enum() # 0x2080: def markersizemode(self, size): reff.marker.sizemode = self.Enum() # 0x20a0: def edgewidthmode(self, size): reff.edge.widthmode = self.Enum() def mktrafo(self, extend): if reff.scale.mode == 0: left, bottom = extend[0] right, top = extend[1] width = right - left height = top - bottom sc = 841 / (1.0 * max(abs(width) , abs(height))) else: left = 0 bottom = 0 width = 1 height = 1 sc = reff.scale.metric * 72 / 25.4 self.Scale = sc self.trafo = Scale(sign(width)*sc , sign(height)*sc)(Translation(-left , -bottom)) # 0x20c0: def vdcext(self, size): ll = self.Pnt() ur = self.Pnt() reff.vdc.extend = (ll,ur) # 0x20e0: def backcolr(self, size): self.getcol() # 0x3020: def vdcintegerprec(self, size): bits = self.Int() if bits in (8,16,24,32): reff.vdc.intsize = (bits / 8) reff.vdc.intprec = reff.vdc.intsize - 1 if reff.vdc.type == 0: reff.vdc.size = reff.vdc.intsize reff.vdc.prec = reff.vdc.intprec else: raise SketchLoadError("This implementation can't work with %d bit integers" % (bits,)) # 0x3040: def vdcrealprec(self, size): type = self.Enum() prec = (self.Int(), self.Int()) if type == 1: if prec == (16, 16): reff.vdc.realprec = 0 # 32 bit fixed precision elif prec == (32, 32): reff.vdc.realprec = 1 # 64 bit fixed precision else: raise SketchLoadError("This implementation can't work with %d,%d bit fixed points" % prec) else: if prec == (9, 23): reff.vdc.realprec = 2 # 32 bit floating point elif prec == (12, 52): reff.vdc.realprec = 3 # 64 bit floating point else: raise SketchLoadError("This implementation can't work with %d,%d bit floatingpoints" % prec) if reff.vdc.type == 1: reff.vdc.size = reff.vdc.realsize reff.vdc.prec = reff.vdc.realprec # 0x30a0: def cliprect(self, size): reff.clip.rect = (self.Pnt(), self.Pnt()) def Path(self, size): path = CreatePath() for i in range(size / (2 * reff.vdc.size)): path.AppendLine(self.trafo(self.Pnt())) return path def setlinestyle(self): style = basestyle.Duplicate() style.line_pattern = SolidPattern(apply(CreateRGBColor , reff.line.color)) style.line_width = reff.line.width if reff.line.widthmode == 0: style.line_width = style.line_width * self.Scale style.line_dashes = reff.line.dashtable[reff.line.type - 1] self.prop_stack.AddStyle(style) # 0x4020: def LINE(self, size): path = self.Path(size) self.setlinestyle() self.bezier((path,)) # 0x4040: def DISJTLINE(self, size): path = () for i in range(size / (4 * reff.vdc.size)): subpath = CreatePath() P = self.Pnt() subpath.AppendLine(self.trafo(P)) P = self.Pnt() subpath.AppendLine(self.trafo(P)) path = path + (subpath,) self.setlinestyle() self.bezier(path) # 0x4080: def TEXT(self, size): P = self.Pnt() F = self.Enum() S = self.getstr() T = Translation(self.trafo(P)) Py = Point(reff.text.orientation[0]).normalized() Px = Point(reff.text.orientation[1]).normalized() B = transform_base(Point(0.0, 0.0) , reff.text.expansion * Px , Py) self.style = basestyle.Duplicate() self.style.font = GetFont(fntlst[self.fntmap[reff.text.fontindex]]) self.style.font_size = reff.text.height * self.Scale self.style.fill_pattern = SolidPattern(apply(CreateRGBColor , reff.text.color)) O = text.SimpleText(text = S, trafo = T(B), halign = text.ALIGN_LEFT, valign = text.ALIGN_BASE, properties = self.get_prop_stack()) self.append_object(O) def setfillstyle(self): style = basestyle.Duplicate() if reff.fill.type == 1: style.fill_pattern = SolidPattern(apply(CreateRGBColor , reff.fill.color)) elif reff.fill.type == 3: style.fill_pattern = HatchingPattern(apply(CreateRGBColor , reff.fill.color), StandardColors.white, Point(2.0, 1.0), 5 , 1) if reff.edge.visible: style.line_pattern = SolidPattern(apply(CreateRGBColor , reff.edge.color)) style.line_width = reff.edge.width if reff.edge.widthmode == 0: style.line_width = style.line_width * self.Scale style.line_dashes = reff.edge.dashtable[reff.edge.type - 1] self.prop_stack.AddStyle(style) # 0x40e0: def POLYGON(self, size): path = self.Path(size) if path.Node(-1) != path.Node(0): path.AppendLine(path.Node(0)) path.load_close() self.setfillstyle() self.bezier((path,)) # 0x4100: def POLYGONSET(self, size): path = () subpath = CreatePath() for i in range(size / (2 * reff.vdc.size + 2)): P = self.Pnt() F = self.Enum() subpath.AppendLine(self.trafo(P)) if F in (2,3): if subpath.Node(-1) != subpath.Node(0): subpath.AppendLine(subpath.Node(0)) subpath.load_close() path = path + (subpath,) subpath = CreatePath() if subpath.len != 0: if subpath.Node(-1) != subpath.Node(0): subpath.AppendLine(subpath.Node(0)) subpath.load_close() path = path + (subpath,) self.setfillstyle() self.bezier(path) def bugmark(self, P): P = P - Point(1,1) style = basestyle.Duplicate() style.fill_pattern = SolidPattern(StandardColors.black) style.line_pattern = SolidPattern(StandardColors.black) self.prop_stack.AddStyle(style) self.rectangle(2, 0, 0, 2, P.x, P.y) # 0x4160: def RECT(self,size): ll = self.trafo(self.Pnt()) ur = self.trafo(self.Pnt()) lr = Point(ur.x , ll.y) ul = Point(ll.x , ur.y) T = transform_base(ll , lr , ul) self.setfillstyle() apply(self.rectangle , T.coeff()) # 0x4180: def CIRCLE(self,size): centre = self.trafo(self.Pnt()) radius = self.VDC() * self.Scale self.setfillstyle() self.ellipse(radius, 0, 0, radius, centre.x, centre.y) # 0x41a0: def ARC3PT(self, size): Po = self.trafo(self.Pnt()) Pm = self.trafo(self.Pnt()) Pe = self.trafo(self.Pnt()) Pc = Cnt3Pnt(Po, Pm, Pe) radius = abs(Po - Pc) if Angle2(Po - Pc , Pm - Pc) < Angle2(Po - Pc, Pe - Pc): Ao = Angle(Po - Pc) Ae = Angle(Pe - Pc) else: Ao = Angle(Pe - Pc) Ae = Angle(Po - Pc) self.setlinestyle() self.ellipse(radius, 0, 0, radius, Pc.x, Pc.y, Ao, Ae, 0) # 0x41c0: def ARC3PTCLOSE(self, size): Po = self.trafo(self.Pnt()) Pm = self.trafo(self.Pnt()) Pe = self.trafo(self.Pnt()) closetype = self.Enum() Pc = Cnt3Pnt(Po, Pm, Pe) radius = abs(Po - Pc) if Angle2(Po - Pc , Pm - Pc) < Angle2(Po - Pc, Pe - Pc): Ao = Angle(Po - Pc) Ae = Angle(Pe - Pc) else: Ao = Angle(Pe - Pc) Ae = Angle(Po - Pc) self.setfillstyle() self.ellipse(radius, 0, 0, radius, Pc.x, Pc.y, Ao, Ae, 2 - closetype) # 0x41e0: def ARCCTR(self,size): centre = self.trafo(self.Pnt()) Vo = self.trafo.DTransform(self.Pnt()) Ve = self.trafo.DTransform(self.Pnt()) radius = self.VDC() * self.Scale Ao = Angle(Vo) Ae = Angle(Ve) self.setlinestyle() self.ellipse(radius, 0, 0, radius, centre.x, centre.y, Ao, Ae, 0) # 0x4200: 'ARCCTRCLOSE', def ARCCTRCLOSE(self,size): centre = self.trafo(self.Pnt()) Vo = self.trafo.DTransform(self.Pnt()) Ve = self.trafo.DTransform(self.Pnt()) radius = self.VDC() * self.Scale closetype = self.Enum() Ao = Angle(Vo) Ae = Angle(Ve) self.setfillstyle() self.ellipse(radius, 0, 0, radius, centre.x, centre.y, Ao, Ae, 2 - closetype) # 0x4220: def ELLIPSE(self,size): centre = self.trafo(self.Pnt()) cdp1 = self.trafo(self.Pnt()) cdp2 = self.trafo(self.Pnt()) T = transform_base(centre , cdp1 , cdp2) self.setfillstyle() apply(self.ellipse , T.coeff()) # 0x4240: def ELLIPARC(self, size): centre = self.trafo(self.Pnt()) cdp1 = self.trafo(self.Pnt()) cdp2 = self.trafo(self.Pnt()) Vo = self.trafo.DTransform(self.Pnt()) Ve = self.trafo.DTransform(self.Pnt()) T = transform_base(centre , cdp1 , cdp2) Vo = T.inverse().DTransform(Vo) Ve = T.inverse().DTransform(Ve) Ao = Angle(Vo) Ae = Angle(Ve) self.setlinestyle() apply(self.ellipse , T.coeff() + (Ao, Ae, 0)) # 0x4260: def ELLIPARCCLOSE(self, size): centre = self.trafo(self.Pnt()) cdp1 = self.trafo(self.Pnt()) cdp2 = self.trafo(self.Pnt()) Vo = self.trafo.DTransform(self.Pnt()) Ve = self.trafo.DTransform(self.Pnt()) closetype = self.Enum() T = transform_base(centre , cdp1 , cdp2) Vo = T.inverse().DTransform(Vo) Ve = T.inverse().DTransform(Ve) Ao = Angle(Vo) Ae = Angle(Ve) self.setfillstyle() apply(self.ellipse , T.coeff() + (Ao, Ae, 2 - closetype)) # 0x5040 def linetype(self, size): reff.line.type = self.Inx() # 0x5060: def linewidth(self, size): if reff.line.widthmode == 0: reff.line.width = self.VDC() else: reff.line.width = self.Real() # 0x5080: def linecolr(self, size): reff.line.color = self.getcol() # 0x5100: def markercolr(self, size): reff.marker.color = self.getcol() # 0x5140: 'textfontindex', def textfontindex(self, size): reff.text.fontindex = self.Inx() self._print(10 , 'font[%d]: => %s\n' , reff.text.fontindex , fntlst[self.fntmap[reff.text.fontindex]]) # 0x5180: 'charexpan', def charexpan(self,size): reff.text.expansion = self.Real() # 0x51c0: def textcolr(self, size): reff.text.color = self.getcol() # 0x51e0: def charheight(self, size): reff.text.height = self.VDC() # 0x5200: def charori(self, size): reff.text.orientation = (self.Pnt(), self.Pnt()) # 0x52c0: def intstyle(self, size): reff.fill.type = self.Enum() # 0x52e0: def fillcolr(self, size): reff.fill.color = self.getcol() # 0x5360: def edgetype(self, size): reff.edge.type = self.Inx() # 0x5380: def edgewidth(self, size): if reff.edge.widthmode == 0: reff.edge.width = self.VDC() else: reff.edge.width = self.Real() # 0x53a0: def edgecolr(self, size): reff.edge.color = self.getcol() # 0x53c0: def edgevis(self, size): reff.edge.visible = self.Enum() # 0x5440: 'colrtable', def colrtable(self, size): i = self.unpack(reff.color.inxstruct)[0] size = size - struct.calcsize(reff.color.inxstruct) while size > struct.calcsize(reff.color.absstruct): cgmcol = self.unpack(reff.color.absstruct) cgmcol = map(operator.sub , cgmcol , reff.color.offset) cgmcol = map(operator.div , cgmcol , reff.color.scale) reff.color.table[i] = cgmcol size = size - struct.calcsize(reff.color.absstruct) i = i + 1 def interpret(self, sz): tell = self.file.tell Id = -1 pos = tell() start = pos while Id != 0x40 and pos < start + sz: head = self.u16() Id = head & 0xffe0 size = head & 0x001f hdsz = 2 if size == 31: size = self.u16() hdsz = 4 pdsz = ((size + 1) / 2) * 2 self._print(20 , '%4x at %5d) %5d(%5d): %4x: %s' , head, pos, size, pdsz, Id, CGM_ID.get(Id, '')) if hasattr(self, CGM_ID.get(Id, '')): self._print(30 , 'Calling %s' % (CGM_ID.get(Id, ''))) getattr(self, CGM_ID[Id])(size) else: if Id: self.file.read(pdsz) name = CGM_ID.get(Id, '') Class = Id >> 12 Elem = (Id & 0x0fff) >> 5 self._print(2, '*** unimplemented: %4x; class = %d, element = %2d %s' , Id , Class , Elem, name) pos = pos + hdsz + pdsz if tell() < pos: self.file.read(pos - tell()) elif tell() > pos: self._print(2, 'read too many bytes') self.file.seek(pos - tell(), 1) if pos != tell(): raise SketchLoadError("Lost position in File") def Load(self): self.file.seek(0,2) where = self.file.tell() self.file.seek(0,0) self.interpret(where) self.end_all() self.object.load_Completed() return self.object UniConvertor-1.1.4/src/app/plugins/Filters/aisaver.py0000755000076400007640000002562711042221303021313 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2007 by Igor Novikov # Copyright (C) 1998, 1999, 2000, 2002 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ###Sketch Config #type = Export #tk_file_type = (_("Adobe Illustrator v.5.0"), '.ai') #extensions = '.ai' format_name = 'Adobe Illustrator' #unload = 1 #standard_messages = 1 ###End (''"Adobe Illustrator") from math import floor, ceil, atan2, pi, hypot from app import ContAngle, ContSmooth, ContSymmetrical, Bezier, Line, \ StandardColors, EmptyPattern, TransformRectangle, UnitRect, \ RectanglePath, approx_arc, _sketch, Rotation, Translation from app.Lib.psmisc import quote_ps_string from app.Lib import encoding def cmyk(color): return color.getCMYK() #c = 1.0 - color.red #m = 1.0 - color.green #y = 1.0 - color.blue #k = min(c, m, y) #return c - k, m - k, y - k, k ps_join = (0, 1, 2) ps_cap = (None, 0, 1, 2) class AISaver: def __init__(self, file, filename, document, options): self.file = file self.filename = filename self.document = document self.options = options self.init_style() self.layers_as_groups = options.get('layers_as_groups', 0) self.gradients = {} self.fonts = {} document.WalkHierarchy(self.analyze, all = 1) self.write_header(document) def init_style(self): self.line_color = None self.line_width = None self.line_join = None self.line_cap = None self.line_dashes = None self.fill_color = None def analyze(self, object): if object.has_fill: fill = object.properties.fill_pattern if fill.is_Gradient: key = self.gradient_id(fill) if key is not None: self.gradients[key[0]] = key if object.has_font: font = object.properties.font self.fonts[font.PostScriptName()] = 1 def gradient_id(self, fill): gradient = fill.Gradient() if fill.is_AxialGradient: type = 0 name = '(Linear Gradient %d)' elif fill.is_RadialGradient: type = 1 name = '(Radial Gradient %d)' else: type = -1 if type >= 0: name = name % id(gradient) return (name, type, gradient) return None def write_header(self, document): write = self.file.write write('%!PS-Adobe-3.0 EPSF\n') # Illustrator seems to require 'Adobe Illustrator' in the # Creator comment. write('%%Creator: Adobe Illustrator exported by sK1\n') llx, lly, urx, ury = document.BoundingRect() write('%%%%BoundingBox: %d %d %d %d\n' % (floor(llx), floor(lly), ceil(urx), ceil(ury))) write('%%%%HiResBoundingBox: %g %g %g %g\n' % (llx, lly, urx, ury)) write('%AI5_FileFormat 3\n') # necessary for gradients write('%%EndComments\n') write('%%BeginProlog\n%%EndProlog\n') # Setup section write('%%BeginSetup\n') if self.gradients: write('%d Bn\n' % len(self.gradients)) for name, type, gradient in self.gradients.values(): colors = gradient.Colors()[:] write('%%AI5_BeginGradient: %s\n' % name) write('%s %d %d Bd\n' % (name, type, len(colors))) write('[\n') # reverse the colors for radial gradients if type == 1: for i in range(len(colors)): pos, color = colors[i] colors[i] = 1.0 - pos, color # also reverse for linear gradients because for some # reason Illustrator only accepts linear gradients when # they are stored with decreasing position. colors.reverse() for pos, color in colors: c, m, y, k = cmyk(color) write('%f %f %f %f 1 50 %d %%_Bs\n' % (c, m, y, k, 100*pos)) write('BD\n%AI5_EndGradient\n') if self.fonts: self.write_standard_encoding(encoding.iso_latin_1) for key in self.fonts.keys(): new_name = '_' + key write("%%AI3_BeginEncoding: %s %s\n" % (new_name, key)) # assume horizontal writing, roman typefaces, TE encoding: write("[/%s/%s %d %d %d TZ\n" % (new_name, key, 0, 0, 1)) write("%AI3_EndEncoding AdobeType\n") write('%%EndSetup\n') write('1 XR\n') def write_standard_encoding(self, new): write = self.file.write standard = encoding.adobe_standard last = None write("[") for i in range(len(standard)): if standard[i] != new[i]: if last != i - 1: if last is not None: write("\n") write("%d" % i) write("/%s" % new[i]) last = i write(" TE\n") def close(self): self.file.write('%%Trailer\n') self.file.write('%%EOF\n') self.file.close() def write_properties(self, properties, bounding_rect = None): write = self.file.write style = 0; gradient = None if properties.line_pattern is not EmptyPattern: style = style | 0x01 if properties.line_pattern.is_Solid: color = properties.line_pattern.Color() if color != self.line_color: self.line_color = color write('%f %f %f %f K\n' % cmyk(color)) if properties.line_dashes != self.line_dashes: self.line_dashes = properties.line_dashes write('[') for d in self.line_dashes: write('%d ') write('] 0 d\n') if properties.line_width != self.line_width: self.line_width = properties.line_width write('%f w\n' % self.line_width) if properties.line_join != self.line_join: self.line_join = properties.line_join write('%d j\n' % ps_join[self.line_join]) if properties.line_cap != self.line_cap: self.line_cap = properties.line_cap write('%d J\n' % ps_cap[self.line_cap]) if properties.fill_pattern is not EmptyPattern: style = style | 0x02 pattern = properties.fill_pattern if pattern.is_Solid: color = pattern.Color() if color != self.fill_color: self.fill_color = color write('%f %f %f %f k\n' % cmyk(color)) elif pattern.is_Gradient and bounding_rect: if pattern.is_AxialGradient or pattern.is_RadialGradient: self.write_gradient((pattern, bounding_rect), style) self.fill_color = None #gradient = pattern, bounding_rect return style, gradient def write_gradient(self, gradient, style): pattern, rect = gradient key = self.gradient_id(pattern) write = self.file.write #write('Bb\n') if pattern.is_AxialGradient: vx, vy = pattern.Direction() angle = atan2(vy, vx) - pi / 2 center = rect.center() rot = Rotation(angle, center) left, bottom, right, top = rot(rect) height = (top - bottom) * (1.0 - pattern.Border()) trafo = rot(Translation(center)) start = trafo(0, height / 2) write("1 %s %g %g %g %g 1 0 0 1 0 0 Bg\n" % (key[0], start.x, start.y, atan2(-vy, -vx) * 180.0 / pi, height)) elif pattern.is_RadialGradient: cx, cy = pattern.Center() cx = cx * rect.right + (1 - cx) * rect.left cy = cy * rect.top + (1 - cy) * rect.bottom radius = max(hypot(rect.left - cx, rect.top - cy), hypot(rect.right - cx, rect.top - cy), hypot(rect.right - cx, rect.bottom - cy), hypot(rect.left - cx, rect.bottom - cy)) radius = radius * (1.0 - pattern.Border()) write("0 0 0 0 Bh\n") write("1 %s %g %g 0 %g 1 0 0 1 0 0 Bg\n" % (key[0], cx, cy,radius)) #write("f\n") # XXX #if style == 0x07: # write("2 BB\n") #elif style == 0x03: # write("1 BB\n") #else: # write("0 BB\n") def PolyBezier(self, paths, properties, bounding_rect): write = self.file.write style, gradient = self.write_properties(properties, bounding_rect) if len(paths) > 1: write('*u\n') for path in paths: for i in range(path.len): type, control, p, cont = path.Segment(i) if type == Bezier: p1, p2 = control write('%g %g %g %g %g %g ' % (p1.x, p1.y, p2.x, p2.y, p.x, p.y)) if cont == ContAngle: write('C') else: write('c') else: write('%g %g ' % tuple(p)) if i > 0: if cont == ContAngle: write('L') else: write('l') else: write('m') write('\n') if path.closed: style = style | 0x04 if path is not paths[-1] or gradient is None: write('nSFBNsfb'[style] + '\n') else: self.write_gradient(gradient, style) style = style & ~0x04 if len(paths) > 1: write('*U\n') def SimpleText(self, object): properties = object.Properties() style, gradient = self.write_properties(properties, object.bounding_rect) write = self.file.write write("0 To\n") # point text trafo = object.FullTrafo() write("%g %g %g %g %g %g 0 Tp\n" % trafo.coeff()) write("TP\n") write("0 Tr\n") # fill text font = properties.font size = properties.font_size write("/_%s %g Tf\n" % (font.PostScriptName(), size)) write("(%s) Tx\n" % quote_ps_string(object.Text())) write("TO\n") def Image(self, object): write = self.file.write write("%AI5_File:\n%AI5_BeginRaster\n") write("[%g %g %g %g %g %g] " % object.Trafo().coeff()) bbox = object.bounding_rect image = object.Data().image # .image is the PIL image write("0 0 %d %d " % image.size) write("%d %d " % image.size) write("8 %d 0 0 0 0 XI\n" % len(image.mode)) _sketch.write_ps_hex(image.im, self.file, 72, '%') write("%AI5_EndRaster\n") # for whatever reason an image has to be followed by a N operator write("N\n") def BeginGroup(self): self.file.write('u\n') def EndGroup(self): self.file.write('U\n') def BeginLayer(self, layer): if self.layers_as_groups: self.BeginGroup() else: write = self.file.write write('%AI5_BeginLayer\n') write('%d 1 %d %d 0 0 ' % (layer.Visible(), not layer.Locked(), layer.Printable())) color = layer.OutlineColor() write('-1 %d %d %d Lb\n' % (255 * color.red, 255 * color.green, 255 * color.blue)) write('(%s) Ln\n' % quote_ps_string(layer.Name())) self.init_style() def EndLayer(self): if self.layers_as_groups: self.EndGroup() else: self.file.write('LB\n') self.file.write('%AI5_EndLayer--\n') def Save(self): for layer in self.document.Layers(): if not layer.is_SpecialLayer: self.BeginLayer(layer) self.save_objects(layer.GetObjects()) self.EndLayer() def save_objects(self, objects): for object in objects: if object.is_Compound: self.BeginGroup() self.save_objects(object.GetObjects()) self.EndGroup() elif object.is_SimpleText: self.SimpleText(object) elif object.is_Image: self.Image(object) elif object.is_Bezier or object.is_Rectangle or object.is_Ellipse: self.PolyBezier(object.Paths(), object.Properties(), object.bounding_rect) def save(document, file, filename, options = {}): #options['layers_as_groups'] = 1 saver = AISaver(file, filename, document, options) saver.Save() saver.close() UniConvertor-1.1.4/src/app/plugins/Filters/svgsaver.py0000755000076400007640000002227711162535604021536 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2007, 2008 by Igor E. Novikov # Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 by Bernhard Herzog # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. ###Sketch Config #type = Export #tk_file_type = ("Scalable Vector Graphics (SVG)", '.svg') #extensions = '.svg' format_name = 'SVG' #unload = 1 ###End from string import join, replace import re from math import atan2, hypot, pi from app import Bezier, EmptyPattern, Trafo, Rotation, Translation from app.conf import const from app._sketch import RGBColor def csscolor(color): return "#%02x%02x%02x" \ % (255 * color.red, 255 * color.green, 255 * color.blue) svg_joins = ('miter', 'round', 'bevel') svg_caps = (None, 'butt', 'round', 'square') def escape(data): """Escape &, \", ', <, and > in the string data. """ data = replace(data, "&", "&") data = replace(data, "<", "<") data = replace(data, ">", ">") data = replace(data, '"', """) data = replace(data, "'", "'") return data # FIXME: This is the definition of a simple triangular arrowhead It is # copied directly from the an example in the SVG spec This is a # prototype only. Do not consider this an acceptable long term solution arrow_head_def = ''' ''' class SVGSaver: def __init__(self, file, filename, document, options): self.file = file self.filename = filename self.document = document self.options = options self.idcount = 0 def close(self): self.file.close() def new_id(self): self.idcount = self.idcount + 1 return self.idcount fontMap = {"Times" : re.compile("Times-Roman.*"), "Helvetica" : re.compile("Helvetica.*"), "Courier" : re.compile("Courier.*"), } def make_style(self, properties, bounding_rect = None, omit_stroke = 0): """Return the properties as a value for the SVG style attribute If omit_stroke is true, ignore the line properties. This is needed when creating the style for text objects which can't be stroked in Sketch currently but may nevertheless have line properties (e.g. because of dynamic styles). """ style = [] if not omit_stroke and properties.line_pattern is not EmptyPattern: if properties.line_pattern.is_Solid: color = properties.line_pattern.Color().RGB() style.append("stroke:" + csscolor(color)) if properties.line_dashes != (): dash=[] for d in properties.line_dashes: dd=d*properties.line_width dash.append ('%g' % dd) style.append("stroke-dasharray:" + join(dash,',')) style.append('stroke-width:%g' % properties.line_width) if properties.line_join != const.JoinMiter: style.append('stroke-linejoin:' + svg_joins[properties.line_join]) if properties.line_cap != const.CapButt: style.append('stroke-linecap:' + svg_caps[properties.line_cap]) # FIXME: when arrow heads are implemented properly, change # this accordingly # FIXME: currently the orientation of the arrow heads is # wrong. if properties.line_arrow1 <> None: style.append('marker-start:url(#ArrowStart)') if properties.line_arrow2 <> None: style.append('marker-end:url(#ArrowEnd)') else: style.append("stroke:none") if properties.fill_pattern is not EmptyPattern: pattern = properties.fill_pattern if pattern.is_Solid: style.append("fill:" + csscolor(pattern.Color().RGB())) elif pattern.is_Gradient and bounding_rect: if pattern.is_AxialGradient or pattern.is_RadialGradient: gradient_id = self.write_gradient((pattern, bounding_rect), style) style.append("fill:url(#%s)" % gradient_id) else: style.append("fill:black") else: style.append("fill:none") if properties.font is not None: font = properties.font.PostScriptName() size = properties.font_size for svgfont, pattern in self.fontMap.items(): if pattern.match(font): font = svgfont break style.append("font-family:" + font) style.append("font-size:" + str(size)) return join(style, '; ') def write_gradient(self, (pattern, rect), style): write = self.file.write gradient_id = self.new_id() stops = pattern.Gradient().Colors() write('') if pattern.is_AxialGradient: vx, vy = pattern.Direction() angle = atan2(vy, vx) - pi / 2 center = rect.center() rot = Rotation(angle, center) left, bottom, right, top = rot(rect) height = (top - bottom) * (1.0 - pattern.Border()) trafo = self.trafo(rot(Translation(center))) start = trafo(0, height / 2) end = trafo(0, - height / 2) write('\n' % ((gradient_id,) + tuple(start) + tuple(end))) tag = 'linearGradient' elif pattern.is_RadialGradient: cx, cy = pattern.Center() cx = cx * rect.right + (1 - cx) * rect.left cy = cy * rect.top + (1 - cy) * rect.bottom radius = max(hypot(rect.left - cx, rect.top - cy), hypot(rect.right - cx, rect.top - cy), hypot(rect.right - cx, rect.bottom - cy), hypot(rect.left - cx, rect.bottom - cy)) radius = radius * (1.0 - pattern.Border()) cx, cy = self.trafo(cx, cy) write('\n' % (gradient_id, cx, cy, radius, cx, cy)) tag = 'radialGradient' stops = stops[:] stops.reverse() for i in range(len(stops)): pos, color = stops[i] stops[i] = 1.0 - pos, color for pos, color in stops: write('\n' % (pos, csscolor(color.RGB()))) write('\n' % tag) write('') return gradient_id def PolyBezier(self, paths, properties, bounding_rect): style = self.make_style(properties, bounding_rect) write = self.file.write write(' 0: data.append('L %g %g' % tuple(p)) else: data.append('M %g %g' % tuple(p)) if path.closed: data.append('z') write('d="%s"/>\n' % join(data, '')) def SimpleText(self, object): style = self.make_style(object.Properties(), object.bounding_rect, omit_stroke = 1) # Char glyphs are inherently upside-down in SVG (compared to # sketch) since the y axis points toward the bottom of the page # in SVG, but the characters are oriented toward the top of the # page. This extra transform inverts them. textTrafo = Trafo(1, 0, 0, -1, 0, 0) tm = self.trafo(object.Trafo()(textTrafo)).coeff() beginText = '\n' endText = '\n' self.file.write(beginText % (( style, ) + tm) ) self.file.write(escape(object.Text().encode("utf-8"))) self.file.write("\n") self.file.write(endText) def BeginGroup(self): self.file.write('\n') def EndGroup(self): self.file.write('\n') def Save(self): self.file.write('\n') self.file.write('\n') left, bottom, right, top = self.document.PageRect() width = right - left height = top - bottom self.trafo = Trafo(1, 0, 0, -1, -left, top) self.file.write('\n') # Put the definition of a simple triangular arrowhead in the file, # whether it is used or not # FIXME: for a proper solution for arrow heads, we could walk # over the object tree, collect all arrow heads actually used # and write them out here. self.file.write( arrow_head_def ) for layer in self.document.Layers(): if not layer.is_SpecialLayer and layer.Printable(): self.BeginGroup() self.save_objects(layer.GetObjects()) self.EndGroup() self.file.write('') def save_objects(self, objects): for object in objects: if object.is_Compound: self.BeginGroup() self.save_objects(object.GetObjects()) self.EndGroup() elif object.is_SimpleText: self.SimpleText(object) #elif object.is_Image: # self.Image(object) elif object.is_Bezier or object.is_Rectangle or object.is_Ellipse: self.PolyBezier(object.Paths(), object.Properties(), object.bounding_rect) def save(document, file, filename, options = {}): saver = SVGSaver(file, filename, document, options) saver.Save() saver.close() UniConvertor-1.1.4/src/app/plugins/Filters/sk1loader.py0000775000076400007640000004041311171455444021560 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2003-2008 by Igor Novikov # Copyright (C) 1997, 1998, 1999, 2000 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ###Sketch Config #type = Import #class_name = 'SKLoader' #rx_magic = '^##sK1 1 *(?P[0-9]+)' #tk_file_type = ('sK1 drawing', '.sk1') format_name = 'sK1' #standard_messages = 1 ###End (''"sK1 Document") from types import StringType, TupleType import os, sys, app from string import atoi from app.events.warn import warn, INTERNAL, pdebug, warn_tb from app import SketchLoadError, SketchError from app.plugins import plugins from app.io import load from app.conf import const from app.Graphics.color import ParseSketchColor, CreateCMYKColor, CreateSPOTColor from app import CreateRGBColor, SolidPattern, HatchingPattern,EmptyPattern,\ LinearGradient, ConicalGradient, RadialGradient, ImageTilePattern, \ Style, MultiGradient, Trafo, Translation, Point, \ GridLayer, GuideLayer, GuideLine, Arrow, CreatePath, StandardColors, \ GetFont from app.io.load import GenericLoader from app.Graphics import pagelayout, plugobj, blendgroup, text, image, eps,\ properties base_style = Style() base_style.fill_pattern = EmptyPattern base_style.fill_transform = 1 base_style.line_pattern = SolidPattern(StandardColors.black) base_style.line_width = 0.0 base_style.line_join = const.JoinMiter base_style.line_cap = const.CapButt base_style.line_dashes = () base_style.line_arrow1 = None base_style.line_arrow2 = None base_style.font = None base_style.font_size = 12.0 # sanity check: does base_style have all properties? for key in dir(properties.factory_defaults): if not hasattr(base_style, key): #warn(INTERNAL, 'added default for property %s', key) setattr(base_style, key, getattr(properties.factory_defaults, key)) papersizes = [ 'A0', 'A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7', 'letter', 'legal', 'executive', ] 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': c = CreateRGBColor(color_spec[1],color_spec[2],color_spec[3]) elif color_spec[0]=='CMYK': c = CreateCMYKColor(color_spec[1],color_spec[2],color_spec[3],color_spec[4]) elif color_spec[0]=='SPOT': 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]) 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 = plugins.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: # An exception was not converted to a SketchLoadError. # This should be considered a bug. warn_tb(INTERNAL, 'error in line %d:\n%s', num, `line`) if load._dont_handle_exceptions: raise else: raise SketchLoadError(_("error %s:%s in line %d:\n%s") % (sys.exc_info()[:2] +(num, `line`))) self.end_all() if self.page_layout: self.object.load_SetLayout(self.page_layout) for style in self.style_dict.values(): self.object.load_AddStyle(style) self.object.load_Completed() app.updateInfo(inf2='Pasing is finished',inf3=100) self.object.meta.native_format = 1 if __debug__: pdebug('timing', 'time:', time.clock() - start_time) return self.object def call_function(function, args, kwargs): if hasattr(function, 'im_func'): args = (function.im_self,) + args function = function.im_func code = function.func_code if code.co_flags & 0x000C: # uses *args or **kwargs return 0 args = args[:code.co_argcount] argnames = code.co_varnames[:code.co_argcount] for key in kwargs.keys(): if key not in argnames: del kwargs[key] try: apply(function, args, kwargs) except: warn_tb(INTERNAL, 'Trying to call function %s with reduced arglist', function.func_name) return 0 return 1 UniConvertor-1.1.4/src/app/plugins/Filters/wmfsaver.py0000644000076400007640000003114110740000122021472 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003 by Bernhard Herzog # This WMFsaver by Lukasz Pankowski (2003) # Based on CGMsaver mostly by Antoon Pardon (2002) # and WMFloader by Bernhard Herzog (2002) # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # $Id: wmfsaver.py,v 1.1.2.1 2003/06/09 17:56:13 bherzog Exp $ ###Sketch Config #type = Export #tk_file_type = ("Windows Metafile", '.wmf') #extensions = '.wmf' format_name = 'WMF' #unload = 1 ###End import struct from app import Bezier, EmptyPattern, Point, Polar, Trafo from app.conf import const # Fault Tolerance for flattening Beziers. # If you find the curves not smooth enough, lower this value. EPS = 2 rx_magic = '\xd7\xcd\xc6\x9a' struct_wmf_header = ('<' 'H' # Type 'H' # header size 'H' # Version 'I' # FileSize 'H' # Num. objects 'I' # Max. record size 'H' # Num. Parameters ) struct_placeable_header = ('<' '4s' # Key 'H' # handle 'h' # left 'h' # top 'h' # right 'h' # bottom 'H' # Inch 'I' # Reserved 'H' # Checksum ) EMPTY_PEN = 0 EMPTY_PATTERN = 1 MIN_OBJECT = 2 # less are reserved MAX_OBJECTS = 16 def rndtoint(num): return int(round(num)) def cr(P1, P2): return P1.x * P2.y - P1.y * P2.x def FlattenPath(P0, P1, P2, P3): P4 = (P0 + P1) / 2 P5 = (P1 + P2) / 2 P6 = (P2 + P3) / 2 P7 = (P4 + P5) / 2 P8 = (P5 + P6) / 2 P9 = (P7 + P8) / 2 B = P3 - P0 S = P9 - P0 C1 = P1 - P0 C2 = P2 - P3 # I couldn't find an example of a flattening algorithm so I came up # with the following criteria for deciding to stop the approximation # or to continue. # if either control vector is larger than the base vector continue if abs(C1) > abs(B) or abs(C2) > abs(B): return FlattenPath(P0, P4, P7, P9) + FlattenPath(P9, P8, P6, P3) # otherwise if the base is smaller than half the fault tolerance stop. elif abs(B) < EPS / 2: return (P9, P3) else: # if neither of the above applies, check for the following conditions. # if one of them is true continue the approximation otherwise stop # # The first constrol vector goes too far before the base # The seconde control vector goes too far behind the base # Both control vectors lie on either side of the base. # The midpoint is too far from base. N = B.normalized() if ((C1 * N) < -EPS or (C2 * N) > EPS or cr(C1,B)*cr(C2,B) < 0 or abs(cr(N,S)) > EPS): return FlattenPath(P0, P4, P7, P9) + FlattenPath(P9, P8, P6, P3) else: return (P9, P3) class WMFSaver: def __init__(self, file, pathname, options): self.file = file self.pathname = pathname self.options = options self.numobj = 0 self.maxrecord = 10 def pack(self, *args): self.file.write(apply(struct.pack , args)) def packrec(self, *args): if args[1] is None: # compute size of the record args = (args[0], struct.calcsize(args[0]) / 2) + args[2:] self.maxrecord = max(args[1], self.maxrecord) apply(self.pack, args) def putpolyrec(self, function, seq): fmt = '= 3: style = 1 # dash else: style = 2 # dot elif len(dashes) == 4: style = 3 # dash-dot else: style = 4 # dash-dot-dot self.add_select_object(struct.pack(' 32767: inch = 32767 / x sc = inch / 72. self.trafo = Trafo(sc, 0, 0, -sc, - sc * left, sc * top) self.Scale = sc self.inch = inch self.extend = map(rndtoint, tuple(self.trafo(left,bottom)) + tuple(self.trafo(right,top))) self.numobj = self.idx = MIN_OBJECT self.objects = [] self.maxrecord = 0 self.cur_pen = -1 self.cur_brush = -1 # Header self.write_headers() # SetWindowOrg self.packrec('' if hasattr(self, 'listtype'): return '<'+self.listtype+'>' return '<'+self.fourcc+'>' class Outline: outlineIndex='' color = None width = 0 caps=0 corner=0 spec=0 dashes = [] class BezierNode: point1=None point2=None point3=None class BezierCurve: outlineIndex='' colorIndex='' paths = [] scale=1 def __init__(self, outlineIndex, colorIndex, paths, scale): self.colorIndex=colorIndex self.outlineIndex=outlineIndex self.paths=paths self.scale=scale class InfoCollector: image=None cdr_version=0 objects=0 pages=0 layers=0 groups=0 bitmaps=0 compression=False numcount=0 bmp_chunks=[] bmp_dict={} obj_chunks=[] fill_chunks=[] outl_chunks=[] fill_data={} outl_data={} paths_heap=[] current_paths=[] outlineIndex=None default_outl_data=None colorIndex=None loda_type_func = None scale =.0002835 loader=None trafo_list=[] extracted_image = None page_chunk=None doc_page=() scale_with=1 def process_properties(self): self.loda_type_func = {0xa:self.loda_outl,0x14:self.loda_fild,0x1e:self.loda_coords} for chunk in self.fill_chunks: self.process_fill(chunk) outl_index=0 for chunk in self.outl_chunks: self.process_outline(chunk,outl_index) outl_index+=1 self.obj_chunks.reverse() for bmp in self.bmp_chunks: self.bmp_dict[ord(bmp.data[0])]=bmp self.get_page_size() for chunk in self.obj_chunks: if chunk: if chunk.is_group: self.paths_heap.append(0) else: self.process_paths(chunk) else: self.paths_heap.append(1) self.validate_heap() def validate_heap(self): paths_heap=self.paths_heap result=[] for obj in paths_heap: if obj==0: if result[-1]==1: result=result[:-1] elif result==[]: pass else: result.append(obj) else: result.append(obj) self.paths_heap=result def get_page_size(self): if self.page_chunk is None: return offset=0x4 if self.cdr_version >= 13: offset=0xc if self.cdr_version in [7,8]: offset=0x0 [width] = struct.unpack('= 13: ieeestart = 40 if cdr_version == 5: ieeestart = 18 (x_shift,y_shift)=self.doc_page [var0] = struct.unpack(' 1: self.bmpbuf = self.bmpbuf+'\x00\x01\x00\x00\x00\x00\x00\x00' for i in range (numcol): self.bmpbuf = self.bmpbuf+chunk.data[122+i*3:125+i*3]+'\x00' self.bmpbuf += chunk.data[bmpstart+40:] self.extracted_image = PIL.Image.open(StringIO.StringIO(self.bmpbuf )) self.extracted_image.load() def loda_coords(self,chunk,type,offset,version,trafo): if type == 1: # rectangle CoordX1 = 0 CoordY1 = 0 [CoordX2] = struct.unpack(' 0x7FFFFFFF: CoordX2 = CoordX2 - 0x100000000 if CoordY2 > 0x7FFFFFFF: CoordY2 = CoordY2 - 0x100000000 CoordX1, CoordY1=trafo(CoordX1, CoordY1) CoordX2, CoordY2=trafo(CoordX2, CoordY2) path = CreatePath() path.AppendLine(Point(CoordX1*self.scale, CoordY1*self.scale)) path.AppendLine(Point(CoordX2*self.scale, CoordY1*self.scale)) path.AppendLine(Point(CoordX2*self.scale, CoordY2*self.scale)) path.AppendLine(Point(CoordX1*self.scale, CoordY2*self.scale)) path.AppendLine(Point(CoordX1*self.scale, CoordY1*self.scale)) path.AppendLine(path.Node(0)) path.ClosePath() self.current_paths.append(path) if type == 3: # line and curve [pointnum] = struct.unpack(' 0x7FFFFFFF: CoordX = CoordX - 0x100000000 if CoordY > 0x7FFFFFFF: CoordY = CoordY - 0x100000000 CoordX, CoordY=trafo(CoordX, CoordY) Type = ord(chunk.data[offset+4+pointnum*8+i]) if Type&2 == 2: pass if Type&4 == 4: pass if Type&0x10 == 0 and Type&0x20 == 0: cont=ContAngle if Type&0x10 == 0x10: cont=ContSmooth if Type&0x20 == 0x20: cont=ContSymmetrical if Type&0x40 == 0 and Type&0x80 == 0: if path: self.current_paths.append(path) path = CreatePath() path.AppendLine(Point(CoordX*self.scale, CoordY*self.scale)) point1=None point2=None if Type&0x40 == 0x40 and Type&0x80 == 0: if path: path.AppendLine(Point(CoordX*self.scale, CoordY*self.scale)) point1=None point2=None if Type&0x40 == 0 and Type&0x80 == 0x80: path.AppendBezier(point1,point2,Point(CoordX*self.scale, CoordY*self.scale),cont) point1=None point2=None if Type&0x40 == 0x40 and Type&0x80 == 0x80: if point1: point2=Point(CoordX*self.scale, CoordY*self.scale) else: point1=Point(CoordX*self.scale, CoordY*self.scale) if Type&8 == 8: if path: path.ClosePath() if path: self.current_paths.append(path) if type == 5: # bitmap bmp_color_models = ('Invalid','Pal1','CMYK255','RGB','Gray','Mono','Pal6','Pal7','Pal8') bmp_clrmode = ord(chunk.data[offset+0x30]) clrdepth = ord(chunk.data[offset+0x22]) [width] = struct.unpack('= 13: ls_offset = 0x18 lc_offset = 0x1a ct_offset = 0x1c lw_offset = 0x1e offset = 0x28 dash_offset = 0x74 outl.spec=ord(chunk.data[ls_offset]) outl.caps=ord(chunk.data[lc_offset]) outl.corner=ord(chunk.data[ct_offset]) [line_width] = struct.unpack(' 0: outl.dashes = range(dashnum) for i in outl.dashes: [dash] = struct.unpack('= 13: pal = ord(chunk.data[0xc]) if pal < 3: fild_type = fild_pal_type[pal] else: fild_type = 'Unknown (%X)'%pal clr_offset = 0x8 if cdr_version >= 13: clr_offset = 0x1b if clr_offset < chunk.rawsize: clrmode = ord(chunk.data[clr_offset]) if fild_type == 'Solid': offset = 0x10 if cdr_version >= 13: offset =0x23 if clrmode == 9: #Grayscale fill_data[colorIndex]=CreateCMYKColor(0, 0, 0, 1.0 - ord(chunk.data[offset]) /255.0) elif clrmode == 5: #RGB fill_data[colorIndex]=CreateRGBColor(ord(chunk.data[offset+2]) / 255.0, ord(chunk.data[offset+1])/ 255.0, ord(chunk.data[offset]) / 255.0) elif clrmode == 4: #CMY fill_data[colorIndex]=CreateCMYKColor(ord(chunk.data[offset])/255.0, ord(chunk.data[offset+1])/255.0, ord(chunk.data[offset+2])/255.0, 0.0) elif clrmode == 3:#CMYK255 fill_data[colorIndex]=CreateCMYKColor(ord(chunk.data[offset])/255.0, ord(chunk.data[offset+1])/255.0, ord(chunk.data[offset+2])/255.0, ord(chunk.data[offset+3])/255.0) elif clrmode == 2: #CMYK fill_data[colorIndex]=CreateCMYKColor(ord(chunk.data[offset])/100.0, ord(chunk.data[offset+1])/100.0, ord(chunk.data[offset+2])/100.0, ord(chunk.data[offset+3])/100.0) elif clrmode == 1: fill_data[colorIndex]=CreateCMYKColor(ord(chunk.data[offset])/255.0, ord(chunk.data[offset+1])/255.0, ord(chunk.data[offset+2])/255.0, ord(chunk.data[offset+3])/255.0) elif clrmode == 0x11: fill_data[colorIndex]=CreateCMYKColor(ord(chunk.data[offset])/255.0, ord(chunk.data[offset+1])/255.0, ord(chunk.data[offset+2])/255.0, ord(chunk.data[offset+3])/255.0) elif clrmode == 0x14: #Registration Color fill_data[colorIndex]=CreateCMYKColor(1,1,1,1) else: fill_data[colorIndex]=CreateCMYKColor(0, 0, 0, .20) if fild_type == 'Transparent': fill_data[colorIndex]=None if fild_type == 'Gradient': fill_data[colorIndex]=CreateCMYKColor(0, 0, 0, .3) # else: # fill_data[colorIndex]=CreateCMYKColor(0, 1, 0, 0) class RiffEOF(Exception): pass class CDRLoader(GenericLoader): def __init__(self, file, filename, match): GenericLoader.__init__(self, file, filename, match) self.fix_tile = None self.fix_clip = 0 self.fix_lens = () self.object = None self.filename =filename self.verbosity=False self.info = None self.file=file def Load(self): try: self.file.seek(0) cdr = RiffChunk() cdr.load(self.file.read()) app.updateInfo(inf2=_("Parsing is finished"),inf3=10) self.document() self.layer('cdr_object', 1, 1, 0, 0, ('RGB',0,0,0)) if self.verbosity: text='' if cdr.infocollector.cdr_version>0: text+='CorelDRAW ver.%u'%cdr.infocollector.cdr_version+' \n' text+=' Pages: %u'%(cdr.infocollector.pages-1)+'\n' text+=' Layers: %u'%(cdr.infocollector.layers/cdr.infocollector.pages)+'\n' text+=' Groups: %u'%cdr.infocollector.groups+'\n' text+=' Objects: %u'%cdr.infocollector.objects+'\n' text+=' Bitmaps: %u'%cdr.infocollector.bitmaps+'\n' if cdr.infocollector.compression: text+=' COMPRESSED' sys.stderr.write(text) if cdr.infocollector.cdr_version>6: self.info=cdr.infocollector self.info.loader=self self.info.process_properties() self.import_curves() else: warn(USER, 'File <'+self.filename+ '> contains usupported CorelDRAW ver.%u'%cdr.infocollector.cdr_version+'.0 drawing') self.end_all() self.object.load_Completed() return self.object except RiffEOF: raise SketchLoadError(_("Unexpected problems in file parsing")) except: import traceback traceback.print_exc() raise def import_curves(self): objcount=0 objnum=len(self.info.paths_heap) jump=87.0/objnum interval=int((objnum/20)/10)+1 interval_count=0 for obj in self.info.paths_heap: objcount+=1 interval_count+=1 if interval_count>interval: interval_count=0 app.updateInfo(inf2=_("Interpreting object %u of %u")%(objcount,objnum),inf3=10+int(jump*objcount)) if obj==1: self.begin_group() elif obj==0: self.end_group() elif type(obj)==TupleType and obj[0]=='BMP': self.image(obj[1],obj[2]) else: style = self.style if obj.colorIndex: if self.info.fill_data.has_key(obj.colorIndex): style.fill_pattern = SolidPattern(self.info.fill_data[obj.colorIndex]) else: style.fill_pattern = EmptyPattern else: style.fill_pattern = EmptyPattern if obj.outlineIndex: if self.info.outl_data.has_key(obj.outlineIndex): if self.info.outl_data[obj.outlineIndex].spec & 0x01: style.line_pattern = EmptyPattern else: style.line_pattern = SolidPattern(self.info.outl_data[obj.outlineIndex].color) if self.info.outl_data[obj.outlineIndex].spec & 0x04: style.line_dashes = self.info.outl_data[obj.outlineIndex].dashes if self.info.outl_data[obj.outlineIndex].spec & 0x20: style.line_width = self.info.outl_data[obj.outlineIndex].width*obj.scale else: style.line_width = self.info.outl_data[obj.outlineIndex].width style.line_cap = self.info.outl_data[obj.outlineIndex].caps + 1 style.line_join = self.info.outl_data[obj.outlineIndex].corner else: style.line_pattern = EmptyPattern else: if self.info.default_outl_data: if self.info.default_outl_data.spec & 0x01: style.line_pattern = EmptyPattern else: style.line_pattern = SolidPattern(self.info.default_outl_data.color) if self.info.default_outl_data.spec & 0x04: style.line_dashes = self.info.default_outl_data.dashes if self.info.default_outl_data.spec & 0x20: style.line_width = self.info.default_outl_data.width*obj.scale else: style.line_width = self.info.default_outl_data.width style.line_cap = self.info.default_outl_data.caps + 1 style.line_join = self.info.default_outl_data.corner else: style.line_pattern = EmptyPattern object = PolyBezier(paths = tuple(obj.paths), properties = self.get_prop_stack()) self.append_object(object) if obj.outlineIndex: if self.info.outl_data[obj.outlineIndex].spec & 0x10: copy = object.Duplicate() copy.properties.SetProperty(line_width=0) self.append_object(copy) else: if self.info.default_outl_data.spec & 0x10: copy = object.Duplicate() copy.properties.SetProperty(line_width=0) self.append_object(copy) UniConvertor-1.1.4/src/app/plugins/Filters/bziploader.py0000644000076400007640000000347310740000121022001 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1998, 1999, 2000, 2002 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ###Sketch Config #type = Import #class_name = 'BZIP2Loader' #rx_magic = 'BZh' #tk_file_type = ('bzipped Files', '.bz2') #format_name = 'bzipped-Meta' #standard_messages = 1 ###End (''"Bzipped Files") import os from app import SketchLoadError from app.io import load from app.utils.os_utils import sh_quote class BZIP2Loader: def __init__(self, file, filename, match): self.file = file self.filename = filename self.match = match self.messages = '' self.doc_class = None def set_doc_class(self, doc_class): self.doc_class = doc_class def Load(self): basename, ext = os.path.splitext(self.filename) if ext != '.bz2': basename = self.filename stream = os.popen('bzip2 -d -c ' + sh_quote(self.filename)) doc = load.load_drawing_from_file(stream, basename, doc_class = self.doc_class) if doc: doc.meta.compressed = "bzip2" doc.meta.compressed_file = self.filename self.messages = doc.meta.load_messages return doc def Messages(self): return self.messages UniConvertor-1.1.4/src/app/plugins/Filters/pdfgensaver.py0000664000076400007640000002661311207635252022177 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2007-2008 by Igor Novikov # Copyright (C) 2000, 2001, 2002 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ###Sketch Config #type = Export #tk_file_type = ("Portable Document Format (PDF)", '.pdf') #extensions = '.pdf' #format_name = 'PDF-Reportlab' #unload = 1 ###End from math import atan2, pi import PIL from app import _,Bezier, EmptyPattern, Rotation, Translation, _sketch from app.Graphics.curveop import arrow_trafos import reportlab.pdfgen.canvas import app def make_pdf_path(pdfpath, paths): for path in paths: for i in range(path.len): type, control, p, cont = path.Segment(i) if type == Bezier: p1, p2 = control pdfpath.curveTo(p1.x, p1.y, p2.x, p2.y, p.x, p.y) else: if i > 0: pdfpath.lineTo(p.x, p.y) else: pdfpath.moveTo(p.x, p.y) if path.closed: pdfpath.close() return pdfpath class PDFDevice: has_axial_gradient = 0 has_radial_gradient = 0 has_conical_gradient = 0 gradient_steps = 100 def __init__(self, pdf): self.pdf = pdf def PushTrafo(self): self.pdf.saveState() def Concat(self, trafo): apply(self.pdf.transform, trafo.coeff()) def Translate(self, x, y = None): if y is None: x, y = x self.pdf.translate(x, y) def Rotate(self, angle): self.pdf.rotate(angle) def Scale(self, scale): self.pdf.scale(scale, scale) def PopTrafo(self): self.pdf.restoreState() PushClip = PushTrafo PopClip = PopTrafo def SetFillColor(self, color): self.pdf.setFillColor(tuple(color)) def SetLineColor(self, color): self.pdf.setStrokeColor(tuple(color)) def SetLineAttributes(self, width, cap = 1, join = 0, dashes = ()): self.pdf.setLineWidth(width) self.pdf.setLineCap(cap - 1) self.pdf.setLineJoin(join) if dashes: dashes = list(dashes) w = width if w < 1.0: w = 1.0 for i in range(len(dashes)): dashes[i] = w * dashes[i] self.pdf.setDash(dashes) def DrawLine(self, start, end): self.pdf.line(start.x, start.y, end.x, end.y) def DrawLineXY(self, x1, y1, x2, y2): self.pdf.line(x1, y1, x2, y2) def DrawRectangle(self, start, end): self.pdf.rectangle(start.x, start.y, end.x - start.x, end.y - start.y, 1, 0) def FillRectangle(self, left, bottom, right, top): self.pdf.rect(left, bottom, right - left, top - bottom, 0, 1) def DrawCircle(self, center, radius): self.pdf.circle(center.x, center.y, radius, 1, 0) def FillCircle(self, center, radius): self.pdf.circle(center.x, center.y, radius, 0, 1) def FillPolygon(self, pts): path = self.pdf.beginPath() apply(path.moveTo, pts[0]) for x, y in pts: path.lineTo(x, y) path.close() self.pdf.drawPath(path, 0, 1) def DrawBezierPath(self, path, rect = None): self.pdf.drawPath(make_pdf_path(self.pdf.beginPath(), (path,)), 1, 0) def FillBezierPath(self, path, rect = None): self.pdf.drawPath(make_pdf_path(self.pdf.beginPath(), (path,)), 0, 1) class PDFGenSaver: def __init__(self, file, filename, document, options): self.file = file self.filename = filename self.document = document self.options = options # if there's a pdfgen_canvas option assume it's an instance of # reportlab.pdfgen.canvas.Canvas that we should render on. This # allows multiple documents to be rendered into the same PDF # file or to have other python code outside of Sketch such as # reportlab itself (more precisely one of its other components # besides pdfgen) render into to too. # # The code here assumes that the canvas is already setup # properly. if options.has_key("pdfgen_canvas"): self.pdf = options["pdfgen_canvas"] else: self.pdf = reportlab.pdfgen.canvas.Canvas(file) self.pdf.setPageSize(document.PageSize()) def close(self): if not self.options.has_key("pdfgen_canvas"): self.pdf.save() def set_properties(self, properties, bounding_rect = None): pattern = properties.line_pattern if not pattern.is_Empty: if pattern.is_Solid: c, m, y, k =pattern.Color().getCMYK() self.pdf.setStrokeColorCMYK(c, m, y, k) self.pdf.setLineWidth(properties.line_width) self.pdf.setLineJoin(properties.line_join) self.pdf.setLineCap(properties.line_cap - 1) dashes = properties.line_dashes if dashes: dashes = list(dashes) w = properties.line_width if w < 1.0: w = 1.0 for i in range(len(dashes)): dashes[i] = w * dashes[i] self.pdf.setDash(dashes) active_fill = None pattern = properties.fill_pattern if not pattern.is_Empty: if pattern.is_Solid: c, m, y, k =pattern.Color().getCMYK() self.pdf.setFillColorCMYK(c, m, y, k) elif pattern.is_Tiled: pass elif pattern.is_AxialGradient: active_fill = self.axial_gradient else: active_fill = self.execute_pattern return active_fill def axial_gradient(self, properties, rect): pattern = properties.fill_pattern vx, vy = pattern.Direction() angle = atan2(vy, vx) - pi / 2 center = rect.center() rot = Rotation(angle, center) left, bottom, right, top = rot(rect) trafo = rot(Translation(center)) image = PIL.Image.new('RGB', (1, 200)) border = int(round(100 * pattern.Border())) _sketch.fill_axial_gradient(image.im, pattern.Gradient().Colors(), 0, border, 0, 200 - border) self.pdf.saveState() apply(self.pdf.transform, trafo.coeff()) self.pdf.drawInlineImage(image, (left - right) / 2, (bottom - top) / 2, right - left, top - bottom) self.pdf.restoreState() def execute_pattern(self, properties, rect): device = PDFDevice(self.pdf) properties.fill_pattern.Execute(device, rect) def make_pdf_path(self, paths): return make_pdf_path(self.pdf.beginPath(), paths) def polybezier(self, paths, properties, bounding_rect, clip = 0): pdfpath = self.make_pdf_path(paths) active_fill = self.set_properties(properties, bounding_rect) if active_fill: if not clip: self.pdf.saveState() self.pdf.clipPath(pdfpath, 0, 0) active_fill(properties, bounding_rect) if not clip: self.pdf.restoreState() if properties.HasLine(): self.pdf.drawPath(pdfpath, 1, 0) else: if clip: method = self.pdf.clipPath else: method = self.pdf.drawPath method(self.make_pdf_path(paths), properties.HasLine(), properties.HasFill()) # draw the arrows if properties.HasLine(): # Set the pdf fill color to the line color to make sure that # arrows that are filled are filled with the line color of # the object. Since lines are always drawn last, this # shouldn't interfere with the object's fill. c, m, y, k = properties.line_pattern.Color().getCMYK() self.pdf.setFillColorCMYK(c, m, y, k) arrow1 = properties.line_arrow1 arrow2 = properties.line_arrow2 if arrow1 or arrow2: for path in paths: t1, t2 = arrow_trafos(path, properties) if arrow1 and t1 is not None: self.draw_arrow(arrow1, t1) if arrow2 and t2 is not None: self.draw_arrow(arrow2, t2) def draw_arrow(self, arrow, trafo): path = arrow.Paths()[0].Duplicate() path.Transform(trafo) pdfpath = self.make_pdf_path((path,)) if arrow.IsFilled(): self.pdf.drawPath(pdfpath, 0, 1) else: self.pdf.drawPath(pdfpath, 1, 0) def mask_group(self, object): mask = object.Mask() if not mask.has_properties: # XXX implement this case (raster images) return if mask.is_curve: self.pdf.saveState() prop = mask.Properties().Duplicate() prop.SetProperty(line_pattern = EmptyPattern) self.polybezier(mask.Paths(), prop, mask.bounding_rect, clip = 1) self.save_objects(object.MaskedObjects()) if mask.has_line and mask.Properties().HasLine(): prop = mask.Properties().Duplicate() prop.SetProperty(fill_pattern = EmptyPattern) self.polybezier(mask.Paths(), prop, mask.bounding_rect, clip = 1) self.pdf.restoreState() def raster_image(self, object): self.pdf.saveState() apply(self.pdf.transform, object.Trafo().coeff()) self.pdf.drawInlineImage(object.Data().Image(), 0, 0) self.pdf.restoreState() def simple_text(self, object, clip = 0): properties = object.Properties() active_fill = self.set_properties(properties, object.bounding_rect) fontname = properties.font.PostScriptName() if fontname not in self.pdf.getAvailableFonts(): fontname = 'Times-Roman' if active_fill and not clip: self.pdf.saveState() pdftext = self.pdf.beginText() if active_fill: pdftext.setTextRenderMode(7) elif clip: pdftext.setTextRenderMode(4) pdftext.setFont(fontname, properties.font_size) apply(pdftext.setTextTransform, object.FullTrafo().coeff()) pdftext.textOut(object.Text()) self.pdf.drawText(pdftext) if active_fill: active_fill(properties, object.bounding_rect) if not clip: self.pdf.restoreState() def path_text(self, object, clip = 0): properties = object.Properties() active_fill = self.set_properties(properties, object.bounding_rect) fontname = properties.font.PostScriptName() if fontname not in self.pdf.getAvailableFonts(): fontname = 'Times-Roman' if active_fill and not clip: self.pdf.saveState() pdftext = self.pdf.beginText() if active_fill: pdftext.setTextRenderMode(7) elif clip: pdftext.setTextRenderMode(4) pdftext.setFont(fontname, properties.font_size) trafos = object.CharacterTransformations() text = object.Text() for i in range(len(trafos)): apply(pdftext.setTextTransform, trafos[i].coeff()) pdftext.textOut(text[i]) self.pdf.drawText(pdftext) if active_fill: active_fill(properties, object.bounding_rect) if not clip: self.pdf.restoreState() def Save(self): self.document.updateActivePage() masters=self.document.getMasterLayers() count=0 pagenum=len(self.document.pages) interval=int(97/pagenum) for page in self.document.pages: count+=1 app.updateInfo(inf2=_('Composing page %u of %u')%(count,pagenum),inf3=count*interval) layers=page+masters for layer in layers: if not layer.is_SpecialLayer and layer.Printable(): self.save_objects(layer.GetObjects()) self.pdf.showPage() def save_objects(self, objects): for object in objects: if object.is_Compound: if object.is_MaskGroup: self.mask_group(object) else: self.save_objects(object.GetObjects()) elif object.is_SimpleText: # self.simple_text(object) obj=object.AsBezier() self.polybezier(obj.Paths(), obj.Properties(), obj.bounding_rect) elif object.is_PathTextText: self.path_text(object) elif object.is_Image: self.raster_image(object) elif object.is_Bezier or object.is_Rectangle or object.is_Ellipse: self.polybezier(object.Paths(), object.Properties(), object.bounding_rect) def save(document, file, filename, options = {}): app.updateInfo(inf1=_('PDF generation.'),inf2=_('Start document composing'),inf3=3) saver = PDFGenSaver(file, filename, document, options) saver.Save() saver.close() app.updateInfo(inf2=_('Document generation is finished'),inf3=100) UniConvertor-1.1.4/src/app/plugins/Filters/sk1saver.py0000775000076400007640000003733711102454046021434 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2003-2008 by Igor Novikov # Copyright (C) 1997, 1998, 1999, 2000 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ###Sketch Config #type = Export #tk_file_type = (_("sK1 Document"), '.sk1') #extensions = '.sk1' format_name = 'sK1' #standard_messages = 1 ###End # # A class to save a document in sK1 format (sK1 own format) # # # Functions used in an sk file: # # document() # # Marks the beginning of the document. In the future, this # function may take some arguments that describe global properties # of the document # # layout(PAPER_FORMAT, ORIENTATION) # # Defines the paper format. PAPER_FORMAT may be a string such 'A4' # or a tuple (WIDTH, HEIGHT) defining a non standard paper size. # ORIENTATION is 0 for portrait or 1 for landscape (see # pagelayout.py) # # # Properties: # # gl([(POS1, COLOR1), (POS2, COLOR2), ...]) # # `gradient linear' # # pe() # # `pattern empty' # # ps(COLOR) # # `pattern solid' # # pgl(DX, DY, BORDER) # # `pattern gradient linear' # # pgr(CX, CY, BORDER) # # `pattern gradient radial' # # pgc(CX, CY, DX, DY) # # `pattern gradient conical' # # phs(COLOR, BACKGROUND, DX, DY, GAP, WIDTH) # # `pattern hatching simple' # # pit(ID, TRAFO) # # `pattern image tile' (id as in `im') # # fp() `fill pattern' # # fe() `fill empty' # # ft(BOOL) `fill transform' # # lp() `line pattern' # # le() `line empty' # # lw() `line width' # # lc(CAP) # # `line cap' # # lj(JOIN) # # `line join' # # ld(DASHES) # # `line dashes' # # la1([ARROW]) # missing ARROW means explicit empty arrow # la2([ARROW]) # # `line arrows' # # Fn(FONTNAME) # # `font name' # # Fs(SIZE) # # `font size' # # dstyle(NAME) # # Define new dynamic style named NAME with the current properties # # style(name) # # Use style NAME. # # # Objects: # # layer(NAME, VISIBLE, PRINTABLE, LOCKED, OUTLINED, OUTLINE_COLOR) # # Start a new layer named NAME. # # grid((XORIG, YORIG, XWIDTH, YWIDTH)) # # Insert the grid layer at this position and set the grid geometry. # # guidelayer(NAME, ...) # # Start the guide layer # # guide(COORD, HOR) # # Define a guide line. If HOR is true, it is horizontal, else it # is vertical. guide() is only allowed in a guide layer # # G() # G_() # # All objects defined between G() and the corresponding G_() are # part of a group. Groups may be nested. # # B() # Bi(STEPS) # B_() # # A blend group # # # M() # M_() # # A Mask group. the first object after M() is the mask # # PT() # pt(TEXT[, MATRIX][, MODEL) # PT_() # # Text on a path. The path is the only object between the pt and # PT_ functions. # # # b() # start a bezier obj # bs(X, Y, CONT) append a line segment # bc(X1, Y1, X2, Y2, X3, Y3, CONT) append a bezier segment # bn() start a new path # bC() close path # # r(TRAFO [, RADIUS1, RADIUS2]) # # Rectangle, described by the transformation needed to transform # the unit square into the rectangle. # # e(TRAFO, [start_angle, end_angle, arc_type]) # # Ellipse, described similarly to the rectangle. # # txt(TEXT, TRAFO[, HORIZ_ALIGN, VERT_ALIGN]) # # bm(ID[, filename]) # # Bitmap image data. The bitmap data is either read from the file # given by filename of if that parameter is not present, follows # as a base64 encoded ppm file. (We should have compression here, # maybe by using png or a similar format) # # im(TRAFO, ID) # # A bitmap image. ID has to be the id of a previously defined # bitmap data object (defined by bm). # # eps(TRAFO, FILENAME) # # external EPS file # # # PC(NAME[, arg1, arg2, ...][, kwarg, kwarg, ...]) # PC_() # # A plugin compound object. The arguments to PC() should be # sufficient to describe the entire compound and to reconstruct # the objects in between PC and PC_. These contained objects are # meant for installations where the plugin is not available. # # Default properties in an sk-file: # # Fill Properties: # # fill_pattern EmptyPattern # fill_transform 1 # # Line Properties: # # line_pattern solid black # line_width 0pt # line_join JoinMiter # line_cap CapButt # line_dashes () # # Font Properties: # # font None # font_size 12pt # # import os from app.utils.os_utils import relpath, Empty from app import IdentityMatrix, EmptyPattern, SolidPattern, Style, \ StandardColors, SketchError, const from app.Graphics import properties from app.Graphics.image import CMYK_IMAGE from app.Lib.units import m_to_pt, in_to_pt base_style = Style() base_style.fill_pattern = EmptyPattern base_style.fill_transform = 1 base_style.line_pattern = SolidPattern(StandardColors.black) base_style.line_width = 0.0 base_style.line_join = const.JoinMiter base_style.line_cap = const.CapButt base_style.line_dashes = () base_style.line_arrow1 = None base_style.line_arrow2 = None base_style.font = None base_style.font_size = 12.0 papersizes = { 'A0': (0.841 * m_to_pt, 1.189 * m_to_pt), 'A1': (0.594 * m_to_pt, 0.841 * m_to_pt), 'A2': (0.420 * m_to_pt, 0.594 * m_to_pt), 'A3': (0.297 * m_to_pt, 0.420 * m_to_pt), 'A4': (0.210 * m_to_pt, 0.297 * m_to_pt), 'A5': (0.148 * m_to_pt, 0.210 * m_to_pt), 'A6': (0.105 * m_to_pt, 0.148 * m_to_pt), 'A7': (0.074 * m_to_pt, 0.105 * m_to_pt), 'letter': (8.5 * in_to_pt, 11 * in_to_pt), 'legal': (8.5 * in_to_pt, 14 * in_to_pt), 'executive': (7.25 * in_to_pt, 10.5 * in_to_pt) } class SketchSaveError(SketchError): pass def color_repr(color): # return '(%g,%g,%g)' % tuple(color.RGB()) return color.toSave() default_options = {'full_blend' : 0} class SKSaver: def __init__(self, file, filename, kw): self.file = file self.filename = filename if self.filename: self.directory = os.path.split(filename)[0] else: self.directory = '' self.style_dict = {} self.write_header() options = default_options.copy() options.update(kw) self.options = apply(Empty, (), options) self.saved_ids = {} def __del__(self): self.Close() def Close(self): pass #if not self.file.closed: # self.file.close() def write_header(self): self.file.write('##sK1 1 2\n') def BeginDocument(self): self.file.write('document()\n') def EndDocument(self): pass def BeginLayer(self, name, visible, printable, locked, outlined, color): self.file.write('layer(%s,%d,%d,%d,%d,%s)\n' % (`name`, visible, printable, locked, outlined, color_repr(color))) def BeginMasterLayer(self, name, visible, printable, locked, outlined, color): self.file.write('masterlayer(%s,%d,%d,%d,%d,%s)\n' % (`name`, visible, printable, locked, outlined, color_repr(color))) def EndLayer(self): pass def Page(self): self.file.write('page()\n') def BeginGuideLayer(self, name, visible, printable, locked, outlined, color): self.file.write('guidelayer(%s,%d,%d,%d,%d,%s)\n' % (`name`, visible, printable, locked, outlined, color_repr(color))) EndGuideLayer = EndLayer def BeginGridLayer(self, geometry, visible, outline_color, name): self.file.write('grid((%g,%g,%g,%g),%d,%s,%s)\n' % (geometry + (visible, color_repr(outline_color), `name`))) EndGridLayer = EndLayer def PageLayout(self, format, width, height, orientation): if format and papersizes.has_key(format): self.file.write('layout(%s,%d)\n' % (`format`, orientation)) else: self.file.write('layout((%g,%g),%d)\n' % (width, height, orientation)) def BeginGroup(self): self.file.write('G()\n') def EndGroup(self): self.file.write('G_()\n') def Gradient(self, colors): write = self.file.write write('gl([') write_comma = 0 for pos, color in colors: if write_comma: write(',') else: write_comma = 1 write('(%g,%s)' % (pos, color_repr(color))) write('])\n') def EmptyPattern(self): self.file.write('pe()\n') def SolidPattern(self, color): self.file.write('ps(%s)\n' % color_repr(color)) def LinearGradientPattern(self, gradient, direction, border): gradient.SaveToFile(self) self.file.write('pgl(%g,%g,%g)\n' % (round(direction.x, 10), round(direction.y, 10), border)) def RadialGradientPattern(self, gradient, center, border): gradient.SaveToFile(self) self.file.write('pgr(%g,%g,%g)\n' % (center.x, center.y, border)) def ConicalGradientPattern(self, gradient, center, direction): gradient.SaveToFile(self) self.file.write('pgc(%g,%g,%g,%g)\n' % (tuple(center) + (round(direction.x, 10), round(direction.y, 10)))) def HatchingPattern(self, color, background, direction, distance, width): self.file.write('phs(%s,%s,%g,%g,%g,%g)\n' % (color_repr(color), color_repr(background), direction.x, direction.y, distance, width)) def ImageTilePattern(self, image, trafo, relative_filename = 1): self.write_image(image, relative_filename) self.file.write('pit(%d,(%g,%g,%g,%g,%g,%g))\n' % ((id(image),) + trafo.coeff())) def write_style(self, style): write = self.file.write if hasattr(style, 'fill_pattern'): pattern = style.fill_pattern if pattern is EmptyPattern: write('fe()\n') elif isinstance(pattern, SolidPattern): write('fp(%s)\n' % color_repr(pattern.Color())) else: pattern.SaveToFile(self) write('fp()\n') if hasattr(style, 'fill_transform'): write('ft(%d)\n' % style.fill_transform) if hasattr(style, 'line_pattern'): pattern = style.line_pattern if pattern is EmptyPattern: write('le()\n') elif isinstance(pattern, SolidPattern): write('lp(%s)\n' % color_repr(pattern.Color())) else: pattern.SaveToFile(self) write('lp()\n') if hasattr(style, 'line_width') : write('lw(%g)\n' % style.line_width) if hasattr(style, 'line_cap'): write('lc(%d)\n' % style.line_cap) if hasattr(style, 'line_join'): write('lj(%d)\n' % style.line_join) if hasattr(style, 'line_dashes'): write('ld(%s)\n' % `style.line_dashes`) if hasattr(style, 'line_arrow1'): if style.line_arrow1 is not None: write('la1(%s)\n' % `style.line_arrow1.SaveRepr()`) else: write('la1()\n') if hasattr(style, 'line_arrow2'): if style.line_arrow2 is not None: write('la2(%s)\n' % `style.line_arrow2.SaveRepr()`) else: write('la2()\n') if hasattr(style, 'font'): write('Fn(%s)\n' % `style.font.PostScriptName()`) if hasattr(style, 'font_size'): write('Fs(%g)\n' % style.font_size) def DynamicStyle(self, style): self.write_style(style) self.file.write('dstyle(%s)\n' % `style.Name()`) def write_style_no_defaults(self, style): style = style.Copy() for key, value in base_style.__dict__.items(): if hasattr(style, key) and getattr(style, key) == value: delattr(style, key) self.write_style(style) def Properties(self, properties): styles = properties.stack[:] styles.reverse() if styles[0].is_dynamic: self.file.write('style(%s)\n' % `style[0].Name()`) else: self.write_style_no_defaults(styles[0]) for style in styles[1:]: if style.is_dynamic: self.file.write('style(%s)\n' % `style.Name()`) else: self.write_style(style) def Rectangle(self, trafo, radius1 = 0, radius2 = 0): if radius1 == radius2 == 0: self.file.write('r(%g,%g,%g,%g,%g,%g)\n' % trafo.coeff()) else: self.file.write('r(%g,%g,%g,%g,%g,%g,%g,%g)\n' % (trafo.coeff() + (radius1, radius2))) def Ellipse(self, trafo, start_angle, end_angle, arc_type): if start_angle == end_angle: self.file.write('e(%g,%g,%g,%g,%g,%g)\n' % trafo.coeff()) else: self.file.write('e(%g,%g,%g,%g,%g,%g,%g,%g,%d)\n' % (trafo.coeff()+(start_angle,end_angle,arc_type))) def PolyBezier(self, paths): write = self.file.write write('b()\n') for path in paths: if path is not paths[0]: write('bn()\n') try: path.write_to_file(self.file) except TypeError: # self.file is no ordinary file (not tested!) list = path.get_save() for item in list: if len(item) == 3: write('bs(%g,%g,%d)\n' % item) elif len(item) == 7: write('bc(%g,%g,%g,%g,%g,%g,%d)\n' % item) else: raise SketchSaveError('got ivalid item: ' + `item`) if path.closed: write("bC()\n") def SimpleText(self, text, trafo, halign, valign, chargap, wordgap, linegap): text = self.unicode_encoder(text) write = self.file.write write('txt(%s,' % `text`) if trafo.matrix() != IdentityMatrix: write('(%g,%g,%g,%g,%g,%g)' % trafo.coeff()) else: write('(%g,%g)' % (trafo.v1, trafo.v2)) write(',%d,%d' % (halign, valign)) write(',%g,%g,%g' % (chargap, wordgap, linegap)) write(')\n') def unicode_encoder(self, text): output='' for char in text: output+='\u0%x'%ord(char) return output def write_image(self, image, relative_filename = 1): write = self.file.write if not self.saved_ids.has_key(id(image)): imagefile = image.Filename() if not imagefile: from streamfilter import Base64Encode write('bm(%d)\n' % id(image)) file = Base64Encode(self.file) if image.image_mode == CMYK_IMAGE: image.orig_image.save(file, 'JPEG', quality=100) else: image.orig_image.save(file, 'PNG') file.close() write('-\n') else: if self.directory and relative_filename: imagefile = relpath(self.directory, imagefile) write('bm(%d,%s)\n' % (id(image), `imagefile`)) self.saved_ids[id(image)] = image def Image(self, image, trafo, relative_filename = 1): self.write_image(image, relative_filename) write = self.file.write write('im(') if trafo.matrix() != IdentityMatrix: write('(%g,%g,%g,%g,%g,%g)' % trafo.coeff()) else: write('(%g,%g)' % (trafo.v1, trafo.v2)) write(',%d)\n' % id(image)) def EpsFile(self, data, trafo, relative_filename = 1): write = self.file.write write('eps(') if trafo.matrix() != IdentityMatrix: write('(%g,%g,%g,%g,%g,%g)' % trafo.coeff()) else: write('(%g,%g)' % (trafo.v1, trafo.v2)) filename = data.Filename() if self.directory and relative_filename: filename = relpath(self.directory, filename) write(',%s)\n' % `filename`) def BeginBlendGroup(self): self.file.write('B()\n') def EndBlendGroup(self): self.file.write('B_()\n') def BeginBlendInterpolation(self, steps): self.file.write('blendinter(%d)\n' % steps) def EndBlendInterpolation(self): self.file.write('endblendinter()\n') def BlendInterpolation(self, steps): self.file.write('Bi(%d)\n' % steps) def BeginMaskGroup(self): self.file.write('M()\n') def EndMaskGroup(self): self.file.write('M_()\n') def BeginPathText(self): self.file.write('PT()\n') def InternalPathText(self, text, trafo, model, start_pos = 0): matrix = trafo.matrix() if matrix != IdentityMatrix: self.file.write('pt(%s,(%g,%g,%g,%g),%d' % ((`text`,) + matrix + (model,))) else: self.file.write('pt(%s,%d' % (`text`, model)) if start_pos > 0: self.file.write(',%g)\n' % start_pos) else: self.file.write(')\n') def EndPathText(self): self.file.write('PT_()\n') def GuideLine(self, point, horizontal): if horizontal: args = point.y, 1 else: args = point.x, 0 self.file.write('guide(%g,%d)\n' % args) def BeginPluginCompound(self, plugin_name, *args, **kw): write = self.file.write write('PC(%s' % `plugin_name`) for arg in args: write(',%s' % `arg`) for key, value in kw.items(): write(',%s=%s' % (key, `value`)) write(')\n') def EndPluginCompound(self): self.file.write('PC_()\n') def save(document, file, filename, options = {}): saver = SKSaver(file, filename, options) document.SaveToFile(saver) UniConvertor-1.1.4/src/app/plugins/Filters/cmxloader.py0000644000076400007640000015501710740000121021626 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1999, 2000 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ###Sketch Config #type = Import #class_name = 'CMXLoader' #rx_magic = '(?s)RIFF....CMX1' #tk_file_type = ('Corel Presentation Exchange 6/7', '.cmx') #format_name = 'CMX1' #unload = 1 #standard_messages = 1 ###End # The regular expression must allow . to match all characters including # newline. # # Import Filter for CMX files # # Status: # # The filter has support for 16 bit (generated by Corel Draw 5) and 32 # bit (generated by Corel Draw 6 and later) CMX files. Only little # endian byte order is implemented. # # Supported for both variants: # # - Multipath Polybezier curves # - Uniform fills, gradient fills (linear for both, linear, radial and # conical for 32bit) # - Dashes # - Layers and groups # - Color models: RGB, Gray, CMYK, CMYK255 # # Supported only for 32bit files: # # - Images, incomplete. # - Containers a.k.a Powerclip in Corel Draw # - Arrowheads, incomplete # - Additional fills: color bitmap, monochrome bitmap (both incomplete) # # Unsupported as yet: # # - Color models: Pantone, HSB, and others # - Lenses. (there is some experimental support for magnifying lenses # but that's deactivated) # - Fills: texture, postscript pattern # - Text # # # The code here is really ugly. This is mostly due to Corel's CMX # documentation, which is very incomplete and in some cases incorrect, # so I have to reverse engineer some aspects of CMX files. Therefore the # code in here is still quite experimental and propably buggy. # import sys, types from math import pi from struct import unpack, calcsize import struct from streamfilter import BinaryInput from app import CreatePath, ContSmooth, ContAngle, ContSymmetrical, \ SolidPattern, EmptyPattern, LinearGradient, RadialGradient, \ ConicalGradient, MultiGradient,\ CreateRGBColor, CreateCMYKColor, Trafo, Point, Polar, Translation, \ Scale, StandardColors, ImageTilePattern, ImageData, MaskGroup, \ Arrow from app.events.warn import INTERNAL, warn_tb from app.io.load import GenericLoader, SketchLoadError, EmptyCompositeError from app.Lib import units # # Generic RIFF stuff. # # Might be a good idea to put it into a separate module # RIFF = 'RIFF' LIST = 'LIST' compound_chunks = (RIFF, LIST) class RiffEOF(Exception): pass class ChunkHeader: def __init__(self, filepos, chunk_type, length, sub_type = ''): self.filepos = filepos self.chunk_type = chunk_type self.length = length self.sub_type = sub_type if sub_type: self.headersize = 12 self.data_length = self.length - 4 else: self.headersize = 8 self.data_length = self.length self.data_start = self.filepos + self.headersize self.has_subchunks = self.sub_type != '' self.subchunks = None def SetSubchunks(self, chunks): self.subchunks = chunks def read_chunk_header(file): # read the riff chunk header at the current position in file and # return a ChunkHeader instance containing the header data filepos = file.tell() data = file.read(8) if len(data) < 8: raise RiffEOF chunk_type, length = unpack('<4si', data) if length % 2 != 0: length = length + 1 if chunk_type in compound_chunks: sub_type = file.read(4) if len(sub_type) < 4: raise RiffEOF else: sub_type = '' return ChunkHeader(filepos, chunk_type, length, sub_type) # # CMX specific stuff # struct_cmxheader_start = ('32s' # Id '16s' # OS '4s' # ByteOrder, 2 little, 4 big endian '2s' # coord size, 2 = 16bit, 4 = 32bit '4s' # major version '4s' # minor version ) struct_cmxheader_end = ('<' 'H' # Unit, 35 = mm, 64 = inch 'd' # factor 'xxxx' # option, unused 'xxxx' # foreign key, unused 'xxxx' # capability, unused 'l' # index section, offset 'l' # info section, offset 'l' # thumbnail, offset (the docs differ here) 'l' # bb_left 'l' # bb_top 'l' # bb_right 'l' # bb_bottom 'l' # tally '64x' # reserved ) color_models = ('Invalid', 'Pantone', 'CMYK', 'CMYK255', 'CMY', 'RGB', 'HSB', 'HLS', 'BW', 'Gray', 'YIQ255', 'LAB') color_bytes = (0, 4, 4, 4, 4, 3, 4, 4, 1, 4, 1, 4) color_palettes = ('Invalid', 'Truematch', 'PantoneProcess', 'PantoneSpot', 'Image', 'User', 'CustomFixed') cmx_commands = { 88: 'AddClippingRegion', 94: 'AddGlobalTransform', 22: 'BeginEmbedded', 13: 'BeginGroup', 11: 'BeginLayer', 9: 'BeginPage', 99: 'BeginParagraph', 17: 'BeginProcedure', 72: 'BeginTextGroup', 70: 'BeginTextObject', 20: 'BeginTextStream', 101:'CharInfo', 102:'Characters', 90: 'ClearClipping', 2: 'Comment', 69: 'DrawImage', 65: 'DrawChars', 66: 'Ellipse', 23: 'EndEmbedded', 14: 'EndGroup', 12: 'EndLayer', 10: 'EndPage', 100:'EndParagraph', 18: 'EndSection', 73: 'EndTextGroup', 71: 'EndTextObject', 21: 'EndTextStream', 111:'JumpAbsolute', 67: 'PolyCurve', 92: 'PopMappingMode', 104:'PopTint', 91: 'PushMappingMode', 103:'PushTint', 68: 'Rectangle', 89: 'RemoveLastClippingRegion', 95: 'RestoreLastGlobalTransfo', 85: 'SetCharStyle', 93: 'SetGlobalTransfo', 86: 'SimpleWideText', 98: 'TextFrame' } class Outline: def __init__(self, style, screen, color, arrowheads, pen, dashes): self.style = style self.screen = screen self.color = color self.arrowheads = arrowheads self.pen = pen self.dashes = dashes class CMXFile: def __init__(self, loader, file): self.file = file self.loader = loader self.tagged = 0 self.colors = [] self.screens = [] self.dashes = [] self.pens = [] self.line_styles = [] self.procindex = [None,] self.bitmapindex = [None,] self.embeddedindex = [None,] self.arrowindex = [None,] self.verbosity = 0 self.angle_factor = 1 self.pages = [] def warn(self, message): self.loader.add_message(message) def _print(self, format, *args, **kw): if self.verbosity: if kw: text = format % kw elif args: text = format % args else: text = format sys.stderr.write(text) def read_header(self): self.file.seek(0) self.riff_header = h = read_chunk_header(self.file) self._print('%6d %s %s %d\n', h.filepos, h.chunk_type, h.sub_type, h.data_length) def read_subchunks(self, header, indent = 1): bytesread = 0 chunks = [] self.file.seek(header.data_start) while bytesread < header.data_length: subheader = read_chunk_header(self.file) bytesread = bytesread + subheader.headersize self._print('%6d %s%s %s %d\n', subheader.filepos, indent * ' ', subheader.chunk_type, subheader.sub_type, subheader.data_length) if subheader.sub_type != '': subchunks = self.read_subchunks(subheader, indent + 1) subheader.SetSubchunks(subchunks) self.file.seek(subheader.data_start + subheader.data_length) bytesread = bytesread + subheader.data_length chunks.append(subheader) return chunks def read_16(self): read = self.file.read lo, hi = read(2) value = ord(lo) + 256 * ord(hi) if value > 0x7FFF: value = value - 0x010000 return value def read_32(self): data = self.file.read(4) return int(unpack('> 4 cap = capjoin & 0x0f styles.append((spec, cap, join)) self._print('%3d ', len(styles)) self.print_linestyle((spec, cap, join)) def read_linestyles(self, chunk): self._print('Line Styles\n-----------\n') styles = [None,] self.file.seek(chunk.data_start) count = self.read_16() self._print('%d %s\n', count, 'line styles') if self.tagged: for i in range(count): tag = -1 while tag != 255: tag, data = self.read_tag((1,)) if tag == 1: self.append_linestyle(styles, data) else: read = self.file.read for i in range(count): self.append_linestyle(styles, self.file.read(2)) self._print('\n') return styles def append_arrowhead(self, heads, data): head1, head2 = unpack(' 64: self.file.seek(start_pos + palette_offset) palette_type, length = unpack('\n', tag, size) self.push_source(self.source.subfile(size - 3)) else: self._print('\n', tag) return tag def skip_tags(self): # skip tags until end tag tag = -1 while tag != 255: tag = ord(self.source.read(1)) if tag != 255: size = self.get_int16() self.source.seek(self.source.tell() + size - 3) # def Run(self, data, data_start): if self.cmxfile.byte_order == 2: # little endian byte_order = 0 else: byte_order = 1 self.push_source(BinaryInput(data, byte_order, self.cmxfile.coord_size)) get_int16 = self.get_int16 get_int32 = self.get_int32 try: while self.source.tell() < len(data): p = self.pos length = get_int16() if length < 0: length = get_int32() - 4 code = abs(get_int16()) # for some reason the codes are # negative in CMX1 command = cmx_commands.get(code) self._print('%-20s(%3d) %d\n', command, code, length) if command is not None and hasattr(self, command): try: try: self.push_source(self.source.subfile(length - 4)) jump = getattr(self, command)() finally: self.pop_source() if jump: self.source.seek(jump - data_start) except SketchLoadError: raise except: warn_tb(INTERNAL, "Exception in CMX command") else: self.source.seek(self.source.tell() + length - 4) finally: self.pop_source() def execute_procedure(self, reference): reflist, offset = self.cmxfile.procindex[reference] file = self.cmxfile.file file.seek(offset) chunk = read_chunk_header(file) if chunk.chunk_type in ('pvtr', 'pctn', 'proc'): data = file.read(chunk.data_length) self.loader.begin_group() try: self.Run(data, chunk.data_start) finally: self.loader.end_group() elif chunk.chunk_type == 'plns': # lens data = file.read(14) parent, page, parent_ref, start, end = unpack('> 6 if type == 0: if close and path is not None: path.load_close(1) close = node & 0x08 path = CreatePath() paths.append(path) path.AppendLine(p, (node & 0x30) >> 4) elif type == 1: path.AppendLine(p, (node & 0x30) >> 4) elif type == 3: path.AppendBezier(p, points[i + 1], points[i + 2], (nodes[i + 2] & 0x30) >> 4) i = i + 2 i = i + 1 if close: path.load_close(1) for i in range(len(paths) - 1, -1, -1): path = paths[i] if path.len == 0: #print 'empty path %d deleted' % i del paths[i] if paths: self.loader.bezier(tuple(paths)) else: self.get_prop_stack() def JumpAbsolute(self): return self.get_int32() class CMXInterpreter32(CMXInterpreter): def BeginPage(self): fmt = ' % 10s: %s\n' tag = -1 while tag != 255: tag = self.get_tag() if tag != 255: try: if tag == 1: read_struct = self.source.read_struct page_number, flags = read_struct('hi') bbox = read_struct('iiii') endpage, group_count, tally = read_struct('ihi') self._print(fmt, 'PageNumber', page_number) self._print(fmt, 'flags', flags) self._print(fmt, 'GroupCount:', group_count) self._print(fmt, 'Tally.', tally) elif tag == 2: matrix = self.get_matrix() self._print(fmt, 'Matrix', matrix) elif tag == 3: flag = self.get_boolean() if flag: old = self.get_rectangle() new = self.get_rectangle() if flag: self._print(fmt, 'mapping', '') self._print(fmt, 'old rect', old) self._print(fmt, 'new rect', new) else: self._print(' no mapping\n') finally: self.pop_source() def BeginLayer(self): fmt = ' % 11s: %s\n' tag = -1 while tag != 255: tag = self.get_tag() if tag != 255: try: if tag == 1: page_number, layer_number, flags, tally \ = self.source.read_struct('hhll') layer_name = self.get_string() self._print(fmt, 'LayerName', layer_name) self._print(fmt, 'LayerNumber', layer_number) self._print(fmt, 'PageNumber', page_number) self._print(fmt, 'flags', flags) elif tag == 2: matrix = self.get_matrix() self._print(fmt, 'Matrix', matrix) elif tag == 3: flag = self.get_boolean() if flag: old = self.get_rectangle() new = self.get_rectangle() if flag: self._print(fmt, 'mapping', '') self._print(fmt, 'old rect', old) self._print(fmt, 'new rect', new) else: self._print(' no mapping\n') finally: self.pop_source() # start layer self.loader.layer(self.layer_prefix + layer_name, 1, 1, 0, 0, ('RGB',0,0,0)) def BeginGroup(self): fmt = ' % 10s: %s\n' tag = -1 while tag != 255: tag = self.get_tag() if tag == 1: try: bbox = self.get_rectangle() group_count, end, tally = self.source.read_struct('hll') self._print(fmt, 'Bound.Box', bbox) self._print(fmt, 'GroupCount', group_count) self._print(fmt, 'Tally', tally) finally: self.pop_source() # start group self.loader.begin_group() def get_rendering_attrs(self): self._print(' Rendering Attributes:\n') style = self.loader.style mask = self.get_byte() if mask & 0x01: # fill attrs self._print(' Fill:') self.get_fill(style) else: style.fill_pattern = EmptyPattern if mask & 0x02: # line attrs self.get_outline(style) else: style.line_pattern = EmptyPattern if mask & 0x04: # lens attributes self.warn(_("Lens specification ignored")) self.skip_tags() #self.get_lens() if mask & 0x08: # canvas (XXX what is that, actually?) self.warn(_("Canvas specification ignored")) self.skip_tags() # ? if mask & 0x10: # container #self.warn("Container specification ignored") stack = self.loader.get_prop_stack() self.get_container() self.loader.set_prop_stack(stack) def get_fill(self, style): tag = -1 while tag != 255: tag = self.get_tag() try: if tag == 1: fill = self.get_int16() if fill == 0: # no fill self._print('no fill\n') style.fill_pattern = EmptyPattern elif fill == 1: # uniform self.get_uniform_fill(style) elif fill == 2: # fountain (gradient) self.get_fountain_fill(style) elif fill == 7: # monochrome bitmap 1 (according to cmxbrowser) self.get_monochrome_fill(style) elif fill == 9: # color bitmap self.get_colorbitmap_fill(style) elif fill == 11: # texture self.warn(_("Texture fill not implemented, " "using solid black")) style.fill_pattern = SolidPattern(StandardColors.black) else: self.warn(_("fill type %d not implemented, " "using solid black") % fill) style.fill_pattern = SolidPattern(StandardColors.black) finally: if tag != 255: self.pop_source() def get_uniform_fill(self, style): tag = -1 while tag != 255: tag = self.get_tag() try: if tag == 1: color, screen = self.source.read_struct('hh') self._print('uniform %s %s, screen %s\n', color, self.cmxfile.colors[color], screen) color = self.cmxfile.colors[color] style.fill_pattern = SolidPattern(color) finally: if tag != 255: self.pop_source() def get_fountain_fill(self, style): tag = -1 while tag != 255: tag = self.get_tag() try: if tag == 1: fountain, screen, padding = self.source.read_struct('hhh') angle = self.get_angle() xoff, yoff, steps, mode, rate_method, rate_value \ = self.source.read_struct('iihhhh') self._print('fountain: %s pad %d angle %f off %d %d\n', ('linear','radial','conical','square')[fountain], padding, angle, xoff, yoff) self._print(' steps %d mode %s\n', steps, ('RGB', 'HSB_CW', 'HSB_CCW', 'Custom')[mode]) self._print(' rate %d value %d\n', rate_method, rate_value) elif tag == 2: color_count = self.get_int16() colors = [] for i in range(color_count): color, pos = self.source.read_struct('hh') color = self.cmxfile.colors[color] colors.append((pos / 100.0, color)) self._print(' colors %s\n', colors) if mode == 0 and rate_value != 50 and len(colors) == 2: # len(colors) should always be 2 for mode != 3 start = colors[0][1] end = colors[1][1] colors.insert(1, (rate_value / 100.0, start.Blend(end, 0.5, 0.5))) gradient = MultiGradient(colors) border = padding / 50.0 center = Point(xoff / 100.0 + 0.5, yoff / 100.0 + 0.5) if fountain == 0: pattern = LinearGradient(gradient, -Polar(angle), border = border) elif fountain == 1: pattern = RadialGradient(gradient, center, border = border) elif fountain == 2: pattern = ConicalGradient(gradient, center, -Polar(angle)) else: # probably a square gradient which sketch doesn't have # use a radial gradient instead self.warn(_("Substituting radial gradient for square " "gradient")) pattern = RadialGradient(gradient, center, border = border) style.fill_pattern = pattern finally: if tag != 255: self.pop_source() def get_monochrome_fill(self, style): tag = -1 while tag != 255: tag = self.get_tag() try: if tag == 1: bitmap = self.get_int16() self._print('twocolor: bitmap %d\n', bitmap) image = self.cmxfile.GetBitmap(bitmap) if image.mode != '1': raise SketchLoadError(_("Image for twocolor fill is " "not 1 bit deep")) width, height, xoff, yoff, inter, flags = self.get_tiling() background, foreground, screen \ = self.source.read_struct('hhh') self._print(' foreground %s, background %s,' ' screen %d\n', self.cmxfile.colors[foreground], self.cmxfile.colors[background], screen) foreground = self.cmxfile.colors[foreground] background = self.cmxfile.colors[background] image = image.convert('L') pal = [0] * 768 pal[0] = background.red * 255 pal[1] = background.green * 255 pal[2] = background.blue * 255 pal[765] = foreground.red * 255 pal[766] = foreground.green * 255 pal[767] = foreground.blue * 255 image.putpalette(pal, 'RGB') image = image.convert('RGB') trafo = Trafo(width / image.size[0], 0, 0, height / image.size[1], 0, 0) style.fill_pattern = ImageTilePattern(ImageData(image), trafo = trafo) self.loader.fix_tile = xoff, yoff finally: if tag != 255: self.pop_source() def get_colorbitmap_fill(self, style): tag = -1 while tag != 255: tag = self.get_tag() try: if tag == 1: procedure = self.get_int16() self._print('Color image procedure %d\n', procedure) width, height, xoff, yoff, inter, flags = self.get_tiling() stack = self.loader.get_prop_stack() self.execute_procedure(procedure) self.loader.set_prop_stack(stack) group = self.loader.pop_last() image = group[0] image_data = image.Data() trafo = Trafo(width / image_data.size[0], 0, 0, height / image_data.size[1], 0, 0) trafo = trafo(image.Trafo()) pattern = ImageTilePattern(image_data, trafo = trafo) self.loader.style.fill_pattern = pattern elif tag != 255: self._print('Unknown tag %d\n', tag) self._print(`self.source.stream` + '\n') finally: if tag != 255: self.pop_source() def get_tiling(self): tiling = None tag = -1 while tag != 255: tag = self.get_tag() try: if tag == 1: width, height = self.source.read_struct('ii') width, height = self.trafo.DTransform(width, height) xoff, yoff, inter, flags = self.source.read_struct('hhhh') self._print(' size (%d, %d), off (%d, %d), inter %d, ' 'flags %x\n', width, height, xoff, yoff, inter, flags) tiling = width, height, xoff, yoff, inter, flags elif tag != 255: self._print('Unknown tag %d\n', tag) self._print(`self.source.stream` + '\n') finally: if tag != 255: self.pop_source() return tiling def get_outline(self, style): tag = -1 while tag != 255: tag = self.get_tag() try: if tag == 1: outline = self.get_int16() outline = self.cmxfile.outlines[outline] spec, cap, join = outline.style if spec & 1: # the none bit is set. no outline style.line_pattern = EmptyPattern else: style.line_pattern = SolidPattern(outline.color) style.line_width = abs(outline.pen[0] * self.factor) style.line_cap = cap + 1 style.line_join = join self._print('Arrow heads %d\n', outline.arrowheads) heads = self.cmxfile.arrow_heads[outline.arrowheads] style.line_arrow1 = self.get_arrow(heads[0]) style.line_arrow2 = self.get_arrow(heads[1]) if spec & 0x04: # dash dot style.line_dashes = outline.dashes else: style.line_dashes = () # XXX whats the precise meaning of the dash-dot flag and # the solid outline flag? finally: if tag != 255: self.pop_source() def get_arrow(self, index): if index == 0: return None offset = self.cmxfile.arrowindex[index] if type(offset) == types.InstanceType: return offset file = self.cmxfile.file file.seek(offset) data = file.read(3) tag, length = unpack('> 6 if type == 0: if close and path is not None: path.load_close(1) close = node & 0x08 path = CreatePath() paths.append(path) path.AppendLine(p, (node & 0x30) >> 4) elif type == 1: path.AppendLine(p, (node & 0x30) >> 4) elif type == 3: path.AppendBezier(p, points[i + 1], points[i + 2], (nodes[i + 2] & 0x30) >> 4) i = i + 2 i = i + 1 if close: path.load_close(1) for i in range(len(paths) - 1, -1, -1): path = paths[i] if path.len == 0: #print 'empty path %d deleted' % i del paths[i] return tuple(paths) def PolyCurve(self): # We do the tag handling 'manually' here because the file format # is broken for very long point lists. The CMX format has # provisisions for very long point lists at the command level # where there's a way to use 32 bit sizes instead of the normal # 16 bit but this doesn't work at the tag level. In the case of # the PolyCurve command the only problematic tag is probably the # tag with id 2 containing the point list so we have to ignore # the tag size for that. paths = () while 1: tag = ord(self.source.read(1)) if tag == 255: self._print('\n', tag) break else: size = self.get_uint16() self._print('\n', tag, size) if tag != 2: # don't push for the points as the size may not be # accurate. For very long point lists the size of # the tag is > 2**16 and wraps around self.push_source(self.source.subfile(size - 3)) pop = 1 else: pop = 0 try: if tag == 1: self.get_rendering_attrs() elif tag == 2: paths = self.read_pointlist() elif tag == 3: # ignore bounding box pass elif tag != 255: self._print('PolyCurve: Unknown tag %d\n', tag) #self._print(`self.source.stream` + '\n') finally: if pop: self.pop_source() if paths: self.loader.bezier(tuple(paths)) else: self.get_prop_stack() def DrawImage(self): tag = -1 while tag != 255: tag = self.get_tag() try: if tag == 1: self.get_rendering_attrs() elif tag == 2: left, top, right, bottom = self.get_rectangle() crop = self.get_rectangle() matrix = self.get_matrix() image_type, image1, image2 = self.source.read_struct('hhh') real_width = self.factor * abs(right - left) real_height = self.factor * abs(top - bottom) self._print('extent %gx%g, crop %s\n', real_width, real_height, crop) self._print('matrix %s\n', matrix) self._print('type %d, references %d, %d\n', image_type, image1, image2) image = self.cmxfile.GetImage(image1) if image is not None: width, height = image.size trafo = Trafo(real_width / float(width), 0, 0, real_height / float(height), 0, 0) trafo = apply(Trafo, matrix[:4])(trafo) trafo = Translation(self.trafo(matrix[-2:]))(trafo) self.loader.image(image, trafo) elif tag != 255: self._print('Unknown tag %d\n', tag) self._print(`self.source.stream` + '\n') finally: if tag != 255: self.pop_source() def JumpAbsolute(self): tag = -1 while tag != 255: tag = self.get_tag() try: if tag == 1: return self.get_int32() finally: if tag != 255: self.pop_source() class CMXLoader(GenericLoader): def __init__(self, file, filename, match): GenericLoader.__init__(self, file, filename, match) self.fix_tile = None self.fix_clip = 0 self.fix_lens = () def Load(self): try: cmx = CMXFile(self, self.file) cmx.Load() self.document() prefix = '' num_pages = cmx.NumPages() for num in range(num_pages): data, start = cmx.PageData(num) if data: if num_pages > 1: prefix = 'Page %d: ' % num if cmx.coord_size == 2: interpreter = CMXInterpreter16(self, cmx, layer_prefix = prefix) else: interpreter = CMXInterpreter32(self, cmx, layer_prefix = prefix) interpreter.Run(data, start) self.end_all() self.object.load_Completed() return self.object except RiffEOF: raise SketchLoadError(_("Unexpected end of file")) except: import traceback traceback.print_exc() raise def append_object(self, object): if self.fix_tile: if object.has_fill: pattern = object.Properties().fill_pattern if pattern.is_Tiled: width, height = pattern.data.size trafo = pattern.trafo y = height * trafo.m22 * self.fix_tile[1] / 100.0 rect = object.coord_rect pattern.Transform(Translation(0, rect.bottom-rect.top + y)) self.fix_tile = None if self.fix_clip: group = self.pop_last() if group is not None and group.is_Group: objects = group.GetObjects() objects.insert(0, object) object = MaskGroup(objects) self.fix_clip = 0 if self.fix_lens: group = self.pop_last() if group is not None and group.is_Group: lens = self.fix_lens[0] if lens == 2: rate, viewpoint = self.fix_lens[1:] center = object.coord_rect.center() trafo = Translation(-viewpoint) trafo = Translation(center)(Scale(rate)(trafo)) group.Transform(trafo) objects = group.GetObjects() objects.insert(0, object) object = MaskGroup(objects) self.fix_lens = () GenericLoader.append_object(self, object) UniConvertor-1.1.4/src/app/plugins/Filters/skloader.py0000755000076400007640000003553411005161473021475 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1997, 1998, 1999, 2000 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ###Sketch Config #type = Import #class_name = 'SKLoader' #rx_magic = '^##Sketch 1 *(?P[0-9]+)' #tk_file_type = ('Sketch Document', '.sk') format_name = 'SK' #standard_messages = 1 ###End (''"Sketch Document") from types import StringType, TupleType import os, sys from string import atoi from app.events.warn import warn, INTERNAL, pdebug, warn_tb from app import SketchLoadError, SketchError from app.plugins import plugins from app.io import load from app.conf import const from app import CreateRGBColor, SolidPattern, HatchingPattern,EmptyPattern,\ LinearGradient, ConicalGradient, RadialGradient, ImageTilePattern, \ Style, MultiGradient, Trafo, Translation, Point, \ GridLayer, GuideLayer, GuideLine, Arrow, CreatePath, StandardColors, \ GetFont from app.io.load import GenericLoader from app.Graphics import pagelayout, plugobj, blendgroup, text, image, eps, properties base_style = Style() base_style.fill_pattern = EmptyPattern base_style.fill_transform = 1 base_style.line_pattern = SolidPattern(StandardColors.black) base_style.line_width = 0.0 base_style.line_join = const.JoinMiter base_style.line_cap = const.CapButt base_style.line_dashes = () base_style.line_arrow1 = None base_style.line_arrow2 = None base_style.font = None base_style.font_size = 12.0 # sanity check: does base_style have all properties? for key in dir(properties.factory_defaults): if not hasattr(base_style, key): #warn(INTERNAL, 'added default for property %s', key) setattr(base_style, key, getattr(properties.factory_defaults, key)) papersizes = [# 'A0', 'A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7', 'letter', 'legal', 'executive', ] class SKLoader(GenericLoader): format_name = format_name base_style = base_style functions = ['document', 'layer', ('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 Sketch, 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 c = apply(CreateRGBColor, 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): 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()) self.append_object(object) 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 = plugins.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 = '#' if __debug__: import time start_time = time.clock() try: line = readline() 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() 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: # An exception was not converted to a SketchLoadError. # This should be considered a bug. warn_tb(INTERNAL, 'error in line %d:\n%s', num, `line`) if load._dont_handle_exceptions: raise else: raise SketchLoadError(_("error %s:%s in line %d:\n%s") % (sys.exc_info()[:2] +(num, `line`))) self.end_all() if self.page_layout: self.object.load_SetLayout(self.page_layout) for style in self.style_dict.values(): self.object.load_AddStyle(style) self.object.load_Completed() self.object.meta.native_format = 1 if __debug__: pdebug('timing', 'time:', time.clock() - start_time) return self.object def call_function(function, args, kwargs): if hasattr(function, 'im_func'): args = (function.im_self,) + args function = function.im_func code = function.func_code if code.co_flags & 0x000C: # uses *args or **kwargs return 0 args = args[:code.co_argcount] argnames = code.co_varnames[:code.co_argcount] for key in kwargs.keys(): if key not in argnames: del kwargs[key] try: apply(function, args, kwargs) except: warn_tb(INTERNAL, 'Trying to call function %s with reduced arglist', function.func_name) return 0 return 1 UniConvertor-1.1.4/src/app/plugins/Filters/cdrziploader.py0000664000076400007640000000254511042221356022344 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2008 by Igor E. Novikov # # This library is covered by GNU General Public License v2.0. # For more info see COPYRIGHTS file in sK1 root directory. ###Sketch Config #type = Import #class_name = 'CDRZIPLoader' #rx_magic = 'PK' #tk_file_type = ('CorelDRAW X4 files', '.cdr') #format_name = 'zipped-Meta' #standard_messages = 1 ###End (''"zipped CDR files") import os from app import SketchLoadError from app.io import load from app.utils.os_utils import sh_quote from zipfile import ZipFile from tempfile import NamedTemporaryFile class CDRZIPLoader: def __init__(self, file, filename, match): self.file = file self.filename = filename self.match = match self.messages = '' self.doc_class = None def set_doc_class(self, doc_class): self.doc_class = doc_class def Load(self): doc = None basename, ext = os.path.splitext(self.filename) if ext == '.cdr': file = ZipFile(self.filename) target=None for name in file.namelist(): if name[-3:]=='cdr': target=name break if target: cdrfile=NamedTemporaryFile() content = file.read(target) cdrfile.write(content) cdrfile.file.seek(0) doc = load.load_drawing_from_file(cdrfile, cdrfile.name, doc_class = self.doc_class) cdrfile.close() file.close() return doc def Messages(self): return self.messages UniConvertor-1.1.4/src/app/plugins/Filters/gziploader.py0000644000076400007640000000364210740000121022004 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1998, 1999, 2000, 2002 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ###Sketch Config #type = Import #class_name = 'GZIPLoader' #rx_magic = '\037\213' #tk_file_type = ('Gzipped Files', '.gz') #format_name = 'Gzipped-Meta' #standard_messages = 1 ###End (''"Gzipped Files") import os from app import SketchLoadError from app.io import load from app.utils.os_utils import sh_quote class GZIPLoader: def __init__(self, file, filename, match): self.file = file self.filename = filename self.match = match self.messages = '' self.doc_class = None def set_doc_class(self, doc_class): self.doc_class = doc_class def Load(self): if self.filename: basename, ext = os.path.splitext(self.filename) if ext != '.gz': basename = self.filename stream = os.popen('gzip -d -c ' + sh_quote(self.filename)) doc = load.load_drawing_from_file(stream, basename, doc_class = self.doc_class) if doc: doc.meta.compressed = "gzip" doc.meta.compressed_file = self.filename self.messages = doc.meta.load_messages return doc raise SketchLoadError('gziploader must be instantiated with filename') def Messages(self): return self.messages UniConvertor-1.1.4/src/app/plugins/Filters/garbage/0000755000076400007640000000000011211455344020674 5ustar igorigorUniConvertor-1.1.4/src/app/plugins/Filters/garbage/cgmloader.py0000775000076400007640000002457111060500573023216 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 by Bernhard Herzog # This CGMloader mostly by Antoon Pardon (2002) # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Libary General Public License as published # by the Free Software Foundation; either version 2 of the License, or (at # your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # $Id: cgmloader.py,v 1.1.1.2 2002/05/16 14:22:46 apardon Exp apardon $ ###Sketch Config #type = Import #class_name = 'CGMLoader' rx_magic = '\\000' #tk_file_type = ('Computer Graphics Metafile', '.cgm') #format_name = 'CGM' #unload = 1 #standard_messages = 1 ###End # # Import Filter for CGM files # # Status: # # First implementation. import sys, os, string from math import sin, cos, pi import struct import operator from app import _, Trafo, Scale, Translation, Point, Polar, CreatePath, \ CreateRGBColor, SolidPattern, EmptyPattern, LinearGradient, \ MultiGradient, Style, const, StandardColors, GridLayer from app.events.warn import INTERNAL, warn_tb from app.io.load import GenericLoader, SketchLoadError basestyle = Style() basestyle.fill_pattern = EmptyPattern basestyle.fill_transform = 1 #baseline.line_pattern = SolidPattern(StandardColors.black) basestyle.line_pattern = EmptyPattern basestyle.line_width = 1.0 basestyle.line_join = const.JoinMiter basestyle.line_cap = const.CapButt basestyle.line_dashes = () basestyle.line_arrow1 = None basestyle.line_arrow2 = None basestyle.font = None basestyle.font_size = 12.0 CGM_ID = { 0x0020: 'BEGMF', 0x0060: 'BEGPIC', 0x0080: 'BEGPICBODY', 0x00A0: 'ENDPIC', 0x0040: 'ENDMF', 0x1020: 'mfversion', 0x1040: 'mfdesc', 0x1060: 'vdctype', 0x1080: 'integerprec', 0x10e0: 'colrprec', 0x1100: 'colrindexprec', 0x1140: 'colrvalueext', 0x1160: 'mfelemlist', 0x2040: 'colrmode', 0x2080: 'markersizemode', 0x20a0: 'edgewidthmode', 0x2060: 'linewidthmode', 0x20c0: 'vdcext', 0x20e0: 'backcolr', 0x3020: 'vdcintegerprec', 0x5040: 'linetype', 0x5100: 'markercolr', 0x51c0: 'textcolr', 0x52e0: 'fillcolr', 0x52c0: 'intstyle', 0x5360: 'edgetype', 0x5380: 'edgewidth', 0x53c0: 'edgevis', 0x53a0: 'edgecolr', 0x5060: 'linewidth', 0x5080: 'linecolr', 0x40e0: 'POLYGON', 0x4020: 'LINE', } def noop(self): pass class cgminfo: def __init__(self): pass class CGMLoader(GenericLoader): def __init__(self, file, filename, match): GenericLoader.__init__(self, file, filename, match) self.file = file self.cgm = cgminfo() self.verbosity = 1 def _print(self, format, *args, **kw): if self.verbosity: try: if kw: text = format % kw elif args: text = format % args else: text = format except: text = string.join([format] + map(str, args)) if text[-1] != '\n': text = text + '\n' sys.stdout.write(text) def unpack(self, format): # self._print(format) size = struct.calcsize(format) return struct.unpack(format, self.file.read(size)) def get_u8(self): return self.unpack("!B")[0] def get_u16(self): return self.unpack("!H")[0] def get_int32(self): return self.get_struct('!i')[0] def getstr(self): lng = self.get_u8() return self.unpack("!" + `lng` + "s")[0] def getcol(self): cgmcol = self.unpack(self.cgm.color.struct) cgmcol = map(operator.sub , cgmcol , self.cgm.color.offset) cgmcol = map(operator.div , cgmcol , self.cgm.color.scale) #self._print("color %3d %3d %3d" % cgmcol) return apply(CreateRGBColor , cgmcol) def BEGMF(self, size): # self._print (self.getstr()) self.cgm.color = cgminfo() self.cgm.color.struct = "!BBB" self.cgm.color.offset = (0.0, 0.0, 0.0) self.cgm.color.scale = (255.0, 255.0, 255.0) self.cgm.line = cgminfo() self.cgm.fill = cgminfo() self.cgm.edge = cgminfo() self.cgm.text = cgminfo() self.cgm.marker = cgminfo() self.document() def mfversion(self, size): if self.get_u16() != 1: raise SketchLoadError("Can only load CGM version 1") def mfdesc(self,size): pass # self._print(self.getstr()) def vdctype(self,size): if size != 2: raise SketchLoadError("Size for vdctype is %d" % (size,)) if self.get_u16() != 0: raise SketchLoadError("This implementation only works with integer VDC's") def integerprec(self, size): if size != 2: raise SketchLoadError("Size for integer precision is %d" % (size,)) bits = self.get_u16() if bits != 16: raise SketchLoadError("This implementation only allows 16 bit integers not %d" % (bits,)) def vdcintegerprec(self, size): if size != 2: raise SketchLoadError("Size for integer precision is %d" % (size,)) bits = self.get_u16() if bits != 16: raise SketchLoadError("This implementation only allows 16 bit VDC integers not %d" % (bits,)) def colrvalueext(self, size): bottom = self.unpack(self.cgm.color.struct) top = self.unpack(self.cgm.color.struct) # self._print('bottom =' , bottom) # self._print('top =' , top) self.cgm.color.offset = map(operator.mul , bottom , (1.0, 1.0, 1.0)) self.cgm.color.scale = map(operator.sub, top , self.cgm.color.offset) # self._print('offset =' , self.cgm.color.offset) # self._print('scale =' , self.cgm.color.scale) def colrprec(self, size): if size != 2: raise SketchLoadError("Size for colour index precision is %d" % (size,)) bits = self.get_u16() if bits == 8: self.cgm.color.struct = "!BBB" elif bits == 16: self.cgm.color.struct = "!HHH" elif bits == 32: self.cgm.color.struct = "!III" else: raise SketchLoadError("This implementation can't work with %d bit colour components" % (bits,)) def colrindexprec(self, size): if size != 2: raise SketchLoadError("Size for colour index precision is %d" % (size,)) bits = self.get_u16() if bits != 16: raise SketchLoadError("This implementation only allows 16 colour indices not %d" % (bits,)) def mfelemlist(self, size): pass def BEGPIC(self, size): ln = self.getstr() self.layer(name = ln) # self._print("layer:" , ln) def colrmode(self, size): self.cgm.color.mode = self.get_u16() if self.cgm.color.mode != 1: raise SketchLoadError("Only direct color mode is implemented") def linewidthmode(self, size): self.cgm.line.widthmode = self.get_u16() if self.cgm.line.widthmode != 0: raise SketchLoadError("Only absolute width mode is implemented") def edgewidthmode(self, size): self.cgm.edge.widthmode = self.get_u16() if self.cgm.edge.widthmode != 0: raise SketchLoadError("Only absolute width mode is implemented") def markersizemode(self, size): self.cgm.marker.sizemode = self.get_u16() if self.cgm.marker.sizemode != 0: raise SketchLoadError("Only absolute size mode is implemented") def vdcext(self, size): left, bottom, right, top = self.unpack("!hhhh") width = right - left height = top - bottom sc = 841 / (1.0 * max(abs(width) , abs(height))) self.Scale = sc # self._print("Scale =" , sc) self.trafo = Scale(sc)(Translation(-left , -bottom)) # self._print("(%d %d) => %s" % (left , bottom , self.trafo(left,bottom))) def backcolr(self, size): self.getcol() def BEGPICBODY(self, size): self.cgm.fill.type = 1 self.cgm.edge.visible = 1 self.cgm.fill.color = StandardColors.black self.cgm.edge.color = StandardColors.black self.cgm.line.color = StandardColors.black def ENDPIC(self, size): pass def ENDMF(self, size): pass def edgevis(self, size): self.cgm.edge.visible = self.get_u16() def edgewidth(self, size): self.cgm.edge.width = self.get_u16() * self.Scale def linewidth(self, size): self.cgm.line.width = self.get_u16() * self.Scale def linetype(self, size): self.cgm.line.type = self.get_u16() def edgetype(self, size): self.cgm.edge.type = self.get_u16() def edgecolr(self, size): self.cgm.edge.color = self.getcol() def linecolr(self, size): self.cgm.line.color = self.getcol() def textcolr(self, size): self.cgm.text.color = self.getcol() def markercolr(self, size): self.cgm.marker.color = self.getcol() def intstyle(self, size): self.cgm.fill.type = self.get_u16() def fillcolr(self, size): self.cgm.fill.color = self.getcol() def Path(self, size): path = CreatePath() for i in range(size / 4): path.AppendLine(self.trafo(self.unpack("!hh"))) return path def POLYGON(self, size): path = self.Path(size) if path.Node(-1) != path.Node(0): path.AppendLine(path.Node(0)) path.load_close() style = basestyle.Duplicate() if self.cgm.fill.type == 1: style.fill_pattern = SolidPattern(self.cgm.fill.color) if self.cgm.edge.visible: style.line_pattern = SolidPattern(self.cgm.edge.color) style.line_pattern.line_width = self.edgewidth self.prop_stack.AddStyle(style) self.bezier((path,)) def LINE(self, size): path = self.Path(size) style = basestyle.Duplicate() style.line_pattern = SolidPattern(self.cgm.line.color) style.line_pattern.line_width = self.cgm.line.width self.prop_stack.AddStyle(style) self.bezier((path,)) def interpret(self): tell = self.file.tell Id = -1 while Id != 0x40: pos = tell() head = self.get_u16() Id = head & 0xffe0 size = head & 0x001f hdsz = 2 if size == 31: size = self.get_u16() hdsz = 4 pdsz = ((size + 1) / 2) * 2 #self._print('%5d(%5d): %4x: %s' % (size, pdsz, Id, CGM_ID.get(Id, ''))) if hasattr(self, CGM_ID.get(Id, '')): # self._print('Calling %s' % (CGM_ID.get(Id, ''))) getattr(self, CGM_ID[Id])(size) else: if Id: self.file.read(pdsz) name = CGM_ID.get(Id, '') Class = Id >> 12 Elem = (Id & 0x0fff) >> 5 self._print('*** unimplemented: %4x; class = %d, element = %2d %s' % (Id , Class , Elem, name)) pos = pos + hdsz + pdsz if tell() < pos: self.file.read(pos - tell()) elif tell() > pos: self._print('read too many bytes') self.file.seek(pos - tell(), 1) def Load(self): self.file.seek(0) self.interpret() # self.begin_layer_class(GridLayer, ((0,0,20,20), 0 , StandardColors.blue , "Grid")) # self.end_composite() self.end_all() self.object.load_Completed() return self.object UniConvertor-1.1.4/src/app/plugins/Filters/garbage/cgmsaver.py0000775000076400007640000001633311060500573023065 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 by Bernhard Herzog # This CGMsaver from Antoon Pardon (2002) # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Libary General Public License as published # by the Free Software Foundation; either version 2 of the License, or (at # your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # $Id: $ ###Sketch Config #type = Export #tk_file_type = ("Computer Graphics Metafile (CGM)", '.cgm') #extensions = '.cgm' format_name = 'CGM' #unload = 1 ###End import struct import os.path from app import Scale, Translation, Bezier, CreateRGBColor, EmptyPattern # Fault Tolerance for flattening Beziers. EPS = 2 def rndtoint(num): return int(round(num)) def cr(P1, P2): return P1.x * P2.y - P1.y * P2.x def FlattenPath(P0, P1, P2, P3): P4 = (P0 + P1) / 2 P5 = (P1 + P2) / 2 P6 = (P2 + P3) / 2 P7 = (P4 + P5) / 2 P8 = (P5 + P6) / 2 P9 = (P7 + P8) / 2 B = P3 - P0 S = P9 - P0 C1 = P1 - P0 C2 = P2 - P3 # I couldn't find an example of a flattening algorithm so I came up # with the following criteria for deciding to stop the approximation # or to continue. # if either control vector is larger than the base vector continue if abs(C1) > abs(B) or abs(C2) > abs(B): return FlattenPath(P0, P4, P7, P9) + FlattenPath(P9, P8, P6, P3) # otherwise if the base is smaller than half the fault tolerance stop. elif abs(B) < EPS / 2: return (P9, P3) else: # if neither of the above applies, check for the following conditions. # if one of them is true continue the approximation otherwise stop # # The first constrol vector goes too far before the base # The seconde control vector goes too far behind the base # Both control vectors lie on either side of the base. # The midpoint is too far from base. N = B.normalized() if (C1 * N) < -EPS or (C2 * N) > EPS or cr(C1,B)*cr(C2,B) < 0 or abs(cr(N,S)) > EPS: return FlattenPath(P0, P4, P7, P9) + FlattenPath(P9, P8, P6, P3) else: return (P9, P3) class CGMSaver: def __init__(self, file, pathname, options): self.file = file self.pathname = pathname self.options = options self.white = CreateRGBColor(1.0 , 1.0 , 1.0) def w(self, str): self.file.write(str) def pack(self , *args): self.file.write(apply(struct.pack , args)) def putstr(self, Id , str): lng = len(str) if lng < 30: self.pack("!H" , Id | (lng + 1)) else: self.pack("!H" , Id | 31) self.pack("!H" , lng + 1) self.pack("!B" , lng) fmt = '!' + `lng` + 's' self.pack(fmt , str) if lng % 2 == 0: self.pack("!B" , 0) def putintseq(self , Id , seq): lng = len(seq) if 2 * lng < 31: self.pack("!H" , Id | 2 * lng) else: self.pack("!H" , Id | 31) self.pack("!H" , 2 * lng) fmt = '!' + `lng` + "h" args = (fmt,) + tuple(seq) apply(self.pack , args) def putcol(self , Id , color): red = rndtoint(255 * color.red) green = rndtoint(255 * color.green) blue = rndtoint(255 * color.blue) self.pack("!HBBBB" , Id , red , green , blue , 0) def close(self): self.file.close() def PolyBezier(self, Paths, Properties): line_pattern = Properties.line_pattern fill_pattern = Properties.fill_pattern if line_pattern is EmptyPattern: # Edge Visibility Off self.pack("!HH" , 0x53c2 , 0x0000) else: # Edge Visibility On self.pack("!HH" , 0x53c2 , 0x0001) line_width = rndtoint(self.Scale * Properties.line_width) # Sketch doesn't distinghuish between Polylines and # Polygons. Instead of trying to figure out what # kind we are dealing with, we set the cgm linewidth # as well as the cgm edgewidth # Edge width self.pack("!HH" , 0x5382 , line_width) # Line width self.pack("!HH" , 0x5062 , line_width) if line_pattern.is_Solid: self.putcol(0x53a3 , line_pattern.Color()) self.putcol(0x5083 , line_pattern.Color()) if fill_pattern is EmptyPattern: # Fill type is Hollow self.pack("!HH" , 0x52c2 , 0x0004) else: # Fill type is Solid self.pack("!HH" , 0x52c2 , 0x0001) if fill_pattern.is_Solid: self.putcol(0x52e3 , fill_pattern.Color()) for path in Paths: if path.closed: Id = 0x40e0 # Polygon else: Id = 0x4020 # Polyline parlst = () for i in range(path.len): type, control, p, cont = path.Segment(i) if type == Bezier: p1 , p2 = control tmplst = FlattenPath(p0, p1, p2, p) for tp in tmplst: parlst = parlst + tuple(self.trafo(tp)) else: parlst = parlst + tuple(self.trafo(p)) p0 = p if Id == 0x40e0: parlst = parlst[:-2] self.putintseq(Id , map(rndtoint , parlst)) def SaveObjects(self, Objects): for object in Objects: if object.is_Compound: self.SaveObjects(object.GetObjects()) elif object.is_curve: self.PolyBezier(object.Paths(), object.Properties()) def SaveLayers(self, Layers): # We put each layer in a picture, that seems at the # moment to be the approach for layer in Layers: if not layer.is_SpecialLayer and layer.Printable(): # Begin Picture self.putstr(0x0060 , layer.name) # Color Selection Mode: Direct self.pack("!HH" , 0x2042 , 0x0001) # Edge Width Specification: Absolute self.pack("!HH" , 0x20a2 , 0x0000) # Line Width Specification: Absolute self.pack("!HH" , 0x2062 , 0x0000) # VDC Extend self.putintseq(0x20c0 , self.extend) # Background Colour self.putcol(0x20e3 , self.white) # Begin Picture Body self.pack("!H" , 0x0080) self.SaveObjects(layer.GetObjects()) # End Picture self.pack("!H" , 0x00a0) #if #for #end def SaveDocument(self, doc): # A dillema # Should the design fill the CGM-file or # Should it be placed approximately in # the same place as it is put on the page. if 0: left, bottom, right, top = doc.BoundingRect() width = right - left hight = top - bottom else: left, bottom = 0, 0 width = doc.page_layout.width hight = doc.page_layout.height right , top = width , hight sc = 65534 / max(width , hight) self.trafo = Translation(-32767,-32767)(Scale(sc)(Translation(-left , -bottom))) self.Scale = sc self.extend = map(rndtoint , tuple(self.trafo(left,bottom)) + tuple(self.trafo(right,top))) # Begin Metafile filename = os.path.basename(self.pathname) title = filename + " generated by sK1" self.putstr(0x0020 , title) # Metafile Version self.pack("!H" , 0x1022) self.pack("!H" , 0x0001) # Metafile Description self.putstr(0x1040 , filename + "created by sk1") # Metafile Element List self.pack("!HHHH" , 0x1166 , 0x0001 , 0xffff , 0x0001) #Font List # self.SaveLayers(doc.Layers()) # End Meta File self.pack("!H" , 0x0040) #end def save(document, file, filename, options = {}): saver = CGMSaver(file, filename, options) saver.SaveDocument(document) saver.close() UniConvertor-1.1.4/src/app/plugins/Filters/wmfloader.py0000644000076400007640000003373410740000122021632 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1999, 2002 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ###Sketch Config #type = Import #class_name = 'WMFLoader' rx_magic = '\xd7\xcd\xc6\x9a' #tk_file_type = ('Windows Metafile', '.wmf') #format_name = 'WMF' #unload = 1 #standard_messages = 1 ###End # # Import Filter for Windows WMF files # # Status: # # The filter only handles the 16 bit WMF files of Windows 3.1 # # Things it can't handle: # # - Text. Some text support would be possible, I guess, but there would # probably be problems with TrueType fonts, because Sketch doesn't # support them yet. # # - Winding number rule for Polygons. # # - Clip-Regions # import sys, os, string from math import sin, cos, pi from struct import unpack, calcsize import struct from app import _, Trafo, Scale, Translation, Point, Polar, CreatePath, \ CreateRGBColor, SolidPattern, EmptyPattern, LinearGradient, \ MultiGradient, Style, const, StandardColors from app.events.warn import INTERNAL, warn_tb from app.io.load import GenericLoader, SketchLoadError struct_wmf_header = ('<' 'H' # Type 'H' # header size 'H' # Version 'I' # FileSize 'H' # Num. objects 'I' # Max. record size 'H' # Num. Parameters ) struct_placeable_header = ('<' '4s' # Key 'H' # handle 'h' # left 'h' # top 'h' # right 'h' # bottom 'H' # Inch 'I' # Reserved 'H' # Checksum ) wmf_functions = { 0x0052: 'AbortDoc', 0x0817: 'Arc', 0x0830: 'Chord', 0x01f0: 'DeleteObject', 0x0418: 'Ellipse', 0x005E: 'EndDoc', 0x0050: 'EndPage', 0x0415: 'ExcludeClipRect', 0x0548: 'ExtFloodFill', 0x0228: 'FillRegion', 0x0419: 'FloodFill', 0x0429: 'FrameRegion', 0x0416: 'IntersectClipRect', 0x012A: 'InvertRegion', 0x0213: 'LineTo', 0x0214: 'MoveTo', 0x0220: 'OffsetClipRgn', 0x0211: 'OffsetViewportOrg', 0x020F: 'OffsetWindowOrg', 0x012B: 'PaintRegion', 0x061D: 'PatBlt', 0x081A: 'Pie', 0x0035: 'RealizePalette', 0x041B: 'Rectangle', 0x014C: 'ResetDc', 0x0139: 'ResizePalette', 0x0127: 'RestoreDC', 0x061C: 'RoundRect', 0x001E: 'SaveDC', 0x0412: 'ScaleViewportExt', 0x0410: 'ScaleWindowExt', 0x012C: 'SelectClipRegion', 0x012D: 'SelectObject', 0x0234: 'SelectPalette', 0x012E: 'SetTextAlign', 0x0201: 'SetBkColor', 0x0102: 'SetBkMode', 0x0d33: 'SetDibToDev', 0x0103: 'SetMapMode', 0x0231: 'SetMapperFlags', 0x0037: 'SetPalEntries', 0x041F: 'SetPixel', 0x0106: 'SetPolyFillMode', 0x0105: 'SetRelabs', 0x0104: 'SetROP2', 0x0107: 'SetStretchBltMode', 0x012E: 'SetTextAlign', 0x0108: 'SetTextCharExtra', 0x0209: 'SetTextColor', 0x020A: 'SetTextJustification', 0x020E: 'SetViewportExt', 0x020D: 'SetViewportOrg', 0x020C: 'SetWindowExt', 0x020B: 'SetWindowOrg', 0x014D: 'StartDoc', 0x004F: 'StartPage', # 0x0436: 'AnimatePalette', 0x0922: 'BitBlt', 0x06FE: 'CreateBitmap', 0x02FD: 'CreateBitmapIndirect', 0x00F8: 'CreateBrush', 0x02FC: 'CreateBrushIndirect', 0x02FB: 'CreateFontIndirect', 0x00F7: 'CreatePalette', 0x01F9: 'CreatePatternBrush', 0x02FA: 'CreatePenIndirect', 0x06FF: 'CreateRegion', 0x01F0: 'DeleteObject', 0x0940: 'DibBitblt', 0x0142: 'DibCreatePatternBrush', 0x0B41: 'DibStretchBlt', 0x062F: 'DrawText', 0x0626: 'Escape', 0x0A32: 'ExtTextOut', 0x0324: 'Polygon', 0x0538: 'PolyPolygon', 0x0325: 'Polyline', 0x0521: 'TextOut', 0x0B23: 'StretchBlt', 0x0F43: 'StretchDIBits' } def noop(self): pass class WMFLoader(GenericLoader): def __init__(self, file, filename, match): GenericLoader.__init__(self, file, filename, match) self.file = file self.curstyle = Style() self.verbosity = 0 self.gdiobjects = [] self.dcstack = [] self.curpoint = Point(0, 0) def _print(self, format, *args, **kw): if self.verbosity: try: if kw: text = format % kw elif args: text = format % args else: text = format except: text = string.join([format] + map(str, args)) if text[-1] != '\n': text = text + '\n' sys.stdout.write(text) def get_struct(self, format): size = calcsize(format) return unpack(format, self.file.read(size)) def get_int16(self): return self.get_struct('', idx, object) def DeleteObject(self): idx = self.get_int16() self.delete_gdiobject(idx) self._print('->', idx) def get_dc(self): return self.curstyle.Duplicate(), self.trafo, self.curpoint def set_dc(self, dc): self.curstyle, self.trafo, self.curpoint = dc def SaveDC(self): self.dcstack.append(self.get_dc()) def RestoreDC(self): self.set_dc(self.dcstack[-1]) del self.dcstack[-1] def SetMapMode(self): mode = self.get_int16() self._print('->', mode) def SetWindowOrg(self): self.wy, self.wx = self.get_struct('', self.wx, self.wy) def SetWindowExt(self): self.wheight, self.wwidth = self.get_struct('', self.wwidth, self.wheight) def SetPolyFillMode(self): mode = self.get_int16() self._print('->', mode) SetBkMode = noop SetBkColor = noop SetROP2 = noop def CreateBrushIndirect(self): style, r, g, b, hatch = self.get_struct('', style, r, g, b, hatch) def DibCreatePatternBrush(self): self.add_message(_("Bitmap brushes not yet implemented. Using black")) pattern = SolidPattern(StandardColors.black) self.add_gdiobject((('fill_pattern', pattern),)) def CreatePenIndirect(self): style, widthx, widthy, r, g, b = self.get_struct('> 8 join = (style & 0x00F0) >> 4 style = style & 0x000F if style == 5: pattern = EmptyPattern else: pattern = SolidPattern(CreateRGBColor(r/255.0, g/255.0, b/255.0)) width = abs(widthx * self.trafo.m11) self.add_gdiobject((('line_pattern', pattern,), ('line_width', width))) self._print('->', style, widthx, widthy, r, g, b, cap, join) def CreatePalette(self): self.add_gdiobject((('ignore', None),)) def CreateRegion(self): self.add_gdiobject((('ignore', None),)) CreateFontIndirect = CreatePalette SelectPalette = noop RealizePalette = noop SetTextColor = noop SetTextAlign = noop SetTextJustification = noop SetStretchBltMode = noop def read_points(self, num): coords = self.get_struct('<' + num * 'hh') points = []; append = points.append trafo = self.trafo for i in range(0, 2 * num, 2): append(trafo(coords[i], coords[i + 1])) return points def Polyline(self): points = self.read_points(self.get_int16()) if points: path = CreatePath() map(path.AppendLine, points) self.prop_stack.AddStyle(self.curstyle.Duplicate()) self.prop_stack.SetProperty(fill_pattern = EmptyPattern) self.bezier((path,)) #for i in range(len(points)): # self._print('->', points[i]) def Polygon(self): points = self.read_points(self.get_int16()) if points: path = CreatePath() map(path.AppendLine, points) if path.Node(-1) != path.Node(0): #print 'correct polygon' path.AppendLine(path.Node(0)) path.load_close() self.prop_stack.AddStyle(self.curstyle.Duplicate()) self.bezier((path,)) #for i in range(len(points)): # self._print('->', points[i]) def PolyPolygon(self): nr_of_polygons = self.get_int16() nr_of_points = [] for i in range(nr_of_polygons): nr_of_points.append(self.get_int16()) path = () for i in nr_of_points: points = self.read_points(i) if points: subpath = CreatePath() map(subpath.AppendLine, points) if subpath.Node(-1) != subpath.Node(0): subpath.AppendLine(subpath.Node(0)) subpath.load_close() path = path + (subpath,) if path: self.prop_stack.AddStyle(self.curstyle.Duplicate()) self.bezier(path) def MoveTo(self): y, x = self.get_struct('', self.curpoint) def LineTo(self): y, x = self.get_struct('', self.curpoint) def Ellipse(self): bottom, right, top, left = self.get_struct('', left, top, right, bottom, ellw, ellh) self.prop_stack.AddStyle(self.curstyle.Duplicate()) self.rectangle(right - left, 0, 0, bottom - top, left, top, radius1 = abs(ellw / (right - left)), radius2 = abs(ellh / (bottom - top))) def Escape(self): pass def interpret(self): tell = self.file.tell function = -1 while function: pos = tell() size, function = self.get_struct(' pos: self._print('read too many bytes') self.file.seek(pos - tell(), 1) def Load(self): self.document() self.layer(name = _("WMF objects")) self.read_headers() self.interpret() self.end_all() self.object.load_Completed() return self.object UniConvertor-1.1.4/src/app/plugins/Filters/pltloader.py0000664000076400007640000001137111210053325021641 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2009 by Barabash Maxim # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ###Sketch Config #type=Import #class_name='PLTLoader' #rx_magic='^IN|^PA|^PU|^PD|^PR|^PS' #tk_file_type=('PLT - HPGL Plotter file', ('.plt', '.hgl')) #format_name='PLT' #unload=1 #standard_messages=1 ###End # # Import Filter for HPGL Plotter files # import sys, os, string from types import StringType, TupleType from app import _, CreatePath, Style from app.events.warn import INTERNAL, pdebug, warn_tb from app.io.load import GenericLoader, SketchLoadError import app plu=1016.0/72.0 class PLTLoader(GenericLoader): functions={"PD": 'pen_down', "PU": 'pen_up', "PA": 'plot_absolute', "PR": 'plot_relative', "IN": 'initialize' #"SP": 'select_pen', #"LT": 'linetype', #"PT": 'pen_metric', #"PL": 'pen_plu', #"SP": 'set_pen' } def __init__(self, file, filename, match): GenericLoader.__init__(self, file, filename, match) self.file=file self.initialize() def get_position(self,x,y): if x=='' is not None: x=self.cur_x else: x=float(x)/plu if y=='' is not None: y=self.cur_y else: y=float(y)/plu if self.absolute==0: x=x+self.cur_x y=y+self.cur_y return x,y def bezier(self): if self.path.len > 1: GenericLoader.bezier(self, paths=(self.path,)) self.path=CreatePath() def move(self, x,y): if self.draw==1: self.path.AppendLine(x, y) else: self.bezier() self.path.AppendLine(x, y) self.cur_x=x self.cur_y=y def pen_down(self,x='',y=''): self.draw=1 if x !='' is not None: x,y=self.get_position(x,y) self.move(x,y) def pen_up(self,x='',y=''): if self.draw==1: self.bezier() self.draw=0 if x !='' is not None: x,y=self.get_position(x,y) self.move(x,y) def plot_absolute(self,x='',y=''): self.absolute=1 if x !='' is not None: x,y=self.get_position(x,y) self.move(x,y) def plot_relative(self,x='',y=''): self.absolute=0 if x !='' is not None: x,y=self.get_position(x,y) self.move(x,y) def initialize(self): self.curstyle=Style() self.cur_x=0.0 self.cur_y=0.0 self.draw=0 self.absolute=1 self.path=CreatePath() 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 interpret(self): import shlex def is_number(a): try: i=float(a) except ValueError: return 0 return 1 file = self.file if type(file) == StringType: file = open(file, 'r') file.seek(0) readline = file.readline fileinfo=os.stat(self.filename) totalsize=fileinfo[6] lexer=shlex.shlex(file) lexer.debug=0 lexer.wordchars=lexer.wordchars + ".-+" lexer.whitespace=lexer.whitespace + ';,' keyword=None args=[] parsed=0 parsed_interval=totalsize/99+1 while 1: interval_count=file.tell()/parsed_interval if interval_count > parsed: parsed+=10 # 10% progress app.updateInfo(inf2='%u'%parsed+'% of file is parsed...',inf3=parsed) token=lexer.get_token() if not token: # run last command self.run(keyword,args) # pun up self.run('PU',[]) # END INTERPRETATION app.updateInfo(inf2=_('Parsing is finished'),inf3=100) break if keyword and is_number(token): args.append(token) else: self.run(keyword,args) keyword=token[0:2] args=[] if token[2:]: lexer.push_token(token[2:]) def run(self,keyword,args): if keyword is None: return unknown_operator=(None, None) funclist=self.funclist if keyword is not None: method, argc=funclist.get(keyword, unknown_operator) if method is not None: try: if len(args): i=0 while i3\.[012])' #tk_file_type = ('XFig', '.fig') format_name = 'XFig' #unload = 1 #standard_messages = 1 ###End (''"XFig") # # This is a simple import filter for Fig files. # # It is incomplete and may refuse to load some valid Fig files. It # should work with versions 3.0, 3.1 and 3.2 of the Fig format. # # TODO (not in any particular order): # # - convert X-Splines (new in XFig 3.2) to bezier curves if that can be done # # - parse strings properly # # - use more of the header information: pagesize, orientation, ... # # - patterns, arrows, dashes # import sys from string import strip, atoi, split, atof, lower from operator import getitem from math import atan2 from app import SketchLoadError from app.conf import const from app.events.warn import warn, INTERNAL, warn_tb from app.io.load import SimplifiedLoader, EmptyCompositeError from app import skread from app.io import load tokenize = skread.tokenize_line from app import Scale, Trafo, Translation, Rotation, StandardColors, \ XRGBColor, SolidPattern, EmptyPattern, Blend, GetFont, SimpleText, \ CreatePath from app.Graphics import text, pagelayout std_colors = [StandardColors.black, StandardColors.blue, StandardColors.green, StandardColors.cyan, StandardColors.red, StandardColors.magenta, StandardColors.yellow, StandardColors.white] + \ map(XRGBColor, ("#000090", "#0000b0", "#0000d0", "#87ceff", "#009000", "#00b000", "#00d000", "#009090", "#00b0b0", "#00d0d0", "#900000", "#b00000", "#d00000", "#900090", "#b000b0", "#d000d0", "#803000", "#a04000", "#c06000", "#ff8080", "#ffa0a0", "#ffc0c0", "#ffe0e0", "#ffd700")) BLACK = 0 WHITE = 7 DEFAULT_COLOR = -1 xfig_join = (const.JoinMiter, const.JoinBevel, const.JoinRound) xfig_cap = (const.CapButt, const.CapRound, const.CapProjecting) psfonts = ("Times-Roman", "Times-Italic", "Times-Bold", "Times-BoldItalic", "AvantGarde-Book", "AvantGarde-BookOblique", "AvantGarde-Demi", "AvantGarde-DemiOblique", "Bookman-Light", "Bookman-LightItalic", "Bookman-Demi", "Bookman-DemiItalic", "Courier", "Courier-Oblique", "Courier-Bold", "Courier-BoldOblique", "Helvetica", "Helvetica-Oblique", "Helvetica-Bold", "Helvetica-BoldOblique", "Helvetica-Narrow", "Helvetica-Narrow-Oblique", "Helvetica-Narrow-Bold", "Helvetica-Narrow-BoldOblique", "NewCenturySchlbk-Roman", "NewCenturySchlbk-Italic", "NewCenturySchlbk-Bold", "NewCenturySchlbk-BoldItalic", "Palatino-Roman", "Palatino-Italic", "Palatino-Bold", "Palatino-BoldItalic", "Symbol", "ZapfChancery-MediumItalic", "ZapfDingbats", "Times-Roman") texfonts = ('Times-Roman', 'Times-Roman', 'Times-Bold', 'Times-Italic', 'Helvetica', 'Courier') # for user messages: tex_font_names = ('Default font', 'Roman', 'Bold', 'Italic', 'Sans Serif', 'Typewriter') align = (text.ALIGN_LEFT, text.ALIGN_CENTER, text.ALIGN_RIGHT) def coords_to_points(coords, trafo): # Given a list coords of coordinate pairs [x1, y1, x2, y2, ...], # return a new list of point objects: [Point(x1, y1), Point(x2, y2),...] # TRAFO is applied to point. # coords must have an even length. if len(coords) % 2 != 0: raise ValueError("coordinate has odd length") num = len(coords) coords = [coords] * (num / 2) return map(trafo, map(getitem, coords, range(0, num, 2)), map(getitem, coords, range(1, num, 2))) class XFigLoader(SimplifiedLoader): format_name = format_name functions = [('define_color', 0), ('read_ellipse', 1), ('read_polyline', 2), ('read_spline', 3), ('read_text', 4), ('read_arc', 5), ('begin_compound', 6), ('end_compound', -6)] def __init__(self, file, filename, match): SimplifiedLoader.__init__(self, file, filename, match) self.layout = None self.format_version = atof(match.group('version')) self.trafo = Trafo(1.0, 0.0, 0.0, -1.0, 0.0, 800) self.colors = std_colors + [StandardColors.black] * 512 self.depths = {} # map object ids to depth self.guess_cont() def readline(self): line = SimplifiedLoader.readline(self) while line[:1] == '#': line = SimplifiedLoader.readline(self) return line def get_compiled(self): funclist = {} for name, rx in self.functions: funclist[rx] = getattr(self, name) return funclist def set_depth(self, depth): self.depths[id(self.object)] = -depth def define_color(self, line): idx, color = split(line, None, 1) self.colors[atoi(idx)] = XRGBColor(color) def get_pattern(self, color, style = None): if style == -1: return EmptyPattern rgb = self.colors[color] if style is not None: if color in (BLACK, DEFAULT_COLOR): if style > 0 and style <= 20: rgb = Blend(self.colors[WHITE], rgb, (20 - style) / 20.0) elif style == 0: rgb = self.colors[WHITE] else: if style >= 0 and style < 20: rgb = Blend(self.colors[BLACK], rgb, (20 - style) / 20.0) elif style > 20 and style <= 40: rgb = Blend(self.colors[WHITE], rgb, (style - 20) / 20.0) return SolidPattern(rgb) def line(self, color, width, join, cap, style = 0, style_val = 0): if width: val = style_val / width width = width * 72.0/80.0 pattern = self.get_pattern(color) dashes = () if style == 1: # dashed dashes = (val, val) elif style == 2: # dotted dashes = (1 / width, val) elif style == 3: # dash-dot dashes = (val, 0.5 * val, 1 / width, 0.5 * val) elif style == 4: # dash-dot-dot dashes = (val, 0.45 * val, 1 / width, 0.333 * val, 1 / width, 0.45 * val) elif style == 5: # dash-dot-dot-dot dashes = (val, 0.4 * val, 1 / width, 0.333 * val, 1 / width, 0.333 * val, 1 / width, 0.4 * val) try: self.set_properties(line_pattern = pattern, line_width = width, line_join = xfig_join[join], line_cap = xfig_cap[cap], line_dashes = dashes) except: raise SketchLoadError("can't assign line style: %s:%s" % sys.exc_info()[:2]) else: self.empty_line() def fill(self, color, style): pattern = self.get_pattern(color, style) try: self.set_properties(fill_pattern = pattern) except: raise SketchLoadError("can't assign fill style: %s:%s" % sys.exc_info()[:2]) def font(self, font, size, flags): if flags & 4: # A PostScript font name = psfonts[font] else: # A TeX font. map to psfont name = texfonts[font] self.add_message(_("PostScript font `%(ps)s' substituted for " "TeX-font `%(tex)s'") % {'ps':name, 'tex':tex_font_names[font]}) self.set_properties(font = GetFont(name), font_size = size) def read_tokens(self, num): # read NUM tokens from the input file. return an empty list if # eof met before num items are read readline = self.readline; tokenize = skread.tokenize_line tokens = [] while len(tokens) < num: line = readline() if not line: return [] tokens = tokens + tokenize(line) return tokens def read_arc(self, line): readline = self.readline; tokenize = skread.tokenize_line args = tokenize(line) if len(args) != 21: raise SketchLoadError('Invalid Arc specification') sub_type, line_style, thickness, pen_color, fill_color, depth, \ pen_style, area_fill, style, cap, direction, \ forward_arrow, backward_arrow, \ cx, cy, x1, y1, x2, y2, x3, y3 = args self.fill(fill_color, area_fill) self.line(pen_color, thickness, const.JoinMiter, cap, line_style, style) if forward_arrow: readline() # XXX: implement this if backward_arrow:readline() # XXX: implement this trafo = self.trafo center = trafo(cx, cy); start = trafo(x1, y1); end = trafo(x3, y3) radius = abs(start - center) start_angle = atan2(start.y - center.y, start.x - center.x) end_angle = atan2(end.y - center.y, end.x - center.x) if direction == 0: start_angle, end_angle = end_angle, start_angle if sub_type == 1: sub_type = const.ArcArc else: sub_type = const.ArcPieSlice self.ellipse(radius, 0, 0, radius, center.x, center.y, start_angle, end_angle, sub_type) self.set_depth(depth) def read_ellipse(self, line): readline = self.readline; tokenize = skread.tokenize_line args = tokenize(line) if len(args) != 19: raise SketchLoadError('Invalid Ellipse specification') sub_type, line_style, thickness, pen_color, fill_color, depth, \ pen_style, area_fill, style, direction, angle, \ cx, cy, rx, ry, sx, sy, ex, ey = args self.fill(fill_color, area_fill) self.line(pen_color, thickness, const.JoinMiter, const.CapButt, line_style, style) center = self.trafo(cx, cy); radius = self.trafo.DTransform(rx, ry) trafo = Trafo(radius.x, 0, 0, radius.y) trafo = Rotation(angle)(trafo) trafo = Translation(center)(trafo) apply(self.ellipse, trafo.coeff()) self.set_depth(depth) def read_polyline(self, line): readline = self.readline; tokenize = skread.tokenize_line args = tokenize(line) if len(args) != 15: raise SketchLoadError('Invalid PolyLine specification') sub_type, line_style, thickness, pen_color, fill_color, depth, \ pen_style, area_fill, style, join, cap, \ radius, forward_arrow, backward_arrow, npoints = args self.fill(fill_color, area_fill) self.line(pen_color, thickness, join, cap, line_style, style) if forward_arrow: readline() # XXX: implement this if backward_arrow:readline() # XXX: implement this if sub_type == 5: readline() # imported picture path = CreatePath() ncoords = npoints * 2 pts = self.read_tokens(ncoords) if not pts: raise SketchLoadError('Missing points for polyline') if len(pts) > ncoords: del pts[ncoords:] map(path.AppendLine, coords_to_points(pts, self.trafo)) if sub_type in (2, 3, 4): path.load_close(1) self.bezier(paths = path) self.set_depth(depth) def read_spline(self, line): readline = self.readline; tokenize = skread.tokenize_line args = tokenize(line) if len(args) != 13: raise SketchLoadError('Invalid Spline specification') sub_type, line_style, thickness, pen_color, fill_color, depth, \ pen_style, area_fill, style, cap, \ forward_arrow, backward_arrow, npoints = args closed = sub_type & 1 if forward_arrow: readline() if backward_arrow:readline() # in 3.2 all splines are stored as x-splines... if self.format_version == 3.2: if sub_type in (0, 2): sub_type = 4 else: sub_type = 5 self.fill(fill_color, area_fill) self.line(pen_color, thickness, 0, cap, line_style, style) ncoords = npoints * 2 pts = self.read_tokens(ncoords) if not pts: raise SketchLoadError('Missing points for spline') if len(pts) > ncoords: del pts[ncoords:] pts = coords_to_points(pts, self.trafo) path = CreatePath() if sub_type in (2, 3): # interpolated spline, read 2 control points for each node ncontrols = 4 * npoints controls = self.read_tokens(ncontrols) if not controls: raise SketchLoadError('Missing control points for spline') if len(controls) > ncontrols: del controls[ncontrols:] controls = coords_to_points(controls[2:-2], self.trafo) path.AppendLine(pts[0]) ncontrols = 2 * (npoints - 1) controls = [controls] * (npoints - 1) map(path.AppendBezier, map(getitem, controls, range(0, ncontrols, 2)), map(getitem, controls, range(1, ncontrols, 2)), pts[1:]) elif sub_type in (0, 1): # approximated spline f13 = 1.0 / 3.0; f23 = 2.0 / 3.0 curve = path.AppendBezier straight = path.AppendLine last = pts[0] cur = pts[1] start = node = (last + cur) / 2 if closed: straight(node) else: straight(last) straight(node) last = cur for cur in pts[2:]: c1 = f13 * node + f23 * last node = (last + cur) / 2 c2 = f13 * node + f23 * last curve(c1, c2, node) last = cur if closed: curve(f13 * node + f23 * last, f13 * start + f23 * last, start) else: straight(last) elif sub_type in (4, 5): # An X-spline. Treat it like a polyline for now. # read and discard the control info self.read_tokens(npoints) self.add_message(_("X-Spline treated as PolyLine")) map(path.AppendLine, pts) if closed: path.AppendLine(path.Node(0)) if closed: path.load_close(1) self.bezier(paths = path) self.set_depth(depth) def read_text(self, line): args = tokenize(line, 12) # don't tokenize the text itself if len(args) != 13: # including the unparsed rest of the line raise SketchLoadError('Invalid text specification') sub_type, color, depth, pen_style, font, size, angle, flags, \ height, length, x, y, rest = args self.fill(color, None) self.font(font, size * 0.9, flags) if len(rest) > 2: #at least a space and a newline # read the actual text. This implementation may fail in # certain cases! string = rest[1:] while string[-5:] != '\\001\n': line = self.readline() if not line: raise SketchLoadError('Premature end of string') string = string + line globals = {'__builtins__': {}} try: # using eval here might be a security hole! string = eval('"""' + string[:-5] + '"""', globals) except: string = eval("'''" + string[:-5] + "'''", globals) else: raise SketchLoadError('Invalid text string') trafo = Translation(self.trafo(x, y))(Rotation(angle)) self.simple_text(string, trafo = trafo, halign = align[sub_type]) self.set_depth(depth) def begin_compound(self, line): self.begin_group() def end_compound(self, line): try: self.end_group() except EmptyCompositeError: pass def end_composite(self): # sort composite_items by their depth items = self.composite_items depths = map(self.depths.get, map(id, items), [-10000]*len(items)) depths = map(None, depths, range(len(items)), items) depths.sort() self.composite_items = map(getitem, depths, [2] * len(items)) SimplifiedLoader.end_composite(self) def read_header(self): format = orientation = None if self.format_version >= 3.0: line = strip(self.readline()) if line: # portrait/landscape if lower(line) == 'landscape': orientation = pagelayout.Landscape else: orientation = pagelayout.Portrait else: raise SketchLoadError('No format specification') line = strip(self.readline()) if line: # centering line = lower(line) if line == 'center' or line == 'flush left': line = lower(strip(self.readline())) if not line: raise SketchLoadError( 'No Center/Flushleft or Units specification') if line == 'metric': # ignore for now pass if self.format_version >= 3.2: self.readline() # papersize self.readline() # magnification self.readline() # pages self.readline() # transparent color line = strip(self.readline()) if line: try: ppi, coord = map(atoi, split(line)) except: raise SketchLoadError('Invalid Resolution specification') self.trafo = self.trafo(Scale(72.0 / ppi)) def Load(self): file = self.file funclist = self.get_compiled() # binding frequently used functions to local variables speeds up # the process considerably... readline = file.readline; tokenize = skread.tokenize_line self.document() self.layer(_("Layer 1")) try: self.read_header() line = self.readline() while line: tokens = tokenize(line, 1) if len(tokens) > 1: function, rest = tokens else: function = tokens[0] rest = '' if type(function) == type(0): function = funclist.get(function) if function: function(rest) line = self.readline() except SketchLoadError, value: warn_tb(INTERNAL) raise SketchLoadError('%d:%s' % (self.lineno, str(value))), None,\ sys.exc_traceback except: if load._dont_handle_exceptions: warn(INTERNAL, 'XFigLoader: error reading line %d:%s', self.lineno, `line`) raise raise SketchLoadError(_("error in line %d:\n%s") % (self.lineno, `line`)), None,\ sys.exc_traceback self.end_all() self.object.load_Completed() return self.object UniConvertor-1.1.4/src/app/plugins/Filters/pssaver.py0000664000076400007640000000166411042232543021346 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2007-2008 by Igor Novikov # # This library is covered by GNU General Public License v2.0. # For more info see COPYRIGHTS file in sK1 root directory. ###Sketch Config #type = Export #tk_file_type = ("PostScript (PS)", '.ps') #extensions = '.ps' #format_name = 'PS' #unload = 1 ###End import app from app.utils import os_utils from app import PostScriptDevice def save(doc, file, filename, options = {}): app.updateInfo(inf1=_('Postscript generation.'),inf2=_('Start document composing'),inf3=3) bbox = doc.BoundingRect(visible = 0, printable = 1) dev = PostScriptDevice ps_dev = dev(file, as_eps = 1, bounding_box = tuple(bbox), rotate = 0, # page rotate? For = os_utils.get_real_username(), CreationDate = os_utils.current_date(), Title = 'generated by sK1', document = doc) doc.Draw(ps_dev) ps_dev.Close() app.updateInfo(inf2=_('Document generation is finished'),inf3=100)UniConvertor-1.1.4/src/app/plugins/Filters/ailoader.py0000755000076400007640000007646611042221206021452 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2007 by Igor Novikov # Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ###Sketch Config #type = Import #class_name = 'AILoader' #rx_magic = r'^%!PS-Adobe-[23]\.0' #tk_file_type = ("Adobe Illustrator (up to 7 ver.)", '.ai') format_name = 'Adobe Illustrator' #unload = 1 #standard_messages = 1 ###End # # Import Filter for Adobe Illustrator files # # # Status: # # This filter is work in progress, and not everything works yet. Here's # an outline of what should work, what will be implemented in future # versions and what is not currently possible in Sketch. # # If this filter fails to import a file that it should be able to # handle, please contact me (see the Sketch README for the email # address). # # # - Path construction: # # The filters understands all path construction operators including # compound paths. # # - Path attributes: # # The filter handles line width, dash pattern (without the phase), # line join and line cap. Miter limit is restricted to the value # implied by the X-server (11 degrees). Flatness is ignored. Likewise # the winding order, because Sketch currently only supports the # even-odd-rule for filled paths. # # - Stroke/Fill color # # The filter handles the colormodels gray scale, CMYK, RGB, custom # CMYK and custom RGB. # # - Patterns/Gradients # # The filter understands Gradients, but they may not always look # exactly like in Illustrator because Sketch's model is a bit more # limited than Illustrator's in some respects (but this may change) # and not all of Illustrator's colormodels are implemented yet here. # # Patterns are not implemented, since Sketch doesn't have Vector # patterns yet. # # - Containers # # The filter handles groups and layers. # # - Text # # The filter can handle point text and path text to some degree. It # doesn't handle encodings at the moment. # # Because of Sketch's limited Text capabilities it can't handle area # text and ignores spacing information. # # - Raster Images # # Sketch doesn't handle all variants yet # # - Guides # # The filter puts all guides it finds into the guide layer, regardless # of where they are defined in the file. # # # - As yet unimplemented Features that could be handled by Sketch # # - Masks # # - Embedded EPS files (''"Adobe Illustrator") import string, re from math import pi, hypot from types import IntType, StringType, ListType import string from PIL import Image import streamfilter from app import Document, Layer, CreatePath, ContSmooth, \ SolidPattern, EmptyPattern, LinearGradient, RadialGradient,\ CreateRGBColor, CreateCMYKColor, MultiGradient, Trafo, Point, Polar, \ StandardColors, GetFont, PathText, SimpleText, const, UnionRects from app.Graphics import text from app.events.warn import INTERNAL, warn_tb from app.Lib import encoding from app.io.load import GenericLoader, EmptyCompositeError from app.pstokenize import PSTokenizer, DSC_COMMENT, OPERATOR, END, \ MAX_DATA_TOKEN def cmyk_custom_color(c, m, y, k, t): # t = tint t = 1.0 - t return CreateCMYKColor(t * c, t * m, t * y, t * k) def rgb_custom_color(r, g, b, t): t = 1.0 - t return CreateRGBColor(1 - (t * (1 - r)), 1 - (t * (1 - g)), 1 - (t * (1 - b))) def fix_gradient(array, factor, offset): array = array[:] for i in range(len(array)): pos, color = array[i] array[i] = factor * pos + offset, color if factor < 0: array.reverse() if array[0][0] < 0: if array[-1][0] > 0: while array[0][0] < 0: pos, color = array[0] del array[0] if array[0][0] > 0: frac = -pos / (array[0][0] - pos) array.insert(0, (0.0, color.Blend(array[0][1], 1-frac, frac))) else: color = array[-1][1] array = [(0.0, color), (1.0, color)] elif array[0][0] > 0: array.insert(0, (0.0, array[0][1])) if array[-1][0] > 1: if array[0][0] < 1: while array[-1][0] > 1: pos, color = array[-1] del array[-1] if array[-1][0] < 1: frac = (pos - 1) / (pos - array[-1][0]) array.append((1.0, color.Blend(array[-1][1], 1-frac, frac))) else: color = array[0][1] array = [(0.0, color), (1.0, color)] elif array[-1][0] < 1: array.append((1.0, array[-1][1])) return array # arrays to convert AI join and cap to Sketch's join and cap. In AI # files they're given as small ints so we just use a tuple where we can # use the AI cap/join value as index to get the corresponding value in # Sketch. _ai_join = (const.JoinMiter, const.JoinRound, const.JoinBevel) _ai_cap = (const.CapButt, const.CapRound, const.CapProjecting) # The same for text alignment. The last two values are two variants of # justified text, which Sketch doesn't have, so we're just using # centered for now. _ai_text_align = (text.ALIGN_LEFT, text.ALIGN_CENTER, text.ALIGN_RIGHT, text.ALIGN_CENTER, text.ALIGN_CENTER) artboard_trafo = Trafo(1, 0, 0, -1, 4014, 4716) artboard_trafo_inv = artboard_trafo.inverse() class FontInfo: def __init__(self, psname, newname, encoding): self.psname = psname self.newname = newname self.encoding = encoding self.reencoder = None def Reencode(self, text): if self.reencoder is None: self.reencoder = encoding.Reencoder(self.encoding, encoding.iso_latin_1) return self.reencoder(text) grow_join = [5.240843064, 0.5, 0.5] grow_cap = [None, 0.5, 0.5, 0.70710678] def fix_bounding_rect(rect, style): return rect.grown(style.line_width * max(grow_cap[style.line_cap], grow_join[style.line_join])) 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', "cmyk": 'set_fill_cmyk', "Xa": 'set_fill_rgb', "rgb": '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', "closepath": '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', "newpath": 'begin_compound_path', "*U": 'end_compound_path', "gsave": '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 UniConvertor-1.1.4/src/app/plugins/Filters/svgloader.py0000755000076400007640000007003311211355067021653 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2007 by Igor E. Novikov # Copyright (C) 1997, 1998, 2001 by Bernhard Herzog # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in sK1 root directory. ###Sketch Config #type = Import #class_name = 'SVGLoader' #rx_magic = '.*\\<(\\?xml|svg)' #tk_file_type = ('Scalable Vector Graphics (SVG)', ('.svg', '.xml')) format_name = 'SVG' #unload = 1 ###End from types import StringType from math import pi, tan import os, sys import re from string import strip, split, atoi, lower import string import operator import streamfilter from app import Document, Layer, CreatePath, ContSmooth, \ SolidPattern, EmptyPattern, LinearGradient, RadialGradient,\ CreateRGBColor, CreateCMYKColor, MultiGradient, \ Trafo, Translation, Rotation, Scale, Point, Polar, \ StandardColors, GetFont, PathText, SimpleText, const, UnionRects, \ Bezier, Line, load_image, skread from app.events.warn import INTERNAL, USER, warn_tb from app.io.load import GenericLoader, EmptyCompositeError from app.Graphics import text, properties from xml.sax import handler import xml.sax from xml.sax.xmlreader import InputSource # beginning with Python 2.0, the XML modules return Unicode strings, # while for older versions they're 'normal' 8-bit strings. Provide some # functions to make this code work with both string types. def as_latin1(s): # convert the string s to iso-latin-1 if it's a unicode string encode = getattr(s, "encode", None) if encode is not None: s = encode("iso-8859-1", "replace") return s # Conversion factors to convert standard CSS/SVG units to userspace # units. factors = {'pt': 1.0, 'px': 1.0, 'in': 72.0, 'cm': 72.0 / 2.54, 'mm': 7.20 / 2.54} degrees = pi / 180.0 def csscolor(str): str = strip(str) if str[0] == '#': if len(str) == 7: r = atoi(str[1:3], 16) / 255.0 g = atoi(str[3:5], 16) / 255.0 b = atoi(str[5:7], 16) / 255.0 elif len(str) == 4: # According to the CSS rules a single HEX digit is to be # treated as a repetition of the digit, so that for a digit # d the value is (16 * d + d) / 255.0 which is equal to d / 15.0 r = atoi(str[1], 16) / 15.0 g = atoi(str[2], 16) / 15.0 b = atoi(str[3], 16) / 15.0 color = CreateRGBColor(r, g, b) elif namedcolors.has_key(str): color = namedcolors[str] else: color = StandardColors.black return color namedcolors = { 'aliceblue': csscolor('#f0f8ff'), 'antiquewhite': csscolor('#faebd7'), 'aqua': csscolor('#00ffff'), 'aquamarine': csscolor('#7fffd4'), 'azure': csscolor('#f0ffff'), 'beige': csscolor('#f5f5dc'), 'bisque': csscolor('#ffe4c4'), 'black': csscolor('#000000'), 'blanchedalmond': csscolor('#ffebcd'), 'blue': csscolor('#0000ff'), 'blueviolet': csscolor('#8a2be2'), 'brown': csscolor('#a52a2a'), 'burlywood': csscolor('#deb887'), 'cadetblue': csscolor('#5f9ea0'), 'chartreuse': csscolor('#7fff00'), 'chocolate': csscolor('#d2691e'), 'coral': csscolor('#ff7f50'), 'cornflowerblue': csscolor('#6495ed'), 'cornsilk': csscolor('#fff8dc'), 'crimson': csscolor('#dc143c'), 'cyan': csscolor('#00ffff'), 'darkblue': csscolor('#00008b'), 'darkcyan': csscolor('#008b8b'), 'darkgoldenrod': csscolor('#b8860b'), 'darkgray': csscolor('#a9a9a9'), 'darkgreen': csscolor('#006400'), 'darkgrey': csscolor('#a9a9a9'), 'darkkhaki': csscolor('#bdb76b'), 'darkmagenta': csscolor('#8b008b'), 'darkolivegreen': csscolor('#556b2f'), 'darkorange': csscolor('#ff8c00'), 'darkorchid': csscolor('#9932cc'), 'darkred': csscolor('#8b0000'), 'darksalmon': csscolor('#e9967a'), 'darkseagreen': csscolor('#8fbc8f'), 'darkslateblue': csscolor('#483d8b'), 'darkslategray': csscolor('#2f4f4f'), 'darkslategrey': csscolor('#2f4f4f'), 'darkturquoise': csscolor('#00ced1'), 'darkviolet': csscolor('#9400d3'), 'deeppink': csscolor('#ff1493'), 'deepskyblue': csscolor('#00bfff'), 'dimgray': csscolor('#696969'), 'dimgrey': csscolor('#696969'), 'dodgerblue': csscolor('#1e90ff'), 'firebrick': csscolor('#b22222'), 'floralwhite': csscolor('#fffaf0'), 'forestgreen': csscolor('#228b22'), 'fuchsia': csscolor('#ff00ff'), 'gainsboro': csscolor('#dcdcdc'), 'ghostwhite': csscolor('#f8f8ff'), 'gold': csscolor('#ffd700'), 'goldenrod': csscolor('#daa520'), 'gray': csscolor('#808080'), 'green': csscolor('#008000'), 'greenyellow': csscolor('#adff2f'), 'grey': csscolor('#808080'), 'honeydew': csscolor('#f0fff0'), 'hotpink': csscolor('#ff69b4'), 'indianred': csscolor('#cd5c5c'), 'indigo': csscolor('#4b0082'), 'ivory': csscolor('#fffff0'), 'khaki': csscolor('#f0e68c'), 'lavender': csscolor('#e6e6fa'), 'lavenderblush': csscolor('#fff0f5'), 'lawngreen': csscolor('#7cfc00'), 'lemonchiffon': csscolor('#fffacd'), 'lightblue': csscolor('#add8e6'), 'lightcoral': csscolor('#f08080'), 'lightcyan': csscolor('#e0ffff'), 'lightgoldenrodyellow': csscolor('#fafad2'), 'lightgray': csscolor('#d3d3d3'), 'lightgreen': csscolor('#90ee90'), 'lightgrey': csscolor('#d3d3d3'), 'lightpink': csscolor('#ffb6c1'), 'lightsalmon': csscolor('#ffa07a'), 'lightseagreen': csscolor('#20b2aa'), 'lightskyblue': csscolor('#87cefa'), 'lightslategray': csscolor('#778899'), 'lightslategrey': csscolor('#778899'), 'lightsteelblue': csscolor('#b0c4de'), 'lightyellow': csscolor('#ffffe0'), 'lime': csscolor('#00ff00'), 'limegreen': csscolor('#32cd32'), 'linen': csscolor('#faf0e6'), 'magenta': csscolor('#ff00ff'), 'maroon': csscolor('#800000'), 'mediumaquamarine': csscolor('#66cdaa'), 'mediumblue': csscolor('#0000cd'), 'mediumorchid': csscolor('#ba55d3'), 'mediumpurple': csscolor('#9370db'), 'mediumseagreen': csscolor('#3cb371'), 'mediumslateblue': csscolor('#7b68ee'), 'mediumspringgreen': csscolor('#00fa9a'), 'mediumturquoise': csscolor('#48d1cc'), 'mediumvioletred': csscolor('#c71585'), 'midnightblue': csscolor('#191970'), 'mintcream': csscolor('#f5fffa'), 'mistyrose': csscolor('#ffe4e1'), 'moccasin': csscolor('#ffe4b5'), 'navajowhite': csscolor('#ffdead'), 'navy': csscolor('#000080'), 'oldlace': csscolor('#fdf5e6'), 'olive': csscolor('#808000'), 'olivedrab': csscolor('#6b8e23'), 'orange': csscolor('#ffa500'), 'orangered': csscolor('#ff4500'), 'orchid': csscolor('#da70d6'), 'palegoldenrod': csscolor('#eee8aa'), 'palegreen': csscolor('#98fb98'), 'paleturquoise': csscolor('#afeeee'), 'palevioletred': csscolor('#db7093'), 'papayawhip': csscolor('#ffefd5'), 'peachpuff': csscolor('#ffdab9'), 'peru': csscolor('#cd853f'), 'pink': csscolor('#ffc0cb'), 'plum': csscolor('#dda0dd'), 'powderblue': csscolor('#b0e0e6'), 'purple': csscolor('#800080'), 'red': csscolor('#ff0000'), 'rosybrown': csscolor('#bc8f8f'), 'royalblue': csscolor('#4169e1'), 'saddlebrown': csscolor('#8b4513'), 'salmon': csscolor('#fa8072'), 'sandybrown': csscolor('#f4a460'), 'seagreen': csscolor('#2e8b57'), 'seashell': csscolor('#fff5ee'), 'sienna': csscolor('#a0522d'), 'silver': csscolor('#c0c0c0'), 'skyblue': csscolor('#87ceeb'), 'slateblue': csscolor('#6a5acd'), 'slategray': csscolor('#708090'), 'slategrey': csscolor('#708090'), 'snow': csscolor('#fffafa'), 'springgreen': csscolor('#00ff7f'), 'steelblue': csscolor('#4682b4'), 'tan': csscolor('#d2b48c'), 'teal': csscolor('#008080'), 'thistle': csscolor('#d8bfd8'), 'tomato': csscolor('#ff6347'), 'turquoise': csscolor('#40e0d0'), 'violet': csscolor('#ee82ee'), 'wheat': csscolor('#f5deb3'), 'white': csscolor('#ffffff'), 'whitesmoke': csscolor('#f5f5f5'), 'yellow': csscolor('#ffff00'), 'yellowgreen': csscolor('#9acd32'), } join = {'miter': const.JoinMiter, 'round': const.JoinRound, 'bevel': const.JoinBevel} cap = {'butt': const.CapButt, 'round': const.CapRound, 'square': const.CapProjecting} commatospace = string.maketrans(',', ' ') rx_command = re.compile(r'[a-df-zA-DF-Z]((\s*[-0-9.e]+)*)\s*') rx_trafo = re.compile(r'\s*([a-zA-Z]+)\(([^)]*)\)') class SVGHandler(handler.ContentHandler): dispatch_start = {'svg': 'initsvg', 'g': 'begin_group', 'circle': 'circle', 'ellipse': 'ellipse', 'rect': 'rect', 'polyline': 'polyline', 'polygon': 'polygon', 'path': 'begin_path', 'text': 'begin_text', 'image': 'image', 'data': 'data', 'use': 'use', 'defs': 'begin_defs', } dispatch_end = {'g': 'end_group', 'path': 'end_path', 'defs': 'end_defs', 'text': 'end_text' } def __init__(self, loader): self.loader = loader self.trafo = self.basetrafo = Trafo() self.state_stack = () self.style = loader.style.Copy() self.style.line_pattern = EmptyPattern self.style.fill_pattern = SolidPattern(StandardColors.black) self.current_text = "" #self.style.font = GetFont("Times-Roman") self.style.font_size = 12 self.halign = text.ALIGN_LEFT self.named_objects = {} self.in_defs = 0 self.paths = None self.path = None self.depth = 0 self.indent = ' ' def _print(self, *args): return if args: print self.depth * self.indent + args[0], for s in args[1:]: print s, print def parse_transform(self, trafo_string): trafo = self.trafo #print trafo trafo_string = as_latin1(trafo_string) while trafo_string: #print trafo_string match = rx_trafo.match(trafo_string) if match: function = match.group(1) args = string.translate(match.group(2), commatospace) args = map(float, split(args)) trafo_string = trafo_string[match.end(0):] if function == 'matrix': trafo = trafo(apply(Trafo, tuple(args))) elif function == 'scale': trafo = trafo(Scale(args[0])) elif function == 'translate': dx, dy = args trafo = trafo(Translation(dx, dy)) elif function == 'rotate': trafo = trafo(Rotation(args[0] * degrees)) elif function == 'skewX': trafo = trafo(Trafo(1, 0, tan(args[0] * degrees), 1, 0, 0)) elif function == 'skewY': trafo = trafo(Trafo(1, tan(args[0] * degrees), 0, 1, 0, 0)) else: trafo_string = '' #print trafo self.trafo = trafo def startElement(self, name, attrs): self._print('(', name) for key, value in attrs.items(): self._print(' -', key, `value`) self.depth = self.depth + 1 self.push_state() if attrs.has_key('transform'): self.parse_transform(attrs['transform']) self._print("applied transormation", self.trafo) method = self.dispatch_start.get(name) if method is not None: getattr(self, method)(attrs) def endElement(self, name): self.depth = self.depth - 1 self._print(')', name) method = self.dispatch_end.get(name) if method is not None: getattr(self, method)() self.pop_state() def characters(self, data): self.current_text = self.current_text + as_latin1(data) def error(self, exception): print 'error', exception def fatalError(self, exception): print 'fatalError', exception def warning(self, exception): print 'warning', exception def initsvg(self, attrs): width = self.user_length(attrs.get('width', '100%')) height = self.user_length(attrs.get('height', '100%')) self._print('initsvg', width, height) self.trafo = Trafo(1, 0, 0, -1, 0, height) self.basetrafo = self.trafo # evaluate viewBox # FIXME: Handle preserveAspectRatio as well viewbox = attrs.get("viewBox", "") if viewbox: vx, vy, vwidth, vheight = map(float, split(viewbox)) t = Scale(width / vwidth, height / vheight) t = t(Translation(-vx, -vy)) self.trafo = self.trafo(t) self._print("basetrafo", self.basetrafo) def parse_style(self, style): parts = filter(None, map(strip, split(style, ';'))) for part in parts: key, val = map(strip, split(part, ':', 1)) self._print('style', key, val) # only try to parse the value if it's not empty if val: self.try_add_style(key, val) else: # FIXME: we should probably print a message or something pass def try_add_style(self,key,val): if key == 'fill': if val == 'none': self.style.fill_pattern = EmptyPattern else: color = csscolor(val) self._print('fill', color) self.style.fill_pattern = SolidPattern(color) elif key == 'stroke': if val == 'none': self.style.line_pattern = EmptyPattern else: color = csscolor(val) self._print('stroke', color) self.style.line_pattern = SolidPattern(color) elif key == 'stroke-width': width = self.user_length(val) # Multiply the width with a value taken from the # transformation matrix because so far transforming an # object in Sketch does not affect the stroke width in any # way. Thus we have to do that explicitly here. # FIXME: using m11 is not really the best approach but in # many cases better than using the width as is. width = self.trafo.m11 * width self._print('width', width) self.style.line_width = width elif key == 'stroke-linejoin': self.style.line_join = join[val] elif key == 'stroke-linecap': self.style.line_cap = cap[val] elif key == 'font-family': try: # convert val to 8bit string. self.style.font = GetFont(str(val)) except UnicodeError: # If it's not ASCII we probably won't have the font, so # use the default one. # FIXME: Give a warning pass elif key == 'font-size': self.style.font_size = self.user_length(val) ####self.style.font_size = float(val) elif key == 'text-anchor': if val=='start': self.halign = text.ALIGN_LEFT elif val == 'middle': self.halign = text.ALIGN_CENTER elif val == 'end': self.halign = text.ALIGN_RIGHT def set_loader_style(self, allow_font = 0): # Copy self.style to loader. # If allow_font is false (the default) do not copy the font # properties. property_types = properties.property_types style = self.style.Copy() if not allow_font: for name in style.__dict__.keys(): if property_types.get(name)==properties.FontProperty: delattr(style, name) self.loader.style = style def push_state(self): self.state_stack = self.style, self.trafo, self.state_stack self.style = self.style.Copy() def pop_state(self): self.style, self.trafo, self.state_stack = self.state_stack def user_length(self, str): # interpret string as a length and return the appropriate value # user coordinates str = strip(str) factor = factors.get(str[-2:]) if factor is not None: str = str[:-2] elif str[-1] == '%': # FIXME: this case depends on the width/height attrs of the # SVG element str = str[:-1] factor = 1.0 else: factor = 1.0 return float(str) * factor def user_point(self, x, y): # Return the point described by the SVG coordinates x and y as # an SKPoint object in user coordinates. x and y are expected to # be strings. x = strip(x) y = strip(y) # extract the units from the coordinate values if any and # determine the appropriate factor to convert those units to # user space units. xunit = x[-2:] factor = factors.get(xunit) if factor is not None: x = x[:-2] elif x[-1] == '%': # XXX this is wrong x = x[:-1] xunit = '%' factor = 1 else: xunit = '' factor = 1.0 x = float(x) * factor yunit = y[-2:] factor = factors.get(yunit) if factor is not None: y = y[:-2] elif y[-1] == '%': y = y[:-1] yunit = '%' factor = 1.0 else: yunit = '' factor = 1.0 y = float(y) * factor return Point(x, y) def point(self, x, y, relative = 0): # Return the point described by the SVG coordinates x and y as # an SKPoint object in absolute, i.e. document coordinates. x # and y are expected to be strings. If relative is true, they're # relative coordinates. x, y = self.user_point(x, y) if relative: p = self.trafo.DTransform(x, y) else: p = self.trafo(x, y) return p def circle(self, attrs): if self.in_defs: id = attrs.get('id', '') if id: self.named_objects[id] = ('object', 'circle', attrs) return if attrs.has_key('cx'): x = attrs['cx'] else: x = '0' if attrs.has_key('cy'): y = attrs['cy'] else: y = '0' x, y = self.point(x, y) r = self.point(attrs['r'], '0', relative = 1).x t = Trafo(r, 0, 0, r, x, y) self._print('circle', t) style = attrs.get('style', '') if style: self.parse_style(style) self.set_loader_style() apply(self.loader.ellipse, t.coeff()) def ellipse(self, attrs): if self.in_defs: id = attrs.get('id', '') if id: self.named_objects[id] = ('object', 'ellipse', attrs) return if attrs.has_key('cx'): x = attrs['cx'] else: x = '0' if attrs.has_key('cy'): y = attrs['cy'] else: y = '0' x, y = self.point(x, y) rx, ry = self.point(attrs['rx'], attrs['ry'], relative = 1) t = Trafo(rx, 0, 0, ry, x, y) self._print('ellipse', t) style = attrs.get('style', '') if style: self.parse_style(style) self.set_loader_style() apply(self.loader.ellipse, t.coeff()) def rect(self, attrs): #print 'rect', attrs.map if self.in_defs: id = attrs.get('id', '') if id: self.named_objects[id] = ('object', 'rect', attrs) return if attrs.has_key('x'): x = attrs['x'] else: x = '0' if attrs.has_key('y'): y = attrs['y'] else: y = '0' x, y = self.point(x, y) wx, wy = self.point(attrs['width'], "0", relative = 1) hx, hy = self.point("0", attrs['height'], relative = 1) t = Trafo(wx, wy, hx, hy, x, y) self._print('rect', t) style = attrs.get('style', '') if style: self.parse_style(style) self.set_loader_style() apply(self.loader.rectangle, t.coeff()) def polyline(self, attrs): if self.in_defs: id = attrs.get('id', '') if id: self.named_objects[id] = ('object', 'polyline', attrs) return points = as_latin1(attrs['points']) points = string.translate(points, commatospace) points = split(points) path = CreatePath() point = self.point for i in range(0, len(points), 2): path.AppendLine(point(points[i], points[i + 1])) style = attrs.get('style', '') if style: self.parse_style(style) self.set_loader_style() self.loader.bezier(paths = (path,)) def polygon(self, attrs): if self.in_defs: id = attrs.get('id', '') if id: self.named_objects[id] = ('object', 'polygon', attrs) return points = as_latin1(attrs['points']) points = string.translate(points, commatospace) points = split(points) path = CreatePath() point = self.point for i in range(0, len(points), 2): path.AppendLine(point(points[i], points[i + 1])) path.AppendLine(path.Node(0)) path.ClosePath() style = attrs.get('style', '') if style: self.parse_style(style) self.set_loader_style() self.loader.bezier(paths = (path,)) def parse_path(self, str): paths = self.paths path = self.path trafo = self.trafo str = strip(string.translate(as_latin1(str), commatospace)) last_quad = None last_cmd = cmd = None f13 = 1.0 / 3.0; f23 = 2.0 / 3.0 #print '*', str while 1: match = rx_command.match(str) #print match if match: last_cmd = cmd cmd = str[0] str = str[match.end():] #print '*', str points = match.group(1) #print '**', points if points: # use tokenize_line to parse the arguments so that # we deal with signed numbers following another # number without intervening whitespace other # characters properls. # FIXME: tokenize_line works but is not the best way # to do it because it accepts input that wouldn't be # valid here. points = filter(operator.isNumberType, skread.tokenize_line(points)) #print cmd, points if cmd in 'mM': path = CreatePath() paths.append(path) if cmd == 'M' or len(paths) == 1: path.AppendLine(trafo(points[0], points[1])) else: p = trafo.DTransform(points[0], points[1]) path.AppendLine(paths[-2].Node(-1) + p) if len(points) > 2: if cmd == 'm': for i in range(2, len(points), 2): p = trafo.DTransform(points[i], points[i + 1]) path.AppendLine(path.Node(-1) + p) else: for i in range(2, len(points), 2): path.AppendLine(trafo(points[i], points[i+1])) elif cmd == 'l': for i in range(0, len(points), 2): p = trafo.DTransform(points[i], points[i + 1]) path.AppendLine(path.Node(-1) + p) elif cmd == 'L': for i in range(0, len(points), 2): path.AppendLine(trafo(points[i], points[i+1])) elif cmd =='H': for num in points: path.AppendLine(Point(num, path.Node(-1).y)) elif cmd =='h': for num in points: x, y = path.Node(-1) dx, dy = trafo.DTransform(num, 0) path.AppendLine(Point(x + dx, y + dy)) elif cmd =='V': for num in points: path.AppendLine(Point(path.Node(-1).x, num)) elif cmd =='v': for num in points: x, y = path.Node(-1) dx, dy = trafo.DTransform(0, num) path.AppendLine(Point(x + dx, y + dy)) elif cmd == 'C': if len(points) % 6 != 0: self.loader.add_message("number of parameters of 'C'"\ "must be multiple of 6") else: for i in range(0, len(points), 6): p1 = trafo(points[i], points[i + 1]) p2 = trafo(points[i + 2], points[i + 3]) p3 = trafo(points[i + 4], points[i + 5]) path.AppendBezier(p1, p2, p3) elif cmd == 'c': if len(points) % 6 != 0: self.loader.add_message("number of parameters of 'c'"\ "must be multiple of 6") else: for i in range(0, len(points), 6): p = path.Node(-1) p1 = p + trafo.DTransform(points[i], points[i + 1]) p2 = p + trafo.DTransform(points[i+2], points[i+3]) p3 = p + trafo.DTransform(points[i+4], points[i+5]) path.AppendBezier(p1, p2, p3) elif cmd == 'S': if len(points) % 4 != 0: self.loader.add_message("number of parameters of 'S'"\ "must be multiple of 4") else: for i in range(0, len(points), 4): type, controls, p, cont = path.Segment(-1) if type == Bezier: q = controls[1] else: q = p p1 = 2 * p - q p2 = trafo(points[i], points[i + 1]) p3 = trafo(points[i + 2], points[i + 3]) path.AppendBezier(p1, p2, p3) elif cmd == 's': if len(points) % 4 != 0: self.loader.add_message("number of parameters of 's'"\ "must be multiple of 4") else: for i in range(0, len(points), 4): type, controls, p, cont = path.Segment(-1) if type == Bezier: q = controls[1] else: q = p p1 = 2 * p - q p2 = p + trafo.DTransform(points[i], points[i + 1]) p3 = p + trafo.DTransform(points[i+2], points[i+3]) path.AppendBezier(p1, p2, p3) elif cmd == 'Q': if len(points) % 4 != 0: self.loader.add_message("number of parameters of 'Q'"\ "must be multiple of 4") else: for i in range(0, len(points), 4): q = trafo(points[i], points[i + 1]) p3 = trafo(points[i + 2], points[i + 3]) p1 = f13 * path.Node(-1) + f23 * q p2 = f13 * p3 + f23 * q path.AppendBezier(p1, p2, p3) last_quad = q elif cmd == 'q': if len(points) % 4 != 0: self.loader.add_message("number of parameters of 'q'"\ "must be multiple of 4") else: for i in range(0, len(points), 4): p = path.Node(-1) q = p + trafo.DTransform(points[i], points[i + 1]) p3 = p + trafo.DTransform(points[i+2], points[i+3]) p1 = f13 * p + f23 * q p2 = f13 * p3 + f23 * q path.AppendBezier(p1, p2, p3) last_quad = q elif cmd == 'T': if len(points) % 2 != 0: self.loader.add_message("number of parameters of 'T'"\ "must be multiple of 4") else: if last_cmd not in 'QqTt' or last_quad is None: last_quad = path.Node(-1) for i in range(0, len(points), 2): p = path.Node(-1) q = 2 * p - last_quad p3 = trafo(points[i], points[i + 1]) p1 = f13 * p + f23 * q p2 = f13 * p3 + f23 * q path.AppendBezier(p1, p2, p3) last_quad = q elif cmd == 't': if len(points) % 2 != 0: self.loader.add_message("number of parameters of 't'"\ "must be multiple of 4") else: if last_cmd not in 'QqTt' or last_quad is None: last_quad = path.Node(-1) for i in range(0, len(points), 2): p = path.Node(-1) q = 2 * p - last_quad p3 = p + trafo.DTransform(points[i], points[i + 1]) p1 = f13 * p + f23 * q p2 = f13 * p3 + f23 * q path.AppendBezier(p1, p2, p3) last_quad = q elif cmd in 'zZ': path.AppendLine(path.Node(0)) path.ClosePath() else: break self.path = path def begin_path(self, attrs): if self.in_defs: id = attrs.get('id', '') if id: self.named_objects[id] = ('object', 'path', attrs) return self.paths = [] self.path = None self.parse_path(attrs['d']) style = attrs.get('style', '') if style: self.parse_style(style) self.set_loader_style() def end_path(self): if self.in_defs: return self.loader.bezier(paths = tuple(self.paths)) self.paths = None def image(self, attrs): if self.in_defs: id = attrs.get('id', '') if id: self.named_objects[id] = ('object', 'image', attrs) return href = attrs['xlink:href'] if os.path.isfile(os.path.join(self.loader.directory, href)): image = load_image(os.path.join(self.loader.directory, href)).image if attrs.has_key('x'): x = attrs['x'] else: x = '0' if attrs.has_key('y'): y = attrs['y'] else: y = '0' x, y = self.user_point(x, y) width = self.user_length(attrs['width']) scalex = width / image.size[0] height = self.user_length(attrs['height']) scaley = -height / image.size[1] style = attrs.get('style', '') if style: self.parse_style(style) self.set_loader_style() t = self.trafo(Trafo(scalex, 0, 0, scaley, x, y + height)) self._print('image', t) self.loader.image(image, t) def begin_text(self, attrs): if self.in_defs: id = attrs.get('id', '') if id: self.named_objects[id] = ('object', 'text', attrs) return # parse the presentation attributes if any. # FIXME: this has to be implemented for the other elements that # can have presentation attributes as well. for key,value in attrs.items(): self.try_add_style(key,value) if attrs.has_key('x'): x = attrs['x'] else: x = '0' if attrs.has_key('y'): y = attrs['y'] else: y = '0' x, y = self.user_point(x, y) self.text_trafo = self.trafo(Trafo(1, 0, 0, -1, x, y)) self._print('text', self.text_trafo) style = attrs.get('style', '') if style: self.parse_style(style) self.set_loader_style(allow_font = 1) self.current_text='' def end_text(self): self.loader.simple_text(strip(self.current_text), self.text_trafo, halign = self.halign) def data(self, attrs): pass def begin_group(self, attrs): style = attrs.get('style', '') if style: self.parse_style(style) self.loader.begin_group() def end_group(self): try: self.loader.end_group() except EmptyCompositeError: pass def use(self, attrs): #print 'use', attrs.map if attrs.has_key('xlink:href'): name = attrs['xlink:href'] else: name = attrs.get('href', '') if name: data = self.named_objects.get(name[1:]) if data[0] == 'object': if attrs.has_key('style'): self.parse_style(attrs['style']) self.startElement(data[1], data[2]) self.endElement(data[1]) def begin_defs(self, attrs): self.in_defs = 1 def end_defs(self): self.in_defs = 0 class SVGLoader(GenericLoader): format_name = format_name def __init__(self, file, filename, match): GenericLoader.__init__(self, file, filename, match) if self.filename: self.directory = os.path.split(filename)[0] else: self.directory = '' def __del__(self): pass def Load(self): try: self.document() self.layer() error_handler = ErrorHandler() entity_resolver = EntityResolver() dtd_handler = DTDHandler() input = open(self.filename, "r") input_source = InputSource() input_source.setByteStream(input) xml_reader = xml.sax.make_parser() xml_reader.setContentHandler(SVGHandler(self)) xml_reader.setErrorHandler(error_handler) xml_reader.setEntityResolver(entity_resolver) xml_reader.setDTDHandler(dtd_handler) xml_reader.parse(input_source) input.close self.end_all() self.object.load_Completed() return self.object except: warn_tb('INTERNAL') raise class ErrorHandler(handler.ErrorHandler): pass class EntityResolver(handler.EntityResolver): pass class DTDHandler(handler.DTDHandler): pass UniConvertor-1.1.4/src/app/plugins/Filters/sksaver.py0000644000076400007640000003612510740000122021325 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1997, 1998, 1999, 2000 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ###Sketch Config #type = Export #tk_file_type = (_("Sketch Document"), '.sk') #extensions = '.sk' format_name = 'SK' #standard_messages = 1 ###End # # A class to save a document in SK format (Sketch's own format) # # # Functions used in an sk file: # # document() # # Marks the beginning of the document. In the future, this # function may take some arguments that describe global properties # of the document # # layout(PAPER_FORMAT, ORIENTATION) # # Defines the paper format. PAPER_FORMAT may be a string such 'A4' # or a tuple (WIDTH, HEIGHT) defining a non standard paper size. # ORIENTATION is 0 for portrait or 1 for landscape (see # pagelayout.py) # # # Properties: # # gl([(POS1, COLOR1), (POS2, COLOR2), ...]) # # `gradient linear' # # pe() # # `pattern empty' # # ps(COLOR) # # `pattern solid' # # pgl(DX, DY, BORDER) # # `pattern gradient linear' # # pgr(CX, CY, BORDER) # # `pattern gradient radial' # # pgc(CX, CY, DX, DY) # # `pattern gradient conical' # # phs(COLOR, BACKGROUND, DX, DY, GAP, WIDTH) # # `pattern hatching simple' # # pit(ID, TRAFO) # # `pattern image tile' (id as in `im') # # fp() `fill pattern' # # fe() `fill empty' # # ft(BOOL) `fill transform' # # lp() `line pattern' # # le() `line empty' # # lw() `line width' # # lc(CAP) # # `line cap' # # lj(JOIN) # # `line join' # # ld(DASHES) # # `line dashes' # # la1([ARROW]) # missing ARROW means explicit empty arrow # la2([ARROW]) # # `line arrows' # # Fn(FONTNAME) # # `font name' # # Fs(SIZE) # # `font size' # # dstyle(NAME) # # Define new dynamic style named NAME with the current properties # # style(name) # # Use style NAME. # # # Objects: # # layer(NAME, VISIBLE, PRINTABLE, LOCKED, OUTLINED, OUTLINE_COLOR) # # Start a new layer named NAME. # # grid((XORIG, YORIG, XWIDTH, YWIDTH)) # # Insert the grid layer at this position and set the grid geometry. # # guidelayer(NAME, ...) # # Start the guide layer # # guide(COORD, HOR) # # Define a guide line. If HOR is true, it is horizontal, else it # is vertical. guide() is only allowed in a guide layer # # G() # G_() # # All objects defined between G() and the corresponding G_() are # part of a group. Groups may be nested. # # B() # Bi(STEPS) # B_() # # A blend group # # # M() # M_() # # A Mask group. the first object after M() is the mask # # PT() # pt(TEXT[, MATRIX][, MODEL) # PT_() # # Text on a path. The path is the only object between the pt and # PT_ functions. # # # b() # start a bezier obj # bs(X, Y, CONT) append a line segment # bc(X1, Y1, X2, Y2, X3, Y3, CONT) append a bezier segment # bn() start a new path # bC() close path # # r(TRAFO [, RADIUS1, RADIUS2]) # # Rectangle, described by the transformation needed to transform # the unit square into the rectangle. # # e(TRAFO, [start_angle, end_angle, arc_type]) # # Ellipse, described similarly to the rectangle. # # txt(TEXT, TRAFO[, HORIZ_ALIGN, VERT_ALIGN]) # # bm(ID[, filename]) # # Bitmap image data. The bitmap data is either read from the file # given by filename of if that parameter is not present, follows # as a base64 encoded ppm file. (We should have compression here, # maybe by using png or a similar format) # # im(TRAFO, ID) # # A bitmap image. ID has to be the id of a previously defined # bitmap data object (defined by bm). # # eps(TRAFO, FILENAME) # # external EPS file # # # PC(NAME[, arg1, arg2, ...][, kwarg, kwarg, ...]) # PC_() # # A plugin compound object. The arguments to PC() should be # sufficient to describe the entire compound and to reconstruct # the objects in between PC and PC_. These contained objects are # meant for installations where the plugin is not available. # # Default properties in an sk-file: # # Fill Properties: # # fill_pattern EmptyPattern # fill_transform 1 # # Line Properties: # # line_pattern solid black # line_width 0pt # line_join JoinMiter # line_cap CapButt # line_dashes () # # Font Properties: # # font None # font_size 12pt # # import os from app.utils.os_utils import relpath, Empty from app import IdentityMatrix, EmptyPattern, SolidPattern, Style, \ StandardColors, SketchError, const from app.Graphics import properties from app.Lib.units import m_to_pt, in_to_pt base_style = Style() base_style.fill_pattern = EmptyPattern base_style.fill_transform = 1 base_style.line_pattern = SolidPattern(StandardColors.black) base_style.line_width = 0.0 base_style.line_join = const.JoinMiter base_style.line_cap = const.CapButt base_style.line_dashes = () base_style.line_arrow1 = None base_style.line_arrow2 = None base_style.font = None base_style.font_size = 12.0 papersizes = { # 'A0': (0.841 * m_to_pt, 1.189 * m_to_pt), # 'A1': (0.594 * m_to_pt, 0.841 * m_to_pt), # 'A2': (0.420 * m_to_pt, 0.594 * m_to_pt), 'A3': (0.297 * m_to_pt, 0.420 * m_to_pt), 'A4': (0.210 * m_to_pt, 0.297 * m_to_pt), 'A5': (0.148 * m_to_pt, 0.210 * m_to_pt), 'A6': (0.105 * m_to_pt, 0.148 * m_to_pt), 'A7': (0.074 * m_to_pt, 0.105 * m_to_pt), 'letter': (8.5 * in_to_pt, 11 * in_to_pt), 'legal': (8.5 * in_to_pt, 14 * in_to_pt), 'executive': (7.25 * in_to_pt, 10.5 * in_to_pt) } class SketchSaveError(SketchError): pass def color_repr(color): return '(%g,%g,%g)' % tuple(color.RGB()) default_options = {'full_blend' : 0} class SKSaver: def __init__(self, file, filename, kw): self.file = file self.filename = filename if self.filename: self.directory = os.path.split(filename)[0] else: self.directory = '' self.style_dict = {} self.write_header() options = default_options.copy() options.update(kw) self.options = apply(Empty, (), options) self.saved_ids = {} def __del__(self): self.Close() def Close(self): pass #if not self.file.closed: # self.file.close() def write_header(self): self.file.write('##Sketch 1 2\n') def BeginDocument(self): self.file.write('document()\n') def EndDocument(self): pass def BeginLayer(self, name, visible, printable, locked, outlined, color): self.file.write('layer(%s,%d,%d,%d,%d,%s)\n' % (`name`, visible, printable, locked, outlined, color_repr(color))) def EndLayer(self): pass def BeginGuideLayer(self, name, visible, printable, locked, outlined, color): self.file.write('guidelayer(%s,%d,%d,%d,%d,%s)\n' % (`name`, visible, printable, locked, outlined, color_repr(color))) EndGuideLayer = EndLayer def BeginGridLayer(self, geometry, visible, outline_color, name): self.file.write('grid((%g,%g,%g,%g),%d,%s,%s)\n' % (geometry + (visible, color_repr(outline_color), `name`))) EndGridLayer = EndLayer def PageLayout(self, format, width, height, orientation): if format and papersizes.has_key(format): self.file.write('layout(%s,%d)\n' % (`format`, orientation)) else: self.file.write('layout((%g,%g),%d)\n' % (width, height, orientation)) def BeginGroup(self): self.file.write('G()\n') def EndGroup(self): self.file.write('G_()\n') def Gradient(self, colors): write = self.file.write write('gl([') write_comma = 0 for pos, color in colors: if write_comma: write(',') else: write_comma = 1 write('(%g,%s)' % (pos, color_repr(color))) write('])\n') def EmptyPattern(self): self.file.write('pe()\n') def SolidPattern(self, color): self.file.write('ps(%s)\n' % color_repr(color)) def LinearGradientPattern(self, gradient, direction, border): gradient.SaveToFile(self) self.file.write('pgl(%g,%g,%g)\n' % (round(direction.x, 10), round(direction.y, 10), border)) def RadialGradientPattern(self, gradient, center, border): gradient.SaveToFile(self) self.file.write('pgr(%g,%g,%g)\n' % (center.x, center.y, border)) def ConicalGradientPattern(self, gradient, center, direction): gradient.SaveToFile(self) self.file.write('pgc(%g,%g,%g,%g)\n' % (tuple(center) + (round(direction.x, 10), round(direction.y, 10)))) def HatchingPattern(self, color, background, direction, distance, width): self.file.write('phs(%s,%s,%g,%g,%g,%g)\n' % (color_repr(color), color_repr(background), direction.x, direction.y, distance, width)) def ImageTilePattern(self, image, trafo, relative_filename = 1): self.write_image(image, relative_filename) self.file.write('pit(%d,(%g,%g,%g,%g,%g,%g))\n' % ((id(image),) + trafo.coeff())) def write_style(self, style): write = self.file.write if hasattr(style, 'fill_pattern'): pattern = style.fill_pattern if pattern is EmptyPattern: write('fe()\n') elif isinstance(pattern, SolidPattern): write('fp(%s)\n' % color_repr(pattern.Color())) else: pattern.SaveToFile(self) write('fp()\n') if hasattr(style, 'fill_transform'): write('ft(%d)\n' % style.fill_transform) if hasattr(style, 'line_pattern'): pattern = style.line_pattern if pattern is EmptyPattern: write('le()\n') elif isinstance(pattern, SolidPattern): write('lp(%s)\n' % color_repr(pattern.Color())) else: pattern.SaveToFile(self) write('lp()\n') if hasattr(style, 'line_width') : write('lw(%g)\n' % style.line_width) if hasattr(style, 'line_cap'): write('lc(%d)\n' % style.line_cap) if hasattr(style, 'line_join'): write('lj(%d)\n' % style.line_join) if hasattr(style, 'line_dashes'): write('ld(%s)\n' % `style.line_dashes`) if hasattr(style, 'line_arrow1'): if style.line_arrow1 is not None: write('la1(%s)\n' % `style.line_arrow1.SaveRepr()`) else: write('la1()\n') if hasattr(style, 'line_arrow2'): if style.line_arrow2 is not None: write('la2(%s)\n' % `style.line_arrow2.SaveRepr()`) else: write('la2()\n') if hasattr(style, 'font'): write('Fn(%s)\n' % `style.font.PostScriptName()`) if hasattr(style, 'font_size'): write('Fs(%g)\n' % style.font_size) def DynamicStyle(self, style): self.write_style(style) self.file.write('dstyle(%s)\n' % `style.Name()`) def write_style_no_defaults(self, style): style = style.Copy() for key, value in base_style.__dict__.items(): if hasattr(style, key) and getattr(style, key) == value: delattr(style, key) self.write_style(style) def Properties(self, properties): styles = properties.stack[:] styles.reverse() if styles[0].is_dynamic: self.file.write('style(%s)\n' % `style[0].Name()`) else: self.write_style_no_defaults(styles[0]) for style in styles[1:]: if style.is_dynamic: self.file.write('style(%s)\n' % `style.Name()`) else: self.write_style(style) def Rectangle(self, trafo, radius1 = 0, radius2 = 0): if radius1 == radius2 == 0: self.file.write('r(%g,%g,%g,%g,%g,%g)\n' % trafo.coeff()) else: self.file.write('r(%g,%g,%g,%g,%g,%g,%g,%g)\n' % (trafo.coeff() + (radius1, radius2))) def Ellipse(self, trafo, start_angle, end_angle, arc_type): if start_angle == end_angle: self.file.write('e(%g,%g,%g,%g,%g,%g)\n' % trafo.coeff()) else: self.file.write('e(%g,%g,%g,%g,%g,%g,%g,%g,%d)\n' % (trafo.coeff()+(start_angle,end_angle,arc_type))) def PolyBezier(self, paths): write = self.file.write write('b()\n') for path in paths: if path is not paths[0]: write('bn()\n') try: path.write_to_file(self.file) except TypeError: # self.file is no ordinary file (not tested!) list = path.get_save() for item in list: if len(item) == 3: write('bs(%g,%g,%d)\n' % item) elif len(item) == 7: write('bc(%g,%g,%g,%g,%g,%g,%d)\n' % item) else: raise SketchSaveError('got ivalid item: ' + `item`) if path.closed: write("bC()\n") def SimpleText(self, text, trafo, halign, valign): write = self.file.write write('txt(%s,' % `text`) if trafo.matrix() != IdentityMatrix: write('(%g,%g,%g,%g,%g,%g)' % trafo.coeff()) else: write('(%g,%g)' % (trafo.v1, trafo.v2)) if halign or valign: write(',%d,%d' % (halign, valign)) write(')\n') def write_image(self, image, relative_filename = 1): write = self.file.write if not self.saved_ids.has_key(id(image)): imagefile = image.Filename() if not imagefile: from streamfilter import Base64Encode write('bm(%d)\n' % id(image)) file = Base64Encode(self.file) image.image.save(file, 'PPM') file.close() write('-\n') else: if self.directory and relative_filename: imagefile = relpath(self.directory, imagefile) write('bm(%d,%s)\n' % (id(image), `imagefile`)) self.saved_ids[id(image)] = image def Image(self, image, trafo, relative_filename = 1): self.write_image(image, relative_filename) write = self.file.write write('im(') if trafo.matrix() != IdentityMatrix: write('(%g,%g,%g,%g,%g,%g)' % trafo.coeff()) else: write('(%g,%g)' % (trafo.v1, trafo.v2)) write(',%d)\n' % id(image)) def EpsFile(self, data, trafo, relative_filename = 1): write = self.file.write write('eps(') if trafo.matrix() != IdentityMatrix: write('(%g,%g,%g,%g,%g,%g)' % trafo.coeff()) else: write('(%g,%g)' % (trafo.v1, trafo.v2)) filename = data.Filename() if self.directory and relative_filename: filename = relpath(self.directory, filename) write(',%s)\n' % `filename`) def BeginBlendGroup(self): self.file.write('B()\n') def EndBlendGroup(self): self.file.write('B_()\n') def BeginBlendInterpolation(self, steps): self.file.write('blendinter(%d)\n' % steps) def EndBlendInterpolation(self): self.file.write('endblendinter()\n') def BlendInterpolation(self, steps): self.file.write('Bi(%d)\n' % steps) def BeginMaskGroup(self): self.file.write('M()\n') def EndMaskGroup(self): self.file.write('M_()\n') def BeginPathText(self): self.file.write('PT()\n') def InternalPathText(self, text, trafo, model, start_pos = 0): matrix = trafo.matrix() if matrix != IdentityMatrix: self.file.write('pt(%s,(%g,%g,%g,%g),%d' % ((`text`,) + matrix + (model,))) else: self.file.write('pt(%s,%d' % (`text`, model)) if start_pos > 0: self.file.write(',%g)\n' % start_pos) else: self.file.write(')\n') def EndPathText(self): self.file.write('PT_()\n') def GuideLine(self, point, horizontal): if horizontal: args = point.y, 1 else: args = point.x, 0 self.file.write('guide(%g,%d)\n' % args) def BeginPluginCompound(self, plugin_name, *args, **kw): write = self.file.write write('PC(%s' % `plugin_name`) for arg in args: write(',%s' % `arg`) for key, value in kw.items(): write(',%s=%s' % (key, `value`)) write(')\n') def EndPluginCompound(self): self.file.write('PC_()\n') def save(document, file, filename, options = {}): saver = SKSaver(file, filename, options) document.SaveToFile(saver) UniConvertor-1.1.4/src/app/plugins/Filters/pltsaver.py0000664000076400007640000001264211207631503021523 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2009 by Barabash Maxim # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ###Sketch Config #type = Export #tk_file_type = ("HPGL Plotter file", '.plt') #extensions = '.plt' format_name = 'PLT' #unload = 1 ###End __version__='0.1' from app import CreatePath, PolyBezier from app import Bezier, EmptyPattern, Point, Polar, Trafo from math import pi, cos, sin degrees = pi / 180.0 plt_options={ 'max_steps_across':'', 'max_steps_down':'', 'per_inch':'1016', 'first_command':'IN', 'final_command':'PU', 'move_command':'PU', 'draw_command':'PD', 'XY_separator':',', 'coordinate_separator':',', 'command_terminator':';', 'page_feed_command':'', 'page_terminator_command':'', 'name': 'Generic Cutter HPGL', 'output': '' } command={ 'rotation':'no', 'mirror':'no', 'remove_empty_lines':'no', 'origin':'no' } ########### PLS # Fault Tolerance for flattening Beziers. # If you find the curves not smooth enough, lower this value. EPS=0.5 def rndtoint(num): return int(round(num)) def cr(P1, P2): return P1.x * P2.y - P1.y * P2.x def FlattenPath(P0, P1, P2, P3): P4=(P0 + P1) / 2 P5=(P1 + P2) / 2 P6=(P2 + P3) / 2 P7=(P4 + P5) / 2 P8=(P5 + P6) / 2 P9=(P7 + P8) / 2 B=P3 - P0 S=P9 - P0 C1=P1 - P0 C2=P2 - P3 # I couldn't find an example of a flattening algorithm so I came up # with the following criteria for deciding to stop the approximation # or to continue. # if either control vector is larger than the base vector continue if abs(C1) > abs(B) or abs(C2) > abs(B): return FlattenPath(P0, P4, P7, P9) + FlattenPath(P9, P8, P6, P3) # otherwise if the base is smaller than half the fault tolerance stop. elif abs(B) < EPS / 2: return (P9, P3) else: # if neither of the above applies, check for the following conditions. # if one of them is true continue the approximation otherwise stop # # The first constrol vector goes too far before the base # The seconde control vector goes too far behind the base # Both control vectors lie on either side of the base. # The midpoint is too far from base. N=B.normalized() if ((C1 * N) < -EPS or (C2 * N) > EPS or cr(C1,B)*cr(C2,B) < 0 or abs(cr(N,S)) > EPS): return FlattenPath(P0, P4, P7, P9) + FlattenPath(P9, P8, P6, P3) else: return (P9, P3) class PLTSaver: def __init__(self, file, pathname, options=None): self.file=file self.pathname=pathname self.options={} self.options.update(plt_options) self.options.update(command) self.options.update(options) def write_headers(self): self.file.write(self.options['first_command']+self.options['command_terminator']) def write_terminator(self): self.file.write(self.options['final_command']+self.options['command_terminator']) def putpolyrec(self, seq): self.file.write(self.options['move_command']+str(seq[0])+','+str(seq[1])+self.options['command_terminator']) l=len(seq) i=2 while i < l: self.file.write(self.options['draw_command']+str(seq[i])+','+str(seq[i+1])+self.options['command_terminator']) i=2 + i def PathToSeq(self, Path): parlst=() p0=Point(0,0) for i in range(Path.len): type, control, p, cont=Path.Segment(i) if type==Bezier: p1, p2=control tmplst=FlattenPath(p0, p1, p2, p) for tp in tmplst: parlst=parlst + tuple(self.trafo(tp)) else: parlst=parlst + tuple(self.trafo(p)) p0=p return parlst def PolyBezier(self, Paths, Properties): line_pattern=Properties.line_pattern path=Paths[0] if line_pattern is EmptyPattern and self.options['remove_empty_lines']=='yes': pass else: for path in Paths: lst=self.PathToSeq(path) self.putpolyrec(map(rndtoint , lst)) def close(self): self.file.close() def SaveObjects(self, Objects): for object in Objects: if object.is_Compound: self.SaveObjects(object.GetObjects()) elif object.is_Bezier or object.is_Rectangle or object.is_Ellipse: self.PolyBezier(object.Paths(), object.Properties()) elif object.is_SimpleText: obj=object.AsBezier() self.PolyBezier(obj.Paths(), obj.Properties()) def SaveLayers(self, Layers): for layer in Layers: if not layer.is_SpecialLayer and layer.Printable(): self.SaveObjects(layer.GetObjects()) def SaveDocument(self, doc): left, bottom, right, top=doc.PageRect() inch=int(self.options['per_inch']) sc=inch / 72.0 self.trafo=Trafo(sc, 0, 0, sc, 0, bottom*sc) self.Scale=sc self.inch=inch self.extend=map(rndtoint, tuple(self.trafo(left,bottom)) + tuple(self.trafo(right,top))) # Header self.write_headers() self.SaveLayers(doc.Layers()) self.write_terminator() #end def save(document, file, filename, options={}): saver=PLTSaver(file, filename, options) saver.SaveDocument(document) saver.close() ######## END PLS UniConvertor-1.1.4/src/app/plugins/Filters/ccxloader.py0000644000076400007640000016045711042221211021621 0ustar igorigor# -*- coding: utf-8 -*- # Copyright (C) 2007 by Igor Novikov # Copyright (C) 1999, 2000 by Bernhard Herzog (CMX parsing) # # This library is covered by GNU General Public License v2.0. # For more info see COPYRIGHTS file in sK1 root directory. ###Sketch Config #type = Import #class_name = 'CCXLoader' #rx_magic = '(?s)RIFF....CDRX' #tk_file_type = ('Corel Compressed Presentation Exchange', '.ccx') #format_name = 'CDRX' #unload = 1 #standard_messages = 1 ###End # The regular expression must allow . to match all characters including # newline. # # Import Filter for CMX files # # Status: # # The filter has support for 16 bit (generated by Corel Draw 5) and 32 # bit (generated by Corel Draw 6 and later) CMX files. Only little # endian byte order is implemented. # # Supported for both variants: # # - Multipath Polybezier curves # - Uniform fills, gradient fills (linear for both, linear, radial and # conical for 32bit) # - Dashes # - Layers and groups # - Color models: RGB, Gray, CMYK, CMYK255 # # Supported only for 32bit files: # # - Images, incomplete. # - Containers a.k.a Powerclip in Corel Draw # - Arrowheads, incomplete # - Additional fills: color bitmap, monochrome bitmap (both incomplete) # # Unsupported as yet: # # - Color models: Pantone, HSB, and others # - Lenses. (there is some experimental support for magnifying lenses # but that's deactivated) # - Fills: texture, postscript pattern # - Text # # # The code here is really ugly. This is mostly due to Corel's CMX # documentation, which is very incomplete and in some cases incorrect, # so I have to reverse engineer some aspects of CMX files. Therefore the # code in here is still quite experimental and propably buggy. # from math import pi from struct import unpack, calcsize import struct import sys, types, struct, zlib, math from struct import unpack, calcsize from streamfilter import BinaryInput from app import CreatePath, ContSmooth, ContAngle, ContSymmetrical, \ SolidPattern, EmptyPattern, LinearGradient, RadialGradient, \ ConicalGradient, MultiGradient,\ CreateRGBColor, CreateCMYKColor, Trafo, Point, Polar, Translation, \ Scale, StandardColors, ImageTilePattern, ImageData, MaskGroup, \ Arrow from app.events.warn import INTERNAL, warn_tb from app.io.load import GenericLoader, SketchLoadError, EmptyCompositeError from app.Lib import units # # Generic RIFF stuff. # # Might be a good idea to put it into a separate module # RIFF = 'RIFF' LIST = 'LIST' compound_chunks = (RIFF, LIST) class RiffEOF(Exception): pass class ChunkHeader: def __init__(self, filepos, chunk_type, length, sub_type = ''): self.filepos = filepos self.chunk_type = chunk_type self.length = length self.sub_type = sub_type if sub_type: self.headersize = 12 self.data_length = self.length - 4 else: self.headersize = 8 self.data_length = self.length self.data_start = self.filepos + self.headersize self.has_subchunks = self.sub_type != '' self.subchunks = None def SetSubchunks(self, chunks): self.subchunks = chunks def read_chunk_header(file): # read the riff chunk header at the current position in file and # return a ChunkHeader instance containing the header data filepos = file.tell() data = file.read(8) if len(data) < 8: raise RiffEOF chunk_type, length = unpack('<4si', data) if length % 2 != 0: length = length + 1 if chunk_type in compound_chunks: sub_type = file.read(4) if len(sub_type) < 4: raise RiffEOF else: sub_type = '' return ChunkHeader(filepos, chunk_type, length, sub_type) # # CMX specific stuff # struct_cmxheader_start = ('32s' # Id '16s' # OS '4s' # ByteOrder, 2 little, 4 big endian '2s' # coord size, 2 = 16bit, 4 = 32bit '4s' # major version '4s' # minor version ) struct_cmxheader_end = ('<' 'H' # Unit, 35 = mm, 64 = inch 'd' # factor 'xxxx' # option, unused 'xxxx' # foreign key, unused 'xxxx' # capability, unused 'l' # index section, offset 'l' # info section, offset 'l' # thumbnail, offset (the docs differ here) 'l' # bb_left 'l' # bb_top 'l' # bb_right 'l' # bb_bottom 'l' # tally '64x' # reserved ) color_models = ('Invalid', 'Pantone', 'CMYK', 'CMYK255', 'CMY', 'RGB', 'HSB', 'HLS', 'BW', 'Gray', 'YIQ255', 'LAB') color_bytes = (0, 4, 4, 4, 4, 3, 4, 4, 1, 4, 1, 4) color_palettes = ('Invalid', 'Truematch', 'PantoneProcess', 'PantoneSpot', 'Image', 'User', 'CustomFixed') cmx_commands = { 88: 'AddClippingRegion', 94: 'AddGlobalTransform', 22: 'BeginEmbedded', 13: 'BeginGroup', 11: 'BeginLayer', 9: 'BeginPage', 99: 'BeginParagraph', 17: 'BeginProcedure', 72: 'BeginTextGroup', 70: 'BeginTextObject', 20: 'BeginTextStream', 101:'CharInfo', 102:'Characters', 90: 'ClearClipping', 2: 'Comment', 69: 'DrawImage', 65: 'DrawChars', 66: 'Ellipse', 23: 'EndEmbedded', 14: 'EndGroup', 12: 'EndLayer', 10: 'EndPage', 100:'EndParagraph', 18: 'EndSection', 73: 'EndTextGroup', 71: 'EndTextObject', 21: 'EndTextStream', 111:'JumpAbsolute', 67: 'PolyCurve', 92: 'PopMappingMode', 104:'PopTint', 91: 'PushMappingMode', 103:'PushTint', 68: 'Rectangle', 89: 'RemoveLastClippingRegion', 95: 'RestoreLastGlobalTransfo', 85: 'SetCharStyle', 93: 'SetGlobalTransfo', 86: 'SimpleWideText', 98: 'TextFrame' } class Outline: def __init__(self, style, screen, color, arrowheads, pen, dashes): self.style = style self.screen = screen self.color = color self.arrowheads = arrowheads self.pen = pen self.dashes = dashes class CCXFile: def __init__(self, loader, file): self.file = file self.loader = loader self.tagged = 0 self.colors = [] self.screens = [] self.dashes = [] self.pens = [] self.line_styles = [] self.procindex = [None,] self.bitmapindex = [None,] self.embeddedindex = [None,] self.arrowindex = [None,] self.verbosity = 0 self.angle_factor = 1 self.pages = [] def warn(self, message): self.loader.add_message(message) def _print(self, format, *args, **kw): if self.verbosity: if kw: text = format % kw elif args: text = format % args else: text = format sys.stderr.write(text) def read_header(self): self.file.seek(0) self.riff_header = h = read_chunk_header(self.file) self._print('%6d %s %s %d\n', h.filepos, h.chunk_type, h.sub_type, h.data_length) def read_subchunks(self, header, indent = 1): bytesread = 0 chunks = [] self.file.seek(header.data_start) while bytesread < header.data_length: subheader = read_chunk_header(self.file) bytesread = bytesread + subheader.headersize self._print('%6d %s%s %s %d\n', subheader.filepos, indent * ' ', subheader.chunk_type, subheader.sub_type, subheader.data_length) if subheader.sub_type != '': subchunks = self.read_subchunks(subheader, indent + 1) subheader.SetSubchunks(subchunks) self.file.seek(subheader.data_start + subheader.data_length) bytesread = bytesread + subheader.data_length chunks.append(subheader) return chunks def read_16(self): read = self.file.read lo, hi = read(2) value = ord(lo) + 256 * ord(hi) if value > 0x7FFF: value = value - 0x010000 return value def read_32(self): data = self.file.read(4) return int(unpack('> 4 cap = capjoin & 0x0f styles.append((spec, cap, join)) self._print('%3d ', len(styles)) self.print_linestyle((spec, cap, join)) def read_linestyles(self, chunk): self._print('Line Styles\n-----------\n') styles = [None,] self.file.seek(chunk.data_start) count = self.read_16() self._print('%d %s\n', count, 'line styles') if self.tagged: for i in range(count): tag = -1 while tag != 255: tag, data = self.read_tag((1,)) if tag == 1: self.append_linestyle(styles, data) else: read = self.file.read for i in range(count): self.append_linestyle(styles, self.file.read(2)) self._print('\n') return styles def append_arrowhead(self, heads, data): head1, head2 = unpack(' 64: self.file.seek(start_pos + palette_offset) palette_type, length = unpack('\n', tag, size) self.push_source(self.source.subfile(size - 3)) else: self._print('\n', tag) return tag def skip_tags(self): # skip tags until end tag tag = -1 while tag != 255: tag = ord(self.source.read(1)) if tag != 255: size = self.get_int16() self.source.seek(self.source.tell() + size - 3) # def Run(self, data, data_start): if self.cmxfile.byte_order == 2: # little endian byte_order = 0 else: byte_order = 1 self.push_source(BinaryInput(data, byte_order, self.cmxfile.coord_size)) get_int16 = self.get_int16 get_int32 = self.get_int32 try: while self.source.tell() < len(data): p = self.pos length = get_int16() if length < 0: length = get_int32() - 4 code = abs(get_int16()) # for some reason the codes are # negative in CMX1 command = cmx_commands.get(code) self._print('%-20s(%3d) %d\n', command, code, length) if command is not None and hasattr(self, command): try: try: self.push_source(self.source.subfile(length - 4)) jump = getattr(self, command)() finally: self.pop_source() if jump: self.source.seek(jump - data_start) except SketchLoadError: raise except: warn_tb(INTERNAL, "Exception in CMX command") else: self.source.seek(self.source.tell() + length - 4) finally: self.pop_source() def execute_procedure(self, reference): reflist, offset = self.cmxfile.procindex[reference] file = self.cmxfile.file file.seek(offset) chunk = read_chunk_header(file) if chunk.chunk_type in ('pvtr', 'pctn', 'proc'): data = file.read(chunk.data_length) self.loader.begin_group() try: self.Run(data, chunk.data_start) finally: self.loader.end_group() elif chunk.chunk_type == 'plns': # lens data = file.read(14) parent, page, parent_ref, start, end = unpack('> 6 if type == 0: if close and path is not None: path.load_close(1) close = node & 0x08 path = CreatePath() paths.append(path) path.AppendLine(p, (node & 0x30) >> 4) elif type == 1: path.AppendLine(p, (node & 0x30) >> 4) elif type == 3: path.AppendBezier(p, points[i + 1], points[i + 2], (nodes[i + 2] & 0x30) >> 4) i = i + 2 i = i + 1 if close: path.load_close(1) for i in range(len(paths) - 1, -1, -1): path = paths[i] if path.len == 0: #print 'empty path %d deleted' % i del paths[i] if paths: self.loader.bezier(tuple(paths)) else: self.get_prop_stack() def JumpAbsolute(self): return self.get_int32() class CMXInterpreter32(CMXInterpreter): def BeginPage(self): fmt = ' % 10s: %s\n' tag = -1 while tag != 255: tag = self.get_tag() if tag != 255: try: if tag == 1: read_struct = self.source.read_struct page_number, flags = read_struct('hi') bbox = read_struct('iiii') endpage, group_count, tally = read_struct('ihi') self._print(fmt, 'PageNumber', page_number) self._print(fmt, 'flags', flags) self._print(fmt, 'GroupCount:', group_count) self._print(fmt, 'Tally.', tally) elif tag == 2: matrix = self.get_matrix() self._print(fmt, 'Matrix', matrix) elif tag == 3: flag = self.get_boolean() if flag: old = self.get_rectangle() new = self.get_rectangle() if flag: self._print(fmt, 'mapping', '') self._print(fmt, 'old rect', old) self._print(fmt, 'new rect', new) else: self._print(' no mapping\n') finally: self.pop_source() def BeginLayer(self): fmt = ' % 11s: %s\n' tag = -1 while tag != 255: tag = self.get_tag() if tag != 255: try: if tag == 1: page_number, layer_number, flags, tally \ = self.source.read_struct('hhll') layer_name = self.get_string() self._print(fmt, 'LayerName', layer_name) self._print(fmt, 'LayerNumber', layer_number) self._print(fmt, 'PageNumber', page_number) self._print(fmt, 'flags', flags) elif tag == 2: matrix = self.get_matrix() self._print(fmt, 'Matrix', matrix) elif tag == 3: flag = self.get_boolean() if flag: old = self.get_rectangle() new = self.get_rectangle() if flag: self._print(fmt, 'mapping', '') self._print(fmt, 'old rect', old) self._print(fmt, 'new rect', new) else: self._print(' no mapping\n') finally: self.pop_source() # start layer self.loader.layer(self.layer_prefix + layer_name, 1, 1, 0, 0, ('RGB',0,0,0)) def BeginGroup(self): fmt = ' % 10s: %s\n' tag = -1 while tag != 255: tag = self.get_tag() if tag == 1: try: bbox = self.get_rectangle() group_count, end, tally = self.source.read_struct('hll') self._print(fmt, 'Bound.Box', bbox) self._print(fmt, 'GroupCount', group_count) self._print(fmt, 'Tally', tally) finally: self.pop_source() # start group self.loader.begin_group() def get_rendering_attrs(self): self._print(' Rendering Attributes:\n') style = self.loader.style mask = self.get_byte() if mask & 0x01: # fill attrs self._print(' Fill:') self.get_fill(style) else: style.fill_pattern = EmptyPattern if mask & 0x02: # line attrs self.get_outline(style) else: style.line_pattern = EmptyPattern if mask & 0x04: # lens attributes self.warn(_("Lens specification ignored")) self.skip_tags() #self.get_lens() if mask & 0x08: # canvas (XXX what is that, actually?) self.warn(_("Canvas specification ignored")) self.skip_tags() # ? if mask & 0x10: # container #self.warn("Container specification ignored") stack = self.loader.get_prop_stack() self.get_container() self.loader.set_prop_stack(stack) def get_fill(self, style): tag = -1 while tag != 255: tag = self.get_tag() try: if tag == 1: fill = self.get_int16() if fill == 0: # no fill self._print('no fill\n') style.fill_pattern = EmptyPattern elif fill == 1: # uniform self.get_uniform_fill(style) elif fill == 2: # fountain (gradient) self.get_fountain_fill(style) elif fill == 7: # monochrome bitmap 1 (according to cmxbrowser) self.get_monochrome_fill(style) elif fill == 9: # color bitmap self.get_colorbitmap_fill(style) elif fill == 11: # texture self.warn(_("Texture fill not implemented, " "using solid black")) style.fill_pattern = SolidPattern(StandardColors.black) else: self.warn(_("fill type %d not implemented, " "using solid black") % fill) style.fill_pattern = SolidPattern(StandardColors.black) finally: if tag != 255: self.pop_source() def get_uniform_fill(self, style): tag = -1 while tag != 255: tag = self.get_tag() try: if tag == 1: color, screen = self.source.read_struct('hh') self._print('uniform %s %s, screen %s\n', color, self.cmxfile.colors[color], screen) color = self.cmxfile.colors[color] style.fill_pattern = SolidPattern(color) finally: if tag != 255: self.pop_source() def get_fountain_fill(self, style): tag = -1 while tag != 255: tag = self.get_tag() try: if tag == 1: fountain, screen, padding = self.source.read_struct('hhh') angle = self.get_angle() xoff, yoff, steps, mode, rate_method, rate_value \ = self.source.read_struct('iihhhh') self._print('fountain: %s pad %d angle %f off %d %d\n', ('linear','radial','conical','square')[fountain], padding, angle, xoff, yoff) self._print(' steps %d mode %s\n', steps, ('RGB', 'HSB_CW', 'HSB_CCW', 'Custom')[mode]) self._print(' rate %d value %d\n', rate_method, rate_value) elif tag == 2: color_count = self.get_int16() colors = [] for i in range(color_count): color, pos = self.source.read_struct('hh') color = self.cmxfile.colors[color] colors.append((pos / 100.0, color)) self._print(' colors %s\n', colors) if mode == 0 and rate_value != 50 and len(colors) == 2: # len(colors) should always be 2 for mode != 3 start = colors[0][1] end = colors[1][1] colors.insert(1, (rate_value / 100.0, start.Blend(end, 0.5, 0.5))) gradient = MultiGradient(colors) border = padding / 50.0 center = Point(xoff / 100.0 + 0.5, yoff / 100.0 + 0.5) if fountain == 0: pattern = LinearGradient(gradient, -Polar(angle), border = border) elif fountain == 1: pattern = RadialGradient(gradient, center, border = border) elif fountain == 2: pattern = ConicalGradient(gradient, center, -Polar(angle)) else: # probably a square gradient which sketch doesn't have # use a radial gradient instead self.warn(_("Substituting radial gradient for square " "gradient")) pattern = RadialGradient(gradient, center, border = border) style.fill_pattern = pattern finally: if tag != 255: self.pop_source() def get_monochrome_fill(self, style): tag = -1 while tag != 255: tag = self.get_tag() try: if tag == 1: bitmap = self.get_int16() self._print('twocolor: bitmap %d\n', bitmap) image = self.cmxfile.GetBitmap(bitmap) if image.mode != '1': raise SketchLoadError(_("Image for twocolor fill is " "not 1 bit deep")) width, height, xoff, yoff, inter, flags = self.get_tiling() background, foreground, screen \ = self.source.read_struct('hhh') self._print(' foreground %s, background %s,' ' screen %d\n', self.cmxfile.colors[foreground], self.cmxfile.colors[background], screen) foreground = self.cmxfile.colors[foreground] background = self.cmxfile.colors[background] image = image.convert('L') pal = [0] * 768 pal[0] = background.red * 255 pal[1] = background.green * 255 pal[2] = background.blue * 255 pal[765] = foreground.red * 255 pal[766] = foreground.green * 255 pal[767] = foreground.blue * 255 image.putpalette(pal, 'RGB') image = image.convert('RGB') trafo = Trafo(width / image.size[0], 0, 0, height / image.size[1], 0, 0) style.fill_pattern = ImageTilePattern(ImageData(image), trafo = trafo) self.loader.fix_tile = xoff, yoff finally: if tag != 255: self.pop_source() def get_colorbitmap_fill(self, style): tag = -1 while tag != 255: tag = self.get_tag() try: if tag == 1: procedure = self.get_int16() self._print('Color image procedure %d\n', procedure) width, height, xoff, yoff, inter, flags = self.get_tiling() stack = self.loader.get_prop_stack() self.execute_procedure(procedure) self.loader.set_prop_stack(stack) group = self.loader.pop_last() image = group[0] image_data = image.Data() trafo = Trafo(width / image_data.size[0], 0, 0, height / image_data.size[1], 0, 0) trafo = trafo(image.Trafo()) pattern = ImageTilePattern(image_data, trafo = trafo) self.loader.style.fill_pattern = pattern elif tag != 255: self._print('Unknown tag %d\n', tag) self._print(`self.source.stream` + '\n') finally: if tag != 255: self.pop_source() def get_tiling(self): tiling = None tag = -1 while tag != 255: tag = self.get_tag() try: if tag == 1: width, height = self.source.read_struct('ii') width, height = self.trafo.DTransform(width, height) xoff, yoff, inter, flags = self.source.read_struct('hhhh') self._print(' size (%d, %d), off (%d, %d), inter %d, ' 'flags %x\n', width, height, xoff, yoff, inter, flags) tiling = width, height, xoff, yoff, inter, flags elif tag != 255: self._print('Unknown tag %d\n', tag) self._print(`self.source.stream` + '\n') finally: if tag != 255: self.pop_source() return tiling def get_outline(self, style): tag = -1 while tag != 255: tag = self.get_tag() try: if tag == 1: outline = self.get_int16() outline = self.cmxfile.outlines[outline] spec, cap, join = outline.style if spec & 1: # the none bit is set. no outline style.line_pattern = EmptyPattern else: style.line_pattern = SolidPattern(outline.color) style.line_width = abs(outline.pen[0] * self.factor) style.line_cap = cap + 1 style.line_join = join self._print('Arrow heads %d\n', outline.arrowheads) heads = self.cmxfile.arrow_heads[outline.arrowheads] style.line_arrow1 = self.get_arrow(heads[0]) style.line_arrow2 = self.get_arrow(heads[1]) if spec & 0x04: # dash dot style.line_dashes = outline.dashes else: style.line_dashes = () # XXX whats the precise meaning of the dash-dot flag and # the solid outline flag? finally: if tag != 255: self.pop_source() def get_arrow(self, index): if index == 0: return None offset = self.cmxfile.arrowindex[index] if type(offset) == types.InstanceType: return offset file = self.cmxfile.file file.seek(offset) data = file.read(3) tag, length = unpack('> 6 if type == 0: if close and path is not None: path.load_close(1) close = node & 0x08 path = CreatePath() paths.append(path) path.AppendLine(p, (node & 0x30) >> 4) elif type == 1: path.AppendLine(p, (node & 0x30) >> 4) elif type == 3: path.AppendBezier(p, points[i + 1], points[i + 2], (nodes[i + 2] & 0x30) >> 4) i = i + 2 i = i + 1 if close: path.load_close(1) for i in range(len(paths) - 1, -1, -1): path = paths[i] if path.len == 0: #print 'empty path %d deleted' % i del paths[i] return tuple(paths) def PolyCurve(self): # We do the tag handling 'manually' here because the file format # is broken for very long point lists. The CMX format has # provisisions for very long point lists at the command level # where there's a way to use 32 bit sizes instead of the normal # 16 bit but this doesn't work at the tag level. In the case of # the PolyCurve command the only problematic tag is probably the # tag with id 2 containing the point list so we have to ignore # the tag size for that. paths = () while 1: tag = ord(self.source.read(1)) if tag == 255: self._print('\n', tag) break else: size = self.get_uint16() self._print('\n', tag, size) if tag != 2: # don't push for the points as the size may not be # accurate. For very long point lists the size of # the tag is > 2**16 and wraps around self.push_source(self.source.subfile(size - 3)) pop = 1 else: pop = 0 try: if tag == 1: self.get_rendering_attrs() elif tag == 2: paths = self.read_pointlist() elif tag == 3: # ignore bounding box pass elif tag != 255: self._print('PolyCurve: Unknown tag %d\n', tag) #self._print(`self.source.stream` + '\n') finally: if pop: self.pop_source() if paths: self.loader.bezier(tuple(paths)) else: self.get_prop_stack() def DrawImage(self): tag = -1 while tag != 255: tag = self.get_tag() try: if tag == 1: self.get_rendering_attrs() elif tag == 2: left, top, right, bottom = self.get_rectangle() crop = self.get_rectangle() matrix = self.get_matrix() image_type, image1, image2 = self.source.read_struct('hhh') real_width = self.factor * abs(right - left) real_height = self.factor * abs(top - bottom) self._print('extent %gx%g, crop %s\n', real_width, real_height, crop) self._print('matrix %s\n', matrix) self._print('type %d, references %d, %d\n', image_type, image1, image2) image = self.cmxfile.GetImage(image1) if image is not None: width, height = image.size trafo = Trafo(real_width / float(width), 0, 0, real_height / float(height), 0, 0) trafo = apply(Trafo, matrix[:4])(trafo) trafo = Translation(self.trafo(matrix[-2:]))(trafo) self.loader.image(image, trafo) elif tag != 255: self._print('Unknown tag %d\n', tag) self._print(`self.source.stream` + '\n') finally: if tag != 255: self.pop_source() def JumpAbsolute(self): tag = -1 while tag != 255: tag = self.get_tag() try: if tag == 1: return self.get_int32() finally: if tag != 255: self.pop_source() class CCXLoader(GenericLoader): def __init__(self, file, filename, match): GenericLoader.__init__(self, file, filename, match) self.fix_tile = None self.filename =filename self.fix_clip = 0 self.fix_lens = () def Load(self): try: self.file=CDRXfile(self.filename) cmx = CCXFile(self, self.file) cmx.Load() self.document() prefix = '' num_pages = cmx.NumPages() for num in range(num_pages): data, start = cmx.PageData(num) if data: if num_pages > 1: prefix = 'Page %d: ' % num if cmx.coord_size == 2: interpreter = CMXInterpreter16(self, cmx, layer_prefix = prefix) else: interpreter = CMXInterpreter32(self, cmx, layer_prefix = prefix) interpreter.Run(data, start) self.end_all() self.object.load_Completed() return self.object except RiffEOF: raise SketchLoadError(_("Unexpected end of file")) except: import traceback traceback.print_exc() raise def append_object(self, object): if self.fix_tile: if object.has_fill: pattern = object.Properties().fill_pattern if pattern.is_Tiled: width, height = pattern.data.size trafo = pattern.trafo y = height * trafo.m22 * self.fix_tile[1] / 100.0 rect = object.coord_rect pattern.Transform(Translation(0, rect.bottom-rect.top + y)) self.fix_tile = None if self.fix_clip: group = self.pop_last() if group is not None and group.is_Group: objects = group.GetObjects() objects.insert(0, object) object = MaskGroup(objects) self.fix_clip = 0 if self.fix_lens: group = self.pop_last() if group is not None and group.is_Group: lens = self.fix_lens[0] if lens == 2: rate, viewpoint = self.fix_lens[1:] center = object.coord_rect.center() trafo = Translation(-viewpoint) trafo = Translation(center)(Scale(rate)(trafo)) group.Transform(trafo) objects = group.GetObjects() objects.insert(0, object) object = MaskGroup(objects) self.fix_lens = () GenericLoader.append_object(self, object) class CDRXfile: file_content='' point=0 def __init__(self, filename): f = open(filename, 'rb') buf = f.read() f.close() cdrx = RiffChunk() cdrx.load(buf) self.construct_file(cdrx) def construct_file(self, cdrx): for chunk in cdrx.contents: if chunk.fourcc=='pack': pass else: [rawsize] = struct.unpack(' 0.0 and height > 0.0: self.object.load_SetLayout( pagelayout.PageLayout(width = width, height = height) ) else: self.object.load_SetLayout(self.page_layout) self.object.load_Completed() # Return the document return self.object 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 textobj=SimpleText() width = width + use_font.TextCoordBox(text, font_size, textobj.properties)[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, textobj.properties)[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, textobj.properties)[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, textobj.properties)[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, textobj.properties)[2] # Can't position cursor? if cursor == [None, None]: break # Remember this command last_command = command # Render any remaining text if line != [] and cursor != [None, None]: # Write current line self.ta_write_line(align, cursor, line, 0) else: pass # Return the number of recognised objects return n_objects def ta_write_line(self, align, cursor, line, wrapped): # print 'ta_write_line:', align, cursor, margins textobj=SimpleText() if line == [] or cursor == [None, None]: return # Remove leading and trailing spaces if line[0][0] == ' ': line = line[1:] if line != [] and line[-1][0] == ' ': line = line[:-1] if line == []: return # Depending on the justification of the text, either just write the # text to the page striaght from the line list (L, R, C) or space it # out appropriately (D) if align == 'L': # Left justification cursor[0] = self.margins[0] for word, font_name, font_size, font_width, fg, bg in line: # Set the font name self.style.font = RISCOSFont(font_name) # Set the font size self.style.font_size = font_size # Set the text colour self.style.line_pattern = bg self.style.fill_pattern = fg # Determine the horizontal position of the next word next = cursor[0] + self.style.font.TextCoordBox(word, font_size, textobj.properties)[2] # Write the text to the page self.simple_text(word, Translation(cursor)) # Reposition the cursor cursor[0] = next elif align == 'R': # Right justification line.reverse() cursor[0] = self.margins[1] for word, font_name, font_size, font_width, fg, bg in line: # Set the font name self.style.font = RISCOSFont(font_name) # Set the font size self.style.font_size = font_size # Set the text colour self.style.line_pattern = bg self.style.fill_pattern = fg # Determine the horizontal position of the this word cursor[0] = cursor[0] - self.style.font.TextCoordBox(word, font_size, textobj.properties)[2] # Write the text to the page self.simple_text(word, Translation(cursor)) elif align == 'C': # Centred text # Determine the total width of the line total_width = 0.0 for word, font_name, font_size, font_width, fg, bg in line: # Set the font self.style.font = RISCOSFont(font_name) # Increase the width total_width = total_width + self.style.font.TextCoordBox(word, font_size, textobj.properties)[2] # Place the cursor at a suitable place and render the text as if it # was left justified cursor[0] = self.margins[0] + (self.margins[1] - self.margins[0] - total_width)/2.0 for word, font_name, font_size, font_width, fg, bg in line: # Set the font name self.style.font = RISCOSFont(font_name) # Set the font size self.style.font_size = font_size # Set the text colour self.style.line_pattern = bg self.style.fill_pattern = fg # Determine the horizontal position of the next word next = cursor[0] + self.style.font.TextCoordBox(word, font_size, textobj.properties)[2] # Write the text to the page self.simple_text(word, Translation(cursor)) # Reposition the cursor cursor[0] = next elif align == 'D' and wrapped == 1: # Text is wrapped due to an overflow # Double (full) justification # Take the width of each word which is not a space and create a # total width. # Subtract this from the column width and divide the remainder by # the number of spaces that should occur. # Also, remove the spaces from the list by creating a new list. total_width = 0.0 new_line = [] for word, font_name, font_size, font_width, fg, bg in line: if word != '': # Set the font self.style.font = RISCOSFont(font_name) # Increase the width total_width = total_width + self.style.font.TextCoordBox(word, font_size, textobj.properties)[2] # Add this word to the new list new_line.append( (word, font_name, font_size, font_width, fg, bg) ) # If there are no words then return to the caller if len(new_line) == 0: return # Determine the spacing required between each word if len(new_line) > 1: spacing = (self.margins[1] - self.margins[0] - total_width) / (len(new_line) - 1) else: spacing = 0.0 # Place the cursor and render the new line cursor[0] = self.margins[0] for word, font_name, font_size, font_width, fg, bg in new_line: # Set the font name self.style.font = RISCOSFont(font_name) # Set the font size self.style.font_size = font_size # Set the text colour self.style.line_pattern = bg self.style.fill_pattern = fg # Determine the horizontal position of the end of this word next = cursor[0] + self.style.font.TextCoordBox(word, font_size, textobj.properties)[2] # Write the text to the page self.simple_text(word, Translation(cursor)) # Reposition the cursor cursor[0] = next + spacing elif align == 'D' and wrapped == 0: # Text is not wrapped due to an overflow # Left justification cursor[0] = self.margins[0] for word, font_name, font_size, font_width, fg, bg in line: # Set the font name self.style.font = RISCOSFont(font_name) # Set the font size self.style.font_size = font_size # Set the text colour self.style.line_pattern = bg self.style.fill_pattern = fg # Determine the horizontal position of the next word next = cursor[0] + self.style.font.TextCoordBox(word, font_size, textobj.properties)[2] # Write the text to the page self.simple_text(word, Translation(cursor)) # Reposition the cursor cursor[0] = next else: # Can't align the text return def ta_new_line(self, cursor, object, vspacing): # Don't try to move to a new line if the cursor is undefined. if cursor == [None, None]: return [None, None] if (cursor[1] - vspacing) < self.column_base: # If below the column base then try to move to the next column cursor = self.ta_next_column(cursor, object) # Any more columns? if cursor != [None, None]: # Reset cursor to leftmost margin cursor = [self.margins[0], cursor[1] - self.linespacing] else: # Move cursor down by the specified amount cursor[1] = cursor[1] - vspacing return cursor def ta_next_column(self, cursor, object): # Try to move to the next column if self.column < (self.columns - 1): # Go to next column self.column = self.column + 1 self.margins = [(object.columns[self.column].x1 / scale) + \ self.margin_offsets[0], (object.columns[self.column].x2 / scale) - \ self.margin_offsets[1]] cursor = [self.margins[0], object.columns[column].y2 / scale] self.column_base = self.columns[self.column].y1 / scale else: # No more columns to go to #print 'No more columns' cursor = [None, None] return cursor def ta_set_colour(self, colour): if tuple(colour) == (255, 255, 255): return EmptyPattern else: return SolidPattern( CreateRGBColor( float(colour[0]) / 255.0, float(colour[1]) / 255.0, float(colour[2]) / 255.0 ) ) def make_safe(self, s): new = '' for i in s: if ord(i) >= 32: new = new + i return new def relative(self, x, y): # return (x - self.drawfile.x1)/scale, (y - self.drawfile.y1)/scale return x/scale, y/scale UniConvertor-1.1.4/src/app/plugins/Filters/cgmsaver.py0000644000076400007640000003341110740000121021450 0ustar igorigor# Sketch - A Python-based interactive drawing program # Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003 by Bernhard Herzog # This CGMsaver mostly by Antoon Pardon (2002) # # This library is free software; you can redistribute it and/or modify # it under the terms of the GNU Library General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # # $Id: cgmsaver.py,v 1.1.2.4 2003/05/17 18:22:14 bherzog Exp $ ###Sketch Config #type = Export #tk_file_type = ("Computer Graphics Metafile (CGM)", '.cgm') #extensions = '.cgm' format_name = 'CGM' #unload = 1 ###End import struct import os.path from app import Scale, Translation, Bezier, CreateRGBColor, EmptyPattern, \ Point import app.events.warn from math import sqrt, sin, cos # Fault Tolerance for flattening Beziers. # If you find the curves not smooth enough, lower this value. EPS = 2 def rndtoint(num): return int(round(num)) def cr(P1, P2): return P1.x * P2.y - P1.y * P2.x def FlattenPath(P0, P1, P2, P3): P4 = (P0 + P1) / 2 P5 = (P1 + P2) / 2 P6 = (P2 + P3) / 2 P7 = (P4 + P5) / 2 P8 = (P5 + P6) / 2 P9 = (P7 + P8) / 2 B = P3 - P0 S = P9 - P0 C1 = P1 - P0 C2 = P2 - P3 # I couldn't find an example of a flattening algorithm so I came up # with the following criteria for deciding to stop the approximation # or to continue. # if either control vector is larger than the base vector continue if abs(C1) > abs(B) or abs(C2) > abs(B): return FlattenPath(P0, P4, P7, P9) + FlattenPath(P9, P8, P6, P3) # otherwise if the base is smaller than half the fault tolerance stop. elif abs(B) < EPS / 2: return (P9, P3) else: # if neither of the above applies, check for the following conditions. # if one of them is true continue the approximation otherwise stop # # The first constrol vector goes too far before the base # The seconde control vector goes too far behind the base # Both control vectors lie on either side of the base. # The midpoint is too far from base. N = B.normalized() if (C1 * N) < -EPS or (C2 * N) > EPS or cr(C1,B)*cr(C2,B) < 0 \ or abs(cr(N,S)) > EPS: return FlattenPath(P0, P4, P7, P9) + FlattenPath(P9, P8, P6, P3) else: return (P9, P3) class Incr: def __init__(self, Base = 0): self.Value = Base def __call__(self, delta = 1): Result = self.Value self.Value = self.Value + delta return Result WarnTable = ["- CGM has no Bezier curves. These are flattened\n\n" , "- CGM has no Gradients. These are filled black\n\n" , "- cgmaver doesn't handle Hatchings. These are filled black\n\n" , "- cgmaver doesn't handle Tiles. These are filled black\n\n" , "- cgmaver doesn't handle images. These are omitted\n\n" , "- cgmaver doesn't save text. Text is converted\n to Bezier curves"] Next = Incr() W_Bezier = Next() W_Gradient = Next() W_Hatched = Next() W_Tiled = Next() W_Image = Next() W_Text = Next() class Warnings: def __init__(self): self.set = 0 def Incl(self, num): self.set = self.set | 1 << num def show(self): inx = 0 msg = "" while self.set != 0: if self.set % 2 == 1: msg = msg + WarnTable[inx] inx = inx + 1 self.set = self.set >> 1 if msg != "": msg = "The design you tried to save in CGM Version 1 format\n" + \ "hit some limitations\n\n" + msg app.events.warn.warn(app.events.warn.USER , msg) class CGMSaver: def __init__(self, file, pathname, options): self.file = file self.pathname = pathname self.options = options self.white = CreateRGBColor(1.0 , 1.0 , 1.0) self.black = CreateRGBColor(0.0 , 0.0 , 0.0) self.Msg = Warnings() def w(self, str): self.file.write(str) def pack(self , *args): self.file.write(apply(struct.pack , args)) def putstr(self, Id , str): lng = len(str) if lng < 30: self.pack("!H" , Id | (lng + 1)) else: self.pack("!H" , Id | 31) self.pack("!H" , lng + 1) self.pack("!B" , lng) fmt = '!' + `lng` + 's' self.pack(fmt , str) if lng % 2 == 0: self.pack("!B" , 0) def putlongseq(self, Id , seq): lng = len(seq) if 4 * lng < 31: self.pack("!H" , Id | 4 * lng) else: self.pack("!H" , Id | 31) self.pack("!H" , 4 * lng) fmt = '!' + `lng` + "i" args = (fmt,) + tuple(seq) apply(self.pack , args) def putcol(self , Id , color): red = rndtoint(255 * color.red) green = rndtoint(255 * color.green) blue = rndtoint(255 * color.blue) self.pack("!HBBBB" , Id , red , green , blue , 0) def close(self): self.Msg.show() self.file.close() def PathToSeq(self, Path): parlst = () for i in range(Path.len): type, control, p, cont = Path.Segment(i) if type == Bezier: self.Msg.Incl(W_Bezier) p1 , p2 = control tmplst = FlattenPath(p0, p1, p2, p) for tp in tmplst: parlst = parlst + tuple(self.trafo(tp)) else: parlst = parlst + tuple(self.trafo(p)) p0 = p return parlst def LineStyle(self, Props): # Line width self.pack("!Hi" , 0x5064 , rndtoint(self.Scale * Props.line_width)) # Line color self.putcol(0x5083 , Props.line_pattern.Color().RGB()) def FillStyle(self, Props): line_pattern = Props.line_pattern fill_pattern = Props.fill_pattern line_width = rndtoint(self.Scale * Props.line_width) if line_pattern is EmptyPattern: # Edge Visibility Off self.pack("!HH" , 0x53c2 , 0x0000) else: # Edge Visibility On self.pack("!HH" , 0x53c2 , 0x0001) # Edge width self.pack("!Hi" , 0x5384 , line_width) # Edge color self.putcol(0x53a3 , line_pattern.Color().RGB()) if fill_pattern is EmptyPattern: # Fill type is Hollow self.pack("!HH" , 0x52c2 , 0x0004) elif fill_pattern.is_Gradient: self.pack("!HH" , 0x52c2 , 0x0001) self.putcol(0x52e3 , self.black) self.Msg.Incl(W_Gradient) elif fill_pattern.is_Hatching: self.pack("!HH" , 0x52c2 , 0x0001) self.putcol(0x52e3 , self.black) self.Msg.Incl(W_Hatched) elif fill_pattern.is_Tiled: self.pack("!HH" , 0x52c2 , 0x0001) self.putcol(0x52e3 , self.black) self.Msg.Incl(W_Tiled) else: # Fill type is Solid self.pack("!HH" , 0x52c2 , 0x0001) #if fill_pattern.is_Solid: self.putcol(0x52e3 , fill_pattern.Color().RGB()) def PolyBezier(self, Paths, Properties): line_pattern = Properties.line_pattern fill_pattern = Properties.fill_pattern line_width = rndtoint(self.Scale * Properties.line_width) if len(Paths) == 1: path = Paths[0] if fill_pattern is EmptyPattern and not path.closed: Id = 0x4020 # Polyline self.LineStyle(Properties) lst = self.PathToSeq(path) else: Id = 0x40e0 # Polygon self.FillStyle(Properties) lst = self.PathToSeq(path) if path.closed: lst = lst[:-2] self.putlongseq(Id , map(rndtoint , lst)) elif fill_pattern is EmptyPattern: self.LineStyle(Properties) self.FillStyle(Properties) for path in Paths: lst = self.PathToSeq(path) if path.closed: Id = 0x40e0 # Polygon lst = lst[:-2] else: Id = 0x4020 # Polyline self.putlongseq(Id , map(rndtoint , lst)) else: # The polygonset case self.FillStyle(Properties) set = [] size = 0 for path in Paths: lst = self.PathToSeq(path) if path.closed: lst = lst[:-2] size = size + 5 * len(lst) set.append(lst) if size < 31: self.pack("!H" , 0x4100 | size) else: self.pack("!H" , 0x4100 | 31) self.pack("!H" , size) for lst in set: while lst <> (): Arg = tuple(map(rndtoint , lst[:2])) #Arg = lst[:2] lst = lst[2:] if lst == (): Arg = Arg + (3,) else: Arg = Arg + (1,) Arg = ("!iiH",) + Arg apply(self.pack , Arg) def Rectangle(self, rct): trf = rct.trafo if rct.radius1 != 0 or rct.radius2 != 0: self.PolyBezier(rct.Paths(), rct.Properties()) elif (trf.m12 == 0 and trf.m21 == 0) or (trf.m11 == 0 and trf.m22 == 0): self.FillStyle(rct.Properties()) P1 = trf(Point(0,0)) P2 = trf(Point(1,1)) self.putlongseq(0x4160 , map(rndtoint , tuple(self.trafo(P1)) \ + tuple(self.trafo(P2)))) else: self.PolyBezier(rct.Paths(), rct.Properties()) def Ellipse(self, ell): trf = ell.trafo if (abs(trf.m11 - trf.m22) < 0.001 and abs(trf.m21 + trf.m12) < 0.001) \ or (abs(trf.m11 + trf.m22) < 0.001 and abs(trf.m21 - trf.m12) < 0.001): if ell.start_angle == ell.end_angle: self.FillStyle(ell.Properties()) C = trf(Point(0,0)) R = sqrt(trf.m11 * trf.m11 + trf.m12 * trf.m12) self.putlongseq(0x4180 , map(rndtoint , tuple(self.trafo(C)) \ + (R * self.Scale,))) else: C = trf(Point(0,0)) S = Point(cos(ell.start_angle) , sin(ell.start_angle)) E = Point(cos(ell.end_angle) , sin(ell.end_angle)) R = sqrt(trf.m11 * trf.m11 + trf.m12 * trf.m12) if trf.m11 * trf.m22 - trf.m12 * trf.m21 > 0: S,E = trf.DTransform(S) , trf.DTransform(E) else: S,E = trf.DTransform(E) , trf.DTransform(S) S = 1000000 * S / abs(S) E = 1000000 * E / abs(E) if ell.arc_type == 0 \ and ell.Properties().fill_pattern == EmptyPattern: self.LineStyle(ell.Properties()) self.putlongseq(0x41e0 , map(rndtoint , tuple(self.trafo(C)) \ + tuple(S) + tuple(E) + (R * self.Scale,))) else: #self.PolyBezier(ell.Paths(), ell.Properties()) self.FillStyle(ell.Properties()) if ell.arc_type == 0: cp = 1 else: cp = 2 - ell.arc_type Args = ["!H7iH" , 0x4200 + 30] + map(rndtoint , tuple(self.trafo(C)) \ + tuple(S) + tuple(E) + (R * self.Scale , cp)) apply(self.pack , Args) else: if ell.start_angle == ell.end_angle: self.FillStyle(ell.Properties()) C = trf(Point(0,0)) P1 = trf(Point(1,0)) P2 = trf(Point(0,1)) self.putlongseq(0x4220 , map(rndtoint , tuple(self.trafo(C)) \ + tuple(self.trafo(P1)) + tuple(self.trafo(P2)))) else: C = trf(Point(0,0)) P1 = trf(Point(1,0)) P2 = trf(Point(0,1)) S = trf.DTransform(Point(cos(ell.start_angle) , sin(ell.start_angle))) E = trf.DTransform(Point(cos(ell.end_angle) , sin(ell.end_angle))) S = 1000000 * S / abs(S) E = 1000000 * E / abs(E) if ell.arc_type == 0 and ell.Properties().fill_pattern == EmptyPattern: self.LineStyle(ell.Properties()) self.putlongseq(0x4240 , map(rndtoint , tuple(self.trafo(C)) \ + tuple(self.trafo(P1)) + tuple(self.trafo(P2)) + tuple(S) \ + tuple(E))) else: #self.PolyBezier(ell.Paths(), ell.Properties()) self.FillStyle(ell.Properties()) if ell.arc_type == 0: cp = 1 else: cp = 2 - ell.arc_type Args = ["!HH10iH" , 0x4260 + 31 , 42] + map(rndtoint , tuple(self.trafo(C)) \ + tuple(self.trafo(P1)) + tuple(self.trafo(P2)) + tuple(S) \ + tuple(E) + (cp,)) apply(self.pack , Args) def Image(self, object): self.Msg.Incl(W_Image) def Text(self, object): self.Msg.Incl(W_Text) self.PolyBezier(object.Paths(), object.Properties()) def SaveObjects(self, Objects): for object in Objects: if object.is_Compound: self.SaveObjects(object.GetObjects()) elif object.is_Rectangle: self.Rectangle(object) elif object.is_Ellipse: self.Ellipse(object) elif object.is_Image: self.Image(object) elif object.is_Text: self.Text(object) elif object.is_curve: self.PolyBezier(object.Paths(), object.Properties()) def SaveLayers(self, Layers): # We put each layer in a picture, that seems at the # moment to be the approach for layer in Layers: if not layer.is_SpecialLayer and layer.Printable(): # Begin Picture self.putstr(0x0060 , layer.name) # Color Selection Mode: Direct self.pack("!HH" , 0x2042 , 0x0001) # Edge Width Specification: Absolute self.pack("!HH" , 0x20a2 , 0x0000) # Line Width Specification: Absolute self.pack("!HH" , 0x2062 , 0x0000) # VDC Extend self.putlongseq(0x20c0 , self.extend) # Background Colour self.putcol(0x20e3 , self.white) # Begin Picture Body self.pack("!H" , 0x0080) self.SaveObjects(layer.GetObjects()) # End Picture self.pack("!H" , 0x00a0) #if #for #end def SaveDocument(self, doc): # A dillema # Should the design fill the CGM-file or # Should it be placed approximately in # the same place as it is put on the page. if 0: left, bottom, right, top = doc.BoundingRect() width = right - left hight = top - bottom else: left, bottom = 0, 0 width = doc.page_layout.width hight = doc.page_layout.height right , top = width , hight #sc = 65534 / max(width , hight) sc = 1000 #self.trafo = Translation(-32767,-32767)(Scale(sc)(Translation(-left , -bottom))) self.trafo = Scale(sc)(Translation(-left , -bottom)) self.Scale = sc self.extend = map(rndtoint , \ tuple(self.trafo(left,bottom)) + tuple(self.trafo(right,top))) # Begin Metafile filename = os.path.basename(self.pathname) title = filename + " generated by sK1" self.putstr(0x0020 , title) # Metafile Version self.pack("!H" , 0x1022) self.pack("!H" , 0x0001) # Metafile Description self.putstr(0x1040 , filename + " created by sk1") # Metafile Element List self.pack("!HHHH" , 0x1166 , 0x0001 , 0xffff , 0x0001) # Default Replacements self.pack("!H" , 0x1184) # VDC Integer precision 32 bits self.pack("!Hh" , 0x3022 , 32) #Font List # self.SaveLayers(doc.Layers()) # End Meta File self.pack("!H" , 0x0040) #end def save(document, file, filename, options = {}): saver = CGMSaver(file, filename, options) saver.SaveDocument(document) saver.close() UniConvertor-1.1.4/src/app/plugins/__init__.py0000775000076400007640000000000011060500600017764 0ustar igorigorUniConvertor-1.1.4/src/share/0000755000076400007640000000000011211455344014535 5ustar igorigorUniConvertor-1.1.4/src/share/icc/0000755000076400007640000000000011211455344015273 5ustar igorigorUniConvertor-1.1.4/src/share/icc/sRGB.icm0000775000076400007640000000611011060500604016555 0ustar igorigor HLinomntrRGB XYZ Î 1acspMSFTIEC sRGBöÖÓ-HP cprtP3desc„lwtptðbkptrXYZgXYZ,bXYZ@dmndTpdmddĈvuedL†viewÔ$lumiømeas $tech0 rTRC< gTRC< bTRC< textCopyright (c) 1998 Hewlett-Packard CompanydescsRGB IEC61966-2.1sRGB IEC61966-2.1XYZ óQÌXYZ XYZ o¢8õXYZ b™·…ÚXYZ $ „¶ÏdescIEC http://www.iec.chIEC http://www.iec.chdesc.IEC 61966-2.1 Default RGB colour space - sRGB.IEC 61966-2.1 Default RGB colour space - sRGBdesc,Reference Viewing Condition in IEC61966-2.1,Reference Viewing Condition in IEC61966-2.1view¤þ_.ÏíÌ \žXYZ L VPWçmeassig CRT curv #(-27;@EJOTY^chmrw|†‹•šŸ¤©®²·¼ÁÆËÐÕÛàåëðöû %+28>ELRY`gnu|ƒ‹’š¡©±¹ÁÉÑÙáéòú &/8AKT]gqz„Ž˜¢¬¶ÁËÕàëõ !-8COZfr~Š–¢®ºÇÓàìù -;HUcq~Œš¨¶ÄÓáðþ +:IXgw†–¦µÅÕåö'7HYj{Œ¯ÀÑãõ+=Oat†™¬¿Òåø 2FZn‚–ª¾Òçû  % : O d y ¤ º Ï å û  ' = T j ˜ ® Å Ü ó " 9 Q i € ˜ ° È á ù  * C \ u Ž § À Ù ó & @ Z t Ž © Ã Þ ø.Id›¶Òî %A^z–³Ïì &Ca~›¹×õ1OmŒªÉè&Ed„£Ãã#Ccƒ¤Åå'Ij‹­Îð4Vx›½à&Il²ÖúAe‰®Ò÷@eНÕú Ek‘·Ý*QwžÅì;cвÚ*R{£ÌõGp™Ãì@j”¾é>i”¿ê  A l ˜ Ä ð!!H!u!¡!Î!û"'"U"‚"¯"Ý# #8#f#”#Â#ð$$M$|$«$Ú% %8%h%—%Ç%÷&'&W&‡&·&è''I'z'«'Ü( (?(q(¢(Ô))8)k))Ð**5*h*›*Ï++6+i++Ñ,,9,n,¢,×- -A-v-«-á..L.‚.·.î/$/Z/‘/Ç/þ050l0¤0Û11J1‚1º1ò2*2c2›2Ô3 3F33¸3ñ4+4e4ž4Ø55M5‡5Â5ý676r6®6é7$7`7œ7×88P8Œ8È99B99¼9ù:6:t:²:ï;-;k;ª;è<' >`> >à?!?a?¢?â@#@d@¦@çA)AjA¬AîB0BrBµB÷C:C}CÀDDGDŠDÎEEUEšEÞF"FgF«FðG5G{GÀHHKH‘H×IIcI©IðJ7J}JÄK KSKšKâL*LrLºMMJM“MÜN%NnN·OOIO“OÝP'PqP»QQPQ›QæR1R|RÇSS_SªSöTBTTÛU(UuUÂVV\V©V÷WDW’WàX/X}XËYYiY¸ZZVZ¦Zõ[E[•[å\5\†\Ö]']x]É^^l^½__a_³``W`ª`üaOa¢aõbIbœbðcCc—cëd@d”dée=e’eçf=f’fèg=g“géh?h–hìiCišiñjHjŸj÷kOk§kÿlWl¯mm`m¹nnknÄooxoÑp+p†pàq:q•qðrKr¦ss]s¸ttptÌu(u…uáv>v›vøwVw³xxnxÌy*y‰yçzFz¥{{c{Â|!||á}A}¡~~b~Â#„å€G€¨ kÍ‚0‚’‚ôƒWƒº„„€„ã…G…«††r†×‡;‡ŸˆˆiˆÎ‰3‰™‰þŠdŠÊ‹0‹–‹üŒcŒÊ1˜ÿŽfŽÎ6žnÖ‘?‘¨’’z’ã“M“¶” ”Š”ô•_•É–4–Ÿ— —u—à˜L˜¸™$™™üšhšÕ›B›¯œœ‰œ÷dÒž@ž®ŸŸ‹Ÿú i Ø¡G¡¶¢&¢–££v£æ¤V¤Ç¥8¥©¦¦‹¦ý§n§à¨R¨Ä©7©©ªª««u«é¬\¬Ð­D­¸®-®¡¯¯‹°°u°ê±`±Ö²K²Â³8³®´%´œµµŠ¶¶y¶ð·h·à¸Y¸Ñ¹J¹Âº;ºµ».»§¼!¼›½½¾ ¾„¾ÿ¿z¿õÀpÀìÁgÁãÂ_ÂÛÃXÃÔÄQÄÎÅKÅÈÆFÆÃÇAÇ¿È=ȼÉ:ɹÊ8Ê·Ë6˶Ì5̵Í5͵Î6ζÏ7ϸÐ9кÑ<ѾÒ?ÒÁÓDÓÆÔIÔËÕNÕÑÖUÖØ×\×àØdØèÙlÙñÚvÚûÛ€ÜÜŠÝÝ–ÞÞ¢ß)߯à6à½áDáÌâSâÛãcãëäsäü儿 æ–çç©è2è¼éFéÐê[êåëpëûì†ííœî(î´ï@ïÌðXðåñrñÿòŒóó§ô4ôÂõPõÞömöû÷Šøø¨ù8ùÇúWúçûwüü˜ý)ýºþKþÜÿmÿÿUniConvertor-1.1.4/src/share/icc/GenericCMYK.icm0000775000076400007640000067104011060500610020027 0ustar igorigorr argl prtrCMYKLab Õ acspMSFT????????öÖÓ-argl descucprtŒdmdd¤íwtpt”bkpt¨A2B1¼ÑúA2B0¼ÑúA2B2¼ÑúB2A1ԸѼB2A0¦tѼB2A2x0ѼgamtIì(4descFogra27L CMYK Coated PresstextPublic Domaindesc“Offset printing, according to ISO/DIS 12647-2:2004, OFCOM, paper type 1 or 2 = coated art, 115 g/m2, screen ruling 60 cm-1, positive-acting platesXYZ Þ°æ?ÇÎXYZ "vmft2 6l¢ØDz°çS‰¿õ,b˜Ï;r¨ÞK‚¸ï%\’Éÿ6l£ÚG~´ë " Y Æ ý 4 k ¢ Ù  G } ´ ë " Z ‘ È ÿ 6 m ¤ ÛJ¸ï'^•Í;sªâPˆ¿÷.fžÕ D|´ë#[“Ê:rªâQ‰Áù1i¡ÙI¹ò*bšÒ C{³ì$\•Í>v¯ç X‘É:s¬äVŽÇ 9 q ª ã!!U!Ž!Ç!ÿ"8"q"ª"ã##U#Ž#È$$:$s$¬$å%%X%‘%Ê&&=&v&¯&é'"'\'•'Î((A({(´(î)')a)›)Ô**G**»*õ+.+h+¢+Ü,,O,‰,Ã,ý-6-p-ª-ä..X.’.Ì//@/z/´/î0(0b0œ0×11K1…1¿1ù242n2¨2â33W3‘3Ì44@4{4µ4ï5*5d5Ÿ5Ù66N6‰6Ã6þ787s7­7è8#8]8˜8Ò9 9H9ƒ9½9ø:3:m:¨:ã;;Y;”;Î< >W>’>Í??C?~?¹?ô@/@k@¦@áAAXA“AÎB BEB€B¼B÷C3CnCªCåD!D]D˜DÔEEKE‡EÃEÿF;FwF²FîG*GfG¢GÞHHVH“HÏI IGIƒIÀIüJ8JuJ±JíK*KfK£KßLLXL•LÑMMKM‡MÄNN>N{N·NôO1OnO«OèP%PbPŸPÜQQVQ”QÑRRKR‰RÆSSAS~S»SùT6TtT±TïU-UjU¨UæV#VaVŸVÝWWYW—WÕXXRXXÎY YKYŠYÈZZFZ„ZÃ[[A[€[¿[þ\=\|\»\ú]9]y]¸]ø^7^w^¶^ö_5_u_µ_õ`5`u`µ`õa5auaµaõb6bvb·b÷c8cxc¹cùd:d{d¼dýe>eeÀffBfƒfÅggGg‰gÊh hNhhÑiiUi—iÙjj]jŸják#kfk¨kêl-lol²lõm7mzm½nnCn†nÉo oOo’oÕpp\p pãq'qjq®qòr6rzr½ssFsŠsÎttVt›tßu$uhu­uñv6v{vÀwwIwŽwÔxx^x£xèy.ysy¹yþzDzŠzÏ{{[{¡{ç|-|s|¹|ÿ}F}Œ}Ó~~`~¦~í4zÁ€€O€–€Ý#j±ø‚?‚†‚̃ƒZƒ¡ƒè„.„u„¼……I……׆†d†«†ò‡8‡‡Æˆ ˆSˆšˆà‰'‰n‰´‰ûŠBŠˆŠÏ‹‹\‹£‹éŒ0ŒwŒ½J‘׎ŽeŽ«Žò8Å R™ß‘&‘l‘³‘ù’@’†’Ì““Y“ “æ”-”s”¹••F••Ó––`–¦–ì—3—y—¿˜˜L˜’˜Ù™™e™¬™òš8ššÅ› ›Q›˜›Þœ$œjœ°œ÷=ƒÉžžVžœžâŸ(ŸnŸ´Ÿú A ‡ Í¡¡Y¡Ÿ¡å¢+¢q¢·¢ý£C£‰£Ï¤¤\¤¢¤ç¥-¥s¥¹¥ÿ¦E¦‹¦Ñ§§]§£§é¨/¨u¨»©©F©Œ©Òªª^ª¤ªé«/«u«»¬¬F¬Œ¬Ò­­]­£­è®.®s®¹®þ¯D¯‰¯Ï°°Z°Ÿ°ä±*±o±´±ú²?²„²É³³T³™³Þ´#´h´­´òµ7µ|µÁ¶¶K¶¶Õ··^·£·è¸-¸r¸¶¸û¹@¹„¹ÉººRº—ºÛ» »d»©»í¼2¼v¼»¼ÿ½C½ˆ½Ì¾¾U¾™¾Ý¿!¿f¿ª¿îÀ2ÀvÀºÀÿÁCÁ‡ÁËÂÂS—ÂÛÃÃcçÃìÄ0ÄtĸÄüÅ@Å„ÅÈÆ ÆPÆ”ÆØÇÇ`ǤÇèÈ,ÈpÈ´È÷É;ÉÉÃÊÊKÊÊÓËË[ËŸËâÌ&ÌjÌ®ÌòÍ6ÍzͽÎÎEΉÎÍÏÏTϘÏÜÐ ÐdЧÐëÑ/ÑsÑ·ÑúÒ>Ò‚ÒÆÓ ÓMÓ‘ÓÕÔÔ\Ô ÔäÕ'ÕkÕ¯ÕòÖ6ÖzÖ½××E׈×ÌØØSØ—ØÚÙÙaÙ¥ÙéÚ,ÚpÚ³Ú÷Û:Û~ÛÁÜÜHÜ‹ÜÏÝÝVÝ™ÝÜÞ ÞcÞ¦Þêß-ßpß´ß÷à:à~àÁááGá‹áÎââTâ—âÛããaã¤ãçä*änä±äôå7åz彿æCæ†æÉç çOç’çÕèè[èžèáé$égéªéíê0êsêµêøë;ë~ëÁììGì‰ìÌííRí”í×îî]îŸîâï%ïhïªïíð0ðrðµð÷ñ:ñ}ñ¿òòEò‡òÊó óOó‘óÔôôYô›ôÞõ õcõ¥õèö*ölö¯öñ÷4÷v÷¸÷ûø=øøÂùùFù‰ùËú úPú’úÔûûYû›ûÝüüaü¤üæý(ýjý¬ýîþ0þsþµþ÷ÿ9ÿ{ÿ½ÿÿ5j Õ @u«àK¶ì!WÂø.c™Ï;q§ÝIµë!WÄú0fÓ @ v ­ ã  P ‡ ½ ô + a ˜ Ï  < s ª á  O † ½ ô+b™Ð>v­äSŠÂù0hŸ×F~µí%\”Ì<t«ãS‹Ãü4l¤ÜM…½ö.fŸ×H¹ò+cœÕ F¸ñ*cœÕG€¹ò+dž×Iƒ¼ö / i ¢ Ü!!O!ˆ!Â!ü"5"o"©"ã##V##Ê$$>$x$²$ì%&%a%›%Õ&&I&„&¾&ø'3'm'¨'â((W(’(Ì))B)|)·)ò*-*g*¢*Ý++S+Ž+É,,@,{,¶,ñ---h-¤-ß..V.’.Î/ /E//½/ù050q0®0ê1&1b1Ÿ1Û22T22Í3 3F3ƒ3À3ý494v4³4ð5-5j5§5ä6"6_6œ6Ù77T7’7Ï8 8J8ˆ8Å99A9~9¼9ú:8:v:´:ò;0;n;¬;ê<(>Z>™>×??U?”?Ò@@P@@ÎA ALA‹AÊB BIBˆBÇCCFC…CÄDDCDƒDÂEEAEEÀFF@FF¿FÿG?G~G¾GþH>H~H¾HþI>I~I¾IÿJ?JJ¿JÿK@K€KÁLLAL‚LÂMMCM„MÅNNFN‡NÈOOIOŠOËP PMPŽPÏQQQQ’QÓRRVR—RØSS[SœSÞTTaT¢TäU%UgU©UêV,VnV°VñW3WuW·WùX;X}XÀYYDY†YÉZ ZMZZÒ[[W[š[Ý\\b\¥\è]+]n]±]ô^7^z^½__D_‡_Ê``Q`”`Øaa_a£aæb*bnb²böc:c~cÂddJdŽdÒee[eŸeãf(flf±fõg:ggÄhhMh’h×iiai¦iìj1jvj¼kkGkŒkÒll^l£lém/mum»nnHnŽnÔooao¨oîp5p|pÂq qPq—qÞr%rlr´rûsBsŠsÑtt`t¨tïu7uuÇvvWvŸvçw/wxwÀx xQxšxây+yty¼zzNz—zà{){s{¼||O|˜|â}+}u}¾~~R~œ~æ0zÄ€€X€¢€ì6€Ê‚‚^‚¨‚òƒ<ƒ…ƒÏ„„c„­„÷…A…‹…Õ††i†³†ý‡G‡‘‡Ûˆ&ˆpˆº‰‰N‰˜‰âŠ,ŠvŠÀ‹ ‹T‹ž‹èŒ2Œ|ŒÆ[¥ïŽ9ŽƒŽÍa«õ@ŠÔ‘‘h‘²‘ü’G’‘’Û“%“o“¹””N”˜”â•,•v•À– –U–Ÿ–é—3—~—Ș˜\˜¦˜ñ™;™…™Ïššdš®šø›B›Œ›×œ!œkœµœÿI“Ýž'žqž»ŸŸOŸ™Ÿã - w Á¡ ¡U¡Ÿ¡è¢2¢|¢Æ££Z£££í¤7¤¤Ë¥¥^¥¨¥ñ¦;¦…¦Î§§b§«§õ¨?¨ˆ¨Ò©©e©®©øªBª‹ªÕ««g«±«ú¬C¬Œ¬Õ­­f­¯­ø®@®‰®Ñ¯¯b¯ª¯ò°:°‚°Ê±±Z±¢±é²1²x²À³³N³•³Ü´#´j´±´øµ?µ…µÌ¶¶Y¶Ÿ¶æ·,·r·¸·þ¸D¸Š¸Ð¹¹[¹¡¹æº,ºqº·ºü»A»†»Ë¼¼U¼š¼ß½#½h½­½ñ¾6¾z¾¾¿¿G¿‹¿ÏÀÀVÀšÀÞÁ"ÁeÁ©ÁìÂ/Âs¶ÂùÃ<ÃÃÂÄÄGÄŠÄÌÅÅQÅ“ÅÕÆÆZÆœÆÝÇÇaÇ£ÇäÈ&ÈgÈ©ÈêÉ+ÉlÉ­ÉîÊ/ÊpʱÊñË2Ër˳ËóÌ4ÌtÌ´ÌôÍ4ÍtÍ´ÍôÎ3ÎsγÎòÏ2ÏqϱÏðÐ/ÐnЮÐíÑ,ÑkÑ©ÑèÒ'ÒfÒ¤ÒãÓ"Ó`ÓžÓÝÔÔYÔ—ÔÖÕÕRÕÕÍÖ ÖIÖ‡ÖÅ××@×~×»×ùØ6ØtرØïÙ,ÙiÙ§ÙäÚ!Ú^Ú›ÚØÛÛRÛÛÌÜ ÜF܃ܿÜüÝ9ÝuݲÝïÞ+ÞhÞ¤ÞàßßYß•ßÒààJà†àÂàþá:ává²áîâ*âfâ¢âÞããUã‘ãÌääCääºäöå1ålå¨åãææYæ”æÐç çFçç¼ç÷è1èlè§èâééWé’éÍêêBê|ê·êñë,ëfë ëÛììOì‰ìÄìþí8írí¬íæî îZî“îÍïïAï{ï´ïîð'ðað›ðÔññGñ€ñºñóò,òfòŸòØóóJóƒó¼óõô.ôgô ôÙõõKõƒõ¼õõö-öföŸö×÷÷H÷÷¹÷ñø*øbøšøÓù ùCù{ù³ùëú#ú[ú“úËûû;ûsûªûâüüRü‰üÁüøý0ýhýŸýÖþþEþ}þ´þëÿ"ÿZÿ‘ÿÈÿÿ7n¥ÜJ¸ï&]”Ì:q©àO†¾õ-dœÓ Cz²ê"Y‘É9q©á  Q ‰ Á ù 1 i ¢ Ú  J ƒ » ó , d Õ  F  ¸ ð)bšÓ E}¶ï(ašÓ E~·ð*cœÕH»ô.g¡ÚM‡Àú4n§áUÉ<v°ë%_™Ó H‚¼ö1k¦àUÊ?zµï*e ÚP‹Æ  < w ² í!(!d!Ÿ!Ú""Q"Œ"Ç##>#y#µ#ð$,$g$£$ß%%V%’%Í& &E&&½&ù'5'q'­'é(%(a((Ù))Q)Ž)Ê**B**»*ø+4+q+­+ê,',c, ,Ý--V-“-Ð. .J.‡.Å//?/|/º/÷040r0¯0í1*1h1¦1ã2!2_22Û33W3•3Ó44O44Ë5 5H5†5Å66B6€6¿6ý7<7{7¹7ø878v8µ8ô939r9±9ð:/:n:®:í;,;l;«;ë<*(>h>¨>è?(?h?¨?è@(@h@©@éA)AjAªAëB+BlB¬BíC.CnC¯CðD1DqD²DóE4EuE¶E÷F8FzF»FüG=GGÀHHCH„HÆIIIIŠIÌJJOJ‘JÓKKWK™KÛLL_L¡LãM%MgM©MìN.NpN³NõO7OzO¼OÿPBP„PÇQ QLQQÒRRXR›RÞS!SdS§SêT.TqT´T÷U;U~UÂVVIVŒVÐWWWWšWÞX"XeX©XíY1YtY¸YüZ@Z„ZÈ[ [P[”[Ø\\`\¤\è],]p]´]ù^=^^Å_ _N_“_×````¤`éa-ara¶aûb@b„bÉccSc—cÜd!dfd«dðe4eye¾ffHfŽfÓgg]g¢gçh,hrh·hüiBi‡iÍjjWjjãk(knk³kùl?l„lÊmmVmœmân'nmn³nùo?o…oÌppXpžpäq+qqq·qýrDrŠrÑss^s¤sët1txt¿uuLu“uÚv vgv®võw|†|Í}}]}¤}ì~4~{~à S›ã€+€s€ºJ’Ú‚"‚i‚±‚ùƒAƒˆƒÐ„„_„§„î…6…~…ņ †T†›†ã‡*‡r‡¹ˆˆHˆˆÖ‰‰e‰¬‰óŠ:Š‚ŠÉ‹‹W‹ž‹åŒ,ŒsŒºHÖŽŽdŽªŽñ8Æ Sšà‘'‘n‘´‘û’A’ˆ’Ï““\“¢“è”/”u”¼••H••Õ––b–¨–î—4—z—Á˜˜M˜“˜Ù™™e™«™ñš7š}šÃ› ›O›•›Úœ œfœ¬œñ7}žžNž“žÙŸŸdŸ©Ÿï 4 z ¿¡¡J¡¡Ô¢¢_¢¤¢é£.£s£¹£þ¤C¤ˆ¤Í¥¥W¥œ¥á¦&¦j¦¯¦ô§9§~§Â¨¨L¨‘¨Õ©©_©£©èª,ªqªµªú«>«ƒ«Ç¬ ¬O¬”¬Ø­­`­¤­è®,®p®´®ø¯<¯€¯Ã°°K°Ž°Ò±±Y±±à²$²g²ª²î³1³t³·³ú´>´´ÄµµJµµÏ¶¶U¶˜¶Û··`·£·å¸(¸j¸­¸ï¹2¹t¹¶¹ùº;º}º¿»»D»†»È¼ ¼L¼Ž¼Ï½½S½•½×¾¾Z¾œ¾Ý¿¿`¿¢¿ãÀ%ÀfÀ§ÀéÁ*ÁkÁ¬ÁíÂ.Âo°ÂñÃ2ÃsôÃõÄ5ÄvÄ·Ä÷Å8ÅxŹÅùÆ:ÆzƺÆúÇ;Ç{Ç»ÇûÈ;È{È»ÈûÉ;É{ɺÉúÊ:ÊyʹÊùË8ËxË·Ë÷Ì6Ìu̵ÌôÍ3ÍrͱÍðÎ/ÎnέÎìÏ+ÏjÏ©ÏèÐ&ÐeФÐãÑ!Ñ`ÑžÑÝÒÒZÒ˜ÒÖÓÓSÓ‘ÓÏÔ ÔKÔŠÔÈÕÕDÕÕ¿ÕýÖ;ÖyÖ·Öõ×2×p×®×ëØ)ØgؤØâÙ Ù]Ù›ÙØÚÚSÚÚÎÛ ÛHÛ†ÛÃÜÜ>Ü{ܸÜõÝ2ÝpÝ­ÝêÞ'ÞdÞ¡ÞÞßßXß•ßÑààKàˆàÅáá>á{á¸áôâ1ânâªâçã#ã`ãœãÙääRäŽäËååCå€å¼åøæ5æqæ­æéç%çaçžçÚèèRèŽèÊééBé~é¹éõê1êmê©êåë ë\ë˜ëÔììKì†ìÂìþí9íuí°íìî'îcîžîÙïïPï‹ïÇðð=ðxð´ðïñ*ñeñ ñÛòòQòŒòÈóó=óxó³óîô)ôdôŸôÙõõOõŠõÄõÿö:ötö¯öê÷$÷_÷™÷ÔøøIøƒø¾øøù2ùmù§ùáúúVúúÊûû?ûyû³ûíü'üaü›üÕýýIýƒý½ý÷þ1þkþ¤þÞÿÿRÿŒÿÅÿÿ3gšÎ5hœÐ7jžÒ9m¡Ô<p£× ?s§ÛCw«ßG{¯ãK³è  P „ ¹ í ! U Š ¾ ò ' [ Ä ø - a – Ê ÿ 4 h Ñ;o¤Ù Bw¬àJ´éR‡¼ñ&[Åú/dšÏ9n£ÙCx®ãNƒ¸î#YŽÄù/dšÏ:p¦ÛG|²èS‰¿õ+a–Ì8n¤ÚF|²èU‹Á÷ - d š Ð!!=!s!©!à""L"ƒ"¹"ð#&#]#“#Ê$$7$m$¤$Û%%H%%µ%ì&#&Y&&Ç&þ'5'k'¢'Ù((G(~(µ(ì)#)Z)‘)È)ÿ*6*m*¤*Ü++J++¸+ï,',^,•,Ì--;-r-ª-á..P.‡.¾.ö/-/e/œ/Ô0 0C0z0²0é1!1X11Ç1ÿ272n2¦2Ý33M3„3¼3ô4,4c4›4Ó5 5C5{5³5ë6"6Z6’6Ê77;7s7«7ã88S8‹8Ä8ü949l9¥9Ý::N:†:¾:÷;/;h; ;Ù<>H>>º>ò?+?d??Ö@@H@@º@óA,AeAžA×BBIB‚B¼BõC.CgC CÚDDLD…D¿DøE2EkE¤EÞFFQFŠFÄFýG7GpGªGãHHWHHÊII=IwI±IëJ$J^J˜JÒK KFK€KºKóL-LgL¡LÛMMPMŠMÄMþN8NrN¬NçO!O[O•OÏP PDP~P¹PóQ.QhQ¢QÝRRRRŒRÇSS¨„¨Ê©©U©›©áª'ªmª³ªú«@«†«Ì¬¬Y¬Ÿ¬æ­-­s­º®®G®Ž®Õ¯¯c¯ª¯ñ°9°€°Ç±±V±±å²,²t²¼³³K³“³Û´#´k´³´ûµCµŒµÔ¶¶e¶­¶ö·?·‡·Ð¸¸b¸ª¸ó¹<¹…¹Ïººaºªºô»=»‡»Ð¼¼c¼­¼÷½A½‹½Õ¾¾i¾³¾ý¿G¿‘¿ÜÀ&ÀqÀ»ÁÁQÁ›ÁæÂ1Â|ÂÇÃÃ]èÃóÄ>ĉÄÔÅ ÅkÅ·ÆÆNÆ™ÆåÇ1Ç}ÇÉÈÈ`ȬÈùÉEÉ‘ÉÝÊ)ÊvÊÂËË[˨ËôÌAÌŽÌÛÍ(ÍtÍÁÎÎ[ΩÎöÏCÏÏÝÐ+ÐxÐÅÑÑ`Ñ®ÑûÒIÒ–ÒäÓ2Ó€ÓÍÔÔiÔ·ÕÕSÕ¡ÕïÖ=ÖŒÖÚ×(×v×ÄØØaذØþÙLÙ›ÙéÚ8Ú‡ÚÕÛ$ÛsÛÁÜÜ_Ü®ÜüÝKÝšÝéÞ8Þ‡ÞÖß%ßtßÄààbà±ááPáŸáïâ>ââÝã,ã|ãÌääkäºå åZåªåúæIæ™æéç9ç‰çÙè)èyèÉééjéºê êZê«êûëLëœëìì=ììÞí/ííÐî!îqîÂïïdïµððVð§ðøñIñ›ñìò=òŽòßó0ó‚óÓô$ôvôÇõõjõ¼ö ö_ö°÷÷T÷¦÷÷øIø›øíù?ù‘ùãú5ú‡úÙû+û}ûÏü"ütüÆýýký½þþbþµÿÿZÿ¬ÿÿÿÿ€€ç¹ÇVÏA›~æµí†~¬›ÿ™~É€r©eKÏkJ€€-ò€O€©ý¤~¥Œåh~„‰üÍ~tˆ'³ç~€†ªšI~¬……é~Þ„—e+ƒÐJ;fƒ#/¸‚ûa}w—»ã}i”VÊ£}i‘9±Î}ˆŽ„˜«}ÉŒ.~Šdõ~bˆ5J~͆B0D„MùJ|Ž£&àô|~žÈ{|ˆš@¯é|¯–[—$|û’Û~}Q¯d«}«Œ®JÛ~?‰[0ï~à†÷C{æ®ðÞØ{å©$Æs{ó£§®|ž•¬|h™Ò}|Å•Yd6}3ðK }ÅŒU1»~e‡¤õQ{» ÜÜ{x´#ÄŠ{„­t¬]{®§”D{ý¡ ||\›.cÁ|È•XK}VC2b}ë‰ói{eÇÚÚû{J¿›Â½{L·›ª»{m¯ê’ä{¸¨u{|¡%c3|n™ÏK |ò’ 2à}xŠcñ”{zÕBÙ*{eËuÁ{W©0{d¸ð‘’{“°yð{Ú§'bg|2ž%JÖ|”à3?}‹yïÖ{­âÙ×d{x×x¿I{ܧ³{NÂ}{j·Îx™{£­(a–{뢖J•|I—›3Œ|®Œcïˆá}µØr‡ª}JÁ†‘}©ï…˜}*‘Æ„Ê}†xÒ„~_Žƒ[~ÐF,‚¥§,§ì€ìÖ‡ ˆûÖ*†‡‡P¿K…‡…à§ú„¨„À<ƒðƒïwóƒKƒZ_O‚¬‚íF„‚ ‚•-¦n‚H굆‘”Ô…ˆ‘0½-„–Ž‘¦ƒÆŒGŽÆƒŠPw‚Šˆ›_ ÿ‡ FÖy….”€õƒõè¾…¼žéÑþ„¸šñ»4ƒÎ—:¤\ƒ“Óm‚]¾v5Øæ^º^‹(G€êˆ_/h€{…ŽæÕ…$ªÐ„1¥¹eƒL K¢Á‚‚›»ŒÜ—puRR“U^\€×HGE€e‹.0"÷‡ äþ„͵±Î9ƒÎ¯”·˜‚穚¡$‚£ÖŠÔržLts€æ˜ß]ö€f“rG_êï0¿oˆaã7„´ÁÑÌmƒ¢ºiµà‚³³0Ÿšß¬*‰—*¥Rs’€”žƒ]€—¢Gdz 1D~ê‰á€„ÉÎcÊʃÀÅ ´P‚Á½ž4Ú´¿ˆc¬ˆr¨€_¤@\úº›ÓGW“A1¹~nŠßÞ„ôÛÉ ƒÊÐÓ²¢‚ºÆìœ»¿½V‡:€Þ³Óqº€!ª \lm GB~´•Ú2'}ö‹•Þ^‘Û{ÉÉ#¸{“³·ª{–è‹Ê{㇨Š#|tp߈}>Y±†ñ~5Bx…LQ+Cƒ£€zÜVµ†gÇŽ«… ±ÇŒ¶ƒíœ6Šìƒ†O‰T‚p‡Ì‚BY}†C‚ BÙ„¶‚,1ƒ'‚Úc¸ÔÅ.¿Žj¯ð‹ÚŒ7š•ŠŠM…ˆˆ«o2‡‡CY6…š†C)„!„Ï-‚¨ƒ£ØŽõ›!ÃU—»®.‹&”†™ ‰j‘ƒÜ‡ÛŽÕn_†fŒKXâ„û‰ÝCeƒŽ‡}-ã‚!… ÖÒŽh¥»Á¨Œ‰¡h¬”г+—Ÿˆ÷™‚²‡a•7m¨…ã‘vXœ„pÇC”‚ýŠ.ŒŠ†wÕ"ްÀ¿íŒ2«XªíŠ[¥ÿ–*ˆš Ç‘†û›¶ló…v–³XPƒø‘²C¶‚tŒ°/ €í‡«Ó~ú¼3¾@Œ µ’©TŠ-¯ ”Áˆa¨ €w†µ¢Sl7…"œWúƒ‘• CÏ÷4/ª€VˆÁÑäŽ Ç÷¼ÁŒ À§åŠ5¸R“wˆY°²d†–©kx„æ¡hW¡ƒ9™’C䃑«05ȉ½ÐcŽ.ÓÒ»Œ.Ê‘¦JŠ1Á”’ˆ?¸Æ~P†f¯õj»„¥¦ÙW>‚å~Có”0ÅAŠ«Î'›zŒºB—ùzn¦[”õz“’;’'{}½¬{»hð'|¬SЊ“}Ä>߇ý )ß…e€}ÌS™ñ„ƒ¸z–øƒc¤«”‚€Á‘Wæ|¢ŽÞhLŒdwS͉ä‹?H‡`º*Ä„Üóʘ™ŽZ¶É–ŒA£“DŠa`•ˆÃ{“އcgŽ‹ª†9S’‰6…5?™†Ã„G+ž„SƒbÈû˜F˜ µ.•d•¡’—’CŽé¦z£xBfªŠýŠøS9ˆˆÎ?Õ†&†Ä,hƒÄ„ÂÇk—À¢ ³¯”úž4 (’4šrŒÔ…–Íy˜Œú“Jf@ŠzÛSˆŒ@ …‰‰4-ƒ…ðÅé—j¬n²$”¡§Žž±‘Ü¢¿‹‹'žxŒ”™`e¼Š ”ÂR懀$@6„ò‹“-Œ‚`‡Är—E·3°¤”q±$C‘¦«2ŠKŽç¥Uw©ŒIŸˆe#‰°™°R§‡ “Å@_„eä.¶‡þ×MÂ/¯K”†ºô›ø‘°³Û‰%Žà¬Øv»Œ"¥Ódˆ‰iž®Rv†«—l@Œƒä(.µˆâÁ§—cͭΔ~Ä¼š†‘¼ƒ‡à޾´auÇ‹î¬1cæ‰!£²R#†Nšô@³ƒj’\/d€…‰´½Ó¤¹z«Œ ›yþ™3œ¡z1†Ã˜Þz³t'•R{aY‘Ï|‹NgŽD}É;€Š¶6(ž‡(€±¼\£žƒRªŸ¨‚f—ڛƮ…š˜z'mˆã¬Ò­’‚ç›î¨›‚‹6£¸cz¬žú j8ši€ðY°•ã I ‘VS8ŒÀ¸(%ˆ%‚+«¸¬¥‹šÜ§À‰áŠ:¢èˆnyØž.‡4iž™ž†,Y]•…II”„„8óŒƒÖ(Õ‡eƒ2ªª«á“ô™Õ§‘¤‰F¢8jy €Ri ˜é‹]Y ”b‰xIÒ‡£9>‹>…ä)y† „,©§«Iœ’˜Þ¦†™ˆ_¡¿–lx?“ihf˜ayX­“Ç’I 'Š´9ˆŠ‡â*…Ö…¨¤ªÙ¥w—Ú¦¡s‡k¡MpwnœŒ™xgÏ—á•‹X\“9‘ŸI Ž…¶9ω̉Ï*£… …ë§žª’®£–Ô¥Ã©Š†t ô¤vœœ)Ÿ‰g9—sššX ’º•£Iî§:‰‹¨+2„E†«¦›ªm·í•Ü¥¤±½…‡ Ë«¨uÊ›ñ¥©f’—!Ÿ¬W©’L™ Hïh“ˆ:Qˆzq+Áƒ…‡\¥¡ªTÁ”Ë¥p¹æ„ †²Ôt뛚«Ñeô–»¤ÍWR‘Ö¢HæŒÙ–b:“‡Ö0,P‚Ɉž#¸ù{ Ž ²ûzËj­ zäpv§7{aa¥¡‘|4RÛÞ}MCØ–~5 T€(&JŠ‘Àl·ãƒ6ï±æ‚<~ì’oê¦;«š[l1ê“K€÷%5Œ0‚š÷„„€»‚ñr~´¶‚#eF­ï¶Xc§(œK¢ LÂ>ø™T‚2g’F‚—%ä‹.ƒ%½Áp‹oغ„‰¾r6³­ˆGe ¬ä‡XK¦†(KºŸC…`?B˜N„»2â‘C„1&‘Š-ƒµ{À‡’ˆ¹Pqë²ÆŽ4dÑ«øŒGX-¥+ŠƒKÆžLˆÖ?—R‡>3RH…º'1‰2„E2¿¿™£>¸â–ºq² “âdŒ«8‘,Wÿ¤`Ž•KÁuŒ?®–q‰˜3­`‡*'¶ˆG„Ìۿ Î~ݸ9 q@±a™^dAª‡•ÕWÑ£¨’lKºœ±?Ô• ‹Ì3üކˆ(*‡h…6Œt¾‰¨~q·¬£UpÛ°Íž¾cð©êšYWž£–K¬›þ‘ú?ð”ßá4Bº‰Ä(›†’…¥Œ¾"¯O~·@©˜pt°V¤c™©fžÍW`¢k™ºK›W”Å?ü”,ß4‚ŒøŠû)…À†‹…½×¶R}ƒ¶Î¯Éoþ¯Ï©ac=¨Ñ£½v‚ßZ¬µ¼‚WO›­Ø‚/D³¥½‚Q9ùy‚¬/c•ƒ2$ÜŒ£ƒÑy˳‹ör›ÄŠ$fV¼Gˆ—ZÆ´‡_OŬœ†iE ¤ƒ…¢:yœ>…/þ“䄊%š‹{„( Ê³’cr²Âð fe»-ŽZسYŒOç«vŠfEQ£^ˆÃ:þ›‡H0’Ã…Õ&WŠY„–´ÉИ¹r½Â•ÁfgºX’ðZײ{QO쪇ÙEq¢d‹{;1š‰40î‘ņó&¸‰g„ÄµÉ Ÿr®ÁR›0f[¹†—•Zر¤”5Où©­ýE“¡ñ;q™'Šý19Ù‡ö'ˆ†„ñ È^¥Dr‘À› ‚fE¸Êœ ZѰߗäP¨å“ðE« °8;¦˜GŒ£1|ýˆé']‡¬…/zÇÏ«prvÀ¥¼f)¸7 aZ¶°A›hOò¨1–ÁE¢Ÿî’[;¡—Ž1²3‰Ð'ˆօƒLÇP±irA¿nª×f·z¤¢Z«¯xžáP§u™…E—Ÿ”m;³–È›1ñŽlм(R†…åòŒzåy-Û°{*y_Ć{y·¬™{îzL” |t{z€}{ü`“}¦}F›~J~.,ƒ~ìmðyX…Ù8yƒíÂ"z=‚óª|zЂ@’_{|Íy‡|-‚`D|é]Fî}­N-‹~rRí³xÁÖÙx‰ŽE¿ÍyŒ ¨tyÌŠÄzˆ}xŒ{Z‡_ö|0…¯G>}„g.~~ƒ'ëˆwœ"Ô®w˜~½´x1•¦Ÿxï’XyÁ8wŸzŒ”_{‡ŠG~|ˆ‡u/V}“„æéivC§ÁÒ€vÒ¢û»¤wyžm¤Ìx;š&Ùy–v£yþ’4_6zùŽUG«|Šn0}†çeuÓ³ÄÐ…vb­Ö¹Çw¨£(wÇ¢’Œ‹x:u¿yˆ—ö^Ëz„’®GÃ{’U0¶|§‡óåvu´À5Ϊv7¹¸vв¡žw†«5‹PxR¤†tÜy2Ó^Sz%— GÇ{',1<|0‰>ã”uÑÌûÌÎv;Äo¶JvÀ¼ wc³ý‰ÿx «ìsâxó£À]ÇyÖ›lG¸zÆ’ô1±{»ŠháÏvÙêËvfÏï´©vÒÆHž¨w[¼åˆçx³}rüx©Ä]6yŽŸÒG zh•´2{F‹âoƒewÌæ‚Ów‚·‚Vx ÒïxÞŠ¢yßr¢e{ZØ0|kC€ø}Û+/€½]à‚‚[ÊšŽ‚´õ-€Ûžõ€à€xˆš€·€Nq¸€”€YZ…€v€‘CX€X€Ó,(€6,Ýá€ÍtÈf€m‹h²Ô€!‰“툇#Ó†¸pÑÆ…ŸZBÀ„§C¦¼ƒ¿-¹‚ßÛØܘRÆd†•1°êE’H›o¡…Ú 4oýŠïYöˆÁCå'†-Ó8„}Ùß+£lÄk~ÞŸD¯ ~¤›@™Ê~—p„‡~u“Ño"~zPY¡~‰ŒÜD~‰l.ˆ~³…ú×ü~Å®åÂ~w©š­N~=¤q˜D~ŸrƒY~š–nY~•ÈYI~ûD7~Œ)/&~,‡RÖ-~¦º¿ÀÍ~N´7«©~ ­×–Õ}Ú§Ÿ‚:}»¡}m”}®›UXë}©•DM}¦Ž×/´}¤ˆˆÔk~¼ÆØ¿~S¿ ª}ÿ·j•l}¿¯ð }ލlÂ}l òX…}P™BDY}7‘x08}‰¢Ò¿~îÒò½m~wÉÛ¨~ Á”}°¸W€}h¯£l}/¦¥X|ükDb|Ë”0¹|šŠ®ÒŒu|¾!Ьuú©´‰Vv®•ˆw¤€ †úxÖjÄ…ÞzDU+„Ã{Þ?‚ƒª}’)Õ‚”OÐ[ŠË€ ¼‰k§ÈˆB“j‡~â~Ò†~úió…NTè„ Í?Òƒ€`*¼ÿ€ýÎV‰®ŠºˆpˆÔ¥ö‡E‡W‘Õ†2†}š…8…!i.„F„YT§ƒSƒµ@‚aƒ#+“n‚šÌuˆÒ”â¸F‡š’0¤C†w±b…kk|„x‹Xhyƒ‰jTf‚ª‡˜@[Ä…×,U€Ü„ʦˆ,Ÿn¶Ž†ý›Ë¢¥…ã˜BŽò„Ü”á{\ƒç‘§gÁ‚ýމT$‚‹|@.ˆy-€E…zÈë‡ÉªT´Ö†ž¥¡ …„ ú„{œvzNƒ€˜g‚‹“´Sâ—^@»€¢‹ -›¬†·ÇA‡¦µ“³1†u¯¨Ÿ‚…T©ÙŒ?„A¤*yLƒ8žfm‚1˜ìS)“@@â€..‡ÖÅ£‡³Á±¬†v¹Þž…J²ÞŠù„'«þxCƒ ¥#e¿éž1SX€Æ—#A¢.Á~~ˆÜćÚÌT° †•Ä œ‘…W»é‰·„³äwQ‚æ«Õe©£ˆS€i›A,)’s/U}ê‰ÕÂä•tj¯¿’Ét󜲓u»‰«ŽwvÎvyŒx#cŠwy½Ošˆf{ˆ<†`}o(†„b^Àÿ“Í~X­ë‘¬}Ý›}¤ˆ6…}²u[‹}ýbm‰¦~ŠOn‡¨C |Ú-ï1ˆý³&žtsê¡q›At„Þ˜uT~s•vtm’wã[µy™JFŒ{ƒ8Љ}‹'T† ±ŒO}6Ÿûš,|쎗|Ô}Z”}lF‘3}w[:ŽF~(J/‹R9#ˆ]ù(…j€ö° œI†…ž•™/…RP–)„T|P“8ƒ“kVƒ ZÄu‚³JŠ‚~9o‡¢‚^(Єµ‚F®¡›y±F˜`´Œ$•_‹×{V’rŠ'jÇ“ˆŸZSŒµ‡9Iø‰Ò…ì9´†é„²)zƒÿƒ€­GšÔ˜ûœ—Ä–-‹”È“hz^‘Û½j ŽöŽ.Y匋¶Ià‰'‰N9õ†7†ô*ƒD„ž«øšd¢šš¾—YžË‰Ö”_›y`‘o—UiNŽ“¹Yy‹*IʈŒ¤:5…މ"*«‚‰…¢ª¯š)¬‚™—§ˆ¯”¢²xe‘$óh”Ž(™DY‹”šIµˆí:v„í‹?+BІ©mš¶x˜L—°a‡“ùªqwoò¤ gÚàžØX£Š½™ I¡‡Œ“/:»„UL+߇g¨9š!À/—–ü¹1†d“è²;voÒ«Xgª¤vX:Šh}I‡–m;ƒ¿R,€iˆ4£­¨Ht“_¤$t–ƒZ ufs¨›óvd+—òx T«“ñyÐE)ç{Ì5°‹Û}é&9‡Ï€¢u§!|¿’D£|‚‚bžñ|rÜšï|Çc—–ü}VT]“ ~E+6‹ €&è‡4¡K¦…s‘0¢„joöƒ”r™ý‚üc –‚›T’*‚cE*Ž9‚J6XŠ=‚G'†<‚M 0¥AŽ)¡(Œ?€ˆ"ŠqY™,‰%b…•A‡ÔSÉ‘Z†™E'l…t6£‰r„b(-…qƒXŸ¤’–Ÿ6 ‚”®œ‘™pž˜‡5añ”˜ŒìSy¨Š´E$Œ¯ˆ‰6숭†j(Ä¥„Ož¤ŸyŽ0 ›û~Áœ˜ŒoÚ˜•6ag”‘ôS0޼E$Œ‹Š76‡òˆ])UƒÛ…2 £Ç¨Ž+Ÿ²£ø}Ó›¨Ÿ„o—¤›0`Þ“–ñRꊒµE$‹jŽy7‡AŠ=)烆œ£š±±Œ=Ÿ¬|ô›l¦‡nT—Y¡/`J“?›ëR–¦E%ŠÜ‘[7ˆšŒ *|‚R†¿› £†º™‹2Ÿ]´{ø›<­Žm†—§6_Ä’ê ïR[ލš™E*ŠS”78…õÕ+“‡u”3²”tº…z­mu)w¨Fuèi&£*w [~žx‡MÕ™zR@3“Ú|X2ªŽ¨~„%+‰q€¿“k±^|愽¬7|›vu§|™h¡¢|ë[# }…M³˜~Y@S’èZ3 ¾€x%ψ¢’¨°M…„«&„uЦƒ?h ¡‚¾ZÏœ ‚uM”—‚S@o‘ø‚O3gŒÖ‚c&m‡«‚‘æ¯iŒäƒJª;‹Au1¥%‰Ãg  ˆsZ›!‡LMu–!†3@…‘….3¼‹ó„>'†ÊƒW‘(®§”Ì‚«©„’kt¢¤n!g#ŸeöZša‹åMD•W‰ä@™@‡î4‹†'‘…î„k®œÒê¨ð™‹sö£Ø–_fšžË“RY¿™¿^M ”§t@®€Š‘4]ŠN‡³(…„×®­¯¤ý'¨~ °sG£`œfžL˜™Yj™3”¿Lþ” ë@ÂŽÑ4«‰Ž‰N(¡„F…Žò­a­7€‡¨.§âr¯£¢ÄeŠâÙXÿ˜·™LΓ|”P@ÔŽ/“4ùˆÖŠÙ)*ƒx† Ž7­,µ8·§í¯q¨õdôƒ£X¯˜FmL³’ö—¹@ì’’5Iˆ"Œ])²‚«†º„‡½=uÞw¬¶ùv,k°×vÆ^¿ª¿wÎRѤšyBGžU{ ;S—ó}/¶‘M$%‹ –„5»ú}€wXµº}"j¶¯›} ^†©‡}SR¸£g}íG.~Ç;”–ÚÑ0(t€ý$ÈŠ‚8ƒäºØ…w´“ƒþji®sƒ<^L¨`‚ÂRŸ¢D‚…G(œ‚q;ѕɂ~0•k‚¦%h‰‚ÜƒŠ¹åŒ0v®³“Š£j­n‰8^§Z‡þR‚¡>†ìG0›…ò<”Ç… 0ùŽm„>%üˆ ƒ{ƒ3¹“Vv[²»‘iÓ¬”Žð]Ö¦{ŒëRY [‹G.š&‰5<1“Û‡r1Q…¼&~‡„ ‚иWšƒuö² —mix«à”q]‘¥Â‘™R2Ÿ›ŽãG+™\ŒBuÙ‹Ãy>wJsýzVxÕ[´{€zCV|Ÿ|H*ú}·~#â*sÎ}æÌ»tÓ}·¶ýuâ}¥ Íw}ÁŠ,x+~róyf~^[Wz®~ÙC§{ùl+ù}A€߸rD‰”ÊTslˆ ´¾tž†»žáuÞ…¥ˆªw)„ºqïx„ƒèZøyëƒ.Cï{[‚‹,å|ÌùÝvq”ðÈ"rN’N²±s“Ö t蕇UvH‹pöw¾‰zZ•y>‡ƒD)zÇ…œ-¹|TƒÍÛNp- zÅýqnœ¼°³r½™›at•©…æuŽ’UpwZ;x¤‹ÌDUz?ˆ‘.m{Þ…`Ù=o¡¬UÄpè§p®Ür9¢¢™Ësšó„­u ™XoWv”»Yäx$DtyËq/ {f†Ê×Bom¸zÂ!p­²^­"qô¬V˜NsL¦fƒˆt³ }n‘v-š}Y‡wº”hDŠyPŽD/˜zëˆÕYoÄÈÀKpŸ½d«pqÒ¶–Ðs®ð‚Ltt§´mºuâ KY!wa˜¸Dšxå‘ 0#zl‰TÓˆo¯Ñ ¾pÃÈp©ÖqÝ¿ú•ps ·–@tR¯lýu«¦1Xºw D¥x€“Ë0³yêŠ~Õx}½p‹Á }Ýq¸¬c~s—i~1tƒ‚~iv(l?~±wûVyô?ìR|)µ ~Ó|{“¾º|g{™ªI|±{¿•ž}|€¬}`|škX}É}JUÄ~7~ @4~¤*£ýÐÇz¯†«¼†{…z¨<{„w“Ü{ꃫQ|aƒjz|炘Uy}r‚?@y}üú+|~„Äΰy’‘Œº…z P¦`z6’Ezþ‹L~{‡‰i±|!‡êU0|Á†Z@¶}_„Ý,A}üƒk̬x½œ—¸”y<™P¤“yÀ–²zK“ |Ùzàhæ{ATç|'Šp@ê|·ª,ð}u„êʾx8§ê¶¸x¾£…¢ÞyDŸ.+}»ŽY-N};‡ü·˜€ÔÄΤä€x½r’´€¶*¡®Úp)§“_s~|  Nå}Õ˜ƒ>f}6Ç-í|Ÿˆû·bŽÏnL¤ü}ow’ÌŒpۀъ±rˆnõ‰Ct]‡ºvÄKM†+y19E„¾{³'ƒe~2µndx£+Œ/xS‘1ŠãxÁw‰ypmíˆ?z`\}†Õ{•K…c|ô9Šƒü~d'÷‚žÙ³•Œ%‚¡zŠú<²‰À€¯~:ˆ€_m‡D€G[î…ò€iJÞ„˜€°9σ? (Áål±á‹)‹ÓŸè‰ýŠ%ŽMˆÍˆ¥}‡œ‡Vl.†e†5[n…#…€ÀŽì–qcŒÊ“b‹ŠšT ˆX9E»† Šl7‚ƒ±‡¤)PV„ÜŸŒ’å¦8j¹¡Ý¨Ž–žp|Œm™‚aåŠ1•…S²‡Ü‘™E´…x±7̃ ‰Ä)ꀞ…ÔžI’¿¯üŽ>Žª‚~›Žd¥/oŒ/ aD‰ßšøS^‡r•ôE´„òê8‚m‹Ô*‹熶’²¹}€³}qŽQ¬Ãn£Œ¦Œ`“‰« nS‡šOEº„v”!8uÑÝ+01‡Š™S¡±m󉱞€oz|›>p…kÆ—írG]x”tgOV‘%v×AB¯y}3)Š3|@% †· ˜ ^v›ˆ”5wy™ýw¡k –»xˆ\õ“syºO{-ABŒ»|Ë3x‰Q~€%®…å€>–ÍŸ)O‡„œ~íxª˜Ñ~ºjV•›~Ã\v’aNÌ{A>‹Ð€3Èu€¸&M…i•¢ž0‡ë†€šþ†ÊwΗυÆi¥”Ÿ„ï[ý‘k„ENŠŽ4ƒ·A9ŠôƒA4 ‡ ‚ß&å„F‚„”Š`‘…”š0Ž w—Œ¿i“ÖŠý[£‰\NJj‡ÔA@Š%†]4V†Ò„ñ'xƒyƒŠ“uœÊ™`„“™š–zv&–m“¥hO“?ï[ŽYNŒÀ‹ØAJ‰m‰b4¤††í( ‚¯„z’eœh¢Wƒ’™4ž^uD–š„g˜’ΖÓZŠˆ“EMÖŒ0ÇAVˆÊŒP4ö…[ˆÔ(Ÿé…V‘`œ'«R‚«˜î¦Ett•µ¡efï’tœµZ˜(MŸ‹´“ªAiˆ7-5M„°Š©)7%†"cœ´Ÿ˜Å®s{•„¨Af%’6¢”Y‘ŽË Ml‹G—‹A‚‡±’5¨„ Œx)Ñ€c†æŠK«Ýn‚|G§žo™nÄ£Np÷aÉžôr·U?štÛHá–wT<•‘Œz0SŒñ|Ý$ˆQ½‰gª€vž{‚¦Ewn$¡üwªaR­x Tô™ZyâHÆ”õ{e<±~}0ª‹÷~à$©‡k€³ˆ‘©D~ºzË¥~nmŒ À~U`Ýœy~{Tª˜0~ÝH©“Þj<Æz€0ý‹€Ù%=††¤‡»¨B†¯z£õ…¸löŸ­„â`i›i„9Tf—"ƒ¼H’܃S<ÔŽ‰ƒ1JŠ‚Â%Ë…¥‚‹†ò§eŽ¢y}£Œèl{žÎ‹C`š‰¿T–Hˆ[Hm‘ü‡ <ò¤…Ë1‰:„•&T„ȃd†,¦¾–¤x½¢n”kÕž%‘|_ƒ™ãSÕ—ŒÏHP‘>Š=ŒØˆv1ðˆg†P&݃ñ„+…h¦Dž¼wú¡ï›k)¡— ^ý™Y”OSs•‘'H6˜Ž=+Œ"‹2D‡¢‡ó'gƒ„ℬ¥â¦Úw`¡Š¢2j 4¿^‘˜ß™S$”|•nH‘q=O‹~|2†é‰†'ô‚O…Žƒð¥¡®·vŒ¡E©0iÝœå£Ò]ú˜ƒžªRÔ” ™µH ‚”Ï=yŠèð2ú†6‹(ƒ†7zÿ¶soko°ûprc]« q¼X¦CsoM7 ÎuB’›3x 8 •{zÅ-³}¦#‰å€™z‘µwn°¯™wec ªAxWÞ¤èxúM Ÿ|zDB ™ó{Ð8A”P}Œ-õŽ›g#²ˆßNz$³¿~„nP®M~Eb½¨ó~;W¨£~sMž;~æB«˜Á†8s“0€H.WŠ!$G‡Ú‚yµ²¬…Ämó­*„õbt§É„AWt¢uƒ¹LïƒYB±—©ƒ8Ÿ’$‚å.±Œˆ‚É$Ó†à‚ºyE±ÃŒømœ¬6‹ub0¦ÏŠWB¡zˆ±LÒœ‡~Bµ–¯†a8Ì‘,…U/‹—„U%P…ùƒ[xÓ±”)m2«s‘ÐaÖ¦ ŠVÿ ±eL¯›O‹bB¸•؉x8÷M‡›/XŠ·…Ã%Æ…ƒíxb°l›Xlª֘au¥h”éV·  ‘ìLŠšŸBº•Œf9"…‰¿/«‰å‡&?„D„vwð¯í¢xl\ªQž@a¤Úš1VuŸr–\Lf™ý’ºB¾”o<9QŽÌ‹Î0‰ˆd&ƒe„úw¯‰©[ká©á¤U`¬¤aŸlV$žïš¿L@™m–PBēҒ 9ƒŽÕ0`ˆU‰¨'J‚‚…|leÀ¸pa÷ºyq_Wø´-rNt­Ðt7EQ§FvV<_ txÔ3‰™z{˜*Ñ’w~†"%‹tln¿?w—aï¹ wßW첿xtNr¬ZykEk¥Ìz½<•Ÿ |Q3á˜,~+K‘=€"ÊIülb½ô~~aé·¬~AWç±X~?Ntªö~‡E¤p<ÄÀ¼40–ð€+À |#c‰‚lY¼Ð…aضy„]WÞ°ƒÇNw©µƒdE£:ƒ$<뜓‚ô4u•Í‚á,)Žî‚å#ù‡ýƒl?»Ø‹‚aصdŠ&W䮸ˆáN¨‡¿E¢†º= ›y…Ë4³”½„ï,î„$$_‡ƒgl#»‘äa¸´ŒºWÌ®«Nx§°‹ÂEª¡5Š=/šŽˆ\4ì“͆É,Ì…?$¹†?ƒ¹lºI˜0a³Ö•"W¬­`’>Ni¦ñE´ s=P™¿Š¿5$’ôˆ-Œ'†F%…`„ k๫žNa|³$š_Wž¬¦–¨Ne¦-“6EºŸ¢=n˜ñ5^’%Š-s‹L‡B%—„q„dk¹¹"¤.aM²™ŸvWx¬šöNT¥˜–ÃEÆŸ’á=‘˜E<5™‘h‹¶-ÑŠrˆ9&#ƒp„¼ÖÖp)kb“qƒmD­ùróo>˜åttqOƒQuðsjm:wœuœV´ybwë@{zQ)l|ž|ËÔ7nvÇÀo¤w««qNxF–ísy)Ôt²z lDv„{'V`xi|F@dzH}{*e|"~½Ñ´l(‚T½°m÷©„oÏU•q­ €ps‡€ÝkDuƒ€ºUýw€¥@¥y˜€¤+J{¢€·Ï]j´­»‚lžŒ§nŠs“zpЉ@r€‡ªj,t©†UUšvÏ… @Ýx÷ƒ¿,{ ‚¬Í,i’™$¹jkŠ–a¥¦mŽ“©‘×o‘}àq¶ŽpiŒsè‹ÚUKv$‰DAxd†³,Åz¦„5ËhÒ¤Ú·jjÚ ð£Ôlæ Lnþ™/|µq!•WhésS‘rUu–†A/w߉‘-_z,… Éhu°Äµ„j|«¢¢l„¦…ŽØn˜¡q{Ÿp´œUh8rá—T¿u ‘ÈATwdŒa-õy®†ýÇhf¼¶³¹jN¶T zlE°mnK©»zyp^£Tgwr„œ»Tot¹•ûAwvñ$.‘y*ˆFÅHhvÈZ±÷jTÁžál9¹—Œn-²yxp&ªufËr<¢tTtnš1Ašv†‘Þ/?x¡‰vÈiwþj µ xÃkð¡ŠyŽmòâzVpyû{rceÌ{ñtÙQ`|Ôwq<Ö}°z(>~Š|ÑÅóvtܲ»výu«Ÿywúv”Œ"xðw£x¨yàxÖdñzâz,Q {ë{¡=|ï}()}ð~»ÃtBѰŒui|‡vŒDŠ€w¤6wlx¸Md!yà€P¹{Ì=U|6€*)ó}X€™Ávrߊ®®t ‰L›¹u[‡ÿˆýv†ÔvNw¹…Ïcby„ÕPtzJƒë=‘{‹ƒ*²|É‚M¿jqÌ•¨¬¯s“<štlÖ‡ˆu¶Ž…uvýŒMb©xMŠ P4yŸ‡ý=Çzï…ä+^|?ƒÓ½uq ÛªÒrqO˜]sǙņ!u–Jtvh’Übw¹qOúy Œ=ùzaˆ+ý{¶…6»”p¸¬B© r§~–Èsa¢Æ„Ðt­žsu÷™yajwB”ÈOÃxŽ >*yÛ‹G,—{*†€¹Åp·°§oqå±µ•Rs,«Ðƒ‘tm¥ùru¨ `Îvâš!OŽx” >]y[å-3zš‡¶¸p¥ÂÄ¥¶qé»Ú“Ás´á‚HtG­áq5uh¦Ï`?vŸ…O\wµ˜ >’xÝ}-Ôzˆàº €hú§‡€;jÑ• €`lтـro p£€jq~^a€`t2L€Vw 9€ayõ&ó€z|Ý·Ð~4s5¥r~t“M~ÛuVvBo€5w«]®VyIKÍv{ 9Ù˜|à'×»~½µ¬|’}—£q}}j‘•}x}]å}È}…ns~}Ü]~Q~`K}~– :~Ô½(¦€‘³¹{8‡ì¡µ{ʆÃ÷|L…²~Š|º„Êmm}„\c}sƒrKU}΂ì:V~‚|)]~i‚±Ùz2’a zÑ=Ž€{dŽ!}K{æŒ lp|VŠB[Å|½ˆ}K)}†É:”}y…#*}у…°y žez'™ÒzÁ–|{J“}k‡{Áx[7|(…Jÿ|ˆŠœ:Î|⇶*ž};„Ò®Uy§çœÊy£}‹žzYŸ!zêzÞšÞj±{O–±Z¸{­’ŒJÝ|Že; |RŠ7+:|£†¬¬xô²É›Yy’­,ŠWz#§«yÜzž¢FiæzýœïZC{F—”JÅ{„’);P{ÃŒ«+Û|‡!«xì½Q™³y’¶¿ˆÓz°0xœz†©­izÍ£1YÊzòœŸJ·{•í;{6,‚{gˆ)«Ùˆ~hEš0ˆjˆë‡vlwý†ÈnAgƒ…æpØW„ísÇGƒàvÍ6€ƒyð%›‚e|ÿ©Ú†¿qô˜_†lr͇_…ösÜv¾…cu)fŒ„·v¾V ƒæxœFŃ z¡6¼‚@|²&š~~ǧû…*{À–µ„ð{©…é„•{Ãu‹„|e¤ƒ|¥V‚ã}rFŒ‚*~c6üqd'f€·€s¦:ƒã…‰•ƒ³„…„yƒlƒ­tXƒ ƒdÄ‚‚‹UŒø‚AFdV‚7=€¯ü(€ý¤‚âp“¸‚º„ƒJ‚‹®se‚+‰úd¹ˆqU1‡ FL€›…º7þ„z(±\ƒE¢ö‚/™Œ’:‚–’‚Û“«rQãcI"ŽBT­€–‹ÃF6€‰T7Á\†ã)G~¹„t¡iÅ£ÓÆ§Ÿ¬€¾p›¨qF%—ÆbŒ€°” TH€rF(yŒÞ8~Á‰7)â~…ŸñŒ®žf¨ÊÆ)£­p”€Ïž¹b€K™æTŸ•#F0~ãZ8]~$‹{*ˆ}j†ž‹p¸Žl±¿~B2«–oA€Ø¥Ša€@Ÿ¦S’Y™ÉFP~e“Ú8Â}‰¶+=|À‡^m‘|gñ<i¸}`ެk³nn_#‹ap°P‰…s±B!‡vá3ƒ…Èz'$¼„}n›¹Íq‹ÇŽŒr |(4s-m ‹½t—^mŠ%vNP&ˆnxLAþ†©zu3Æ„ç|²%ƒ*~óš"ŽEzGŠ]zczï‹Õz¨l Št{(]°ˆø{èO¶‡`|áAß…¹}ÿ4 „ 1&0‚]€k˜ž ƒx‰‹ß‚¿y¾Š§‚"k ‰W³\÷‡ìxOJ†ihAÅ„Øw4Lƒ;š&Ӛɗ8Œ ŒÃ‡ÌŠä‹)x¼‰³‰j@ˆmˆ6\c‡ †ùN÷…“…àA½„ „Þ4’‚yƒë'i€áƒ•׋Q–=† Š+“¡wƈþ‘i‚‡¼Ž§[á†\ŒdN´„ãŠBA¿ƒYˆ24ÜĆ$'ý€-„”€ŠÙŸÛ…y‰¯œvЈ€˜€hÇ;• [a…Õ‘ÁNw„PŽ”AÈ‚»‹q5.ˆH(–w…“>ŠŽ©p„[‰c¤–uÛˆ/Ÿçh†á›jZà…m—N>ƒÔ’ÛAÜ‚'Ž¡5Š€tŠZ)8~Á†’Šd²¸ƒ‰;¬ôt°ˆ§Dg †±¡½Z4…*œbMõƒp—Aø ‘Î5ðÑŒe)â~ †äšñh€<˜œiÁqê–4k¾d/“®nW‘pÒJ5Ž7sæ=}‹\w.0·ˆzz–#à…›~¹™Jp›— qq”´rÖcƒ’@tYV±v+I÷xD=}ŠKz‹0ÿ‡ˆ|æ${„ÄHŒo—Ìy7~ •™y|p“MyëbÎèz”VŽo{{I°‹ã|˜=s‰G}Ù1C†ž,%ƒï€…‹4–’Ì|ø”XPo;’€ðb¹€¼U™J€ºIlŠÖ€Ø=`ˆZ1……¿^%§ƒ ²Š•Šs|“U‰$nŽ‘‡äa–ŽÄ†ÅU8Œa…ÏI=‰ñ„ö=w‡u„31Ó„èƒ{&6‚T‚Ȉô”Æ“9{"’‘òmÀT޽`÷Ž ŒªTЋ¦ŠÁI‰0ˆö=†­‡<2&„…&ƃŇà”;œz&’˜¾líÆ•…`Ry’yTi‹ H눌ŒÝ=¥…ýŠ+2}ƒd‡l'[€È„­†Ú“ݤëyP‘  ƒl9ZœI_͘CTŠ”mHΈ³=Ë…`2Þ‚´‰F'õ€…‡…Ö“Ÿ­~x9‘\¨,k9¢ú^þŒ²ûSŽŠ+™0H£‡ˆ”=÷„×Õ3E‚ ‹(‘A†Y€–¤ähys[¡|j(f½žl!Z±š~nzO6–Ñq?D“t`9w¸.‹){7"þ‡2~»œ£?p…rŠŸçq‹f!œzrÔZJ˜útjO•hvOD ‘¸x~98ózÚ.aŠ!}K#ІJÄ~­¡Æx‰qמnxîe’›yYß—ŽzJNÀ” {RCñ{|Œ9DŒÙ}è.ª‰"U$…e€È}Ä †€zq"€3dø™»€ Yj–K€ Nƒ’Ì€8CÖV€~9<‹Ü€Ù.ïˆ1K$œ„…À|ëŸyˆrp’œ‡idž˜¯†oY2•H…”NT‘Ö„ÜCÉŽ`„=9oŠÞƒ®/C‡Gƒ+%"ƒª‚«|ž¡uoÌ›AŽ‚c÷—⌢X¬”ŠãN‘ ‰LC²‡Ñ9•Іe/š†n„ò%©‚Ôƒ{Uÿ˜nÿš •‹cF—A’¸X“ßM°c›CŒØ‹C9¸‰Dˆü/󅤆Ÿ&4‚„Fz‘Š n{š œbÝ–¶˜ÉWß“I•:M‡Ê‘ÝCŒ8ŽŸ9툗‹n0U„çˆ8&Ã3…yÏ.¨=m™Â£ib–Rž½W3’ßšFM$N– CŠ‹®‘ð:-ˆâ0¾„4‰Í'T€b…·qâ¯5i7f᪘jê\¦ lÜQÇ¡wo3GéœÀq÷>X—âu4è’æx}+Ø|"ˆÄ›qL­“p»fk©qÖ[Ťxs%Q’ŸëtÃGÕ›Fvµ>e–‚xì5‘£{T+ÞŒ´}Ö"¦‡¿€cp¿¬x*eþ§~x¯[t¢÷y\Q^žpzAG¿™Û{`>m•0|®5En~,6‹™£#2†»1p9ªÒke–¦*`[)¡žeQ-G«˜’Ü>t“û€H5jQ€Ë,‰ŠŽ]#¹…¿ûo¸©º†ªe:¥ …çZé ~….Q›ÿ„ŽG˜—{„ >~’ꃢ5™ŽIƒH,Û‰—‚÷$.„Ý‚®o:¨ÐádȤ%ŒGZ‹ŸšŠ¹P›‰GGw––‡ø>„’†Â5È^…™--ˆ³„s$ „ƒNnÀ¨• dS£k’‰Z'žÞPyš_ÑGT•Ó‹µ>‹‘7‰·5øŒŽ‡È-„‡Þ…Ö%ƒ,ƒånE§ƒœ cì¢Ì˜°YÓž8•aP@™±’EG:•#Z>˜‚Œ’60‹Ñ‰Ü-䇇(%¢‚L„umϦÿ¢íck¢CžºY`¦š˜Oí™–§G”…’ï>£ßa6m‹#‹è.J†Jˆu&2d…cƹ^jZi³Êk¶Qm®:mŸHÛ¨œoñ@¢¢Órµ8¢œØuá0Á–¼yW(øˆ|é!9ŠQ€§c–·½qZA²/r-QM¬ s€Hʧu&@­¡Kw$8Ì›byj1•Z{á)fD~t!ȉ*cg¶è†¢+Š…$ „Lƒ„bޱ:—åYv«†”ÛPÈ¥Û‘úH£ =N@ùš™ŒÖ9¡”àŠ…2~ˆK+}‰9†$‹ƒWƒåbV°ª¿Y9ªß™úPŸ¥$–\H•Ÿ’òA™éÀ9Æ”1ŒÆ2ÀŽc‰é+äˆ^‡%.‚E„BÉj¨dŵól€gR¢žnuiøŽøp‚l´zðr¡o|f€tàrWQÀw0uI<âygxO'û{“{`Ærh oñ³}j:qc hlzrénÉtƒy‡q"v-e›s’wãQnvy©=&x‘{(Ú{ }]Ããe¿{6±&h,{žVj§{â‹cm-|\x7oº|åd·r]}tQu~ =_wÉ~¬)®zXÁŠc冮üfw…«œni„ì‰ÐkÉ„Cwnƒ£cÝqRƒP´t1‚d=‘wÈ*pyú=¿Nbe‘à¬ïeôšœgÞŽˆHj¬Œ8uÔm‡Šbc&puˆ†Posp†¥=Âvs„Å+yx‚ì½-a[qªód'š\˜Õfü—K†ËiÝ”>t·lÊ‘+b}oÄŽ P2rÍŠÛ=òuᇨ+¼xú„v»$`Á©#©cޤӗ$fg …`iLœLs¨l<—ûaÖo9“ŒOôrE > u]Šy,Wx{…ç¹0`|´Â§Gc8¯>•Œf©Ï„ há¤\r kËžÊa/nÆ™ O¸qÑ“*>Otä:,ôwú‡E·W`]¿ÿ¥‰c¹““ýeų‚¾h’¬tq«kn¥¥`“nažOzqi—G>}tpò-—wwˆ—»Šr2cÈ©-sfH–Öt÷hè„v^k°rwÂnž_yy-q´L±ztê9Ê| x3&Ô}v{‚¹ o¸nf¦ëqboã”Ús qw‚Øt­s0pÑvIu ^¬wéwLdyy:{3{C'¥|Õ}r¶¯m†y¤ÇojyŠ’øqGzHszµo£tæ{|]çv¸|ZLx}L:Bzd~K(m|7U´„k»ƒá¢ÍmƒB‘:oÆ‚¶ÕqÀ‚Dn‹s³î]1u­§KÒw«m:zy§>)&{¡²qjOŽÆ òlv™n—‹s~wp®‰àm|r¿ˆ`\ˆtцêK™væ…|:²xü„)Ð{‚±°viN™×Ÿ"k†— Žm·”?}&oß‘ƒl‚qÿŽÔ[òtŒ(Kiv?‰}:êxc†Ò*pzˆ„'®‘h°¥ijꡌm{êoI™(k›qk•H[hs‹‘bK@u¯r;#wÕ‰|+ yþ…ƒ¬Âh[°+›Éjªö‹l½¥Úz»ná Ìjºpû›»Zâs–—Ku/‘_;`wNŒ+«yq†Ì« h0ºïš!jX´Î‰”l{®¤y„n‘¨siÜp›¢1Zar¦›ÍJùt¶•H;ŸvÊŽ®,Lxሠ­ûyõbøœszÎed‹"{¡gúz|_jËiD}mÜX€}q)G³~t6¼~´x#%§V{­«½w—mšlx¸n‹‰`yÂp)x¦z²qûh.{‰tWÐ|PvBGm}x¢6÷}Õ{&t~œ}‹©”u„w7˜vvÒw»‡¦x x\w5y'y,gz)z,W { {WG*||Ÿ72|þ}÷'6}é[§˜sÌq–¢u9€ú†v–€™uÎw×€_fxý€OVxz€aFô{(€‰7o|7€Á'è}B¥¶rc‹Õ”úsôŠY„‘umˆìtœvɇex†rUéy7…bFËz_„d7­{„ƒp(Œ|§‚‚£éqe–a“ks“σBt‰‘Is”uóŽÞd`wAŒ‘Uxx}Š[F¯y°ˆ/7ìzâ†)(|ƒÞ¢.pÅ¡ ‘êr`Mÿs陣r—uV–c¯v¦’©UwãIFžy‹ì80zJˆˆ)Ä{}…  †pb««tqþ¦½€¿s„¡ôq—tëLbùv3˜»T²wc”0F“xŒ8|y¶Šû*ezä†Pžñp,µìŽäq°UsCª;ppt¢¤yb+uÙžÇTIvô™FŽx“K8Ïy#h+ zG‡u ‹‚brê‚Zdɸ‚gUp‚†j,`Â[mYQÂpÓBâªtz3Ê`x5$†){ðž†ÞlŽ#€Um|~4€¬o"nÊ€Ûq _Ú€âs>Q6€Æu±B¨€œxN4€xzý%P€Z}±œ}Ùu¡Œa~}v3| ~þvïmrUwå^Ô…yP–”z†Bq–|4B–}¸& –dšÝ|0LŠ–|ô~ðzö}–~»kó~~µ]«~^~èOé~ˆKB?~¦Ê4‚~À[&·~Ý€û™+zÒ‰#‰${±‡Ðyº|j†jñ|ý…’\í}_„¸Oy}Ÿ„B'}Ôƒm4Ã~‚à'R~5‚_—‘yÏ“#‡ôz´ÈxØ{vއj`|Œm\•|…Š€OF|Öˆ·B&}‡5 }X…K'ç}—ƒ™–y(=†³z™¾wàzÊ–di­{i“6\${Þ6O|2UB,|vŠ5W|µ‡¡(€|ö„À”‹x¸§I…sy˜¢¤v×zZž3håzô™õ[ž{a•áNÑ{§‘çB={Þï5±|‰ç)"|O…Õ“ xk°÷„yT«gu„z¥ïgÍz¯ ŸZÜ{ ›{N{{7–oBS{P‘Z6{uŒ')Í{¥†Ú’àŠÙb8ƒ Š8dtɉŒgfˆˆ»iøXÕ‡·m9K~††pÎ>K…Ct™0þ„ x{#–‚Û|b‘&ˆ«kF‚'ˆBlÍsއ¿n„e‡p…X†9r×K…9uo>+„(x51>ƒ{$E‚}ñ††´tT€§†tu r9†uëdm…‹w WF„ÕxiJƒþz>ƒ{Å1|‚)}š$í;wý…}f„í}IpЄ¨}Kc,„=}}VOƒ£}èJ‚æ~…=ã‚@1¼J€%Œ€v€éŒ‹ƒÄ†£}惧…œo΃s„°b`ƒƒìU½‚–ƒ[I³î‚ô=Ü9‚¦2€}‚g&!¿‚2‹)‚½|ï‚¢Žo%‚tŒb‚%ŠQUŽ®ˆ¾IŸ‡P=î€o…ö2OÁ„¤&±ƒT‰Ï‚™}{çè–anc»“ga€mŸUE€ùŽI…€d‹”>½‰/2¥†Ê'G~b„bˆ‰…¢æzÏiž­m};š§`Ô€ê–ÚTØ€o“@I_ÏÇ>(ŒX3~dˆà'æ}­…a‡O/«üy˜ ¦Ýle€Ý¡Ù_ò€‰TC€˜kI&M“ñ>K~ƒz3p}ºŠð(Œ|õ†V…4”bQw_’vd¢j Ûg.]YjQ?.mgEŠ‹q9úˆátô.c†­xõ"¾„~|üƒÏ‘åjØv8†lpi n<\§ipUPÅ‹žrÁEI‰±ut9ö‡±xX.§…«{R#Rƒ¥~S‚vsVu޹t8h+UuA[ä‹Ïv‡P;Š%x DúˆbyË9膎{®.愱}¤#å‚Т2Žh{Ísú){ög<‹Ö|6["Šg|¡O±ˆ×}@D­‡3~9Û…‚~í/&ƒÄã$t‚€â€„is ‹Þƒ¸f€Š˜ƒZ•‰9‚ŸOO‡½‚OD~†,‚$9脎‚/q‚æ‚ %;‚~àŒr&ŠÖ‹we׉˜‰æZ#ˆCˆ|O †Ò‡=Dh…K†:ƒ¶…/„%Ž€zƒ}Ç‹@•àqBŠ“.e*ˆÔYª‡Ž=NʆŒ DX„Œ‰þ:'‚÷ˆ0Z†&º„|·Š³žŽp^‰šÑdoˆ?—BY"†é“ëNw…vÉDEƒèÊ:U‚IŠØ0ƒ€¢‡å&·~ø„î{¶ŠD¦þo\‰¢Zc‰‡ÄØXm†j™ŒN„î•yD'ƒT‘Œ:†§ª0ïî‰À'T~4…Ïwa«b·k8› e_‘˜gg“Tƒ•ªj‚J’ÂmÜ?å®q”5ꌂu‰+ð‰My!ï†}ºvS›˜j¸jc™l_^ò–n@T“äpoIÄ‘rð?ÎŽ6u¸6‹K¢™z-Cžâ{4<„™4|Q5©”p}‰.÷š~Ø(bŠÃ€(!ã…ä‡Z ªÐ~æR¡¦Jí¡?^C•œ±<—ã€5É“.€‹/-Žr (¯‰·"C„ú‚ZY©¤…YRe¤Ú„ÅJÀ !„DC|›wƒØ<”–̓†5ç’ƒE/bpƒ (úˆÇ‚Ð"Ÿ„!‚”Z¨§‹­R)£ÞŠCJŸ)ˆõC`š„‡É<••݆¿6‘2…Ë/šŒˆ„â)L‡åƒö# ƒDƒ Yä§¾‘ØQü¢û›JpžA†CT™š‹œ<›”ý‰Ù6%\ˆ0/Ù‹µ†˜)©‡ …#‡‚[ƒzY¦§ —ÂQâ,”ÒJGc’CD˜¼_<¥”+Œä6I”Š…0Šîˆ>* †1†$gƒå»e+^_©Œg|a†—}iìdÍ…Bl„h,rÄo8k•_ëroLÐur¦9­wÆvJ&…z‹yø¸ÏaåiP§dŸkp•Ogvm¡ƒmjgoçqfmar<_pwt›L‹s¡w9ðvÍy'Ryø|¶;_tP¤Àbu\“Ge9vyÂhpw©p(k­xç^Goz%L6ro{h:&ué|²(ye~³ß\¼b¢œ_ùY‘kcO`€?fºuoj1–]‹m¬KÕqiÃ:SuÒ(Óx×籞ZΊ® š^<‰ƒ¯a¿ˆe~ÒeU‡Pmìhü†=\Ôl¹… K£p‹ƒý:‹tk‚Õ)xP¯¯{Ya–ž£\ò“Åô`–‘{}edM6lâhŒë\1k犎Kjoψ(:ÁsÍ…½*"wуT­pXr¡ŠœÄ\žŒM_ÄšŠ| c‹—kèg^“”[—k?óK1o3ŒD:ósAˆŽ*½wV„Û«zWâ¬à› [{¨&ŠË_+£„zÊbðžãjëfÇš(Zÿj±•GK n²L;(rÄ‹L+Sv݆M© W·Ó™R[²)‰I^½¬zy‘by¦¶jfH ÌZ~j3šžJ×n9”F;XrLþ+èvd‡·®Ål`]­“nF`¾Œvp9cõ{tr8gWjt8jßYbv@nHxOrc6Êz[vI%m|fz6¬FiDh›Mk…j5Š}mÒljyÕp nÆiGrlqEXžt¼sâGÚwv˜7yhy^&1{Á|+©éf…r˜™0is²ˆ§k£tåxRn3v6h%p¿w§WåsOy/G•uæzÈ7Bx|l&ï{~§¹dB}#—:fû};†ôiÃ}fvîl†}©g"oD~W?r ~sGUtÝ~é7yw¯e'£z‚䥤bd‡ï•ieU†ó…chG†u£k2…,fn„eVqƒªG"sú‚ô7³vô‚B(Ky𒣪`ý’Û“™d¼ƒÑg!Žªt]j'Œ¬e2m$оVp+ˆÔFùs:†ë7îvN…(ëyeƒ¡Ç` Ò‘ác)š‚WfH—Fs-i\”"d_lc‘ U˜oxñFÙr—ŠÑ8,uº‡®)†xà„ŠŸö_o¨¯Wb¤1e¯ŸÖrhÅ›cŠkÔ—MUné“F¾rލ8ku/ŠI*x[…æž=_ ³(Ž·b­Á—e4¨\pöhC¢ùbÉkI‘T±n^˜F¦q’y8¬t©ŒÛ*¹wÖ‡:¢sÒ]‘–uC`xv±c;qºxfžbFy`j@Räz”n#Cv{År/3ñ|ûvS$\~9z}ŸÌpØgržiºtZkSpNvmÀa5w“pfR8ys>C9zŠv94,|yG%}…|Z£n6qªpöPr12¡v{#[€zÙ“_xËfAƒùyüh=u;{jwg|löYf|Ðo¼L }rrÈ>Æ~uÿ1m~ yI$ @|—‘qv:o³‚Jw«pÕs¹xür,eÄz$s¾Xl{%uKr|w>’|ÒyÎ1©}¨| $µ~~O®ty)€…uµyerw4yÔdZx‹zrWUy¶{MJÍz¾|\>`{µ}‰1æ|Á~¸%\}ÇôørS‚Ùt‚'pÜuÁœcJw>9V…x‰ JUy° >GzÐ%2({÷D%ù}nŒ\p쌴}ÚrËŠúoõt‡‰ab´v‡ôV0wƒ†»J'xÅ…¬>Iyû„²2o{?ƒ´&Ž|„‚¸ŠÐoñ–¡|’qÔ“Ánüs›‘bu;Ž”UÇvµŒPIúxŠ4>Uy@ˆ)2¾z”† '${êƒï‰Yo> q{|q"œyn$r꘹astŒ•1Uhv‘ÞIÓwXŽ­>qx ‹…3yòˆS'¾{J…‡ïn°©ëyøp¢¤þlÉrp :`St›£T¢uŒ—GI€vÌ“>‘xŽà3|yPŠ–(]z§†Aˆ€„\®zX„ _l£„'b¢_‡„f R÷ƒÈi×FǃPmû:¶‚ÅrX.š‚=vÓ"p½{U†ÂIe§xÚ©g¸kdðiþ^‰‚ l‘RAûotFYÄr¡:™|uý.Ø4yq#€ð|ë…~Çn’wXeoÚjâqQ]h€0sQg€QuE×€Qw7:s€By”/€3|#¯€%~|ƒŠ|¹wvuÇ}xwôh~x”\~›yePg~äzrEE {³:M%}/QC~ƒ$Kaþ‚zú€‰t‹{Ø€'g•|Þ[H};½OÍ}¥ÔDð}ì€:D~+€w/–~l€ã$ß~¯Y€®y‰Ês™z€ˆkfô{W‡%Zô|† O«|…#Dã|ò„h:V}KƒÃ/á}©ƒ%%o~ ‚‹Uxˆ“r—y}¦f@z]ŽVZ†{Œ;Ot{­ŠWDÖ|!ˆ:v|‡†õ07|ò…N&}aƒ§~ wÆœQqŒx½˜Éery •sYöz_’YOz÷wD¿{nŒ¾:¡{׊0š|B‡h&˜|±„·|Íw,¥:p[x! Ìdhyœ}Y'yɘcNšz_”†D”zÎÓ:Ì{--1{“‰|'5{þ…Â{¢Œä\Ón׌_«bš‹6bÄVüŠ=f7Kð‰jAB‡±nN6¸†?rÅ,1„ÑwZ!¡ƒi{÷z-Š7eJm£‰¦gka£ˆýiÇV?ˆ.lrKp‡1op@þ†r¸6µ„ëv2,tƒ½yÃ"0‚}\xʇËm¬l{‡go`©†ép¾Uu†Er›Já…xt»@¬„”w6£ƒ¤y‘,°‚°|#"Àº~¼w|…ÄuýkT…qvÂ_­…wŸT¦„—x¨JPƒòyè@Xƒ:{P6‰‚w|Ö,ë´~n#N€î€vD„~rjaƒÒ~g^íƒ~lTƒ*~”I储~ê@"‚e6–pù-3€Ð€œ#Ù€1Fu‚Ÿ‡ iw‚{† ^@‚G…"Sžø„]I¦ƒÊ@ ƒX6¨€‡‚ü-€‚¬$c}‚as÷ޝh”o¨]D‹ÂS9ŠIv€¢ˆ@€3‡06λ…ã-Ù=„¡$ð~Ãbr瀽˜9g¾€¢•.\û€z’PRÌ€:¬I2á=@tŠó7~ûˆ¼.C~~†‰%‚~„Uqဠ f¼òœ•\ȘÍR'ˆ•8HÔ2‘Ü?ñ~ÂŽ¬7I~C‹Ž.±}ˆk&}B…Bnœ–,]4cg”W` XË’yc(NÀ‹f¤E<Žoj< ŒnÛ2û‰½sd)ô‡ax å… |ºmr“že)b{‘ðg[X8iÑNBŽil•DóŒxo«;ñŠns 3ˆVv™*=†;z>!h„}ëlX‘Jma«µnŸWvŽpjMÈŒrrnD§Š¬t°;̈Ùw%3†ýy½*}…|f!ëƒ9kTLt¸`×¼uÀVÒŒ=vÝMPгxDd‰y‘;©‡j{3…¼|Å*¹„~z"m‚^€8j^Ž|‡`-Œ |ÔVWг}+Lü‰?}œD!‡¾~1;†4~à3-„¨¢+ƒ€r"í‘FirŒ!„m_eŠÅƒÕU¼‰kƒMLˆ ‚ãC憜‚Ÿ;…*‚t3Dƒ·‚W+Q‚?‚I#n€Ì‚>h“‹ŒY^£‰­ŠÅU(ˆ]‰QL+‡ˆCº…§†ì;„D…æ3h‚à„î+©p„#ô€ƒgÁŠ(”%^ˆÓ‘ŸT±‡ƒEK܆1C†„Ø‹;„ƒx‰B3·‚‡w,€¦…³$}:ƒñf÷‰_›²]0ˆ˜XSø†¼•#KR…h’C>„A;ƒ‚¶Œ’3úK‰ø,Þ‡[% ~p„½aYŸÙ]ÁXDœÅ`¬Ol™ÊcÔFû–Ñg\>ï“¿kP7$Šo¡/x@t1'Õ‰ðxÞ 2† }”`be2W¢šmg‹Nù—jF±”¶l÷>Ê‘Íp7#ŽÏsŠ/ž‹Âw$(%ˆ°zÖ °…Ÿ~_µ›l~W ˜?nQN‹•xpLFi’ºrx>¥÷tÖ7'wa/¼ŠOz (o‡y|Ç!+„¤ˆ_™+sžV}–QtíN%“vKF&÷wÅ>„ŽRy\7‹§{/Õˆú|Õ(µ†V~¤!£ƒ´€w^U—mz»Uþ”­{dMË’ |Eê||È>dŒî}˜7Š`~z/ù‡Ôj(ÿ…R€`"‚ÔX]¯•öçUu“N½MaÁ™E£ŽB‰>B‹Å‘7‰J¬0†ÕÑ)L„hû"Œÿ‚%]”͉Tï’,‡ûLùª†ýE]6†>$ŠÆ…^7$ˆY„³0M…ñ„)¡ƒŽƒx#-‚ß\“å Tu‘:ŽLšŽ¶ŒHEŒHŠ›>‰â‰72‡‡Ÿ0‹…†<*‚¼„â#†€\ƒŠ[ñ“–ÍSí\” L(Ò‘„DÏ‹f =ä‰ Œ¸7B†®Š0Ë„OˆZ*gî†B$ Œ„0Tš©a^`M3¥PaMF¡@d€?L:h8È™2l2]• pu,Îu%ÁŒy¿‚ˆ3~wT¦÷eSL΢ígÇEÕžöjv?+›ml8»—5p¡2o“;t,<0wÀ&‹#{|þ‡?S–¤ÀlLj ÍnE‘œãpE?™rœ8°•Pu2‚‘{w±,pšzo&o‰Ë}7 v†€S"¢çr“Lžít.EO› uÔ>Ú—Dw8¦“šyM2—à{,¥Œ}&¿ˆ‹~Þ ä„þ€¿RÁ¡y KÒGzE'™~{>Å•Ì|8¤’*}2¬Ž‹~),ЊòG' ‡z€\!M„ qR^Ÿ¦}K›â±Dè˜0í>”’€58¤û€Ž2Ål€ò,ý‰é['S†„·!µƒ&‚Qþžx…×K0š³…8D¨— „«>t“~„58 óƒÙ2ÞŒpƒ‹-/ˆöƒF'£… ‚ò""‚M‚ Q¯qŒ J÷™ªŠ›Dƒ•ÿ‰L>f’sˆ8˜Žù‡ 2ô‹‰†-lˆ#…'û„Ë„"•uƒ"Qlœ„’J®˜³ÛDN•Õ>P‘x‹õ8¡ŽŠ13Š­ˆk-®‡M†ª(Xƒ÷… # €Ÿƒž­â_XHbK[ÜŒe+_µ{·hAc¥jµk…g§YqnökÄGùroÿ6€vtI%y†x›«7[˜bÝšÝ^ÚežŠrb9hjyöe»kKikiXnBX«mqIGÁqtb6Ètôw‹%Ìxézº¨©Xm£˜’[Æo]ˆv_„q x[c_rõh>gQtÜWðkivÌGzo¤xÅ7sõzÆ&xN|˦KU>xn–sY*y'†¤].yàvâaOz¤g2eŒ{wWBið|GG'nw}78s}ì'Iw¹~À¤ RÔƒ‰”zVÿƒ„õ[?‚¹u…_œ‚[fd‚V˜h¸ªFümyP7srO€ó'ôw/€—¡íPôŽÅ’UT.ƒNYÆ‹t/^RŠe'búˆVg‡FÈl§…n7ªq§ƒÚ(”v±‚HŸäOœ™ý¾T—1½X­”rré]X‘Àd@b UrfýŒEF•kû‰v7Üq†¦))v=ƒÙïN³¥ S;¡€LWÖ+q¿\Œ™QcWaa•lTèfW‘qFrkng8pœ‰])µuÒ…VœMû¯­cRŽªÈ~ÜW,¥Ûpš[å ãb•`À›×Tte–œFFjé‘G8uj†Í¢fW´’3hÕ[]‚jk@_"r¾mÆcc+p`g(SyskoC¬uÓoÝ3Öxšt`#ü{cx퟊b¯aÿ÷ed¯€~h^gzq,kHjjaþnAmRÁqLp¼Cqtht4ww$½z·zñ0_MlDábn~²e¸oÓoµh÷qÃ`èlCsÕRo¡vC7sxF4Wvz•%yz |ê›\~vŠ‹ò_îwY}cmx0n\fóy_ðjz"Qzn'{=Cqá|e4“u¤}–&+yl~ʘôZŠ']Ù€Þ{a”€£meR€y^öi€fPálò€cBÓp߀k4ÐtØ€x&ÒxÖ€†–þXC‹Ùˆa\4Šsyý`‰kæd‡Ï^gñ†žP`kó…wB¯p„W5 t(ƒ:'oxM‚•Vè–—†³Zø“ÿx‘_‘{jÆc]TgŒÃOîk#ŠvB’oTˆ+5Hs…à(wσ–“MU÷¡0…2ZnwO^$™ÎiÅb5–I\fM’ÕOjxbB|n¹‹ì5†sˆu(•wZ„ý‘™U;«^ƒ¡YS¦ºuõ]h¢h¶a~z[áe›˜çOiÕ”JBin&¥5Är„‹)$vç†]–-mªWk‡oZõxJq|^¦iÓspb[¤uaf»MˆwLk*?fyÿv|wØ1µy zp$h{É}ÓcêtÆ~fäu¦s}iÔv–eél´w¨X¿o‹xæK¾rfzI>ÔuF{Å1óx/}K%{~Öòa˜~Ìád×~±rh~ dÆk~«WÞn$~ÝK8q/0>°tA—23w[€%¶zy€yŒ$_É~Hc5‡ÊpÃf†¤c´iÏ…žWlý„ÀJÍp,ƒÿ>›scƒO2uv£‚¢&PyçöŠi^i“?|ÈaõÝo‡ejŽ˜bÀhÄŒ~V}l ŠŽJyoTˆ¹>“r¦†ï2ºv…#&äy_ƒVˆÃ]oZ{qa™Ønpd„–~aìgí“RUåkFMJ+nŸ`>“rŠ{3ul‡’'wxÞ„¦‡3\ª§yò`;¢­m&cÀžS`ìg3šU?j“–IÜmõ‘ÿ>˜qaŽ3St܉û( x^…ðŠXu3WI|!v”Z¶nfwì^Xa y@b?TNzƒfyGÃ{œk ;C|¶oÕ.¯}ÔtÁ"~ÿy´ˆTq—`¥zUsoc4lãu*eü_îvÍiSqx[lWG;yÏoñ;{As¹.î|¼w—"¼~<{}†gnaiíx»pk¬krm˜^ÊtŠo¾R˜v`r#Fµx#tÄ:ðyâwŒ/,{¬zb#d}{}>„¨k¯s(w1ntj2pbu)]ÂrŠveQ×tŸwØFrùi)Õf²kð‹ØZõn•Š Oâqˆ~E7s…‡:µuê…Î0Axh„t%Ìzëƒ~Qe8™‘qäh)–Weàjû“PZjm«OŠp?êEr¾‹|:Ñu<‰0˜wȆ¹&^z[„R|ëd_¢Êpug]ž¨d¡j8š¥Yjlø–ÎNÝo–“5DÐrÉ:òt’Œi0õw+ˆú&òyË……~L};WSq}}ÕZÂe~q^`YbMM•uf˜BiËk>7\€p#,N€gu+!;€½z>|Šy¼`3oüzÑbÕcÆ{Êe¨X|¢hÃLÜ}^l/Aý~oå7C~¤sÏ,ŽFwÓ!Øì{ázÞv˜hün‰xjÜbˆyLlåWzjo+L{jqµAŠ|Xt|7%}Bwl,Í~/zq"v}~yRsøq°m%u˜r×aSwtV xuu~K^y±w AzÛx÷7|zð- }.|ù#~]wÙqÁzˆkãs‘zÝ`BuC{:U.vÏ{ºJ½x<|o@Ày•}R6ûzë~S-N|Ja#¥}¯€tvnoðƒ™j½qè‚î_Ws¾‚QT}uoÜJNwŸ@‹x6ÿyø˜-–{¨$5}½unˆŒ¾iªp—Šü^r…‰ZS÷tO‡ìJuü†¹@sw“…²7y&„Â-êzǃÔ$Á|o‚çsÇm{•¼h¤o’ò]Ëq†TSxs^îI¶u‹Ã@bvĉÃ7Ixj‡Ø.Jz…î%P{Ì„rˆl“žmg‚n¨šÊ\Ûp§—€ODœ{ê€h<|‰€¤3‰}"€ö+c}ÔM#F~˜°jowI‰Ž`MxQˆ:Vty?†ÿMz…ñDkzØ…";þ{“„t3´|LƒÙ+º}ƒG#Ì}₼i`v(‘ò_|w<¡Uçx7yLÎy‹†D:yí‰È<z¹ˆ03þ{„†­,"|S…1$S}'ƒ¶hRu/š ^…vH–éU"wF“áLFx0‘CòyŽ^;üyê‹ã4;z¾‰},Œ{˜‡$ß|s„­f%ކWé\(W[WR¥Œ&_I˜Šõc@õ‰¬gq8–ˆClB0R†ÔqS(…mv„Þ„ {¾dç‹W_Ò[%Šub›QÛ‰…e¡Iˆ‰hï@™‡{lŽ8o†dpw0d…Kt’(c„2xÆ eƒ}c¾ˆmg•Z?‡ÄiÍQ"‡l+Ht†JnÀ@<…wq‘8@„£t—0dƒÑwÁ(¤ƒzþ ê‚0~Db§…ño0YW…bpÖPh„ÚrGæ„Hte?烨vm8ƒx’0R‚vzÑ(Þç}!!mWxa ƒËvÕX˜ƒiwÎOÓƒxÇGv‚—yÔ?‘‚&{7ê¶|T0kL}¶)#€ì%!쀓€š`¤û~˜Wý~µO$v~ÔFì) ?I€Úl7Ò€Žç0v€G€r)j€"lâ¤_¿€‚†nVý€[…N”€,„ËF•ö„)?!¿ƒ·7ÒŽƒ[0œeƒ )¾?‚Ì"ê$‚‘^åRŽ&V`<ŒZN)еF[~ï‰;>ù~ɇë7Ü~¨†¹0ì~…™*$~v„#j~bƒm^~L•U~<“M€~‹Eæ}õŽ8>Á}àŒ7ã}ÌŠ 10}ºˆ*Œ}¯†1#î}¥„CY—ÄXmQÄ•b[ôIî“_¯BeìcÂ;(޹h<4Œym-&Š2r,&8‡éwdM…£|¦XÊ”¹_ÖQ’˜bÌI[‹eòAþŽŒi\:쌒m4 Š”q-Cˆ•u/&…†˜ypË„}ºWâ‘ëgPKüiˆHÏŽlAšŒOnÜ:°ŠˆqË3÷ˆÅtç-Y‡x$&Ì…P{s GƒŸ~ÉW„nO¡¯pHM‹÷rA<ŠQt%:zˆ¶vT3ä‡#xœ-j…šzù'„"}a ½‚±ÍVIcu!O ‹¶v{GÚŠ"wÌ@눟y%:H‡)z‘3Ö…¿|-…„c}›'Tƒ-!1×€ÁU‹’|7NpŠ |ÆGcˆ˜}Q@˜‡3}å:…Û~3Є‘I-£ƒV€'›‚-€Ö!¤  TኃPM߈¦‚üFõ‡I‚²@Q…û‚~9ý„º‚c3Õƒ‰‚Z-Ì‚h‚\'ëU‚b"€I‚jT8ˆçŠGM[‡x‰F•†#ˆ@„ã‡9ჵ†!3ₘ…S.Š„‘(E€…ƒÙ"†ƒ$S‡ÚýL͆bF$…A?ʃً}9½‰Õ3ò±ˆA.C€°†½(£¹…G# ~ƃØMÍ áXøGl‡\ŒA=š5`Y;F—d~5~“ãi /Á¿mó*–s$eŠgxS‡9}–Mò_êFàš¼c@Ù—šfQ;”™iÛ5Y‘µm /ÀŽÊq§*6‹ÞuÜ$¶ˆúz':†~{L{›=fªFY˜9iU@r•:l!:Á’\o56¢r/ÂŒçuK*^Š0x›%‡–{ö¬…WKã˜õmEÛ–oe@“q°:|]t5Åv^/Ç‹/xÃ*ˆˆš{A%N†L}± „€(K_–×s‰E€”u8?Ï‘Tvá:Q޵xˆ5Œ3z0/ωÃ{ß*¬‡g}”%”…5C €ƒ€ðJÝ• yîE’gzâ?€Ï{Ò:Q|Â4öŠå}º/݈~¹*ІT»%Ù„>€± è‚4¥J_“—€ED²þ€n?1Ž|€›9錀Ñ4é‰Á/퇂f*ú…Yº&"ƒ\‚!Qg‚FIõ’V†€DeÀ…Ü>þD…K9Њê„Ì4Ùˆ­„\/û†‰ƒó+.„}ƒ&r‚Šƒ3!½€¡‚ÛI¡‘#Œ‰D Ž—‹,>»Œ ‰ç9®‰Ñˆº4ׇ©‡0…š†t+gƒž…L&ź„W")Þƒk YóR ‘9\þVUÃ`=Z§rGcÅ_b»g–cªS k¥hqCEoÝmT3bt,rK#ox‰wKïUJ\±ŽáXü_óÄ\ÆcBp«`Éf®a‘e j=R^iŠmðCn:q¿3¯su¡$Ewßy‹›gQ gFŒ£UMi‡}ÛYŒk×o#]ünA`vb©pÉQ¸g“slBàl°v%3ûqïxî%w={Á™M¢qÅŠŽR#s|V¿tlmµ[ŒuÎ_n`–wJQeÝxÙB±kVzy4Apö|!%Ñv¥}Õ–×J±|’ˆœO†|ÓzkTs}l_Y}\^p^è}½P~d{~.B€j>~©4}p$'&zv«”½HU‡“†ÃMt†¢xÚR¥…¸kX„ã]…]–„!OïccƒjBPi_‚¸4°ow‚'ušS’¹F’“…KàfwbQBŽOiòVÌŒS\¬\ŠlOib…ˆŠB"h«†©4Ýné„Ä'šu0‚àÇEAQƒ[J¯švP.–ÉhÚUÖ“¬[Ý[µžNéaÏAõhŠ€5nr‡o(tØ„`ŽõD*§¤ÀI®£rt©OBŸ4gÎTüšü[!Zð–ÉNqa+’AÌgŒŽK50nŠ(štŠ…Ü•ˆ`±Qö‡cPV%xžfZfjHi^Ð\lFcrM·o§hN?Ws,mW0ìvÉrz"}zsw¨“\!\ „ê_e_XvÅbµb¤hÇf*fZìiÍiÀMm m—?'q–q”18u£u©#Fy¹yÈÂXf7‚Ü[Ìhu_jÛgYcom[Yàg{pLok¶rØ>ùpuÈ1t‰xÍ$ y{ÜŽ›T¤p5€óX´q¤s_\Òsfa t•Xçesv>KÙjx>În¾yç1ÆsŠ{Ø$Àx^}ÑŒ™Q»zx-V&zåqÜZ–{LdÍ_{ÉWþcË|dKLh¤}>¦mŸ}æ2r®~»%fwÄ•ЧOc„ð}yT„:pmX̃‡cª]Ž‚îW,bt‚vJÑg„‚>„l´Ä2?qöw%ýw=-ˆÈM™t{ÞRƒˆoWc‹±bŸ\Q‰ÿVpaaˆpJef›†ö>ikô…„2xq]„&‰vË‚¦‡LE™Èz^QG–µmÜVD“Åa©[PýUÁ`ŽUJeØ‹¿>SkP‰/2¯pÚ†Ÿ'vj„…RK(£©xäP6Ÿ¼l¢UD›Ì`¹Ze—òU_«”2I§e >Aj¸ŒÏ2çpa‰!'‘v…rЉgœQï|õiÅVo…l Z3bVn|^šU[q cGHis¼h:;tv†me.yir³!|ZxˆMc#[¡{eó^ÍmÕhÂb`ôk£e”TTn¢iSGÎq¿mN;Ktõqv.Ìx=u¼"O{z †3_"e>y$bog‰l;e®iï_¥h÷l†S\l[oTG8oÜrT;#suu{/wxº# zÊ|„B[Ãn¿wk_fp>jÁcqÄ^qf¢slRxj[uCF®n0wE:ýryg/Yv{#ºz}Þ‚jXêxtuÏ\ãyic`Îy˜]UdºzEQ¤h·{F1lÍ|:Þpù}3/šu2~\$[yp‹€£Vž‚etBZÜáh_e\Kc, Pèg_€ÜEÈk©€Ó:Ëp€à/Ütp€õ$ïxß ~îTÕŒlrÍYBйfã]˜‰"[`a臻PHfD†„Etj¶…p:Âo;„o0s̓p%{xc‚p}LSt–CqtWü“teÍ\mÎZŽ`ÚŽ\OºeUŒE,iæ‰ú:ÁnŒ‡è0bs?…Ö&wùƒÁ{½RXŸ®pVÞœ d±[^˜nY¹_â”óO0dv‘¦Dìi"Žz:Æmè‹Y0¨r¼ˆ4&Œw™… —nËRröpvVf—r0Z#Z’t^‰NØuëc@C>wáhJ7±yèm“,0|s µ~1x‡}“jp[Aq4l¾^benúa¬YTq:e7Mísˆi B¶uæm(7”xTqx,|zÏuè!i}Rze{ªf‹dbo‹iOfµc§kùi+X)nœkÚMqHnËB2tqö7tvÏuM,Äy¦x¿"|‚|=yíc>mcnfRnúbXiUpWlMrkLEoKtqA¹rWvª7Vupy- x˜{|"Ä{Ä}ýxH`jvŽl“c×wJag'x VjgxêK‰m¨yýAMpò{>7AtI| -Lw­~#^{Žv®^#ök+aÓ¦_ðe`fU*hÙMJálOh@õoʳ7:sP€-’v〆#îz|€öu"\\‰wiß`5ˆ^åc놳T`g…›J]k,„¼@·n΄ 7Brzƒt-Ûv5‚Û$xyö‚Bs§Zú’Åh±^åF]úb³õS¹fq‹ßIôj-Š@ŒmðˆS7TqÀ†·.)u…%yƒzrGYÖ›·gy]½˜j\ýa˜•)Rþeo’I€iE;@am#ŒŽ7mq‰ó.|u‡Q%Šy„§tjvoR2iwlV)]êxyZFSy™^¯H¡zÉct>W|h’4&}Emô*~sôãyr§r7Zøg‰sÕ^"\šu_awR väeGÙxqi=çzm74{©q§*U}Rv8 œ~ÿzÖpúnic fp{f [XrohQtSkkGv8n}=xx'qÍ4z uJ*ž|"xã!B~&|‡opk,l$d·mmáZ.oÔo°Pr qªFgt=sÝ=vyvD3ëx¾xÑ*á{ {u!á}`~"múhrtºcrku´Ym¤v®O1pwÉEÆr“y<¶u z•3ÞwŽ|4+$z}ã"u|°šl‘f+}b=i}Xk×}˜Ngn‡}ÇE?q0~-;в`L7¼‰geD1Vˆ4j›*þ‡ p6$²…áuún„µ{ÎQˆ‹òZÄJÓŠ^ID'‰?aõ=©ˆeä7]‡j1/†)n˜+…OsI%„xxòƒ¢|øP„ˆˆaúI÷‡}dûCr†~h=…›kb7„ÚnÛ1„7r‚+$ƒ¦vN%Jƒz1t‚™~O…œhðI-„ÈkzBÍ„n<žƒbp§6¬‚Þsd0ã‚yv=+2‚(y.%Žæ|0ï¨:N·ƒoÜHx‚vqÍB=ës´<3|u£6h,w£0È€úy·+C€à{Û%΀×~ a€×€¦†ð{â:…™|·5¨„j}Ž1^ƒ]~k-%‚vQ(ô·€:$à “€•ùB»†ÐE>:…}Z9Ê„:v5yƒ!œ1H‚5É-*oú)€Í‚/%€M‚g üÞ‚¢BJ…\‡X=Ô„!†Á9~‚ë†.5Mã…¦17…#-4€s„š)@è„%R|ƒª!kƒM“ÜT•L]…cWÐPæw[XUhí_KZZëc£_šL¥hUe'>´m!j­0SrGpO!Ëw™v‘6O2VÚƒSZqu$W8^,gm[·bYÑ`f9L&eÝj™>sk^o0¢qs¾"½vÚxk޹JRaD€ÞNÁcÙsJSlfœeùXli“XÍ]Ól¸KŸc–p>^iŸs1oßw(#Ÿv3zØŒoF4k}~×Km$q‘P"nõdœU”póWØ[ksKaœus>chwò1`nÊzp$lu¡})Š:B­uã|ðGùv™oôM~wfcLSVxXVÞY‹yvJ„`z¶>"fè|1šmï}n% u~ãˆ%?×€š{"E|€+nsKZØbQ…±VX¸Iý^åÛ=óf€1ÆmB€;%“t‡€k†(=£‹byqC‰‰±mI«ˆ1aP†êU>VÓ…ÖIƒ]ð„Ú=ÊeLƒí1ìl¸‚ê&t(ê„<;å•ÚwÛB’ÿkÇHcb`NùŽT}Uå‹ÎHþ]3‰±=Šd´‡2 lJ…ƒ&…sèƒh‚l:€ŸÜvR@Íœ jˆGJ˜|_Mÿ•SãT÷‘»Hw\˜Ž{=bd2‹F2/këˆ&ûs„牖[Lˆ|(]×Qnñ`âU­aödMZ‘Uh_³Hl"e);prjÐ.tóp” óyve‡1UÃV‘zY/Z#m\Ý]Û`y`ÙaÎT e&fGƒiÎjt:ïn°o.^s³s×!ÍxÃx§„çPù`~wúT÷c kYY,eî_]§h÷Sbwl;Fíg o°:ÏmsO.µr~w"žxzß‚ÇLòj;vQYkýiºUúmë]ÁZÝp R `rZF^e·tÓ:²kƒwo/qgz%#]wU|ï€ÍIstt[N]tühBSjuô\ŒX³wQ^MxbEÐd3yÕ:ŒjK{e/Gp|}$v·~¶~ßF¨~Dr±Kä~fáQD}ø[xVÛ~ P\\¿~QE^bñ~¸:liP5/~o¿¼$v2€F}Dzˆ‰q Ií‡ eO‰…æZ‚UY„èO´[y„!Düaêƒy:Rh†‚á/°o(‚M%u̸{FBº’›o¸Hid{N7¸Y T:‹¬OZ…‰ÓD›aˆ::g݆r/án®„É%‰uƒƒyœARœ)nWG ˜¾ceLø•wXØS’aN•Y…zDS`OŒ²:(gC‰÷0nC‡<&uK„~caÆL»sdQ'fàf‹UÅ[ilZ¦OPl™_ÍCwp eD7s¼jý+Êwœpà {™vÔ}:\†VRq _yYÛe"b§]“Y‘faN?iÌeÔBÝmÎj[7xro,'vPsü åz©xñ{)WÒ_Èo([[bocz_eKX1bÿhhM7g1kÇBDk¯o]7WpYs!,}uw !®yÒ{y=SËi mgWÏjæaö[ölíVñ`Ko)LDdêqŸA³iÏtA73nÚw ,Ésöyø"jy|þwkPZrhk×TÜso`¢YntUÛ^(uÜKncwZA5hKy7mzÒ- s|¸#xk~­u©Mœ|jORm|_[WO|$TÜ\S|sJ¼a‘|ö@Óg}§7 l—~u-Mr7M#“wÛ€+süKq…ÆhàPu„^3Uƒ¨SüZË‚òJ'`D‚w@…eô‚)7kÀô-‹q•¿$wlŠrjI¥]g¢Nà]9T*‹SAY˜‰bI§_;‡å@Ee†”7k …W-Çq„$’w‚ÔpßH=˜{feMp•i\FRÓ’|R–XgÅIE^.E@d5Šó7jgˆ±.p£†g%vè„uKh¡LüiòjjQS^çlhUéT-n±ZËIšqE_ú>àte}4wk?)«z9qDN}šw[sUc…V!h%eõY¦]Fhš]aRÃktahHn‰eÀ>QqÚj^4uSo6*xát: |{yPqu^í_#fdaùaÕ[·e#dÁQthugóGŽkþkp=ÀoÁo(4s¦s*fwšw" Ö{’{NoÄZögïdË^liäZLblP=eÑnbF›i¼q=3mßsÐ3çrvË*²vsyí!–zÔ}FnW“pÊcl[qùY&_•sFOUc³tÁEígõvs<Îl_xW3Øpàza*÷uz|†"#z~Ál‡TÔyßbY'zWù]|zmNfaâzøEFfp{ºjèUü_ElÎY‰U‹nÏ]RL#pûakBûs[eÖ9îuîjŒ0øx o(!{^tœ]~!yËg’ft^…]¼hæaVT-kkd_Jýng°B!pÜkI9vs×o!0êvís,(wzwY }6{˜f b”fØ\WetiRôhikQIùkvmØA^n¤p›9qús“0×uhv¶(Âxæy÷ ¼|i}Nd—_Jo/[b‘p Qåeår(I!iKsÚ@ÃlÍuÂ8±pmwÛ0Ît#z)wç|k!M{°~Êc0\‹w·Yá`)xAPçcËxåH]gyy¸@>k@zÀ8qo {ú0Ôs}S)Mw~·!Ï{€aÕZR€bXÃ^-äPb”G·eé?Öiç¤8Fmþû0ær'€m)•v[€â"Lz’V`‹X–ˆõWÁ\…‡{OE`†=G6dŠ…@?h±„8/lùƒê0ýqZƒm)ÞuÂò"Çz/‚u_OW&‘0VÇ[ŽùNŽ_ ŒàF½c:Šü?Ng„‰Q8!kÿ‡Ò1pœ†e*)u<„÷#Ayáƒ`ÄwsM°WÚwìRO1xŽV•FÌyv[€>‰zµ`Ç6:|0fn-ù}Åla&6r#€¤xË_7rªUêVjsÓYˆMèu]gEµvxa—=¼xf5Ùyìjè.{Üoö&e}Æu*έzl]¹nU]ýUp`÷L´qÀd(D²s—gœ<ÿuškT5vwÏoM. zsv&º|pw¸s~À|\Zj”eØSÒl§h=K nÇjÅCÏpúm|r —}/~îYÔd±u¥Q¹glvŠIíj.wƒB‡mx¤;€o÷yô4±s{j. v8|ù'Šyc~•!|“€4X¨bw}»PÇei}¯I6h`}ÅB kf~ ;›yg¶8zHk1»{²o§+m}6së%1~·xEý€3|¨RsdòKNtg–Dfu>j\=Òv‚mO7‹x pi1nyÐs­+i{¯w%w}z‡ˆQ}þQhoôlDJdq^nIC«rÙpc=Dtur›7(vFtô12~¯d+8¼~¸gù3nkø.4p')€9t#×€èxúª’}ƒH{ØdBê{Çg=h{çj8|BmA2÷|àpˆ-ü}¶sì)~©wr$ £{ ,€š~·G•xåjæBymE<ÂyŠo³7™z0r32¤{tÄ-Ö|"wk)}Sz&$[~’|ï›ÒÂF¨vcq¹AdvÔsg<8wu!7Ò„°c6: ƒcf˜6€‚uiû2uámm.yšpØ*¯‡tN'Œwò"ð³{¯òȵ>Ði…9ë€âlR5ë€=o 2éqê.5Öt®*ôw|&î€8z`#€›}VD€i=JLoÕ9Z~£qñ5{~Ft1µ~1v-.~ExG*~zp&÷ |¦#E°~ÕŠ€e< })v%8Ù|°w5|Šxë1y|©zY-ö|ã{Æ*}X}F'}ü~Ð#x~Ý€BÝÒ±;ô{d|[8J{|ü4¹{ }µ1={=~z-Ñ{¯B*q|L€ '}€Ú#°~¤ H(‚p;ayÊ‚b7Ìy¤‚q4fy’‚†1yÏ‚¤-¶z‡‚Ã*j{N‚Ð'5|(‚Ü#õ}9ƒ Ë~TƒC@ÁB‚ÃC„ÄE…ÆF‡ÇHˆÈ I‰Ê J‹Ë LŒÌ M Í  N Ž Ï  O Ð  P ‘ Ñ  Q ’ ÒR“ÓT”ÔT•ÕU–ÖV—×W—ØX˜ØY™ÙZšÚZšÛ[›Û\œÜ\œÜ]Ý]Ý^žÞ^žÞ_Ÿß  _ Ÿ ß!!_!Ÿ!ß""`" "à# #`# #à$ $`$ $à% %`% %à& &`& &à' '`' 'à(!(a(¡(á)!)a)¡)á*!*a*¡*á+!+a+ +à, ,`, ,à- -`- -à. .`. .à/ /`/ /à0 0`0 0à1 1_1Ÿ1ß22_2Ÿ2ß33_3Ÿ3ß44^4ž4Þ55^5ž5Þ66^66Ý77]77Ý88]8œ8Ü99\9œ9Ü::[:›:Û;;[;›;Ú<>Y>˜>Ø??X?˜?×@@W@—@×AAVA–AÖBBUB•BÕCCTC”CÔDDSD“DÓEERE’EÒFFQF‘FÑGGPGGÐHHOHHÏIINIŽIÎJ JMJJÍK KLKŒKËL LKLŠLÊM MJM‰MÉN NHNˆNÈOOGO‡OÆPPFP…PÅQQDQ„QÄRRCRƒRÂSSBSSÁTT@T€TÀTÿU?U~U¾UþV=V}V½VüW¢}¢½¢ü£;£z£º£ù¤8¤w¤·¤ö¥5¥u¥´¥ó¦2¦r¦±¦ð§0§o§®§î¨-¨l¨«¨ë©*©i©©©èª'ªgª¦ªå«%«d«¤«ã¬"¬b¬¡¬à­ ­_­Ÿ­Þ®®]®œ®Ü¯¯[¯š¯Ú°°Y°˜°×±±V±–±Õ²²T²”²Ó³³R³’³Ò´´Q´´ÐµµOµŽµÎ¶¶M¶¶Ì· ·L·‹·Ë¸ ¸J¸Š¸É¹ ¹I¹ˆ¹ÈººGº‡ºÇ»»F»†»Å¼¼E¼…¼Ä½½D½ƒ½Ã¾¾C¾‚¾Â¿¿B¿‚¿ÁÀÀAÀÀÀÁÁ@Á€ÁÀÂÂ?¿ÂÿÃ?ÃÿÃþÄ>Ä~ľÄþÅ>Å~žÅþÆ>Æ~ƾÆýÇ=Ç}ǽÇýÈ=È}ȽÈýÉ=É}ɽÉýÊ=Ê}ʽÊýË=Ë~˾ËþÌ>Ì~̾ÌþÍ>Í~;ÍþÎ>ÎοÎÿÏ?ÏÏ¿ÏÿÐ@ЀÐÀÑÑ@Ñ€ÑÁÒÒAÒÒÁÓÓBÓ‚ÓÂÔÔCÔƒÔÃÕÕDÕ„ÕÄÖÖEÖ…ÖÆ××Fׇ×ÇØØH؈ØÈÙ ÙIÙ‰ÙÊÚ ÚKÚ‹ÚËÛ ÛLÛÛÍÜ ÜNÜŽÜÏÝÝPÝÝÑÞÞRÞ’ÞÓßßTß”ßÕààVà–à×ááXá˜áÙââZâ›âÛãã\ããÞää_äŸäàå!åaå¢åãæ#ædæ¥æåç&çgç§çèè)èièªèëé,élé­éîê/êoê°êñë2ërë³ëôì5ìvì¶ì÷í8íyíºíûî;î|î½îþï?ï€ïÁððBðƒðÄññFñ‡ñÈò òJò‹òÌó óNóŽóÏôôQô’ôÓõõUõ–õ×ööZö›öÜ÷÷^÷Ÿ÷àø!øbø£øäù%ùfù§ùéú*úkú¬úíû.ûoû°ûòü3ütüµüöý8ýyýºýûþ<þ~þ¿ÿ>|ºø6t²ð.lªè&e£á]›ÚV”ÒOÌ H‡ÅB€¾ý ; z ¸ ÷ 5 t ² ñ / n ¬ ë ) h § å $ c ¡ à]œÛX—ÖS’ÑOŽÍ J‰ÈF…ÄBÀÿ>}½ü;z¹ø7w¶õ4t³ò1q°ï/n­í,k«ê*i©è(g§æ&e¥å$d£ã # b ¢ â!!!a!¡!á" "`" "à# #_#Ÿ#ß$$_$Ÿ$ß%%_%Ÿ%Þ&&^&ž&Þ''_'Ÿ'ß((_(Ÿ(ß))_) )à* *`* *á+!+a+¡+â,",b,£,ã-#-d-¤-ä.%.e.¦.æ/'/g/¨/è0)0i0ª0ê1+1k1¬1í2-2n2¯2ï303q3±3ò434t4´4õ565w5¸5ø696z6»6ü7=7~7¿88A8‚8Ã99E9†9Ç::I:Š:Ë; ;M;Ž;Ï<>\>>Þ? ?a?¢?ä@%@g@¨@éA+AlA­AïB0BqB³BôC5CwC¸CùD;D|D½DþE@EEÂFFEF†FÇGGJG‹GÌH HNHHÑIISI”IÕJJXJ™JÚKK\KKßL LaL¢LãM$MeM¦MçN(NiNªNëO,OmO®OðP1PrP³PôQ5QuQ¶Q÷R8RyRºRûS9>|>¿??F?‰?Ì@@R@•@ÙAA_A¢AåB'BjB­BðC3CuC¸CúD=D€DÂEEGE‰EÌFFPF’FÔGGYG›GÜHH`H¢HäI&IgI©IëJ,JnJ°JñK2KtKµK÷L8LyLºLûM=M~M¿NNAN‚NÂOODO…OÆPPGPˆPÈQ QIQŠQÊR RKR‹RËS SKSŒSÌT TLTŒTÌU UKU‹UËV VJVŠVÊW WIWˆWÈXXGX†XÆYYDY„YÃZZBZZÀZÿ[>[}[¼[û\:\y\¸\÷]6]u]´]ò^1^p^¯^í_,_j_©_è`&`e`£`áa a^aaÛbbWb–bÔccPcŽcÌd dId‡dÅee@e~e¼eúf8fvf³fñg/glgªgèh%hch hÞiiYi–iÓjjNj‹jÉkkCk€k½kûl8lul²lïm,mim¦mãn n\n™nÖooOoŒoÉppBpp»pøq4qqq­qêr&rcrŸrÛssTssÌttEtt½tùu5uqu­uév%vavvÙwwPwŒwÈxx?x{x¶xòy.yiy¥yàzzWz“zÎ{ {E{€{»{÷|2|m|¨|ã}}Z}•}Ð~ ~F~~¼~÷2l§â€€X€“€Î Cºõ‚0‚k‚¦‚ჃXƒ“ƒÎ„ „E„„¼„ø…3…o…«…æ†"†^†™†Õ‡‡M‡‰‡Åˆˆ=ˆyˆµˆñ‰-‰i‰¥‰âŠŠZŠ—ŠÓ‹‹L‹ˆ‹ÅŒŒ>Œ{Œ¸Œô1n«çŽ$ŽaŽžŽÛU’Ð J‡Ä‘‘?‘}‘º‘÷’5’r’°’î“+“i“§“ä”"”`”ž”Ü••X•–•Ô––P–Ž–Ì— —I—‡—Ř˜B˜˜¿˜þ™<™{™¹™øš7švš´šó›2›q›°›ïœ.œmœ¬œë*i©èž'žgž¦žåŸ%ŸdŸ¤Ÿä # c ¢ â¡"¡b¡¢¡á¢!¢a¢¡¢á£!£b£¢£â¤"¤b¤£¤ã¥#¥d¥¤¥å¦%¦f¦§¦ç§(§i§©§ê¨+¨l¨­¨î©/©p©±©òª3ªtª¶ª÷«8«y«»«ü¬>¬¬À­­C­…­Ç®®J®‹®Í¯¯Q¯’¯Ô°°X°š°Ü±±`±¢±ä²&²h²ª²ì³/³q³³³õ´8´z´½´ÿµAµ„µÆ¶ ¶L¶Ž¶Ñ··V·™·Ü¸¸a¸¤¸ç¹*¹m¹°¹óº6ºyº¼ºÿ»C»†»É¼ ¼P¼“¼Ö½½]½ ½ä¾'¾k¾¯¾ò¿6¿z¿½ÀÀEÀˆÀÌÁÁSÁ—ÁÛÂÂbÂ¥ÂéÃ,ÃpóÃöÄ:Ä}ÄÁÅÅGÅŠÅÎÆÆTÆ—ÆÚÇÇ`Ç£ÇæÈ)ÈlȯÈòÉ5ÉxÉ»ÉþÊAÊƒÊÆË ËLËŽËÑÌÌVÌ™ÌÛÍÍ`Í£ÍåÎ(ÎjέÎïÏ1ÏtÏ¶ÏøÐ:Ð}пÑÑCÑ…ÑÇÒ ÒLÒŽÒÐÓÓTÓ–Ó×ÔÔ[ÔÔßÕ!ÕbÕ¤ÕæÖ(ÖiÖ«Öí×.×p×±×óØ4ØvØ·ØùÙ:Ù|Ù½ÙþÚ@ÚÚÂÛÛEÛ†ÛÇÜÜIÜ‹ÜÌÝ ÝNÝÝÐÞÞRÞ“ÞÔßßUß–ß×ààXà™àÚáá[áœáÝââ^âžâßãã`ã ãáä!äbä¢äâå#åcå£åãæ$ædæ¤æäç$çdç¥çåè%èeè¥èåé%éeé¥éäê$êdê¤êäë$ëcë£ëãì"ìbì¢ìáí!íaí íàîî_îžîÝïï\ïœïÛððZð™ðØññWñ–ñÕòòSò’òÑóóPóóÎô ôKôŠôÉõõGõ†õÅööBööÀöþ÷=÷|÷º÷ùø8øvøµøóù2ùpù¯ùíú,újú¨úçû%ûcû¢ûàüü\ü›üÙýýUý“ýÑþþMþ‹þÉÿÿEÿƒÿÁÿÿmft2@ÁB‚ÂCƒÃD„ÅE†ÆG‡ÇHˆÈ I‰Ê J‹Ë L Œ Ì M Í  N Ž Ï  O Ð  Q ‘ ÑR’ÒS“ÔT•ÕV–ÖW—×X˜ÙYšÚ[›Û\œÜ]Þ^Ÿß_ à a¡á"b¢ã#c¤ä$d¥å%f¦æ'g§è ( h © é!)!i!ª!ê"*"k"«"ë#,#l#¬#í$-$m$®$î%.%n%¯%ï&/&p&°&ð'1'q'±'ò(2(r(³(ó)3)s)´)ô*4*u*µ*õ+6+v+¶+÷,7,w,¸,ø-8-x-¹-ù.9.z.º.ú/;/{/»/ü0<0|0¼0ý1=1}1¾1þ2>22¿2ÿ3@3€3À44A44Á55B5‚5Ã66C6„6Ä77E7…7Å88F8†8Æ99G9‡9È::H:‰:É; ;J;Š;Ê< >M>Ž>Î??O??Ï@@P@@ÐAAQA‘AÒBBRB“BÓCCTC”CÔDDUD•DÕEEVE–E×FFWF˜FØGGYG™GÙHHZHšHÚII[I›IÜJJ\JJÝKK^KžKÞLL_LŸLßM M`M MáN!NaN¢NâO"OcO£OãP#PdP¤PäQ%QeQ¥QæR&RfR§RçS'ShS¨SèT(TiT©TéU*UjUªUëV+VkV¬VìW,WmW­WíX-XnX®XîY/YoY¯YðZ0ZpZ±Zñ[1[r[²[ò\2\s\³\ó]4]t]´]õ^5^u^¶^ö_6_v_·_÷`7`x`¸`øa9aya¹aúb:bzb»bûc;c{c¼cüde~e¾eÿf?ffÀgg@g€gÁhhAh‚hÂiiCiƒiÃjjDj„jÅkkEk…kÆllFl‡lÇmmHmˆmÈn nIn‰nÊo oJoŠoËp pKpŒpÌq qMqqÍrrNrŽrÏssOssÐttPt‘tÑuuRu’uÒvvSv“vÓwwTw”wÕxxUx–xÖyyWy—y×zzXz˜zØ{{Y{™{Ú||Z|›|Û}}\}œ}Ü~~]~~Ý^ž߀€_€ €à a¡á‚"‚b‚¢‚âƒ#ƒcƒ£ƒä„$„d„¥„å…%…f…¦…æ†'†g†§†ç‡(‡h‡¨‡éˆ)ˆiˆªˆê‰*‰k‰«‰ëŠ,ŠlЬŠì‹-‹m‹­‹îŒ.ŒnŒ¯Œï/p°ðŽ0ŽqޱŽñ2r²ó3s´ô‘4‘u‘µ‘õ’5’v’¶’ö“7“w“·“ø”8”x”¹”ù•9•z•º•ú–:–{–»–û—<—|—¼—ý˜=˜}˜¾˜þ™>™™¿™ÿš?š€šÀ››A››ÁœœBœ‚œÃC„ÄžžDž…žÅŸŸFŸ†ŸÆ  G ‡ È¡¡H¡‰¡É¢ ¢I¢Š¢Ê£ £K£‹£Ë¤ ¤L¤Œ¤Í¥ ¥M¥¥Î¦¦N¦¦Ï§§P§§Ð¨¨Q¨‘¨Ò©©R©’©ÓªªSª”ªÔ««U«•«Õ¬¬V¬–¬×­­W­—­Ø®®X®™®Ù¯¯Z¯š¯Ú°°[°›°Ü±±\±œ±Ý²²]²ž²Þ³³_³Ÿ³ß´ ´`´ ´áµ!µaµ¡µâ¶"¶b¶£¶ã·#·d·¤·ä¸%¸e¸¥¸æ¹&¹f¹¦¹çº'ºgº¨ºè»(»i»©»é¼*¼j¼ª¼ê½+½k½«½ì¾,¾l¾­¾í¿-¿n¿®¿îÀ/ÀoÀ¯ÀïÁ0ÁpÁ°ÁñÂ1Âq²ÂòÃ2ÃsóÃóÄ4ÄtÄ´ÄôÅ5ÅuŵÅöÆ6ÆvÆ·Æ÷Ç7ÇxÇ¸ÇøÈ9ÈyȹÈùÉ:ÉzɺÉûÊ;Ê{ʼÊüË<Ë}˽ËýÌ>Ì~̾ÌþÍ?ÍÍ¿ÎÎ@΀ÎÁÏÏAÏ‚ÏÂÐÐCЃÐÃÑÑDÑ„ÑÄÒÒEÒ…ÒÆÓÓFÓ‡ÓÇÔÔGÔˆÔÈÕÕIÕ‰ÕÉÖ ÖJÖŠÖË× ×K׌×ÌØ ØLØØÍÙ ÙNÙŽÙÎÚÚOÚÚÐÛÛPÛ‘ÛÑÜÜQÜ’ÜÒÝÝSÝ“ÝÓÞÞTÞ”ÞÕßßUß–ßÖààVà—à×ááXá˜áØââYâ™âÚããZã›ãÛää[äœäÜåå]ååÝææ^æžæßçç_ç çàè è`è¡èáé!ébé¢éâê#êcê£êäë$ëdë¤ëåì%ìeì¦ìæí&ígí§íçî(îhî¨îéï)ïiï©ïêð*ðjð«ðëñ+ñlñ¬ñìò-òmò­òîó.ónó®óïô/ôoô°ôðõ0õqõ±õñö2örö²öó÷3÷s÷³÷ôø4øtøµøõù5ùvù¶ùöú7úwú·úøû8ûxû¸ûùü9üyüºüúý:ý{ý»ýûþ<þ|þ¼þýÿ=ÿ}ÿ½ÿþÿÿÿÿÿÿÿÿAÂC„ÄE†ÆHˆÉ JŠË LÍNÐQ‘ÒS“Ô  U – Ö  W ˜ Ù  Z š Û  \ œ Ý  ^ Ÿ ß `¡â"c£ä$e¦æ'g¨è)iªë+l¬í-n¯ï0p±ñ2s³ô4uµö6w¸ø9yºú;|¼ý=~¾ÿ@€ÁB‚ÃD…ÅF‡Ç  I ‰ Ê! !K!‹!Ì" "M"Ž"Î##O##Ð$$R$’$Ó%%T%”%Õ&&V&—&×''X'™'Ú(([(›(Ü))]))Þ**_* *à+!+a+¢+ã,#,d,¤,å-%-f-§-ç.(.h.©.é/*/j/«/ì0,0m0­0î1.1o1°1ð212q2²2ò333t3´3õ454v4¶4÷575x5¹5ù6:6z6»6û7<7}7½7þ8>88¿99A99Â::C:ƒ:Ä;;E;†;Æ< >L>Œ>Í??N??Ï@@P@‘@ÑAASA“AÔBBUB•BÖCCWC˜CØDDYDšDÚEE\EœEÝFF^FžFßG G`G¡GáH"HbH£HäI$IeI¥IæJ&JgJ§JèK)KiKªKêL+LkL¬LíM-MnM®MïN/NpN±NñO2OrO³OóP4PtPµPöQ6QwQ·QøR8RyRºRúS;S{S¼SüT=T~T¾TÿU?U€UÀVVAV‚VÃWWDW„WÅXXFX‡XÇYYHY‰YÉZ ZKZ‹ZÌ[ [M[[Î\\O\\Ð]]Q]’]Ò^^T^”^Õ__V_–_×``X`™`ÙaaZa›aÛbb]bbÞcc_cŸcàd!dad¢dâe#ece¤eåf%fff¦fçg'ghg¨géh*hjh«hëi,ili­iîj.joj¯jðk0kqk²kòl3lsl´lôm5mum¶m÷n7nxn¸nùo9ozo»oûpqq¿rr@rrÁssBsƒsÄttEt…tÆuuGuˆuÈv vIvŠvÊw wLwŒwÍx xNxŽxÏyyPy‘yÑzzRz“zÓ{{U{•{Ö||W|—|Ø}}Y}š}Ú~~[~œ~Ü^ž߀€`€ €á"b£ã‚$‚d‚¥‚åƒ&ƒgƒ§ƒè„(„i„©„ê…+…k…¬…ì†-†m†®†ï‡/‡p‡°‡ñˆ1ˆrˆ²ˆó‰4‰t‰µ‰õŠ6ŠvŠ·Šø‹8‹y‹¹‹úŒ:Œ{Œ¼Œü=}¾þŽ?ŽŽÀA‚ÂC„Å‘‘F‘†‘Ç’’H’‰’É“ “J“‹“Ë” ”L””Ε•O••Ж–Q–’–Ò——S—”—Ô˜˜V˜–˜×™™X™˜™ÙššZš›šÛ››\››Ýœœ_œŸœà a¡âž#žcž¤žäŸ%ŸeŸ¦Ÿæ ' h ¨ é¡)¡j¡ª¡ë¢,¢l¢­¢í£.£n£¯£ð¤0¤q¤±¤ò¥2¥s¥³¥ô¦5¦u¦¶¦ö§7§w§¸§ù¨9¨z¨º¨û©;©|©½©ýª>ª~ª¿ªÿ«@«€«Á¬¬B¬ƒ¬Ã­­D­…­Æ®®G®‡®È¯¯I¯Š¯Ê° °K°Œ°Ì± ±M±Ž±Ï²²P²²Ñ³³R³“³Ó´´T´•´ÕµµVµ—µØ¶¶Y¶™¶Ú··[·œ·Ü¸¸]¸ž¸Þ¹¹`¹ ¹áº!ºbº¢ºã»#»d»¥»å¼&¼f¼§¼ç½(½i½©½ê¾*¾k¾«¾ì¿-¿m¿®¿îÀ/ÀoÀ°ÀðÁ1ÁrÁ²ÁóÂ3Ât´ÂõÃ6Ãv÷Ã÷Ä8ÄxĹÄúÅ:Å{Å»ÅüÆ<Æ}ƽÆþÇ?ÇÇÀÈÈAÈÈÂÉÉCÉ„ÉÄÊÊEʆÊÇËËHˈËÉÌ ÌJÌŠÌËÍ ÍLÍÍÍÎÎNÎÎÐÏÏQÏ‘ÏÒÐÐSДÐÔÑÑUÑ–ÑÖÒÒWÒ˜ÒÙÓÓZÓšÓÛÔÔ\ÔÔÝÕÕ^ÕŸÕßÖ ÖaÖ¡Öâ×"×c×£×äØ$ØeØ¦ØæÙ'ÙgÙ¨ÙèÚ)ÚjÚªÚëÛ+ÛlÛ¬ÛíÜ.ÜnܯÜïÝ0ÝpݱÝñÞ2ÞsÞ³Þôß4ßußµßöà7àwà¸àøá9áyáºáûâ;â|â¼âýã=ã~ã¾ãÿä@ä€äÁååBå‚åÃææDæ…æÅççFç‡çÈèèIè‰èÊé éKé‹éÌê êMêŽêÎëëOëëÑììRì’ìÓííTí”íÕîîVî—î×ïïXï™ïÚðð[ð›ðÜññ]ñžñÞòò_ò òàó!óaó¢óãô#ôdô¤ôåõ%õfõ§õçö(öhö©öé÷*÷k÷«÷ìø,ømø­øîù.ùoù°ùðú1úqú²úòû3ûtû´ûõü5üvü¶ü÷ý8ýxý¹ýùþ:þzþ»þûÿ<ÿ}ÿ½ÿþÿÿÿÿÿÿÿÿAÂC„ÄE†ÆHˆÉ JŠË LÍNÐQ‘ÒS“Ô  U – Ö  W ˜ Ù  Z š Û  \ œ Ý  ^ Ÿ ß `¡â"c£ä$e¦æ'g¨è)iªë+l¬í-n¯ï0p±ñ2s³ô4uµö6w¸ø9yºú;|¼ý=~¾ÿ@€ÁB‚ÃD…ÅF‡Ç  I ‰ Ê! !K!‹!Ì" "M"Ž"Î##O##Ð$$R$’$Ó%%T%”%Õ&&V&—&×''X'™'Ú(([(›(Ü))]))Þ**_* *à+!+a+¢+ã,#,d,¤,å-%-f-§-ç.(.h.©.é/*/j/«/ì0,0m0­0î1.1o1°1ð212q2²2ò333t3´3õ454v4¶4÷575x5¹5ù6:6z6»6û7<7}7½7þ8>88¿99A99Â::C:ƒ:Ä;;E;†;Æ< >L>Œ>Í??N??Ï@@P@‘@ÑAASA“AÔBBUB•BÖCCWC˜CØDDYDšDÚEE\EœEÝFF^FžFßG G`G¡GáH"HbH£HäI$IeI¥IæJ&JgJ§JèK)KiKªKêL+LkL¬LíM-MnM®MïN/NpN±NñO2OrO³OóP4PtPµPöQ6QwQ·QøR8RyRºRúS;S{S¼SüT=T~T¾TÿU?U€UÀVVAV‚VÃWWDW„WÅXXFX‡XÇYYHY‰YÉZ ZKZ‹ZÌ[ [M[[Î\\O\\Ð]]Q]’]Ò^^T^”^Õ__V_–_×``X`™`ÙaaZa›aÛbb]bbÞcc_cŸcàd!dad¢dâe#ece¤eåf%fff¦fçg'ghg¨géh*hjh«hëi,ili­iîj.joj¯jðk0kqk²kòl3lsl´lôm5mum¶m÷n7nxn¸nùo9ozo»oûpqq¿rr@rrÁssBsƒsÄttEt…tÆuuGuˆuÈv vIvŠvÊw wLwŒwÍx xNxŽxÏyyPy‘yÑzzRz“zÓ{{U{•{Ö||W|—|Ø}}Y}š}Ú~~[~œ~Ü^ž߀€`€ €á"b£ã‚$‚d‚¥‚åƒ&ƒgƒ§ƒè„(„i„©„ê…+…k…¬…ì†-†m†®†ï‡/‡p‡°‡ñˆ1ˆrˆ²ˆó‰4‰t‰µ‰õŠ6ŠvŠ·Šø‹8‹y‹¹‹úŒ:Œ{Œ¼Œü=}¾þŽ?ŽŽÀA‚ÂC„Å‘‘F‘†‘Ç’’H’‰’É“ “J“‹“Ë” ”L””Ε•O••Ж–Q–’–Ò——S—”—Ô˜˜V˜–˜×™™X™˜™ÙššZš›šÛ››\››Ýœœ_œŸœà a¡âž#žcž¤žäŸ%ŸeŸ¦Ÿæ ' h ¨ é¡)¡j¡ª¡ë¢,¢l¢­¢í£.£n£¯£ð¤0¤q¤±¤ò¥2¥s¥³¥ô¦5¦u¦¶¦ö§7§w§¸§ù¨9¨z¨º¨û©;©|©½©ýª>ª~ª¿ªÿ«@«€«Á¬¬B¬ƒ¬Ã­­D­…­Æ®®G®‡®È¯¯I¯Š¯Ê° °K°Œ°Ì± ±M±Ž±Ï²²P²²Ñ³³R³“³Ó´´T´•´ÕµµVµ—µØ¶¶Y¶™¶Ú··[·œ·Ü¸¸]¸ž¸Þ¹¹`¹ ¹áº!ºbº¢ºã»#»d»¥»å¼&¼f¼§¼ç½(½i½©½ê¾*¾k¾«¾ì¿-¿m¿®¿îÀ/ÀoÀ°ÀðÁ1ÁrÁ²ÁóÂ3Ât´ÂõÃ6Ãv÷Ã÷Ä8ÄxĹÄúÅ:Å{Å»ÅüÆ<Æ}ƽÆþÇ?ÇÇÀÈÈAÈÈÂÉÉCÉ„ÉÄÊÊEʆÊÇËËHˈËÉÌ ÌJÌŠÌËÍ ÍLÍÍÍÎÎNÎÎÐÏÏQÏ‘ÏÒÐÐSДÐÔÑÑUÑ–ÑÖÒÒWÒ˜ÒÙÓÓZÓšÓÛÔÔ\ÔÔÝÕÕ^ÕŸÕßÖ ÖaÖ¡Öâ×"×c×£×äØ$ØeØ¦ØæÙ'ÙgÙ¨ÙèÚ)ÚjÚªÚëÛ+ÛlÛ¬ÛíÜ.ÜnܯÜïÝ0ÝpݱÝñÞ2ÞsÞ³Þôß4ßußµßöà7àwà¸àøá9áyáºáûâ;â|â¼âýã=ã~ã¾ãÿä@ä€äÁååBå‚åÃææDæ…æÅççFç‡çÈèèIè‰èÊé éKé‹éÌê êMêŽêÎëëOëëÑììRì’ìÓííTí”íÕîîVî—î×ïïXï™ïÚðð[ð›ðÜññ]ñžñÞòò_ò òàó!óaó¢óãô#ôdô¤ôåõ%õfõ§õçö(öhö©öé÷*÷k÷«÷ìø,ømø­øîù.ùoù°ùðú1úqú²úòû3ûtû´ûõü5üvü¶ü÷ý8ýxý¹ýùþ:þzþ»þûÿ<ÿ}ÿ½ÿþÿÿÿÿÿÿÿÿÿÿ*ëÿÿ@ÿÿV=ÿÿfõøÿÿt0`ÿÿ{€ÂWÿÿtý¬ÿÿ“Ëÿÿð=ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ/ÿÿDWÿÿ^&ÿÿi%FÆÿÿo¶ÿÿs0ëÆÿÿ…ü`ÿÿ ÿÿ”ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ4vÿÿJáÿÿ` ÿÿgׄ?¿ÿŸÿ`ÿÿÿÿztDÿÿ€ÿÿ€ÿÿšÎÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿßÿÿÿÿÿÿÿÿÿÿÿ9¥ÿÿQ÷ÿÿ`JÁÿÿ`Ϫÿÿh°…ÿÿp?UÿÿkÿÿªÿÿŸÿÿÿÿÿÿÿÿÿîµÿÿßÿÿÿÿÿÿÿÿÿ@ÿÿÿÿ?(ÿÿU¥‰ÿÿ^m.ÿÿ``ÿÿÿÿd9`ÿÿR’ÿÿ`º"ÿÿ€*Ÿÿÿ£\$ÿÿÿÿæ›ÿÿÐÿÿÿÿÿÿÿÿ¿ÿÿÿßÿÿÿÿÿB.ÿÿV\5Bÿÿ[¶.ÿÿ[a¾×ÿÿXx<ÿÿ:V>^ÿÿOƒ[òÿÿu/dmÿÿ¦+cÿÿÿÿÈMÿÿuÿÿÿÿÿÿÿÿßÿÿÿÿÿÿÿÿÿH©ÿÿUT[ƒÿÿ@þ¦ÿÿYþwUÿÿ!8½ÿÿonŽÿÿ0ÿÜÿÿ^ã ÿÿ£Õ¦$Þ³ÿÿ ŸÿÿÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿßÿÿÿLõ ýÿÿU€èÿÿ@üèÿÿIóÿÿw0ÿÿqÿÿÿÿÿÿÿÿÿÿÿÿR€ÿÿ@ï¯ÿÿJq'òÿÿ,©ÿÿ;ÎFîÿÿP?ycÿÿ@²±ÿÿ ¤êCÿÿž<ùq­ÿÿéf€ÿÿ( X7ÿÿãÿÿÿÿÿÿÿÿÿÿÿÿP¥ŸÿÿÿLk½Tÿÿ:¬ÿÿ ,£ÿÿmŠ@ÿÿŸÿe ÿÿ° ÿÿŸÿ㈶RŸÿ`ÿÿ9OÿÿüŽ/÷ÿÿ`fÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿMeÆFÿÿL}ÿÿsÿÿUF ÿÿ¡Ø1HÿÿßÿRÕü6ÿÿ¦ÒrÿÿÇ O¹ÿÿú–Íûÿÿßÿÿÿ|Wÿÿ<¢ÿÿOÿÿÿÿÿÿÿÿÿÿ@ÿÿÿÿC^_jÿÿ 3ÿÿ–Á‰ÿÿßÿ…ï=ÿÿA¸ÃVÿÿwü„Šÿÿ«ß UÿÿØ[ÿÿÿÿ¿ÈKˆÿÿX[6Åÿÿ "˜ÿÿÿÿÿÿÿÿÿÿÿÿ@íÖÿÿ3Ð ÿÿf}ÿÿÊô^ÿÿÇzÿÿ+2•\ÿÿ\ÏRWÿÿ‰£ÿÿªËÿÿ£G”¥:ÿÿc“Ùÿÿv)ÿÿI}ÿÿÿÿÿÿÿÿÿÿ@™ÿÿ#eÿÿ¤úÿÿÿÿÏÛÿÿ£Zÿÿ‚n]ÿÿ9}.Cÿÿ`ÿÿwp@ÿÿu«[&âŒÿÿìGÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ0øBÿÿvŸÿÿçgÛoÿÿ©Yÿÿ}lÿÿSüÿÿb‚ÿÿ"fÿÿª)TÿÿQaÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ3ãÿÿÃræÿÿ±ãÿÿ{ÆÿÿIBÿÿ—ÿÿÿÿÿÿÿÿ%!ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ˜ûÿÿ¿Aÿÿ€ÿÿBøÿÿCÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîMË—ÿÿŠ1ÿÿC|ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿYV*ÿÿ[í¿ÿÿÿ`œQÿÿZÿÿ)Ûÿÿ>ÿÿ\ýÿÿ€5ÿÿ¢äÕÿÿÇ'ÿÿÿÿù”ÿÿßÿÿÿÖ€ÿÿ¿ÿÿÿ®ÐÿÿÿÿÿÿZR@ÿÿSâíõÿÿ`q¦ÿÿIÞÿÿ$¦ÿÿ;޶ÿÿTÚžÿÿ{©#ÿÿ¤ àÿÿÌp„ÿÿÿÿßÿÿÿÖ3ÿÿ¿ÿÿÿ©‚ÿÿÿÿÿÿÿÿXf_»¿ÿŸÿ`ÿÿÿÿ\ÅKµÿÿÿÿ 9ÿÿ.# ÿÿJÙ9ÿÿvA¹ÿÿ¥@BNÿÿÓÄ@ÿÿÿÿ†Þÿÿ¿ÿÿÿ¨Ùÿÿ íÿÿÿÿÿÿÿÿX—zÿÿDóÿÿW¢)—ÿÿ@ÿÿ*õÿÿm:Mÿÿ?kT@ÿÿmøaÅÿÿ¦e(ÿÿÝ(aPÿÿÿÿ@ÀØÿÿ¬ÇÿÿiÿÿÿÿÿÿÿÿÿÿXO–éÿÿU¸ÂRÿÿP¦ ªÿÿ ÿÿ›'=ÿÿTgÿÿ-ÑqÈÿÿ`±ƒ>ÿÿ¤Ç‰Aÿÿ킇]ñŸÿÿe·œÿÿ—ÿÿQÿÿÿÿÿÿÿÿÿÿS¤¸aÿÿUªÿÿBÿÿp«ÿÿ<5ÿÿk4ÿÿ'•ÿÿP~£|ÿÿ¢Á­ÿÿÿÿ­NÔéÿÿ‡Ÿÿÿÿyÿÿ*ÐÿÿÿÿÿÿÿÿÿÿKžÝ&ÿÿSŠz)ÿÿ$ÿÿ‡ÿÿ•@ÿÿ4œdÿÿ ¤iÿÿ3ÄÿÿŸÿÑrßÿÿÿÎÉ«èÿÿ¥fŽÒÿÿ\¥ÿÿ·ÿÿÿÿÿÿÿÿÿÿ ±I±ÿÿÿÿO©YÖÿÿÿÿÇ ÿÿCJ60ÿÿdp`ÿÿ`›×ÿÿäõÿÿ<÷dŸÿÿÿîºn?ÿÿÁ&}èÿÿØINÿÿîÿÿÿÿÿÿÿÿÿÿŠL<ÿÿÿÿI24zÿÿÿÿ@ µÿÿ€ ÿÿ£'Pöÿÿ¹ç‹lÿÿ°²Òæ´Øb`ÿÿ@ÿÿÿ# öÿÿÜ)-gÿÿ@ vÿÿ ÿÿÿÿÿÿÿÿÿÿL*ÅÖÿÿ?û|ÿÿ‰ÿÿrUÿÿ¹Ÿ âÿÿë‰7òòÿÿuÇýÿÿµ|?ÿÿñ1ßÿÿÿÞc„wÿÿ¾–ÿÿ`ÿÿ/?ÿÿ ³ÿÿÿÿÿÿÿÿJUƒ(ÿÿÿÿDFÿÿ£+ÿÿî½åêÿÿ ;½5ÿÿ] ÿÿ—\—ÿÿËrÿÿ©!¸×Øõÿÿ#XLÿÿ,9Hcÿÿ&ÿÿÑÿÿÿÿÿÿÿÿ@@ÿÿ ,ÿÿ€ÂÿÿÝ¿é°ÿÿÀ1ÿÿ ¶ÿÿ?ãPuÿÿs#ÿÿ˜Lÿÿt›†ÿÿÿÿs§ìÿÿ•ïÿÿŠÿÿT/ÿÿ-Úÿÿ bÿÿÿÿ¹ÿÿUgÿÿÁÊóhÿÿÄÿÿœgÿÿnÿÿÌ2ÿÿÿB{ÿÿ Y¿ÿÿTÏI¨ÿÿß7Yìzÿÿó…ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóÿÿŸÿÿÿÿÿ˰ÿÿœÛÿÿnÝÿÿGjÿÿ ÿÿ¥ÿÿ]“ÿÿ9o{ÿÿš˜ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿuÇÿÿê™×†ÿÿ¡Tÿÿjêÿÿ;«ÿÿ ÿÿÿÿÿÿÿÿ¨ÿÿeòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÊàÿÿªìÿÿljÿÿ3Ðÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ¯ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿó{ÿÿ¸œÿÿsÿÿ2ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ,YÿÿÞëÿÿÿÿÿÿÿÿÿÿÿÿÿÿSÁÛ!ÿÿ]õx×ÿÿH ÿÿ)ÿÿ5ÿÿ1ÚÿÿPgTÿÿvê ÿÿ£wEÿÿÏ” ÿÿü ÿÿÿÿßÿÿÿÀ8ÿÿ¿ÿÿÿ¥WÿÿÿÿÿÿB´ÿÿÿÿ[ ]}ÿÿ>Éÿÿ¼ÿÿÀÿÿ+GÎÿÿI1%Ûÿÿsv4ñÿÿ¤6ÍÿÿÕÂ0Aÿÿÿÿ$ZüûÿÿÉíÿÿ¿ÿÿÿ¦ìÿÿ‘&ÿÿÿÿ¿ÿ—ž`ÿÿÿÿX±@ÿÿ-jÿÿÿÿQÿÿ!R!Rÿÿ?ú>±ÿÿneJèÿÿ¤œOJÿÿÜ K/ÿÿÿÿ?[ì7ÿÿ¿ÿÿÿ«Sÿÿ•èÿÿ”ÿÿÿÿÿÿ@øGÿÿUš$äÿÿÿÿÿÿüþÿÿú:ÿÿ5…Sÿÿf¤bâÿÿ¤Ði+ÿÿå”g|ÿÿÿÿXðßÿÿÿ´ÿÿÿÿuÿÿÿÿÿÿÿÿTßвÿÿP¼ ÿÿÿÿÿÿ ÿÿM†ÿÿ&þiÛÿÿ]J}Íÿÿ¤êƒÑÿÿòC„sù‰ÿÿsÉÍ‹ÿÿ–Ÿÿÿÿ‡uÿÿN(ÿÿÿÿÿÿÿÿV°ÿÿI ÿÿÿÿÿÿ,¤ÿÿ^KÿÿZÿÿO„–Nÿÿ¢ùŸÿÿÿÿÿ¡ßÿÿÿŒf¿†ÿÿ6_•tÿÿpìÿÿ-ØÿÿÿÿÿÿÿÿUÀ(ÿÿ?#ÿÿÿÿsÿÿ 'òÿÿ(ÍZ]ÿÿ™Vÿÿ9k²]ÿÿ €¿Ñó‚ÿÿ¾Ó¿ÿÿÿ¦vŸÿÿÿT\€¯ÿÿUÁÿÿ§ÿÿÿÿÿÿÿÿS fÿÿ)pÿÿÿÿ!sÿÿQX|ÿÿm_HOÿÿ\¨oÿÿVÙøšå¨ÿÿßÿ~ÝÿÿÅän4ÿÿ{lNÿÿ='ÿÿ ÒÿÿÿÿÿÿÿÿJÉ?%ÿÿÿÿÿÿK'ÿÿæ‚ÿÿ®ó92ÿÿ¿ÿzÙóÚ½g²¢r¿`]Pí_>«ÿÿõ¾ ÿÿÞ*ÿÿ¤K7Üÿÿ ÿÿñÿÿÿÿÿÿÿÿAú 9ÿÿÿÿ#dÿÿ}ÿÿÀÑÿÿ÷eé­ÿÿ^M¾ý4Þ£IÂí«9KÖßÿÿÿÔ“¢ÿÿ¾<@ÿÿ’çÿÿE¾ÿÿ"+ÿÿŒÿÿÿÿÿÿ"Eÿÿÿÿ_Fÿÿ²ýÿÿøåßÿÿÿ¸4ÿÿD·|œÿÿ†=”ÿÿÁÿÿº¬ïí¬ÿÿœzžëÿÿf¼I?ÿÿ >Òÿÿ!äÿÿ wÿÿÿÿÿÿÿÿ5 ÿÿjÿÿí:àÿÿ»šÿÿÌÿÿ$6Qÿÿ` ÿÿÒ‹ ÿÿ{üz;ÿÿõŸm¹Ö€ÿÿ@€ÿÿŒÂÿÿq*ÿÿOÄÿÿ1Mÿÿÿÿÿÿ~ ÿÿÝÉåyÿÿ¼ÿÿÿ“„ÿÿpÿÿ6Mÿÿ0«ÿÿ­LÿÿWà>Xÿÿ§z2Xÿÿÿÿ†çàÿÿêGÿÿð#ÿÿøµÿÿÿÿÿÿÿÿS°ÿÿ÷ïQÿÿ¿ÿÿÿ‘œÿÿeÿÿ@ÿÿÖÿÿÿÿ@&ÿÿŒÿÿ£â@ÿÿÞH@ÿÿÿÿ4IÿÿÿÿÓÄ_ÿÿ·ŒÿÿŸÿÿÿ!ÿÿ0ÿÿÿÿSI ÿÿ›ÿÿÿÿÿÿQ6ÿÿ%õÿÿ7@ÿÿg¨Ouÿÿ£éUÈÿÿå™TèÿÿÿÿGOòzÿÿ&t¿ÿÿÿ§¿ÿÿ–èÿÿuxÿÿŠÿÿÿÿQ 0ÿÿÿÿÿÿÿÿŒÿÿÝ:6ÿÿ-R}ÿÿ`|cÿÿ£ñjêÿÿïskôüÛÿÿ^Óßÿÿÿ?ÿ·FÿÿŸ±ÿÿ…úÿÿQžÿÿåÿÿÿÿH¨ÿÿÿÿÿÿÿÿeÿÿKBÿÿkÿÿUP+ÿÿ£7ˆÿÿþ4‹4åÿÿx¼Ë¤ÿÿ[¤¶ÿÿ‹Ôÿÿm4ÿÿ/cÿÿÿÿÿÿ=Nÿÿÿÿÿÿÿÿ&ø,ÿÿ1ØCéÿÿ‹ÿÿ>ò êÿÿ¡~­ƒðJÿÿ­PÁŠÿÿ˜ª&ÿÿz~–ÿÿtcÿÿLØÿÿÿÿÿÿÿÿ)^ÿÿÿÿÿÿ"¡ÿÿb›ÿÿn‚:­ÿÿ\ €öõi"!ìÿÿkº2Dÿÿ¢Ê80ÿÿß7lÿÿÿÿ*ûwÿÿàêÿÿ¹Nÿÿ¢ÿÿ•‘ÿÿwÇÿÿÿÿ 2ÿÿÿÿÿÿÿÿÿÿ@"ÿÿ2¥;CÿÿeIåÿÿ¢ûQ’ÿÿèRSBûÎÿÿC¼åpÿÿ.~Ùeÿÿð§?ÿÿ–hÿÿz„ÿÿOÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ=ÿÿ!XqÿÿZ*iVÿÿ¢‚s€ÿÿøhx àúÿÿc³Ð«ÿÿL1¿ÿÿÿ#•˜õÿÿ|^ÿÿUëÿÿ)÷ÿÿÿÿÿÿÿÿÿÿÿÿ3Eÿÿ+Š;ÿÿ[~\ÿÿCº‘nø!3¨H|ìè=ú Ž[¾ÿÿ†« ÿÿp¹ŸÿÿÿG†€ÿÿdÿÿ4òÿÿxÿÿÿÿÿÿÿÿÿÿ#¿ÿÿ`ÿÿxr$0ý­eC™k4çNsrRVgçÔBt¿ŸÔl©f1vâΡêy„ÿÿ­:k`ÿÿ˜'fÿÿs\gˆÿÿETÿÿTÿÿÿÿÿÿÿÿÿÿAÿÿW5ÿÿ’ÄÿÿÂG 纤¡3C¡¤C]:™Wˆ~ yûyŒÙC•µ ÿÿÇãžÿÿ·¸ÿÿŸ./Ñÿÿ ,±ÿÿ ÿÿÿÿÿÿÿÿÿÿDÄÿÿަÿÿÊgíiäà˪ÜñÊ!‰¤UÎ=7\LâzÝÀçs«mQ[Û»Ò¯~ù“«ÿÿ¥-z:ÿÿ“éSæÿÿ€ÿÿ@ÿÿ ¯ÿÿ]ÿÿÿÿÿÿ,‡ÿÿ‚sÿÿÉOïcïcÕSüž³Âù­ 1‡Çï(3=óVuèJ[_d EÿíÓÍÀi1×Yÿÿ„¾Ùÿÿq'ŸÿÿÿW„AÐÿÿ7ûÿÿh ÿÿÿÿÿÿ Óÿÿp8ÿÿÁò;ò;ÕLÿÿ±ZÿÿëÿÿW¯ÿÿCbÿÿÝ|3ÿÿ‹TlEÿÿÿÿa/ìàÿÿN÷ÑÍÿÿ3ØŽäÿÿq(ÿÿT÷ÿÿCHÿÿÿÿVŒÿÿµyüQüQÖ¥ÿÿ®Îÿÿˆÿÿfeÿÿ>Øÿÿ€ÿÿY@šÿÿeû3?ÿÿ»o)tÿÿÿÿ"ùÃÿÿüÐñÿÿÈÿÿÄÿÿ¸çÿÿÿÿ =ÿÿõýÛ@ÿÿ¯ƒÿÿ‚èÿÿZ\ÿÿ8 ÿÿRÿÿÿÿ§ÿÿJ0ÿÿäÿÿË5ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäcßÿÿÿ³Sÿÿ=ÿÿQ°ÿÿ(ÊÿÿWÿÿÿÿÿÿÿÿ)ºÿÿh¿ÿÿ£ôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáûSº ÿÿ‚éÿÿLîÿÿôÿÿÿÿÿÿÿÿÿÿÿÿÜÿÿ@ÿÿtˆÿÿÖ§ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿ÿÿÿ‡ÊÿÿKæÿÿòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ@ÿÿ’)ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿG0ÿÿs#ÿÿ "ÿÿÒÿÿÿÿÿÿÿÿÿÿÿÿà›ÿÿ¿ÿÿÿ¿ÿÿÿ¶Œÿÿÿÿÿÿÿÿÿÿÿÿÿÿ—ÿÿFsÿÿrõÿÿ ŸÿÿÒbÿÿÿÿÿÿÿÿÿªÿÿßÿÿÿ¿ÿÿÿ¹çÿÿªÿÿÿÿÿÿÿÿÿÿÿÿÿÿVÿÿEkÿÿr­ÿÿ¡îÿÿÓ‚9ÿÿÿÿÿÿÿÿô_ÿÿÞ>ÿÿ½,ÿÿ«5ÿÿŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¨ÿÿAH ÿÿn.?ÿÿ¡v¨ÿÿÙE7ÿÿÿÿ·ùÿÿæFÿÿÏjÿÿ«çÿÿŸŒÿÿjÿÿÿÿÿÿÿÿÿÿÿÿÿÿ™ÿÿ7È#KÿÿgØ3;ÿÿ¡Á:¹ÿÿá¾=ú‚ÿÿ/çãkÿÿÕÛyÿÿ¿ÿÿÿ°ÿÿŠYÿÿp…ÿÿÿÿÿÿÿÿÿÿÿÿ6ÿÿ-Òÿÿ&‰E8ÿÿ] V_ÿÿ¢_ƒýúïÁU¸ßèÿÿSʲÿÿp4l q |tvÀŸmBì)þý·5Îÿÿ§Æÿÿ›ýÿÿr76$ÿÿ<ÿÿÿÿÿÿÿÿÓÿÿQ'ÿÿ•MøÂÄ<ÚdÍ[·XÅ ILü³f,ø?Äg ¦»aª]Äaª«[ºñYx üþ“sÿÿ…_ÅÿÿvÇ1jÿÿ`ÿÿ(æÿÿÿÿÿÿÿÿ@ÿÿnÿÿÑ^ßÿÞÖ¿ÿá 'áÜývýר °./P‚ÒTUáKoK²ÖE²xG<€ÌOþ¿sù³Fÿÿb ŸÿÿÿR@ÿÿ3:`ÿÿÐ#Yÿÿ4ÿÿÿÿ)4ÿÿ„|ÿÿÎÚßÿßÿÈAó×­|ÿÿ‹Õý¹b+ô­|;IòóEC9ë4$ø>¢g8ÐøŒÿˆVößÿÿÿAâÑ?ÿÿ/‘µ8ÿÿ ²qOÿÿSÿÿ<3ÿÿÿÿsÿÿÆCßÿßÿÏ“ÿÿ©žÿÿƒéÿÿcÿÿBîÿÿ nÿÿÉ<÷ÿÿi’0:ÿÿİ'ÿÿÿÿÇöhÿÿõáeÿÿº•ÿÿ¯Šÿÿ§ãÿÿÿÿ»ðélè¦Ñóÿÿ¨±ÿÿ{âÿÿVÿÿ5IÿÿÿÿÿÿöÿÿNIÿÿ—ÃÿÿÛ®ÿÿÿÿÿÿÿÿÿÿÿÿýÿÿûÏÿÿô&ëâ×ÿÿª‘ÿÿxÿÿL‘ÿÿ%ñÿÿ×ÿÿÿÿÿÿÿÿ0UÿÿuÇÿÿ·­ÿÿ÷ûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞîÿÿ¯%ÿÿwgÿÿF“ÿÿ?ÿÿÿÿÿÿÿÿÿÿÿÿ,ÿÿP÷ÿÿŒýÿÿÐÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¶TÿÿyßÿÿCíÿÿAÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'(ÿÿ`ÿÿ—_ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¸ÿÿDÖÿÿpÿÿÿž¬ÿÿÑUÿÿÿÿÿÿÿÿÿÿÿÿõâÿÿÏBÿÿ¿ÿÿÿ¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿœÿÿCòÿÿpÆÿÿŸ4ÿÿѬÿÿÿÿÿÿÿÿþÿÿê×ÿÿ˜ÿÿ¿ÿÿÿ²ôÿÿÿÿÿÿÿÿÿÿÿÿÿÿ&ÿÿBÀÿÿppÿÿŸÙÿÿÒÿÿÿÿÿÿÿÿñNÿÿßÿÿÿ¿ÿÿÿ°Þÿÿ¡Ïÿÿÿÿÿÿÿÿÿÿÿÿÿÿ?ÿÿA'ÿÿoíÿÿ ÿÿÓtÿÿÿÿóâÿÿáVÿÿÙÿÿ³'ÿÿŸÿÿÿ“hÿÿÿÿÿÿÿÿÿÿÿÿÿÿÈÿÿ;-ÿÿiË`ÿÿ Z#šÿÿÛM'÷/ÿÿ˜ßÿÿÿ®Õ‘ÿÿÆgÿÿŸÿÿÿŒ˜ÿÿsÿÿÿÿÿÿÿÿÿÿÿÿÿÿîÿÿ*V3‡ÿÿ_¡BäøÐ)¤±'·ïÃ*Ü…(£Û3ÿÿCÞÅšÿÿ+G¿ÿÿÿ¶Ãÿÿ‹hÿÿneÿÿKˆÿÿÿÿÿÿÿÿÿÿÿÿ ïÿÿ1¡–÷¦3Æ#ž3\ä8£XÌ6·Õô:x’39’ËÂ:[Î4:o´$àÿfgŸÿÿÿQ°˜”ÿÿ=û•Ôÿÿ pßÿÿK•ÿÿ(xÿÿÿÿÿÿÿÿÿÿ!ÍÿÿV`÷±ymØ?a,()Žº,IÌ<ƒJ«K6z¢L域J2»­N‰§.HõhÃ`ÿÿ}`ÿÿe˜`ÿÿLUäÿÿ.†ÿÿ†ÿÿÿÿÿÿÿÿ ÿÿ[êÿÿ‘ßÇš—¼òŒÓo—ÀtÊ/gA{qJ]y\Aaâkbz§‘]¬[õY*ëÃgKÍÿÿš·öÿÿŠñÿÿ~õÿÿ \ÿÿÿÿÿÿÿÿJÿÿYÖÿÿ™å{°¢Æ‹¶¤¿±Å® 6|<Ÿ#õ2†Wø”UINgS9—R¡fJOÛ—ŠìG6mÏÿÿwp]TÿÿhNFÆÿÿ^Dÿÿ4ýÿÿhÿÿÿÿÿÿPnÿÿšÂì¿ÿÏÌì²ÑùGÏe(Âþ!dAK¼æHÏ<=\¿ú˜È8+:úÀöæ¤57ª-ÿÿUOš’ÿÿDó‰Jÿÿ78@ÿÿ ©%žÿÿ9ÿÿÿÿ@ÿÿ•@ïPĦ×=Ù€¿ÿêÈŸÿð{fë³SàÞ Uï.\ÞN9*ô*ŸâpŒ'Y(.ä•ßk$‘Öýÿÿ5ÆÇHÿÿ#9»ÿÿ½xÊÿÿQƒÿÿ8Òÿÿÿÿ‰ù”ͶßÿåÏ¿ÿð¥0ÿÿ~¤ÿÿ_¹ÿÿAÅûZâúo'[EþptØÿÿÉÔ%%ú‚ÿÿqëÿÿeâRÿÿ¸×ÿÿ¥­ÿÿŽÿÿÿÿÌÏßÿâdÅOø`¢ùÿÿv“ÿÿR0ÿÿ2Ÿÿÿ”ÿÿÿÿpÿÿPàÿÿ•ÿÿè?ÿÿÿÿÿÿÿÿöÝÿÿìÆÿÿè0ÿÿßÿßÿÍkÿÿ£NÿÿqÿÿHÿÿ#Iÿÿÿÿÿÿÿÿÿÿ4üÿÿ~4ÿÿÉÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÒýÿÿ¦ÿÿowÿÿA'ÿÿãÿÿÿÿÿÿÿÿÿÿÿÿÂÿÿ\Øÿÿ¢ßÿÿßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ«@ÿÿp.ÿÿ?Îÿÿ/ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃÿÿ7}ÿÿvÚÿÿ³¾ÿÿúKÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ3ÿÿB“ÿÿnêÿÿœ„ÿÿÐ*ÿÿÿÿÿÿÿÿÿÿÿÿöÿÿßÿÿÿ¿ÿÿÿ¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿêÿÿA‹ÿÿn¥ÿÿÿÿЃÿÿÿÿÿÿÿÿù–ÿÿéNÿÿßÿÿÿ¿ÿÿÿµ—ÿÿÿÿÿÿÿÿÿÿÿÿÿÿDÿÿ@2ÿÿn>ÿÿÄÿÿÐæÿÿÿÿÿðÿÿê‘ÿÿßÿÿÿÕØÿÿ³fÿÿ£ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ)ÿÿ?ÿÿm§ÿÿž‡ÿÿÑNÿÿÿÿî•ÿÿßÿÿÿÔæÿÿáÿÿŸÿÿÿ’žÿÿÿÿÿÿÿÿÿÿÿÿÿÿyÿÿ<ÿÿl ôÿÿŸi üýN ÓÔ\¤ôqÿÿAݰÿÿÍÿÿÀ€ÿÿ·¾ÿÿŒ¡ÿÿqèÿÿÿÿÿÿÿÿÿÿÿÿÿÿè ÿÿ.‰ Põ\~d-&ç÷¢•äªÞ»)ÇìŽØ•7úçgÁÿÿ¹rÿÿ¯Yÿÿ ¥ÿÿnpÿÿJÿÿÿÿÿÿÿÿÿÿÿÿÏþí7Óeå&jŽ#‹Òû*ÞO*'ÈÅJ,kƒg*_»˜,j¹¢+ µ«,ïé*œ›=ÿÿCÍ”Uÿÿ+¸Ž‹ÿÿ‹îÿÿLÁÿÿ'‹ÿÿÿÿÿÿÿÿÿÿ)ÿÿO¸æ?aþÅÔP·&í¨Ð;^4?;tš&¦Ši;oáE>[gÿÿp‚VŽÿÿ[ […ÿÿBÐ`ÿÿ#51ÿÿïÿÿÿÿÿÿÿÿ¥ÿÿ[ñ€Í¾…«±|R燉eÅ'#3»c_QxQzR”]cU?•NZ1V!ÔàJØzÿÿ‹Y ƒÿÿ~3ÿÿtÿÿ]§Èÿÿÿÿÿÿÿÿ@ÿÿ_Bü*—ÒÖ· ”¶G£I“¤ llÌaÃ%cJ!ƒlI@?.Eφ¡ŽÃ;BƪÐE8<kÿÿhKX¼ÿÿZ¦I¦ÿÿP…7|ÿÿ@:ÿÿGÿÿÿÿ :ÿÿ\|ÿÿ¡oßÿ¶q¿ÿ¼{Ÿÿ¼º~V»ËV¿³8­ 5‡¬Ã> -W1p¯8†)Ä.ޝ~Êë''¢QÿÿGQ’¸ÿÿ7ª…üÿÿ*ÝsMÿÿ*~ÿÿ ÿÿÿÿS)ÿÿ Ñßÿ¼ÒÀ‹ÁÖ¬Ô4ŽGÜci=Ù<D.Ð>äj!ŒÌÙ/ÇýÏëy\ǽÑ[Á#_Í?ÿÿ)¾áÿÿ¬´Qÿÿ2¥ÿÿSiÿÿ5=ÿÿÿÿ›Ûãg¿ÿËlÒ ¼nÿü"ziÿÿV§ù¸1VíS蚨 w Ììßi1 ¥ yïAµâr•ðÿ{ Hã#ÿÿÝ ÿÿÒÒÿÿ—9ÿÿu8ÿÿèd¿ÿÚOär¿ÿö`›'ÿÿq¶ÿÿNoÿÿ/Ëÿÿµÿÿÿÿ ÿÿRMÿÿ¡ÿÿï°ÿÿÿÿÿÿÿÿÿÿÿÿâ©ÿÿÕáÿÿßÿçÒ¿ÿöc™õÿÿkÍÿÿCçÿÿ ‹ÿÿÿÿÿÿÿÿÿÿ8#ÿÿƒMÿÿÒ]ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄ·ùW›3ÿÿhŒÿÿ=üÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿcÿÿ¯Tÿÿõ„ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡öÿÿgÛÿÿ9bÿÿ Yÿÿÿÿÿÿÿÿÿÿÿÿÿÿ HÿÿAÄÿÿ‡!ÿÿÑÔÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÜÿÿ@WÿÿlÖÿÿšZÿÿηÿÿÿÿÿÿÿÿÿÿÿÿðxÿÿàšÿÿÛ¤ÿÿ¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿiÿÿ@ÿÿlÿÿšóÿÿÏ ÿÿÿÿÿÿÿÿôœÿÿâÿÿßÿÿÿÌ„ÿÿµ[ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ•ÿÿ>\ÿÿlÿÿ›žÿÿÏgÿÿÿÿû§ÿÿä(ÿÿß6ÿÿÓjÿÿ¿ÿÿÿ¡ÛÿÿÿÿÿÿÿÿÿÿÿÿÿÿHÿÿ<3ÿÿkZÿÿœ\ÿÿÏÃÿÿÿÿéÿÿÚ;ÿÿÌÿÿÀÿÿ´ÿÿ¤ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ cÿÿ9lÿÿj]ùR™Mð5ÅÖéIó[×CÿÿÄèÿÿ¼.ÿÿ²*ÿÿŸÿÿÿneÿÿÿÿÿÿÿÿÿÿÿÿÿÿÂõu0ªÄå@ åZ³Óر {ˆÔ MÐ+º· ÊHå' ͽ¶ÿÿn°«ÿÿ¤¨ÿÿ%ÿÿ‰Aÿÿ71ÿÿÿÿÿÿÿÿÿÿÿÿ bî]%ŽÔoÛyvÂØSE»µöZv¿Z¬Ö¨¨fé§ çÙ €•cÿÿ7[Œ‚ÿÿ ‹Þÿÿ“„LÿÿoÓÿÿ%ÿÿÿÿÿÿÿÿÿÿ$ýùFs×IN µ§B¢ ™ô0--PŒ¨1ž`»/R‚»1v— /ü}0œÌÝ/xkïzúRBASeÿÿLmPÿÿ:WX—ÿÿ ¿XEÿÿ îÿÿÿÿÿÿÿÿŠÿÿXQßÿmŽûrp›mH9ùxÓXÖ&rVeE¿F CÜPøI„>>÷M³JZ¾O;¼G}EîóÆ=¬®ÿÿmWÿÿ`ëÿÿWÿÿ9KÿÿÿÿÿÿpÿÿaöåkVÄù¤-ŽùƒK]^€’Lî>vvY>Å0Õ:,xÓ~i,Á7xÔº1*#4Ówqñÿ(UWÓÿÿIÒHÿÿ@=&ÿÿ75 ÿÿ*¥ÿÿÿÿ ÿÿd_ñš3Ì:£¯+ª¾¨«èn$ª–HP£ãi ¤(½œÐ4÷$²žÒu~Ÿ!Ëžñ³ƒCÙží?Šÿÿ(6€&ÿÿwÿÿ^&ÿÿdÿÿÿÿ`ãõõŸÿו·•»x½TŸÿÆ8_˽XŠÉ83•Á[¼½%o¿1iG ˆCÀ ªé j W¿êçË Ü¶pÿÿ =«¢ÿÿ¢‡ÿÿŠTÿÿ3cÿÿÿÿ¨„ßÿ¿ÿ¿ÿÈr¦€Õû޵éjjÙñ¶DÒé~LÝÕ×ùÛPXöß6¡9âèÝÿÿÕ§ÿÿΔÿÿ»ãÿÿoÿÿßÿ¿ÿ¿ÿÊ„·ûð4•­ÿÿmÿÿJ¨ÿÿ,®ÿÿ ûÖýìÿÿRÓÿÿ¢Gÿÿó#ÿÿÿÿÿÿÿÿý ÿÿðâÿÿË•ÿÿË÷×p¿ÿü“mÿÿfÿÿ@ÿÿyÿÿÿÿÿÿÿÿÿÿ:"ÿÿ†wÿÿ×zÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿ÿû“jÿÿbVÿÿ8Óÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿh›ÿÿ· ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ•’ÿÿ`„ÿÿ3yÿÿŠÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿÿJÿÿ’^ÿÿßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿÿ>úÿÿj¹ÿÿ˜(ÿÿÍÿÿÿÿÿÿÿÿþþÿÿêNÿÿßÿÿÿßÿÿÿ¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ=|ÿÿjQÿÿ˜¿ÿÿÍ\ÿÿÿÿÿÿÿÿïzÿÿßÿÿÿÝ|ÿÿÐñÿÿµßÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ; ÿÿiÃÿÿ™dÿÿͪÿÿÿÿöÍÿÿßÿÿÿ×LÿÿËNÿÿ¿ÿÿÿ¡Øÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ‡ÿÿ9IÿÿhýÿÿšÿÿÍöÿÿÿÿã ÿÿÓ'ÿÿÃ!ÿÿ¾\ÿÿ³ÖÿÿŽ#ÿÿÿÿÿÿÿÿÿÿÿÿÿÿnÿÿ6Rüžf'îè×åJ¼]ß–ëµÐ„ÿÿ¿³ÿÿ²(ÿÿ§þÿÿž²ÿÿm$ÿÿÿÿÿÿÿÿÿÿÿÿþ¸$èŒ)kÖéQîÊ|ÁJµ§a»ScÒK·3ý6§“ÿÿ›Eÿÿ‘^ÿÿ†ÅÿÿG@ÿÿÿÿÿÿÿÿÿÿÿÿà ¯Ä§ ȱ {´Þó=% ò¨okÊ¿Ÿ:#™ä~™ÌéÇ_–èôV‡3ÿÿ"‚=ÿÿx¸ÿÿlöÿÿ'ÿÿÿÿÿÿÿÿÿÿ‹îI4 È?:¡¦5rf¢‹–$û$— 9~Ú&VV!Åu¹&ƈæ"noø%à¹r!ýlj$Wè½ åLwÿÿ@}OBÿÿ)ÂLBÿÿ UžÿÿG7ÿÿÿÿÿÿÿÿúŸOºÒ‡__­»`Œ!]&kFLùÈKs;ëëÞM+pýÿÿ]/{ÿÿT*ÿÿIÿÿ:® ÿÿÿÿÿÿ"òÿÿböÛóz#·DÓ•C~/sÑ{ªQtc ß ™3iþ59#ˆ.Ikôp‹Ì+l§™W(÷jÝÚÊšW ÿÿ9ùG8ÿÿ0N<»ÿÿ&ò-ûÿÿ ÿÿÿÿÿ"+ÿÿiùßÿ‹k¿ÿ—ûŸÿ™Ó€˜œ]û˜…9Ï•¥:“*±ð‘Rgpâ ‘k h»ú‘ÖU -‡”ÿÿf|ÿÿ >r>ÿÿÂcÈÿÿ)ÿÿÿÿkUäÞ˜AÀ¿ ¿§ß©8é²}lº¸°Ff¸ "²Yö®‡ÎÔ°/Z—ɰƗ[$°¦Ïè®óÿÿ¤±ÿÿ˜äÿÿŒnÿÿ:lÿÿì#ŸÿÌý±‡¸i®ŸÿÎÏ€ØVÜJ0ÖÑ <ÏeÏþ²Õ8U9Ù#›ùÛpád×1ÿÿÎìÿÿÇÄÿÿ»Uÿÿs/ÿÿÙ¿ÿ¿ÿÎø¡,ÖügþXh£ÿÿF‹ÿ¼! ÷Yïü÷¥ªþÌR(ÿÿ¢Nÿÿôÿÿÿÿû5ÿÿõ6ÿÿî7ÿÿÉ ÿÿ¿ÿÐ ·úßÿÿa‚ÿÿ<ÿÿÉÿÿÿÿÿÿÿÿÿÿ;9ÿÿˆÿÿÙàÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¼‰ÿÿŒyÿÿ[ÿÿ3Ëÿÿ kÿÿÿÿÿÿÿÿÿÿÿÿ"Éÿÿl$ÿÿ» ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿpÿÿX_ÿÿ-ßÿÿ¥ÿÿÿÿÿÿÿÿÿÿÿÿÿÿfÿÿOÜÿÿ™ÆÿÿèÆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ rÿÿ<ÿÿhŒÿÿ•èÿÿË?ÿÿÿÿÿÿÿÿú¶ÿÿä\ÿÿßÿÿÿÛÿÿÌðÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ­ÿÿ:Ùÿÿhÿÿ–xÿÿË~ÿÿÿÿÿÿÿÿêÿÿßÿÿÿÔÂÿÿÊ ÿÿ¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ8Òÿÿgjÿÿ—ÿÿ˽ÿÿÿÿñeÿÿßÿÿÿÏ™ÿÿÁÿÿ¿ÿÿÿ´ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÕÿÿ6PÿÿfŠÿÿ—¸ÿÿËõú'ÿ$ßÿÿÿËêÿÿ¿\ÿÿ³Ÿÿÿª°ÿÿž·ÿÿÿÿÿÿÿÿÿÿÿÿÿÿŠÿÿ3)ñ-]´ã ‡kÚܳ‹Ö?äQÉlÿÿ·!ÿÿ¨rÿÿœ“ÿÿ“×ÿÿ‡¸ÿÿÿÿÿÿÿÿÿÿÿÿõªÜj!RËhIf¾Ar̵÷°‹É»¬?õãúÿÿêÿÿ„hÿÿwõÿÿj_ÿÿÿÿÿÿÿÿÿÿþ£ÔƒµG+ ‹À¦²Œ4¥‡š‹¨_É4’¶ŒUÙŒqR¶Á„ˆÚßãž‚]ÿÿ=vpÿÿj ÿÿ\êÿÿLÿÿÿÿÿÿÿÿÿÿÿ|ßÿ!qºç(n—s'y~dÙCHrÅ™Kñi—Ç{©c©ú©Å`)Ö ×V/þý»ÈF„ÿÿ Hÿÿ >GÞÿÿ5ãÿÿÿÿÿÿÿÿ‚é{@Á4LŸ•OŠ}xLK]ãAN[è@ß2ó3¼'R;<6ië#.7P6ÚœJ N4ï6$ËR]34d÷í €ÿÿB ÿÿ;Kÿÿ.™ÿÿ£ÿÿÿÿ$òÍ\/ÈŸfï¥Ëm …l‡d¬j:Cg™Z¾'Í^7+·s"Ä_Èc"//_Æ—Wûõ^ØÇW?]5ôx@Gcÿÿ ;{ÿÿ8-ÿÿ#Þÿÿ ÿÿ(Aúih*ш€®`†¥eˆ«p܈CN·ˆX*.†·ÜÙ Œ 8ƒ:Y”Ñ΃mÙ[‚‡Áux€Þðwvèÿÿh¦ÿÿZBÿÿF’ÿÿÿÿr¡Û¹“º÷š÷==€¢¶\j§ö4_¨_ú£â¢ºà¦;R8¨wß©\Éo©WÿÁ›†ÿÿrÿÿ…ÿÿntÿÿßÿx¿ÿ§¡zª²‹Y¸ÜnHÇŠCÛÌw‹ÇÄEÊh Ï?P"Òç–aÕÚtÑËÿÿÉÿÿ¼Iÿÿ´ÿÿ§¾ÿÿÃ/®“³|Ä|Ÿ¯Õð€î]êö^6ñ³ wèçëlòUøòOþ`Ÿ<ÿÿóSüÿÿô¢ÿÿíÿÿçŠÿÿÚƒÿÿ¿ÿÔÿŸÿÛ͈ÿÿZÿÿ7MÿÿÂÿÿÿÿÿÿÿÿÿÿ;˜ÿÿˆ˜ÿÿÚxÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ´þ †)ÿÿU ÿÿ.Êÿÿ zÿÿÿÿÿÿÿÿÿÿÿÿ$ÿÿn@ÿÿ¾ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ†&ÿÿQ›ÿÿ(sÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõÿÿSËÿÿžŒÿÿïŠÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ Eÿÿ9íÿÿfLÿÿ“—ÿÿÉLÿÿÿÿÿÿÿÿõæÿÿßÿÿÿÞ¢ÿÿÒŽÿÿÈIÿÿÿÿÿÿÿÿÿÿÿÿÿÿWÿÿ8ÿÿe¹ÿÿ”ÿÿÉ{ÿÿÿÿþøÿÿä2ÿÿÛÊÿÿÌrÿÿÀ.ÿÿ¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ5ïÿÿdúÿÿ”¨ÿÿɧÿÿÿÿëvÿÿÚ)ÿÿÇãÿÿ¿3ÿÿµÿÿ¬ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ%ÿÿ3@ÿÿcþü*“õ¥ÃïõúÙ¹ÿÿÄoÿÿ¶Sÿÿ©TÿÿÎÿÿ•wÿÿÿÿÿÿÿÿÿÿÿÿÿÿø©-ä›TÛ×}ϼª Ê ØdÁúÿÿ®Uÿÿ#ÿÿ”ÿÿ†ŒÿÿvvÿÿÿÿÿÿÿÿÿÿÿÿílÐJ1½Ï?¢²hø©ö“\¤y¿ žì‹“iÿÿ„ŸÿÿsÊÿÿhÙÿÿUèÿÿÿÿÿÿÿÿÿÿõRË·¨yN˜Ä+y,U„~y~©z!Òw?ûƒizÿÿX˜ÿÿLÿÿ;"ÿÿÿÿÿÿÿÿüXÓ´Å­ÃÀ‹_q˜ ž foA' ]+Ùob Wo¬š˜ QSïŒÄ7 •Q´áìÛ ‘BÿÿFC•ÿÿ5½ÿÿ)9ÿÿÿÿÿÿÿÿÝJ1µ.>u‘?ýo£=&På6YÒ7 *Q*Û[1.->]—ë,þ.6Ž^u*-xºHÐ'õ+ÈãÆÁüÿÿ2mYÿÿ+Ëÿÿ ÿÿÿÿÿÿ"ãßÿJb½^²˜ƒ`v¾\ýVÝYª5=YÇIS>! ÿÿT¡Vs ST¯ˆ8lPSɵ OR2ßM-H²ÿÿ†;£ÿÿ?/Ýÿÿƒ"¡ÿÿÿÿ+wé^`¿ÿpñŸÿxÀ€x`u ?Kvì wµtîðYv7MEvžƒv*´ÒtþâOnäÿÿ]DÿÿNÿÿ>ÌÿÿñCjeÅŸ€§ªˆD‹¾‹¾oc#KQ–©"¬˜•S˜Ö’œ,KCžDˆ¢,Â?¢÷¹”ÿÿ‡ ÿÿtIÿÿhPÿÿÓ•N´àžJ¦…€¬[×´g-ü¸Îҵ»¹ÄªÉ6K ̲WÎÇÓN̯ÿÿáÿÿ´{ÿÿ«ÿÿ˜®ÿÿ¿ÿ¯IŸÿ°UŒÁÃdkØqJƒåÐUÙ#ÙÖæàí>óHKŽø`™~ûèìŠöªÿÿîÒÿÿç ÿÿÛXÿÿÔ‡ÿÿ¯ÆBŸÿä=€úxU$ÿÿ2cÿÿÚý‹ÿÿÿÿÿÿÿÿ;aÿÿˆ@ÿÿÙÏÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸÿéƒy—ÿÿO9ÿÿ)¾ÿÿ@ÿÿÿÿÿÿÿÿÿÿÿÿ%pÿÿoFÿÿ¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿxXÿÿK6ÿÿ#ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ•ÿÿVWÿÿ£’ÿÿóQÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ7Bÿÿcõÿÿ‘3ÿÿÇ;ÿÿÿÿÿÿÿÿð‘ÿÿßÿÿÿ×VÿÿÉ£ÿÿ¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿÿ5MÿÿcJÿÿ‘ªÿÿÇWÿÿÿÿùfÿÿßÿÿÿÔ¹ÿÿÄJÿÿ¿/ÿÿ¶‰ÿÿÿÿÿÿÿÿÿÿÿÿÿÿyÿÿ2òÿÿboÿÿ’$ÿÿÇnÿÿÿÿå ÿÿÓÿÿÀ ÿÿµºÿÿª:ÿÿ¡eÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ0ýh_Õï?ˆìåú´DâåëîÒ‘ÿÿ½iÿÿ­&ÿÿœ^ÿÿ‘5ÿÿˆ™ÿÿÿÿÿÿÿÿÿÿÿÿÿÿëù$Ê×KoÊs-½ Íû·àÿL¥Eÿÿ’&ÿÿ„ÃÿÿqFÿÿgÄÿÿÿÿÿÿÿÿÿÿÿÿä€Ã_ʰö6¬¤‚]Õ›‘‡‘–´&‘o䈎ÿÿt\ÿÿf8ÿÿPÿÿEŒÿÿÿÿÿÿÿÿÿÿë€Â_œ Š!ç}šH½uÅsožÐj¡È»gBñ×Y ÿÿIuÿÿ5Ïÿÿ)Zÿÿÿÿÿÿÿÿñ]Æ lh~Ñ“c¶g Ë1XvX6–P6aêuJ!Œ>\FHá³éßCÐÙÅ(BÿCH3Nÿÿ#Ïÿÿøÿÿÿÿÿÿù‡ºÍ7 ¦2-¡ƒ0b;.C-*,»!R!•ý&#žR p à$B‰ š#“ªµ LA"ÑüttúøQË ÿÿCÿÿðÿÿMÿÿÿÿEÔ¡@¬GN‰PhM IeI…(’JjWG‡PØ OH™IݧHxxjFG¤ aF)ÍßDTö6pÿÿ%Äÿÿ ÿÿÿÿ,0ßËXH³xdÙaišpthFSÿÿÿÿÿÿ:¬ÿÿ‡<ÿÿØ@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýOÿÿŸÿìs,ÿÿIˆÿÿ$ÿÿ¼ÿÿÿÿÿÿÿÿÿÿÿÿ%»ÿÿosÿÿ¾Ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿq?ÿÿEÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿuÿÿWÒÿÿ¤÷ÿÿõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÕÿÿ4|ÿÿa‡ÿÿŽ»ÿÿÅÿÿÿÿÿÿÿÿêºÿÿßÿÿÿÏêÿÿÁ ÿÿ¿Rÿÿÿÿÿÿÿÿÿÿÿÿÿÿ•ÿÿ2^ÿÿ`Áÿÿ ÿÿÅÿÿÿÿóKÿÿßÿÿÿÍ^ÿÿ¿ÿÿÿµNÿÿ«)ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ/Ùÿÿ_’ÿÿ…ÿÿÅüXýÕßÿÿÿ˰ÿÿºÅÿÿ¬2ÿÿ›¬ÿÿ‘Ñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ,Ñð V}àˆ}.Ût«ÙñäËÿÿ´˜ÿÿ£Âÿÿÿÿÿy®ÿÿnÖÿÿÿÿÿÿÿÿÿÿÿÿûòÜ×ÉÁBº¦g!³»‘R±@Ä›«Õök–öÿÿ†îÿÿp"ÿÿUYÿÿHÛÿÿÿÿÿÿÿÿÿÿÿÿÙ³u:£V-8“PúŒµyu‡¸«4|ûÕwžÿÿgEÿÿOZÿÿ6—ÿÿ(‚ÿÿÿÿÿÿÿÿÿÿá7·*‘þyÞ‘nU<´f–g[û’hUG¼îQîåüJEÿÿ4Ìÿÿ$§ÿÿ ÿÿÿÿÿÿÿÿæ»p–?s£TN’I^*¢@˜TÅ9€~†4w¥á0õËÑ.6ñŽ"¸ÿÿ1ÿÿÎÿÿÿÿÿÿë"¿ÿx™þkuï!CU 7+ –=€èhE3ðhÜq^½jWšæ “ÀsR {Zåì Lÿÿ Aÿÿ­ÿÿÿÿòžjÄJ1ßžï@y¦@Z?J;È;Èë?[":?ZcpÆX÷¡ WÍËÊVŠõJ}ÿÿ6ÿÿ&²ÿÿÓ``¬ÃmŒÙn:mýmýP­pª)éw"zj‚³…YZˆR;6Š˜wf‹$°Їàý…Eÿÿo6ÿÿX ÿÿI9ÿÿ·M€™œ„߀†F`‰*5ö‘È_——¥@¬Ä±5µv;C¹ }\» ¾»¸û±Ñÿÿ¦©ÿÿ‡ÿÿuµÿÿŸÿ“Ê…#•]p4¥D´´Œh¶}Ä/Ï9× Üˆè^CìùðÙâìâÿÿä¸ÿÿÕàÿÿÌéÿÿºÿÿ™á²!€ÄÁ`è<:ì3Fç)íÚø¹ÿÿÿÿÿÿ9ÿÿ…°ÿÿÖÿÿÿÿÿÿÿÿÿÿÿÿü;ÿÿõÿÿ‹ÒØ?mÿÿCëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ%~ÿÿnóÿÿ½áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿjŽÿÿ?fÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¸ÿÿXrÿÿ¥QÿÿõEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿˆÿÿ1™ÿÿ_nÿÿŒ-ÿÿ¼sÿÿÿ±ÿÿÿÿäjÿÿÚÿÿÈGÿÿ¿ÿÿÿµ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ/Rÿÿ^ÿÿŒ}ÿÿ½?ÿÿý¬ì°ÿÿÙèÿÿÅ´ÿÿ¸•ÿÿ«sÿÿ›ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ,¡ÿÿ\zøýˆ¼ð%´Gï±óDÚÐÿÿÃéÿÿ±±ÿÿœ¢ÿÿôÿÿvÿÿÿÿÿÿÿÿÿÿÿÿÿÿø'&`á«LˆÒžržËë›GÊ!ÒÃÿÿ«pÿÿ”ÿÿyÿÿlÿÿL´ÿÿÿÿÿÿÿÿÿÿÿÿò³Î] ·Ó6ì¬ZŦ^† †²D˜ê‹ÝÿÿqÛÿÿUÿÿ7²ÿÿ(›ÿÿÿÿÿÿÿÿÿÿû|Ïc¦ Ÿ!_„‰D‰zNjŽrËœl×˯g°÷ÔP“ÿÿ6mÿÿ%×ÿÿ ÿÿÿÿÿÿÿÿÿÿÕš¬ö‡qjm oZa.`RÍW0JA‡#<Ù°78\×456þˆ$4ÿÿ ñÿÿÿÿÿÿÿÿÿÿÚR°×‹„hÐH8a.G%$ãrë„™×-¿[Eäo¾ÿÿÿÿÿÿÿÿÿÿÞè³âŒ¨ºi‚SIur+æj ® ÿ £ ï7j×>@b F Í‹D ƱD ;Ô ÷÷Ýÿÿÿÿÿÿßÿ•¹u$zÕ2èlõ4µM¾1—.Û.ÛÚ-þ+Ž«*Q4}(—b·'[j%û´½$yÙÃ"áþ¸«ÿÿKÿÿì ¿-A»—ÖL‚r|MðS…Js9F¿ûJ’NÞKå›L 1žKg…IÖ}HÈÀzG³ç²<¦ÿÿ+ÐÿÿŠÿÿÂðVEŸÿ`± `)_C_C@¿^{Rg›o‹sBs+u1Wvôk»v=£uCÒ.týf_ÿÿMåÿÿ6Êÿÿ¦¸s·ˆVu2lÜr%OîwzŠ„´—t¤ú¨È­5°¯v²®¶³>ð««fÿÿ”4ÿÿxýÿÿj­ÿÿ–I‹’~‰Ç`‘õ/j¤‡©:·»É»Ñ ÕñÚ›9~çt‡têiÑpècÿÿÙ<ÿÿÐÿÿÆÍÿÿ±:ÿÿ‚ÏšenS²¹B­ËngÍ Ô¨ê ô5ûòÿÿÿÿ8ÿÿƒ±ÿÿÓBÿÿÿÿÿÿÿÿþÿÿõìÿÿîÿÿ€Í`õ¸;ýÿ¤øÉÿjÿÿÿÿÿÿÿÿÿÿ$Ìÿÿmäÿÿ¼>ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿd-ÿÿ8Úÿÿ,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿxÿÿXbÿÿ¤Úÿÿôjÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ.™ÿÿ\ÿÿ‰‰ÿÿ¹ÆÿÿýXúkÿÿßÿÿÿÒËÿÿÀbÿÿ¶Åÿÿªäÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ,'ÿÿ[ÿÿ‰Ãÿÿº~ÿÿû0å¡ÿÿÒiÿÿ¾ëÿÿ¯Cÿÿšƒÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ)KùŠVSéJ|áK¨3â;è Óÿÿº'ÿÿ¨Nÿÿ‘]ÿÿt“ÿÿPµÿÿÿÿÿÿÿÿÿÿÿÿÿÿéPAÒAïÅÇfÇ»+ÕºÛÆµü ™·ÿÿˆ©ÿÿlVÿÿK ÿÿ))ÿÿÿÿÿÿÿÿÿÿÿÿèáº÷ôªl,¤–±KŠ‘¢q¢—§}ˆjÚÔuÄÿÿVÓÿÿ8ÿÿ'-ÿÿ ÿÿÿÿÿÿÿÿÿÿñÅF˜F|Œup^5h†X«YnŒ NÕ¼AJÁèÅ7­ÿÿ%Ìÿÿ ÿÿÿÿÿÿÿÿÿÿ÷ÉË¢RzVÆXHð!=,G/{’'J¥‰É©4ït ·ÿÿÿÿÿÿÿÿÿÿýíÏ5¥ã~•\<'`G^<¬ „fI²Ž  ³×¶üñÿÿÿÿÿÿÿÿÓ¨€Ž^6>@±”+(VVó¦ÂË6ï0ÿÿÿÿÿÿ×¾« $ƒf!F^…&?l"â!n!nK   *§‘Vzè]¦¦\ÿÊzí=\ÿÿÿÿßÿ۰7öˆÁ@c@FÌ>Ã,7Ã÷?”>­917ã&”39\1 ˆb0œ°^0bÓÕ/Røb†ÿÿ iÿÿ¹´MR’lWDpâSZQ\Q\6|Q‘[¼dAgKf¸hN(Oj b²h☯hÇžgIð/T`ÿÿ;Ñÿÿ+™ÿÿ›ii›{˜hÍ`d%Akgø ƒv`„½Æ’Ö”õ˜,+¼¨;n]ª=­Ùª°åñ™ÿÿ‹ýÿÿoCÿÿR¬ÿÿ„”{âjÐxÕP›ƒfp;£$°Äº³ÊàÏSÓÖ3ÖØªxªäðÈÕäÿÿÓÁÿÿÊŽÿÿ´Fÿÿ©7ÿÿ~D•(`™û(K²Ò·„Ðæ+ï°öÜüÿÿ6GÿÿQÿÿÐÿÿÿÿÿÿÿÿøÿÿð5ÿÿçæÿÿo<¼ REêÓ'ï0ì¼ûÊÿÿÿÿÿÿÿÿÿÿ#¶ÿÿl]ÿÿºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿY¹ÿÿ2^ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÈÿÿW¾ÿÿ£»ÿÿòºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿL˜ä0|È_«÷BŽÚ%q½TŸë6Íc¯úEÛ & q ½  S ž è 3 ~ É  _ © ô ? Š Ôi´þI“Þ(s½Rœæ0zÅY£í7Ë_¨ò<†Ðc­ö@‰Óf¯ùBŒÕh±úCŒÕh±úCŒÕf¯ø A Š Ò!!d!¬!õ">"†"Ï##`#¨#ñ$9$$Ê%%Z%£%ë&3&{&Ã' 'T'œ'ä(,(t(¼))L)“)Û*#*k*³*ú+B+Š+Ñ,,a,¨,ð-7--Æ..U.œ.ä/+/r/º00H00Ö11e1¬1ó2:22È33V33ä4+4q4¸4ÿ5F55Ó66a6¨6î757|7Â8 8O8–8Ü9#9i9°9ö:=:ƒ:Ê;;V;;ã<)>[>¡>ç?-?s?¹?ÿ@E@‹@ÑAA]A£AéB.BtBºCCFC‹CÑDD\D¢DèE-EsE¹EþFDF‰FÏGGZGŸGåH*HpHµHúI@I…IÊJJTJšJßK$KiK®KóL8L}LÁMMKMMÕNN^N£NçO,OpOµOùP>P‚PÆQ QOQ“QØRR`R¤RèS,SpS´SøTÊ{Ê·ÊôË0ËlË©ËåÌ"Ì^ÌšÌ×ÍÍPÍŒÍÉÎÎBÎ~κÎ÷Ï3ÏpϬÏéÐ%ÐbОÐÛÑÑTÑÑÍÒ ÒFÒƒÒ¿ÒüÓ8ÓuÓ±ÓîÔ*ÔgÔ¤ÔàÕÕYÕ–ÕÒÖÖLÖˆÖÅ××>×{×·×ôØ1ØmØªØæÙ#Ù`ÙÙÙÚÚSÚÚÌÛ ÛFÛ‚Û¿ÛüÜ9ÜvܲÜïÝ,ÝiݦÝãÞÞ\Þ™ÞÖßßPßßÊààDàà¾àûá8áuá²áïâ,âiâ¦âãã ã]ãšã×ääQäŽäËååFåƒåÀåýæ:æwæµæòç/çlç©ççè$èaèžèÜééVé“éÑêêKê‰êÆëëAë~ë»ëùì6ìtì±ìîí,íií§íäî"î_îîÚïïUï“ïÐððKð‰ðÆññBññ½ñúò8òvò³òñó.ólóªóçô%ôcô¡ôÞõõZõ—õÕööQööÌ÷ ÷H÷†÷Äøø?ø}ø»øùù7ùuù²ùðú.úlúªúèû&ûdû¢ûàüü\üšüØýýTý’ýÐþþLþŠþÈÿÿEÿƒÿÁÿÿMšç4Îg´Mšç3€Ìe±ýI•á-yÅ]©õ @ Œ Ø # o º  Q œ è 3 ~ É  _ « ö@‹Ö!l¶L–á+vÀ UŸé4~È\¦ð:ƒÍaªô=‡Ðc­ö?ˆÒd­ö?ˆÑb«ô<…Î_§ï8€È  Y ¡ é!1!y!Á" "Q"™"á#(#p#¸#ÿ$G$$Ö%%e%¬%ô&;&‚&É''X'Ÿ'æ(-(t(»))I))Ö**d*ª*ñ+7+~+Ä, ,Q,˜,Þ-$-j-±-÷.=.ƒ.É//U/›/á0'0l0²0ø1=1ƒ1È22S2™2Þ3#3h3­3ò474|4Á55K55Ô66]6¡6æ7*7n7²7ö8:8~8Â99J9Ž9Ñ::Y:œ:à;#;f;ª;í<0>I>‹>Î??T?–?Ù@@^@ @ãA%AgAªAìB.BpB²BôC6CxCºCüD>D€DÁEEEE†EÈF FKFŒFÍGGPG‘GÒHHUH–H×IIYIšIÛJJ]JJÞKK`K KáL"LbL£LãM$MdM¥MåN&NfN¦NçO'OgO§OçP(PhP¨PèQ(QhQ¨QçR'RgR§RçS'SfS¦SæT%TeT¤TäU#UcU¢UâV!V`V VßWW]WœWÜXXZX™XØYYVY•YÔZZQZZÏ[ [L[‹[É\\G\…\Ä]]A]]¾]ü^:^x^·^õ_3_q_¯_í`+`i`§`åa#aaažaÜbbWb•bÒccMc‹cÈddCd€d½dûe8eue²eïf,fif¦fãg g]g™gÖhhOhŒhÉiiBi~i»i÷j3jpj¬jèk$kakkÙllQllÉmmAm|m¸môn0nkn§nâooYo•oÐp pFp‚p½pøq3qnq©qärrYr”rÏs sDss¹sôt.tit£tÝuuRuŒuÆvv:vtv®vèw"w[w•wÏx xBx|xµxïy(yay›yÔz zGz€z¹zò{+{d{{Ö||G|€|¹|ñ}*}c}›}Ô~ ~D~}~µ~í%^–΀€>€v€®€æUÅü‚4‚k‚£‚ÚƒƒIƒƒ¸ƒð„'„_„–„Î……=…t…¬…ㆆR†Š†Á†ø‡0‡g‡Ÿ‡ÖˆˆEˆ}ˆ´ˆì‰#‰[‰’‰ÉŠŠ8ŠpЧŠß‹‹N‹…‹¼‹ôŒ+ŒcŒšŒÒ @x¯çŽŽUŽŽÄŽü3j¢ÙH·î‘%‘]‘”‘Ì’’:’r’©’à““O“†“¾“õ”-”d”›”Ó• •A•y•°•ç––V––Å–ü—3—j—¢—Ù˜˜H˜˜¶˜î™%™\™“™Ëšš9šqš¨šß››N›…›½›ôœ+œcœšœÒ @x¯çžžVžžÄžüŸ3ŸkŸ¢ŸÚ  I € ¸ ð¡'¡_¡–¡Î¢¢=¢u¢¬¢ä££S£‹£Â£ú¤2¤i¤¡¤Ù¥¥H¥€¥¸¥ï¦'¦_¦—¦Î§§>§v§­§å¨¨U¨¨Å¨ü©4©l©¤©ÜªªMª…ª½ªõ«.«f«Ÿ«×¬¬I¬¬º¬ó­,­d­­Ö®®I®‚®»®ô¯-¯g¯ ¯Ú°°M°†°À°ú±4±n±§±á²²U²²Ê³³>³x³³³í´(´b´´ØµµMµˆµÃµþ¶9¶t¶¯¶ê·%·`·œ·×¸¸N¸‰¸Å¹¹<¹x¹´¹ðº,ºhº¤ºà»»X»”»Ñ¼ ¼I¼†¼Â¼ÿ½<½y½µ½ò¾/¾l¾©¾ç¿$¿a¿Ÿ¿ÜÀÀWÀ•ÀÓÁÁNÁŒÁÊÂÂFÂ…ÂÃÃÃ@Ã~ýÃüÄ:ÄyĸÄ÷Å6ÅuÅ´ÅóÆ3ÆrƲÆñÇ1ÇpǰÇðÈ0ÈpȰÈðÉ0ÉpɱÉñÊ2ÊrʳÊóË4Ëu˵ËöÌ7Ìx̹ÌúÍ<Í};ÎÎA΃ÎÄÏÏHωÏËÐ ÐOБÐÓÑÑXÑšÑÜÒÒaÒ£ÒæÓ(ÓkÓ®ÓðÔ3ÔvÔ¹ÔüÕ>ÕÕÄÖÖJÖŽÖÑ××W×›×ÞØ!ØeبØìÙ/ÙsÙ·ÙúÚ>Ú‚ÚÆÛ ÛNÛ’ÛÖÜÜ^ܢܿÝ+ÝoÝ³ÝøÞ<ÞÞÅß ßOߓߨààbà§àëá0áuá»ââEâŠâÏããZãŸãåä*äpäµäûåAå‡åÌææXæžæäç*çpç¶çüèCè‰èÏéé\é¢ééê/êvê½ëëJë‘ëØììfì­ìôí;í‚íÉîîXîŸîæï.ïuï½ððLð”ðÜñ#ñkñ³ñûòCò‹òÓóódó¬óôô<ô…ôÍõõ^õ§õðö8ööÊ÷÷\÷¥÷îø7ø€øÉùù[ù¥ùîú8úúËûû^û§ûñü;ü…üÏýýcý­ý÷þAþ‹þÕÿ ÿjÿµÿÿK•à+uÀ UŸê4~É]§ñ;…Ïc­÷@ŠÔg±ú C Ö i ² û D Ž × i ² ú C Œ Õf¯ø@‰Ñb«ó;ƒÌ\¤ì4|Ä T›ã+sºI‘Ø g¯ö=„ÌZ¡è/v½J‘Øe¬ó9€Æ S™à&l²ù ? … Ë!!W!!ã"("n"´"ú#?#…#Ë$$V$›$á%&%l%±%ö&<&&Æ' 'P'•'Ú((d(©(î)3)x)½**F*‹*Ï++X++á,&,j,¯,ó-7-{-À..H.Œ.Ð//X/œ/à0$0g0«0ï121v1º1ý2@2„2Ç3 3N3‘3Ô44Z44à5#5f5¨5ë6.6p6³6ö787z7½7ÿ8A8„8Æ99J9Œ9Î::R:”:Ö;;Y;›;Ü<<_<¡<â=$=e=¦=è>)>j>«>ì?-?n?¯?ð@1@r@³@óA4AuAµAöB6BwB·B÷C8CxC¸CùD9DyD¹DùE9EyE¹EøF8FxF¸FøG7GwG¶GöH6HuH´HôI3IsI²IñJ0JpJ¯JîK-KlK«KêL)LhL§LæM$McM¢MáNN^NœNÛOOXO–OÕPPRPPÎQ QKQ‰QÇRRCRR¿RýS;SyS·SôT2TpT®TëU)UfU¤UâVV\VšV×WWRWWÌX XGX„XÁXþY;YxYµYòZ/ZlZ©Zæ["[_[œ[Ù\\R\\Ì]]E]]¾]ú^7^t^°^ì_)_e_¢_Þ``W`“`Ïa aHa„aÀaýb9bub±bíc)cec¡cÝddUd‘dÍe eEee½eøf4fpf¬fèg#g_g›gÖhhNh‰hÅii,>u>¿? ?R?›?å@.@x@ÁA ATAAæB/BxBÁC CSCœCåD.DwDÀE ERE›EãF,FuF½GGOG—GàH(HqH¹IIJI’IÚJ#JkJ³JûKDKŒKÔLLdL¬LôM×sש×ÞØØIØ~سØèÙÙSوٽÙòÚ'Ú\Ú‘ÚÆÚûÛ0ÛeÛšÛÎÜÜ8ÜmÜ¢ÜÖÝ Ý@ÝtÝ©ÝÞÞÞGÞ{Þ°ÞäßßM߂߶ßëààSàˆà¼àðá%áYááÂáöâ*â^â’âÇâûã/ãcã—ãËãÿä3ägä›äÏåå7åkåŸåÓææ;æoæ¢æÖç ç>çrç¥çÙè è@ètè¨èÛééCévéªéÝêêDêxê«êßëëFëyë­ëàììGìzì­ìáííGízí®íáîîGîzî­îáïïGïzï­ïàððFðyð¬ðßññEñxñªñÝòòCòvò©òÛóóAótó¦óÙô ô>ôqô¤ôÖõ õ;õnõ õÓöö8öjööÏ÷÷4÷g÷™÷Ë÷þø0øbø•øÇøùù+ù^ùùÂùôú&úXúŠú½úïû!ûSû…û·ûéüüMüü±üãýýFýxýªýÜþþ@þqþ£þÕÿÿ8ÿjÿœÿÍÿÿmft2@ÁB‚ÂCƒÃD„ÅE†ÆG‡ÇHˆÈ I‰Ê J‹Ë L Œ Ì M Í  N Ž Ï  O Ð  Q ‘ ÑR’ÒS“ÔT•ÕV–ÖW—×X˜ÙYšÚ[›Û\œÜ]Þ^Ÿß_ à a¡á"b¢ã#c¤ä$d¥å%f¦æ'g§è ( h © é!)!i!ª!ê"*"k"«"ë#,#l#¬#í$-$m$®$î%.%n%¯%ï&/&p&°&ð'1'q'±'ò(2(r(³(ó)3)s)´)ô*4*u*µ*õ+6+v+¶+÷,7,w,¸,ø-8-x-¹-ù.9.z.º.ú/;/{/»/ü0<0|0¼0ý1=1}1¾1þ2>22¿2ÿ3@3€3À44A44Á55B5‚5Ã66C6„6Ä77E7…7Å88F8†8Æ99G9‡9È::H:‰:É; ;J;Š;Ê< >M>Ž>Î??O??Ï@@P@@ÐAAQA‘AÒBBRB“BÓCCTC”CÔDDUD•DÕEEVE–E×FFWF˜FØGGYG™GÙHHZHšHÚII[I›IÜJJ\JJÝKK^KžKÞLL_LŸLßM M`M MáN!NaN¢NâO"OcO£OãP#PdP¤PäQ%QeQ¥QæR&RfR§RçS'ShS¨SèT(TiT©TéU*UjUªUëV+VkV¬VìW,WmW­WíX-XnX®XîY/YoY¯YðZ0ZpZ±Zñ[1[r[²[ò\2\s\³\ó]4]t]´]õ^5^u^¶^ö_6_v_·_÷`7`x`¸`øa9aya¹aúb:bzb»bûc;c{c¼cüde~e¾eÿf?ffÀgg@g€gÁhhAh‚hÂiiCiƒiÃjjDj„jÅkkEk…kÆllFl‡lÇmmHmˆmÈn nIn‰nÊo oJoŠoËp pKpŒpÌq qMqqÍrrNrŽrÏssOssÐttPt‘tÑuuRu’uÒvvSv“vÓwwTw”wÕxxUx–xÖyyWy—y×zzXz˜zØ{{Y{™{Ú||Z|›|Û}}\}œ}Ü~~]~~Ý^ž߀€_€ €à a¡á‚"‚b‚¢‚âƒ#ƒcƒ£ƒä„$„d„¥„å…%…f…¦…æ†'†g†§†ç‡(‡h‡¨‡éˆ)ˆiˆªˆê‰*‰k‰«‰ëŠ,ŠlЬŠì‹-‹m‹­‹îŒ.ŒnŒ¯Œï/p°ðŽ0ŽqޱŽñ2r²ó3s´ô‘4‘u‘µ‘õ’5’v’¶’ö“7“w“·“ø”8”x”¹”ù•9•z•º•ú–:–{–»–û—<—|—¼—ý˜=˜}˜¾˜þ™>™™¿™ÿš?š€šÀ››A››ÁœœBœ‚œÃC„ÄžžDž…žÅŸŸFŸ†ŸÆ  G ‡ È¡¡H¡‰¡É¢ ¢I¢Š¢Ê£ £K£‹£Ë¤ ¤L¤Œ¤Í¥ ¥M¥¥Î¦¦N¦¦Ï§§P§§Ð¨¨Q¨‘¨Ò©©R©’©ÓªªSª”ªÔ««U«•«Õ¬¬V¬–¬×­­W­—­Ø®®X®™®Ù¯¯Z¯š¯Ú°°[°›°Ü±±\±œ±Ý²²]²ž²Þ³³_³Ÿ³ß´ ´`´ ´áµ!µaµ¡µâ¶"¶b¶£¶ã·#·d·¤·ä¸%¸e¸¥¸æ¹&¹f¹¦¹çº'ºgº¨ºè»(»i»©»é¼*¼j¼ª¼ê½+½k½«½ì¾,¾l¾­¾í¿-¿n¿®¿îÀ/ÀoÀ¯ÀïÁ0ÁpÁ°ÁñÂ1Âq²ÂòÃ2ÃsóÃóÄ4ÄtÄ´ÄôÅ5ÅuŵÅöÆ6ÆvÆ·Æ÷Ç7ÇxÇ¸ÇøÈ9ÈyȹÈùÉ:ÉzɺÉûÊ;Ê{ʼÊüË<Ë}˽ËýÌ>Ì~̾ÌþÍ?ÍÍ¿ÎÎ@΀ÎÁÏÏAÏ‚ÏÂÐÐCЃÐÃÑÑDÑ„ÑÄÒÒEÒ…ÒÆÓÓFÓ‡ÓÇÔÔGÔˆÔÈÕÕIÕ‰ÕÉÖ ÖJÖŠÖË× ×K׌×ÌØ ØLØØÍÙ ÙNÙŽÙÎÚÚOÚÚÐÛÛPÛ‘ÛÑÜÜQÜ’ÜÒÝÝSÝ“ÝÓÞÞTÞ”ÞÕßßUß–ßÖààVà—à×ááXá˜áØââYâ™âÚããZã›ãÛää[äœäÜåå]ååÝææ^æžæßçç_ç çàè è`è¡èáé!ébé¢éâê#êcê£êäë$ëdë¤ëåì%ìeì¦ìæí&ígí§íçî(îhî¨îéï)ïiï©ïêð*ðjð«ðëñ+ñlñ¬ñìò-òmò­òîó.ónó®óïô/ôoô°ôðõ0õqõ±õñö2örö²öó÷3÷s÷³÷ôø4øtøµøõù5ùvù¶ùöú7úwú·úøû8ûxû¸ûùü9üyüºüúý:ý{ý»ýûþ<þ|þ¼þýÿ=ÿ}ÿ½ÿþÿÿÿÿÿÿÿÿAÂC„ÄE†ÆHˆÉ JŠË LÍNÐQ‘ÒS“Ô  U – Ö  W ˜ Ù  Z š Û  \ œ Ý  ^ Ÿ ß `¡â"c£ä$e¦æ'g¨è)iªë+l¬í-n¯ï0p±ñ2s³ô4uµö6w¸ø9yºú;|¼ý=~¾ÿ@€ÁB‚ÃD…ÅF‡Ç  I ‰ Ê! !K!‹!Ì" "M"Ž"Î##O##Ð$$R$’$Ó%%T%”%Õ&&V&—&×''X'™'Ú(([(›(Ü))]))Þ**_* *à+!+a+¢+ã,#,d,¤,å-%-f-§-ç.(.h.©.é/*/j/«/ì0,0m0­0î1.1o1°1ð212q2²2ò333t3´3õ454v4¶4÷575x5¹5ù6:6z6»6û7<7}7½7þ8>88¿99A99Â::C:ƒ:Ä;;E;†;Æ< >L>Œ>Í??N??Ï@@P@‘@ÑAASA“AÔBBUB•BÖCCWC˜CØDDYDšDÚEE\EœEÝFF^FžFßG G`G¡GáH"HbH£HäI$IeI¥IæJ&JgJ§JèK)KiKªKêL+LkL¬LíM-MnM®MïN/NpN±NñO2OrO³OóP4PtPµPöQ6QwQ·QøR8RyRºRúS;S{S¼SüT=T~T¾TÿU?U€UÀVVAV‚VÃWWDW„WÅXXFX‡XÇYYHY‰YÉZ ZKZ‹ZÌ[ [M[[Î\\O\\Ð]]Q]’]Ò^^T^”^Õ__V_–_×``X`™`ÙaaZa›aÛbb]bbÞcc_cŸcàd!dad¢dâe#ece¤eåf%fff¦fçg'ghg¨géh*hjh«hëi,ili­iîj.joj¯jðk0kqk²kòl3lsl´lôm5mum¶m÷n7nxn¸nùo9ozo»oûpqq¿rr@rrÁssBsƒsÄttEt…tÆuuGuˆuÈv vIvŠvÊw wLwŒwÍx xNxŽxÏyyPy‘yÑzzRz“zÓ{{U{•{Ö||W|—|Ø}}Y}š}Ú~~[~œ~Ü^ž߀€`€ €á"b£ã‚$‚d‚¥‚åƒ&ƒgƒ§ƒè„(„i„©„ê…+…k…¬…ì†-†m†®†ï‡/‡p‡°‡ñˆ1ˆrˆ²ˆó‰4‰t‰µ‰õŠ6ŠvŠ·Šø‹8‹y‹¹‹úŒ:Œ{Œ¼Œü=}¾þŽ?ŽŽÀA‚ÂC„Å‘‘F‘†‘Ç’’H’‰’É“ “J“‹“Ë” ”L””Ε•O••Ж–Q–’–Ò——S—”—Ô˜˜V˜–˜×™™X™˜™ÙššZš›šÛ››\››Ýœœ_œŸœà a¡âž#žcž¤žäŸ%ŸeŸ¦Ÿæ ' h ¨ é¡)¡j¡ª¡ë¢,¢l¢­¢í£.£n£¯£ð¤0¤q¤±¤ò¥2¥s¥³¥ô¦5¦u¦¶¦ö§7§w§¸§ù¨9¨z¨º¨û©;©|©½©ýª>ª~ª¿ªÿ«@«€«Á¬¬B¬ƒ¬Ã­­D­…­Æ®®G®‡®È¯¯I¯Š¯Ê° °K°Œ°Ì± ±M±Ž±Ï²²P²²Ñ³³R³“³Ó´´T´•´ÕµµVµ—µØ¶¶Y¶™¶Ú··[·œ·Ü¸¸]¸ž¸Þ¹¹`¹ ¹áº!ºbº¢ºã»#»d»¥»å¼&¼f¼§¼ç½(½i½©½ê¾*¾k¾«¾ì¿-¿m¿®¿îÀ/ÀoÀ°ÀðÁ1ÁrÁ²ÁóÂ3Ât´ÂõÃ6Ãv÷Ã÷Ä8ÄxĹÄúÅ:Å{Å»ÅüÆ<Æ}ƽÆþÇ?ÇÇÀÈÈAÈÈÂÉÉCÉ„ÉÄÊÊEʆÊÇËËHˈËÉÌ ÌJÌŠÌËÍ ÍLÍÍÍÎÎNÎÎÐÏÏQÏ‘ÏÒÐÐSДÐÔÑÑUÑ–ÑÖÒÒWÒ˜ÒÙÓÓZÓšÓÛÔÔ\ÔÔÝÕÕ^ÕŸÕßÖ ÖaÖ¡Öâ×"×c×£×äØ$ØeØ¦ØæÙ'ÙgÙ¨ÙèÚ)ÚjÚªÚëÛ+ÛlÛ¬ÛíÜ.ÜnܯÜïÝ0ÝpݱÝñÞ2ÞsÞ³Þôß4ßußµßöà7àwà¸àøá9áyáºáûâ;â|â¼âýã=ã~ã¾ãÿä@ä€äÁååBå‚åÃææDæ…æÅççFç‡çÈèèIè‰èÊé éKé‹éÌê êMêŽêÎëëOëëÑììRì’ìÓííTí”íÕîîVî—î×ïïXï™ïÚðð[ð›ðÜññ]ñžñÞòò_ò òàó!óaó¢óãô#ôdô¤ôåõ%õfõ§õçö(öhö©öé÷*÷k÷«÷ìø,ømø­øîù.ùoù°ùðú1úqú²úòû3ûtû´ûõü5üvü¶ü÷ý8ýxý¹ýùþ:þzþ»þûÿ<ÿ}ÿ½ÿþÿÿÿÿÿÿÿÿAÂC„ÄE†ÆHˆÉ JŠË LÍNÐQ‘ÒS“Ô  U – Ö  W ˜ Ù  Z š Û  \ œ Ý  ^ Ÿ ß `¡â"c£ä$e¦æ'g¨è)iªë+l¬í-n¯ï0p±ñ2s³ô4uµö6w¸ø9yºú;|¼ý=~¾ÿ@€ÁB‚ÃD…ÅF‡Ç  I ‰ Ê! !K!‹!Ì" "M"Ž"Î##O##Ð$$R$’$Ó%%T%”%Õ&&V&—&×''X'™'Ú(([(›(Ü))]))Þ**_* *à+!+a+¢+ã,#,d,¤,å-%-f-§-ç.(.h.©.é/*/j/«/ì0,0m0­0î1.1o1°1ð212q2²2ò333t3´3õ454v4¶4÷575x5¹5ù6:6z6»6û7<7}7½7þ8>88¿99A99Â::C:ƒ:Ä;;E;†;Æ< >L>Œ>Í??N??Ï@@P@‘@ÑAASA“AÔBBUB•BÖCCWC˜CØDDYDšDÚEE\EœEÝFF^FžFßG G`G¡GáH"HbH£HäI$IeI¥IæJ&JgJ§JèK)KiKªKêL+LkL¬LíM-MnM®MïN/NpN±NñO2OrO³OóP4PtPµPöQ6QwQ·QøR8RyRºRúS;S{S¼SüT=T~T¾TÿU?U€UÀVVAV‚VÃWWDW„WÅXXFX‡XÇYYHY‰YÉZ ZKZ‹ZÌ[ [M[[Î\\O\\Ð]]Q]’]Ò^^T^”^Õ__V_–_×``X`™`ÙaaZa›aÛbb]bbÞcc_cŸcàd!dad¢dâe#ece¤eåf%fff¦fçg'ghg¨géh*hjh«hëi,ili­iîj.joj¯jðk0kqk²kòl3lsl´lôm5mum¶m÷n7nxn¸nùo9ozo»oûpqq¿rr@rrÁssBsƒsÄttEt…tÆuuGuˆuÈv vIvŠvÊw wLwŒwÍx xNxŽxÏyyPy‘yÑzzRz“zÓ{{U{•{Ö||W|—|Ø}}Y}š}Ú~~[~œ~Ü^ž߀€`€ €á"b£ã‚$‚d‚¥‚åƒ&ƒgƒ§ƒè„(„i„©„ê…+…k…¬…ì†-†m†®†ï‡/‡p‡°‡ñˆ1ˆrˆ²ˆó‰4‰t‰µ‰õŠ6ŠvŠ·Šø‹8‹y‹¹‹úŒ:Œ{Œ¼Œü=}¾þŽ?ŽŽÀA‚ÂC„Å‘‘F‘†‘Ç’’H’‰’É“ “J“‹“Ë” ”L””Ε•O••Ж–Q–’–Ò——S—”—Ô˜˜V˜–˜×™™X™˜™ÙššZš›šÛ››\››Ýœœ_œŸœà a¡âž#žcž¤žäŸ%ŸeŸ¦Ÿæ ' h ¨ é¡)¡j¡ª¡ë¢,¢l¢­¢í£.£n£¯£ð¤0¤q¤±¤ò¥2¥s¥³¥ô¦5¦u¦¶¦ö§7§w§¸§ù¨9¨z¨º¨û©;©|©½©ýª>ª~ª¿ªÿ«@«€«Á¬¬B¬ƒ¬Ã­­D­…­Æ®®G®‡®È¯¯I¯Š¯Ê° °K°Œ°Ì± ±M±Ž±Ï²²P²²Ñ³³R³“³Ó´´T´•´ÕµµVµ—µØ¶¶Y¶™¶Ú··[·œ·Ü¸¸]¸ž¸Þ¹¹`¹ ¹áº!ºbº¢ºã»#»d»¥»å¼&¼f¼§¼ç½(½i½©½ê¾*¾k¾«¾ì¿-¿m¿®¿îÀ/ÀoÀ°ÀðÁ1ÁrÁ²ÁóÂ3Ât´ÂõÃ6Ãv÷Ã÷Ä8ÄxĹÄúÅ:Å{Å»ÅüÆ<Æ}ƽÆþÇ?ÇÇÀÈÈAÈÈÂÉÉCÉ„ÉÄÊÊEʆÊÇËËHˈËÉÌ ÌJÌŠÌËÍ ÍLÍÍÍÎÎNÎÎÐÏÏQÏ‘ÏÒÐÐSДÐÔÑÑUÑ–ÑÖÒÒWÒ˜ÒÙÓÓZÓšÓÛÔÔ\ÔÔÝÕÕ^ÕŸÕßÖ ÖaÖ¡Öâ×"×c×£×äØ$ØeØ¦ØæÙ'ÙgÙ¨ÙèÚ)ÚjÚªÚëÛ+ÛlÛ¬ÛíÜ.ÜnܯÜïÝ0ÝpݱÝñÞ2ÞsÞ³Þôß4ßußµßöà7àwà¸àøá9áyáºáûâ;â|â¼âýã=ã~ã¾ãÿä@ä€äÁååBå‚åÃææDæ…æÅççFç‡çÈèèIè‰èÊé éKé‹éÌê êMêŽêÎëëOëëÑììRì’ìÓííTí”íÕîîVî—î×ïïXï™ïÚðð[ð›ðÜññ]ñžñÞòò_ò òàó!óaó¢óãô#ôdô¤ôåõ%õfõ§õçö(öhö©öé÷*÷k÷«÷ìø,ømø­øîù.ùoù°ùðú1úqú²úòû3ûtû´ûõü5üvü¶ü÷ý8ýxý¹ýùþ:þzþ»þûÿ<ÿ}ÿ½ÿþÿÿÿÿÿÿÿÿÿÿÿÿ`ÞHÿÿIÒÌÿÿTXXÿÿ7´ ÿÿOLåÿÿhìXÿÿƒÆ ÿÿŸg ÿÿÈE ÈÐÿÿÂoÿÿ¿ÿÿÿ¼ÿÿ´•ÿÿ¨\ÿÿ’-ÿÿqÿÿœœÝc`kõÿÿÿÿ:¤àÿÿ/ù ­ÿÿ4C üÿÿOÛQÿÿlÓdÿÿ†ÀIÿÿ ãøÿÿÌr5ˤÿÿÅèÿÿÂÿÿ¿ÿÿÿ½ÿÿµÿÿ§lÿÿÿÿÝtÿÿ^_àÿÿ9m`ÿÿG¢ÿÿ1ùÿÿLeÉÿÿj ÿÿ†é ÿÿ¢2ÿÿÍÜÐÎ^ÿÿÉ<ÿÿÅàÿÿÂÿÿ¿ÿÿÿ¾ÿÿµ…ÿÿÿÿ3mðÿÿ@òžÿÿK½ÿÿT­ÿÿ)¬ ÿÿ@:Âÿÿ`@E,ÿÿÏHûÿÿ¢ÆFmÿÿÖ? ÐøÿÿÌqÿÿɈÿÿÆÂÿÿÃÅÿÿÀ<ÿÿ¿uÿÿÿÿNKæ¶ÿÿ5žØšÿÿ4‡eÿÿUÿÿ^@ÿÿ/P]]ÿÿU0kÿÿ|rùÿÿ¢etLÿÿã…mÆÓqÿÿÏ€ÿÿÍÿÿÊÇÿÿÈXÿÿʼnÿÿÂÿÿÿÿWµìÿÿ)°ÿÿ}sÿÿ3.ÿÿ`ÿÿç€ÿÿ?~’­ÿÿm4ºÿÿ¡(ŸÿÿÿûâŸoÀÿÿÒbÿÿÐeÿÿΜÿÿ̽ÿÿʨÿÿÈÿÿô8^ô`ÿÿÿÿ>qÇÿÿÿÿE(ÿÿiJÿÿž ÿÿѹÿÿU˜Å ÿÿŸÿ̨ßÿÿÿÈj¦‰ÿÿ ÈÿÿÓˆÿÿÒ?ÿÿÐûÿÿÏŸÿÿÎÿÿÿÿR{àßÿÿ0À88ÿÿ ÿÿ1@ÿÿYUb4ÿÿ_X’ñÿÿ7±É®ÿÿ«ïÿÿŸîú“%ÿÿí€ÿÿNu©5ÿÿ<¿,ÿÿÀPÿÿÈQÿÿÏlÿÿÓÖÿÿÿÿ5îó»ÿÿ,®üÿÿD ÿÿ`;¬ÿÿ‹—_#ÿÿ«Ø†ÿÿ°ÔºëðüŸÿ [躘“1lHÿ @ÿÿú¿:ÿÿ€~SÿÿF³…/ÿÿ@ÿÿ9zÿÿ7ÉŠ`ÿÿ;Ù€Øÿÿ@ÿÿ ÜÿÿÿŒÿÿ@ ÿÿ†Õ7ÇÿÿÁ.RÿÿðŒw5ðÎÿÿ¥/É‚ÿÿÐùSÿ Áù‘¿ÿÿÿä‰xÿÿ›ßFÿÿ€ ÿÿ€ ÿÿ€ÿÿНÞÿÿ‚Ç@ÿÿuÔÿÿº"ÿÿ„ÿÿaÈÿÿ´2)ýÿÿò*A@è9ÿÿju¿ÿÿÿ–t‰5ÿÿ½í%øÿÿáÿÿßÿÓžD ÿÿ€#bÿÿ€(Ùÿÿ€@ÿÿtügÿÿ`”ÁÿÿEæ¿ÿÿÿ(Rÿÿ{‘ÿÿ!˜ñÿÿŒ8ˆÿÿÛ{ ð—ÿÿ7˜Èÿÿ\~›ÿÿ„êbÿÿ©#yÿÿÈÌÿÿ­TºK€ÿÿfÆqžÿÿ^³„”ÿÿP…Ÿÿÿÿ@¿ÿÿÿ*ßÿÿÿØÿÿÿÿÿÿ"X&Šÿÿ`ÿÿ¾ ý­ÿÿ Ôÿÿ)Ë©¸ÿÿJ'y]ÿÿmûBñÿÿŽxÿÿ©|ÿÿ}ò˜ñ¶kÿÿHñ¸¬ÿÿ/HÈEÿÿ ßÿÿÿmý€ÿÿÿÿÿÿü¥ÿÿÿÿ ‚ÿÿ’Aÿÿí"ßÿÿÿþ·YÿÿÕ‹ ÿÿ3Ï[‰ÿÿPì*xÿÿlSÿÿð}>ÿÿZWkøßÿÿÿ(®å¸ÿÿLÿÿÿÿÿÿÿÿþ ÿÿùVÿÿóqÿÿÿÿS7ÿÿÉšîÿÿÂæÿÿ›aÿÿmÿÿi@ÿÿ/À:ÿÿ@{ÿÿ3D[ÿÿA(2¯ÿÿÿÿ©ÿÿÿÿý™ÿÿùÓÿÿõGÿÿðsÿÿíoÿÿÿÿÿBÿÿÏ ÿÿ¤ÿÿyKÿÿPžÿÿ$–ÿÿ úÿÿ_ÿÿ(ÿÿ7)^ÿÿÿÿøùÿÿôþÿÿðõÿÿíæÿÿíæÿÿð¢ÿÿÿÿá“Ýÿÿ° ÿÿ€ÛÿÿP“ÿÿ8Šÿÿ'8ÿÿ Êÿÿ¡ÿÿ!ÿÿ0è †ÿÿÿÿ¤ð^ÿÿíÆÿÿíÖÿÿð“ÿÿð¢ÿÿð¢ÿÿÿÿ<òõ©ÿÿ@ç~ÿÿKdÿÿV /ÿÿ&x ¾ÿÿDÄ 3ÿÿcžtÿÿ…#,ÿÿ¢ hÿÿÃGÿÿù! mÊ ÿÿÆ ÿÿÃûÿÿÁuÿÿ¿ÿÿÿ¿ÉÿÿÿÿUÖáÿÿ9•Íoÿÿ=VÝÿÿ ¹(ÿÿ#=ÿÿ@ÿÿ`$†ÿÿ„='0ÿÿ¢#oÿÿÄ(Ðÿÿûƒ&Ì4ÿÿÉÿÿƱÿÿÄuÿÿÂ)ÿÿ¿ÿÿÿÿÿYÊóŠÿÿ"V¨µÿÿ"† Æÿÿ²Úÿÿr$ÿÿ6<5ÿÿZ4A[ÿÿ€HDÿÿ¢ŽGIÿÿÉÊAÿÿÿÿ0ÂÎMÿÿË|ÿÿÉUÿÿÇ]ÿÿÅ\ÿÿÃ6ÿÿúUc`ÿÿÿÿ qƒÿÿÖÿÿSÿÿ<5‚ÿÿ'mP–ÿÿM—`üÿÿ{j-ÿÿ¢2l/ÿÿÒ£i3ÿÿÿÿV¬ÐMÿÿÍÉÿÿËèÿÿÊ/ÿÿÈyÿÿƤÿÿÿÿWWàXÿÿ88@ÿÿ nÿÿ ÿÿM©ÿÿcmZÿÿ=€ìÿÿqŒïÿÿ ù‘ ÿÿÞ‡‘òýÿÿ}Ò:ÿÿÏûÿÿÎaÿÿÌêÿÿË‚ÿÿÊÿÿÿÿ@îeÿÿ@~ÿÿ 5ÿÿ/ÿÿî_Þÿÿˆÿÿ#B ×ÿÿ`¯UÿÿŸÿµpÿÿö’·ßÒ”ÿÿÞ¿ÿÿÿÒÿÿоÿÿÏÿÿÎsÿÿÍNÿÿÿÿ00ãœÿÿ.ŧÿÿ kÿÿ ./ÿÿ@QÔÿÿ:Ì„fÿÿ¿ÿÿÿCöÐÝÿÿŸŠÙ—ßÿÿÿÙb¡*ÿÿºÓ«7ÿÿ^¿ÿÿÿ̱ÿÿÒÿÿÑIÿÿÐÿÿÿÿàËqÿÿ 0)ÿÿ ÿÿ@,Yÿÿc%Q–ÿÿsrÒÿÿ`·¥ðÇ"÷QæUÌÊQ„jïe†ÿÿõ|XÖÿÿÕ‰Aÿÿ9;¥2ÿÿ¹!ÿÿ¿ÿÿÿ¿ÿÿÿÄÿÿÿÿ"¦Aÿÿ,ÿÿ"­Hÿÿ`v)vÿÿ”;Ecÿÿ® t¼ÿÿº"ªÏV˜Ònfæ`ÓTròÊOcÿÿûçÛÿÿßÿ`ÿÿ[€ÿÿ@‹ÿÿ7‡™Yÿÿ(­’ÿÿ#¦žØÿÿ"ÿÿ ˜oSÿÿ–ÿÿM@‘ÿÿˆ ÿÿ»#@ÿÿæf7ñ%ö&0ï±]ØgIÄ¡=ÉÄ3Óë•¿ÿøèªuƒÿÿÐ Uÿÿ€@ÿÿl3Qþÿÿ`´S»ÿÿ`Oªÿÿb=@ÿÿl¨ÿÿ#ð8‡ÿÿ éÿÿr&ÿÿ®†ºÿÿé·2™íGÿÿVÄÈÓÿÿ‡œŽ{ÿÿ¹(;ý”ÝŸÿÿÑ Ôݽ×ÿÿ»ƒ ÿÿ€ ÿÿ€ ÿÿ€ ÿÿ€-ÿÿ|sQ¼ÿÿeôÿÿ 1¡ÿÿ<íÿÿ›{ÿÿß?›ïeÿÿ"°Ê0ÿÿGŽ3ÿÿs9^„ÿÿŸe ÿÿÁÿÿ|5¶ÁðWÿÿ£=qXÿÿY¸zÿÿSA€œÿÿM–ÿÿ@©ÿÿ6`¿ÿÿÿ"Jÿÿ îÿÿopÿÿÉ ÷Qÿÿ5Ñ(ÿÿ›¨tÿÿ3xÂÿÿYH=6ÿÿŒÿÿš<ÿÿUŸÖÿÿð~¾¶ÿÿ*Ê¿ÿÿÿ#Í‹ÿÿqßÿÿÿ âðßÿÿÿÿÿÿÿÿ3µÿÿ§Úÿÿü{Ù5ÿÿ³²ÿÿU‰ÿÿzYÿÿ:ñ!{ÿÿYýÿÿ-ehÿÿ@YÀÿÿŸÿJÌßÿÿÿéõPÿÿÿÿÿÿÿ¯ÿÿýKÿÿú^ÿÿÿÿ}ÿÿã âÿÿ¼ ÿÿ” ÿÿlYÿÿ?|ÿÿéšÿÿ+ ÿÿy)ÿÿ.NÒÿÿsfwÿÿÿýÿÿúÂÿÿøÿÿõÿÿñÎÿÿÿÿÃíSþ>Äÿÿÿœ3ÿÿoFÿÿC°ÿÿ*DÿÿDÿÿ,ÿÿ€$ÿÿ)ߥÿÿm ³ø3ÿÿõnÿÿò«ÿÿðÿÿîÿÿí\ÿÿóIòÏÚÿÿ¦’ÿÿx4ÿÿP ÿÿ=šÿÿ)Uÿÿàÿÿ ÿÿý ÿÿ#a/ÿÿcçðèÿÿî‰ÿÿílÿÿíÜÿÿï©ÿÿð«ÿÿÿÿ õZÈÿÿÿÿ*0aÿÿÍ ÿÿ ÎÿÿÝ_ÿÿ5¾þÿÿW  ÿÿ~v$gÿÿ¡Ø"ŒÿÿÀZ ÿÿç•/ÿÿÿÿÊ~ÿÿÈ>ÿÿÆeÿÿĸÿÿà ÿÿÿÿX`ßÿÿÿ@•9µÿÿÓÿÿ õÿÿŽÿÿ,š(žÿÿN|;œÿÿyÝ@ÿÿ¢w@ÿÿÄï@ÿÿñG7Öÿÿÿÿ©Ì(ÿÿÊÿÿÈhÿÿÆÜÿÿÅ^ÿÿÿÿA>í·ÿÿDÓÝÿÿœÿÿ *ÿÿ V#¥ÿÿ '@ÿÿCÞSøÿÿrå_ÿÿ¢E`ÿÿʇ_Ïÿÿþ4YóôÎÿÿ$êÍÈÿÿËÞÿÿÊZÿÿÈøÿÿǤÿÿÿÿ7¬å’ÿÿ:ÞÇÿÿaÿÿ ]ÿÿ9ÿÿŽY€ÿÿ7ÏlÛÿÿj8zÌÿÿ¡S»ÿÿÒ€ÿÿÿÿy„ßÿÿÿGCÏYÿÿÍœÿÿÌ>ÿÿË ÿÿÉÚÿÿÿÿ)YÒ?ÿÿ-FÁÿÿ’ÿÿÿÿÿEÿÿoæÿÿ$ð‡eÿÿ^ñ–¢ÿÿ  ž ÿÿ݈Ÿÿôÿÿ–nÇxÿÿdNÐÜÿÿÏKÿÿÎÿÿÍÿÿÌÿÿÿÿvµ¾ÿÿ qÿÿ•ÿÿ ¯ ÿÿ&Ü@ÿÿ rWÿÿ 2¡‡ÿÿL—³ ÿÿŸx¼2ÿÿó.¿¸Ô¶ÿÿ°$«»ÿÿ€¿ÿÿÿÐêÿÿÏÝÿÿÎûÿÿÎ)ÿÿÿÿRޏÿÿÍÿÿyÿÿ&$ÿÿE¤@ÿÿIp€ÿÿ °«Öÿÿ1»Î×ú§œ»ÔÙßÿüüÚõ£hÿÿÇw†Zÿÿšzµuÿÿ¿ÿÿÿÈßÿÿÐÞÿÿÐ5ÿÿÿÿuX(ÿÿ„ÿÿCÿÿAáÿÿf¢=%ÿÿ}Áfÿÿlº¢ÉîZR1˜Äu½Æ:ïÙ×ü8~ì,ë'X~ÿÿÝŒXýÿÿ°œRÿÿ ÷¯ŽÿÿQº!ÿÿ¿ÿÿÿ¿ÿÿÿÿÿ1Ó5ÿÿàÿÿ+gÿÿcöªÿÿÇ5…ÿÿ¯-`þ ½m’¶È¶›K*¶“i`=W±ßÛè[SâíñW-ðÿÿåÞÿÿȰsÊÿÿGP»ÿÿ;€•:ÿÿ(ÿÿÿ Ÿÿÿÿ/ÿÿ0s¡ÿÿ\ÿÿN×ÿÿ’5 ûÿÿ¿ÿ+«ÿÿï§NBâ¶íÊ¿i]´aÞS0ãšžL¹ÀuHiÑÿÑ1×~Ýã#ÿÿÏŽXIÿÿ³ Šÿÿ€:nÿÿl—@ÿÿh+@ÿÿgs@ÿÿgÐÿÿ ôôÿÿˆÿÿzAÿÿ¾oÿÿøŠã¯ýs}9?¾týiS†£ú‹ `šo/iö&Âÿÿ•ÍÉ åÿÿ·˜ šÿÿ˜s;«ÿÿnì)¾ÿÿy{-ÿÿx@ÿÿlˆL¥ÿÿc©ÿÿ `ÿÿP%ÿÿ¨÷ÿÿï=õæÿÿèŠÿÿ+¯—dÿÿUuZÿÿ„¡Øÿÿ­’ÿÿZn£Zÿÿán—:Øiÿÿ|ÿÿ@”Vÿÿ@Ÿÿÿÿ7•ª!ÿÿ/õºÀÿÿ"¦ÿÿÖÿÿ‰ÅÿÿÝÖëkÿÿÈ–ÿÿ‚¢Kÿÿ—t^ÿÿ9è;;ÿÿ`ÿÿÚ}ÿÿ@CrÄÿÿ•»gùÿÿÿÿYåÊÖÿÿþ×3ÿÿ Áßÿÿÿxîiÿÿÿÿÿÿÿÿ`ÿÿÆPí/õ–Î~ÿÿª'ÿÿ„ŸÿÿlUÿÿ' ÿÿ9éÿÿÜB|ÿÿ2U7Øÿÿh],Vÿÿ×g ÿÿÿÿÿÿÿýºÿÿüÿÿú9ÿÿÿÿ§QïDè­ÔçýÒ±`ÿÿ‰ÿÿ_ðÿÿ9QÿÿPÿÿWÿÿæÿÿ'¥‚ÿÿVZ £ÿÿ¶ù3úPÿÿøšÿÿö®ÿÿô¬ÿÿò—ÿÿ÷ÞoÔˆð@º8ÿÿ‘‚ÿÿcŽÿÿByÿÿ.*ÿÿÿÿïÿÿEQÿÿ#½ ÿÿP·<ÿÿ®óˆÿÿñÿÿïØÿÿîsÿÿí†ÿÿÔ>ßÿ¿ÿùÎÿÿmñÿÿX¹ÿÿCðÿÿ.dÿÿÿÿÿÿF OÿÿDÿÿHÿÿ¦ î"ÿÿíiÿÿížÿÿî¬ÿÿð%ÿÿÿÿ"d´lÿÿ/.ZÿÿÿÿYÿÿ Q ’ÿÿ(GcÿÿKñ#×ÿÿv ,"ÿÿ¡•,ÏÿÿÇõ(µÿÿè‹"EÿÿÿÿÌ4ÿÿÉ[ÿÿÇÇÿÿÆpÿÿÅAÿÿÿÿ%’Úÿÿ#b ^ÿÿÿÿŠÿÿÂXÿÿ C'“ÿÿBá;"ÿÿqöAnÿÿ¡ËDSÿÿÌsAðÿÿñ@ÿÿÿÿ(£ÍPÿÿÊ›ÿÿÉÿÿÇßÿÿƾÿÿÿÿ eÿÿL ëÿÿÿÿ·ÿÿ ÿÿY=Žÿÿ;`MµÿÿkT[ ÿÿ¡˜_oÿÿÒ__FÿÿüG\÷ù©ÿÿBŸÎfÿÿËÏÿÿÊgÿÿÉ?ÿÿÈ;ÿÿÿÿ2#IãÿÿÿÿÿÿÞÿÿ+*ÿÿ ONðÿÿ/™bŠÿÿcqpÿÿ¡ wîÿÿÚ}yrÿÿÿÿv›æLÿÿ]»ÇÃÿÿÌúÿÿË­ÿÿÊŸÿÿÉ©ÿÿÿÿDÚ ÿÿÿÿ¥ÿÿ7ÿÿQ7Ëÿÿ`ÿÿày„ÿÿZ†ˆ=ÿÿ Úÿÿã¾” öÿÿøÕûÿÿt]¿ÿÿÿÎ"ÿÿÌèÿÿËòÿÿËÿÿÿÿ?œµÿÿÿÿÿÿÕÿÿ 2Wÿÿ`ÿÿ#ŽÝÿÿKùŸÿÿÿŸ©Õÿÿõs®VÝæÿÿ£J¾Šÿÿ‰q³ÚÿÿËJÿÿÎ"ÿÿÍ?ÿÿÌzÿÿÿÿ5‰Ùÿÿ®ÿÿµÿÿ3Qÿÿ@-Õÿÿ@_ ÿÿœ‰ÿÿ6àºIöÎ0£¯RÜ b飾”º»ÿÿ¹oœöÿÿŸÿŸÿÿÿ¿ÿÿÿÇÂÿÿαÿÿÎÿÿÿÿ%lHÿÿ…ÿÿ ÿÿO"ÿÿke$»ÿÿyRTÿÿfo“¼êmÇDµžo½¾A…„л’—ó>Ö›ÎÝopÿÿÓÜ`ÿÿº{Fÿÿ@£ˆÿÿ2¶äÿÿ¿”ÿÿ¿ÿÿÿÿÿ ¨ñÿÿ©ÿÿ.«ÿÿrˆÿÿ—î ÿÿ´kKôÓÆòãaY×ÁY[oÖ€szm‚¼ÞEÌ_*ÊeÔ]1Hÿÿà4 g(ÿÿÈ@ÿÿczycÿÿ@‡ÿÿ1Ÿ—Úÿÿ Ÿÿÿÿbÿÿ•ÿÿ ÿÿYbÿÿœSÿÿÉ>çöaé±×$=Ø“å(óA4³ÕÛ”B³nã^Ÿ¼ô\¨®®Ķ·œÅ9§ìÿÿÆ({Óÿÿ³åÿÿ€Nÿÿ€"‘ÿÿrb@ÿÿcæ@ÿÿbëÿÿ ¿ÿÿ4âÿÿˆÿÿÊ&ô0íÛ¶ HÙaóë÷Ú·ðõoéBg‡Ëò¢tªHXìÞG‘šÍ(Qð}¥]£×îGÿÿ«ëÀºÿÿ—¿`ÿÿZ@ÿÿey@ÿÿf@ÿÿeè@ÿÿehÿÿpÿÿlÈÿÿ½{ðœæ»Ùnö¥s+½òÿÿ†”ÿÿ<õYÜÿÿnNÿÿcÿÿcPöÿÿÂçˆoòíÿÿzc¢Dÿÿ6ôÅÿÿ2{Ÿÿÿÿ/¸¤vÿÿ+v¯‘ÿÿ"rÿÿDLÿÿ¨ïï Ü2×hïÏÁŠÿÿŸaÿÿñsKÿÿ!Q;„ÿÿHŒÿÿ fÐÿÿG¿\ÿÿNTÿÿßÿLPßÿÿÿ!ÞÕìÿÿTÝËÿÿŽåNÿÿñªÿÿÿÿŒrñöÒ3Ô_âÕÁD÷Ä£õÿÿ~_ÿÿVÿÿ¶ Oÿÿ#çÿÿ­+òÿÿ4ë:ÿÿ[‹lÿÿ— µÿÿÿÿüðÿÿüÿÿûÿÿùæÿÿû×Ê¥Ò<ÖZ¿ÿì>ª„ÿÿ‚âÿÿXÿÿ2Iÿÿ(ÿÿ ÿÿ°ùÿÿ-Ï ƒÿÿSüÿÿU«ÿÿÿÿ÷:ÿÿöÿÿô»ÿÿókÿÿÒºÊ×¾žß~¯¿ûAŒÿÿ_,ÿÿHTÿÿ1‰ÿÿÿÿ ÿÿÅ áÿÿ)¸ÃÿÿMøÿÿ‡¼ÿÿÿÿñnÿÿð1ÿÿï.ÿÿîSÿÿ¾àÔ~°Ìîg—Òÿÿt&ÿÿ`ÿÿJÿÿ2£ÿÿ¦ÿÿÿÿÁÑÿÿ#¹ÿÿG,ÿÿ‚Fÿÿÿÿí¤ÿÿí\ÿÿíŒÿÿî,ÿÿÿÿ7yBÿÿÿÿÿÿÿÿoÿÿ$ ÄÿÿFâ4ÿÿrÝ ÿÿ¡ ÿÿÌÎ ÿÿî‘sÿÿÿÿ Hÿ¹ÿÿȹÿÿÇÿÿÅÔÿÿÄÖÿÿÿÿ04ôÿÿÿÿÿÿÿÿºÿÿFÄÿÿAn'ýÿÿo¿1õÿÿ¡ 4ÆÿÿÑP3~ÿÿõ–/îÿÿÿÿ PõÿÿɈÿÿÇèÿÿÆÂÿÿÅÐÿÿÿÿ(q OÿÿÿÿÿÿÿÿŒÿÿD(“ÿÿ;9äÿÿkWBÿÿ¡G˜ÿÿÕºGöÿÿüÓFSÿÆÿÿ5:æšÿÿÊ[ÿÿÈÆÿÿDzÿÿÆÇÿÿÿÿ[ Áÿÿÿÿÿÿÿÿàÿÿ ‰9ãÿÿ3HI1ÿÿeLWÿÿ¡]AÿÿÛc_cÿÿÿÿ\$ñ ÿÿI²ÝNÿÿ&ÁË(ÿÿÉŸÿÿÈšÿÿÇÀÿÿÿÿ– fÿÿ‰ÿÿÿÿÿÿ"ÿÿîH¤ÿÿ'k].ÿÿ__iïÿÿ ŒrJÿÿâÅv+ú7ÿÿq7à ÿÿ_«Ìÿÿ=ÓËùÿÿʇÿÿɉÿÿȾÿÿÿÿB ›ÿÿuÿÿzÿÿÏÿÿ‚ ÿÿ”V´ÿÿtÖÿÿRú„iÿÿ #Ž/ÿ yñ¸Jâ‰ÿÿŠÊÈÿÿxAºVÿÿTÏÄNÿÿ˨ÿÿÊÁÿÿÊÿÿÿÿÿÿºÿÿMÿÿ)¸ÿÿ@èÿÿ?ýJÍÿÿQŽüý§ d@ Ÿˆñ/N©£~ÖSêß9œÂ¼æÿÿ¦ ¦ÿÿ”iš–ÿÿq¬·YÿÿÃ@ÿÿÌ=ÿÿË™ÿÿÿÿ¤ÿÿQÿÿÿÿL ÿÿjàLÿÿx¾C›ý@l# )z‡äQ|HUr5Ðá}£¡r¸¢G<3Ѓ¦}r*ÿÿÅcÛÿÿ³`ÿÿ” —sÿÿ ¯Õÿÿºÿÿ¿ÿÿÿÿÿâÿÿ 2ÿÿ9lÿÿu©ÿÿŸIÒÿÿ¸à:笼G'0B#çK¨d#œí÷‡1¸^}f×ÁL°×»;õýDÍ‹ ÿÿÆ©—ÿÿµ~`ÿÿJ€ÿÿ3‹ÿÿ" –©ÿÿ ÿÿ ÿÿüÿÿf¶ÿÿ£†üÉκpJãÒû hÆðм¼5¯¦öËý?óV0ƒÅŠ{ôr¥S˴ݽµ“Jé ›ü𱉀‚ÿÿªÌ`ÿÿ—Æÿÿ~¼‡ÿÿp63_ÿÿd7<¶ÿÿ`ÿÿòÿÿP"ÿÿ› ö–ÊÖ݆Öi_ÁÇºà† é¯?éaL'æëS2bJ^ñãï^umZ÷示Øg $Ý0óŒÅSÿÿ¤ÿÿx–Nÿÿ['Azÿÿ`@ÿÿ`@ÿÿ`ÿÿ.ÿÿЧô4ÂÖÚÏðÍdÅðâ,¤»³Èóì?•”Püê?">]Ñý? ƒT¶ü»L}@IÿªsP€ÀÿÿÐû{õcÿÿoö×pÿÿYqžÿÿ( ›ÿÿ(üŸÿÿÿ'J ÿÿ$€ÿÿoqù&½ñÑïÆŒÀðÙ̲sðkœÀÿÿt3ÿÿ›<èÿÿ8]ÿÿ ´XêÿÿQãL"ÿÿŒ²E†ÿÿÎ @eÿÿÿÿ95ІÿÿïÕÿÿÛ¹ÿÿá2ÿÿÿÿ²)ÐŒ½£½"ϼ¯VåΟ\ý×z½ÿÿTSÿÿ úÿÿ¸ÿÿ¨ûÿÿ9.ÿÿ`½úÿÿœwÿÿוXú­ÿÿúCÿÿù«ÿÿùÿÿÏ+±Öº³Å¡­cÛ,Ÿvôw€ÂÿÿUHÿÿ4¨ÿÿYÿÿ©ÿÿn  ÿÿ5AEÿÿ]{¢ÿÿ‰¶ÿÿÒ-;õóÿÿõEÿÿô|ÿÿóµÿÿ¶ó¹Ø¬ôÑ_ŸÿéTŠ$ÿÿeÿÿMZÿÿ4Dÿÿ9ÿÿÿÿáiÿÿ1ÌÿÿY|ÿÿ†@ÿÿÍnñbÿÿðŽÿÿïÚÿÿï2ÿÿ¬áÇŽ¡“Ýj•tþ<{YÿÿfÿÿNéÿÿ5¤ÿÿÿÿÿÿmçÿÿ.!ÿÿU™ÿÿ‚íÿÿÈXîÿÿí¢ÿÿíkÿÿí_ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ!ÿÿH ÿÿs›ÝÿÿŸÿhÿÿËÖÿÿðÔÿÿÿÿÿÿÿÿßÿÿÿÅ)ÿÿÃóÿÿà ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ llÿÿD’ )ÿÿpÇÒÿÿŸÿ}ÿÿÏNeÿÿöÑÀÿÿÿÿÛýkÿÿßÿÿÿÅÁÿÿÄ—ÿÿñÿÿÿÿÿÿ'ÿÿÿÿÿÿÿÿr ûÿÿ@æÿÿmï •ÿÿŸÿ$mÿÿÒh$ˆÿÿûž"îÿÿÿÿßô¹ÿÿÞ ÿÿÆWÿÿÅ9ÿÿÄ^ÿÿÿÿÿÿaÿÿÿÿÿÿNÿÿ²9ÿÿ9í)§ÿÿiƒ4»ÿÿ K9çÿÿ׿;÷ÿÿÿÿ9 øuÿÿ(ýæLÿÿÓ‹ÿÿÇÿÿÅþÿÿÅ/ÿÿÿÿ¸ÿÿåÿÿÿÿÿÿAÿÿ€1òÿÿ.Þ@ÿÿbzL¼ÿÿ YTÿÿßsXpùPÿÿS2å\ÿÿC…Øÿÿ-wÆVÿÿÈÿÿÆùÿÿÆ4ÿÿÿÿÊÿÿ³ÿÿÎÿÿpÿÿ +ÿÿFpÿÿ^qÿÿW/ltÿÿŸúv3ùb.¸ê¼Zxߤÿÿr=Ìqÿÿb2¿îÿÿKª²fÿÿGÉ$ÿÿÈ0ÿÿÇyÿÿÿÿñÿÿÌÿÿ³ÿÿ%šÿÿ@èÙÿÿ8q@ÿÿ§‚^õä:fVViæéƒX"¦|TòÛÂW”á©U¸õØþú¢¦„ÿÿƒ(œŒÿÿl{” ÿÿ@¿ÿÿÿÉ—ÿÿÈ÷ÿÿÿÿ 4ÿÿ 4ÿÿ”ÿÿM$ÿÿt ÿÿ{€4*òÆ€ï#&D/Ò¨g•J½b}¾¸f|QeO²SfSÓdf½„b$Ë÷•šðbÏÿÿ¦S`ÿÿæ`hÿÿe™ ÿÿÕ²ÿÿ»Mÿÿÿÿ –ÿÿ ØÿÿCÙÿÿ{hÿÿ¦y–ð}°4 ¼kÕ¥pI6›±£‘Ì?zW”‰Ôy€v%v/ƒ£œÇs0HœR²ðw  ÿÿ»ÿÿ¬zÿÿ“«lÔÿÿ<~€ÿÿ)Žiÿÿ ÿÿÿÿÿ3Qÿÿw‚ÿ2¬'䊶`,éϽ} cµw¼Ò‹+”Û·Ó5¤IÓp“°l)efk²½Ka;4›oð–„‹}Œÿÿ fÌÿÿÑApÿÿ}dÿÿv ÿÿfÎ0'ÿÿ`ÿÿ7ÿÿkýª@Úp³jÇ}Àoý)¶;Ω•|žtÖÚ/}ÍØ®*Ä:aZ,Ö6[.T®U×§­ñPH?QÐ3ì(]j½nÿÿ~ШHÿÿp ˆíÿÿYõFµÿÿU­@ÿÿYƒ7ÿÿ`ÿÿOãÿÿ¥ýÔŽ­Ý¿ºªª×±¶ÍCí¢ ÞúNu‹žîÑ Öhj·ô¬Ë*<óT@ÎL9+õg–3E<žôëṬKŲŸ¾Ú9ðUtõÿü O?¨ÿÿ+Áÿÿ fNëÿÿW·B!ÿÿŸ~;ÄÿÿÙ÷8aÿÿÿÿ3çæ‰ÿÿ*̤ÿÿÏÚÿÿÔ,ÿÿÓòk¶¥«—3R¨Ù½û[Òޤé0zÿÿR”ÿÿ%ÿÿ ÿÿô ÿÿ=Ñÿÿoßÿÿ—¿ÖÿÿÌ)Ùÿÿÿÿøÿÿ÷Ïÿÿ÷wÿÿ±^ŸðÛg§¾¶‹Ê3^á~ñýUsÿÿ6Òÿÿ[ÿÿÿÿ8 ²ÿÿ:’ÿÿlÑÿÿ•æÿÿÉtÿÿÿÿô ÿÿó­ÿÿóAÿÿ£«äöÂ’‹Ù9ƒòö‚jÜÿÿQºÿÿ6¡ÿÿ\ÿÿÿÿ`ÿÿ8ÿÿjÚÿÿ”yÿÿÇDÿÿÿÿðlÿÿðÿÿïÿÿh¹I–°ÏŠlí*w‹ò¿aãôuK€÷™2ñúÉKüÇVý£ ‡òÿÿ7Bÿÿiÿÿ’{ÿÿÃÇþ?ÿÿíðÿÿí´ÿÿízÿÿÿÿcÿÿÿÿÿÿÿÿÿÿƒÿÿDœÿÿpéÿÿÇÿÿËGÿÿö“ÿÿÿÿÿöÿÿãæÿÿÂÿÿÿÁvÿÿÀŽÿÿÿÿ‹ÿÿRÿÿÿÿÿÿÿÿ!ÿÿDkÿÿpÆÿÿžÿÿÌÿÿöÇÿÿÿÿÿÿÿÿärÿÿÃvÿÿÁøÿÿÁÿÿÿÿïÿÿTÿÿÿÿÿÿÿÿ˜ÿÿCJÿÿp=ôÿÿŸ.ÑÿÿÍÒ¿ÿÿ÷Š,ÿÿÿÿ÷qÿÿä.ÿÿÄÿÿÂÿÿÁÅÿÿÿÿÿÿ’ÿÿÿÿÿÿÿÿ¥Çÿÿ>²ÿÿk™#ÿÿŸ„öÿÿÓ_ƒÿÿÿÿ8÷ïÿÿ jê'ÿÿÝ!ÿÿÄÕÿÿÃhÿÿœÿÿÿÿfÿÿÿÿÿÿÿÿOÿÿ  ÿÿ3v(Tÿÿdò2óÿÿŸ¦9œÿÿÛ¹>5÷@ÿÿ;wá»ÿÿ+^Øÿÿ,Í1ÿÿxżÿÿÄ_ÿÿÚÿÿÿÿèÿÿÊÿÿÿÿÿÿ Kÿÿ8Zÿÿ!fI,ÿÿZ”V úP"£¦CÇñy6»ßL5gÛ2ÿÿ^•Ç“ÿÿMÁ¿2ÿÿ´6)4`æÁŠ›1Y1†ä®Ïf-äÝÂüwEåÐÿÿC²¿ÿÿÿ3úžúÿÿ Žéÿÿ ÿÿ ß)ж}•/ ¥B¢žæ_™L²¾%9œÅY~*Úh‘íÑ}J$û ÉÏòþ%Ø8çÿÿ\m;Ëÿÿ­ª5Òÿÿñª2¨÷Jÿÿ)çÿÿaʯÿÿÅÿÿÇ>ÿÿ³¸ŒÆ¢žœ^Ë—´­‡ŒP¿|~‡ÓülzíœSaÿÿ)xÿÿþÿÿ _Xÿÿ@¸ÿÿy¹ÿÿ«ÿÿÔþÿÿÿÿ÷‰ÿÿõzÿÿõ\ÿÿ/“0­ý˜s¨ä¹ÊÎYqÂêÉW™ÿÿ9ÿÿÿÿÿÿø|ÿÿ>.ÿÿy ÿÿªÈÿÿÓûÿÿÿÿõÓÿÿòQÿÿòÿÿ“_Ÿ&‘9³m…ãÈx¥åFemòôO•ù&7_þybÿÿÿÿ Yÿÿ=mÿÿx­ÿÿªÿÿÒWÿÿÿÿôKÿÿï¥ÿÿïmÿÿ}«±Šë¾Ù:×ÕlÝüXšä_B°éÉ*ïú£ô‚ø9 ýÖ>ÿöx:ÿÿ¨AÿÿÎÿBÿÿó.ÿÿíÔÿÿí²ÿÿÿÿŽÿÿÿÿÿÿÿÿÿÿSÿÿ@¿ÿÿmÿÿšVÿÿȾÿÿõÍÿÿÿÿòÀÿÿßÿÿÿÇ@ÿÿ¿ÿÿÿ¿ÿÿÿÿÿÑÿÿXÿÿÿÿÿÿÿÿÙÿÿ@ÿÿmcÿÿ›&ÿÿÉëÿÿ÷#ÿÿÿÿó.ÿÿßÿÿÿÈÿÿ¿ÿÿÿ¿ÿÿÿÿÿ#ÿÿWÿÿÿÿÿÿÿÿ/ÿÿ@UÿÿmdÿÿœVÿÿË·ÿÿørÿÿÿÿóÍÿÿßÿÿÿÉ"ÿÿÀ ÿÿ¿ÿÿÿÿÿ…ÿÿ‡ÿÿÿÿÿÿÿÿ_ÿÿ?-ÿÿl¿ÿÿ‘—ÿÿÎÆ“ÿÿüCó¥ÿÿäWÿÿÜ$ÿÿÊ~ÿÿÀÐÿÿ¿ÿÿÿÿÿöÿÿéÿÿÿÿÿÿÿÿ¡ ‰ÿÿ6–fÿÿfd_ÿÿð!Áÿ?tÖ¸"Úô6ÿD$<ß_ÿÿ[Ô{ÿÿ Ê0ÿÿ¿ÿÿÁ¶ÿÿÀàÿÿÿÿxÿÿ~ÿÿÿÿÿÿ“ÿÿ*Ìÿÿ%q6û©ŒbK)³ï®%š›„$!äg&ãÏ%uÙ6&©÷ü%‡Áæÿÿ:ý¹aÿÿ*f±ßÿÿ(«5ÿÿµÿÿÁñÿÿÿÿ ÿÿ¼ÿÿ9ÿÿ$xÿÿ@þm>58í 2í0/²Ûå5YQ¥3Í%6⊣5’À´6—À6ó¸6¦íN6h™Ÿÿÿ_Þ“›ÿÿNºƒÿÿ> ‹ÿÿ ¹gÿÿÃÿÿÿÿ¢ÿÿ]ÿÿ)”ÿÿSøLlÛä(lJ ÿÎ}\í-&²±¦EŸ7,Fc¡E±tH¥–FP±MIçXEã I_[Ùÿb‚åW¸ÿÿuòX@ÿÿb´^ÿÿCØž=ÿÿ©-ÿÿÿÿ>ÿÿ"Iÿÿ\ÑðÝyúÖ‡€\ÅAƒñ -®Ó~7Œ cro’-G=]jýYíZF[»dù`&¡OWˆb_ÙØTe!V1ûµ‡™¥ÿÿ¼ ƒÿÿ†ÿÿv1b5ÿÿ0ï{ìÿÿ ÿÿ ÿÿX¹ì©€Ê)…M¶Ù !^§”î² D“™EÖéuÅ–­#å1S"RI+N»‘µ™ÆEˆ¶E8:¸ŒÖ48Oµñ˵1eö«ùD5šJÿÿQ zÿÿEÝ| ÿÿ8ÂF)ÿÿ@:ÿÿG_ì°t¿„ª¦‹f¬—:–ƽŒ‹_¥U9'}ç¶$~zj¢ÈñÞKàÒâ²-+Õ`72(×(;×Pzä$'%Õ%»o!#•Ò¨óPÅÆÿÿ3Óº¦ÿÿ(kª¾ÿÿ$Špÿÿg„,ÿÿ-¾Lz}¡ú†½”·“Û¦ïˆh¡v¥ä|š±™m‰ÅàX¥Ú;é]  o´ð›&ª¼·õyiÇ‚?õ9¯¾{òÙèÙ³êÃÿÿ£ßÿÿÿLÑ}ÿÿ޾5ÿÿ¼òÿÿž-ì’Ò3:QˆÖ´}­‡o4Á\]¿ØªG)ïë+þ_§¾ÿÿ wøÿÿD nÿÿƒFÿÿ¼‹ÿÿèlÿÿÿÿûÿÿìÿÿÿï"ÿÿ숋ݙȀ}©sÙ½§dh×åPô@8PüÛùÿÿÿÿ ^ÿÿAÿÿ‚Fÿÿ¼}ÿÿçÿÿÿÿúeÿÿð±ÿÿð®ÿÿ†Ê’n…’£Ïz}·}léÓcY˜ß“D£è½,×ñð÷ú½ ÈÿÿC.ÿÿ‚£ÿÿ¼!ÿÿålÿtÿÿø<ÿÿîÚÿÿîÍÿÿ„³œ¯€®syÃa½ËêNƒÔ¦8‡ÛÏ ±ãg_éÚóùÂDuüèáþ¶!ýöÝý/ÿÿöÍÿÿí¶ÿÿí£ÿÿÿÿÿÿÿÿÿÿÿÿÿÿCÿÿ>Bÿÿiµÿÿ–ÿÿĶÿÿñÈüËÿÿå‹ÿÿÙµÿÿÆõÿÿ¿ÿÿÿ¾åÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÿÿ>ÿÿjÿÿ—ŒÿÿÆ0ÿÿó[ý7ÿÿæÿÿÚYÿÿÇÿÿ¿ÿÿÿ¿Ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçÿÿ=Îÿÿjjÿÿ˜ÇÿÿÈÿÿõTý²ÿÿæªÿÿÛ8ÿÿÈÿÿ¿ÿÿÿ¿ÿÿÿÿÿÿÿ{ÿÿÿÿÿÿÿÿÙÿÿ<\ÿÿi»ÿÿšaÿÿÊñÿÿ÷àîGÿÿßÿÿÿÖ”ÿÿÉÐÿÿ¿ÿÿÿ¿ÿÿÿÿÿÿÿóÿÿÿÿÿÿÿÿ~ÿÿ8½Iÿÿg°JýþÛ›ïÇóÊ ÞÉöaêc 'ñÈ PÚÿÿ JÌ‚ÿÿÂÕÿÿ¼+ÿÿ¿ÿÿÿ¿ÿÿÿÿÿÿÿtÿÿÿÿÿÿ³ÿÿgü@0R®îm\aáá,ÔŽËÈ×9q¾’0ÍjÎçØ–¼£ÿÿ+űÿÿ¬?ÿÿT¦¹ÿÿ«æÿÿ¿ÿÿÿÿÿÿÿûÿÿXÿÿ%ÿÿ=Ûî57°… Ûe$®0"ÌÌ'ÆH/$g¾Ú(½}ƒ&¿´P*±ÿ'ï¬ )ÎÜÔ'ä›ÎÈý< ‹§ÿÿ=õˆ ÿÿ/„ýÿÿ”Iÿÿ:¾Ùÿÿÿÿÿÿÿÿ1ÿÿV9ãRYõЪY—0$½LÇåV¢«8…/v8”J9Òh°:‰e9ó¢;<‚8õÓ@:ïq-#^øºN±QÜÿÿcóPÿÿVÃU¹ÿÿ@bÿÿ&Ÿÿÿÿÿÿ”ÿÿ.ý×c¾Ùzi*À´n±,q’$B¨m¹þ!¥a8%p0™^—NÓO¼M¹XÒU“SIUÜSîÉ,FE½Eoô–SQÿÿ€ ÿÿx˜ÿÿmþ–ÿÿYÚmíÿÿ ÿÿŽú‚`‡Óêq·´)uƒ¢,{:·‡“©‚+ÂÖ‡g Õ g‡˜ã%²FLóFï:ÏB'ƒúо6é?µÌÃÛ4’;¼|çô¾5ïZ_ÿÿ^íN~ÿÿWÍ@ÿÿNÐ*¸ÿÿDö­ÿÿ[ý:WéΉmɯqx/™7}ՔЋZ‡Z´¥~’žôl„Ÿ-î ðOC¦-‚ü2í§Ô;©*l.ƒª)}‰&…+ܨ¹\$,*b¤½í"ǰÿÿ?”†ÿÿ7zŸÿÿ,ãdîÿÿ 7ÿÿ@ÌreΪ‹t– ~†þ‡?þPzt“µ4l¾£4ùvYp´B<7¿i–b!ÅU0#^ÎÈÉm³ñÇ›ªÀYÞÄæßäæ»mÿÿ#8² ÿÿ;§ ÿÿþ–ÿÿ0€ÿÿ'§Cmþ’³z±†Ñ…³!,yÍ‘ ÌlÜŸ´H]]°²åHùlj,¾ÖÅБߑ!­ vå[ò ô (å›› â ›ãYÔyqß!ÿÿ‹ÖÿÿúÌÿÿÿ¿Mÿÿ³ÿÿuM…Õ‚^|mŽpošœßaf¯ÝPÇN8ÕÝŸÁîO[ùósÿÿHsºÿÿŒfÿÿÊàÿÿÿÔ.úÞÿÿôcÿÿé!ÿÿã]ÿÿ¢|΀V‹?u™<h­ˆX ÇHC”á&+›ìöqôÚú âÿÿF~ÿÿ‰UÿÿÇ’ÿÿùý‰ÿÿøHÿÿïÏÿÿï2ÿÿ{ð…Œ{g“ÛoI§/`ãÁfNÎï8½ØL €á;é¬ó×ÿúýH>þPˆÿ?Ä]þÌóÚû¶ÿÿöÔÿÿï6ÿÿîÿÿzrÄ1>vVŽhg°iVï» CÍÅ7.0ÎW Ö4ݺëöôK½øœ„4ú›¼ ûeéÓú/ÿÿõÒÿÿï!ÿÿísÿÿÿÿÿÿÿÿÿÿÿÿÿÿbÿÿ;îÿÿfäÿÿ’Ëÿÿ¾^ÿÿê¨ô0ÿÿÞ°ÿÿÍÐÿÿ¿ÿÿÿ½Ãÿÿ»Oÿÿÿÿÿÿÿÿÿÿÿÿÿÿ´ÿÿ;±ÿÿgBÿÿ“»ÿÿÀÔÿÿì”ôƒÿÿß7ÿÿÎpÿÿÀ@ÿÿ¾Xÿÿ¼ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÿÿ;Mÿÿg•ÿÿ”Þÿÿ³ÿÿî½ôûÿÿßîÿÿÏ3ÿÿÁÿÿ¿ÿÿ¼Úÿÿÿÿÿÿÿÿÿÿÿÿÿÿ›ÿÿ:Qÿÿg/ÿÿ–©ÿÿÆB÷ ì¾è­ÿÿÙeÿÿΟÿÿÂLÿÿ¿»ÿÿ½µÿÿÿÿÿÿÿÿÿÿÿÿÿÿJÿÿ7,úTcã¸0ÚÌIÝb;Òž üÞÕÄTÿÿ¼'ÿÿµmÿÿ·Öÿÿ¾£ÿÿÿÿÿÿÿÿÿÿÿÿ õö ƒ ­¦ë ˆ)ÑNÝ¿ uR½ÒV ~€Æ ÈРʯ™ %¿î Õ× »¸4 ÷n š«uÿÿ4¥zÿÿŸÿÿÿŸÿÿÿ¿ ÿÿÿÿÿÿÿÿ ÿÿ%Xðj1ÛV(ý~ ËÁ£÷ý½·9?¨±L qÓ`§Jj£YŸ…<΀š%¢ò᳆zÿÿ-ëuÿÿf€ÿÿ‘†.ÿÿ®çÿÿÿÿÿÿùÿÿ6é×F°ÐJ½uHÝw ¬X>„ vs•-è(l*‡û/],}Ÿ/D•V-(wc.ÐÄL,âsí-?ïe+{MÿÿT¥NÿÿB£L+ÿÿ81Vìÿÿ"îüÿÿÿÿÿÿ6Šä½UÌÄþZ|­:^}#`Œt^ <ösâTÌz$àR¯C­Dd?íMaJf„ó:ÅJ|I‹¹„7ÒHÀF»è6žÿÿmÖ éÿÿdÏÿÿ`ÿÿQ›[™ÿÿ ÿÿ.ßÿSØ¿0c+¢Åf‹¼lºìfq;§¿q‘vÊ â [X‚xÚ•Î;Buõ<Æ-46æwë|+)H4QvW³ê'2ïs#ãÚ%àXÿÿLBJéÿÿC²>fÿÿ@-™ÿÿ;ÿÿQ¥ÜÒLñ¸’`ŸÿjxŠzpTzòw$;zm7€ÛRû[ÙˆÙÕ@Æ–bRA'™d2Îè"qœo‚3蛨Ä-ϘmÛ[ÿ‹Gÿÿ.Ž~Þÿÿ$átvÿÿ fwÿÿ_A™ÿÿ*:µ,[:šHgSŠ&qÆytxˆk]‚+Üû] — Iš¡í•Ø.0®Ï š £¶-*AÊ:º_è x`º™  3¸ Îò  íµ~þ l¨ùÿÿ ÈŸÿÿÿv”Oÿÿ6ÿÿ–Ub®†Òn“{xam']&,_Jޱ!Nì¡Â9Ûµ³‰ÄòB óÏ ½x3ÔÑQ÷‰ÖŠm!cÔàÀ¼G{Ó"ô]WÌ’ÿÿÆ;ÿÿ¼ÿÿ±¹ÿÿ‚»iâ{¦v q«ëdŒÖTË wBh¶Ì*çÌT‡ÜXé™Aô±J‘øßŒsùGÉô÷ÄÿñŒÿÿìzÿÿå[ÿÿÞ>ÿÿxqâvI}jj»‰ËJW\€{Kå¶Ü6&ÎþÚËYävñrçùIK~ý‹£þDÉ‘ýéþªù ÿÿõ=ÿÿð+ÿÿíåÿÿrày r„^"Vd›–c%Uw°†B½,,ÈOÜÑáÛˆ±ê ãómOGøc‰ú±ÃFûb÷ø÷Üÿÿô\ÿÿï¿ÿÿíuÿÿpòllàŒÚ¾R]-¸kLªH8¢µ "4¿% xǸÔW Äã^'xìáRqó‡ö•¼–øuï™ö§ÿÿó¤ÿÿï­ÿÿí]ÿÿÿÿÿÿÿÿÿÿÿÿÿÿïÿÿ:™ÿÿdßýaœ÷i³zð”×*ç²÷ËÕÿÿÿ÷ÿÿ¹6ÿÿ¸ùÿÿ¸Uÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ:LÿÿeÿÛ ùж^òÁÚRé—úÆÖ}ÿÿÄXÿÿ¹Ãÿÿ¹‰ÿÿ¸üÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ9ÔÿÿeTÿÿ‘1ü„¹¶õ@ÝØëÃþ×(ÿÿÅ ÿÿº_ÿÿº'ÿÿ¹¤ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ·ÿÿ9ÿÿe6÷6Æí³áåpØ%ß•ùÐÒ‹ÿÿÆ)ÿÿ»…ÿÿºÓÿÿºZÿÿÿÿÿÿÿÿÿÿÿÿþì íõÀ2FéX5ÜQ€/ÓI¨ÓÌ9ÎkÆ6ïÀ½‹ÿÿ³ÿÿ¬¢ÿÿ±½ÿÿ»ÿÿÿÿÿÿÿÿÿÿøK@áü™%ØÇ#Ì`:HWÞÁ s4A¸ž!±+ŵ«ŸnèE÷¤¾ÿÿEÿÿ–˜ÿÿš#ÿÿ¤¶ÿÿÿÿÿÿÿÿ(÷m ÜÆ É6Me»‹ÿj g¯»74 a¢„Óe^¾™´¨•F€“Aê¾Ý†Ž–7äoô€¡ÿÿq} ÿÿs|ÿÿéÿÿ‰€ÿÿÿÿÿÿ f÷2ÅÖ¿;¾¡;̬:}›­0÷ î j‡ #t!,Zzö$ŒRªUq§$Í‡Æ k[$µ«Ègê"¦ß.©O£Yþÿ;§Gÿÿ4‚KFÿÿ$íLÿÿP`ÿÿCÿÿîî3hÑMHk´aM OÇ‹‰QJÄ {™O~ à ‚f HfrHÙ:·;]2B‰?ðx-?c?6ª»*G=³=×÷(‘"õ(óü @[ ÄÿÿTäÿÿNãÿÿE Ëÿÿ?îê-/È~E ¯‡V»•¤Y¤€b]pâaÛ©Ðaf‘F…JùjVi0Aio3Ò Ù+”k®n„(Òj…£G 'xgÓÓ-Õ8_‚ûº!‹FÛÿÿ3ˆ<âÿÿ,à2Qÿÿ'Å" ÿÿ2Ã@n§BSc“×^w~ bm5h]^-oÈ«°Lÿ{¾6N3…û Ñ2‹+¨üŽ™`©Ô7Ž…—ù åöŒ€ÊP ëS‰A÷ _yiÿÿªp#ÿÿocÏÿÿ ¾[æÿÿ ç¢QO@Ž[Á¹d]nŠj¡^ËqäO~æ;bݨ¢ œÒí Õ¦ $- ¥«Sßâc«Ø‰ÅDªž»× B¨¨ìª „ÿÿ–¦ÿÿŒ‰ÿÿ…’ÿÿ‰ÒXK|˜bDr‹kxb»r‡S]~ÄBÇ+v¤-’²~°Yo½¢LJÇÛM’ÌT„ZÌêºÝË@ïÅÿÿ¼”ÿÿ´‘ÿÿ®÷ÿÿy_røiÆhÃrY¯}´IuÅ4Þ¦˜úºvÈá.)Ü>èõOGëð×ðú©éçÿÿä†ÿÿÝmÿÿÙ~ÿÿoÃf[m¾p aV­gdϤÍxŒWM  b³ˆC(0×Ô!œ"ªú x“©ÿÿPÿÿ’éÿÿšÜÿÿÿÿÿÿÿÿ¨âãËr­¸«(« ‡ûÌ ‰.öu’ ¡WÛ¿Š …Vø„, F¯·¤€ 7Õ84~c :ø™Šv¾ÿÿ¡pòÿÿvBÿÿ}¶ÿÿÿÿûô qßÿ%ÚÃÔ,©®’/qœ- ‹€%Œâyq7—nhÎG§fe»…zÄŠ_Uê§oR[ÖÐ…Z+Yö}AC;ÿÿ%xB—ÿÿÁJ©ÿÿ‚Y—ÿÿ™önBØ,&$¿ÿ;¥u?AiA®zÚAíûk«A~‚X«­1Û2±$½806€jø L56›À34]ÇÊ1©1Öñ ÝÿÿBMÿÿ=±ÿÿ9 Ýÿÿ6Ò"ô¶Ë;r¡@HoŠLltãO b•SR¾V½|=Œ[X[ V&)]%+zâ ­_`,zÅ^K“¤¥^\LÁQ•7Y“ì›ÒFÿÿ!½7¢ÿÿÏ.ÿÿ²'Jÿÿ °ú8ÑšGŠYRt¤UaèY0Q_?”jS®'¯uÆ®2‘{ó#6  chRßu à†––º~•·,Î |EäŸotÿÿSgùÿÿ\ŸÿÿY.ÿÿ”°Dƃ·O»w¼X7dì\TKbïC®mn.Á}Æ4Œ 1]Ù•†$›•J žÎ}†Ÿ°Í`àÿ—ÿÿ‹oÿÿËÿÿ{•ÿÿtMtÁVÙjc]çY˜dwIfoo6Åu‘ꚟɄ¯Ñ!Ϻ¾LÚ €‰Ä ´›Ã—èK»ùÿÿ²ˆÿÿ«ÿÿ¨‚ÿÿqcT|kM]`‚d*Po!>/€K'î”Ì ¦Â¹q äÍ+ÙÃTMãB‡Væê¼ èò¬ãÿÿÚàÿÿÕÓÿÿÓñÿÿhxZ›eÒbÐX­kÞýôGB}6#"2›“§B¨ãµ(ÈX‰×93ä¯ZoìÆ‹¢ò6¾õ¸õ{ópÿÿð$ÿÿí¢ÿÿí“ÿÿc`8a£gbaQv·»=.Œ-æÔ'ÿ™©z¥ ± ÃM^Ñ·7XÝ6\®è¨Šîîø»°óñòšÿÿïóÿÿííÿÿíÚÿÿ`d ÞÊZwm0ãHñzý5“ˆs$÷ k”x\QÙžÈm[¬ ¼: vÌ;Ð×Ò]ÂäuŠWë­¸öñëßñÒÿÿïÎÿÿîUÿÿî5ÿÿ÷>û'ù/ïdãNÕ±zÒ\(‰ËúIÅ^hÀ¨†p¿i¦óÀ$Ë¿`ñ€´€ÿÿ¬Óÿÿ¬¡ÿÿ¬jÿÿ÷¿û«úYð£ä¨Ö» Ó (±ÍQJLÆÁišÂˆiÀí©2ÁZÍÀQòô´ôÿÿ­?ÿÿ­ÿÿ¬Þÿÿø?ü,ûšñÿæרhÔÿ(¶ÎÌKÈGk<Ã¶ŠŠÂs«‘Â¥Ï%Á@ô@µÿÿ­¯ÿÿ­ƒÿÿ­Tÿÿø¸ü¦üñóuç±Ù‚Öx(‡ÐKÃÄXj7¼ˆ¤¹±ª»΄½9ô6¶Zÿÿ®"ÿÿ­øÿÿ­Ìÿÿù$ýþTôïébÑúTÈ!ºì@(®Å`¦€æ£ù¤ª¤úÊÔ¦Ïð ÛÿÿŸÿÿ¥ÿÿ«àÿÿùzýfÿ·ì¿Ò›¼Ç±âÀ¤ï40—ÄU±¿y³ŒÁ ¹ŒTÇ€˜íˆPÿÿƒ„ÿÿŠKÿÿ½ÿÿù²ý˜ëâÐIÇ¹Ø §I ˜P (i‹,S&0ø;J'’w« sìxrÉàoöÄPo`}èËolnÿÿe.ÿÿk±ÿÿqÄÿÿù½â,Î+ò´y žz Œê ‚{(s ÀjÅ@ðW‡D EƒGuáS1—M ¥{ûQ!õ †JRïR  ² R9‚Æ1P|°Ã=gN?ÙÖtHÛÿg ö7”ÿÿ /8ÿÿ L'"ÿÿæ ë-Žõ;­0Dƒl/G›XæJ‘FÛOØ4)Y*ewnæ mBn3Œp„Díq]vÀp‡§un–Ô’køþ¦ZùÿÿQ¥ÿÿNËÿÿ‰ß:d{JC§pÑKÜ]=N>KzSæ9m]5$Hk†ÖyÏ%Å…ÜDaE‘,vƒ’.¨ä‘ا£ÿÿ|ªÿÿsëÿÿqtÿÿwBVm”K2cÌP¯QàVG?È_ˆ+ÆnãÚ Žê½žð%‹«èMb²}{]µÁ­[¶nÞ³#ÿÿ©sÿÿ£5ÿÿ›cÿÿjEI¢dÝP€YVâGŸ`N4Fp]‚8’`¨öºf3¬Ê0XäÓM…:Øý´MÛóåûÙëÿÿÔÿÿÏèÿÿÎEÿÿb#NÐ_ UßPb]ÈÀ©=ˆlᡆ&%€î ê”O¤¦Ï 0·¹"-ÈÝ=ãÕ]a½âš‹èêK¹¬ð@ì’ñ2ÿÿï^ÿÿî_ÿÿîZÿÿ\TaZ/Y‡ª]Hfîƒ2z}ÅRL‡vû’î’P6£_Ó³&ÿÄlCNÐædÛc‰ùç­·þîRéSð`ÿÿïxÿÿî±ÿÿî¬ÿÿX•W|‹Qò^$ – %?ek,+Sw¥æC ƒ~¬Žƒ¯û®d+S¼yDÌffI×7Šlä¶€ìå¦ïŽÿÿïHÿÿïÿÿïÿÿçtêRçÆÛfÎ5¼d·VG°Ö;óªMW¦r§“‘L¬¶·7°ÿáù¬Šÿÿ¥ÿÿ¥eÿÿ¥;ÿÿç¨ê…èªÜ=Ï ½2¸@±|kUHZÆ ÿ/ýPF 0#I( á\ýB‚ ‰‡ï> 4®è .î¶2!ÿÿ+!ÿÿ'Nÿÿ,•\"±…Ö/gzm8›eO:OQ$þVd@uC>Dj0•Me›YÂfŸžrø–yúBq}fp‚÷¡û};ÌÎ{Ÿ÷ßoûÿÿh2ÿÿeWÿÿpþ8SgÒ>þ]°CWJ\H97’P!I]L¤lQ{㋌@+ –êMÖ¢ÇyQ§W¨2©sÕÍ©„ÿÿ™’ÿÿ“_ÿÿØÿÿd†>E^ÐC¹S#I?ÂQk*5_Eƒo¼2ô”|ب:tµU[(ÄT…P˨¯mвÜ"Ò#ÿÿͯÿÿÊKÿÿÈCÿÿ\MC"X×I_HÍP%4c]â¦gnÈò¥6€„õ• ÿ§g-xµÙG™Ç[hÒ½‹Áܘ²kéÇâ¹î—ÿÿïyÿÿïhÿÿïcÿÿVCHÕS Lݵ?*W}À(im×cöu,ƒ‚ A‘»N£È0±ÉJ-ÃËifÏcŒwÙȱÜèà£íÞÿÿïNÿÿï¬ÿÿï§ÿÿQ8K®5J?Pè T …6÷[jr !ÍgzB \rE5j~ʆŽOQœ2q­ÈL ºöhmÌ:Œ:× °±æ[Þrí)ÿÿïzÿÿïíÿÿïèÿÿ×MØçÕÊÈ-¸$§ œšM–.ò:Fõq_¥Ú|¾˜Kk£¿F¡–5ÒB#£ÜÑÿVúÿÿàÿÿÀÿÿ×'غÖGÈŸ¸§Zœ  •ü/.Gq~`g}¶˜ e¤ÉB¡»3ÒÝ"£îÐÿižÿÿðÿÿÓÿÿÖíØxÖÈɸ槵œšž•ë/$Gï|a+=&~«˜ä_¥¯>¡Ë2Ó#!£óËÿ ‹ž ÿÿ÷ÿÿÞÿÿÖšØ×IÉ–¹W¨!œŠö•Ð/ŽbGƈ¢_ʉg|‘£H›½Ñ„£g§ýþržÿÿõÿÿßÿÿÖ)ס×ÑÊ ¹Í¤d’燇Â)/|_@£u²X#vvyùœÁxÊ[…Ñù8‡ÿÿÖÿÿš‹ÿÿՔרOÂjªC”+p_ué i½7ÊcdQHbßs¥bÌ›’cfÇeõõBevÿÿngÿÿwÚÿÿÔÕÕaÆ4¬Ô–v‚>nÎa¥PýU±>-'pPLòMeuÂJÙžÙHÁÇYHGñ+D¿ÿÿL>ÿÿTÿÿÏ>¼1­«˜%«‚5 Àot3\b I’,>"Ì7§GKŽƒ1Ífv-¹­Ì²+~¢ÃÃm*»xè”t'ÿÿ,úÿÿ3Yÿÿµæ¥ ™LƒFgn)zY±tEoƒ0#ø™Ð¢bݬ4B¥/îðn³;.u˜ … j¥½Þ ‚â ¨ Öÿÿ gÿÿo®ÿÿ ÌŸQXŒå„Ê p^$œZ¸&F '~15)ȯ.Š3⨠59ï2ng 08’×.ºØ+aà]%Ïÿÿ¦ÿÿžÿÿŠ÷e~ "Øt²+N_A,èJ{.ñ5²1Ø ¹7Ö@ìH·zKœ:“KWh{Iz”gG›¼ÿEÄãâ= ÿÿ5Êÿÿ2ÿÿyz$of,Þf‘2YQ*4;ï7'e=ÉGÎR³Ü\õe’CmhÖn"i™i™ÂÖiéÊ]ÿÿUxÿÿR(ÿÿkƒ-bcQ3X®7ëCÛ:w/-A¸L[XúhNsë.|úM˜ŠKuÛ"ž»’+É3“ÙòpŽ4ÿÿˆ…ÿÿ…3ÿÿ_ó2òYÒ8ÏM<ƒ7ìB´!ÑN\“mJ6|ñ$?y>›ô[ô­]ƒ\¶§ÅÄTÔ"ÉáÿÂÇžÿÿÄ“ÿÿ¹tÿÿW8ŽRÚ=&A@Bw,"M6¸Ž\f¦}nK,‚ñ"/‘y5¤¤Nð² k£ÅGŒ}ѯyÛÞÔîë}ÿÿï…ÿÿðuÿÿðpÿÿP(=YL@{Wb7AI%)¯ºX0Øæ]cÖ–Ðpº }˜#{Žm7;›•NŒ®íl«»ö‰:Îæ®æÚ8Óòêüÿÿï«ÿÿð¢ÿÿðÿÿIÄ?Ú‡ÃBCó ” C/[L¹ ³[WÀbb ?tnº¦zÕ%ä‹l9˜HO߬lC¹ ‰e̾­ýØÒ¢êcÿÿï‚ÿÿðÍÿÿðÉÿÿÇÃÈGÄ%³£Œ‚Ó ˆ|!ßwÂ7v^u·oNÈy°‰j¸÷ƒ£è~°¾’šäìE’gÿÿ7’cÿÿ<’XÿÿFÇ9ǰÄF³’¢ñH‚! {q!¤viø7wyu)˜NZÜyQk/‚­‘]ƒŒ¥·¾’'îë“K’%ÿÿz’#ÿÿ|’ÿÿƒÆ’ÆûÄo³’¢ÉT Vz©!OuŸ57k–t‚ÈN”óxضk•‚Q¯‘¢†Œý´‘Œõê…N’ÿá±’úÿâ·’ÿß¡ÅÊÆ$Ä”³¢ž¿€igy¾ Ütg7 «p2~M ¶rwæiB~z@_ŽëDŽт»JLŽî‘芒Êÿmè’$Éÿtç’%ÉÿuçÄÝÅ&Ä®³d¢nŒ{w>;m— d*ú0tb_b‡E›¦`À)b‚dĉ'iµ¶}påZxþó³ þŒ[Öþj3ÃÇÃüÄU­”þ~ há]DÖRêm(P#MÜ=¦NÇ_@Nó‰!MÛ´²P0ã‰Tôÿÿ^$ÿÿi5ÿÛ¾c³Q››…7oà[_JÅ "@\y !(eßî yë"4m³]¸Ćo­$ßÑš³ôëúâ:ú¤ø ÏxÙ¸o¿YÛ C5!ž-ø#1&µ-0Úˆ0€<).‹bä,ú‰y,­È*ñÐó)¶ôŠ' þf#·þfsáŸj– b@%%KT'Q5t)>­,äa5íz•Ñn;}<耟}iè®}™úèàš³r² °k4Šùwae1^c(;oZŸ']øX™M;[ìYV¿ëbÅô{%Úk#«¤´`u“ÏoØ{½0æñ´{ñ*ç'±|($ça®²±+¯ûœ”Š(t=^«RóY ¼L[ÖÉJG4œ©L¯AS#ÅPFz¢T ¦#ZiÑ)aÿéÁj è#‘sËßæÔ¸°Š¯‰®¢—$~ÀiSúCüš<Þ6„:±5.OM<RE;‘}·;8©s<²Ó°B?ðOJxîŽTìc®Ñª³¡ø‹sƒ]!HŽ4£+Ï)C)Ï' SV$yË!ü¬Ü DÔW!±óª)Ló$1íòE¦3›“g}¦g¥QÕ{¯R€dm§œ‰Þµ±¤µÍÅÉœæö¥òÿÿôáÿÿôáÿÿL˜ä0|È_«÷BŽÚ%q½TŸë6Íc¯úEÛ & q ½  S ž è 3 ~ É  _ © ô ? Š Ôi´þI“Þ(s½Rœæ0zÅY£í7Ë_¨ò<†Ðc­ö@‰Óf¯ùBŒÕh±úCŒÕh±úCŒÕf¯ø A Š Ò!!d!¬!õ">"†"Ï##`#¨#ñ$9$$Ê%%Z%£%ë&3&{&Ã' 'T'œ'ä(,(t(¼))L)“)Û*#*k*³*ú+B+Š+Ñ,,a,¨,ð-7--Æ..U.œ.ä/+/r/º00H00Ö11e1¬1ó2:22È33V33ä4+4q4¸4ÿ5F55Ó66a6¨6î757|7Â8 8O8–8Ü9#9i9°9ö:=:ƒ:Ê;;V;;ã<)>[>¡>ç?-?s?¹?ÿ@E@‹@ÑAA]A£AéB.BtBºCCFC‹CÑDD\D¢DèE-EsE¹EþFDF‰FÏGGZGŸGåH*HpHµHúI@I…IÊJJTJšJßK$KiK®KóL8L}LÁMMKMMÕNN^N£NçO,OpOµOùP>P‚PÆQ QOQ“QØRR`R¤RèS,SpS´SøTÊ{Ê·ÊôË0ËlË©ËåÌ"Ì^ÌšÌ×ÍÍPÍŒÍÉÎÎBÎ~κÎ÷Ï3ÏpϬÏéÐ%ÐbОÐÛÑÑTÑÑÍÒ ÒFÒƒÒ¿ÒüÓ8ÓuÓ±ÓîÔ*ÔgÔ¤ÔàÕÕYÕ–ÕÒÖÖLÖˆÖÅ××>×{×·×ôØ1ØmØªØæÙ#Ù`ÙÙÙÚÚSÚÚÌÛ ÛFÛ‚Û¿ÛüÜ9ÜvܲÜïÝ,ÝiݦÝãÞÞ\Þ™ÞÖßßPßßÊààDàà¾àûá8áuá²áïâ,âiâ¦âãã ã]ãšã×ääQäŽäËååFåƒåÀåýæ:æwæµæòç/çlç©ççè$èaèžèÜééVé“éÑêêKê‰êÆëëAë~ë»ëùì6ìtì±ìîí,íií§íäî"î_îîÚïïUï“ïÐððKð‰ðÆññBññ½ñúò8òvò³òñó.ólóªóçô%ôcô¡ôÞõõZõ—õÕööQööÌ÷ ÷H÷†÷Äøø?ø}ø»øùù7ùuù²ùðú.úlúªúèû&ûdû¢ûàüü\üšüØýýTý’ýÐþþLþŠþÈÿÿEÿƒÿÁÿÿMšç4Îg´Mšç3€Ìe±ýI•á-yÅ]©õ @ Œ Ø # o º  Q œ è 3 ~ É  _ « ö@‹Ö!l¶L–á+vÀ UŸé4~È\¦ð:ƒÍaªô=‡Ðc­ö?ˆÒd­ö?ˆÑb«ô<…Î_§ï8€È  Y ¡ é!1!y!Á" "Q"™"á#(#p#¸#ÿ$G$$Ö%%e%¬%ô&;&‚&É''X'Ÿ'æ(-(t(»))I))Ö**d*ª*ñ+7+~+Ä, ,Q,˜,Þ-$-j-±-÷.=.ƒ.É//U/›/á0'0l0²0ø1=1ƒ1È22S2™2Þ3#3h3­3ò474|4Á55K55Ô66]6¡6æ7*7n7²7ö8:8~8Â99J9Ž9Ñ::Y:œ:à;#;f;ª;í<0>I>‹>Î??T?–?Ù@@^@ @ãA%AgAªAìB.BpB²BôC6CxCºCüD>D€DÁEEEE†EÈF FKFŒFÍGGPG‘GÒHHUH–H×IIYIšIÛJJ]JJÞKK`K KáL"LbL£LãM$MdM¥MåN&NfN¦NçO'OgO§OçP(PhP¨PèQ(QhQ¨QçR'RgR§RçS'SfS¦SæT%TeT¤TäU#UcU¢UâV!V`V VßWW]WœWÜXXZX™XØYYVY•YÔZZQZZÏ[ [L[‹[É\\G\…\Ä]]A]]¾]ü^:^x^·^õ_3_q_¯_í`+`i`§`åa#aaažaÜbbWb•bÒccMc‹cÈddCd€d½dûe8eue²eïf,fif¦fãg g]g™gÖhhOhŒhÉiiBi~i»i÷j3jpj¬jèk$kakkÙllQllÉmmAm|m¸môn0nkn§nâooYo•oÐp pFp‚p½pøq3qnq©qärrYr”rÏs sDss¹sôt.tit£tÝuuRuŒuÆvv:vtv®vèw"w[w•wÏx xBx|xµxïy(yay›yÔz zGz€z¹zò{+{d{{Ö||G|€|¹|ñ}*}c}›}Ô~ ~D~}~µ~í%^–΀€>€v€®€æUÅü‚4‚k‚£‚ÚƒƒIƒƒ¸ƒð„'„_„–„Î……=…t…¬…ㆆR†Š†Á†ø‡0‡g‡Ÿ‡ÖˆˆEˆ}ˆ´ˆì‰#‰[‰’‰ÉŠŠ8ŠpЧŠß‹‹N‹…‹¼‹ôŒ+ŒcŒšŒÒ @x¯çŽŽUŽŽÄŽü3j¢ÙH·î‘%‘]‘”‘Ì’’:’r’©’à““O“†“¾“õ”-”d”›”Ó• •A•y•°•ç––V––Å–ü—3—j—¢—Ù˜˜H˜˜¶˜î™%™\™“™Ëšš9šqš¨šß››N›…›½›ôœ+œcœšœÒ @x¯çžžVžžÄžüŸ3ŸkŸ¢ŸÚ  I € ¸ ð¡'¡_¡–¡Î¢¢=¢u¢¬¢ä££S£‹£Â£ú¤2¤i¤¡¤Ù¥¥H¥€¥¸¥ï¦'¦_¦—¦Î§§>§v§­§å¨¨U¨¨Å¨ü©4©l©¤©ÜªªMª…ª½ªõ«.«f«Ÿ«×¬¬I¬¬º¬ó­,­d­­Ö®®I®‚®»®ô¯-¯g¯ ¯Ú°°M°†°À°ú±4±n±§±á²²U²²Ê³³>³x³³³í´(´b´´ØµµMµˆµÃµþ¶9¶t¶¯¶ê·%·`·œ·×¸¸N¸‰¸Å¹¹<¹x¹´¹ðº,ºhº¤ºà»»X»”»Ñ¼ ¼I¼†¼Â¼ÿ½<½y½µ½ò¾/¾l¾©¾ç¿$¿a¿Ÿ¿ÜÀÀWÀ•ÀÓÁÁNÁŒÁÊÂÂFÂ…ÂÃÃÃ@Ã~ýÃüÄ:ÄyĸÄ÷Å6ÅuÅ´ÅóÆ3ÆrƲÆñÇ1ÇpǰÇðÈ0ÈpȰÈðÉ0ÉpɱÉñÊ2ÊrʳÊóË4Ëu˵ËöÌ7Ìx̹ÌúÍ<Í};ÎÎA΃ÎÄÏÏHωÏËÐ ÐOБÐÓÑÑXÑšÑÜÒÒaÒ£ÒæÓ(ÓkÓ®ÓðÔ3ÔvÔ¹ÔüÕ>ÕÕÄÖÖJÖŽÖÑ××W×›×ÞØ!ØeبØìÙ/ÙsÙ·ÙúÚ>Ú‚ÚÆÛ ÛNÛ’ÛÖÜÜ^ܢܿÝ+ÝoÝ³ÝøÞ<ÞÞÅß ßOߓߨààbà§àëá0áuá»ââEâŠâÏããZãŸãåä*äpäµäûåAå‡åÌææXæžæäç*çpç¶çüèCè‰èÏéé\é¢ééê/êvê½ëëJë‘ëØììfì­ìôí;í‚íÉîîXîŸîæï.ïuï½ððLð”ðÜñ#ñkñ³ñûòCò‹òÓóódó¬óôô<ô…ôÍõõ^õ§õðö8ööÊ÷÷\÷¥÷îø7ø€øÉùù[ù¥ùîú8úúËûû^û§ûñü;ü…üÏýýcý­ý÷þAþ‹þÕÿ ÿjÿµÿÿK•à+uÀ UŸê4~É]§ñ;…Ïc­÷@ŠÔg±ú C Ö i ² û D Ž × i ² ú C Œ Õf¯ø@‰Ñb«ó;ƒÌ\¤ì4|Ä T›ã+sºI‘Ø g¯ö=„ÌZ¡è/v½J‘Øe¬ó9€Æ S™à&l²ù ? … Ë!!W!!ã"("n"´"ú#?#…#Ë$$V$›$á%&%l%±%ö&<&&Æ' 'P'•'Ú((d(©(î)3)x)½**F*‹*Ï++X++á,&,j,¯,ó-7-{-À..H.Œ.Ð//X/œ/à0$0g0«0ï121v1º1ý2@2„2Ç3 3N3‘3Ô44Z44à5#5f5¨5ë6.6p6³6ö787z7½7ÿ8A8„8Æ99J9Œ9Î::R:”:Ö;;Y;›;Ü<<_<¡<â=$=e=¦=è>)>j>«>ì?-?n?¯?ð@1@r@³@óA4AuAµAöB6BwB·B÷C8CxC¸CùD9DyD¹DùE9EyE¹EøF8FxF¸FøG7GwG¶GöH6HuH´HôI3IsI²IñJ0JpJ¯JîK-KlK«KêL)LhL§LæM$McM¢MáNN^NœNÛOOXO–OÕPPRPPÎQ QKQ‰QÇRRCRR¿RýS;SyS·SôT2TpT®TëU)UfU¤UâVV\VšV×WWRWWÌX XGX„XÁXþY;YxYµYòZ/ZlZ©Zæ["[_[œ[Ù\\R\\Ì]]E]]¾]ú^7^t^°^ì_)_e_¢_Þ``W`“`Ïa aHa„aÀaýb9bub±bíc)cec¡cÝddUd‘dÍe eEee½eøf4fpf¬fèg#g_g›gÖhhNh‰hÅii,>u>¿? ?R?›?å@.@x@ÁA ATAAæB/BxBÁC CSCœCåD.DwDÀE ERE›EãF,FuF½GGOG—GàH(HqH¹IIJI’IÚJ#JkJ³JûKDKŒKÔLLdL¬LôM×sש×ÞØØIØ~سØèÙÙSوٽÙòÚ'Ú\Ú‘ÚÆÚûÛ0ÛeÛšÛÎÜÜ8ÜmÜ¢ÜÖÝ Ý@ÝtÝ©ÝÞÞÞGÞ{Þ°ÞäßßM߂߶ßëààSàˆà¼àðá%áYááÂáöâ*â^â’âÇâûã/ãcã—ãËãÿä3ägä›äÏåå7åkåŸåÓææ;æoæ¢æÖç ç>çrç¥çÙè è@ètè¨èÛééCévéªéÝêêDêxê«êßëëFëyë­ëàììGìzì­ìáííGízí®íáîîGîzî­îáïïGïzï­ïàððFðyð¬ðßññEñxñªñÝòòCòvò©òÛóóAótó¦óÙô ô>ôqô¤ôÖõ õ;õnõ õÓöö8öjööÏ÷÷4÷g÷™÷Ë÷þø0øbø•øÇøùù+ù^ùùÂùôú&úXúŠú½úïû!ûSû…û·ûéüüMüü±üãýýFýxýªýÜþþ@þqþ£þÕÿÿ8ÿjÿœÿÍÿÿmft2@ÁB‚ÂCƒÃD„ÅE†ÆG‡ÇHˆÈ I‰Ê J‹Ë L Œ Ì M Í  N Ž Ï  O Ð  Q ‘ ÑR’ÒS“ÔT•ÕV–ÖW—×X˜ÙYšÚ[›Û\œÜ]Þ^Ÿß_ à a¡á"b¢ã#c¤ä$d¥å%f¦æ'g§è ( h © é!)!i!ª!ê"*"k"«"ë#,#l#¬#í$-$m$®$î%.%n%¯%ï&/&p&°&ð'1'q'±'ò(2(r(³(ó)3)s)´)ô*4*u*µ*õ+6+v+¶+÷,7,w,¸,ø-8-x-¹-ù.9.z.º.ú/;/{/»/ü0<0|0¼0ý1=1}1¾1þ2>22¿2ÿ3@3€3À44A44Á55B5‚5Ã66C6„6Ä77E7…7Å88F8†8Æ99G9‡9È::H:‰:É; ;J;Š;Ê< >M>Ž>Î??O??Ï@@P@@ÐAAQA‘AÒBBRB“BÓCCTC”CÔDDUD•DÕEEVE–E×FFWF˜FØGGYG™GÙHHZHšHÚII[I›IÜJJ\JJÝKK^KžKÞLL_LŸLßM M`M MáN!NaN¢NâO"OcO£OãP#PdP¤PäQ%QeQ¥QæR&RfR§RçS'ShS¨SèT(TiT©TéU*UjUªUëV+VkV¬VìW,WmW­WíX-XnX®XîY/YoY¯YðZ0ZpZ±Zñ[1[r[²[ò\2\s\³\ó]4]t]´]õ^5^u^¶^ö_6_v_·_÷`7`x`¸`øa9aya¹aúb:bzb»bûc;c{c¼cüde~e¾eÿf?ffÀgg@g€gÁhhAh‚hÂiiCiƒiÃjjDj„jÅkkEk…kÆllFl‡lÇmmHmˆmÈn nIn‰nÊo oJoŠoËp pKpŒpÌq qMqqÍrrNrŽrÏssOssÐttPt‘tÑuuRu’uÒvvSv“vÓwwTw”wÕxxUx–xÖyyWy—y×zzXz˜zØ{{Y{™{Ú||Z|›|Û}}\}œ}Ü~~]~~Ý^ž߀€_€ €à a¡á‚"‚b‚¢‚âƒ#ƒcƒ£ƒä„$„d„¥„å…%…f…¦…æ†'†g†§†ç‡(‡h‡¨‡éˆ)ˆiˆªˆê‰*‰k‰«‰ëŠ,ŠlЬŠì‹-‹m‹­‹îŒ.ŒnŒ¯Œï/p°ðŽ0ŽqޱŽñ2r²ó3s´ô‘4‘u‘µ‘õ’5’v’¶’ö“7“w“·“ø”8”x”¹”ù•9•z•º•ú–:–{–»–û—<—|—¼—ý˜=˜}˜¾˜þ™>™™¿™ÿš?š€šÀ››A››ÁœœBœ‚œÃC„ÄžžDž…žÅŸŸFŸ†ŸÆ  G ‡ È¡¡H¡‰¡É¢ ¢I¢Š¢Ê£ £K£‹£Ë¤ ¤L¤Œ¤Í¥ ¥M¥¥Î¦¦N¦¦Ï§§P§§Ð¨¨Q¨‘¨Ò©©R©’©ÓªªSª”ªÔ««U«•«Õ¬¬V¬–¬×­­W­—­Ø®®X®™®Ù¯¯Z¯š¯Ú°°[°›°Ü±±\±œ±Ý²²]²ž²Þ³³_³Ÿ³ß´ ´`´ ´áµ!µaµ¡µâ¶"¶b¶£¶ã·#·d·¤·ä¸%¸e¸¥¸æ¹&¹f¹¦¹çº'ºgº¨ºè»(»i»©»é¼*¼j¼ª¼ê½+½k½«½ì¾,¾l¾­¾í¿-¿n¿®¿îÀ/ÀoÀ¯ÀïÁ0ÁpÁ°ÁñÂ1Âq²ÂòÃ2ÃsóÃóÄ4ÄtÄ´ÄôÅ5ÅuŵÅöÆ6ÆvÆ·Æ÷Ç7ÇxÇ¸ÇøÈ9ÈyȹÈùÉ:ÉzɺÉûÊ;Ê{ʼÊüË<Ë}˽ËýÌ>Ì~̾ÌþÍ?ÍÍ¿ÎÎ@΀ÎÁÏÏAÏ‚ÏÂÐÐCЃÐÃÑÑDÑ„ÑÄÒÒEÒ…ÒÆÓÓFÓ‡ÓÇÔÔGÔˆÔÈÕÕIÕ‰ÕÉÖ ÖJÖŠÖË× ×K׌×ÌØ ØLØØÍÙ ÙNÙŽÙÎÚÚOÚÚÐÛÛPÛ‘ÛÑÜÜQÜ’ÜÒÝÝSÝ“ÝÓÞÞTÞ”ÞÕßßUß–ßÖààVà—à×ááXá˜áØââYâ™âÚããZã›ãÛää[äœäÜåå]ååÝææ^æžæßçç_ç çàè è`è¡èáé!ébé¢éâê#êcê£êäë$ëdë¤ëåì%ìeì¦ìæí&ígí§íçî(îhî¨îéï)ïiï©ïêð*ðjð«ðëñ+ñlñ¬ñìò-òmò­òîó.ónó®óïô/ôoô°ôðõ0õqõ±õñö2örö²öó÷3÷s÷³÷ôø4øtøµøõù5ùvù¶ùöú7úwú·úøû8ûxû¸ûùü9üyüºüúý:ý{ý»ýûþ<þ|þ¼þýÿ=ÿ}ÿ½ÿþÿÿÿÿÿÿÿÿAÂC„ÄE†ÆHˆÉ JŠË LÍNÐQ‘ÒS“Ô  U – Ö  W ˜ Ù  Z š Û  \ œ Ý  ^ Ÿ ß `¡â"c£ä$e¦æ'g¨è)iªë+l¬í-n¯ï0p±ñ2s³ô4uµö6w¸ø9yºú;|¼ý=~¾ÿ@€ÁB‚ÃD…ÅF‡Ç  I ‰ Ê! !K!‹!Ì" "M"Ž"Î##O##Ð$$R$’$Ó%%T%”%Õ&&V&—&×''X'™'Ú(([(›(Ü))]))Þ**_* *à+!+a+¢+ã,#,d,¤,å-%-f-§-ç.(.h.©.é/*/j/«/ì0,0m0­0î1.1o1°1ð212q2²2ò333t3´3õ454v4¶4÷575x5¹5ù6:6z6»6û7<7}7½7þ8>88¿99A99Â::C:ƒ:Ä;;E;†;Æ< >L>Œ>Í??N??Ï@@P@‘@ÑAASA“AÔBBUB•BÖCCWC˜CØDDYDšDÚEE\EœEÝFF^FžFßG G`G¡GáH"HbH£HäI$IeI¥IæJ&JgJ§JèK)KiKªKêL+LkL¬LíM-MnM®MïN/NpN±NñO2OrO³OóP4PtPµPöQ6QwQ·QøR8RyRºRúS;S{S¼SüT=T~T¾TÿU?U€UÀVVAV‚VÃWWDW„WÅXXFX‡XÇYYHY‰YÉZ ZKZ‹ZÌ[ [M[[Î\\O\\Ð]]Q]’]Ò^^T^”^Õ__V_–_×``X`™`ÙaaZa›aÛbb]bbÞcc_cŸcàd!dad¢dâe#ece¤eåf%fff¦fçg'ghg¨géh*hjh«hëi,ili­iîj.joj¯jðk0kqk²kòl3lsl´lôm5mum¶m÷n7nxn¸nùo9ozo»oûpqq¿rr@rrÁssBsƒsÄttEt…tÆuuGuˆuÈv vIvŠvÊw wLwŒwÍx xNxŽxÏyyPy‘yÑzzRz“zÓ{{U{•{Ö||W|—|Ø}}Y}š}Ú~~[~œ~Ü^ž߀€`€ €á"b£ã‚$‚d‚¥‚åƒ&ƒgƒ§ƒè„(„i„©„ê…+…k…¬…ì†-†m†®†ï‡/‡p‡°‡ñˆ1ˆrˆ²ˆó‰4‰t‰µ‰õŠ6ŠvŠ·Šø‹8‹y‹¹‹úŒ:Œ{Œ¼Œü=}¾þŽ?ŽŽÀA‚ÂC„Å‘‘F‘†‘Ç’’H’‰’É“ “J“‹“Ë” ”L””Ε•O••Ж–Q–’–Ò——S—”—Ô˜˜V˜–˜×™™X™˜™ÙššZš›šÛ››\››Ýœœ_œŸœà a¡âž#žcž¤žäŸ%ŸeŸ¦Ÿæ ' h ¨ é¡)¡j¡ª¡ë¢,¢l¢­¢í£.£n£¯£ð¤0¤q¤±¤ò¥2¥s¥³¥ô¦5¦u¦¶¦ö§7§w§¸§ù¨9¨z¨º¨û©;©|©½©ýª>ª~ª¿ªÿ«@«€«Á¬¬B¬ƒ¬Ã­­D­…­Æ®®G®‡®È¯¯I¯Š¯Ê° °K°Œ°Ì± ±M±Ž±Ï²²P²²Ñ³³R³“³Ó´´T´•´ÕµµVµ—µØ¶¶Y¶™¶Ú··[·œ·Ü¸¸]¸ž¸Þ¹¹`¹ ¹áº!ºbº¢ºã»#»d»¥»å¼&¼f¼§¼ç½(½i½©½ê¾*¾k¾«¾ì¿-¿m¿®¿îÀ/ÀoÀ°ÀðÁ1ÁrÁ²ÁóÂ3Ât´ÂõÃ6Ãv÷Ã÷Ä8ÄxĹÄúÅ:Å{Å»ÅüÆ<Æ}ƽÆþÇ?ÇÇÀÈÈAÈÈÂÉÉCÉ„ÉÄÊÊEʆÊÇËËHˈËÉÌ ÌJÌŠÌËÍ ÍLÍÍÍÎÎNÎÎÐÏÏQÏ‘ÏÒÐÐSДÐÔÑÑUÑ–ÑÖÒÒWÒ˜ÒÙÓÓZÓšÓÛÔÔ\ÔÔÝÕÕ^ÕŸÕßÖ ÖaÖ¡Öâ×"×c×£×äØ$ØeØ¦ØæÙ'ÙgÙ¨ÙèÚ)ÚjÚªÚëÛ+ÛlÛ¬ÛíÜ.ÜnܯÜïÝ0ÝpݱÝñÞ2ÞsÞ³Þôß4ßußµßöà7àwà¸àøá9áyáºáûâ;â|â¼âýã=ã~ã¾ãÿä@ä€äÁååBå‚åÃææDæ…æÅççFç‡çÈèèIè‰èÊé éKé‹éÌê êMêŽêÎëëOëëÑììRì’ìÓííTí”íÕîîVî—î×ïïXï™ïÚðð[ð›ðÜññ]ñžñÞòò_ò òàó!óaó¢óãô#ôdô¤ôåõ%õfõ§õçö(öhö©öé÷*÷k÷«÷ìø,ømø­øîù.ùoù°ùðú1úqú²úòû3ûtû´ûõü5üvü¶ü÷ý8ýxý¹ýùþ:þzþ»þûÿ<ÿ}ÿ½ÿþÿÿÿÿÿÿÿÿAÂC„ÄE†ÆHˆÉ JŠË LÍNÐQ‘ÒS“Ô  U – Ö  W ˜ Ù  Z š Û  \ œ Ý  ^ Ÿ ß `¡â"c£ä$e¦æ'g¨è)iªë+l¬í-n¯ï0p±ñ2s³ô4uµö6w¸ø9yºú;|¼ý=~¾ÿ@€ÁB‚ÃD…ÅF‡Ç  I ‰ Ê! !K!‹!Ì" "M"Ž"Î##O##Ð$$R$’$Ó%%T%”%Õ&&V&—&×''X'™'Ú(([(›(Ü))]))Þ**_* *à+!+a+¢+ã,#,d,¤,å-%-f-§-ç.(.h.©.é/*/j/«/ì0,0m0­0î1.1o1°1ð212q2²2ò333t3´3õ454v4¶4÷575x5¹5ù6:6z6»6û7<7}7½7þ8>88¿99A99Â::C:ƒ:Ä;;E;†;Æ< >L>Œ>Í??N??Ï@@P@‘@ÑAASA“AÔBBUB•BÖCCWC˜CØDDYDšDÚEE\EœEÝFF^FžFßG G`G¡GáH"HbH£HäI$IeI¥IæJ&JgJ§JèK)KiKªKêL+LkL¬LíM-MnM®MïN/NpN±NñO2OrO³OóP4PtPµPöQ6QwQ·QøR8RyRºRúS;S{S¼SüT=T~T¾TÿU?U€UÀVVAV‚VÃWWDW„WÅXXFX‡XÇYYHY‰YÉZ ZKZ‹ZÌ[ [M[[Î\\O\\Ð]]Q]’]Ò^^T^”^Õ__V_–_×``X`™`ÙaaZa›aÛbb]bbÞcc_cŸcàd!dad¢dâe#ece¤eåf%fff¦fçg'ghg¨géh*hjh«hëi,ili­iîj.joj¯jðk0kqk²kòl3lsl´lôm5mum¶m÷n7nxn¸nùo9ozo»oûpqq¿rr@rrÁssBsƒsÄttEt…tÆuuGuˆuÈv vIvŠvÊw wLwŒwÍx xNxŽxÏyyPy‘yÑzzRz“zÓ{{U{•{Ö||W|—|Ø}}Y}š}Ú~~[~œ~Ü^ž߀€`€ €á"b£ã‚$‚d‚¥‚åƒ&ƒgƒ§ƒè„(„i„©„ê…+…k…¬…ì†-†m†®†ï‡/‡p‡°‡ñˆ1ˆrˆ²ˆó‰4‰t‰µ‰õŠ6ŠvŠ·Šø‹8‹y‹¹‹úŒ:Œ{Œ¼Œü=}¾þŽ?ŽŽÀA‚ÂC„Å‘‘F‘†‘Ç’’H’‰’É“ “J“‹“Ë” ”L””Ε•O••Ж–Q–’–Ò——S—”—Ô˜˜V˜–˜×™™X™˜™ÙššZš›šÛ››\››Ýœœ_œŸœà a¡âž#žcž¤žäŸ%ŸeŸ¦Ÿæ ' h ¨ é¡)¡j¡ª¡ë¢,¢l¢­¢í£.£n£¯£ð¤0¤q¤±¤ò¥2¥s¥³¥ô¦5¦u¦¶¦ö§7§w§¸§ù¨9¨z¨º¨û©;©|©½©ýª>ª~ª¿ªÿ«@«€«Á¬¬B¬ƒ¬Ã­­D­…­Æ®®G®‡®È¯¯I¯Š¯Ê° °K°Œ°Ì± ±M±Ž±Ï²²P²²Ñ³³R³“³Ó´´T´•´ÕµµVµ—µØ¶¶Y¶™¶Ú··[·œ·Ü¸¸]¸ž¸Þ¹¹`¹ ¹áº!ºbº¢ºã»#»d»¥»å¼&¼f¼§¼ç½(½i½©½ê¾*¾k¾«¾ì¿-¿m¿®¿îÀ/ÀoÀ°ÀðÁ1ÁrÁ²ÁóÂ3Ât´ÂõÃ6Ãv÷Ã÷Ä8ÄxĹÄúÅ:Å{Å»ÅüÆ<Æ}ƽÆþÇ?ÇÇÀÈÈAÈÈÂÉÉCÉ„ÉÄÊÊEʆÊÇËËHˈËÉÌ ÌJÌŠÌËÍ ÍLÍÍÍÎÎNÎÎÐÏÏQÏ‘ÏÒÐÐSДÐÔÑÑUÑ–ÑÖÒÒWÒ˜ÒÙÓÓZÓšÓÛÔÔ\ÔÔÝÕÕ^ÕŸÕßÖ ÖaÖ¡Öâ×"×c×£×äØ$ØeØ¦ØæÙ'ÙgÙ¨ÙèÚ)ÚjÚªÚëÛ+ÛlÛ¬ÛíÜ.ÜnܯÜïÝ0ÝpݱÝñÞ2ÞsÞ³Þôß4ßußµßöà7àwà¸àøá9áyáºáûâ;â|â¼âýã=ã~ã¾ãÿä@ä€äÁååBå‚åÃææDæ…æÅççFç‡çÈèèIè‰èÊé éKé‹éÌê êMêŽêÎëëOëëÑììRì’ìÓííTí”íÕîîVî—î×ïïXï™ïÚðð[ð›ðÜññ]ñžñÞòò_ò òàó!óaó¢óãô#ôdô¤ôåõ%õfõ§õçö(öhö©öé÷*÷k÷«÷ìø,ømø­øîù.ùoù°ùðú1úqú²úòû3ûtû´ûõü5üvü¶ü÷ý8ýxý¹ýùþ:þzþ»þûÿ<ÿ}ÿ½ÿþÿÿÿÿÿÿÿÿÿÿÿÿ[…ÿþ”ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ8gnr›ÿÿÜ`ÿÿňL΂ÿÿÅ‚ÿÿÁîÿÿÀ#ÿÿ¿¸ÿÿ¹ôÿÿ°<ÿÿÿÿéÒ`pÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÞÿÿžUÿÿ¿ÿçѤÿÿÉ]ÿÿÅ'ÿÿÂÿÿÀBÿÿ¿Óÿÿ¹Jÿÿÿÿÿÿÿÿ`Nóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ=Ù5¡ÿÿ·›ýà ŸÿÿÿÕ‹ÿÿÍÁÿÿÉTÿÿÅ®ÿÿ ÿÿÀ{ÿÿ¿ñÿÿÿÿÿÿ±:B|ÿÿB|ÿÿÿÿÿÿÿÿÿÿÿÿÍÈÿÿ ÿÿÚ9Ÿÿúb ÿÿÿÿ͇£IÚ.ÿÿÒZÿÿÎ1ÿÿÊyÿÿÆâÿÿÆÿÿÀËÿÿŸÿŸÿ€ÿÿÙ“ÿÿÿÿÿÿÿÿÿÿqÀÿÿÿÿ¿ÿë«¥æŸÿ`ÿÿÿÿ¢ß0ÿÿÛ€eDß‹ÿÿÖéÿÿÓGÿÿÏôÿÿÌŽÿÿÈîÿÿÅÿÿÿÿ`ïŠÿÿÿÿÿÿÿÿÿÿÿÿöúÿÿmGØÿÿ£Ê«`ÿÿðA–åËÿÿÛWÿÿØNÿÿÕ–ÿÿÒÏÿÿÏÊÿÿÌ0ÿÿá,Kál\ÿÿÿÿÿÿÿÿŸÿŸÿ€ÿÿÿÿ_+ª¢ÿÿ Ì|ÿÿ`ÄÖÿÿ¢¢ÉNÿÿÿÿ¿ÿ·íÿÿ@ÑÿÿÝ"ÿÿÛ!ÿÿÙ3ÿÿ×ÿÿÔºÿÿÿÿR}ü6ÿÿÿÿ¿ÿŸÿ`ÿÿÿÿ€môÿÿ7Ž«~ÿÿÜ.ÿÿ2âíÿÿ¤÷üŸÿÿÿì{жÿÿ@¶ìÿÿ¿ÿÿÿÉ9ÿÿѲÿÿÙ.ÿÿÝšÿÿ%¾ÿÿE¾ÿÿÿÿÿÿÿÿ“G%mÿÿl¡€ÿÿ”°›Hÿÿ ¥ÅÁïr–k Ÿí«˜“1lHÿ @ÿÿûHB¸ÿÿwQ€ÿÿ;»– ÿÿ%]œIÿÿ Qÿÿ šÉÿÿ бÿÿ1½¿ÿÿÿpÿÿ»øŸÿ`ÿÿÿÿm`ÿÿ¶meóÿÿî,ì“ýˆ§æ¿ØÿÿÐN>ÿÿööÒ ÿÿßÿPÿÿŽr ÿÿz%ÿÿv˜ ÿÿy®ÿÿ„ËÄÿÿ€O¤ÿÿh"ÿÿÿÿÿÿÿÿ¤õÿÿŸÿOèÿÿìªNåä•ÿÿi+ºˆÿÿ“{5ÿÿ»!ŒÿÿÝ>ÿÿßÿÏNÓÿÿ€- ÿÿ{??¾ÿÿpÔV;ÿÿd‘||ÿÿU椢ÿÿ@Ó;ÿÿ ÿÿßÿÿÿÿÿsÓBöÿÿÑÊ;'î_ÿÿ7BÃÿÿVï“OÿÿUZíÿÿ¤Ø eÿÿÃJÿÿ?´-’^ÿÿ`„~ÿÿP@šÈÿÿC³¯ÿÿ:.Ðÿÿ ÿÿÿÿL$ÿÿ!ÿÿÿÿÿÿ¸4ÿÿ«ƒ5ý¹ÿÿ0Ïÿÿ$T¢ªÿÿCOqÿÿhÍ<Ýÿÿ‡·ÿÿ WÿÿrsÅoÿÿ@ýÈÿÿ$ßÿÿÿ>ÿÿÿÿQðÿÿö‚ÿÿ—ÝlPÿÿÿÿÿÿÿÿ}€-\ÿÿé¬7ÝsÿÿÚ°éÿÿþ‚§ÿÿ+¡S ÿÿIý!ñÿÿdcÿÿGpoÿÿRã`ôdÿÿ#,ÿÿÒ(iñÿÿ÷%ÿÿ—GoÍÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄD¹í4ÿÿ¼ÿÿ1ÿÿb'ÿÿœ8•ÿÿ#J ÿÿ:/ÿÿÆ53ÿÿ?å"Šÿÿ©;%:ÿÿßÿ*ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿÿÿÿŸþØÿÿÉÿÿ“òÿÿctÿÿ;êÿÿÿÿÿÿ ±ÿÿ™ÿÿ2´×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñûÿÿÕ–äÞÿÿäCÚÿÿžQÿÿbÅÿÿ/ÿÿjÿÿÿÿwÿÿÿÿQ:ÿÿ+³ÿÿèÍÿÿÿÿÿ[ÿÿòŽÿÿÕËåÍÕ—äáÕ™äèÿÿ÷Šÿÿ¿ÿÆhÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ1R ÿÿ¼ÿÿ¿ÿÿÿÿÿíÎìÿÿÊLÿÿÇ!ÿÿÄ”ÿÿ€ÿÿÀëÿÿÿÿK*`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÓÿÿÐýÿÿÎáÿÿ€ßö`ÿÿÿÿÿÿÿÿÿÿþ-ÿÿv¢…>ÿÿÄåÿÿk!®üÿÿ¢I±ôÿÿëðúätÿÿ˜ÇÞÿÿÚèÿÿØÖÿÿ×ÿÿÕYÿÿÓ¥ÿÿÿÿÿÿ§šÿÿÿÿ¼Ÿÿ`ÿÿÿÿ|hMrÿÿ#tŸÿÿÿåÀõÿÿSWÏfÿÿ¢ÖÌßÿù¥Õ µ)ÿÿ·é¶àÿÿÇ8ÿÿÕ™ÿÿÚéÿÿÙªÿÿØuÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸÿÿÿ@áxúÿÿL¥›ßÿÿ=ÑÇÿÿ<ñ¬úU«žûZ’ÊÿÿõZa.ÿÿÔF˜¡ÿÿ ³'ÿÿ½ýÿÿÀ•ÿÿÇ7ÿÿ̱ÿÿÿÿÿÿ¿ÿ9P9ÿÿÿÿ[HGàÿÿx€i1ÿÿ†¸ÿÿ¬´?Ìç’«u×fs`!SíòTºÿÿüJ ÿÿàÓg ÿÿLÔˆ…ÿÿ0™~ÿÿ Ÿÿÿÿסìÿÿf£KÿÿÉÿÿÿÿÿÿÂÇÿÿ`Uëÿÿ§[]ÿÿáo¶å¨ë…ø¬Ù|v¾RBÝÕ42ïè¾Ñ›ßÿèþ~¼ÿÿÏ öÿÿ}Iÿÿ`Q€ÿÿ]˜]ÞÿÿUàN˜ÿÿ_@GÄÿÿ`ÿÿÿÿÿÿCç@ÿÿŸC@ÿÿã…@7åê÷Ø ýIÊ¿ûCx~U…aÿÊs¶"¯ÿÿÛÊÿÿ·ÒCÉþÿÿ¹|)Ìÿÿxï ÿÿ|a ÿÿ|v&™ÿÿz8@ÿÿl±^*ÿÿ`ÿÿÿÿÿÿ€2ôÿÿØ&'`ìŒüµ0nĶÿÿAš–<ÿÿn»V¿ÿÿ›T‘ÿÿ»ìÿÿj&°Õþ‡ÿÿŸÿƒGÿÿL†YÿÿJâ–iÿÿ@¦Åÿÿ;n¿ÿÿ,Õÿÿ ÿÿÿÿY#ÿÿ¿ÿ÷ûÿÿ fÌìÿÿ¡)ÿÿ+ÈpqÿÿS¤5åÿÿytÿÿšÿÿNƒÂÿÿØftqÅ2ÿÿ Òûÿÿ ßÿÿÿ•ò½ÿÿ.ÿÿ¶ÿbåÿÿ„b‘GÿÿÿÿŸÿýÿÿü’“ÕÓÿÿª-ÿÿ€ÿÿmOÿÿ3XÝÿÿN9ÿÿ XÔÿÿ=fLNÿÿ‘Á>%úÙÿÿÿÿ³­[ÿÿ­uuÿÿÿÿÿÿÿÿÿÿÿÿÿÿvº ’ÿÿäÊá ÿÿ¯òÿÿÿÿ[ÿÿ4dÿÿ ü}ÿÿ ÿÿ ÿÿ*à úÿÿhŒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃðÿÿºßÿÿ‚°ÿÿQÅÿÿ'ÿÿlÿÿÿÿøÿÿÿÿ%xÿÿbNÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøÖÿÿÿÄÿÿÈNÿÿˆÚÿÿMÍÿÿ&ƒÿÿŸÿÿMÿÿèÿÿÿÿÿÿËÿÿZÿÿÿÿÿÿÿÿû©ÿÿòSÿÿætÿÿÕyäYÿÿa[ÿÿÿÿÿÿH;¹&Ÿÿ`ÿÿÿÿÿÿÿÿÑÃÿÿÿÿ=6Ëì;ÈÿÿäÿÿÁ6Ÿÿÿÿß/úÿÿÿÿÏŽÿÿÌkÿÿÉüÿÿÇüÿÿÆÿÿÿÿ]¯úzÿÿÿÿJJÿÿŸÿKàÿÿôÿÿz%ÿÿNè@ÕÿÿVÿÿÿÿ§gÛ¡ÿÿÀkÿÿçÄ/™ÿÿÿÿÑèÿÿÎöÿÿÌ¢ÿÿÊ›ÿÿȽÿÿ€ÛŸ`ÿÿÿÿ”»ÿÿÿÿÿÿÿÿ‘ÿÿ€gÿÿTýè²ÿÿ¥å§‡ÿÿÇä\mÿÿòzN³ÿÿÿÿÔ\ÿÿÑÿÿÏ[ÿÿÍ_ÿÿË¡ÿÿÿÿÿÿ´ùÿÿÿÿÿÿu]ÕÇÿÿ·¶Ÿÿ`ÿÿÿÿ[χÿÿf2Ézÿÿ¤É€ÿÿ͸zIÿÿÿÈq:òÿÿE’bÿÿ!;Ÿÿÿÿ¡¥ÿÿ åªpÿÿDÿÿ ¾ÿÿÿÿ 6jÿÿo†@ÿÿ³•ELþ1å‹SYÜçè%-[’±’á¹1|”H ÏÏA)Ñ[®ØÊÊDÛ—©äÿÿÎæ`ÿÿ±D Iÿÿ| @ÿÿb€E¸ÿÿ`Gfÿÿ`F~ÿÿ`ÿÿÿÿÿÿ`' ÿÿ¯À*~ÿÿñ—)©Ûðóñ$0·A÷Ð:Xh}½ü4 4—gèû23ÅJÿÿ€Å[õ!ÿÿµž±ÿÿ•ò@ÿÿh@ÿÿhÓ@ÿÿh½L4ÿÿa.W{ÿÿ`Ÿÿÿÿÿÿ2D ÿÿžÉ•ÿÿégäá}ø£v 2¼×ÿÿ%°ÿÿO—Qÿÿ€ ÿÿ¦eÿÿRŒ›‡ÿÿÅшå¶ÿÿweŸÿÿÿ9“¥tÿÿ6Ô³tÿÿ-Í¿ýÿÿ&a̱ÿÿ ¿ÿÿÿÿÿ€ óÿÿÜ\ªìsÿÿÂÿÿ™ÿÿÂj‘ÿÿ3f1ÿÿÿZ“ÿÿ{qxÿÿ>Õg*ÿÿ„w]ÿÿÿÿMyßÿÿÿsè[ÿÿ¢öâÿÿÿÿÿÿÿÿ¤tN®ÿÿXŸÿÿÅbõ˜ÿÿÈUÿÿšòÿÿsÁÿÿHÿÿ3#ÿÿ,€ÿÿ &4ÿÿ09)ÿÿ_É×ÿÿ¾¼·ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢]ÿÿþ„ÒÇÿÿÌÿÿlãÿÿDÿÿ ›ÿÿ ÿÿÇÿÿ¯ÿÿ#à·ÿÿN<ÿÿ¥ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçŽßÿÿÿ¤óÿÿjªÿÿ:˜ÿÿÿÿ®ÿÿöÿÿÿÿwÿÿ ÿÿH4ÿÿëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý|ÿÿï“ÿÿ±3ÿÿlçÿÿ3üÿÿ&yÿÿƒÿÿÁÿÿ]ÿÿÿÿÿÿ=ÿÿ@-ÿÿ–HÿÿÿÿûVÿÿô‰ÿÿìªÿÿÚð½ÿÿÿÿÿÿÿÿà ˆÿÿ†d¿ÿŸÿ`ÿÿÿÿ@ÿÿL„ÿÿßÿÈšÿÿ¬9©uÿÿÇ(ùÿÿã ÿÿÿÿ ÅÑúÿÿÍïÿÿËÐÿÿÊ ÿÿÈ›ÿÿÿÿÿÿÿÿÿÿ×?ÿÿvÿÿŸÿ^¿ÿŸÿ`ÿÿ¿ÿŸÿ`ÿÿÿÿYGÍùÿÿ©ˆ ÿÿÊR@ÿÿé#9»ÿÿÿÿ Ó©ÿÿÏ·ÿÿÍŠÿÿËïÿÿÊsÿÿÿÿÿÿ4í@ÿÿ²•lÿÿBeÿÿ £ÿÿÿÿ~ÿÿbÅ¢ÿÿ¥þi’ÿÿÎé[#ÿÿñpRÂÿÿÿÿ9–ÕFÿÿÑkÿÿÏmÿÿÍÄÿÿÌgÿÿÿÿÿÿð íÿÿøÿÿÿÿâ;Ãÿÿ iÿÿ²ÿÿh!£ÿÿ¤wÿÿÕrÔÿÿû÷miý÷ÿÿRíÎìÿÿÓ)ÿÿÑ;ÿÿϼÿÿÎaÿÿÿÿÿÿ °ÿÿSÿÿÿÿR:ÿÿ€ÿÿ œ}ÿÿdíŠ%ÿÿ¢+ßÿÿÜtŒ“ÿÿÿÿ‡æ«ÿÿlQÀ½ÿÿÔáÿÿÓÿÿÑÿÿÐjÿÿÿÿÿÿÿÿÏÿÿ0ÿÿZÝÿÿ|Àÿÿ!…’DÿÿYWŸÿÿÿŸÿ¦ÿÿê›§8ó:ÿÿžïÏ>ÿÿ‚¸ÿÿÒÿÿÔàÿÿÓÿÿÒgÿÿÿÿÿÿÿÿÄ0ÿÿJj¹^³Ëƒ\ò«_‘À ´˜Â@²šÿÿÅ„…¢ÿÿ²àkÿÿsüÁÿÿq'9Ðÿÿaœ@ÿÿ`C,ÿÿ]ßÿÿÿÿ( ÿÿ{ƒÿÿ¿ÿô™ét l"Ðxê[ Ou³–õwº3}.÷<ŸtÝ3íõ 5D¡Ö%½úr 4 §ùÇÿÿ©ÑÐqÿÿ•Äl\ÿÿUbMàÿÿ`JWÿÿ`K[ÿÿ`Osÿÿ`ÿÿÿÿaeÿÿ·rùï6²ÖòõwÞ¶êþ6O ‹‹ÿÿ6‹PÌÿÿiKÿÿ0”ÿÿ[oˆ ÿÿ«æÿÿÿÿva¸‚ÿÿ0³®qÿÿ)`²Ñÿÿ&ñ¹êÿÿ"ÖÀéÿÿ×ÿÿ4ÿÿÿ¡uÿÿòaàžüà·ðÿÿ‘Rÿÿf˜ÿÿ"2@ÿÿA`ÿÿ M[‰ÿÿAÞP ÿÿvüH„ÿÿÄ€@óÿÿ çûÿÿò×ÿÿÿƒÿÿÿÿÿÿÿÿ€ÿÿßÿëkÿÿ»ÓÿÿŒÿÿeCÿÿDâÿÿÝÿÿÏÿÿ ytÿÿ0–eÿÿS—£ÿÿ‡¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉ7öŽÿÿÃ2ÿÿŒÿÿ\íÿÿ5"ÿÿžÿÿóÿÿÿÿàøÿÿ(äÿÿJ­ÿÿèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýåÏCÿÿÌÿÿWfÿÿ)Àÿÿ¨ÿÿÿÿ­ÿÿÿÿ—ÿÿ$ÿÿEvÿÿzÍÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞÄÿÿ˜yÿÿU·ÿÿ.üÿÿ&ÿÿ:ÿÿ ÿÿ(ÿÿÿÿÿÿÏÿÿ?wÿÿv ÿÿí“þ]ÿÿùåÿÿõ,ÿÿïþÿÿÿÿÿÿÿÿÿÿ © mÿÿ&¾ Aÿÿ@½Áÿÿ'xbÝÿÿv' ÿÿ¨Ë@ÿÿÌ ÿÿçœÿÿÿ ÊÿÿÿÿÍ ÿÿÊÎÿÿÉFÿÿÈÿÿÿÿÿÿÿÿÿÿ sÿÿ%€cÿÿ)?[ÿÿ&F~êÿÿs‘˜€ÿÿ¦Ï;›ÿÿÏ{2“ÿÿìò'\ÿÿÿÿ©ÿÿÿÿÎ1ÿÿËûÿÿÊyÿÿÉAÿÿÿÿÿÿÿÿÿÿÿÿZ oÿÿ\†ÿÿ&@ÿÿ.{Ÿÿÿr!ˆúÿÿ¥ýJÿÿÒpC{ÿÿó•?Ãÿÿÿÿ,_üíÿÿÏ\ÿÿÍÿÿË´ÿÿÊÿÿÿÿÿÿÿÿÿÿÿÿ²`ÿÿé!½ÿÿ+1@ÿÿ7Enþÿÿnzÿÿ¤Ö_àÿÿ×QZ¯ÿÿûwS\ÿÿÿÿ@ìÈÿÿÇÐ~ÿÿÎQÿÿÌäÿÿËÆÿÿÿÿÿÿÿÿÿÿóbÿÿ.2¤ÿÿ ¨Màÿÿ=Vc|ÿÿjÊnÿÿ£Kq™ÿÿܘoÿÿÿÿiG÷fÿÿU²ßHÿÿ.kѧÿÿÏ›ÿÿÎ2ÿÿÍÿÿÿÿ" ÿÿÿÿÿÿ'ÿÿBÍÿÿ]aMÿÿ+Ñy•ÿÿ`„ìÿÿ Ë‹™ÿÿçlŒ>ù1ÿÿ„\ÞuÿÿoûÆÐÿÿIÆÊyÿÿÑ4ÿÿÏíÿÿÎÞÿÿÿÿ Ûsÿÿÿÿÿÿ).ÿÿTëÿÿy|ÿÿ•õÿÿIt¤óÿÿŸº®¨þîÿÿ²ÐÂÿÿ¡Å¸®ÿÿަŠÿÿip»$ÿÿÉlÿÿÒ ÿÿÑÿÿÿÿÿÿÿÿ ÿÿ *Éÿÿ@GÿÿRýg˜ÿÿ@™îÿd×ÑÌ8ÒF2öûµû­c-ÜØ¯ZžûV´³ubÿÿ°GqqÿÿŽŸÿÿÿA³ÿÿ½ÖÿÿÁ6ÿÿÿÿÿÿÿÿ ¨ÿÿUw"5ÿÿ€:¢ÿÿ¥RDñCÁK*HŒÇI¨¼L¶eBœ=Ž9…°U^’fÅ¿h®H´X•òƒµì›ÿÿÈPÿÿÿ¶9jÿÿ>Ö‰¹ÿÿ ›.ÿÿÌŸÿÿÿÁÿÿÿÿ4ÿÿY™ÁÿÿŽ-¼ÿÿÈ/#ŠåÑMª³Ã—ÐWŽ.飨Ñ|íäMêyqEáñÔ´˜r•-jè¸ðn~yÓxÿÿ‹ã³þÿÿvª`ÿÿON5ÿÿZkHÎÿÿ\ÓGØÿÿ]Fÿÿ*ÿÿzÿÿÉÈêÅãªüÊ»ëóS«¥öÙt ‰„þÁŽRÿo=Tïÿÿ „±ÿÿffyÊÿÿ·?rÿÿÿÿk¿åSÿÿU„®Šÿÿ C­ ÿÿ!°oÿÿ µrÿÿ ÿÿU›ÿÿ»©ôjëSÕQ÷Ø®Vÿÿˆÿÿd§ÿÿ ç3´ÿÿ0¤ÿÿ ‚M ÿÿJ^AAÿÿ~Q:»ÿÿ¶4Oÿÿÿÿ,jä5ÿÿêÿÿñËÿÿú£ÿÿÿÿ Oþ íÍßÿü^¯@ÿÿ€»ÿÿ[uÿÿ<ÿÿ/ÿÿöÿÿ "Óÿÿ4ÝÿÿWÓÿÿ€ÿÿÃîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿfã2êZý´mÿÿ|ÈÿÿP ÿÿ+ÿÿ8ÿÿÿÿÿÿtÿÿ/TÿÿTÿÿ|Âÿÿ¾¶ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñ[öj¾ÿÿ|yÿÿGðÿÿ(¦ÿÿŠÿÿnÿÿ‘ÿÿÿÿ_ÿÿ+ZÿÿPKÿÿyvÿÿ¹¹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉnÿÿ€¼ÿÿBVÿÿ,Âÿÿ%gÿÿŽÿÿÙÿÿ¢ÿÿÿÿ%ÿÿ'ÿÿL²ÿÿvÁÿÿ´UÿÿÿÿþNÿÿûÿÿø™ÿÿÿÿ4ÿÿÿÿÿÿÿÿÅÿÿ@Êðÿÿ` Oÿÿ€é.ÿÿ§…|ÿÿÎt ÿÿï¡ éÿÿÿÿÿÿÿÿçcÿÿÈÿÿÇÿÿÆÿÿÿÿ ñÿÿÿÿÿÿÿÿaxÿÿ?l´ÿÿ\§¿ÿÿ€ ÿÿ§+ ÿÿÏrÿÿï) ÿÿÿÿÿÿÿÿè•ÿÿÉ/ÿÿÇÖÿÿÆÌÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ9³ÿÿX“ nÿÿ}*Zÿÿ¦¥+ÿÿÑX#2ÿÿóÿÆÿÿÿÿ ¼ÿÿÿÿäžÿÿÉéÿÿÈ’ÿÿÇ”ÿÿÿÿÿÿÿÿÿÿÿÿ° Ìÿÿ2ö ÿÿPj3KÿÿvÖ?zÿÿ¥Á@ÿÿÖ :Þÿÿúö0ûÿÿÿÿ û¦ÿÿ¤ß¼ÿÿÊæÿÿÉvÿÿȆÿÿÿÿÿÿÿÿÿÿsÿÿódÿÿ&´5NÿÿE E½ÿÿoqSZÿÿ¤dWÿÿÜ SÓÿÿÿÿKû4ÿÿ9Äæâÿÿ Ñ ÿÿÌ!ÿÿÊÁÿÿɸÿÿÿÿÿÿÿÿÿÿmÿÿÐ2ÿÿ¤L¶ÿÿ3ã`ÿÿao²ÿÿ¡ruÄÿÿævÎõqÿÿkÈßaÿÿZÍôÿÿ@¿ÿÿÿ îÍœÿÿÌYÿÿËiÿÿÿÿÿÿÿÿÿÿÿÿD“ÿÿiÿÿÖƒ©ÿÿKë”ÿÿŸMœzøh÷øbŒÊËíÿÿë·9ÿÿ|Óª-ÿÿcGŸÿÿÿ0ö¿ÿÿÿÎGÿÿÍ^ÿÿÿÿÿÿÿÿÿÿ ïÿÿ@:ÌÿÿN¹\Iÿÿ?ÍŒ5÷Ó182f¥¾ÜÆs5¦`jF¿Žg3ÝLeó—n12ôóŒ/qîÿÿ¢…o'ÿÿ‹Ûoéÿÿ^4¨™ÿÿµ[ÿÿ¾Dÿÿÿÿÿÿÿÿ,ÿÿ`ÿÿÿ†˜*ný«•6—àάª •9L²jv@"XS‰@xruAu´éèÅ8qùXý\lë·‘î5ÿÿ¼Ðwÿÿ®Ïÿÿ“Œ|iÿÿ'²ŒÒÿÿtœ}ÿÿnÿÿÿÿqÿÿ`ÿÿ›Á kô?½ÙìzÕ›¿n p³”¾Ï8$²’½2 Eém±·ÀkØcÜgÙ¹ ½à_žF¦ ìÏx8†ÿÿÿœ5rµÿÿŽÙKýÿÿ{Nÿÿr»(=ÿÿ`@ÿÿTyÿÿÿÿJšÿÿœ%ôÏÇŒ¿ÕëÌÀª öµÇÑ‘Y›XÞ¼ >YzªãÓ&:4¢VŽâÎYÆRnQ³äµ°ÙM§@3ÛÀê”VïÊ ÿÿ}y¶ÀÿÿnkšÿÿVSXµÿÿK+H:ÿÿU?B ÿÿXÿÿ'ÿÿ[ü3Ñ ÞrØUJI¾œâ5òž€í2³žƒ@÷c# žZ~ûF”/ü!Ãû!*_YçëýG~Vè©ýqÒWOÉøÜÿµ^2æ-ÿÿN‚Ízÿÿ6S¨‰ÿÿ §ÿÿ ¨Eÿÿ ÿÿp"ÿÿÎ*é±àÃÉ©ñT¥×ÿÿ€wÿÿaùÿÿÄ4ùÿÿ# ÿÿ ¿C ÿÿOh8Tÿÿ£0Áÿÿ½>+öÿÿøn'QùòÿÿQàÒÿÿä„ÿÿéxÿÿÿÿ»oô¢æÔšù×£äÿÿvJÿÿSŒÿÿ5èÿÿŽÿÿÿÿÉ®ÿÿ6úÿÿdÿÿ‰žÿÿ¹kÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôHÜ ß÷þù¥:ÿÿnðÿÿF¡ÿÿ%=ÿÿðÿÿYÿÿÿÿáÿÿ3îÿÿb#ÿÿ‡Îÿÿ¶‡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿßÿô«\ÿÿk…ÿÿ=~ÿÿ'&ÿÿ1ÿÿÄÿÿÿÿÿÿ£ÿÿ1mÿÿ`¬ÿÿ†–ÿÿ´ÿÿþ¦ÿÿÿÿÿÿÿÿÿÿÿÿ³QÿÿkÀÿÿ4hÿÿ*3ÿÿ$FÿÿbÿÿUÿÿùÿÿÿÿlÿÿ/$ÿÿ^Õÿÿ„ ÿÿ¯”ÿÿðgÿÿÿÿþÒÿÿý$ÿÿÿÿÿÿÿÿÿÿÿÿÍÿÿ9(ÿÿ[vÿÿ€68ÿÿ©¯ÿÿÐÐ&ÿÿïQÿÿÿÿÿÿÿÿôˆÿÿÆ ÿÿÄ•ÿÿÃÀÿÿÿÿÿÿÿÿÿÿÿÿ¦ÿÿ9Lÿÿ\‘ Ìÿÿ€Wÿÿ¨d2ÿÿÑ>¶ÿÿð>ÿÿÿÿÿÿÿÿõ<ÿÿÆ„ÿÿÅÿÿÄ9ÿÿÿÿÿÿÿÿÿÿÿÿjÿÿ9ƒÿÿYäÿÿÛºÿÿ§‹ÿÿÑuˆÿÿðèÿÿÿÿÿÿÿÿõ#ÿÿÇ<ÿÿůÿÿÄàÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ5ÉÐÿÿTïÏÿÿzƒ ÿÿ¥â ÿÿÓË ÿÿùdeÿÿÿÿ)ü^ÿÿæšÿÿÈÿÿÆrÿÿÅ­ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ)¤<ÿÿI©-»ÿÿq÷;sÿÿ¤µ@ÿÿÛi>xÿÿÿÿ4˜õ™ÿÿ ç0ÿÿFÙKÿÿÉ)ÿÿÇ•ÿÿƯÿÿÿÿÿÿÿÿÿÿÿÿÌ ÿÿ9¹ÿÿ8ˆKHÿÿcÿ[êÿÿ¢€`ÿÿæ®cqïsÿÿXAØ#ÿÿBëÌœÿÿ0eÀ¥ÿÿ8ÅTÿÿÈâÿÿÈÿÿÿÿÿÿÿÿÿÿ ƒÿÿ7ÿÿ[ ÿÿKsBÿÿO‚~ÿÿž«‹vñeG9ñ.UÇáuúfeð°)ÿÿhʦHÿÿV<Ÿÿÿÿ9±ÿÿÄ€ÿÿÉÿÿÿÿÿÿÿÿÿÿ5A ÿÿ@b,%ÿÿR©Jÿÿ>¦ûí°\èMªlåɤ[ß“#[-¯÷TýÈþW.›æ@¡î¯b u6 ÿý®‡êi§ÿÿ€4kPÿÿd‰—%ÿÿ0« ÿÿµÿÿÿÿÿÿÿÿ,úÿÿgúVÿÿŠ^겜k ÝÚÏF—Ýu,\ž•{ò6­Jåxg)f|hºrdo²d¹kh æRe›)Æ52úú—[ÿÿ£*ÿÿ–àUBÿÿ@€ÿÿÏ’åÿÿ õÿÿÿÿ¨ÿÿe ÿÿ¡Üâ`ª;¿ tÃñ­. }H¤2®µzš‚N­-)-9^¥ ^ UÂYЧ´«ÊQÏV§¤çBN¬‚èû{ónbÿÿ‚[{ÿÿtz_ÿÿc2ÿÿ`/ÖÿÿVÿÿÿÿWÿÿ£Ã甹 ¼Lɹ¾Çgð¨’ÄÆú ‘ŒÌÑ1 >¥kŽÔL,(”H!чNŒCðC_Ò­6?¼@¯ÏQÝì<'¾žúeZL¯ÿÿaž}ÿÿR5f]ÿÿ<äM¿ÿÿGô@ÿÿPÿÿ;ýÿÿ› ñ#È Ó§ÍkLƒ²žÙdQ‡‘Lâä'9tæê ·{VÞòO7ï4¦ó:Ç2©0Tô­‰*.-,ò¬Ï–*O´ê‡ø4µÞ)ÿÿBωÿÿ2Ë­]ÿÿŸÿÿÿþŸ«ÿÿKÿÿ„ßøKËßW×"¾ïëK™’úuygÿÿ^ÿÿ7Oÿÿ¯ÿÿ -Mÿÿæ¶QéºÿÿGÒÿÿ0_Æ-ÿÿü¿Óÿÿ Ö±‘ÿÿÅÆÿÿÅ ÿÿÿÿÿÿÿÿÿÿÿÿ'ƒÿÿÐIÔÿÿ$`ÿÿRÏq‰ÿÿŸHy&å®=êâ”=ÚŹ0öl9©«Þ ,þjNxŸÿÿÿD^œÑÿÿ0]™xÿÿÇ»!ÿÿÆ%ÿÿÿÿÿÿÿÿÿÿ.ÕÿÿBñ¨úí\À 3%oòEc d9XÛPTnJ?Q ¶YIÊ2K%Ÿ,D µH.—)AúåPEÕ} lø`d`ÿÿqd1ÿÿ^f¾ÿÿ>ןÎÿÿ«ðÿÿÿÿÿÿÿÿ+ÿÿ`ñn}=á UÔhð Í•¹“)J æŽ,l¹-s<ìj|YkZ['d+_ÁŸnV°a,]ˆ×USYFíDyóMk*Zÿÿ”˜Sÿÿ‹ÝÆÿÿw\píÿÿ! „Ûÿÿÿÿÿÿ¿ÿÿf,ö3“·Ô¼—¹þµ ›j{E•ÇŸ îÏst| v-PÕ•×R£G‚L——‹™CÉI@•Ô@¨7 „lôâP_h–ÿÿrZœÿÿh BÚÿÿ\úÿÿdv$‹ÿÿTÿÿJÿÿ]õÿÿ¦Ý-«,£½u±}@r›i¸HRe} Á$Ï W[ËÄpè›;ÀªBü5}7¹Î1õ4€ÀËî.{1e¸—ôØ/à¥rÿÿP­™UÿÿF†÷ÿÿ9%N‘ÿÿ@@ÿÿF)ÿÿI-ÿÿ 8éT½ÉħЪƒìØ´fÜË.H+ãù –·'Ñã1$Õ#bäÀx¿ ± ã+»µ+¤Üœéœ!ÓÃÿÿ2ÈÃÿÿ&³»ÅÿÿÛ›îÿÿc“,ÿÿ¥ÿÿ‘;î÷¿ëÔ˜Ñ)³凋eñékyöQõ÷R7û„¬¦Bý•¨T‘þŒ^ÙþÆ¥0/ìý²Ýî ¸ù«ÿÿÐðÿÿâ:ÿÿЊÿÿÏ(ÿÿ툷Û×¼™ô3ŒàÿÿdªÿÿH«ÿÿ/óÿÿJÿÿÿÿׂÿÿ<–ÿÿv¸ÿÿªÀÿÿÒÿÿù˜ÿÿÿÿÿÿÿÿÿÿÿÿÖîÑ.Ã)úk‰–ÿÿYµÿÿ:rÿÿ ÿÿäÿÿÿÿÿÿdÿÿ:3ÿÿv ÿÿªPÿÿÏÐÿÿôÿÿÿÿÿÿÿÿÿÿÿÿ¿ÿøL‹ÿÿQÇÿÿ+ÿÿÿ#ÿÿSÿÿÔÿÿïÿÿÿÿˆÿÿ:wÿÿuáÿÿ¨íÿÿÌdÿÿí^ÿÿÿÿÿÿÿÿÿÿÿÿ‰'ÿÿJòÿÿ&ÿÿ#Òÿÿ cÿÿðÿÿÿÿÿÿÿÿ gÿÿ;€ÿÿs<ÿÿ¢ÊÿÿijÿÿäÃÿÿÿÿþâÿÿþVÿÿÿÿÿÿÿÿÿÿÿÿÿÿ(_ÿÿQbÿÿzÉÿÿ¢ÿÿÊÌÿÿòhÿÿÿÿõ¼ÿÿá]ÿÿÎäÿÿÁžÿÿÀ©ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ(hÿÿQˆÿÿ{fÿÿ£xÿÿ˯ÿÿó-ÿÿÿÿöQÿÿáÿÿÿÏŠÿÿÁÝÿÿÀÛÿÿÿÿÿÿÿÿÿÿÿÿÿÿ(ZÿÿQ–ÿÿ|ÿÿ¤<ÿÿ̸ÿÿô$ÿÿÿÿ÷!ÿÿââÿÿÐsÿÿÂ8ÿÿÁ,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ(%ÿÿPÉÿÿyôÿÿ£=ÿÿÎwÿÿõ ÿÿÿÿì<ÿÿßÿÿÿѹÿÿ´ÿÿÁ‰ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ&ÞÿÿMÿÿu®ÿÿ¢òèÿÿÖUÿÿþöGéÿÿØñÿÿͤÿÿÀiÿÿÂÞÿÿÁðÿÿÿÿÿÿÿÿÿÿÿÿdÿÿÔÿÿ?A ÿÿjÏ,»ÿÿ¡17¢ÿÿå/=ãCûÔgÌXeþÚ¿fÿÿ ϸÀÿÿ­Ôÿÿ®ÿÿ‘ÿÿÿÿÿÿÿÿÿÿÿÿnbÿÿ16Àÿÿ$ÿKsÿÿV½Z;ø´+w£Ë;‘ÔI.GÏ.,–»?'äê &­"TùÄ ®š8ÿÿ3c–[ÿÿ"ò“þÿÿÝ–ôÿÿ¿ÿÿÿÿÿÿÿÿÿÿÿ$yÿÿAÁòçoI s³Ú8H–„(»Å…>Î?>™¤º:sC;]¡8ƒ¤b9SŠf7ÕÈ7©…J3¦õü5:_"ÿÿ``ÿÿO`¬ÿÿ;Ímqÿÿº¡Zÿÿÿÿÿÿÿÿ%ÿÿÿ[—âjgûÆÃÂNlEô œ¦ƒm¸f ^Ü%/«^NtOMX6TùîHaUHSÆ EJTNï?CÊÄÙþŒ{¾—ÿÿ}kÿÿqV ÿÿNw{¶ÿÿ]ÿÿÿÿ ÿÿ`ëv€¸Èù†«‰-¦ÿ‹`ò ¨‡© @ÜdxŽòš!±D-‡KG!9@c‰L‰Þ5·=‹‡í¶2Ý<‡Èíd1‚Öb ÿ·_ÅUßÿÿZ$FÿÿQâ/ÿÿF æÿÿOòÿÿ †ÿÿ_MøwšnÓ÷ž²Q¤îzU\¬è/³n•³'nFM€¶çÑ“.ï² 8Ê'a+b³[}ˆ$g(%±ß¹Ù!~&ج‚çTüœ‹þø;‘ˆÿÿ8„öÿÿ.˜ošÿÿ @ÿÿ=«ÿÿQØûEšxßÿ°›¾kº!›ØÇÝvÅÏWÐ=8ÚÕ¾ù ;þÕ,)k6ÖŒkŽL:Õ¦ªó^òѕܖÖ×ÉXüéô¿hÿÿÛ¶ÿÿ¤†ÿÿ‹äÿÿùönâ+³èÇ™É˦Ý„}&èâ\Øì6CBëÁ(éî{â @ò§_ * ó¢VVè¹ó]–‡tÖñ|ÏÁÖxëõúäžÿÿ¥Ý\ÿÿÒÿÿÄÿÿßU®«Ë+ÐÖ­(í{ ý~]8ÿÿDLÿÿ.ÿÿ€ÿÿÿÿ pmÿÿ@ÿÿ~Sÿÿ¶PÿÿâUÿÿÿÿÿÿÿÿÿÿÿÿùÔÿÿÄé̶Kú¿~äÿÿQAÿÿ4öÿÿ«ÿÿRÿÿ`ÿÿÿÿÿÿ=~ÿÿ{Öÿÿ³7ÿÿÜÙÿÿü>ÿÿÿÿÿÿÿÿÿÿÿÿ¯Jø}QÿÿGUÿÿ$Úÿÿ "ÿÿÿÿQÿÿÿÿÿÿ 2ÿÿ? ÿÿz&ÿÿ¯aÿÿ×èÿÿö?ÿÿÿÿÿÿÿÿÿÿÿÿvúÿÿ>ûÿÿ!ÿÿ ÿÿbÿÿþÿÿZÿÿúþbÿÿùÿÿA¬ÿÿvÙÿÿ¨¸ÿÿÐ]ÿÿî ÿÿÿÿýÖÿÿücÿÿÿÿÿÿÿÿÿÿÿÿÿÿùÿÿIlÿÿuÿÿŸvÿÿÊ;ÿÿòÈÿÿÿÿê¬ÿÿØçÿÿÈØÿÿÀhÿÿ¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿIÅÿÿuÃÿÿŸÿÿÿËÿÿóÿÿÿÿê÷ÿÿÙUÿÿÉ*ÿÿÀŠÿÿÀ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿåÿÿJÿÿvÿÿ áÿÿÌÿÿôiÿÿÿÿë‰ÿÿÙåÿÿɤÿÿÀ¯ÿÿÀ4ÿÿÿÿÿÿÿÿÿÿÿÿÿÿšÿÿIÓÿÿu³ÿÿ ¿ÿÿÍ1ÿÿõ†ùÜÿÿäyÿÿÙCÿÿʰÿÿÀæÿÿÀeÿÿÿÿÿÿÿÿÿÿÿÿÿÿÓÿÿGQÿÿrÃÿÿ ZÿÿÏjôóð@áøÿÿÐlÿÿÃòÿÿ¼Lÿÿ»ÿÿÀšÿÿÿÿÿÿÿÿÿÿÿÿÿÿ/ÿÿA-´ÿÿlhßÿÿŸÕ'îWÎÏ !Ö ½é¡ ãÃeúôßT·¾ÿÿy®dÿÿ¦Lÿÿ£ØÿÿÀÛÿÿÿÿÿÿÿÿÿÿÿÿ \´úñ/+›ò{0¹Kî¹%]uÙŒ;¾&µàû®NéÙ³â£x”ð—‘–'£ÿVÿÿySÿÿ_‹wÿÿ²ŸÿÿÿÿÿÿÿÿÿÿÓò'5îÔK3v‚ ÑÂ0E¹ ­%.Â5+‰“£.veE+‚Ú-•z+%}˜,CÅ0* z¾)óëG'ËhÁ8ý;œZòÿÿ?ß]½ÿÿ0`ÿÿÊ”ÿÿÿÿÿÿÿÿÂþ5NÛÖ6Tlu¼´ªX€¾m–z[ï rÂS¼#¥R0CyDr?KLãJŽ‚:=J)IP·/7hI&E0â$61º/*ùØM’6ÿÿmÿÿd”éÿÿSacóÿÿ•ÿÿÿÿÊÿÿ\ßâ"p!½™vc˜îš¼{¢ºÀy€¥¤ :Wlˆ«|8Øz^×ÿÿTåÿÿZÿÿ\nðìŒ5Éñö¦ó˜Žƒ  ª£Þ_¹¥(ÅA>Z¨í Õ ì"\£@.üe¤xo¶x¤9©”• qØ„-f™‚ù Þ‰ÿÿ'E ÿÿ 1pÊÿÿsHºÿÿ->ÿÿUäïŒSÖ§”±”°k¾¾ hQÄGñÄ÷)¥ÇçßÇ"ë Ø µÇ¦]z d üǘãEhÄ%Ì Ò¼ÂïÂÞ´áÿÿ ñ¬ÕÿÿA¢ÿÿŒ¤ÿÿꥅóÔy«»ºÔˆ—Ô6muß5MLâg3à”“à$½ãê‚ââÆK“ã †â»ëÞ<çÛÚ†ÿÿÕÿÿÍÿÿ¡ÿÿÎ|§ÿ¼9ËSžÐåˆtkö°RúÌ;Wû¹&Ëû_ù}ûØaÿÜD•ÿÿ‚ÿÿº_ÿÿëBÿÿÿÿýmÿÿø ÿÿòÿÿµœÊ¥NóìvÿÿJ®ÿÿ/ûÿÿÒÿÿ÷ÿÿˆÿÿÿÿÿÿA×ÿÿ}Rÿÿ³ìÿÿàèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸRö{qÿÿ?~ÿÿvÿÿÿÿ*ÿÿªÿÿüÀÿÿ¤ÿÿDàÿÿ{ ÿÿ¯0ÿÿÛ÷ÿÿÿÿÿÿÿÿþŒÿÿü¤ÿÿgÿÿ2¨ÿÿ\ÿÿíÿÿ}ÿÿJÿÿHýkúðþÿùH–ÿÿyJÿÿ©üÿÿÕ{ÿÿùXÿ ÿÿû(ÿÿø¹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¼ÿÿ@áÿÿn¯ÿÿ›àÿÿÉ>ÿÿó þ…ÿÿã ÿÿÐÿÿÁÿÿ¿ÿÿÿ¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©ÿÿA.ÿÿooÿÿœÕÿÿÊ]ÿÿóÞþ§ÿÿã8ÿÿÐöÿÿÁ\ÿÿ¿ÿÿÿ¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿnÿÿAdÿÿp3ÿÿžÿÿËÿÿô¾þëÿÿã‘ÿÿÑtÿÿÁÎÿÿ¿ÿÿÿ¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùÿÿAlÿÿp ÿÿžÿÿËÝÿÿõVó4ÿÿßÿÿÿÒÿÿŠÿÿ¿ÿÿÿ¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@ÿÿmòÿÿ÷ÅÒæ;äØàûßÉ:ÿÿ½¢ÿÿµ7ÿÿ¸ÿÿ¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¶ü9<åë¯`Ù܇,Ëÿ«ÿÂ@Ðx¸èëG°ý¦$ÿÿžœÿÿŸÜÿÿ«‡ÿÿÿÿÿÿÿÿÿÿÿÿç/ü)²Ö]f%¾É-ãI9´¸ ²s· ب¿fë ÛŸ;™ÆY ‰™; *å´ ’ª ±øl£‰ÿÿ l†iÿÿ……ÿÿ6ÿÿÿÿÿÿÿÿÿÿ ç3 ðÃO!‚\¬OÍ ÚÉ”ï)Ñ«ƒb"dXz‹uï#‡I8q!áµ}_olÂݰ•mfÏ÷tWšÿÿ1Xšÿÿ"Œ\yÿÿSn.ÿÿ ÕÿÿÿÿÿÿKõI>tÌIC¼¨H2²¼ˆLŠ ¬ fßH"dH]:¦;‡1zB@iu˜,µ?+?‹¨/*>:˜7Éô(úðÿÿ\sÿÿUø9ÿÿJa Pÿÿ;±ÿÿÿÿ2ÿÿS±Ù c5³Si•n†Hôl‹rl€yJ¾uKbè-Znj2|Ž)toÔl{ƒ&…oË¡ú†%km Ñ“‹&šfiñ§JLNÿÿ9 @ÿÿ3Æ5›ÿÿ/C#ûÿÿ7àÿÿ/ÿÿXñç×~ï½Ý„ǚ팮wt•RC˜Ç0/šÙÄÇ7— &²€G—\_š °—˜• +ߕɠA`ŽIì3 &€Üÿÿrxÿÿ„nªÿÿ ±aíÿÿ[ýV“ßÿ€WÉ-¢£î€±8Xÿº>7¼¹©]¹ ""º¹$Ö¸PO°·Ã‡¾³±·n¯hßšªbÿÿ#¢Zÿÿ˜§ÿÿ±ÿÿÛ©€Ã¡?«‰¹9†šÈ§^Ôê<ò׋#Ô× ±Ò+ÕBÕ+H¤Õz{æÔë¯oÔ/Þ×ÑÿÿʳÿÿÄAÿÿ¼mÿÿ½Æ¡¯«àólÜÿfxì¬Dðó+ýñ,îÃëHòÀ÷wGôùp~1ùí´½ù¾æo÷ÿÿòxÿÿíÍÿÿéÁÿÿ£åÃå–ÀïFmíÿÿDøÿÿ*Fÿÿîÿÿ [ÿ¯ûMþúÿÿHòÿÿ}ÿÿ°Lÿÿߎÿÿÿÿÿ2ÿÿü5ÿÿúÀÿÿŽ¡ô³fÿÿ7 ÿÿÿÿÙÿÿ ÿÿeù úwü§"‰þ”Kšÿh{¾ÿϬºÿÿÛÿÅÿÿüxÿÿùSÿÿ÷µÿÿY˜ÿÿ(Mÿÿóÿÿÿÿ=ÿÿ þ ÷­ø¿ |úv(ûûÏNwüqy¾ü¯¦ÝüžÒ½üˆú.ù¸ÿÿölÿÿôxÿÿÿÿÿÿÿÿÿÿÿÿÿÿ gÿÿ8Sÿÿg´ÿÿ—ÔÿÿÇüÿÿó6÷òÿÿßÍÿÿ˶ÿÿ¿\ÿÿ¿Lÿÿ¿3ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ *ÿÿ8‡ÿÿhhÿÿ˜øÿÿÉÿÿô÷ÍÿÿßÒÿÿËÞÿÿ¿†ÿÿ¿wÿÿ¿_ÿÿÿÿÿÿÿÿÿÿÿÿÿÿºÿÿ8›ÿÿiÿÿš(ÿÿÊdÿÿõ÷ÁÿÿßúÿÿÌ/ÿÿ¿³ÿÿ¿¥ÿÿ¿Žÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ8~ÿÿidÿÿšVÿÿʉÿÿõlíÿÿÛRÿÿÌÕÿÿ¿Ûÿÿ¿Òÿÿ¿Àÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 8ÿÿ8hÿÿgøô²’^å`·ÿÙ¢ÛOÏpöjýÿÿ¸’ÿÿ±„ÿÿ·ÿÿ¾íÿÿÿÿÿÿÿÿÿÿÿÿÿ«:è.~Ô×P{Å&u¸¸øœ±öÁ笺á¨!øp xÿÿ˜„ÿÿÝÿÿ¥¹ÿÿÿÿÿÿÿÿÿÿþ·Öòνô´ªh8šŸü˜aޏ–ŒŠQ‘çµa9/óÙ —ˆçﺃ„ÿÿó~7ÿÿëÿÿ‰EÿÿÿÿÿÿÿÿÿÿÝóã¶š/6šZ‹  ¹‚¼£ "‹u{LçZjbT{neD€§$c…ÑÐÉðbb5îP×QXÿÿ%R×ÿÿzZðÿÿ‡däÿÿ 6ÿÿÿÿÿÿ7ìk/,Â(6FœÃ:Ù#(zì<ð%[ØD1Þ2ý$S7‘7KhÎ×4­6ꙟ”3Ÿ4qÆ5œ3ù0‹ê… 4TÿB&ÿÿFJÿÿ?Ì!ÿÿ3ˆÿÿÿÿñüºIÏdW÷§I\”ƒ¬aó`df' =Ýh` †S"cE(ËS“d>^ÊŽÿcè“0 aïÁÊmå]8æôüKªÿº&P<Õÿÿ"ç2±ÿÿ l*hÿÿ&†ÿÿüóN ÝtD±zD‹àâjW‡ûDvŒ´"‹ÁÎ ÕŠÃ߉îQ’—‰%†Ù ÷‡·L±ˆƒo߯,|]ý ëoÚÿÿòeûÿÿa‰ÿÿìaPmÒÙxü»S“{“Š™Poæ¤úHÍ®Å'J®v ‰©©÷1§ïGß©Eza¦2©˜£ãÕr¡"þ•>ÿÿŒ)ÿÿ‡ÌÿÿÊÒyÑ´£™Íd°Fw½qOÝÊ -/ÍoÁÉŒÆ[ÉHKËH™ËÆvÙËu¨ÊÖ•Ç•ÿÿ¼ùÿÿµ×ÿÿ²[ÿÿ¬µ›]œÂ¼€DÓ×Xðâá6KæÿåpAáÀæ|Ëë²#¯ïLßðzzLðz«„ïˆÚ íEÿÿçÿÿâRÿÿÝÿÿ•»Á†·éªa½ú >rÿ"íþµ ¬ü{÷”ø’ $ú^)WûÝO³ü–{Óüå©§üô×¥üŸÿÿøuÿÿõ\ÿÿôñÿÿEðž\Dÿÿ/uÿÿ¥ÿÿ~ÿÿýù·÷ ÷|ø¡.ãù¥RŽú5z?úS¦úÑ’ùÊûIö ÿÿò×ÿÿòeÿÿMjÿÿ ÿÿ¬ÿÿÒÿÿ 3ÿÿ÷ùö´Äö¥Ñöü4÷kTå÷²y#÷¢÷Ë öZñró¢ÿÿðYÿÿï½ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ/ÿÿ`}ÿÿ“EÿÿÆÿÿóGóvÿÿÜÔÿÿÈVÿÿ¼Åÿÿ¼®ÿÿ¼–ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ/ ÿÿaÿÿ”UÿÿÇjÿÿôMó"ÿÿܹÿÿÈ{ÿÿ¼öÿÿ¼áÿÿ¼Êÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ.äÿÿa¥ÿÿ•~ÿÿÈêÿÿõLòãÿÿܾÿÿÈÉÿÿ½*ÿÿ½ÿÿ½ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ.zÿÿbÿÿ–ÿÿÉ@ùÍñàç§ÿÿשÿÿÉVÿÿ½^ÿÿ½Kÿÿ½7ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ/,ñŠY²ã:ƒ›Ø ® ÎÔÆÅwðÀ¾ÿè´”ÿÿ¯áÿÿµüÿÿ»ˆÿÿÿÿÿÿÿÿÿÿÿÿñÖ¤çÃ9Aâ¶ hò«û’¥KºD áÚ¨žPòï›ÿÿ–úÿÿKÿÿ£›ÿÿÿÿÿÿÿÿÿÿú Í ­îá™'-,S3„›}¤!|§Èø{çç |òþ©xÈÿÿ‹ÿÿ†BÿÿÿÿÿÿÿÿÿÿÕ©ì­k M‹ 'îuâ »ÂgÁæ@á`]É‚oU Xz?™ÇV €ÂwUi SãØíVl #û8TPHÿÿ Z…ÿÿïc›ÿÿtÿÿÿÿÿÿãA÷¹$+“’z/²o0v@«PØ0o ? h4ú)o*G-».V\R1*W-닌W(Ã,·¬‰(­)wÞ"µ*(%DùQ…˜ÿÿ7¹9ÿÿ2£Qÿÿ)ÿÿÿÿ ðt<ÁÃLšïRv¿UWTJWR^P1˜Z9¯ÁXrÆ XÝR ØpXFƒž¼vVG±ž>iSÙO?*MgõZÜ;’ÿÿE0åÿÿe)Èÿÿöÿÿ˜âõDcÍþhf¢)nÐ|°shZßyÇ5>€Ì¨})b{Y¢zäD­xWtwu‚¡°r–ÊÛnkïŒdÆÿÿYmÿÿV†ÿÿÝMJêÁ«o ¬‰fƒü?`–Â8Ì¡€¢*œ,›LxšiAì™§oî˜6ž•¹ÊÌ’ó ˆ–ÿÿ{mÿÿxþÿÿºzt²¤pŽ«§*h(²¬@м»ÂÀž¼VÀÖ<Â/# “HGÂ2r὇‡»GÉú¸1óë°:ÿÿ©ÿÿ¦üÿÿ •ЛµrÇÍ#IŒ×Š&ÚŠ›ØÁÛ™áË6å-<æÕP^ç,w¢æƒ¢®äíÍlâ›÷KØkÿÿÓÿÿÑ‘ÿÿ…»cxã¹T…òõ0ö-ôüôföl õøÌö$6%ötVtöˆzEöU¢.õ¼ÊiôêñÙñÿÿîÿÿîÂÿÿo¯ìÝS>ÿÿ(ÿÿ ÿÿ©ýâøö ;õ)çôí9ô½W“ôoy²óâž1ó7Åëòë ï}ÿÿì‰ÿÿì«ÿÿBÔÿÿÑÿÿ oÿÿ|ÿÿúä÷Ìõ©QôV%ó£k›iÝ:ÀhiCe¥”ßb ¼ƒZðÞ¿ViÿÿMÿÿJ ÿÿÌ4EL²þhrž-v‚ÐQnŠ~* ’ü#“’8Ü ¿Á>‹ñiB‰t”m†½²{ÙßúvÂÿÿmòÿÿk[ÿÿ©Cnb•؉—€œÿY[§î1o°¤ A²8´Ü¶© —¶(´oH—²in'°•D¬ì½C©Aä3£¾ÿÿ•iÿÿ“"ÿÿÀŽ €®¡c+Åw:®Íy÷χлּ÷ÙµµÚ•5äÚGSôØþuÖé— Ô»ýÐãá³Ì¦ÿÿÇmÿÿŸÿÿwø¸ûhÜ.E›ê– ÕìXÚê_òÞõvÿó*ò3Bbñ\ðzQîàšb펻Öë÷ÝdêKþ è ÿÿè0ÿÿ`ènJüÿÿ" ÿÿ¡ÿÿûr÷Þõ ÊòÁ.Šñ1EÆï¸]Âîlz¯í ™/ë}¹é¹ÙbçÚø[æcÿÿæ„ÿÿ9tÿÿ‹ÿÿmÿÿAÿÿúð÷} —ô‘Cò122ð>HôîŒazìÌzlë.—éo¶çvÔÇåZñšä’þÕäÁÿWÿÿÿÿÿÿÿÿÿÿÿÿü¤õrM&ôσ÷`ºR÷6êžì´ÿÿÙ{ÿÿ¿ÿÿ²Gÿÿ²>ÿÿ²2ÿÿÿÿÿÿÿÿÿÿÿÿÿÿü&3õoMoôà„*÷»Â÷"ëÂìPÿÿÙFÿÿ¿ ÿÿ²Lÿÿ²Cÿÿ²9ÿÿÿÿÿÿÿÿÿÿÿÿÿÿü-€õeM˜ôê…B÷˜½J÷ìæëõÿÿÙ-ÿÿ¿\ÿÿ²Nÿÿ²Fÿÿ²=ÿÿÿÿÿÿÿÿÿÿÿÿÿÿü2rõRMñƒµë¶°äâàÜ.ühÒ ÿÿ¿‘ÿÿ²Lÿÿ²Eÿÿ²=ÿÿÿÿÿÿÿÿÿÿÿÿÿÿã¸Ñ];pÇ7i+¾àš‡´IÈÙ«óçd«¯ø[ª¹ÿÿ¦Îÿÿ«Âÿÿ°ŒÿÿÿÿÿÿÿÿÿÿÿÿåDºx!¨f'霵Pb‘ŒU‚⬶zω|ãéhŠUûö½ÿÿ—ÐÿÿËÿÿÿÿÿÿÿÿÿÿðtÁJ—Øž8rc:çebfªX-‘œQF¶¢R>Õç\{ð²j©ÿÿuâÿÿUÿÿÿÿÿÿÿÿûÈßÞw³Y`I ‹((?R›8}2¥=.§Ê90¡äÚ8XûCöþ)O<ÿÓÿÿÿÿÿÿУ ¤:u|Z„X†Ù6Ù \e‡~³öIBúû´-m Æ*“AR ¬·‘“àB×^ òV$ 2õÔg$,öˆÿÿîþÖ%Ûªò9þÝ>ZŠ=R9…;þD><>9 5ç5Š_ö1ì†.•ªé+uÍŒ(ï‹$Røk Ïøàú œÇ7h± W;†[\^Ù[=]|aÖaj[8ðVá5+R|]ÖMñ†6I„«EvÍ1<£êû9Tø`6¬øR¼¤B¯¢F`ÙvHg™wSBœ|¾“ƒ…\†hiƒJmyý:uŽ^jq‡UkÚ¬ùfæÎŠYfè“VUövSÜ÷\šhΆ:/s ”¶JJJ ô£í¥`«î­Bµ«“,Ö¨—I/¥Nié™v‰”y¬íŽîÏ/‰‰îmy¥ô˜wbõö€‰Gqʨ«T½ + ÃÂöÄ#Ì&Ñ)úÒá%ÝÒs<<ОVZÍërÞÊÄÆÎ°H¹uʳðç$°„õ†®öìiG´ËZÖÄ7(â’zâÿègðòøô<%dñ 7Pî:N:ë³eçéF{mç”ñäâ¯ÝÚsĤ×ÛúÖ/ç™Ö_èQòä¶BÕÿiœÿÿÿlû^ r÷Š&óÊ)ÿðy:‚í‰Ofê°gLè{¹å°”OÛ¨®ØCÁÕÙBÕ™äêÕ¨å/0€ÿÿ ÷ÿÿRÿÿþ®6ú×g÷9óU. ïÞ=AìËQWéÑhÑæ÷{üä\“xÙ³¦¹Õó¼BÕ×°ÕâIÕâŠÿÿÿÿÿÿÿÿÿÿÿÿì 6ã½>‘á9s7ãéÓå`ÙÀàŒøñÒ,ÿÿ¼^ÿÿ°Aÿÿ°@ÿÿ°?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿì½ êã9>{à²sÐãHªÏä×Úyßÿùѳÿÿ¼9ÿÿ°;ÿÿ°;ÿÿ°;ÿÿÿÿÿÿÿÿÿÿÿÿÿÿìc Íâ§>;àtd⵫Óä'Ûß©ùkÑEÿÿ¼Hÿÿ°9ÿÿ°9ÿÿ°9ÿÿÿÿÿÿÿÿÿÿÿÿÿÿì Hâ=¿Ý®s䨧¦ÓÓ1ÒìÍZñ²Ç«ÿÿºËÿÿ°:ÿÿ°:ÿÿ°:ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÕS0­.]¶*[%­½Œb¢H¹¸˜¦Û¤™óò»žKÿÿž,ÿÿ¥ßÿÿ¬‡ÿÿÿÿÿÿÿÿÿÿÿÿày³šJ ªCXrklö ûdñÆôfÍâÝu—÷â‚Öÿÿ‹¸ÿÿ“6ÿÿÿÿÿÿÿÿÿÿëzºÓ’KrT ·d -îThZéCdˆˆ:e¬ 8ÁÌ*A*èÂQxýbŠÿÿn'ÿÿÿÿÿÿÿÿôÇÁî–¸pxMW94r+âE%"‘m$´‘@µÐ‡Ô¬ð^#¨ô0á÷ÿÿÿÿ÷ Å­š®¯q ßM‰C,vj! É ¶SÞ !4΃7 ÷Xº( iù ¡àïÂycä@içÅ‘çÒÿÿâ±ÈZsžÅ0ãsj5ØN3k,á/* Ó/ü*: Æ$*/ÂR  uÑú˜XºjJÛaå Éæ™Õs1º>1P¡QMxJ4—äF#¶q:µÐ 8äÜJ6Ý%Š!bÌycyce‹ñ;]’±–šÙ¢¢Tœ:‚˜<0Y’ïHGcs‡}äs)™Ÿp)¸òm^ÖÐjUäg`åYrX„yc…¡æEšµ]˸S¼MÆûöËGõËì/]ÉæGçÆZP¹¥nŠ´ †h®E«û»Ž©HÙõ¦†çÊ–—ßÿ[V°ÖJËÑ”'Ù/Ø©æVîŽ(ò¢5MîeI ê"XMæ@kBÚºycÖ‹¤ÓœâÒñ¶H҄ϰÒC×÷ÐÙÙ.BÝÿ6ùÚ ¯ø´ÿD …ûöÝ*Žòs8©íæKÖéYäå‰lÙ¡yoÕF‹6Ò¼œäÒ~µºÑÿÎXÑ¿ÖLÑÏÖ(Yÿÿ­ÿÿÿÿ7þ—0ú«ðöi.Oò;eí†M†é [Läæl­ØÎxÎÔ6ŠÆÒfœ¼ÒµчÍ<ÑCÔÃÑQÔòÿÿÿÿÿÿÿÿÿÿÿÿÝ^ÓŒ1—Ïd@ÏS—ŸÐyÇoÍùì7ÆFÿÿ¸oÿÿ°Mÿÿ°Mÿÿ°MÿÿÿÿÿÿÿÿÿÿÿÿÿÿÝEÒ¦1,ÍúdpÎC˜ÏDǤ̻ìÅ[ÿÿ¸&ÿÿ°Mÿÿ°Mÿÿ°MÿÿÿÿÿÿÿÿÿÿÿÿÿÿÝMѦ0ŽÌÎd‘Í ˜ˆÍæÇ¹ËCëÖÄ_ÿÿ·¶ÿÿ°Nÿÿ°Nÿÿ°Nÿÿÿÿÿÿÿÿÿÿÿÿÿÿ݈Ð/®Ë-dpÂ퓌¼§¾.¸Ãâÿ·Ìþb³Uÿÿ°Lÿÿ°Lÿÿ°Mÿÿÿÿÿÿÿÿÿÿÿÿÿ¸Î0²¿!j£—Mt—ºy‰ŽÄ§‡ßÍj‡ëìZŒBÿÿ ÿÿ›ÿÿ§JÿÿÿÿÿÿÿÿÿÿÿÿÚ‰­—Œg·y¨6gmb^éT˜·GS×IXÆôqcJÿÿozÿÿ{Çÿÿÿÿÿÿÿÿÿÿæ<µŒpe¥P¾ qBYK 2^y~)ÊžÀ&nÀ†&·ß%-eùÝ9‹ýÔI•ÿÿÿÿÿÿÿÿ휹Æ-hµDû(yôs6J %^Ìу|,¦vÈ é3ìÕ„ñ ÿÿÿÿìå¼Bö üf~ñB)Ì ñ À'&ÕK«q–ºfݳä]æaüÖû½1ó‘ï(lfÁ,åAï)!8"ü!Õ Ó*îKA m¡ 9 ³Z Õë Ôàd™àþÆzêY,¥”‚E½kkIºFKFK%/CÜDõ=½5Aë+t2&'ÛP¿%‘oä$(Z#R±\çÎÖ!Û{CÜ~Ÿ€:¿‡¶TXuñfJP7eª*{goi”kPhv Ž[„RR‚5÷K%U]GÊuöE”â9h®´8ÌŒ6RÙ3\Ú~a^anös§[–‡;1EŒ ÙŽ”S–EX”|ú±0³‰HÊvFXÇrAx±oJ˜YlG¶i{Ò²YEØ{VÐØ.gº•Y• l;ɱ;³T¹kÄ BÇe6Çl3ßÄñH…··W¸±’mâ¬~„6ª.œ`§¢¹—»Î‰”wÚû‘ÜTRt°«B”ÏVxÕôØ åi öìµ%0ð':XíŽMè’\€ä.m½×¼w¼Ò»‰ýÑã›ÝÑs³4Ð=Ê]Í–ÓÑËHÕ{:“Þ 03øR˜ödÿ;×û&÷ö½/×ò=íkMœè\µä+mÅ×¼w¼ÒµŠ ÑáœÑo³‰Ð¸ÊpÐRÑ×ÐRÑ×#¥ÿÿÉÿÿDÿÿUþ”Rú¦!ªöW1@ñÒ=ÍíGNèu\Ýä,mÃ×ÀwÀÒ¹ŠÑáœÑm³±ÐµÊ¶ÐRÑ×ÐRÑ×L˜ä0|È_«÷BŽÚ%q½TŸë6Íc¯úEÛ & q ½  S ž è 3 ~ É  _ © ô ? Š Ôi´þI“Þ(s½Rœæ0zÅY£í7Ë_¨ò<†Ðc­ö@‰Óf¯ùBŒÕh±úCŒÕh±úCŒÕf¯ø A Š Ò!!d!¬!õ">"†"Ï##`#¨#ñ$9$$Ê%%Z%£%ë&3&{&Ã' 'T'œ'ä(,(t(¼))L)“)Û*#*k*³*ú+B+Š+Ñ,,a,¨,ð-7--Æ..U.œ.ä/+/r/º00H00Ö11e1¬1ó2:22È33V33ä4+4q4¸4ÿ5F55Ó66a6¨6î757|7Â8 8O8–8Ü9#9i9°9ö:=:ƒ:Ê;;V;;ã<)>[>¡>ç?-?s?¹?ÿ@E@‹@ÑAA]A£AéB.BtBºCCFC‹CÑDD\D¢DèE-EsE¹EþFDF‰FÏGGZGŸGåH*HpHµHúI@I…IÊJJTJšJßK$KiK®KóL8L}LÁMMKMMÕNN^N£NçO,OpOµOùP>P‚PÆQ QOQ“QØRR`R¤RèS,SpS´SøTÊ{Ê·ÊôË0ËlË©ËåÌ"Ì^ÌšÌ×ÍÍPÍŒÍÉÎÎBÎ~κÎ÷Ï3ÏpϬÏéÐ%ÐbОÐÛÑÑTÑÑÍÒ ÒFÒƒÒ¿ÒüÓ8ÓuÓ±ÓîÔ*ÔgÔ¤ÔàÕÕYÕ–ÕÒÖÖLÖˆÖÅ××>×{×·×ôØ1ØmØªØæÙ#Ù`ÙÙÙÚÚSÚÚÌÛ ÛFÛ‚Û¿ÛüÜ9ÜvܲÜïÝ,ÝiݦÝãÞÞ\Þ™ÞÖßßPßßÊààDàà¾àûá8áuá²áïâ,âiâ¦âãã ã]ãšã×ääQäŽäËååFåƒåÀåýæ:æwæµæòç/çlç©ççè$èaèžèÜééVé“éÑêêKê‰êÆëëAë~ë»ëùì6ìtì±ìîí,íií§íäî"î_îîÚïïUï“ïÐððKð‰ðÆññBññ½ñúò8òvò³òñó.ólóªóçô%ôcô¡ôÞõõZõ—õÕööQööÌ÷ ÷H÷†÷Äøø?ø}ø»øùù7ùuù²ùðú.úlúªúèû&ûdû¢ûàüü\üšüØýýTý’ýÐþþLþŠþÈÿÿEÿƒÿÁÿÿMšç4Îg´Mšç3€Ìe±ýI•á-yÅ]©õ @ Œ Ø # o º  Q œ è 3 ~ É  _ « ö@‹Ö!l¶L–á+vÀ UŸé4~È\¦ð:ƒÍaªô=‡Ðc­ö?ˆÒd­ö?ˆÑb«ô<…Î_§ï8€È  Y ¡ é!1!y!Á" "Q"™"á#(#p#¸#ÿ$G$$Ö%%e%¬%ô&;&‚&É''X'Ÿ'æ(-(t(»))I))Ö**d*ª*ñ+7+~+Ä, ,Q,˜,Þ-$-j-±-÷.=.ƒ.É//U/›/á0'0l0²0ø1=1ƒ1È22S2™2Þ3#3h3­3ò474|4Á55K55Ô66]6¡6æ7*7n7²7ö8:8~8Â99J9Ž9Ñ::Y:œ:à;#;f;ª;í<0>I>‹>Î??T?–?Ù@@^@ @ãA%AgAªAìB.BpB²BôC6CxCºCüD>D€DÁEEEE†EÈF FKFŒFÍGGPG‘GÒHHUH–H×IIYIšIÛJJ]JJÞKK`K KáL"LbL£LãM$MdM¥MåN&NfN¦NçO'OgO§OçP(PhP¨PèQ(QhQ¨QçR'RgR§RçS'SfS¦SæT%TeT¤TäU#UcU¢UâV!V`V VßWW]WœWÜXXZX™XØYYVY•YÔZZQZZÏ[ [L[‹[É\\G\…\Ä]]A]]¾]ü^:^x^·^õ_3_q_¯_í`+`i`§`åa#aaažaÜbbWb•bÒccMc‹cÈddCd€d½dûe8eue²eïf,fif¦fãg g]g™gÖhhOhŒhÉiiBi~i»i÷j3jpj¬jèk$kakkÙllQllÉmmAm|m¸môn0nkn§nâooYo•oÐp pFp‚p½pøq3qnq©qärrYr”rÏs sDss¹sôt.tit£tÝuuRuŒuÆvv:vtv®vèw"w[w•wÏx xBx|xµxïy(yay›yÔz zGz€z¹zò{+{d{{Ö||G|€|¹|ñ}*}c}›}Ô~ ~D~}~µ~í%^–΀€>€v€®€æUÅü‚4‚k‚£‚ÚƒƒIƒƒ¸ƒð„'„_„–„Î……=…t…¬…ㆆR†Š†Á†ø‡0‡g‡Ÿ‡ÖˆˆEˆ}ˆ´ˆì‰#‰[‰’‰ÉŠŠ8ŠpЧŠß‹‹N‹…‹¼‹ôŒ+ŒcŒšŒÒ @x¯çŽŽUŽŽÄŽü3j¢ÙH·î‘%‘]‘”‘Ì’’:’r’©’à““O“†“¾“õ”-”d”›”Ó• •A•y•°•ç––V––Å–ü—3—j—¢—Ù˜˜H˜˜¶˜î™%™\™“™Ëšš9šqš¨šß››N›…›½›ôœ+œcœšœÒ @x¯çžžVžžÄžüŸ3ŸkŸ¢ŸÚ  I € ¸ ð¡'¡_¡–¡Î¢¢=¢u¢¬¢ä££S£‹£Â£ú¤2¤i¤¡¤Ù¥¥H¥€¥¸¥ï¦'¦_¦—¦Î§§>§v§­§å¨¨U¨¨Å¨ü©4©l©¤©ÜªªMª…ª½ªõ«.«f«Ÿ«×¬¬I¬¬º¬ó­,­d­­Ö®®I®‚®»®ô¯-¯g¯ ¯Ú°°M°†°À°ú±4±n±§±á²²U²²Ê³³>³x³³³í´(´b´´ØµµMµˆµÃµþ¶9¶t¶¯¶ê·%·`·œ·×¸¸N¸‰¸Å¹¹<¹x¹´¹ðº,ºhº¤ºà»»X»”»Ñ¼ ¼I¼†¼Â¼ÿ½<½y½µ½ò¾/¾l¾©¾ç¿$¿a¿Ÿ¿ÜÀÀWÀ•ÀÓÁÁNÁŒÁÊÂÂFÂ…ÂÃÃÃ@Ã~ýÃüÄ:ÄyĸÄ÷Å6ÅuÅ´ÅóÆ3ÆrƲÆñÇ1ÇpǰÇðÈ0ÈpȰÈðÉ0ÉpɱÉñÊ2ÊrʳÊóË4Ëu˵ËöÌ7Ìx̹ÌúÍ<Í};ÎÎA΃ÎÄÏÏHωÏËÐ ÐOБÐÓÑÑXÑšÑÜÒÒaÒ£ÒæÓ(ÓkÓ®ÓðÔ3ÔvÔ¹ÔüÕ>ÕÕÄÖÖJÖŽÖÑ××W×›×ÞØ!ØeبØìÙ/ÙsÙ·ÙúÚ>Ú‚ÚÆÛ ÛNÛ’ÛÖÜÜ^ܢܿÝ+ÝoÝ³ÝøÞ<ÞÞÅß ßOߓߨààbà§àëá0áuá»ââEâŠâÏããZãŸãåä*äpäµäûåAå‡åÌææXæžæäç*çpç¶çüèCè‰èÏéé\é¢ééê/êvê½ëëJë‘ëØììfì­ìôí;í‚íÉîîXîŸîæï.ïuï½ððLð”ðÜñ#ñkñ³ñûòCò‹òÓóódó¬óôô<ô…ôÍõõ^õ§õðö8ööÊ÷÷\÷¥÷îø7ø€øÉùù[ù¥ùîú8úúËûû^û§ûñü;ü…üÏýýcý­ý÷þAþ‹þÕÿ ÿjÿµÿÿK•à+uÀ UŸê4~É]§ñ;…Ïc­÷@ŠÔg±ú C Ö i ² û D Ž × i ² ú C Œ Õf¯ø@‰Ñb«ó;ƒÌ\¤ì4|Ä T›ã+sºI‘Ø g¯ö=„ÌZ¡è/v½J‘Øe¬ó9€Æ S™à&l²ù ? … Ë!!W!!ã"("n"´"ú#?#…#Ë$$V$›$á%&%l%±%ö&<&&Æ' 'P'•'Ú((d(©(î)3)x)½**F*‹*Ï++X++á,&,j,¯,ó-7-{-À..H.Œ.Ð//X/œ/à0$0g0«0ï121v1º1ý2@2„2Ç3 3N3‘3Ô44Z44à5#5f5¨5ë6.6p6³6ö787z7½7ÿ8A8„8Æ99J9Œ9Î::R:”:Ö;;Y;›;Ü<<_<¡<â=$=e=¦=è>)>j>«>ì?-?n?¯?ð@1@r@³@óA4AuAµAöB6BwB·B÷C8CxC¸CùD9DyD¹DùE9EyE¹EøF8FxF¸FøG7GwG¶GöH6HuH´HôI3IsI²IñJ0JpJ¯JîK-KlK«KêL)LhL§LæM$McM¢MáNN^NœNÛOOXO–OÕPPRPPÎQ QKQ‰QÇRRCRR¿RýS;SyS·SôT2TpT®TëU)UfU¤UâVV\VšV×WWRWWÌX XGX„XÁXþY;YxYµYòZ/ZlZ©Zæ["[_[œ[Ù\\R\\Ì]]E]]¾]ú^7^t^°^ì_)_e_¢_Þ``W`“`Ïa aHa„aÀaýb9bub±bíc)cec¡cÝddUd‘dÍe eEee½eøf4fpf¬fèg#g_g›gÖhhNh‰hÅii,>u>¿? ?R?›?å@.@x@ÁA ATAAæB/BxBÁC CSCœCåD.DwDÀE ERE›EãF,FuF½GGOG—GàH(HqH¹IIJI’IÚJ#JkJ³JûKDKŒKÔLLdL¬LôM×sש×ÞØØIØ~سØèÙÙSوٽÙòÚ'Ú\Ú‘ÚÆÚûÛ0ÛeÛšÛÎÜÜ8ÜmÜ¢ÜÖÝ Ý@ÝtÝ©ÝÞÞÞGÞ{Þ°ÞäßßM߂߶ßëààSàˆà¼àðá%áYááÂáöâ*â^â’âÇâûã/ãcã—ãËãÿä3ägä›äÏåå7åkåŸåÓææ;æoæ¢æÖç ç>çrç¥çÙè è@ètè¨èÛééCévéªéÝêêDêxê«êßëëFëyë­ëàììGìzì­ìáííGízí®íáîîGîzî­îáïïGïzï­ïàððFðyð¬ðßññEñxñªñÝòòCòvò©òÛóóAótó¦óÙô ô>ôqô¤ôÖõ õ;õnõ õÓöö8öjööÏ÷÷4÷g÷™÷Ë÷þø0øbø•øÇøùù+ù^ùùÂùôú&úXúŠú½úïû!ûSû…û·ûéüüMüü±üãýýFýxýªýÜþþ@þqþ£þÕÿÿ8ÿjÿœÿÍÿÿmft2UniConvertor-1.1.4/src/share/icc/sRGB_Color_Space_Profile.icm0000664000076400007640000000611011060500610022500 0ustar igorigor HLinomntrRGB XYZ Î 1acspMSFTIEC sRGBöÖÓ-HP cprtP3desc„lwtptðbkptrXYZgXYZ,bXYZ@dmndTpdmddĈvuedL†viewÔ$lumiømeas $tech0 rTRC< gTRC< bTRC< textCopyright (c) 1998 Hewlett-Packard CompanydescsRGB IEC61966-2.1sRGB IEC61966-2.1XYZ óQÌXYZ XYZ o¢8õXYZ b™·…ÚXYZ $ „¶ÏdescIEC http://www.iec.chIEC http://www.iec.chdesc.IEC 61966-2.1 Default RGB colour space - sRGB.IEC 61966-2.1 Default RGB colour space - sRGBdesc,Reference Viewing Condition in IEC61966-2.1,Reference Viewing Condition in IEC61966-2.1view¤þ_.ÏíÌ \žXYZ L VPWçmeassig CRT curv #(-27;@EJOTY^chmrw|†‹•šŸ¤©®²·¼ÁÆËÐÕÛàåëðöû %+28>ELRY`gnu|ƒ‹’š¡©±¹ÁÉÑÙáéòú &/8AKT]gqz„Ž˜¢¬¶ÁËÕàëõ !-8COZfr~Š–¢®ºÇÓàìù -;HUcq~Œš¨¶ÄÓáðþ +:IXgw†–¦µÅÕåö'7HYj{Œ¯ÀÑãõ+=Oat†™¬¿Òåø 2FZn‚–ª¾Òçû  % : O d y ¤ º Ï å û  ' = T j ˜ ® Å Ü ó " 9 Q i € ˜ ° È á ù  * C \ u Ž § À Ù ó & @ Z t Ž © Ã Þ ø.Id›¶Òî %A^z–³Ïì &Ca~›¹×õ1OmŒªÉè&Ed„£Ãã#Ccƒ¤Åå'Ij‹­Îð4Vx›½à&Il²ÖúAe‰®Ò÷@eНÕú Ek‘·Ý*QwžÅì;cвÚ*R{£ÌõGp™Ãì@j”¾é>i”¿ê  A l ˜ Ä ð!!H!u!¡!Î!û"'"U"‚"¯"Ý# #8#f#”#Â#ð$$M$|$«$Ú% %8%h%—%Ç%÷&'&W&‡&·&è''I'z'«'Ü( (?(q(¢(Ô))8)k))Ð**5*h*›*Ï++6+i++Ñ,,9,n,¢,×- -A-v-«-á..L.‚.·.î/$/Z/‘/Ç/þ050l0¤0Û11J1‚1º1ò2*2c2›2Ô3 3F33¸3ñ4+4e4ž4Ø55M5‡5Â5ý676r6®6é7$7`7œ7×88P8Œ8È99B99¼9ù:6:t:²:ï;-;k;ª;è<' >`> >à?!?a?¢?â@#@d@¦@çA)AjA¬AîB0BrBµB÷C:C}CÀDDGDŠDÎEEUEšEÞF"FgF«FðG5G{GÀHHKH‘H×IIcI©IðJ7J}JÄK KSKšKâL*LrLºMMJM“MÜN%NnN·OOIO“OÝP'PqP»QQPQ›QæR1R|RÇSS_SªSöTBTTÛU(UuUÂVV\V©V÷WDW’WàX/X}XËYYiY¸ZZVZ¦Zõ[E[•[å\5\†\Ö]']x]É^^l^½__a_³``W`ª`üaOa¢aõbIbœbðcCc—cëd@d”dée=e’eçf=f’fèg=g“géh?h–hìiCišiñjHjŸj÷kOk§kÿlWl¯mm`m¹nnknÄooxoÑp+p†pàq:q•qðrKr¦ss]s¸ttptÌu(u…uáv>v›vøwVw³xxnxÌy*y‰yçzFz¥{{c{Â|!||á}A}¡~~b~Â#„å€G€¨ kÍ‚0‚’‚ôƒWƒº„„€„ã…G…«††r†×‡;‡ŸˆˆiˆÎ‰3‰™‰þŠdŠÊ‹0‹–‹üŒcŒÊ1˜ÿŽfŽÎ6žnÖ‘?‘¨’’z’ã“M“¶” ”Š”ô•_•É–4–Ÿ— —u—à˜L˜¸™$™™üšhšÕ›B›¯œœ‰œ÷dÒž@ž®ŸŸ‹Ÿú i Ø¡G¡¶¢&¢–££v£æ¤V¤Ç¥8¥©¦¦‹¦ý§n§à¨R¨Ä©7©©ªª««u«é¬\¬Ð­D­¸®-®¡¯¯‹°°u°ê±`±Ö²K²Â³8³®´%´œµµŠ¶¶y¶ð·h·à¸Y¸Ñ¹J¹Âº;ºµ».»§¼!¼›½½¾ ¾„¾ÿ¿z¿õÀpÀìÁgÁãÂ_ÂÛÃXÃÔÄQÄÎÅKÅÈÆFÆÃÇAÇ¿È=ȼÉ:ɹÊ8Ê·Ë6˶Ì5̵Í5͵Î6ζÏ7ϸÐ9кÑ<ѾÒ?ÒÁÓDÓÆÔIÔËÕNÕÑÖUÖØ×\×àØdØèÙlÙñÚvÚûÛ€ÜÜŠÝÝ–ÞÞ¢ß)߯à6à½áDáÌâSâÛãcãëäsäü儿 æ–çç©è2è¼éFéÐê[êåëpëûì†ííœî(î´ï@ïÌðXðåñrñÿòŒóó§ô4ôÂõPõÞömöû÷Šøø¨ù8ùÇúWúçûwüü˜ý)ýºþKþÜÿmÿÿUniConvertor-1.1.4/src/share/fonts/0000755000076400007640000000000011211455344015666 5ustar igorigorUniConvertor-1.1.4/src/share/fonts/descr.txt0000664000076400007640000000004611060500604017521 0ustar igorigorThis folder is planned for font info. UniConvertor-1.1.4/src/share/ps_templates/0000755000076400007640000000000011211455344017235 5ustar igorigorUniConvertor-1.1.4/src/share/ps_templates/sk1-proc.ps0000775000076400007640000002614111060500604021241 0ustar igorigor%%BeginResource: procset Linux-sK-Procset 1.0 2 % version 1.0 0 released with Sketch 0.5.0, 0.5.1 and 0.5.2 % version 1.0 1 released with Sketch 0.5.3 % changes: rect procedure extended to allow rounded corners % version 1.0 2 released as patch for 0.6.3 and with 0.6.4 and 0.7.4 % changes: in skeps, redefine showpage after userdict begin % version 1.0 3 released with sK1 v0.9 % changes: CMYK colorspace support /SketchDict 100 dict def SketchDict begin /bd { bind def } bind def /x { exch } bd /xd { exch def } bd /PI 3.14159265358979323846264338327 def % convert radians to grad /radgrad { 180 mul PI div } bd /skstartmatrix matrix currentmatrix def /tmpmat matrix def /ISOLatin1Encoding dup where { pop pop } { [/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quoteright /parenleft /parenright /asterisk /plus /comma /minus /period /slash /zero /one /two /three /four /five /six /seven /eight /nine /colon /semicolon /less /equal /greater /question /at /A /B /C /D /E /F /G /H /I /J /K /L /M /N /O /P /Q /R /S /T /U /V /W /X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore /quoteleft /a /b /c /d /e /f /g /h /i /j /k /l /m /n /o /p /q /r /s /t /u /v /w /x /y /z /braceleft /bar /braceright /asciitilde /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /dotlessi /grave /acute /circumflex /tilde /macron /breve /dotaccent /dieresis /.notdef /ring /cedilla /.notdef /hungarumlaut /ogonek /caron /space /exclamdown /cent /sterling /currency /yen /brokenbar /section /dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered /cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] def } ifelse % define arct for level 1 /arct dup where {pop pop} { /arct {arcto pop pop pop pop} bd } ifelse /size 0 def /fontname 0 def /newfont 0 def % sf - /sf { /size xd /fontname xd fontname findfont % reencode fonts if they use the standard encoding dup /Encoding get StandardEncoding eq { dup length dict /newfont xd { 1 index /FID ne { newfont 3 1 roll put } { pop pop } ifelse } forall newfont /Encoding ISOLatin1Encoding put fontname newfont definefont } if size scalefont setfont } bd /pusht {matrix currentmatrix} bd /popt {setmatrix} bd /pushc {gsave} bd /popc {grestore} bd % rgb - /rgb {setrgbcolor} bd % cmyk - /cmyk {setcmykcolor} bd % w - /w { setlinewidth } bd % j - /j { setlinejoin } bd % J - /J { setlinecap } bd % [] d - /d { setdash } bd /F { eofill } bd /f { closepath F } bd /S { pusht skstartmatrix setmatrix stroke popt } bd /s { closepath S } bd /m { moveto } bd /l { lineto } bd /c { curveto } bd % [] txt [] % txt [] /txt { /tmpmat tmpmat currentmatrix def dup type /arraytype eq {concat} {translate} ifelse 0 0 m tmpmat } bd % [] T - % T - /T {txt x show popt} bd % [] P - % P - /P {txt x true charpath popt} bd % [] TP - % TP - /TP {txt x dup show 0 0 m true charpath popt} bd % C - /C {newpath 0 360 arc} bd % R - /R { 2 copy m x 2 index l x 2 index x l l closepath } bd % [] ellipse - % [] ellipse - /ellipse { dup type /arraytype eq { pusht x concat 0 0 1.0 C popt } { pusht 5 1 roll 4 -1 roll concat newpath dup 2 eq { % pie slice 0 0 m } if % save arc type 3 1 roll % convert radians to degree radgrad x radgrad x % center 0 0 and radius 1 0 0 1 5 -2 roll arc % close for pie slice and chord 0 ne { closepath } if popt } ifelse } bd % [] rect - % [] radius1 radius2 rect - /radius1 0 def /radius2 0 def /factor 0 def /rect { dup type /arraytype eq { % normal rectangle pusht x concat 0 0 m 1 0 l 1 1 l 0 1 l closepath popt } { % rectangle with round corners /radius2 xd /radius1 xd pusht x concat radius1 radius2 div 1 scale 0 radius2 m 0 1 radius2 1 radius2 arct radius2 radius1 div dup 1 1 index 0 radius2 arct 0 0 0 radius2 arct 0 0 0 1 radius2 arct closepath popt } ifelse } bd /buf 0 def /width 0 def /height 0 def % [] true skcimg - % false skcimg - /skcimg { % this needs the color extension of Level 1 or Level 2 /tmpmat tmpmat currentmatrix def { concat } if /height xd /width xd /buf width 3 mul string def width height scale width height 8 [width 0 0 height neg 0 height] { currentfile buf readhexstring pop } bind false 3 colorimage tmpmat setmatrix } bd % [] true skgimg - % false skgimg - /skgimg { /tmpmat tmpmat currentmatrix def { concat } if /height xd /width xd /buf width string def width height scale width height 8 [width 0 0 height neg 0 height] { currentfile buf readhexstring pop } bind image tmpmat setmatrix } bd % rclip - /rclip { 4 2 roll m dup 0 x rlineto x 0 rlineto neg 0 x rlineto closepath clip } bd % [] skeps /skeps { 10 dict begin /sk_state save def concat 3 index neg 3 index neg translate rclip 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin 10 setmiterlimit [ ] 0 setdash newpath /sk_dict_count countdictstack def /sk_count count 1 sub def userdict begin /showpage { } def /languagelevel where { pop languagelevel 1 ne { false setstrokeadjust false setoverprint } if } if } bd /skepsend { count sk_count sub { pop } repeat countdictstack sk_dict_count sub { end } repeat sk_state restore end } bd % % Gradient Patterns % /gradidx 0 def % num gradient /gradient { 3 mul array /gradidx 0 def } bd % array red green blue $ array /$ { 3 index gradidx 5 -1 roll put 2 index gradidx 1 add 4 -1 roll put 1 index gradidx 2 add 3 -1 roll put /gradidx gradidx 3 add def } bd % array ! array % repeat the last color specified /! { 3 { dup dup gradidx dup 3 1 roll 3 sub get put /gradidx gradidx 1 add def } repeat } bd % array idx gradcolor array red green blue /gradcolor { 3 mul dup 2 add 1 exch % idx 1 idx+2 { 1 index exch % array array i get % array component exch % component array } for 4 1 roll } bd % array x0 y0 x1 y1 axial - /x0 0 def /y0 0 def /x1 0 def /y1 0 def /left 0 def /right 0 def /top 0 def /bottom 0 def /numcolors 0 def /axial { /y1 xd /x1 xd /y0 xd /x0 xd dup length 3 idiv /numcolors xd pusht exch % ctm array x0 x1 ne y0 y1 ne or { x0 y0 translate [x1 x0 sub y1 y0 sub dup neg 2 index 0 0] concat clippath flattenpath pathbbox /top xd /right xd /bottom xd /left xd newpath 0 gradcolor rgb clippath f 0 1 numcolors 1 sub { dup numcolors div 3 1 roll gradcolor rgb exch bottom right top R f } for } if % pop the colors array pop % restore CTM popt } bd % array x y r0 r1 radial - %reuse: /x0 0 def /y0 0 def %reuse: /left 0 def /right 0 def /top 0 def /bottom 0 def %reuse: /numcolors 0 def /r0 0 def /r1 0 def /dr 0 def /radial { /r1 xd /r0 xd /y0 xd /x0 xd /dr r1 r0 sub def dup length 3 idiv /numcolors xd pusht exch % ctm array r0 r1 ne { x0 y0 translate clippath flattenpath pathbbox /top xd /right xd /bottom xd /left xd newpath dr 0 gt {numcolors 1 sub}{0} ifelse gradcolor rgb clippath f dr 0 gt {numcolors 1 sub -1 0} { 0 1 numcolors 1 sub} ifelse { dup numcolors div dr mul r0 add 3 1 roll gradcolor rgb exch 0 0 3 -1 roll C f } for } if % pop the colors array pop % restore CTM popt } bd % a b max /max { 2 copy lt {exch} if pop } bd % array x y angle conical - %reuse: /x0 0 def /y0 0 def %reuse: /numcolors 0 def /conical { pusht 5 1 roll 3 1 roll /y0 xd /x0 xd x0 y0 translate radgrad rotate dup length 3 idiv /numcolors xd clippath flattenpath pathbbox newpath 4 { abs 4 1 roll} repeat 3 { max } repeat 2 mul dup scale % now we can use radius 1 0 gradcolor rgb 0 0 1 0 360 arc f 1 1 numcolors 1 sub { dup numcolors div 180 mul 3 1 roll gradcolor rgb exch 0 0 moveto 0 0 1 4 -1 roll dup neg arc closepath f } for % pop the colors array pop % restore CTM popt } bd % width height ncomp trafo tileimage - % followed by hexdata %reuse: width height /XStep 0 def /YStep 0 def /imagedata 0 def /components 0 def /tileimage2 { exch 4 2 roll /height xd /width xd %<< mark /components 2 index /PatternType 1 /PaintType 1 /TilingType 1 /BBox [0 0 width height] /XStep width /YStep height /PaintProc { begin % XStep and YStep must contain the image size XStep YStep 8 % identity matrix, since the mapping from image % space to user space is already contained in % the trafo passed to makepattern matrix imagedata false components colorimage end } %>> counttomark 2 div cvi dup dict begin { def } repeat pop currentdict end % read the image and put into pattern dict % stack now: trafo ncomp patterndict dup /imagedata 4 -1 roll width height mul mul string currentfile exch readhexstring pop put % stack now: trafo patterndict exch makepattern setpattern clippath eofill } bd /tileimage1 { concat /components xd /height xd /width xd /imagedata currentfile width height mul components mul string readhexstring pop def clippath flattenpath pathbbox /top xd /right xd /bottom xd /left xd left width div floor width mul bottom height div floor height mul translate top bottom sub height div ceiling cvi { gsave right left sub width div ceiling cvi { width height 8 matrix components 1 eq { { imagedata } image } { imagedata false components colorimage } ifelse width 0 translate } repeat grestore 0 height translate } repeat } bd /makepattern where { pop /tileimage /tileimage2 load def } { /tileimage /tileimage1 load def } ifelse % end SketchDict end %%EndResource UniConvertor-1.1.4/src/share/ps_templates/sketch-proc.ps0000775000076400007640000002574611060500604022036 0ustar igorigor%%BeginResource: procset Linux-Sketch-Procset 1.0 2 % version 1.0 0 released with Sketch 0.5.0, 0.5.1 and 0.5.2 % version 1.0 1 released with Sketch 0.5.3 % changes: rect procedure extended to allow rounded corners % version 1.0 2 released as patch for 0.6.3 and with 0.6.4 and 0.7.4 % changes: in skeps, redefine showpage after userdict begin /SketchDict 100 dict def SketchDict begin /bd { bind def } bind def /x { exch } bd /xd { exch def } bd /PI 3.14159265358979323846264338327 def % convert radians to grad /radgrad { 180 mul PI div } bd /skstartmatrix matrix currentmatrix def /tmpmat matrix def /ISOLatin1Encoding dup where { pop pop } { [/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quoteright /parenleft /parenright /asterisk /plus /comma /minus /period /slash /zero /one /two /three /four /five /six /seven /eight /nine /colon /semicolon /less /equal /greater /question /at /A /B /C /D /E /F /G /H /I /J /K /L /M /N /O /P /Q /R /S /T /U /V /W /X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore /quoteleft /a /b /c /d /e /f /g /h /i /j /k /l /m /n /o /p /q /r /s /t /u /v /w /x /y /z /braceleft /bar /braceright /asciitilde /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /dotlessi /grave /acute /circumflex /tilde /macron /breve /dotaccent /dieresis /.notdef /ring /cedilla /.notdef /hungarumlaut /ogonek /caron /space /exclamdown /cent /sterling /currency /yen /brokenbar /section /dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered /cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] def } ifelse % define arct for level 1 /arct dup where {pop pop} { /arct {arcto pop pop pop pop} bd } ifelse /size 0 def /fontname 0 def /newfont 0 def % sf - /sf { /size xd /fontname xd fontname findfont % reencode fonts if they use the standard encoding dup /Encoding get StandardEncoding eq { dup length dict /newfont xd { 1 index /FID ne { newfont 3 1 roll put } { pop pop } ifelse } forall newfont /Encoding ISOLatin1Encoding put fontname newfont definefont } if size scalefont setfont } bd /pusht {matrix currentmatrix} bd /popt {setmatrix} bd /pushc {gsave} bd /popc {grestore} bd % rgb - /rgb {setrgbcolor} bd % w - /w { setlinewidth } bd % j - /j { setlinejoin } bd % J - /J { setlinecap } bd % [] d - /d { setdash } bd /F { eofill } bd /f { closepath F } bd /S { pusht skstartmatrix setmatrix stroke popt } bd /s { closepath S } bd /m { moveto } bd /l { lineto } bd /c { curveto } bd % [] txt [] % txt [] /txt { /tmpmat tmpmat currentmatrix def dup type /arraytype eq {concat} {translate} ifelse 0 0 m tmpmat } bd % [] T - % T - /T {txt x show popt} bd % [] P - % P - /P {txt x true charpath popt} bd % [] TP - % TP - /TP {txt x dup show 0 0 m true charpath popt} bd % C - /C {newpath 0 360 arc} bd % R - /R { 2 copy m x 2 index l x 2 index x l l closepath } bd % [] ellipse - % [] ellipse - /ellipse { dup type /arraytype eq { pusht x concat 0 0 1.0 C popt } { pusht 5 1 roll 4 -1 roll concat newpath dup 2 eq { % pie slice 0 0 m } if % save arc type 3 1 roll % convert radians to degree radgrad x radgrad x % center 0 0 and radius 1 0 0 1 5 -2 roll arc % close for pie slice and chord 0 ne { closepath } if popt } ifelse } bd % [] rect - % [] radius1 radius2 rect - /radius1 0 def /radius2 0 def /factor 0 def /rect { dup type /arraytype eq { % normal rectangle pusht x concat 0 0 m 1 0 l 1 1 l 0 1 l closepath popt } { % rectangle with round corners /radius2 xd /radius1 xd pusht x concat radius1 radius2 div 1 scale 0 radius2 m 0 1 radius2 1 radius2 arct radius2 radius1 div dup 1 1 index 0 radius2 arct 0 0 0 radius2 arct 0 0 0 1 radius2 arct closepath popt } ifelse } bd /buf 0 def /width 0 def /height 0 def % [] true skcimg - % false skcimg - /skcimg { % this needs the color extension of Level 1 or Level 2 /tmpmat tmpmat currentmatrix def { concat } if /height xd /width xd /buf width 3 mul string def width height scale width height 8 [width 0 0 height neg 0 height] { currentfile buf readhexstring pop } bind false 3 colorimage tmpmat setmatrix } bd % [] true skgimg - % false skgimg - /skgimg { /tmpmat tmpmat currentmatrix def { concat } if /height xd /width xd /buf width string def width height scale width height 8 [width 0 0 height neg 0 height] { currentfile buf readhexstring pop } bind image tmpmat setmatrix } bd % rclip - /rclip { 4 2 roll m dup 0 x rlineto x 0 rlineto neg 0 x rlineto closepath clip } bd % [] skeps /skeps { 10 dict begin /sk_state save def concat 3 index neg 3 index neg translate rclip 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin 10 setmiterlimit [ ] 0 setdash newpath /sk_dict_count countdictstack def /sk_count count 1 sub def userdict begin /showpage { } def /languagelevel where { pop languagelevel 1 ne { false setstrokeadjust false setoverprint } if } if } bd /skepsend { count sk_count sub { pop } repeat countdictstack sk_dict_count sub { end } repeat sk_state restore end } bd % % Gradient Patterns % /gradidx 0 def % num gradient /gradient { 3 mul array /gradidx 0 def } bd % array red green blue $ array /$ { 3 index gradidx 5 -1 roll put 2 index gradidx 1 add 4 -1 roll put 1 index gradidx 2 add 3 -1 roll put /gradidx gradidx 3 add def } bd % array ! array % repeat the last color specified /! { 3 { dup dup gradidx dup 3 1 roll 3 sub get put /gradidx gradidx 1 add def } repeat } bd % array idx gradcolor array red green blue /gradcolor { 3 mul dup 2 add 1 exch % idx 1 idx+2 { 1 index exch % array array i get % array component exch % component array } for 4 1 roll } bd % array x0 y0 x1 y1 axial - /x0 0 def /y0 0 def /x1 0 def /y1 0 def /left 0 def /right 0 def /top 0 def /bottom 0 def /numcolors 0 def /axial { /y1 xd /x1 xd /y0 xd /x0 xd dup length 3 idiv /numcolors xd pusht exch % ctm array x0 x1 ne y0 y1 ne or { x0 y0 translate [x1 x0 sub y1 y0 sub dup neg 2 index 0 0] concat clippath flattenpath pathbbox /top xd /right xd /bottom xd /left xd newpath 0 gradcolor rgb clippath f 0 1 numcolors 1 sub { dup numcolors div 3 1 roll gradcolor rgb exch bottom right top R f } for } if % pop the colors array pop % restore CTM popt } bd % array x y r0 r1 radial - %reuse: /x0 0 def /y0 0 def %reuse: /left 0 def /right 0 def /top 0 def /bottom 0 def %reuse: /numcolors 0 def /r0 0 def /r1 0 def /dr 0 def /radial { /r1 xd /r0 xd /y0 xd /x0 xd /dr r1 r0 sub def dup length 3 idiv /numcolors xd pusht exch % ctm array r0 r1 ne { x0 y0 translate clippath flattenpath pathbbox /top xd /right xd /bottom xd /left xd newpath dr 0 gt {numcolors 1 sub}{0} ifelse gradcolor rgb clippath f dr 0 gt {numcolors 1 sub -1 0} { 0 1 numcolors 1 sub} ifelse { dup numcolors div dr mul r0 add 3 1 roll gradcolor rgb exch 0 0 3 -1 roll C f } for } if % pop the colors array pop % restore CTM popt } bd % a b max /max { 2 copy lt {exch} if pop } bd % array x y angle conical - %reuse: /x0 0 def /y0 0 def %reuse: /numcolors 0 def /conical { pusht 5 1 roll 3 1 roll /y0 xd /x0 xd x0 y0 translate radgrad rotate dup length 3 idiv /numcolors xd clippath flattenpath pathbbox newpath 4 { abs 4 1 roll} repeat 3 { max } repeat 2 mul dup scale % now we can use radius 1 0 gradcolor rgb 0 0 1 0 360 arc f 1 1 numcolors 1 sub { dup numcolors div 180 mul 3 1 roll gradcolor rgb exch 0 0 moveto 0 0 1 4 -1 roll dup neg arc closepath f } for % pop the colors array pop % restore CTM popt } bd % width height ncomp trafo tileimage - % followed by hexdata %reuse: width height /XStep 0 def /YStep 0 def /imagedata 0 def /components 0 def /tileimage2 { exch 4 2 roll /height xd /width xd %<< mark /components 2 index /PatternType 1 /PaintType 1 /TilingType 1 /BBox [0 0 width height] /XStep width /YStep height /PaintProc { begin % XStep and YStep must contain the image size XStep YStep 8 % identity matrix, since the mapping from image % space to user space is already contained in % the trafo passed to makepattern matrix imagedata false components colorimage end } %>> counttomark 2 div cvi dup dict begin { def } repeat pop currentdict end % read the image and put into pattern dict % stack now: trafo ncomp patterndict dup /imagedata 4 -1 roll width height mul mul string currentfile exch readhexstring pop put % stack now: trafo patterndict exch makepattern setpattern clippath eofill } bd /tileimage1 { concat /components xd /height xd /width xd /imagedata currentfile width height mul components mul string readhexstring pop def clippath flattenpath pathbbox /top xd /right xd /bottom xd /left xd left width div floor width mul bottom height div floor height mul translate top bottom sub height div ceiling cvi { gsave right left sub width div ceiling cvi { width height 8 matrix components 1 eq { { imagedata } image } { imagedata false components colorimage } ifelse width 0 translate } repeat grestore 0 height translate } repeat } bd /makepattern where { pop /tileimage /tileimage2 load def } { /tileimage /tileimage1 load def } ifelse % end SketchDict end %%EndResource UniConvertor-1.1.4/src/modules/0000755000076400007640000000000011211455344015103 5ustar igorigorUniConvertor-1.1.4/src/modules/pstokenize/0000755000076400007640000000000011211455344017276 5ustar igorigorUniConvertor-1.1.4/src/modules/pstokenize/pschartab.c0000664000076400007640000000505611060500611021407 0ustar igorigor#define WHITESPACE 0x0001 #define NEWLINE 0x0002 #define DELIMITER 0x0004 #define COMMENT 0x0008 #define DIGIT 0x0010 #define INTCHAR 0x0020 #define FLOATCHAR 0x0040 #define NAMECHAR 0x0100 static int char_types[] = { 0x001, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, /* '\000' .. '\007' */ 0x100, 0x001, 0x003, 0x100, 0x003, 0x003, 0x100, 0x100, /* '\010' .. '\017' */ 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, /* '\020' .. '\027' */ 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, /* '\030' .. '\037' */ 0x001, 0x100, 0x100, 0x100, 0x100, 0x008, 0x100, 0x100, /* ' ' .. '\'' */ 0x004, 0x004, 0x100, 0x160, 0x100, 0x160, 0x140, 0x004, /* '(' .. '/' */ 0x170, 0x170, 0x170, 0x170, 0x170, 0x170, 0x170, 0x170, /* '0' .. '7' */ 0x170, 0x170, 0x100, 0x100, 0x004, 0x100, 0x004, 0x100, /* '8' .. '?' */ 0x100, 0x100, 0x100, 0x100, 0x100, 0x140, 0x100, 0x100, /* '@' .. 'G' */ 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, /* 'H' .. 'O' */ 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, /* 'P' .. 'W' */ 0x100, 0x100, 0x100, 0x004, 0x100, 0x004, 0x100, 0x100, /* 'X' .. '_' */ 0x100, 0x100, 0x100, 0x100, 0x100, 0x140, 0x100, 0x100, /* '`' .. 'g' */ 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, /* 'h' .. 'o' */ 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, /* 'p' .. 'w' */ 0x100, 0x100, 0x100, 0x004, 0x100, 0x004, 0x100, 0x100, /* 'x' .. '' */ 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, /* '\200' .. '\207' */ 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, /* '\210' .. '\217' */ 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, /* '\220' .. '\227' */ 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, /* '\230' .. '\237' */ 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, /* ' ' .. '§' */ 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, /* '¨' .. '¯' */ 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, /* '°' .. '·' */ 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, /* '¸' .. '¿' */ 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, /* 'À' .. 'Ç' */ 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, /* 'È' .. 'Ï' */ 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, /* 'Ð' .. '×' */ 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, /* 'Ø' .. 'ß' */ 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, /* 'à' .. 'ç' */ 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, /* 'è' .. 'ï' */ 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, /* 'ð' .. '÷' */ 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, /* 'ø' .. 'ÿ' */ }; UniConvertor-1.1.4/src/modules/pstokenize/filterobj.h0000664000076400007640000001066711060500611021431 0ustar igorigor/* * Copyright (C) 1998, 1999 by Bernhard Herzog. * * All Rights Reserved * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear in * supporting documentation, and that the name of the author not be * used in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef FILTEROBJ_H #define FILTEROBJ_H #include #if defined(__cplusplus) extern "C" { #endif typedef size_t (*filter_read_proc)(void *, PyObject * source, char * buffer, size_t length); typedef size_t (*filter_write_proc)(void *, PyObject * target, const char * buffer, size_t length); typedef int (*filter_close_proc)(void *, PyObject * target); typedef void (*filter_dealloc_proc)(void * client_data); PyObject * Filter_NewEncoder(PyObject * target, const char * filtername, int flags, filter_write_proc, filter_close_proc, filter_dealloc_proc, void * client_data); PyObject * Filter_NewDecoder(PyObject * source, const char * filtername, int flags, filter_read_proc, filter_close_proc, filter_dealloc_proc, void * client_data); /* decoder methods */ size_t Filter_Read(PyObject * filter, char * buffer, size_t length); size_t Filter_ReadToChar(PyObject * filter, char * buffer, size_t length, int character); PyObject * Filter_GetLine(PyObject * filter, int); int Filter_Ungetc(PyObject * filter, int); /* encoder methods */ int Filter_Write(PyObject * filter, const char * buffer, size_t length); int Filter_Flush(PyObject * filter, int flush_target); /* common filter methods */ int Filter_Close(PyObject * filter); #define FILTER_CLOSED 0x0001 #define FILTER_EOF 0x0002 #define FILTER_BAD 0x0004 #define FILTER_CLOSE_STREAM 0x0100 typedef struct { PyObject_HEAD char * buffer; char * buffer_end; char * current; char * end; char * base; int flags; long streampos; PyObject * stream; /* source or target */ PyObject * filtername; filter_read_proc read; filter_write_proc write; filter_close_proc close; filter_dealloc_proc dealloc; void * client_data; } FilterObject; extern DL_IMPORT(PyTypeObject) FilterType; #define Filter_Check(op) ((op)->ob_type == &FilterType) int _Filter_Underflow(FilterObject*); int _Filter_Overflow(FilterObject*, int); #define __Filter_PUTC(filter, c, overflow)\ ((filter)->current >= (filter)->end \ ? (overflow)((filter),(unsigned char)(c))\ : (unsigned char)(*((filter)->current++) = (c))) #define __Filter_GETC(filter, underflow)\ ((filter)->current >= (filter)->end ? (underflow)(filter)\ : *(unsigned char*)((filter)->current++)) #define Filter_PUTC(filter, c) __Filter_PUTC((filter), (c), _Filter_Overflow) #define Filter_GETC(filter) __Filter_GETC((filter), _Filter_Underflow) typedef struct { int (*Filter_Underflow)(FilterObject*); int (*Filter_Overflow)(FilterObject*, int); /* decoder methods */ size_t (*Filter_Read)(PyObject * filter, char * buffer, size_t length); size_t (*Filter_ReadToChar)(PyObject * filter, char * buffer, size_t length, int character); PyObject * (*Filter_GetLine)(PyObject * filter, int); int (*Filter_Ungetc)(PyObject*, int); /* endcoder methods */ int (*Filter_Write)(PyObject * filter, const char * buffer, size_t length); int (*Filter_Flush)(PyObject * filter, int flush_target); /* common filter methods */ int (*Filter_Close)(PyObject * filter); } Filter_Functions; #define Filter_DL_PUTC(func, filter, c) \ __Filter_PUTC((filter), (c), ((func)->Filter_Overflow)) #define Filter_DL_GETC(func, filter) \ __Filter_GETC((filter), ((func)->Filter_Underflow)) #if defined(__cplusplus) } #endif #endif /* FILTEROBJ_H */ UniConvertor-1.1.4/src/modules/pstokenize/pstokenize.c0000664000076400007640000004135511060500611021635 0ustar igorigor/* Sketch - A Python-based interactive drawing program * Copyright (C) 1998, 1999, 2000, 2001, 2003, 2006 by Bernhard Herzog * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Functions to tokenize PostScript-files. */ #include #include #include #include "filterobj.h" #include "pschartab.c" static PyObject * Filter_Type = NULL; static Filter_Functions *filter_functions = NULL; typedef struct { PyObject_HEAD FilterObject * source; int beginning_of_line; char ai_pseudo_comments; char ai_dsc; } PSTokenizerObject; staticforward PyTypeObject PSTokenizerType; #define NAME 258 #define INT 259 #define FLOAT 260 #define STRING 261 #define MAX_DATA_TOKEN 261 #define OPERATOR 262 #define DSC_COMMENT 263 #define END 264 #define GETC() (Filter_DL_GETC((filter_functions), (self->source))) #define BACK(c) \ (filter_functions->Filter_Ungetc(((PyObject*)self->source), (c))) static int read_newline(PSTokenizerObject * self, int c) { if (c == '\r') { c = GETC(); if (c != '\n') { BACK(c); } } self->beginning_of_line = 1; return 0; } /* Return the contents of a DSC-comment as a string object. * * The stream is assumed to be positioned after the '%%' or '%!'. * Afterwards, the stream is positioned at the beginning of the next * line or EOF. The string does not contain the final newline character. */ static PyObject * read_dsc_comment(PSTokenizerObject * self) { int size; int maxsize = 256; PyObject * value; char * buf, * end; value = PyString_FromStringAndSize((char*)NULL, maxsize); if (!value) return NULL; buf = PyString_AsString(value); end = buf + maxsize; for (;;) { int c = GETC(); if (c == EOF) break; *buf++ = c; if ((char_types[c] & NEWLINE) == NEWLINE) { read_newline(self, c); buf -= 1; break; } if (buf == end) { size = maxsize; maxsize = maxsize + 1000; if (_PyString_Resize(&value, maxsize) < 0) return NULL; buf = PyString_AsString(value) + size; end = PyString_AsString(value) + maxsize; } } if (buf < end) { size = buf - PyString_AsString(value); if (_PyString_Resize(&value, size) < 0) return NULL; } return value; } static void discard_comment(PSTokenizerObject * self) { int c; for (;;) { c = GETC(); if (c == EOF) return; if (char_types[c] & NEWLINE) { read_newline(self, c); return; } } } /* Read a comment. The stream is assumed to be positioned just after the * initial '%'. If the comment is a DSC-comment (the next char is a '%' * or a '!' and the comment is at the beginning of a line), the contents * of the comment (without the leading '%%' or '%!') are returned as a * string object. * * In a normal comments discard all input until the next newline and * return NULL. * * Furthermore, if ai_pseudo_comments is true, recognize Adobe * Illustrator pseudo comments, which start with '%_' and discard just * these two characters. The rest of the comment is treated as normal * input. */ static PyObject * read_comment(PSTokenizerObject * self) { int c; PyObject * value = NULL; c = GETC(); if (self->beginning_of_line && (c == '%' || c == '!')) { value = read_dsc_comment(self); } else { if (c == '_' && self->ai_pseudo_comments) { } else if (self->beginning_of_line && c == 'A' && self->ai_dsc) { BACK(c); value = read_dsc_comment(self); } else if (c != EOF && (char_types[c] & NEWLINE)) read_newline(self, c); else discard_comment(self); } return value; } /* * Return the PostScript string literal as a Python string object. * * The stream is assumed to be positioned just after the initial '('. * Afterwards, the stream is positioned just after the closing ')'. */ static PyObject * read_string(PSTokenizerObject * self) { int depth = 0; int size; int maxsize = 256; PyObject * value; char * buf, * end; value = PyString_FromStringAndSize((char*)NULL, maxsize); if (!value) return NULL; buf = PyString_AsString(value); end = buf + maxsize; for (;;) { int c = GETC(); switch (c) { case EOF: /* end of input in string constant! */ Py_DECREF(value); PyErr_SetString(PyExc_EOFError, "unexpected end of input"); return NULL; case '(': depth += 1; *buf++ = c; break; case ')': depth -= 1; if (depth < 0) { size = buf - PyString_AsString(value); if (_PyString_Resize(&value, size) < 0) { return NULL; } return value; } else *buf++ = c; break; case '\\': c = GETC(); switch(c) { case 'b': *buf++ = '\b'; break; case 'f': *buf++ = '\f'; break; case 'n': *buf++ = '\n'; break; case 'r': *buf++ = '\r'; break; case 't': *buf++ = '\t'; break; case '\\': *buf++ = '\\'; break; case '(': *buf++ = '('; break; case ')': *buf++ = ')'; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': { int code = c - '0'; c = GETC(); if ('0' <= c && c <= '7') { code = code * 8 + c - '0'; c = GETC(); if ('0' <= c && c <= '7') { code = code * 8 + c - '0'; c = GETC(); } } *buf++ = code; BACK(c); } break; case '\r': c = GETC(); if (c != '\n') BACK(c); break; case '\n': break; default: *buf++ = c; } break; case '\r': c = GETC(); if (c != '\n') BACK(c); *buf++ = '\n'; break; default: *buf++ = c; } if (buf == end) { size = maxsize; maxsize = maxsize + 1000; if (_PyString_Resize(&value, maxsize) < 0) return NULL; buf = PyString_AsString(value) + size; end = PyString_AsString(value) + maxsize; } } /* for (;;) */ /* unreachable */ return NULL; } /* * Return the PostScript hex string literal as a Python string object. * * The stream is assumed to be positioned just after the initial '<'. * Afterwards, the stream is positioned just after the closing '>'. */ static PyObject * read_hex_string(PSTokenizerObject * self) { int size; int maxsize = 256; int last_digit = -1, digit; PyObject * value; char * buf, * end; value = PyString_FromStringAndSize((char*)NULL, maxsize); if (!value) return NULL; buf = PyString_AsString(value); end = buf + maxsize; for (;;) { int c = GETC(); digit = -1; switch (c) { case EOF: /* end of input in string constant! */ Py_DECREF(value); PyErr_SetString(PyExc_EOFError, "unexpected end of input"); return NULL; case '>': size = buf - PyString_AsString(value); if (_PyString_Resize(&value, size) < 0) { return NULL; } return value; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': digit = c - '0'; break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': digit = c - 'A' + 10; break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': digit = c - 'a' + 10; break; default: if (!(char_types[c] & WHITESPACE)) { Py_DECREF(value); PyErr_SetString(PyExc_SyntaxError, "invalid character in hex string"); return NULL; } } if (digit >= 0) { if (last_digit < 0) { last_digit = digit; } else { *buf++ = last_digit * 16 + digit; last_digit = -1; } if (buf == end) { size = maxsize; maxsize = maxsize + 1000; if (_PyString_Resize(&value, maxsize) < 0) return NULL; buf = PyString_AsString(value) + size; end = PyString_AsString(value) + maxsize; } } } /* for (;;) */ /* unreachable */ return NULL; } /* * */ static PyObject * read_name_or_number(PSTokenizerObject * self, int * token, int isname) { int size; int maxsize = 256; PyObject * value; char * buf, * end; *token = 0; value = PyString_FromStringAndSize((char*)NULL, maxsize); if (!value) return NULL; buf = PyString_AsString(value); end = buf + maxsize; for (;;) { int c = GETC(); if (c == EOF) break; if ((char_types[c] & NAMECHAR) == 0) { BACK(c); *buf = '\0'; break; } *buf++ = c; if (buf == end) { size = maxsize; maxsize = maxsize + 1000; if (_PyString_Resize(&value, maxsize) < 0) return NULL; buf = PyString_AsString(value) + size; end = PyString_AsString(value) + maxsize; } } /* check for a number */ if (!isname) { char * start = PyString_AsString(value); char * p = start; char * numend; while(char_types[(int)*p] & INTCHAR) p += 1; if (char_types[(int)*p] & FLOATCHAR) { char * old_locale; double result; /* Change LC_NUMERIC locale to "C" around the strtod * call so that it parses the number correctly. */ old_locale = strdup(setlocale(LC_NUMERIC, NULL)); setlocale(LC_NUMERIC, "C"); result = strtod(start, &numend); setlocale(LC_NUMERIC, old_locale); free(old_locale); if (numend == buf) { Py_DECREF(value); *token = FLOAT; return PyFloat_FromDouble(result); } } else { int result = strtol(start, &numend, 10); if (numend == buf) { Py_DECREF(value); *token = INT; return PyInt_FromLong(result); } } } if (buf < end) { size = buf - PyString_AsString(value); if (_PyString_Resize(&value, size) < 0) return NULL; } *token = OPERATOR; return value; } static PyObject * pslex(PSTokenizerObject * self) { int token = 0; PyObject * value = NULL; PyObject * result; int c, ctype; while (token == 0) { c = GETC(); if (c != '%') self->beginning_of_line = 0; switch (c) { case EOF: Py_INCREF(Py_None); value = Py_None; token = END; break; case '%': value = read_comment(self); if (value) token = DSC_COMMENT; break; case '[': case ']': case '{': case '}': /* a single character token */ { char buf[2] = "\000\000"; buf[0] = c; value = PyString_FromString(buf); token = OPERATOR; } break; case '(': value = read_string(self); token = STRING; break; case '<': /* this case should check the next character to also recognize the << operator and base85 encoded strings */ value = read_hex_string(self); token = STRING; break; case '/': value = read_name_or_number(self, &token, 1); token = NAME; break; default: ctype = char_types[c]; if (ctype & WHITESPACE) { while (ctype & WHITESPACE) { self->beginning_of_line = (ctype & NEWLINE) == NEWLINE; c = GETC(); if (c == EOF) break; ctype = char_types[c]; } if (c != EOF) BACK(c); } else if (ctype & NAMECHAR) { BACK(c); /* NAMECHAR includes digits */ value = read_name_or_number(self, &token, 0); } else { PyErr_Format(PyExc_IOError, "unexpected character %d (flags %.4x)", c, ctype); token = -1; } } /* switch */ } /* while token == 0 */ if (token < 0 || value == NULL) return NULL; result = Py_BuildValue("(iO)", token, value); Py_DECREF(value); return result; } /* * */ static PyObject * pstokenizer_next(PSTokenizerObject * self, PyObject * args) { return pslex(self); } /* * */ static PyObject * pstokenizer_next_dsc(PSTokenizerObject * self, PyObject * args) { PyObject * result = NULL; int c; for (;;) { c = GETC(); if (c == EOF) break; else if (char_types[c] & NEWLINE) { read_newline(self, c); } else if (c == '%') { result = read_comment(self); if (result) break; } else { self->beginning_of_line = 0; } } if (!result) { result = PyString_FromString(""); } return result; } /* * */ static PyObject * pstokenizer_read(PSTokenizerObject * self, PyObject * args) { PyObject * result = NULL; long length, read; if (!PyArg_ParseTuple(args, "l", &length)) return NULL; result = PyString_FromStringAndSize(NULL, length); if (!result) return NULL; read = filter_functions->Filter_Read((PyObject*)(self->source), PyString_AsString(result), length); if (read == 0 && PyErr_Occurred()) { Py_DECREF(result); return NULL; } if (_PyString_Resize(&result, read) < 0) return NULL; return result; } /* * */ static PyObject * PSTokenizer_FromStream(FilterObject * filter) { PSTokenizerObject * self; self = PyObject_New(PSTokenizerObject, &PSTokenizerType); if (!self) return NULL; Py_INCREF(filter); self->source = filter; self->beginning_of_line = 1; self->ai_pseudo_comments = 0; self->ai_dsc = 0; return (PyObject*)self; } static void pstokenizer_dealloc(PSTokenizerObject * self) { Py_DECREF(self->source); PyObject_Del(self); } static PyObject * pstokenizer_repr(PSTokenizerObject * self) { char buf[1000]; PyObject * streamrepr; streamrepr = PyObject_Repr((PyObject*)(self->source)); if (!streamrepr) return NULL; sprintf(buf, "", PyString_AsString(streamrepr)); Py_DECREF(streamrepr); return PyString_FromString(buf); } #define OFF(x) offsetof(PSTokenizerObject, x) static struct memberlist pstokenizer_memberlist[] = { {"source", T_OBJECT, OFF(source), RO}, {"ai_pseudo_comments", T_BYTE, OFF(ai_pseudo_comments)}, {"ai_dsc", T_BYTE, OFF(ai_dsc)}, {NULL} }; static struct PyMethodDef pstokenizer_methods[] = { {"next", (PyCFunction)pstokenizer_next, 1}, {"next_dsc", (PyCFunction)pstokenizer_next_dsc, 1}, {"read", (PyCFunction)pstokenizer_read, 1}, {NULL, NULL} }; static PyObject * pstokenizer_getattr(PyObject * self, char * name) { PyObject * result; result = Py_FindMethod(pstokenizer_methods, self, name); if (result != NULL) return result; PyErr_Clear(); return PyMember_Get((char *)self, pstokenizer_memberlist, name); } static int pstokenizer_setattr(PyObject * self, char * name, PyObject * v) { if (v == NULL) { PyErr_SetString(PyExc_AttributeError, "can't delete object attributes"); return -1; } return PyMember_Set((char *)self, pstokenizer_memberlist, name, v); } static PyTypeObject PSTokenizerType = { PyObject_HEAD_INIT(NULL) 0, "pstokenizer", sizeof(PSTokenizerObject), 0, (destructor)pstokenizer_dealloc,/*tp_dealloc*/ (printfunc)0, /*tp_print*/ pstokenizer_getattr, /*tp_getattr*/ pstokenizer_setattr, /*tp_setattr*/ (cmpfunc)0, /*tp_compare*/ (reprfunc)pstokenizer_repr, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ }; /* * */ static PyObject * pstokenizer_new(PyObject * self, PyObject * args) { FilterObject * source; if (!PyArg_ParseTuple(args, "O!", Filter_Type, &source)) return NULL; return PSTokenizer_FromStream(source); } /* * Method table and module initialization */ static PyMethodDef pstokenize_functions[] = { {"PSTokenizer", pstokenizer_new, 1}, {NULL, NULL} }; static void add_int(PyObject * dict, int i, char * name) { PyObject *v; v = Py_BuildValue("i", i); if (!v) PyErr_Clear(); if (PyDict_SetItemString(dict, name, v) < 0) PyErr_Clear(); } DL_EXPORT(void) initpstokenize(void) { PyObject * d, *m, *r, *filter; PSTokenizerType.ob_type = &PyType_Type; m = Py_InitModule("pstokenize", pstokenize_functions); d = PyModule_GetDict(m); #define ADD_INT(name) add_int(d, name, #name) ADD_INT(NAME); ADD_INT(INT); ADD_INT(FLOAT); ADD_INT(STRING); ADD_INT(OPERATOR); ADD_INT(DSC_COMMENT); ADD_INT(END); ADD_INT(MAX_DATA_TOKEN); /* import some objects from filter */ filter = PyImport_ImportModule("streamfilter"); if (filter) { Filter_Type = PyObject_GetAttrString(filter, "FilterType"); if (!Filter_Type) return; r = PyObject_GetAttrString(filter, "Filter_Functions"); if (!r) return; filter_functions = (Filter_Functions*)PyCObject_AsVoidPtr(r); Py_DECREF(r); } } UniConvertor-1.1.4/src/modules/skmod/0000755000076400007640000000000011211455344016220 5ustar igorigorUniConvertor-1.1.4/src/modules/skmod/sktrafo.c0000664000076400007640000003104511060500613020032 0ustar igorigor/* Sketch - A Python-based interactive drawing program * Copyright (C) 1997, 1998, 2000, 2002, 2006 by Bernhard Herzog * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "skpoint.h" #include "skrect.h" #include "sktrafo.h" /* Trafo implements a 2D affine transformation T: * * / x \ / m11 m12 \ / x \ / v1 \ * T * | | = | | | | + | | * \ y / \ m21 m22 / \ y / \ v2 / * * * or, in homogeneous coordinates: * * / m11 m12 v1 \ / x \ * | | | | * ^= | m21 m22 v2 | | y | * | | | | * \ 0 0 1 / \ 1 / * * * SKTrafo objects are immutable. */ /* exception raised when a singular matrix is inverted */ PyObject * SKTrafo_ExcSingular = NULL; #define SKTRAFO_COUNT_ALLOC 1 #if SKTRAFO_COUNT_ALLOC static int allocated = 0; #endif PyObject * SKTrafo_FromDouble(double m11, double m21, double m12, double m22, double v1, double v2) { SKTrafoObject * self; self = PyObject_New(SKTrafoObject, &SKTrafoType); if (self == NULL) return NULL; self->m11 = m11; self->m12 = m12; self->m21 = m21; self->m22 = m22; self->v1 = v1; self->v2 = v2; #if SKTRAFO_COUNT_ALLOC allocated++; #endif return (PyObject*)self; } static void sktrafo_dealloc(SKTrafoObject * self) { PyObject_Del(self); #if SKTRAFO_COUNT_ALLOC allocated--; #endif } /* there is no true way to order transforms, so simply test for equality */ static int sktrafo_compare(SKTrafoObject * v, SKTrafoObject * w) { if (v == w || (v->m11 == w->m11 && v->m12 == w->m12 && v->m21 == w->m21 && v->m22 == w->m22 && v->v1 == w->v1 && v->v2 == w->v2)) return 0; return v < w ? -1 : +1; } static PyObject * sktrafo_repr(SKTrafoObject * self) { char buf[1000]; sprintf(buf, "Trafo(%.10g, %.10g, %.10g, %.10g, %.10g, %.10g)", self->m11, self->m21, self->m12, self->m22, self->v1, self->v2); return PyString_FromString(buf); } /* * trafo(OBJ) * * If OBJ is a SKPoint return the transformed point. * * If OBJ is a tuple of two floats treat it as a point. Return a * SKPoint. (Should a tuple be returned?) * * If OBJ is a SKTrafo return the concatenation of trafo and OBJ (such * that trafo(OBJ)(x) == trafo(OBJ(x)) * * If OBJ is a SKRect return the the bounding rect of the transformed * corners of OBJ as a SKRect. (an alternative would be to return the * transformed rect as a list of points) * */ static PyObject * sktrafo_call(SKTrafoObject * self, PyObject * args, PyObject * kw) { PyObject * arg; double x, y; if (PyTuple_Size(args) == 2) arg = args; else if (!PyArg_ParseTuple(args, "O", &arg)) return NULL; if (skpoint_extract_xy(arg, &x, &y)) { return SKPoint_FromXY(self->m11 * x + self->m12 * y + self->v1, self->m21 * x + self->m22 * y + self->v2); } else if (SKTrafo_Check(arg)) { register SKTrafoObject * t = (SKTrafoObject *) arg; return SKTrafo_FromDouble(self->m11 * t->m11 + self->m12 * t->m21, self->m21 * t->m11 + self->m22 * t->m21, self->m11 * t->m12 + self->m12 * t->m22, self->m21 * t->m12 + self->m22 * t->m22, self->m11*t->v1 + self->m12*t->v2 + self->v1, self->m21*t->v1 + self->m22*t->v2 +self->v2); } else if (SKRect_Check(arg)) { SKRectObject * result; register SKRectObject * r = (SKRectObject*)arg; if (r == SKRect_InfinityRect || r == SKRect_EmptyRect) { Py_INCREF(r); return (PyObject*)r; } result = (SKRectObject*) \ SKRect_FromDouble(self->m11 * r->left + self->m12 * r->top, self->m21 * r->left + self->m22 * r->top, self->m11 * r->right + self->m12 * r->bottom, self->m21 * r->right + self->m22 * r->bottom); if (result) { SKRect_AddXY(result, self->m11 * r->right + self->m12 * r->top, self->m21 * r->right + self->m22 * r->top); SKRect_AddXY(result, self->m11 * r->left + self->m12 * r->bottom, self->m21 * r->left + self->m22 * r->bottom); result->left += self->v1; result->right += self->v1; result->top += self->v2; result->bottom += self->v2; } return (PyObject*) result; } PyErr_SetString(PyExc_TypeError, "SKTrafo must be applied to SKPoints, " "SKRects or SKTrafos"); return NULL; } #define OFF(x) offsetof(SKTrafoObject, x) static struct memberlist sktrafo_memberlist[] = { {"m11", T_DOUBLE, OFF(m11), RO}, {"m12", T_DOUBLE, OFF(m12), RO}, {"m21", T_DOUBLE, OFF(m21), RO}, {"m22", T_DOUBLE, OFF(m22), RO}, {"v1", T_DOUBLE, OFF(v1), RO}, {"v2", T_DOUBLE, OFF(v2), RO}, {NULL} /* Sentinel */ }; /* methods */ /* trafo.DocToWin(POINT) * or: trafo.DocToWin(X, Y) * * Return the point POINT or (X, Y) in window coordinates as a tuple of ints. * POINT may be an SKPointObject or any sequence of two numbers. */ PyObject * sktrafo_DocToWin(SKTrafoObject * self, PyObject * args) { PyObject * arg; double docx, docy; int x, y; if (PyTuple_Size(args) == 2) arg = args; else if (!PyArg_ParseTuple(args, "O", &arg)) return NULL; if (skpoint_extract_xy(arg, &docx, &docy)) { x = ceil(self->m11 * docx + self->m12 * docy + self->v1); y = ceil(self->m21 * docx + self->m22 * docy + self->v2); return Py_BuildValue("ii", x, y); } PyErr_SetString(PyExc_TypeError, "arguments must be either be two numbers, " "a point or a sequence of two numbers"); return NULL; } /* trafo.DTransform(POINT) * * Transform POINT as a vector. This means that the translation is not * applied. (In homogeneous coordinates: if POINT is SKPoint(x, y), treat it as * (x, y, 0) and not (x, y, 1)) */ static PyObject * sktrafo_dtransform(SKTrafoObject * self, PyObject * args) { PyObject * arg; double x, y; if (PyTuple_Size(args) == 2) arg = args; else if (!PyArg_ParseTuple(args, "O", &arg)) return NULL; if (skpoint_extract_xy(arg, &x, &y)) { return SKPoint_FromXY(self->m11 * x + self->m12 * y, self->m21 * x + self->m22 * y); } PyErr_SetString(PyExc_TypeError, "arguments must be either be two numbers, " "a point or a sequence of two numbers"); return NULL; } /* * trafo.inverse() * * Return the inverse of trafo. If the matrix is singular, raise a * SingularMatrix exception. */ static PyObject * sktrafo_inverse(SKTrafoObject * self, PyObject * args) { double det = self->m11 * self->m22 - self->m12 * self->m21; double m11, m12, m21, m22; if (det == 0.0) { PyErr_SetString(SKTrafo_ExcSingular, "inverting singular matrix"); return NULL; } m11 = self->m22 / det; m12 = -self->m12 / det; m21 = -self->m21 / det; m22 = self->m11 / det; return SKTrafo_FromDouble(m11, m21, m12, m22, -m11 * self->v1 - m12 * self->v2, -m21 * self->v1 - m22 * self->v2); } /* * */ static PyObject * sktrafo_offset(SKTrafoObject * self, PyObject * args) { return SKPoint_FromXY(self->v1, self->v2); } static PyObject * sktrafo_matrix(SKTrafoObject * self, PyObject * args) { return Py_BuildValue("dddd", self->m11, self->m21, self->m12, self->m22); } /* trafo.coeff() * * Return the coefficients of trafo as a 6-tuple in the order used in * PostScript: (m11, m21, m12, m22, v1, v2) */ static PyObject * sktrafo_coeff(SKTrafoObject * self, PyObject * args) { return Py_BuildValue("dddddd", self->m11, self->m21, self->m12, self->m22, self->v1, self->v2); } /* * */ static struct PyMethodDef sktrafo_methods[] = { {"DocToWin", (PyCFunction)sktrafo_DocToWin, 1}, {"DTransform", (PyCFunction)sktrafo_dtransform, 1}, {"inverse", (PyCFunction)sktrafo_inverse, 1}, {"offset", (PyCFunction)sktrafo_offset, 1}, {"matrix", (PyCFunction)sktrafo_matrix, 1}, {"coeff", (PyCFunction)sktrafo_coeff, 1}, {NULL, NULL} }; static PyObject * sktrafo_getattr(PyObject * self, char * name) { PyObject * result; result = Py_FindMethod(sktrafo_methods, self, name); if (result != NULL) return result; PyErr_Clear(); return PyMember_Get((char *)self, sktrafo_memberlist, name); } PyTypeObject SKTrafoType = { PyObject_HEAD_INIT(NULL) 0, "sktrafo", sizeof(SKTrafoObject), 0, (destructor)sktrafo_dealloc, /*tp_dealloc*/ (printfunc)NULL, /*tp_print*/ sktrafo_getattr, /*tp_getattr*/ 0, /*tp_setattr*/ (cmpfunc)sktrafo_compare, /*tp_compare*/ (reprfunc)sktrafo_repr, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ (ternaryfunc)sktrafo_call, /* tp_call */ }; /* * Module Functions */ PyObject * sktrafo_allocated(PyObject * self, PyObject * args) { #if SKTRAFO_COUNT_ALLOC return PyInt_FromLong(allocated); #else return PyInt_FromLong(-1); #endif } /* * sktrafo.Trafo([m11, m21, m12, m22, v1, v2]) * * Return the transformation given by the m_i_j and the v_i (see above). * All arguments are optional. They default to the values of an identity * transformation. Note: the order of the parameters is the same as for * a transformation matrix in PostScript. */ PyObject * sktrafo_sktrafo(PyObject * self, PyObject * args) { /* default trafo is identity */ double m11 = 1.0, m12 = 0.0, m21 = 0.0, m22 = 1.0; double v1 = 0.0, v2 = 0.0; if (!PyArg_ParseTuple(args, "|dddddd", &m11, &m21, &m12, &m22, &v1, &v2)) return NULL; return SKTrafo_FromDouble(m11, m21, m12, m22, v1, v2); } /* * Some standard transformations */ /* Scale: XXX allow specification of a centerpoint. */ PyObject * sktrafo_scale(PyObject * self, PyObject * args) { double factorx, factory; if (PyTuple_Size(args) == 1) { if (!PyArg_ParseTuple(args, "d", &factorx)) return NULL; factory = factorx; } else { if (!PyArg_ParseTuple(args, "dd", &factorx, &factory)) return NULL; } return SKTrafo_FromDouble(factorx, 0.0, 0.0, factory, 0.0, 0.0); } PyObject * sktrafo_translation(PyObject * self, PyObject * args) { double offx, offy; if (PyTuple_Size(args) == 1) { PyObject * point; if (!PyArg_ParseTuple(args, "O", &point)) return NULL; if (!skpoint_extract_xy(point, &offx, &offy)) { PyErr_SetString(PyExc_ValueError, "Offset must be a point object " "or a tuple of floats"); return NULL; } } else if (!PyArg_ParseTuple(args, "dd", &offx, &offy)) return NULL; return SKTrafo_FromDouble(1.0, 0.0, 0.0, 1.0, offx, offy); } PyObject * sktrafo_rotation(PyObject * self, PyObject * args) { double angle, cx = 0.0, cy = 0.0; double s, c, offx, offy; if (PyTuple_Size(args) == 2) { PyObject * point; if (!PyArg_ParseTuple(args, "dO", &angle, &point)) return NULL; if (!skpoint_extract_xy(point, &cx, &cy)) { PyErr_SetString(PyExc_ValueError, "Center must be a point object " "or a tuple of floats"); return NULL; } } else if (!PyArg_ParseTuple(args, "d|dd", &angle, &cx, &cy)) return NULL; s = sin(angle); c = cos(angle); /* compute offset. * The rotation around center is * T(p) = center + M * (p - center) * => offset = center - M * center */ offx = cx - c * cx + s * cy; offy = cy - s * cx - c * cy; return SKTrafo_FromDouble(c, s, -s, c, offx, offy); } /* * C Functions for other modules */ /* * Apply SELF to the point (x, y) and store the result in *OUT_X, *OUT_Y. */ #define SELF ((SKTrafoObject*)self) void SKTrafo_TransformXY(PyObject * self, double x, double y, SKCoord * out_x, SKCoord * out_y) { if (SKTrafo_Check(self)) { *out_x = SELF->m11 * x + SELF->m12 * y + SELF->v1; *out_y = SELF->m21 * x + SELF->m22 * y + SELF->v2; } } /* * Apply SELF to the vector (x, y) and store the result in *OUT_X, *OUT_Y. * (see the comments for sktrafo_dtransform() above. */ void SKTrafo_DTransformXY(PyObject * self, double x, double y, SKCoord * out_x, SKCoord * out_y) { if (SKTrafo_Check(self)) { *out_x = SELF->m11 * x + SELF->m12 * y; *out_y = SELF->m21 * x + SELF->m22 * y; } } #undef SELF UniConvertor-1.1.4/src/modules/skmod/sktrafo.h0000664000076400007640000000354311060500613020041 0ustar igorigor/* Sketch - A Python-based interactive drawing program * Copyright (C) 1997, 1998 by Bernhard Herzog * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef SKTRAFO_MODULE_H #define SKTRAFO_MODULE_H #if defined(__cplusplus) extern "C" { #endif #include "skpoint.h" typedef struct { PyObject_HEAD double m11, m21, m12, m22, v1, v2; } SKTrafoObject; extern PyTypeObject SKTrafoType; #define SKTrafo_Check(v) ((v)->ob_type == &SKTrafoType) PyObject * SKTrafo_FromDouble(double m11, double m21, double m12, double m22, double v1, double v2); void SKTrafo_TransformXY(PyObject * trafo, double x, double y, SKCoord * out_x, SKCoord * out_y); void SKTrafo_DTransformXY(PyObject * trafo, double x, double y, SKCoord * out_x, SKCoord * out_y); extern PyObject * SKTrafo_ExcSingular; PyObject * sktrafo_rotation(PyObject * self, PyObject * args); PyObject * sktrafo_translation(PyObject * self, PyObject * args); PyObject * sktrafo_scale(PyObject * self, PyObject * args); PyObject * sktrafo_sktrafo(PyObject * self, PyObject * args); PyObject * sktrafo_allocated(PyObject * self, PyObject * args); #if defined(__cplusplus) } #endif #endif /* SKTRAFO_MODULE_H */ UniConvertor-1.1.4/src/modules/skmod/curveobject.c0000664000076400007640000013641011060500613020676 0ustar igorigor/* Sketch - A Python-based interactive drawing program * Copyright (C) 1997 -- 2006 by Bernhard Herzog * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* a poly bezier object */ #include #include #include #include #include #include #include "skpoint.h" #include "skrect.h" #include "sktrafo.h" #include "curveobject.h" #include "curvelow.h" #include "curvemisc.h" /* * gcc stuff */ #ifndef __GNUC__ #define FUNCTION_NAME "" #else #define FUNCTION_NAME __FUNCTION__ #endif /* * Methods for segments */ static void init_segment(CurveSegment * segment, int type) { segment->type = type; segment->cont = ContAngle; segment->selected = 0; segment->x1 = segment->y1 = segment->x2 = segment->y2 = 0.0; segment->x = segment->y = 0.0; } /* compute the new position of a control point, given the other one and * the continuity at the node */ static void SKCurve_AdjustControlPoint(SKCoord * x, SKCoord * y, double cur_x, double cur_y, double node_x, double node_y, int cont) { switch (cont) { case ContSymmetrical: *x = 2 * node_x - cur_x; *y = 2 * node_y - cur_y; break; case ContSmooth: { double dx = cur_x - node_x; double dy = cur_y - node_y; double length = hypot(*x - node_x, *y - node_y); double cur_length = hypot(dx, dy); if (cur_length < 0.1) cur_length = 0.1; *x = node_x - length * dx / cur_length; *y = node_y - length * dy / cur_length; break; } default: break; } } #define CURVE_BLOCK_LEN 9 #define ROUNDUP(n, block) ((n)>0 ? (((n)+(block)-1)/(block))*(block) : (block)) static int paths_allocated = 0; PyObject * SKCurve_New(int length) { SKCurveObject * self; int i; self = PyObject_New(SKCurveObject, &SKCurveType); if (self == NULL) return NULL; length = ROUNDUP(length, CURVE_BLOCK_LEN); self->len = 0; self->closed = 0; self->segments = malloc(length * sizeof(CurveSegment)); if (!self->segments) { PyObject_Del(self); return PyErr_NoMemory(); } self->allocated = length; for (i = 0; i < self->allocated; i++) { init_segment(self->segments + i, CurveLine); } paths_allocated++; return (PyObject *)self; } /* * */ static int check_index(SKCurveObject * self, int index, const char * funcname) { if (index < 0) index = index + self->len; if (index < 0 || index >= self->len) { char message[1000]; sprintf(message, "%s: index out of range", funcname); PyErr_SetString(PyExc_IndexError, message); return -1; } return index; } /* * update internal data */ /* Check the state of self and make corrections if necessary. If WARN, * print some warning messages to stderr if corrections are made. * * In particular, do these things: XXX: update * */ static void curve_check_state(SKCurveObject * self, int warn, const char * funcname) { } /* Resize the bezier path SELF to have at least NEW_LEN allocated * segments. * * Return true if successful, otherwise, return false and set an * exception. */ static int curve_realloc(SKCurveObject * self, int new_len) { new_len = ROUNDUP(new_len, CURVE_BLOCK_LEN); if (new_len != self->allocated) { CurveSegment * new_segments; new_segments = realloc(self->segments, new_len * sizeof(CurveSegment)); if (!new_segments) { PyErr_NoMemory(); return 0; } self->segments = new_segments; self->allocated = new_len; } return 1; } /* Make certain that there are at least GROW free nodes. * * Return true if successful, return false and set an exception * otherwise. */ static int curve_grow(SKCurveObject * self, int grow) { return curve_realloc(self, self->len + grow); } /* * Some standard python methods */ static void curve_dealloc(SKCurveObject * self) { free(self->segments); PyObject_Del(self); paths_allocated--; } static int curve_compare(SKCurveObject * v, SKCurveObject * w) { /* there is no true way to order curves, so for now, compare the * pointers instead. We should compare all the segments... */ if (v == w) return 0; return v < w ? -1 : +1; } static PyObject * curve_repr(SKCurveObject * self) { char buf[100]; sprintf(buf, "", (long)self, self->len); return PyString_FromString(buf); } /* * Bezier specific methods */ /* * curve.duplicate() * * Return a new SKCurveObject object that is a copy of self. This is * essentially the same as the GraphicsObject.Duplicate() method. */ static PyObject * curve_duplicate(SKCurveObject * self, PyObject * args) { SKCurveObject * copy; int i; copy = (SKCurveObject*)SKCurve_New(self->len); if (!copy) return NULL; copy->len = self->len; copy->closed = self->closed; for (i = 0; i < self->len; i++) copy->segments[i] = self->segments[i]; return (PyObject*)copy; } /* * curve.node(IDX) * * Return the node at position IDX as an SKPoint object. A negative IDX * is interpreted like a negative list index in Python. */ static PyObject * curve_node(SKCurveObject * self, PyObject *args) { int idx; if (!PyArg_ParseTuple(args, "i", &idx)) return NULL; if (idx < 0) idx = idx + self->len; if (idx < 0 || idx >= self->len) { PyErr_SetString(PyExc_IndexError, "curve_node: index out of range"); return NULL; } return SKPoint_FromXY(self->segments[idx].x, self->segments[idx].y); } /* * curve.node_list() * * Return all nodes of path as a list of SKPoint objects. For a closed * curve, the last node is omitted, since it is identical to the first. */ static PyObject * curve_node_list(SKCurveObject * self, PyObject *args) { int i, length; CurveSegment * segment; PyObject * list, *point; if (!PyArg_ParseTuple(args, "")) return NULL; length = self->len; if (self->closed) length -= 1; list = PyList_New(length); if (!list) return NULL; segment = self->segments; for (i = 0; i < length; i++, segment++) { point = SKPoint_FromXY(segment->x, segment->y); if (!point) { Py_DECREF(list); return NULL; } PyList_SetItem(list, i, point); } return list; } /* * curve.continuity(IDX) * * Return the continuity of node IDX. The continuity is one of * ContAngle, ContSmooth or ContSymmetrical. A negative IDX is * interpreted like a negative list index in Python */ static PyObject * curve_continuity(SKCurveObject * self, PyObject * args) { int idx; if (!PyArg_ParseTuple(args, "i", &idx)) return NULL; if (idx < 0) idx = idx + self->len; if (idx < 0 || idx >= self->len) { PyErr_SetString(PyExc_IndexError, "curve_continuity: index out of range"); return NULL; } return PyInt_FromLong(self->segments[idx].cont); } /* * curve.segment_type(IDX) * * Return the type of segment IDX. The type is one of Bezier, Line or * Gap. A negative IDX is interpreted like a negative list index in * Python. */ static PyObject * curve_segment_type(SKCurveObject * self, PyObject * args) { int idx; if (!PyArg_ParseTuple(args, "i", &idx)) return NULL; if (idx < 0) idx = idx + self->len; if (idx < 0 || idx >= self->len) { PyErr_SetString(PyExc_IndexError, "curve_segment_type: index out of range"); return NULL; } return PyInt_FromLong(self->segments[idx].type); } /* * curve.segment(IDX) */ static PyObject * curve_segment(SKCurveObject * self, PyObject * args) { int idx; CurveSegment * segment; PyObject * result, *p1, *p2, *p; if (!PyArg_ParseTuple(args, "i", &idx)) return NULL; idx = check_index(self, idx, "path.Segment"); if (idx < 0) return NULL; segment = self->segments + idx; p = SKPoint_FromXY(segment->x, segment->y); if (segment->type == CurveBezier) { p1 = SKPoint_FromXY(segment->x1, segment->y1); p2 = SKPoint_FromXY(segment->x2, segment->y2); result = Py_BuildValue("i(OO)Oi", segment->type, p1, p2, p, segment->cont); Py_XDECREF(p1); Py_XDECREF(p2); } else { result = Py_BuildValue("i()Oi", segment->type, p, segment->cont); } Py_XDECREF(p); return result; } /* * */ static PyObject * curve_set_continuity(SKCurveObject * self, PyObject * args) { int idx, cont; if (!PyArg_ParseTuple(args, "ii", &idx, &cont)) return NULL; if (idx < 0) idx = idx + self->len; if (idx < 0 || idx >= self->len) { PyErr_SetString(PyExc_IndexError, "curve_set_continuity: index out of range"); return NULL; } if (!CHECK_CONT(cont)) { PyErr_SetString(PyExc_ValueError, "curve_set_continuity: " "cont must be one of ContAngle, ContSmooth " "or ContSymmetrical"); return NULL; } self->segments[idx].cont = cont; if (self->closed) { if (idx == 0) self->segments[self->len - 1].cont = cont; else if (idx == self->len - 1) self->segments[0].cont = cont; } Py_INCREF(Py_None); return Py_None; } /* Append a new segment to the curve. * * This only works correctly when the path is not closed. If the path is * closed a new node should be inserted, not appended. * * Return true if successful, otherwise, return false and set an * exception. */ int SKCurve_AppendSegment(SKCurveObject * self, CurveSegment * segment) { if (self->len == 0 && segment->type == CurveBezier) { PyErr_SetString(PyExc_TypeError, "The first segment added to a curve must be a line"); return 0; } if (!curve_grow(self, 1)) return 0; self->segments[self->len] = *segment; self->len += 1; curve_check_state(self, 1, FUNCTION_NAME); return 1; } int SKCurve_AppendLine(SKCurveObject * self, double x, double y, int continuity) { CurveSegment segment; segment.type = CurveLine; segment.cont = continuity; segment.selected = 0; segment.x = x; segment.y = y; return SKCurve_AppendSegment(self, &segment); } int SKCurve_AppendBezier(SKCurveObject * self, double x1, double y1, double x2, double y2, double x, double y, int continuity) { CurveSegment segment; segment.type = CurveBezier; segment.cont = continuity; segment.selected = 0; segment.x1 = x1; segment.y1 = y1; segment.x2 = x2; segment.y2 = y2; segment.x = x; segment.y = y; return SKCurve_AppendSegment(self, &segment); } /* create full undo, i.e. undo info for nodes AND segments */ static PyObject * set_nodes_and_segments_string = NULL; /* init on import*/ static PyObject * curve_create_full_undo(SKCurveObject * self) { PyObject * undo_segments; PyObject * result; CurveSegment * segments; segments = malloc(self->allocated * sizeof(CurveSegment)); if (!segments) return PyErr_NoMemory(); memcpy(segments, self->segments, self->allocated * sizeof(CurveSegment)); undo_segments = PyCObject_FromVoidPtr(segments, free); if (!undo_segments) { free(segments); return NULL; } result = Py_BuildValue("OOiii", set_nodes_and_segments_string, undo_segments, self->len, self->allocated, self->closed); Py_DECREF(undo_segments); return result; } /* undo nodes and segments */ static PyObject * curve__set_nodes_and_segments(SKCurveObject * self, PyObject * args) { int allocated = -1, length = -1, closed = 0; PyObject * undo_segments = NULL; PyObject * result; if (!PyArg_ParseTuple(args, "O!iii", &PyCObject_Type, &undo_segments, &length, &allocated, &closed)) return NULL; result = curve_create_full_undo(self); if (!result) return NULL; if (!curve_realloc(self, allocated)) { Py_DECREF(result); return NULL; } memcpy(self->segments, PyCObject_AsVoidPtr(undo_segments), allocated * sizeof(CurveSegment)); self->allocated = allocated; self->len = length; self->closed = closed; curve_check_state(self, 1, FUNCTION_NAME); return result; } /* * Hit tests */ static PyObject * curve_hit_point(SKCurveObject * self, PyObject * args) { SKRectObject * rect; int i, result = 0; CurveSegment * segment; if (!PyArg_ParseTuple(args, "O!", &SKRectType, &rect)) return NULL; segment = self->segments; for (i = 0; i < self->len; i++, segment++) { if (SKRect_ContainsXY(rect, segment->x, segment->y)) result = 1; } return PyInt_FromLong(result); } /* * Bounding boxes */ /* curve.coord_rect([TRAFO]) * * Return the smallest aligned rectangle that contains all nodes and * control points. With optional argument TRAFO, compute the rectangle * as if the path were transformed by the transformation TRAFO. */ static PyObject * curve_coord_rect(SKCurveObject * self, PyObject * args) { SKRectObject * rect = NULL; CurveSegment * segment; PyObject * trafo = NULL; int i; if (!PyArg_ParseTuple(args, "|O!", &SKTrafoType, &trafo)) return NULL; if (self->len == 0) { Py_INCREF(SKRect_EmptyRect); return (PyObject*)SKRect_EmptyRect; } segment = self->segments; if (!trafo) { rect = (SKRectObject*)SKRect_FromDouble(segment->x, segment->y, segment->x, segment->y); if (!rect) return NULL; segment += 1; for (i = 1; i < self->len; i++, segment++) { SKRect_AddXY(rect, segment->x, segment->y); if (segment->type == CurveBezier) { SKRect_AddXY(rect, segment->x1, segment->y1); SKRect_AddXY(rect, segment->x2, segment->y2); } } } else { SKCoord x, y; SKTrafo_TransformXY(trafo, segment->x, segment->y, &x, &y); rect = (SKRectObject*)SKRect_FromDouble(x, y, x, y); if (!rect) return NULL; segment += 1; for (i = 1; i < self->len; i++, segment++) { SKTrafo_TransformXY(trafo, segment->x, segment->y, &x, &y); SKRect_AddXY(rect, x, y); if (segment->type == CurveBezier) { SKTrafo_TransformXY(trafo, segment->x1, segment->y1, &x, &y); SKRect_AddXY(rect, x, y); SKTrafo_TransformXY(trafo, segment->x2, segment->y2, &x, &y); SKRect_AddXY(rect, x, y); } } } return (PyObject*)rect; } /* Add the `real' bounding rectangle of the bezier segment given by the Ps to * RECT. `real' means that it is as tight as possible, not just the bounding * box of the Ps. * * Assumes that the end points are already accounted for */ #define DISCR(p1,p2,p3,p4) (p1*p4 - p1*p3 - p2*p3 - p2*p4 + p2*p2 + p3*p3) #define DENOM(p1,p2,p3,p4) (p1 - 3 * p2 + 3 * p3 - p4) static void add_bezier_rect(SKRectObject * rect, double p1x, double p1y, double p2x, double p2y, double p3x, double p3y, double p4x, double p4y) { double discr, denom, p; discr = DISCR(p1x, p2x, p3x, p4x); if (discr >= 0) { double p13 = 3 * p1x, p23 = 3 * p2x, p33 = 3 * p3x; double c1 = p23 - p1x - p33 + p4x; double c2 = p13 - 2 * p23 + p33; double c3 = p23 - p13; double t; denom = DENOM(p1x, p2x, p3x, p4x); if (denom) { discr = sqrt(discr); p = p1x - 2 * p2x + p3x; t = (p + discr) / denom; if (0 < t && t < 1) SKRect_AddX(rect, ((c1 * t + c2) * t + c3) * t + p1x); t = (p - discr) / denom; if (0 < t && t < 1) SKRect_AddX(rect, ((c1 * t + c2) * t + c3) * t + p1x); } else { denom = p1x - 2 * p2x + p3x; if (denom) { t = 0.5 * (p1x - p2x) / denom; if (0 < t && t < 1) SKRect_AddX(rect, ((c1 * t + c2) * t + c3) * t + p1x); } } } discr = DISCR(p1y, p2y, p3y, p4y); if (discr >= 0) { double p13 = 3 * p1y, p23 = 3 * p2y, p33 = 3 * p3y; double c1 = p23 - p1y - p33 + p4y; double c2 = p13 - 2 * p23 + p33; double c3 = p23 - p13; double t; denom = DENOM(p1y, p2y, p3y, p4y); if (denom) { discr = sqrt(discr); p = p1y - 2 * p2y + p3y; t = (p + discr) / denom; if (0 < t && t < 1) SKRect_AddY(rect, ((c1 * t + c2) * t + c3) * t + p1y); t = (p - discr) / denom; if (0 < t && t < 1) SKRect_AddY(rect, ((c1 * t + c2) * t + c3) * t + p1y); } else { denom = p1y - 2 * p2y + p3y; if (denom) { t = 0.5 * (p1y - p2y) / denom; if (0 < t && t < 1) SKRect_AddY(rect, ((c1 * t + c2) * t + c3) * t + p1y); } } } } static PyObject * curve_accurate_rect(SKCurveObject * self, PyObject * args) { SKRectObject * rect = NULL; CurveSegment * segment; PyObject * trafo = NULL; int i; if (!PyArg_ParseTuple(args, "|O!", &SKTrafoType, &trafo)) return NULL; if (self->len == 0) { Py_INCREF(SKRect_EmptyRect); return (PyObject*)SKRect_EmptyRect;; } segment = self->segments; if (!trafo) { rect = (SKRectObject*)SKRect_FromDouble(segment->x, segment->y, segment->x, segment->y); if (!rect) return NULL; segment += 1; for (i = 1; i < self->len; i++, segment++) { SKRect_AddXY(rect, segment->x, segment->y); if (segment->type == CurveBezier) { add_bezier_rect(rect, segment[-1].x, segment[-1].y, segment->x1, segment->y1, segment->x2, segment->y2, segment->x, segment->y); } } } else { SKCoord x, y; SKTrafo_TransformXY(trafo, segment->x, segment->y, &x, &y); rect = (SKRectObject*)SKRect_FromDouble(x, y, x, y); if (!rect) return NULL; segment += 1; for (i = 1; i < self->len; i++, segment++) { SKTrafo_TransformXY(trafo, segment->x, segment->y, &x, &y); SKRect_AddXY(rect, x, y); if (segment->type == CurveBezier) { SKCoord p1x, p1y, p2x, p2y, p3x, p3y; SKTrafo_TransformXY(trafo, segment[-1].x, segment[-1].y, &p1x, &p1y); SKTrafo_TransformXY(trafo, segment->x1, segment->y1, &p2x, &p2y); SKTrafo_TransformXY(trafo, segment->x2, segment->y2, &p3x, &p3y); add_bezier_rect(rect, p1x, p1y, p2x, p2y, p3x, p3y, x, y); } } } return (PyObject*)rect; } /* * Transformation */ int SKCurve_Transform(SKCurveObject * self, PyObject * trafo) { int i; CurveSegment * segment; segment = self->segments; for (i = 0; i < self->len; i++, segment++) { SKTrafo_TransformXY(trafo, segment->x, segment->y, &segment->x, &segment->y); if (segment->type == CurveBezier) { SKTrafo_TransformXY(trafo, segment->x1, segment->y1, &segment->x1, &segment->y1); SKTrafo_TransformXY(trafo, segment->x2, segment->y2, &segment->x2, &segment->y2); } } return 0; } static PyObject * curve_apply_trafo(SKCurveObject * self, PyObject * args) { PyObject * trafo; PyObject * undo; if (!PyArg_ParseTuple(args, "O!", &SKTrafoType, &trafo)) return NULL; undo = curve_create_full_undo(self); if (!undo) return NULL; SKCurve_Transform(self, trafo); return undo; } static PyObject * curve_apply_translation(SKCurveObject * self, PyObject * args) { double x, y; int i; CurveSegment * segment; if (!PyArg_ParseTuple(args, "dd", &x, &y)) { PyObject * sequence; PyErr_Clear(); if (!PyArg_ParseTuple(args, "O", &sequence)) return NULL; if (!skpoint_extract_xy(sequence, &x, &y)) { PyErr_SetString(PyExc_TypeError, "argument is neither number nor sequence of two numbers"); return NULL; } } segment = self->segments; for (i = 0; i < self->len; i++, segment++) { segment->x += x; segment->y += y; if (segment->type == CurveBezier) { segment->x1 += x; segment->y1 += y; segment->x2 += x; segment->y2 += y; } } Py_INCREF(Py_None); return Py_None; } /* * close the contour */ #define SWAP(tmp,a,b) tmp = a; a = b; b = tmp static PyObject * undo_close_string = NULL; /* initialized in initbezierobj()*/ static PyObject * curve__undo_close(SKCurveObject * self, PyObject * args) { int closed = 0, itemp; double last_x, last_y, dtemp; int first_cont, last_cont; int lastidx = self->len - 1; if (!PyArg_ParseTuple(args, "iiidd", &closed, &first_cont, &last_cont, &last_x, &last_y)) return NULL; SWAP(itemp, self->segments[0].cont, first_cont); SWAP(dtemp, self->segments[lastidx].x, last_x); SWAP(dtemp, self->segments[lastidx].y, last_y); SWAP(itemp, self->segments[lastidx].cont, last_cont); self->closed = closed; if (self->segments[lastidx].type == CurveBezier) { self->segments[lastidx].x2 += self->segments[lastidx].x - last_x; self->segments[lastidx].y2 += self->segments[lastidx].y - last_y; } curve_check_state(self, 1, FUNCTION_NAME); return Py_BuildValue("Oiiidd", undo_close_string, !self->closed, first_cont, last_cont, last_x, last_y); } #undef SWAP int SKCurve_ClosePath(SKCurveObject * self) { double last_x, last_y; int lastidx = self->len - 1; if (lastidx <= 0) { return 0; } last_x = self->segments[lastidx].x; last_y = self->segments[lastidx].y; self->segments[lastidx].x = self->segments[0].x; self->segments[lastidx].y = self->segments[0].y; self->segments[0].cont = self->segments[lastidx].cont = ContAngle; self->closed = 1; if (self->segments[lastidx].type == CurveBezier) { self->segments[lastidx].x2 += self->segments[lastidx].x - last_x; self->segments[lastidx].y2 += self->segments[lastidx].y - last_y; } curve_check_state(self, 1, FUNCTION_NAME); return 0; } static PyObject * curve_close_contour(SKCurveObject * self, PyObject * args) { double last_x, last_y; int first_cont, last_cont; int lastidx = self->len - 1; if (lastidx <= 0) { Py_INCREF(Py_None); return Py_None; } first_cont = self->segments[0].cont; last_x = self->segments[lastidx].x; last_y = self->segments[lastidx].y; last_cont = self->segments[lastidx].cont; SKCurve_ClosePath(self); return Py_BuildValue("Oiiidd", undo_close_string, 0, first_cont, last_cont, last_x, last_y); } /* * saving and loading */ static int save_segment(PyObject * list, int i, CurveSegment * segment) { PyObject * tuple; if (segment->type == CurveBezier) { tuple = Py_BuildValue("ddddddi", segment->x1, segment->y1, segment->x2, segment->y2, segment->x, segment->y, segment->cont); } else { tuple = Py_BuildValue("ddi", segment->x, segment->y, segment->cont); } if (!tuple) { Py_DECREF(list); return 0; } if (PyList_SetItem(list, i, tuple) == -1) { Py_DECREF(tuple); Py_DECREF(list); return 0; } return 1; } /* curve.get_save() * * Convert self to a list of tuples. Each tuple may have one of these * forms: * * (X, Y, CONT) * * A line segment ending at (X, Y) with the continuity CONT * at the end point. * * (X1, Y1, X2, Y2, X3, Y3, CONT) * * A bezier segment with the control points (X1, Y1) and * (X2, Y2) and the node (X3, Y3) with continuity CONT. * */ static PyObject * curve_get_save(SKCurveObject * self, PyObject * args) { CurveSegment * segment; PyObject * list; int i; list = PyList_New(self->len); if (!list) return NULL; segment = self->segments; for (i = 0; i < self->len; i++, segment++) { if (!save_segment(list, i, segment)) return NULL; } return list; } static int write_segment(FILE * file, CurveSegment * segment) { int result = 0; if (segment->type == CurveBezier) { result = fprintf(file, "bc(%g,%g,%g,%g,%g,%g,%d)\n", segment->x1, segment->y1, segment->x2, segment->y2, segment->x, segment->y, segment->cont); } else { result = fprintf(file, "bs(%g,%g,%d)\n", segment->x, segment->y, segment->cont); } if (result < 0) { PyErr_SetFromErrno(PyExc_IOError); return 0; } return 1; } static PyObject * curve_write_to_file(SKCurveObject * self, PyObject * args) { PyObject * pyfile = NULL; FILE * file = NULL; CurveSegment * segment; int i; if (!PyArg_ParseTuple(args, "O!", &PyFile_Type, &pyfile)) return NULL; file = PyFile_AsFile(pyfile); segment = self->segments; for (i = 0; i < self->len; i++, segment++) { if (!write_segment(file, segment)) return NULL; } Py_INCREF(Py_None); return Py_None; } /* append a straight line to self. */ static PyObject * curve_append_straight(SKCurveObject * self, PyObject * args) { double x, y; int cont = ContAngle; if (!PyArg_ParseTuple(args, "dd|i", &x, &y, &cont)) { PyObject * sequence; PyErr_Clear(); if (!PyArg_ParseTuple(args, "O|i", &sequence, &cont)) return NULL; if (!skpoint_extract_xy(sequence, &x, &y)) { PyErr_SetString(PyExc_TypeError, "first argument is neither number nor sequence of two numbers"); return NULL; } } if (!SKCurve_AppendLine(self, x, y, cont)) return NULL; Py_INCREF(Py_None); return Py_None; } /* append a bezier segment to self. */ static PyObject * curve_append_curve(SKCurveObject * self, PyObject * args) { int cont = ContAngle; double x, y, x1, y1, x2, y2; if (PyTuple_Size(args) > 4) { if (!PyArg_ParseTuple(args, "dddddd|i", &x1, &y1, &x2, &y2, &x, &y, &cont)) return NULL; } else { PyObject *p1, *p2, *p3; int result; if (!PyArg_ParseTuple(args, "OOO|i", &p1, &p2, &p3, &cont)) return NULL; result = skpoint_extract_xy(p1, &x1, &y1); result = result && skpoint_extract_xy(p2, &x2, &y2); result = result && skpoint_extract_xy(p3, &x, &y); if (!result) { PyErr_SetString(PyExc_TypeError, "three points expected"); return NULL; } } if (!SKCurve_AppendBezier(self, x1, y1, x2, y2, x, y, cont)) return NULL; Py_INCREF(Py_None); return Py_None; } static PyObject * curve_append_segment(SKCurveObject * self, PyObject * args) { double x, y, x1, y1, x2, y2; int cont = ContAngle; int type; PyObject * p, *p1, *p2, *tuple; if (!PyArg_ParseTuple(args, "iOO|i", &type, &tuple, &p, &cont)) return NULL; if (!skpoint_extract_xy(p, &x, &y)) { PyErr_SetString(PyExc_TypeError, "third argument must be a point spec"); return NULL; } if (type == CurveLine) { if (!SKCurve_AppendLine(self, x, y, cont)) return NULL; } else if (type == CurveBezier) { if (!PyArg_ParseTuple(tuple, "OO", &p1, &p2)) return NULL; if (!skpoint_extract_xy(p1, &x1, &y1) || !skpoint_extract_xy(p2, &x2, &y2)) { PyErr_SetString(PyExc_TypeError, "for bezier segments, second argument " "must be a sequence of two point specs "); return NULL; } if (!SKCurve_AppendBezier(self, x1, y1, x2, y2, x, y, cont)) return NULL; } Py_INCREF(Py_None); return Py_None; } static PyObject * curve_set_straight(SKCurveObject * self, PyObject * args) { double x, y; int idx, cont = ContAngle; if (!PyArg_ParseTuple(args, "idd|i", &idx, &x, &y, &cont)) { PyObject * sequence; PyErr_Clear(); if (!PyArg_ParseTuple(args, "iO|i", &idx, &sequence, &cont)) return NULL; if (!skpoint_extract_xy(sequence, &x, &y)) { PyErr_SetString(PyExc_TypeError, "first argument is neither number nor sequence of two numbers"); return NULL; } } idx = check_index(self, idx, "SetLine"); if (idx < 0) return NULL; self->segments[idx].type = CurveLine; self->segments[idx].cont = cont; self->segments[idx].x = x; self->segments[idx].y = y; if (self->closed) { if (idx == 0) { self->segments[self->len - 1].x = x; self->segments[self->len - 1].y = y; self->segments[self->len - 1].cont = cont; } else if (idx == self->len - 1) { self->segments[0].x = x; self->segments[0].y = y; self->segments[0].cont = cont; } } Py_INCREF(Py_None); return Py_None; } /* append a bezier segment to self. */ static PyObject * curve_set_curve(SKCurveObject * self, PyObject * args) { int idx, cont = ContAngle; double x, y, x1, y1, x2, y2; if (PyTuple_Size(args) > 5) { if (!PyArg_ParseTuple(args, "idddddd|i", &idx, &x1, &y1, &x2, &y2, &x, &y, &cont)) return NULL; } else { PyObject *p1, *p2, *p3; int result; if (!PyArg_ParseTuple(args, "iOOO|i", &idx, &p1, &p2, &p3, &cont)) return NULL; result = skpoint_extract_xy(p1, &x1, &y1); result = result && skpoint_extract_xy(p2, &x2, &y2); result = result && skpoint_extract_xy(p3, &x, &y); if (!result) { PyErr_SetString(PyExc_TypeError, "three points expected"); return NULL; } } idx = check_index(self, idx, "SetBezier"); if (idx < 0) return NULL; self->segments[idx].type = CurveBezier; self->segments[idx].cont = cont; self->segments[idx].x = x; self->segments[idx].y = y; self->segments[idx].x1 = x1; self->segments[idx].y1 = y1; self->segments[idx].x2 = x2; self->segments[idx].y2 = y2; if (self->closed) { if (idx == 0) { self->segments[self->len - 1].x = x; self->segments[self->len - 1].y = y; self->segments[self->len - 1].cont = cont; } else if (idx == self->len - 1) { self->segments[0].x = x; self->segments[0].y = y; self->segments[0].cont = cont; } } Py_INCREF(Py_None); return Py_None; } static PyObject * curve_set_segment(SKCurveObject * self, PyObject * args) { double x, y, x1, y1, x2, y2; int cont = ContAngle; int idx, type; PyObject * p, *p1, *p2, *tuple; if (!PyArg_ParseTuple(args, "iOO|i", &idx, &type, &tuple, &p, &cont)) return NULL; if (!skpoint_extract_xy(p, &x, &y)) { PyErr_SetString(PyExc_TypeError, "third argument must be a point spec"); return NULL; } idx = check_index(self, idx, "SetSegment"); if (idx < 0) return NULL; self->segments[idx].type = CurveLine; self->segments[idx].cont = cont; self->segments[idx].x = x; self->segments[idx].y = y; if (type == CurveBezier) { if (!PyArg_ParseTuple(tuple, "OO", &p1, &p2)) return NULL; if (!skpoint_extract_xy(p1, &x1, &y1) || !skpoint_extract_xy(p2, &x2, &y2)) { PyErr_SetString(PyExc_TypeError, "for bezier segments, second argument " "must be a sequence of two point specs "); return NULL; } self->segments[idx].x1 = x1; self->segments[idx].y1 = y1; self->segments[idx].x2 = x2; self->segments[idx].y2 = y2; } if (self->closed) { if (idx == 0) { self->segments[self->len - 1].x = x; self->segments[self->len - 1].y = y; self->segments[self->len - 1].cont = cont; } else if (idx == self->len - 1) { self->segments[0].x = x; self->segments[0].y = y; self->segments[0].cont = cont; } } Py_INCREF(Py_None); return Py_None; } static int curve_parse_string_append(SKCurveObject * self, const char * string) { CurveSegment segment; char * old_locale; old_locale = strdup(setlocale(LC_NUMERIC, NULL)); setlocale(LC_NUMERIC, "C"); if (string[1] == 'c') { double x, y, x1, y1, x2, y2; int cont; segment.type = CurveBezier; if (sscanf(string, "bc%*[ (]%lf,%lf,%lf,%lf,%lf,%lf,%d", &x1, &y1, &x2, &y2, &x, &y, &cont) != 7) { PyErr_SetString(PyExc_ValueError, "cannot parse string"); goto fail; } segment.cont = cont; segment.x = x; segment.y = y; segment.x1 = x1; segment.y1 = y1; segment.x2 = x2; segment.y2 = y2; if (!SKCurve_AppendSegment(self, &segment)) goto fail; } else if (string[1] == 's') { double x, y; int cont; segment.type = CurveLine; if (sscanf(string, "bs%*[ (]%lf,%lf,%d", &x, &y, &cont) != 3) { PyErr_SetString(PyExc_ValueError, "cannot parse string"); goto fail; } segment.cont = cont; segment.x = x; segment.y = y; if (!SKCurve_AppendSegment(self, &segment)) goto fail; } else { PyErr_SetString(PyExc_ValueError, "string must begin with 'bc' or 'bs'"); goto fail; } return 1; fail: setlocale(LC_NUMERIC, old_locale); free(old_locale); return 0; } static PyObject * curve_append_from_string(SKCurveObject * self, PyObject * args) { char * string = NULL; int len; if (!PyArg_ParseTuple(args, "s#", &string, &len)) return NULL; if (len < 4) { PyErr_SetString(PyExc_ValueError, "string too short"); return NULL; } if (!curve_parse_string_append(self, string)) return NULL; Py_INCREF(Py_None); return Py_None; } static PyObject * curve_append_from_file(SKCurveObject * self, PyObject * args) { PyObject * pyfile = NULL; PyObject * line = NULL; const char *buf; if (!PyArg_ParseTuple(args, "O", &pyfile)) return NULL; while ((line = PyFile_GetLine(pyfile, 0)) && (PyString_Size(line) != 0)) { buf = PyString_AsString(line); if (buf[0] != 'b' || (buf[1] != 'c' && buf[1] != 's')) break; if (!curve_parse_string_append(self, buf)) { Py_DECREF(line); return NULL; } Py_DECREF(line); } return line; } /* close self. * * When importing from a ai-file, the last node is often given twice, by * a lineto or curveto and by closepath. In that case, the last node is * silently removed. * * Also (optionally) guess the continuity of the nodes. This is useful * when importing from eps files. Adobe Illustrator files already * contain some of this information */ #define GUESS_EPSILON 0.1 static PyObject * curve_guess_continuity(SKCurveObject * self, PyObject * args) { int i; CurveSegment * segment, *pred; segment = self->segments; for (i = 0; i < self->len; i++, segment++) { if (i > 0) pred = segment - 1; else if (self->closed) pred = self->segments + self->len - 1; else pred = NULL; if (pred && pred->type == CurveBezier && segment->type == CurveBezier) { if (fabs(pred->x2 + segment->x1 - 2 * segment->x) < GUESS_EPSILON && fabs(pred->y2 + segment->y1 - 2*segment->y) < GUESS_EPSILON) { segment->cont = ContSymmetrical; } else { SKCoord x, y; x = pred->x2; y = pred->y2; SKCurve_AdjustControlPoint(&x, &y, segment->x1, segment->y1, segment->x, segment->y, ContSmooth); if (fabs(x - pred->x2) < GUESS_EPSILON && fabs(y - pred->y2) < GUESS_EPSILON) { segment->cont = ContSmooth; } else { x = segment->x1; y = segment->y1; SKCurve_AdjustControlPoint(&x, &y, pred->x2, pred->y2, segment->x, segment->y, ContSmooth); if (fabs(x - segment->x1) < GUESS_EPSILON && fabs(y - segment->y1) < GUESS_EPSILON) { segment->cont = ContSmooth; } } } if (i == 0 && self->closed) self->segments[self->len - 1].cont = segment->cont; } } Py_INCREF(Py_None); return Py_None; } static PyObject * curve_load_close(SKCurveObject * self, PyObject * args) { int copy_cont_from_last = 0; if (!PyArg_ParseTuple(args, "|i", ©_cont_from_last)) return NULL; self->closed = 1; if (copy_cont_from_last) { self->segments[0].cont = self->segments[self->len - 1].cont; } if (self->len > 2 && self->segments[self->len - 1].type == CurveLine) { if (self->segments[self->len - 1].x == self->segments[self->len - 2].x && self->segments[self->len-1].y == self->segments[self->len-2].y) { self->len -= 1; } } curve_check_state(self, 0, FUNCTION_NAME); Py_INCREF(Py_None); return Py_None; } #define DUMP_TEST 0 int SKCurve_TestTransformed(SKCurveObject * self, PyObject * trafo, int test_x, int test_y, int closed) { CurveSegment * segment; SKCoord nx, ny, x1, y1, x2, y2, lastx, lasty, i; int x[4], y[4]; int result; int cross_count; #if DUMP_TEST fprintf(stderr, "Testing path %ld at %d, %d\n", (long)self, test_x, test_y); #endif segment = self->segments; SKTrafo_TransformXY(trafo, segment->x, segment->y, &lastx, &lasty); segment += 1; cross_count = 0; for (i = 1; i < self->len; i++, segment++) { if (segment->type == CurveBezier) { SKTrafo_TransformXY(trafo, segment->x1, segment->y1, &x1, &y1); SKTrafo_TransformXY(trafo, segment->x2, segment->y2, &x2, &y2); SKTrafo_TransformXY(trafo, segment->x, segment->y, &nx, &ny); x[0] = lastx + 0.5; y[0] = lasty + 0.5; x[1] = x1 + 0.5; y[1] = y1 + 0.5; x[2] = x2 + 0.5; y[2] = y2 + 0.5; x[3] = nx + 0.5; y[3] = ny + 0.5; result = bezier_hit_segment(x, y, test_x, test_y); } else { /* a line segment */ SKTrafo_TransformXY(trafo, segment->x, segment->y, &nx, &ny); result = bezier_hit_line(lastx + 0.5, lasty + 0.5, nx + 0.5, ny + 0.5, test_x, test_y); } lastx = nx; lasty = ny; if (result < 0) { cross_count = -1; break; } if (result > 0) cross_count += result; } if (!self->closed && closed && self->len >= 2 && cross_count >= 0) { /* an open subpath in a closed multi path berzier object */ SKTrafo_TransformXY(trafo, self->segments[0].x, self->segments[0].y, &nx, &ny); result = bezier_hit_line(lastx + 0.5, lasty + 0.5, nx + 0.5, ny + 0.5, test_x, test_y); if (result > 0) cross_count += result; } #if DUMP_TEST fprintf(stderr, "result: cross_count = %d\n", cross_count); #endif return cross_count; } /* * Methods for interactive creation */ #define DRAW_BEZIER(x1,y1,x2,y2,x3,y3,x4,y4)\ result = PyObject_CallFunction(draw_bezier, "(dd)(dd)(dd)(dd)",\ x1,y1,x2,y2,x3,y3,x4,y4);\ if (!result)\ return 0; \ Py_DECREF(result) #define DRAW_LINE(x1,y1,x2,y2)\ result = PyObject_CallFunction(draw_line, "(dd)(dd)",x1,y1,x2,y2);\ if (!result)\ return 0;\ Py_DECREF(result) static PyObject * creator_draw_not_last(SKCurveObject * curve, PyObject * args) { PyObject * result; CurveSegment * segments = curve->segments; int i; PyObject * draw_bezier; PyObject * draw_line; if (!PyArg_ParseTuple(args, "OO", &draw_bezier, &draw_line)) return NULL; for (i = 1; i < SKCurve_LENGTH(curve) - 1; i++) { if (segments[i].type == CurveBezier) { DRAW_BEZIER(segments[i - 1].x, segments[i - 1].y, segments[i].x1, segments[i].y1, segments[i].x2, segments[i].y2, segments[i].x, segments[i].y); } else if (segments[i].type == CurveLine) { DRAW_LINE(segments[i - 1].x, segments[i - 1].y, segments[i].x, segments[i].y); } } Py_INCREF(Py_None); return Py_None; } /* * Methods for interactive editing */ /* the same as in const.py: */ #define SelectSet 0 #define SelectAdd 1 #define SelectSubtract 2 #define SelectSubobjects 3 /* unused here */ #define SelectDrag 4 /* * Return the number of selected nodes as a Python int. */ static PyObject * curve_selection_count(SKCurveObject * self) { int count = 0; int i; for (i = 0; i < self->len; i++) { if (self->segments[i].selected && (!self->closed || i < self->len - 1)) ++count; } return PyInt_FromLong(count); } /* * curve.node_selected(IDX) * * Return true (1) if the node IDX is marked as selected, false (0) * otherwise. A negative IDX is interpreted like a negative list index * in Python. */ static PyObject * curve_node_selected(SKCurveObject * self, PyObject * args) { int idx; if (!PyArg_ParseTuple(args, "i", &idx)) return NULL; idx = check_index(self, idx, "NodeSelected"); if (idx < 0) return NULL; return PyInt_FromLong(self->segments[idx].selected); } static PyObject * curve_select_rect(SKCurveObject * self, PyObject * args) { SKRectObject * rect; CurveSegment * segment; int i, mode = SelectSet; int selected = 0; if (!PyArg_ParseTuple(args, "O!|i", &SKRectType, &rect, &mode)) return NULL; segment = self->segments; for (i = 0; i < self->len; i++, segment++) { if (SKRect_ContainsXY(rect, segment->x, segment->y)) { if (mode == SelectSubtract) segment->selected = 0; else segment->selected = 1; } else { if (mode == SelectSet) segment->selected = 0; } selected = selected || segment->selected; } curve_check_state(self, 1, FUNCTION_NAME); return PyInt_FromLong(selected); } static PyObject * curve_select_segment(SKCurveObject * self, PyObject * args) { int idx, value = 1; if (!PyArg_ParseTuple(args, "i|i", &idx, &value)) return NULL; if (idx < 0) idx = idx + self->len; if (idx < 0 || idx >= self->len) { PyErr_SetString(PyExc_IndexError, "curve_continuity: index out of range"); return NULL; } self->segments[idx].selected = value; if (self->closed) { if (idx == self->len - 1) self->segments[0].selected = value; else if (idx == 0) self->segments[self->len - 1].selected = value; } Py_INCREF(Py_None); return Py_None; } static PyObject * curve_deselect(SKCurveObject * self, PyObject * args) { int i; for (i = 0; i < self->len; i++) { self->segments[i].selected = 0; } Py_INCREF(Py_None); return Py_None; } /* */ static PyObject * curve_draw_dragged_nodes(SKCurveObject * self, PyObject * args) { PyObject * draw_bezier; PyObject * draw_line; PyObject * result; SKPointObject * offset; CurveSegment * segment = self->segments + 1; int partially; int i; if (!PyArg_ParseTuple(args, "O!iOO", &SKPointType, &offset, &partially, &draw_bezier, &draw_line)) return NULL; for (i = 1; i < self->len; i++, segment++) { if (segment[-1].selected || segment->selected || !partially) { double nx = segment[-1].x; double ny = segment[-1].y; CurveSegment seg = *segment; if (segment[-1].selected) { nx += offset->x; ny += offset->y; seg.x1 += offset->x; seg.y1 += offset->y; } if (segment->selected) { seg.x += offset->x; seg.y += offset->y; seg.x2 += offset->x; seg.y2 += offset->y; } if (segment->type == CurveBezier) { DRAW_BEZIER(nx, ny, seg.x1, seg.y1, seg.x2, seg.y2, seg.x, seg.y); } else { DRAW_LINE(nx, ny, seg.x, seg.y); } } } Py_INCREF(Py_None); return Py_None; } static PyObject * curve_move_selected_nodes(SKCurveObject * self, PyObject * args) { SKPointObject * offset; int i; CurveSegment * segment; PyObject * undo_object = NULL; if (!PyArg_ParseTuple(args, "O!", &SKPointType, &offset)) return NULL; undo_object = curve_create_full_undo(self); if (!undo_object) return NULL; segment = self->segments; for (i = 0; i < self->len; i++, segment++) { if (segment->selected) { segment->x += offset->x; segment->y += offset->y; if (segment->type == CurveBezier) { segment->x2 += offset->x; segment->y2 += offset->y; } if (i < self->len - 1 && segment[1].type == CurveBezier) { segment[1].x1 += offset->x; segment[1].y1 += offset->y; } } } return undo_object; } static PyObject * curve_draw_unselected(SKCurveObject * self, PyObject * args) { PyObject * draw_bezier; PyObject * draw_line; PyObject * result; CurveSegment * segment = self->segments + 1; int i; if (!PyArg_ParseTuple(args, "OO", &draw_bezier, &draw_line)) return NULL; for (i = 1; i < self->len; i++, segment++) { if (segment->type == CurveLine) { DRAW_LINE(segment[-1].x, segment[-1].y, segment->x, segment->y); } else if (!segment[-1].selected && !segment->selected) { DRAW_BEZIER(segment[-1].x, segment[-1].y, segment->x1, segment->y1, segment->x2, segment->y2, segment->x, segment->y); } } Py_INCREF(Py_None); return Py_None; } /* * tables for python, to access various attributes */ #define OFF(x) offsetof(SKCurveObject, x) static struct memberlist curve_memberlist[] = { {"len", T_INT, OFF(len), RO}, {"closed", T_BYTE, OFF(closed), RO}, {NULL} }; /* for maximum performance, this list should contain the methods called most frequently at the beginning */ static struct PyMethodDef curve_methods[] = { // {"draw_transformed",(PyCFunction)SKCurve_PyDrawTransformed, 1}, {"hit_point", (PyCFunction)curve_hit_point, 1}, {"accurate_rect", (PyCFunction)curve_accurate_rect, 1}, {"coord_rect", (PyCFunction)curve_coord_rect, 1}, /* new method names */ {"Translate", (PyCFunction)curve_apply_translation, 1}, {"Transform", (PyCFunction)curve_apply_trafo, 1}, {"Duplicate", (PyCFunction)curve_duplicate, 1}, {"AppendLine", (PyCFunction)curve_append_straight, 1}, {"AppendBezier", (PyCFunction)curve_append_curve, 1}, {"AppendSegment", (PyCFunction)curve_append_segment, 1}, {"NodeList", (PyCFunction)curve_node_list, 1}, {"Node", (PyCFunction)curve_node, 1}, {"Segment", (PyCFunction)curve_segment, 1}, {"SetContinuity", (PyCFunction)curve_set_continuity, 1}, {"SetLine", (PyCFunction)curve_set_straight, 1}, {"SetBezier", (PyCFunction)curve_set_curve, 1}, {"SetSegment", (PyCFunction)curve_set_segment, 1}, {"Continuity", (PyCFunction)curve_continuity, 1}, {"SegmentType", (PyCFunction)curve_segment_type, 1}, {"ClosePath", (PyCFunction)curve_close_contour, 1}, /**/ {"arc_lengths", (PyCFunction)curve_arc_lengths, 1}, {"_set_nodes_and_segments",(PyCFunction)curve__set_nodes_and_segments,1}, {"_undo_close", (PyCFunction)curve__undo_close, 1}, {"append_from_file",(PyCFunction)curve_append_from_file, 1}, {"get_save", (PyCFunction)curve_get_save, 1}, {"write_to_file", (PyCFunction)curve_write_to_file, 1}, {"guess_continuity",(PyCFunction)curve_guess_continuity, 1}, {"load_close", (PyCFunction)curve_load_close, 1}, {"append_from_string",(PyCFunction)curve_append_from_string,1}, /* editor methods */ {"SegmentSelected", (PyCFunction)curve_node_selected, 1}, {"SelectSegment", (PyCFunction)curve_select_segment, 1}, {"draw_dragged_nodes",(PyCFunction)curve_draw_dragged_nodes,1}, {"draw_unselected", (PyCFunction)curve_draw_unselected, 1}, {"selection_count", (PyCFunction)curve_selection_count, 1}, {"node_selected", (PyCFunction)curve_node_selected, 1}, {"select_rect", (PyCFunction)curve_select_rect, 1}, {"select_segment", (PyCFunction)curve_select_segment, 1}, {"deselect", (PyCFunction)curve_deselect, 1}, {"move_selected_nodes",(PyCFunction)curve_move_selected_nodes,1}, {"nearest_point", (PyCFunction)SKCurve_NearestPointPy, 1}, {"point_at", (PyCFunction)SKCurve_PointAtPy, 1}, /* creator methods */ {"draw_not_last", (PyCFunction)creator_draw_not_last, 1}, {NULL, NULL} }; static PyObject * curve_getattr(PyObject * self, char * name) { PyObject * result; result = Py_FindMethod(curve_methods, self, name); if (result != NULL) return result; PyErr_Clear(); return PyMember_Get((char *)self, curve_memberlist, name); } PyTypeObject SKCurveType = { PyObject_HEAD_INIT(NULL) 0, "SKCurveObject", sizeof(SKCurveObject), 0, (destructor)curve_dealloc, /*tp_dealloc*/ (printfunc)NULL, /*tp_print*/ curve_getattr, /*tp_getattr*/ 0, /*tp_setattr*/ (cmpfunc)curve_compare, /*tp_compare*/ (reprfunc)curve_repr, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ (ternaryfunc)0, /*tp_call*/ }; PyObject * _SKCurve_NumAllocated(PyObject * self, PyObject * args) { return PyInt_FromLong(paths_allocated); } int _SKCurve_InitCurveObject(void) { set_nodes_and_segments_string = PyString_InternFromString("_set_nodes_and_segments"); undo_close_string = PyString_InternFromString("_undo_close"); return 1; } UniConvertor-1.1.4/src/modules/skmod/curveobject.h0000664000076400007640000000640611060500613020704 0ustar igorigor/* Sketch - A Python-based interactive drawing program * Copyright (C) 1997, 1998 by Bernhard Herzog * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef CURVEOBJECT_H #define CURVEOBJECT_H #if defined(__cplusplus) extern "C" { #endif #include "skpoint.h" /* for SKCoord */ #include "sktrafo.h" /* * A single curve segment */ typedef struct { char type; /* whether segment is a bezier segment or a line */ char cont; /* continuity */ char selected; /* true, if node is selected */ SKCoord x1, y1, x2, y2; /* for beziers, the coordinates of the * control points */ SKCoord x, y; } CurveSegment; #define CurveBezier 1 #define CurveLine 2 #define IsValidSegmentType(type) ((type) == CurveBezier || (type) == CurveLine) /* The values for continuity are ordered by increasing interdependence * of the control points of the bezier segments to which the node * belongs. For ContAngle, the controlpoints are completely independent, * for ContSmooth, the control points and the node lie on a line and for * ContSymmetrical the distances between the control points and the node * are the same, additionally. Some functions use this fact. */ #define ContAngle 0 #define ContSmooth 1 #define ContSymmetrical 2 #define CHECK_CONT(cont) ((cont) >= 0 && (cont) <= 2) /* * A single open or closed path of bezier or line segments */ typedef struct { PyObject_HEAD int len; /* # of nodes */ int allocated; /* # of entries allocated */ CurveSegment * segments; /* list of segments */ char closed; /* true, if path is closed */ } SKCurveObject; extern PyTypeObject SKCurveType; #define SKCurve_Check(v) ((v)->ob_type == &SKCurveType) PyObject * SKCurve_New(int len); int SKCurve_AppendSegment(SKCurveObject * self, CurveSegment * segment); int SKCurve_AppendLine(SKCurveObject * self, double x, double y, int continuity); int SKCurve_AppendBezier(SKCurveObject * self, double x1, double y1, double x2, double y2, double x, double y, int continuity); int SKCurve_ClosePath(SKCurveObject * self); int SKCurve_TestTransformed(SKCurveObject * self, PyObject * trafo, int test_x, int test_y, int closed); int SKCurve_Transform(SKCurveObject * self, PyObject * trafo); #define SKCurve_LENGTH(curve) (((SKCurveObject*)curve)->len) /* internal functions */ PyObject * _SKCurve_NumAllocated(PyObject * self, PyObject * args); int _SKCurve_InitCurveObject(void); /* * Editor */ #define SelNone 0 #define SelNodes 1 #define SelSegmentFirst 2 #define SelSegmentLast 3 #if defined(__cplusplus) } #endif #endif /* CURVEOBJECT_H*/ UniConvertor-1.1.4/src/modules/skmod/Imaging.h0000664000076400007640000004452111060500613017744 0ustar igorigor/* * The Python Imaging Library * $Id: Imaging.h 2308 2005-03-02 12:00:55Z fredrik $ * * declarations for the imaging core library * * Copyright (c) 1997-2003 by Secret Labs AB * Copyright (c) 1995-2003 by Fredrik Lundh * * See the README file for information on usage and redistribution. */ #include "ImPlatform.h" #if defined(__cplusplus) extern "C" { #endif #ifndef M_PI #define M_PI 3.14159265359 #endif /* -------------------------------------------------------------------- */ /* * Image data organization: * * mode bytes byte order * ------------------------------- * 1 1 1 * L 1 L * P 1 P * I 4 I (32-bit integer, native byte order) * F 4 F (32-bit IEEE float, native byte order) * RGB 4 R, G, B, - * RGBA 4 R, G, B, A * CMYK 4 C, M, Y, K * YCbCr 4 Y, Cb, Cr, - * * experimental modes (incomplete): * LA 4 L, -, -, A * PA 4 P, -, -, A * I;16 2 I (16-bit integer, native byte order) * * "P" is an 8-bit palette mode, which should be mapped through the * palette member to get an output image. Check palette->mode to * find the corresponding "real" mode. * * For information on how to access Imaging objects from your own C * extensions, see http://www.effbot.org/zone/pil-extending.htm */ /* Handles */ typedef struct ImagingMemoryInstance* Imaging; typedef struct ImagingAccessInstance* ImagingAccess; typedef struct ImagingHistogramInstance* ImagingHistogram; typedef struct ImagingOutlineInstance* ImagingOutline; typedef struct ImagingPaletteInstance* ImagingPalette; /* handle magics (used with PyCObject). */ #define IMAGING_MAGIC "PIL Imaging" #define IMAGING_ACCESS_MAGIC "PIL ImagingAccess" /* pixel types */ #define IMAGING_TYPE_UINT8 0 #define IMAGING_TYPE_INT32 1 #define IMAGING_TYPE_FLOAT32 2 #define IMAGING_TYPE_SPECIAL 3 /* check mode for details */ struct ImagingMemoryInstance { /* Format */ char mode[4+1]; /* Band names ("1", "L", "P", "RGB", "RGBA", "CMYK") */ int type; /* Data type (IMAGING_TYPE_*) */ int depth; /* Depth (ignored in this version) */ int bands; /* Number of bands (1, 2, 3, or 4) */ int xsize; /* Image dimension. */ int ysize; /* Colour palette (for "P" images only) */ ImagingPalette palette; /* Data pointers */ UINT8 **image8; /* Set for 8-bit images (pixelsize=1). */ INT32 **image32; /* Set for 32-bit images (pixelsize=4). */ /* Internals */ char **image; /* Actual raster data. */ char *block; /* Set if data is allocated in a single block. */ int pixelsize; /* Size of a pixel, in bytes (1, 2 or 4) */ int linesize; /* Size of a line, in bytes (xsize * pixelsize) */ /* Virtual methods */ void (*destroy)(Imaging im); }; #define IMAGING_PIXEL_1(im,x,y) ((im)->image8[(y)][(x)]) #define IMAGING_PIXEL_L(im,x,y) ((im)->image8[(y)][(x)]) #define IMAGING_PIXEL_LA(im,x,y) ((im)->image[(y)][(x)*4]) #define IMAGING_PIXEL_P(im,x,y) ((im)->image8[(y)][(x)]) #define IMAGING_PIXEL_PA(im,x,y) ((im)->image[(y)][(x)*4]) #define IMAGING_PIXEL_I(im,x,y) ((im)->image32[(y)][(x)]) #define IMAGING_PIXEL_F(im,x,y) (((FLOAT32*)(im)->image32[y])[x]) #define IMAGING_PIXEL_RGB(im,x,y) ((im)->image[(y)][(x)*4]) #define IMAGING_PIXEL_RGBA(im,x,y) ((im)->image[(y)][(x)*4]) #define IMAGING_PIXEL_CMYK(im,x,y) ((im)->image[(y)][(x)*4]) #define IMAGING_PIXEL_YCbCr(im,x,y) ((im)->image[(y)][(x)*4]) #define IMAGING_PIXEL_UINT8(im,x,y) ((im)->image8[(y)][(x)]) #define IMAGING_PIXEL_INT32(im,x,y) ((im)->image32[(y)][(x)]) #define IMAGING_PIXEL_FLOAT32(im,x,y) (((FLOAT32*)(im)->image32[y])[x]) #define IMAGING_ACCESS_HEAD\ int (*getline)(ImagingAccess access, char *buffer, int y);\ void (*destroy)(ImagingAccess access) struct ImagingAccessInstance { IMAGING_ACCESS_HEAD; /* Data members */ Imaging im; }; struct ImagingHistogramInstance { /* Format */ char mode[4+1]; /* Band names (of corresponding source image) */ int bands; /* Number of bands (1, 3, or 4) */ /* Data */ long *histogram; /* Histogram (bands*256 longs) */ }; struct ImagingPaletteInstance { /* Format */ char mode[4+1]; /* Band names */ /* Data */ UINT8 palette[1024];/* Palette data (same format as image data) */ INT16* cache; /* Palette cache (used for predefined palettes) */ int keep_cache; /* This palette will be reused; keep cache */ }; /* Objects */ /* ------- */ extern Imaging ImagingNew(const char* mode, int xsize, int ysize); extern Imaging ImagingNew2(const char* mode, Imaging imOut, Imaging imIn); extern void ImagingDelete(Imaging im); extern Imaging ImagingNewBlock(const char* mode, int xsize, int ysize); extern Imaging ImagingNewArray(const char* mode, int xsize, int ysize); extern Imaging ImagingNewMap(const char* filename, int readonly, const char* mode, int xsize, int ysize); extern Imaging ImagingNewPrologue(const char *mode, unsigned xsize, unsigned ysize); extern Imaging ImagingNewPrologueSubtype(const char *mode, unsigned xsize, unsigned ysize, int structure_size); extern Imaging ImagingNewEpilogue(Imaging im); extern void ImagingCopyInfo(Imaging destination, Imaging source); extern void ImagingHistogramDelete(ImagingHistogram histogram); extern ImagingAccess ImagingAccessNew(Imaging im); extern void ImagingAccessDelete(ImagingAccess access); extern ImagingPalette ImagingPaletteNew(const char *mode); extern ImagingPalette ImagingPaletteNewBrowser(void); extern ImagingPalette ImagingPaletteDuplicate(ImagingPalette palette); extern void ImagingPaletteDelete(ImagingPalette palette); extern int ImagingPaletteCachePrepare(ImagingPalette palette); extern void ImagingPaletteCacheUpdate(ImagingPalette palette, int r, int g, int b); extern void ImagingPaletteCacheDelete(ImagingPalette palette); #define ImagingPaletteCache(p, r, g, b)\ p->cache[(r>>2) + (g>>2)*64 + (b>>2)*64*64] extern Imaging ImagingQuantize(Imaging im, int colours, int mode, int kmeans); /* Threading */ /* --------- */ typedef void* ImagingSectionCookie; extern void ImagingSectionEnter(ImagingSectionCookie* cookie); extern void ImagingSectionLeave(ImagingSectionCookie* cookie); /* Exceptions */ /* ---------- */ extern void* ImagingError_IOError(void); extern void* ImagingError_MemoryError(void); extern void* ImagingError_ModeError(void); /* maps to ValueError by default */ extern void* ImagingError_Mismatch(void); /* maps to ValueError by default */ extern void* ImagingError_ValueError(const char* message); /* Transform callbacks */ /* ------------------- */ /* standard transforms */ #define IMAGING_TRANSFORM_AFFINE 0 #define IMAGING_TRANSFORM_PERSPECTIVE 2 #define IMAGING_TRANSFORM_QUAD 3 /* standard filters */ #define IMAGING_TRANSFORM_NEAREST 0 #define IMAGING_TRANSFORM_ANTIALIAS 1 #define IMAGING_TRANSFORM_BILINEAR 2 #define IMAGING_TRANSFORM_BICUBIC 3 typedef int (*ImagingTransformMap)(double* X, double* Y, int x, int y, void* data); typedef int (*ImagingTransformFilter)(void* out, Imaging im, double x, double y, void* data); /* Image Manipulation Methods */ /* -------------------------- */ extern Imaging ImagingBlend(Imaging imIn1, Imaging imIn2, float alpha); extern Imaging ImagingCopy(Imaging im); extern Imaging ImagingConvert( Imaging im, const char* mode, ImagingPalette palette, int dither); extern Imaging ImagingConvertMatrix(Imaging im, const char *mode, float m[]); extern Imaging ImagingCrop(Imaging im, int x0, int y0, int x1, int y1); extern Imaging ImagingExpand(Imaging im, int x, int y, int mode); extern Imaging ImagingFill(Imaging im, const void* ink); extern int ImagingFill2( Imaging into, const void* ink, Imaging mask, int x0, int y0, int x1, int y1); extern Imaging ImagingFillBand(Imaging im, int band, int color); extern Imaging ImagingFillLinearGradient(const char* mode); extern Imaging ImagingFillRadialGradient(const char* mode); extern Imaging ImagingFilter( Imaging im, int xsize, int ysize, const FLOAT32* kernel, FLOAT32 offset, FLOAT32 divisor); extern Imaging ImagingFlipLeftRight(Imaging imOut, Imaging imIn); extern Imaging ImagingFlipTopBottom(Imaging imOut, Imaging imIn); extern Imaging ImagingGetBand(Imaging im, int band); extern int ImagingGetBBox(Imaging im, int bbox[4]); typedef struct { int x, y; INT32 count; INT32 pixel; } ImagingColorItem; extern ImagingColorItem* ImagingGetColors(Imaging im, int maxcolors, int *colors); extern int ImagingGetExtrema(Imaging im, void *extrema); extern int ImagingGetProjection(Imaging im, UINT8* xproj, UINT8* yproj); extern ImagingHistogram ImagingGetHistogram( Imaging im, Imaging mask, void *extrema); extern Imaging ImagingModeFilter(Imaging im, int size); extern Imaging ImagingNegative(Imaging im); extern Imaging ImagingOffset(Imaging im, int xoffset, int yoffset); extern int ImagingPaste( Imaging into, Imaging im, Imaging mask, int x0, int y0, int x1, int y1); extern Imaging ImagingPoint( Imaging im, const char* tablemode, const void* table); extern Imaging ImagingPointTransform( Imaging imIn, double scale, double offset); extern Imaging ImagingPutBand(Imaging im, Imaging imIn, int band); extern Imaging ImagingRankFilter(Imaging im, int size, int rank); extern Imaging ImagingResize(Imaging imOut, Imaging imIn, int filter); extern Imaging ImagingRotate( Imaging imOut, Imaging imIn, double theta, int filter); extern Imaging ImagingRotate90(Imaging imOut, Imaging imIn); extern Imaging ImagingRotate180(Imaging imOut, Imaging imIn); extern Imaging ImagingRotate270(Imaging imOut, Imaging imIn); extern Imaging ImagingStretch(Imaging imOut, Imaging imIn, int filter); extern Imaging ImagingTransformPerspective( Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1, double a[8], int filter, int fill); extern Imaging ImagingTransformAffine( Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1, double a[6], int filter, int fill); extern Imaging ImagingTransformQuad( Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1, double a[8], int filter, int fill); extern Imaging ImagingTransform( Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1, ImagingTransformMap transform, void* transform_data, ImagingTransformFilter filter, void* filter_data, int fill); extern Imaging ImagingCopy2(Imaging imOut, Imaging imIn); extern Imaging ImagingConvert2(Imaging imOut, Imaging imIn); /* Channel operations */ /* any mode, except "F" */ extern Imaging ImagingChopLighter(Imaging imIn1, Imaging imIn2); extern Imaging ImagingChopDarker(Imaging imIn1, Imaging imIn2); extern Imaging ImagingChopDifference(Imaging imIn1, Imaging imIn2); extern Imaging ImagingChopMultiply(Imaging imIn1, Imaging imIn2); extern Imaging ImagingChopScreen(Imaging imIn1, Imaging imIn2); extern Imaging ImagingChopAdd( Imaging imIn1, Imaging imIn2, float scale, int offset); extern Imaging ImagingChopSubtract( Imaging imIn1, Imaging imIn2, float scale, int offset); extern Imaging ImagingChopAddModulo(Imaging imIn1, Imaging imIn2); extern Imaging ImagingChopSubtractModulo(Imaging imIn1, Imaging imIn2); /* "1" images only */ extern Imaging ImagingChopAnd(Imaging imIn1, Imaging imIn2); extern Imaging ImagingChopOr(Imaging imIn1, Imaging imIn2); extern Imaging ImagingChopXor(Imaging imIn1, Imaging imIn2); /* Image measurement */ extern void ImagingCrack(Imaging im, int x0, int y0); /* Graphics */ struct ImagingAffineMatrixInstance { float a[9]; }; typedef struct ImagingAffineMatrixInstance *ImagingAffineMatrix; extern int ImagingDrawArc(Imaging im, int x0, int y0, int x1, int y1, int start, int end, const void* ink, int op); extern int ImagingDrawBitmap(Imaging im, int x0, int y0, Imaging bitmap, const void* ink, int op); extern int ImagingDrawChord(Imaging im, int x0, int y0, int x1, int y1, int start, int end, const void* ink, int fill, int op); extern int ImagingDrawEllipse(Imaging im, int x0, int y0, int x1, int y1, const void* ink, int fill, int op); extern int ImagingDrawLine(Imaging im, int x0, int y0, int x1, int y1, const void* ink, int op); extern int ImagingDrawWideLine(Imaging im, int x0, int y0, int x1, int y1, const void* ink, int width, int op); extern int ImagingDrawPieslice(Imaging im, int x0, int y0, int x1, int y1, int start, int end, const void* ink, int fill, int op); extern int ImagingDrawPoint(Imaging im, int x, int y, const void* ink, int op); extern int ImagingDrawPolygon(Imaging im, int points, int *xy, const void* ink, int fill, int op); extern int ImagingDrawRectangle(Imaging im, int x0, int y0, int x1, int y1, const void* ink, int fill, int op); /* Level 2 graphics (WORK IN PROGRESS) */ extern ImagingOutline ImagingOutlineNew(void); extern void ImagingOutlineDelete(ImagingOutline outline); extern int ImagingDrawOutline(Imaging im, ImagingOutline outline, const void* ink, int fill, int op); extern int ImagingOutlineMove(ImagingOutline outline, float x, float y); extern int ImagingOutlineLine(ImagingOutline outline, float x, float y); extern int ImagingOutlineCurve(ImagingOutline outline, float x1, float y1, float x2, float y2, float x3, float y3); extern int ImagingOutlineTransform(ImagingOutline outline, double a[6]); extern int ImagingOutlineClose(ImagingOutline outline); /* Special effects */ extern Imaging ImagingEffectSpread(Imaging imIn, int distance); extern Imaging ImagingEffectNoise(int xsize, int ysize, float sigma); extern Imaging ImagingEffectMandelbrot(int xsize, int ysize, double extent[4], int quality); /* Obsolete */ extern int ImagingToString(Imaging im, int orientation, char *buffer); extern int ImagingFromString(Imaging im, int orientation, char *buffer); /* File I/O */ /* -------- */ /* Built-in drivers */ extern Imaging ImagingOpenPPM(const char* filename); extern int ImagingSavePPM(Imaging im, const char* filename); /* Utility functions */ extern UINT32 ImagingCRC32(UINT32 crc, UINT8* buffer, int bytes); /* Codecs */ typedef struct ImagingCodecStateInstance *ImagingCodecState; typedef int (*ImagingCodec)(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); extern int ImagingBitDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); extern int ImagingEpsEncode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); extern int ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); extern int ImagingGifDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); extern int ImagingGifEncode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); extern int ImagingHexDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); #ifdef HAVE_LIBJPEG extern int ImagingJpegDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); extern int ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); #endif extern int ImagingLzwDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); #ifdef HAVE_LIBMPEG extern int ImagingMpegDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); #endif extern int ImagingMspDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); extern int ImagingPackbitsDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); extern int ImagingPcdDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); extern int ImagingPcxDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); extern int ImagingPcxEncode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); extern int ImagingRawDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); extern int ImagingRawEncode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); extern int ImagingSunRleDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); extern int ImagingTgaRleDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); extern int ImagingXbmDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); extern int ImagingXbmEncode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); #ifdef HAVE_LIBZ extern int ImagingZipDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); extern int ImagingZipEncode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); #endif typedef void (*ImagingShuffler)(UINT8* out, const UINT8* in, int pixels); /* Public shufflers */ extern void ImagingPackRGB(UINT8* out, const UINT8* in, int pixels); extern void ImagingPackBGR(UINT8* out, const UINT8* in, int pixels); extern void ImagingUnpackRGB(UINT8* out, const UINT8* in, int pixels); extern void ImagingUnpackBGR(UINT8* out, const UINT8* in, int pixels); extern void ImagingUnpackYCC(UINT8* out, const UINT8* in, int pixels); extern void ImagingUnpackYCCA(UINT8* out, const UINT8* in, int pixels); extern void ImagingUnpackYCbCr(UINT8* out, const UINT8* in, int pixels); extern void ImagingConvertRGB2YCbCr(UINT8* out, const UINT8* in, int pixels); extern void ImagingConvertYCbCr2RGB(UINT8* out, const UINT8* in, int pixels); extern ImagingShuffler ImagingFindUnpacker(const char* mode, const char* rawmode, int* bits_out); extern ImagingShuffler ImagingFindPacker(const char* mode, const char* rawmode, int* bits_out); struct ImagingCodecStateInstance { int count; int state; int errcode; int x, y; int ystep; int xsize, ysize, xoff, yoff; ImagingShuffler shuffle; int bits, bytes; UINT8 *buffer; void *context; }; /* Errcodes */ #define IMAGING_CODEC_END 1 #define IMAGING_CODEC_OVERRUN -1 #define IMAGING_CODEC_BROKEN -2 #define IMAGING_CODEC_UNKNOWN -3 #define IMAGING_CODEC_CONFIG -8 #define IMAGING_CODEC_MEMORY -9 #if defined(__cplusplus) } #endif UniConvertor-1.1.4/src/modules/skmod/skaux.c0000664000076400007640000001332211060500613017512 0ustar igorigor/* Sketch - A Python-based interactive drawing program * Copyright (C) 1996, 1997, 1998, 1999, 2002, 2006 by Bernhard Herzog * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "curvelow.h" #include "skrect.h" #include "sktrafo.h" #include "_sketchmodule.h" /* * Bezier functions. Should be in a separate module ? */ PyObject * SKAux_DrawBezier(PyObject * self, PyObject * arg) { return NULL; } /* * */ PyObject * SKAux_TransformRectangle(PyObject * self, PyObject * args) { SKRectObject * rect; PyObject * trafo; SKCoord dx, dy; int x[4], y[4]; if (!PyArg_ParseTuple(args, "O!O!", &SKTrafoType, &trafo, &SKRectType, &rect)) return NULL; SKTrafo_TransformXY(trafo, rect->left, rect->top, &dx, &dy); x[0] = ceil(dx); y[0] = ceil(dy); SKTrafo_TransformXY(trafo, rect->right, rect->top, &dx, &dy); x[1] = ceil(dx); y[1] = ceil(dy); SKTrafo_TransformXY(trafo, rect->right, rect->bottom, &dx, &dy); x[2] = ceil(dx); y[2] = ceil(dy); SKTrafo_TransformXY(trafo, rect->left, rect->bottom, &dx, &dy); x[3] = ceil(dx); y[3] = ceil(dy); if ((x[0] == x[3] && y[0] == y[1]) || (y[0] == y[3] && x[0] == x[1])) { int temp; if (x[0] > x[2]) { temp = x[0]; x[0] = x[2]; x[2] = temp; } if (y[0] > y[2]) { temp = y[0]; y[0] = y[2]; y[2] = temp; } return Py_BuildValue("iiii", x[0], y[0], x[2] - x[0], y[2] - y[0]); } return Py_BuildValue("[(ii)(ii)(ii)(ii)(ii)]", x[0], y[0], x[1], y[1], x[2], y[2], x[3], y[3], x[0], y[0]); } /* * */ PyObject * SKAux_IdIndex(PyObject * self, PyObject * args) { PyObject * list, *obj, *item; int length, i, equal; if (!PyArg_ParseTuple(args, "OO", &list, &obj)) return NULL; if (!PySequence_Check(list)) { PyErr_SetString(PyExc_TypeError, "argument must be a sequence"); return NULL; } length = PySequence_Length(list); for (i = 0; i < length; i++) { item = PySequence_GetItem(list, i); equal = (item == obj); Py_DECREF(item); if (equal) break; } if (i < length) return PyInt_FromLong(i); Py_INCREF(Py_None); return Py_None; } /* * xlfd_char_range */ PyObject * xlfd_char_range(PyObject * self, PyObject * args) { unsigned char * text; int len; PyObject * result; int idx, count; char used[256]; char * cur; char * ranges; if (!PyArg_ParseTuple(args, "s#", &text, &len)) return NULL; if (len == 0) { return PyString_FromString(""); } for (idx = 0; idx < 256; idx++) used[idx] = 0; for (idx = 0; idx < len; idx++) used[(int)(text[idx])] = 1; count = 0; for (idx = 0; idx < 256; idx++) if (used[idx]) count++; ranges = malloc(4 * count + 1); if (!ranges) return NULL; cur = ranges; idx = 0; while (idx < 256) { if (used[idx]) { int first = idx, last; while (idx < 256 && used[idx]) idx++; last = idx - 1; if (first == last) cur += sprintf(cur, " %d", first); else cur += sprintf(cur, " %d_%d", first, last); } else idx++; } result = PyString_FromString(ranges + 1); free(ranges); return result; } typedef struct { PyObject_HEAD PyObject * dict; } SKCacheObject; static void SKCache_dealloc(SKCacheObject * self) { Py_DECREF(self->dict); PyObject_Del(self); } static PyObject * SKCache_getattr(SKCacheObject * self, char * name) { return PyObject_GetAttrString(self->dict, name); } static int SKCache_length(SKCacheObject * self) { return PyDict_Size(self->dict); } static PyObject * SKCache_subscript(SKCacheObject * self, PyObject *key) { PyObject * result = PyDict_GetItem(self->dict, key); if (result) { result = PyCObject_AsVoidPtr(result); Py_INCREF(result); } return result; } static int SKCache_ass_sub(SKCacheObject * self, PyObject * v, PyObject * w) { if (w == NULL) return PyDict_DelItem(self->dict, v); else { PyObject * obj = PyCObject_FromVoidPtr(w, NULL); int result = PyDict_SetItem(self->dict, v, obj); Py_DECREF(obj); return result; } } static PyMappingMethods SKCache_as_mapping = { (inquiry)SKCache_length, /*mp_length*/ (binaryfunc)SKCache_subscript, /*mp_subscript*/ (objobjargproc)SKCache_ass_sub, /*mp_ass_subscript*/ }; PyTypeObject SKCacheType = { PyObject_HEAD_INIT(NULL) 0, "SKCache", sizeof(SKCacheObject), 0, (destructor)SKCache_dealloc, /*tp_dealloc*/ (printfunc)0, /*tp_print*/ (getattrfunc)SKCache_getattr, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ &SKCache_as_mapping, /*tp_as_mapping*/ 0, /*tp_hash*/ 0, /* tp_call */ }; static PyObject * SKCache_New(void) { SKCacheObject * self = PyObject_New(SKCacheObject, &SKCacheType); if (!self) return NULL; self->dict = PyDict_New(); if (!self->dict) { PyObject_Del(self); return NULL; } return (PyObject*)self; } PyObject * SKCache_PyCreate(PyObject * self, PyObject * args) { return SKCache_New(); } UniConvertor-1.1.4/src/modules/skmod/skaux.h0000664000076400007640000000262111060500613017517 0ustar igorigor/* Sketch - A Python-based interactive drawing program * Copyright (C) 1996, 1997, 1998, 1999 by Bernhard Herzog * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef SKAUX_H #define SKAUX_H #include #if defined(__cplusplus) extern "C" { #endif PyObject * SKAux_DrawBezier(PyObject * self, PyObject * args); PyObject * SKAux_TransformRectangle(PyObject * self, PyObject * args); PyObject * SKAux_IdIndex(PyObject * self, PyObject * args); PyObject * xlfd_char_range(PyObject * self, PyObject * args); PyObject * SKCache_PyCreate(PyObject * self, PyObject * args); extern PyTypeObject SKCacheType; #define SKCache_Check(v) ((v)->ob_type == &SKCacheType) #if defined(__cplusplus) } #endif #endif /* SKAUX_H */ UniConvertor-1.1.4/src/modules/skmod/skimage.c0000664000076400007640000004525511060500613020011 0ustar igorigor/* Sketch - A Python-based interactive drawing program * Copyright (C) 1997, 1998, 1999, 2000 by Bernhard Herzog * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define __NO_MATH_INLINES #include #include #include #include "Imaging.h" #include "_sketchmodule.h" #include "sktrafo.h" #include "skcolor.h" #ifndef PI #define PI 3.14159265358979323846264338327 #endif /* redefine the ImagingObject struct defined in _imagingmodule.c */ /* there should be a better way to do this... */ typedef struct { PyObject_HEAD Imaging image; } ImagingObject; PyObject * fill_rgb_xy(PyObject * self, PyObject * args) { ImagingObject * image; int x, y, width, height; int xidx, yidx, otheridx, othercolor; double color[3]; unsigned char *dest; if (!PyArg_ParseTuple(args, "Oii(ddd)", &image, &xidx, &yidx, &color[0], &color[1], &color[2])) return NULL; if (xidx < 0 || xidx > 2 || yidx < 0 || yidx > 2 || xidx == yidx) return PyErr_Format(PyExc_ValueError, "xidx and yidx must be different " "and in the range [0..2] (x:%d, y:%d)", xidx, yidx); otheridx = 3 - xidx - yidx; othercolor = 255 * color[otheridx]; width = image->image->xsize - 1; height = image->image->ysize - 1; for (y = 0; y <= height; y++) { dest = (unsigned char*)(image->image->image32[y]); for (x = 0; x <= width; x++, dest += 4) { dest[xidx] = (255 * x) / width; dest[yidx] = (255 * (height - y)) / height; dest[otheridx] = othercolor; } } Py_INCREF(Py_None); return Py_None; } PyObject * fill_rgb_z(PyObject * self, PyObject * args) { ImagingObject * image; int x, y, width, height; int idx, idx1, idx2, val1, val2; double r, g, b; unsigned char *dest; if (!PyArg_ParseTuple(args, "Oi(ddd)", &image, &idx, &r, &g, &b)) return NULL; switch (idx) { case 0: idx1 = 1; val1 = 255 * g; idx2 = 2; val2 = 255 * b; break; case 1: idx1 = 0; val1 = 255 * r; idx2 = 2; val2 = 255 * b; break; case 2: idx1 = 0; val1 = 255 * r; idx2 = 1; val2 = 255 * g; break; default: PyErr_SetString(PyExc_ValueError, "idx must 0, 1 or 2"); return NULL; } width = image->image->xsize - 1; height = image->image->ysize - 1; for (y = 0; y <= height; y++) { dest = (unsigned char*)(image->image->image32[y]); for (x = 0; x <= width; x++, dest += 4) { dest[idx1] = val1; dest[idx2] = val2; dest[idx] = (255 * (height - y)) / height; } } Py_INCREF(Py_None); return Py_None; } static void hsv_to_rgb(double h, double s, double v, unsigned char * rgb) { if (s == 0.0) { rgb[0] = rgb[1] = rgb[2] = 255 * v; } else { double p, q, t, f; int i; h *= 6; i = (int)h; f = h - i; p = v * (1.0 - s); q = v * (1.0 - s * f); t = v * (1.0 - s * (1.0 - f)); switch (i) { case 0: case 6: rgb[0] = 255 * v; rgb[1] = 255 * t; rgb[2] = 255 * p; break; case 1: rgb[0] = 255 * q; rgb[1] = 255 * v; rgb[2] = 255 * p; break; case 2: rgb[0] = 255 * p; rgb[1] = 255 * v; rgb[2] = 255 * t; break; case 3: rgb[0] = 255 * p; rgb[1] = 255 * q; rgb[2] = 255 * v; break; case 4: rgb[0] = 255 * t; rgb[1] = 255 * p; rgb[2] = 255 * v; break; case 5: rgb[0] = 255 * v; rgb[1] = 255 * p; rgb[2] = 255 * q; break; } } } PyObject * fill_hsv_xy(PyObject * self, PyObject * args) { ImagingObject * image; int x, y, width, height; int xidx, yidx; double color[3]; unsigned char *dest; if (!PyArg_ParseTuple(args, "Oii(ddd)", &image, &xidx, &yidx, &color[0], &color[1], &color[2])) return NULL; if (xidx < 0 || xidx > 2 || yidx < 0 || yidx > 2 || xidx == yidx) return PyErr_Format(PyExc_ValueError, "xidx and yidx must be different and in the range " "[0..2] (x:%d, y:%d)", xidx, yidx); width = image->image->xsize - 1; height = image->image->ysize - 1; for (y = 0; y <= height; y++) { dest = (unsigned char*)(image->image->image32[y]); for (x = 0; x <= width; x++, dest += 4) { color[xidx] = (double)x / width; color[yidx] = (double)(height - y) / height; hsv_to_rgb(color[0], color[1], color[2], dest); } } Py_INCREF(Py_None); return Py_None; } PyObject * fill_hsv_z(PyObject * self, PyObject * args) { ImagingObject * image; int x, y, width, height; int idx; double hsv[3]; unsigned char *dest; if (!PyArg_ParseTuple(args, "Oi(ddd)", &image, &idx, &hsv[0], &hsv[1], &hsv[2])) return NULL; if (idx < 0 || idx > 2) { PyErr_SetString(PyExc_ValueError, "idx must be in the range [0,2]"); return NULL; } width = image->image->xsize - 1; height = image->image->ysize - 1; for (y = 0; y <= height; y++) { dest = (unsigned char*)(image->image->image32[y]); for (x = 0; x <= width; x++, dest += 4) { hsv[idx] = (double)(height - y) / height; hsv_to_rgb(hsv[0], hsv[1], hsv[2], dest); } } Py_INCREF(Py_None); return Py_None; } /* * */ static void fill_transformed_tile_gray(ImagingObject * image, ImagingObject * tile, SKTrafoObject * trafo) { int x, y, width, height, itx, ity; int tile_width, tile_height; double tx, ty, dx, dy; UINT8 *dest, **src; width = image->image->xsize; height = image->image->ysize; tile_width = tile->image->xsize; tile_height = tile->image->ysize; src = tile->image->image8; dx = trafo->m11; dy = trafo->m21; for (y = 0; y < height; y++) { dest = (UINT8*)(image->image->image32[y]); tx = y * trafo->m12 + trafo->v1; ty = y * trafo->m22 + trafo->v2; for (x = 0; x < width; x++, dest += 4, tx += dx, ty += dy) { itx = ((int)tx) % tile_width; if (itx < 0) itx += tile_width; ity = ((int)ty) % tile_height; if (ity < 0) ity += tile_height; dest[0] = dest[1] = dest[2] = src[ity][itx]; } } } static void fill_transformed_tile_rgb(ImagingObject * image, ImagingObject * tile, SKTrafoObject * trafo) { int x, y, width, height, itx, ity; int tile_width, tile_height; double tx, ty, dx, dy; INT32 *dest, **src; width = image->image->xsize; height = image->image->ysize; tile_width = tile->image->xsize; tile_height = tile->image->ysize; src = tile->image->image32; dx = trafo->m11; dy = trafo->m21; for (y = 0; y < height; y++) { dest = image->image->image32[y]; tx = y * trafo->m12 + trafo->v1; ty = y * trafo->m22 + trafo->v2; for (x = 0; x < width; x++, dest++, tx += dx, ty += dy) { itx = ((int)tx) % tile_width; if (itx < 0) itx += tile_width; ity = ((int)ty) % tile_height; if (ity < 0) ity += tile_height; *dest = src[ity][itx]; } } } PyObject * fill_transformed_tile(PyObject * self, PyObject * args) { ImagingObject * image, *tile; SKTrafoObject * trafo; if (!PyArg_ParseTuple(args, "OOO!", &image, &tile, &SKTrafoType, &trafo)) return NULL; if (strncmp(tile->image->mode, "RGB", 3) == 0) { fill_transformed_tile_rgb(image, tile, trafo); } else if (strcmp(tile->image->mode, "L") == 0) { fill_transformed_tile_gray(image, tile, trafo); } else return PyErr_Format(PyExc_TypeError, "Images of mode %s cannot be used as tiles", tile->image->mode); Py_INCREF(Py_None); return Py_None; } #define POS_FACTOR 65536 typedef struct { unsigned int pos; int r, g, b; } GradientEntry; typedef GradientEntry * Gradient; static int convert_color(PyObject * object, GradientEntry * entry) { if (PyTuple_Check(object)) { double red, green, blue; if (!PyArg_ParseTuple(object, "ddd", &red, &green, &blue)) return 0; entry->r = 255 * red; entry->g = 255 * green; entry->b = 255 * blue; } else if (SKColor_Check(object)) { entry->r = 255 * ((SKColorObject*)object)->red; entry->g = 255 * ((SKColorObject*)object)->green; entry->b = 255 * ((SKColorObject*)object)->blue; } else { PyErr_SetString(PyExc_TypeError, "color spec must be tuple of floats or color object"); return 0; } return 1; } static Gradient gradient_from_list(PyObject * list) { int idx, length; Gradient gradient; length = PySequence_Length(list); if (length < 2) { PyErr_SetString(PyExc_TypeError, "gradient list too short"); return NULL; } gradient = malloc(length * sizeof(GradientEntry)); if (!gradient) { PyErr_NoMemory(); return NULL; } for (idx = 0; idx < length; idx++) { int result; double pos; PyObject * item = PySequence_GetItem(list, idx); result = PyArg_ParseTuple(item, "dO&:" "Gradient Element must be a tuple of " "a float and a color", &pos, convert_color, &(gradient[idx])); gradient[idx].pos = POS_FACTOR * pos; Py_DECREF(item); if (!result) goto fail; } return gradient; fail: free(gradient); return NULL; } void store_gradient_color(Gradient gradient, int length, double t, unsigned char *dest) { GradientEntry * entry = gradient; unsigned int it = (t < 0) ? 0 : POS_FACTOR * t; if (it <= 0 || it >= POS_FACTOR) { if (it <= 0) entry = gradient; else entry = gradient + length - 1; dest[0] = entry->r; dest[1] = entry->g; dest[2] = entry->b; } else { int min = 0, max = length - 1, idx = (max + min) / 2; unsigned int tt; while (max - min != 1) { if (gradient[idx].pos < it) min = idx; else max = idx; idx = (max + min) / 2; } entry = gradient + min; tt = (POS_FACTOR * (it - entry->pos)) / (entry[1].pos - entry->pos); dest[0] = entry->r + (tt * (entry[1].r - entry->r)) / POS_FACTOR; dest[1] = entry->g + (tt * (entry[1].g - entry->g)) / POS_FACTOR; dest[2] = entry->b + (tt * (entry[1].b - entry->b)) / POS_FACTOR; } } #define free_gradient(gradient) free(gradient) static void horizontal_axial_gradient(ImagingObject * image, Gradient gradient, int length, int x0, int x1) { unsigned char *dest; int maxx, height, x, y; double factor = 1.0 / (x1 - x0); maxx = image->image->xsize - x0; height = image->image->ysize; dest = (unsigned char*)(image->image->image32[0]); for (x = -x0; x < maxx; x++, dest += 4) { store_gradient_color(gradient, length, factor * x, dest); } for (y = 1; y < height; y++) { memcpy(image->image->image32[y], image->image->image32[0], 4 * image->image->xsize); } } static void vertical_axial_gradient(ImagingObject * image, Gradient gradient, int length, int y0, int y1) { INT32 *dest; int height, width, x, y; double factor = 1.0 / (y1 - y0); width = image->image->xsize; height = image->image->ysize; for (y = 0; y < height; y++) { dest = image->image->image32[y]; store_gradient_color(gradient, length, factor * (y - y0), (unsigned char*)dest); for (x = 1; x < width; x++) { dest[x] = dest[0]; } } } #define ANGLE_EPSILON 0.01 PyObject * fill_axial_gradient(PyObject * self, PyObject * args) { ImagingObject * image; int x, y, maxx, maxy; double x0, y0, x1, y1, dx, dy, angle; int length; unsigned char *dest; PyObject * list; Gradient gradient; if (!PyArg_ParseTuple(args, "OOdddd", &image, &list, &x0, &y0, &x1, &y1)) return NULL; if (!PySequence_Check(list)) { PyErr_SetString(PyExc_TypeError, "gradient argument must be a sequence"); return NULL; } if (x0 == x1 && y0 == y1) { Py_INCREF(Py_None); return Py_None; } length = PySequence_Length(list); gradient = gradient_from_list(list); if (!gradient) return NULL; dx = x1 - x0; dy = y1 - y0; angle = atan2(dy, dx); if (fabs(angle) < ANGLE_EPSILON || fabs(fabs(angle) - PI) < ANGLE_EPSILON) { horizontal_axial_gradient(image, gradient, length, (int)(ceil(x0)), (int)(ceil(x1))); } else if (fabs(angle - PI/2) < ANGLE_EPSILON || fabs(angle + PI/2) < ANGLE_EPSILON) { vertical_axial_gradient(image, gradient, length, (int)(ceil(y0)), (int)(ceil(y1))); } else { double t, dt; double lensqr = hypot(dx, dy) ; /*(double)dx * dx + (double)dy * dy;*/ lensqr *= lensqr; dt = dx / lensqr; maxx = image->image->xsize; maxy = image->image->ysize; for (y = 0; y < maxy; y++) { dest = (unsigned char*)(image->image->image32[y]); t = (dx * -x0 + dy * (y - y0)) / lensqr; for (x = 0; x < maxx; x++, dest += 4, t += dt) { store_gradient_color(gradient, length, t, dest); } } } free_gradient(gradient); Py_INCREF(Py_None); return Py_None; } #if 0 PyObject * fill_axial_gradient(PyObject * self, PyObject * args) { ImagingObject * image; int x, y, maxx, maxy, dx, dy; int x0, y0, x1, y1; int length; unsigned char *dest; PyObject * list; Gradient gradient; if (!PyArg_ParseTuple(args, "OOiiii", &image, &list, &x0, &y0, &x1, &y1)) return NULL; if (!PySequence_Check(list)) { PyErr_SetString(PyExc_TypeError, "gradient argument must be a sequence"); return NULL; } if (x0 == x1 && y0 == y1) { Py_INCREF(Py_None); return Py_None; } length = PySequence_Length(list); gradient = gradient_from_list(list); if (!gradient) return NULL; dx = x1 - x0; dy = y1 - y0; if (dy == 0) { horizontal_axial_gradient(image, gradient, length, x0, x1); } else if (dx == 0) { vertical_axial_gradient(image, gradient, length, y0, y1); } else { double t, dt; double lensqr = hypot(dx, dy) ; /*(double)dx * dx + (double)dy * dy;*/ lensqr *= lensqr; dt = dx / lensqr; maxx = image->image->xsize - x0; maxy = image->image->ysize - y0; for (y = -y0; y < maxy; y++) { dest = (unsigned char*)(image->image->image32[y + y0]); t = (dx * -x0 + dy * y) / lensqr; for (x = -x0; x < maxx; x++, dest += 4, t += dt) { store_gradient_color(gradient, length, t, dest); } } } free_gradient(gradient); Py_INCREF(Py_None); return Py_None; } #endif PyObject * fill_radial_gradient(PyObject * self, PyObject * args) { ImagingObject * image; int x, y, maxx, maxy; int cx, cy, r0, r1; double factor; int length; unsigned char *dest; PyObject * list; Gradient gradient; if (!PyArg_ParseTuple(args, "OOiiii", &image, &list, &cx, &cy, &r0, &r1)) return NULL; if (!PySequence_Check(list)) { PyErr_SetString(PyExc_TypeError, "gradient argument must be a sequence"); return NULL; } length = PySequence_Length(list); gradient = gradient_from_list(list); if (!gradient) return NULL; factor = 1.0 / (r1 - r0); maxx = image->image->xsize - cx; maxy = image->image->ysize - cy; for (y = -cy; y < maxy; y++) { dest = (unsigned char*)(image->image->image32[y + cy]); for (x = -cx; x < maxx; x++, dest += 4) { store_gradient_color(gradient, length, factor * (hypot(x, y) - r0), dest); } } free_gradient(gradient); Py_INCREF(Py_None); return Py_None; } PyObject * fill_conical_gradient(PyObject * self, PyObject * args) { ImagingObject * image; int x, y, maxx, maxy; int cx, cy; double angle, t; int length; unsigned char *dest; PyObject * list; Gradient gradient; if (!PyArg_ParseTuple(args, "OOiid", &image, &list, &cx, &cy, &angle)) return NULL; if (!PySequence_Check(list)) { PyErr_SetString(PyExc_TypeError, "gradient argument must be a sequence"); return NULL; } length = PySequence_Length(list); gradient = gradient_from_list(list); if (!gradient) return NULL; angle = fmod(angle, 2 * PI); if (angle < -PI) angle += 2 * PI; else if (angle > PI) angle -= 2 * PI; maxx = image->image->xsize - cx; maxy = image->image->ysize - cy; for (y = -cy; y < maxy; y++) { dest = (unsigned char*)(image->image->image32[y + cy]); for (x = -cx; x < maxx; x++, dest += 4) { if (x || y) { t = atan2(y, x) - angle; if (t < -PI) t += 2 * PI; else if (t > PI) t -= 2 * PI; t = fabs(t / PI); } else t = 0; store_gradient_color(gradient, length, t, dest); } } free_gradient(gradient); Py_INCREF(Py_None); return Py_None; } static char * hexdigit = "0123456789ABCDEF"; static void write_ps_hex_rgb(FILE * out, int width, int height, char ** data, int line_length, char * prefix) { int x, y; char * line; int written = 0; for (y = 0; y < height; y++) { line = data[y]; for (x = 0; x < width; x++) { if (x % 4 == 3) continue; if (written == 0 && prefix) { fputs(prefix, out); } putc(hexdigit[(int)(line[x] >> 4) & 0x0F], out); putc(hexdigit[(int)(line[x] & 0x0F)], out); written += 2; if (written > line_length) { putc('\n', out); written = 0; } } } if (written) putc('\n', out); } static void write_ps_hex_gray(FILE * out, int width, int height, char ** data, int line_length, char * prefix) { int x, y; char * line; int written = 0; for (y = 0; y < height; y++) { line = data[y]; for (x = 0; x < width; x++) { if (written == 0 && prefix) { fputs(prefix, out); } putc(hexdigit[(int)(line[x] >> 4) & 0x0F], out); putc(hexdigit[(int)(line[x] & 0x0F)], out); written += 2; if (written > line_length) { putc('\n', out); written = 0; } } } if (written) putc('\n', out); } PyObject * skimage_write_ps_hex(PyObject * self, PyObject * args) { PyObject * pyfile; ImagingObject * imobj; int line_length = 80; char * prefix = NULL; if (!PyArg_ParseTuple(args, "OO!|is", &imobj, &PyFile_Type, &pyfile, &line_length, &prefix)) return NULL; line_length = line_length - 2; if (line_length < 0) line_length = 0; if (imobj->image->pixelsize == 4) write_ps_hex_rgb(PyFile_AsFile(pyfile), imobj->image->linesize, imobj->image->ysize, imobj->image->image, line_length, prefix); else if (imobj->image->pixelsize == 1) write_ps_hex_gray(PyFile_AsFile(pyfile), imobj->image->linesize, imobj->image->ysize, imobj->image->image, line_length, prefix); Py_INCREF(Py_None); return Py_None; } UniConvertor-1.1.4/src/modules/skmod/skimage.h0000664000076400007640000000271611060500613020011 0ustar igorigor/* Sketch - A Python-based interactive drawing program * Copyright (C) 1996, 1997, 1998 by Bernhard Herzog * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef SKIMAGE_H #define SKIMAGE_H #include PyObject * skimage_write_ps_hex(PyObject * self, PyObject * args); PyObject * fill_conical_gradient(PyObject * self, PyObject * args); PyObject * fill_radial_gradient(PyObject * self, PyObject * args); PyObject * fill_axial_gradient(PyObject * self, PyObject * args); PyObject * fill_transformed_tile(PyObject * self, PyObject * args); PyObject * fill_hsv_z(PyObject * self, PyObject * args); PyObject * fill_hsv_xy(PyObject * self, PyObject * args); PyObject * fill_rgb_z(PyObject * self, PyObject * args); PyObject * fill_rgb_xy(PyObject * self, PyObject * args); #endif UniConvertor-1.1.4/src/modules/skmod/skpoint.c0000664000076400007640000003445311060500613020056 0ustar igorigor/* Sketch - A Python-based interactive drawing program * Copyright (C) 1996, 1997, 1998, 2001, 2002, 2006 by Bernhard Herzog * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* A Python Object, `SKPoint', representing a single 2D point or vector * with float coordinates. * * SKPoint objects are immutable. */ #include #include #include #include "skpoint.h" #if defined(PY_MAJOR_VERSION) && PY_VERSION_HEX >= 0x02010000 /* Beginning with Python 2.1, use the new style numbers */ #define USE_NEWSTYLE_NUMBERS #endif #define SKPOINT_COUNT_ALLOC 1 #if SKPOINT_COUNT_ALLOC static int allocated = 0; #endif PyObject * SKPoint_FromXY(SKCoord x, SKCoord y) { SKPointObject * self; self = PyObject_New(SKPointObject, &SKPointType); if (self == NULL) return NULL; self->x = x; self->y = y; #if SKPOINT_COUNT_ALLOC allocated += 1; #endif return (PyObject*)self; } static void skpoint_dealloc(SKPointObject * self) { PyObject_Del(self); #if SKPOINT_COUNT_ALLOC allocated -= 1; #endif } static int skpoint_compare(SKPointObject * v, SKPointObject * w) { int ret; if (!SKPoint_Check(v) || !SKPoint_Check(w)) { return strcmp(v->ob_type->tp_name, w->ob_type->tp_name); } ret = (v->x < w->x) ? -1 : (v->x > w->x) ? +1 : 0; if (ret) return ret; return (v->y < w->y) ? -1 : (v->y > w->y) ? +1 : 0; } static PyObject * skpoint_repr(SKPointObject * self) { char buf[1000]; sprintf(buf, "Point(%g, %g)", self->x, self->y); return PyString_FromString(buf); } /* mathematical operations */ #define POINT(w) ((SKPointObject*)w) static PyObject * skpoint_add(SKPointObject * v, PyObject * w) { if (SKPoint_Check(v) && SKPoint_Check(w)) return SKPoint_FromXY(v->x + POINT(w)->x, v->y + POINT(w)->y); #ifdef USE_NEWSTYLE_NUMBERS Py_INCREF(Py_NotImplemented); return Py_NotImplemented; #else PyErr_SetString(PyExc_TypeError, "Points must be added to Points"); return NULL; #endif } static PyObject * skpoint_sub(SKPointObject * v, PyObject * w) { if (SKPoint_Check(v) && SKPoint_Check(w)) return SKPoint_FromXY(v->x - POINT(w)->x, v->y - POINT(w)->y); #ifdef USE_NEWSTYLE_NUMBERS Py_INCREF(Py_NotImplemented); return Py_NotImplemented; #else PyErr_SetString(PyExc_TypeError, "Points must be subtracted from Points"); return NULL; #endif } #define FLOAT_AS_DOUBLE(object, var) \ (var = PyFloat_AsDouble(object), \ PyErr_Occurred() ? (PyErr_Clear(), 0) : 1) static PyObject * skpoint_multiply(PyObject * v, PyObject * w) { /* We want to allow products of two points as well as products of * points and numbers. Python garantees that at least one of the * operands is a point, so we first test whether both operands are * points and compute the inner product in that case. Otherwise, at * least one parameter must be a number, so try to convert that * number to float and return the product of the number and the * point */ double number; SKPointObject * point = NULL; if (SKPoint_Check(v) && SKPoint_Check(w)) { /* Both are points, return the inner product */ return PyFloat_FromDouble(POINT(v)->x * POINT(w)->x + POINT(v)->y * POINT(w)->y); } else if (FLOAT_AS_DOUBLE(w, number)) { /* w is a number, so v must be the point */ point = (SKPointObject*)v; } else if (FLOAT_AS_DOUBLE(v, number)) { /* v is a number, so w must be the point */ point = (SKPointObject*)w; } if (point) { return SKPoint_FromXY(number * point->x, number * point->y); } #ifdef USE_NEWSTYLE_NUMBERS Py_INCREF(Py_NotImplemented); return Py_NotImplemented; #else PyErr_SetString(PyExc_TypeError, "Point can only be multiplied by a Point or a number"); return NULL; #endif } static PyObject * skpoint_divide(PyObject * v, PyObject * w) { /* we can only divide if v is a point and w is a number. Since one * of the operands will always be a number, we only have to check w. */ double number; if (FLOAT_AS_DOUBLE(w, number)) { return SKPoint_FromXY(POINT(v)->x / number, POINT(v)->y / number); } #ifdef USE_NEWSTYLE_NUMBERS Py_INCREF(Py_NotImplemented); return Py_NotImplemented; #else PyErr_SetString(PyExc_TypeError, "Point can only be divided by a number"); return NULL; #endif } static PyObject * skpoint_neg(SKPointObject * self) { return SKPoint_FromXY(-self->x, -self->y); } static PyObject * skpoint_pos(PyObject * self) { Py_INCREF(self); return (PyObject *)self; } static PyObject * skpoint_abs(SKPointObject * self) { return PyFloat_FromDouble(hypot(self->x, self->y)); } static int skpoint_nonzero(SKPointObject * self) { return self->x != 0.0 || self->y != 0.0; } /* this coercion can be dangerous: We want to be able to multiply a * point with a number. To achieve this, we must convert the second * operand to a float object. * * Unfortunately we don't know whether it really was the second operand * or even if the results will be multiplied or added. NUMBER + POINT * calls this function to coerce, but calls NUMBER's operator method, * treating the point object as a float object. In the current Python * implementation on Linux, this doesn't lead to segmentation faults, * because the C-struct of a float object is at least not larger than * the struct for a point, the results are meaningless, though. * * Fortunately, for multiplication, python swaps the operands if the * second operand is a sequence before it attempts to coerce, so that * skpoint_multiply is finally called as intended. * * Another useful coercion would convert sequences of two numbers to * point objects, allowing to write e. g. `p + (1, 0)'. This doesn't * work, because skpoint_coerce will never be called (at least not in * Python 1.5.1) * * Coerce will only be called when compiled for Python version older * than 2.1. */ static int skpoint_coerce(PyObject ** pv, PyObject ** pw) { PyObject * as_float = PyNumber_Float(*pw); if (as_float) { *pw = as_float; Py_INCREF(*pv); return 0; } return -1; } /* point as sequence */ static int skpoint_length(PyObject *self) { return 2; } /* Concatenate Points. * * Conceptually, points treated as sequences cannot be concatenated. It * doesn't make sense: the concatenation of two points in R^2 yields a * point in R^4 ?. * * Normally, we should be able to set the corresponding field in * tp_as_sequence to 0. This does not work as some code in the python * interpreter just tests whether tp_as_sequence is not NULL and doesn't * test sq_concat. Thus we have to provide this method and it should * raise an exception to indicte that concat is imposssible. * * Annoyingly, even this dos not work. PyNumber_Add in abstract.c checks * for tp_as_sequence before it checks for tp_as_number which means that * whenever we want to add two points skpoint_concat is called! Since * concat is only called from abstract.c we might get away with treating * `concat' as `add'. (This applies to Python 1.5.1) */ static PyObject * skpoint_concat(PyObject *self, PyObject *other) { if (SKPoint_Check(self) && SKPoint_Check(other)) return skpoint_add((SKPointObject*)self, other); PyErr_SetString(PyExc_TypeError, "concat/add requires two SKPoint objects"); return NULL; } /* Repeat is needed for Python 2.1 and higher */ static PyObject * skpoint_repeat(PyObject *self, int n) { return SKPoint_FromXY(POINT(self)->x * n, POINT(self)->y * n); } static PyObject * skpoint_item(SKPointObject *self, int i) { double item; switch (i) { case 0: item = self->x; break; case 1: item = self->y; break; default: PyErr_SetString(PyExc_IndexError, "index must be 0 or 1"); return NULL; } return PyFloat_FromDouble(item); } static PyObject * skpoint_slice(PyObject *self, int ilow, int ihigh) { PyErr_SetString(PyExc_RuntimeError, "slicing not supported for SKPointObjects"); return NULL; } /* * Methods */ /* * point.normalized() * * Return a point with the same direction but length 1. */ static PyObject * skpoint_normalized(SKPointObject * self, PyObject * args) { double len; if (!PyArg_ParseTuple(args, "")) return NULL; len = hypot(self->x, self->y); if (len == 0) { PyErr_SetString(PyExc_ZeroDivisionError, "Point().normalized"); return NULL; } return SKPoint_FromXY(self->x / len, self->y / len); } /* in earlier python implementations it seemed impossible to allow * multiplication of numbers and point objects. Now it works, so this * method is obsolete: */ /* * point.scaled(FACTOR) * * Return FACTOR * point (scalar multiplication). * */ #if 0 static PyObject * skpoint_scaled(SKPointObject * self, PyObject * args) { double scalar; if (!PyArg_ParseTuple(args, "d", &scalar)) return NULL; return SKPoint_FromXY(self->x * scalar, self->y * scalar); } #endif /* * point.polar() * * Return a tuple (R, PHI), the polar coordinates describing the * same point */ static PyObject * skpoint_polar(SKPointObject * self, PyObject * args) { double r = hypot(self->x, self->y); double phi = atan2(self->y, self->x); if (r == 0.0) phi = 0.0; return Py_BuildValue("dd", r, phi); } static struct PyMethodDef skpoint_methods[] = { {"normalized", (PyCFunction)skpoint_normalized, 1}, {"polar", (PyCFunction)skpoint_polar, 1}, {NULL, NULL} }; static PyObject * skpoint_getattr(PyObject * self, char * name) { if (name[0] == 'x' && name[1] == '\0') return PyFloat_FromDouble(((SKPointObject*)self)->x); if (name[0] == 'y' && name[1] == '\0') return PyFloat_FromDouble(((SKPointObject*)self)->y); return Py_FindMethod(skpoint_methods, self, name); } static PyObject * skpoint_not_implemented(void) { PyErr_SetString(PyExc_TypeError, "operator not implemented for SKPoint"); return NULL; } static PyNumberMethods skpoint_as_number = { (binaryfunc)skpoint_add, /*nb_add*/ (binaryfunc)skpoint_sub, /*nb_subtract*/ (binaryfunc)skpoint_multiply, /*nb_multiply*/ (binaryfunc)skpoint_divide, /*nb_divide*/ (binaryfunc)skpoint_not_implemented, /*nb_remainder*/ (binaryfunc)skpoint_not_implemented, /*nb_divmod*/ (ternaryfunc)skpoint_not_implemented, /*nb_power*/ (unaryfunc)skpoint_neg, /*nb_negative*/ (unaryfunc)skpoint_pos, /*nb_positive*/ (unaryfunc)skpoint_abs, /*nb_absolute*/ (inquiry)skpoint_nonzero, /*nb_nonzero*/ (unaryfunc)0, /*nb_invert*/ (binaryfunc)0, /*nb_lshift*/ (binaryfunc)0, /*nb_rshift*/ (binaryfunc)0, /*nb_and*/ (binaryfunc)0, /*nb_xor*/ (binaryfunc)0, /*nb_or*/ skpoint_coerce, /*nb_coerce*/ (unaryfunc)0, /*nb_int*/ (unaryfunc)0, /*nb_long*/ (unaryfunc)0, /*nb_float*/ (unaryfunc)0, /*nb_oct*/ (unaryfunc)0, /*nb_hex*/ }; static PySequenceMethods skpoint_as_sequence = { skpoint_length, /*sq_length*/ skpoint_concat, /*sq_concat*/ skpoint_repeat, /*sq_repeat*/ (intargfunc)skpoint_item, /*sq_item*/ skpoint_slice, /*sq_slice*/ 0, /*sq_ass_item*/ 0, /*sq_ass_slice*/ }; PyTypeObject SKPointType = { PyObject_HEAD_INIT(NULL) 0, "skpoint", sizeof(SKPointObject), 0, (destructor)skpoint_dealloc, /*tp_dealloc*/ (printfunc)NULL, /*tp_print*/ skpoint_getattr, /*tp_getattr*/ 0, /*tp_setattr*/ (cmpfunc)skpoint_compare, /*tp_compare*/ (reprfunc)skpoint_repr, /*tp_repr*/ &skpoint_as_number, /*tp_as_number*/ &skpoint_as_sequence, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ #ifdef USE_NEWSTYLE_NUMBERS Py_TPFLAGS_CHECKTYPES /*tp_flags*/ #endif }; /* * Module Functions */ /* * skpoint.Point(X, Y) * * Return a new point object with coordinates X, Y. */ PyObject * SKPoint_PyPoint(PyObject * self, PyObject * args) { double x, y; PyObject * coords; if (PyTuple_Size(args) == 1) { coords = PyTuple_GET_ITEM(args, 0); if (SKPoint_Check(coords)) { Py_INCREF(coords); return coords; } } else coords = args; if (!skpoint_extract_xy(coords, &x, &y)) { PyErr_SetString(PyExc_TypeError, "expected two numbers or a sequence of two numbers"); return NULL; } return SKPoint_FromXY(x, y); } /* * skpoint.Polar(R, PHI) * skpoint.Polar(PHI) * * Return a new point object given in polar coordinates R and PHI. */ PyObject * SKPoint_PyPolar(PyObject * self, PyObject * args) { double r = 1.0, phi; if (PyTuple_Size(args) == 2) { if (!PyArg_ParseTuple(args, "dd", &r, &phi)) return NULL; } else { if (!PyArg_ParseTuple(args, "d", &phi)) return NULL; } return SKPoint_FromXY(r * cos(phi), r * sin(phi)); } /* * */ PyObject * skpoint_allocated(PyObject * self, PyObject * args) { #if SKPOINT_COUNT_ALLOC return PyInt_FromLong(allocated); #else return PyInt_FromLong(-1); #endif } /* * Convenience functions for other C modules */ /* Extract a coordinate pair from SEQUENCE and store it in *X, *Y. * SEQUENCE may be an SKPointObject or any sequence of two objects that * can be converted to a double. Return 1 on success, 0 otherwise. */ int skpoint_extract_xy(PyObject * sequence, double * x, double * y) { if (SKPoint_Check(sequence)) { *x = ((SKPointObject*)sequence)->x; *y = ((SKPointObject*)sequence)->y; return 1; } if (PySequence_Check(sequence) && PySequence_Length(sequence) == 2) { PyObject * xo = PySequence_GetItem(sequence, 0); PyObject * yo = PySequence_GetItem(sequence, 1); if (xo && yo) { *x = PyFloat_AsDouble(xo); *y = PyFloat_AsDouble(yo); } Py_XDECREF(xo); Py_XDECREF(yo); if(PyErr_Occurred()) return 0; return 1; } return 0; } UniConvertor-1.1.4/src/modules/skmod/skpoint.h0000664000076400007640000000315511060500613020056 0ustar igorigor/* Sketch - A Python-based interactive drawing program * Copyright (C) 1996, 1997, 1998 by Bernhard Herzog * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef SKPOINT_H #define SKPOINT_H #if defined(__cplusplus) extern "C" { #endif #include /* the type of a coordinate. T_SKCOORD is used for struct member access in python and MUST correspond to the typedef */ typedef float SKCoord; #define T_SKCOORD T_FLOAT typedef struct { PyObject_HEAD SKCoord x, y; } SKPointObject; extern PyTypeObject SKPointType; #define SKPoint_Check(v) ((v)->ob_type == &SKPointType) PyObject * SKPoint_FromXY(SKCoord x, SKCoord y); int skpoint_extract_xy(PyObject * sequence, double * x, double * y); PyObject * skpoint_allocated(PyObject * self, PyObject * args); PyObject * SKPoint_PyPolar(PyObject * self, PyObject * args); PyObject * SKPoint_PyPoint(PyObject * self, PyObject * args); #if defined(__cplusplus) } #endif #endif UniConvertor-1.1.4/src/modules/skmod/skcolor.c0000664000076400007640000001541711060500613020042 0ustar igorigor/* Sketch - A Python-based interactive drawing program * Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2006 by Bernhard Herzog * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "_sketchmodule.h" #include "skcolor.h" /* * RGBColor */ #define SKCOLOR_COUNT_ALLOC 1 #define SKCOLOR_SUBALLOCATE 1 #if SKCOLOR_COUNT_ALLOC static int skcolor_allocated = 0; #endif #if SKCOLOR_SUBALLOCATE #define BLOCK_SIZE 1000 /* 1K less typical malloc overhead */ #define N_COLOROBJECTS (BLOCK_SIZE / sizeof(SKColorObject)) static SKColorObject * fill_free_list(void) { SKColorObject *p, *q; p = PyMem_Malloc(sizeof(SKColorObject) * N_COLOROBJECTS); if (p == NULL) return (SKColorObject *)PyErr_NoMemory(); q = p + N_COLOROBJECTS; while (--q > p) q->ob_type = (PyTypeObject*)(q-1); q->ob_type = NULL; return p + N_COLOROBJECTS - 1; } static SKColorObject *free_list = NULL; #endif /* SKCOLOR_SUBALLOCATE */ #define SKColor_CHECK_COMPONENT(comp) (0.0 <= (comp) && (comp) <= 1.0) PyObject * SKColor_FromRGB(double red, double green, double blue) { SKColorObject * self; if (!SKColor_CHECK_COMPONENT(red) || !SKColor_CHECK_COMPONENT(green) || !SKColor_CHECK_COMPONENT(blue)) { /*fprintf(stderr, "SKColor_FromRGB %g, %g, %g\n", red, green, blue);*/ PyErr_SetString(PyExc_ValueError, "color components must be in the range [0.0 .. 1.0]"); return NULL; } #if SKCOLOR_SUBALLOCATE if (free_list == NULL) { if ((free_list = fill_free_list()) == NULL) return NULL; } self = free_list; free_list = (SKColorObject *)(free_list->ob_type); self->ob_type = &SKColorType; _Py_NewReference(self); #else self = PyObject_New(SKColorObject, &SKColorType); if (!self) return NULL; #endif self->red = red; self->green = green; self->blue = blue; #if SKCOLOR_COUNT_ALLOC skcolor_allocated++; #endif return (PyObject*)self; } static void skcolor_dealloc(SKColorObject * self) { #if SKCOLOR_SUBALLOCATE self->ob_type = (PyTypeObject*)free_list; free_list = self; #else PyObject_Del(self); #endif #if SKCOLOR_COUNT_ALLOC skcolor_allocated--; #endif } #define COMPARE(c1,c2) ((c1) < (c2) ? -1 : ((c1) > (c2) ? +1 : 0 )) static int skcolor_compare(SKColorObject * v, SKColorObject * w) { int result; if ((result = COMPARE(v->red, w->red)) != 0) return result; if ((result = COMPARE(v->green, w->green)) != 0) return result; return COMPARE(v->blue, w->blue); } #undef COMPARE static long skcolor_hash(SKColorObject * self) { long x; x = self->red * 65535.0; x = (255 * x) ^ (long)(self->green * 65535.0); x = (255 * x) ^ (long)(self->blue * 65535.0); if (x == -1) return -2; return x; } static PyObject * skcolor_repr(SKColorObject * self) { char buf[1000]; sprintf(buf, "RGBColor(%g,%g,%g)", self->red, self->green, self->blue); return PyString_FromString(buf); } static int skcolor_length(PyObject *self) { return 3; } static PyObject * skcolor_concat(PyObject *self, PyObject *bb) { PyErr_SetString(PyExc_RuntimeError, "concat not supported for SKColorObjects"); return NULL; } static PyObject * skcolor_repeat(PyObject *self, int n) { PyErr_SetString(PyExc_RuntimeError, "repeat not supported for SKColorObjects"); return NULL; } static PyObject * skcolor_item(SKColorObject *self, int i) { double item; switch (i) { case 0: item = self->red; break; case 1: item = self->green; break; case 2: item = self->blue; break; default: PyErr_SetString(PyExc_IndexError, "index must be 0, 1 or 2"); return NULL; } return PyFloat_FromDouble(item); } static PyObject * skcolor_slice(PyObject *self, int ilow, int ihigh) { PyErr_SetString(PyExc_RuntimeError, "slicing not supported for SKColorObjects"); return NULL; } static PySequenceMethods skcolor_as_sequence = { skcolor_length, /*sq_length*/ skcolor_concat, /*sq_concat*/ skcolor_repeat, /*sq_repeat*/ (intargfunc)skcolor_item, /*sq_item*/ skcolor_slice, /*sq_slice*/ 0, /*sq_ass_item*/ 0, /*sq_ass_slice*/ }; /* * Python methods */ static PyObject * skcolor_blend(SKColorObject * self, PyObject * args) { SKColorObject * color2; double frac1, frac2; if (!PyArg_ParseTuple(args, "O!dd", &SKColorType, &color2, &frac1, &frac2)) return NULL; return SKColor_FromRGB(frac1 * self->red + frac2 * color2->red, frac1 * self->green + frac2 * color2->green, frac1 * self->blue + frac2 * color2->blue); } #define OFF(x) offsetof(SKColorObject, x) static struct memberlist skcolor_memberlist[] = { {"red", T_FLOAT, OFF(red), RO}, {"green", T_FLOAT, OFF(green), RO}, {"blue", T_FLOAT, OFF(blue), RO}, {NULL} }; #undef OFF static struct PyMethodDef skcolor_methods[] = { {"Blend", (PyCFunction)skcolor_blend, 1}, {NULL, NULL} }; static PyObject * skcolor_getattr(PyObject * self, char * name) { PyObject * result; result = Py_FindMethod(skcolor_methods, self, name); if (result != NULL) return result; PyErr_Clear(); return PyMember_Get((char *)self, skcolor_memberlist, name); } PyTypeObject SKColorType = { PyObject_HEAD_INIT(NULL) 0, "skcolor", sizeof(SKColorObject), 0, (destructor)skcolor_dealloc, /*tp_dealloc*/ (printfunc)NULL, /*tp_print*/ skcolor_getattr, /*tp_getattr*/ 0, /*tp_setattr*/ (cmpfunc)skcolor_compare, /*tp_compare*/ (reprfunc)skcolor_repr, /*tp_repr*/ 0, /*tp_as_number*/ &skcolor_as_sequence, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ (hashfunc)&skcolor_hash, /*tp_hash*/ (ternaryfunc)0, /*tp_call */ }; /* * Module Functions */ /* * skcolor.RGBColor(RED, GREEN, BLUE) */ PyObject * skcolor_rgbcolor(PyObject * self, PyObject * args) { double red, green, blue; if (!PyArg_ParseTuple(args, "ddd", &red, &green, &blue)) return NULL; return SKColor_FromRGB(red, green, blue); } PyObject * skcolor_num_allocated(PyObject * self, PyObject * args) { #if SKCOLOR_COUNT_ALLOC return PyInt_FromLong(skcolor_allocated); #else return PyInt_FromLong(-1); #endif } UniConvertor-1.1.4/src/modules/skmod/skcolor.h0000664000076400007640000000306011060500613020036 0ustar igorigor/* Sketch - A Python-based interactive drawing program * Copyright (C) 1998 by Bernhard Herzog * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef SKCOLOR_H #define SKCOLOR_H #if defined(__cplusplus) extern "C" { #endif typedef struct { PyObject_HEAD float red; float green; float blue; } SKColorObject; extern PyTypeObject SKColorType; #define SKColor_Check(v) ((v)->ob_type == &SKColorType) PyObject * SKColor_FromRGB(double red, double green, double blue); typedef union { unsigned short s[2]; unsigned char c[4]; } SKDitherInfo; extern PyTypeObject SKVisualType; #define SKVisual_Check(v) ((v)->ob_type == &SKVisualType) /* Python functions */ PyObject * skcolor_rgbcolor(PyObject * self, PyObject * args); PyObject * skcolor_num_allocated(PyObject * self, PyObject * args); #if defined(__cplusplus) } #endif #endif /* SKCOLOR_H */ UniConvertor-1.1.4/src/modules/skmod/skrect.c0000664000076400007640000004202011060500613017647 0ustar igorigor/* Sketch - A Python-based interactive drawing program * Copyright (C) 1997, 1998, 1999, 2001, 2002, 2006 by Bernhard Herzog * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * A Simple Rectangle object: SKRect * * A rectangle is given by the coordinates of its edges, which are * assumed to be parallel to the axes of the coordinate system. * * The coordinates are: left, bottom, right, top. * * The origin is assumed to be in the lower left corner and the * rectangle keeps its coordinates in a normalized form with * left < right and bottom < top. * * Rectangles are immutable. * * In Sketch these rectangles are used to represent bounding rects and * similar things. */ #include #include #include #include "skpoint.h" #include "skrect.h" static void skrect_normalize(SKRectObject * self); SKRectObject * SKRect_InfinityRect = NULL; SKRectObject * SKRect_EmptyRect = NULL; #define SKRECT_COUNT_ALLOC 1 #define SKRECT_SUBALLOCATE 1 #if SKRECT_COUNT_ALLOC static int allocated = 0; #endif #if SKRECT_SUBALLOCATE #define BLOCK_SIZE 1000 /* 1K less typical malloc overhead */ #define N_RECTOBJECTS (BLOCK_SIZE / sizeof(SKRectObject)) static SKRectObject * fill_free_list(void) { SKRectObject *p, *q; p = PyMem_Malloc(sizeof(SKRectObject) * N_RECTOBJECTS); if (p == NULL) return (SKRectObject *)PyErr_NoMemory(); q = p + N_RECTOBJECTS; while (--q > p) q->ob_type = (PyTypeObject*)(q-1); q->ob_type = NULL; return p + N_RECTOBJECTS - 1; } static SKRectObject *free_list = NULL; #endif PyObject * SKRect_FromDouble(double left, double bottom, double right, double top) { SKRectObject * self; #if SKRECT_SUBALLOCATE if (free_list == NULL) { if ((free_list = fill_free_list()) == NULL) return NULL; } self = free_list; free_list = (SKRectObject *)(free_list->ob_type); self->ob_type = &SKRectType; _Py_NewReference(self); #else self = PyObject_New(SKRectObject, &SKRectType); if (self == NULL) return NULL; #endif self->left = left; self->bottom = bottom; self->right = right; self->top = top; skrect_normalize(self); #if SKRECT_COUNT_ALLOC allocated++; #endif return (PyObject*)self; } static void skrect_dealloc(SKRectObject * self) { #if SKRECT_SUBALLOCATE self->ob_type = (PyTypeObject*)free_list; free_list = self; #else PyObject_Del(self); #endif #if SKRECT_COUNT_ALLOC allocated--; #endif } #define COMPARE(c1,c2) ((c1) < (c2) ? -1 : ((c1) > (c2) ? +1 : 0 )) static int skrect_compare(SKRectObject * v, SKRectObject * w) { int result; /* In Python 2.1 (and probably later), the interpreter doesn't take * a shortcut anymore when comparing and object to itself (in * earlier versions an object was always equal to itself. Now we * have to take care of that special case here too. */ if (v == w) return 0; /* The EmptyRect is smaller than any other rect. */ if (v == SKRect_EmptyRect) return -1; if (w == SKRect_EmptyRect) return +1; /* The InfinityRect is larger than any other rect */ if (v == SKRect_InfinityRect) return +1; if (w == SKRect_InfinityRect) return -1; if ((result = COMPARE(v->left, w->left)) != 0) return result; if ((result = COMPARE(v->bottom, w->bottom)) != 0) return result; if ((result = COMPARE(v->right, w->right)) != 0) return result; return COMPARE(v->top, w->top); } #undef COMPARE static PyObject * skrect_repr(SKRectObject * self) { if (self == SKRect_EmptyRect) return PyString_FromString("EmptyRect"); else if (self == SKRect_InfinityRect) return PyString_FromString("InfinityRect"); else { char buf[1000]; sprintf(buf, "Rect(%.10g, %.10g, %.10g, %.10g)", self->left, self->bottom, self->right, self->top); return PyString_FromString(buf); } } static int skrect_length(PyObject *self) { return 4; } static PyObject * skrect_concat(PyObject *self, PyObject *bb) { PyErr_SetString(PyExc_RuntimeError, "concat not supported for SKRectObjects"); return NULL; } static PyObject * skrect_repeat(PyObject *self, int n) { PyErr_SetString(PyExc_RuntimeError, "repeat not supported for SKRectObjects"); return NULL; } static PyObject * skrect_item(SKRectObject *self, int i) { double item; switch (i) { case 0: item = self->left; break; case 1: item = self->bottom; break; case 2: item = self->right; break; case 3: item = self->top; break; default: PyErr_SetString(PyExc_IndexError, "index must be 0, 1, 2 or 3"); return NULL; } return PyFloat_FromDouble(item); } static PyObject * skrect_slice(PyObject *self, int ilow, int ihigh) { PyErr_SetString(PyExc_RuntimeError, "slicing not supported for SKRectObjects"); return NULL; } static PySequenceMethods skrect_as_sequence = { skrect_length, /*sq_length*/ skrect_concat, /*sq_concat*/ skrect_repeat, /*sq_repeat*/ (intargfunc)skrect_item, /*sq_item*/ skrect_slice, /*sq_slice*/ 0, /*sq_ass_item*/ 0, /*sq_ass_slice*/ }; /* normalize the rectangle: make sure that left is smaller than right * and bottom is smaller than top. */ static void skrect_normalize(SKRectObject * self) { SKCoord temp; if (self->left > self->right) { temp = self->left; self->left = self->right; self->right = temp; } if (self->top < self->bottom) { temp = self->top; self->top = self->bottom; self->bottom = temp; } } /* * Python methods */ /* * rect.grown(AMOUNT) * * Return a new rectangle that is bigger by AMOUNT in each direction. */ static PyObject * skrect_grown(SKRectObject * self, PyObject * args) { double amount; if (!PyArg_ParseTuple(args, "d", &amount)) return NULL; if (self != SKRect_InfinityRect && self != SKRect_EmptyRect) return SKRect_FromDouble(self->left - amount, self->bottom - amount, self->right + amount, self->top + amount); else { /* XXX: is this appropriate for SKRect_EmptyRect? */ Py_INCREF(self); return (PyObject*)self; } } /* * rect.translated(P) * * Return RECT translated by P */ static PyObject * skrect_translated(SKRectObject * self, PyObject * args) { PyObject * arg; double x, y; if (self == SKRect_EmptyRect || self == SKRect_InfinityRect) { Py_INCREF(self); return (PyObject*)self; } if (PyTuple_Size(args) == 2) arg = args; else if (!PyArg_ParseTuple(args, "O", &arg)) return NULL; if (skpoint_extract_xy(arg, &x, &y)) { return SKRect_FromDouble(self->left + x, self->bottom + y, self->right + x, self->top + y); } PyErr_SetString(PyExc_TypeError, "arguments must be either two numbers " "or one seqeuence of two numbers"); return NULL; } /* * rect.contains_point(P) * * Return true if P lies in rect, false otherwise. */ static PyObject * skrect_contains_point(SKRectObject * self, PyObject * args) { PyObject * arg; double x, y; if (PyTuple_Size(args) == 2) arg = args; else if (!PyArg_ParseTuple(args, "O", &arg)) return NULL; if (skpoint_extract_xy(arg, &x, &y)) { return PyInt_FromLong(SKRect_ContainsXY(self, x, y)); } PyErr_SetString(PyExc_TypeError, "arguments must be either two numbers " "or one seqeuence of two numbers"); return NULL; } /* * rect.contains_rect(RECT) * * Return true if RECT lies completely inside of rect, false otherwise. */ static PyObject * skrect_contains_rect(SKRectObject * self, PyObject * args) { SKRectObject * r; if (!PyArg_ParseTuple(args, "O!", &SKRectType, &r)) return NULL; /* special cases: I = InfinityRect, E = EmptyRect, r = other rect * * self * I E r * +------------------------- * I | 1 0 0 * other E | 1 1 1 * r | 1 0 ? */ if (self == SKRect_InfinityRect || r == SKRect_EmptyRect) return PyInt_FromLong(1); if (self == SKRect_EmptyRect || r == SKRect_InfinityRect) return PyInt_FromLong(0); return PyInt_FromLong( r->left >= self->left && r->right <= self->right && r->top <= self->top && r->bottom >= self->bottom); } /* * rect.overlaps(RECT) * * Return true if rect contains all or part of RECT, i.e. if they * overlap, otherwise, return false. */ static PyObject * skrect_overlaps(SKRectObject * self, PyObject * args) { SKRectObject * r; if (!PyArg_ParseTuple(args, "O!", &SKRectType, &r)) return NULL; if (self == SKRect_InfinityRect || self == SKRect_EmptyRect || r == SKRect_InfinityRect || r == SKRect_EmptyRect) return PyInt_FromLong(1); return PyInt_FromLong( r->left <= self->right && r->right >= self->left && r->top >= self->bottom && r->bottom <= self->top); } /* * rect.center() * * Return the center of rect as a SKPoint. */ static PyObject * skrect_center(SKRectObject * self, PyObject * args) { SKCoord cx, cy; if (self != SKRect_InfinityRect && self != SKRect_EmptyRect) { cx = (self->left + self->right) / 2; cy = (self->top + self->bottom) / 2; } else { /* the center is really undefined in this case... */ cx = cy = 0.0; } return SKPoint_FromXY(cx, cy); } #define OFF(x) offsetof(SKRectObject, x) static struct memberlist skrect_memberlist[] = { {"left", T_SKCOORD, OFF(left), RO}, {"bottom", T_SKCOORD, OFF(bottom), RO}, {"right", T_SKCOORD, OFF(right), RO}, {"top", T_SKCOORD, OFF(top), RO}, {NULL} }; static struct PyMethodDef skrect_methods[] = { {"contains_point", (PyCFunction)skrect_contains_point, METH_VARARGS}, {"contains_rect", (PyCFunction)skrect_contains_rect, METH_VARARGS}, {"overlaps", (PyCFunction)skrect_overlaps, METH_VARARGS}, {"grown", (PyCFunction)skrect_grown, METH_VARARGS}, {"center", (PyCFunction)skrect_center, METH_VARARGS}, {"translated", (PyCFunction)skrect_translated, METH_VARARGS}, {NULL, NULL} }; static PyObject * skrect_getattr(PyObject * self, char * name) { PyObject * result; result = Py_FindMethod(skrect_methods, self, name); if (result != NULL) return result; PyErr_Clear(); return PyMember_Get((char *)self, skrect_memberlist, name); } PyTypeObject SKRectType = { PyObject_HEAD_INIT(NULL) 0, "skrect", sizeof(SKRectObject), 0, (destructor)skrect_dealloc, /*tp_dealloc*/ (printfunc)NULL, /*tp_print*/ skrect_getattr, /*tp_getattr*/ 0, /*tp_setattr*/ (cmpfunc)skrect_compare, /*tp_compare*/ (reprfunc)skrect_repr, /*tp_repr*/ 0, /*tp_as_number*/ &skrect_as_sequence, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ (ternaryfunc)0, /*tp_call */ }; /* * Module Functions */ /* * skrect.Rect(LEFT, BOTTOM, RIGHT, TOP) * skrect.Rect(LLCORNER, URCORNER) * * Return a new Rect object with the coordinates LEFT, BOTTOM, RIGHT and * TOP or the opposite corners LLCORNER and URCORNER given as SKPoints. * The rectangle is automaticall normalized, so you can swap LEFT/RIGHT * or TOP/BOTTOM or choose any opposite corners. */ PyObject * skrect_skrect(PyObject * self, PyObject * args) { double left, top, right, bottom; if (PyTuple_Size(args) == 2) { /* two opposite corners */ SKPointObject * p1, *p2; if (!PyArg_ParseTuple(args, "O!O!", &SKPointType, &p1, &SKPointType, &p2)) return NULL; return SKRect_FromDouble(p1->x, p1->y, p2->x, p2->y); } /* four numbers */ if (!PyArg_ParseTuple(args, "dddd", &left, &bottom, &right, &top)) return NULL; return SKRect_FromDouble(left, bottom, right, top); } /* * skrect.UnionRects(RECT1, RECT2) * * Return the smallest rectangle that contains RECT1 and RECT2. */ PyObject * skrect_unionrects(PyObject * self, PyObject * args) { SKRectObject * r1, * r2; if (!PyArg_ParseTuple(args, "O!O!", &SKRectType, &r1, &SKRectType, &r2)) return NULL; if (r1 == SKRect_EmptyRect) { Py_INCREF(r2); return (PyObject*)r2; } if (r2 == SKRect_EmptyRect) { Py_INCREF(r1); return (PyObject*)r1; } if (r1 == SKRect_InfinityRect || r2 == SKRect_InfinityRect) { Py_INCREF(SKRect_InfinityRect); return (PyObject*)SKRect_InfinityRect; } return SKRect_FromDouble((r1->left < r2->left) ? r1->left : r2->left, (r1->bottombottom) ? r1->bottom: r2->bottom, (r1->right > r2->right) ? r1->right : r2->right, (r1->top > r2->top) ? r1->top : r2->top); } /* * skrect.IntersectRects(RECT1, RECT2) * * Return the largest rectangle contained in RECT1 and RECT2. */ PyObject * skrect_intersect(PyObject * self, PyObject * args) { SKRectObject * r1, * r2; double left, bottom, right, top; if (!PyArg_ParseTuple(args, "O!O!", &SKRectType, &r1, &SKRectType, &r2)) return NULL; /* special cases: I = InfinityRect, E = EmptyRect, r = other rect * * r1 * I E r * +------------------------- * I | I E r2 * r2 E | E E E * r | r1 E ? */ if (r1 == SKRect_InfinityRect) { Py_INCREF(r2); return (PyObject*)r2; } if (r2 == SKRect_InfinityRect) { Py_INCREF(r1); return (PyObject*)r1; } if (r1 == SKRect_EmptyRect || r2 == SKRect_EmptyRect) { Py_INCREF(SKRect_EmptyRect); return (PyObject*)SKRect_EmptyRect; } left = (r1->left > r2->left) ? r1->left : r2->left; bottom = (r1->bottom > r2->bottom) ? r1->bottom : r2->bottom; right = (r1->right < r2->right) ? r1->right : r2->right; top = (r1->top < r2->top) ? r1->top : r2->top; if (left > right || bottom > top) { Py_INCREF(SKRect_EmptyRect); return (PyObject*)SKRect_EmptyRect; } return SKRect_FromDouble(left, bottom, right, top); } /* * skrect.PointsToRect(SEQUENCE) * * Return the smallest rectangle that contains all the points in * SEQUENCE. SEQUENCE is a sequence (any sequence type) of SKPoint * objects. */ PyObject * skrect_PointsToRect(PyObject * self, PyObject * args) { PyObject * points; int length, idx; SKRectObject * rect = NULL; if (!PyArg_ParseTuple(args, "O", &points)) return NULL; length = PySequence_Length(points); if (length <= 0) { Py_INCREF(SKRect_EmptyRect); return (PyObject*)SKRect_EmptyRect; } for (idx = 0; idx < length; idx++) { double x, y; PyObject * p; int is_point = 0; p = PySequence_GetItem(points, idx); is_point = skpoint_extract_xy(p, &x, &y); Py_DECREF(p); if (!is_point) { PyErr_SetString(PyExc_TypeError, "nonempty sequence of points expected"); return NULL; } if (!rect) { rect = (SKRectObject*)SKRect_FromDouble(x, y, x, y); if (!rect) return NULL; } SKRect_AddXY(rect, x, y); } return (PyObject*)rect; } /* * */ PyObject * skrect_allocated(PyObject * self, PyObject * args) { #if SKRECT_COUNT_ALLOC return PyInt_FromLong(allocated); #else return PyInt_FromLong(-1); #endif } /* * some functions that can be called from other modules */ /* * Return true if the point (X, Y) lies in rect, false otherwise. */ int SKRect_ContainsXY(SKRectObject * self, double x, double y) { if (self != SKRect_EmptyRect && (self == SKRect_InfinityRect || (self->left <= x && self->right >= x && self->top >= y && self->bottom <= y))) return 1; return 0; } /* If (x, y) lies outside of self, make self larger to include (x,y). * Assume self is normalized. * * While SKRects are considered immutable, this function actually * changes self. It is only meant to be called for `new' rectangles that * are created by a C-function. Once the rect has been passed to the * python interpreter or is known by some other parts of the code, the * rect should not be changed anymore. */ int SKRect_AddXY(SKRectObject * self, double x, double y) { skrect_normalize(self); if (x < self->left) self->left = x; else if (x > self->right) self->right = x; if (y > self->top) self->top = y; else if (y < self->bottom) self->bottom = y; return 1; } /* * If X lies outside of self, make self larger to include X. * See SKRect_AddXY for comments on immutablility. */ int SKRect_AddX(SKRectObject * self, double x) { skrect_normalize(self); if (x < self->left) self->left = x; else if (x > self->right) self->right = x; return 1; } /* * If Y lies outside of self, make self larger to include Y. * See SKRect_AddXY for comments on immutablility. */ int SKRect_AddY(SKRectObject * self, double y) { skrect_normalize(self); if (y > self->top) self->top = y; else if (y < self->bottom) self->bottom = y; return 1; } UniConvertor-1.1.4/src/modules/skmod/skrect.h0000664000076400007640000000366411060500613017667 0ustar igorigor/* Sketch - A Python-based interactive drawing program * Copyright (C) 1997, 1998 by Bernhard Herzog * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef SKRECT_MODULE_H #define SKRECT_MODULE_H #if defined(__cplusplus) extern "C" { #endif #include "skpoint.h" typedef struct { PyObject_HEAD SKCoord left, top, right, bottom; } SKRectObject; extern PyTypeObject SKRectType; #define SKRect_Check(v) ((v)->ob_type == &SKRectType) extern SKRectObject * SKRect_InfinityRect; extern SKRectObject * SKRect_EmptyRect; PyObject * SKRect_FromDouble(double left, double top, double right, double bottom); int SKRect_ContainsXY(SKRectObject * self, double x, double y); /* SKRect_AddXY and Co modify self! see comments in skrectmodule.c */ int SKRect_AddXY(SKRectObject * self, double x, double y); int SKRect_AddX(SKRectObject * self, double x); int SKRect_AddY(SKRectObject * self, double y); PyObject * skrect_allocated(PyObject * self, PyObject * args); PyObject * skrect_PointsToRect(PyObject * self, PyObject * args); PyObject * skrect_intersect(PyObject * self, PyObject * args); PyObject * skrect_unionrects(PyObject * self, PyObject * args); PyObject * skrect_skrect(PyObject * self, PyObject * args); #if defined(__cplusplus) } #endif #endif UniConvertor-1.1.4/src/modules/skmod/skfm.c0000664000076400007640000002242611060500613017324 0ustar igorigor/* Sketch - A Python-based interactive drawing program * Copyright (C) 1997, 1998, 2002, 2006 by Bernhard Herzog * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * A Python Object for fontmetrics. * * A fontmetrics object contains all the information about a scalable * font needed to `typeset' some text. It provides some methods that * compute various rectangles for given strings. * * All dimensions are stored as ints in 1000-pixel metric. That means * that the ascender of a 12pt instance of a font is 12 * ascender / * 1000.0 points, etc. * * All strings are assumed to be in the same encoding as the * fontmetrics. In fact, the fontmetric object doesn't know anything * about encodings. That is assumed to be handled appropriately in * Python code. (The Fontmetric object has to be changed, of course, if * we want to use more than 8 bits per character) * * Currently, font metrics are read from afm-files by some python code * and passed to the constructors for this object type. * * Fontmetric objects are immutable. * */ #include #include #include "skpoint.h" /* the metrics for one character */ struct SKCharMetric_s { int width; /* width */ int llx, lly, urx, ury; /* bounding box */ }; typedef struct SKCharMetric_s SKCharMetric; /* the font metrics */ struct SKFontMetric_s { PyObject_HEAD int ascender, descender; /* global ascender/descender */ int llx, lly, urx, ury; /* font bounding box */ float italic_angle; /* italic angle as in afm file */ SKCharMetric char_metric[256]; }; typedef struct SKFontMetric_s SKFontMetric; static void skfm_dealloc(SKFontMetric * self) { PyObject_Del(self); } /* * fm.string_bbox(STRING) * * Return the bounding box of STRING as a tuple (llx, lly, urx, ury) in * 1000-pixel metric. The first character is positioned at (0, 0). */ static PyObject * skfm_string_bbox(SKFontMetric * self, PyObject * args) { unsigned char * string; int length, i; int llx = 0, lly = 0, urx = 0, ury = 0; int pos = 0; SKCharMetric * metric; if (!PyArg_ParseTuple(args, "s#", &string, &length)) return NULL; for (i = 0; i < length; i++) { metric = self->char_metric + string[i]; if (pos + metric->llx < llx) llx = pos + metric->llx; if (pos + metric->urx > urx) urx = pos + metric->urx; if (metric->lly < lly) lly = metric->lly; if (metric->ury > ury) ury = metric->ury; pos += metric->width; } return Py_BuildValue("iiii", llx, lly, urx, ury); } /* * fm.string_width(STRING[, MAXPOS]) * * Return the setwidth of STRING in 1000-pixel metrics. No kerning or * ligatures are taken into account (maybe in the future). * * If provided, MAXCHAR is equivalent to using STRING[:MAXCHAR] instead * of STRING. MAXCHAR defaults to the length of STRING. It usefule to * compute the position of a caret (See Font.TextCaretData() in font.py) */ static PyObject * skfm_string_width(SKFontMetric * self, PyObject * args) { unsigned char * string; int length, i, maxpos = -1; int width = 0; if (!PyArg_ParseTuple(args, "s#|i", &string, &length, &maxpos)) return NULL; if (maxpos >= 0 && maxpos < length) length = maxpos; for (i = 0; i < length; i++) width += self->char_metric[string[i]].width; return Py_BuildValue("i", width); } /* * fm.char_width(CHR) * * Return the setwidth of the character with code CHR (CHR is an int). */ static PyObject * skfm_char_width(SKFontMetric * self, PyObject * args) { int chr; if (!PyArg_ParseTuple(args, "i", &chr)) return NULL; if (0 <= chr && chr < 256) return PyInt_FromLong(self->char_metric[chr].width); PyErr_SetString(PyExc_ValueError, "argument must be in the range [0 .. 255]"); return NULL; } /* * fm.char_bbox(CHR) * * Return the bounding box of the character with code CHR (CHR is an * int). */ static PyObject * skfm_char_bbox(SKFontMetric * self, PyObject * args) { int chr; if (!PyArg_ParseTuple(args, "i", &chr)) return NULL; if (0 <= chr && chr < 256) { SKCharMetric * metric = self->char_metric + chr; return Py_BuildValue("iiii", metric->llx, metric->lly, metric->urx, metric->ury); } PyErr_SetString(PyExc_ValueError, "argument must be in the range [0 .. 255]"); return NULL; } /* * fm.typeset_string(STRING) * * Return a list of SKPoint objects, one for each char in STRING, * indicating the coordinates of the characters origin. The first char * is set at (0, 0). */ static PyObject * skfm_typeset_string(SKFontMetric * self, PyObject * args) { unsigned char * string; int length, i; int width = 0; PyObject * list; PyObject * point; if (!PyArg_ParseTuple(args, "s#", &string, &length)) return NULL; list = PyList_New(length); if (!list) return NULL; for (i = 0; i < length; i++) { point = SKPoint_FromXY(width / 1000.0, 0.0); if (!point) { Py_DECREF(list); return NULL; } if (PyList_SetItem(list, i, point) < 0) { Py_DECREF(list); return NULL; } width += self->char_metric[string[i]].width; } return list; } #define OFF(x) offsetof(SKFontMetric, x) static struct memberlist skfm_memberlist[] = { {"ascender", T_INT, OFF(ascender), RO}, {"descender", T_INT, OFF(descender), RO}, {"llx", T_INT, OFF(llx), RO}, {"lly", T_INT, OFF(lly), RO}, {"urx", T_INT, OFF(urx), RO}, {"ury", T_INT, OFF(ury), RO}, {"italic_angle", T_FLOAT, OFF(italic_angle), RO}, {NULL} }; static struct PyMethodDef skfm_methods[] = { {"typeset_string", (PyCFunction)skfm_typeset_string, 1}, {"string_bbox", (PyCFunction)skfm_string_bbox, 1}, {"string_width", (PyCFunction)skfm_string_width, 1}, {"char_width", (PyCFunction)skfm_char_width, 1}, {"char_bbox", (PyCFunction)skfm_char_bbox, 1}, {NULL, NULL} }; static PyObject * skfm_getattr(PyObject * self, char * name) { PyObject * result; result = Py_FindMethod(skfm_methods, self, name); if (result != NULL) return result; PyErr_Clear(); return PyMember_Get((char *)self, skfm_memberlist, name); } PyTypeObject SKFontMetricType = { PyObject_HEAD_INIT(NULL) 0, "skfm", sizeof(SKFontMetric), 0, (destructor)skfm_dealloc, /*tp_dealloc*/ (printfunc)NULL, /*tp_print*/ skfm_getattr, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ }; /* * Return a new, empty fontmetric object. */ PyObject * SKFontMetric_New() { SKFontMetric * self; self = PyObject_New(SKFontMetric, &SKFontMetricType); if (self == NULL) return NULL; return (PyObject*)self; } /* the module functions */ /* * skfm.CreateMetric(ASCENDER, DESCENDER, FONTBBOX, ITALIC, CHARMETRICS) * * Return a new fontmetric object initialized with the given parameters: * * ASCENDER ascender * DESCENDER descender * FONTBBOX The font bounding box as a tuple (llx, lly, urx, ury) * in 1000-pixel metrics. * ITALIC the italic angle in degrees ccw from straight up. * CHARMETRICS A sequence of 256 tuples (width, llx, lly, urx, ury), * the metrics of each character in 1000-pixel metrics */ PyObject * SKFM_PyCreateMetric(PyObject * self, PyObject * args) { int ascender, descender; PyObject * list; SKFontMetric * metric; int fllx, flly, furx, fury; float italic_angle; int i; if (!PyArg_ParseTuple(args, "ii(iiii)fO", &ascender, &descender, &fllx, &flly, &furx, &fury, &italic_angle, &list)) return NULL; if (!PySequence_Check(list)) { PyErr_SetString(PyExc_TypeError, "fifth argument must be a sequence of tuples"); return NULL; } if (PySequence_Length(list) < 256) { PyErr_SetString(PyExc_ValueError, "CHARMETRICS must have 256 elements"); return NULL; } metric = (SKFontMetric*)SKFontMetric_New(); if (!metric) return NULL; metric->ascender = ascender; metric->descender = descender; metric->llx = fllx; metric->lly = flly; metric->urx = furx; metric->ury = fury; metric->italic_angle = italic_angle; for (i = 0; i < 256; i++) { int width, llx, lly, urx, ury; PyObject * tuple = PySequence_GetItem(list, i); SKCharMetric * char_metric = metric->char_metric + i; if (!PyArg_ParseTuple(tuple, "iiiii;" "CHARMETRICS item must be (w, llx, lly, urx, ury)", &width, &llx, &lly, &urx, &ury)) { Py_DECREF(tuple); return NULL; } Py_DECREF(tuple); char_metric->width = width; char_metric->llx = llx; char_metric->lly = lly; char_metric->urx = urx; char_metric->ury = ury; } return (PyObject*) metric; } UniConvertor-1.1.4/src/modules/skmod/skfm.h0000664000076400007640000000223211060500613017322 0ustar igorigor/* Sketch - A Python-based interactive drawing program * Copyright (C) 1996, 1997, 1998 by Bernhard Herzog * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef SKFM_H #define SKFM_H #include #if defined(__cplusplus) extern "C" { #endif PyObject * SKFM_PyCreateMetric(PyObject * self, PyObject * args); extern DL_IMPORT(PyTypeObject) SKFontMetricType; #define SKFontMetric_Check(obj) ((v)->ob_type == &SKFontMetricType) #if defined(__cplusplus) } #endif #endif UniConvertor-1.1.4/src/modules/skmod/curvefunc.c0000664000076400007640000003100511060500613020355 0ustar igorigor/* Sketch - A Python-based interactive drawing program * Copyright (C) 1997, 1998, 1999 by Bernhard Herzog * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "sktrafo.h" #include "curveobject.h" #include "curvefunc.h" #include "curvelow.h" /* * Module Functions */ PyObject * SKCurve_PyCreatePath(PyObject * self, PyObject * args) { int length = 2; if (!PyArg_ParseTuple(args, "|i", &length)) return NULL; return SKCurve_New(length); } /* * SKCurve_TestTransformed(PATHS, TRAFO, X, Y, FILLED) * * Test whether a multipath object is hit by a mouse click at (X, Y). * * PATHS tuple of bezier objects * TRAFO SKTrafo mapping doccoords to window coords * X, Y Window coords of mouse click * FILLED Whether object is filled or not * * Return: * * 0 Object was not hit * -1 Object was hit on the border * +1 Object was hit in the interior (even odd rule). Only if FILLED * is true */ PyObject * SKCurve_PyTestTransformed(PyObject * self, PyObject * args) { PyObject * paths; PyObject * trafo; int x, y, filled; int i, cross_count = 0, result; if (!PyArg_ParseTuple(args, "O!O!iii", &PyTuple_Type, &paths, &SKTrafoType, &trafo, &x, &y, &filled)) return NULL; for (i = 0; i < PyTuple_Size(paths); i++) { PyObject * path = PyTuple_GetItem(paths, i); if (!SKCurve_Check(path)) { PyErr_SetString(PyExc_TypeError, "First argument must be tuple of bezier paths"); return NULL; } } for (i = 0; i < PyTuple_Size(paths); i++) { SKCurveObject * path = (SKCurveObject*)PyTuple_GetItem(paths, i); result = SKCurve_TestTransformed(path, trafo, x, y, filled); if (result < 0) { cross_count = -1; break; } cross_count += result; } if (cross_count >= 0 && filled) return PyInt_FromLong(cross_count & 1); return PyInt_FromLong(cross_count >= 0 ? 0 : -1); } /* * */ PyObject * SKCurve_PyBlendPaths(PyObject * self, PyObject * args) { SKCurveObject *path1 = NULL, *path2 = NULL, *result; CurveSegment *seg1, *seg2, *resseg; double frac1, frac2, f13 = 1.0 / 3.0, f23 = 2.0 / 3.0; int i, length; if (!PyArg_ParseTuple(args, "O!O!dd", &SKCurveType, &path1, &SKCurveType, &path2, &frac1, &frac2)) return NULL; length = path1->len > path2->len ? path2->len : path1->len; result = (SKCurveObject*)SKCurve_New(length); if (!result) return NULL; seg1 = path1->segments; seg2 = path2->segments; resseg = result->segments; resseg->x = frac1 * seg1->x + frac2 * seg2->x; resseg->y = frac1 * seg1->y + frac2 * seg2->y; resseg->cont = seg1->cont == seg2->cont ? seg1->cont : ContAngle; resseg += 1; seg1 += 1; seg2 += 1; for (i = 1; i < length; i++, seg1++, seg2++, resseg++) { resseg->x = frac1 * seg1->x + frac2 * seg2->x; resseg->y = frac1 * seg1->y + frac2 * seg2->y; resseg->cont = seg1->cont == seg2->cont ? seg1->cont : ContAngle; if (seg1->type == seg2->type && seg1->type == CurveLine) { resseg->type = CurveLine; } else { /* at least one of the segments is a bezier segment convert the other into a bezier segment first if necessary */ double x11, y11, x12, y12, x21, y21, x22, y22; if (seg1->type == CurveLine) { x11 = f13 * seg1[-1].x + f23 * seg1->x; y11 = f13 * seg1[-1].y + f23 * seg1->y; x12 = f23 * seg1[-1].x + f13 * seg1->x; y12 = f23 * seg1[-1].y + f13 * seg1->y; } else { x11 = seg1->x1; y11 = seg1->y1; x12 = seg1->x2; y12 = seg1->y2; } if (seg2->type == CurveLine) { x21 = f13 * seg2[-1].x + f23 * seg2->x; y21 = f13 * seg2[-1].y + f23 * seg2->y; x22 = f23 * seg2[-1].x + f13 * seg2->x; y22 = f23 * seg2[-1].y + f13 * seg2->y; } else { x21 = seg2->x1; y21 = seg2->y1; x22 = seg2->x2; y22 = seg2->y2; } resseg->x1 = frac1 * x11 + frac2 * x21; resseg->y1 = frac1 * y11 + frac2 * y21; resseg->x2 = frac1 * x12 + frac2 * x22; resseg->y2 = frac1 * y12 + frac2 * y22; resseg->type = CurveBezier; } } if (path1->len == path2->len && path1->closed &&path2->closed) result->closed = 1; else result->closed = 0; result->len = length; return (PyObject*)result; } #ifndef PI #define PI 3.14159265358979323846264338327 #endif #define EVAL(coeff, t) (((coeff[0]*t + coeff[1])*t + coeff[2]) * t + coeff[3]) static double arc_param(double * x, double * y, double angle) { double coeff_x[4], coeff_y[4]; double min_angle, min_t, max_angle, max_t, cur_angle, cur_t; int i, j, depth = 0; /* assume angle >= 0 */ while (angle > PI) { angle -= 2 * PI; } for (i = 0; i < 4; i++) { coeff_x[i] = 0; coeff_y[i] = 0; for (j = 0; j < 4; j++) { coeff_x[i] += bezier_basis[i][j] * x[j]; coeff_y[i] += bezier_basis[i][j] * y[j]; } } min_angle = atan2(y[0], x[0]); max_angle = atan2(y[3], x[3]); if (max_angle < min_angle) min_angle -= PI + PI; if (angle > max_angle) angle -= PI + PI; min_t = 0.0; max_t = 1.0; while (depth < 15) { cur_t = (max_t + min_t) / 2; cur_angle = atan2(EVAL(coeff_y, cur_t), EVAL(coeff_x, cur_t)); if (angle > cur_angle) { min_t = cur_t; min_angle = cur_angle; } else { max_t = cur_t; max_angle = cur_angle; } depth += 1; } if (max_angle - angle < angle - min_angle) return max_t; return min_t; } #undef EVAL static void subdivide(double * x, double * y, double t, int first) { double s = 1.0 - t; double rx[7], ry[7]; double ax, ay; rx[0] = x[0]; ry[0] = y[0]; rx[6] = x[3]; ry[6] = y[3]; ax = s * x[1] + t * x[2]; ay = s * y[1] + t * y[2]; rx[1] = s * rx[0] + t * x[1]; ry[1] = s * ry[0] + t * y[1]; rx[2] = s * rx[1] + t * ax; ry[2] = s * ry[1] + t * ay; rx[5] = s * x[2] + t * rx[6]; ry[5] = s * y[2] + t * ry[6]; rx[4] = s * ax + t * rx[5]; ry[4] = s * ay + t * ry[5]; rx[3] = s * rx[2] + t * rx[4]; ry[3] = s * ry[2] + t * ry[4]; if (first) { memcpy(x, rx, 4 * sizeof(double)); memcpy(y, ry, 4 * sizeof(double)); } else { memcpy(x, rx + 3, 4 * sizeof(double)); memcpy(y, ry + 3, 4 * sizeof(double)); } } static double arc_nodes_x[4] = {1.0, 0.0, -1.0, 0.0}; static double arc_nodes_y[4] = {0.0, 1.0, 0.0, -1.0}; static double arc_controls_x[8] = {1.0, 0.55197, -0.55197, -1.0, -1.0, -0.55197, 0.55197, 1.0}; static double arc_controls_y[8] = {0.55197, 1.0, 1.0, 0.55197, -0.55197, -1.0, -1.0, -0.55197}; PyObject * SKCurve_PyApproxArc(PyObject * self, PyObject * args) { double start_angle, end_angle; SKCurveObject * arc; int start_quad, end_quad, quadrant; int type = 0; /* 0: arc, 1: chord, 2: pie slice as in const.py */ /* special case: type = 3 for a complete ellipse */ if (!PyArg_ParseTuple(args, "dd|i", &start_angle, &end_angle, &type)) return NULL; /* normalize start_angle and end_angle */ start_angle = fmod(start_angle, 2 * PI); if (start_angle < 0) start_angle += 2 * PI; end_angle = fmod(end_angle, 2 * PI); if (end_angle < 0) end_angle += 2 * PI; if (start_angle >= end_angle) { if (start_angle == end_angle) type = 3; end_angle += 2 * PI; } /* now 0 <= start_angle < 2 * PI and start_angle <= end_angle */ start_quad = start_angle / (PI / 2); end_quad = end_angle / (PI / 2); arc = (SKCurveObject*)SKCurve_New(4); if (!arc) return NULL; for (quadrant = start_quad; quadrant <= end_quad; quadrant++) { double x[4], y[4], t; x[0] = arc_nodes_x[quadrant % 4]; y[0] = arc_nodes_y[quadrant % 4]; x[1] = arc_controls_x[2 * (quadrant % 4)]; y[1] = arc_controls_y[2 * (quadrant % 4)]; x[2] = arc_controls_x[2 * (quadrant % 4) + 1]; y[2] = arc_controls_y[2 * (quadrant % 4) + 1]; x[3] = arc_nodes_x[(quadrant + 1) % 4]; y[3] = arc_nodes_y[(quadrant + 1) % 4]; if (quadrant == start_quad) { t = arc_param(x, y, start_angle); subdivide(x, y, t, 0); /* the path is still empty and we have to create the first * node */ SKCurve_AppendLine(arc, x[0], y[0], ContAngle); } if (quadrant == end_quad) { t = arc_param(x, y, end_angle); if (!t) break; subdivide(x, y, t, 1); } SKCurve_AppendBezier(arc, x[1], y[1], x[2], y[2], x[3], y[3], ContAngle); } if (type > 0) { if (type < 3) { if (type == 2) { SKCurve_AppendLine(arc, 0.0, 0.0, ContAngle); } SKCurve_AppendLine(arc, arc->segments[0].x, arc->segments[0].y, ContAngle); } arc->closed = 1; } return (PyObject*)arc; } /* * */ PyObject * SKCurve_PyRectanglePath(PyObject * self, PyObject * args) { SKTrafoObject * trafo; SKCurveObject * path; if (!PyArg_ParseTuple(args, "O!", &SKTrafoType, &trafo)) return NULL; path = (SKCurveObject*)SKCurve_New(5); SKCurve_AppendLine(path, trafo->v1, trafo->v2, ContAngle); SKCurve_AppendLine(path, trafo->v1 + trafo->m11, trafo->v2 + trafo->m21, ContAngle); SKCurve_AppendLine(path, trafo->v1 + trafo->m11 + trafo->m12, trafo->v2 + trafo->m21 + trafo->m22, ContAngle); SKCurve_AppendLine(path, trafo->v1 + trafo->m12, trafo->v2 + trafo->m22, ContAngle); SKCurve_AppendLine(path, trafo->v1, trafo->v2, ContAngle); SKCurve_ClosePath(path); return (PyObject*)path; } static void append_round_corner(SKCurveObject * path, SKTrafoObject * trafo, int quadrant) { double x[4], y[4]; int i; CurveSegment * last_segment; x[0] = arc_nodes_x[quadrant % 4]; y[0] = arc_nodes_y[quadrant % 4]; x[1] = arc_controls_x[2 * (quadrant % 4)]; y[1] = arc_controls_y[2 * (quadrant % 4)]; x[2] = arc_controls_x[2 * (quadrant % 4) + 1]; y[2] = arc_controls_y[2 * (quadrant % 4) + 1]; x[3] = arc_nodes_x[(quadrant + 1) % 4]; y[3] = arc_nodes_y[(quadrant + 1) % 4]; last_segment = path->segments + path->len - 1; /*fprintf(stderr, "last_xy %g %g\n", last_segment->x, last_segment->y);*/ trafo->v1 = last_segment->x - trafo->m11 * x[0] - trafo->m12 * y[0]; trafo->v2 = last_segment->y - trafo->m21 * x[0] - trafo->m22 * y[0]; /*fprintf(stderr, "trafo->vi %g %g\n", trafo->v1, trafo->v2);*/ for (i = 1; i < 4; i++) { double tx = x[i], ty = y[i]; x[i] = trafo->m11 * tx + trafo->m12 * ty + trafo->v1; y[i] = trafo->m21 * tx + trafo->m22 * ty + trafo->v2; } SKCurve_AppendBezier(path, x[1], y[1], x[2], y[2], x[3], y[3], ContSmooth); } PyObject * SKCurve_PyRoundedRectanglePath(PyObject * self, PyObject * args) { SKTrafoObject * trafo; SKTrafoObject ellipse_trafo; SKCurveObject * path; double radius1, radius2; if (!PyArg_ParseTuple(args, "O!dd", &SKTrafoType, &trafo, &radius1, &radius2)) return NULL; ellipse_trafo.m11 = radius1 * trafo->m11; ellipse_trafo.m21 = radius1 * trafo->m21; ellipse_trafo.m12 = radius2 * trafo->m12; ellipse_trafo.m22 = radius2 * trafo->m22; path = (SKCurveObject*)SKCurve_New(9); SKCurve_AppendLine(path, trafo->v1 + ellipse_trafo.m11, trafo->v2 + ellipse_trafo.m21, ContSmooth); SKCurve_AppendLine(path, trafo->v1 + trafo->m11 - ellipse_trafo.m11, trafo->v2 + trafo->m21 - ellipse_trafo.m21, ContSmooth); append_round_corner(path, &ellipse_trafo, 3); SKCurve_AppendLine(path, trafo->v1 + trafo->m11 + trafo->m12 - ellipse_trafo.m12, trafo->v2 + trafo->m21 + trafo->m22 - ellipse_trafo.m22, ContSmooth); append_round_corner(path, &ellipse_trafo, 0); SKCurve_AppendLine(path, trafo->v1 + ellipse_trafo.m11 + trafo->m12, trafo->v2 + ellipse_trafo.m21 + trafo->m22, ContSmooth); append_round_corner(path, &ellipse_trafo, 1); SKCurve_AppendLine(path, trafo->v1 + ellipse_trafo.m12, trafo->v2 + ellipse_trafo.m22, ContSmooth); append_round_corner(path, &ellipse_trafo, 2); SKCurve_ClosePath(path); return (PyObject*)path; } UniConvertor-1.1.4/src/modules/skmod/curvefunc.h0000664000076400007640000000251411060500613020365 0ustar igorigor/* Sketch - A Python-based interactive drawing program * Copyright (C) 1997, 1998 by Bernhard Herzog * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef CURVEFUNC_H #define CURVEFUNC_H #include PyObject * SKCurve_PyCreatePath(PyObject * self, PyObject * args); PyObject * SKCurve_PyTestTransformed(PyObject * self, PyObject * args); PyObject * SKCurve_PyBlendPaths(PyObject * self, PyObject * args); PyObject * SKCurve_PyApproxArc(PyObject * self, PyObject * args); PyObject * SKCurve_PyRectanglePath(PyObject * self, PyObject * args); PyObject * SKCurve_PyRoundedRectanglePath(PyObject * self, PyObject * args); #endif /* CURVEFUNC_H */ UniConvertor-1.1.4/src/modules/skmod/_sketchmodule.c0000664000076400007640000001155711060500613021215 0ustar igorigor/* Sketch - A Python-based interactive drawing program * Copyright (C) 2006 by Igor E.Novikov * Copyright (C) 1997, 1998, 1999, 2006 by Bernhard Herzog * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "skpoint.h" #include "skrect.h" #include "sktrafo.h" #include "skfm.h" #include "curvefunc.h" #include "curveobject.h" #include "skimage.h" #include "skcolor.h" #include "skaux.h" #include "_sketchmodule.h" static PyMethodDef curve_functions[] = { /* Point functions */ {"Point", SKPoint_PyPoint, 1}, {"Polar", SKPoint_PyPolar, 1}, {"points_allocated", skpoint_allocated, 1}, /* Rect functions */ {"Rect", skrect_skrect, 1}, {"UnionRects", skrect_unionrects, 1}, {"PointsToRect", skrect_PointsToRect, 1}, {"IntersectRects", skrect_intersect, 1}, {"rects_allocated", skrect_allocated, 1}, /* Trafo functions */ {"Trafo", sktrafo_sktrafo, 1}, {"Scale", sktrafo_scale, 1}, {"Translation", sktrafo_translation, 1}, {"Rotation", sktrafo_rotation, 1}, {"trafos_allocted", sktrafo_allocated, 1}, /* FontMetric functions */ {"CreateFontMetric", SKFM_PyCreateMetric, 1}, /* Curve functions */ {"test_transformed", SKCurve_PyTestTransformed, 1}, {"blend_paths", SKCurve_PyBlendPaths, 1}, {"CreatePath", SKCurve_PyCreatePath, 1}, {"approx_arc", SKCurve_PyApproxArc, 1}, {"RectanglePath", SKCurve_PyRectanglePath, 1}, {"RoundedRectanglePath", SKCurve_PyRoundedRectanglePath, 1}, {"num_allocated", _SKCurve_NumAllocated, 1}, /* image functions */ {"write_ps_hex", skimage_write_ps_hex, 1}, /* color functions */ {"RGBColor", skcolor_rgbcolor, 1}, {"colors_allocated", skcolor_num_allocated, 1}, /* skaux */ {"DrawBezier", SKAux_DrawBezier, 1}, {"TransformRectangle", SKAux_TransformRectangle, 1}, {"IdIndex", SKAux_IdIndex, 1}, {"xlfd_char_range", xlfd_char_range, 1}, {"SKCache", SKCache_PyCreate, 1}, /* */ {NULL, NULL} }; /* * Init module */ static void add_int(PyObject * dict, int i, char * name) { PyObject *v; v = Py_BuildValue("i", i); if (v) { PyDict_SetItemString(dict, name, v); Py_DECREF(v); } } DL_EXPORT(void) init_sketch(void) { PyObject * d, *m, *r; SKCurveType.ob_type = &PyType_Type; SKCacheType.ob_type = &PyType_Type; SKColorType.ob_type = &PyType_Type; SKFontMetricType.ob_type = &PyType_Type; SKPointType.ob_type = &PyType_Type; SKRectType.ob_type = &PyType_Type; SKTrafoType.ob_type = &PyType_Type; m = Py_InitModule("_sketch", curve_functions); d = PyModule_GetDict(m); /* Rect specific initialization */ /* The InfinityRect is initialized with FLT_MAX instead of HUGE_VAL now (Sketch 0.5.4), because of problems with HUGE_VAL on Alpha Linux. */ r = SKRect_FromDouble(-FLT_MAX, -FLT_MAX, FLT_MAX, FLT_MAX); if (r) { PyDict_SetItemString(d, "InfinityRect", r); SKRect_InfinityRect = (SKRectObject*)r; } r = SKRect_FromDouble(0.0, 0.0, 0.0, 0.0); if (r) { PyDict_SetItemString(d, "EmptyRect", r); SKRect_EmptyRect = (SKRectObject*)r; } /* Trafo specific initialization */ SKTrafo_ExcSingular = PyErr_NewException("_sketch.SingularMatrix", PyExc_ArithmeticError, NULL); if (SKTrafo_ExcSingular) { PyDict_SetItemString(d, "SingularMatrix", SKTrafo_ExcSingular); } /* Sketch type objects */ PyDict_SetItemString(d, "RectType", (PyObject*)&SKRectType); PyDict_SetItemString(d, "PointType", (PyObject*)&SKPointType); PyDict_SetItemString(d, "TrafoType", (PyObject*)&SKTrafoType); PyDict_SetItemString(d, "CurveType", (PyObject*)&SKCurveType); /* Curve specific initialization */ #define ADD_INT(name) add_int(d, name, #name) #define ADD_INT2(i, name) add_int(d, i, name) ADD_INT(ContAngle); ADD_INT(ContSmooth); ADD_INT(ContSymmetrical); ADD_INT2(CurveBezier, "Bezier"); ADD_INT2(CurveLine, "Line"); ADD_INT(SelNone); ADD_INT(SelNodes); ADD_INT(SelSegmentFirst); ADD_INT(SelSegmentLast); _SKCurve_InitCurveObject(); } UniConvertor-1.1.4/src/modules/skmod/_sketchmodule.h0000664000076400007640000000215611060500613021215 0ustar igorigor/* Sketch - A Python-based interactive drawing program * Copyright (C) 2006 by Igor E.Novikov * Copyright (C) 1998 by Bernhard Herzog * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _SKETCH_H #define _SKETCH_H // #include // #include // #include // // extern PyObject * Pax_GCType; // extern PyObject * Pax_ImageType; // extern Pax_Functions * pax_functions; #endif /* _SKETCH_H */ UniConvertor-1.1.4/src/modules/skmod/curvelow.c0000664000076400007640000002104611060500613020227 0ustar igorigor/* Sketch - A Python-based interactive drawing program * Copyright (C) 2006 by Igor E.Novikov * Copyright (C) 1997, 1998, 1999 by Bernhard Herzog * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * `low level' routines for bezier curves */ #include "math.h" // #include "X11/Xlib.h" #include "curvelow.h" #define PREC_BITS 4 #define ROUND(x) (((x) + (1 << (PREC_BITS - 1))) >> PREC_BITS) #define SMOOTH_EPSILON 8 /* return true if the curve segment given by x[] and y[] is smooth. */ static int is_smooth(int * x, int * y) { long vx, vy, dx, dy, len = 0, lensqr, dist, par; vx = x[3] - x[0]; vy = y[3] - y[0]; lensqr = vx * vx + vy * vy; dx = x[1] - x[0]; dy = y[1] - y[0]; if (lensqr) { par = vx * dx + vy * dy; if (par < 0 || par > lensqr) return 0; len = sqrt(lensqr); dist = abs(vx * dy - vy * dx); if (dist > len * SMOOTH_EPSILON) return 0; } else if (dx != 0 || dy != 0) return 0; dx = x[2] - x[3]; dy = y[2] - y[3]; if (lensqr) { par = vx * dx + vy * dy; if (par > 0 || par < -lensqr) return 0; dist = abs(vx * dy - vy * dx); if (dist > len * SMOOTH_EPSILON) return 0; } else if (dx != 0 || dy != 0) return 0; return 1; } /* determine whether the line fom (SX, SY) to (EX, EY) is `hit' by a * click at (PX, PY), or whether a polygon containing this line is hit * in the interior at (PX, PY). * * Return -1 if the line it self his hit. Otherwise, return +1 if a * horizontal line from (PX, PY) to (-Infinity, PY) intersects the line * and 0 if it doesn't. * * The nonnegative return values can be used to determine whether (PX, PY) * is an interior point of a polygon according to the even-odd rule. */ static int bezier_test_line(int sx, int sy, int ex, int ey, int px, int py) { long vx, vy, dx, dy, len, dist, not_horizontal; if (ey < sy) { dist = ex; ex = sx; sx = dist; dist = ey; ey = sy; sy = dist; } not_horizontal = ey > sy + (2 << PREC_BITS); if (not_horizontal && (py >= ey || py < sy)) return 0; vx = ex - sx; vy = ey - sy; len = sqrt(vx * vx + vy * vy); if (!len) /* degenerate case of coincident end points. Assumes that some * other part of the code has already determined whether the end * point is hit. */ return 0; dx = px - sx; dy = py - sy; dist = vx * dy - vy * dx; if ((not_horizontal || (px >= sx && px <= ex) || (px >= ex && px <= sx)) && abs(dist) <= (len << (PREC_BITS + 1))) return -1; /* horizontal lines (vy == 0) always return 0 here. */ return vy && py < ey && py >= sy && dx * abs(vy) > vx * abs(dy); } #define DUMP_TEST 0 static int bezier_hit_recurse(int * x, int * y, int px, int py, int depth) { int u[7], v[7]; int tx, ty; int i, result1, result2; int minx = *x, maxx = *x, miny = *y, maxy = *y; for (i = 1; i < 4; i++) { if (x[i] < minx) minx = x[i]; if (x[i] > maxx) maxx = x[i]; if (y[i] < miny) miny = y[i]; if (y[i] > maxy) maxy = y[i]; } if (px <= minx || py >= maxy || py < miny) { #if DUMP_TEST fprintf(stderr, "/\\/(%d) %d %d: %d %d %d %d --> 0\n", depth, px, py, x[0], y[0], x[3], y[3]); #endif return 0; } if (px >= maxx && ( (py >= y[0] && py < y[3]) || (py >= y[3] && py < y[0]))) { #if DUMP_TEST fprintf(stderr, "/\\/(%d) %d %d: %d %d %d %d --> 1\n", depth, px, py, x[0], y[0], x[3], y[3]); #endif return +1; } #if DUMP_TEST fprintf(stderr, ">>>(%d) %d %d: %d %d %d %d [%d, %d, %d, %d]--> ?\n", depth, px, py, x[0], y[0], x[3], y[3], minx, maxx, miny, maxy); #endif u[1] = x[0] + x[1]; v[1] = y[0] + y[1]; tx = x[1] + x[2]; ty = y[1] + y[2]; u[5] = x[2] + x[3]; v[5] = y[2] + y[3]; u[2] = u[1] + tx; v[2] = v[1] + ty; u[4] = u[5] + tx; v[4] = v[5] + ty; u[3] = (u[2] + u[4] + 4) >> 3; v[3] = (v[2] + v[4] + 4) >> 3; if (depth > 0) { u[0] = x[0]; v[0] = y[0]; u[1] = (u[1] + 1) >> 1; v[1] = (v[1] + 1) >> 1; u[2] = (u[2] + 2) >> 2; v[2] = (v[2] + 2) >> 2; if (is_smooth(u, v)) { result1 = bezier_test_line(u[0], v[0], u[3], v[3], px, py); #if DUMP_TEST if (result1) fprintf(stderr, "##1(%d) %d %d: %d %d %d %d --> %d\n", depth, px, py, u[0], v[0], u[3], v[3], result1); #endif } else { result1 = bezier_hit_recurse(u, v, px, py, depth - 1); #if DUMP_TEST if (result1) fprintf(stderr, "<<1(%d) %d %d: %d %d %d %d %d %d %d %d --> %d\n", depth, px, py, u[0], v[0], u[1], v[1], u[2], v[2], u[3], v[3], result1); #endif } if (result1 < 0) { return result1; } u[4] = (u[4] + 2) >> 2; v[4] = (v[4] + 2) >> 2; u[5] = (u[5] + 1) >> 1; v[5] = (v[5] + 1) >> 1; u[6] = x[3]; v[6] = y[3]; if (is_smooth(u + 3, v + 3)) { result2 = bezier_test_line(u[3], v[3], u[6], v[6], px, py); #if DUMP_TEST if (result2) fprintf(stderr, "##2(%d) %d %d: %d %d %d %d --> %d\n", depth, px, py, u[3], v[3], u[6], v[6], result2); #endif } else { result2 = bezier_hit_recurse(u + 3, v + 3, px, py, depth - 1); #if DUMP_TEST if (result2) fprintf(stderr, "<<2(%d) %d %d: %d %d %d %d %d %d %d %d --> %d\n", depth, px, py, u[0], v[0], u[1], v[1], u[2], v[2], u[3], v[3], result2); #endif } if (result2 < 0) { return result2; } if (result1 || result2) { #if DUMP_TEST fprintf(stderr, "+++(%d) %d %d: %d %d %d %d %d %d %d %d --> %d\n", depth, px, py, u[0], v[0], u[1], v[1], u[2], v[2], u[3], v[3], result1 + result2); #endif return result1 + result2; } return 0; } result1 = bezier_test_line(x[0], y[0], x[3], y[3], px, py); #if DUMP_TEST if (result1) fprintf(stderr, "***(%d) %d %d: %d %d %d %d --> %d\n", depth, px, py, x[0], y[0], x[3], y[3], result1); #endif return result1; } int bezier_hit_segment(int * x, int * y, int px, int py) { int i; for (i = 0; i < 4; i++) { x[i] <<= PREC_BITS; y[i] <<= PREC_BITS; } px = (px << PREC_BITS) + 1; py = (py << PREC_BITS) + 1; if (is_smooth(x, y)) { #if DUMP_TEST int result = bezier_test_line(x[0], y[0], x[3], y[3], px, py); fprintf(stderr, "---(*) %d %d: %d %d %d %d --> %d\n", px, py, x[0], y[0], x[3], y[3], result); return result; #else return bezier_test_line(x[0], y[0], x[3], y[3], px, py); #endif } return bezier_hit_recurse(x, y, px, py, BEZIER_DEPTH); } int bezier_hit_line(int sx, int sy, int ex, int ey, int px, int py) { sx <<= PREC_BITS; sy <<= PREC_BITS; ex <<= PREC_BITS; ey <<= PREC_BITS; px = (px << PREC_BITS) + 1; py = (py << PREC_BITS) + 1; #if DUMP_TEST { int result = bezier_test_line(sx, sy, ex, ey, px, py); fprintf(stderr, "---(-) %d %d: %d %d %d %d --> %d\n", px, py, sx, sy, ex, ey, result); return result; } #else return bezier_test_line(sx, sy, ex, ey, px, py); #endif } /* * */ int bezier_basis[4][4] = { { -1, 3, -3, 1}, { 3, -6, 3, 0}, { -3, 3, 0, 0}, { 1, 0, 0, 0} }; #define EVAL(coeff, t) (((coeff[0]*t + coeff[1])*t + coeff[2]) * t + coeff[3]) void bezier_point_at(double *x, double *y, double t, double * result_x, double * result_y) { double coeff_x[4], coeff_y[4]; int i, j; for (i = 0; i < 4; i++) { coeff_x[i] = 0; coeff_y[i] = 0; for (j = 0; j < 4; j++) { coeff_x[i] += bezier_basis[i][j] * x[j]; coeff_y[i] += bezier_basis[i][j] * y[j]; } } *result_x = EVAL(coeff_x, t); *result_y = EVAL(coeff_y, t); } #define EVALDIFF(coeff, t) ((3 * coeff[0] * t + 2 * coeff[1]) * t + coeff[2]) void bezier_tangent_at(double *x, double *y, double t, double * result_x, double * result_y) { double coeff_x[3], coeff_y[3]; int i, j; for (i = 0; i < 3; i++) { coeff_x[i] = 0; coeff_y[i] = 0; for (j = 0; j < 4; j++) { coeff_x[i] += bezier_basis[i][j] * x[j]; coeff_y[i] += bezier_basis[i][j] * y[j]; } } *result_x = EVALDIFF(coeff_x, t); *result_y = EVALDIFF(coeff_y, t); } #undef EVAL #undef EVALDIFF UniConvertor-1.1.4/src/modules/skmod/curvelow.h0000664000076400007640000000116211060500613020231 0ustar igorigor#ifndef CURVE_LOW_H #define CURVE_LOW_H #if defined(__cplusplus) extern "C" { #endif #define BEZIER_DEPTH 5 #define BEZIER_NUM_STEPS ((2 << (BEZIER_DEPTH + 1)) + 1) #define BEZIER_FILL_LENGTH (BEZIER_NUM_STEPS + 1) int bezier_hit_segment(int * x, int * y, int px, int py); int bezier_hit_line(int sx, int sy, int ex, int ey, int px, int py); void bezier_point_at(double *x, double *y, double t, double * result_x, double * result_y); void bezier_tangent_at(double *x, double *y, double t, double * result_x, double * result_y); extern int bezier_basis[4][4]; #if defined(__cplusplus) } #endif #endif UniConvertor-1.1.4/src/modules/skmod/curvemisc.c0000664000076400007640000002350511060500613020363 0ustar igorigor/* Sketch - A Python-based interactive drawing program * Copyright (C) 1998, 1999 by Bernhard Herzog * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "sktrafo.h" #include "skrect.h" #include "curvemisc.h" #include "curvelow.h" #define EVAL(coeff, t) (((coeff[0]*t + coeff[1])*t + coeff[2]) * t + coeff[3]) PyObject * curve_local_coord_system(SKCurveObject * self, PyObject * args) { double pos, t; int index; double x[4], y[4]; double point_x, point_y, tangent_x, tangent_y, length; if(!PyArg_ParseTuple(args, "d", &pos)) return NULL; index = floor(pos); if (index >= self->len - 1 || index < 0) { PyErr_SetString(PyExc_ValueError, "parameter out of range"); return NULL; } t = pos - index; x[0] = self->segments[index].x; y[0] = self->segments[index].y; x[3] = self->segments[index + 1].x; y[3] = self->segments[index + 1].y; if (self->segments[index].type == CurveBezier) { x[1] = self->segments[index + 1].x1; y[1] = self->segments[index + 1].y1; x[2] = self->segments[index + 1].x2; y[2] = self->segments[index + 1].y2; bezier_point_at(x, y, t, &point_x, &point_y); bezier_tangent_at(x, y, t, &tangent_x, &tangent_y); } else { point_x = x[0] * (1 - t) + x[3] * t; point_y = y[0] * (1 - t) + y[3] * t; tangent_x = x[3] - x[0]; tangent_y = y[3] - y[0]; } length = hypot(tangent_x, tangent_y); if (length > 0) { /* XXX what to do in degenerate case length == 0?*/ tangent_x /= length; tangent_y /= length; } return SKTrafo_FromDouble(tangent_x, tangent_y, -tangent_y, tangent_x, point_x, point_y); } static int add_point(PyObject * list, double length, PyObject * point) { PyObject * tuple = NULL; int result = -1; if (point) { tuple = Py_BuildValue("dO", length, point); if (tuple) result = PyList_Append(list, tuple); } Py_XDECREF(tuple); Py_XDECREF(point); return result; } static int curve_arc_length_curve(double * xs, double * ys, double start_param, double * length, PyObject * list) { double coeff_x[4], coeff_y[4]; int i, j; double delta, t, t2, t3, x, y, lastx, lasty; int num_steps = BEZIER_NUM_STEPS; for (i = 0; i < 4; i++) { coeff_x[i] = 0; coeff_y[i] = 0; for (j = 0; j < 4; j++) { coeff_x[i] += bezier_basis[i][j] * xs[j]; coeff_y[i] += bezier_basis[i][j] * ys[j]; } } lastx = EVAL(coeff_x, start_param); lasty = EVAL(coeff_y, start_param); delta = 1.0 / num_steps; t = start_param; num_steps = (1.0 - start_param) / delta; for (i = 0; i < num_steps; i++) { t += delta; t2 = t * t; t3 = t2 * t; x = coeff_x[0] * t3 + coeff_x[1] * t2 + coeff_x[2] * t + coeff_x[3]; y = coeff_y[0] * t3 + coeff_y[1] * t2 + coeff_y[2] * t + coeff_y[3]; *length += hypot(x - lastx, y - lasty); if (add_point(list, *length, SKPoint_FromXY(x, y)) < 0) return -1; lastx = x; lasty = y; } return 0; } static int curve_arc_length_straight(double x1, double y1, double x2, double y2, double start_param, double * length, PyObject * list) { *length += (1.0 - start_param) * hypot(x2 - x1, y2 - y1); return add_point(list, *length, SKPoint_FromXY(x2, y2)); } PyObject * curve_arc_lengths(SKCurveObject * self, PyObject * args) { PyObject * list; int index, first = 1; double length = 0; double start_param = 0.0; if (!PyArg_ParseTuple(args, "|d", &start_param)) return NULL; index = floor(start_param); start_param = start_param - index; index = index + 1; if (index < 1 || index > self->len) { PyErr_SetString(PyExc_ValueError, "invalid start parameter"); return NULL; } if (index == self->len) { index = self->len - 1; start_param = 1.0; } list = PyList_New(0); if (!list) return NULL; for (; index < self->len; index++) { if (self->segments[index].type == CurveBezier) { double x[4], y[4]; double sx, sy; CurveSegment * segment = self->segments + index; x[0] = segment[-1].x; y[0] = segment[-1].y; x[1] = segment->x1; y[1] = segment->y1; x[2] = segment->x2; y[2] = segment->y2; x[3] = segment->x; y[3] = segment->y; if (first) { bezier_point_at(x, y, start_param, &sx, &sy); if (add_point(list, 0, SKPoint_FromXY(sx, sy)) < 0) goto fail; first = 0; } if (curve_arc_length_curve(x, y, start_param, &length, list) < 0) goto fail; } else { if (first) { double sx, sy; sx = (1 - start_param) * self->segments[index - 1].x + start_param * self->segments[index].x; sy = (1 - start_param) * self->segments[index - 1].y + start_param * self->segments[index].y; if (add_point(list, 0, SKPoint_FromXY(sx, sy)) < 0) goto fail; first = 0; } if (curve_arc_length_straight(self->segments[index - 1].x, self->segments[index - 1].y, self->segments[index].x, self->segments[index].y, start_param, &length, list) < 0) goto fail; } start_param = 0.0; } return list; fail: Py_DECREF(list); return NULL; } /* * */ static double nearest_on_line(double x1, double y1, double x2, double y2, double x, double y, double * t) { double vx = x2 - x1; double vy = y2 - y1; double length = hypot(vx, vy); double dx = x - x1; double dy = y - y1; double distance, linepos; if (length > 0) { distance = abs((dx * vy - dy * vx) / length); linepos = (dx * vx + dy * vy) / length; if (linepos < 0.0) { *t = 0; distance = hypot(dx, dy); } else if (linepos > length) { *t = 1; distance = hypot(x - x2, y - y2); } else { *t = linepos / length; } } else { distance = hypot(dx, dy); *t = 0; } return distance; } double nearest_on_curve(double *x, double *y, double px, double py, double *pt) { double coeff_x[4], coeff_y[4]; int i, j; double t, lt, mint = 0, mindist = 1e100, dist; double x1, y1, x2, y2; for (i = 0; i < 4; i++) { coeff_x[i] = 0; coeff_y[i] = 0; for (j = 0; j < 4; j++) { coeff_x[i] += bezier_basis[i][j] * x[j]; coeff_y[i] += bezier_basis[i][j] * y[j]; } } x1 = coeff_x[3]; y1 = coeff_y[3]; for (t = 0.015625; t <= 1.0; t += 0.015625) { x2 = EVAL(coeff_x, t); y2 = EVAL(coeff_y, t); dist = nearest_on_line(x1, y1, x2, y2, px, py, <); if (dist < mindist) { mindist = dist; mint = t + (lt - 1) * 0.015625; } x1 = x2; y1 = y2; } *pt = mint; return mindist; } PyObject * SKCurve_NearestPointPy(SKCurveObject * self, PyObject * args) { double x, y; double bx[4], by[4]; double min_distance = 1e100, max_distance = 0.0, distance; double nearest_t = 0, t; double bound_left = 0, bound_right = 0, bound_top = 0, bound_bottom = 0; int use_max_dist = 0; int i, found = 0; CurveSegment * segment; PyObject * result; if (!PyArg_ParseTuple(args, "dd|d", &x, &y, &max_distance)) return NULL; use_max_dist = max_distance > 0; bound_left = x - max_distance; bound_right = x + max_distance; bound_top = y + max_distance; bound_bottom = y - max_distance; segment = self->segments + 1; for (i = 1; i < self->len; i++, segment++) { if (segment->type == CurveBezier) { bx[0] = segment[-1].x; by[0] = segment[-1].y; bx[1] = segment->x1; by[1] = segment->y1; bx[2] = segment->x2; by[2] = segment->y2; bx[3] = segment->x; by[3] = segment->y; if (use_max_dist) { SKRectObject r; r.left = r.right = bx[0]; r.top = r.bottom = by[0]; SKRect_AddXY(&r, bx[1], by[1]); SKRect_AddXY(&r, bx[2], by[2]); SKRect_AddXY(&r, bx[3], by[3]); if (r.left > bound_right || r.right < bound_left || r.top < bound_bottom || r.bottom > bound_top) { continue; } } distance = nearest_on_curve(bx, by, x, y, &t); } else { distance = nearest_on_line(segment[-1].x, segment[-1].y, segment->x, segment->y, x, y, &t); } if (distance < min_distance) { min_distance = distance; nearest_t = (double)(i - 1) + t; found = 1; } } if (found) { result = PyFloat_FromDouble(nearest_t); } else { Py_INCREF(Py_None); result = Py_None; } return result; } PyObject * SKCurve_PointAtPy(SKCurveObject * self, PyObject * args) { double x[4], y[4]; double t, px, py; int i; if (!PyArg_ParseTuple(args, "d", &t)) return NULL; i = floor(t); t = t - i; i = i + 1; if (i < 1 || i > self->len) { PyErr_SetString(PyExc_ValueError, "invalid curve parameter"); return NULL; } if (i == self->len) { i = self->len - 1; t = 1.0; } if (self->segments[i].type == CurveBezier) { x[0] = self->segments[i - 1].x; y[0] = self->segments[i - 1].y; x[1] = self->segments[i].x1; y[1] = self->segments[i].y1; x[2] = self->segments[i].x2; y[2] = self->segments[i].y2; x[3] = self->segments[i].x; y[3] = self->segments[i].y; bezier_point_at(x, y, t, &px, &py); } else { px = (1 - t) * self->segments[i - 1].x + t * self->segments[i].x; py = (1 - t) * self->segments[i - 1].y + t * self->segments[i].y; } return SKPoint_FromXY(px, py); } UniConvertor-1.1.4/src/modules/skmod/curvemisc.h0000664000076400007640000000227411060500613020370 0ustar igorigor/* Sketch - A Python-based interactive drawing program * Copyright (C) 1998, 1999 by Bernhard Herzog * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef CURVEMISC_H #define CURVEMISC_H #include "curveobject.h" PyObject * curve_local_coord_system(SKCurveObject * self, PyObject * args); PyObject * curve_arc_lengths(SKCurveObject * self, PyObject * args); PyObject * SKCurve_NearestPointPy(SKCurveObject * self, PyObject * args); PyObject * SKCurve_PointAtPy(SKCurveObject * self, PyObject * args); #endif UniConvertor-1.1.4/src/modules/skmod/ImPlatform.h0000664000076400007640000000341011060500613020433 0ustar igorigor/* * The Python Imaging Library * $Id: ImPlatform.h 2134 2004-10-06 08:55:20Z fredrik $ * * platform declarations for the imaging core library * * Copyright (c) Fredrik Lundh 1995-2003. */ #include "Python.h" /* Check that we have an ANSI compliant compiler */ #ifndef HAVE_PROTOTYPES #error Sorry, this library requires support for ANSI prototypes. #endif #ifndef STDC_HEADERS #error Sorry, this library requires ANSI header files. #endif #if defined(_MSC_VER) #ifndef WIN32 #define WIN32 #endif /* VC++ 4.0 is a bit annoying when it comes to precision issues (like claiming that "float a = 0.0;" would lead to loss of precision). I don't like to see warnings from my code, but since I still want to keep it readable, I simply switch off a few warnings instead of adding the tons of casts that VC++ seem to require. This code is compiled with numerous other compilers as well, so any real errors are likely to be catched anyway. */ #pragma warning(disable: 4244) /* conversion from 'float' to 'int' */ #endif #if defined(_MSC_VER) #define inline __inline #elif !defined(USE_INLINE) #define inline #endif #if SIZEOF_SHORT == 2 #define INT16 short #elif SIZEOF_INT == 2 #define INT16 int #else #define INT16 short /* most things works just fine anyway... */ #endif #if SIZEOF_SHORT == 4 #define INT32 short #elif SIZEOF_INT == 4 #define INT32 int #elif SIZEOF_LONG == 4 #define INT32 long #else #error Cannot find required 32-bit integer type #endif #if SIZEOF_LONG == 8 #define INT64 long #elif SIZEOF_LONG_LONG == 8 #define INT64 long #endif /* assume IEEE; tweak if necessary (patches are welcome) */ #define FLOAT32 float #define FLOAT64 double #define INT8 signed char #define UINT8 unsigned char #define UINT16 unsigned INT16 #define UINT32 unsigned INT32 UniConvertor-1.1.4/src/modules/type1mod/0000755000076400007640000000000011211455344016645 5ustar igorigorUniConvertor-1.1.4/src/modules/type1mod/_type1module.c0000664000076400007640000000760711060500610021421 0ustar igorigor/* Sketch - A Python-based interactive drawing program * Copyright (C) 1998, 1999, 2001 by Bernhard Herzog * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include /* * Functions for decoding a PostScript Type1 font program. * * Reference: "Adobe Type1 Font Format" * from Adobe Systems Incorporated */ /* decode(BUFFER[, R]) -> data, R * * Perform eexec/charstring decryption on the buffer BUFFER, using R as * the start value for the key. Return the decoded data as a string * object and the value of the key at the end of decoding. * * If not provided, R defaults to 4330, the initial key for char string * encryption/decryption. */ static PyObject * decode(PyObject * self, PyObject * args) { unsigned char * buffer, *result; int buffer_length, i; unsigned short r = 4330, c1 = 52845, c2 = 22719; int temp = 4330; int cipher; PyObject * oresult, *tuple; /* Use a temporary int vrbl for the optional parameter instead of h * to make it work with Python >= 2.0 too */ if (!PyArg_ParseTuple(args, "s#|i", &buffer, &buffer_length, &temp)) return NULL; r = temp; oresult = PyString_FromStringAndSize(NULL, buffer_length); if (!oresult) return NULL; result = (unsigned char*)PyString_AsString(oresult); for (i = 0; i < buffer_length; i++) { cipher = buffer[i]; result[i] = cipher ^ (r >> 8); r = (cipher + r) * c1 + c2; } tuple = Py_BuildValue("Oi", oresult, r); Py_DECREF(oresult); return tuple; } /* * hexdecode(BUFFER) * * Convert a buffer of hex-digits to binary. Whitespace is ignored. * * Return a tuple (BINARY, REST). BINARY is a string containing the * binary bytes. REST is either the empty string (if BUFFER contained an * even number of hex-digits) or the last hex-digit of BUFFER if the * number of hex-digits in BUFFER was odd. */ static char * hex_digits = "0123456789ABCDEF"; static PyObject * hexdecode(PyObject * self, PyObject * args) { unsigned char * hex, *result, *buffer; PyObject *tuple; int length, i, last_digit = -1, c; if (!PyArg_ParseTuple(args, "s#", &hex, &length)) return NULL; buffer = malloc((length + 1) / 2); if (!buffer) return PyErr_NoMemory(); for (i = 0, result = buffer; i < length; i++, hex++) { c = *hex; if (isspace(c)) continue; if (isxdigit(c)) { if (isdigit(c)) c = c - '0'; else { if (isupper(c)) c = c - 'A' + 10; else c = c - 'a' + 10; } if (last_digit >= 0) { *result++ = last_digit * 16 + c; last_digit = -1; } else last_digit = c; } else { free(buffer); PyErr_SetString(PyExc_ValueError, "invalid character found"); return NULL; } } if (last_digit >= 0) tuple = Py_BuildValue("s#c", buffer, result - buffer, hex_digits[last_digit]); else tuple = Py_BuildValue("s#s", buffer, result - buffer, ""); free(buffer); return tuple; } /* * Method table and module initialization */ static PyMethodDef type1_methods[] = { {"decode", decode, METH_VARARGS}, {"hexdecode", hexdecode, METH_VARARGS}, {NULL, NULL} }; DL_EXPORT(void) init_type1(void) { Py_InitModule("_type1", type1_methods); } UniConvertor-1.1.4/src/modules/filter/0000755000076400007640000000000011211455344016370 5ustar igorigorUniConvertor-1.1.4/src/modules/filter/stringfilter.c0000664000076400007640000000453511060500611021247 0ustar igorigor/* * Copyright (C) 1998, 1999 by Bernhard Herzog. * * All Rights Reserved * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear in * supporting documentation, and that the name of the author not be * used in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "stringfilter.h" #define FBUFLEN 1024 typedef struct { PyObject * string; char * pos; size_t left; } StringDecodeState; static size_t read_string(void* clientdata, PyObject * source, char * buf, size_t length) { StringDecodeState * state = (StringDecodeState*)clientdata; size_t copy; if (state->left > 0) { if (state->left > length) copy = length; else copy = state->left; memcpy(buf, state->pos, copy); state->left -= copy; state->pos += copy; } else if (source != Py_None) { copy = Filter_Read(source, buf, length); } else { copy = 0; } return copy; } static void string_state_dealloc(void * clientdata) { Py_DECREF(((StringDecodeState*)clientdata)->string); free(clientdata); } PyObject * Filter_StringDecode(PyObject * self, PyObject * args) { PyObject * source; PyObject * string; StringDecodeState * state; if (!PyArg_ParseTuple(args, "SO", &string, &source)) return NULL; state = malloc(sizeof (StringDecodeState)); if (!state) return PyErr_NoMemory(); state->string = string; Py_INCREF(state->string); state->pos = PyString_AsString(string); state->left = PyString_Size(string); return Filter_NewDecoder(source, "StringDecode", 0, read_string, NULL, string_state_dealloc, state); } UniConvertor-1.1.4/src/modules/filter/stringfilter.h0000664000076400007640000000227211060500611021250 0ustar igorigor#ifndef STRINGFILTER_H #define STRINGFILTER_H /* * Copyright (C) 1998, 1999 by Bernhard Herzog. * * All Rights Reserved * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear in * supporting documentation, and that the name of the author not be * used in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "filterobj.h" PyObject * Filter_StringDecode(PyObject * self, PyObject * args); #endif /* STRINGFILTER_H */ UniConvertor-1.1.4/src/modules/filter/zlibfilter.h0000664000076400007640000000226411060500611020703 0ustar igorigor#ifndef ZLIBFILTER_H #define ZLIBFILTER_H /* * Copyright (C) 1998, 1999 by Bernhard Herzog. * * All Rights Reserved * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear in * supporting documentation, and that the name of the author not be * used in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "filterobj.h" PyObject * Filter_FlateDecode(PyObject * self, PyObject * args); #endif /* ZLIBFILTER_H */ UniConvertor-1.1.4/src/modules/filter/binfile.c0000664000076400007640000003124611060500611020142 0ustar igorigor/* * Copyright (C) 1998, 1999, 2001, 2006 by Bernhard Herzog. * * All Rights Reserved * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear in * supporting documentation, and that the name of the author not be * used in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "filterobj.h" #include "binfile.h" /* some code here taken from structmodule.c in Python's Modules directory */ static PyObject * BinFile_FromStream(PyObject * string, int byte_order, int int_size); /* helper functions for floats */ static PyObject * unpack_float(const char *p, /* Where the high order byte is */ int incr) /* 1 for big-endian; -1 for little-endian */ { int s; int e; long f; double x; /* First byte */ s = (*p>>7) & 1; e = (*p & 0x7F) << 1; p += incr; /* Second byte */ e |= (*p>>7) & 1; f = (*p & 0x7F) << 16; p += incr; /* Third byte */ f |= (*p & 0xFF) << 8; p += incr; /* Fourth byte */ f |= *p & 0xFF; x = (double)f / 8388608.0; /* XXX This sadly ignores Inf/NaN issues */ if (e == 0) e = -126; else { x += 1.0; e -= 127; } x = ldexp(x, e); if (s) x = -x; return PyFloat_FromDouble(x); } static PyObject * unpack_double(const char *p, /* Where the high order byte is */ int incr) /* 1 for big-endian; -1 for little-endian */ { int s; int e; long fhi, flo; double x; /* First byte */ s = (*p>>7) & 1; e = (*p & 0x7F) << 4; p += incr; /* Second byte */ e |= (*p>>4) & 0xF; fhi = (*p & 0xF) << 24; p += incr; /* Third byte */ fhi |= (*p & 0xFF) << 16; p += incr; /* Fourth byte */ fhi |= (*p & 0xFF) << 8; p += incr; /* Fifth byte */ fhi |= *p & 0xFF; p += incr; /* Sixth byte */ flo = (*p & 0xFF) << 16; p += incr; /* Seventh byte */ flo |= (*p & 0xFF) << 8; p += incr; /* Eighth byte */ flo |= *p & 0xFF; p += incr; x = (double)fhi + (double)flo / 16777216.0; /* 2**24 */ x /= 268435456.0; /* 2**28 */ /* XXX This sadly ignores Inf/NaN */ if (e == 0) e = -1022; else { x += 1.0; e -= 1023; } x = ldexp(x, e); if (s) x = -x; return PyFloat_FromDouble(x); } /* converter funcitons */ typedef PyObject* (*UnpackFunction)(const char *); typedef PyObject* (*UnpackFunctionInt)(const char *, int size); typedef struct { UnpackFunction unpack_char; UnpackFunction unpack_float; UnpackFunction unpack_double; UnpackFunctionInt unpack_signed; UnpackFunctionInt unpack_unsigned; } UnpackFunctionTable; static PyObject * nu_char(const char *p) { return PyString_FromStringAndSize(p, 1); } static PyObject * bu_int(const char *p, int size) { long x = 0; int i = size; do { x = (x<<8) | (*p++ & 0xFF); } while (--i > 0); i = 8*(sizeof(long) - size); if (i) { x <<= i; x >>= i; } return PyInt_FromLong(x); } static PyObject * bu_uint(const char *p, int size) { unsigned long x = 0; int i = size; do { x = (x<<8) | (*p++ & 0xFF); } while (--i > 0); if (size >= 4) return PyLong_FromUnsignedLong(x); else return PyInt_FromLong((long)x); } static PyObject * bu_float(const char *p) { return unpack_float(p, 1); } static PyObject * bu_double(const char *p) { return unpack_double(p, 1); } UnpackFunctionTable bigendian_table = { nu_char, bu_float, bu_double, bu_int, bu_uint }; static PyObject * lu_int(const char *p, int size) { long x = 0; int i = size; do { x = (x<<8) | (p[--i] & 0xFF); } while (i > 0); i = 8*(sizeof(long) - size); if (i) { x <<= i; x >>= i; } return PyInt_FromLong(x); } static PyObject * lu_uint(const char *p, int size) { unsigned long x = 0; int i = size; do { x = (x<<8) | (p[--i] & 0xFF); } while (i > 0); if (size >= 4) return PyLong_FromUnsignedLong(x); else return PyInt_FromLong((long)x); } static PyObject * lu_float(const char *p) { return unpack_float(p+3, -1); } static PyObject * lu_double(const char *p) { return unpack_double(p+7, -1); } UnpackFunctionTable littleendian_table = { nu_char, lu_float, lu_double, lu_int, lu_uint }; typedef struct { PyObject_HEAD PyObject * stream; int byte_order; int int_size; int string_pos; } BinaryInputObject; enum ByteOrder { LittleEndian, BigEndian }; static void binfile_dealloc(BinaryInputObject * self) { Py_DECREF(self->stream); PyObject_Del(self); } static PyObject * binfile_repr(FilterObject * self) { char buf[1000]; PyObject * streamrepr; streamrepr = PyObject_Repr(self->stream); if (!streamrepr) return NULL; sprintf(buf, "", PyString_AsString(streamrepr)); Py_DECREF(streamrepr); return PyString_FromString(buf); } static int calcsize(BinaryInputObject * self, const char * format) { int size = 0; const char * p; p = format; while (*p) { switch (*p) { case 'b': case 'B': case 'c': case 'x': size += 1; break; case 'h': case 'H': size += 2; break; case 'l': case 'L': case 'f': size += 4; break; case 'd': size += 8; break; case 'i': case 'I': size += self->int_size; break; default: break; } p += 1; } return size; } static char * read_data(BinaryInputObject * self, int size) { char * result; if (PyString_Check(self->stream)) { int length = PyString_Size(self->stream); if (self->string_pos + size <= length) { result = PyString_AsString(self->stream) + self->string_pos; self->string_pos += size; } else { PyErr_Format(PyExc_ValueError, "Only %d bytes left, need %d", length - self->string_pos, size); result = NULL; } } else { PyErr_SetString(PyExc_TypeError, "Only strings as data source supported"); result = NULL; } return result; } static PyObject * binfile_readstruct(BinaryInputObject * self, PyObject * args) { UnpackFunctionTable * table; int size; PyObject * list = NULL, *v = NULL; const char * format; const char * p; char * buffer; char * data; if (!PyArg_ParseTuple(args, "s", &format)) return NULL; if (self->byte_order == LittleEndian) table = &littleendian_table; else table = &bigendian_table; size = calcsize(self, format); buffer = read_data(self, size); if (!buffer) return NULL; list = PyList_New(0); if (!list) return NULL; data = buffer; p = format; while (*p) { v = NULL; switch (*p) { case 'c': v = table->unpack_char(data); data += 1; break; case 'b': v = table->unpack_signed(data, 1); data += 1; break; case 'B': v = table->unpack_unsigned(data, 1); data += 1; break; case 'h': v = table->unpack_signed(data, 2); data += 2; break; case 'H': v = table->unpack_unsigned(data, 2); data += 2; break; case 'l': v = table->unpack_signed(data, 4); data += 4; break; case 'L': v = table->unpack_unsigned(data, 4); data += 4; break; case 'f': v = table->unpack_float(data); data += 4; break; case 'd': v = table->unpack_double(data); data += 8; break; case 'i': v = table->unpack_signed(data, self->int_size); data += self->int_size; break; case 'I': v = table->unpack_unsigned(data, self->int_size); data += self->int_size; break; case 'x': data += 1; default: continue; } p += 1; if (!v) goto error; if (PyList_Append(list, v) < 0) goto error; Py_DECREF(v); } v = PyList_AsTuple(list); Py_DECREF(list); return v; error: Py_XDECREF(v); Py_XDECREF(list); return NULL; } static PyObject * binfile_subfile(BinaryInputObject * self, PyObject * args) { int length; int left; PyObject * string, *binfile; if (!PyArg_ParseTuple(args, "i", &length)) return NULL; left = PyString_Size(self->stream) - self->string_pos; if (left < length) { PyErr_Format(PyExc_ValueError, "Only %d bytes left, need %d", left, length); return NULL; } string = PyString_FromStringAndSize(PyString_AsString(self->stream) + self->string_pos, length); if (!string) return NULL; binfile = BinFile_FromStream(string, self->byte_order, self->int_size); Py_DECREF(string); if (binfile) self->string_pos += length; return binfile; } static PyObject * binfile_read(BinaryInputObject * self, PyObject * args) { int length; int left; PyObject * string; if (!PyArg_ParseTuple(args, "i", &length)) return NULL; left = PyString_Size(self->stream) - self->string_pos; if (left < length) { PyErr_Format(PyExc_ValueError, "Only %d bytes left, need %d", left, length); return NULL; } string = PyString_FromStringAndSize(PyString_AsString(self->stream) + self->string_pos, length); if (string) self->string_pos += length; return string; } static PyObject * binfile_seek(BinaryInputObject * self, PyObject * args) { int pos; if (!PyArg_ParseTuple(args, "i", &pos)) return NULL; if (pos >= 0 && pos <= PyString_Size(self->stream)) { self->string_pos = pos; } else { PyErr_Format(PyExc_ValueError, "Can't seek to %d", pos); return NULL; } Py_INCREF(Py_None); return Py_None; } static PyObject * binfile_tell(BinaryInputObject * self, PyObject * args) { return PyInt_FromLong(self->string_pos); } #define OFF(x) offsetof(BinaryInputObject, x) static struct memberlist binfile_memberlist[] = { {"stream", T_OBJECT, OFF(stream), RO}, {NULL} }; static struct PyMethodDef binfile_methods[] = { {"read_struct", (PyCFunction)binfile_readstruct, 1}, {"read", (PyCFunction)binfile_read, 1}, {"subfile", (PyCFunction)binfile_subfile, 1}, {"seek", (PyCFunction)binfile_seek, 1}, {"tell", (PyCFunction)binfile_tell, 1}, {NULL, NULL} }; static PyObject * binfile_getattr(PyObject * self, char * name) { PyObject * result; result = Py_FindMethod(binfile_methods, self, name); if (result != NULL) return result; PyErr_Clear(); return PyMember_Get((char *)self, binfile_memberlist, name); } static int binfile_setattr(PyObject * self, char * name, PyObject * v) { if (v == NULL) { PyErr_SetString(PyExc_AttributeError, "can't delete object attributes"); return -1; } return PyMember_Set((char *)self, binfile_memberlist, name, v); } staticforward PyTypeObject BinaryInputType; statichere PyTypeObject BinaryInputType = { PyObject_HEAD_INIT(NULL) 0, "binaryinput", sizeof(BinaryInputObject), 0, (destructor)binfile_dealloc, /*tp_dealloc*/ (printfunc)0, /*tp_print*/ binfile_getattr, /*tp_getattr*/ binfile_setattr, /*tp_setattr*/ (cmpfunc)0, /*tp_compare*/ (reprfunc)binfile_repr, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ }; static PyObject * BinFile_FromStream(PyObject * stream, int byte_order, int int_size) { BinaryInputObject * binfile; if (byte_order != LittleEndian && byte_order != BigEndian) { PyErr_Format(PyExc_ValueError, "Invalid byte order %d", byte_order); return NULL; } if (int_size != 2 && int_size != 4) { PyErr_Format(PyExc_ValueError, "Invalid int size %d, must be 2 or 4", int_size); return NULL; } if (!PyString_Check(stream)) { PyErr_SetString(PyExc_TypeError, "Only strings supported as input"); return NULL; } BinaryInputType.ob_type = &PyType_Type; binfile = PyObject_New(BinaryInputObject, &BinaryInputType); if (!binfile) return NULL; binfile->stream = stream; Py_INCREF(binfile->stream); binfile->int_size = int_size; binfile->byte_order = byte_order; binfile->string_pos = 0; return (PyObject*)binfile; } PyObject * BinFile_New(PyObject * self, PyObject * args) { PyObject * stream; int byte_order, int_size; if (!PyArg_ParseTuple(args, "Oii", &stream, &byte_order, &int_size)) return NULL; return BinFile_FromStream(stream, byte_order, int_size); } UniConvertor-1.1.4/src/modules/filter/binfile.h0000664000076400007640000000224311060500611020142 0ustar igorigor#ifndef BINFILE_H #define BINFILE_H /* * Copyright (C) 1998, 1999 by Bernhard Herzog. * * All Rights Reserved * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear in * supporting documentation, and that the name of the author not be * used in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "filterobj.h" PyObject * BinFile_New(PyObject * self, PyObject * args); #endif /* BINFILE_H */ UniConvertor-1.1.4/src/modules/filter/ascii85filter.h0000664000076400007640000000237311060500611021211 0ustar igorigor#ifndef ASCII85FILTER_H #define ASCII85FILTER_H /* * Copyright (C) 2000 by Bernhard Herzog. * * All Rights Reserved * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear in * supporting documentation, and that the name of the author not be * used in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "filterobj.h" PyObject * Filter_ASCII85Encode(PyObject * self, PyObject * args); PyObject * Filter_ASCII85Decode(PyObject * self, PyObject * args); #endif /* ASCII85FILTER_H */ UniConvertor-1.1.4/src/modules/filter/nullfilter.c0000664000076400007640000000375511060500611020716 0ustar igorigor/* * Copyright (C) 1998, 1999 by Bernhard Herzog. * * All Rights Reserved * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear in * supporting documentation, and that the name of the author not be * used in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "nullfilter.h" /* * NullEncode filter. Just copy the data to the target. * NullDecode filter. Just copy the data from the source. * * These filter are only useful for debugging/testing. */ static size_t write_null(void* clientdata, PyObject * target, const char * buf, size_t length) { return Filter_Write(target, buf, length); } PyObject * Filter_NullEncode(PyObject * self, PyObject * args) { PyObject * target; if (!PyArg_ParseTuple(args, "O", &target)) return NULL; return Filter_NewEncoder(target, "NullEncode", 0, write_null, NULL, NULL, NULL); } static size_t read_null(void* clientdata, PyObject * source, char * buf, size_t length) { return Filter_Read(source, buf, length); } PyObject * Filter_NullDecode(PyObject * self, PyObject * args) { PyObject * source; if (!PyArg_ParseTuple(args, "O", &source)) return NULL; return Filter_NewDecoder(source, "NullDecode", 0, read_null, NULL, NULL, NULL); } UniConvertor-1.1.4/src/modules/filter/nullfilter.h0000664000076400007640000000236311060500611020715 0ustar igorigor#ifndef NULLFILTER_H #define NULLFILTER_H /* * Copyright (C) 1998, 1999 by Bernhard Herzog. * * All Rights Reserved * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear in * supporting documentation, and that the name of the author not be * used in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "filterobj.h" PyObject * Filter_NullEncode(PyObject * self, PyObject * args); PyObject * Filter_NullDecode(PyObject * self, PyObject * args); #endif /* NULLFILTER_H */ UniConvertor-1.1.4/src/modules/filter/README0000664000076400007640000000051111060500611017235 0ustar igorigor Streamfilter module =================== This module provides a variety of file like objects that encode or decode data in various formats. The stream filters are modelled after the filters in the PostScript language. License: -------- GNU LIBRARY GENERAL PUBLIC LICENSE Version 2 Copyright (C) 1998, 1999 by Bernhard Herzog.UniConvertor-1.1.4/src/modules/filter/streamfilter.c0000664000076400007640000000552611060500611021235 0ustar igorigor/* * Copyright (C) 1998, 1999, 2000, 2001 by Bernhard Herzog. * * All Rights Reserved * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear in * supporting documentation, and that the name of the author not be * used in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "filterobj.h" #include "linefilter.h" #include "subfilefilter.h" #include "base64filter.h" #include "stringfilter.h" #include "nullfilter.h" #include "hexfilter.h" /* hack to deselect the filters not needed by Sketch */ #ifdef ALL_FILTERS #include "zlibfilter.h" #include "ascii85filter.h" #endif #include "binfile.h" static PyMethodDef filter_functions[] = { {"Base64Decode", Filter_Base64Decode, METH_VARARGS}, {"Base64Encode", Filter_Base64Encode, METH_VARARGS}, {"LineDecode", Filter_LineDecode, METH_VARARGS}, {"SubFileDecode", Filter_SubFileDecode, METH_VARARGS}, {"StringDecode", Filter_StringDecode, METH_VARARGS}, {"NullEncode", Filter_NullEncode, METH_VARARGS}, {"NullDecode", Filter_NullDecode, METH_VARARGS}, {"HexEncode", Filter_HexEncode, METH_VARARGS}, {"HexDecode", Filter_HexDecode, METH_VARARGS}, #ifdef ALL_FILTERS {"FlateDecode", Filter_FlateDecode, METH_VARARGS}, {"ASCII85Encode", Filter_ASCII85Encode, METH_VARARGS}, {"ASCII85Decode", Filter_ASCII85Decode, METH_VARARGS}, #endif {"BinaryInput", BinFile_New, METH_VARARGS}, {NULL, NULL} }; static Filter_Functions functions = { /* internal methods */ _Filter_Underflow, _Filter_Overflow, /* decoder methods */ Filter_Read, Filter_ReadToChar, Filter_GetLine, Filter_Ungetc, /* endcoder methods */ Filter_Write, Filter_Flush, /* common filter methods */ Filter_Close }; DL_EXPORT(void) initstreamfilter(void) { PyObject * d, *m, *v; FilterType.ob_type = &PyType_Type; m = Py_InitModule("streamfilter", filter_functions); d = PyModule_GetDict(m); PyDict_SetItemString(d, "FilterType", (PyObject*)(&FilterType)); v = PyCObject_FromVoidPtr(&functions, NULL); PyDict_SetItemString(d, "Filter_Functions", v); Py_DECREF(v); } UniConvertor-1.1.4/src/modules/filter/filterobj.c0000664000076400007640000005122511060500611020511 0ustar igorigor/* * Copyright (C) 1998, 1999, 2001, 2006 by Bernhard Herzog. * * All Rights Reserved * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear in * supporting documentation, and that the name of the author not be * used in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "filterobj.h" #include /* * Semantics of the methods read, write and close * ============================================== * * * size_t write(void *, PyObject * target, const char * buf, size_t length) * * When the buffer is full and data is written to the filter or when the * encode filter is flushed, the buffer data has to be encoded and to be * written to the target stream. The filter object will call the write * method to achieve this. * * write is supposed to consume at least 1 byte of the data at buf and * at most length bytes. It returns the number of bytes it actually read * from buffer. * * If successful the return value must be at least 1 and at most length. * 0 indicates indicates an error, that is, a problem with the * underlying data target or an error in the encoder. The filter assumes * that a Python exception has been raised. * * * * size_t read(void *, PyObject * source, char * buf, size_t length) * * When the buffer is empty and data is read from the decode filter, the * filter calls read to read data from the data source, decode it and * store into the buffer. * * read is supposed to put at least one byte into the buffer beginning * at buf and at most length bytes. It returns the number of bytes * actually written to buf. * * If successful, the return value must be at least 1 and at most * length. If unsuccessful, a return value of 0 indicates either EOF in * the data source or end of data or an error. End of data means that * the decoder cannot deliver more data for decoder specific reasons, * even if more data is available in the source. The filter assumes that * a Python exception has been raised in case of an error. * * For compatibility, an EOF or EOD (end of data) condition is not * converted into an exception. * * * int close(void*, PyObject * target) * * Called when the filter is being closed. Any data still contained in * filter internal state should be written to target. target should not * be closed. Return 0 on success and EOF on error (and set an * exception). */ #define Filter_IsEncoder(filter) ((filter)->write != NULL) #define Filter_IsDecoder(filter) ((filter)->read != NULL) #define Filter_CheckEncodeState(filter) \ (((filter)->flags & (FILTER_CLOSED | FILTER_EOF | FILTER_BAD))\ ? setexc(filter) : 1) #define Filter_CheckDecodeState(filter) \ (((filter)->flags & (FILTER_CLOSED | FILTER_BAD))\ ? setexc(filter) : 1) static int setexc(FilterObject * self) { if (self->flags & FILTER_BAD) { PyErr_Format(PyExc_IOError, "filter %s in bad state", PyString_AsString(self->filtername)); } else if (self->flags & FILTER_CLOSED) { PyErr_Format(PyExc_IOError, "filter %s already closed", PyString_AsString(self->filtername)); } else if (self->flags & FILTER_EOF) { PyErr_Format(PyExc_EOFError, "filter %s reached EOF", PyString_AsString(self->filtername)); } return 0; } #define FILTER_BUFSIZE (BUFSIZ - 1) static FilterObject * new_filter(PyObject * stream, const char * name, int flags, filter_close_proc close, filter_dealloc_proc dealloc, void * client_data) { FilterObject * self; self = PyObject_New(FilterObject, &FilterType); if (!self) return NULL; self->buffer = PyMem_Malloc(FILTER_BUFSIZE + 1); if (!self->buffer) { error: PyObject_Del(self); PyErr_NoMemory(); if (dealloc) dealloc(client_data); return NULL; } self->filtername = PyString_FromString(name); if (!self->filtername) { PyMem_Free(self->buffer); goto error; } self->current = self->base = self->buffer + 1; self->buffer_end = self->base + FILTER_BUFSIZE; self->end = self->current; self->stream = stream; Py_INCREF(self->stream); self->client_data = client_data; self->dealloc = dealloc; self->close = close; self->write = NULL; self->read = NULL; self->flags = flags; self->streampos = 0; return self; } PyObject * Filter_NewEncoder(PyObject * target, const char * name, int flags, filter_write_proc write, filter_close_proc close, filter_dealloc_proc dealloc, void * client_data) { FilterObject * self; if (!PyFile_Check(target) && !Filter_Check(target)) { PyErr_SetString(PyExc_TypeError, "target must be file or filter"); return NULL; } self = new_filter(target, name, flags, close, dealloc, client_data); if (!self) return NULL; self->write = write; self->end = self->buffer_end; return (PyObject*)self; } PyObject * Filter_NewDecoder(PyObject * source, const char * name, int flags, filter_read_proc read, filter_close_proc close, filter_dealloc_proc dealloc, void * client_data) { FilterObject * self; /* if (!PyFile_Check(source) && !Filter_Check(source)) { PyErr_SetString(PyExc_TypeError, "source must be file or filter"); return NULL; } */ self = new_filter(source, name, flags, close, dealloc, client_data); if (!self) return NULL; self->read = read; self->end = self->current; return (PyObject*)self; } static void filter_dealloc(FilterObject * self) { Filter_Close((PyObject*)self); if (self->dealloc) self->dealloc(self->client_data); Py_DECREF(self->filtername); Py_DECREF(self->stream); PyMem_Free(self->buffer); PyObject_Del(self); } static PyObject * filter_repr(FilterObject * self) { char buf[1000]; PyObject * streamrepr; streamrepr = PyObject_Repr(self->stream); if (!streamrepr) return NULL; sprintf(buf, "", PyString_AsString(self->filtername), Filter_IsEncoder(self) ? "writing to" : "reading from", PyString_AsString(streamrepr)); Py_DECREF(streamrepr); return PyString_FromString(buf); } int _Filter_Overflow(FilterObject * self, int c) { if (Filter_Flush((PyObject*)self, 1) != EOF) { *self->current++ = c; return c; } return EOF; } static int _Filter_Uflow(FilterObject * self) { if (Filter_IsDecoder(self)) { size_t result; if (!Filter_CheckDecodeState(self) || self->flags & FILTER_EOF) return EOF; if (self->current == self->end) { result = self->read(self->client_data, self->stream, self->base, self->buffer_end - self->base); if (result == 0) { if (PyErr_Occurred()) { self->flags |= FILTER_BAD; } else { self->flags |= FILTER_EOF; } return EOF; } self->current = self->base; self->end = self->current + result; self->streampos += result; } return *self->current & 0377; } return EOF; } int _Filter_Underflow(FilterObject * self) { int c; c = _Filter_Uflow(self); if (c != EOF) self->current++; return c; } int Filter_Flush(PyObject * filter, int flush_target) { FilterObject * self; if (!Filter_Check(filter)) { PyErr_SetString(PyExc_TypeError, "FilterObject expected"); return EOF; } self = (FilterObject*)filter; if (Filter_IsEncoder(self)) { size_t result, length; if (!Filter_CheckEncodeState(self)) return EOF; length = self->current - self->base; while (length > 0) { result = self->write(self->client_data, self->stream, self->current - length, length); if (result == 0) { self->flags |= FILTER_BAD; return EOF; } length -= result; } self->current = self->base; /* XXX flush target even if error occurred? */ if (flush_target) { if (PyFile_Check(self->stream)) { int fflush_result; Py_BEGIN_ALLOW_THREADS fflush_result = fflush(PyFile_AsFile(self->stream)); Py_END_ALLOW_THREADS if (result < 0) { PyErr_SetFromErrno(PyExc_IOError); return EOF; } return 0; } else if (Filter_Check(self->stream)) return Filter_Flush(self->stream, flush_target); } return 0; } else { PyErr_SetString(PyExc_TypeError, "flush requires an encode filter"); } return EOF; } int Filter_Close(PyObject * filter) { FilterObject * self; int result = 0; if (!Filter_Check(filter)) { PyErr_SetString(PyExc_TypeError, "FilterObject expected"); return EOF; } self = (FilterObject*)filter; if (self->flags & FILTER_CLOSED) /* filter is already closed, do nothing */ return 0; if (Filter_IsEncoder(self) && Filter_Flush((PyObject*)self, 1) < 0) return EOF; if (self->close) result = self->close(self->client_data, self->stream); self->flags |= FILTER_CLOSED; return result; } size_t Filter_Read(PyObject * filter, char * buffer, size_t length) { if (length <= 0) return 0; if (PyFile_Check(filter)) { FILE * file = PyFile_AsFile(filter); size_t result; Py_BEGIN_ALLOW_THREADS result = fread(buffer, 1, length, file); Py_END_ALLOW_THREADS if (result == 0) { if (ferror(file)) { PyErr_SetFromErrno(PyExc_IOError); } return 0; } return result; } else if (Filter_Check(filter)) { size_t to_do = length; size_t count; char * dest = buffer; FilterObject * self = (FilterObject*)filter; if (!Filter_CheckDecodeState(self) || self->flags & FILTER_EOF) return 0; for (;;) { count = self->end - self->current; if (count > to_do) count = to_do; if (count > 0) { memcpy(dest, self->current, count); self->current += count; dest += count; to_do -= count; } if (to_do == 0 || _Filter_Uflow(self) == EOF) break; } if (PyErr_Occurred()) return 0; return length - to_do; } PyErr_SetString(PyExc_TypeError, "filter may be FileObject or FilterObject"); return 0; } size_t Filter_ReadToChar(PyObject * filter, char * buffer, size_t length, int endchar) { if (length <= 0) return 0; if (Filter_Check(filter)) { FilterObject * self = (FilterObject*)filter; int c; char * dest = buffer, *end = buffer + length; for (;;) { c = Filter_GETC(self); if (c == EOF) break; *dest++ = c; if (c == endchar || dest == end) break; } if ((c == EOF && dest == buffer) || PyErr_Occurred()) return 0; return dest - buffer; } else if (PyFile_Check(filter)) { FILE* file = PyFile_AsFile(filter); int c; char * dest = buffer, *end = buffer + length; Py_BEGIN_ALLOW_THREADS for (;;) { c = getc(file); if (c == EOF) break; *dest++ = c; if (c == endchar || dest == end) break; } Py_END_ALLOW_THREADS if (c == EOF && dest == buffer) { if (ferror(file)) PyErr_SetFromErrno(PyExc_IOError); return 0; } return dest - buffer; } PyErr_SetString(PyExc_TypeError, "filter must be FilterObject or FileObject"); return 0; } #define BUF(v) PyString_AS_STRING((PyStringObject *)v) PyObject * Filter_GetLine(PyObject * filter, int n) { int n1, n2; size_t charsread; char * buf, *end; PyObject *v; if (!Filter_Check(filter)) { PyErr_SetString(PyExc_TypeError, "FilterObject expected"); return NULL; } n2 = n > 0 ? n : 100; v = PyString_FromStringAndSize((char *)NULL, n2); if (v == NULL) return NULL; buf = BUF(v); end = buf + n2; for (;;) { charsread = Filter_ReadToChar(filter, buf, n2, '\n'); if (charsread == 0) { if (PyErr_CheckSignals()) { Py_DECREF(v); return NULL; } if (n < 0 && buf == BUF(v)) { Py_DECREF(v); PyErr_SetString(PyExc_EOFError, "EOF when reading a line"); return NULL; } break; } buf += charsread; if (buf[-1] == '\n') { if (n < 0) buf--; break; } if (buf == end) { if (n > 0) break; n1 = n2; n2 += 1000; if (_PyString_Resize(&v, n2) < 0) return NULL; buf = BUF(v) + n1; end = BUF(v) + n2; } } n1 = buf - BUF(v); if (n1 != n2) _PyString_Resize(&v, n1); return v; } int Filter_Ungetc(PyObject * filter, int c) { if (Filter_Check(filter)) { FilterObject * self = (FilterObject*)filter; if (self->current >= self->base) { self->current -= 1; *(self->current) = c; } } else { PyErr_SetString(PyExc_TypeError, "FilterObject required"); return -1; } return 0; } int Filter_Write(PyObject * filter, const char * buffer, size_t length) { if (length <= 0) return 0; if (PyFile_Check(filter)) { FILE * file = PyFile_AsFile(filter); int result; Py_BEGIN_ALLOW_THREADS result = fwrite(buffer, 1, length, file); Py_END_ALLOW_THREADS if (result < length) { if (ferror(file)) { PyErr_SetFromErrno(PyExc_IOError); return EOF; } } return result; } else if (Filter_Check(filter)) { size_t to_do = length; size_t count; const unsigned char * src = (const unsigned char*)buffer; FilterObject * self = (FilterObject*)filter; for (;;) { count = self->end - self->current; if (count > to_do) count = to_do; if (count > 0) { memcpy(self->current, src, count); self->current += count; src += count; to_do -= count; } if (to_do == 0 || _Filter_Overflow(self, *src++) == EOF) break; to_do -= 1; } if (to_do != 0 || PyErr_Occurred()) return EOF; return length - to_do; } PyErr_SetString(PyExc_TypeError, "filter may be FileObject or FilterObject"); return EOF; } static PyObject * filter_read(PyObject * self, PyObject * args) { int length; size_t read; PyObject * string; if (!PyArg_ParseTuple(args, "i", &length)) return NULL; string = PyString_FromStringAndSize((const char*)NULL, length); if (!string) return NULL; read = Filter_Read(self, PyString_AsString(string), length); if (read == 0) { Py_DECREF(string); if (PyErr_Occurred()) return NULL; return PyString_FromString(""); } if (read < length) { if (_PyString_Resize(&string, read) < 0) return NULL; } return string; } static PyObject * filter_readline(PyObject * self, PyObject * args) { int length = -1; if (!PyArg_ParseTuple(args, "|i", &length)) return NULL; if (length == 0) return PyString_FromString(""); if (length < 0) length = 0; return Filter_GetLine(self, length); } #if BUFSIZ < 8192 #define SMALLCHUNK 8192 #else #define SMALLCHUNK BUFSIZ #endif static PyObject * filter_readlines(PyObject *self, PyObject *args) { long sizehint = 0; PyObject *list; PyObject *line; char small_buffer[SMALLCHUNK]; char *buffer = small_buffer; size_t buffersize = SMALLCHUNK; PyObject *big_buffer = NULL; size_t nfilled = 0; size_t nread; size_t totalread = 0; char *p, *q, *end; int err; if (!PyArg_ParseTuple(args, "|l", &sizehint)) return NULL; if ((list = PyList_New(0)) == NULL) return NULL; for (;;) { nread = Filter_Read(self, buffer + nfilled, buffersize - nfilled); if (nread == 0) { sizehint = 0; if (!PyErr_Occurred()) break; error: Py_DECREF(list); list = NULL; goto cleanup; } totalread += nread; p = memchr(buffer+nfilled, '\n', nread); if (p == NULL) { /* Need a larger buffer to fit this line */ nfilled += nread; buffersize *= 2; if (big_buffer == NULL) { /* Create the big buffer */ big_buffer = PyString_FromStringAndSize(NULL, buffersize); if (big_buffer == NULL) goto error; buffer = PyString_AS_STRING(big_buffer); memcpy(buffer, small_buffer, nfilled); } else { /* Grow the big buffer */ if (_PyString_Resize(&big_buffer, buffersize) < 0) goto error; buffer = PyString_AS_STRING(big_buffer); } continue; } end = buffer+nfilled+nread; q = buffer; do { /* Process complete lines */ p++; line = PyString_FromStringAndSize(q, p-q); if (line == NULL) goto error; err = PyList_Append(list, line); Py_DECREF(line); if (err != 0) goto error; q = p; p = memchr(q, '\n', end-q); } while (p != NULL); /* Move the remaining incomplete line to the start */ nfilled = end-q; memmove(buffer, q, nfilled); if (sizehint > 0) if (totalread >= (size_t)sizehint) break; } if (nfilled != 0) { /* Partial last line */ line = PyString_FromStringAndSize(buffer, nfilled); if (line == NULL) goto error; if (sizehint > 0) { /* Need to complete the last line */ PyObject *rest = Filter_GetLine(self, 0); if (rest == NULL) { Py_DECREF(line); goto error; } PyString_Concat(&line, rest); Py_DECREF(rest); if (line == NULL) goto error; } err = PyList_Append(list, line); Py_DECREF(line); if (err != 0) goto error; } cleanup: if (big_buffer) { Py_DECREF(big_buffer); } return list; } static PyObject * filter_write(PyObject * self, PyObject * args) { const char * buffer; int length; if (!PyArg_ParseTuple(args, "s#", &buffer, &length)) return NULL; if (Filter_Write(self, buffer, length) == EOF) return NULL; Py_INCREF(Py_None); return Py_None; } static PyObject * filter_flush(PyObject * self, PyObject * args) { int flush_target = 1; if (!PyArg_ParseTuple(args, "|i", &flush_target)) return NULL; if (Filter_Flush(self, flush_target) < 0) return NULL; Py_INCREF(Py_None); return Py_None; } static PyObject * filter_close(PyObject * self, PyObject * args) { if (!PyArg_ParseTuple(args, "")) return NULL; if (Filter_Close(self) < 0) return NULL; Py_INCREF(Py_None); return Py_None; } static PyObject * filter_seek(FilterObject * self, PyObject * args) { int pos; long cur_pos, offset; if (!PyArg_ParseTuple(args, "i", &pos)) return NULL; cur_pos = self->streampos - (self->end - self->current); offset = pos - cur_pos; if (self->base - self->current <= offset && offset < self->end - self->current) { self->current += offset; /* reset the EOF flag if we're not at the end */ if (self->current < self->end) self->flags &= ~FILTER_EOF; } else { PyErr_SetString(PyExc_IOError, "cannot seek to specified position"); return NULL; } Py_INCREF(Py_None); return Py_None; } static PyObject * filter_tell(FilterObject * self, PyObject * args) { long cur_pos; if (!PyArg_ParseTuple(args, "")) return NULL; cur_pos = self->streampos - (self->end - self->current); return PyInt_FromLong(cur_pos); } #define OFF(x) offsetof(FilterObject, x) static struct memberlist filter_memberlist[] = { {"stream", T_OBJECT, OFF(stream), RO}, {"source", T_OBJECT, OFF(stream), RO}, {"target", T_OBJECT, OFF(stream), RO}, {NULL} }; static struct PyMethodDef filter_methods[] = { {"read", (PyCFunction)filter_read, 1}, {"write", (PyCFunction)filter_write, 1}, {"readline", (PyCFunction)filter_readline, 1}, {"readlines", (PyCFunction)filter_readlines, 1}, {"flush", (PyCFunction)filter_flush, 1}, {"seek", (PyCFunction)filter_seek, 1}, {"tell", (PyCFunction)filter_tell, 1}, {"close", (PyCFunction)filter_close, 1}, {NULL, NULL} }; static PyObject * filter_getattr(PyObject * self, char * name) { PyObject * result; result = Py_FindMethod(filter_methods, self, name); if (result != NULL) return result; PyErr_Clear(); return PyMember_Get((char *)self, filter_memberlist, name); } static int filter_setattr(PyObject * self, char * name, PyObject * v) { if (v == NULL) { PyErr_SetString(PyExc_AttributeError, "can't delete object attributes"); return -1; } return PyMember_Set((char *)self, filter_memberlist, name, v); } PyTypeObject FilterType = { PyObject_HEAD_INIT(NULL) 0, "filter", sizeof(FilterObject), 0, (destructor)filter_dealloc, /*tp_dealloc*/ (printfunc)0, /*tp_print*/ filter_getattr, /*tp_getattr*/ filter_setattr, /*tp_setattr*/ (cmpfunc)0, /*tp_compare*/ (reprfunc)filter_repr, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ }; UniConvertor-1.1.4/src/modules/filter/filterobj.h0000664000076400007640000001066711060500611020523 0ustar igorigor/* * Copyright (C) 1998, 1999 by Bernhard Herzog. * * All Rights Reserved * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear in * supporting documentation, and that the name of the author not be * used in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef FILTEROBJ_H #define FILTEROBJ_H #include #if defined(__cplusplus) extern "C" { #endif typedef size_t (*filter_read_proc)(void *, PyObject * source, char * buffer, size_t length); typedef size_t (*filter_write_proc)(void *, PyObject * target, const char * buffer, size_t length); typedef int (*filter_close_proc)(void *, PyObject * target); typedef void (*filter_dealloc_proc)(void * client_data); PyObject * Filter_NewEncoder(PyObject * target, const char * filtername, int flags, filter_write_proc, filter_close_proc, filter_dealloc_proc, void * client_data); PyObject * Filter_NewDecoder(PyObject * source, const char * filtername, int flags, filter_read_proc, filter_close_proc, filter_dealloc_proc, void * client_data); /* decoder methods */ size_t Filter_Read(PyObject * filter, char * buffer, size_t length); size_t Filter_ReadToChar(PyObject * filter, char * buffer, size_t length, int character); PyObject * Filter_GetLine(PyObject * filter, int); int Filter_Ungetc(PyObject * filter, int); /* encoder methods */ int Filter_Write(PyObject * filter, const char * buffer, size_t length); int Filter_Flush(PyObject * filter, int flush_target); /* common filter methods */ int Filter_Close(PyObject * filter); #define FILTER_CLOSED 0x0001 #define FILTER_EOF 0x0002 #define FILTER_BAD 0x0004 #define FILTER_CLOSE_STREAM 0x0100 typedef struct { PyObject_HEAD char * buffer; char * buffer_end; char * current; char * end; char * base; int flags; long streampos; PyObject * stream; /* source or target */ PyObject * filtername; filter_read_proc read; filter_write_proc write; filter_close_proc close; filter_dealloc_proc dealloc; void * client_data; } FilterObject; extern DL_IMPORT(PyTypeObject) FilterType; #define Filter_Check(op) ((op)->ob_type == &FilterType) int _Filter_Underflow(FilterObject*); int _Filter_Overflow(FilterObject*, int); #define __Filter_PUTC(filter, c, overflow)\ ((filter)->current >= (filter)->end \ ? (overflow)((filter),(unsigned char)(c))\ : (unsigned char)(*((filter)->current++) = (c))) #define __Filter_GETC(filter, underflow)\ ((filter)->current >= (filter)->end ? (underflow)(filter)\ : *(unsigned char*)((filter)->current++)) #define Filter_PUTC(filter, c) __Filter_PUTC((filter), (c), _Filter_Overflow) #define Filter_GETC(filter) __Filter_GETC((filter), _Filter_Underflow) typedef struct { int (*Filter_Underflow)(FilterObject*); int (*Filter_Overflow)(FilterObject*, int); /* decoder methods */ size_t (*Filter_Read)(PyObject * filter, char * buffer, size_t length); size_t (*Filter_ReadToChar)(PyObject * filter, char * buffer, size_t length, int character); PyObject * (*Filter_GetLine)(PyObject * filter, int); int (*Filter_Ungetc)(PyObject*, int); /* endcoder methods */ int (*Filter_Write)(PyObject * filter, const char * buffer, size_t length); int (*Filter_Flush)(PyObject * filter, int flush_target); /* common filter methods */ int (*Filter_Close)(PyObject * filter); } Filter_Functions; #define Filter_DL_PUTC(func, filter, c) \ __Filter_PUTC((filter), (c), ((func)->Filter_Overflow)) #define Filter_DL_GETC(func, filter) \ __Filter_GETC((filter), ((func)->Filter_Underflow)) #if defined(__cplusplus) } #endif #endif /* FILTEROBJ_H */ UniConvertor-1.1.4/src/modules/filter/subfilefilter.c0000664000076400007640000000716611060500611021375 0ustar igorigor/* * Copyright (C) 1998, 1999, 2006 by Bernhard Herzog. * * All Rights Reserved * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear in * supporting documentation, and that the name of the author not be * used in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "subfilefilter.h" typedef struct { char * delim; int chars_matched; int length; PyObject * delim_object; int shift[1]; } SubFileDecodeState; static size_t read_subfile(void* clientdata, PyObject * source, char * buf, size_t length) { char * data = buf; size_t bytesread = 0, datalen = 0; int *shift; SubFileDecodeState *state = (SubFileDecodeState*)clientdata; if (!state->delim) /* eof data */ return 0; if (state->chars_matched) { memcpy(data, state->delim, state->chars_matched); datalen = state->chars_matched; } while (datalen < state->length) { bytesread = Filter_ReadToChar(source, buf + datalen, length - datalen, state->delim[state->length - 1]); if (bytesread == 0) { if (PyErr_Occurred()) return 0; return datalen; } datalen += bytesread; } /* now, if the delimiter is contained in the the first datalen bytes * of the buffer, it is located at the end. */ data = buf + datalen; if (!memcmp(data - state->length, state->delim, state->length)) { state->delim = NULL; return datalen - state->length; } for (shift = state->shift; *shift > 0; shift++) { if (!memcmp(data - *shift, state->delim, *shift)) { state->chars_matched = *shift; return datalen - *shift; } } state->chars_matched = 0; return datalen; } static void dealloc_subfile(void * clientdata) { SubFileDecodeState * state = (SubFileDecodeState*)clientdata; Py_DECREF(state->delim_object); PyMem_Free(state); } static void init_shift(SubFileDecodeState * state) { int i, j; int lastchar = state->delim[state->length - 1]; for (i = 0, j = 0; i < state->length; i++) if (state->delim[i] == lastchar) state->shift[j++] = i + 1; state->shift[j - 1] = -1; } PyObject * Filter_SubFileDecode(PyObject * self, PyObject * args) { PyObject * target; PyObject * delim_object; SubFileDecodeState * state; int length; if (!PyArg_ParseTuple(args, "OS", &target, &delim_object)) return NULL; length = PyString_Size(delim_object); if (length < 1) return PyErr_Format(PyExc_ValueError, "empty delimiter"); state = PyMem_Malloc(sizeof (SubFileDecodeState) + length * sizeof(int)); if (!state) return PyErr_NoMemory(); state->delim_object = delim_object; Py_INCREF(state->delim_object); state->delim = PyString_AsString(delim_object); state->chars_matched = 0; state->length = length; init_shift(state); return Filter_NewDecoder(target, "SubFileDecode", 0, read_subfile, NULL, dealloc_subfile, state); } UniConvertor-1.1.4/src/modules/filter/subfilefilter.h0000664000076400007640000000227611060500611021377 0ustar igorigor#ifndef SUBFILEFILTER_H #define SUBFILEFILTER_H /* * Copyright (C) 1998, 1999 by Bernhard Herzog. * * All Rights Reserved * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear in * supporting documentation, and that the name of the author not be * used in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "filterobj.h" PyObject * Filter_SubFileDecode(PyObject * self, PyObject * args); #endif /* SUBFILEFILTER_H */ UniConvertor-1.1.4/src/modules/filter/hexfilter.c0000664000076400007640000001136311060500611020522 0ustar igorigor/* * Copyright (C) 1998, 1999, 2002 by Bernhard Herzog. * * All Rights Reserved * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear in * supporting documentation, and that the name of the author not be * used in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "hexfilter.h" #define FBUFLEN 1024 /* * Hex Encode */ typedef struct { int column; int maxcolumn; } HexEncodeState; static size_t write_hex(void* clientdata, PyObject * target, const char * buf, size_t length) { static char * hexdigits = "0123456789abcdef"; HexEncodeState * state = (HexEncodeState*)clientdata; int i, todo = length, chunk, maxbinary; char encoded[FBUFLEN]; char * dest; /* Estimate the number of unencoded bytes that fit into the buffer * in encoded form. Each encoded line will have state->maxcolumn + 1 * characters which represent state->maxcolumn / 2 binary bytes. */ /* assume that state->maxcolumn is even */ maxbinary = (FBUFLEN / (state->maxcolumn + 1)) * (state->maxcolumn / 2); if (maxbinary == 0) /* for small FBUFLEN or large maxcolumn */ maxbinary = FBUFLEN / 3; if (todo > maxbinary) chunk = maxbinary; else chunk = todo; for (i = 0, dest = encoded; i < chunk; i++, buf++) { *dest++ = hexdigits[(int)((*buf & 0xF0) >> 4)]; *dest++ = hexdigits[(int)(*buf & 0x0F)]; state->column += 2; if (state->column >= state->maxcolumn) { *dest++ = '\n'; state->column = 0; } } if (Filter_Write(target, encoded, dest - encoded) < 0) return 0; return chunk; } static int close_hex(void * clientdata, PyObject * target) { if (((HexEncodeState*)clientdata)->column > 0) { if (Filter_Write(target, "\n", 1) == 0) return EOF; } return 0; } PyObject * Filter_HexEncode(PyObject * self, PyObject * args) { PyObject * target; HexEncodeState * state; int maxcolumn = 72; if (!PyArg_ParseTuple(args, "O|i", &target, &maxcolumn)) return NULL; state = malloc(sizeof(HexEncodeState)); if (!state) return PyErr_NoMemory(); state->maxcolumn = maxcolumn - (maxcolumn & 1); state->column = 0; return Filter_NewEncoder(target, "HexEncode", 0, write_hex, close_hex, free, state); } /* * Hex Decode */ typedef struct { int last_digit; } HexDecodeState; static size_t read_hex(void * clientdata, PyObject * source, char * buf, size_t length) { size_t i, srclen, bytesread; char encoded[FBUFLEN]; char *dest = buf; HexDecodeState * state = (HexDecodeState*)clientdata; int last_digit = state->last_digit; if (2 * length > FBUFLEN) srclen = FBUFLEN; else srclen = 2 * length; bytesread = Filter_Read(source, encoded, srclen); if (bytesread == 0) { /* end of file or data */ if (state->last_digit >= 0) { /* assume a trailing 0 (this is the PostScript filter behaviour */ *((unsigned char*)dest) = state->last_digit * 16; return 1; } return 0; } for (i = 0; i < bytesread; i++) { if (isxdigit(encoded[i])) { int digit = ((unsigned char*)encoded)[i]; if ('0' <= digit && digit <= '9') { digit = digit - '0'; } else if ('a' <= digit && digit <= 'f') { digit = digit - 'a' + 10; } else if ('A' <= digit && digit <= 'F') { digit = digit - 'A' + 10; } if (last_digit >= 0) { *((unsigned char*)dest) = last_digit * 16 + digit; last_digit = -1; dest += 1; } else { last_digit = digit; } } /* else maybe raise error if not whitespace */ } state->last_digit = last_digit; return dest - buf; } PyObject * Filter_HexDecode(PyObject * self, PyObject * args) { PyObject * source; HexDecodeState * state; if (!PyArg_ParseTuple(args, "O", &source)) return NULL; state = malloc(sizeof(HexDecodeState)); if (!state) return PyErr_NoMemory(); state->last_digit = -1; return Filter_NewDecoder(source, "HexDecode", 0, read_hex, NULL, free, state); } UniConvertor-1.1.4/src/modules/filter/hexfilter.h0000664000076400007640000000235511060500611020530 0ustar igorigor#ifndef HEXFILTER_H #define HEXFILTER_H /* * Copyright (C) 1998, 1999 by Bernhard Herzog. * * All Rights Reserved * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear in * supporting documentation, and that the name of the author not be * used in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "filterobj.h" PyObject * Filter_HexEncode(PyObject * self, PyObject * args); PyObject * Filter_HexDecode(PyObject * self, PyObject * args); #endif /* HEXFILTER_H */ UniConvertor-1.1.4/src/modules/filter/base64filter.c0000664000076400007640000001713511060500611021025 0ustar igorigor/* Copyright (C) 1998, 1999 by Bernhard Herzog. All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of the author not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. parts of this file are based on code from the python interpreter, which comes with the following license: Copyright 1991, 1992, 1993, 1994 by Stichting Mathematisch Centrum, Amsterdam, The Netherlands. All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the names of Stichting Mathematisch Centrum or CWI or Corporation for National Research Initiatives or CNRI not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. */ #include "base64filter.h" #define FBUFLEN 1024 /* some code here taken from binascii.c in Python's Modules directory */ static char table_a2b_base64[] = { -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1, /* Note PAD->0 */ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 }; #define BASE64_PAD '=' typedef struct { int leftbits; unsigned int leftchar; } Base64DecodeState; static size_t read_base64(void* clientdata, PyObject * source, char * buf, size_t length) { size_t bytes_read; unsigned char encoded[FBUFLEN]; unsigned char * ascii_data; unsigned char * bin_data; int ascii_len, bin_len = 0; Base64DecodeState * state = (Base64DecodeState*)clientdata; int leftbits = state->leftbits; unsigned int leftchar = state->leftchar; unsigned char this_ch; int npad; bin_data = (unsigned char*)buf; while (!bin_len) { ascii_len = (length / 3) * 4; /* lower bound */ if (ascii_len > FBUFLEN) ascii_len = FBUFLEN; ascii_data = encoded; npad = 0; bytes_read = Filter_Read(source, (char*)ascii_data, ascii_len); if (bytes_read == 0) { if (!PyErr_Occurred()) { if (leftbits != 0) PyErr_Format(PyExc_ValueError, "Base64Decode: premature end of data"); } return 0; } ascii_len = bytes_read; for(; ascii_len > 0 ; ascii_len--, ascii_data++) { /* Skip some punctuation */ this_ch = (*ascii_data & 0x7f); if (this_ch == '\r' || this_ch == '\n' || this_ch == ' ') continue; if (this_ch == BASE64_PAD) npad++; this_ch = table_a2b_base64[(*ascii_data) & 0x7f]; if (this_ch == (unsigned char) -1) continue; /* ** Shift it in on the low end, and see if there's ** a byte ready for output. */ leftchar = (leftchar << 6) | (this_ch); leftbits += 6; if (leftbits >= 8) { leftbits -= 8; *bin_data++ = (leftchar >> leftbits) & 0xff; leftchar &= ((1 << leftbits) - 1); bin_len++; } } bin_len -= npad; } state->leftbits = leftbits; state->leftchar = leftchar; return bin_len; } PyObject * Filter_Base64Decode(PyObject * self, PyObject * args) { PyObject * source; Base64DecodeState * state; if (!PyArg_ParseTuple(args, "O", &source)) return NULL; state = malloc(sizeof (Base64DecodeState)); if (!state) return PyErr_NoMemory(); state->leftbits = 0; state->leftchar = 0; return Filter_NewDecoder(source, "Base64Decode", 0, read_base64, NULL, free, state); } #define BASE64_MAXASCII 76 /* Max chunk size (76 char line) */ static unsigned char table_b2a_base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; typedef struct { int leftbits; unsigned int leftchar; int column; } Base64EncodeState; static size_t write_base64(void * clientdata, PyObject * target, const char * buf, size_t length) { Base64EncodeState * state = (Base64EncodeState*)clientdata; unsigned char encoded[FBUFLEN]; unsigned char *ascii_data = encoded; const unsigned char *bin_data = (const unsigned char*)buf; int leftbits = state->leftbits; unsigned char this_ch; unsigned int leftchar = state->leftchar; int bin_len = ((FBUFLEN / 4) * 3); size_t ascii_left; if (bin_len > length) bin_len = length; /* first, fill the ascii buffer and don't care about max. line length */ for( ; bin_len > 0 ; bin_len--, bin_data++ ) { /* Shift the data into our buffer */ leftchar = (leftchar << 8) | *bin_data; leftbits += 8; /* See if there are 6-bit groups ready */ while ( leftbits >= 6 ) { this_ch = (leftchar >> (leftbits-6)) & 0x3f; leftbits -= 6; *ascii_data++ = table_b2a_base64[this_ch]; } } state->leftbits = leftbits; state->leftchar = leftchar; /* now output the ascii data line by line */ ascii_left = ascii_data - encoded; while (ascii_left > 0) { int linelength = BASE64_MAXASCII - state->column; if (ascii_left < linelength) linelength = ascii_left; if (Filter_Write(target, ((char*)ascii_data) - ascii_left, linelength) == 0) return 0; ascii_left -= linelength; state->column += linelength; if (state->column >= BASE64_MAXASCII) { if (Filter_Write(target, "\n", 1) == 0) return 0; state->column = 0; } } return bin_data - (const unsigned char *)buf; } static int close_base64encode(void * clientdata, PyObject * target) { Base64EncodeState * state = (Base64EncodeState*)clientdata; unsigned char buf[4]; unsigned char * ascii_data = buf; if (state->leftbits == 2) { *ascii_data++ = table_b2a_base64[(state->leftchar & 3) << 4]; *ascii_data++ = BASE64_PAD; *ascii_data++ = BASE64_PAD; } else if (state->leftbits == 4) { *ascii_data++ = table_b2a_base64[(state->leftchar & 0xf) << 2]; *ascii_data++ = BASE64_PAD; } if (ascii_data > buf || state->column != 0) { *ascii_data++ = '\n'; /* Append a courtesy newline */ } if (ascii_data > buf) { if (Filter_Write(target, (char*)buf, ascii_data - buf) == 0) return EOF; } return 0; } PyObject * Filter_Base64Encode(PyObject * self, PyObject * args) { PyObject * target; Base64EncodeState * state; if (!PyArg_ParseTuple(args, "O", &target)) return NULL; state = malloc(sizeof (Base64EncodeState)); if (!state) return PyErr_NoMemory(); state->leftbits = 0; state->leftchar = 0; state->column = 0; /* assume target is at the beginning of a line */ return Filter_NewEncoder(target, "Base64Decode", 0, write_base64, close_base64encode, free, state); } UniConvertor-1.1.4/src/modules/filter/base64filter.h0000664000076400007640000000237411060500611021031 0ustar igorigor#ifndef BASE64FILTER_H #define BASE64FILTER_H /* * Copyright (C) 1998, 1999 by Bernhard Herzog. * * All Rights Reserved * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear in * supporting documentation, and that the name of the author not be * used in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "filterobj.h" PyObject * Filter_Base64Decode(PyObject * self, PyObject * args); PyObject * Filter_Base64Encode(PyObject * self, PyObject * args); #endif /* BASE64FILTER_H */ UniConvertor-1.1.4/src/modules/filter/linefilter.c0000664000076400007640000000432111060500611020661 0ustar igorigor/* * Copyright (C) 1998, 1999 by Bernhard Herzog. * * All Rights Reserved * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear in * supporting documentation, and that the name of the author not be * used in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "linefilter.h" #define FBUFLEN 2048 static size_t read_nl(void * clientdata, PyObject * source, char * buf, size_t length) { size_t i, maxlen, bytesread; char encoded[FBUFLEN + 1]; char * src, *dest = buf; int converted_last = *((int*)clientdata); if (length > FBUFLEN) maxlen = FBUFLEN; else maxlen = length; bytesread = Filter_Read(source, encoded, maxlen); if (bytesread == 0) return 0; if (converted_last && encoded[0] == '\n') { src = encoded + 1; bytesread -= 1; } else src = encoded; for (i = 0; i < bytesread; i++) { if ((*dest++ = *src++) == '\r') { dest[-1] = '\n'; if (i + 1 >= bytesread) break; if (*src == '\n') { src += 1; i += 1; continue; } } } *((int*)clientdata) = (src[-1] == '\r'); return dest - buf; } PyObject * Filter_LineDecode(PyObject * self, PyObject * args) { PyObject * source; int * data; if (!PyArg_ParseTuple(args, "O", &source)) return NULL; data = malloc(sizeof(int)); if (!data) return PyErr_NoMemory(); *data = 0; return Filter_NewDecoder(source, "LineDecode", 0, read_nl, NULL, free, data); } UniConvertor-1.1.4/src/modules/filter/linefilter.h0000664000076400007640000000226311060500611020671 0ustar igorigor#ifndef LINEFILTER_H #define LINEFILTER_H /* * Copyright (C) 1998, 1999 by Bernhard Herzog. * * All Rights Reserved * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear in * supporting documentation, and that the name of the author not be * used in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "filterobj.h" PyObject * Filter_LineDecode(PyObject * self, PyObject * args); #endif /* LINEFILTER_H */ UniConvertor-1.1.4/src/modules/skread/0000755000076400007640000000000011211455344016354 5ustar igorigorUniConvertor-1.1.4/src/modules/skread/skreadmodule.c0000664000076400007640000003121411060500611021170 0ustar igorigor/* Sketch - A Python-based interactive drawing program * Copyright (C) 1998, 1999, 2000, 2001, 2006 by Bernhard Herzog * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Functions to parse SK-files and related formats. * * This modules contains functions to parse one line of a SK-file. Each * line looks just like a Python function call with any number of * positional- and keyword arguments. Arguments must be literals, that * is (float or int), strings, tuples or lists. Tuples and list must in * turn be composed of these types of literals. */ #include #include #include #include typedef struct { int length; char * buffer; PyObject * funcname; PyObject * args; PyObject * kwargs; int token; PyObject * value; char * error; } SKLineInfo; #define NAME 258 #define INT 259 #define FLOAT 260 #define STRING 261 #define GETC() (*(buffer->buffer++)) #define BACK() (buffer->buffer--) static int sklex(PyObject ** lval, SKLineInfo * buffer) { int c; *lval = NULL; for (;;) { c = GETC(); switch (c) { case '#': /* ignore comments */ case '\n': /* end of line/input */ case '\0': return 0; case '(': case ')': case '[': case ']': case ',': case '=': /* a single character token */ return c; case '\'': case '"': { int string_delimiter = c; char * dest; *lval = PyString_FromStringAndSize(NULL, buffer->length); if (!*lval) { buffer->error = "no memory"; return 0; } dest = PyString_AsString(*lval); for (;;) { c = GETC(); switch (c) { case '\0': case '\n': /* end of input in string constant! */ Py_DECREF(*lval); *lval = NULL; buffer->error = "unexpected end of input"; return 0; case '\'': case '"': if (c == string_delimiter) { int size; size = dest - PyString_AsString(*lval); if (_PyString_Resize(lval, size) < 0) { *lval = NULL; buffer->error = "no memory"; return 0; } return STRING; } else *dest++ = c; break; case '\\': c = GETC(); switch(c) { case 'a': *dest++ = '\a'; break; case 'b': *dest++ = '\b'; break; case 'f': *dest++ = '\f'; break; case 'n': *dest++ = '\n'; break; case 'r': *dest++ = '\r'; break; case 't': *dest++ = '\t'; break; case 'v': *dest++ = '\v'; break; case '\\': *dest++ = '\\'; break; case '\'': *dest++ = '\''; break; case '"': *dest++ = '"'; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': { int code = c - '0'; c = GETC(); if ('0' <= c && c <= '7') { code = code * 8 + c - '0'; c = GETC(); if ('0' <= c && c <= '7') { code = code * 8 + c - '0'; (void)GETC(); } } *dest++ = code; BACK(); } break; case 'x': /* A hexcoded character. unlike in Python 1.5.2, * we use the conventions of 2.1 here, that is, * \x has to be followed by exactly 2 hex * digits, because this code is really only * needed for Python 2.1 compatibility. * Beginning with Python 2.1 repr of a string * generates \x escapes instead of octal */ { int digit1, digit2; digit1 = GETC(); digit2 = GETC(); if (isxdigit(digit1) && isxdigit(digit2)) { int code = 0; if (isdigit(digit1)) code = digit1 - '0'; else if (islower(digit1)) code = digit1 - 'a' + 10; else code = digit1 - 'A' + 10; code *= 16; if (isdigit(digit2)) code += digit2 - '0'; else if (islower(digit2)) code += digit2 - 'a' + 10; else code += digit2 - 'A' + 10; *dest++ = code; } else { Py_DECREF(*lval); *lval = NULL; buffer->error = "Invalid \\x escape"; return 0; } } break; default: /* copy the \ and character literally */ *dest++ = '\\'; *dest++ = c; } break; default: *dest++ = c; } } } /* case string */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '+': case '-': { char * p = buffer->buffer; while(isdigit(*p)) p += 1; if (*p == '.' || *p == 'e' || *p == 'E') { char * old_locale; double result; /* Change LC_NUMERIC locale to "C" around the strtod * call so that it parses the number correctly. */ old_locale = strdup(setlocale(LC_NUMERIC, NULL)); setlocale(LC_NUMERIC, "C"); result = strtod(buffer->buffer - 1, &(buffer->buffer)); setlocale(LC_NUMERIC, old_locale); free(old_locale); *lval = PyFloat_FromDouble(result); return FLOAT; } else { *lval = PyInt_FromLong(strtol(buffer->buffer - 1, &(buffer->buffer), 10)); return INT; } } default: if (isalpha(c) || c == '_') { /* arbitrary limit for identifiers: */ char ident[101]; sscanf(buffer->buffer - 1, "%100[a-zA-Z0-9_]", ident); buffer->buffer = buffer->buffer + strlen(ident) - 1; *lval = PyString_FromString(ident); return NAME; } if (!isspace(c)) { buffer->error = "unexpected character"; return 0; } } /* switch */ } /* for ever */ /* unreachable... */ return 0; } #define GET_TOKEN(line) ((line)->token = sklex(&((line)->value), (line))) static PyObject * parse_literal(SKLineInfo * line); static PyObject * parse_litlist(SKLineInfo * line) { PyObject * list = PyList_New(0); if (!list) return NULL; for (;;) { PyObject * literal = parse_literal(line); if (literal) { PyList_Append(list, literal); Py_DECREF(literal); if (line->token == ',') GET_TOKEN(line); else break; } else break; } return list; } static PyObject * parse_literal(SKLineInfo * line) { PyObject * result = NULL; switch (line->token) { case INT: case FLOAT: case STRING: result = line->value; GET_TOKEN(line); break; case '(': { PyObject * list = NULL; GET_TOKEN(line); list = parse_litlist(line); if (list) { if (line->token == ')') { GET_TOKEN(line); result = PySequence_Tuple(list); } } Py_DECREF(list); } break; case '[': GET_TOKEN(line); result = parse_litlist(line); if (result) { if (line->token != ']') { Py_DECREF(result); result = NULL; } else GET_TOKEN(line); } break; default: break; } return result; } static int parse_arg(SKLineInfo * line) { PyObject * literal = NULL; if (line->token == NAME) { /* keyword argument */ PyObject * name = line->value; GET_TOKEN(line); if (line->token == '=') { GET_TOKEN(line); literal = parse_literal(line); if (literal) { PyObject_SetItem(line->kwargs, name, literal); } else { line->error = "literal expected"; } } else { line->error = "'=' expected"; } Py_XDECREF(literal); Py_XDECREF(name); } else { literal = parse_literal(line); if (literal) { PyList_Append(line->args, literal); } Py_XDECREF(literal); } return literal == NULL; } static int parse_arglist(SKLineInfo * line) { if (parse_arg(line) != 0) return 0; while (line->token == ',') { GET_TOKEN(line); if (parse_arg(line) != 0) return 1; } return 0; } static int parse_line(SKLineInfo * line) { GET_TOKEN(line); switch (line->token) { case 0: /* empty line */ return 0; case NAME: { PyObject * temp; line->funcname = line->value; GET_TOKEN(line); if (line->token != '(') { line->error = "'(' expected"; return 1; } GET_TOKEN(line); /* experimental code that makes the parens optional if (line->token == '(') { GET_TOKEN(line); } */ if (parse_arglist(line)) return 1; if (line->token != ')') { line->error = "')' expected"; return 1; } GET_TOKEN(line); /* experimental if (line->token == ')') { GET_TOKEN(line); } */ if (line->token != 0) return 1; temp = PySequence_Tuple(line->args); Py_DECREF(line->args); line->args = temp; } break; default: return 1; } return 0; } static PyObject * parse_sk_line(PyObject * self, PyObject * args) { char * string; int length; SKLineInfo info; PyObject * result, * function, *funcdict; if (!PyArg_ParseTuple(args, "s#O", &string, &length, &funcdict)) return NULL; info.buffer = string; info.length = length; info.error = NULL; info.funcname = NULL; info.args = PyList_New(0); info.kwargs = PyDict_New(); if (!info.args || !info.kwargs) goto fail; if (parse_line((void*)&info)) { if (info.error) PyErr_SetString(PyExc_SyntaxError, info.error); else PyErr_SetString(PyExc_SyntaxError, "parse error"); goto fail; } if (info.funcname) { function = PyObject_GetItem(funcdict, info.funcname); if (function) { result = PyEval_CallObjectWithKeywords(function, info.args, info.kwargs); } else { char buffer[200]; sprintf(buffer, "unknown function %.100s", PyString_AsString(info.funcname)); PyErr_SetString(PyExc_NameError, buffer); result = NULL; } Py_XDECREF(function); } else { Py_INCREF(Py_None); result = Py_None; } Py_XDECREF(info.funcname); Py_XDECREF(info.args); Py_XDECREF(info.kwargs); return result; fail: Py_XDECREF(info.funcname); Py_XDECREF(info.args); Py_XDECREF(info.kwargs); return NULL; } static PyObject * parse_sk_line2(PyObject * self, PyObject * args) { char * string; int length; SKLineInfo info; PyObject * result = NULL; if (!PyArg_ParseTuple(args, "s#", &string, &length)) return NULL; info.buffer = string; info.length = length; info.error = NULL; info.funcname = NULL; info.args = PyList_New(0); info.kwargs = PyDict_New(); if (!info.args || !info.kwargs) goto fail; if (parse_line((void*)&info)) { if (info.error) PyErr_SetString(PyExc_SyntaxError, info.error); else PyErr_SetString(PyExc_SyntaxError, "parse error"); goto fail; } if (!info.funcname) { /* an empty or comment line */ Py_INCREF(Py_None); info.funcname = Py_None; } result = Py_BuildValue("OOO", info.funcname, info.args, info.kwargs); fail: Py_XDECREF(info.funcname); Py_XDECREF(info.args); Py_XDECREF(info.kwargs); return result; } static PyObject * tokenize_line(PyObject * self, PyObject * args) { char * string; int length, max_tokens = -1; SKLineInfo info; PyObject * result; if (!PyArg_ParseTuple(args, "s#|i", &string, &length, &max_tokens)) return NULL; info.buffer = string; info.length = length; info.error = NULL; info.funcname = NULL; info.args = NULL; info.kwargs = NULL; result = PyList_New(0); if (!result) return NULL; GET_TOKEN(&info); /* XXX case max_tokens == 0 ?*/ while (info.token && max_tokens != 0) { switch (info.token) { case STRING: case NAME: case FLOAT: case INT: if (PyList_Append(result, info.value) == -1) goto fail; Py_DECREF(info.value); if (max_tokens > 0) max_tokens--; break; default: /* this must be a single character: ()[],= * just ignore for now... */ break; } if (max_tokens != 0) GET_TOKEN(&info); } info.value = NULL; if (max_tokens == 0) { /*info.buffer -= 1;*/ if (info.buffer - string < length) { PyObject *rest = PyString_FromString(info.buffer); if (PyList_Append(result, rest) == -1) goto fail; } } return result; fail: Py_DECREF(result); Py_XDECREF(info.value); return NULL; } /* * Method table and module initialization */ static PyMethodDef sk_methods[] = { {"parse_sk_line", parse_sk_line, METH_VARARGS}, {"parse_sk_line2", parse_sk_line2, METH_VARARGS}, {"tokenize_line", tokenize_line, METH_VARARGS}, {NULL, NULL} }; DL_EXPORT(void) initskread(void) { Py_InitModule("skread", sk_methods); } UniConvertor-1.1.4/src/GNU_LGPL_v20000775000076400007640000006127311060500613015242 0ustar igorigor GNU LIBRARY GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights. Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library. Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. GNU LIBRARY GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Library General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! UniConvertor-1.1.4/src/uniconv0000664000076400007640000000163011211361677015046 0ustar igorigor#!/bin/sh # # Wrapper script to start a UniConvertor application once it is installed # # Copyright (C) 2007-2009 Igor E. Novikov # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA # python -c "from uniconvertor import uniconv; uniconv();" "$@"UniConvertor-1.1.4/src/COPYRIGHTS0000775000076400007640000000201711060500613015046 0ustar igorigorUniConvertor - A Python-based universal verctor graphics translator which uses sK1 engine. Copyright (C) 2007-2008 by Igor E. Novikov sK1 is a fork of Sketch/Skencil (1999-2006 (c) Bernhard Herzog) This program is free software; you can redistribute it and/or modify it under the terms of GNU Library General Public License and some files under terms of GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA GPL and LGPL license agreement you can find in "GNU_GPL_v2" and "GNU_LGPL_v2" files.UniConvertor-1.1.4/src/GNU_GPL_v20000664000076400007640000004311111060500613015112 0ustar igorigor GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. UniConvertor-1.1.4/src/__init__.py0000775000076400007640000000500511211361732015546 0ustar igorigor#! /usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2007-2009 by Igor E. Novikov # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in root directory. ''' USAGE: uniconv [OPTIONS] [INPUT FILE] [OUTPUT FILE] Converts one vector graphics format to another using sK1 engine. sK1 Team (http://sk1project.org), copyright (C) 2007-2009 by Igor E. Novikov Allowed input formats: AI - Adobe Illustrator files (postscript based) CDR - CorelDRAW Graphics files (7-X3,X4 versions) CDT - CorelDRAW templates files (7-X3,X4 versions) CCX - Corel Compressed Exchange files CMX - Corel Presentation Exchange files (CMX1 format) SVG - Scalable Vector Graphics files FIG - XFig files CGM - Computer Graphics Metafile files AFF - Draw files WMF - Windows Metafile files SK - Sketch/Skencil files SK1 - sK1 vector graphics files PLT - HPGL for cutting plotter files Allowed output formats: AI - Adobe Illustrator files (postscript based) SVG - Scalable Vector Graphics files CGM - Computer Graphics Metafile files WMF - Windows Metafile files SK - Sketch/Skencil files SK1 - sK1 vector graphics files PDF - Portable Document Format PS - PostScript PLT - HPGL for cutting plotter files Example: uniconv drawing.cdr drawing.svg\n ''' import sys, os, string def init_uniconv(): _pkgdir = __path__[0] app_dir = os.path.join(_pkgdir, 'app') sys.path.insert(1, _pkgdir) def uniconv(): _pkgdir = __path__[0] app_dir = os.path.join(_pkgdir, 'app') app_ver = string.strip(open(os.path.join(app_dir, 'VERSION')).read()) if len(sys.argv)<3 or sys.argv[1]=='--help': print '\nUniConvertor',app_ver print __doc__ sys.exit(0) options=[] input_file=sys.argv[-2] output_file=sys.argv[-1] if len(sys.argv)>3: options=sys.argv[1:-2] if not os.path.isfile(input_file): print '\nERROR: %s file is not found!' % input_file print '\nUniConvertor',app_ver print __doc__ sys.exit(1) sys.path.insert(1, _pkgdir) from app.io import load from app.plugins import plugins import app app.init_lib() doc = load.load_drawing(input_file) extension = os.path.splitext(output_file)[1] plugins.load_plugin_configuration() fileformat = plugins.guess_export_plugin(extension) if fileformat: saver = plugins.find_export_plugin(fileformat) saver(doc, output_file) else: sys.stderr.write('ERROR: unrecognized extension %s\n' % extension) sys.exit(1) doc.Destroy() sys.exit(0) UniConvertor-1.1.4/src/uniconv.cmd0000664000076400007640000000210311211361643015575 0ustar igorigor:: --------------------------------------------------------------------- :: Wrapper script to start a UniConvertor application once it is installed :: :: Copyright (C) 2007-2009 Igor E. Novikov :: :: This library is free software; you can redistribute it and/or :: modify it under the terms of the GNU Lesser General Public :: License as published by the Free Software Foundation; either :: version 2.1 of the License, or (at your option) any later version. :: :: This library is distributed in the hope that it will be useful, :: but WITHOUT ANY WARRANTY; without even the implied warranty of :: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU :: Lesser General Public License for more details. :: :: You should have received a copy of the GNU Lesser General Public :: License along with this library; if not, write to the Free Software :: Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA :: --------------------------------------------------------------------- @python -c "from uniconvertor import uniconv; uniconv();" "%~1" "%~2" UniConvertor-1.1.4/README0000664000076400007640000000162011060500613013515 0ustar igorigorUniConvertor is a universal vector graphics translator. It uses sK1 engine to convert one format to another. sK1 Team, copyright (C) 2007-2008 by Igor E. Novikov sK1 is a fork of Sketch/Skencil (1999-2006 (c) Bernhard Herzog) How to install: ======================================== to install package: python setup.py install (will be installed into Python site-packages/ directory) ======================================== to create source distribution: python setup.py sdist (creates .tar.gz archive) ======================================== to create binary RPM distribution: python setup.py bdist_rpm to create deb package just use alien command (i.e. rpm2deb) ======================================== to create binary Win32 distribution: python setup.py bdist_wininst ======================================== help on available distribution formats: python setup.py bdist --help-formats UniConvertor-1.1.4/PKG-INFO0000644000076400007640000000424711211455344013750 0ustar igorigorMetadata-Version: 1.0 Name: UniConvertor Version: 1.1.4 Summary: Universal vector graphics translator Home-page: http://sk1project.org Author: Igor E. Novikov Author-email: igor.e.novikov@gmail.com License: LGPL v2, GPL v2 (some packages) Download-URL: http://sk1project.org/modules.php?name=Products&product=uniconvertor Description: UniConvertor is a multiplatform universal vector graphics translator. It uses sK1 engine to convert one format to another. sK1 Team (http://sk1project.org), Copyright (C) 2007-2009 by Igor E. Novikov ------------------------------------------------------------------------------------ Import filters: * CorelDRAW ver.7-X3,X4 (CDR/CDT/CCX/CDRX/CMX) * Adobe Illustrator up to 9 ver. (AI postscript based) * Postscript (PS) * Encapsulated Postscript (EPS) * Computer Graphics Metafile (CGM) * Windows Metafile (WMF) * XFIG * Scalable Vector Graphics (SVG) * Skencil/Sketch/sK1 (SK and SK1) * Acorn Draw (AFF) * HPGL for cutting plotter files (PLT) ------------------------------------------------------------------------------------ Export filters: * AI (Postscript based Adobe Illustrator 5.0 format) * SVG (Scalable Vector Graphics) * SK (Sketch/Skencil format) * SK1 (sK1 format) * CGM (Computer Graphics Metafile) * WMF (Windows Metafile) * PDF - Portable Document Format * PS - PostScript * PLT - HPGL for cutting plotter files ------------------------------------------------------------------------------------ Platform: UNKNOWN Classifier: Development Status :: 6 - Mature Classifier: Environment :: Console Classifier: Intended Audience :: End Users/Desktop Classifier: License :: OSI Approved :: LGPL v2 Classifier: License :: OSI Approved :: GPL v2 Classifier: Operating System :: POSIX Classifier: Operating System :: MacOS :: MacOS X Classifier: Programming Language :: Python Classifier: Programming Language :: C Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion UniConvertor-1.1.4/setup.cfg0000664000076400007640000000021611060500613014456 0ustar igorigor[bdist_rpm] release = 1 packager = Igor Novikov provides = uniconvertor requires = python-imaging python-reportlab UniConvertor-1.1.4/MANIFEST.in0000664000076400007640000000131311060500613014372 0ustar igorigorinclude MANIFEST.in include README setup.cfg include setup_win32.py include src/GNU_GPL_v2 src/GNU_LGPL_v2 src/COPYRIGHTS include src/uniconv include src/uniconv.cmd include src/modules/filter/README include src/app/modules/descr.txt include src/app/VERSION recursive-include src/share *.* recursive-include src/share/icc *.* recursive-include src/share/fonts *.* recursive-include src/share/ps_templates *.* recursive-include src/share/ps_templates *.* recursive-include src/modules/filter *.h recursive-include src/modules/skmod *.h recursive-include src/modules/skread *.h recursive-include src/modules/pstokenize *.h recursive-include src/modules/type1mod *.h recursive-include src/app/plugins/Filters *.py UniConvertor-1.1.4/setup.py0000664000076400007640000001515011211362563014362 0ustar igorigor#!/usr/bin/env python # # Setup script for UniConvertor # # Copyright (C) 2007 Igor E. Novikov # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA # # Usage: # -------------------------------------------------------------------------- # to build package: python setup.py build # to install package: python setup.py install # -------------------------------------------------------------------------- # to create source distribution: python setup.py sdist # -------------------------------------------------------------------------- # to create binary RPM distribution: python setup.py bdist_rpm # # to create deb package just use alien command (i.e. rpm2deb) # -------------------------------------------------------------------------- # to create Win32 distribution: python setup.py bdist_wininst # -------------------------------------------------------------------------- # help on available distribution formats: python setup.py bdist --help-formats # from distutils.core import setup, Extension ######################## # # Main build procedure # ######################## if __name__ == "__main__": src_path='src/' import os if os.name == 'nt': script_name='src/uniconv.cmd' else: script_name='src/uniconv' filter_src=src_path+'modules/filter/' filter_module = Extension('uniconvertor.app.modules.streamfilter', define_macros = [('MAJOR_VERSION', '1'), ('MINOR_VERSION', '1')], sources = [filter_src+'streamfilter.c', filter_src+'filterobj.c', filter_src+'linefilter.c', filter_src+'subfilefilter.c', filter_src+'base64filter.c', filter_src+'nullfilter.c', filter_src+'stringfilter.c', filter_src+'binfile.c', filter_src+'hexfilter.c']) type1mod_src=src_path+'modules/type1mod/' type1mod_module = Extension('uniconvertor.app.modules._type1', define_macros = [('MAJOR_VERSION', '1'), ('MINOR_VERSION', '1')], sources = [type1mod_src+'_type1module.c']) skread_src=src_path+'modules/skread/' skread_module = Extension('uniconvertor.app.modules.skread', define_macros = [('MAJOR_VERSION', '1'), ('MINOR_VERSION', '1')], sources = [skread_src+'skreadmodule.c']) pstokenize_src=src_path+'modules/pstokenize/' pstokenize_module = Extension('uniconvertor.app.modules.pstokenize', define_macros = [('MAJOR_VERSION', '1'), ('MINOR_VERSION', '1')], sources = [pstokenize_src+'pstokenize.c', pstokenize_src+'pschartab.c']) skmod_src=src_path+'modules/skmod/' skmod_module = Extension('uniconvertor.app.modules._sketch', define_macros = [('MAJOR_VERSION', '1'), ('MINOR_VERSION', '1')], sources = [skmod_src+'_sketchmodule.c', skmod_src+'skpoint.c', skmod_src+'skcolor.c', skmod_src+'sktrafo.c', skmod_src+'skrect.c', skmod_src+'skfm.c', skmod_src+'curvefunc.c', skmod_src+'curveobject.c', skmod_src+'curvelow.c', skmod_src+'curvemisc.c', skmod_src+'skaux.c', skmod_src+'skimage.c', ]) setup (name = 'UniConvertor', version = '1.1.4', description = 'Universal vector graphics translator', author = 'Igor E. Novikov', author_email = 'igor.e.novikov@gmail.com', maintainer = 'Igor E. Novikov', maintainer_email = 'igor.e.novikov@gmail.com', license = 'LGPL v2, GPL v2 (some packages)', url = 'http://sk1project.org', download_url = 'http://sk1project.org/modules.php?name=Products&product=uniconvertor', long_description = ''' UniConvertor is a multiplatform universal vector graphics translator. It uses sK1 engine to convert one format to another. sK1 Team (http://sk1project.org), Copyright (C) 2007-2009 by Igor E. Novikov ------------------------------------------------------------------------------------ Import filters: * CorelDRAW ver.7-X3,X4 (CDR/CDT/CCX/CDRX/CMX) * Adobe Illustrator up to 9 ver. (AI postscript based) * Postscript (PS) * Encapsulated Postscript (EPS) * Computer Graphics Metafile (CGM) * Windows Metafile (WMF) * XFIG * Scalable Vector Graphics (SVG) * Skencil/Sketch/sK1 (SK and SK1) * Acorn Draw (AFF) * HPGL for cutting plotter files (PLT) ------------------------------------------------------------------------------------ Export filters: * AI (Postscript based Adobe Illustrator 5.0 format) * SVG (Scalable Vector Graphics) * SK (Sketch/Skencil format) * SK1 (sK1 format) * CGM (Computer Graphics Metafile) * WMF (Windows Metafile) * PDF - Portable Document Format * PS - PostScript * PLT - HPGL for cutting plotter files ------------------------------------------------------------------------------------ ''', classifiers=[ 'Development Status :: 6 - Mature', 'Environment :: Console', 'Intended Audience :: End Users/Desktop', 'License :: OSI Approved :: LGPL v2', 'License :: OSI Approved :: GPL v2', 'Operating System :: POSIX', 'Operating System :: MacOS :: MacOS X', 'Programming Language :: Python', 'Programming Language :: C', "Topic :: Multimedia :: Graphics :: Graphics Conversion", ], packages = ['uniconvertor', 'uniconvertor.app', 'uniconvertor.app.Graphics', 'uniconvertor.app.Lib', 'uniconvertor.app.Scripting', 'uniconvertor.app.conf', 'uniconvertor.app.events', 'uniconvertor.app.io', 'uniconvertor.app.managers', 'uniconvertor.app.modules', 'uniconvertor.app.plugins', 'uniconvertor.app.scripts', 'uniconvertor.app.utils' ], package_dir = {'uniconvertor': 'src', 'uniconvertor.app': 'src/app', 'uniconvertor.app.plugins': 'src/app/plugins', 'uniconvertor.app.modules': 'src/app/modules', }, package_data={'uniconvertor.app': ['VERSION','modules/*.*'], 'uniconvertor.app.plugins': ['Filters/*.py'], 'uniconvertor': ['GNU_GPL_v2', 'GNU_LGPL_v2', 'COPYRIGHTS', 'share/icc/*.*', 'share/fonts/*.*', 'share/ps_templates/*.*'], }, scripts=[script_name], ext_modules = [filter_module, type1mod_module, skread_module, pstokenize_module, skmod_module])