Merge branch 'master' into master

This commit is contained in:
tzjtan 2019-06-20 18:42:54 -04:00 committed by GitHub
commit ca96f84c69
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
213 changed files with 4977 additions and 3232 deletions

View File

@ -52,7 +52,7 @@ install:
}
else
{
c:\python34\python.exe c:\pillow\winbuild\build_dep.py
c:\python37\python.exe c:\pillow\winbuild\build_dep.py
c:\pillow\winbuild\build_deps.cmd
$host.SetShouldExit(0)
}

View File

@ -34,6 +34,4 @@ The best reproductions are self-contained scripts with minimal dependencies. If
## Security vulnerabilities
To report sensitive vulnerability information, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure.
If your organisation/employer is a distributor of Pillow and would like advance notification of security-related bugs, please let us know your preferred contact method.
Please see our [security policy](https://github.com/python-pillow/Pillow/blob/master/.github/SECURITY.md).

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
tidelift: pypi/pillow

View File

@ -1,19 +0,0 @@
### What did you do?
### What did you expect to happen?
### What actually happened?
### What are your OS, Python and Pillow versions?
* OS:
* Python:
* Pillow:
Please include **code** that reproduces the issue and whenever possible, an **image** that demonstrates the issue. Please upload images to GitHub, not to third-party file hosting sites. If necessary, add the image to a zip or tar archive.
The best reproductions are self-contained scripts with minimal dependencies. If you are using a framework such as plone, Django, or buildout, try to replicate the issue just using Pillow.
```python
code goes here
```

59
.github/ISSUE_TEMPLATE/ISSUE_REPORT.md vendored Normal file
View File

@ -0,0 +1,59 @@
---
name: Issue report
about: Create a report to help us improve Pillow
---
<!--
Thank you for reporting an issue.
Follow these guidelines to ensure your issue is handled properly.
If you have a ...
1. General question: consider asking the question on Stack Overflow
with the python-imaging-library tag:
* https://stackoverflow.com/questions/tagged/python-imaging-library
Do not ask a question in both places.
If you think you have found a bug or have an unexplained exception
then file a bug report here.
2. Bug report: include a self-contained, copy-pastable example that
generates the issue if possible. Be concise with code posted.
Guidelines on how to provide a good bug report:
* https://stackoverflow.com/help/mcve
Bug reports which follow these guidelines are easier to diagnose,
and are often handled much more quickly.
3. Feature request: do a quick search of existing issues
to make sure this has not been asked before.
We know asking good questions takes effort, and we appreciate your time.
Thank you.
-->
### What did you do?
### What did you expect to happen?
### What actually happened?
### What are your OS, Python and Pillow versions?
* OS:
* Python:
* Pillow:
<!--
Please include **code** that reproduces the issue and whenever possible, an **image** that demonstrates the issue. Please upload images to GitHub, not to third-party file hosting sites. If necessary, add the image to a zip or tar archive.
The best reproductions are self-contained scripts with minimal dependencies. If you are using a framework such as Plone, Django, or Buildout, try to replicate the issue just using Pillow.
-->
```python
code goes here
```

5
.github/SECURITY.md vendored Normal file
View File

@ -0,0 +1,5 @@
# Security policy
To report sensitive vulnerability information, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure.
If your organisation/employer is a distributor of Pillow and would like advance notification of security-related bugs, please let us know your preferred contact method.

View File

@ -16,50 +16,36 @@ matrix:
- python: "3.6"
name: "Lint"
env: LINT="true"
- python: "pypy2.7-6.0"
- python: "pypy"
name: "PyPy2 Xenial"
dist: xenial
- python: "pypy3.5-6.0"
- python: "pypy3"
name: "PyPy3 Xenial"
dist: xenial
- python: '3.7'
name: "3.7 Xenial"
- python: '2.7'
name: "2.7 Xenial"
- python: '2.7'
name: "2.7 Trusty"
dist: trusty
- python: "2.7_with_system_site_packages" # For PyQt4
name: "2.7_with_system_site_packages Xenial"
services: xvfb
- python: "2.7_with_system_site_packages" # For PyQt4
name: "2.7_with_system_site_packages Trusty"
dist: trusty
- python: '3.6'
name: "3.6 Xenial"
- python: '3.6'
name: "3.6 Trusty PYTHONOPTIMIZE=1"
dist: trusty
name: "3.6 Xenial PYTHONOPTIMIZE=1"
env: PYTHONOPTIMIZE=1
- python: '3.5'
name: "3.5 Xenial"
- python: '3.5'
name: "3.5 Trusty PYTHONOPTIMIZE=2"
dist: trusty
name: "3.5 Xenial PYTHONOPTIMIZE=2"
env: PYTHONOPTIMIZE=2
- python: "3.8-dev"
name: "3.8-dev Xenial"
- env: DOCKER="alpine" DOCKER_TAG="master"
- env: DOCKER="arch" DOCKER_TAG="master" # contains PyQt5
- env: DOCKER="ubuntu-trusty-x86" DOCKER_TAG="master"
- env: DOCKER="ubuntu-xenial-amd64" DOCKER_TAG="master"
- env: DOCKER="ubuntu-16.04-xenial-amd64" DOCKER_TAG="master"
- env: DOCKER="ubuntu-18.04-bionic-amd64" DOCKER_TAG="master"
- env: DOCKER="debian-stretch-x86" DOCKER_TAG="master"
- env: DOCKER="centos-6-amd64" DOCKER_TAG="master"
- env: DOCKER="centos-7-amd64" DOCKER_TAG="master"
- env: DOCKER="amazon-1-amd64" DOCKER_TAG="master"
- env: DOCKER="amazon-2-amd64" DOCKER_TAG="master"
- env: DOCKER="fedora-28-amd64" DOCKER_TAG="master"
- env: DOCKER="fedora-29-amd64" DOCKER_TAG="master"
- env: DOCKER="fedora-30-amd64" DOCKER_TAG="master"
services:
- docker
@ -75,14 +61,6 @@ install:
.travis/install.sh;
fi
before_script:
# Qt needs a display for some of the tests, and it's only run on the system site packages install
- |
if [ "$TRAVIS_JOB_NAME" == "2.7_with_system_site_packages Trusty" ]; then
export DISPLAY=:99.0
sh -e /etc/init.d/xvfb start
fi
script:
- |
if [ "$LINT" == "true" ]; then

View File

@ -2,6 +2,87 @@
Changelog (Pillow)
==================
6.1.0 (unreleased)
------------------
- Fixed bugs in calculating text size #3864
[radarhere]
- Add __main__.py to output basic format and support information #3870
[jdufresne]
- Added variation font support #3802
[radarhere]
- Do not down-convert if image is LA when showing with PNG format #3869
[radarhere]
- Improve handling of PSD frames #3759
[radarhere]
- Improved ICO and ICNS loading #3897
[radarhere]
- Changed Preview application path so that it is no longer static #3896
[radarhere]
- Corrected ttb text positioning #3856
[radarhere]
- Handle unexpected ICO image sizes #3836
[radarhere]
- Fixed bits value for RGB;16N unpackers #3837
[kkopachev]
- Travis CI: Add Fedora 30, remove Fedora 28 #3821
[hugovk]
- Added reading of CMYK;16L TIFF images #3817
[radarhere]
- Fixed dimensions of 1-bit PDFs #3827
[radarhere]
- Fixed opening mmap image through Path on Windows #3825
[radarhere]
- Fixed ImageDraw arc gaps #3824
[radarhere]
- Expand GIF to include frames with extents outside the image size #3822
[radarhere]
- Fixed ImageTk getimage #3814
[radarhere]
- Fixed bug in decoding large images #3791
[radarhere]
- Fixed reading APP13 marker without Photoshop data #3771
[radarhere]
- Added option to include layered windows in ImageGrab.grab on Windows #3808
[radarhere]
- Detect libimagequant when installed by pacman on MingW #3812
[radarhere]
- Fixed raqm layout bug #3787
[radarhere]
- Fixed loading font with non-Unicode path on Windows #3785
[radarhere]
- Travis CI: Upgrade PyPy from 6.0.0 to 7.1.1 #3783
[hugovk, johnthagen]
- Depends: Updated openjpeg to 2.3.1 #3794, raqm to 0.7.0 #3877, libimagequant to 2.12.3 #3889
[radarhere]
- Fix numpy bool bug #3790
[radarhere]
6.0.0 (2019-04-01)
------------------
@ -1408,7 +1489,7 @@ Changelog (Pillow)
- Test: Faster assert_image_similar #2279
[homm]
- Removed depreciated internal "stretch" method #2276
- Removed deprecated internal "stretch" method #2276
[homm]
- Removed the handles_eof flag in decode.c #2223

View File

@ -4,7 +4,7 @@ Pillow
Python Imaging Library (Fork)
-----------------------------
Pillow is the friendly PIL fork by `Alex Clark and Contributors <https://github.com/python-pillow/Pillow/graphs/contributors>`_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors.
Pillow is the friendly PIL fork by `Alex Clark and Contributors <https://github.com/python-pillow/Pillow/graphs/contributors>`_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. As of 2019, Pillow development is `supported by Tidelift <https://tidelift.com/subscription/pkg/pypi-pillow>`_.
.. start-badges

View File

@ -102,4 +102,4 @@ Released as needed privately to individual vendors for critical security-related
## Documentation
* [ ] Make sure the default version for Read the Docs is the latest tagged release e.g. `d2d43879` (5.4.0)
* [ ] Make sure the [default version for Read the Docs](https://pillow.readthedocs.io/en/stable/) is up-to-date with the release changes

View File

@ -12,6 +12,12 @@ from .helper import unittest, PillowTestCase
# 2.7 and 3.2.
from PIL import Image
try:
import numpy
except ImportError:
numpy = None
YDIM = 32769
XDIM = 48000
@ -32,6 +38,11 @@ class LargeMemoryTest(PillowTestCase):
"""failed prepatch"""
self._write_png(XDIM, XDIM)
@unittest.skipIf(numpy is None, "Numpy is not installed")
def test_size_greater_than_int(self):
arr = numpy.ndarray(shape=(16394, 16394))
Image.fromarray(arr)
if __name__ == '__main__':
unittest.main()

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,13 +1,13 @@
NotoNastaliqUrdu-Regular.ttf:
NotoNastaliqUrdu-Regular.ttf, from https://github.com/googlei18n/noto-fonts
NotoSansJP-Thin.otf, from https://www.google.com/get/noto/help/cjk/
AdobeVFPrototype.ttf, from https://github.com/adobe-fonts/adobe-variable-font-prototype
TINY5x3GX.ttf, from http://velvetyne.fr/fonts/tiny
ArefRuqaa-Regular.ttf, from https://github.com/google/fonts/tree/master/ofl/arefruqaa
(from https://github.com/googlei18n/noto-fonts)
All Noto fonts are published under the SIL Open Font License (OFL) v1.1 (http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL), which allows you to copy, modify, and redistribute them if you need to.
All of the above fonts are published under the SIL Open Font License (OFL) v1.1 (http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL), which allows you to copy, modify, and redistribute them if you need to.
10x20-ISO8859-1.pcf
(from https://packages.ubuntu.com/xenial/xfonts-base)
10x20-ISO8859-1.pcf, from https://packages.ubuntu.com/xenial/xfonts-base
"Public domain font. Share and enjoy."

Binary file not shown.

BIN
Tests/fonts/TINY5x3GX.ttf Executable file

Binary file not shown.

BIN
Tests/images/app13.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 563 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 846 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 810 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 937 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -175,7 +175,7 @@ class TestBoxBlur(PillowTestCase):
delta=0,
)
def test_exteme_large_radius(self):
def test_extreme_large_radius(self):
self.assertBlur(
sample, 600,
[

View File

@ -1,3 +1,7 @@
from __future__ import unicode_literals
import io
from .helper import unittest, PillowTestCase
from PIL import features
@ -63,3 +67,25 @@ class TestFeatures(PillowTestCase):
module = "unsupported_module"
# Act / Assert
self.assertRaises(ValueError, features.check_module, module)
def test_pilinfo(self):
buf = io.StringIO()
features.pilinfo(buf)
out = buf.getvalue()
lines = out.splitlines()
self.assertEqual(lines[0], "-" * 68)
self.assertTrue(lines[1].startswith("Pillow "))
self.assertEqual(lines[2], "-" * 68)
self.assertTrue(lines[3].startswith("Python modules loaded from "))
self.assertTrue(lines[4].startswith("Binary modules loaded from "))
self.assertEqual(lines[5], "-" * 68)
self.assertTrue(lines[6].startswith("Python "))
jpeg = (
"\n" +
"-" * 68 + "\n" +
"JPEG image/jpeg\n" +
"Extensions: .jfif, .jpe, .jpeg, .jpg\n" +
"Features: open, save\n" +
"-" * 68 + "\n"
)
self.assertIn(jpeg, out)

View File

@ -663,3 +663,9 @@ class TestFileGif(PillowTestCase):
self.assertEqual(im.tile[0][3][0], 11) # LZW bits
# codec error prepatch
im.load()
def test_extents(self):
im = Image.open('Tests/images/test_extents.gif')
self.assertEqual(im.size, (100, 100))
im.seek(1)
self.assertEqual(im.size, (150, 150))

View File

@ -61,11 +61,10 @@ class TestFileIcns(PillowTestCase):
for w, h, r in im.info['sizes']:
wr = w * r
hr = h * r
im2 = Image.open(TEST_FILE)
im2.size = (w, h, r)
im2.load()
self.assertEqual(im2.mode, 'RGBA')
self.assertEqual(im2.size, (wr, hr))
im.size = (w, h, r)
im.load()
self.assertEqual(im.mode, 'RGBA')
self.assertEqual(im.size, (wr, hr))
# Check that we cannot load an incorrect size
with self.assertRaises(ValueError):

View File

@ -1,7 +1,7 @@
from .helper import PillowTestCase, hopper
import io
from PIL import Image, IcoImagePlugin
from PIL import Image, ImageDraw, IcoImagePlugin
TEST_ICO_FILE = "Tests/images/hopper.ico"
@ -83,3 +83,23 @@ class TestFileIco(PillowTestCase):
self.assertEqual(
im_saved.info['sizes'],
{(16, 16), (24, 24), (32, 32), (48, 48)})
def test_unexpected_size(self):
# This image has been manually hexedited to state that it is 16x32
# while the image within is still 16x16
im = self.assert_warning(UserWarning,
Image.open, "Tests/images/hopper_unexpected.ico")
self.assertEqual(im.size, (16, 16))
def test_draw_reloaded(self):
im = Image.open(TEST_ICO_FILE)
outfile = self.tempfile("temp_saved_hopper_draw.ico")
draw = ImageDraw.Draw(im)
draw.line((0, 0) + im.size, '#f00')
im.save(outfile)
im = Image.open(outfile)
im.save("Tests/images/hopper_draw.ico")
reloaded = Image.open("Tests/images/hopper_draw.ico")
self.assert_image_equal(im, reloaded)

View File

@ -48,7 +48,7 @@ class TestFileIm(PillowTestCase):
im.seek(n_frames-1)
def test_roundtrip(self):
for mode in ["RGB", "P"]:
for mode in ["RGB", "P", "PA"]:
out = self.tempfile('temp.im')
im = hopper(mode)
im.save(out)

View File

@ -620,6 +620,10 @@ class TestFileJpeg(PillowTestCase):
'DisplayedUnitsY': 1,
})
# This image does not contain a Photoshop header string
im = Image.open("Tests/images/app13.jpg")
self.assertNotIn("photoshop", im.info)
@unittest.skipUnless(sys.platform.startswith('win32'), "Windows only")
class TestFileCloseW32(PillowTestCase):

View File

@ -631,6 +631,21 @@ class TestFileLibTiff(LibTiffTestCase):
# Should not raise UnicodeDecodeError or anything else
im.save(outfile)
def test_16bit_RGB_tiff(self):
im = Image.open("Tests/images/tiff_16bit_RGB.tiff")
self.assertEqual(im.mode, "RGB")
self.assertEqual(im.size, (100, 40))
self.assertEqual(
im.tile,
[('tiff_adobe_deflate', (0, 0, 100, 40), 0,
('RGB;16N', 'tiff_adobe_deflate', False))]
)
im.load()
self.assert_image_equal_tofile(
im, "Tests/images/tiff_16bit_RGB_target.png")
def test_16bit_RGBa_tiff(self):
im = Image.open("Tests/images/tiff_16bit_RGBa.tiff")
@ -687,6 +702,12 @@ class TestFileLibTiff(LibTiffTestCase):
self.assert_image_similar_tofile(im, "Tests/images/pil_sample_cmyk.jpg", 0.5)
def test_strip_cmyk_16l_jpeg(self):
infile = "Tests/images/tiff_strip_cmyk_16l_jpeg.tif"
im = Image.open(infile)
self.assert_image_similar_tofile(im, "Tests/images/pil_sample_cmyk.jpg", 0.5)
def test_strip_ycbcr_jpeg_2x2_sampling(self):
infile = "Tests/images/tiff_strip_ycbcr_jpeg_2x2_sampling.tif"
im = Image.open(infile)

View File

@ -46,6 +46,13 @@ class TestFilePalm(PillowTestCase):
self.skipKnownBadTest("Palm P image is wrong")
self.roundtrip(mode)
def test_l_ioerror(self):
# Arrange
mode = "L"
# Act / Assert
self.assertRaises(IOError, self.helper_save_as_palm, mode)
def test_rgb_ioerror(self):
# Arrange
mode = "RGB"

View File

@ -26,6 +26,11 @@ class TestFilePdf(PillowTestCase):
self.assertGreater(len(pdf.pages), 1)
else:
self.assertGreater(len(pdf.pages), 0)
with open(outfile, 'rb') as fp:
contents = fp.read()
size = tuple(int(d) for d in
contents.split(b'/MediaBox [ 0 0 ')[1].split(b']')[0].split())
self.assertEqual(im.size, size)
return outfile

View File

@ -17,6 +17,12 @@ class TestImagePsd(PillowTestCase):
im2 = hopper()
self.assert_image_similar(im, im2, 4.8)
def test_unclosed_file(self):
def open():
im = Image.open(test_file)
im.load()
self.assert_warning(None, open)
def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg"
@ -65,6 +71,12 @@ class TestImagePsd(PillowTestCase):
self.assertRaises(EOFError, im.seek, -1)
def test_open_after_exclusive_load(self):
im = Image.open(test_file)
im.load()
im.seek(im.tell()+1)
im.load()
def test_icc_profile(self):
im = Image.open(test_file)
self.assertIn("icc_profile", im.info)

View File

@ -489,6 +489,16 @@ class TestFileTiff(PillowTestCase):
self.assert_image_equal_tofile(im,
"Tests/images/tiff_adobe_deflate.png")
def test_palette(self):
for mode in ["P", "PA"]:
outfile = self.tempfile("temp.tif")
im = hopper(mode)
im.save(outfile)
reloaded = Image.open(outfile)
self.assert_image_equal(im.convert("RGB"), reloaded.convert("RGB"))
def test_tiff_save_all(self):
import io
import os

View File

@ -76,6 +76,10 @@ class TestImage(PillowTestCase):
@unittest.skipUnless(Image.HAS_PATHLIB, "requires pathlib/pathlib2")
def test_pathlib(self):
from PIL.Image import Path
im = Image.open(Path("Tests/images/multipage-mmap.tiff"))
self.assertEqual(im.mode, "P")
self.assertEqual(im.size, (10, 10))
im = Image.open(Path("Tests/images/hopper.jpg"))
self.assertEqual(im.mode, "RGB")
self.assertEqual(im.size, (128, 128))

View File

@ -26,6 +26,17 @@ class TestImageMode(PillowTestCase):
self.assertEqual(m.basemode, "L")
self.assertEqual(m.basetype, "L")
for mode in ("I;16", "I;16S",
"I;16L", "I;16LS",
"I;16B", "I;16BS",
"I;16N", "I;16NS"):
m = ImageMode.getmode(mode)
self.assertEqual(m.mode, mode)
self.assertEqual(str(m), mode)
self.assertEqual(m.bands, ("I",))
self.assertEqual(m.basemode, "L")
self.assertEqual(m.basetype, "L")
m = ImageMode.getmode("RGB")
self.assertEqual(m.mode, "RGB")
self.assertEqual(str(m), "RGB")

View File

@ -160,6 +160,15 @@ class TestImageTransform(PillowTestCase):
im = hopper()
self.assertRaises(ValueError, im.transform, (100, 100), None)
def test_unknown_resampling_filter(self):
im = hopper()
(w, h) = im.size
for resample in (Image.BOX, "unknown"):
self.assertRaises(ValueError, im.transform, (100, 100), Image.EXTENT,
(0, 0,
w, h),
resample)
class TestImageTransformAffine(PillowTestCase):
transform = Image.AFFINE

View File

@ -114,6 +114,19 @@ class TestImageDraw(PillowTestCase):
# Assert
self.assert_image_similar(im, Image.open(expected), 1)
def test_arc_width_pieslice_large(self):
# Tests an arc with a large enough width that it is a pieslice
# Arrange
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
expected = "Tests/images/imagedraw_arc_width_pieslice.png"
# Act
draw.arc(BBOX1, 10, 260, fill="yellow", width=100)
# Assert
self.assert_image_similar(im, Image.open(expected), 1)
def test_arc_width_fill(self):
# Arrange
im = Image.new("RGB", (W, H))
@ -239,6 +252,18 @@ class TestImageDraw(PillowTestCase):
# Assert
self.assert_image_similar(im, Image.open(expected), 1)
def test_ellipse_width_large(self):
# Arrange
im = Image.new("RGB", (500, 500))
draw = ImageDraw.Draw(im)
expected = "Tests/images/imagedraw_ellipse_width_large.png"
# Act
draw.ellipse((25, 25, 475, 475), outline="blue", width=75)
# Assert
self.assert_image_similar(im, Image.open(expected), 1)
def test_ellipse_width_fill(self):
# Arrange
im = Image.new("RGB", (W, H))

View File

@ -7,6 +7,7 @@ import os
import sys
import copy
import re
import shutil
import distutils.version
FONT_PATH = "Tests/fonts/FreeMono.ttf"
@ -131,6 +132,27 @@ class TestImageFont(PillowTestCase):
with open(FONT_PATH, 'rb') as f:
self._render(f)
def test_non_unicode_path(self):
try:
tempfile = self.tempfile("temp_"+chr(128)+".ttf")
except UnicodeEncodeError:
self.skipTest("Unicode path could not be created")
shutil.copy(FONT_PATH, tempfile)
ImageFont.truetype(tempfile, FONT_SIZE)
def test_unavailable_layout_engine(self):
have_raqm = ImageFont.core.HAVE_RAQM
ImageFont.core.HAVE_RAQM = False
try:
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE,
layout_engine=ImageFont.LAYOUT_RAQM)
finally:
ImageFont.core.HAVE_RAQM = have_raqm
self.assertEqual(ttf.layout_engine, ImageFont.LAYOUT_BASIC)
def _render(self, font):
txt = "Hello World!"
ttf = ImageFont.truetype(font, FONT_SIZE,
@ -400,6 +422,7 @@ class TestImageFont(PillowTestCase):
# Act/Assert
self.assertRaises(IOError, ImageFont.load_path, filename)
self.assertRaises(IOError, ImageFont.truetype, filename)
def test_default_font(self):
# Arrange
@ -547,6 +570,91 @@ class TestImageFont(PillowTestCase):
self.assertRaises(KeyError, t.getmask, 'абвг', features=['-kern'])
self.assertRaises(KeyError, t.getmask, 'абвг', language='sr')
def test_variation_get(self):
font = self.get_font()
freetype = distutils.version.StrictVersion(ImageFont.core.freetype2_version)
if freetype < '2.9.1':
self.assertRaises(NotImplementedError, font.get_variation_names)
self.assertRaises(NotImplementedError, font.get_variation_axes)
return
self.assertRaises(IOError, font.get_variation_names)
self.assertRaises(IOError, font.get_variation_axes)
font = ImageFont.truetype("Tests/fonts/AdobeVFPrototype.ttf")
self.assertEqual(
font.get_variation_names(),
[b'ExtraLight', b'Light', b'Regular', b'Semibold', b'Bold',
b'Black', b'Black Medium Contrast', b'Black High Contrast', b'Default'])
self.assertEqual(
font.get_variation_axes(),
[{'name': b'Weight', 'minimum': 200, 'maximum': 900, 'default': 389},
{'name': b'Contrast', 'minimum': 0, 'maximum': 100, 'default': 0}])
font = ImageFont.truetype("Tests/fonts/TINY5x3GX.ttf")
self.assertEqual(
font.get_variation_names(),
[b'20', b'40', b'60', b'80', b'100', b'120', b'140', b'160', b'180',
b'200', b'220', b'240', b'260', b'280', b'300', b'Regular'])
self.assertEqual(
font.get_variation_axes(),
[{'name': b'Size', 'minimum': 0, 'maximum': 300, 'default': 0}])
def test_variation_set_by_name(self):
font = self.get_font()
freetype = distutils.version.StrictVersion(ImageFont.core.freetype2_version)
if freetype < '2.9.1':
self.assertRaises(NotImplementedError, font.set_variation_by_name, "Bold")
return
self.assertRaises(IOError, font.set_variation_by_name, "Bold")
def _check_text(font, path, epsilon):
im = Image.new("RGB", (100, 75), "white")
d = ImageDraw.Draw(im)
d.text((10, 10), "Text", font=font, fill="black")
expected = Image.open(path)
self.assert_image_similar(im, expected, epsilon)
font = ImageFont.truetype("Tests/fonts/AdobeVFPrototype.ttf", 36)
_check_text(font, "Tests/images/variation_adobe.png", 11)
for name in ["Bold", b"Bold"]:
font.set_variation_by_name(name)
_check_text(font, "Tests/images/variation_adobe_name.png", 11)
font = ImageFont.truetype("Tests/fonts/TINY5x3GX.ttf", 36)
_check_text(font, "Tests/images/variation_tiny.png", 40)
for name in ["200", b"200"]:
font.set_variation_by_name(name)
_check_text(font, "Tests/images/variation_tiny_name.png", 40)
def test_variation_set_by_axes(self):
font = self.get_font()
freetype = distutils.version.StrictVersion(ImageFont.core.freetype2_version)
if freetype < '2.9.1':
self.assertRaises(NotImplementedError, font.set_variation_by_axes, [100])
return
self.assertRaises(IOError, font.set_variation_by_axes, [500, 50])
def _check_text(font, path, epsilon):
im = Image.new("RGB", (100, 75), "white")
d = ImageDraw.Draw(im)
d.text((10, 10), "Text", font=font, fill="black")
expected = Image.open(path)
self.assert_image_similar(im, expected, epsilon)
font = ImageFont.truetype("Tests/fonts/AdobeVFPrototype.ttf", 36)
font.set_variation_by_axes([500, 50])
_check_text(font, "Tests/images/variation_adobe_axes.png", 5.1)
font = ImageFont.truetype("Tests/fonts/TINY5x3GX.ttf", 36)
font.set_variation_by_axes([100])
_check_text(font, "Tests/images/variation_tiny_axes.png", 32.5)
@unittest.skipUnless(HAS_RAQM, "Raqm not Available")
class TestImageFont_RaqmLayout(TestImageFont):

View File

@ -54,6 +54,18 @@ class TestImagecomplextext(PillowTestCase):
self.assert_image_similar(im, target_img, .5)
ttf = ImageFont.truetype("Tests/fonts/KhmerOSBattambang-Regular.ttf",
FONT_SIZE)
im = Image.new(mode='RGB', size=(300, 100))
draw = ImageDraw.Draw(im)
draw.text((0, 0), 'លោកុប្បត្តិ', font=ttf, fill=500)
target = 'Tests/images/test_complex_unicode_text2.png'
target_img = Image.open(target)
self.assert_image_similar(im, target_img, 2.3)
def test_text_direction_rtl(self):
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
@ -92,6 +104,22 @@ class TestImagecomplextext(PillowTestCase):
self.assert_image_similar(im, target_img, .5)
def test_text_direction_ttb(self):
ttf = ImageFont.truetype("Tests/fonts/NotoSansJP-Regular.otf", FONT_SIZE)
im = Image.new(mode='RGB', size=(100, 300))
draw = ImageDraw.Draw(im)
try:
draw.text((0, 0), 'English あい', font=ttf, fill=500, direction='ttb')
except ValueError as ex:
if str(ex) == "libraqm 0.7 or greater required for 'ttb' direction":
self.skipTest('libraqm 0.7 or greater not available')
target = 'Tests/images/test_direction_ttb.png'
target_img = Image.open(target)
self.assert_image_similar(im, target_img, 1.15)
def test_ligature_features(self):
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
@ -131,6 +159,18 @@ class TestImagecomplextext(PillowTestCase):
self.assert_image_similar(im, target_img, .5)
def test_x_max_and_y_offset(self):
ttf = ImageFont.truetype("Tests/fonts/ArefRuqaa-Regular.ttf", 40)
im = Image.new(mode='RGB', size=(50, 100))
draw = ImageDraw.Draw(im)
draw.text((0, 0), 'لح', font=ttf, fill=500)
target = 'Tests/images/test_x_max_and_y_offset.png'
target_img = Image.open(target)
self.assert_image_similar(im, target_img, .5)
def test_language(self):
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)

View File

@ -9,8 +9,11 @@ try:
class TestImageGrab(PillowTestCase):
def test_grab(self):
im = ImageGrab.grab()
self.assert_image(im, im.mode, im.size)
for im in [
ImageGrab.grab(),
ImageGrab.grab(include_layered_windows=True)
]:
self.assert_image(im, im.mode, im.size)
def test_grabclipboard(self):
if sys.platform == "darwin":

View File

@ -32,6 +32,12 @@ class TestImageSequence(PillowTestCase):
self.assertRaises(IndexError, lambda: i[index+1])
self.assertRaises(StopIteration, next, i)
def test_iterator_min_frame(self):
im = Image.open('Tests/images/hopper.psd')
i = ImageSequence.Iterator(im)
for index in range(1, im.n_frames):
self.assertEqual(i[index], next(i))
def _test_multipage_tiff(self):
im = Image.open('Tests/images/multipage.tiff')
for index, frame in enumerate(ImageSequence.Iterator(im)):

View File

@ -18,18 +18,19 @@ class TestImageShow(PillowTestCase):
ImageShow._viewers.pop()
def test_show(self):
class TestViewer:
class TestViewer(ImageShow.Viewer):
methodCalled = False
def show(self, image, title=None, **options):
def show_image(self, image, **options):
self.methodCalled = True
return True
viewer = TestViewer()
ImageShow.register(viewer, -1)
im = hopper()
self.assertTrue(ImageShow.show(im))
self.assertTrue(viewer.methodCalled)
for mode in ("1", "I;16", "LA", "RGB", "RGBA"):
im = hopper(mode)
self.assertTrue(ImageShow.show(im))
self.assertTrue(viewer.methodCalled)
# Restore original state
ImageShow._viewers.pop(0)

View File

@ -61,9 +61,8 @@ class TestImageTk(PillowTestCase):
self.assertEqual(im_tk.width(), im.width)
self.assertEqual(im_tk.height(), im.height)
# _tkinter.TclError: this function is not yet supported
# reloaded = ImageTk.getimage(im_tk)
# self.assert_image_equal(reloaded, im)
reloaded = ImageTk.getimage(im_tk)
self.assert_image_equal(reloaded, im.convert("RGBA"))
def test_photoimage_blank(self):
# test a image using mode/size:

28
Tests/test_main.py Normal file
View File

@ -0,0 +1,28 @@
from __future__ import unicode_literals
import os
import subprocess
import sys
from unittest import TestCase
class TestMain(TestCase):
def test_main(self):
out = subprocess.check_output([sys.executable, "-m", "PIL"]).decode("utf-8")
lines = out.splitlines()
self.assertEqual(lines[0], "-" * 68)
self.assertTrue(lines[1].startswith("Pillow "))
self.assertEqual(lines[2], "-" * 68)
self.assertTrue(lines[3].startswith("Python modules loaded from "))
self.assertTrue(lines[4].startswith("Binary modules loaded from "))
self.assertEqual(lines[5], "-" * 68)
self.assertTrue(lines[6].startswith("Python "))
jpeg = (
os.linesep +
"-" * 68 + os.linesep +
"JPEG image/jpeg" + os.linesep +
"Extensions: .jfif, .jpe, .jpeg, .jpg" + os.linesep +
"Features: open, save" + os.linesep +
"-" * 68 + os.linesep
)
self.assertIn(jpeg, out)

View File

@ -113,12 +113,7 @@ class TestNumpy(PillowTestCase):
img = Image.fromarray(arr * 255).convert('1')
self.assertEqual(img.mode, '1')
arr_back = numpy.array(img)
# numpy 1.8 and earlier return this as a boolean. (trusty/precise)
if arr_back.dtype == numpy.bool:
arr_bool = numpy.array([[1, 0, 0, 1, 0], [0, 1, 0, 0, 0]], numpy.bool)
numpy.testing.assert_array_equal(arr_bool, arr_back)
else:
numpy.testing.assert_array_equal(arr, arr_back)
numpy.testing.assert_array_equal(arr, arr_back)
def test_save_tiff_uint16(self):
# Tests that we're getting the pixel value in the right byte order.
@ -183,6 +178,14 @@ class TestNumpy(PillowTestCase):
self.assertEqual(len(im.getdata()), len(arr))
def test_roundtrip_eye(self):
for dtype in (numpy.bool, numpy.bool8,
numpy.int8, numpy.int16, numpy.int32,
numpy.uint8, numpy.uint16, numpy.uint32,
numpy.float, numpy.float32, numpy.float64):
arr = numpy.eye(10, dtype=dtype)
numpy.testing.assert_array_equal(arr, numpy.array(Image.fromarray(arr)))
def test_zero_size(self):
# Shouldn't cause floating point exception
# See https://github.com/python-pillow/Pillow/issues/2259

View File

@ -75,6 +75,15 @@ class TestPickle(PillowTestCase):
protocol=protocol,
test_file=test_file)
def test_pickle_pa_mode(self):
# Arrange
import pickle
# Act / Assert
for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1):
self.helper_pickle_string(pickle, protocol, mode="PA")
self.helper_pickle_file(pickle, protocol, mode="PA")
def test_pickle_l_mode(self):
# Arrange
import pickle
@ -84,6 +93,24 @@ class TestPickle(PillowTestCase):
self.helper_pickle_string(pickle, protocol, mode="L")
self.helper_pickle_file(pickle, protocol, mode="L")
def test_pickle_la_mode_with_palette(self):
# Arrange
import pickle
im = Image.open('Tests/images/hopper.jpg')
filename = self.tempfile('temp.pkl')
im = im.convert("PA")
# Act / Assert
for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1):
im.mode = "LA"
with open(filename, 'wb') as f:
pickle.dump(im, f, protocol)
with open(filename, 'rb') as f:
loaded_im = pickle.load(f)
im.mode = "PA"
self.assertEqual(im, loaded_im)
def test_cpickle_l_mode(self):
# Arrange
try:

View File

@ -26,5 +26,5 @@ class TestPyroma(PillowTestCase):
)
else:
# Should have a near-perfect score
self.assertEqual(rating, (9, ["Your package does not have license data."]))
# Should have a perfect score
self.assertEqual(rating, (10, []))

View File

@ -23,13 +23,13 @@ jobs:
- template: .azure-pipelines/jobs/test-docker.yml
parameters:
docker: 'ubuntu-trusty-x86'
name: 'ubuntu_trusty_x86'
docker: 'ubuntu-16.04-xenial-amd64'
name: 'ubuntu_16_04_xenial_amd64'
- template: .azure-pipelines/jobs/test-docker.yml
parameters:
docker: 'ubuntu-xenial-amd64'
name: 'ubuntu_xenial_amd64'
docker: 'ubuntu-18.04-bionic-amd64'
name: 'ubuntu_18_04_bionic_amd64'
- template: .azure-pipelines/jobs/test-docker.yml
parameters:
@ -58,10 +58,10 @@ jobs:
- template: .azure-pipelines/jobs/test-docker.yml
parameters:
docker: 'fedora-28-amd64'
name: 'fedora_28_amd64'
docker: 'fedora-29-amd64'
name: 'fedora_29_amd64'
- template: .azure-pipelines/jobs/test-docker.yml
parameters:
docker: 'fedora-29-amd64'
name: 'fedora_29_amd64'
docker: 'fedora-30-amd64'
name: 'fedora_30_amd64'

View File

@ -1,7 +1,7 @@
#!/bin/bash
# install libimagequant
archive=libimagequant-2.12.2
archive=libimagequant-2.12.3
./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/master/$archive.tar.gz

View File

@ -1,7 +1,7 @@
#!/bin/bash
# install openjpeg
archive=openjpeg-2.3.0
archive=openjpeg-2.3.1
./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/master/$archive.tar.gz

View File

@ -2,7 +2,7 @@
# install raqm
archive=raqm-0.5.0
archive=raqm-0.7.0
./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/master/$archive.tar.gz

View File

@ -2,7 +2,7 @@
# install raqm
archive=raqm-cmake-b517ba80
archive=raqm-cmake-99300ff3
./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/master/$archive.tar.gz

View File

@ -76,8 +76,8 @@ PILLOW_VERSION constant
.. deprecated:: 5.2.0
``PILLOW_VERSION`` has been deprecated and will be removed in the next
major release. Use ``__version__`` instead.
``PILLOW_VERSION`` has been deprecated and will be removed in 7.0.0. Use ``__version__``
instead.
ImageCms.CmsProfile attributes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -24,8 +24,10 @@ To get the number and names of bands in an image, use the
Modes
-----
The ``mode`` of an image defines the type and depth of a pixel in the
image. The current release supports the following standard modes:
The ``mode`` of an image defines the type and depth of a pixel in the image.
Each pixel uses the full range of the bit depth. So a 1-bit pixel has a range
of 0-1, an 8-bit pixel has a range of 0-255 and so on. The current release
supports the following standard modes:
* ``1`` (1-bit pixels, black and white, stored with one pixel per byte)
* ``L`` (8-bit pixels, black and white)

View File

@ -33,7 +33,7 @@ DIB interface <PIL.ImageWin>` that can be used with PythonWin and other
Windows-based toolkits. Many other GUI toolkits come with some kind of PIL
support.
For debugging, theres also a :py:meth:`show` method which saves an image to
For debugging, theres also a :py:meth:`~PIL.Image.Image.show` method which saves an image to
disk, and calls an external display utility.
Image Processing

View File

@ -473,8 +473,8 @@ Reading from a string
::
from PIL import Image
import StringIO
im = Image.open(StringIO.StringIO(buffer))
Note that the library rewinds the file (using ``seek(0)``) before reading the

View File

@ -255,9 +255,10 @@ If the raw decoder cannot handle your format, PIL also provides a special “bit
decoder that can be used to read various packed formats into a floating point
image memory.
To use the bit decoder with the frombytes function, use the following syntax::
To use the bit decoder with the :py:func:`PIL.Image.frombytes` function, use
the following syntax::
image = frombytes(
image = Image.frombytes(
mode, size, data, "bit",
bits, pad, fill, sign, orientation
)

View File

@ -163,17 +163,17 @@ Many of Pillow's features require external libraries:
* **openjpeg** provides JPEG 2000 functionality.
* Pillow has been tested with openjpeg **2.0.0** and **2.1.0**.
* Pillow has been tested with openjpeg **2.0.0**, **2.1.0** and **2.3.1**.
* Pillow does **not** support the earlier **1.5** series which ships
with Ubuntu <= 14.04 and Debian Jessie.
with Debian Jessie.
* **libimagequant** provides improved color quantization
* Pillow has been tested with libimagequant **2.6-2.12.2**
* Pillow has been tested with libimagequant **2.6-2.12.3**
* Libimagequant is licensed GPLv3, which is more restrictive than
the Pillow license, therefore we will not be distributing binaries
with libimagequant support enabled.
* Windows support: Libimagequant requires VS2013/MSVC 18 to compile,
* Windows support: Libimagequant requires VS2015/MSVC 19 to compile,
so it is unlikely to work with Python 2.7 on Windows.
* **libraqm** provides complex text layout support.
@ -382,37 +382,35 @@ These platforms are built and tested for every change.
+----------------------------------+-------------------------------+-----------------------+
|**Operating system** |**Tested Python versions** |**Tested Architecture**|
+----------------------------------+-------------------------------+-----------------------+
| Alpine | 2.7 |x86-64 |
| Alpine | 2.7, 3.6 |x86-64 |
+----------------------------------+-------------------------------+-----------------------+
| Arch | 2.7 |x86-64 |
| Arch | 2.7, 3.7 |x86-64 |
+----------------------------------+-------------------------------+-----------------------+
| Amazon | 2.7 |x86-64 |
| Amazon Linux 1 | 2.7, 3.6 |x86-64 |
+----------------------------------+-------------------------------+-----------------------+
| Centos 6 | 2.7 |x86-64 |
| Amazon Linux 2 | 2.7, 3.6 |x86-64 |
+----------------------------------+-------------------------------+-----------------------+
| Centos 7 | 2.7 |x86-64 |
| CentOS 6 | 2.7, 3.6 |x86-64 |
+----------------------------------+-------------------------------+-----------------------+
| Debian Stretch | 2.7 |x86 |
| CentOS 7 | 2.7, 3.6 |x86-64 |
+----------------------------------+-------------------------------+-----------------------+
| Fedora 28 | 2.7 |x86-64 |
| Debian 9 Stretch | 2.7, 3.5 |x86 |
+----------------------------------+-------------------------------+-----------------------+
| Fedora 29 | 2.7 |x86-64 |
| Fedora 29 | 2.7, 3.7 |x86-64 |
+----------------------------------+-------------------------------+-----------------------+
| Mac OS X 10.10 Yosemite* | 2.7, 3.5, 3.6, 3.7 |x86-64 |
| Fedora 30 | 2.7, 3.7 |x86-64 |
+----------------------------------+-------------------------------+-----------------------+
| macOS 10.13 High Sierra* | 2.7, 3.5, 3.6, 3.7 |x86-64 |
+----------------------------------+-------------------------------+-----------------------+
| Ubuntu Linux 16.04 LTS | 2.7, 3.5, 3.6, 3.7, |x86-64 |
| | PyPy, PyPy3 | |
+----------------------------------+-------------------------------+-----------------------+
| Ubuntu Linux 14.04 LTS | 2.7, 3.5, 3.6 |x86-64 |
| +-------------------------------+-----------------------+
| | 2.7 |x86 |
+----------------------------------+-------------------------------+-----------------------+
| Windows Server 2012 R2 | 2.7, 3.5, 3.6, 3.7 |x86, x86-64 |
| +-------------------------------+-----------------------+
| | PyPy, 3.7/MinGW |x86 |
+----------------------------------+-------------------------------+-----------------------+
\* Mac OS X CI is not run for every commit, but is run for every release.
\* macOS CI is not run for every commit, but is run for every release.
Other Platforms
^^^^^^^^^^^^^^^
@ -427,7 +425,9 @@ These platforms have been reported to work at the versions mentioned.
+----------------------------------+------------------------------+--------------------------------+-----------------------+
|**Operating system** |**Tested Python versions** |**Latest tested Pillow version**|**Tested processors** |
+----------------------------------+------------------------------+--------------------------------+-----------------------+
| macOS 10.14 Mojave | 2.7, 3.4, 3.5, 3.6, 3.7 | 5.4.1 |x86-64 |
| macOS 10.14 Mojave | 2.7, 3.5, 3.6, 3.7 | 6.0.0 |x86-64 |
| +------------------------------+--------------------------------+ +
| | 3.4 | 5.4.1 | |
+----------------------------------+------------------------------+--------------------------------+-----------------------+
| macOS 10.13 High Sierra | 2.7, 3.4, 3.5, 3.6 | 4.2.1 |x86-64 |
+----------------------------------+------------------------------+--------------------------------+-----------------------+

View File

@ -210,9 +210,9 @@ Instances of the :py:class:`Image` class have the following attributes:
.. py:attribute:: palette
Colour palette table, if any. If mode is “P”, this should be an instance of
the :py:class:`~PIL.ImagePalette.ImagePalette` class. Otherwise, it should
be set to ``None``.
Colour palette table, if any. If mode is "P" or "PA", this should be an
instance of the :py:class:`~PIL.ImagePalette.ImagePalette` class.
Otherwise, it should be set to ``None``.
:type: :py:class:`~PIL.ImagePalette.ImagePalette` or ``None``

View File

@ -291,7 +291,7 @@ Methods
different glyph shapes or ligatures. This parameter tells
the font which language the text is in, and to apply the
correct substitutions as appropriate, if available.
It should be a `BCP47 language code
It should be a `BCP 47 language code
<https://www.w3.org/International/articles/language-tags/>`
Requires libraqm.
@ -330,7 +330,7 @@ Methods
different glyph shapes or ligatures. This parameter tells
the font which language the text is in, and to apply the
correct substitutions as appropriate, if available.
It should be a `BCP47 language code
It should be a `BCP 47 language code
<https://www.w3.org/International/articles/language-tags/>`
Requires libraqm.
@ -366,7 +366,7 @@ Methods
different glyph shapes or ligatures. This parameter tells
the font which language the text is in, and to apply the
correct substitutions as appropriate, if available.
It should be a `BCP47 language code
It should be a `BCP 47 language code
<https://www.w3.org/International/articles/language-tags/>`
Requires libraqm.
@ -402,7 +402,7 @@ Methods
different glyph shapes or ligatures. This parameter tells
the font which language the text is in, and to apply the
correct substitutions as appropriate, if available.
It should be a `BCP47 language code
It should be a `BCP 47 language code
<https://www.w3.org/International/articles/language-tags/>`
Requires libraqm.

View File

@ -38,12 +38,31 @@ image enhancement filters:
* **SMOOTH_MORE**
.. autoclass:: PIL.ImageFilter.Color3DLUT
:members:
.. autoclass:: PIL.ImageFilter.BoxBlur
:members:
.. autoclass:: PIL.ImageFilter.GaussianBlur
:members:
.. autoclass:: PIL.ImageFilter.UnsharpMask
:members:
.. autoclass:: PIL.ImageFilter.Kernel
:members:
.. autoclass:: PIL.ImageFilter.RankFilter
:members:
.. autoclass:: PIL.ImageFilter.MedianFilter
:members:
.. autoclass:: PIL.ImageFilter.MinFilter
:members:
.. autoclass:: PIL.ImageFilter.MaxFilter
:members:
.. autoclass:: PIL.ImageFilter.ModeFilter
:members:

View File

@ -77,7 +77,7 @@ Methods
different glyph shapes or ligatures. This parameter tells
the font which language the text is in, and to apply the
correct substitutions as appropriate, if available.
It should be a `BCP47 language code
It should be a `BCP 47 language code
<https://www.w3.org/International/articles/language-tags/>`
Requires libraqm.
@ -123,7 +123,7 @@ Methods
different glyph shapes or ligatures. This parameter tells
the font which language the text is in, and to apply the
correct substitutions as appropriate, if available.
It should be a `BCP47 language code
It should be a `BCP 47 language code
<https://www.w3.org/International/articles/language-tags/>`
Requires libraqm.
@ -131,3 +131,6 @@ Methods
:return: An internal PIL storage memory instance as defined by the
:py:mod:`PIL.Image.core` interface module.
.. autoclass:: PIL.ImageFont.FreeTypeFont
:members:

View File

@ -11,7 +11,7 @@ or the clipboard to a PIL image memory.
.. versionadded:: 1.1.3
.. py:function:: PIL.ImageGrab.grab(bbox=None)
.. py:function:: PIL.ImageGrab.grab(bbox=None, include_layered_windows=False)
Take a snapshot of the screen. The pixels inside the bounding box are
returned as an "RGB" image on Windows or "RGBA" on macOS.
@ -20,6 +20,7 @@ or the clipboard to a PIL image memory.
.. versionadded:: 1.1.3 (Windows), 3.0.0 (macOS)
:param bbox: What region to copy. Default is the entire screen.
:param include_layered_windows: Includes layered windows. Windows OS only.
:return: An image
.. py:function:: PIL.ImageGrab.grabclipboard()

View File

@ -5,8 +5,8 @@
==========================
The :py:mod:`ImageMath` module can be used to evaluate “image expressions”. The
module provides a single eval function, which takes an expression string and
one or more images.
module provides a single :py:meth:`~PIL.ImageMath.eval` function, which takes
an expression string and one or more images.
Example: Using the :py:mod:`~PIL.ImageMath` module
--------------------------------------------------

View File

@ -25,3 +25,4 @@ The :py:class:`~PIL.ImageSequence.Iterator` class
-------------------------------------------------
.. autoclass:: PIL.ImageSequence.Iterator
:members:

View File

@ -20,6 +20,13 @@ for a region of an image.
Min/max values for each band in the image.
.. Note:: This relies on the :py:meth:`~PIL.Image.histogram` method, and simply
returns the low and high bins used. This is correct for images with 8 bits per
channel, but fails for other modes such as ``I`` or ``F``. Instead, use
:py:meth:`~PIL.Image.getextrema` to return per-band extrema for the image.
This is more correct and efficient because, for non-8-bit modes, the histogram
method uses :py:meth:`~PIL.Image.getextrema` to determine the bins used.
.. py:attribute:: count
Total number of pixels for each band in the image.

View File

@ -24,6 +24,8 @@ Tkinter makes the window handle available via the winfo_id method:
.. autoclass:: PIL.ImageWin.Dib
:members:
.. autoclass:: PIL.ImageWin.HDC
:members:
.. autoclass:: PIL.ImageWin.HWND
:members:

View File

@ -0,0 +1,39 @@
6.1.0
-----
API Additions
=============
ImageGrab.grab
^^^^^^^^^^^^^^
An optional ``include_layered_windows`` parameter has been added to ``ImageGrab.grab``,
defaulting to ``False``. If true, layered windows will be included in the resulting
image on Windows.
Variation fonts
^^^^^^^^^^^^^^^
Variation fonts are now supported, allowing for different styles from the same font
file. ``ImageFont.FreeTypeFont`` has four new methods,
:py:meth:`PIL.ImageFont.FreeTypeFont.get_variation_names` and
:py:meth:`PIL.ImageFont.FreeTypeFont.set_variation_by_name` for using named styles, and
:py:meth:`PIL.ImageFont.FreeTypeFont.get_variation_axes` and
:py:meth:`PIL.ImageFont.FreeTypeFont.set_variation_by_axes` for using font axes
instead. An ``IOError`` will be raised if the font is not a variation font. FreeType
2.9.1 or greater is required.
Other Changes
=============
ImageTk.getimage
^^^^^^^^^^^^^^^^
This function is now supported. It returns the contents of an ``ImageTk.PhotoImage`` as
an RGBA ``Image.Image`` instance.
Top To Bottom Complex Text Rendering
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Drawing text in the 'ttb' direction with ImageFont has been significantly improved
and requires Raqm 0.7 or greater.

View File

@ -6,6 +6,7 @@ Release Notes
.. toctree::
:maxdepth: 2
6.1.0
6.0.0
5.4.1
5.4.0

View File

@ -2,6 +2,7 @@
-e .
alabaster
Babel
black; python_version >= '3.6'
check-manifest
cov-core
coverage

View File

@ -1,8 +1,6 @@
[aliases]
test=pytest
[metadata]
license_file = LICENSE
[flake8]
extend-ignore = E203, W503
max-line-length = 88

View File

@ -439,9 +439,13 @@ class pil_build_ext(build_ext):
# alpine, at least
_add_directory(library_dirs, "/lib")
# on Windows, look for the OpenJPEG libraries in the location that
# the official installer puts them
if sys.platform == "win32":
if PLATFORM_MINGW:
_add_directory(include_dirs,
"C:\\msys64\\mingw32\\include\\libimagequant")
# on Windows, look for the OpenJPEG libraries in the location that
# the official installer puts them
program_files = os.environ.get('ProgramFiles', '')
best_version = (0, 0)
best_path = None

View File

@ -32,14 +32,10 @@ bdf_slant = {
"O": "Oblique",
"RI": "Reverse Italic",
"RO": "Reverse Oblique",
"OT": "Other"
"OT": "Other",
}
bdf_spacing = {
"P": "Proportional",
"M": "Monospaced",
"C": "Cell"
}
bdf_spacing = {"P": "Proportional", "M": "Monospaced", "C": "Cell"}
def bdf_char(f):
@ -50,7 +46,7 @@ def bdf_char(f):
return None
if s[:9] == b"STARTCHAR":
break
id = s[9:].strip().decode('ascii')
id = s[9:].strip().decode("ascii")
# load symbol properties
props = {}
@ -59,7 +55,7 @@ def bdf_char(f):
if not s or s[:6] == b"BITMAP":
break
i = s.find(b" ")
props[s[:i].decode('ascii')] = s[i+1:-1].decode('ascii')
props[s[:i].decode("ascii")] = s[i + 1 : -1].decode("ascii")
# load bitmap
bitmap = []
@ -73,7 +69,7 @@ def bdf_char(f):
[x, y, l, d] = [int(p) for p in props["BBX"].split()]
[dx, dy] = [int(p) for p in props["DWIDTH"].split()]
bbox = (dx, dy), (l, -d-y, x+l, -d), (0, 0, x, y)
bbox = (dx, dy), (l, -d - y, x + l, -d), (0, 0, x, y)
try:
im = Image.frombytes("1", (x, y), bitmap, "hex", "1")
@ -87,8 +83,8 @@ def bdf_char(f):
##
# Font file plugin for the X11 BDF format.
class BdfFontFile(FontFile.FontFile):
class BdfFontFile(FontFile.FontFile):
def __init__(self, fp):
FontFile.FontFile.__init__(self)
@ -105,10 +101,10 @@ class BdfFontFile(FontFile.FontFile):
if not s or s[:13] == b"ENDPROPERTIES":
break
i = s.find(b" ")
props[s[:i].decode('ascii')] = s[i+1:-1].decode('ascii')
props[s[:i].decode("ascii")] = s[i + 1 : -1].decode("ascii")
if s[:i] in [b"COMMENT", b"COPYRIGHT"]:
if s.find(b"LogicalFontDescription") < 0:
comments.append(s[i+1:-1].decode('ascii'))
comments.append(s[i + 1 : -1].decode("ascii"))
while True:
c = bdf_char(fp)

View File

@ -47,11 +47,7 @@ BLP_ALPHA_ENCODING_DXT5 = 7
def unpack_565(i):
return (
((i >> 11) & 0x1f) << 3,
((i >> 5) & 0x3f) << 2,
(i & 0x1f) << 3
)
return (((i >> 11) & 0x1F) << 3, ((i >> 5) & 0x3F) << 2, (i & 0x1F) << 3)
def decode_dxt1(data, alpha=False):
@ -119,7 +115,7 @@ def decode_dxt3(data):
for block in range(blocks):
idx = block * 16
block = data[idx:idx + 16]
block = data[idx : idx + 16]
# Decode next 16-byte block.
bits = struct.unpack_from("<8B", block)
color0, color1 = struct.unpack_from("<HH", block, 8)
@ -139,7 +135,7 @@ def decode_dxt3(data):
a >>= 4
else:
high = True
a &= 0xf
a &= 0xF
a *= 17 # We get a value between 0 and 15
color_code = (code >> 2 * (4 * j + i)) & 0x03
@ -172,14 +168,12 @@ def decode_dxt5(data):
for block in range(blocks):
idx = block * 16
block = data[idx:idx + 16]
block = data[idx : idx + 16]
# Decode next 16-byte block.
a0, a1 = struct.unpack_from("<BB", block)
bits = struct.unpack_from("<6B", block, 2)
alphacode1 = (
bits[2] | (bits[3] << 8) | (bits[4] << 16) | (bits[5] << 24)
)
alphacode1 = bits[2] | (bits[3] << 8) | (bits[4] << 16) | (bits[5] << 24)
alphacode2 = bits[0] | (bits[1] << 8)
color0, color1 = struct.unpack_from("<HH", block, 8)
@ -242,6 +236,7 @@ class BlpImageFile(ImageFile.ImageFile):
"""
Blizzard Mipmap Format
"""
format = "BLP"
format_description = "Blizzard Mipmap Format"
@ -258,9 +253,7 @@ class BlpImageFile(ImageFile.ImageFile):
else:
raise BLPFormatError("Bad BLP magic %r" % (self.magic))
self.tile = [
(decoder, (0, 0) + self.size, 0, (self.mode, 0, 1))
]
self.tile = [(decoder, (0, 0) + self.size, 0, (self.mode, 0, 1))]
def _read_blp_header(self):
self._blp_compression, = struct.unpack("<i", self.fp.read(4))
@ -324,7 +317,6 @@ class _BLPBaseDecoder(ImageFile.PyDecoder):
class BLP1Decoder(_BLPBaseDecoder):
def _load(self):
if self._blp_compression == BLP_FORMAT_JPEG:
self._decode_jpeg_stream()
@ -368,7 +360,6 @@ class BLP1Decoder(_BLPBaseDecoder):
class BLP2Decoder(_BLPBaseDecoder):
def _load(self):
palette = self._read_palette()
@ -393,8 +384,7 @@ class BLP2Decoder(_BLPBaseDecoder):
linesize = (self.size[0] + 3) // 4 * 8
for yb in range((self.size[1] + 3) // 4):
for d in decode_dxt1(
self.fd.read(linesize),
alpha=bool(self._blp_alpha_depth)
self.fd.read(linesize), alpha=bool(self._blp_alpha_depth)
):
data += d
@ -410,18 +400,14 @@ class BLP2Decoder(_BLPBaseDecoder):
for d in decode_dxt5(self.fd.read(linesize)):
data += d
else:
raise BLPFormatError("Unsupported alpha encoding %r" % (
self._blp_alpha_encoding
))
raise BLPFormatError(
"Unsupported alpha encoding %r" % (self._blp_alpha_encoding)
)
else:
raise BLPFormatError(
"Unknown BLP encoding %r" % (self._blp_encoding)
)
raise BLPFormatError("Unknown BLP encoding %r" % (self._blp_encoding))
else:
raise BLPFormatError(
"Unknown BLP compression %r" % (self._blp_compression)
)
raise BLPFormatError("Unknown BLP compression %r" % (self._blp_compression))
self.set_as_raw(bytes(data))

View File

@ -25,8 +25,7 @@
from . import Image, ImageFile, ImagePalette
from ._binary import i8, i16le as i16, i32le as i32, \
o8, o16le as o16, o32le as o32
from ._binary import i8, i16le as i16, i32le as i32, o8, o16le as o16, o32le as o32
# __version__ is deprecated and will be removed in a future version. Use
# PIL.__version__ instead.
@ -66,15 +65,9 @@ class BmpImageFile(ImageFile.ImageFile):
format = "BMP"
# -------------------------------------------------- BMP Compression values
COMPRESSIONS = {
'RAW': 0,
'RLE8': 1,
'RLE4': 2,
'BITFIELDS': 3,
'JPEG': 4,
'PNG': 5
}
RAW, RLE8, RLE4, BITFIELDS, JPEG, PNG = 0, 1, 2, 3, 4, 5
COMPRESSIONS = {"RAW": 0, "RLE8": 1, "RLE4": 2, "BITFIELDS": 3, "JPEG": 4, "PNG": 5}
for k, v in COMPRESSIONS.items():
vars()[k] = v
def _bitmap(self, header=0, offset=0):
""" Read relevant info about the BMP """
@ -83,53 +76,54 @@ class BmpImageFile(ImageFile.ImageFile):
seek(header)
file_info = {}
# read bmp header size @offset 14 (this is part of the header size)
file_info['header_size'] = i32(read(4))
file_info['direction'] = -1
file_info["header_size"] = i32(read(4))
file_info["direction"] = -1
# -------------------- If requested, read header at a specific position
# read the rest of the bmp header, without its size
header_data = ImageFile._safe_read(self.fp,
file_info['header_size'] - 4)
header_data = ImageFile._safe_read(self.fp, file_info["header_size"] - 4)
# -------------------------------------------------- IBM OS/2 Bitmap v1
# ----- This format has different offsets because of width/height types
if file_info['header_size'] == 12:
file_info['width'] = i16(header_data[0:2])
file_info['height'] = i16(header_data[2:4])
file_info['planes'] = i16(header_data[4:6])
file_info['bits'] = i16(header_data[6:8])
file_info['compression'] = self.RAW
file_info['palette_padding'] = 3
if file_info["header_size"] == 12:
file_info["width"] = i16(header_data[0:2])
file_info["height"] = i16(header_data[2:4])
file_info["planes"] = i16(header_data[4:6])
file_info["bits"] = i16(header_data[6:8])
file_info["compression"] = self.RAW
file_info["palette_padding"] = 3
# --------------------------------------------- Windows Bitmap v2 to v5
# v3, OS/2 v2, v4, v5
elif file_info['header_size'] in (40, 64, 108, 124):
file_info['y_flip'] = i8(header_data[7]) == 0xff
file_info['direction'] = 1 if file_info['y_flip'] else -1
file_info['width'] = i32(header_data[0:4])
file_info['height'] = (i32(header_data[4:8])
if not file_info['y_flip']
else 2**32 - i32(header_data[4:8]))
file_info['planes'] = i16(header_data[8:10])
file_info['bits'] = i16(header_data[10:12])
file_info['compression'] = i32(header_data[12:16])
elif file_info["header_size"] in (40, 64, 108, 124):
file_info["y_flip"] = i8(header_data[7]) == 0xFF
file_info["direction"] = 1 if file_info["y_flip"] else -1
file_info["width"] = i32(header_data[0:4])
file_info["height"] = (
i32(header_data[4:8])
if not file_info["y_flip"]
else 2 ** 32 - i32(header_data[4:8])
)
file_info["planes"] = i16(header_data[8:10])
file_info["bits"] = i16(header_data[10:12])
file_info["compression"] = i32(header_data[12:16])
# byte size of pixel data
file_info['data_size'] = i32(header_data[16:20])
file_info['pixels_per_meter'] = (i32(header_data[20:24]),
i32(header_data[24:28]))
file_info['colors'] = i32(header_data[28:32])
file_info['palette_padding'] = 4
file_info["data_size"] = i32(header_data[16:20])
file_info["pixels_per_meter"] = (
i32(header_data[20:24]),
i32(header_data[24:28]),
)
file_info["colors"] = i32(header_data[28:32])
file_info["palette_padding"] = 4
self.info["dpi"] = tuple(
int(x / 39.3701 + 0.5) for x in file_info['pixels_per_meter'])
if file_info['compression'] == self.BITFIELDS:
int(x / 39.3701 + 0.5) for x in file_info["pixels_per_meter"]
)
if file_info["compression"] == self.BITFIELDS:
if len(header_data) >= 52:
for idx, mask in enumerate(['r_mask',
'g_mask',
'b_mask',
'a_mask']):
file_info[mask] = i32(
header_data[36 + idx * 4:40 + idx * 4]
)
for idx, mask in enumerate(
["r_mask", "g_mask", "b_mask", "a_mask"]
):
file_info[mask] = i32(header_data[36 + idx * 4 : 40 + idx * 4])
else:
# 40 byte headers only have the three components in the
# bitfields masks, ref:
@ -139,121 +133,133 @@ class BmpImageFile(ImageFile.ImageFile):
# There is a 4th component in the RGBQuad, in the alpha
# location, but it is listed as a reserved component,
# and it is not generally an alpha channel
file_info['a_mask'] = 0x0
for mask in ['r_mask', 'g_mask', 'b_mask']:
file_info["a_mask"] = 0x0
for mask in ["r_mask", "g_mask", "b_mask"]:
file_info[mask] = i32(read(4))
file_info['rgb_mask'] = (file_info['r_mask'],
file_info['g_mask'],
file_info['b_mask'])
file_info['rgba_mask'] = (file_info['r_mask'],
file_info['g_mask'],
file_info['b_mask'],
file_info['a_mask'])
file_info["rgb_mask"] = (
file_info["r_mask"],
file_info["g_mask"],
file_info["b_mask"],
)
file_info["rgba_mask"] = (
file_info["r_mask"],
file_info["g_mask"],
file_info["b_mask"],
file_info["a_mask"],
)
else:
raise IOError("Unsupported BMP header type (%d)" %
file_info['header_size'])
raise IOError("Unsupported BMP header type (%d)" % file_info["header_size"])
# ------------------ Special case : header is reported 40, which
# ---------------------- is shorter than real size for bpp >= 16
self._size = file_info['width'], file_info['height']
self._size = file_info["width"], file_info["height"]
# ------- If color count was not found in the header, compute from bits
file_info["colors"] = (file_info["colors"]
if file_info.get("colors", 0)
else (1 << file_info["bits"]))
file_info["colors"] = (
file_info["colors"]
if file_info.get("colors", 0)
else (1 << file_info["bits"])
)
# ------------------------------- Check abnormal values for DOS attacks
if file_info['width'] * file_info['height'] > 2**31:
if file_info["width"] * file_info["height"] > 2 ** 31:
raise IOError("Unsupported BMP Size: (%dx%d)" % self.size)
# ---------------------- Check bit depth for unusual unsupported values
self.mode, raw_mode = BIT2MODE.get(file_info['bits'], (None, None))
self.mode, raw_mode = BIT2MODE.get(file_info["bits"], (None, None))
if self.mode is None:
raise IOError("Unsupported BMP pixel depth (%d)"
% file_info['bits'])
raise IOError("Unsupported BMP pixel depth (%d)" % file_info["bits"])
# ---------------- Process BMP with Bitfields compression (not palette)
if file_info['compression'] == self.BITFIELDS:
if file_info["compression"] == self.BITFIELDS:
SUPPORTED = {
32: [(0xff0000, 0xff00, 0xff, 0x0),
(0xff0000, 0xff00, 0xff, 0xff000000),
(0xff, 0xff00, 0xff0000, 0xff000000),
(0x0, 0x0, 0x0, 0x0),
(0xff000000, 0xff0000, 0xff00, 0x0)],
24: [(0xff0000, 0xff00, 0xff)],
16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)]
32: [
(0xFF0000, 0xFF00, 0xFF, 0x0),
(0xFF0000, 0xFF00, 0xFF, 0xFF000000),
(0xFF, 0xFF00, 0xFF0000, 0xFF000000),
(0x0, 0x0, 0x0, 0x0),
(0xFF000000, 0xFF0000, 0xFF00, 0x0),
],
24: [(0xFF0000, 0xFF00, 0xFF)],
16: [(0xF800, 0x7E0, 0x1F), (0x7C00, 0x3E0, 0x1F)],
}
MASK_MODES = {
(32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX",
(32, (0xff000000, 0xff0000, 0xff00, 0x0)): "XBGR",
(32, (0xff, 0xff00, 0xff0000, 0xff000000)): "RGBA",
(32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA",
(32, (0xFF0000, 0xFF00, 0xFF, 0x0)): "BGRX",
(32, (0xFF000000, 0xFF0000, 0xFF00, 0x0)): "XBGR",
(32, (0xFF, 0xFF00, 0xFF0000, 0xFF000000)): "RGBA",
(32, (0xFF0000, 0xFF00, 0xFF, 0xFF000000)): "BGRA",
(32, (0x0, 0x0, 0x0, 0x0)): "BGRA",
(24, (0xff0000, 0xff00, 0xff)): "BGR",
(16, (0xf800, 0x7e0, 0x1f)): "BGR;16",
(16, (0x7c00, 0x3e0, 0x1f)): "BGR;15"
(24, (0xFF0000, 0xFF00, 0xFF)): "BGR",
(16, (0xF800, 0x7E0, 0x1F)): "BGR;16",
(16, (0x7C00, 0x3E0, 0x1F)): "BGR;15",
}
if file_info['bits'] in SUPPORTED:
if file_info['bits'] == 32 and \
file_info['rgba_mask'] in SUPPORTED[file_info['bits']]:
raw_mode = MASK_MODES[
(file_info["bits"], file_info["rgba_mask"])
]
if file_info["bits"] in SUPPORTED:
if (
file_info["bits"] == 32
and file_info["rgba_mask"] in SUPPORTED[file_info["bits"]]
):
raw_mode = MASK_MODES[(file_info["bits"], file_info["rgba_mask"])]
self.mode = "RGBA" if "A" in raw_mode else self.mode
elif (file_info['bits'] in (24, 16) and
file_info['rgb_mask'] in SUPPORTED[file_info['bits']]):
raw_mode = MASK_MODES[
(file_info['bits'], file_info['rgb_mask'])
]
elif (
file_info["bits"] in (24, 16)
and file_info["rgb_mask"] in SUPPORTED[file_info["bits"]]
):
raw_mode = MASK_MODES[(file_info["bits"], file_info["rgb_mask"])]
else:
raise IOError("Unsupported BMP bitfields layout")
else:
raise IOError("Unsupported BMP bitfields layout")
elif file_info['compression'] == self.RAW:
if file_info['bits'] == 32 and header == 22: # 32-bit .cur offset
elif file_info["compression"] == self.RAW:
if file_info["bits"] == 32 and header == 22: # 32-bit .cur offset
raw_mode, self.mode = "BGRA", "RGBA"
else:
raise IOError("Unsupported BMP compression (%d)" %
file_info['compression'])
raise IOError("Unsupported BMP compression (%d)" % file_info["compression"])
# --------------- Once the header is processed, process the palette/LUT
if self.mode == "P": # Paletted for 1, 4 and 8 bit images
# ---------------------------------------------------- 1-bit images
if not (0 < file_info['colors'] <= 65536):
raise IOError("Unsupported BMP Palette size (%d)" %
file_info['colors'])
if not (0 < file_info["colors"] <= 65536):
raise IOError("Unsupported BMP Palette size (%d)" % file_info["colors"])
else:
padding = file_info['palette_padding']
palette = read(padding * file_info['colors'])
padding = file_info["palette_padding"]
palette = read(padding * file_info["colors"])
greyscale = True
indices = (0, 255) if file_info['colors'] == 2 else \
list(range(file_info['colors']))
indices = (
(0, 255)
if file_info["colors"] == 2
else list(range(file_info["colors"]))
)
# ----------------- Check if greyscale and ignore palette if so
for ind, val in enumerate(indices):
rgb = palette[ind*padding:ind*padding + 3]
rgb = palette[ind * padding : ind * padding + 3]
if rgb != o8(val) * 3:
greyscale = False
# ------- If all colors are grey, white or black, ditch palette
if greyscale:
self.mode = "1" if file_info['colors'] == 2 else "L"
self.mode = "1" if file_info["colors"] == 2 else "L"
raw_mode = self.mode
else:
self.mode = "P"
self.palette = ImagePalette.raw(
"BGRX" if padding == 4 else "BGR", palette)
"BGRX" if padding == 4 else "BGR", palette
)
# ---------------------------- Finally set the tile data for the plugin
self.info['compression'] = file_info['compression']
self.info["compression"] = file_info["compression"]
self.tile = [
('raw',
(0, 0, file_info['width'], file_info['height']),
offset or self.fp.tell(),
(raw_mode,
((file_info['width'] * file_info['bits'] + 31) >> 3) & (~3),
file_info['direction']))
(
"raw",
(0, 0, file_info["width"], file_info["height"]),
offset or self.fp.tell(),
(
raw_mode,
((file_info["width"] * file_info["bits"] + 31) >> 3) & (~3),
file_info["direction"],
),
)
]
def _open(self):
@ -280,6 +286,7 @@ class DibImageFile(BmpImageFile):
def _open(self):
self._bitmap()
#
# --------------------------------------------------------------------
# Write BMP file
@ -311,31 +318,36 @@ def _save(im, fp, filename, bitmap_header=True):
# 1 meter == 39.3701 inches
ppm = tuple(map(lambda x: int(x * 39.3701 + 0.5), dpi))
stride = ((im.size[0]*bits+7)//8+3) & (~3)
stride = ((im.size[0] * bits + 7) // 8 + 3) & (~3)
header = 40 # or 64 for OS/2 version 2
image = stride * im.size[1]
# bitmap header
if bitmap_header:
offset = 14 + header + colors * 4
fp.write(b"BM" + # file type (magic)
o32(offset+image) + # file size
o32(0) + # reserved
o32(offset)) # image data offset
fp.write(
b"BM"
+ o32(offset + image) # file type (magic)
+ o32(0) # file size
+ o32(offset) # reserved
) # image data offset
# bitmap info header
fp.write(o32(header) + # info header size
o32(im.size[0]) + # width
o32(im.size[1]) + # height
o16(1) + # planes
o16(bits) + # depth
o32(0) + # compression (0=uncompressed)
o32(image) + # size of bitmap
o32(ppm[0]) + o32(ppm[1]) + # resolution
o32(colors) + # colors used
o32(colors)) # colors important
fp.write(
o32(header) # info header size
+ o32(im.size[0]) # width
+ o32(im.size[1]) # height
+ o16(1) # planes
+ o16(bits) # depth
+ o32(0) # compression (0=uncompressed)
+ o32(image) # size of bitmap
+ o32(ppm[0]) # resolution
+ o32(ppm[1]) # resolution
+ o32(colors) # colors used
+ o32(colors) # colors important
)
fp.write(b"\0" * (header - 40)) # padding (for OS/2 format)
fp.write(b"\0" * (header - 40)) # padding (for OS/2 format)
if im.mode == "1":
for i in (0, 255):
@ -346,8 +358,8 @@ def _save(im, fp, filename, bitmap_header=True):
elif im.mode == "P":
fp.write(im.im.getpalette("RGB", "BGRX"))
ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0,
(rawmode, stride, -1))])
ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, stride, -1))])
#
# --------------------------------------------------------------------

View File

@ -27,6 +27,7 @@ def register_handler(handler):
# --------------------------------------------------------------------
# Image adapter
def _accept(prefix):
return prefix[:4] == b"BUFR" or prefix[:4] == b"ZCZC"

View File

@ -22,7 +22,6 @@ import io
class ContainerIO(object):
def __init__(self, file, offset, length):
"""
Create file object.

View File

@ -36,6 +36,7 @@ def _accept(prefix):
##
# Image plugin for Windows Cursor files.
class CurImageFile(BmpImagePlugin.BmpImageFile):
format = "CUR"
@ -65,9 +66,9 @@ class CurImageFile(BmpImagePlugin.BmpImageFile):
self._bitmap(i32(m[12:]) + offset)
# patch up the bitmap height
self._size = self.size[0], self.size[1]//2
self._size = self.size[0], self.size[1] // 2
d, e, o, a = self.tile[0]
self.tile[0] = d, (0, 0)+self.size, o, a
self.tile[0] = d, (0, 0) + self.size, o, a
return

View File

@ -39,6 +39,7 @@ def _accept(prefix):
##
# Image plugin for the Intel DCX format.
class DcxImageFile(PcxImageFile):
format = "DCX"

View File

@ -61,8 +61,7 @@ DDS_LUMINANCEA = DDPF_LUMINANCE | DDPF_ALPHAPIXELS
DDS_ALPHA = DDPF_ALPHA
DDS_PAL8 = DDPF_PALETTEINDEXED8
DDS_HEADER_FLAGS_TEXTURE = (DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH |
DDSD_PIXELFORMAT)
DDS_HEADER_FLAGS_TEXTURE = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT
DDS_HEADER_FLAGS_MIPMAP = DDSD_MIPMAPCOUNT
DDS_HEADER_FLAGS_VOLUME = DDSD_DEPTH
DDS_HEADER_FLAGS_PITCH = DDSD_PITCH
@ -130,8 +129,8 @@ class DdsImageFile(ImageFile.ImageFile):
masks = {mask: ["R", "G", "B", "A"][i] for i, mask in enumerate(masks)}
rawmode = ""
if bitcount == 32:
rawmode += masks[0xff000000]
rawmode += masks[0xff0000] + masks[0xff00] + masks[0xff]
rawmode += masks[0xFF000000]
rawmode += masks[0xFF0000] + masks[0xFF00] + masks[0xFF]
self.tile = [("raw", (0, 0) + self.size, 0, (rawmode, 0, 1))]
else:
@ -151,24 +150,21 @@ class DdsImageFile(ImageFile.ImageFile):
# ignoring flags which pertain to volume textures and cubemaps
dxt10 = BytesIO(self.fp.read(20))
dxgi_format, dimension = struct.unpack("<II", dxt10.read(8))
if dxgi_format in (DXGI_FORMAT_BC7_TYPELESS,
DXGI_FORMAT_BC7_UNORM):
if dxgi_format in (DXGI_FORMAT_BC7_TYPELESS, DXGI_FORMAT_BC7_UNORM):
self.pixel_format = "BC7"
n = 7
elif dxgi_format == DXGI_FORMAT_BC7_UNORM_SRGB:
self.pixel_format = "BC7"
self.im_info["gamma"] = 1/2.2
self.im_info["gamma"] = 1 / 2.2
n = 7
else:
raise NotImplementedError("Unimplemented DXGI format %d" %
(dxgi_format))
raise NotImplementedError(
"Unimplemented DXGI format %d" % (dxgi_format)
)
else:
raise NotImplementedError("Unimplemented pixel format %r" %
(fourcc))
raise NotImplementedError("Unimplemented pixel format %r" % (fourcc))
self.tile = [
("bcn", (0, 0) + self.size, data_start, (n))
]
self.tile = [("bcn", (0, 0) + self.size, data_start, (n))]
def load_seek(self, pos):
pass

View File

@ -38,15 +38,17 @@ split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$")
field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$")
gs_windows_binary = None
if sys.platform.startswith('win'):
if sys.platform.startswith("win"):
import shutil
if hasattr(shutil, 'which'):
if hasattr(shutil, "which"):
which = shutil.which
else:
# Python 2
import distutils.spawn
which = distutils.spawn.find_executable
for binary in ('gswin32c', 'gswin64c', 'gs'):
for binary in ("gswin32c", "gswin64c", "gs"):
if which(binary) is not None:
gs_windows_binary = binary
break
@ -57,11 +59,12 @@ if sys.platform.startswith('win'):
def has_ghostscript():
if gs_windows_binary:
return True
if not sys.platform.startswith('win'):
if not sys.platform.startswith("win"):
import subprocess
try:
with open(os.devnull, 'wb') as devnull:
subprocess.check_call(['gs', '--version'], stdout=devnull)
with open(os.devnull, "wb") as devnull:
subprocess.check_call(["gs", "--version"], stdout=devnull)
return True
except OSError:
# No Ghostscript
@ -82,8 +85,10 @@ def Ghostscript(tile, size, fp, scale=1):
# orig_bbox = bbox
size = (size[0] * scale, size[1] * scale)
# resolution is dependent on bbox and size
res = (float((72.0 * size[0]) / (bbox[2]-bbox[0])),
float((72.0 * size[1]) / (bbox[3]-bbox[1])))
res = (
float((72.0 * size[0]) / (bbox[2] - bbox[0])),
float((72.0 * size[1]) / (bbox[3] - bbox[1])),
)
import subprocess
import tempfile
@ -92,7 +97,7 @@ def Ghostscript(tile, size, fp, scale=1):
os.close(out_fd)
infile_temp = None
if hasattr(fp, 'name') and os.path.exists(fp.name):
if hasattr(fp, "name") and os.path.exists(fp.name):
infile = fp.name
else:
in_fd, infile_temp = tempfile.mkstemp()
@ -102,7 +107,7 @@ def Ghostscript(tile, size, fp, scale=1):
# Ignore length and offset!
# Ghostscript can read it
# Copy whole file to read in Ghostscript
with open(infile_temp, 'wb') as f:
with open(infile_temp, "wb") as f:
# fetch length of fp
fp.seek(0, io.SEEK_END)
fsize = fp.tell()
@ -111,38 +116,42 @@ def Ghostscript(tile, size, fp, scale=1):
fp.seek(0)
lengthfile = fsize
while lengthfile > 0:
s = fp.read(min(lengthfile, 100*1024))
s = fp.read(min(lengthfile, 100 * 1024))
if not s:
break
lengthfile -= len(s)
f.write(s)
# Build Ghostscript command
command = ["gs",
"-q", # quiet mode
"-g%dx%d" % size, # set output geometry (pixels)
"-r%fx%f" % res, # set input DPI (dots per inch)
"-dBATCH", # exit after processing
"-dNOPAUSE", # don't pause between pages
"-dSAFER", # safe mode
"-sDEVICE=ppmraw", # ppm driver
"-sOutputFile=%s" % outfile, # output file
# adjust for image origin
"-c", "%d %d translate" % (-bbox[0], -bbox[1]),
"-f", infile, # input file
# showpage (see https://bugs.ghostscript.com/show_bug.cgi?id=698272)
"-c", "showpage",
]
command = [
"gs",
"-q", # quiet mode
"-g%dx%d" % size, # set output geometry (pixels)
"-r%fx%f" % res, # set input DPI (dots per inch)
"-dBATCH", # exit after processing
"-dNOPAUSE", # don't pause between pages
"-dSAFER", # safe mode
"-sDEVICE=ppmraw", # ppm driver
"-sOutputFile=%s" % outfile, # output file
# adjust for image origin
"-c",
"%d %d translate" % (-bbox[0], -bbox[1]),
"-f",
infile, # input file
# showpage (see https://bugs.ghostscript.com/show_bug.cgi?id=698272)
"-c",
"showpage",
]
if gs_windows_binary is not None:
if not gs_windows_binary:
raise WindowsError('Unable to locate Ghostscript on paths')
raise WindowsError("Unable to locate Ghostscript on paths")
command[0] = gs_windows_binary
# push data through Ghostscript
try:
startupinfo = None
if sys.platform.startswith('win'):
if sys.platform.startswith("win"):
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
subprocess.check_call(command, startupinfo=startupinfo)
@ -163,6 +172,7 @@ class PSFile(object):
"""
Wrapper for bytesio object that treats either CR or LF as end of line.
"""
def __init__(self, fp):
self.fp = fp
self.char = None
@ -185,12 +195,12 @@ class PSFile(object):
if self.char in b"\r\n":
self.char = None
return s.decode('latin-1')
return s.decode("latin-1")
def _accept(prefix):
return prefix[:4] == b"%!PS" or \
(len(prefix) >= 4 and i32(prefix) == 0xC6D3D0C5)
return prefix[:4] == b"%!PS" or (len(prefix) >= 4 and i32(prefix) == 0xC6D3D0C5)
##
# Image plugin for Encapsulated Postscript. This plugin supports only
@ -224,7 +234,7 @@ class EpsImageFile(ImageFile.ImageFile):
# Load EPS header
s_raw = fp.readline()
s = s_raw.strip('\r\n')
s = s_raw.strip("\r\n")
while s_raw:
if s:
@ -246,8 +256,9 @@ class EpsImageFile(ImageFile.ImageFile):
# put floating point values there anyway.
box = [int(float(i)) for i in v.split()]
self._size = box[2] - box[0], box[3] - box[1]
self.tile = [("eps", (0, 0) + self.size, offset,
(length, box))]
self.tile = [
("eps", (0, 0) + self.size, offset, (length, box))
]
except Exception:
pass
@ -262,7 +273,7 @@ class EpsImageFile(ImageFile.ImageFile):
self.info[k[:8]] = k[9:]
else:
self.info[k] = ""
elif s[0] == '%':
elif s[0] == "%":
# handle non-DSC Postscript comments that some
# tools mistakenly put in the Comments section
pass
@ -270,7 +281,7 @@ class EpsImageFile(ImageFile.ImageFile):
raise IOError("bad EPS header")
s_raw = fp.readline()
s = s_raw.strip('\r\n')
s = s_raw.strip("\r\n")
if s and s[:1] != "%":
break
@ -297,7 +308,7 @@ class EpsImageFile(ImageFile.ImageFile):
self._size = int(x), int(y)
return
s = fp.readline().strip('\r\n')
s = fp.readline().strip("\r\n")
if not s:
break
@ -344,6 +355,7 @@ class EpsImageFile(ImageFile.ImageFile):
#
# --------------------------------------------------------------------
def _save(im, fp, filename, eps=1):
"""EPS Writer for the Python Imaging Library."""
@ -366,7 +378,7 @@ def _save(im, fp, filename, eps=1):
wrapped_fp = False
if fp != sys.stdout:
if sys.version_info.major > 2:
fp = io.TextIOWrapper(fp, encoding='latin-1')
fp = io.TextIOWrapper(fp, encoding="latin-1")
wrapped_fp = True
try:
@ -381,7 +393,7 @@ def _save(im, fp, filename, eps=1):
fp.write("%%EndComments\n")
fp.write("%%Page: 1 1\n")
fp.write("%%ImageData: %d %d " % im.size)
fp.write("%d %d 0 1 1 \"%s\"\n" % operator)
fp.write('%d %d 0 1 1 "%s"\n' % operator)
#
# image header
@ -396,7 +408,7 @@ def _save(im, fp, filename, eps=1):
if hasattr(fp, "flush"):
fp.flush()
ImageFile._save(im, base_fp, [("eps", (0, 0)+im.size, 0, None)])
ImageFile._save(im, base_fp, [("eps", (0, 0) + im.size, 0, None)])
fp.write("\n%%%%EndBinary\n")
fp.write("grestore end\n")
@ -406,6 +418,7 @@ def _save(im, fp, filename, eps=1):
if wrapped_fp:
fp.detach()
#
# --------------------------------------------------------------------

View File

@ -18,11 +18,10 @@
# Maps EXIF tags to tag names.
TAGS = {
# possibly incomplete
0x000b: "ProcessingSoftware",
0x00fe: "NewSubfileType",
0x00ff: "SubfileType",
0x000B: "ProcessingSoftware",
0x00FE: "NewSubfileType",
0x00FF: "SubfileType",
0x0100: "ImageWidth",
0x0101: "ImageLength",
0x0102: "BitsPerSample",
@ -31,10 +30,10 @@ TAGS = {
0x0107: "Thresholding",
0x0108: "CellWidth",
0x0109: "CellLength",
0x010a: "FillOrder",
0x010d: "DocumentName",
0x010e: "ImageDescription",
0x010f: "Make",
0x010A: "FillOrder",
0x010D: "DocumentName",
0x010E: "ImageDescription",
0x010F: "Make",
0x0110: "Model",
0x0111: "StripOffsets",
0x0112: "Orientation",
@ -43,10 +42,10 @@ TAGS = {
0x0117: "StripByteCounts",
0x0118: "MinSampleValue",
0x0119: "MaxSampleValue",
0x011a: "XResolution",
0x011b: "YResolution",
0x011c: "PlanarConfiguration",
0x011d: "PageName",
0x011A: "XResolution",
0x011B: "YResolution",
0x011C: "PlanarConfiguration",
0x011D: "PageName",
0x0120: "FreeOffsets",
0x0121: "FreeByteCounts",
0x0122: "GrayResponseUnit",
@ -55,24 +54,24 @@ TAGS = {
0x0125: "T6Options",
0x0128: "ResolutionUnit",
0x0129: "PageNumber",
0x012d: "TransferFunction",
0x012D: "TransferFunction",
0x0131: "Software",
0x0132: "DateTime",
0x013b: "Artist",
0x013c: "HostComputer",
0x013d: "Predictor",
0x013e: "WhitePoint",
0x013f: "PrimaryChromaticities",
0x013B: "Artist",
0x013C: "HostComputer",
0x013D: "Predictor",
0x013E: "WhitePoint",
0x013F: "PrimaryChromaticities",
0x0140: "ColorMap",
0x0141: "HalftoneHints",
0x0142: "TileWidth",
0x0143: "TileLength",
0x0144: "TileOffsets",
0x0145: "TileByteCounts",
0x014a: "SubIFDs",
0x014c: "InkSet",
0x014d: "InkNames",
0x014e: "NumberOfInks",
0x014A: "SubIFDs",
0x014C: "InkSet",
0x014D: "InkNames",
0x014E: "NumberOfInks",
0x0150: "DotRange",
0x0151: "TargetPrinter",
0x0152: "ExtraSamples",
@ -83,9 +82,9 @@ TAGS = {
0x0157: "ClipPath",
0x0158: "XClipPathUnits",
0x0159: "YClipPathUnits",
0x015a: "Indexed",
0x015b: "JPEGTables",
0x015f: "OPIProxy",
0x015A: "Indexed",
0x015B: "JPEGTables",
0x015F: "OPIProxy",
0x0200: "JPEGProc",
0x0201: "JpegIFOffset",
0x0202: "JpegIFByteCount",
@ -99,20 +98,20 @@ TAGS = {
0x0212: "YCbCrSubSampling",
0x0213: "YCbCrPositioning",
0x0214: "ReferenceBlackWhite",
0x02bc: "XMLPacket",
0x02BC: "XMLPacket",
0x1000: "RelatedImageFileFormat",
0x1001: "RelatedImageWidth",
0x1002: "RelatedImageLength",
0x4746: "Rating",
0x4749: "RatingPercent",
0x800d: "ImageID",
0x828d: "CFARepeatPatternDim",
0x828e: "CFAPattern",
0x828f: "BatteryLevel",
0x800D: "ImageID",
0x828D: "CFARepeatPatternDim",
0x828E: "CFAPattern",
0x828F: "BatteryLevel",
0x8298: "Copyright",
0x829a: "ExposureTime",
0x829d: "FNumber",
0x83bb: "IPTCNAA",
0x829A: "ExposureTime",
0x829D: "FNumber",
0x83BB: "IPTCNAA",
0x8649: "ImageResources",
0x8769: "ExifOffset",
0x8773: "InterColorProfile",
@ -122,8 +121,8 @@ TAGS = {
0x8827: "ISOSpeedRatings",
0x8828: "OECF",
0x8829: "Interlace",
0x882a: "TimeZoneOffset",
0x882b: "SelfTimerMode",
0x882A: "TimeZoneOffset",
0x882B: "SelfTimerMode",
0x9000: "ExifVersion",
0x9003: "DateTimeOriginal",
0x9004: "DateTimeDigitized",
@ -138,142 +137,142 @@ TAGS = {
0x9207: "MeteringMode",
0x9208: "LightSource",
0x9209: "Flash",
0x920a: "FocalLength",
0x920b: "FlashEnergy",
0x920c: "SpatialFrequencyResponse",
0x920d: "Noise",
0x920A: "FocalLength",
0x920B: "FlashEnergy",
0x920C: "SpatialFrequencyResponse",
0x920D: "Noise",
0x9211: "ImageNumber",
0x9212: "SecurityClassification",
0x9213: "ImageHistory",
0x9214: "SubjectLocation",
0x9215: "ExposureIndex",
0x9216: "TIFF/EPStandardID",
0x927c: "MakerNote",
0x927C: "MakerNote",
0x9286: "UserComment",
0x9290: "SubsecTime",
0x9291: "SubsecTimeOriginal",
0x9292: "SubsecTimeDigitized",
0x9c9b: "XPTitle",
0x9c9c: "XPComment",
0x9c9d: "XPAuthor",
0x9c9e: "XPKeywords",
0x9c9f: "XPSubject",
0xa000: "FlashPixVersion",
0xa001: "ColorSpace",
0xa002: "ExifImageWidth",
0xa003: "ExifImageHeight",
0xa004: "RelatedSoundFile",
0xa005: "ExifInteroperabilityOffset",
0xa20b: "FlashEnergy",
0xa20c: "SpatialFrequencyResponse",
0xa20e: "FocalPlaneXResolution",
0xa20f: "FocalPlaneYResolution",
0xa210: "FocalPlaneResolutionUnit",
0xa214: "SubjectLocation",
0xa215: "ExposureIndex",
0xa217: "SensingMethod",
0xa300: "FileSource",
0xa301: "SceneType",
0xa302: "CFAPattern",
0xa401: "CustomRendered",
0xa402: "ExposureMode",
0xa403: "WhiteBalance",
0xa404: "DigitalZoomRatio",
0xa405: "FocalLengthIn35mmFilm",
0xa406: "SceneCaptureType",
0xa407: "GainControl",
0xa408: "Contrast",
0xa409: "Saturation",
0xa40a: "Sharpness",
0xa40b: "DeviceSettingDescription",
0xa40c: "SubjectDistanceRange",
0xa420: "ImageUniqueID",
0xa430: "CameraOwnerName",
0xa431: "BodySerialNumber",
0xa432: "LensSpecification",
0xa433: "LensMake",
0xa434: "LensModel",
0xa435: "LensSerialNumber",
0xa500: "Gamma",
0xc4a5: "PrintImageMatching",
0xc612: "DNGVersion",
0xc613: "DNGBackwardVersion",
0xc614: "UniqueCameraModel",
0xc615: "LocalizedCameraModel",
0xc616: "CFAPlaneColor",
0xc617: "CFALayout",
0xc618: "LinearizationTable",
0xc619: "BlackLevelRepeatDim",
0xc61a: "BlackLevel",
0xc61b: "BlackLevelDeltaH",
0xc61c: "BlackLevelDeltaV",
0xc61d: "WhiteLevel",
0xc61e: "DefaultScale",
0xc61f: "DefaultCropOrigin",
0xc620: "DefaultCropSize",
0xc621: "ColorMatrix1",
0xc622: "ColorMatrix2",
0xc623: "CameraCalibration1",
0xc624: "CameraCalibration2",
0xc625: "ReductionMatrix1",
0xc626: "ReductionMatrix2",
0xc627: "AnalogBalance",
0xc628: "AsShotNeutral",
0xc629: "AsShotWhiteXY",
0xc62a: "BaselineExposure",
0xc62b: "BaselineNoise",
0xc62c: "BaselineSharpness",
0xc62d: "BayerGreenSplit",
0xc62e: "LinearResponseLimit",
0xc62f: "CameraSerialNumber",
0xc630: "LensInfo",
0xc631: "ChromaBlurRadius",
0xc632: "AntiAliasStrength",
0xc633: "ShadowScale",
0xc634: "DNGPrivateData",
0xc635: "MakerNoteSafety",
0xc65a: "CalibrationIlluminant1",
0xc65b: "CalibrationIlluminant2",
0xc65c: "BestQualityScale",
0xc65d: "RawDataUniqueID",
0xc68b: "OriginalRawFileName",
0xc68c: "OriginalRawFileData",
0xc68d: "ActiveArea",
0xc68e: "MaskedAreas",
0xc68f: "AsShotICCProfile",
0xc690: "AsShotPreProfileMatrix",
0xc691: "CurrentICCProfile",
0xc692: "CurrentPreProfileMatrix",
0xc6bf: "ColorimetricReference",
0xc6f3: "CameraCalibrationSignature",
0xc6f4: "ProfileCalibrationSignature",
0xc6f6: "AsShotProfileName",
0xc6f7: "NoiseReductionApplied",
0xc6f8: "ProfileName",
0xc6f9: "ProfileHueSatMapDims",
0xc6fa: "ProfileHueSatMapData1",
0xc6fb: "ProfileHueSatMapData2",
0xc6fc: "ProfileToneCurve",
0xc6fd: "ProfileEmbedPolicy",
0xc6fe: "ProfileCopyright",
0xc714: "ForwardMatrix1",
0xc715: "ForwardMatrix2",
0xc716: "PreviewApplicationName",
0xc717: "PreviewApplicationVersion",
0xc718: "PreviewSettingsName",
0xc719: "PreviewSettingsDigest",
0xc71a: "PreviewColorSpace",
0xc71b: "PreviewDateTime",
0xc71c: "RawImageDigest",
0xc71d: "OriginalRawFileDigest",
0xc71e: "SubTileBlockSize",
0xc71f: "RowInterleaveFactor",
0xc725: "ProfileLookTableDims",
0xc726: "ProfileLookTableData",
0xc740: "OpcodeList1",
0xc741: "OpcodeList2",
0xc74e: "OpcodeList3",
0xc761: "NoiseProfile"
0x9C9B: "XPTitle",
0x9C9C: "XPComment",
0x9C9D: "XPAuthor",
0x9C9E: "XPKeywords",
0x9C9F: "XPSubject",
0xA000: "FlashPixVersion",
0xA001: "ColorSpace",
0xA002: "ExifImageWidth",
0xA003: "ExifImageHeight",
0xA004: "RelatedSoundFile",
0xA005: "ExifInteroperabilityOffset",
0xA20B: "FlashEnergy",
0xA20C: "SpatialFrequencyResponse",
0xA20E: "FocalPlaneXResolution",
0xA20F: "FocalPlaneYResolution",
0xA210: "FocalPlaneResolutionUnit",
0xA214: "SubjectLocation",
0xA215: "ExposureIndex",
0xA217: "SensingMethod",
0xA300: "FileSource",
0xA301: "SceneType",
0xA302: "CFAPattern",
0xA401: "CustomRendered",
0xA402: "ExposureMode",
0xA403: "WhiteBalance",
0xA404: "DigitalZoomRatio",
0xA405: "FocalLengthIn35mmFilm",
0xA406: "SceneCaptureType",
0xA407: "GainControl",
0xA408: "Contrast",
0xA409: "Saturation",
0xA40A: "Sharpness",
0xA40B: "DeviceSettingDescription",
0xA40C: "SubjectDistanceRange",
0xA420: "ImageUniqueID",
0xA430: "CameraOwnerName",
0xA431: "BodySerialNumber",
0xA432: "LensSpecification",
0xA433: "LensMake",
0xA434: "LensModel",
0xA435: "LensSerialNumber",
0xA500: "Gamma",
0xC4A5: "PrintImageMatching",
0xC612: "DNGVersion",
0xC613: "DNGBackwardVersion",
0xC614: "UniqueCameraModel",
0xC615: "LocalizedCameraModel",
0xC616: "CFAPlaneColor",
0xC617: "CFALayout",
0xC618: "LinearizationTable",
0xC619: "BlackLevelRepeatDim",
0xC61A: "BlackLevel",
0xC61B: "BlackLevelDeltaH",
0xC61C: "BlackLevelDeltaV",
0xC61D: "WhiteLevel",
0xC61E: "DefaultScale",
0xC61F: "DefaultCropOrigin",
0xC620: "DefaultCropSize",
0xC621: "ColorMatrix1",
0xC622: "ColorMatrix2",
0xC623: "CameraCalibration1",
0xC624: "CameraCalibration2",
0xC625: "ReductionMatrix1",
0xC626: "ReductionMatrix2",
0xC627: "AnalogBalance",
0xC628: "AsShotNeutral",
0xC629: "AsShotWhiteXY",
0xC62A: "BaselineExposure",
0xC62B: "BaselineNoise",
0xC62C: "BaselineSharpness",
0xC62D: "BayerGreenSplit",
0xC62E: "LinearResponseLimit",
0xC62F: "CameraSerialNumber",
0xC630: "LensInfo",
0xC631: "ChromaBlurRadius",
0xC632: "AntiAliasStrength",
0xC633: "ShadowScale",
0xC634: "DNGPrivateData",
0xC635: "MakerNoteSafety",
0xC65A: "CalibrationIlluminant1",
0xC65B: "CalibrationIlluminant2",
0xC65C: "BestQualityScale",
0xC65D: "RawDataUniqueID",
0xC68B: "OriginalRawFileName",
0xC68C: "OriginalRawFileData",
0xC68D: "ActiveArea",
0xC68E: "MaskedAreas",
0xC68F: "AsShotICCProfile",
0xC690: "AsShotPreProfileMatrix",
0xC691: "CurrentICCProfile",
0xC692: "CurrentPreProfileMatrix",
0xC6BF: "ColorimetricReference",
0xC6F3: "CameraCalibrationSignature",
0xC6F4: "ProfileCalibrationSignature",
0xC6F6: "AsShotProfileName",
0xC6F7: "NoiseReductionApplied",
0xC6F8: "ProfileName",
0xC6F9: "ProfileHueSatMapDims",
0xC6FA: "ProfileHueSatMapData1",
0xC6FB: "ProfileHueSatMapData2",
0xC6FC: "ProfileToneCurve",
0xC6FD: "ProfileEmbedPolicy",
0xC6FE: "ProfileCopyright",
0xC714: "ForwardMatrix1",
0xC715: "ForwardMatrix2",
0xC716: "PreviewApplicationName",
0xC717: "PreviewApplicationVersion",
0xC718: "PreviewSettingsName",
0xC719: "PreviewSettingsDigest",
0xC71A: "PreviewColorSpace",
0xC71B: "PreviewDateTime",
0xC71C: "RawImageDigest",
0xC71D: "OriginalRawFileDigest",
0xC71E: "SubTileBlockSize",
0xC71F: "RowInterleaveFactor",
0xC725: "ProfileLookTableDims",
0xC726: "ProfileLookTableData",
0xC740: "OpcodeList1",
0xC741: "OpcodeList2",
0xC74E: "OpcodeList3",
0xC761: "NoiseProfile",
}
##

View File

@ -23,6 +23,7 @@ def register_handler(handler):
global _handler
_handler = handler
# --------------------------------------------------------------------
# Image adapter

View File

@ -27,6 +27,7 @@ __version__ = "0.2"
#
# decoder
def _accept(prefix):
return len(prefix) >= 6 and i16(prefix[4:6]) in [0xAF11, 0xAF12]
@ -35,6 +36,7 @@ def _accept(prefix):
# Image plugin for the FLI/FLC animation format. Use the <b>seek</b>
# method to load individual frames.
class FliImageFile(ImageFile.ImageFile):
format = "FLI"
@ -46,9 +48,11 @@ class FliImageFile(ImageFile.ImageFile):
# HEAD
s = self.fp.read(128)
magic = i16(s[4:6])
if not (magic in [0xAF11, 0xAF12] and
i16(s[14:16]) in [0, 3] and # flags
s[20:22] == b"\x00\x00"): # reserved
if not (
magic in [0xAF11, 0xAF12]
and i16(s[14:16]) in [0, 3] # flags
and s[20:22] == b"\x00\x00" # reserved
):
raise SyntaxError("not an FLI/FLC file")
# frames
@ -84,7 +88,7 @@ class FliImageFile(ImageFile.ImageFile):
elif i16(s[4:6]) == 4:
self._palette(palette, 0)
palette = [o8(r)+o8(g)+o8(b) for (r, g, b) in palette]
palette = [o8(r) + o8(g) + o8(b) for (r, g, b) in palette]
self.palette = ImagePalette.raw("RGB", b"".join(palette))
# set things up to decode first frame
@ -106,8 +110,8 @@ class FliImageFile(ImageFile.ImageFile):
s = self.fp.read(n * 3)
for n in range(0, len(s), 3):
r = i8(s[n]) << shift
g = i8(s[n+1]) << shift
b = i8(s[n+2]) << shift
g = i8(s[n + 1]) << shift
b = i8(s[n + 2]) << shift
palette[i] = (r, g, b)
i += 1
@ -152,7 +156,7 @@ class FliImageFile(ImageFile.ImageFile):
framesize = i32(s)
self.decodermaxblock = framesize
self.tile = [("fli", (0, 0)+self.size, self.__offset, None)]
self.tile = [("fli", (0, 0) + self.size, self.__offset, None)]
self.__offset += framesize

View File

@ -33,6 +33,7 @@ def puti16(fp, values):
##
# Base class for raster font file handlers.
class FontFile(object):
bitmap = None
@ -61,7 +62,7 @@ class FontFile(object):
w = w + (src[2] - src[0])
if w > WIDTH:
lines += 1
w = (src[2] - src[0])
w = src[2] - src[0]
maxwidth = max(maxwidth, w)
xsize = maxwidth
@ -103,7 +104,7 @@ class FontFile(object):
# font metrics
with open(os.path.splitext(filename)[0] + ".pil", "wb") as fp:
fp.write(b"PILfont\n")
fp.write((";;;;;;%d;\n" % self.ysize).encode('ascii')) # HACK!!!
fp.write((";;;;;;%d;\n" % self.ysize).encode("ascii")) # HACK!!!
fp.write(b"DATA\n")
for id in range(256):
m = self.metrics[id]

Some files were not shown because too many files have changed in this diff Show More