231
233
if store: self.fontname = fontname
232
234
if store: self.fontsize = fontsize
234
def set_hatch(self, hatch):
237
/ - diagonal hatching
244
letters can be combined, in which case all the specified
247
if same letter repeats, it increases the density of hatching
250
hatches = {'horiz':0, 'vert':0, 'diag1':0, 'diag2':0}
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
260
elif (letter.lower() == 'x'):
261
hatches['diag1'] += 1
262
hatches['diag2'] += 1
264
def do_hatch(angle, density):
265
if (density == 0): return ""
268
eoclip %s rotate 0.0 0.0 0.0 0.0 setrgbcolor 0 setlinewidth
270
pathbbox /hatchb exch def /hatchr exch def /hatcht exch def /hatchl exch def
271
hatchl cvi hatchgap idiv hatchgap mul
273
hatchr cvi hatchgap idiv hatchgap mul
274
{hatcht m 0 hatchb hatcht sub r }
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):
238
if self._hatches.has_key(hatch):
239
return self._hatches[hatch]
240
name = 'H%d' % len(self._hatches)
241
self._pswriter.write("""\
245
/BBox[0 0 %(sidelen)d %(sidelen)d]
253
self._pswriter.write(
254
self._convert_path(Path.hatch(hatch), Affine2D().scale(72.0),
256
self._pswriter.write("""\
264
self._hatches[hatch] = name
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)
302
# TODO: We need a way to get a good baseline from
280
w, h, d = texmanager.get_text_width_height_descent(s, fontsize,
307
285
width, height, descent, pswriter, used_characters = \
367
348
def _rgb(self, im):
368
349
h,w,s = im.as_rgba_str()
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()
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()
386
367
def _hex_lines(self, s, chars_per_line=128):
401
382
return self.image_magnification
403
def draw_image(self, x, y, im, bbox, clippath=None, clippath_trans=None):
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
409
bbox is a matplotlib.transforms.BBox instance for clipping, or
384
def option_scale_image(self):
386
ps backend support arbitrary scaling of image.
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"
419
395
h, w, bits = self._rgb(im)
420
396
imagecmd = "false 3 colorimage"
398
return h, w, bits, imagecmd
400
def draw_image(self, gc, x, y, im, dx=None, dy=None, transform=None):
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
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.
413
h, w, bits, imagecmd = self._get_image_h_w_bits_command(im)
421
414
hexlines = '\n'.join(self._hex_lines(bits))
424
w/self.image_magnification, h/self.image_magnification)
417
xscale = w / self.image_magnification
422
yscale = h/self.image_magnification
427
if transform is None:
428
matrix = "1 0 0 1 0 0"
430
matrix = " ".join(map(str, transform.to_values()))
426
432
figh = self.height*72
427
433
#print 'values', origin, flipud, figh, h, y
435
bbox = gc.get_clip_rectangle()
436
clippath, clippath_trans = gc.get_clip_path()
430
439
if bbox is not None:
431
440
clipx,clipy,clipw,cliph = bbox.bounds
456
def _convert_path(self, path, transform, simplify=None):
457
path = transform.transform_path(path)
466
def _convert_path(self, path, transform, clip=False, simplify=None):
460
468
last_points = None
461
for points, code in path.iter_segments(simplify):
470
clip = (0.0, 0.0, self.width * 72.0,
474
for points, code in path.iter_segments(transform, clip=clip,
462
476
if code == Path.MOVETO:
463
477
ps.append("%g %g m" % tuple(points))
464
478
elif code == Path.LINETO:
492
507
Draws a Path instance using the given affine transform.
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)
497
515
def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None):
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,
518
537
ps_cmd.extend(['gsave', ps_color, 'fill', 'grestore'])
520
539
ps_cmd.extend(['stroke', 'grestore', '} bind def'])
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)
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
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)
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):
553
568
ps = "%g %g %s" % (xo, yo, path_id)
554
self._draw_ps(ps, gc, rgbFace)
569
self._draw_ps(ps, gc0, rgbFace)
556
571
self._path_collection_id += 1
570
583
rcParams['font.family'], r'{\rmfamily %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))
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))
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))
600
621
return self.draw_mathtext(gc, x, y, s, prop, angle)
602
elif isinstance(s, unicode):
603
return self.draw_unicode(gc, x, y, s, prop, angle)
605
623
elif rcParams['ps.useafm']:
606
font = self._get_font_afm(prop)
608
l,b,w,h = font.get_str_bbox(s)
610
fontsize = prop.get_size_in_points()
616
if angle==90: l,b = -b, l # todo generalize for arb rotations
618
pos = _nums_to_str(x-l, y-b)
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]
627
/%(fontname)s findfont
628
%(fontsize)s scalefont
637
self._draw_ps(ps, gc, None)
640
font = self._get_font_ttf(prop)
641
font.set_text(s, 0, flags=LOAD_NO_HINTING)
642
self.track_characters(font, s)
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))
649
write("%s rotate\n"%_num_to_str(angle))
650
descent = font.get_descent() / 64.0
652
write("0 %s rmoveto\n"%_num_to_str(descent))
653
write("(%s) show\n"%quote_ps_string(s))
658
return GraphicsContextPS()
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
664
if rcParams['ps.useafm']:
665
624
self.set_color(*gc.get_rgb())
667
626
font = self._get_font_afm(prop)
771
733
self._pswriter.write(ps)
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)
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
748
points = trans.transform(points)
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)
757
xmin, ymin = points_min
758
xmax, ymax = points_max
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
769
stream = quote_ps_string(streamarr.tostring())
771
self._pswriter.write("""
774
/ColorSpace [/DeviceRGB]
775
/BitsPerCoordinate 32
779
/Decode [ %(xmin)f %(xmax)f %(ymin)f %(ymax)f 0 1 0 1 0 1 ]
780
/DataSource (%(stream)s)
773
786
def _draw_ps(self, ps, gc, rgbFace, fill=True, stroke=True, command=None):
775
788
Emit the PostScript sniplet 'ps' with all the attributes from 'gc'
877
899
raise RuntimeError( '%s is not a valid papertype. Use one \
878
900
of %s'% (papertype, ', '.join( papersize.keys() )) )
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"')
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")
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,
894
917
self._print_figure(outfile, format, imagedpi, facecolor, edgecolor,
895
orientation, isLandscape, papertype)
918
orientation, isLandscape, papertype,
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,
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
964
989
self.figure.set_facecolor(facecolor)
965
990
self.figure.set_edgecolor(edgecolor)
967
self._pswriter = StringIO()
968
renderer = RendererPS(width, height, self._pswriter, imagedpi=dpi)
993
dryrun = kwargs.get("dryrun", False)
995
class NullWriter(object):
996
def write(self, *kl, **kwargs):
999
self._pswriter = NullWriter()
1001
self._pswriter = StringIO()
1004
# mixed mode rendering
1005
_bbox_inches_restore = kwargs.pop("bbox_inches_restore", None)
1006
ps_renderer = self._renderer_class(width, height, self._pswriter,
1008
renderer = MixedModeRenderer(self.figure,
1009
width, height, dpi, ps_renderer,
1010
bbox_inches_restore=_bbox_inches_restore)
969
1012
self.figure.draw(renderer)
1014
if dryrun: # return immediately if dryrun (tightbbox=True)
971
1017
self.figure.set_facecolor(origfacecolor)
972
1018
self.figure.set_edgecolor(origedgecolor)
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():
1000
font = FT2Font(font_filename)
1046
font = FT2Font(str(font_filename))
1001
1047
cmap = font.get_charmap()
1003
1049
for c in chars:
1004
1050
gind = cmap.get(c) or 0
1005
1051
glyph_ids.append(gind)
1053
fonttype = rcParams['ps.fonttype']
1055
# Can not use more than 255 characters from a
1056
# single font for Type 3
1057
if len(glyph_ids) > 255:
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
1038
1091
xpdf_distill(tmpfile, isEPSF, ptype=papertype, bbox=bbox)
1040
1093
if passed_in_file_object:
1042
1095
print >>outfile, fh.read()
1098
mode = os.stat(outfile).st_mode
1044
1099
shutil.move(tmpfile, outfile)
1100
os.chmod(outfile, mode)
1046
1102
def _print_figure_tex(self, outfile, format, dpi, facecolor, edgecolor,
1047
orientation, isLandscape, papertype):
1103
orientation, isLandscape, papertype,
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
1075
1132
self.figure.set_facecolor(facecolor)
1076
1133
self.figure.set_edgecolor(edgecolor)
1078
self._pswriter = StringIO()
1079
renderer = RendererPS(width, height, self._pswriter, imagedpi=dpi)
1135
dryrun = kwargs.get("dryrun", False)
1137
class NullWriter(object):
1138
def write(self, *kl, **kwargs):
1141
self._pswriter = NullWriter()
1143
self._pswriter = StringIO()
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)
1080
1154
self.figure.draw(renderer)
1156
if dryrun: # return immediately if dryrun (tightbbox=True)
1082
1159
self.figure.set_facecolor(origfacecolor)
1083
1160
self.figure.set_edgecolor(origedgecolor)
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]
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'
1205
paperWidth, paperHeight = self.figure.get_size_inches()
1207
paperWidth, paperHeight = paperHeight, paperWidth
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')
1134
texmanager = renderer.get_texmanager()
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')
1221
texmanager = ps_renderer.get_texmanager()
1135
1222
font_preamble = texmanager.get_font_preamble()
1136
1223
custom_preamble = texmanager.get_custom_preamble()
1138
convert_psfrags(tmpfile, renderer.psfrag, font_preamble,
1139
custom_preamble, paperWidth, paperHeight,
1225
psfrag_rotated = convert_psfrags(tmpfile, ps_renderer.psfrag,
1227
custom_preamble, paperWidth, paperHeight,
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)
1150
1241
if isinstance(outfile, file):
1151
1242
fh = file(tmpfile)
1152
1243
print >>outfile, fh.read()
1153
else: shutil.move(tmpfile, outfile)
1246
mode = os.stat(outfile).st_mode
1247
shutil.move(tmpfile, outfile)
1248
os.chmod(outfile, mode)
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)
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
1351
psfrag_rotated = False
1245
1353
if not debugPS:
1246
1354
for fname in glob.glob(tmpfile+'.*'):
1247
1355
os.remove(fname)
1357
return psfrag_rotated
1250
def gs_distill(tmpfile, eps=False, ptype='letter', bbox=None):
1359
def gs_distill(tmpfile, eps=False, ptype='letter', bbox=None, rotated=False):
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.
1256
paper = '-sPAPERSIZE=%s'% ptype
1366
paper_option = "-sPAPERSIZE=%s" % ptype
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'
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)
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.
1275
pstoeps(tmpfile, bbox)
1278
def xpdf_distill(tmpfile, eps=False, ptype='letter', bbox=None):
1393
pstoeps(tmpfile, bbox, rotated=rotated)
1396
def xpdf_distill(tmpfile, eps=False, ptype='letter', bbox=None, rotated=False):
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'
1407
if eps: paper_option = "-dEPSCrop"
1408
else: paper_option = "-sPAPERSIZE=%s" % ptype
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)
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.
1313
pstoeps(tmpfile, bbox)
1441
pstoeps(tmpfile, bbox, rotated)
1314
1442
for fname in glob.glob(tmpfile+'.*'):
1315
1443
os.remove(fname)
1446
def get_bbox_header(lbrt, rotated=False):
1448
return a postscript header stringfor the given bbox lbrt=(l, b, r, t).
1449
Optionally, return rotate command.
1454
rotate = "%.2f %.2f translate\n90 rotate" % (l+r, 0)
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)
1460
return '\n'.join([bbox_info, hires_bbox_info]), rotate
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):
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.
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)
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)
1357
1505
return '\n'.join([bbox_info, hires_bbox_info])
1360
def pstoeps(tmpfile, bbox):
1508
def pstoeps(tmpfile, bbox, rotated=False):
1362
1510
Convert the postscript to encapsulated postscript.
1364
bbox_info = get_bbox(tmpfile, bbox)
1513
# if rotated==True, the output eps file need to be rotated
1514
bbox_info, rotate = get_bbox_header(bbox, rotated=rotated)
1366
1516
epsfile = tmpfile + '.eps'
1367
1517
epsh = file(epsfile, 'w')