uniconvertor-1.1.5/0000755000076400007640000000000011411006426012740 5ustar igorigoruniconvertor-1.1.5/src/0000755000076400007640000000000011411006426013527 5ustar igorigoruniconvertor-1.1.5/src/app/0000755000076400007640000000000011411006426014307 5ustar igorigoruniconvertor-1.1.5/src/app/io/0000755000076400007640000000000011411006426014716 5ustar igorigoruniconvertor-1.1.5/src/app/io/load.py0000775000076400007640000002632411407115771016234 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 sk1libs import filters 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() #may be just pass the problem? #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 masterlayer(self, *args, **kw): kw['is_MasterLayer']=1 self.begin_layer_class(layer.Layer, args, kw) def page(self, *args, **kw): kw['is_Page']=1 self.begin_layer_class(layer.Layer, args, kw) def end_layer(self): self.end_composite() def begin_layer_class(self, layer_class, args, kw = None): if issubclass(self.composite_class, layer.Layer): self.end_composite() if issubclass(self.composite_class, doc_class): self.begin_composite(layer_class, args, kw) else: raise SketchLoadError('self.composite_class is %s, not a document', self.composite_class) def bezier(self, paths = None): self.append_object(PolyBezier(paths = paths, properties = self.get_prop_stack())) def rectangle(self, m11, m21, m12, m22, v1, v2, radius1 = 0, radius2 = 0): trafo = Trafo(m11, m21, m12, m22, v1, v2) self.append_object(Rectangle(trafo, radius1 = radius1, radius2 = radius2, properties = self.get_prop_stack())) def ellipse(self, m11, m21, m12, m22, v1, v2, start_angle = 0.0, end_angle = 0.0, arc_type = ArcPieSlice): self.append_object(Ellipse(Trafo(m11, m21, m12, m22, v1, v2), start_angle, end_angle, arc_type, properties = self.get_prop_stack())) def simple_text(self, str, trafo = None, valign = text.ALIGN_BASE, halign = text.ALIGN_LEFT): if type(trafo) == TupleType: if len(trafo) == 2: trafo = apply(Translation, trafo) else: raise TypeError, "trafo must be a Trafo-object or a 2-tuple" self.append_object(text.SimpleText(text = str, trafo = trafo, valign = valign, halign = halign, properties = self.get_prop_stack())) def image(self, image, trafo): if type(trafo) == TupleType: if len(trafo) == 2: trafo = apply(Translation, trafo) else: raise TypeError, "trafo must be a Trafo-object or a 2-tuple" image = ImageData(image) self.append_object(Image(image, trafo = trafo)) def begin_group(self, *args, **kw): self.begin_composite(group.Group, args, kw) def end_group(self): self.end_composite() def guess_cont(self): self.guess_continuity = 1 def end_composite(self): isdoc = self.composite_class is doc_class LoaderWithComposites.end_composite(self) if isdoc: self.add_meta(self.object) def add_meta(self, doc): doc.meta.fullpathname = self.filename dir, name = os.path.split(self.filename) doc.meta.directory = dir doc.meta.filename = name doc.meta.native_format = 0 doc.meta.format_name = self.format_name def add_message(self, message): pdebug(('load', 'echo_messages'), message) self.messages[message] = self.messages.get(message, 0) + 1 def Messages(self): messages = self.messages.items() list = [] for message, count in messages: if count > 1: list.append(_("%(message)s (%(count)d times)") % locals()) else: list.append(message) list.sort() return string.join(list, '\n') 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 filters.import_plugins: match = info.rx_magic.match(line) if match: loader = info(file, filename, match) try: 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 except Exception, value: raise SketchLoadError(_("Parsing error: ")+ str(value)) 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) ########################################### def parse_drawing_from_file(file, filename, doc_class = None): line = file.readline() if line[:4] == 'RIFF' and len(line) < 12: line = line + file.read(12 - len(line)) for info in filters.parsing_plugins: match = info.rx_magic.match(line) if match: parser = info(file, filename, match) try: try: parser.Load() return except Exception, value: raise SketchLoadError(_("Parsing error: ")+ str(value)) finally: info.UnloadPlugin() else: raise SketchLoadError(_("Unrecognised file type")) def parse_drawing(filename,output_filename): name=locale_utils.utf_to_locale(filename) try: file = open(name, 'rb') except IOError, value: message = value.strerror raise SketchLoadError(_("Cannot open %(filename)s:\n%(message)s") % locals()) return parse_drawing_from_file(file, output_filename) uniconvertor-1.1.5/src/app/io/loadres.py0000775000076400007640000000151511407115771016741 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.5/src/app/io/__init__.py0000775000076400007640000000000011407115771017033 0ustar igorigoruniconvertor-1.1.5/src/app/Lib/0000755000076400007640000000000011411006426015015 5ustar igorigoruniconvertor-1.1.5/src/app/Lib/dscparser.py0000775000076400007640000001614311407115771017400 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.5/src/app/Lib/psmisc.py0000775000076400007640000000171711407115771016711 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.5/src/app/Lib/units.py0000775000076400007640000000122211407115771016544 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.5/src/app/Lib/type1.py0000775000076400007640000002455311407115771016460 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.5/src/app/Lib/skcompleter.py0000775000076400007640000000311111407115771017731 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.5/src/app/Lib/encoding.py0000775000076400007640000001355411407115771017203 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.5/src/app/Lib/__init__.py0000775000076400007640000000000011407115771017132 0ustar igorigoruniconvertor-1.1.5/src/app/conf/0000755000076400007640000000000011411006426015234 5ustar igorigoruniconvertor-1.1.5/src/app/conf/const.py0000775000076400007640000001067411407115770016761 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.5/src/app/conf/__init__.py0000775000076400007640000000000011407115770017350 0ustar igorigoruniconvertor-1.1.5/src/app/conf/configurator.py0000775000076400007640000003650711407115770020340 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 sk1libs.utils.fs import gethome 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_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_ps) for dir in dirs: if not os.path.isdir(dir): result = False if not result: print 'UniConvertor installation is corrupted. Please check UniConvertor directories or reinstall UniConvertor!' sys.exit(1) 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: sys.stderr('cannot write preferences into %s.' % user_config_dir) sys.exit(1) 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) 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 #Grid style: 0 - dotted; 1- lines grid_style = 1 #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) grid_color = ('RGB', 0.83, 0.87, 0.91) #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) horizontal_guide_shape = [5, 7] vertical_guide_shape = [5, 8] 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' default_unit_jump = 0.1 poslabel_sets_default_unit = 1 #How many steps to draw in a gradient pattern gradient_steps_editor = 100 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 #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. draw_page_border = 1 #TODO: Should be merged with show_page_outline! page_border_size = 5 #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 = 0 #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 drawing_precision = 3 #---------UI managment--------- style = 'Plastik' # style = 'Clearlooks' # style = 'eXPect' color_theme = 'built-in' # color_theme = 'UbuntuLooks' # color_theme = 'eXPect' # color_theme = 'ClassicPlastik' icons='CrystalSVG' # icons='eXPect' # icons='Tango' # icons='Human' #---------UI fonts--------- small_font='Tahoma 8' normal_font='Tahoma 9' large_font='Tahoma 10 bold' fixed_font='CourierNew 9' #---------Color managment--------- user_rgb_profile=0 user_cmyk_profile=0 user_monitor_profile=0 printer_intent=0 monitor_intent=0 use_cms=1 use_cms_for_bitmap=1 simulate_printer=0 # 0 - use RGB, 1 - use CMYK color_blending_rule=1 #----------Document font managment------------- default_font = 'BitstreamVeraSans-Roman' # The PS name of the font used for new text-objects system_font_dir='/usr/share/fonts' user_font_dir='.fonts' # should be expanded to absolute path #If the font file for a font can't be found or if a requested font #is not known at all, the fallback_font is used (PS name here): fallback_font = 'BitstreamVeraSans-Roman' #---------Open/save dialogs managment---- dir_for_open='~' dir_for_save='~' dir_for_vector_import='~' dir_for_vector_export='~' dir_for_bitmap_import='~' dir_for_bitmap_export='~' #0- autodetect; 1- kdialog(KDE); 2- zenity(Gnome); 3 - Tk (modified); dialog_type=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=1 alpha_channel_enabled=1 bitmap_alpha_channel_enabled=1 cairo_tolerance=.1 cairo_antialias=0 cairo_bitmap_filter=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 #How to insert from clipboard: 0 - on the same place; 1 - as a floating insertion insertion_mode = 0 #How to insert imported graphics: 0 - on the same place; 1 - as a floating insertion import_insertion_mode = 0 # 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.5/src/app/utils/0000755000076400007640000000000011411006426015447 5ustar igorigoruniconvertor-1.1.5/src/app/utils/locale_utils.py0000775000076400007640000000231211407115770020513 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 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 #FIXME: command line call should be replaced by regular Python string expressions from_bash = os.popen('echo "'+string+'" |iconv -f '+from_codec+' -t '+to_codec) result=from_bash.read() from_bash.close() return result def strip_line(string=''): #may be .rstrip("\n") use? 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.5/src/app/utils/__init__.py0000775000076400007640000000000011407115770017563 0ustar igorigoruniconvertor-1.1.5/src/app/scripts/0000755000076400007640000000000011411006426015776 5ustar igorigoruniconvertor-1.1.5/src/app/scripts/save_selection.py0000775000076400007640000000441611407115772021377 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.5/src/app/scripts/create_star_outline.py0000775000076400007640000000574111407115772022431 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.5/src/app/scripts/create_spiral.py0000775000076400007640000000557511407115772021220 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.5/src/app/scripts/read_gimp_path.py0000775000076400007640000000635111407115772021337 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.5/src/app/scripts/footprints.py0000775000076400007640000001100311407115772020571 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.5/src/app/scripts/simple_separation.py0000775000076400007640000002040011407115772022101 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 sk1libs.utils import system 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 = system.get_real_username(), CreationDate = system.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.5/src/app/scripts/abut_horizontal.py0000775000076400007640000000645011407115772021600 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.5/src/app/scripts/unit.py0000775000076400007640000000253311407115772017351 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.5/src/app/scripts/export_raster.py0000775000076400007640000001756211407115772021303 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 from sk1libs import imaging.Image, imaging.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 = imaging.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 = imaging.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.5/src/app/scripts/spread.py0000775000076400007640000001306511407115772017652 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.5/src/app/scripts/inspect_beziers.py0000775000076400007640000000625511407115772021567 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.5/src/app/scripts/create_text.py0000775000076400007640000000343211407115772020700 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.5/src/app/scripts/select_same_fill_color.py0000775000076400007640000000630011407115772023056 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.5/src/app/scripts/create_star.py0000775000076400007640000001222511407115772020665 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.5/src/app/scripts/usersguide.py0000775000076400007640000000045511407115772020552 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.5/src/app/scripts/reload_image.py0000775000076400007640000000533311407115772021003 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.5/src/app/scripts/__init__.py0000775000076400007640000000000011407115772020114 0ustar igorigoruniconvertor-1.1.5/src/app/scripts/average_points.py0000775000076400007640000001060711407115772021401 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.5/src/app/scripts/select_same_line_color.py0000775000076400007640000000621711407115772023066 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.5/src/app/scripts/abut_vertical.py0000775000076400007640000000605511407115772021221 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.5/src/app/managers/0000755000076400007640000000000011411006426016104 5ustar igorigoruniconvertor-1.1.5/src/app/managers/colormanager.py0000775000076400007640000001144611407115772021154 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 from sk1libs.pycms import cmsOpenProfileFromFile,cmsCreateTransform,cmsDoTransform, \ cmsCreateRGBProfile, cmsCreateCMYKProfile, cmsCreateLabProfile, cmsCreateGrayProfile, \ cmsDoBitmapTransform, cmsDeleteTransform,cmsCloseProfile,TYPE_RGB_8,TYPE_CMYK_8, \ INTENT_PERCEPTUAL,cmsFLAGS_NOTPRECALC,COLORB, INTENT_RELATIVE_COLORIMETRIC class ColorManager: rgb_monitor=None cmyk_rgb=None rgb_cmyk=None cmyk_monitor=None hRGB=None hCMYK=None hMONITOR=None colors_pool=[] image_pool=[] def __init__(self): self.refresh_profiles() def add_to_pool(self,color): self.colors_pool.append(color) def add_to_image_pool(self,image): self.image_pool.append(image) def remove_from_pool(self,color): if color in self.colors_pool: self.colors_pool.remove(color) def remove_from_image_pool(self,image): if image in self.image_pool: self.image_pool.remove(image) def update(self): for color in self.colors_pool: color.update() for image in self.image_pool: image.update() 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 self.hRGB = cmsOpenProfileFromFile(rgb_file, "r") else: self.hRGB = cmsCreateRGBProfile() 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 self.hCMYK = cmsOpenProfileFromFile(cmyk_file, "r") else: self.hCMYK = cmsCreateCMYKProfile() 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 self.hMONITOR = cmsOpenProfileFromFile(cmyk_file, "r") else: self.hMONITOR = cmsCreateRGBProfile() 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) def ImageRGBtoCMYK(self, image): return cmsDoBitmapTransform(self.rgb_cmyk, image, image.mode, TYPE_CMYK_8) def ImageCMYKtoRGB(self, image): return cmsDoBitmapTransform(self.cmyk_rgb, image, TYPE_CMYK_8, TYPE_RGB_8) uniconvertor-1.1.5/src/app/managers/__init__.py0000775000076400007640000000000011407115772020222 0ustar igorigoruniconvertor-1.1.5/src/app/modules/0000755000076400007640000000000011411006426015757 5ustar igorigoruniconvertor-1.1.5/src/app/modules/descr.txt0000664000076400007640000000006611407115772017636 0ustar igorigorAfter build this folder will contains native modules. uniconvertor-1.1.5/src/app/events/0000755000076400007640000000000011411006426015613 5ustar igorigoruniconvertor-1.1.5/src/app/events/skexceptions.py0000775000076400007640000000105611407115770020723 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.5/src/app/events/undodict.py0000775000076400007640000000302511407115770020013 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.5/src/app/events/undo.py0000775000076400007640000001321611407115770017152 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.5/src/app/events/warn.py0000775000076400007640000000467111407115770017161 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.5/src/app/events/connector.py0000775000076400007640000001063111407115770020175 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.5/src/app/events/__init__.py0000775000076400007640000000000011407115770017727 0ustar igorigoruniconvertor-1.1.5/src/app/Scripting/0000755000076400007640000000000011411006426016251 5ustar igorigoruniconvertor-1.1.5/src/app/Scripting/wrapper.py0000775000076400007640000000660111407115770020323 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.5/src/app/Scripting/registry.py0000775000076400007640000000362711407115770020520 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.5/src/app/Scripting/__init__.py0000775000076400007640000000174211407115770020403 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.5/src/app/Scripting/script.py0000775000076400007640000000414011407115770020143 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.5/src/app/VERSION0000664000076400007640000000000611410752553015365 0ustar igorigor1.1.5 uniconvertor-1.1.5/src/app/Graphics/0000755000076400007640000000000011411006426016047 5ustar igorigoruniconvertor-1.1.5/src/app/Graphics/clone.py0000775000076400007640000001042611407115770017541 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.5/src/app/Graphics/selinfo.py0000775000076400007640000001706011407115770020101 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.5/src/app/Graphics/dashes.py0000775000076400007640000000264011407115770017707 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.5/src/app/Graphics/compound.py0000775000076400007640000004646111407115770020275 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.5/src/app/Graphics/pagelayout.py0000775000076400007640000000450011407115770020607 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.5/src/app/Graphics/selection.py0000775000076400007640000007512711407115770020437 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 sk1libs.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.5/src/app/Graphics/blend.py0000775000076400007640000000305511407115770017525 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.5/src/app/Graphics/group.py0000775000076400007640000000304011407115770017567 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.5/src/app/Graphics/text.py0000775000076400007640000007627111407115770017437 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 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.replace('\t','') text=text.strip() if len(text)>25: text=text[:25]+'...' text=text.encode('utf-8') return (_("Text `%(text)s' at %(position)[position]"), {'text':text, '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.5/src/app/Graphics/rectangle.py0000775000076400007640000002642011407115770020406 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 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): if trafo is not None and trafo.m11==trafo.m21==trafo.m12==trafo.m22==0: trafo=Trafo(1,0,0,-1,trafo.v1,trafo.v2) 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): print 'TRAFO >>>>>>>>',trafo 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.5/src/app/Graphics/color.py0000775000076400007640000002677311407115770017573 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 from app import config, _ import app, string skvisual = None CMYK = 'CMYK' RGB = 'RGB' colormanager=app.colormanager def CreateRGBColor(r, g, b): return RGB_Color(round(r, 3), round(g, 3), round(b, 3)) def CreateRGBAColor(r, g, b, a): return RGB_Color(round(r, 3), round(g, 3), round(b, 3), round(a, 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 CreateCMYKAColor(c, m, y, k, a): return CMYK_Color(c, m, y, k, a) def CreateSPOTColor(r,g,b,c,m,y,k,name,palette): return SPOT_Color(r,g,b,c,m,y,k, alpha=1, name=name, palette=palette) def CreateSPOTAColor(r,g,b,c,m,y,k, alpha, name,palette): return SPOT_Color(r,g,b,c,m,y,k, alpha=alpha, 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=1, 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=1, 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': return CMYK_Color(v1, v2, v3, v4) if model=='CMYKA': return CMYK_Color(v1, v2, v3, v4, v5) if model=='RGB': return RGB_Color(round(v1, 3), round(v2, 3), round(v3, 3)) if model=='RGBA': return RGB_Color(round(v1, 3), round(v2, 3), round(v3, 3), round(v4, 3)) class SK1_Color: rgb=None rgba=None RGB_object=None def __init__(self): colormanager.add_to_pool(self) def __del__(self): if colormanager is not None: colormanager.remove_from_pool(self) def getScreenColor(self): pass def RGB(self): return self.RGB_object def cRGB(self): return self.rgb def cRGBA(self): return self.rgba def update(self): rgb=self.getScreenColor() self.RGB_object=rgb self.rgb=(rgb.red, rgb.green, rgb.blue) self.rgba=(rgb.red, rgb.green, rgb.blue, self.alpha) def Blend(self, color, frac1, frac2): if self.model==color.model==CMYK: return self.blend_cmyka(color, frac1, frac2) if self.model==color.model==RGB: return self.blend_rgba(color, frac1, frac2) if app.config.preferences.color_blending_rule: return self.blend_cmyka(color, frac1, frac2) else: return self.blend_rgba(color, frac1, frac2) def blend_cmyka(self, color, frac1, frac2): c1,m1,y1,k1=self.getCMYK() c2,m2,y2,k2=color.getCMYK() return CMYK_Color(c1*frac1+c2*frac2, m1*frac1+m2*frac2, y1*frac1+y2*frac2, k1*frac1+k2*frac2, self.alpha*frac1+color.alpha*frac2) def blend_rgba(self, color, frac1, frac2): r1,g1,b1 = self.getRGB() r2,g2,b2 = color.getRGB() return RGB_Color(r1*frac1+r2*frac2, g1*frac1+g2*frac2, b1*frac1+b2*frac2, self.alpha*frac1+color.alpha*frac2) class RGB_Color(SK1_Color): def __init__(self, r, g, b, alpha=1, name='Not defined'): SK1_Color.__init__(self) self.model = 'RGB' self.red=r self.green=g self.blue=b self.alpha=alpha self.name=name self.update() 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 getRGB(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 r, g, b else: r,g,b = app.colormanager.processRGB(self.red, self.green, self.blue) return r, g, b else: return self.red, self.green, self.blue 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(r, g, b) 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, 5))+',' G= str(round(self.green, 5))+',' B= str(round(self.blue, 5)) result='"'+self.model+'",'+R+G+B if self.alpha<1: result += ','+ str(round(self.alpha, 5)) return '('+result+')' class CMYK_Color(SK1_Color): def __init__(self, c, m, y, k, alpha=1, name='Not defined'): SK1_Color.__init__(self) self.model = 'CMYK' self.c=c self.m=m self.y=y self.k=k self.alpha=alpha self.name=name self.update() def getCMYK(self): return self.c, self.m, self.y, self.k def getRGB(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 r,g,b 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, 5))+',' M= str(round(self.m, 5))+',' Y= str(round(self.y, 5))+',' K= str(round(self.k, 5)) result='"'+self.model+'",'+C+M+Y+K if self.alpha<1: result += ','+ str(round(self.alpha, 5)) return '('+result+')' class SPOT_Color(SK1_Color): def __init__(self, r, g, b, c, m, y, k, alpha=1, name='Not defined', palette='Unknown'): SK1_Color.__init__(self) 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 self.update() def getCMYK(self): return self.c, self.m, self.y, self.k def getRGB(self): if app.config.preferences.use_cms: if app.config.preferences.simulate_printer: r,g,b = app.colormanager.processCMYK(c,m,y,k) return r, g, b else: return self.r, self.g, self.b else: return self.r, self.g, self.b 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, 5))+',' G= str(round(self.g, 5))+',' B= str(round(self.b, 5))+',' C= str(round(self.c, 5))+',' M= str(round(self.m, 5))+',' Y= str(round(self.y, 5))+',' K= str(round(self.k, 5)) result='"'+self.model+'",'+self.palette+'","'+self.name+'",'+R+G+B+C+M+Y+K if self.alpha<1: result += ','+ str(round(self.alpha, 5)) return '('+result+')' class Registration_Black(SPOT_Color): def __init__(self, r=0, g=0, b=0, c=1, m=1, y=1, k=1, alpha=1, name='All', palette='Unknown'): SPOT_Color.__init__(self,r, g, b, c, m, y, k, alpha, name, palette) def toString(self): return _('Registration Black (')+self.name + ')' # # 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): _init_from_widget_done = 1 uniconvertor-1.1.5/src/app/Graphics/pattern.py0000775000076400007640000004240011407115770020113 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, other.color, 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.5/src/app/Graphics/bezier.py0000775000076400007640000011567211407115770017732 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 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') 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")) 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, key_stroke = 'a') #AddCmd(commands, 'ContSmooth', _("Smooth"), SetContinuity, args = ContSmooth, key_stroke = 's') #AddCmd(commands, 'ContSymmetrical', _("Symmetrical"), SetContinuity, args = ContSymmetrical, 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') 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') 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"), 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 = '+') 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.5/src/app/Graphics/font.py0000775000076400007640000003014511407115770017407 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 sk1libs.utils.fs 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.5/src/app/Graphics/eps.py0000775000076400007640000000777211407115770017242 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 from sk1libs.imaging import Image from app.Lib import dscparser from sk1libs import 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 = 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 = 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.5/src/app/Graphics/plugobj.py0000775000076400007640000001345411407115770020107 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.5/src/app/Graphics/ellipse.py0000775000076400007640000003162111407115770020076 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 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 trafo is not None and trafo.m11==trafo.m21==trafo.m12==trafo.m22==0: trafo=Trafo(1,0,0,-1,trafo.v1,trafo.v2) 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.5/src/app/Graphics/image.py0000775000076400007640000001315411407115770017524 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, app from types import StringType from sk1libs.imaging import ImageChops from sk1libs import imaging from app import _, RegisterCommands, colormanager #from app.UI.command import AddCmd from external import ExternalData, get_cached, ExternalGraphics RGB_IMAGE=_('RGB') RGBA_IMAGE=_('RGBA') GRAYSCALE_IMAGE=_('Grayscale') CMYK_IMAGE=_('CMYK') BW_IMAGE=_('Monochrome') UNSUPPORTED=_('UNSUPPORTED') class ImageData(ExternalData): attributes = {'mode':0, 'size':0, 'im':0, 'info':0} cached =0 def __init__(self, image, filename = '', cache = 1): self.orig_image=image.copy() if image.mode=='1': self.image_mode=BW_IMAGE elif image.mode=='L': self.image_mode=GRAYSCALE_IMAGE elif image.mode=='RGB': self.image_mode=RGB_IMAGE elif image.mode=='RGBA': self.image_mode=RGBA_IMAGE elif image.mode=='CMYK': self.image_mode=CMYK_IMAGE colormanager.add_to_image_pool(self) self.cached=1 else: self.image_mode=UNSUPPORTED if image.mode not in ('RGB', 'RGBA'): image.load() if image.mode=='CMYK': if app.config.preferences.use_cms_for_bitmap: self.image=colormanager.ImageCMYKtoRGB(image) else: self.image = image.convert('RGB') else: self.image = image.convert('RGB') else: image.load() self.image = image if self.image_mode==UNSUPPORTED: self.orig_image=self.image.copy() self.image_mode=RGB_IMAGE ExternalData.__init__(self, filename, cache) def __del__(self): if self.cached and colormanager is not None: colormanager.remove_from_image_pool(self) def __getattr__(self, attr): if self.attributes.has_key(attr): return getattr(self.image, attr) raise AttributeError, attr def update(self): if app.config.preferences.use_cms_for_bitmap: if self.image_mode==CMYK_IMAGE: self.image=colormanager.ImageCMYKtoRGB(self.orig_image) else: self.image = self.orig_image.convert('RGB') else: self.image = self.orig_image.convert('RGB') def AsEmbedded(self): if self.filename: return ImageData(self.orig_image) else: return self def IsEmbedded(self): return not self.filename def Size(self): return self.size def Image(self): return self.orig_image def Convert(self, mode): if mode != self.orig_image.mode: if app.config.preferences.use_cms_for_bitmap: if mode=='RGB'and self.orig_image.mode=='CMYK': return ImageData(colormanager.ImageCMYKtoRGB(self.orig_image)) if mode=='CMYK'and self.orig_image.mode=='RGB': return ImageData(colormanager.ImageRGBtoCMYK(self.orig_image)) return ImageData(self.orig_image.convert(mode)) else: return ImageData(self.orig_image.convert(mode)) else: return self def Invert(self): return ImageData(ImageChops.invert(self.orig_image)) def load_image(filename, cache = 0): image = imaging.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) self.Embed() def DrawShape(self, device, rect = None, clip = 0): device.DrawImage(self.data, self.trafo, clip) def Info(self): mode=self.data.image_mode width, height = self.data.Size() x, y = self.trafo.offset() return _("Embedded %(mode)s image %(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()) def InvertImage(self): return self.SetData(self.data.Invert()) def Convert(self, image_mode): undo = (self.SetData, self.data) if image_mode==RGB_IMAGE: self.SetData(self.data.Convert('RGB')) if image_mode==RGBA_IMAGE: self.SetData(self.data.Convert('RGB')) if image_mode==GRAYSCALE_IMAGE: self.SetData(self.data.Convert('L')) if image_mode==CMYK_IMAGE: self.SetData(self.data.Convert('CMYK')) if image_mode==BW_IMAGE: self.SetData(self.data.Convert('1')) return undo def CallImageFunction(self, function, args = ()): if type(args) != type(()): args = (args,) data = apply(getattr(self.data, function), args) return self.SetData(data) uniconvertor-1.1.5/src/app/Graphics/maskgroup.py0000775000076400007640000001141211407115770020445 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.5/src/app/Graphics/guide.py0000775000076400007640000000533211407115770017536 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,1) def DrawDragged(self, device, partially): device.DrawGuideLine(self.drag_cur, self.horizontal,0) 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.5/src/app/Graphics/handle.py0000775000076400007640000001044011407115770017670 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.5/src/app/Graphics/curveop.py0000775000076400007640000000410711407115770020123 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.5/src/app/Graphics/base.py0000775000076400007640000006237411407115770017364 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.5/src/app/Graphics/external.py0000775000076400007640000001013311407115770020256 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.5/src/app/Graphics/document.py0000775000076400007640000023351211407115770020262 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_Blend: objs=object.Ungroup() for item in objs: objects+=self.ExtractNonGroup(item) elif 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 obj.__class__.__name__== aclass.EditedClass.__name__ else: return obj.__class__.__name__== aclass.__name__ 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() # # IMAGES MANAGMENT # ############ def CanBeRGB(self): from image import RGB_IMAGE, RGBA_IMAGE obj = self.CurrentObject() if obj: if obj.is_Image: if obj.data.image_mode==RGB_IMAGE or obj.data.image_mode==RGBA_IMAGE: return 0 else: return 1 return 0 def CanBeCMYK(self): from image import CMYK_IMAGE obj = self.CurrentObject() if obj: if obj.is_Image and not obj.data.image_mode==CMYK_IMAGE: return 1 return 0 def CanBeGrayscale(self): from image import GRAYSCALE_IMAGE obj = self.CurrentObject() if obj: if obj.is_Image and not obj.data.image_mode==GRAYSCALE_IMAGE: return 1 return 0 def CanBeBW(self): from image import BW_IMAGE obj = self.CurrentObject() if obj: if obj.is_Image and not obj.data.image_mode==BW_IMAGE: return 1 return 0 def ConvertImage(self, mode): obj = self.CurrentObject() self.CallObjectMethod(obj.__class__, _("Convert Image"), 'Convert', mode) # obj.Convert(mode) self.SelectNone() self.SelectObject(obj) def CanEmbed(self): obj = self.CurrentObject() if obj: if obj.is_Image and obj.CanEmbed(): return obj.CanEmbed() return 0 def Embed(self): obj = self.CurrentObject() obj.Embed() self.SelectNone() self.SelectObject(obj) def CanInvert(self): obj = self.CurrentObject() if obj: if obj.is_Image and obj.IsEmbedded(): return 1 return 0 def Invert(self): obj = self.CurrentObject() self.CallObjectMethod(obj.__class__, _("Invert Image"), 'InvertImage') app.mw.canvas.ForceRedraw() # # 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.5/src/app/Graphics/arrow.py0000775000076400007640000000437111407115770017575 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.5/src/app/Graphics/psdevice.py0000775000076400007640000005023011407115770020240 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 sk1libs.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 from sk1libs import ft2engine as 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.getRGB() 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.5/src/app/Graphics/properties.py0000775000076400007640000003161511407115770020640 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.5/src/app/Graphics/gradient.py0000775000076400007640000000715511407115770020243 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.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.Blend frac = (frac - pos1) / diff result.append(blend(color2, 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.5/src/app/Graphics/__init__.py0000775000076400007640000000000011407115770020163 0ustar igorigoruniconvertor-1.1.5/src/app/Graphics/blendgroup.py0000775000076400007640000002457311407115770020612 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.5/src/app/Graphics/layer.py0000775000076400007640000002560011407115770017555 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.5/src/app/Graphics/graphics.py0000775000076400007640000015606611407115770020254 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 from sk1libs import imaging #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 sk1libs.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 = imaging.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.5/src/app/Graphics/papersize.py0000775000076400007640000000550711407115770020447 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.5/src/app/__init__.py0000775000076400007640000001111111407315650016427 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 receiver=None def updateInfo(inf1='',inf2='',inf3=0): if not receiver is None: receiver(inf1,inf2,inf3) 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 sk1libs.ft2engine 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 sk1libs import filters # config.load_user_preferences() Issue(None, const.INITIALIZE) def init_ui(): pass def init_modules_from_widget(root): pass uniconvertor-1.1.5/src/share/0000755000076400007640000000000011411006426014631 5ustar igorigoruniconvertor-1.1.5/src/share/fonts/0000755000076400007640000000000011411006426015762 5ustar igorigoruniconvertor-1.1.5/src/share/fonts/descr.txt0000664000076400007640000000004611407115772017637 0ustar igorigorThis folder is planned for font info. uniconvertor-1.1.5/src/share/ps_templates/0000755000076400007640000000000011411006426017331 5ustar igorigoruniconvertor-1.1.5/src/share/ps_templates/sk1-proc.ps0000775000076400007640000002614111407115772021357 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.5/src/share/ps_templates/sketch-proc.ps0000775000076400007640000002574611407115772022154 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.5/src/uniconvertor.cmd0000664000076400007640000000113711410753301016755 0ustar igorigor:: -------------------------------------------------------------------- :: Wrapper script to execute UniConvertor application once it is installed :: :: Copyright (C) 2007-2010 Igor E. Novikov :: :: This library is covered by GNU Library General Public License. :: For more info see COPYRIGHTS file in uniconvertor root directory. :: --------------------------------------------------------------------- @echo off if "%~3"=="" ( pyVM -c "from uniconvertor import uniconv_run; uniconv_run();" "%~1" "%~2" ) else ( pyVM -c "from uniconvertor import uniconv_run; uniconv_run();" "%~1" "%~2" "%~3" ) uniconvertor-1.1.5/src/modules/0000755000076400007640000000000011411006426015177 5ustar igorigoruniconvertor-1.1.5/src/modules/pstokenize/0000755000076400007640000000000011411006426017372 5ustar igorigoruniconvertor-1.1.5/src/modules/pstokenize/pschartab.c0000664000076400007640000000505611407115774021531 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.5/src/modules/pstokenize/filterobj.h0000664000076400007640000001066711407115774021553 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.5/src/modules/pstokenize/pstokenize.c0000664000076400007640000004135511407115774021757 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.5/src/modules/skmod/0000755000076400007640000000000011411006426016314 5ustar igorigoruniconvertor-1.1.5/src/modules/skmod/sktrafo.c0000664000076400007640000003104511407115775020153 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.5/src/modules/skmod/sktrafo.h0000664000076400007640000000354311407115775020162 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.5/src/modules/skmod/curveobject.c0000664000076400007640000013641011407115775021017 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.5/src/modules/skmod/curveobject.h0000664000076400007640000000640611407115775021025 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.5/src/modules/skmod/Imaging.h0000664000076400007640000004452111407115775020065 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.5/src/modules/skmod/skaux.c0000664000076400007640000001332211407115775017633 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.5/src/modules/skmod/skaux.h0000664000076400007640000000262111407115775017640 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.5/src/modules/skmod/skimage.c0000664000076400007640000004525511407115775020132 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.5/src/modules/skmod/skimage.h0000664000076400007640000000271611407115775020132 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.5/src/modules/skmod/skpoint.c0000664000076400007640000003445311407115775020177 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.5/src/modules/skmod/skpoint.h0000664000076400007640000000315511407115775020177 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.5/src/modules/skmod/skcolor.c0000664000076400007640000001541711407115775020163 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.5/src/modules/skmod/skcolor.h0000664000076400007640000000306011407115775020157 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.5/src/modules/skmod/skrect.c0000664000076400007640000004202011407115775017770 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.5/src/modules/skmod/skrect.h0000664000076400007640000000366411407115775020010 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.5/src/modules/skmod/skfm.c0000664000076400007640000002242611407115775017445 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.5/src/modules/skmod/skfm.h0000664000076400007640000000223211407115775017443 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.5/src/modules/skmod/curvefunc.c0000664000076400007640000003100511407115775020476 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.5/src/modules/skmod/curvefunc.h0000664000076400007640000000251411407115775020506 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.5/src/modules/skmod/_sketchmodule.c0000664000076400007640000001155711407115775021336 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.5/src/modules/skmod/_sketchmodule.h0000664000076400007640000000215611407115775021336 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.5/src/modules/skmod/curvelow.c0000664000076400007640000002104611407115775020350 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.5/src/modules/skmod/curvelow.h0000664000076400007640000000116211407115775020352 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.5/src/modules/skmod/curvemisc.c0000664000076400007640000002350511407115775020504 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.5/src/modules/skmod/curvemisc.h0000664000076400007640000000227411407115775020511 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.5/src/modules/skmod/ImPlatform.h0000664000076400007640000000341011407115775020554 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.5/src/modules/type1mod/0000755000076400007640000000000011411006426016741 5ustar igorigoruniconvertor-1.1.5/src/modules/type1mod/_type1module.c0000664000076400007640000000760711407115772021542 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.5/src/modules/filter/0000755000076400007640000000000011411006426016464 5ustar igorigoruniconvertor-1.1.5/src/modules/filter/stringfilter.c0000664000076400007640000000453511407115774021371 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.5/src/modules/filter/stringfilter.h0000664000076400007640000000227211407115774021372 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.5/src/modules/filter/zlibfilter.c0000664000076400007640000000734611407115774021026 0ustar igorigor/* * Copyright (C) 1998, 1999, 2000, 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 "zlibfilter.h" #define INBUFSIZE 1024 typedef struct { char * buffer; size_t buffer_length; int eod_reached; z_stream zstr; } FlateDecodeState; static size_t read_zlib(void* clientdata, PyObject * source, char * buf, size_t length) { size_t bytesread = 0; int result; FlateDecodeState *state = (FlateDecodeState*)clientdata; if (state->eod_reached) return 0; state->zstr.next_out = buf; state->zstr.avail_out = length; do { if (state->zstr.avail_in == 0) { state->zstr.next_in = state->buffer; bytesread = Filter_Read(source, state->buffer, state->buffer_length); if (bytesread == 0) { if (PyErr_Occurred()) return 0; } state->zstr.avail_in = bytesread; } result = inflate(&state->zstr, Z_SYNC_FLUSH); if (result == Z_STREAM_END) { state->eod_reached = 1; } else if (result != Z_OK) { if (state->zstr.msg == Z_NULL) PyErr_Format(PyExc_IOError, "FlateDecode: Error %i", result); else PyErr_Format(PyExc_IOError, "FlateDecode: Error %i: %.200s", result, state->zstr.msg); return 0; } } while (state->zstr.avail_out == length && !state->eod_reached); return length - state->zstr.avail_out; } static void dealloc_zlib(void * clientdata) { FlateDecodeState * state = (FlateDecodeState*)clientdata; inflateEnd(&state->zstr); /* XXX error handling */ PyMem_Free(state->buffer); PyMem_Free(state); } PyObject * Filter_FlateDecode(PyObject * self, PyObject * args) { PyObject * target; FlateDecodeState * state; int result; if (!PyArg_ParseTuple(args, "O", &target)) return NULL; state = PyMem_Malloc(sizeof(FlateDecodeState)); if (!state) return PyErr_NoMemory(); state->buffer = PyMem_Malloc(INBUFSIZE); if (!state->buffer) { PyMem_Free(state); return PyErr_NoMemory(); } state->buffer_length = INBUFSIZE; state->zstr.zalloc = NULL; state->zstr.zfree = NULL; state->zstr.opaque = NULL; state->zstr.next_in = state->buffer; state->zstr.avail_in = 0; state->eod_reached = 0; result = inflateInit(&state->zstr); if (result != Z_OK) { if (result == Z_MEM_ERROR) { PyErr_SetString(PyExc_MemoryError, "FlateDecode: No memory for z_stream"); } else { if (state->zstr.msg == Z_NULL) PyErr_Format(PyExc_IOError, "FlateDecode: Zlib Error %i", result); else PyErr_Format(PyExc_IOError, "FlateDecode: Zlib Error %i: %.200s", result, state->zstr.msg); } PyMem_Free(state->buffer); PyMem_Free(state); return NULL; } return Filter_NewDecoder(target, "FlateDecode", 0, read_zlib, NULL, dealloc_zlib, state); } uniconvertor-1.1.5/src/modules/filter/zlibfilter.h0000664000076400007640000000226411407115774021025 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.5/src/modules/filter/binfile.c0000664000076400007640000003124611407115774020264 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.5/src/modules/filter/binfile.h0000664000076400007640000000224311407115774020264 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.5/src/modules/filter/ascii85filter.c0000664000076400007640000001525611407115774021332 0ustar igorigor/* * Copyright (C) 2000, 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. */ /* some small bits were taken from a public domain ASCII85 encoder by * Robert Kern */ #include #include "ascii85filter.h" #include "subfilefilter.h" #define FBUFLEN 1024 /* * ASCII85 Encode */ typedef struct { int column; int maxcolumn; unsigned long sum; int quartet; } ASCII85EncodeState; static const unsigned long eighty_five[5] = { 1L, 85L, 7225L, 614125L, 52200625L }; static size_t write_ASCII85(void* clientdata, PyObject * target, const char * buf, size_t length) { ASCII85EncodeState * state = (ASCII85EncodeState*)clientdata; unsigned long sum = state->sum; int quartet = state->quartet; int column = state->column; unsigned char encoded[FBUFLEN]; unsigned char * dest = (unsigned char *)encoded; unsigned char * src = (unsigned char *)buf; int i; unsigned long res; /* at each iteration we write at most 6 bytes into dest. */ while ((src - (unsigned char*)buf < length) && (dest - encoded) < FBUFLEN - 6) { sum = (sum << 8) + *src++; quartet++; if (quartet == 4) { if (sum == 0) { *dest++ = 'z'; column++; } else { for (i = 4; i >= 0; i--) { res = sum / eighty_five[i]; *dest++ = res + 33; sum -= res * eighty_five[i]; } sum = 0; column += 5; } if (column >= state->maxcolumn) { *dest++ = '\n'; column = 0; } quartet = 0; } } if (Filter_Write(target, encoded, dest - encoded) < 0) return 0; state->sum = sum; state->quartet = quartet; state->column = column; return src - (unsigned char*)buf; } static int close_ASCII85(void * clientdata, PyObject * target) { ASCII85EncodeState * state = (ASCII85EncodeState*)clientdata; unsigned char encoded[10]; unsigned char * dest = (unsigned char *)encoded; int i; unsigned long res; if (state->quartet) { state->sum <<= 8 * (4 - state->quartet); for (i = 4; i > (4 - state->quartet); i--) { res = state->sum / eighty_five[i]; *dest++ = (char)(res + 33); state->sum -= res * eighty_five[i]; } state->column += state->quartet + 1; if (state->column >= state->maxcolumn) { *dest++ = '\n'; state->column = 0; } if (Filter_Write(target, encoded, dest - encoded) == 0) return EOF; } if (Filter_Write(target, "~>\n", 3) == 0) return EOF; return 0; } PyObject * Filter_ASCII85Encode(PyObject * self, PyObject * args) { PyObject * target; ASCII85EncodeState * state; int maxcolumn = 72; if (!PyArg_ParseTuple(args, "O|i", &target, &maxcolumn)) return NULL; state = malloc(sizeof(ASCII85EncodeState)); if (!state) return PyErr_NoMemory(); state->maxcolumn = maxcolumn - (maxcolumn & 1); state->column = 0; state->sum = 0; state->quartet = 0; return Filter_NewEncoder(target, "ASCII85Encode", 0, write_ASCII85, close_ASCII85, free, state); } /* * ASCII85 Decode */ typedef struct { FilterObject * subfiledecode; int quintet; unsigned long sum; int eod; } ASCII85DecodeState; static size_t read_ASCII85(void * clientdata, PyObject * source, char * buf, size_t length) { unsigned char *dest = (unsigned char *)buf; ASCII85DecodeState * state = (ASCII85DecodeState*)clientdata; int quintet = state->quintet; unsigned long sum = state->sum; int byte; FilterObject * subfile = state->subfiledecode; if (state->eod) return 0; /* in ech iteration, at most 4 bytes are written to buf */ while (dest - (unsigned char *)buf < length - 4) { byte = Filter_GETC(subfile); if (byte >= '!' && byte <= 'u') { sum = sum * 85 + ((unsigned long)byte - '!'); quintet++; if (quintet == 5) { *dest++ = sum >> 24; *dest++ = (sum >> 16) & 0xFF; *dest++ = (sum >> 8) & 0xFF; *dest++ = sum & 0xFF; quintet = 0; sum = 0; } } else if (byte == 'z') { if (quintet) { PyErr_Format(PyExc_ValueError, "ASCII85Decode: z in wrong position"); return 0; } *dest++ = 0; *dest++ = 0; *dest++ = 0; *dest++ = 0; } else if (byte == EOF) { if (quintet == 1) { PyErr_Format(PyExc_ValueError, "ASCII85Decode: only 1 byte in last quintet"); return 0; } if (quintet) { int i; for (i = 0; i < 5 - quintet; i++) sum *= 85; if (quintet > 1) sum += (0xFFFFFF >> ((quintet - 2) * 8)); for (i = 0; i < quintet - 1; i++) { *dest++ = (sum >> (24 - 8 * i)) & 0xFF; } quintet = 0; } state->eod = 1; break; } else if (!isspace(byte)) { PyErr_Format(PyExc_ValueError, "ASCII85Decode: invalid character %x (hex)", byte); return 0; } } state->sum = sum; state->quintet = quintet; return dest - (unsigned char*)buf; } static PyObject * delimiter_string_object = NULL; PyObject * Filter_ASCII85Decode(PyObject * self, PyObject * args) { PyObject *source, *subfiledecode, *tuple; ASCII85DecodeState * state; if (!PyArg_ParseTuple(args, "O", &source)) return NULL; if (!delimiter_string_object) { delimiter_string_object = PyString_FromString("~>"); if (!delimiter_string_object) return NULL; } tuple = Py_BuildValue("OO", source, delimiter_string_object); if (!tuple) return NULL; subfiledecode = Filter_SubFileDecode(NULL, tuple); Py_DECREF(tuple); if (!subfiledecode) return NULL; state = malloc(sizeof(ASCII85DecodeState)); if (!state) return PyErr_NoMemory(); state->subfiledecode = (FilterObject*)subfiledecode; state->eod = 0; state->quintet = 0; state->sum = 0; return Filter_NewDecoder(source, "ASCII85Decode", 0, read_ASCII85, NULL, free, state); } uniconvertor-1.1.5/src/modules/filter/ascii85filter.h0000664000076400007640000000237311407115774021333 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.5/src/modules/filter/nullfilter.c0000664000076400007640000000375511407115774021040 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.5/src/modules/filter/nullfilter.h0000664000076400007640000000236311407115774021037 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.5/src/modules/filter/README0000664000076400007640000000051111407115774017357 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.5/src/modules/filter/streamfilter.c0000664000076400007640000000552611407115774021357 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.5/src/modules/filter/filterobj.c0000664000076400007640000005122511407115774020633 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.5/src/modules/filter/filterobj.h0000664000076400007640000001066711407115774020645 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.5/src/modules/filter/subfilefilter.c0000664000076400007640000000716611407115774021517 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.5/src/modules/filter/subfilefilter.h0000664000076400007640000000227611407115774021521 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.5/src/modules/filter/hexfilter.c0000664000076400007640000001136311407115774020644 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.5/src/modules/filter/hexfilter.h0000664000076400007640000000235511407115774020652 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.5/src/modules/filter/base64filter.c0000664000076400007640000001713511407115774021147 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.5/src/modules/filter/base64filter.h0000664000076400007640000000237411407115774021153 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.5/src/modules/filter/linefilter.c0000664000076400007640000000432111407115774021003 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.5/src/modules/filter/linefilter.h0000664000076400007640000000226311407115774021013 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.5/src/modules/skread/0000755000076400007640000000000011411006426016450 5ustar igorigoruniconvertor-1.1.5/src/modules/skread/skreadmodule.c0000664000076400007640000003121411407115774021312 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.5/src/GNU_LGPL_v20000775000076400007640000006127311407115776015364 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.5/src/COPYRIGHTS0000775000076400007640000000201711407115776015170 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.5/src/uniconvertor0000664000076400007640000000051011410753314016211 0ustar igorigor#! /usr/bin/python # # Wrapper script to execute UniConvertor application once it is installed # # Copyright (C) 2007-2010 Igor E. Novikov # # This library is covered by GNU Library General Public License. # For more info see COPYRIGHTS file in uniconvertor root directory. # from uniconvertor import uniconv_run uniconv_run()uniconvertor-1.1.5/src/GNU_GPL_v20000664000076400007640000004311111407115776015234 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.5/src/__init__.py0000775000076400007640000000553311407115776015671 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: uniconvertor [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 DXF - Autocad Drawing Exchange Format DST - Design format (Tajima) PES - Embroidery file format (Brother) EXP - Embroidery file format (Melco) PCS - Design format (Pfaff home) 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: uniconvertor 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_run(): _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 sk1libs import filters import app app.init_lib() filters.load_plugin_configuration() if len(options) and options[0]=='-parse': load.parse_drawing(input_file, output_file) sys.exit(0) else: doc = load.load_drawing(input_file) extension = os.path.splitext(output_file)[1] fileformat = filters.guess_export_plugin(extension) if fileformat: saver = filters.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.5/README0000664000076400007640000000166511407145041013634 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 binary DEB package: python setup.py bdist_deb ======================================== to create binary Win32 distribution: python setup.py bdist_wininst ======================================== help on available distribution formats: python setup.py bdist --help-formats uniconvertor-1.1.5/PKG-INFO0000644000076400007640000000460011411006426014035 0ustar igorigorMetadata-Version: 1.0 Name: uniconvertor Version: 1.1.5 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-2010 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) * Autocad Drawing Exchange Format (DXF) * Design format (Tajima) (DST) * Embroidery file format (Brother) (PES) * Embroidery file format (Melco) (EXP) * Design format (Pfaff home) (PCS) ------------------------------------------------------------------------------------ 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.5/setup.cfg0000664000076400007640000000016611407116016014570 0ustar igorigor[bdist_rpm] release = 1 packager = Igor Novikov provides = uniconvertor requires = sk1libs uniconvertor-1.1.5/MANIFEST.in0000664000076400007640000000131211407440374014506 0ustar igorigorinclude MANIFEST.in include README setup.cfg include DEBIAN/control include setup_win32.py include src/GNU_GPL_v2 src/GNU_LGPL_v2 src/COPYRIGHTS include src/uniconvertor include src/uniconvertor.cmd include src/modules/filter/README include src/app/modules/descr.txt include src/app/VERSION recursive-include src/share *.* recursive-include src/share/fonts *.* recursive-include src/share/ps_templates *.* recursive-include src/share/ps_templates *.* recursive-include src/modules/filter *.* recursive-include src/modules/skmod *.* recursive-include src/modules/skread *.* recursive-include src/modules/pstokenize *.* recursive-include src/modules/type1mod *.* recursive-include src/app/plugins/Filters *.py uniconvertor-1.1.5/setup.py0000664000076400007640000002271611407471027014474 0ustar igorigor#!/usr/bin/env python # # Setup script for UniConvertor # # Copyright (C) 2007-2010 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 binary DEB package: python setup.py bdist_deb # -------------------------------------------------------------------------- # 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 import sys COPY=False DEBIAN=False VERSION='1.1.5' ######################## # # Main build procedure # ######################## if __name__ == "__main__": if len(sys.argv)>1 and sys.argv[1]=='build©': COPY=True sys.argv[1]='build' if len(sys.argv)>1 and sys.argv[1]=='bdist_deb': DEBIAN=True sys.argv[1]='build' src_path='src/' import os if os.name == 'nt': script_name='src/uniconvertor.cmd' else: script_name='src/uniconvertor' 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 = VERSION, 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-2010 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) * Autocad Drawing Exchange Format (DXF) * Design format (Tajima) (DST) * Embroidery file format (Brother) (PES) * Embroidery file format (Melco) (EXP) * Design format (Pfaff home) (PCS) ------------------------------------------------------------------------------------ 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.scripts', 'uniconvertor.app.utils' ], package_dir = {'uniconvertor': 'src', 'uniconvertor.app': 'src/app', 'uniconvertor.app.modules': 'src/app/modules', }, package_data={'uniconvertor.app': ['VERSION'], 'uniconvertor': ['GNU_GPL_v2', 'GNU_LGPL_v2', 'COPYRIGHTS', 'share/icc/*.*', 'share/fonts/*.*', 'share/ps_templates/*.*'], 'sk1.app.modules': ['descr.txt',] }, scripts=[script_name], ext_modules = [filter_module, type1mod_module, skread_module, pstokenize_module, skmod_module]) ############################################## # This section for developing purpose only # Command 'python setup.py build©' allows # automating build and native extension copying # into package directory ############################################## if COPY: import shutil, string, platform version=(string.split(sys.version)[0])[0:3] shutil.copy('build/lib.linux-'+platform.machine()+'-'+version+'/uniconvertor/app/modules/pstokenize.so','src/app/modules/') print '\n pstokenize.so has been copied to src/ directory' shutil.copy('build/lib.linux-'+platform.machine()+'-'+version+'/uniconvertor/app/modules/_sketch.so','src/app/modules/') print '\n _sketchmodule.so has been copied to src/ directory' shutil.copy('build/lib.linux-'+platform.machine()+'-'+version+'/uniconvertor/app/modules/skread.so','src/app/modules/') print '\n skreadmodule.so has been copied to src/ directory' shutil.copy('build/lib.linux-'+platform.machine()+'-'+version+'/uniconvertor/app/modules/streamfilter.so','src/app/modules/') print '\n streamfilter.so has been copied to src/ directory' shutil.copy('build/lib.linux-'+platform.machine()+'-'+version+'/uniconvertor/app/modules/_type1.so','src/app/modules/') print '\n _type1module.so has been copied to src/ directory' os.system('rm -rf build') ################################################# # Implementation of bdist_deb command ################################################# if DEBIAN: print '\nDEBIAN PACKAGE BUILD' print '====================' import shutil, string, platform version=(string.split(sys.version)[0])[0:3] arch,bin = platform.architecture() if arch=='64bit': arch='amd64' else: arch='i386' target='build/deb-root/usr/lib/python'+version+'/dist-packages' if os.path.lexists(os.path.join('build','deb-root')): os.system('rm -rf build/deb-root') os.makedirs(os.path.join('build','deb-root','DEBIAN')) os.system("cat DEBIAN/control |sed 's//"+arch+"/g'|sed 's//"+VERSION+"/g'> build/deb-root/DEBIAN/control") os.makedirs(target) os.makedirs('build/deb-root/usr/bin') os.system('cp -R build/lib.linux-'+platform.machine()+'-'+version+'/uniconvertor '+target) os.system('cp src/uniconvertor build/deb-root/usr/bin') os.system('chmod +x build/deb-root/usr/bin/uniconvertor') if os.path.lexists('dist'): os.system('rm -rf dist/*.deb') else: os.makedirs('dist') os.system('dpkg --build build/deb-root/ dist/python-uniconvertor-'+VERSION+'_'+arch+'.deb') uniconvertor-1.1.5/DEBIAN/0000755000076400007640000000000011411006426013662 5ustar igorigoruniconvertor-1.1.5/DEBIAN/control0000664000076400007640000000274111407115761015303 0ustar igorigorPackage: python-uniconvertor Version: Architecture: Maintainer: Igor Novikov Depends: libc6, python (>=2.4), python (<<3.0), python-sk1libs Section: python Priority: extra Description: Universal vector graphics translator . 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-2010 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) * Autocad Drawing Exchange Format (DXF) * Design format (Tajima) (DST) * Embroidery file format (Brother) (PES) * Embroidery file format (Melco) (EXP) * Design format (Pfaff home) (PCS) ----------------------------------- 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 .