psdevice.py :  » GUI » Sketch » skencil-0.6.17 » Sketch » Graphics » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » GUI » Sketch 
Sketch » skencil 0.6.17 » Sketch » Graphics » psdevice.py
# 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 Sketch.Lib.util import Empty

from Sketch import _,config,_sketch,Scale,SketchVersion
from Sketch.warn import pdebug,warn_tb,warn,USER,INTERNAL
from Sketch.Lib.psmisc import quote_ps_string,make_textline

from Sketch import IdentityMatrix
from Sketch.Lib import type1

from graphics import CommonDevice
import color
import pagelayout
import font


class _DummyLineStyle:
    def Execute(device):
  device.SetLineColor(color.StandardColors.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 <textline>
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: Skencil %s\n" % SketchVersion)
  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.sketch_dir, 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:
      r, g, b = color
      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
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.