I was trying to use PIL to open an (Illustrator) .eps-file, do some changes and save it. I want to set the document to 300 dpi, and color mode to cmyk before opening, creating or interpreting the object.
First I tried the same with PythonMagick and it worked like this:
import PythonMagick
# That's NOT what I want
img72 = PythonMagick.Image()
img_file = 'epstest.eps'
img.read(img_file)
img_dens = img72.density()
print 'W: %d, H: %d' % (img.size().width(), img.size().height())
# W: 403, H: 2475 <-- See here
print 'Density Width: %r' % img_dens.width() # 72
print 'Density Height: %r' % img_dens.height() # 72
# THAT is what I want
img300 = PythonMagick.Image()
img_file = 'epstest.eps'
img300.density('300') # set density / resolution
img300.read(img_file) # opens with defined density
img_dens = img300.density()
print 'W: %d, H: %d' % (img.size().width(), img.size().height())
# W: 1679, H: 10312 <-- See here!
print 'Density Width: %r' % img_dens.width() # 300
print 'Density Height: %r' % img_dens.height() # 300
Problem with PythonMagick: Converting Color Modes is not working, so i tried the same with PIL, which I would prefer:
from PIL import Image
img = Image.open('epstest.eps')
I know that it is possible to set dpi while saving.
Stuff that didn't work:
img = Image() # TypeError: 'module' object is not callable
img = Image.new() # TypeError: new() takes at least 2 arguments (0 given)
# .new() would create a different object anyway..
img = Image.open('epstest.eps', dpi = 300)
img = Image.open('epstest.eps', dpi = (300, 300) )
# After opening an Image
img.load(dpi=(300,300))
Regarding the input: My .eps file - if it is interpreted with 72dpi (seems to be PIL default) it ends up with 403x2475 px, with 300dpi it should be 1677x10311 px. Also, the .eps-file does not contain a preview-bitmap, nor any bitmap-data. Only 2 colors (black and white), plain vectors. It would be useful to make a catalogue of a huge amount of color-separated .eps-files.
Regarding the output: will be a png.
Solution:
Many thanks to Paulo - this is his solution with very small changes:
from PIL import Image
from PIL import EpsImagePlugin
import math
filename = 'epstest.eps'
def open_eps(filename, dpi=300.0):
img = Image.open(filename)
original = [float(d) for d in img.size]
# scale = width / original[0] # calculated wrong height
scale = dpi/72.0 # this fixed it
if dpi is not 0:
img.load(scale = math.ceil(scale))
if scale != 1:
img.thumbnail([round(scale * d) for d in original], Image.ANTIALIAS)
return img
img = open_eps(filename, dpi=300.0)
img.save('pil_test.png', dpi=(300.0, 300.0))
AFAIK, while it can contain embedded bitmaps and a preview thumbnail, EPS is a vector-based format. It only makes sense to set DPI if you are generating output in a bitmap format.
That is because the EPS driver in PythonMagick converts the EPS to a bitmap representation on input (remember IM, the underlying library, is a 'raster image processor') - while in PIL the EPS driver can also write EPS images.
See "A word about Vector Image formats" in ImageMagick:
See also the note about EPS on PIL:
[Update 1]
This information from Pillow docs is missing from the PIL docs:
[Update 2]
Contrary to my initial guess, PIL also rasterizes the image. When I saved as EPS it just made a wrapper around a bitmap. According to the OP the default resolution seems to be 72 ppi at his environment.
if you know the default resolution is 72 ppi (pixels per inch), calculating the scale for any density you want is a matter of simple proportion - given
r
as the resolution you want,s
is the scale:1 : s = 72 : r
ergo:May be it is best if you just specify the desired width instead of the resolution - for example if you want to have it 1677 pixels wide:
So the final answer is: although there is no built-in parameter to specify the desired resolution in ppi while loading an EPS file, you can use the scale parameter to load it at any resolution you want. If you care enough, I guess the Pillow maintainers would be glad to receive a PR for this.
[Edit 3]
Shame on me for not testing.
Not sure if I should use
math.round
instead ofint
but you got the idea.