# 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 Sketch import _, SolidPattern, StandardColors, CreateListUndo, Undo, \ PostScriptDevice from Sketch.Lib import util from Sketch.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 = util.get_real_username(), CreationDate = util.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 Sketch.Scripting Sketch.Scripting.AddFunction('simple_separation', _("Simple Separation"), simple_separation, script_type = Sketch.Scripting.AdvancedScript)