~ubuntu-branches/ubuntu/trusty/matplotlib/trusty-proposed

« back to all changes in this revision

Viewing changes to lib/matplotlib/backends/backend_ps.py

  • Committer: Package Import Robot
  • Author(s): Sandro Tosi
  • Date: 2011-01-25 18:17:56 UTC
  • mfrom: (0.1.1) (1.2.4) (9.1.7 sid)
  • Revision ID: package-import@ubuntu.com-20110125181756-r2ckh9qfadms2k3x
Tags: 1.0.1-1
* New upstream release; Closes: #606116
* debian/patches/{10_build_fix.patch, 20_matplotlibrc_path_search_fix.patch}
  - updated to new upstream code
* debian/patches/30_doc_pass_dpi.patch
  - removed, merged upstream
* debian/rules
  - don't compress objects.inv; thanks to Michael Fladischer for the report;
    Closes: #608760
* debian/rules, debian/patches/30_disable_sample_downloads.patch
  - bypass examples data download from internet, but use local copy instead
* debian/python-matplotlib-data.install
  - install examples sample data
* debian/control
  - bump Standards-Version: to 3.9.1 (no changes needed)
  - removed DM-U-A flag, no more needed
  - added python-xlwt to b-d, needed to build doc examples
  - Benjamin Drung removed himself from the Uploaders list
* debian/copyright
  - updated debian packaging copyright
* debian/patches/40_bts608939_draw_markers_description.patch
  - fix a glitch (missing reference to documentation page) in draw_markers()
    description; thanks to Jakub Wilk for report and patch; Closes: #608939
* debian/patches/50_bts608942_spaces_in_param_args.patch
  - don't separate param and its argument with a space, new sphinx consider
    the first argument as the type; thanks to Jakub Wilk for the report;
    Closes: #608942
* debian/patches/60_doc_output_format.patch
  - obtain the documentation output format even if the value is a unicode (as
    returned by recent sphinx); thanks to Jakub Wilk for the tip on IRC

Show diffs side-by-side

added added

removed removed

Lines of Context:
11
11
except ImportError:
12
12
    from md5 import md5 #Deprecated in 2.5
13
13
 
14
 
from tempfile import gettempdir
 
14
from tempfile import mkstemp
15
15
from cStringIO import StringIO
16
16
from matplotlib import verbose, __version__, rcParams
17
17
from matplotlib._pylab_helpers import Gcf
31
31
from matplotlib._mathtext_data import uni2type1
32
32
from matplotlib.text import Text
33
33
from matplotlib.path import Path
34
 
from matplotlib.transforms import IdentityTransform
35
 
 
36
 
import numpy as npy
 
34
from matplotlib.transforms import Affine2D
 
35
 
 
36
from matplotlib.backends.backend_mixed import MixedModeRenderer
 
37
 
 
38
 
 
39
import numpy as np
37
40
import binascii
38
41
import re
39
42
try:
112
115
 
113
116
def seq_allequal(seq1, seq2):
114
117
    """
115
 
    seq1 and seq2 are either None or sequences or numerix arrays
 
118
    seq1 and seq2 are either None or sequences or arrays
116
119
    Return True if both are None or both are seqs with identical
117
120
    elements
118
121
    """
124
127
    #ok, neither are None:, assuming iterable
125
128
 
126
129
    if len(seq1) != len(seq2): return False
127
 
    return npy.alltrue(npy.equal(seq1, seq2))
 
130
    return np.alltrue(np.equal(seq1, seq2))
128
131
 
129
132
 
130
133
class RendererPS(RendererBase):
150
153
            self.textcnt = 0
151
154
            self.psfrag = []
152
155
        self.imagedpi = imagedpi
153
 
        if rcParams['path.simplify']:
154
 
            self.simplify = (width * imagedpi, height * imagedpi)
155
 
        else:
156
 
            self.simplify = None
157
156
 
158
157
        # current renderer state (None=uninitialised)
159
158
        self.color = None
163
162
        self.linedash = None
164
163
        self.fontname = None
165
164
        self.fontsize = None
166
 
        self.hatch = None
 
165
        self._hatches = {}
167
166
        self.image_magnification = imagedpi/72.0
168
167
        self._clip_paths = {}
169
168
        self._path_collection_id = 0
171
170
        self.used_characters = {}
172
171
        self.mathtext_parser = MathTextParser("PS")
173
172
 
 
173
        self._afm_font_dir = os.path.join(
 
174
            rcParams['datapath'], 'fonts', 'afm')
 
175
 
174
176
    def track_characters(self, font, s):
175
177
        """Keeps track of which characters are required from
176
178
        each font."""
231
233
            if store: self.fontname = fontname
232
234
            if store: self.fontsize = fontsize
233
235
 
234
 
    def set_hatch(self, hatch):
235
 
        """
236
 
        hatch can be one of:
237
 
            /   - diagonal hatching
238
 
            \   - back diagonal
239
 
            |   - vertical
240
 
            -   - horizontal
241
 
            +   - crossed
242
 
            X   - crossed diagonal
243
 
 
244
 
        letters can be combined, in which case all the specified
245
 
        hatchings are done
246
 
 
247
 
        if same letter repeats, it increases the density of hatching
248
 
        in that direction
249
 
        """
250
 
        hatches = {'horiz':0, 'vert':0, 'diag1':0, 'diag2':0}
251
 
 
252
 
        for letter in hatch:
253
 
            if   (letter == '/'):    hatches['diag2'] += 1
254
 
            elif (letter == '\\'):   hatches['diag1'] += 1
255
 
            elif (letter == '|'):    hatches['vert']  += 1
256
 
            elif (letter == '-'):    hatches['horiz'] += 1
257
 
            elif (letter == '+'):
258
 
                hatches['horiz'] += 1
259
 
                hatches['vert'] += 1
260
 
            elif (letter.lower() == 'x'):
261
 
                hatches['diag1'] += 1
262
 
                hatches['diag2'] += 1
263
 
 
264
 
        def do_hatch(angle, density):
265
 
            if (density == 0): return ""
266
 
            return """\
267
 
  gsave
268
 
   eoclip %s rotate 0.0 0.0 0.0 0.0 setrgbcolor 0 setlinewidth
269
 
   /hatchgap %d def
270
 
   pathbbox /hatchb exch def /hatchr exch def /hatcht exch def /hatchl exch def
271
 
   hatchl cvi hatchgap idiv hatchgap mul
272
 
   hatchgap
273
 
   hatchr cvi hatchgap idiv hatchgap mul
274
 
   {hatcht m 0 hatchb hatcht sub r }
275
 
   for
276
 
   stroke
277
 
  grestore
278
 
 """ % (angle, 12/density)
279
 
        self._pswriter.write("gsave\n")
280
 
        self._pswriter.write(do_hatch(90, hatches['horiz']))
281
 
        self._pswriter.write(do_hatch(0, hatches['vert']))
282
 
        self._pswriter.write(do_hatch(45, hatches['diag1']))
283
 
        self._pswriter.write(do_hatch(-45, hatches['diag2']))
284
 
        self._pswriter.write("grestore\n")
 
236
    def create_hatch(self, hatch):
 
237
        sidelen = 72
 
238
        if self._hatches.has_key(hatch):
 
239
            return self._hatches[hatch]
 
240
        name = 'H%d' % len(self._hatches)
 
241
        self._pswriter.write("""\
 
242
  << /PatternType 1
 
243
     /PaintType 2
 
244
     /TilingType 2
 
245
     /BBox[0 0 %(sidelen)d %(sidelen)d]
 
246
     /XStep %(sidelen)d
 
247
     /YStep %(sidelen)d
 
248
 
 
249
     /PaintProc {
 
250
        pop
 
251
        0 setlinewidth
 
252
""" % locals())
 
253
        self._pswriter.write(
 
254
            self._convert_path(Path.hatch(hatch), Affine2D().scale(72.0),
 
255
                               simplify=False))
 
256
        self._pswriter.write("""\
 
257
          stroke
 
258
     } bind
 
259
   >>
 
260
   matrix
 
261
   makepattern
 
262
   /%(name)s exch def
 
263
""" % locals())
 
264
        self._hatches[hatch] = name
 
265
        return name
285
266
 
286
267
    def get_canvas_width_height(self):
287
268
        'return the canvas width and height in display coords'
296
277
        if rcParams['text.usetex']:
297
278
            texmanager = self.get_texmanager()
298
279
            fontsize = prop.get_size_in_points()
299
 
            l,b,r,t = texmanager.get_ps_bbox(s, fontsize)
300
 
            w = (r-l)
301
 
            h = (t-b)
302
 
            # TODO: We need a way to get a good baseline from
303
 
            # text.usetex
304
 
            return w, h, 0
 
280
            w, h, d = texmanager.get_text_width_height_descent(s, fontsize,
 
281
                                                               renderer=self)
 
282
            return w, h, d
305
283
 
306
284
        if ismath:
307
285
            width, height, descent, pswriter, used_characters = \
338
316
        key = hash(prop)
339
317
        font = self.afmfontd.get(key)
340
318
        if font is None:
341
 
            fname = findfont(prop, fontext='afm')
 
319
            fname = findfont(prop, fontext='afm', directory=self._afm_font_dir)
 
320
            if fname is None:
 
321
                fname = findfont(
 
322
                    "Helvetica", fontext='afm', directory=self._afm_font_dir)
342
323
            font = self.afmfontd.get(fname)
343
324
            if font is None:
344
 
                font = AFM(file(findfont(prop, fontext='afm')))
 
325
                font = AFM(file(fname))
345
326
                self.afmfontd[fname] = font
346
327
            self.afmfontd[key] = font
347
328
        return font
367
348
    def _rgb(self, im):
368
349
        h,w,s = im.as_rgba_str()
369
350
 
370
 
        rgba = npy.fromstring(s, npy.uint8)
 
351
        rgba = np.fromstring(s, np.uint8)
371
352
        rgba.shape = (h, w, 4)
372
353
        rgb = rgba[:,:,:3]
373
354
        return h, w, rgb.tostring()
374
355
 
375
356
    def _gray(self, im, rc=0.3, gc=0.59, bc=0.11):
376
357
        rgbat = im.as_rgba_str()
377
 
        rgba = npy.fromstring(rgbat[2], npy.uint8)
 
358
        rgba = np.fromstring(rgbat[2], np.uint8)
378
359
        rgba.shape = (rgbat[0], rgbat[1], 4)
379
 
        rgba_f = rgba.astype(npy.float32)
 
360
        rgba_f = rgba.astype(np.float32)
380
361
        r = rgba_f[:,:,0]
381
362
        g = rgba_f[:,:,1]
382
363
        b = rgba_f[:,:,2]
383
 
        gray = (r*rc + g*gc + b*bc).astype(npy.uint8)
 
364
        gray = (r*rc + g*gc + b*bc).astype(np.uint8)
384
365
        return rgbat[0], rgbat[1], gray.tostring()
385
366
 
386
367
    def _hex_lines(self, s, chars_per_line=128):
400
381
        """
401
382
        return self.image_magnification
402
383
 
403
 
    def draw_image(self, x, y, im, bbox, clippath=None, clippath_trans=None):
404
 
        """
405
 
        Draw the Image instance into the current axes; x is the
406
 
        distance in pixels from the left hand side of the canvas and y
407
 
        is the distance from bottom
408
 
 
409
 
        bbox is a matplotlib.transforms.BBox instance for clipping, or
410
 
        None
411
 
        """
412
 
 
413
 
        im.flipud_out()
414
 
 
 
384
    def option_scale_image(self):
 
385
        """
 
386
        ps backend support arbitrary scaling of image.
 
387
        """
 
388
        return True
 
389
 
 
390
    def _get_image_h_w_bits_command(self, im):
415
391
        if im.is_grayscale:
416
392
            h, w, bits = self._gray(im)
417
393
            imagecmd = "image"
418
394
        else:
419
395
            h, w, bits = self._rgb(im)
420
396
            imagecmd = "false 3 colorimage"
 
397
 
 
398
        return h, w, bits, imagecmd
 
399
 
 
400
    def draw_image(self, gc, x, y, im, dx=None, dy=None, transform=None):
 
401
        """
 
402
        Draw the Image instance into the current axes; x is the
 
403
        distance in pixels from the left hand side of the canvas and y
 
404
        is the distance from bottom
 
405
 
 
406
        dx, dy is the width and height of the image.  If a transform
 
407
        (which must be an affine transform) is given, x, y, dx, dy are
 
408
        interpreted as the coordinate of the transform.
 
409
        """
 
410
 
 
411
        im.flipud_out()
 
412
 
 
413
        h, w, bits, imagecmd = self._get_image_h_w_bits_command(im)
421
414
        hexlines = '\n'.join(self._hex_lines(bits))
422
415
 
423
 
        xscale, yscale = (
424
 
            w/self.image_magnification, h/self.image_magnification)
 
416
        if dx is None:
 
417
            xscale = w / self.image_magnification
 
418
        else:
 
419
            xscale = dx
 
420
 
 
421
        if dy is None:
 
422
            yscale = h/self.image_magnification
 
423
        else:
 
424
            yscale = dy
 
425
 
 
426
 
 
427
        if transform is None:
 
428
            matrix = "1 0 0 1 0 0"
 
429
        else:
 
430
            matrix = " ".join(map(str, transform.to_values()))
425
431
 
426
432
        figh = self.height*72
427
433
        #print 'values', origin, flipud, figh, h, y
428
434
 
 
435
        bbox = gc.get_clip_rectangle()
 
436
        clippath, clippath_trans = gc.get_clip_path()
 
437
 
429
438
        clip = []
430
439
        if bbox is not None:
431
440
            clipx,clipy,clipw,cliph = bbox.bounds
438
447
        #y = figh-(y+h)
439
448
        ps = """gsave
440
449
%(clip)s
 
450
[%(matrix)s] concat
441
451
%(x)s %(y)s translate
442
452
%(xscale)s %(yscale)s scale
443
453
/DataString %(w)s string def
453
463
        # unflip
454
464
        im.flipud_out()
455
465
 
456
 
    def _convert_path(self, path, transform, simplify=None):
457
 
        path = transform.transform_path(path)
458
 
 
 
466
    def _convert_path(self, path, transform, clip=False, simplify=None):
459
467
        ps = []
460
468
        last_points = None
461
 
        for points, code in path.iter_segments(simplify):
 
469
        if clip:
 
470
            clip = (0.0, 0.0, self.width * 72.0,
 
471
                    self.height * 72.0)
 
472
        else:
 
473
            clip = None
 
474
        for points, code in path.iter_segments(transform, clip=clip,
 
475
                                               simplify=simplify):
462
476
            if code == Path.MOVETO:
463
477
                ps.append("%g %g m" % tuple(points))
464
478
            elif code == Path.LINETO:
481
495
        if id is None:
482
496
            id = 'c%x' % len(self._clip_paths)
483
497
            ps_cmd = ['/%s {' % id]
484
 
            ps_cmd.append(self._convert_path(clippath, clippath_transform))
 
498
            ps_cmd.append(self._convert_path(clippath, clippath_transform,
 
499
                                             simplify=False))
485
500
            ps_cmd.extend(['clip', 'newpath', '} bind def\n'])
486
501
            self._pswriter.write('\n'.join(ps_cmd))
487
502
            self._clip_paths[(clippath, clippath_transform)] = id
491
506
        """
492
507
        Draws a Path instance using the given affine transform.
493
508
        """
494
 
        ps = self._convert_path(path, transform, self.simplify)
 
509
        clip = (rgbFace is None and gc.get_hatch_path() is None)
 
510
        simplify = path.should_simplify and clip
 
511
        ps = self._convert_path(
 
512
            path, transform, clip=clip, simplify=simplify)
495
513
        self._draw_ps(ps, gc, rgbFace)
496
514
 
497
515
    def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None):
512
530
 
513
531
        # construct the generic marker command:
514
532
        ps_cmd = ['/o {', 'gsave', 'newpath', 'translate'] # dont want the translate to be global
515
 
        ps_cmd.append(self._convert_path(marker_path, marker_trans))
 
533
        ps_cmd.append(self._convert_path(marker_path, marker_trans,
 
534
                                         simplify=False))
516
535
 
517
536
        if rgbFace:
518
537
            ps_cmd.extend(['gsave', ps_color, 'fill', 'grestore'])
519
538
 
520
539
        ps_cmd.extend(['stroke', 'grestore', '} bind def'])
521
540
 
522
 
        tpath = trans.transform_path(path)
523
 
        for vertices, code in tpath.iter_segments():
 
541
        for vertices, code in path.iter_segments(trans, simplify=False):
524
542
            if len(vertices):
525
543
                x, y = vertices[-2:]
526
544
                ps_cmd.append("%g %g o" % (x, y))
528
546
        ps = '\n'.join(ps_cmd)
529
547
        self._draw_ps(ps, gc, rgbFace, fill=False, stroke=False)
530
548
 
531
 
    def draw_path_collection(self, master_transform, cliprect, clippath,
532
 
                             clippath_trans, paths, all_transforms, offsets,
533
 
                             offsetTrans, facecolors, edgecolors, linewidths,
534
 
                             linestyles, antialiaseds, urls):
 
549
    def draw_path_collection(self, gc, master_transform, paths, all_transforms,
 
550
                             offsets, offsetTrans, facecolors, edgecolors,
 
551
                             linewidths, linestyles, antialiaseds, urls):
535
552
        write = self._pswriter.write
536
553
 
537
554
        path_codes = []
540
557
            name = 'p%x_%x' % (self._path_collection_id, i)
541
558
            ps_cmd = ['/%s {' % name,
542
559
                      'newpath', 'translate']
543
 
            ps_cmd.append(self._convert_path(path, transform))
 
560
            ps_cmd.append(self._convert_path(path, transform, simplify=False))
544
561
            ps_cmd.extend(['} bind def\n'])
545
562
            write('\n'.join(ps_cmd))
546
563
            path_codes.append(name)
547
564
 
548
 
        for xo, yo, path_id, gc, rgbFace in self._iter_collection(
549
 
            path_codes, cliprect, clippath, clippath_trans,
550
 
            offsets, offsetTrans, facecolors, edgecolors,
 
565
        for xo, yo, path_id, gc0, rgbFace in self._iter_collection(
 
566
            gc, path_codes, offsets, offsetTrans, facecolors, edgecolors,
551
567
            linewidths, linestyles, antialiaseds, urls):
552
 
 
553
568
            ps = "%g %g %s" % (xo, yo, path_id)
554
 
            self._draw_ps(ps, gc, rgbFace)
 
569
            self._draw_ps(ps, gc0, rgbFace)
555
570
 
556
571
        self._path_collection_id += 1
557
572
 
561
576
        """
562
577
        w, h, bl = self.get_text_width_height_descent(s, prop, ismath)
563
578
        fontsize = prop.get_size_in_points()
564
 
        corr = 0#w/2*(fontsize-10)/10
565
 
        pos = _nums_to_str(x-corr, y)
566
579
        thetext = 'psmarker%d' % self.textcnt
567
580
        color = '%1.3f,%1.3f,%1.3f'% gc.get_rgb()[:3]
568
581
        fontcmd = {'sans-serif' : r'{\sffamily %s}',
570
583
                rcParams['font.family'], r'{\rmfamily %s}')
571
584
        s = fontcmd % s
572
585
        tex = r'\color[rgb]{%s} %s' % (color, s)
573
 
        self.psfrag.append(r'\psfrag{%s}[bl][bl][1][%f]{\fontsize{%f}{%f}%s}'%(thetext, angle, fontsize, fontsize*1.25, tex))
 
586
 
 
587
        corr = 0#w/2*(fontsize-10)/10
 
588
        if rcParams['text.latex.preview']:
 
589
            # use baseline alignment!
 
590
            pos = _nums_to_str(x-corr, y+bl)
 
591
            self.psfrag.append(r'\psfrag{%s}[Bl][Bl][1][%f]{\fontsize{%f}{%f}%s}'%(thetext, angle, fontsize, fontsize*1.25, tex))
 
592
        else:
 
593
            # stick to the bottom alignment, but this may give incorrect baseline some times.
 
594
            pos = _nums_to_str(x-corr, y)
 
595
            self.psfrag.append(r'\psfrag{%s}[bl][bl][1][%f]{\fontsize{%f}{%f}%s}'%(thetext, angle, fontsize, fontsize*1.25, tex))
 
596
 
574
597
        ps = """\
575
598
gsave
576
599
%(pos)s moveto
587
610
        draw a Text instance
588
611
        """
589
612
        # local to avoid repeated attribute lookups
590
 
 
591
 
 
592
613
        write = self._pswriter.write
593
614
        if debugPS:
594
615
            write("% text\n")
599
620
        elif ismath:
600
621
            return self.draw_mathtext(gc, x, y, s, prop, angle)
601
622
 
602
 
        elif isinstance(s, unicode):
603
 
            return self.draw_unicode(gc, x, y, s, prop, angle)
604
 
 
605
623
        elif rcParams['ps.useafm']:
606
 
            font = self._get_font_afm(prop)
607
 
 
608
 
            l,b,w,h = font.get_str_bbox(s)
609
 
 
610
 
            fontsize = prop.get_size_in_points()
611
 
            l *= 0.001*fontsize
612
 
            b *= 0.001*fontsize
613
 
            w *= 0.001*fontsize
614
 
            h *= 0.001*fontsize
615
 
 
616
 
            if angle==90: l,b = -b, l # todo generalize for arb rotations
617
 
 
618
 
            pos = _nums_to_str(x-l, y-b)
619
 
            thetext = '(%s)' % s
620
 
            fontname = font.get_fontname()
621
 
            fontsize = prop.get_size_in_points()
622
 
            rotate = '%1.1f rotate' % angle
623
 
            setcolor = '%1.3f %1.3f %1.3f setrgbcolor' % gc.get_rgb()[:3]
624
 
            #h = 0
625
 
            ps = """\
626
 
gsave
627
 
/%(fontname)s findfont
628
 
%(fontsize)s scalefont
629
 
setfont
630
 
%(pos)s moveto
631
 
%(rotate)s
632
 
%(thetext)s
633
 
%(setcolor)s
634
 
show
635
 
grestore
636
 
    """ % locals()
637
 
            self._draw_ps(ps, gc, None)
638
 
 
639
 
        else:
640
 
            font = self._get_font_ttf(prop)
641
 
            font.set_text(s, 0, flags=LOAD_NO_HINTING)
642
 
            self.track_characters(font, s)
643
 
 
644
 
            self.set_color(*gc.get_rgb())
645
 
            self.set_font(font.get_sfnt()[(1,0,0,6)], prop.get_size_in_points())
646
 
            write("%s m\n"%_nums_to_str(x,y))
647
 
            if angle:
648
 
                write("gsave\n")
649
 
                write("%s rotate\n"%_num_to_str(angle))
650
 
            descent = font.get_descent() / 64.0
651
 
            if descent:
652
 
                write("0 %s rmoveto\n"%_num_to_str(descent))
653
 
            write("(%s) show\n"%quote_ps_string(s))
654
 
            if angle:
655
 
                write("grestore\n")
656
 
 
657
 
    def new_gc(self):
658
 
        return GraphicsContextPS()
659
 
 
660
 
    def draw_unicode(self, gc, x, y, s, prop, angle):
661
 
        """draw a unicode string.  ps doesn't have unicode support, so
662
 
        we have to do this the hard way
663
 
        """
664
 
        if rcParams['ps.useafm']:
665
624
            self.set_color(*gc.get_rgb())
666
625
 
667
626
            font = self._get_font_afm(prop)
749
708
""" % locals()
750
709
            self._pswriter.write(ps)
751
710
 
 
711
    def new_gc(self):
 
712
        return GraphicsContextPS()
 
713
 
752
714
    def draw_mathtext(self, gc,
753
715
        x, y, s, prop, angle):
754
716
        """
770
732
""" % locals()
771
733
        self._pswriter.write(ps)
772
734
 
 
735
    def draw_gouraud_triangle(self, gc, points, colors, trans):
 
736
        self.draw_gouraud_triangles(gc, points.reshape((1, 3, 2)),
 
737
                                    colors.reshape((1, 3, 4)), trans)
 
738
 
 
739
    def draw_gouraud_triangles(self, gc, points, colors, trans):
 
740
        assert len(points) == len(colors)
 
741
        assert points.ndim == 3
 
742
        assert points.shape[1] == 3
 
743
        assert points.shape[2] == 2
 
744
        assert colors.ndim == 3
 
745
        assert colors.shape[1] == 3
 
746
        assert colors.shape[2] == 4
 
747
 
 
748
        points = trans.transform(points)
 
749
 
 
750
        shape = points.shape
 
751
        flat_points = points.reshape((shape[0] * shape[1], 2))
 
752
        flat_colors = colors.reshape((shape[0] * shape[1], 4))
 
753
        points_min = np.min(flat_points, axis=0) - (1 << 8)
 
754
        points_max = np.max(flat_points, axis=0) + (1 << 8)
 
755
        factor = float(0xffffffff) / (points_max - points_min)
 
756
 
 
757
        xmin, ymin = points_min
 
758
        xmax, ymax = points_max
 
759
 
 
760
        streamarr = np.empty(
 
761
            (shape[0] * shape[1],),
 
762
            dtype=[('flags', 'u1'),
 
763
                   ('points', '>u4', (2,)),
 
764
                   ('colors', 'u1', (3,))])
 
765
        streamarr['flags'] = 0
 
766
        streamarr['points'] = (flat_points - points_min) * factor
 
767
        streamarr['colors'] = flat_colors[:, :3] * 255.0
 
768
 
 
769
        stream = quote_ps_string(streamarr.tostring())
 
770
 
 
771
        self._pswriter.write("""
 
772
gsave
 
773
<< /ShadingType 4
 
774
   /ColorSpace [/DeviceRGB]
 
775
   /BitsPerCoordinate 32
 
776
   /BitsPerComponent 8
 
777
   /BitsPerFlag 8
 
778
   /AntiAlias true
 
779
   /Decode [ %(xmin)f %(xmax)f %(ymin)f %(ymax)f 0 1 0 1 0 1 ]
 
780
   /DataSource (%(stream)s)
 
781
>>
 
782
shfill
 
783
grestore
 
784
""" % locals())
 
785
 
773
786
    def _draw_ps(self, ps, gc, rgbFace, fill=True, stroke=True, command=None):
774
787
        """
775
788
        Emit the PostScript sniplet 'ps' with all the attributes from 'gc'
816
829
        if fill:
817
830
            if stroke:
818
831
                write("gsave\n")
819
 
                self.set_color(store=0, *rgbFace[:3])
820
 
                write("fill\ngrestore\n")
821
 
            else:
822
 
                self.set_color(store=0, *rgbFace[:3])
823
 
                write("fill\n")
 
832
            self.set_color(store=0, *rgbFace[:3])
 
833
            write("fill\n")
 
834
            if stroke:
 
835
                write("grestore\n")
824
836
 
825
837
        hatch = gc.get_hatch()
826
838
        if hatch:
827
 
            self.set_hatch(hatch)
 
839
            hatch_name = self.create_hatch(hatch)
 
840
            write("gsave\n")
 
841
            write("[/Pattern [/DeviceRGB]] setcolorspace %f %f %f " % gc.get_rgb()[:3])
 
842
            write("%s setcolor fill grestore\n" % hatch_name)
828
843
 
829
844
        if stroke:
830
845
            write("stroke\n")
853
868
    return manager
854
869
 
855
870
class FigureCanvasPS(FigureCanvasBase):
 
871
    _renderer_class = RendererPS
 
872
 
856
873
    def draw(self):
857
874
        pass
858
875
 
868
885
    def print_eps(self, outfile, *args, **kwargs):
869
886
        return self._print_ps(outfile, 'eps', *args, **kwargs)
870
887
 
 
888
 
 
889
 
 
890
 
 
891
 
 
892
 
871
893
    def _print_ps(self, outfile, format, *args, **kwargs):
872
 
        papertype = kwargs.get("papertype", rcParams['ps.papersize'])
 
894
        papertype = kwargs.pop("papertype", rcParams['ps.papersize'])
873
895
        papertype = papertype.lower()
874
896
        if papertype == 'auto':
875
897
            pass
877
899
            raise RuntimeError( '%s is not a valid papertype. Use one \
878
900
                    of %s'% (papertype, ', '.join( papersize.keys() )) )
879
901
 
880
 
        orientation = kwargs.get("orientation", "portrait").lower()
 
902
        orientation = kwargs.pop("orientation", "portrait").lower()
881
903
        if orientation == 'landscape': isLandscape = True
882
904
        elif orientation == 'portrait': isLandscape = False
883
905
        else: raise RuntimeError('Orientation must be "portrait" or "landscape"')
884
906
 
885
907
        self.figure.set_dpi(72) # Override the dpi kwarg
886
 
        imagedpi = kwargs.get("dpi", 72)
887
 
        facecolor = kwargs.get("facecolor", "w")
888
 
        edgecolor = kwargs.get("edgecolor", "w")
 
908
        imagedpi = kwargs.pop("dpi", 72)
 
909
        facecolor = kwargs.pop("facecolor", "w")
 
910
        edgecolor = kwargs.pop("edgecolor", "w")
889
911
 
890
912
        if rcParams['text.usetex']:
891
913
            self._print_figure_tex(outfile, format, imagedpi, facecolor, edgecolor,
892
 
                                   orientation, isLandscape, papertype)
 
914
                                   orientation, isLandscape, papertype,
 
915
                                   **kwargs)
893
916
        else:
894
917
            self._print_figure(outfile, format, imagedpi, facecolor, edgecolor,
895
 
                               orientation, isLandscape, papertype)
 
918
                               orientation, isLandscape, papertype,
 
919
                               **kwargs)
896
920
 
897
921
    def _print_figure(self, outfile, format, dpi=72, facecolor='w', edgecolor='w',
898
 
                      orientation='portrait', isLandscape=False, papertype=None):
 
922
                      orientation='portrait', isLandscape=False, papertype=None,
 
923
                      **kwargs):
899
924
        """
900
925
        Render the figure to hardcopy.  Set the figure patch face and
901
926
        edge colors.  This is useful because some of the GUIs have a
913
938
        passed_in_file_object = False
914
939
        if is_string_like(outfile):
915
940
            title = outfile
916
 
            tmpfile = os.path.join(gettempdir(), md5(outfile).hexdigest())
917
941
        elif is_writable_file_like(outfile):
918
942
            title = None
919
 
            tmpfile = os.path.join(gettempdir(), md5(str(hash(outfile))).hexdigest())
920
943
            passed_in_file_object = True
921
944
        else:
922
945
            raise ValueError("outfile must be a path or a file-like object")
923
 
        fh = file(tmpfile, 'w')
 
946
 
 
947
        fd, tmpfile = mkstemp()
 
948
        fh = os.fdopen(fd, 'w')
924
949
 
925
950
        # find the appropriate papertype
926
951
        width, height = self.figure.get_size_inches()
964
989
        self.figure.set_facecolor(facecolor)
965
990
        self.figure.set_edgecolor(edgecolor)
966
991
 
967
 
        self._pswriter = StringIO()
968
 
        renderer = RendererPS(width, height, self._pswriter, imagedpi=dpi)
 
992
 
 
993
        dryrun = kwargs.get("dryrun", False)
 
994
        if dryrun:
 
995
            class NullWriter(object):
 
996
                def write(self, *kl, **kwargs):
 
997
                    pass
 
998
 
 
999
            self._pswriter = NullWriter()
 
1000
        else:
 
1001
            self._pswriter = StringIO()
 
1002
 
 
1003
 
 
1004
        # mixed mode rendering
 
1005
        _bbox_inches_restore = kwargs.pop("bbox_inches_restore", None)
 
1006
        ps_renderer = self._renderer_class(width, height, self._pswriter,
 
1007
                                           imagedpi=dpi)
 
1008
        renderer = MixedModeRenderer(self.figure,
 
1009
            width, height, dpi, ps_renderer,
 
1010
            bbox_inches_restore=_bbox_inches_restore)
 
1011
 
969
1012
        self.figure.draw(renderer)
970
1013
 
 
1014
        if dryrun: # return immediately if dryrun (tightbbox=True)
 
1015
            return
 
1016
 
971
1017
        self.figure.set_facecolor(origfacecolor)
972
1018
        self.figure.set_edgecolor(origedgecolor)
973
1019
 
987
1033
        Ndict = len(psDefs)
988
1034
        print >>fh, "%%BeginProlog"
989
1035
        if not rcParams['ps.useafm']:
990
 
            Ndict += len(renderer.used_characters)
 
1036
            Ndict += len(ps_renderer.used_characters)
991
1037
        print >>fh, "/mpldict %d dict def"%Ndict
992
1038
        print >>fh, "mpldict begin"
993
1039
        for d in psDefs:
995
1041
            for l in d.split('\n'):
996
1042
                print >>fh, l.strip()
997
1043
        if not rcParams['ps.useafm']:
998
 
            for font_filename, chars in renderer.used_characters.values():
 
1044
            for font_filename, chars in ps_renderer.used_characters.values():
999
1045
                if len(chars):
1000
 
                    font = FT2Font(font_filename)
 
1046
                    font = FT2Font(str(font_filename))
1001
1047
                    cmap = font.get_charmap()
1002
1048
                    glyph_ids = []
1003
1049
                    for c in chars:
1004
1050
                        gind = cmap.get(c) or 0
1005
1051
                        glyph_ids.append(gind)
 
1052
 
 
1053
                    fonttype = rcParams['ps.fonttype']
 
1054
 
 
1055
                    # Can not use more than 255 characters from a
 
1056
                    # single font for Type 3
 
1057
                    if len(glyph_ids) > 255:
 
1058
                        fonttype = 42
 
1059
 
1006
1060
                    # The ttf to ps (subsetting) support doesn't work for
1007
1061
                    # OpenType fonts that are Postscript inside (like the
1008
1062
                    # STIX fonts).  This will simply turn that off to avoid
1010
1064
                    if is_opentype_cff_font(font_filename):
1011
1065
                        raise RuntimeError("OpenType CFF fonts can not be saved using the internal Postscript backend at this time.\nConsider using the Cairo backend.")
1012
1066
                    else:
1013
 
                        fonttype = rcParams['ps.fonttype']
1014
 
                        convert_ttf_to_ps(font_filename, fh, rcParams['ps.fonttype'], glyph_ids)
 
1067
                        convert_ttf_to_ps(font_filename, fh, fonttype, glyph_ids)
1015
1068
        print >>fh, "end"
1016
1069
        print >>fh, "%%EndProlog"
1017
1070
 
1038
1091
            xpdf_distill(tmpfile, isEPSF, ptype=papertype, bbox=bbox)
1039
1092
 
1040
1093
        if passed_in_file_object:
1041
 
            fh = file(tmpfile)
 
1094
            fh = open(tmpfile)
1042
1095
            print >>outfile, fh.read()
1043
1096
        else:
 
1097
            open(outfile, 'w')
 
1098
            mode = os.stat(outfile).st_mode
1044
1099
            shutil.move(tmpfile, outfile)
 
1100
            os.chmod(outfile, mode)
1045
1101
 
1046
1102
    def _print_figure_tex(self, outfile, format, dpi, facecolor, edgecolor,
1047
 
                          orientation, isLandscape, papertype):
 
1103
                          orientation, isLandscape, papertype,
 
1104
                          **kwargs):
1048
1105
        """
1049
1106
        If text.usetex is True in rc, a temporary pair of tex/eps files
1050
1107
        are created to allow tex to manage the text layout via the PSFrags
1054
1111
        title = outfile
1055
1112
 
1056
1113
        # write to a temp file, we'll move it to outfile when done
1057
 
        tmpfile = os.path.join(gettempdir(), md5(outfile).hexdigest())
1058
 
        fh = file(tmpfile, 'w')
 
1114
        fd, tmpfile = mkstemp()
 
1115
        fh = os.fdopen(fd, 'w')
1059
1116
 
1060
1117
        self.figure.dpi = 72 # ignore the dpi kwarg
1061
1118
        width, height = self.figure.get_size_inches()
1075
1132
        self.figure.set_facecolor(facecolor)
1076
1133
        self.figure.set_edgecolor(edgecolor)
1077
1134
 
1078
 
        self._pswriter = StringIO()
1079
 
        renderer = RendererPS(width, height, self._pswriter, imagedpi=dpi)
 
1135
        dryrun = kwargs.get("dryrun", False)
 
1136
        if dryrun:
 
1137
            class NullWriter(object):
 
1138
                def write(self, *kl, **kwargs):
 
1139
                    pass
 
1140
 
 
1141
            self._pswriter = NullWriter()
 
1142
        else:
 
1143
            self._pswriter = StringIO()
 
1144
 
 
1145
 
 
1146
        # mixed mode rendering
 
1147
        _bbox_inches_restore = kwargs.pop("bbox_inches_restore", None)
 
1148
        ps_renderer = self._renderer_class(width, height,
 
1149
                                           self._pswriter, imagedpi=dpi)
 
1150
        renderer = MixedModeRenderer(self.figure,
 
1151
            width, height, dpi, ps_renderer,
 
1152
            bbox_inches_restore=_bbox_inches_restore)
 
1153
 
1080
1154
        self.figure.draw(renderer)
1081
1155
 
 
1156
        if dryrun: # return immediately if dryrun (tightbbox=True)
 
1157
            return
 
1158
 
1082
1159
        self.figure.set_facecolor(origfacecolor)
1083
1160
        self.figure.set_edgecolor(origedgecolor)
1084
1161
 
1120
1197
            isLandscape = True
1121
1198
            width, height = height, width
1122
1199
            bbox = (lly, llx, ury, urx)
1123
 
        temp_papertype = _get_papertype(width, height)
1124
 
        if papertype=='auto':
1125
 
            papertype = temp_papertype
1126
 
            paperWidth, paperHeight = papersize[temp_papertype]
 
1200
 
 
1201
        # set the paper size to the figure size if isEPSF. The
 
1202
        # resulting ps file has the given size with correct bounding
 
1203
        # box so that there is no need to call 'pstoeps'
 
1204
        if isEPSF:
 
1205
            paperWidth, paperHeight = self.figure.get_size_inches()
 
1206
            if isLandscape:
 
1207
                paperWidth, paperHeight = paperHeight, paperWidth
1127
1208
        else:
1128
 
            paperWidth, paperHeight = papersize[papertype]
1129
 
            if (width>paperWidth or height>paperHeight) and isEPSF:
 
1209
            temp_papertype = _get_papertype(width, height)
 
1210
            if papertype=='auto':
 
1211
                papertype = temp_papertype
1130
1212
                paperWidth, paperHeight = papersize[temp_papertype]
1131
 
                verbose.report('Your figure is too big to fit on %s paper. %s \
1132
 
paper will be used to prevent clipping.'%(papertype, temp_papertype), 'helpful')
1133
 
 
1134
 
        texmanager = renderer.get_texmanager()
 
1213
            else:
 
1214
                paperWidth, paperHeight = papersize[papertype]
 
1215
                if (width>paperWidth or height>paperHeight) and isEPSF:
 
1216
                    paperWidth, paperHeight = papersize[temp_papertype]
 
1217
                    verbose.report('Your figure is too big to fit on %s paper. %s \
 
1218
    paper will be used to prevent clipping.'%(papertype, temp_papertype), 'helpful')
 
1219
 
 
1220
 
 
1221
        texmanager = ps_renderer.get_texmanager()
1135
1222
        font_preamble = texmanager.get_font_preamble()
1136
1223
        custom_preamble = texmanager.get_custom_preamble()
1137
1224
 
1138
 
        convert_psfrags(tmpfile, renderer.psfrag, font_preamble,
1139
 
                        custom_preamble, paperWidth, paperHeight,
1140
 
                        orientation)
 
1225
        psfrag_rotated = convert_psfrags(tmpfile, ps_renderer.psfrag,
 
1226
                                         font_preamble,
 
1227
                                         custom_preamble, paperWidth, paperHeight,
 
1228
                                         orientation)
1141
1229
 
1142
1230
        if rcParams['ps.usedistiller'] == 'ghostscript':
1143
 
            gs_distill(tmpfile, isEPSF, ptype=papertype, bbox=bbox)
 
1231
            gs_distill(tmpfile, isEPSF, ptype=papertype, bbox=bbox,
 
1232
                       rotated=psfrag_rotated)
1144
1233
        elif rcParams['ps.usedistiller'] == 'xpdf':
1145
 
            xpdf_distill(tmpfile, isEPSF, ptype=papertype, bbox=bbox)
 
1234
            xpdf_distill(tmpfile, isEPSF, ptype=papertype, bbox=bbox,
 
1235
                         rotated=psfrag_rotated)
1146
1236
        elif rcParams['text.usetex']:
1147
1237
            if False: pass # for debugging
1148
 
            else: gs_distill(tmpfile, isEPSF, ptype=papertype, bbox=bbox)
 
1238
            else: gs_distill(tmpfile, isEPSF, ptype=papertype, bbox=bbox,
 
1239
                             rotated=psfrag_rotated)
1149
1240
 
1150
1241
        if  isinstance(outfile, file):
1151
1242
            fh = file(tmpfile)
1152
1243
            print >>outfile, fh.read()
1153
 
        else: shutil.move(tmpfile, outfile)
 
1244
        else:
 
1245
            open(outfile, 'w')
 
1246
            mode = os.stat(outfile).st_mode
 
1247
            shutil.move(tmpfile, outfile)
 
1248
            os.chmod(outfile, mode)
1154
1249
 
1155
1250
def convert_psfrags(tmpfile, psfrags, font_preamble, custom_preamble,
1156
1251
                    paperWidth, paperHeight, orientation):
1242
1337
    os.remove(outfile)
1243
1338
    os.remove(epsfile)
1244
1339
    shutil.move(psfile, tmpfile)
 
1340
 
 
1341
    # check if the dvips created a ps in landscape paper.  Somehow,
 
1342
    # above latex+dvips results in a ps file in a landscape mode for a
 
1343
    # certain figure sizes (e.g., 8.3in,5.8in which is a5). And the
 
1344
    # bounding box of the final output got messed up. We check see if
 
1345
    # the generated ps file is in landscape and return this
 
1346
    # information. The return value is used in pstoeps step to recover
 
1347
    # the correct bounding box. 2010-06-05 JJL
 
1348
    if "Landscape" in open(tmpfile).read(1000):
 
1349
        psfrag_rotated = True
 
1350
    else:
 
1351
        psfrag_rotated = False
 
1352
 
1245
1353
    if not debugPS:
1246
1354
        for fname in glob.glob(tmpfile+'.*'):
1247
1355
            os.remove(fname)
1248
1356
 
 
1357
    return psfrag_rotated
1249
1358
 
1250
 
def gs_distill(tmpfile, eps=False, ptype='letter', bbox=None):
 
1359
def gs_distill(tmpfile, eps=False, ptype='letter', bbox=None, rotated=False):
1251
1360
    """
1252
1361
    Use ghostscript's pswrite or epswrite device to distill a file.
1253
1362
    This yields smaller files without illegal encapsulated postscript
1254
1363
    operators. The output is low-level, converting text to outlines.
1255
1364
    """
1256
 
    paper = '-sPAPERSIZE=%s'% ptype
 
1365
 
 
1366
    paper_option = "-sPAPERSIZE=%s" % ptype
 
1367
 
1257
1368
    psfile = tmpfile + '.ps'
1258
1369
    outfile = tmpfile + '.output'
1259
1370
    dpi = rcParams['ps.distiller.res']
1260
1371
    if sys.platform == 'win32': gs_exe = 'gswin32c'
1261
1372
    else: gs_exe = 'gs'
 
1373
 
1262
1374
    command = '%s -dBATCH -dNOPAUSE -r%d -sDEVICE=pswrite %s -sOutputFile="%s" \
1263
 
                "%s" > "%s"'% (gs_exe, dpi, paper, psfile, tmpfile, outfile)
 
1375
                "%s" > "%s"'% (gs_exe, dpi, paper_option, psfile, tmpfile, outfile)
1264
1376
    verbose.report(command, 'debug')
1265
1377
    exit_status = os.system(command)
1266
1378
    fh = file(outfile)
1271
1383
    os.remove(outfile)
1272
1384
    os.remove(tmpfile)
1273
1385
    shutil.move(psfile, tmpfile)
 
1386
 
 
1387
 
 
1388
    # While it is best if above steps preserve the original bounding
 
1389
    # box, it does not seems to be the case. pstoeps not only convert
 
1390
    # the input to eps format, but also restores the original bbox.
 
1391
 
1274
1392
    if eps:
1275
 
        pstoeps(tmpfile, bbox)
1276
 
 
1277
 
 
1278
 
def xpdf_distill(tmpfile, eps=False, ptype='letter', bbox=None):
 
1393
        pstoeps(tmpfile, bbox, rotated=rotated)
 
1394
 
 
1395
 
 
1396
def xpdf_distill(tmpfile, eps=False, ptype='letter', bbox=None, rotated=False):
1279
1397
    """
1280
1398
    Use ghostscript's ps2pdf and xpdf's/poppler's pdftops to distill a file.
1281
1399
    This yields smaller files without illegal encapsulated postscript
1285
1403
    pdffile = tmpfile + '.pdf'
1286
1404
    psfile = tmpfile + '.ps'
1287
1405
    outfile = tmpfile + '.output'
 
1406
 
 
1407
    if eps: paper_option = "-dEPSCrop"
 
1408
    else: paper_option = "-sPAPERSIZE=%s" % ptype
 
1409
 
1288
1410
    command = 'ps2pdf -dAutoFilterColorImages=false \
1289
 
-sColorImageFilter=FlateEncode -sPAPERSIZE=%s "%s" "%s" > "%s"'% \
1290
 
(ptype, tmpfile, pdffile, outfile)
 
1411
-sColorImageFilter=FlateEncode %s "%s" "%s" > "%s"'% \
 
1412
(paper_option, tmpfile, pdffile, outfile)
1291
1413
    if sys.platform == 'win32': command = command.replace('=', '#')
1292
1414
    verbose.report(command, 'debug')
1293
1415
    exit_status = os.system(command)
1309
1431
    os.remove(outfile)
1310
1432
    os.remove(tmpfile)
1311
1433
    shutil.move(psfile, tmpfile)
 
1434
 
 
1435
 
 
1436
    # Similar to the gs_distillier case, ps2pdf does not seem to
 
1437
    # preserve the bbox of the original file (at least w/ gs
 
1438
    # 8.61). Thus, the original bbox need to be resotred.
 
1439
 
1312
1440
    if eps:
1313
 
        pstoeps(tmpfile, bbox)
 
1441
        pstoeps(tmpfile, bbox, rotated)
1314
1442
    for fname in glob.glob(tmpfile+'.*'):
1315
1443
        os.remove(fname)
1316
1444
 
1317
1445
 
 
1446
def get_bbox_header(lbrt, rotated=False):
 
1447
    """
 
1448
    return a postscript header stringfor the given bbox lbrt=(l, b, r, t).
 
1449
    Optionally, return rotate command.
 
1450
    """
 
1451
 
 
1452
    l, b, r, t = lbrt
 
1453
    if  rotated:
 
1454
        rotate = "%.2f %.2f  translate\n90 rotate" % (l+r, 0)
 
1455
    else:
 
1456
        rotate = ""
 
1457
    bbox_info = '%%%%BoundingBox: %d %d %d %d' % (l, b, np.ceil(r), np.ceil(t))
 
1458
    hires_bbox_info = '%%%%HiResBoundingBox: %.6f %.6f %.6f %.6f' % (l, b, r, t)
 
1459
 
 
1460
    return '\n'.join([bbox_info, hires_bbox_info]), rotate
 
1461
 
 
1462
 
 
1463
# get_bbox is deprecated. I don't see any reason to use ghostscript to
 
1464
# find the bounding box, as the required bounding box is alread known.
1318
1465
def get_bbox(tmpfile, bbox):
1319
1466
    """
1320
1467
    Use ghostscript's bbox device to find the center of the bounding box. Return
1321
1468
    an appropriately sized bbox centered around that point. A bit of a hack.
1322
1469
    """
 
1470
 
1323
1471
    outfile = tmpfile + '.output'
1324
1472
    if sys.platform == 'win32': gs_exe = 'gswin32c'
1325
1473
    else: gs_exe = 'gs'
1351
1499
        dy = (bbox[3]-bbox[1])/2
1352
1500
        l,b,r,t = (x-dx, y-dy, x+dx, y+dy)
1353
1501
 
1354
 
    bbox_info = '%%%%BoundingBox: %d %d %d %d' % (l, b, npy.ceil(r), npy.ceil(t))
 
1502
    bbox_info = '%%%%BoundingBox: %d %d %d %d' % (l, b, np.ceil(r), np.ceil(t))
1355
1503
    hires_bbox_info = '%%%%HiResBoundingBox: %.6f %.6f %.6f %.6f' % (l, b, r, t)
1356
1504
 
1357
1505
    return '\n'.join([bbox_info, hires_bbox_info])
1358
1506
 
1359
1507
 
1360
 
def pstoeps(tmpfile, bbox):
 
1508
def pstoeps(tmpfile, bbox, rotated=False):
1361
1509
    """
1362
1510
    Convert the postscript to encapsulated postscript.
1363
1511
    """
1364
 
    bbox_info = get_bbox(tmpfile, bbox)
 
1512
 
 
1513
    # if rotated==True, the output eps file need to be rotated
 
1514
    bbox_info, rotate = get_bbox_header(bbox, rotated=rotated)
1365
1515
 
1366
1516
    epsfile = tmpfile + '.eps'
1367
1517
    epsh = file(epsfile, 'w')
1384
1534
            print >>epsh, '/setpagedevice {pop} def'
1385
1535
            print >>epsh, '%%EndProlog'
1386
1536
            print >>epsh, '%%Page 1 1'
 
1537
            if rotate:
 
1538
                print >>epsh, rotate
1387
1539
            break
1388
1540
        elif line.startswith('%%Bound') \
1389
1541
            or line.startswith('%%HiResBound') \
 
1542
            or line.startswith('%%DocumentMedia') \
1390
1543
            or line.startswith('%%Pages'):
1391
1544
            pass
1392
1545
        else:
1416
1569
    shutil.move(epsfile, tmpfile)
1417
1570
 
1418
1571
 
 
1572
 
1419
1573
class FigureManagerPS(FigureManagerBase):
1420
1574
    pass
1421
1575