From 8f6f152d11bd8fececa3d3f664672b4e41731d65 Mon Sep 17 00:00:00 2001 From: Tobi Sim Date: Sat, 21 Feb 2015 15:19:19 +0100 Subject: [PATCH] initial Commit. --- Makefile | 61 + README | 43 + appname.txt | 1 + dat/Sailfish-Scanner.spec | 72 + dat/sailfish-python.desktop | 6 + dat/sailfish-python.sh | 4 + openproject.sh | 6 + pyPackages/pillowarmv7l/PIL/BdfFontFile.py | 133 + pyPackages/pillowarmv7l/PIL/BmpImagePlugin.py | 264 ++ .../pillowarmv7l/PIL/BufrStubImagePlugin.py | 72 + pyPackages/pillowarmv7l/PIL/ContainerIO.py | 117 + pyPackages/pillowarmv7l/PIL/CurImagePlugin.py | 87 + pyPackages/pillowarmv7l/PIL/DcxImagePlugin.py | 79 + pyPackages/pillowarmv7l/PIL/EpsImagePlugin.py | 424 +++ pyPackages/pillowarmv7l/PIL/ExifTags.py | 193 ++ .../pillowarmv7l/PIL/FitsStubImagePlugin.py | 73 + pyPackages/pillowarmv7l/PIL/FliImagePlugin.py | 143 + pyPackages/pillowarmv7l/PIL/FontFile.py | 119 + pyPackages/pillowarmv7l/PIL/FpxImagePlugin.py | 227 ++ pyPackages/pillowarmv7l/PIL/GbrImagePlugin.py | 71 + pyPackages/pillowarmv7l/PIL/GdImageFile.py | 92 + pyPackages/pillowarmv7l/PIL/GifImagePlugin.py | 544 ++++ .../pillowarmv7l/PIL/GimpGradientFile.py | 137 + .../pillowarmv7l/PIL/GimpPaletteFile.py | 62 + .../pillowarmv7l/PIL/GribStubImagePlugin.py | 72 + .../pillowarmv7l/PIL/Hdf5StubImagePlugin.py | 73 + .../pillowarmv7l/PIL/IcnsImagePlugin.py | 311 +++ pyPackages/pillowarmv7l/PIL/IcoImagePlugin.py | 284 ++ pyPackages/pillowarmv7l/PIL/ImImagePlugin.py | 347 +++ pyPackages/pillowarmv7l/PIL/Image.py | 2467 +++++++++++++++++ pyPackages/pillowarmv7l/PIL/ImageChops.py | 283 ++ pyPackages/pillowarmv7l/PIL/ImageCms.py | 972 +++++++ pyPackages/pillowarmv7l/PIL/ImageColor.py | 279 ++ pyPackages/pillowarmv7l/PIL/ImageDraw.py | 383 +++ pyPackages/pillowarmv7l/PIL/ImageDraw2.py | 111 + pyPackages/pillowarmv7l/PIL/ImageEnhance.py | 99 + pyPackages/pillowarmv7l/PIL/ImageFile.py | 523 ++++ pyPackages/pillowarmv7l/PIL/ImageFileIO.py | 40 + pyPackages/pillowarmv7l/PIL/ImageFilter.py | 275 ++ pyPackages/pillowarmv7l/PIL/ImageFont.py | 430 +++ pyPackages/pillowarmv7l/PIL/ImageGrab.py | 52 + pyPackages/pillowarmv7l/PIL/ImageMath.py | 270 ++ pyPackages/pillowarmv7l/PIL/ImageMode.py | 52 + pyPackages/pillowarmv7l/PIL/ImageMorph.py | 245 ++ pyPackages/pillowarmv7l/PIL/ImageOps.py | 462 +++ pyPackages/pillowarmv7l/PIL/ImagePalette.py | 235 ++ pyPackages/pillowarmv7l/PIL/ImagePath.py | 66 + pyPackages/pillowarmv7l/PIL/ImageQt.py | 98 + pyPackages/pillowarmv7l/PIL/ImageSequence.py | 42 + pyPackages/pillowarmv7l/PIL/ImageShow.py | 179 ++ pyPackages/pillowarmv7l/PIL/ImageStat.py | 147 + pyPackages/pillowarmv7l/PIL/ImageTk.py | 292 ++ pyPackages/pillowarmv7l/PIL/ImageTransform.py | 103 + pyPackages/pillowarmv7l/PIL/ImageWin.py | 251 ++ pyPackages/pillowarmv7l/PIL/ImtImagePlugin.py | 94 + .../pillowarmv7l/PIL/IptcImagePlugin.py | 268 ++ .../pillowarmv7l/PIL/Jpeg2KImagePlugin.py | 277 ++ .../pillowarmv7l/PIL/JpegImagePlugin.py | 738 +++++ pyPackages/pillowarmv7l/PIL/JpegPresets.py | 241 ++ .../pillowarmv7l/PIL/McIdasImagePlugin.py | 74 + pyPackages/pillowarmv7l/PIL/MicImagePlugin.py | 96 + .../pillowarmv7l/PIL/MpegImagePlugin.py | 85 + pyPackages/pillowarmv7l/PIL/MpoImagePlugin.py | 90 + pyPackages/pillowarmv7l/PIL/MspImagePlugin.py | 104 + .../pillowarmv7l/PIL/OleFileIO-README.md | 351 +++ pyPackages/pillowarmv7l/PIL/OleFileIO.py | 2089 ++++++++++++++ pyPackages/pillowarmv7l/PIL/PSDraw.py | 237 ++ pyPackages/pillowarmv7l/PIL/PaletteFile.py | 55 + .../pillowarmv7l/PIL/PalmImagePlugin.py | 240 ++ pyPackages/pillowarmv7l/PIL/PcdImagePlugin.py | 79 + pyPackages/pillowarmv7l/PIL/PcfFontFile.py | 252 ++ pyPackages/pillowarmv7l/PIL/PcxImagePlugin.py | 186 ++ pyPackages/pillowarmv7l/PIL/PdfImagePlugin.py | 238 ++ .../pillowarmv7l/PIL/PixarImagePlugin.py | 69 + pyPackages/pillowarmv7l/PIL/PngImagePlugin.py | 811 ++++++ pyPackages/pillowarmv7l/PIL/PpmImagePlugin.py | 172 ++ pyPackages/pillowarmv7l/PIL/PsdImagePlugin.py | 304 ++ pyPackages/pillowarmv7l/PIL/PyAccess.py | 315 +++ pyPackages/pillowarmv7l/PIL/SgiImagePlugin.py | 91 + .../pillowarmv7l/PIL/SpiderImagePlugin.py | 312 +++ pyPackages/pillowarmv7l/PIL/SunImagePlugin.py | 83 + pyPackages/pillowarmv7l/PIL/TarIO.py | 57 + pyPackages/pillowarmv7l/PIL/TgaImagePlugin.py | 199 ++ .../pillowarmv7l/PIL/TiffImagePlugin.py | 1225 ++++++++ pyPackages/pillowarmv7l/PIL/TiffTags.py | 307 ++ pyPackages/pillowarmv7l/PIL/WalImageFile.py | 131 + .../pillowarmv7l/PIL/WebPImagePlugin.py | 80 + pyPackages/pillowarmv7l/PIL/WmfImagePlugin.py | 173 ++ .../pillowarmv7l/PIL/XVThumbImagePlugin.py | 75 + pyPackages/pillowarmv7l/PIL/XbmImagePlugin.py | 96 + pyPackages/pillowarmv7l/PIL/XpmImagePlugin.py | 131 + pyPackages/pillowarmv7l/PIL/__init__.py | 58 + pyPackages/pillowarmv7l/PIL/_binary.py | 76 + .../pillowarmv7l/PIL/_imaging.cpython-34m.so | Bin 0 -> 796425 bytes .../PIL/_imagingmath.cpython-34m.so | Bin 0 -> 46560 bytes .../PIL/_imagingmorph.cpython-34m.so | Bin 0 -> 24279 bytes pyPackages/pillowarmv7l/PIL/_util.py | 27 + .../Pillow-2.7.0.data/scripts/pilconvert.py | 97 + .../Pillow-2.7.0.data/scripts/pildriver.py | 528 ++++ .../Pillow-2.7.0.data/scripts/pilfile.py | 95 + .../Pillow-2.7.0.data/scripts/pilfont.py | 56 + .../Pillow-2.7.0.data/scripts/pilprint.py | 95 + .../Pillow-2.7.0.dist-info/DESCRIPTION.rst | 28 + .../Pillow-2.7.0.dist-info/METADATA | 53 + .../Pillow-2.7.0.dist-info/RECORD | 102 + .../pillowarmv7l/Pillow-2.7.0.dist-info/WHEEL | 5 + .../Pillow-2.7.0.dist-info/metadata.json | 1 + .../Pillow-2.7.0.dist-info/top_level.txt | 1 + .../Pillow-2.7.0.dist-info/zip-safe | 1 + pyPackages/pillowarmv7l/__init__.py | 0 pyPackages/pillowx86/PIL/BdfFontFile.py | 133 + pyPackages/pillowx86/PIL/BmpImagePlugin.py | 264 ++ .../pillowx86/PIL/BufrStubImagePlugin.py | 72 + pyPackages/pillowx86/PIL/ContainerIO.py | 117 + pyPackages/pillowx86/PIL/CurImagePlugin.py | 87 + pyPackages/pillowx86/PIL/DcxImagePlugin.py | 79 + pyPackages/pillowx86/PIL/EpsImagePlugin.py | 424 +++ pyPackages/pillowx86/PIL/ExifTags.py | 193 ++ .../pillowx86/PIL/FitsStubImagePlugin.py | 73 + pyPackages/pillowx86/PIL/FliImagePlugin.py | 143 + pyPackages/pillowx86/PIL/FontFile.py | 119 + pyPackages/pillowx86/PIL/FpxImagePlugin.py | 227 ++ pyPackages/pillowx86/PIL/GbrImagePlugin.py | 71 + pyPackages/pillowx86/PIL/GdImageFile.py | 92 + pyPackages/pillowx86/PIL/GifImagePlugin.py | 544 ++++ pyPackages/pillowx86/PIL/GimpGradientFile.py | 137 + pyPackages/pillowx86/PIL/GimpPaletteFile.py | 62 + .../pillowx86/PIL/GribStubImagePlugin.py | 72 + .../pillowx86/PIL/Hdf5StubImagePlugin.py | 73 + pyPackages/pillowx86/PIL/IcnsImagePlugin.py | 311 +++ pyPackages/pillowx86/PIL/IcoImagePlugin.py | 284 ++ pyPackages/pillowx86/PIL/ImImagePlugin.py | 347 +++ pyPackages/pillowx86/PIL/Image.py | 2467 +++++++++++++++++ pyPackages/pillowx86/PIL/ImageChops.py | 283 ++ pyPackages/pillowx86/PIL/ImageCms.py | 972 +++++++ pyPackages/pillowx86/PIL/ImageColor.py | 279 ++ pyPackages/pillowx86/PIL/ImageDraw.py | 383 +++ pyPackages/pillowx86/PIL/ImageDraw2.py | 111 + pyPackages/pillowx86/PIL/ImageEnhance.py | 99 + pyPackages/pillowx86/PIL/ImageFile.py | 523 ++++ pyPackages/pillowx86/PIL/ImageFileIO.py | 40 + pyPackages/pillowx86/PIL/ImageFilter.py | 275 ++ pyPackages/pillowx86/PIL/ImageFont.py | 430 +++ pyPackages/pillowx86/PIL/ImageGrab.py | 52 + pyPackages/pillowx86/PIL/ImageMath.py | 270 ++ pyPackages/pillowx86/PIL/ImageMode.py | 52 + pyPackages/pillowx86/PIL/ImageMorph.py | 245 ++ pyPackages/pillowx86/PIL/ImageOps.py | 462 +++ pyPackages/pillowx86/PIL/ImagePalette.py | 235 ++ pyPackages/pillowx86/PIL/ImagePath.py | 66 + pyPackages/pillowx86/PIL/ImageQt.py | 98 + pyPackages/pillowx86/PIL/ImageSequence.py | 42 + pyPackages/pillowx86/PIL/ImageShow.py | 179 ++ pyPackages/pillowx86/PIL/ImageStat.py | 147 + pyPackages/pillowx86/PIL/ImageTk.py | 292 ++ pyPackages/pillowx86/PIL/ImageTransform.py | 103 + pyPackages/pillowx86/PIL/ImageWin.py | 251 ++ pyPackages/pillowx86/PIL/ImtImagePlugin.py | 94 + pyPackages/pillowx86/PIL/IptcImagePlugin.py | 268 ++ pyPackages/pillowx86/PIL/Jpeg2KImagePlugin.py | 277 ++ pyPackages/pillowx86/PIL/JpegImagePlugin.py | 738 +++++ pyPackages/pillowx86/PIL/JpegPresets.py | 241 ++ pyPackages/pillowx86/PIL/McIdasImagePlugin.py | 74 + pyPackages/pillowx86/PIL/MicImagePlugin.py | 96 + pyPackages/pillowx86/PIL/MpegImagePlugin.py | 85 + pyPackages/pillowx86/PIL/MpoImagePlugin.py | 90 + pyPackages/pillowx86/PIL/MspImagePlugin.py | 104 + pyPackages/pillowx86/PIL/OleFileIO-README.md | 351 +++ pyPackages/pillowx86/PIL/OleFileIO.py | 2089 ++++++++++++++ pyPackages/pillowx86/PIL/PSDraw.py | 237 ++ pyPackages/pillowx86/PIL/PaletteFile.py | 55 + pyPackages/pillowx86/PIL/PalmImagePlugin.py | 240 ++ pyPackages/pillowx86/PIL/PcdImagePlugin.py | 79 + pyPackages/pillowx86/PIL/PcfFontFile.py | 252 ++ pyPackages/pillowx86/PIL/PcxImagePlugin.py | 186 ++ pyPackages/pillowx86/PIL/PdfImagePlugin.py | 238 ++ pyPackages/pillowx86/PIL/PixarImagePlugin.py | 69 + pyPackages/pillowx86/PIL/PngImagePlugin.py | 811 ++++++ pyPackages/pillowx86/PIL/PpmImagePlugin.py | 172 ++ pyPackages/pillowx86/PIL/PsdImagePlugin.py | 304 ++ pyPackages/pillowx86/PIL/PyAccess.py | 315 +++ pyPackages/pillowx86/PIL/SgiImagePlugin.py | 91 + pyPackages/pillowx86/PIL/SpiderImagePlugin.py | 312 +++ pyPackages/pillowx86/PIL/SunImagePlugin.py | 83 + pyPackages/pillowx86/PIL/TarIO.py | 57 + pyPackages/pillowx86/PIL/TgaImagePlugin.py | 199 ++ pyPackages/pillowx86/PIL/TiffImagePlugin.py | 1225 ++++++++ pyPackages/pillowx86/PIL/TiffTags.py | 307 ++ pyPackages/pillowx86/PIL/WalImageFile.py | 131 + pyPackages/pillowx86/PIL/WebPImagePlugin.py | 80 + pyPackages/pillowx86/PIL/WmfImagePlugin.py | 173 ++ .../pillowx86/PIL/XVThumbImagePlugin.py | 75 + pyPackages/pillowx86/PIL/XbmImagePlugin.py | 96 + pyPackages/pillowx86/PIL/XpmImagePlugin.py | 131 + pyPackages/pillowx86/PIL/__init__.py | 58 + pyPackages/pillowx86/PIL/_binary.py | 76 + .../pillowx86/PIL/_imaging.cpython-34m.so | Bin 0 -> 792244 bytes .../pillowx86/PIL/_imagingmath.cpython-34m.so | Bin 0 -> 43814 bytes .../PIL/_imagingmorph.cpython-34m.so | Bin 0 -> 22691 bytes pyPackages/pillowx86/PIL/_util.py | 27 + .../Pillow-2.7.0.data/scripts/pilconvert.py | 97 + .../Pillow-2.7.0.data/scripts/pildriver.py | 528 ++++ .../Pillow-2.7.0.data/scripts/pilfile.py | 95 + .../Pillow-2.7.0.data/scripts/pilfont.py | 56 + .../Pillow-2.7.0.data/scripts/pilprint.py | 95 + .../Pillow-2.7.0.dist-info/DESCRIPTION.rst | 28 + .../pillowx86/Pillow-2.7.0.dist-info/METADATA | 53 + .../pillowx86/Pillow-2.7.0.dist-info/RECORD | 102 + .../pillowx86/Pillow-2.7.0.dist-info/WHEEL | 5 + .../Pillow-2.7.0.dist-info/metadata.json | 1 + .../Pillow-2.7.0.dist-info/top_level.txt | 1 + .../pillowx86/Pillow-2.7.0.dist-info/zip-safe | 1 + qml/Cover.qml | 8 + qml/FileChooser.qml | 204 ++ qml/FileChooser2.qml | 96 + qml/ImageView.qml | 10 + qml/Main.qml | 8 + qml/sailfish-python.qml | 24 + renamep.py | 26 + sailfish-python-noarch.rpm | Bin 0 -> 10344 bytes src/__init__.py | 0 src/main.py | 7 + 222 files changed, 48432 insertions(+) create mode 100644 Makefile create mode 100644 README create mode 100644 appname.txt create mode 100644 dat/Sailfish-Scanner.spec create mode 100644 dat/sailfish-python.desktop create mode 100644 dat/sailfish-python.sh create mode 100755 openproject.sh create mode 100644 pyPackages/pillowarmv7l/PIL/BdfFontFile.py create mode 100644 pyPackages/pillowarmv7l/PIL/BmpImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/BufrStubImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/ContainerIO.py create mode 100644 pyPackages/pillowarmv7l/PIL/CurImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/DcxImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/EpsImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/ExifTags.py create mode 100644 pyPackages/pillowarmv7l/PIL/FitsStubImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/FliImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/FontFile.py create mode 100644 pyPackages/pillowarmv7l/PIL/FpxImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/GbrImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/GdImageFile.py create mode 100644 pyPackages/pillowarmv7l/PIL/GifImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/GimpGradientFile.py create mode 100644 pyPackages/pillowarmv7l/PIL/GimpPaletteFile.py create mode 100644 pyPackages/pillowarmv7l/PIL/GribStubImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/Hdf5StubImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/IcnsImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/IcoImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/ImImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/Image.py create mode 100644 pyPackages/pillowarmv7l/PIL/ImageChops.py create mode 100644 pyPackages/pillowarmv7l/PIL/ImageCms.py create mode 100644 pyPackages/pillowarmv7l/PIL/ImageColor.py create mode 100644 pyPackages/pillowarmv7l/PIL/ImageDraw.py create mode 100644 pyPackages/pillowarmv7l/PIL/ImageDraw2.py create mode 100644 pyPackages/pillowarmv7l/PIL/ImageEnhance.py create mode 100644 pyPackages/pillowarmv7l/PIL/ImageFile.py create mode 100644 pyPackages/pillowarmv7l/PIL/ImageFileIO.py create mode 100644 pyPackages/pillowarmv7l/PIL/ImageFilter.py create mode 100644 pyPackages/pillowarmv7l/PIL/ImageFont.py create mode 100644 pyPackages/pillowarmv7l/PIL/ImageGrab.py create mode 100644 pyPackages/pillowarmv7l/PIL/ImageMath.py create mode 100644 pyPackages/pillowarmv7l/PIL/ImageMode.py create mode 100644 pyPackages/pillowarmv7l/PIL/ImageMorph.py create mode 100644 pyPackages/pillowarmv7l/PIL/ImageOps.py create mode 100644 pyPackages/pillowarmv7l/PIL/ImagePalette.py create mode 100644 pyPackages/pillowarmv7l/PIL/ImagePath.py create mode 100644 pyPackages/pillowarmv7l/PIL/ImageQt.py create mode 100644 pyPackages/pillowarmv7l/PIL/ImageSequence.py create mode 100644 pyPackages/pillowarmv7l/PIL/ImageShow.py create mode 100644 pyPackages/pillowarmv7l/PIL/ImageStat.py create mode 100644 pyPackages/pillowarmv7l/PIL/ImageTk.py create mode 100644 pyPackages/pillowarmv7l/PIL/ImageTransform.py create mode 100644 pyPackages/pillowarmv7l/PIL/ImageWin.py create mode 100644 pyPackages/pillowarmv7l/PIL/ImtImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/IptcImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/Jpeg2KImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/JpegImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/JpegPresets.py create mode 100644 pyPackages/pillowarmv7l/PIL/McIdasImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/MicImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/MpegImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/MpoImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/MspImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/OleFileIO-README.md create mode 100644 pyPackages/pillowarmv7l/PIL/OleFileIO.py create mode 100644 pyPackages/pillowarmv7l/PIL/PSDraw.py create mode 100644 pyPackages/pillowarmv7l/PIL/PaletteFile.py create mode 100644 pyPackages/pillowarmv7l/PIL/PalmImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/PcdImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/PcfFontFile.py create mode 100644 pyPackages/pillowarmv7l/PIL/PcxImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/PdfImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/PixarImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/PngImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/PpmImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/PsdImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/PyAccess.py create mode 100644 pyPackages/pillowarmv7l/PIL/SgiImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/SpiderImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/SunImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/TarIO.py create mode 100644 pyPackages/pillowarmv7l/PIL/TgaImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/TiffImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/TiffTags.py create mode 100644 pyPackages/pillowarmv7l/PIL/WalImageFile.py create mode 100644 pyPackages/pillowarmv7l/PIL/WebPImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/WmfImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/XVThumbImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/XbmImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/XpmImagePlugin.py create mode 100644 pyPackages/pillowarmv7l/PIL/__init__.py create mode 100644 pyPackages/pillowarmv7l/PIL/_binary.py create mode 100644 pyPackages/pillowarmv7l/PIL/_imaging.cpython-34m.so create mode 100644 pyPackages/pillowarmv7l/PIL/_imagingmath.cpython-34m.so create mode 100644 pyPackages/pillowarmv7l/PIL/_imagingmorph.cpython-34m.so create mode 100644 pyPackages/pillowarmv7l/PIL/_util.py create mode 100644 pyPackages/pillowarmv7l/Pillow-2.7.0.data/scripts/pilconvert.py create mode 100644 pyPackages/pillowarmv7l/Pillow-2.7.0.data/scripts/pildriver.py create mode 100644 pyPackages/pillowarmv7l/Pillow-2.7.0.data/scripts/pilfile.py create mode 100644 pyPackages/pillowarmv7l/Pillow-2.7.0.data/scripts/pilfont.py create mode 100644 pyPackages/pillowarmv7l/Pillow-2.7.0.data/scripts/pilprint.py create mode 100644 pyPackages/pillowarmv7l/Pillow-2.7.0.dist-info/DESCRIPTION.rst create mode 100644 pyPackages/pillowarmv7l/Pillow-2.7.0.dist-info/METADATA create mode 100644 pyPackages/pillowarmv7l/Pillow-2.7.0.dist-info/RECORD create mode 100644 pyPackages/pillowarmv7l/Pillow-2.7.0.dist-info/WHEEL create mode 100644 pyPackages/pillowarmv7l/Pillow-2.7.0.dist-info/metadata.json create mode 100644 pyPackages/pillowarmv7l/Pillow-2.7.0.dist-info/top_level.txt create mode 100644 pyPackages/pillowarmv7l/Pillow-2.7.0.dist-info/zip-safe create mode 100644 pyPackages/pillowarmv7l/__init__.py create mode 100644 pyPackages/pillowx86/PIL/BdfFontFile.py create mode 100644 pyPackages/pillowx86/PIL/BmpImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/BufrStubImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/ContainerIO.py create mode 100644 pyPackages/pillowx86/PIL/CurImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/DcxImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/EpsImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/ExifTags.py create mode 100644 pyPackages/pillowx86/PIL/FitsStubImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/FliImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/FontFile.py create mode 100644 pyPackages/pillowx86/PIL/FpxImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/GbrImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/GdImageFile.py create mode 100644 pyPackages/pillowx86/PIL/GifImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/GimpGradientFile.py create mode 100644 pyPackages/pillowx86/PIL/GimpPaletteFile.py create mode 100644 pyPackages/pillowx86/PIL/GribStubImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/Hdf5StubImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/IcnsImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/IcoImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/ImImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/Image.py create mode 100644 pyPackages/pillowx86/PIL/ImageChops.py create mode 100644 pyPackages/pillowx86/PIL/ImageCms.py create mode 100644 pyPackages/pillowx86/PIL/ImageColor.py create mode 100644 pyPackages/pillowx86/PIL/ImageDraw.py create mode 100644 pyPackages/pillowx86/PIL/ImageDraw2.py create mode 100644 pyPackages/pillowx86/PIL/ImageEnhance.py create mode 100644 pyPackages/pillowx86/PIL/ImageFile.py create mode 100644 pyPackages/pillowx86/PIL/ImageFileIO.py create mode 100644 pyPackages/pillowx86/PIL/ImageFilter.py create mode 100644 pyPackages/pillowx86/PIL/ImageFont.py create mode 100644 pyPackages/pillowx86/PIL/ImageGrab.py create mode 100644 pyPackages/pillowx86/PIL/ImageMath.py create mode 100644 pyPackages/pillowx86/PIL/ImageMode.py create mode 100644 pyPackages/pillowx86/PIL/ImageMorph.py create mode 100644 pyPackages/pillowx86/PIL/ImageOps.py create mode 100644 pyPackages/pillowx86/PIL/ImagePalette.py create mode 100644 pyPackages/pillowx86/PIL/ImagePath.py create mode 100644 pyPackages/pillowx86/PIL/ImageQt.py create mode 100644 pyPackages/pillowx86/PIL/ImageSequence.py create mode 100644 pyPackages/pillowx86/PIL/ImageShow.py create mode 100644 pyPackages/pillowx86/PIL/ImageStat.py create mode 100644 pyPackages/pillowx86/PIL/ImageTk.py create mode 100644 pyPackages/pillowx86/PIL/ImageTransform.py create mode 100644 pyPackages/pillowx86/PIL/ImageWin.py create mode 100644 pyPackages/pillowx86/PIL/ImtImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/IptcImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/Jpeg2KImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/JpegImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/JpegPresets.py create mode 100644 pyPackages/pillowx86/PIL/McIdasImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/MicImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/MpegImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/MpoImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/MspImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/OleFileIO-README.md create mode 100644 pyPackages/pillowx86/PIL/OleFileIO.py create mode 100644 pyPackages/pillowx86/PIL/PSDraw.py create mode 100644 pyPackages/pillowx86/PIL/PaletteFile.py create mode 100644 pyPackages/pillowx86/PIL/PalmImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/PcdImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/PcfFontFile.py create mode 100644 pyPackages/pillowx86/PIL/PcxImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/PdfImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/PixarImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/PngImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/PpmImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/PsdImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/PyAccess.py create mode 100644 pyPackages/pillowx86/PIL/SgiImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/SpiderImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/SunImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/TarIO.py create mode 100644 pyPackages/pillowx86/PIL/TgaImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/TiffImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/TiffTags.py create mode 100644 pyPackages/pillowx86/PIL/WalImageFile.py create mode 100644 pyPackages/pillowx86/PIL/WebPImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/WmfImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/XVThumbImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/XbmImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/XpmImagePlugin.py create mode 100644 pyPackages/pillowx86/PIL/__init__.py create mode 100644 pyPackages/pillowx86/PIL/_binary.py create mode 100644 pyPackages/pillowx86/PIL/_imaging.cpython-34m.so create mode 100644 pyPackages/pillowx86/PIL/_imagingmath.cpython-34m.so create mode 100644 pyPackages/pillowx86/PIL/_imagingmorph.cpython-34m.so create mode 100644 pyPackages/pillowx86/PIL/_util.py create mode 100644 pyPackages/pillowx86/Pillow-2.7.0.data/scripts/pilconvert.py create mode 100644 pyPackages/pillowx86/Pillow-2.7.0.data/scripts/pildriver.py create mode 100644 pyPackages/pillowx86/Pillow-2.7.0.data/scripts/pilfile.py create mode 100644 pyPackages/pillowx86/Pillow-2.7.0.data/scripts/pilfont.py create mode 100644 pyPackages/pillowx86/Pillow-2.7.0.data/scripts/pilprint.py create mode 100644 pyPackages/pillowx86/Pillow-2.7.0.dist-info/DESCRIPTION.rst create mode 100644 pyPackages/pillowx86/Pillow-2.7.0.dist-info/METADATA create mode 100644 pyPackages/pillowx86/Pillow-2.7.0.dist-info/RECORD create mode 100644 pyPackages/pillowx86/Pillow-2.7.0.dist-info/WHEEL create mode 100644 pyPackages/pillowx86/Pillow-2.7.0.dist-info/metadata.json create mode 100644 pyPackages/pillowx86/Pillow-2.7.0.dist-info/top_level.txt create mode 100644 pyPackages/pillowx86/Pillow-2.7.0.dist-info/zip-safe create mode 100644 qml/Cover.qml create mode 100644 qml/FileChooser.qml create mode 100644 qml/FileChooser2.qml create mode 100644 qml/ImageView.qml create mode 100644 qml/Main.qml create mode 100644 qml/sailfish-python.qml create mode 100755 renamep.py create mode 100644 sailfish-python-noarch.rpm create mode 100644 src/__init__.py create mode 100644 src/main.py diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9a242f4 --- /dev/null +++ b/Makefile @@ -0,0 +1,61 @@ +Appname:=sailfish-python +prefix:=/usr +temp:=/tmp/make +builddir:=./build +sdkpath:=$(HOME)/SailfishOS +dependencies:=-d libsailfishapp-launcher -d python3-base -d pyotherside-qml-plugin-python3-qt5 +arch:=noarch +rpmname:=$(Appname)-$(arch).rpm +jolla_usb_ip:=192.168.2.15 +jolla_wifi_ip:=Jolla + + +all: clean build-tmp rpm-virt rpm-jolla + +make-jolla: build-tmp rpm-jolla send-jolla +make-jolla-wifi: build-tmp rpm-jolla send-jolla-wifi +make-jolla-ap: build-tmp rpm-jolla send-jolla-ap +make-virt: build-tmp rpm-virt send-virt + +build-tmp: + rm -rf $(temp) + mkdir -p $(temp)/usr/share/applications + mkdir -p $(temp)/usr/share/$(Appname)/src + mkdir -p $(temp)/usr/share/$(Appname)/src + mkdir -p $(temp)/usr/bin + cp -ar ./qml $(temp)/usr/share/$(Appname) + cp -ar ./src/*.py $(temp)/usr/share/$(Appname)/src + cp -ar ./pyPackages $(temp)/usr/share/$(Appname)/src + cp ./dat/$(Appname).desktop $(temp)/usr/share/applications/ + install -m 755 ./dat/$(Appname).sh $(temp)/usr/bin/$(Appname) + +rpm-virt: + cd $(temp);fpm -f -s dir -t rpm $(dependencies) -p $(CURDIR)/$(Appname)-$(arch).rpm -n $(Appname) -a $(arch) --prefix / * + +rpm-jolla: arch:=noarch +rpm-jolla: + cd $(temp);fpm -f -s dir -t rpm $(dependencies) -p $(CURDIR)/$(Appname)-$(arch).rpm -n $(Appname) -a $(arch) --prefix / * + +send-virt: + rsync -vrp --rsh='ssh -p2223 -i $(sdkpath)/vmshare/ssh/private_keys/SailfishOS_Emulator/root' ./$(Appname)-$(arch).rpm root@localhost:/home/nemo/Downloads + ssh -p2223 -i $(sdkpath)/vmshare/ssh/private_keys/SailfishOS_Emulator/root root@localhost pkcon install-local -y /home/nemo/Downloads/$(Appname)-$(arch).rpm + +send-jolla-wifi: arch:=noarch +send-jolla-wifi: + rsync -vrp ./$(Appname)-$(arch).rpm root@$(jolla_wifi_ip):/home/nemo/Downloads + ssh root@$(jolla_wifi_ip) pkcon install-local -y /home/nemo/Downloads/$(Appname)-$(arch).rpm +send-jolla-ap: arch:=noarch +send-jolla-ap: jolla_wifi_ip:=192.168.1.1 +send-jolla-ap: + rsync -vrp ./$(Appname)-$(arch).rpm root@$(jolla_wifi_ip):/home/nemo/Downloads + ssh root@$(jolla_wifi_ip) pkcon install-local -y /home/nemo/Downloads/$(Appname)-$(arch).rpm +send-jolla: arch:=noarch +send-jolla: + rsync -vrp ./$(Appname)-$(arch).rpm root@$(jolla_usb_ip):/home/nemo/Downloads + ssh root@$(jolla_usb_ip) pkcon install-local -y /home/nemo/Downloads/$(Appname)-$(arch).rpm + +clean: + rm -rf $(temp) + rm -rf $(builddir) + + diff --git a/README b/README new file mode 100644 index 0000000..f3d8ba6 --- /dev/null +++ b/README @@ -0,0 +1,43 @@ +Sailfish Python + +this is a pyotherside based app template for sailfish os. +i simply cant get my head around the way rpm packages are built, +so i found an alternative Way using fpm +https://github.com/jordansissel/fpm + +these packages are needed to build/install the package: + +fpm -- to package a a root tree from a temporary directory to a rpm package +rsync -- to send the package to your testing machine (jolla phone or sailfish emulator) + +use these make commands to build/install your app for testing: + +make make-virt +build your package, and install it on your Sailfish Emulator on localhost:2223, considering +your Sailfish-SDK is installed at ~/SailfishOS + +make make-jolla-wifi +build your package, and install it on your jolla phone, considering your development PC is +authorized for root ssh login on the phone and it is found in your dns-space as "jolla" + +make make-jolla-usb [jolla_usb_ip=192.168.2.15] +build your package, and install it on your jolla phone, considering your development PC is +authorized for root ssh login on the phone, and connected via usb development mode. +set your jollas ip like above. + + +Adding dependencies: +a basic set of dependencies are already added to the Makefile, but you can still add other ones +to the list. + +Adding python Modules: +all python Modules in pyPackages are included in the package. +for now, i like to package both an armv7l and a x86 version and select the package version +within the qml code. Packages are built with "noarch", so they should run an any device. +I had success using pip wheel to build these packages directly on the jolla phone/sailfish emulator +and unpacking them to pyPackages. +as an example, the is pillow (Python Imaging Library) included in the package. + +Renaming your App: +./renamep.py "my-new-appname" + diff --git a/appname.txt b/appname.txt new file mode 100644 index 0000000..add8126 --- /dev/null +++ b/appname.txt @@ -0,0 +1 @@ +sailfish-python diff --git a/dat/Sailfish-Scanner.spec b/dat/Sailfish-Scanner.spec new file mode 100644 index 0000000..6e61b0e --- /dev/null +++ b/dat/Sailfish-Scanner.spec @@ -0,0 +1,72 @@ +Name: scanner + +Summary: Image Cropping Tool +Version: 0.0.1 +Release: 1 +Group: Applications/Internet +License: BSD +Url: https://github.com/dasimmet/sailfish-scanner +Source0: %{name}-%{version}.tar.bz2 +BuildRequires: pkgconfig(Qt5Core) +BuildRequires: pkgconfig(Qt5Qml) +BuildRequires: pkgconfig(Qt5Gui) +BuildRequires: pkgconfig(Qt5Quick) +BuildRequires: pkgconfig(qt5embedwidget) >= 1.9.4 + +Requires: sailfishsilica-qt5 >= 0.11.8 +Requires: jolla-ambient >= 0.3.24 +Requires: pyotherside-qml-plugin-python3-qt5 +Requires: python3-base +Requires: sailfish-components-media-qt5 + +%description +Sailfish Web Browser + +%prep +%setup -q -n %{name}-%{version} + +# >> setup +# << setup + +%build +# >> build pre +# << build pre + +%qmake5 + +make %{?jobs:-j%jobs} + +# >> build post +# << build post + +%install +rm -rf %{buildroot} +# >> install pre +# << install pre +%qmake5_install +chmod +x %{buildroot}/%{_oneshotdir}/* + +# >> install post +# << install post + +%post +# >> post +/usr/bin/update-desktop-database -q + +# Upgrade, count is 2 or higher (depending on the number of versions installed) +if [ "$1" -ge 2 ]; then +%{_bindir}/add-oneshot --user --now cleanup-browser-startup-cache +fi +# << post + +%files +%defattr(-,root,root,-) +# >> files +%{_bindir}/%{name} +%{_datadir}/applications/%{name}.desktop +%{_datadir}/applications/open-url.desktop +%{_datadir}/%{name}/* +%{_datadir}/translations/sailfish-browser_eng_en.qm +%{_datadir}/dbus-1/services/*.service +%{_oneshotdir}/cleanup-browser-startup-cache +# << files diff --git a/dat/sailfish-python.desktop b/dat/sailfish-python.desktop new file mode 100644 index 0000000..6304c03 --- /dev/null +++ b/dat/sailfish-python.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=Application +X-Nemo-Application-Type=silica-qt5 +Name=Scanner +Icon=icon-launcher-component-gallery +Exec=sailfish-qml sailfish-scanner diff --git a/dat/sailfish-python.sh b/dat/sailfish-python.sh new file mode 100644 index 0000000..d3a80cf --- /dev/null +++ b/dat/sailfish-python.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +sailfish-qml sailfish-scanner + diff --git a/openproject.sh b/openproject.sh new file mode 100755 index 0000000..12bf254 --- /dev/null +++ b/openproject.sh @@ -0,0 +1,6 @@ + +qml/sailfish-scanner.qml +src/main.py +Makefile +qml/FileChooser.qml +qml/FileChooser2.qml diff --git a/pyPackages/pillowarmv7l/PIL/BdfFontFile.py b/pyPackages/pillowarmv7l/PIL/BdfFontFile.py new file mode 100644 index 0000000..c528124 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/BdfFontFile.py @@ -0,0 +1,133 @@ +# +# The Python Imaging Library +# $Id$ +# +# bitmap distribution font (bdf) file parser +# +# history: +# 1996-05-16 fl created (as bdf2pil) +# 1997-08-25 fl converted to FontFile driver +# 2001-05-25 fl removed bogus __init__ call +# 2002-11-20 fl robustification (from Kevin Cazabon, Dmitry Vasiliev) +# 2003-04-22 fl more robustification (from Graham Dumpleton) +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1997-2003 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image +from PIL import FontFile + + +# -------------------------------------------------------------------- +# parse X Bitmap Distribution Format (BDF) +# -------------------------------------------------------------------- + +bdf_slant = { + "R": "Roman", + "I": "Italic", + "O": "Oblique", + "RI": "Reverse Italic", + "RO": "Reverse Oblique", + "OT": "Other" +} + +bdf_spacing = { + "P": "Proportional", + "M": "Monospaced", + "C": "Cell" +} + + +def bdf_char(f): + # skip to STARTCHAR + while True: + s = f.readline() + if not s: + return None + if s[:9] == b"STARTCHAR": + break + id = s[9:].strip().decode('ascii') + + # load symbol properties + props = {} + while True: + s = f.readline() + if not s or s[:6] == b"BITMAP": + break + i = s.find(b" ") + props[s[:i].decode('ascii')] = s[i+1:-1].decode('ascii') + + # load bitmap + bitmap = [] + while True: + s = f.readline() + if not s or s[:7] == b"ENDCHAR": + break + bitmap.append(s[:-1]) + bitmap = b"".join(bitmap) + + [x, y, l, d] = [int(s) for s in props["BBX"].split()] + [dx, dy] = [int(s) for s in props["DWIDTH"].split()] + + bbox = (dx, dy), (l, -d-y, x+l, -d), (0, 0, x, y) + + try: + im = Image.frombytes("1", (x, y), bitmap, "hex", "1") + except ValueError: + # deal with zero-width characters + im = Image.new("1", (x, y)) + + return id, int(props["ENCODING"]), bbox, im + + +## +# Font file plugin for the X11 BDF format. + +class BdfFontFile(FontFile.FontFile): + + def __init__(self, fp): + + FontFile.FontFile.__init__(self) + + s = fp.readline() + if s[:13] != b"STARTFONT 2.1": + raise SyntaxError("not a valid BDF file") + + props = {} + comments = [] + + while True: + s = fp.readline() + if not s or s[:13] == b"ENDPROPERTIES": + break + i = s.find(b" ") + 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')) + + font = props["FONT"].split("-") + + font[4] = bdf_slant[font[4].upper()] + font[11] = bdf_spacing[font[11].upper()] + + # ascent = int(props["FONT_ASCENT"]) + # descent = int(props["FONT_DESCENT"]) + + # fontname = ";".join(font[1:]) + + # print "#", fontname + # for i in comments: + # print "#", i + + font = [] + while True: + c = bdf_char(fp) + if not c: + break + id, ch, (xy, dst, src), im = c + if 0 <= ch < len(self.glyph): + self.glyph[ch] = xy, dst, src, im diff --git a/pyPackages/pillowarmv7l/PIL/BmpImagePlugin.py b/pyPackages/pillowarmv7l/PIL/BmpImagePlugin.py new file mode 100644 index 0000000..917d43b --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/BmpImagePlugin.py @@ -0,0 +1,264 @@ +# +# The Python Imaging Library. +# $Id$ +# +# BMP file handler +# +# Windows (and OS/2) native bitmap storage format. +# +# history: +# 1995-09-01 fl Created +# 1996-04-30 fl Added save +# 1997-08-27 fl Fixed save of 1-bit images +# 1998-03-06 fl Load P images as L where possible +# 1998-07-03 fl Load P images as 1 where possible +# 1998-12-29 fl Handle small palettes +# 2002-12-30 fl Fixed load of 1-bit palette images +# 2003-04-21 fl Fixed load of 1-bit monochrome images +# 2003-04-23 fl Added limited support for BI_BITFIELDS compression +# +# Copyright (c) 1997-2003 by Secret Labs AB +# Copyright (c) 1995-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.7" + + +from PIL import Image, ImageFile, ImagePalette, _binary +import math + +i8 = _binary.i8 +i16 = _binary.i16le +i32 = _binary.i32le +o8 = _binary.o8 +o16 = _binary.o16le +o32 = _binary.o32le + +# +# -------------------------------------------------------------------- +# Read BMP file + +BIT2MODE = { + # bits => mode, rawmode + 1: ("P", "P;1"), + 4: ("P", "P;4"), + 8: ("P", "P"), + 16: ("RGB", "BGR;15"), + 24: ("RGB", "BGR"), + 32: ("RGB", "BGRX") +} + + +def _accept(prefix): + return prefix[:2] == b"BM" + + +## +# Image plugin for the Windows BMP format. + +class BmpImageFile(ImageFile.ImageFile): + + format = "BMP" + format_description = "Windows Bitmap" + + def _bitmap(self, header=0, offset=0): + if header: + self.fp.seek(header) + + read = self.fp.read + + # CORE/INFO + s = read(4) + s = s + ImageFile._safe_read(self.fp, i32(s)-4) + + if len(s) == 12: + + # OS/2 1.0 CORE + bits = i16(s[10:]) + self.size = i16(s[4:]), i16(s[6:]) + compression = 0 + lutsize = 3 + colors = 0 + direction = -1 + + elif len(s) in [40, 64, 108, 124]: + + # WIN 3.1 or OS/2 2.0 INFO + bits = i16(s[14:]) + self.size = i32(s[4:]), i32(s[8:]) + compression = i32(s[16:]) + pxperm = (i32(s[24:]), i32(s[28:])) # Pixels per meter + lutsize = 4 + colors = i32(s[32:]) + direction = -1 + if i8(s[11]) == 0xff: + # upside-down storage + self.size = self.size[0], 2**32 - self.size[1] + direction = 0 + + self.info["dpi"] = tuple(map(lambda x: math.ceil(x / 39.3701), + pxperm)) + + else: + raise IOError("Unsupported BMP header type (%d)" % len(s)) + + if (self.size[0]*self.size[1]) > 2**31: + # Prevent DOS for > 2gb images + raise IOError("Unsupported BMP Size: (%dx%d)" % self.size) + + if not colors: + colors = 1 << bits + + # MODE + try: + self.mode, rawmode = BIT2MODE[bits] + except KeyError: + raise IOError("Unsupported BMP pixel depth (%d)" % bits) + + if compression == 3: + # BI_BITFIELDS compression + mask = i32(read(4)), i32(read(4)), i32(read(4)) + if bits == 32 and mask == (0xff0000, 0x00ff00, 0x0000ff): + rawmode = "BGRX" + elif bits == 16 and mask == (0x00f800, 0x0007e0, 0x00001f): + rawmode = "BGR;16" + elif bits == 16 and mask == (0x007c00, 0x0003e0, 0x00001f): + rawmode = "BGR;15" + else: + # print bits, map(hex, mask) + raise IOError("Unsupported BMP bitfields layout") + elif compression != 0: + raise IOError("Unsupported BMP compression (%d)" % compression) + + # LUT + if self.mode == "P": + palette = [] + greyscale = 1 + if colors == 2: + indices = (0, 255) + elif colors > 2**16 or colors <= 0: # We're reading a i32. + raise IOError("Unsupported BMP Palette size (%d)" % colors) + else: + indices = list(range(colors)) + for i in indices: + rgb = read(lutsize)[:3] + if rgb != o8(i)*3: + greyscale = 0 + palette.append(rgb) + if greyscale: + if colors == 2: + self.mode = rawmode = "1" + else: + self.mode = rawmode = "L" + else: + self.mode = "P" + self.palette = ImagePalette.raw( + "BGR", b"".join(palette) + ) + + if not offset: + offset = self.fp.tell() + + self.tile = [("raw", + (0, 0) + self.size, + offset, + (rawmode, ((self.size[0]*bits+31) >> 3) & (~3), + direction))] + + self.info["compression"] = compression + + def _open(self): + + # HEAD + s = self.fp.read(14) + if s[:2] != b"BM": + raise SyntaxError("Not a BMP file") + offset = i32(s[10:]) + + self._bitmap(offset=offset) + + +class DibImageFile(BmpImageFile): + + format = "DIB" + format_description = "Windows Bitmap" + + def _open(self): + self._bitmap() + +# +# -------------------------------------------------------------------- +# Write BMP file + +SAVE = { + "1": ("1", 1, 2), + "L": ("L", 8, 256), + "P": ("P", 8, 256), + "RGB": ("BGR", 24, 0), +} + + +def _save(im, fp, filename, check=0): + try: + rawmode, bits, colors = SAVE[im.mode] + except KeyError: + raise IOError("cannot write mode %s as BMP" % im.mode) + + if check: + return check + + info = im.encoderinfo + + dpi = info.get("dpi", (96, 96)) + + # 1 meter == 39.3701 inches + ppm = tuple(map(lambda x: int(x * 39.3701), dpi)) + + stride = ((im.size[0]*bits+7)//8+3) & (~3) + header = 40 # or 64 for OS/2 version 2 + offset = 14 + header + colors * 4 + image = stride * im.size[1] + + # bitmap header + fp.write(b"BM" + # file type (magic) + o32(offset+image) + # file size + o32(0) + # reserved + o32(offset)) # 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(b"\0" * (header - 40)) # padding (for OS/2 format) + + if im.mode == "1": + for i in (0, 255): + fp.write(o8(i) * 4) + elif im.mode == "L": + for i in range(256): + fp.write(o8(i) * 4) + elif im.mode == "P": + fp.write(im.im.getpalette("RGB", "BGRX")) + + ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0, + (rawmode, stride, -1))]) + +# +# -------------------------------------------------------------------- +# Registry + +Image.register_open(BmpImageFile.format, BmpImageFile, _accept) +Image.register_save(BmpImageFile.format, _save) + +Image.register_extension(BmpImageFile.format, ".bmp") diff --git a/pyPackages/pillowarmv7l/PIL/BufrStubImagePlugin.py b/pyPackages/pillowarmv7l/PIL/BufrStubImagePlugin.py new file mode 100644 index 0000000..45ee547 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/BufrStubImagePlugin.py @@ -0,0 +1,72 @@ +# +# The Python Imaging Library +# $Id$ +# +# BUFR stub adapter +# +# Copyright (c) 1996-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image, ImageFile + +_handler = None + + +## +# Install application-specific BUFR image handler. +# +# @param handler Handler object. + +def register_handler(handler): + global _handler + _handler = handler + + +# -------------------------------------------------------------------- +# Image adapter + +def _accept(prefix): + return prefix[:4] == b"BUFR" or prefix[:4] == b"ZCZC" + + +class BufrStubImageFile(ImageFile.StubImageFile): + + format = "BUFR" + format_description = "BUFR" + + def _open(self): + + offset = self.fp.tell() + + if not _accept(self.fp.read(8)): + raise SyntaxError("Not a BUFR file") + + self.fp.seek(offset) + + # make something up + self.mode = "F" + self.size = 1, 1 + + loader = self._load() + if loader: + loader.open(self) + + def _load(self): + return _handler + + +def _save(im, fp, filename): + if _handler is None or not hasattr("_handler", "save"): + raise IOError("BUFR save handler not installed") + _handler.save(im, fp, filename) + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(BufrStubImageFile.format, BufrStubImageFile, _accept) +Image.register_save(BufrStubImageFile.format, _save) + +Image.register_extension(BufrStubImageFile.format, ".bufr") diff --git a/pyPackages/pillowarmv7l/PIL/ContainerIO.py b/pyPackages/pillowarmv7l/PIL/ContainerIO.py new file mode 100644 index 0000000..dcedcd6 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/ContainerIO.py @@ -0,0 +1,117 @@ +# +# The Python Imaging Library. +# $Id$ +# +# a class to read from a container file +# +# History: +# 1995-06-18 fl Created +# 1995-09-07 fl Added readline(), readlines() +# +# Copyright (c) 1997-2001 by Secret Labs AB +# Copyright (c) 1995 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +## +# A file object that provides read access to a part of an existing +# file (for example a TAR file). + + +class ContainerIO: + + ## + # Create file object. + # + # @param file Existing file. + # @param offset Start of region, in bytes. + # @param length Size of region, in bytes. + + def __init__(self, file, offset, length): + self.fh = file + self.pos = 0 + self.offset = offset + self.length = length + self.fh.seek(offset) + + ## + # Always false. + + def isatty(self): + return 0 + + ## + # Move file pointer. + # + # @param offset Offset in bytes. + # @param mode Starting position. Use 0 for beginning of region, 1 + # for current offset, and 2 for end of region. You cannot move + # the pointer outside the defined region. + + def seek(self, offset, mode=0): + if mode == 1: + self.pos = self.pos + offset + elif mode == 2: + self.pos = self.length + offset + else: + self.pos = offset + # clamp + self.pos = max(0, min(self.pos, self.length)) + self.fh.seek(self.offset + self.pos) + + ## + # Get current file pointer. + # + # @return Offset from start of region, in bytes. + + def tell(self): + return self.pos + + ## + # Read data. + # + # @def read(bytes=0) + # @param bytes Number of bytes to read. If omitted or zero, + # read until end of region. + # @return An 8-bit string. + + def read(self, n=0): + if n: + n = min(n, self.length - self.pos) + else: + n = self.length - self.pos + if not n: # EOF + return "" + self.pos = self.pos + n + return self.fh.read(n) + + ## + # Read a line of text. + # + # @return An 8-bit string. + + def readline(self): + s = "" + while True: + c = self.read(1) + if not c: + break + s = s + c + if c == "\n": + break + return s + + ## + # Read multiple lines of text. + # + # @return A list of 8-bit strings. + + def readlines(self): + l = [] + while True: + s = self.readline() + if not s: + break + l.append(s) + return l diff --git a/pyPackages/pillowarmv7l/PIL/CurImagePlugin.py b/pyPackages/pillowarmv7l/PIL/CurImagePlugin.py new file mode 100644 index 0000000..0178957 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/CurImagePlugin.py @@ -0,0 +1,87 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Windows Cursor support for PIL +# +# notes: +# uses BmpImagePlugin.py to read the bitmap data. +# +# history: +# 96-05-27 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.1" + +from PIL import Image, BmpImagePlugin, _binary + + +# +# -------------------------------------------------------------------- + +i8 = _binary.i8 +i16 = _binary.i16le +i32 = _binary.i32le + + +def _accept(prefix): + return prefix[:4] == b"\0\0\2\0" + + +## +# Image plugin for Windows Cursor files. + +class CurImageFile(BmpImagePlugin.BmpImageFile): + + format = "CUR" + format_description = "Windows Cursor" + + def _open(self): + + offset = self.fp.tell() + + # check magic + s = self.fp.read(6) + if not _accept(s): + raise SyntaxError("not a CUR file") + + # pick the largest cursor in the file + m = b"" + for i in range(i16(s[4:])): + s = self.fp.read(16) + if not m: + m = s + elif i8(s[0]) > i8(m[0]) and i8(s[1]) > i8(m[1]): + m = s + # print "width", i8(s[0]) + # print "height", i8(s[1]) + # print "colors", i8(s[2]) + # print "reserved", i8(s[3]) + # print "hotspot x", i16(s[4:]) + # print "hotspot y", i16(s[6:]) + # print "bytes", i32(s[8:]) + # print "offset", i32(s[12:]) + + # load as bitmap + self._bitmap(i32(m[12:]) + offset) + + # patch up the bitmap height + 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 + + return + + +# +# -------------------------------------------------------------------- + +Image.register_open("CUR", CurImageFile, _accept) + +Image.register_extension("CUR", ".cur") diff --git a/pyPackages/pillowarmv7l/PIL/DcxImagePlugin.py b/pyPackages/pillowarmv7l/PIL/DcxImagePlugin.py new file mode 100644 index 0000000..0940b39 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/DcxImagePlugin.py @@ -0,0 +1,79 @@ +# +# The Python Imaging Library. +# $Id$ +# +# DCX file handling +# +# DCX is a container file format defined by Intel, commonly used +# for fax applications. Each DCX file consists of a directory +# (a list of file offsets) followed by a set of (usually 1-bit) +# PCX files. +# +# History: +# 1995-09-09 fl Created +# 1996-03-20 fl Properly derived from PcxImageFile. +# 1998-07-15 fl Renamed offset attribute to avoid name clash +# 2002-07-30 fl Fixed file handling +# +# Copyright (c) 1997-98 by Secret Labs AB. +# Copyright (c) 1995-96 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.2" + +from PIL import Image, _binary + +from PIL.PcxImagePlugin import PcxImageFile + +MAGIC = 0x3ADE68B1 # QUIZ: what's this value, then? + +i32 = _binary.i32le + + +def _accept(prefix): + return i32(prefix) == MAGIC + + +## +# Image plugin for the Intel DCX format. + +class DcxImageFile(PcxImageFile): + + format = "DCX" + format_description = "Intel DCX" + + def _open(self): + + # Header + s = self.fp.read(4) + if i32(s) != MAGIC: + raise SyntaxError("not a DCX file") + + # Component directory + self._offset = [] + for i in range(1024): + offset = i32(self.fp.read(4)) + if not offset: + break + self._offset.append(offset) + + self.__fp = self.fp + self.seek(0) + + def seek(self, frame): + if frame >= len(self._offset): + raise EOFError("attempt to seek outside DCX directory") + self.frame = frame + self.fp = self.__fp + self.fp.seek(self._offset[frame]) + PcxImageFile._open(self) + + def tell(self): + return self.frame + + +Image.register_open("DCX", DcxImageFile, _accept) + +Image.register_extension("DCX", ".dcx") diff --git a/pyPackages/pillowarmv7l/PIL/EpsImagePlugin.py b/pyPackages/pillowarmv7l/PIL/EpsImagePlugin.py new file mode 100644 index 0000000..d077ca1 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/EpsImagePlugin.py @@ -0,0 +1,424 @@ +# +# The Python Imaging Library. +# $Id$ +# +# EPS file handling +# +# History: +# 1995-09-01 fl Created (0.1) +# 1996-05-18 fl Don't choke on "atend" fields, Ghostscript interface (0.2) +# 1996-08-22 fl Don't choke on floating point BoundingBox values +# 1996-08-23 fl Handle files from Macintosh (0.3) +# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.4) +# 2003-09-07 fl Check gs.close status (from Federico Di Gregorio) (0.5) +# 2014-05-07 e Handling of EPS with binary preview and fixed resolution +# resizing +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1995-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.5" + +import re +import io +from PIL import Image, ImageFile, _binary + +# +# -------------------------------------------------------------------- + +i32 = _binary.i32le +o32 = _binary.o32le + +split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$") +field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$") + +gs_windows_binary = None +import sys +if sys.platform.startswith('win'): + import shutil + if hasattr(shutil, 'which'): + which = shutil.which + else: + # Python < 3.3 + import distutils.spawn + which = distutils.spawn.find_executable + for binary in ('gswin32c', 'gswin64c', 'gs'): + if which(binary) is not None: + gs_windows_binary = binary + break + else: + gs_windows_binary = False + + +def has_ghostscript(): + if gs_windows_binary: + return True + if not sys.platform.startswith('win'): + import subprocess + try: + gs = subprocess.Popen(['gs', '--version'], stdout=subprocess.PIPE) + gs.stdout.read() + return True + except OSError: + # no ghostscript + pass + return False + + +def Ghostscript(tile, size, fp, scale=1): + """Render an image using Ghostscript""" + + # Unpack decoder tile + decoder, tile, offset, data = tile[0] + length, bbox = data + + # Hack to support hi-res rendering + scale = int(scale) or 1 + # orig_size = size + # 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]))) + # print("Ghostscript", scale, size, orig_size, bbox, orig_bbox, res) + + import os + import subprocess + import tempfile + + out_fd, outfile = tempfile.mkstemp() + os.close(out_fd) + + infile_temp = None + if hasattr(fp, 'name') and os.path.exists(fp.name): + infile = fp.name + else: + in_fd, infile_temp = tempfile.mkstemp() + os.close(in_fd) + infile = infile_temp + + # ignore length and offset! + # ghostscript can read it + # copy whole file to read in ghostscript + with open(infile_temp, 'wb') as f: + # fetch length of fp + fp.seek(0, 2) + fsize = fp.tell() + # ensure start position + # go back + fp.seek(0) + lengthfile = fsize + while lengthfile > 0: + 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) + "-dNOPAUSE -dSAFER", # don't pause between pages, + # safe mode + "-sDEVICE=ppmraw", # ppm driver + "-sOutputFile=%s" % outfile, # output file + "-c", "%d %d translate" % (-bbox[0], -bbox[1]), + # adjust for image origin + "-f", infile, # input file + ] + + if gs_windows_binary is not None: + if not gs_windows_binary: + raise WindowsError('Unable to locate Ghostscript on paths') + command[0] = gs_windows_binary + + # push data through ghostscript + try: + gs = subprocess.Popen(command, stdin=subprocess.PIPE, + stdout=subprocess.PIPE) + gs.stdin.close() + status = gs.wait() + if status: + raise IOError("gs failed (status %d)" % status) + im = Image.core.open_ppm(outfile) + finally: + try: + os.unlink(outfile) + if infile_temp: + os.unlink(infile_temp) + except: + pass + + return im + + +class PSFile: + """ + Wrapper for bytesio object that treats either CR or LF as end of line. + """ + def __init__(self, fp): + self.fp = fp + self.char = None + + def seek(self, offset, whence=0): + self.char = None + self.fp.seek(offset, whence) + + def readline(self): + s = self.char or b"" + self.char = None + + c = self.fp.read(1) + while c not in b"\r\n": + s = s + c + c = self.fp.read(1) + + self.char = self.fp.read(1) + # line endings can be 1 or 2 of \r \n, in either order + if self.char in b"\r\n": + self.char = None + + return s.decode('latin-1') + + +def _accept(prefix): + return prefix[:4] == b"%!PS" or i32(prefix) == 0xC6D3D0C5 + +## +# Image plugin for Encapsulated Postscript. This plugin supports only +# a few variants of this format. + + +class EpsImageFile(ImageFile.ImageFile): + """EPS File Parser for the Python Imaging Library""" + + format = "EPS" + format_description = "Encapsulated Postscript" + + mode_map = {1: "L", 2: "LAB", 3: "RGB"} + + def _open(self): + (length, offset) = self._find_offset(self.fp) + + # Rewrap the open file pointer in something that will + # convert line endings and decode to latin-1. + try: + if bytes is str: + # Python2, no encoding conversion necessary + fp = open(self.fp.name, "Ur") + else: + # Python3, can use bare open command. + fp = open(self.fp.name, "Ur", encoding='latin-1') + except: + # Expect this for bytesio/stringio + fp = PSFile(self.fp) + + # go to offset - start of "%!PS" + fp.seek(offset) + + box = None + + self.mode = "RGB" + self.size = 1, 1 # FIXME: huh? + + # + # Load EPS header + + s = fp.readline().strip('\r\n') + + while s: + if len(s) > 255: + raise SyntaxError("not an EPS file") + + try: + m = split.match(s) + except re.error as v: + raise SyntaxError("not an EPS file") + + if m: + k, v = m.group(1, 2) + self.info[k] = v + if k == "BoundingBox": + try: + # Note: The DSC spec says that BoundingBox + # fields should be integers, but some drivers + # put floating point values there anyway. + box = [int(float(s)) for s in v.split()] + self.size = box[2] - box[0], box[3] - box[1] + self.tile = [("eps", (0, 0) + self.size, offset, + (length, box))] + except: + pass + + else: + m = field.match(s) + if m: + k = m.group(1) + + if k == "EndComments": + break + if k[:8] == "PS-Adobe": + self.info[k[:8]] = k[9:] + else: + self.info[k] = "" + elif s[0] == '%': + # handle non-DSC Postscript comments that some + # tools mistakenly put in the Comments section + pass + else: + raise IOError("bad EPS header") + + s = fp.readline().strip('\r\n') + + if s[0] != "%": + break + + # + # Scan for an "ImageData" descriptor + + while s[0] == "%": + + if len(s) > 255: + raise SyntaxError("not an EPS file") + + if s[:11] == "%ImageData:": + # Encoded bitmapped image. + [x, y, bi, mo, z3, z4, en, id] = s[11:].split(None, 7) + + if int(bi) != 8: + break + try: + self.mode = self.mode_map[int(mo)] + except: + break + + self.size = int(x), int(y) + return + + s = fp.readline().strip('\r\n') + if not s: + break + + if not box: + raise IOError("cannot determine EPS bounding box") + + def _find_offset(self, fp): + + s = fp.read(160) + + if s[:4] == b"%!PS": + # for HEAD without binary preview + fp.seek(0, 2) + length = fp.tell() + offset = 0 + elif i32(s[0:4]) == 0xC6D3D0C5: + # FIX for: Some EPS file not handled correctly / issue #302 + # EPS can contain binary data + # or start directly with latin coding + # more info see: + # http://partners.adobe.com/public/developer/en/ps/5002.EPSF_Spec.pdf + offset = i32(s[4:8]) + length = i32(s[8:12]) + else: + raise SyntaxError("not an EPS file") + + return (length, offset) + + def load(self, scale=1): + # Load EPS via Ghostscript + if not self.tile: + return + self.im = Ghostscript(self.tile, self.size, self.fp, scale) + self.mode = self.im.mode + self.size = self.im.size + self.tile = [] + + def load_seek(self, *args, **kwargs): + # we can't incrementally load, so force ImageFile.parser to + # use our custom load method by defining this method. + pass + + +# +# -------------------------------------------------------------------- + +def _save(im, fp, filename, eps=1): + """EPS Writer for the Python Imaging Library.""" + + # + # make sure image data is available + im.load() + + # + # determine postscript image mode + if im.mode == "L": + operator = (8, 1, "image") + elif im.mode == "RGB": + operator = (8, 3, "false 3 colorimage") + elif im.mode == "CMYK": + operator = (8, 4, "false 4 colorimage") + else: + raise ValueError("image mode is not supported") + + class NoCloseStream: + def __init__(self, fp): + self.fp = fp + + def __getattr__(self, name): + return getattr(self.fp, name) + + def close(self): + pass + + base_fp = fp + fp = NoCloseStream(fp) + if sys.version_info[0] > 2: + fp = io.TextIOWrapper(fp, encoding='latin-1') + + if eps: + # + # write EPS header + fp.write("%!PS-Adobe-3.0 EPSF-3.0\n") + fp.write("%%Creator: PIL 0.1 EpsEncode\n") + # fp.write("%%CreationDate: %s"...) + fp.write("%%%%BoundingBox: 0 0 %d %d\n" % im.size) + fp.write("%%Pages: 1\n") + 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) + + # + # image header + fp.write("gsave\n") + fp.write("10 dict begin\n") + fp.write("/buf %d string def\n" % (im.size[0] * operator[1])) + fp.write("%d %d scale\n" % im.size) + fp.write("%d %d 8\n" % im.size) # <= bits + fp.write("[%d 0 0 -%d 0 %d]\n" % (im.size[0], im.size[1], im.size[1])) + fp.write("{ currentfile buf readhexstring pop } bind\n") + fp.write(operator[2] + "\n") + fp.flush() + + ImageFile._save(im, base_fp, [("eps", (0, 0)+im.size, 0, None)]) + + fp.write("\n%%%%EndBinary\n") + fp.write("grestore end\n") + fp.flush() + +# +# -------------------------------------------------------------------- + +Image.register_open(EpsImageFile.format, EpsImageFile, _accept) + +Image.register_save(EpsImageFile.format, _save) + +Image.register_extension(EpsImageFile.format, ".ps") +Image.register_extension(EpsImageFile.format, ".eps") + +Image.register_mime(EpsImageFile.format, "application/postscript") diff --git a/pyPackages/pillowarmv7l/PIL/ExifTags.py b/pyPackages/pillowarmv7l/PIL/ExifTags.py new file mode 100644 index 0000000..52e145f --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/ExifTags.py @@ -0,0 +1,193 @@ +# +# The Python Imaging Library. +# $Id$ +# +# EXIF tags +# +# Copyright (c) 2003 by Secret Labs AB +# +# See the README file for information on usage and redistribution. +# + +## +# This module provides constants and clear-text names for various +# well-known EXIF tags. +## + +## +# Maps EXIF tags to tag names. + +TAGS = { + + # possibly incomplete + 0x00fe: "NewSubfileType", + 0x00ff: "SubfileType", + 0x0100: "ImageWidth", + 0x0101: "ImageLength", + 0x0102: "BitsPerSample", + 0x0103: "Compression", + 0x0106: "PhotometricInterpretation", + 0x0107: "Threshholding", + 0x0108: "CellWidth", + 0x0109: "CellLenght", + 0x010a: "FillOrder", + 0x010d: "DocumentName", + 0x011d: "PageName", + 0x010e: "ImageDescription", + 0x010f: "Make", + 0x0110: "Model", + 0x0111: "StripOffsets", + 0x0112: "Orientation", + 0x0115: "SamplesPerPixel", + 0x0116: "RowsPerStrip", + 0x0117: "StripByteConunts", + 0x0118: "MinSampleValue", + 0x0119: "MaxSampleValue", + 0x011a: "XResolution", + 0x011b: "YResolution", + 0x011c: "PlanarConfiguration", + 0x0120: "FreeOffsets", + 0x0121: "FreeByteCounts", + 0x0122: "GrayResponseUnit", + 0x0123: "GrayResponseCurve", + 0x0128: "ResolutionUnit", + 0x012d: "TransferFunction", + 0x0131: "Software", + 0x0132: "DateTime", + 0x013b: "Artist", + 0x013c: "HostComputer", + 0x013e: "WhitePoint", + 0x013f: "PrimaryChromaticities", + 0x0140: "ColorMap", + 0x0152: "ExtraSamples", + 0x0201: "JpegIFOffset", + 0x0202: "JpegIFByteCount", + 0x0211: "YCbCrCoefficients", + 0x0212: "YCbCrSubSampling", + 0x0213: "YCbCrPositioning", + 0x0214: "ReferenceBlackWhite", + 0x1000: "RelatedImageFileFormat", + 0x1001: "RelatedImageWidth", + 0x1002: "RelatedImageLength", + 0x828d: "CFARepeatPatternDim", + 0x828e: "CFAPattern", + 0x828f: "BatteryLevel", + 0x8298: "Copyright", + 0x829a: "ExposureTime", + 0x829d: "FNumber", + 0x8769: "ExifOffset", + 0x8773: "InterColorProfile", + 0x8822: "ExposureProgram", + 0x8824: "SpectralSensitivity", + 0x8825: "GPSInfo", + 0x8827: "ISOSpeedRatings", + 0x8828: "OECF", + 0x8829: "Interlace", + 0x882a: "TimeZoneOffset", + 0x882b: "SelfTimerMode", + 0x9000: "ExifVersion", + 0x9003: "DateTimeOriginal", + 0x9004: "DateTimeDigitized", + 0x9101: "ComponentsConfiguration", + 0x9102: "CompressedBitsPerPixel", + 0x9201: "ShutterSpeedValue", + 0x9202: "ApertureValue", + 0x9203: "BrightnessValue", + 0x9204: "ExposureBiasValue", + 0x9205: "MaxApertureValue", + 0x9206: "SubjectDistance", + 0x9207: "MeteringMode", + 0x9208: "LightSource", + 0x9209: "Flash", + 0x920a: "FocalLength", + 0x920b: "FlashEnergy", + 0x920c: "SpatialFrequencyResponse", + 0x920d: "Noise", + 0x9211: "ImageNumber", + 0x9212: "SecurityClassification", + 0x9213: "ImageHistory", + 0x9214: "SubjectLocation", + 0x9215: "ExposureIndex", + 0x9216: "TIFF/EPStandardID", + 0x927c: "MakerNote", + 0x9286: "UserComment", + 0x9290: "SubsecTime", + 0x9291: "SubsecTimeOriginal", + 0x9292: "SubsecTimeDigitized", + 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", + +} + +## +# Maps EXIF GPS tags to tag names. + +GPSTAGS = { + 0: "GPSVersionID", + 1: "GPSLatitudeRef", + 2: "GPSLatitude", + 3: "GPSLongitudeRef", + 4: "GPSLongitude", + 5: "GPSAltitudeRef", + 6: "GPSAltitude", + 7: "GPSTimeStamp", + 8: "GPSSatellites", + 9: "GPSStatus", + 10: "GPSMeasureMode", + 11: "GPSDOP", + 12: "GPSSpeedRef", + 13: "GPSSpeed", + 14: "GPSTrackRef", + 15: "GPSTrack", + 16: "GPSImgDirectionRef", + 17: "GPSImgDirection", + 18: "GPSMapDatum", + 19: "GPSDestLatitudeRef", + 20: "GPSDestLatitude", + 21: "GPSDestLongitudeRef", + 22: "GPSDestLongitude", + 23: "GPSDestBearingRef", + 24: "GPSDestBearing", + 25: "GPSDestDistanceRef", + 26: "GPSDestDistance", + 27: "GPSProcessingMethod", + 28: "GPSAreaInformation", + 29: "GPSDateStamp", + 30: "GPSDifferential", + 31: "GPSHPositioningError", +} diff --git a/pyPackages/pillowarmv7l/PIL/FitsStubImagePlugin.py b/pyPackages/pillowarmv7l/PIL/FitsStubImagePlugin.py new file mode 100644 index 0000000..0b851ae --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/FitsStubImagePlugin.py @@ -0,0 +1,73 @@ +# +# The Python Imaging Library +# $Id$ +# +# FITS stub adapter +# +# Copyright (c) 1998-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image, ImageFile + +_handler = None + +## +# Install application-specific FITS image handler. +# +# @param handler Handler object. + +def register_handler(handler): + global _handler + _handler = handler + +# -------------------------------------------------------------------- +# Image adapter + +def _accept(prefix): + return prefix[:6] == b"SIMPLE" + +class FITSStubImageFile(ImageFile.StubImageFile): + + format = "FITS" + format_description = "FITS" + + def _open(self): + + offset = self.fp.tell() + + if not _accept(self.fp.read(6)): + raise SyntaxError("Not a FITS file") + + # FIXME: add more sanity checks here; mandatory header items + # include SIMPLE, BITPIX, NAXIS, etc. + + self.fp.seek(offset) + + # make something up + self.mode = "F" + self.size = 1, 1 + + loader = self._load() + if loader: + loader.open(self) + + def _load(self): + return _handler + + +def _save(im, fp, filename): + if _handler is None or not hasattr("_handler", "save"): + raise IOError("FITS save handler not installed") + _handler.save(im, fp, filename) + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(FITSStubImageFile.format, FITSStubImageFile, _accept) +Image.register_save(FITSStubImageFile.format, _save) + +Image.register_extension(FITSStubImageFile.format, ".fit") +Image.register_extension(FITSStubImageFile.format, ".fits") diff --git a/pyPackages/pillowarmv7l/PIL/FliImagePlugin.py b/pyPackages/pillowarmv7l/PIL/FliImagePlugin.py new file mode 100644 index 0000000..4ecaccd --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/FliImagePlugin.py @@ -0,0 +1,143 @@ +# +# The Python Imaging Library. +# $Id$ +# +# FLI/FLC file handling. +# +# History: +# 95-09-01 fl Created +# 97-01-03 fl Fixed parser, setup decoder tile +# 98-07-15 fl Renamed offset attribute to avoid name clash +# +# Copyright (c) Secret Labs AB 1997-98. +# Copyright (c) Fredrik Lundh 1995-97. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.2" + +from PIL import Image, ImageFile, ImagePalette, _binary + +i8 = _binary.i8 +i16 = _binary.i16le +i32 = _binary.i32le +o8 = _binary.o8 + + +# +# decoder + +def _accept(prefix): + return i16(prefix[4:6]) in [0xAF11, 0xAF12] + + +## +# Image plugin for the FLI/FLC animation format. Use the seek +# method to load individual frames. + +class FliImageFile(ImageFile.ImageFile): + + format = "FLI" + format_description = "Autodesk FLI/FLC Animation" + + def _open(self): + + # 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 + raise SyntaxError("not an FLI/FLC file") + + # image characteristics + self.mode = "P" + self.size = i16(s[8:10]), i16(s[10:12]) + + # animation speed + duration = i32(s[16:20]) + if magic == 0xAF11: + duration = (duration * 1000) / 70 + self.info["duration"] = duration + + # look for palette + palette = [(a, a, a) for a in range(256)] + + s = self.fp.read(16) + + self.__offset = 128 + + if i16(s[4:6]) == 0xF100: + # prefix chunk; ignore it + self.__offset = self.__offset + i32(s) + s = self.fp.read(16) + + if i16(s[4:6]) == 0xF1FA: + # look for palette chunk + s = self.fp.read(6) + if i16(s[4:6]) == 11: + self._palette(palette, 2) + elif i16(s[4:6]) == 4: + self._palette(palette, 0) + + 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 + self.frame = -1 + self.__fp = self.fp + + self.seek(0) + + def _palette(self, palette, shift): + # load palette + + i = 0 + for e in range(i16(self.fp.read(2))): + s = self.fp.read(2) + i = i + i8(s[0]) + n = i8(s[1]) + if n == 0: + n = 256 + 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 + palette[i] = (r, g, b) + i += 1 + + def seek(self, frame): + + if frame != self.frame + 1: + raise ValueError("cannot seek to frame %d" % frame) + self.frame = frame + + # move to next frame + self.fp = self.__fp + self.fp.seek(self.__offset) + + s = self.fp.read(4) + if not s: + raise EOFError + + framesize = i32(s) + + self.decodermaxblock = framesize + self.tile = [("fli", (0, 0)+self.size, self.__offset, None)] + + self.__offset = self.__offset + framesize + + def tell(self): + + return self.frame + +# +# registry + +Image.register_open("FLI", FliImageFile, _accept) + +Image.register_extension("FLI", ".fli") +Image.register_extension("FLI", ".flc") diff --git a/pyPackages/pillowarmv7l/PIL/FontFile.py b/pyPackages/pillowarmv7l/PIL/FontFile.py new file mode 100644 index 0000000..26ddff0 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/FontFile.py @@ -0,0 +1,119 @@ +# +# The Python Imaging Library +# $Id$ +# +# base class for raster font file parsers +# +# history: +# 1997-06-05 fl created +# 1997-08-19 fl restrict image width +# +# Copyright (c) 1997-1998 by Secret Labs AB +# Copyright (c) 1997-1998 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import os +from PIL import Image, _binary + +try: + import zlib +except ImportError: + zlib = None + +WIDTH = 800 + + +def puti16(fp, values): + # write network order (big-endian) 16-bit sequence + for v in values: + if v < 0: + v += 65536 + fp.write(_binary.o16be(v)) + + +## +# Base class for raster font file handlers. + +class FontFile: + + bitmap = None + + def __init__(self): + + self.info = {} + self.glyph = [None] * 256 + + def __getitem__(self, ix): + return self.glyph[ix] + + def compile(self): + "Create metrics and bitmap" + + if self.bitmap: + return + + # create bitmap large enough to hold all data + h = w = maxwidth = 0 + lines = 1 + for glyph in self: + if glyph: + d, dst, src, im = glyph + h = max(h, src[3] - src[1]) + w = w + (src[2] - src[0]) + if w > WIDTH: + lines += 1 + w = (src[2] - src[0]) + maxwidth = max(maxwidth, w) + + xsize = maxwidth + ysize = lines * h + + if xsize == 0 and ysize == 0: + return "" + + self.ysize = h + + # paste glyphs into bitmap + self.bitmap = Image.new("1", (xsize, ysize)) + self.metrics = [None] * 256 + x = y = 0 + for i in range(256): + glyph = self[i] + if glyph: + d, dst, src, im = glyph + xx, yy = src[2] - src[0], src[3] - src[1] + x0, y0 = x, y + x = x + xx + if x > WIDTH: + x, y = 0, y + h + x0, y0 = x, y + x = xx + s = src[0] + x0, src[1] + y0, src[2] + x0, src[3] + y0 + self.bitmap.paste(im.crop(src), s) + # print chr(i), dst, s + self.metrics[i] = d, dst, s + + def save(self, filename): + "Save font" + + self.compile() + + # font data + self.bitmap.save(os.path.splitext(filename)[0] + ".pbm", "PNG") + + # font metrics + fp = open(os.path.splitext(filename)[0] + ".pil", "wb") + fp.write(b"PILfont\n") + fp.write((";;;;;;%d;\n" % self.ysize).encode('ascii')) # HACK!!! + fp.write(b"DATA\n") + for id in range(256): + m = self.metrics[id] + if not m: + puti16(fp, [0] * 10) + else: + puti16(fp, m[0] + m[1] + m[2]) + fp.close() + +# End of file diff --git a/pyPackages/pillowarmv7l/PIL/FpxImagePlugin.py b/pyPackages/pillowarmv7l/PIL/FpxImagePlugin.py new file mode 100644 index 0000000..7e1d09d --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/FpxImagePlugin.py @@ -0,0 +1,227 @@ +# +# THIS IS WORK IN PROGRESS +# +# The Python Imaging Library. +# $Id$ +# +# FlashPix support for PIL +# +# History: +# 97-01-25 fl Created (reads uncompressed RGB images only) +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.1" + + +from PIL import Image, ImageFile +from PIL.OleFileIO import * + + +# we map from colour field tuples to (mode, rawmode) descriptors +MODES = { + # opacity + (0x00007ffe): ("A", "L"), + # monochrome + (0x00010000,): ("L", "L"), + (0x00018000, 0x00017ffe): ("RGBA", "LA"), + # photo YCC + (0x00020000, 0x00020001, 0x00020002): ("RGB", "YCC;P"), + (0x00028000, 0x00028001, 0x00028002, 0x00027ffe): ("RGBA", "YCCA;P"), + # standard RGB (NIFRGB) + (0x00030000, 0x00030001, 0x00030002): ("RGB", "RGB"), + (0x00038000, 0x00038001, 0x00038002, 0x00037ffe): ("RGBA", "RGBA"), +} + + +# +# -------------------------------------------------------------------- + +def _accept(prefix): + return prefix[:8] == MAGIC + + +## +# Image plugin for the FlashPix images. + +class FpxImageFile(ImageFile.ImageFile): + + format = "FPX" + format_description = "FlashPix" + + def _open(self): + # + # read the OLE directory and see if this is a likely + # to be a FlashPix file + + try: + self.ole = OleFileIO(self.fp) + except IOError: + raise SyntaxError("not an FPX file; invalid OLE file") + + if self.ole.root.clsid != "56616700-C154-11CE-8553-00AA00A1F95B": + raise SyntaxError("not an FPX file; bad root CLSID") + + self._open_index(1) + + def _open_index(self, index=1): + # + # get the Image Contents Property Set + + prop = self.ole.getproperties([ + "Data Object Store %06d" % index, + "\005Image Contents" + ]) + + # size (highest resolution) + + self.size = prop[0x1000002], prop[0x1000003] + + size = max(self.size) + i = 1 + while size > 64: + size = size / 2 + i += 1 + self.maxid = i - 1 + + # mode. instead of using a single field for this, flashpix + # requires you to specify the mode for each channel in each + # resolution subimage, and leaves it to the decoder to make + # sure that they all match. for now, we'll cheat and assume + # that this is always the case. + + id = self.maxid << 16 + + s = prop[0x2000002 | id] + + colors = [] + for i in range(i32(s, 4)): + # note: for now, we ignore the "uncalibrated" flag + colors.append(i32(s, 8+i*4) & 0x7fffffff) + + self.mode, self.rawmode = MODES[tuple(colors)] + + # load JPEG tables, if any + self.jpeg = {} + for i in range(256): + id = 0x3000001 | (i << 16) + if id in prop: + self.jpeg[i] = prop[id] + + # print len(self.jpeg), "tables loaded" + + self._open_subimage(1, self.maxid) + + def _open_subimage(self, index=1, subimage=0): + # + # setup tile descriptors for a given subimage + + stream = [ + "Data Object Store %06d" % index, + "Resolution %04d" % subimage, + "Subimage 0000 Header" + ] + + fp = self.ole.openstream(stream) + + # skip prefix + p = fp.read(28) + + # header stream + s = fp.read(36) + + size = i32(s, 4), i32(s, 8) + tilecount = i32(s, 12) + tilesize = i32(s, 16), i32(s, 20) + channels = i32(s, 24) + offset = i32(s, 28) + length = i32(s, 32) + + # print size, self.mode, self.rawmode + + if size != self.size: + raise IOError("subimage mismatch") + + # get tile descriptors + fp.seek(28 + offset) + s = fp.read(i32(s, 12) * length) + + x = y = 0 + xsize, ysize = size + xtile, ytile = tilesize + self.tile = [] + + for i in range(0, len(s), length): + + compression = i32(s, i+8) + + if compression == 0: + self.tile.append(("raw", (x, y, x+xtile, y+ytile), + i32(s, i) + 28, (self.rawmode))) + + elif compression == 1: + + # FIXME: the fill decoder is not implemented + self.tile.append(("fill", (x, y, x+xtile, y+ytile), + i32(s, i) + 28, (self.rawmode, s[12:16]))) + + elif compression == 2: + + internal_color_conversion = i8(s[14]) + jpeg_tables = i8(s[15]) + rawmode = self.rawmode + + if internal_color_conversion: + # The image is stored as usual (usually YCbCr). + if rawmode == "RGBA": + # For "RGBA", data is stored as YCbCrA based on + # negative RGB. The following trick works around + # this problem : + jpegmode, rawmode = "YCbCrK", "CMYK" + else: + jpegmode = None # let the decoder decide + + else: + # The image is stored as defined by rawmode + jpegmode = rawmode + + self.tile.append(("jpeg", (x, y, x+xtile, y+ytile), + i32(s, i) + 28, (rawmode, jpegmode))) + + # FIXME: jpeg tables are tile dependent; the prefix + # data must be placed in the tile descriptor itself! + + if jpeg_tables: + self.tile_prefix = self.jpeg[jpeg_tables] + + else: + raise IOError("unknown/invalid compression") + + x = x + xtile + if x >= xsize: + x, y = 0, y + ytile + if y >= ysize: + break # isn't really required + + self.stream = stream + self.fp = None + + def load(self): + + if not self.fp: + self.fp = self.ole.openstream(self.stream[:2] + + ["Subimage 0000 Data"]) + + ImageFile.ImageFile.load(self) + +# +# -------------------------------------------------------------------- + +Image.register_open("FPX", FpxImageFile, _accept) + +Image.register_extension("FPX", ".fpx") diff --git a/pyPackages/pillowarmv7l/PIL/GbrImagePlugin.py b/pyPackages/pillowarmv7l/PIL/GbrImagePlugin.py new file mode 100644 index 0000000..0e68632 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/GbrImagePlugin.py @@ -0,0 +1,71 @@ +# +# The Python Imaging Library +# $Id$ +# +# load a GIMP brush file +# +# History: +# 96-03-14 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image, ImageFile, _binary + +i32 = _binary.i32be + + +def _accept(prefix): + return i32(prefix) >= 20 and i32(prefix[4:8]) == 1 + + +## +# Image plugin for the GIMP brush format. + +class GbrImageFile(ImageFile.ImageFile): + + format = "GBR" + format_description = "GIMP brush file" + + def _open(self): + + header_size = i32(self.fp.read(4)) + version = i32(self.fp.read(4)) + if header_size < 20 or version != 1: + raise SyntaxError("not a GIMP brush") + + width = i32(self.fp.read(4)) + height = i32(self.fp.read(4)) + bytes = i32(self.fp.read(4)) + if width <= 0 or height <= 0 or bytes != 1: + raise SyntaxError("not a GIMP brush") + + comment = self.fp.read(header_size - 20)[:-1] + + self.mode = "L" + self.size = width, height + + self.info["comment"] = comment + + # Since the brush is so small, we read the data immediately + self.data = self.fp.read(width * height) + + def load(self): + + if not self.data: + return + + # create an image out of the brush data block + self.im = Image.core.new(self.mode, self.size) + self.im.frombytes(self.data) + self.data = b"" + +# +# registry + +Image.register_open("GBR", GbrImageFile, _accept) + +Image.register_extension("GBR", ".gbr") diff --git a/pyPackages/pillowarmv7l/PIL/GdImageFile.py b/pyPackages/pillowarmv7l/PIL/GdImageFile.py new file mode 100644 index 0000000..080153a --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/GdImageFile.py @@ -0,0 +1,92 @@ +# +# The Python Imaging Library. +# $Id$ +# +# GD file handling +# +# History: +# 1996-04-12 fl Created +# +# Copyright (c) 1997 by Secret Labs AB. +# Copyright (c) 1996 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + + +# NOTE: This format cannot be automatically recognized, so the +# class is not registered for use with Image.open(). To open a +# gd file, use the GdImageFile.open() function instead. + +# THE GD FORMAT IS NOT DESIGNED FOR DATA INTERCHANGE. This +# implementation is provided for convenience and demonstrational +# purposes only. + + +__version__ = "0.1" + +from PIL import ImageFile, ImagePalette, _binary +from PIL._util import isPath + +try: + import builtins +except ImportError: + import __builtin__ + builtins = __builtin__ + +i16 = _binary.i16be + + +## +# Image plugin for the GD uncompressed format. Note that this format +# is not supported by the standard Image.open function. To use +# this plugin, you have to import the GdImageFile module and +# use the GdImageFile.open function. + +class GdImageFile(ImageFile.ImageFile): + + format = "GD" + format_description = "GD uncompressed images" + + def _open(self): + + # Header + s = self.fp.read(775) + + self.mode = "L" # FIXME: "P" + self.size = i16(s[0:2]), i16(s[2:4]) + + # transparency index + tindex = i16(s[5:7]) + if tindex < 256: + self.info["transparent"] = tindex + + self.palette = ImagePalette.raw("RGB", s[7:]) + + self.tile = [("raw", (0, 0)+self.size, 775, ("L", 0, -1))] + + +## +# Load texture from a GD image file. +# +# @param filename GD file name, or an opened file handle. +# @param mode Optional mode. In this version, if the mode argument +# is given, it must be "r". +# @return An image instance. +# @exception IOError If the image could not be read. + +def open(fp, mode="r"): + + if mode != "r": + raise ValueError("bad mode") + + if isPath(fp): + filename = fp + fp = builtins.open(fp, "rb") + else: + filename = "" + + try: + return GdImageFile(fp, filename) + except SyntaxError: + raise IOError("cannot identify this image file") diff --git a/pyPackages/pillowarmv7l/PIL/GifImagePlugin.py b/pyPackages/pillowarmv7l/PIL/GifImagePlugin.py new file mode 100644 index 0000000..55aece3 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/GifImagePlugin.py @@ -0,0 +1,544 @@ +# +# The Python Imaging Library. +# $Id$ +# +# GIF file handling +# +# History: +# 1995-09-01 fl Created +# 1996-12-14 fl Added interlace support +# 1996-12-30 fl Added animation support +# 1997-01-05 fl Added write support, fixed local colour map bug +# 1997-02-23 fl Make sure to load raster data in getdata() +# 1997-07-05 fl Support external decoder (0.4) +# 1998-07-09 fl Handle all modes when saving (0.5) +# 1998-07-15 fl Renamed offset attribute to avoid name clash +# 2001-04-16 fl Added rewind support (seek to frame 0) (0.6) +# 2001-04-17 fl Added palette optimization (0.7) +# 2002-06-06 fl Added transparency support for save (0.8) +# 2004-02-24 fl Disable interlacing for small images +# +# Copyright (c) 1997-2004 by Secret Labs AB +# Copyright (c) 1995-2004 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.9" + + +from PIL import Image, ImageFile, ImagePalette, _binary + + +# -------------------------------------------------------------------- +# Helpers + +i8 = _binary.i8 +i16 = _binary.i16le +o8 = _binary.o8 +o16 = _binary.o16le + + +# -------------------------------------------------------------------- +# Identify/read GIF files + +def _accept(prefix): + return prefix[:6] in [b"GIF87a", b"GIF89a"] + + +## +# Image plugin for GIF images. This plugin supports both GIF87 and +# GIF89 images. + +class GifImageFile(ImageFile.ImageFile): + + format = "GIF" + format_description = "Compuserve GIF" + global_palette = None + + def data(self): + s = self.fp.read(1) + if s and i8(s): + return self.fp.read(i8(s)) + return None + + def _open(self): + + # Screen + s = self.fp.read(13) + if s[:6] not in [b"GIF87a", b"GIF89a"]: + raise SyntaxError("not a GIF file") + + self.info["version"] = s[:6] + self.size = i16(s[6:]), i16(s[8:]) + self.tile = [] + flags = i8(s[10]) + bits = (flags & 7) + 1 + + if flags & 128: + # get global palette + self.info["background"] = i8(s[11]) + # check if palette contains colour indices + p = self.fp.read(3 << bits) + for i in range(0, len(p), 3): + if not (i//3 == i8(p[i]) == i8(p[i+1]) == i8(p[i+2])): + p = ImagePalette.raw("RGB", p) + self.global_palette = self.palette = p + break + + self.__fp = self.fp # FIXME: hack + self.__rewind = self.fp.tell() + self.seek(0) # get ready to read first frame + + def seek(self, frame): + + if frame == 0: + # rewind + self.__offset = 0 + self.dispose = None + self.dispose_extent = [0, 0, 0, 0] # x0, y0, x1, y1 + self.__frame = -1 + self.__fp.seek(self.__rewind) + self._prev_im = None + self.disposal_method = 0 + else: + # ensure that the previous frame was loaded + if not self.im: + self.load() + + if frame != self.__frame + 1: + raise ValueError("cannot seek to frame %d" % frame) + self.__frame = frame + + self.tile = [] + + self.fp = self.__fp + if self.__offset: + # backup to last frame + self.fp.seek(self.__offset) + while self.data(): + pass + self.__offset = 0 + + if self.dispose: + self.im.paste(self.dispose, self.dispose_extent) + + from copy import copy + self.palette = copy(self.global_palette) + + while True: + + s = self.fp.read(1) + if not s or s == b";": + break + + elif s == b"!": + # + # extensions + # + s = self.fp.read(1) + block = self.data() + if i8(s) == 249: + # + # graphic control extension + # + flags = i8(block[0]) + if flags & 1: + self.info["transparency"] = i8(block[3]) + self.info["duration"] = i16(block[1:3]) * 10 + + # disposal method - find the value of bits 4 - 6 + dispose_bits = 0b00011100 & flags + dispose_bits = dispose_bits >> 2 + if dispose_bits: + # only set the dispose if it is not + # unspecified. I'm not sure if this is + # correct, but it seems to prevent the last + # frame from looking odd for some animations + self.disposal_method = dispose_bits + elif i8(s) == 255: + # + # application extension + # + self.info["extension"] = block, self.fp.tell() + if block[:11] == b"NETSCAPE2.0": + block = self.data() + if len(block) >= 3 and i8(block[0]) == 1: + self.info["loop"] = i16(block[1:3]) + while self.data(): + pass + + elif s == b",": + # + # local image + # + s = self.fp.read(9) + + # extent + x0, y0 = i16(s[0:]), i16(s[2:]) + x1, y1 = x0 + i16(s[4:]), y0 + i16(s[6:]) + self.dispose_extent = x0, y0, x1, y1 + flags = i8(s[8]) + + interlace = (flags & 64) != 0 + + if flags & 128: + bits = (flags & 7) + 1 + self.palette =\ + ImagePalette.raw("RGB", self.fp.read(3 << bits)) + + # image data + bits = i8(self.fp.read(1)) + self.__offset = self.fp.tell() + self.tile = [("gif", + (x0, y0, x1, y1), + self.__offset, + (bits, interlace))] + break + + else: + pass + # raise IOError, "illegal GIF tag `%x`" % i8(s) + + try: + if self.disposal_method < 2: + # do not dispose or none specified + self.dispose = None + elif self.disposal_method == 2: + # replace with background colour + self.dispose = Image.core.fill("P", self.size, + self.info["background"]) + else: + # replace with previous contents + if self.im: + self.dispose = self.im.copy() + + # only dispose the extent in this frame + if self.dispose: + self.dispose = self.dispose.crop(self.dispose_extent) + except (AttributeError, KeyError): + pass + + if not self.tile: + # self.__fp = None + raise EOFError("no more images in GIF file") + + self.mode = "L" + if self.palette: + self.mode = "P" + + def tell(self): + return self.__frame + + def load_end(self): + ImageFile.ImageFile.load_end(self) + + # if the disposal method is 'do not dispose', transparent + # pixels should show the content of the previous frame + if self._prev_im and self.disposal_method == 1: + # we do this by pasting the updated area onto the previous + # frame which we then use as the current image content + updated = self.im.crop(self.dispose_extent) + self._prev_im.paste(updated, self.dispose_extent, + updated.convert('RGBA')) + self.im = self._prev_im + self._prev_im = self.im.copy() + +# -------------------------------------------------------------------- +# Write GIF files + +try: + import _imaging_gif +except ImportError: + _imaging_gif = None + +RAWMODE = { + "1": "L", + "L": "L", + "P": "P", +} + + +def _save(im, fp, filename): + + if _imaging_gif: + # call external driver + try: + _imaging_gif.save(im, fp, filename) + return + except IOError: + pass # write uncompressed file + + if im.mode in RAWMODE: + imOut = im + else: + # convert on the fly (EXPERIMENTAL -- I'm not sure PIL + # should automatically convert images on save...) + if Image.getmodebase(im.mode) == "RGB": + palette_size = 256 + if im.palette: + palette_size = len(im.palette.getdata()[1]) // 3 + imOut = im.convert("P", palette=1, colors=palette_size) + else: + imOut = im.convert("L") + + # header + try: + palette = im.encoderinfo["palette"] + except KeyError: + palette = None + im.encoderinfo["optimize"] = im.encoderinfo.get("optimize", True) + + header, usedPaletteColors = getheader(imOut, palette, im.encoderinfo) + for s in header: + fp.write(s) + + flags = 0 + + try: + interlace = im.encoderinfo["interlace"] + except KeyError: + interlace = 1 + + # workaround for @PIL153 + if min(im.size) < 16: + interlace = 0 + + if interlace: + flags = flags | 64 + + try: + transparency = im.encoderinfo["transparency"] + except KeyError: + pass + else: + transparency = int(transparency) + # optimize the block away if transparent color is not used + transparentColorExists = True + # adjust the transparency index after optimize + if usedPaletteColors is not None and len(usedPaletteColors) < 256: + for i in range(len(usedPaletteColors)): + if usedPaletteColors[i] == transparency: + transparency = i + transparentColorExists = True + break + else: + transparentColorExists = False + + # transparency extension block + if transparentColorExists: + fp.write(b"!" + + o8(249) + # extension intro + o8(4) + # length + o8(1) + # transparency info present + o16(0) + # duration + o8(transparency) # transparency index + + o8(0)) + + # local image header + fp.write(b"," + + o16(0) + o16(0) + # bounding box + o16(im.size[0]) + # size + o16(im.size[1]) + + o8(flags) + # flags + o8(8)) # bits + + imOut.encoderconfig = (8, interlace) + ImageFile._save(imOut, fp, [("gif", (0, 0)+im.size, 0, + RAWMODE[imOut.mode])]) + + fp.write(b"\0") # end of image data + + fp.write(b";") # end of file + + try: + fp.flush() + except: + pass + + +def _save_netpbm(im, fp, filename): + + # + # If you need real GIF compression and/or RGB quantization, you + # can use the external NETPBM/PBMPLUS utilities. See comments + # below for information on how to enable this. + + import os + from subprocess import Popen, check_call, PIPE, CalledProcessError + import tempfile + file = im._dump() + + if im.mode != "RGB": + with open(filename, 'wb') as f: + stderr = tempfile.TemporaryFile() + check_call(["ppmtogif", file], stdout=f, stderr=stderr) + else: + with open(filename, 'wb') as f: + + # Pipe ppmquant output into ppmtogif + # "ppmquant 256 %s | ppmtogif > %s" % (file, filename) + quant_cmd = ["ppmquant", "256", file] + togif_cmd = ["ppmtogif"] + stderr = tempfile.TemporaryFile() + quant_proc = Popen(quant_cmd, stdout=PIPE, stderr=stderr) + stderr = tempfile.TemporaryFile() + togif_proc = Popen(togif_cmd, stdin=quant_proc.stdout, stdout=f, + stderr=stderr) + + # Allow ppmquant to receive SIGPIPE if ppmtogif exits + quant_proc.stdout.close() + + retcode = quant_proc.wait() + if retcode: + raise CalledProcessError(retcode, quant_cmd) + + retcode = togif_proc.wait() + if retcode: + raise CalledProcessError(retcode, togif_cmd) + + try: + os.unlink(file) + except: + pass + + +# -------------------------------------------------------------------- +# GIF utilities + +def getheader(im, palette=None, info=None): + """Return a list of strings representing a GIF header""" + + optimize = info and info.get("optimize", 0) + + # Header Block + # http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp + header = [ + b"GIF87a" + # signature + version + o16(im.size[0]) + # canvas width + o16(im.size[1]) # canvas height + ] + + if im.mode == "P": + if palette and isinstance(palette, bytes): + sourcePalette = palette[:768] + else: + sourcePalette = im.im.getpalette("RGB")[:768] + else: # L-mode + if palette and isinstance(palette, bytes): + sourcePalette = palette[:768] + else: + sourcePalette = bytearray([i//3 for i in range(768)]) + + usedPaletteColors = paletteBytes = None + + if im.mode in ("P", "L") and optimize: + usedPaletteColors = [] + + # check which colors are used + i = 0 + for count in im.histogram(): + if count: + usedPaletteColors.append(i) + i += 1 + + # create the new palette if not every color is used + if len(usedPaletteColors) < 256: + paletteBytes = b"" + newPositions = {} + + i = 0 + # pick only the used colors from the palette + for oldPosition in usedPaletteColors: + paletteBytes += sourcePalette[oldPosition*3:oldPosition*3+3] + newPositions[oldPosition] = i + i += 1 + + # replace the palette color id of all pixel with the new id + imageBytes = bytearray(im.tobytes()) + for i in range(len(imageBytes)): + imageBytes[i] = newPositions[imageBytes[i]] + im.frombytes(bytes(imageBytes)) + newPaletteBytes = (paletteBytes + + (768 - len(paletteBytes)) * b'\x00') + im.putpalette(newPaletteBytes) + im.palette = ImagePalette.ImagePalette("RGB", palette=paletteBytes, + size=len(paletteBytes)) + + if not paletteBytes: + paletteBytes = sourcePalette + + # Logical Screen Descriptor + # calculate the palette size for the header + import math + colorTableSize = int(math.ceil(math.log(len(paletteBytes)//3, 2)))-1 + if colorTableSize < 0: + colorTableSize = 0 + # size of global color table + global color table flag + header.append(o8(colorTableSize + 128)) + # background + reserved/aspect + header.append(o8(0) + o8(0)) + # end of Logical Screen Descriptor + + # add the missing amount of bytes + # the palette has to be 2< 0: + paletteBytes += o8(0) * 3 * actualTargetSizeDiff + + # Header + Logical Screen Descriptor + Global Color Table + header.append(paletteBytes) + return header, usedPaletteColors + + +def getdata(im, offset=(0, 0), **params): + """Return a list of strings representing this image. + The first string is a local image header, the rest contains + encoded image data.""" + + class collector: + data = [] + + def write(self, data): + self.data.append(data) + + im.load() # make sure raster data is available + + fp = collector() + + try: + im.encoderinfo = params + + # local image header + fp.write(b"," + + o16(offset[0]) + # offset + o16(offset[1]) + + o16(im.size[0]) + # size + o16(im.size[1]) + + o8(0) + # flags + o8(8)) # bits + + ImageFile._save(im, fp, [("gif", (0, 0)+im.size, 0, RAWMODE[im.mode])]) + + fp.write(b"\0") # end of image data + + finally: + del im.encoderinfo + + return fp.data + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(GifImageFile.format, GifImageFile, _accept) +Image.register_save(GifImageFile.format, _save) +Image.register_extension(GifImageFile.format, ".gif") +Image.register_mime(GifImageFile.format, "image/gif") + +# +# Uncomment the following line if you wish to use NETPBM/PBMPLUS +# instead of the built-in "uncompressed" GIF encoder + +# Image.register_save(GifImageFile.format, _save_netpbm) diff --git a/pyPackages/pillowarmv7l/PIL/GimpGradientFile.py b/pyPackages/pillowarmv7l/PIL/GimpGradientFile.py new file mode 100644 index 0000000..696f425 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/GimpGradientFile.py @@ -0,0 +1,137 @@ +# +# Python Imaging Library +# $Id$ +# +# stuff to read (and render) GIMP gradient files +# +# History: +# 97-08-23 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + +from math import pi, log, sin, sqrt +from PIL._binary import o8 + +# -------------------------------------------------------------------- +# Stuff to translate curve segments to palette values (derived from +# the corresponding code in GIMP, written by Federico Mena Quintero. +# See the GIMP distribution for more information.) +# + +EPSILON = 1e-10 + + +def linear(middle, pos): + if pos <= middle: + if middle < EPSILON: + return 0.0 + else: + return 0.5 * pos / middle + else: + pos = pos - middle + middle = 1.0 - middle + if middle < EPSILON: + return 1.0 + else: + return 0.5 + 0.5 * pos / middle + + +def curved(middle, pos): + return pos ** (log(0.5) / log(max(middle, EPSILON))) + + +def sine(middle, pos): + return (sin((-pi / 2.0) + pi * linear(middle, pos)) + 1.0) / 2.0 + + +def sphere_increasing(middle, pos): + return sqrt(1.0 - (linear(middle, pos) - 1.0) ** 2) + + +def sphere_decreasing(middle, pos): + return 1.0 - sqrt(1.0 - linear(middle, pos) ** 2) + +SEGMENTS = [linear, curved, sine, sphere_increasing, sphere_decreasing] + + +class GradientFile: + + gradient = None + + def getpalette(self, entries=256): + + palette = [] + + ix = 0 + x0, x1, xm, rgb0, rgb1, segment = self.gradient[ix] + + for i in range(entries): + + x = i / float(entries-1) + + while x1 < x: + ix += 1 + x0, x1, xm, rgb0, rgb1, segment = self.gradient[ix] + + w = x1 - x0 + + if w < EPSILON: + scale = segment(0.5, 0.5) + else: + scale = segment((xm - x0) / w, (x - x0) / w) + + # expand to RGBA + r = o8(int(255 * ((rgb1[0] - rgb0[0]) * scale + rgb0[0]) + 0.5)) + g = o8(int(255 * ((rgb1[1] - rgb0[1]) * scale + rgb0[1]) + 0.5)) + b = o8(int(255 * ((rgb1[2] - rgb0[2]) * scale + rgb0[2]) + 0.5)) + a = o8(int(255 * ((rgb1[3] - rgb0[3]) * scale + rgb0[3]) + 0.5)) + + # add to palette + palette.append(r + g + b + a) + + return b"".join(palette), "RGBA" + + +## +# File handler for GIMP's gradient format. + +class GimpGradientFile(GradientFile): + + def __init__(self, fp): + + if fp.readline()[:13] != b"GIMP Gradient": + raise SyntaxError("not a GIMP gradient file") + + line = fp.readline() + + # GIMP 1.2 gradient files don't contain a name, but GIMP 1.3 files do + if line.startswith(b"Name: "): + line = fp.readline().strip() + + count = int(line) + + gradient = [] + + for i in range(count): + + s = fp.readline().split() + w = [float(x) for x in s[:11]] + + x0, x1 = w[0], w[2] + xm = w[1] + rgb0 = w[3:7] + rgb1 = w[7:11] + + segment = SEGMENTS[int(s[11])] + cspace = int(s[12]) + + if cspace != 0: + raise IOError("cannot handle HSV colour space") + + gradient.append((x0, x1, xm, rgb0, rgb1, segment)) + + self.gradient = gradient diff --git a/pyPackages/pillowarmv7l/PIL/GimpPaletteFile.py b/pyPackages/pillowarmv7l/PIL/GimpPaletteFile.py new file mode 100644 index 0000000..a066c80 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/GimpPaletteFile.py @@ -0,0 +1,62 @@ +# +# Python Imaging Library +# $Id$ +# +# stuff to read GIMP palette files +# +# History: +# 1997-08-23 fl Created +# 2004-09-07 fl Support GIMP 2.0 palette files. +# +# Copyright (c) Secret Labs AB 1997-2004. All rights reserved. +# Copyright (c) Fredrik Lundh 1997-2004. +# +# See the README file for information on usage and redistribution. +# + +import re +from PIL._binary import o8 + + +## +# File handler for GIMP's palette format. + +class GimpPaletteFile: + + rawmode = "RGB" + + def __init__(self, fp): + + self.palette = [o8(i)*3 for i in range(256)] + + if fp.readline()[:12] != b"GIMP Palette": + raise SyntaxError("not a GIMP palette file") + + i = 0 + + while i <= 255: + + s = fp.readline() + + if not s: + break + # skip fields and comment lines + if re.match(b"\w+:|#", s): + continue + if len(s) > 100: + raise SyntaxError("bad palette file") + + v = tuple(map(int, s.split()[:3])) + if len(v) != 3: + raise ValueError("bad palette entry") + + if 0 <= i <= 255: + self.palette[i] = o8(v[0]) + o8(v[1]) + o8(v[2]) + + i += 1 + + self.palette = b"".join(self.palette) + + def getpalette(self): + + return self.palette, self.rawmode diff --git a/pyPackages/pillowarmv7l/PIL/GribStubImagePlugin.py b/pyPackages/pillowarmv7l/PIL/GribStubImagePlugin.py new file mode 100644 index 0000000..8ffad81 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/GribStubImagePlugin.py @@ -0,0 +1,72 @@ +# +# The Python Imaging Library +# $Id$ +# +# GRIB stub adapter +# +# Copyright (c) 1996-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image, ImageFile + +_handler = None + + +## +# Install application-specific GRIB image handler. +# +# @param handler Handler object. + +def register_handler(handler): + global _handler + _handler = handler + + +# -------------------------------------------------------------------- +# Image adapter + +def _accept(prefix): + return prefix[0:4] == b"GRIB" and prefix[7] == b'\x01' + + +class GribStubImageFile(ImageFile.StubImageFile): + + format = "GRIB" + format_description = "GRIB" + + def _open(self): + + offset = self.fp.tell() + + if not _accept(self.fp.read(8)): + raise SyntaxError("Not a GRIB file") + + self.fp.seek(offset) + + # make something up + self.mode = "F" + self.size = 1, 1 + + loader = self._load() + if loader: + loader.open(self) + + def _load(self): + return _handler + + +def _save(im, fp, filename): + if _handler is None or not hasattr("_handler", "save"): + raise IOError("GRIB save handler not installed") + _handler.save(im, fp, filename) + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(GribStubImageFile.format, GribStubImageFile, _accept) +Image.register_save(GribStubImageFile.format, _save) + +Image.register_extension(GribStubImageFile.format, ".grib") diff --git a/pyPackages/pillowarmv7l/PIL/Hdf5StubImagePlugin.py b/pyPackages/pillowarmv7l/PIL/Hdf5StubImagePlugin.py new file mode 100644 index 0000000..f7945be --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/Hdf5StubImagePlugin.py @@ -0,0 +1,73 @@ +# +# The Python Imaging Library +# $Id$ +# +# HDF5 stub adapter +# +# Copyright (c) 2000-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image, ImageFile + +_handler = None + + +## +# Install application-specific HDF5 image handler. +# +# @param handler Handler object. + +def register_handler(handler): + global _handler + _handler = handler + + +# -------------------------------------------------------------------- +# Image adapter + +def _accept(prefix): + return prefix[:8] == b"\x89HDF\r\n\x1a\n" + + +class HDF5StubImageFile(ImageFile.StubImageFile): + + format = "HDF5" + format_description = "HDF5" + + def _open(self): + + offset = self.fp.tell() + + if not _accept(self.fp.read(8)): + raise SyntaxError("Not an HDF file") + + self.fp.seek(offset) + + # make something up + self.mode = "F" + self.size = 1, 1 + + loader = self._load() + if loader: + loader.open(self) + + def _load(self): + return _handler + + +def _save(im, fp, filename): + if _handler is None or not hasattr("_handler", "save"): + raise IOError("HDF5 save handler not installed") + _handler.save(im, fp, filename) + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(HDF5StubImageFile.format, HDF5StubImageFile, _accept) +Image.register_save(HDF5StubImageFile.format, _save) + +Image.register_extension(HDF5StubImageFile.format, ".h5") +Image.register_extension(HDF5StubImageFile.format, ".hdf") diff --git a/pyPackages/pillowarmv7l/PIL/IcnsImagePlugin.py b/pyPackages/pillowarmv7l/PIL/IcnsImagePlugin.py new file mode 100644 index 0000000..e88b849 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/IcnsImagePlugin.py @@ -0,0 +1,311 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Mac OS X icns file decoder, based on icns.py by Bob Ippolito. +# +# history: +# 2004-10-09 fl Turned into a PIL plugin; removed 2.3 dependencies. +# +# Copyright (c) 2004 by Bob Ippolito. +# Copyright (c) 2004 by Secret Labs. +# Copyright (c) 2004 by Fredrik Lundh. +# Copyright (c) 2014 by Alastair Houghton. +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image, ImageFile, PngImagePlugin, _binary +import io +import struct + +enable_jpeg2k = hasattr(Image.core, 'jp2klib_version') +if enable_jpeg2k: + from PIL import Jpeg2KImagePlugin + +i8 = _binary.i8 + +HEADERSIZE = 8 + + +def nextheader(fobj): + return struct.unpack('>4sI', fobj.read(HEADERSIZE)) + + +def read_32t(fobj, start_length, size): + # The 128x128 icon seems to have an extra header for some reason. + (start, length) = start_length + fobj.seek(start) + sig = fobj.read(4) + if sig != b'\x00\x00\x00\x00': + raise SyntaxError('Unknown signature, expecting 0x00000000') + return read_32(fobj, (start + 4, length - 4), size) + + +def read_32(fobj, start_length, size): + """ + Read a 32bit RGB icon resource. Seems to be either uncompressed or + an RLE packbits-like scheme. + """ + (start, length) = start_length + fobj.seek(start) + pixel_size = (size[0] * size[2], size[1] * size[2]) + sizesq = pixel_size[0] * pixel_size[1] + if length == sizesq * 3: + # uncompressed ("RGBRGBGB") + indata = fobj.read(length) + im = Image.frombuffer("RGB", pixel_size, indata, "raw", "RGB", 0, 1) + else: + # decode image + im = Image.new("RGB", pixel_size, None) + for band_ix in range(3): + data = [] + bytesleft = sizesq + while bytesleft > 0: + byte = fobj.read(1) + if not byte: + break + byte = i8(byte) + if byte & 0x80: + blocksize = byte - 125 + byte = fobj.read(1) + for i in range(blocksize): + data.append(byte) + else: + blocksize = byte + 1 + data.append(fobj.read(blocksize)) + bytesleft -= blocksize + if bytesleft <= 0: + break + if bytesleft != 0: + raise SyntaxError( + "Error reading channel [%r left]" % bytesleft + ) + band = Image.frombuffer( + "L", pixel_size, b"".join(data), "raw", "L", 0, 1 + ) + im.im.putband(band.im, band_ix) + return {"RGB": im} + + +def read_mk(fobj, start_length, size): + # Alpha masks seem to be uncompressed + (start, length) = start_length + fobj.seek(start) + pixel_size = (size[0] * size[2], size[1] * size[2]) + sizesq = pixel_size[0] * pixel_size[1] + band = Image.frombuffer( + "L", pixel_size, fobj.read(sizesq), "raw", "L", 0, 1 + ) + return {"A": band} + + +def read_png_or_jpeg2000(fobj, start_length, size): + (start, length) = start_length + fobj.seek(start) + sig = fobj.read(12) + if sig[:8] == b'\x89PNG\x0d\x0a\x1a\x0a': + fobj.seek(start) + im = PngImagePlugin.PngImageFile(fobj) + return {"RGBA": im} + elif sig[:4] == b'\xff\x4f\xff\x51' \ + or sig[:4] == b'\x0d\x0a\x87\x0a' \ + or sig == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a': + if not enable_jpeg2k: + raise ValueError('Unsupported icon subimage format (rebuild PIL ' + 'with JPEG 2000 support to fix this)') + # j2k, jpc or j2c + fobj.seek(start) + jp2kstream = fobj.read(length) + f = io.BytesIO(jp2kstream) + im = Jpeg2KImagePlugin.Jpeg2KImageFile(f) + if im.mode != 'RGBA': + im = im.convert('RGBA') + return {"RGBA": im} + else: + raise ValueError('Unsupported icon subimage format') + + +class IcnsFile: + + SIZES = { + (512, 512, 2): [ + (b'ic10', read_png_or_jpeg2000), + ], + (512, 512, 1): [ + (b'ic09', read_png_or_jpeg2000), + ], + (256, 256, 2): [ + (b'ic14', read_png_or_jpeg2000), + ], + (256, 256, 1): [ + (b'ic08', read_png_or_jpeg2000), + ], + (128, 128, 2): [ + (b'ic13', read_png_or_jpeg2000), + ], + (128, 128, 1): [ + (b'ic07', read_png_or_jpeg2000), + (b'it32', read_32t), + (b't8mk', read_mk), + ], + (64, 64, 1): [ + (b'icp6', read_png_or_jpeg2000), + ], + (32, 32, 2): [ + (b'ic12', read_png_or_jpeg2000), + ], + (48, 48, 1): [ + (b'ih32', read_32), + (b'h8mk', read_mk), + ], + (32, 32, 1): [ + (b'icp5', read_png_or_jpeg2000), + (b'il32', read_32), + (b'l8mk', read_mk), + ], + (16, 16, 2): [ + (b'ic11', read_png_or_jpeg2000), + ], + (16, 16, 1): [ + (b'icp4', read_png_or_jpeg2000), + (b'is32', read_32), + (b's8mk', read_mk), + ], + } + + def __init__(self, fobj): + """ + fobj is a file-like object as an icns resource + """ + # signature : (start, length) + self.dct = dct = {} + self.fobj = fobj + sig, filesize = nextheader(fobj) + if sig != b'icns': + raise SyntaxError('not an icns file') + i = HEADERSIZE + while i < filesize: + sig, blocksize = nextheader(fobj) + if blocksize <= 0: + raise SyntaxError('invalid block header') + i += HEADERSIZE + blocksize -= HEADERSIZE + dct[sig] = (i, blocksize) + fobj.seek(blocksize, 1) + i += blocksize + + def itersizes(self): + sizes = [] + for size, fmts in self.SIZES.items(): + for (fmt, reader) in fmts: + if fmt in self.dct: + sizes.append(size) + break + return sizes + + def bestsize(self): + sizes = self.itersizes() + if not sizes: + raise SyntaxError("No 32bit icon resources found") + return max(sizes) + + def dataforsize(self, size): + """ + Get an icon resource as {channel: array}. Note that + the arrays are bottom-up like windows bitmaps and will likely + need to be flipped or transposed in some way. + """ + dct = {} + for code, reader in self.SIZES[size]: + desc = self.dct.get(code) + if desc is not None: + dct.update(reader(self.fobj, desc, size)) + return dct + + def getimage(self, size=None): + if size is None: + size = self.bestsize() + if len(size) == 2: + size = (size[0], size[1], 1) + channels = self.dataforsize(size) + + im = channels.get('RGBA', None) + if im: + return im + + im = channels.get("RGB").copy() + try: + im.putalpha(channels["A"]) + except KeyError: + pass + return im + + +## +# Image plugin for Mac OS icons. + +class IcnsImageFile(ImageFile.ImageFile): + """ + PIL read-only image support for Mac OS .icns files. + Chooses the best resolution, but will possibly load + a different size image if you mutate the size attribute + before calling 'load'. + + The info dictionary has a key 'sizes' that is a list + of sizes that the icns file has. + """ + + format = "ICNS" + format_description = "Mac OS icns resource" + + def _open(self): + self.icns = IcnsFile(self.fp) + self.mode = 'RGBA' + self.best_size = self.icns.bestsize() + self.size = (self.best_size[0] * self.best_size[2], + self.best_size[1] * self.best_size[2]) + self.info['sizes'] = self.icns.itersizes() + # Just use this to see if it's loaded or not yet. + self.tile = ('',) + + def load(self): + if len(self.size) == 3: + self.best_size = self.size + self.size = (self.best_size[0] * self.best_size[2], + self.best_size[1] * self.best_size[2]) + + Image.Image.load(self) + if not self.tile: + return + self.load_prepare() + # This is likely NOT the best way to do it, but whatever. + im = self.icns.getimage(self.best_size) + + # If this is a PNG or JPEG 2000, it won't be loaded yet + im.load() + + self.im = im.im + self.mode = im.mode + self.size = im.size + self.fp = None + self.icns = None + self.tile = () + self.load_end() + +Image.register_open("ICNS", IcnsImageFile, lambda x: x[:4] == b'icns') +Image.register_extension("ICNS", '.icns') + +if __name__ == '__main__': + import os + import sys + imf = IcnsImageFile(open(sys.argv[1], 'rb')) + for size in imf.info['sizes']: + imf.size = size + imf.load() + im = imf.im + im.save('out-%s-%s-%s.png' % size) + im = Image.open(open(sys.argv[1], "rb")) + im.save("out.png") + if sys.platform == 'windows': + os.startfile("out.png") diff --git a/pyPackages/pillowarmv7l/PIL/IcoImagePlugin.py b/pyPackages/pillowarmv7l/PIL/IcoImagePlugin.py new file mode 100644 index 0000000..db8cec0 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/IcoImagePlugin.py @@ -0,0 +1,284 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Windows Icon support for PIL +# +# History: +# 96-05-27 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + +# This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis +# . +# https://code.google.com/p/casadebender/wiki/Win32IconImagePlugin +# +# Icon format references: +# * http://en.wikipedia.org/wiki/ICO_(file_format) +# * http://msdn.microsoft.com/en-us/library/ms997538.aspx + + +__version__ = "0.1" + +import struct +from io import BytesIO + +from PIL import Image, ImageFile, BmpImagePlugin, PngImagePlugin, _binary +from math import log, ceil + +# +# -------------------------------------------------------------------- + +i8 = _binary.i8 +i16 = _binary.i16le +i32 = _binary.i32le + +_MAGIC = b"\0\0\1\0" + + +def _save(im, fp, filename): + fp.write(_MAGIC) # (2+2) + sizes = im.encoderinfo.get("sizes", + [(16, 16), (24, 24), (32, 32), (48, 48), + (64, 64), (128, 128), (255, 255)]) + width, height = im.size + filter(lambda x: False if (x[0] > width or x[1] > height or + x[0] > 255 or x[1] > 255) else True, sizes) + sizes = sorted(sizes, key=lambda x: x[0]) + fp.write(struct.pack("H", len(sizes))) # idCount(2) + offset = fp.tell() + len(sizes)*16 + for size in sizes: + width, height = size + fp.write(struct.pack("B", width)) # bWidth(1) + fp.write(struct.pack("B", height)) # bHeight(1) + fp.write(b"\0") # bColorCount(1) + fp.write(b"\0") # bReserved(1) + fp.write(b"\0\0") # wPlanes(2) + fp.write(struct.pack("H", 32)) # wBitCount(2) + + image_io = BytesIO() + tmp = im.copy() + tmp.thumbnail(size, Image.LANCZOS) + tmp.save(image_io, "png") + image_io.seek(0) + image_bytes = image_io.read() + bytes_len = len(image_bytes) + fp.write(struct.pack("I", bytes_len)) # dwBytesInRes(4) + fp.write(struct.pack("I", offset)) # dwImageOffset(4) + current = fp.tell() + fp.seek(offset) + fp.write(image_bytes) + offset = offset + bytes_len + fp.seek(current) + + +def _accept(prefix): + return prefix[:4] == _MAGIC + + +class IcoFile: + def __init__(self, buf): + """ + Parse image from file-like object containing ico file data + """ + + # check magic + s = buf.read(6) + if not _accept(s): + raise SyntaxError("not an ICO file") + + self.buf = buf + self.entry = [] + + # Number of items in file + self.nb_items = i16(s[4:]) + + # Get headers for each item + for i in range(self.nb_items): + s = buf.read(16) + + icon_header = { + 'width': i8(s[0]), + 'height': i8(s[1]), + 'nb_color': i8(s[2]), # No. of colors in image (0 if >=8bpp) + 'reserved': i8(s[3]), + 'planes': i16(s[4:]), + 'bpp': i16(s[6:]), + 'size': i32(s[8:]), + 'offset': i32(s[12:]) + } + + # See Wikipedia + for j in ('width', 'height'): + if not icon_header[j]: + icon_header[j] = 256 + + # See Wikipedia notes about color depth. + # We need this just to differ images with equal sizes + icon_header['color_depth'] = (icon_header['bpp'] or + (icon_header['nb_color'] != 0 and + ceil(log(icon_header['nb_color'], + 2))) or 256) + + icon_header['dim'] = (icon_header['width'], icon_header['height']) + icon_header['square'] = (icon_header['width'] * + icon_header['height']) + + self.entry.append(icon_header) + + self.entry = sorted(self.entry, key=lambda x: x['color_depth']) + # ICO images are usually squares + # self.entry = sorted(self.entry, key=lambda x: x['width']) + self.entry = sorted(self.entry, key=lambda x: x['square']) + self.entry.reverse() + + def sizes(self): + """ + Get a list of all available icon sizes and color depths. + """ + return set((h['width'], h['height']) for h in self.entry) + + def getimage(self, size, bpp=False): + """ + Get an image from the icon + """ + for (i, h) in enumerate(self.entry): + if size == h['dim'] and (bpp is False or bpp == h['color_depth']): + return self.frame(i) + return self.frame(0) + + def frame(self, idx): + """ + Get an image from frame idx + """ + + header = self.entry[idx] + + self.buf.seek(header['offset']) + data = self.buf.read(8) + self.buf.seek(header['offset']) + + if data[:8] == PngImagePlugin._MAGIC: + # png frame + im = PngImagePlugin.PngImageFile(self.buf) + else: + # XOR + AND mask bmp frame + im = BmpImagePlugin.DibImageFile(self.buf) + + # change tile dimension to only encompass XOR image + im.size = (im.size[0], int(im.size[1] / 2)) + d, e, o, a = im.tile[0] + im.tile[0] = d, (0, 0) + im.size, o, a + + # figure out where AND mask image starts + mode = a[0] + bpp = 8 + for k in BmpImagePlugin.BIT2MODE.keys(): + if mode == BmpImagePlugin.BIT2MODE[k][1]: + bpp = k + break + + if 32 == bpp: + # 32-bit color depth icon image allows semitransparent areas + # PIL's DIB format ignores transparency bits, recover them. + # The DIB is packed in BGRX byte order where X is the alpha + # channel. + + # Back up to start of bmp data + self.buf.seek(o) + # extract every 4th byte (eg. 3,7,11,15,...) + alpha_bytes = self.buf.read(im.size[0] * im.size[1] * 4)[3::4] + + # convert to an 8bpp grayscale image + mask = Image.frombuffer( + 'L', # 8bpp + im.size, # (w, h) + alpha_bytes, # source chars + 'raw', # raw decoder + ('L', 0, -1) # 8bpp inverted, unpadded, reversed + ) + else: + # get AND image from end of bitmap + w = im.size[0] + if (w % 32) > 0: + # bitmap row data is aligned to word boundaries + w += 32 - (im.size[0] % 32) + + # the total mask data is + # padded row size * height / bits per char + + and_mask_offset = o + int(im.size[0] * im.size[1] * + (bpp / 8.0)) + total_bytes = int((w * im.size[1]) / 8) + + self.buf.seek(and_mask_offset) + maskData = self.buf.read(total_bytes) + + # convert raw data to image + mask = Image.frombuffer( + '1', # 1 bpp + im.size, # (w, h) + maskData, # source chars + 'raw', # raw decoder + ('1;I', int(w/8), -1) # 1bpp inverted, padded, reversed + ) + + # now we have two images, im is XOR image and mask is AND image + + # apply mask image as alpha channel + im = im.convert('RGBA') + im.putalpha(mask) + + return im + + +## +# Image plugin for Windows Icon files. + +class IcoImageFile(ImageFile.ImageFile): + """ + PIL read-only image support for Microsoft Windows .ico files. + + By default the largest resolution image in the file will be loaded. This + can be changed by altering the 'size' attribute before calling 'load'. + + The info dictionary has a key 'sizes' that is a list of the sizes available + in the icon file. + + Handles classic, XP and Vista icon formats. + + This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis + . + https://code.google.com/p/casadebender/wiki/Win32IconImagePlugin + """ + format = "ICO" + format_description = "Windows Icon" + + def _open(self): + self.ico = IcoFile(self.fp) + self.info['sizes'] = self.ico.sizes() + self.size = self.ico.entry[0]['dim'] + self.load() + + def load(self): + im = self.ico.getimage(self.size) + # if tile is PNG, it won't really be loaded yet + im.load() + self.im = im.im + self.mode = im.mode + self.size = im.size + + def load_seek(self): + # Flage the ImageFile.Parser so that it + # just does all the decode at the end. + pass +# +# -------------------------------------------------------------------- + +Image.register_open("ICO", IcoImageFile, _accept) +Image.register_save("ICO", _save) +Image.register_extension("ICO", ".ico") diff --git a/pyPackages/pillowarmv7l/PIL/ImImagePlugin.py b/pyPackages/pillowarmv7l/PIL/ImImagePlugin.py new file mode 100644 index 0000000..4266f83 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/ImImagePlugin.py @@ -0,0 +1,347 @@ +# +# The Python Imaging Library. +# $Id$ +# +# IFUNC IM file handling for PIL +# +# history: +# 1995-09-01 fl Created. +# 1997-01-03 fl Save palette images +# 1997-01-08 fl Added sequence support +# 1997-01-23 fl Added P and RGB save support +# 1997-05-31 fl Read floating point images +# 1997-06-22 fl Save floating point images +# 1997-08-27 fl Read and save 1-bit images +# 1998-06-25 fl Added support for RGB+LUT images +# 1998-07-02 fl Added support for YCC images +# 1998-07-15 fl Renamed offset attribute to avoid name clash +# 1998-12-29 fl Added I;16 support +# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.7) +# 2003-09-26 fl Added LA/PA support +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1995-2001 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.7" + +import re +from PIL import Image, ImageFile, ImagePalette +from PIL._binary import i8 + + +# -------------------------------------------------------------------- +# Standard tags + +COMMENT = "Comment" +DATE = "Date" +EQUIPMENT = "Digitalization equipment" +FRAMES = "File size (no of images)" +LUT = "Lut" +NAME = "Name" +SCALE = "Scale (x,y)" +SIZE = "Image size (x*y)" +MODE = "Image type" + +TAGS = {COMMENT: 0, DATE: 0, EQUIPMENT: 0, FRAMES: 0, LUT: 0, NAME: 0, + SCALE: 0, SIZE: 0, MODE: 0} + +OPEN = { + # ifunc93/p3cfunc formats + "0 1 image": ("1", "1"), + "L 1 image": ("1", "1"), + "Greyscale image": ("L", "L"), + "Grayscale image": ("L", "L"), + "RGB image": ("RGB", "RGB;L"), + "RLB image": ("RGB", "RLB"), + "RYB image": ("RGB", "RLB"), + "B1 image": ("1", "1"), + "B2 image": ("P", "P;2"), + "B4 image": ("P", "P;4"), + "X 24 image": ("RGB", "RGB"), + "L 32 S image": ("I", "I;32"), + "L 32 F image": ("F", "F;32"), + # old p3cfunc formats + "RGB3 image": ("RGB", "RGB;T"), + "RYB3 image": ("RGB", "RYB;T"), + # extensions + "LA image": ("LA", "LA;L"), + "RGBA image": ("RGBA", "RGBA;L"), + "RGBX image": ("RGBX", "RGBX;L"), + "CMYK image": ("CMYK", "CMYK;L"), + "YCC image": ("YCbCr", "YCbCr;L"), +} + +# ifunc95 extensions +for i in ["8", "8S", "16", "16S", "32", "32F"]: + OPEN["L %s image" % i] = ("F", "F;%s" % i) + OPEN["L*%s image" % i] = ("F", "F;%s" % i) +for i in ["16", "16L", "16B"]: + OPEN["L %s image" % i] = ("I;%s" % i, "I;%s" % i) + OPEN["L*%s image" % i] = ("I;%s" % i, "I;%s" % i) +for i in ["32S"]: + OPEN["L %s image" % i] = ("I", "I;%s" % i) + OPEN["L*%s image" % i] = ("I", "I;%s" % i) +for i in range(2, 33): + OPEN["L*%s image" % i] = ("F", "F;%s" % i) + + +# -------------------------------------------------------------------- +# Read IM directory + +split = re.compile(br"^([A-Za-z][^:]*):[ \t]*(.*)[ \t]*$") + + +def number(s): + try: + return int(s) + except ValueError: + return float(s) + + +## +# Image plugin for the IFUNC IM file format. + +class ImImageFile(ImageFile.ImageFile): + + format = "IM" + format_description = "IFUNC Image Memory" + + def _open(self): + + # Quick rejection: if there's not an LF among the first + # 100 bytes, this is (probably) not a text header. + + if b"\n" not in self.fp.read(100): + raise SyntaxError("not an IM file") + self.fp.seek(0) + + n = 0 + + # Default values + self.info[MODE] = "L" + self.info[SIZE] = (512, 512) + self.info[FRAMES] = 1 + + self.rawmode = "L" + + while True: + + s = self.fp.read(1) + + # Some versions of IFUNC uses \n\r instead of \r\n... + if s == b"\r": + continue + + if not s or s == b'\0' or s == b'\x1A': + break + + # FIXME: this may read whole file if not a text file + s = s + self.fp.readline() + + if len(s) > 100: + raise SyntaxError("not an IM file") + + if s[-2:] == b'\r\n': + s = s[:-2] + elif s[-1:] == b'\n': + s = s[:-1] + + try: + m = split.match(s) + except re.error as v: + raise SyntaxError("not an IM file") + + if m: + + k, v = m.group(1, 2) + + # Don't know if this is the correct encoding, + # but a decent guess (I guess) + k = k.decode('latin-1', 'replace') + v = v.decode('latin-1', 'replace') + + # Convert value as appropriate + if k in [FRAMES, SCALE, SIZE]: + v = v.replace("*", ",") + v = tuple(map(number, v.split(","))) + if len(v) == 1: + v = v[0] + elif k == MODE and v in OPEN: + v, self.rawmode = OPEN[v] + + # Add to dictionary. Note that COMMENT tags are + # combined into a list of strings. + if k == COMMENT: + if k in self.info: + self.info[k].append(v) + else: + self.info[k] = [v] + else: + self.info[k] = v + + if k in TAGS: + n += 1 + + else: + + raise SyntaxError("Syntax error in IM header: " + + s.decode('ascii', 'replace')) + + if not n: + raise SyntaxError("Not an IM file") + + # Basic attributes + self.size = self.info[SIZE] + self.mode = self.info[MODE] + + # Skip forward to start of image data + while s and s[0:1] != b'\x1A': + s = self.fp.read(1) + if not s: + raise SyntaxError("File truncated") + + if LUT in self.info: + # convert lookup table to palette or lut attribute + palette = self.fp.read(768) + greyscale = 1 # greyscale palette + linear = 1 # linear greyscale palette + for i in range(256): + if palette[i] == palette[i+256] == palette[i+512]: + if i8(palette[i]) != i: + linear = 0 + else: + greyscale = 0 + if self.mode == "L" or self.mode == "LA": + if greyscale: + if not linear: + self.lut = [i8(c) for c in palette[:256]] + else: + if self.mode == "L": + self.mode = self.rawmode = "P" + elif self.mode == "LA": + self.mode = self.rawmode = "PA" + self.palette = ImagePalette.raw("RGB;L", palette) + elif self.mode == "RGB": + if not greyscale or not linear: + self.lut = [i8(c) for c in palette] + + self.frame = 0 + + self.__offset = offs = self.fp.tell() + + self.__fp = self.fp # FIXME: hack + + if self.rawmode[:2] == "F;": + + # ifunc95 formats + try: + # use bit decoder (if necessary) + bits = int(self.rawmode[2:]) + if bits not in [8, 16, 32]: + self.tile = [("bit", (0, 0)+self.size, offs, + (bits, 8, 3, 0, -1))] + return + except ValueError: + pass + + if self.rawmode in ["RGB;T", "RYB;T"]: + # Old LabEye/3PC files. Would be very surprised if anyone + # ever stumbled upon such a file ;-) + size = self.size[0] * self.size[1] + self.tile = [("raw", (0, 0)+self.size, offs, ("G", 0, -1)), + ("raw", (0, 0)+self.size, offs+size, ("R", 0, -1)), + ("raw", (0, 0)+self.size, offs+2*size, ("B", 0, -1))] + else: + # LabEye/IFUNC files + self.tile = [("raw", (0, 0)+self.size, offs, + (self.rawmode, 0, -1))] + + def seek(self, frame): + + if frame < 0 or frame >= self.info[FRAMES]: + raise EOFError("seek outside sequence") + + if self.frame == frame: + return + + self.frame = frame + + if self.mode == "1": + bits = 1 + else: + bits = 8 * len(self.mode) + + size = ((self.size[0] * bits + 7) // 8) * self.size[1] + offs = self.__offset + frame * size + + self.fp = self.__fp + + self.tile = [("raw", (0, 0)+self.size, offs, (self.rawmode, 0, -1))] + + def tell(self): + + return self.frame + +# +# -------------------------------------------------------------------- +# Save IM files + +SAVE = { + # mode: (im type, raw mode) + "1": ("0 1", "1"), + "L": ("Greyscale", "L"), + "LA": ("LA", "LA;L"), + "P": ("Greyscale", "P"), + "PA": ("LA", "PA;L"), + "I": ("L 32S", "I;32S"), + "I;16": ("L 16", "I;16"), + "I;16L": ("L 16L", "I;16L"), + "I;16B": ("L 16B", "I;16B"), + "F": ("L 32F", "F;32F"), + "RGB": ("RGB", "RGB;L"), + "RGBA": ("RGBA", "RGBA;L"), + "RGBX": ("RGBX", "RGBX;L"), + "CMYK": ("CMYK", "CMYK;L"), + "YCbCr": ("YCC", "YCbCr;L") +} + + +def _save(im, fp, filename, check=0): + + try: + type, rawmode = SAVE[im.mode] + except KeyError: + raise ValueError("Cannot save %s images as IM" % im.mode) + + try: + frames = im.encoderinfo["frames"] + except KeyError: + frames = 1 + + if check: + return check + + fp.write(("Image type: %s image\r\n" % type).encode('ascii')) + if filename: + fp.write(("Name: %s\r\n" % filename).encode('ascii')) + fp.write(("Image size (x*y): %d*%d\r\n" % im.size).encode('ascii')) + fp.write(("File size (no of images): %d\r\n" % frames).encode('ascii')) + if im.mode == "P": + fp.write(b"Lut: 1\r\n") + fp.write(b"\000" * (511-fp.tell()) + b"\032") + if im.mode == "P": + fp.write(im.im.getpalette("RGB", "RGB;L")) # 768 bytes + ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0, (rawmode, 0, -1))]) + +# +# -------------------------------------------------------------------- +# Registry + +Image.register_open("IM", ImImageFile) +Image.register_save("IM", _save) + +Image.register_extension("IM", ".im") diff --git a/pyPackages/pillowarmv7l/PIL/Image.py b/pyPackages/pillowarmv7l/PIL/Image.py new file mode 100644 index 0000000..98e3644 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/Image.py @@ -0,0 +1,2467 @@ +# +# The Python Imaging Library. +# $Id$ +# +# the Image class wrapper +# +# partial release history: +# 1995-09-09 fl Created +# 1996-03-11 fl PIL release 0.0 (proof of concept) +# 1996-04-30 fl PIL release 0.1b1 +# 1999-07-28 fl PIL release 1.0 final +# 2000-06-07 fl PIL release 1.1 +# 2000-10-20 fl PIL release 1.1.1 +# 2001-05-07 fl PIL release 1.1.2 +# 2002-03-15 fl PIL release 1.1.3 +# 2003-05-10 fl PIL release 1.1.4 +# 2005-03-28 fl PIL release 1.1.5 +# 2006-12-02 fl PIL release 1.1.6 +# 2009-11-15 fl PIL release 1.1.7 +# +# Copyright (c) 1997-2009 by Secret Labs AB. All rights reserved. +# Copyright (c) 1995-2009 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +from __future__ import print_function + +from PIL import VERSION, PILLOW_VERSION, _plugins + +import warnings + + +class DecompressionBombWarning(RuntimeWarning): + pass + + +class _imaging_not_installed: + # module placeholder + def __getattr__(self, id): + raise ImportError("The _imaging C module is not installed") + + +# Limit to around a quarter gigabyte for a 24 bit (3 bpp) image +MAX_IMAGE_PIXELS = int(1024 * 1024 * 1024 / 4 / 3) + +try: + # give Tk a chance to set up the environment, in case we're + # using an _imaging module linked against libtcl/libtk (use + # __import__ to hide this from naive packagers; we don't really + # depend on Tk unless ImageTk is used, and that module already + # imports Tkinter) + __import__("FixTk") +except ImportError: + pass + +try: + # If the _imaging C module is not present, Pillow will not load. + # Note that other modules should not refer to _imaging directly; + # import Image and use the Image.core variable instead. + # Also note that Image.core is not a publicly documented interface, + # and should be considered private and subject to change. + from PIL import _imaging as core + if PILLOW_VERSION != getattr(core, 'PILLOW_VERSION', None): + raise ImportError("The _imaging extension was built for another " + " version of Pillow or PIL") + +except ImportError as v: + core = _imaging_not_installed() + # Explanations for ways that we know we might have an import error + if str(v).startswith("Module use of python"): + # The _imaging C module is present, but not compiled for + # the right version (windows only). Print a warning, if + # possible. + warnings.warn( + "The _imaging extension was built for another version " + "of Python.", + RuntimeWarning + ) + elif str(v).startswith("The _imaging extension"): + warnings.warn(str(v), RuntimeWarning) + elif "Symbol not found: _PyUnicodeUCS2_FromString" in str(v): + warnings.warn( + "The _imaging extension was built for Python with UCS2 support; " + "recompile PIL or build Python --without-wide-unicode. ", + RuntimeWarning + ) + elif "Symbol not found: _PyUnicodeUCS4_FromString" in str(v): + warnings.warn( + "The _imaging extension was built for Python with UCS4 support; " + "recompile PIL or build Python --with-wide-unicode. ", + RuntimeWarning + ) + # Fail here anyway. Don't let people run with a mostly broken Pillow. + # see docs/porting-pil-to-pillow.rst + raise + +try: + import builtins +except ImportError: + import __builtin__ + builtins = __builtin__ + +from PIL import ImageMode +from PIL._binary import i8 +from PIL._util import isPath +from PIL._util import isStringType +from PIL._util import deferred_error + +import os +import sys + +# type stuff +import collections +import numbers + +# works everywhere, win for pypy, not cpython +USE_CFFI_ACCESS = hasattr(sys, 'pypy_version_info') +try: + import cffi + HAS_CFFI = True +except: + HAS_CFFI = False + + +def isImageType(t): + """ + Checks if an object is an image object. + + .. warning:: + + This function is for internal use only. + + :param t: object to check if it's an image + :returns: True if the object is an image + """ + return hasattr(t, "im") + +# +# Debug level + +DEBUG = 0 + +# +# Constants (also defined in _imagingmodule.c!) + +NONE = 0 + +# transpose +FLIP_LEFT_RIGHT = 0 +FLIP_TOP_BOTTOM = 1 +ROTATE_90 = 2 +ROTATE_180 = 3 +ROTATE_270 = 4 +TRANSPOSE = 5 + +# transforms +AFFINE = 0 +EXTENT = 1 +PERSPECTIVE = 2 +QUAD = 3 +MESH = 4 + +# resampling filters +NEAREST = NONE = 0 +LANCZOS = ANTIALIAS = 1 +BILINEAR = LINEAR = 2 +BICUBIC = CUBIC = 3 + +# dithers +NONE = 0 +NEAREST = 0 +ORDERED = 1 # Not yet implemented +RASTERIZE = 2 # Not yet implemented +FLOYDSTEINBERG = 3 # default + +# palettes/quantizers +WEB = 0 +ADAPTIVE = 1 + +MEDIANCUT = 0 +MAXCOVERAGE = 1 +FASTOCTREE = 2 + +# categories +NORMAL = 0 +SEQUENCE = 1 +CONTAINER = 2 + +if hasattr(core, 'DEFAULT_STRATEGY'): + DEFAULT_STRATEGY = core.DEFAULT_STRATEGY + FILTERED = core.FILTERED + HUFFMAN_ONLY = core.HUFFMAN_ONLY + RLE = core.RLE + FIXED = core.FIXED + + +# -------------------------------------------------------------------- +# Registries + +ID = [] +OPEN = {} +MIME = {} +SAVE = {} +EXTENSION = {} + +# -------------------------------------------------------------------- +# Modes supported by this version + +_MODEINFO = { + # NOTE: this table will be removed in future versions. use + # getmode* functions or ImageMode descriptors instead. + + # official modes + "1": ("L", "L", ("1",)), + "L": ("L", "L", ("L",)), + "I": ("L", "I", ("I",)), + "F": ("L", "F", ("F",)), + "P": ("RGB", "L", ("P",)), + "RGB": ("RGB", "L", ("R", "G", "B")), + "RGBX": ("RGB", "L", ("R", "G", "B", "X")), + "RGBA": ("RGB", "L", ("R", "G", "B", "A")), + "CMYK": ("RGB", "L", ("C", "M", "Y", "K")), + "YCbCr": ("RGB", "L", ("Y", "Cb", "Cr")), + "LAB": ("RGB", "L", ("L", "A", "B")), + "HSV": ("RGB", "L", ("H", "S", "V")), + + # Experimental modes include I;16, I;16L, I;16B, RGBa, BGR;15, and + # BGR;24. Use these modes only if you know exactly what you're + # doing... + +} + +if sys.byteorder == 'little': + _ENDIAN = '<' +else: + _ENDIAN = '>' + +_MODE_CONV = { + # official modes + "1": ('|b1', None), # broken + "L": ('|u1', None), + "I": (_ENDIAN + 'i4', None), + "F": (_ENDIAN + 'f4', None), + "P": ('|u1', None), + "RGB": ('|u1', 3), + "RGBX": ('|u1', 4), + "RGBA": ('|u1', 4), + "CMYK": ('|u1', 4), + "YCbCr": ('|u1', 3), + "LAB": ('|u1', 3), # UNDONE - unsigned |u1i1i1 + # I;16 == I;16L, and I;32 == I;32L + "I;16": ('u2', None), + "I;16L": ('i2', None), + "I;16LS": ('u4', None), + "I;32L": ('i4', None), + "I;32LS": ('= 1: + return + + try: + from PIL import BmpImagePlugin + except ImportError: + pass + try: + from PIL import GifImagePlugin + except ImportError: + pass + try: + from PIL import JpegImagePlugin + except ImportError: + pass + try: + from PIL import PpmImagePlugin + except ImportError: + pass + try: + from PIL import PngImagePlugin + except ImportError: + pass +# try: +# import TiffImagePlugin +# except ImportError: +# pass + + _initialized = 1 + + +def init(): + """ + Explicitly initializes the Python Imaging Library. This function + loads all available file format drivers. + """ + + global _initialized + if _initialized >= 2: + return 0 + + for plugin in _plugins: + try: + if DEBUG: + print ("Importing %s" % plugin) + __import__("PIL.%s" % plugin, globals(), locals(), []) + except ImportError: + if DEBUG: + print("Image: failed to import", end=' ') + print(plugin, ":", sys.exc_info()[1]) + + if OPEN or SAVE: + _initialized = 2 + return 1 + + +# -------------------------------------------------------------------- +# Codec factories (used by tobytes/frombytes and ImageFile.load) + +def _getdecoder(mode, decoder_name, args, extra=()): + + # tweak arguments + if args is None: + args = () + elif not isinstance(args, tuple): + args = (args,) + + try: + # get decoder + decoder = getattr(core, decoder_name + "_decoder") + # print(decoder, mode, args + extra) + return decoder(mode, *args + extra) + except AttributeError: + raise IOError("decoder %s not available" % decoder_name) + + +def _getencoder(mode, encoder_name, args, extra=()): + + # tweak arguments + if args is None: + args = () + elif not isinstance(args, tuple): + args = (args,) + + try: + # get encoder + encoder = getattr(core, encoder_name + "_encoder") + # print(encoder, mode, args + extra) + return encoder(mode, *args + extra) + except AttributeError: + raise IOError("encoder %s not available" % encoder_name) + + +# -------------------------------------------------------------------- +# Simple expression analyzer + +def coerce_e(value): + return value if isinstance(value, _E) else _E(value) + + +class _E: + def __init__(self, data): + self.data = data + + def __add__(self, other): + return _E((self.data, "__add__", coerce_e(other).data)) + + def __mul__(self, other): + return _E((self.data, "__mul__", coerce_e(other).data)) + + +def _getscaleoffset(expr): + stub = ["stub"] + data = expr(_E(stub)).data + try: + (a, b, c) = data # simplified syntax + if (a is stub and b == "__mul__" and isinstance(c, numbers.Number)): + return c, 0.0 + if a is stub and b == "__add__" and isinstance(c, numbers.Number): + return 1.0, c + except TypeError: + pass + try: + ((a, b, c), d, e) = data # full syntax + if (a is stub and b == "__mul__" and isinstance(c, numbers.Number) and + d == "__add__" and isinstance(e, numbers.Number)): + return c, e + except TypeError: + pass + raise ValueError("illegal expression") + + +# -------------------------------------------------------------------- +# Implementation wrapper + +class Image: + """ + This class represents an image object. To create + :py:class:`~PIL.Image.Image` objects, use the appropriate factory + functions. There's hardly ever any reason to call the Image constructor + directly. + + * :py:func:`~PIL.Image.open` + * :py:func:`~PIL.Image.new` + * :py:func:`~PIL.Image.frombytes` + """ + format = None + format_description = None + + def __init__(self): + # FIXME: take "new" parameters / other image? + # FIXME: turn mode and size into delegating properties? + self.im = None + self.mode = "" + self.size = (0, 0) + self.palette = None + self.info = {} + self.category = NORMAL + self.readonly = 0 + self.pyaccess = None + + def _new(self, im): + new = Image() + new.im = im + new.mode = im.mode + new.size = im.size + new.palette = self.palette + if im.mode == "P" and not new.palette: + from PIL import ImagePalette + new.palette = ImagePalette.ImagePalette() + try: + new.info = self.info.copy() + except AttributeError: + # fallback (pre-1.5.2) + new.info = {} + for k, v in self.info: + new.info[k] = v + return new + + _makeself = _new # compatibility + + # Context Manager Support + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + + def close(self): + """ + Closes the file pointer, if possible. + + This operation will destroy the image core and release its memory. + The image data will be unusable afterward. + + This function is only required to close images that have not + had their file read and closed by the + :py:meth:`~PIL.Image.Image.load` method. + """ + try: + self.fp.close() + except Exception as msg: + if DEBUG: + print ("Error closing: %s" % msg) + + # Instead of simply setting to None, we're setting up a + # deferred error that will better explain that the core image + # object is gone. + self.im = deferred_error(ValueError("Operation on closed image")) + + def _copy(self): + self.load() + self.im = self.im.copy() + self.pyaccess = None + self.readonly = 0 + + def _dump(self, file=None, format=None): + import tempfile + suffix = '' + if format: + suffix = '.'+format + if not file: + f, file = tempfile.mkstemp(suffix) + os.close(f) + + self.load() + if not format or format == "PPM": + self.im.save_ppm(file) + else: + if not file.endswith(format): + file = file + "." + format + self.save(file, format) + return file + + def __eq__(self, other): + if self.__class__.__name__ != other.__class__.__name__: + return False + a = (self.mode == other.mode) + b = (self.size == other.size) + c = (self.getpalette() == other.getpalette()) + d = (self.info == other.info) + e = (self.category == other.category) + f = (self.readonly == other.readonly) + g = (self.tobytes() == other.tobytes()) + return a and b and c and d and e and f and g + + def __ne__(self, other): + eq = (self == other) + return not eq + + def __repr__(self): + return "<%s.%s image mode=%s size=%dx%d at 0x%X>" % ( + self.__class__.__module__, self.__class__.__name__, + self.mode, self.size[0], self.size[1], + id(self) + ) + + def __getattr__(self, name): + if name == "__array_interface__": + # numpy array interface support + new = {} + shape, typestr = _conv_type_shape(self) + new['shape'] = shape + new['typestr'] = typestr + new['data'] = self.tobytes() + return new + raise AttributeError(name) + + def __getstate__(self): + return [ + self.info, + self.mode, + self.size, + self.getpalette(), + self.tobytes()] + + def __setstate__(self, state): + Image.__init__(self) + self.tile = [] + info, mode, size, palette, data = state + self.info = info + self.mode = mode + self.size = size + self.im = core.new(mode, size) + if mode in ("L", "P"): + self.putpalette(palette) + self.frombytes(data) + + def tobytes(self, encoder_name="raw", *args): + """ + Return image as a bytes object + + :param encoder_name: What encoder to use. The default is to + use the standard "raw" encoder. + :param args: Extra arguments to the encoder. + :rtype: A bytes object. + """ + + # may pass tuple instead of argument list + if len(args) == 1 and isinstance(args[0], tuple): + args = args[0] + + if encoder_name == "raw" and args == (): + args = self.mode + + self.load() + + # unpack data + e = _getencoder(self.mode, encoder_name, args) + e.setimage(self.im) + + bufsize = max(65536, self.size[0] * 4) # see RawEncode.c + + data = [] + while True: + l, s, d = e.encode(bufsize) + data.append(d) + if s: + break + if s < 0: + raise RuntimeError("encoder error %d in tobytes" % s) + + return b"".join(data) + + # Declare tostring as alias to tobytes + def tostring(self, *args, **kw): + """Deprecated alias to tobytes. + + .. deprecated:: 2.0 + """ + warnings.warn( + 'tostring() is deprecated. Please call tobytes() instead.', + DeprecationWarning, + stacklevel=2, + ) + return self.tobytes(*args, **kw) + + def tobitmap(self, name="image"): + """ + Returns the image converted to an X11 bitmap. + + .. note:: This method only works for mode "1" images. + + :param name: The name prefix to use for the bitmap variables. + :returns: A string containing an X11 bitmap. + :raises ValueError: If the mode is not "1" + """ + + self.load() + if self.mode != "1": + raise ValueError("not a bitmap") + data = self.tobytes("xbm") + return b"".join([ + ("#define %s_width %d\n" % (name, self.size[0])).encode('ascii'), + ("#define %s_height %d\n" % (name, self.size[1])).encode('ascii'), + ("static char %s_bits[] = {\n" % name).encode('ascii'), data, b"};" + ]) + + def frombytes(self, data, decoder_name="raw", *args): + """ + Loads this image with pixel data from a bytes object. + + This method is similar to the :py:func:`~PIL.Image.frombytes` function, + but loads data into this image instead of creating a new image object. + """ + + # may pass tuple instead of argument list + if len(args) == 1 and isinstance(args[0], tuple): + args = args[0] + + # default format + if decoder_name == "raw" and args == (): + args = self.mode + + # unpack data + d = _getdecoder(self.mode, decoder_name, args) + d.setimage(self.im) + s = d.decode(data) + + if s[0] >= 0: + raise ValueError("not enough image data") + if s[1] != 0: + raise ValueError("cannot decode image data") + + def fromstring(self, *args, **kw): + """Deprecated alias to frombytes. + + .. deprecated:: 2.0 + """ + warnings.warn( + 'fromstring() is deprecated. Please call frombytes() instead.', + DeprecationWarning) + return self.frombytes(*args, **kw) + + def load(self): + """ + Allocates storage for the image and loads the pixel data. In + normal cases, you don't need to call this method, since the + Image class automatically loads an opened image when it is + accessed for the first time. This method will close the file + associated with the image. + + :returns: An image access object. + :rtype: :ref:`PixelAccess` or :py:class:`PIL.PyAccess` + """ + if self.im and self.palette and self.palette.dirty: + # realize palette + self.im.putpalette(*self.palette.getdata()) + self.palette.dirty = 0 + self.palette.mode = "RGB" + self.palette.rawmode = None + if "transparency" in self.info: + if isinstance(self.info["transparency"], int): + self.im.putpalettealpha(self.info["transparency"], 0) + else: + self.im.putpalettealphas(self.info["transparency"]) + self.palette.mode = "RGBA" + + if self.im: + if HAS_CFFI and USE_CFFI_ACCESS: + if self.pyaccess: + return self.pyaccess + from PIL import PyAccess + self.pyaccess = PyAccess.new(self, self.readonly) + if self.pyaccess: + return self.pyaccess + return self.im.pixel_access(self.readonly) + + def verify(self): + """ + Verifies the contents of a file. For data read from a file, this + method attempts to determine if the file is broken, without + actually decoding the image data. If this method finds any + problems, it raises suitable exceptions. If you need to load + the image after using this method, you must reopen the image + file. + """ + pass + + def convert(self, mode=None, matrix=None, dither=None, + palette=WEB, colors=256): + """ + Returns a converted copy of this image. For the "P" mode, this + method translates pixels through the palette. If mode is + omitted, a mode is chosen so that all information in the image + and the palette can be represented without a palette. + + The current version supports all possible conversions between + "L", "RGB" and "CMYK." The **matrix** argument only supports "L" + and "RGB". + + When translating a color image to black and white (mode "L"), + the library uses the ITU-R 601-2 luma transform:: + + L = R * 299/1000 + G * 587/1000 + B * 114/1000 + + The default method of converting a greyscale ("L") or "RGB" + image into a bilevel (mode "1") image uses Floyd-Steinberg + dither to approximate the original image luminosity levels. If + dither is NONE, all non-zero values are set to 255 (white). To + use other thresholds, use the :py:meth:`~PIL.Image.Image.point` + method. + + :param mode: The requested mode. See: :ref:`concept-modes`. + :param matrix: An optional conversion matrix. If given, this + should be 4- or 16-tuple containing floating point values. + :param dither: Dithering method, used when converting from + mode "RGB" to "P" or from "RGB" or "L" to "1". + Available methods are NONE or FLOYDSTEINBERG (default). + :param palette: Palette to use when converting from mode "RGB" + to "P". Available palettes are WEB or ADAPTIVE. + :param colors: Number of colors to use for the ADAPTIVE palette. + Defaults to 256. + :rtype: :py:class:`~PIL.Image.Image` + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + if not mode: + # determine default mode + if self.mode == "P": + self.load() + if self.palette: + mode = self.palette.mode + else: + mode = "RGB" + else: + return self.copy() + + self.load() + + if matrix: + # matrix conversion + if mode not in ("L", "RGB"): + raise ValueError("illegal conversion") + im = self.im.convert_matrix(mode, matrix) + return self._new(im) + + if mode == "P" and self.mode == "RGBA": + return self.quantize(colors) + + trns = None + delete_trns = False + # transparency handling + if "transparency" in self.info and \ + self.info['transparency'] is not None: + if self.mode in ('L', 'RGB') and mode == 'RGBA': + # Use transparent conversion to promote from transparent + # color to an alpha channel. + return self._new(self.im.convert_transparent( + mode, self.info['transparency'])) + elif self.mode in ('L', 'RGB', 'P') and mode in ('L', 'RGB', 'P'): + t = self.info['transparency'] + if isinstance(t, bytes): + # Dragons. This can't be represented by a single color + warnings.warn('Palette images with Transparency ' + + ' expressed in bytes should be converted ' + + 'to RGBA images') + delete_trns = True + else: + # get the new transparency color. + # use existing conversions + trns_im = Image()._new(core.new(self.mode, (1, 1))) + if self.mode == 'P': + trns_im.putpalette(self.palette) + trns_im.putpixel((0, 0), t) + + if mode in ('L', 'RGB'): + trns_im = trns_im.convert(mode) + else: + # can't just retrieve the palette number, got to do it + # after quantization. + trns_im = trns_im.convert('RGB') + trns = trns_im.getpixel((0, 0)) + + elif self.mode == 'P' and mode == 'RGBA': + t = self.info['transparency'] + delete_trns = True + + if isinstance(t, bytes): + self.im.putpalettealphas(t) + elif isinstance(t, int): + self.im.putpalettealpha(t,0) + else: + raise ValueError("Transparency for P mode should" + + " be bytes or int") + + + if mode == "P" and palette == ADAPTIVE: + im = self.im.quantize(colors) + new = self._new(im) + from PIL import ImagePalette + new.palette = ImagePalette.raw("RGB", new.im.getpalette("RGB")) + if delete_trns: + # This could possibly happen if we requantize to fewer colors. + # The transparency would be totally off in that case. + del(new.info['transparency']) + if trns is not None: + try: + new.info['transparency'] = new.palette.getcolor(trns) + except: + # if we can't make a transparent color, don't leave the old + # transparency hanging around to mess us up. + del(new.info['transparency']) + warnings.warn("Couldn't allocate palette entry " + + "for transparency") + return new + + # colorspace conversion + if dither is None: + dither = FLOYDSTEINBERG + + try: + im = self.im.convert(mode, dither) + except ValueError: + try: + # normalize source image and try again + im = self.im.convert(getmodebase(self.mode)) + im = im.convert(mode, dither) + except KeyError: + raise ValueError("illegal conversion") + + new_im = self._new(im) + if delete_trns: + # crash fail if we leave a bytes transparency in an rgb/l mode. + del(new_im.info['transparency']) + if trns is not None: + if new_im.mode == 'P': + try: + new_im.info['transparency'] = new_im.palette.getcolor(trns) + except: + del(new_im.info['transparency']) + warnings.warn("Couldn't allocate palette entry " + + "for transparency") + else: + new_im.info['transparency'] = trns + return new_im + + def quantize(self, colors=256, method=None, kmeans=0, palette=None): + """ + Convert the image to 'P' mode with the specified number + of colors. + + :param colors: The desired number of colors, <= 256 + :param method: 0 = median cut + 1 = maximum coverage + 2 = fast octree + :param kmeans: Integer + :param palette: Quantize to the :py:class:`PIL.ImagingPalette` palette. + :returns: A new image + + """ + + self.load() + + if method is None: + # defaults: + method = 0 + if self.mode == 'RGBA': + method = 2 + + if self.mode == 'RGBA' and method != 2: + # Caller specified an invalid mode. + raise ValueError('Fast Octree (method == 2) is the ' + + ' only valid method for quantizing RGBA images') + + if palette: + # use palette from reference image + palette.load() + if palette.mode != "P": + raise ValueError("bad mode for palette image") + if self.mode != "RGB" and self.mode != "L": + raise ValueError( + "only RGB or L mode images can be quantized to a palette" + ) + im = self.im.convert("P", 1, palette.im) + return self._makeself(im) + + im = self.im.quantize(colors, method, kmeans) + return self._new(im) + + def copy(self): + """ + Copies this image. Use this method if you wish to paste things + into an image, but still retain the original. + + :rtype: :py:class:`~PIL.Image.Image` + :returns: An :py:class:`~PIL.Image.Image` object. + """ + self.load() + im = self.im.copy() + return self._new(im) + + def crop(self, box=None): + """ + Returns a rectangular region from this image. The box is a + 4-tuple defining the left, upper, right, and lower pixel + coordinate. + + This is a lazy operation. Changes to the source image may or + may not be reflected in the cropped image. To break the + connection, call the :py:meth:`~PIL.Image.Image.load` method on + the cropped copy. + + :param box: The crop rectangle, as a (left, upper, right, lower)-tuple. + :rtype: :py:class:`~PIL.Image.Image` + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + self.load() + if box is None: + return self.copy() + + # lazy operation + return _ImageCrop(self, box) + + def draft(self, mode, size): + """ + Configures the image file loader so it returns a version of the + image that as closely as possible matches the given mode and + size. For example, you can use this method to convert a color + JPEG to greyscale while loading it, or to extract a 128x192 + version from a PCD file. + + Note that this method modifies the :py:class:`~PIL.Image.Image` object + in place. If the image has already been loaded, this method has no + effect. + + :param mode: The requested mode. + :param size: The requested size. + """ + pass + + def _expand(self, xmargin, ymargin=None): + if ymargin is None: + ymargin = xmargin + self.load() + return self._new(self.im.expand(xmargin, ymargin, 0)) + + def filter(self, filter): + """ + Filters this image using the given filter. For a list of + available filters, see the :py:mod:`~PIL.ImageFilter` module. + + :param filter: Filter kernel. + :returns: An :py:class:`~PIL.Image.Image` object. """ + + self.load() + + if isinstance(filter, collections.Callable): + filter = filter() + if not hasattr(filter, "filter"): + raise TypeError("filter argument should be ImageFilter.Filter " + + "instance or class") + + if self.im.bands == 1: + return self._new(filter.filter(self.im)) + # fix to handle multiband images since _imaging doesn't + ims = [] + for c in range(self.im.bands): + ims.append(self._new(filter.filter(self.im.getband(c)))) + return merge(self.mode, ims) + + def getbands(self): + """ + Returns a tuple containing the name of each band in this image. + For example, **getbands** on an RGB image returns ("R", "G", "B"). + + :returns: A tuple containing band names. + :rtype: tuple + """ + return ImageMode.getmode(self.mode).bands + + def getbbox(self): + """ + Calculates the bounding box of the non-zero regions in the + image. + + :returns: The bounding box is returned as a 4-tuple defining the + left, upper, right, and lower pixel coordinate. If the image + is completely empty, this method returns None. + + """ + + self.load() + return self.im.getbbox() + + def getcolors(self, maxcolors=256): + """ + Returns a list of colors used in this image. + + :param maxcolors: Maximum number of colors. If this number is + exceeded, this method returns None. The default limit is + 256 colors. + :returns: An unsorted list of (count, pixel) values. + """ + + self.load() + if self.mode in ("1", "L", "P"): + h = self.im.histogram() + out = [] + for i in range(256): + if h[i]: + out.append((h[i], i)) + if len(out) > maxcolors: + return None + return out + return self.im.getcolors(maxcolors) + + def getdata(self, band=None): + """ + Returns the contents of this image as a sequence object + containing pixel values. The sequence object is flattened, so + that values for line one follow directly after the values of + line zero, and so on. + + Note that the sequence object returned by this method is an + internal PIL data type, which only supports certain sequence + operations. To convert it to an ordinary sequence (e.g. for + printing), use **list(im.getdata())**. + + :param band: What band to return. The default is to return + all bands. To return a single band, pass in the index + value (e.g. 0 to get the "R" band from an "RGB" image). + :returns: A sequence-like object. + """ + + self.load() + if band is not None: + return self.im.getband(band) + return self.im # could be abused + + def getextrema(self): + """ + Gets the the minimum and maximum pixel values for each band in + the image. + + :returns: For a single-band image, a 2-tuple containing the + minimum and maximum pixel value. For a multi-band image, + a tuple containing one 2-tuple for each band. + """ + + self.load() + if self.im.bands > 1: + extrema = [] + for i in range(self.im.bands): + extrema.append(self.im.getband(i).getextrema()) + return tuple(extrema) + return self.im.getextrema() + + def getim(self): + """ + Returns a capsule that points to the internal image memory. + + :returns: A capsule object. + """ + + self.load() + return self.im.ptr + + def getpalette(self): + """ + Returns the image palette as a list. + + :returns: A list of color values [r, g, b, ...], or None if the + image has no palette. + """ + + self.load() + try: + if bytes is str: + return [i8(c) for c in self.im.getpalette()] + else: + return list(self.im.getpalette()) + except ValueError: + return None # no palette + + def getpixel(self, xy): + """ + Returns the pixel value at a given position. + + :param xy: The coordinate, given as (x, y). + :returns: The pixel value. If the image is a multi-layer image, + this method returns a tuple. + """ + + self.load() + if self.pyaccess: + return self.pyaccess.getpixel(xy) + return self.im.getpixel(xy) + + def getprojection(self): + """ + Get projection to x and y axes + + :returns: Two sequences, indicating where there are non-zero + pixels along the X-axis and the Y-axis, respectively. + """ + + self.load() + x, y = self.im.getprojection() + return [i8(c) for c in x], [i8(c) for c in y] + + def histogram(self, mask=None, extrema=None): + """ + Returns a histogram for the image. The histogram is returned as + a list of pixel counts, one for each pixel value in the source + image. If the image has more than one band, the histograms for + all bands are concatenated (for example, the histogram for an + "RGB" image contains 768 values). + + A bilevel image (mode "1") is treated as a greyscale ("L") image + by this method. + + If a mask is provided, the method returns a histogram for those + parts of the image where the mask image is non-zero. The mask + image must have the same size as the image, and be either a + bi-level image (mode "1") or a greyscale image ("L"). + + :param mask: An optional mask. + :returns: A list containing pixel counts. + """ + self.load() + if mask: + mask.load() + return self.im.histogram((0, 0), mask.im) + if self.mode in ("I", "F"): + if extrema is None: + extrema = self.getextrema() + return self.im.histogram(extrema) + return self.im.histogram() + + def offset(self, xoffset, yoffset=None): + """ + .. deprecated:: 2.0 + + .. note:: New code should use :py:func:`PIL.ImageChops.offset`. + + Returns a copy of the image where the data has been offset by the given + distances. Data wraps around the edges. If **yoffset** is omitted, it + is assumed to be equal to **xoffset**. + + :param xoffset: The horizontal distance. + :param yoffset: The vertical distance. If omitted, both + distances are set to the same value. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + if warnings: + warnings.warn( + "'offset' is deprecated; use 'ImageChops.offset' instead", + DeprecationWarning, stacklevel=2 + ) + from PIL import ImageChops + return ImageChops.offset(self, xoffset, yoffset) + + def paste(self, im, box=None, mask=None): + """ + Pastes another image into this image. The box argument is either + a 2-tuple giving the upper left corner, a 4-tuple defining the + left, upper, right, and lower pixel coordinate, or None (same as + (0, 0)). If a 4-tuple is given, the size of the pasted image + must match the size of the region. + + If the modes don't match, the pasted image is converted to the mode of + this image (see the :py:meth:`~PIL.Image.Image.convert` method for + details). + + Instead of an image, the source can be a integer or tuple + containing pixel values. The method then fills the region + with the given color. When creating RGB images, you can + also use color strings as supported by the ImageColor module. + + If a mask is given, this method updates only the regions + indicated by the mask. You can use either "1", "L" or "RGBA" + images (in the latter case, the alpha band is used as mask). + Where the mask is 255, the given image is copied as is. Where + the mask is 0, the current value is preserved. Intermediate + values can be used for transparency effects. + + Note that if you paste an "RGBA" image, the alpha band is + ignored. You can work around this by using the same image as + both source image and mask. + + :param im: Source image or pixel value (integer or tuple). + :param box: An optional 4-tuple giving the region to paste into. + If a 2-tuple is used instead, it's treated as the upper left + corner. If omitted or None, the source is pasted into the + upper left corner. + + If an image is given as the second argument and there is no + third, the box defaults to (0, 0), and the second argument + is interpreted as a mask image. + :param mask: An optional mask image. + """ + + if isImageType(box) and mask is None: + # abbreviated paste(im, mask) syntax + mask = box + box = None + + if box is None: + # cover all of self + box = (0, 0) + self.size + + if len(box) == 2: + # lower left corner given; get size from image or mask + if isImageType(im): + size = im.size + elif isImageType(mask): + size = mask.size + else: + # FIXME: use self.size here? + raise ValueError( + "cannot determine region size; use 4-item box" + ) + box = box + (box[0]+size[0], box[1]+size[1]) + + if isStringType(im): + from PIL import ImageColor + im = ImageColor.getcolor(im, self.mode) + + elif isImageType(im): + im.load() + if self.mode != im.mode: + if self.mode != "RGB" or im.mode not in ("RGBA", "RGBa"): + # should use an adapter for this! + im = im.convert(self.mode) + im = im.im + + self.load() + if self.readonly: + self._copy() + + if mask: + mask.load() + self.im.paste(im, box, mask.im) + else: + self.im.paste(im, box) + + def point(self, lut, mode=None): + """ + Maps this image through a lookup table or function. + + :param lut: A lookup table, containing 256 (or 65336 if + self.mode=="I" and mode == "L") values per band in the + image. A function can be used instead, it should take a + single argument. The function is called once for each + possible pixel value, and the resulting table is applied to + all bands of the image. + :param mode: Output mode (default is same as input). In the + current version, this can only be used if the source image + has mode "L" or "P", and the output has mode "1" or the + source image mode is "I" and the output mode is "L". + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + self.load() + + if isinstance(lut, ImagePointHandler): + return lut.point(self) + + if callable(lut): + # if it isn't a list, it should be a function + if self.mode in ("I", "I;16", "F"): + # check if the function can be used with point_transform + # UNDONE wiredfool -- I think this prevents us from ever doing + # a gamma function point transform on > 8bit images. + scale, offset = _getscaleoffset(lut) + return self._new(self.im.point_transform(scale, offset)) + # for other modes, convert the function to a table + lut = [lut(i) for i in range(256)] * self.im.bands + + if self.mode == "F": + # FIXME: _imaging returns a confusing error message for this case + raise ValueError("point operation not supported for this mode") + + return self._new(self.im.point(lut, mode)) + + def putalpha(self, alpha): + """ + Adds or replaces the alpha layer in this image. If the image + does not have an alpha layer, it's converted to "LA" or "RGBA". + The new layer must be either "L" or "1". + + :param alpha: The new alpha layer. This can either be an "L" or "1" + image having the same size as this image, or an integer or + other color value. + """ + + self.load() + if self.readonly: + self._copy() + + if self.mode not in ("LA", "RGBA"): + # attempt to promote self to a matching alpha mode + try: + mode = getmodebase(self.mode) + "A" + try: + self.im.setmode(mode) + self.pyaccess = None + except (AttributeError, ValueError): + # do things the hard way + im = self.im.convert(mode) + if im.mode not in ("LA", "RGBA"): + raise ValueError # sanity check + self.im = im + self.pyaccess = None + self.mode = self.im.mode + except (KeyError, ValueError): + raise ValueError("illegal image mode") + + if self.mode == "LA": + band = 1 + else: + band = 3 + + if isImageType(alpha): + # alpha layer + if alpha.mode not in ("1", "L"): + raise ValueError("illegal image mode") + alpha.load() + if alpha.mode == "1": + alpha = alpha.convert("L") + else: + # constant alpha + try: + self.im.fillband(band, alpha) + except (AttributeError, ValueError): + # do things the hard way + alpha = new("L", self.size, alpha) + else: + return + + self.im.putband(alpha.im, band) + + def putdata(self, data, scale=1.0, offset=0.0): + """ + Copies pixel data to this image. This method copies data from a + sequence object into the image, starting at the upper left + corner (0, 0), and continuing until either the image or the + sequence ends. The scale and offset values are used to adjust + the sequence values: **pixel = value*scale + offset**. + + :param data: A sequence object. + :param scale: An optional scale value. The default is 1.0. + :param offset: An optional offset value. The default is 0.0. + """ + + self.load() + if self.readonly: + self._copy() + + self.im.putdata(data, scale, offset) + + def putpalette(self, data, rawmode="RGB"): + """ + Attaches a palette to this image. The image must be a "P" or + "L" image, and the palette sequence must contain 768 integer + values, where each group of three values represent the red, + green, and blue values for the corresponding pixel + index. Instead of an integer sequence, you can use an 8-bit + string. + + :param data: A palette sequence (either a list or a string). + """ + from PIL import ImagePalette + + if self.mode not in ("L", "P"): + raise ValueError("illegal image mode") + self.load() + if isinstance(data, ImagePalette.ImagePalette): + palette = ImagePalette.raw(data.rawmode, data.palette) + else: + if not isinstance(data, bytes): + if bytes is str: + data = "".join(chr(x) for x in data) + else: + data = bytes(data) + palette = ImagePalette.raw(rawmode, data) + self.mode = "P" + self.palette = palette + self.palette.mode = "RGB" + self.load() # install new palette + + def putpixel(self, xy, value): + """ + Modifies the pixel at the given position. The color is given as + a single numerical value for single-band images, and a tuple for + multi-band images. + + Note that this method is relatively slow. For more extensive changes, + use :py:meth:`~PIL.Image.Image.paste` or the :py:mod:`~PIL.ImageDraw` + module instead. + + See: + + * :py:meth:`~PIL.Image.Image.paste` + * :py:meth:`~PIL.Image.Image.putdata` + * :py:mod:`~PIL.ImageDraw` + + :param xy: The pixel coordinate, given as (x, y). + :param value: The pixel value. + """ + + self.load() + if self.readonly: + self._copy() + self.pyaccess = None + self.load() + + if self.pyaccess: + return self.pyaccess.putpixel(xy, value) + return self.im.putpixel(xy, value) + + def resize(self, size, resample=NEAREST): + """ + Returns a resized copy of this image. + + :param size: The requested size in pixels, as a 2-tuple: + (width, height). + :param resample: An optional resampling filter. This can be + one of :py:attr:`PIL.Image.NEAREST` (use nearest neighbour), + :py:attr:`PIL.Image.BILINEAR` (linear interpolation), + :py:attr:`PIL.Image.BICUBIC` (cubic spline interpolation), or + :py:attr:`PIL.Image.LANCZOS` (a high-quality downsampling filter). + If omitted, or if the image has mode "1" or "P", it is + set :py:attr:`PIL.Image.NEAREST`. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + if resample not in (NEAREST, BILINEAR, BICUBIC, LANCZOS): + raise ValueError("unknown resampling filter") + + self.load() + + size=tuple(size) + if self.size == size: + return self._new(self.im) + + if self.mode in ("1", "P"): + resample = NEAREST + + if self.mode == 'RGBA': + return self.convert('RGBa').resize(size, resample).convert('RGBA') + + return self._new(self.im.resize(size, resample)) + + def rotate(self, angle, resample=NEAREST, expand=0): + """ + Returns a rotated copy of this image. This method returns a + copy of this image, rotated the given number of degrees counter + clockwise around its centre. + + :param angle: In degrees counter clockwise. + :param resample: An optional resampling filter. This can be + one of :py:attr:`PIL.Image.NEAREST` (use nearest neighbour), + :py:attr:`PIL.Image.BILINEAR` (linear interpolation in a 2x2 + environment), or :py:attr:`PIL.Image.BICUBIC` + (cubic spline interpolation in a 4x4 environment). + If omitted, or if the image has mode "1" or "P", it is + set :py:attr:`PIL.Image.NEAREST`. + :param expand: Optional expansion flag. If true, expands the output + image to make it large enough to hold the entire rotated image. + If false or omitted, make the output image the same size as the + input image. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + if expand: + import math + angle = -angle * math.pi / 180 + matrix = [ + math.cos(angle), math.sin(angle), 0.0, + -math.sin(angle), math.cos(angle), 0.0 + ] + + def transform(x, y, matrix=matrix): + (a, b, c, d, e, f) = matrix + return a*x + b*y + c, d*x + e*y + f + + # calculate output size + w, h = self.size + xx = [] + yy = [] + for x, y in ((0, 0), (w, 0), (w, h), (0, h)): + x, y = transform(x, y) + xx.append(x) + yy.append(y) + w = int(math.ceil(max(xx)) - math.floor(min(xx))) + h = int(math.ceil(max(yy)) - math.floor(min(yy))) + + # adjust center + x, y = transform(w / 2.0, h / 2.0) + matrix[2] = self.size[0] / 2.0 - x + matrix[5] = self.size[1] / 2.0 - y + + return self.transform((w, h), AFFINE, matrix, resample) + + if resample not in (NEAREST, BILINEAR, BICUBIC): + raise ValueError("unknown resampling filter") + + self.load() + + if self.mode in ("1", "P"): + resample = NEAREST + + return self._new(self.im.rotate(angle, resample)) + + def save(self, fp, format=None, **params): + """ + Saves this image under the given filename. If no format is + specified, the format to use is determined from the filename + extension, if possible. + + Keyword options can be used to provide additional instructions + to the writer. If a writer doesn't recognise an option, it is + silently ignored. The available options are described in the + :doc:`image format documentation + <../handbook/image-file-formats>` for each writer. + + You can use a file object instead of a filename. In this case, + you must always specify the format. The file object must + implement the ``seek``, ``tell``, and ``write`` + methods, and be opened in binary mode. + + :param fp: File name or file object. + :param format: Optional format override. If omitted, the + format to use is determined from the filename extension. + If a file object was used instead of a filename, this + parameter should always be used. + :param options: Extra parameters to the image writer. + :returns: None + :exception KeyError: If the output format could not be determined + from the file name. Use the format option to solve this. + :exception IOError: If the file could not be written. The file + may have been created, and may contain partial data. + """ + + if isPath(fp): + filename = fp + else: + if hasattr(fp, "name") and isPath(fp.name): + filename = fp.name + else: + filename = "" + + # may mutate self! + self.load() + + self.encoderinfo = params + self.encoderconfig = () + + preinit() + + ext = os.path.splitext(filename)[1].lower() + + if not format: + try: + format = EXTENSION[ext] + except KeyError: + init() + try: + format = EXTENSION[ext] + except KeyError: + raise KeyError(ext) # unknown extension + + try: + save_handler = SAVE[format.upper()] + except KeyError: + init() + save_handler = SAVE[format.upper()] # unknown format + + if isPath(fp): + fp = builtins.open(fp, "wb") + close = 1 + else: + close = 0 + + try: + save_handler(self, fp, filename) + finally: + # do what we can to clean up + if close: + fp.close() + + def seek(self, frame): + """ + Seeks to the given frame in this sequence file. If you seek + beyond the end of the sequence, the method raises an + **EOFError** exception. When a sequence file is opened, the + library automatically seeks to frame 0. + + Note that in the current version of the library, most sequence + formats only allows you to seek to the next frame. + + See :py:meth:`~PIL.Image.Image.tell`. + + :param frame: Frame number, starting at 0. + :exception EOFError: If the call attempts to seek beyond the end + of the sequence. + """ + + # overridden by file handlers + if frame != 0: + raise EOFError + + def show(self, title=None, command=None): + """ + Displays this image. This method is mainly intended for + debugging purposes. + + On Unix platforms, this method saves the image to a temporary + PPM file, and calls the **xv** utility. + + On Windows, it saves the image to a temporary BMP file, and uses + the standard BMP display utility to show it (usually Paint). + + :param title: Optional title to use for the image window, + where possible. + :param command: command used to show the image + """ + + _show(self, title=title, command=command) + + def split(self): + """ + Split this image into individual bands. This method returns a + tuple of individual image bands from an image. For example, + splitting an "RGB" image creates three new images each + containing a copy of one of the original bands (red, green, + blue). + + :returns: A tuple containing bands. + """ + + self.load() + if self.im.bands == 1: + ims = [self.copy()] + else: + ims = [] + for i in range(self.im.bands): + ims.append(self._new(self.im.getband(i))) + return tuple(ims) + + def tell(self): + """ + Returns the current frame number. See :py:meth:`~PIL.Image.Image.seek`. + + :returns: Frame number, starting with 0. + """ + return 0 + + def thumbnail(self, size, resample=BICUBIC): + """ + Make this image into a thumbnail. This method modifies the + image to contain a thumbnail version of itself, no larger than + the given size. This method calculates an appropriate thumbnail + size to preserve the aspect of the image, calls the + :py:meth:`~PIL.Image.Image.draft` method to configure the file reader + (where applicable), and finally resizes the image. + + Note that this function modifies the :py:class:`~PIL.Image.Image` + object in place. If you need to use the full resolution image as well, + apply this method to a :py:meth:`~PIL.Image.Image.copy` of the original + image. + + :param size: Requested size. + :param resample: Optional resampling filter. This can be one + of :py:attr:`PIL.Image.NEAREST`, :py:attr:`PIL.Image.BILINEAR`, + :py:attr:`PIL.Image.BICUBIC`, or :py:attr:`PIL.Image.LANCZOS`. + If omitted, it defaults to :py:attr:`PIL.Image.BICUBIC`. + (was :py:attr:`PIL.Image.NEAREST` prior to version 2.5.0) + :returns: None + """ + + # preserve aspect ratio + x, y = self.size + if x > size[0]: + y = int(max(y * size[0] / x, 1)) + x = int(size[0]) + if y > size[1]: + x = int(max(x * size[1] / y, 1)) + y = int(size[1]) + size = x, y + + if size == self.size: + return + + self.draft(None, size) + + im = self.resize(size, resample) + + self.im = im.im + self.mode = im.mode + self.size = size + + self.readonly = 0 + self.pyaccess = None + + # FIXME: the different tranform methods need further explanation + # instead of bloating the method docs, add a separate chapter. + def transform(self, size, method, data=None, resample=NEAREST, fill=1): + """ + Transforms this image. This method creates a new image with the + given size, and the same mode as the original, and copies data + to the new image using the given transform. + + :param size: The output size. + :param method: The transformation method. This is one of + :py:attr:`PIL.Image.EXTENT` (cut out a rectangular subregion), + :py:attr:`PIL.Image.AFFINE` (affine transform), + :py:attr:`PIL.Image.PERSPECTIVE` (perspective transform), + :py:attr:`PIL.Image.QUAD` (map a quadrilateral to a rectangle), or + :py:attr:`PIL.Image.MESH` (map a number of source quadrilaterals + in one operation). + :param data: Extra data to the transformation method. + :param resample: Optional resampling filter. It can be one of + :py:attr:`PIL.Image.NEAREST` (use nearest neighbour), + :py:attr:`PIL.Image.BILINEAR` (linear interpolation in a 2x2 + environment), or :py:attr:`PIL.Image.BICUBIC` (cubic spline + interpolation in a 4x4 environment). If omitted, or if the image + has mode "1" or "P", it is set to :py:attr:`PIL.Image.NEAREST`. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + if self.mode == 'RGBA': + return self.convert('RGBa').transform( + size, method, data, resample, fill).convert('RGBA') + + if isinstance(method, ImageTransformHandler): + return method.transform(size, self, resample=resample, fill=fill) + if hasattr(method, "getdata"): + # compatibility w. old-style transform objects + method, data = method.getdata() + if data is None: + raise ValueError("missing method data") + + im = new(self.mode, size, None) + if method == MESH: + # list of quads + for box, quad in data: + im.__transformer(box, self, QUAD, quad, resample, fill) + else: + im.__transformer((0, 0)+size, self, method, data, resample, fill) + + return im + + def __transformer(self, box, image, method, data, + resample=NEAREST, fill=1): + + # FIXME: this should be turned into a lazy operation (?) + + w = box[2]-box[0] + h = box[3]-box[1] + + if method == AFFINE: + # change argument order to match implementation + data = (data[2], data[0], data[1], + data[5], data[3], data[4]) + elif method == EXTENT: + # convert extent to an affine transform + x0, y0, x1, y1 = data + xs = float(x1 - x0) / w + ys = float(y1 - y0) / h + method = AFFINE + data = (x0 + xs/2, xs, 0, y0 + ys/2, 0, ys) + elif method == PERSPECTIVE: + # change argument order to match implementation + data = (data[2], data[0], data[1], + data[5], data[3], data[4], + data[6], data[7]) + elif method == QUAD: + # quadrilateral warp. data specifies the four corners + # given as NW, SW, SE, and NE. + nw = data[0:2] + sw = data[2:4] + se = data[4:6] + ne = data[6:8] + x0, y0 = nw + As = 1.0 / w + At = 1.0 / h + data = (x0, (ne[0]-x0)*As, (sw[0]-x0)*At, + (se[0]-sw[0]-ne[0]+x0)*As*At, + y0, (ne[1]-y0)*As, (sw[1]-y0)*At, + (se[1]-sw[1]-ne[1]+y0)*As*At) + else: + raise ValueError("unknown transformation method") + + if resample not in (NEAREST, BILINEAR, BICUBIC): + raise ValueError("unknown resampling filter") + + image.load() + + self.load() + + if image.mode in ("1", "P"): + resample = NEAREST + + self.im.transform2(box, image.im, method, data, resample, fill) + + def transpose(self, method): + """ + Transpose image (flip or rotate in 90 degree steps) + + :param method: One of :py:attr:`PIL.Image.FLIP_LEFT_RIGHT`, + :py:attr:`PIL.Image.FLIP_TOP_BOTTOM`, :py:attr:`PIL.Image.ROTATE_90`, + :py:attr:`PIL.Image.ROTATE_180`, :py:attr:`PIL.Image.ROTATE_270` or + :py:attr:`PIL.Image.TRANSPOSE`. + :returns: Returns a flipped or rotated copy of this image. + """ + + self.load() + return self._new(self.im.transpose(method)) + + def effect_spread(self, distance): + """ + Randomly spread pixels in an image. + + :param distance: Distance to spread pixels. + """ + self.load() + im = self.im.effect_spread(distance) + return self._new(im) + + +# -------------------------------------------------------------------- +# Lazy operations + +class _ImageCrop(Image): + + def __init__(self, im, box): + + Image.__init__(self) + + x0, y0, x1, y1 = box + if x1 < x0: + x1 = x0 + if y1 < y0: + y1 = y0 + + self.mode = im.mode + self.size = x1-x0, y1-y0 + + self.__crop = x0, y0, x1, y1 + + self.im = im.im + + def load(self): + + # lazy evaluation! + if self.__crop: + self.im = self.im.crop(self.__crop) + self.__crop = None + + if self.im: + return self.im.pixel_access(self.readonly) + + # FIXME: future versions should optimize crop/paste + # sequences! + + +# -------------------------------------------------------------------- +# Abstract handlers. + +class ImagePointHandler: + # used as a mixin by point transforms (for use with im.point) + pass + + +class ImageTransformHandler: + # used as a mixin by geometry transforms (for use with im.transform) + pass + + +# -------------------------------------------------------------------- +# Factories + +# +# Debugging + +def _wedge(): + "Create greyscale wedge (for debugging only)" + + return Image()._new(core.wedge("L")) + + +def new(mode, size, color=0): + """ + Creates a new image with the given mode and size. + + :param mode: The mode to use for the new image. See: + :ref:`concept-modes`. + :param size: A 2-tuple, containing (width, height) in pixels. + :param color: What color to use for the image. Default is black. + If given, this should be a single integer or floating point value + for single-band modes, and a tuple for multi-band modes (one value + per band). When creating RGB images, you can also use color + strings as supported by the ImageColor module. If the color is + None, the image is not initialised. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + if color is None: + # don't initialize + return Image()._new(core.new(mode, size)) + + if isStringType(color): + # css3-style specifier + + from PIL import ImageColor + color = ImageColor.getcolor(color, mode) + + return Image()._new(core.fill(mode, size, color)) + + +def frombytes(mode, size, data, decoder_name="raw", *args): + """ + Creates a copy of an image memory from pixel data in a buffer. + + In its simplest form, this function takes three arguments + (mode, size, and unpacked pixel data). + + You can also use any pixel decoder supported by PIL. For more + information on available decoders, see the section + **Writing Your Own File Decoder**. + + Note that this function decodes pixel data only, not entire images. + If you have an entire image in a string, wrap it in a + :py:class:`~io.BytesIO` object, and use :py:func:`~PIL.Image.open` to load + it. + + :param mode: The image mode. See: :ref:`concept-modes`. + :param size: The image size. + :param data: A byte buffer containing raw data for the given mode. + :param decoder_name: What decoder to use. + :param args: Additional parameters for the given decoder. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + # may pass tuple instead of argument list + if len(args) == 1 and isinstance(args[0], tuple): + args = args[0] + + if decoder_name == "raw" and args == (): + args = mode + + im = new(mode, size) + im.frombytes(data, decoder_name, args) + return im + + +def fromstring(*args, **kw): + """Deprecated alias to frombytes. + + .. deprecated:: 2.0 + """ + warnings.warn( + 'fromstring() is deprecated. Please call frombytes() instead.', + DeprecationWarning, + stacklevel=2 + ) + return frombytes(*args, **kw) + + +def frombuffer(mode, size, data, decoder_name="raw", *args): + """ + Creates an image memory referencing pixel data in a byte buffer. + + This function is similar to :py:func:`~PIL.Image.frombytes`, but uses data + in the byte buffer, where possible. This means that changes to the + original buffer object are reflected in this image). Not all modes can + share memory; supported modes include "L", "RGBX", "RGBA", and "CMYK". + + Note that this function decodes pixel data only, not entire images. + If you have an entire image file in a string, wrap it in a + **BytesIO** object, and use :py:func:`~PIL.Image.open` to load it. + + In the current version, the default parameters used for the "raw" decoder + differs from that used for :py:func:`~PIL.Image.fromstring`. This is a + bug, and will probably be fixed in a future release. The current release + issues a warning if you do this; to disable the warning, you should provide + the full set of parameters. See below for details. + + :param mode: The image mode. See: :ref:`concept-modes`. + :param size: The image size. + :param data: A bytes or other buffer object containing raw + data for the given mode. + :param decoder_name: What decoder to use. + :param args: Additional parameters for the given decoder. For the + default encoder ("raw"), it's recommended that you provide the + full set of parameters:: + + frombuffer(mode, size, data, "raw", mode, 0, 1) + + :returns: An :py:class:`~PIL.Image.Image` object. + + .. versionadded:: 1.1.4 + """ + + # may pass tuple instead of argument list + if len(args) == 1 and isinstance(args[0], tuple): + args = args[0] + + if decoder_name == "raw": + if args == (): + if warnings: + warnings.warn( + "the frombuffer defaults may change in a future release; " + "for portability, change the call to read:\n" + " frombuffer(mode, size, data, 'raw', mode, 0, 1)", + RuntimeWarning, stacklevel=2 + ) + args = mode, 0, -1 # may change to (mode, 0, 1) post-1.1.6 + if args[0] in _MAPMODES: + im = new(mode, (1, 1)) + im = im._new( + core.map_buffer(data, size, decoder_name, None, 0, args) + ) + im.readonly = 1 + return im + + return frombytes(mode, size, data, decoder_name, args) + + +def fromarray(obj, mode=None): + """ + Creates an image memory from an object exporting the array interface + (using the buffer protocol). + + If obj is not contiguous, then the tobytes method is called + and :py:func:`~PIL.Image.frombuffer` is used. + + :param obj: Object with array interface + :param mode: Mode to use (will be determined from type if None) + See: :ref:`concept-modes`. + :returns: An image object. + + .. versionadded:: 1.1.6 + """ + arr = obj.__array_interface__ + shape = arr['shape'] + ndim = len(shape) + try: + strides = arr['strides'] + except KeyError: + strides = None + if mode is None: + try: + typekey = (1, 1) + shape[2:], arr['typestr'] + mode, rawmode = _fromarray_typemap[typekey] + except KeyError: + # print typekey + raise TypeError("Cannot handle this data type") + else: + rawmode = mode + if mode in ["1", "L", "I", "P", "F"]: + ndmax = 2 + elif mode == "RGB": + ndmax = 3 + else: + ndmax = 4 + if ndim > ndmax: + raise ValueError("Too many dimensions: %d > %d." % (ndim, ndmax)) + + size = shape[1], shape[0] + if strides is not None: + if hasattr(obj, 'tobytes'): + obj = obj.tobytes() + else: + obj = obj.tostring() + + return frombuffer(mode, size, obj, "raw", rawmode, 0, 1) + +_fromarray_typemap = { + # (shape, typestr) => mode, rawmode + # first two members of shape are set to one + # ((1, 1), "|b1"): ("1", "1"), # broken + ((1, 1), "|u1"): ("L", "L"), + ((1, 1), "|i1"): ("I", "I;8"), + ((1, 1), "i2"): ("I", "I;16B"), + ((1, 1), "i4"): ("I", "I;32B"), + ((1, 1), "f4"): ("F", "F;32BF"), + ((1, 1), "f8"): ("F", "F;64BF"), + ((1, 1, 3), "|u1"): ("RGB", "RGB"), + ((1, 1, 4), "|u1"): ("RGBA", "RGBA"), + } + +# shortcuts +_fromarray_typemap[((1, 1), _ENDIAN + "i4")] = ("I", "I") +_fromarray_typemap[((1, 1), _ENDIAN + "f4")] = ("F", "F") + + +def _decompression_bomb_check(size): + if MAX_IMAGE_PIXELS is None: + return + + pixels = size[0] * size[1] + + if pixels > MAX_IMAGE_PIXELS: + warnings.warn( + "Image size (%d pixels) exceeds limit of %d pixels, " + "could be decompression bomb DOS attack." % + (pixels, MAX_IMAGE_PIXELS), + DecompressionBombWarning) + + +def open(fp, mode="r"): + """ + Opens and identifies the given image file. + + This is a lazy operation; this function identifies the file, but + the file remains open and the actual image data is not read from + the file until you try to process the data (or call the + :py:meth:`~PIL.Image.Image.load` method). See + :py:func:`~PIL.Image.new`. + + :param file: A filename (string) or a file object. The file object + must implement :py:meth:`~file.read`, :py:meth:`~file.seek`, and + :py:meth:`~file.tell` methods, and be opened in binary mode. + :param mode: The mode. If given, this argument must be "r". + :returns: An :py:class:`~PIL.Image.Image` object. + :exception IOError: If the file cannot be found, or the image cannot be + opened and identified. + """ + + if mode != "r": + raise ValueError("bad mode %r" % mode) + + if isPath(fp): + filename = fp + fp = builtins.open(fp, "rb") + else: + filename = "" + + prefix = fp.read(16) + + preinit() + + for i in ID: + try: + factory, accept = OPEN[i] + if not accept or accept(prefix): + fp.seek(0) + im = factory(fp, filename) + _decompression_bomb_check(im.size) + return im + except (SyntaxError, IndexError, TypeError): + # import traceback + # traceback.print_exc() + pass + + if init(): + + for i in ID: + try: + factory, accept = OPEN[i] + if not accept or accept(prefix): + fp.seek(0) + im = factory(fp, filename) + _decompression_bomb_check(im.size) + return im + except (SyntaxError, IndexError, TypeError): + # import traceback + # traceback.print_exc() + pass + + raise IOError("cannot identify image file %r" + % (filename if filename else fp)) + + +# +# Image processing. + +def alpha_composite(im1, im2): + """ + Alpha composite im2 over im1. + + :param im1: The first image. + :param im2: The second image. Must have the same mode and size as + the first image. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + im1.load() + im2.load() + return im1._new(core.alpha_composite(im1.im, im2.im)) + + +def blend(im1, im2, alpha): + """ + Creates a new image by interpolating between two input images, using + a constant alpha.:: + + out = image1 * (1.0 - alpha) + image2 * alpha + + :param im1: The first image. + :param im2: The second image. Must have the same mode and size as + the first image. + :param alpha: The interpolation alpha factor. If alpha is 0.0, a + copy of the first image is returned. If alpha is 1.0, a copy of + the second image is returned. There are no restrictions on the + alpha value. If necessary, the result is clipped to fit into + the allowed output range. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + im1.load() + im2.load() + return im1._new(core.blend(im1.im, im2.im, alpha)) + + +def composite(image1, image2, mask): + """ + Create composite image by blending images using a transparency mask. + + :param image1: The first image. + :param image2: The second image. Must have the same mode and + size as the first image. + :param mask: A mask image. This image can have mode + "1", "L", or "RGBA", and must have the same size as the + other two images. + """ + + image = image2.copy() + image.paste(image1, None, mask) + return image + + +def eval(image, *args): + """ + Applies the function (which should take one argument) to each pixel + in the given image. If the image has more than one band, the same + function is applied to each band. Note that the function is + evaluated once for each possible pixel value, so you cannot use + random components or other generators. + + :param image: The input image. + :param function: A function object, taking one integer argument. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + return image.point(args[0]) + + +def merge(mode, bands): + """ + Merge a set of single band images into a new multiband image. + + :param mode: The mode to use for the output image. See: + :ref:`concept-modes`. + :param bands: A sequence containing one single-band image for + each band in the output image. All bands must have the + same size. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + if getmodebands(mode) != len(bands) or "*" in mode: + raise ValueError("wrong number of bands") + for im in bands[1:]: + if im.mode != getmodetype(mode): + raise ValueError("mode mismatch") + if im.size != bands[0].size: + raise ValueError("size mismatch") + im = core.new(mode, bands[0].size) + for i in range(getmodebands(mode)): + bands[i].load() + im.putband(bands[i].im, i) + return bands[0]._new(im) + + +# -------------------------------------------------------------------- +# Plugin registry + +def register_open(id, factory, accept=None): + """ + Register an image file plugin. This function should not be used + in application code. + + :param id: An image format identifier. + :param factory: An image file factory method. + :param accept: An optional function that can be used to quickly + reject images having another format. + """ + id = id.upper() + ID.append(id) + OPEN[id] = factory, accept + + +def register_mime(id, mimetype): + """ + Registers an image MIME type. This function should not be used + in application code. + + :param id: An image format identifier. + :param mimetype: The image MIME type for this format. + """ + MIME[id.upper()] = mimetype + + +def register_save(id, driver): + """ + Registers an image save function. This function should not be + used in application code. + + :param id: An image format identifier. + :param driver: A function to save images in this format. + """ + SAVE[id.upper()] = driver + + +def register_extension(id, extension): + """ + Registers an image extension. This function should not be + used in application code. + + :param id: An image format identifier. + :param extension: An extension used for this format. + """ + EXTENSION[extension.lower()] = id.upper() + + +# -------------------------------------------------------------------- +# Simple display support. User code may override this. + +def _show(image, **options): + # override me, as necessary + _showxv(image, **options) + + +def _showxv(image, title=None, **options): + from PIL import ImageShow + ImageShow.show(image, title, **options) + + +# -------------------------------------------------------------------- +# Effects + +def effect_mandelbrot(size, extent, quality): + """ + Generate a Mandelbrot set covering the given extent. + + :param size: The requested size in pixels, as a 2-tuple: + (width, height). + :param extent: The extent to cover, as a 4-tuple: + (x0, y0, x1, y2). + :param quality: Quality. + """ + return Image()._new(core.effect_mandelbrot(size, extent, quality)) + + +def effect_noise(size, sigma): + """ + Generate Gaussian noise centered around 128. + + :param size: The requested size in pixels, as a 2-tuple: + (width, height). + :param sigma: Standard deviation of noise. + """ + return Image()._new(core.effect_noise(size, sigma)) + +# End of file diff --git a/pyPackages/pillowarmv7l/PIL/ImageChops.py b/pyPackages/pillowarmv7l/PIL/ImageChops.py new file mode 100644 index 0000000..ba5350e --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/ImageChops.py @@ -0,0 +1,283 @@ +# +# The Python Imaging Library. +# $Id$ +# +# standard channel operations +# +# History: +# 1996-03-24 fl Created +# 1996-08-13 fl Added logical operations (for "1" images) +# 2000-10-12 fl Added offset method (from Image.py) +# +# Copyright (c) 1997-2000 by Secret Labs AB +# Copyright (c) 1996-2000 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image + + +def constant(image, value): + """Fill a channel with a given grey level. + + :rtype: :py:class:`~PIL.Image.Image` + """ + + return Image.new("L", image.size, value) + + +def duplicate(image): + """Copy a channel. Alias for :py:meth:`PIL.Image.Image.copy`. + + :rtype: :py:class:`~PIL.Image.Image` + """ + + return image.copy() + + +def invert(image): + """ + Invert an image (channel). + + .. code-block:: python + + out = MAX - image + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image.load() + return image._new(image.im.chop_invert()) + + +def lighter(image1, image2): + """ + Compares the two images, pixel by pixel, and returns a new image containing + the lighter values. + + .. code-block:: python + + out = max(image1, image2) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_lighter(image2.im)) + + +def darker(image1, image2): + """ + Compares the two images, pixel by pixel, and returns a new image + containing the darker values. + + .. code-block:: python + + out = min(image1, image2) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_darker(image2.im)) + + +def difference(image1, image2): + """ + Returns the absolute value of the pixel-by-pixel difference between the two + images. + + .. code-block:: python + + out = abs(image1 - image2) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_difference(image2.im)) + + +def multiply(image1, image2): + """ + Superimposes two images on top of each other. + + If you multiply an image with a solid black image, the result is black. If + you multiply with a solid white image, the image is unaffected. + + .. code-block:: python + + out = image1 * image2 / MAX + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_multiply(image2.im)) + + +def screen(image1, image2): + """ + Superimposes two inverted images on top of each other. + + .. code-block:: python + + out = MAX - ((MAX - image1) * (MAX - image2) / MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_screen(image2.im)) + + +def add(image1, image2, scale=1.0, offset=0): + """ + Adds two images, dividing the result by scale and adding the + offset. If omitted, scale defaults to 1.0, and offset to 0.0. + + .. code-block:: python + + out = ((image1 + image2) / scale + offset) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_add(image2.im, scale, offset)) + + +def subtract(image1, image2, scale=1.0, offset=0): + """ + Subtracts two images, dividing the result by scale and adding the + offset. If omitted, scale defaults to 1.0, and offset to 0.0. + + .. code-block:: python + + out = ((image1 - image2) / scale + offset) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_subtract(image2.im, scale, offset)) + + +def add_modulo(image1, image2): + """Add two images, without clipping the result. + + .. code-block:: python + + out = ((image1 + image2) % MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_add_modulo(image2.im)) + + +def subtract_modulo(image1, image2): + """Subtract two images, without clipping the result. + + .. code-block:: python + + out = ((image1 - image2) % MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_subtract_modulo(image2.im)) + + +def logical_and(image1, image2): + """Logical AND between two images. + + .. code-block:: python + + out = ((image1 and image2) % MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_and(image2.im)) + + +def logical_or(image1, image2): + """Logical OR between two images. + + .. code-block:: python + + out = ((image1 or image2) % MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_or(image2.im)) + + +def logical_xor(image1, image2): + """Logical XOR between two images. + + .. code-block:: python + + out = ((bool(image1) != bool(image2)) % MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_xor(image2.im)) + + +def blend(image1, image2, alpha): + """Blend images using constant transparency weight. Alias for + :py:meth:`PIL.Image.Image.blend`. + + :rtype: :py:class:`~PIL.Image.Image` + """ + + return Image.blend(image1, image2, alpha) + + +def composite(image1, image2, mask): + """Create composite using transparency mask. Alias for + :py:meth:`PIL.Image.Image.composite`. + + :rtype: :py:class:`~PIL.Image.Image` + """ + + return Image.composite(image1, image2, mask) + + +def offset(image, xoffset, yoffset=None): + """Returns a copy of the image where data has been offset by the given + distances. Data wraps around the edges. If **yoffset** is omitted, it + is assumed to be equal to **xoffset**. + + :param xoffset: The horizontal distance. + :param yoffset: The vertical distance. If omitted, both + distances are set to the same value. + :rtype: :py:class:`~PIL.Image.Image` + """ + + if yoffset is None: + yoffset = xoffset + image.load() + return image._new(image.im.offset(xoffset, yoffset)) diff --git a/pyPackages/pillowarmv7l/PIL/ImageCms.py b/pyPackages/pillowarmv7l/PIL/ImageCms.py new file mode 100644 index 0000000..ed219f7 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/ImageCms.py @@ -0,0 +1,972 @@ +# The Python Imaging Library. +# $Id$ + +# Optional color managment support, based on Kevin Cazabon's PyCMS +# library. + +# History: + +# 2009-03-08 fl Added to PIL. + +# Copyright (C) 2002-2003 Kevin Cazabon +# Copyright (c) 2009 by Fredrik Lundh +# Copyright (c) 2013 by Eric Soroos + +# See the README file for information on usage and redistribution. See +# below for the original description. + +from __future__ import print_function + +DESCRIPTION = """ +pyCMS + + a Python / PIL interface to the littleCMS ICC Color Management System + Copyright (C) 2002-2003 Kevin Cazabon + kevin@cazabon.com + http://www.cazabon.com + + pyCMS home page: http://www.cazabon.com/pyCMS + littleCMS home page: http://www.littlecms.com + (littleCMS is Copyright (C) 1998-2001 Marti Maria) + + Originally released under LGPL. Graciously donated to PIL in + March 2009, for distribution under the standard PIL license + + The pyCMS.py module provides a "clean" interface between Python/PIL and + pyCMSdll, taking care of some of the more complex handling of the direct + pyCMSdll functions, as well as error-checking and making sure that all + relevant data is kept together. + + While it is possible to call pyCMSdll functions directly, it's not highly + recommended. + + Version History: + + 1.0.0 pil Oct 2013 Port to LCMS 2. + + 0.1.0 pil mod March 10, 2009 + + Renamed display profile to proof profile. The proof + profile is the profile of the device that is being + simulated, not the profile of the device which is + actually used to display/print the final simulation + (that'd be the output profile) - also see LCMSAPI.txt + input colorspace -> using 'renderingIntent' -> proof + colorspace -> using 'proofRenderingIntent' -> output + colorspace + + Added LCMS FLAGS support. + Added FLAGS["SOFTPROOFING"] as default flag for + buildProofTransform (otherwise the proof profile/intent + would be ignored). + + 0.1.0 pil March 2009 - added to PIL, as PIL.ImageCms + + 0.0.2 alpha Jan 6, 2002 + + Added try/except statements arount type() checks of + potential CObjects... Python won't let you use type() + on them, and raises a TypeError (stupid, if you ask + me!) + + Added buildProofTransformFromOpenProfiles() function. + Additional fixes in DLL, see DLL code for details. + + 0.0.1 alpha first public release, Dec. 26, 2002 + + Known to-do list with current version (of Python interface, not pyCMSdll): + + none + +""" + +VERSION = "1.0.0 pil" + +# --------------------------------------------------------------------. + +from PIL import Image +try: + from PIL import _imagingcms +except ImportError as ex: + # Allow error import for doc purposes, but error out when accessing + # anything in core. + from _util import deferred_error + _imagingcms = deferred_error(ex) +from PIL._util import isStringType + +core = _imagingcms + +# +# intent/direction values + +INTENT_PERCEPTUAL = 0 +INTENT_RELATIVE_COLORIMETRIC = 1 +INTENT_SATURATION = 2 +INTENT_ABSOLUTE_COLORIMETRIC = 3 + +DIRECTION_INPUT = 0 +DIRECTION_OUTPUT = 1 +DIRECTION_PROOF = 2 + +# +# flags + +FLAGS = { + "MATRIXINPUT": 1, + "MATRIXOUTPUT": 2, + "MATRIXONLY": (1 | 2), + "NOWHITEONWHITEFIXUP": 4, # Don't hot fix scum dot + # Don't create prelinearization tables on precalculated transforms + # (internal use): + "NOPRELINEARIZATION": 16, + "GUESSDEVICECLASS": 32, # Guess device class (for transform2devicelink) + "NOTCACHE": 64, # Inhibit 1-pixel cache + "NOTPRECALC": 256, + "NULLTRANSFORM": 512, # Don't transform anyway + "HIGHRESPRECALC": 1024, # Use more memory to give better accurancy + "LOWRESPRECALC": 2048, # Use less memory to minimize resouces + "WHITEBLACKCOMPENSATION": 8192, + "BLACKPOINTCOMPENSATION": 8192, + "GAMUTCHECK": 4096, # Out of Gamut alarm + "SOFTPROOFING": 16384, # Do softproofing + "PRESERVEBLACK": 32768, # Black preservation + "NODEFAULTRESOURCEDEF": 16777216, # CRD special + "GRIDPOINTS": lambda n: ((n) & 0xFF) << 16 # Gridpoints +} + +_MAX_FLAG = 0 +for flag in FLAGS.values(): + if isinstance(flag, int): + _MAX_FLAG = _MAX_FLAG | flag + + +# --------------------------------------------------------------------. +# Experimental PIL-level API +# --------------------------------------------------------------------. + +## +# Profile. + +class ImageCmsProfile: + + def __init__(self, profile): + """ + :param profile: Either a string representing a filename, + a file like object containing a profile or a + low-level profile object + + """ + + if isStringType(profile): + self._set(core.profile_open(profile), profile) + elif hasattr(profile, "read"): + self._set(core.profile_frombytes(profile.read())) + else: + self._set(profile) # assume it's already a profile + + def _set(self, profile, filename=None): + self.profile = profile + self.filename = filename + if profile: + self.product_name = None # profile.product_name + self.product_info = None # profile.product_info + else: + self.product_name = None + self.product_info = None + + def tobytes(self): + """ + Returns the profile in a format suitable for embedding in + saved images. + + :returns: a bytes object containing the ICC profile. + """ + + return core.profile_tobytes(self.profile) + + +class ImageCmsTransform(Image.ImagePointHandler): + + # Transform. This can be used with the procedural API, or with the + # standard Image.point() method. + # + # Will return the output profile in the output.info['icc_profile']. + + def __init__(self, input, output, input_mode, output_mode, + intent=INTENT_PERCEPTUAL, proof=None, + proof_intent=INTENT_ABSOLUTE_COLORIMETRIC, flags=0): + if proof is None: + self.transform = core.buildTransform( + input.profile, output.profile, + input_mode, output_mode, + intent, + flags + ) + else: + self.transform = core.buildProofTransform( + input.profile, output.profile, proof.profile, + input_mode, output_mode, + intent, proof_intent, + flags + ) + # Note: inputMode and outputMode are for pyCMS compatibility only + self.input_mode = self.inputMode = input_mode + self.output_mode = self.outputMode = output_mode + + self.output_profile = output + + def point(self, im): + return self.apply(im) + + def apply(self, im, imOut=None): + im.load() + if imOut is None: + imOut = Image.new(self.output_mode, im.size, None) + self.transform.apply(im.im.id, imOut.im.id) + imOut.info['icc_profile'] = self.output_profile.tobytes() + return imOut + + def apply_in_place(self, im): + im.load() + if im.mode != self.output_mode: + raise ValueError("mode mismatch") # wrong output mode + self.transform.apply(im.im.id, im.im.id) + im.info['icc_profile'] = self.output_profile.tobytes() + return im + + +def get_display_profile(handle=None): + """ (experimental) Fetches the profile for the current display device. + :returns: None if the profile is not known. + """ + + import sys + if sys.platform == "win32": + from PIL import ImageWin + if isinstance(handle, ImageWin.HDC): + profile = core.get_display_profile_win32(handle, 1) + else: + profile = core.get_display_profile_win32(handle or 0) + else: + try: + get = _imagingcms.get_display_profile + except AttributeError: + return None + else: + profile = get() + return ImageCmsProfile(profile) + + +# --------------------------------------------------------------------. +# pyCMS compatible layer +# --------------------------------------------------------------------. + +class PyCMSError(Exception): + + """ (pyCMS) Exception class. + This is used for all errors in the pyCMS API. """ + pass + + +def profileToProfile( + im, inputProfile, outputProfile, renderingIntent=INTENT_PERCEPTUAL, + outputMode=None, inPlace=0, flags=0): + """ + (pyCMS) Applies an ICC transformation to a given image, mapping from + inputProfile to outputProfile. + + If the input or output profiles specified are not valid filenames, a + PyCMSError will be raised. If inPlace == TRUE and outputMode != im.mode, + a PyCMSError will be raised. If an error occurs during application of + the profiles, a PyCMSError will be raised. If outputMode is not a mode + supported by the outputProfile (or by pyCMS), a PyCMSError will be + raised. + + This function applies an ICC transformation to im from inputProfile's + color space to outputProfile's color space using the specified rendering + intent to decide how to handle out-of-gamut colors. + + OutputMode can be used to specify that a color mode conversion is to + be done using these profiles, but the specified profiles must be able + to handle that mode. I.e., if converting im from RGB to CMYK using + profiles, the input profile must handle RGB data, and the output + profile must handle CMYK data. + + :param im: An open PIL image object (i.e. Image.new(...) or + Image.open(...), etc.) + :param inputProfile: String, as a valid filename path to the ICC input + profile you wish to use for this image, or a profile object + :param outputProfile: String, as a valid filename path to the ICC output + profile you wish to use for this image, or a profile object + :param renderingIntent: Integer (0-3) specifying the rendering intent you + wish to use for the transform + + INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) + INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) + INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) + INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) + + see the pyCMS documentation for details on rendering intents and what + they do. + :param outputMode: A valid PIL mode for the output image (i.e. "RGB", + "CMYK", etc.). Note: if rendering the image "inPlace", outputMode + MUST be the same mode as the input, or omitted completely. If + omitted, the outputMode will be the same as the mode of the input + image (im.mode) + :param inPlace: Boolean (1 = True, None or 0 = False). If True, the + original image is modified in-place, and None is returned. If False + (default), a new Image object is returned with the transform applied. + :param flags: Integer (0-...) specifying additional flags + :returns: Either None or a new PIL image object, depending on value of + inPlace + :exception PyCMSError: + """ + + if outputMode is None: + outputMode = im.mode + + if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3): + raise PyCMSError("renderingIntent must be an integer between 0 and 3") + + if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): + raise PyCMSError( + "flags must be an integer between 0 and %s" + _MAX_FLAG) + + try: + if not isinstance(inputProfile, ImageCmsProfile): + inputProfile = ImageCmsProfile(inputProfile) + if not isinstance(outputProfile, ImageCmsProfile): + outputProfile = ImageCmsProfile(outputProfile) + transform = ImageCmsTransform( + inputProfile, outputProfile, im.mode, outputMode, + renderingIntent, flags=flags + ) + if inPlace: + transform.apply_in_place(im) + imOut = None + else: + imOut = transform.apply(im) + except (IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + + return imOut + + +def getOpenProfile(profileFilename): + """ + (pyCMS) Opens an ICC profile file. + + The PyCMSProfile object can be passed back into pyCMS for use in creating + transforms and such (as in ImageCms.buildTransformFromOpenProfiles()). + + If profileFilename is not a vaild filename for an ICC profile, a PyCMSError + will be raised. + + :param profileFilename: String, as a valid filename path to the ICC profile + you wish to open, or a file-like object. + :returns: A CmsProfile class object. + :exception PyCMSError: + """ + + try: + return ImageCmsProfile(profileFilename) + except (IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + + +def buildTransform( + inputProfile, outputProfile, inMode, outMode, + renderingIntent=INTENT_PERCEPTUAL, flags=0): + """ + (pyCMS) Builds an ICC transform mapping from the inputProfile to the + outputProfile. Use applyTransform to apply the transform to a given + image. + + If the input or output profiles specified are not valid filenames, a + PyCMSError will be raised. If an error occurs during creation of the + transform, a PyCMSError will be raised. + + If inMode or outMode are not a mode supported by the outputProfile (or + by pyCMS), a PyCMSError will be raised. + + This function builds and returns an ICC transform from the inputProfile + to the outputProfile using the renderingIntent to determine what to do + with out-of-gamut colors. It will ONLY work for converting images that + are in inMode to images that are in outMode color format (PIL mode, + i.e. "RGB", "RGBA", "CMYK", etc.). + + Building the transform is a fair part of the overhead in + ImageCms.profileToProfile(), so if you're planning on converting multiple + images using the same input/output settings, this can save you time. + Once you have a transform object, it can be used with + ImageCms.applyProfile() to convert images without the need to re-compute + the lookup table for the transform. + + The reason pyCMS returns a class object rather than a handle directly + to the transform is that it needs to keep track of the PIL input/output + modes that the transform is meant for. These attributes are stored in + the "inMode" and "outMode" attributes of the object (which can be + manually overridden if you really want to, but I don't know of any + time that would be of use, or would even work). + + :param inputProfile: String, as a valid filename path to the ICC input + profile you wish to use for this transform, or a profile object + :param outputProfile: String, as a valid filename path to the ICC output + profile you wish to use for this transform, or a profile object + :param inMode: String, as a valid PIL mode that the appropriate profile + also supports (i.e. "RGB", "RGBA", "CMYK", etc.) + :param outMode: String, as a valid PIL mode that the appropriate profile + also supports (i.e. "RGB", "RGBA", "CMYK", etc.) + :param renderingIntent: Integer (0-3) specifying the rendering intent you + wish to use for the transform + + INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) + INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) + INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) + INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) + + see the pyCMS documentation for details on rendering intents and what + they do. + :param flags: Integer (0-...) specifying additional flags + :returns: A CmsTransform class object. + :exception PyCMSError: + """ + + if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3): + raise PyCMSError("renderingIntent must be an integer between 0 and 3") + + if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): + raise PyCMSError( + "flags must be an integer between 0 and %s" + _MAX_FLAG) + + try: + if not isinstance(inputProfile, ImageCmsProfile): + inputProfile = ImageCmsProfile(inputProfile) + if not isinstance(outputProfile, ImageCmsProfile): + outputProfile = ImageCmsProfile(outputProfile) + return ImageCmsTransform( + inputProfile, outputProfile, inMode, outMode, + renderingIntent, flags=flags) + except (IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + + +def buildProofTransform( + inputProfile, outputProfile, proofProfile, inMode, outMode, + renderingIntent=INTENT_PERCEPTUAL, + proofRenderingIntent=INTENT_ABSOLUTE_COLORIMETRIC, + flags=FLAGS["SOFTPROOFING"]): + """ + (pyCMS) Builds an ICC transform mapping from the inputProfile to the + outputProfile, but tries to simulate the result that would be + obtained on the proofProfile device. + + If the input, output, or proof profiles specified are not valid + filenames, a PyCMSError will be raised. + + If an error occurs during creation of the transform, a PyCMSError will + be raised. + + If inMode or outMode are not a mode supported by the outputProfile + (or by pyCMS), a PyCMSError will be raised. + + This function builds and returns an ICC transform from the inputProfile + to the outputProfile, but tries to simulate the result that would be + obtained on the proofProfile device using renderingIntent and + proofRenderingIntent to determine what to do with out-of-gamut + colors. This is known as "soft-proofing". It will ONLY work for + converting images that are in inMode to images that are in outMode + color format (PIL mode, i.e. "RGB", "RGBA", "CMYK", etc.). + + Usage of the resulting transform object is exactly the same as with + ImageCms.buildTransform(). + + Proof profiling is generally used when using an output device to get a + good idea of what the final printed/displayed image would look like on + the proofProfile device when it's quicker and easier to use the + output device for judging color. Generally, this means that the + output device is a monitor, or a dye-sub printer (etc.), and the simulated + device is something more expensive, complicated, or time consuming + (making it difficult to make a real print for color judgement purposes). + + Soft-proofing basically functions by adjusting the colors on the + output device to match the colors of the device being simulated. However, + when the simulated device has a much wider gamut than the output + device, you may obtain marginal results. + + :param inputProfile: String, as a valid filename path to the ICC input + profile you wish to use for this transform, or a profile object + :param outputProfile: String, as a valid filename path to the ICC output + (monitor, usually) profile you wish to use for this transform, or a + profile object + :param proofProfile: String, as a valid filename path to the ICC proof + profile you wish to use for this transform, or a profile object + :param inMode: String, as a valid PIL mode that the appropriate profile + also supports (i.e. "RGB", "RGBA", "CMYK", etc.) + :param outMode: String, as a valid PIL mode that the appropriate profile + also supports (i.e. "RGB", "RGBA", "CMYK", etc.) + :param renderingIntent: Integer (0-3) specifying the rendering intent you + wish to use for the input->proof (simulated) transform + + INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) + INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) + INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) + INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) + + see the pyCMS documentation for details on rendering intents and what + they do. + :param proofRenderingIntent: Integer (0-3) specifying the rendering intent you + wish to use for proof->output transform + + INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) + INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) + INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) + INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) + + see the pyCMS documentation for details on rendering intents and what + they do. + :param flags: Integer (0-...) specifying additional flags + :returns: A CmsTransform class object. + :exception PyCMSError: + """ + + if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3): + raise PyCMSError("renderingIntent must be an integer between 0 and 3") + + if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): + raise PyCMSError( + "flags must be an integer between 0 and %s" + _MAX_FLAG) + + try: + if not isinstance(inputProfile, ImageCmsProfile): + inputProfile = ImageCmsProfile(inputProfile) + if not isinstance(outputProfile, ImageCmsProfile): + outputProfile = ImageCmsProfile(outputProfile) + if not isinstance(proofProfile, ImageCmsProfile): + proofProfile = ImageCmsProfile(proofProfile) + return ImageCmsTransform( + inputProfile, outputProfile, inMode, outMode, renderingIntent, + proofProfile, proofRenderingIntent, flags) + except (IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + +buildTransformFromOpenProfiles = buildTransform +buildProofTransformFromOpenProfiles = buildProofTransform + + +def applyTransform(im, transform, inPlace=0): + """ + (pyCMS) Applies a transform to a given image. + + If im.mode != transform.inMode, a PyCMSError is raised. + + If inPlace == TRUE and transform.inMode != transform.outMode, a + PyCMSError is raised. + + If im.mode, transfer.inMode, or transfer.outMode is not supported by + pyCMSdll or the profiles you used for the transform, a PyCMSError is + raised. + + If an error occurs while the transform is being applied, a PyCMSError + is raised. + + This function applies a pre-calculated transform (from + ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles()) + to an image. The transform can be used for multiple images, saving + considerable calcuation time if doing the same conversion multiple times. + + If you want to modify im in-place instead of receiving a new image as + the return value, set inPlace to TRUE. This can only be done if + transform.inMode and transform.outMode are the same, because we can't + change the mode in-place (the buffer sizes for some modes are + different). The default behavior is to return a new Image object of + the same dimensions in mode transform.outMode. + + :param im: A PIL Image object, and im.mode must be the same as the inMode + supported by the transform. + :param transform: A valid CmsTransform class object + :param inPlace: Bool (1 == True, 0 or None == False). If True, im is + modified in place and None is returned, if False, a new Image object + with the transform applied is returned (and im is not changed). The + default is False. + :returns: Either None, or a new PIL Image object, depending on the value of + inPlace. The profile will be returned in the image's info['icc_profile']. + :exception PyCMSError: + """ + + try: + if inPlace: + transform.apply_in_place(im) + imOut = None + else: + imOut = transform.apply(im) + except (TypeError, ValueError) as v: + raise PyCMSError(v) + + return imOut + + +def createProfile(colorSpace, colorTemp=-1): + """ + (pyCMS) Creates a profile. + + If colorSpace not in ["LAB", "XYZ", "sRGB"], a PyCMSError is raised + + If using LAB and colorTemp != a positive integer, a PyCMSError is raised. + + If an error occurs while creating the profile, a PyCMSError is raised. + + Use this function to create common profiles on-the-fly instead of + having to supply a profile on disk and knowing the path to it. It + returns a normal CmsProfile object that can be passed to + ImageCms.buildTransformFromOpenProfiles() to create a transform to apply + to images. + + :param colorSpace: String, the color space of the profile you wish to + create. + Currently only "LAB", "XYZ", and "sRGB" are supported. + :param colorTemp: Positive integer for the white point for the profile, in + degrees Kelvin (i.e. 5000, 6500, 9600, etc.). The default is for D50 + illuminant if omitted (5000k). colorTemp is ONLY applied to LAB + profiles, and is ignored for XYZ and sRGB. + :returns: A CmsProfile class object + :exception PyCMSError: + """ + + if colorSpace not in ["LAB", "XYZ", "sRGB"]: + raise PyCMSError( + "Color space not supported for on-the-fly profile creation (%s)" + % colorSpace) + + if colorSpace == "LAB": + try: + colorTemp = float(colorTemp) + except: + raise PyCMSError( + "Color temperature must be numeric, \"%s\" not valid" + % colorTemp) + + try: + return core.createProfile(colorSpace, colorTemp) + except (TypeError, ValueError) as v: + raise PyCMSError(v) + + +def getProfileName(profile): + """ + + (pyCMS) Gets the internal product name for the given profile. + + If profile isn't a valid CmsProfile object or filename to a profile, + a PyCMSError is raised If an error occurs while trying to obtain the + name tag, a PyCMSError is raised. + + Use this function to obtain the INTERNAL name of the profile (stored + in an ICC tag in the profile itself), usually the one used when the + profile was originally created. Sometimes this tag also contains + additional information supplied by the creator. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: A string containing the internal name of the profile as stored + in an ICC tag. + :exception PyCMSError: + """ + + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + # do it in python, not c. + # // name was "%s - %s" (model, manufacturer) || Description , + # // but if the Model and Manufacturer were the same or the model + # // was long, Just the model, in 1.x + model = profile.profile.product_model + manufacturer = profile.profile.product_manufacturer + + if not (model or manufacturer): + return profile.profile.product_description + "\n" + if not manufacturer or len(model) > 30: + return model + "\n" + return "%s - %s\n" % (model, manufacturer) + + except (AttributeError, IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + + +def getProfileInfo(profile): + """ + (pyCMS) Gets the internal product information for the given profile. + + If profile isn't a valid CmsProfile object or filename to a profile, + a PyCMSError is raised. + + If an error occurs while trying to obtain the info tag, a PyCMSError + is raised + + Use this function to obtain the information stored in the profile's + info tag. This often contains details about the profile, and how it + was created, as supplied by the creator. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: A string containing the internal profile information stored in + an ICC tag. + :exception PyCMSError: + """ + + try: + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + # add an extra newline to preserve pyCMS compatibility + # Python, not C. the white point bits weren't working well, + # so skipping. + # // info was description \r\n\r\n copyright \r\n\r\n K007 tag \r\n\r\n whitepoint + description = profile.profile.product_description + cpright = profile.profile.product_copyright + arr = [] + for elt in (description, cpright): + if elt: + arr.append(elt) + return "\r\n\r\n".join(arr) + "\r\n\r\n" + + except (AttributeError, IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + + +def getProfileCopyright(profile): + """ + (pyCMS) Gets the copyright for the given profile. + + If profile isn't a valid CmsProfile object or filename to a profile, + a PyCMSError is raised. + + If an error occurs while trying to obtain the copyright tag, a PyCMSError + is raised + + Use this function to obtain the information stored in the profile's + copyright tag. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: A string containing the internal profile information stored in + an ICC tag. + :exception PyCMSError: + """ + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return profile.profile.product_copyright + "\n" + except (AttributeError, IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + + +def getProfileManufacturer(profile): + """ + (pyCMS) Gets the manufacturer for the given profile. + + If profile isn't a valid CmsProfile object or filename to a profile, + a PyCMSError is raised. + + If an error occurs while trying to obtain the manufacturer tag, a + PyCMSError is raised + + Use this function to obtain the information stored in the profile's + manufacturer tag. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: A string containing the internal profile information stored in + an ICC tag. + :exception PyCMSError: + """ + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return profile.profile.product_manufacturer + "\n" + except (AttributeError, IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + + +def getProfileModel(profile): + """ + (pyCMS) Gets the model for the given profile. + + If profile isn't a valid CmsProfile object or filename to a profile, + a PyCMSError is raised. + + If an error occurs while trying to obtain the model tag, a PyCMSError + is raised + + Use this function to obtain the information stored in the profile's + model tag. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: A string containing the internal profile information stored in + an ICC tag. + :exception PyCMSError: + """ + + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return profile.profile.product_model + "\n" + except (AttributeError, IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + + +def getProfileDescription(profile): + """ + (pyCMS) Gets the description for the given profile. + + If profile isn't a valid CmsProfile object or filename to a profile, + a PyCMSError is raised. + + If an error occurs while trying to obtain the description tag, a PyCMSError + is raised + + Use this function to obtain the information stored in the profile's + description tag. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: A string containing the internal profile information stored in an + ICC tag. + :exception PyCMSError: + """ + + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return profile.profile.product_description + "\n" + except (AttributeError, IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + + +def getDefaultIntent(profile): + """ + (pyCMS) Gets the default intent name for the given profile. + + If profile isn't a valid CmsProfile object or filename to a profile, + a PyCMSError is raised. + + If an error occurs while trying to obtain the default intent, a + PyCMSError is raised. + + Use this function to determine the default (and usually best optomized) + rendering intent for this profile. Most profiles support multiple + rendering intents, but are intended mostly for one type of conversion. + If you wish to use a different intent than returned, use + ImageCms.isIntentSupported() to verify it will work first. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: Integer 0-3 specifying the default rendering intent for this + profile. + + INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) + INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) + INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) + INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) + + see the pyCMS documentation for details on rendering intents and what + they do. + :exception PyCMSError: + """ + + try: + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return profile.profile.rendering_intent + except (AttributeError, IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + + +def isIntentSupported(profile, intent, direction): + """ + (pyCMS) Checks if a given intent is supported. + + Use this function to verify that you can use your desired + renderingIntent with profile, and that profile can be used for the + input/output/proof profile as you desire. + + Some profiles are created specifically for one "direction", can cannot + be used for others. Some profiles can only be used for certain + rendering intents... so it's best to either verify this before trying + to create a transform with them (using this function), or catch the + potential PyCMSError that will occur if they don't support the modes + you select. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :param intent: Integer (0-3) specifying the rendering intent you wish to + use with this profile + + INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) + INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) + INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) + INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) + + see the pyCMS documentation for details on rendering intents and what + they do. + :param direction: Integer specifing if the profile is to be used for input, + output, or proof + + INPUT = 0 (or use ImageCms.DIRECTION_INPUT) + OUTPUT = 1 (or use ImageCms.DIRECTION_OUTPUT) + PROOF = 2 (or use ImageCms.DIRECTION_PROOF) + + :returns: 1 if the intent/direction are supported, -1 if they are not. + :exception PyCMSError: + """ + + try: + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + # FIXME: I get different results for the same data w. different + # compilers. Bug in LittleCMS or in the binding? + if profile.profile.is_intent_supported(intent, direction): + return 1 + else: + return -1 + except (AttributeError, IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + + +def versions(): + """ + (pyCMS) Fetches versions. + """ + + import sys + return ( + VERSION, core.littlecms_version, + sys.version.split()[0], Image.VERSION + ) + +# -------------------------------------------------------------------- + +if __name__ == "__main__": + # create a cheap manual from the __doc__ strings for the functions above + + from PIL import ImageCms + print(__doc__) + + for f in dir(ImageCms): + doc = None + try: + exec("doc = %s.__doc__" % (f)) + if "pyCMS" in doc: + # so we don't get the __doc__ string for imported modules + print("=" * 80) + print("%s" % f) + print(doc) + except (AttributeError, TypeError): + pass + +# End of file diff --git a/pyPackages/pillowarmv7l/PIL/ImageColor.py b/pyPackages/pillowarmv7l/PIL/ImageColor.py new file mode 100644 index 0000000..fc95e6d --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/ImageColor.py @@ -0,0 +1,279 @@ +# +# The Python Imaging Library +# $Id$ +# +# map CSS3-style colour description strings to RGB +# +# History: +# 2002-10-24 fl Added support for CSS-style color strings +# 2002-12-15 fl Added RGBA support +# 2004-03-27 fl Fixed remaining int() problems for Python 1.5.2 +# 2004-07-19 fl Fixed gray/grey spelling issues +# 2009-03-05 fl Fixed rounding error in grayscale calculation +# +# Copyright (c) 2002-2004 by Secret Labs AB +# Copyright (c) 2002-2004 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image +import re + + +def getrgb(color): + """ + Convert a color string to an RGB tuple. If the string cannot be parsed, + this function raises a :py:exc:`ValueError` exception. + + .. versionadded:: 1.1.4 + + :param color: A color string + :return: ``(red, green, blue[, alpha])`` + """ + try: + rgb = colormap[color] + except KeyError: + try: + # fall back on case-insensitive lookup + rgb = colormap[color.lower()] + except KeyError: + rgb = None + # found color in cache + if rgb: + if isinstance(rgb, tuple): + return rgb + colormap[color] = rgb = getrgb(rgb) + return rgb + # check for known string formats + m = re.match("#\w\w\w$", color) + if m: + return ( + int(color[1]*2, 16), + int(color[2]*2, 16), + int(color[3]*2, 16) + ) + m = re.match("#\w\w\w\w\w\w$", color) + if m: + return ( + int(color[1:3], 16), + int(color[3:5], 16), + int(color[5:7], 16) + ) + m = re.match("rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color) + if m: + return ( + int(m.group(1)), + int(m.group(2)), + int(m.group(3)) + ) + m = re.match("rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color) + if m: + return ( + int((int(m.group(1)) * 255) / 100.0 + 0.5), + int((int(m.group(2)) * 255) / 100.0 + 0.5), + int((int(m.group(3)) * 255) / 100.0 + 0.5) + ) + m = re.match("hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color) + if m: + from colorsys import hls_to_rgb + rgb = hls_to_rgb( + float(m.group(1)) / 360.0, + float(m.group(3)) / 100.0, + float(m.group(2)) / 100.0, + ) + return ( + int(rgb[0] * 255 + 0.5), + int(rgb[1] * 255 + 0.5), + int(rgb[2] * 255 + 0.5) + ) + m = re.match("rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", + color) + if m: + return ( + int(m.group(1)), + int(m.group(2)), + int(m.group(3)), + int(m.group(4)) + ) + raise ValueError("unknown color specifier: %r" % color) + + +def getcolor(color, mode): + """ + Same as :py:func:`~PIL.ImageColor.getrgb`, but converts the RGB value to a + greyscale value if the mode is not color or a palette image. If the string + cannot be parsed, this function raises a :py:exc:`ValueError` exception. + + .. versionadded:: 1.1.4 + + :param color: A color string + :return: ``(graylevel [, alpha]) or (red, green, blue[, alpha])`` + """ + # same as getrgb, but converts the result to the given mode + color, alpha = getrgb(color), 255 + if len(color) == 4: + color, alpha = color[0:3], color[3] + + if Image.getmodebase(mode) == "L": + r, g, b = color + color = (r*299 + g*587 + b*114)//1000 + if mode[-1] == 'A': + return (color, alpha) + else: + if mode[-1] == 'A': + return color + (alpha,) + return color + +colormap = { + # X11 colour table (from "CSS3 module: Color working draft"), with + # gray/grey spelling issues fixed. This is a superset of HTML 4.0 + # colour names used in CSS 1. + "aliceblue": "#f0f8ff", + "antiquewhite": "#faebd7", + "aqua": "#00ffff", + "aquamarine": "#7fffd4", + "azure": "#f0ffff", + "beige": "#f5f5dc", + "bisque": "#ffe4c4", + "black": "#000000", + "blanchedalmond": "#ffebcd", + "blue": "#0000ff", + "blueviolet": "#8a2be2", + "brown": "#a52a2a", + "burlywood": "#deb887", + "cadetblue": "#5f9ea0", + "chartreuse": "#7fff00", + "chocolate": "#d2691e", + "coral": "#ff7f50", + "cornflowerblue": "#6495ed", + "cornsilk": "#fff8dc", + "crimson": "#dc143c", + "cyan": "#00ffff", + "darkblue": "#00008b", + "darkcyan": "#008b8b", + "darkgoldenrod": "#b8860b", + "darkgray": "#a9a9a9", + "darkgrey": "#a9a9a9", + "darkgreen": "#006400", + "darkkhaki": "#bdb76b", + "darkmagenta": "#8b008b", + "darkolivegreen": "#556b2f", + "darkorange": "#ff8c00", + "darkorchid": "#9932cc", + "darkred": "#8b0000", + "darksalmon": "#e9967a", + "darkseagreen": "#8fbc8f", + "darkslateblue": "#483d8b", + "darkslategray": "#2f4f4f", + "darkslategrey": "#2f4f4f", + "darkturquoise": "#00ced1", + "darkviolet": "#9400d3", + "deeppink": "#ff1493", + "deepskyblue": "#00bfff", + "dimgray": "#696969", + "dimgrey": "#696969", + "dodgerblue": "#1e90ff", + "firebrick": "#b22222", + "floralwhite": "#fffaf0", + "forestgreen": "#228b22", + "fuchsia": "#ff00ff", + "gainsboro": "#dcdcdc", + "ghostwhite": "#f8f8ff", + "gold": "#ffd700", + "goldenrod": "#daa520", + "gray": "#808080", + "grey": "#808080", + "green": "#008000", + "greenyellow": "#adff2f", + "honeydew": "#f0fff0", + "hotpink": "#ff69b4", + "indianred": "#cd5c5c", + "indigo": "#4b0082", + "ivory": "#fffff0", + "khaki": "#f0e68c", + "lavender": "#e6e6fa", + "lavenderblush": "#fff0f5", + "lawngreen": "#7cfc00", + "lemonchiffon": "#fffacd", + "lightblue": "#add8e6", + "lightcoral": "#f08080", + "lightcyan": "#e0ffff", + "lightgoldenrodyellow": "#fafad2", + "lightgreen": "#90ee90", + "lightgray": "#d3d3d3", + "lightgrey": "#d3d3d3", + "lightpink": "#ffb6c1", + "lightsalmon": "#ffa07a", + "lightseagreen": "#20b2aa", + "lightskyblue": "#87cefa", + "lightslategray": "#778899", + "lightslategrey": "#778899", + "lightsteelblue": "#b0c4de", + "lightyellow": "#ffffe0", + "lime": "#00ff00", + "limegreen": "#32cd32", + "linen": "#faf0e6", + "magenta": "#ff00ff", + "maroon": "#800000", + "mediumaquamarine": "#66cdaa", + "mediumblue": "#0000cd", + "mediumorchid": "#ba55d3", + "mediumpurple": "#9370db", + "mediumseagreen": "#3cb371", + "mediumslateblue": "#7b68ee", + "mediumspringgreen": "#00fa9a", + "mediumturquoise": "#48d1cc", + "mediumvioletred": "#c71585", + "midnightblue": "#191970", + "mintcream": "#f5fffa", + "mistyrose": "#ffe4e1", + "moccasin": "#ffe4b5", + "navajowhite": "#ffdead", + "navy": "#000080", + "oldlace": "#fdf5e6", + "olive": "#808000", + "olivedrab": "#6b8e23", + "orange": "#ffa500", + "orangered": "#ff4500", + "orchid": "#da70d6", + "palegoldenrod": "#eee8aa", + "palegreen": "#98fb98", + "paleturquoise": "#afeeee", + "palevioletred": "#db7093", + "papayawhip": "#ffefd5", + "peachpuff": "#ffdab9", + "peru": "#cd853f", + "pink": "#ffc0cb", + "plum": "#dda0dd", + "powderblue": "#b0e0e6", + "purple": "#800080", + "red": "#ff0000", + "rosybrown": "#bc8f8f", + "royalblue": "#4169e1", + "saddlebrown": "#8b4513", + "salmon": "#fa8072", + "sandybrown": "#f4a460", + "seagreen": "#2e8b57", + "seashell": "#fff5ee", + "sienna": "#a0522d", + "silver": "#c0c0c0", + "skyblue": "#87ceeb", + "slateblue": "#6a5acd", + "slategray": "#708090", + "slategrey": "#708090", + "snow": "#fffafa", + "springgreen": "#00ff7f", + "steelblue": "#4682b4", + "tan": "#d2b48c", + "teal": "#008080", + "thistle": "#d8bfd8", + "tomato": "#ff6347", + "turquoise": "#40e0d0", + "violet": "#ee82ee", + "wheat": "#f5deb3", + "white": "#ffffff", + "whitesmoke": "#f5f5f5", + "yellow": "#ffff00", + "yellowgreen": "#9acd32", +} diff --git a/pyPackages/pillowarmv7l/PIL/ImageDraw.py b/pyPackages/pillowarmv7l/PIL/ImageDraw.py new file mode 100644 index 0000000..a2a75d1 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/ImageDraw.py @@ -0,0 +1,383 @@ +# +# The Python Imaging Library +# $Id$ +# +# drawing interface operations +# +# History: +# 1996-04-13 fl Created (experimental) +# 1996-08-07 fl Filled polygons, ellipses. +# 1996-08-13 fl Added text support +# 1998-06-28 fl Handle I and F images +# 1998-12-29 fl Added arc; use arc primitive to draw ellipses +# 1999-01-10 fl Added shape stuff (experimental) +# 1999-02-06 fl Added bitmap support +# 1999-02-11 fl Changed all primitives to take options +# 1999-02-20 fl Fixed backwards compatibility +# 2000-10-12 fl Copy on write, when necessary +# 2001-02-18 fl Use default ink for bitmap/text also in fill mode +# 2002-10-24 fl Added support for CSS-style color strings +# 2002-12-10 fl Added experimental support for RGBA-on-RGB drawing +# 2002-12-11 fl Refactored low-level drawing API (work in progress) +# 2004-08-26 fl Made Draw() a factory function, added getdraw() support +# 2004-09-04 fl Added width support to line primitive +# 2004-09-10 fl Added font mode handling +# 2006-06-19 fl Added font bearing support (getmask2) +# +# Copyright (c) 1997-2006 by Secret Labs AB +# Copyright (c) 1996-2006 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import numbers + +from PIL import Image, ImageColor +from PIL._util import isStringType + +try: + import warnings +except ImportError: + warnings = None + + +## +# A simple 2D drawing interface for PIL images. +#

+# Application code should use the Draw factory, instead of +# directly. + +class ImageDraw: + + ## + # Create a drawing instance. + # + # @param im The image to draw in. + # @param mode Optional mode to use for color values. For RGB + # images, this argument can be RGB or RGBA (to blend the + # drawing into the image). For all other modes, this argument + # must be the same as the image mode. If omitted, the mode + # defaults to the mode of the image. + + def __init__(self, im, mode=None): + im.load() + if im.readonly: + im._copy() # make it writeable + blend = 0 + if mode is None: + mode = im.mode + if mode != im.mode: + if mode == "RGBA" and im.mode == "RGB": + blend = 1 + else: + raise ValueError("mode mismatch") + if mode == "P": + self.palette = im.palette + else: + self.palette = None + self.im = im.im + self.draw = Image.core.draw(self.im, blend) + self.mode = mode + if mode in ("I", "F"): + self.ink = self.draw.draw_ink(1, mode) + else: + self.ink = self.draw.draw_ink(-1, mode) + if mode in ("1", "P", "I", "F"): + # FIXME: fix Fill2 to properly support matte for I+F images + self.fontmode = "1" + else: + self.fontmode = "L" # aliasing is okay for other modes + self.fill = 0 + self.font = None + + ## + # Set the default pen color. + + def setink(self, ink): + # compatibility + if warnings: + warnings.warn( + "'setink' is deprecated; use keyword arguments instead", + DeprecationWarning, stacklevel=2 + ) + if isStringType(ink): + ink = ImageColor.getcolor(ink, self.mode) + if self.palette and not isinstance(ink, numbers.Number): + ink = self.palette.getcolor(ink) + self.ink = self.draw.draw_ink(ink, self.mode) + + ## + # Set the default background color. + + def setfill(self, onoff): + # compatibility + if warnings: + warnings.warn( + "'setfill' is deprecated; use keyword arguments instead", + DeprecationWarning, stacklevel=2 + ) + self.fill = onoff + + ## + # Set the default font. + + def setfont(self, font): + # compatibility + self.font = font + + ## + # Get the current default font. + + def getfont(self): + if not self.font: + # FIXME: should add a font repository + from PIL import ImageFont + self.font = ImageFont.load_default() + return self.font + + def _getink(self, ink, fill=None): + if ink is None and fill is None: + if self.fill: + fill = self.ink + else: + ink = self.ink + else: + if ink is not None: + if isStringType(ink): + ink = ImageColor.getcolor(ink, self.mode) + if self.palette and not isinstance(ink, numbers.Number): + ink = self.palette.getcolor(ink) + ink = self.draw.draw_ink(ink, self.mode) + if fill is not None: + if isStringType(fill): + fill = ImageColor.getcolor(fill, self.mode) + if self.palette and not isinstance(fill, numbers.Number): + fill = self.palette.getcolor(fill) + fill = self.draw.draw_ink(fill, self.mode) + return ink, fill + + ## + # Draw an arc. + + def arc(self, xy, start, end, fill=None): + ink, fill = self._getink(fill) + if ink is not None: + self.draw.draw_arc(xy, start, end, ink) + + ## + # Draw a bitmap. + + def bitmap(self, xy, bitmap, fill=None): + bitmap.load() + ink, fill = self._getink(fill) + if ink is None: + ink = fill + if ink is not None: + self.draw.draw_bitmap(xy, bitmap.im, ink) + + ## + # Draw a chord. + + def chord(self, xy, start, end, fill=None, outline=None): + ink, fill = self._getink(outline, fill) + if fill is not None: + self.draw.draw_chord(xy, start, end, fill, 1) + if ink is not None: + self.draw.draw_chord(xy, start, end, ink, 0) + + ## + # Draw an ellipse. + + def ellipse(self, xy, fill=None, outline=None): + ink, fill = self._getink(outline, fill) + if fill is not None: + self.draw.draw_ellipse(xy, fill, 1) + if ink is not None: + self.draw.draw_ellipse(xy, ink, 0) + + ## + # Draw a line, or a connected sequence of line segments. + + def line(self, xy, fill=None, width=0): + ink, fill = self._getink(fill) + if ink is not None: + self.draw.draw_lines(xy, ink, width) + + ## + # (Experimental) Draw a shape. + + def shape(self, shape, fill=None, outline=None): + # experimental + shape.close() + ink, fill = self._getink(outline, fill) + if fill is not None: + self.draw.draw_outline(shape, fill, 1) + if ink is not None: + self.draw.draw_outline(shape, ink, 0) + + ## + # Draw a pieslice. + + def pieslice(self, xy, start, end, fill=None, outline=None): + ink, fill = self._getink(outline, fill) + if fill is not None: + self.draw.draw_pieslice(xy, start, end, fill, 1) + if ink is not None: + self.draw.draw_pieslice(xy, start, end, ink, 0) + + ## + # Draw one or more individual pixels. + + def point(self, xy, fill=None): + ink, fill = self._getink(fill) + if ink is not None: + self.draw.draw_points(xy, ink) + + ## + # Draw a polygon. + + def polygon(self, xy, fill=None, outline=None): + ink, fill = self._getink(outline, fill) + if fill is not None: + self.draw.draw_polygon(xy, fill, 1) + if ink is not None: + self.draw.draw_polygon(xy, ink, 0) + + ## + # Draw a rectangle. + + def rectangle(self, xy, fill=None, outline=None): + ink, fill = self._getink(outline, fill) + if fill is not None: + self.draw.draw_rectangle(xy, fill, 1) + if ink is not None: + self.draw.draw_rectangle(xy, ink, 0) + + ## + # Draw text. + + def text(self, xy, text, fill=None, font=None, anchor=None): + ink, fill = self._getink(fill) + if font is None: + font = self.getfont() + if ink is None: + ink = fill + if ink is not None: + try: + mask, offset = font.getmask2(text, self.fontmode) + xy = xy[0] + offset[0], xy[1] + offset[1] + except AttributeError: + try: + mask = font.getmask(text, self.fontmode) + except TypeError: + mask = font.getmask(text) + self.draw.draw_bitmap(xy, mask, ink) + + ## + # Get the size of a given string, in pixels. + + def textsize(self, text, font=None): + if font is None: + font = self.getfont() + return font.getsize(text) + + +## +# A simple 2D drawing interface for PIL images. +# +# @param im The image to draw in. +# @param mode Optional mode to use for color values. For RGB +# images, this argument can be RGB or RGBA (to blend the +# drawing into the image). For all other modes, this argument +# must be the same as the image mode. If omitted, the mode +# defaults to the mode of the image. + +def Draw(im, mode=None): + try: + return im.getdraw(mode) + except AttributeError: + return ImageDraw(im, mode) + +# experimental access to the outline API +try: + Outline = Image.core.outline +except: + Outline = None + + +## +# (Experimental) A more advanced 2D drawing interface for PIL images, +# based on the WCK interface. +# +# @param im The image to draw in. +# @param hints An optional list of hints. +# @return A (drawing context, drawing resource factory) tuple. + +def getdraw(im=None, hints=None): + # FIXME: this needs more work! + # FIXME: come up with a better 'hints' scheme. + handler = None + if not hints or "nicest" in hints: + try: + from PIL import _imagingagg as handler + except ImportError: + pass + if handler is None: + from PIL import ImageDraw2 as handler + if im: + im = handler.Draw(im) + return im, handler + + +## +# (experimental) Fills a bounded region with a given color. +# +# @param image Target image. +# @param xy Seed position (a 2-item coordinate tuple). +# @param value Fill color. +# @param border Optional border value. If given, the region consists of +# pixels with a color different from the border color. If not given, +# the region consists of pixels having the same color as the seed +# pixel. + +def floodfill(image, xy, value, border=None): + "Fill bounded region." + # based on an implementation by Eric S. Raymond + pixel = image.load() + x, y = xy + try: + background = pixel[x, y] + if background == value: + return # seed point already has fill color + pixel[x, y] = value + except IndexError: + return # seed point outside image + edge = [(x, y)] + if border is None: + while edge: + newedge = [] + for (x, y) in edge: + for (s, t) in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)): + try: + p = pixel[s, t] + except IndexError: + pass + else: + if p == background: + pixel[s, t] = value + newedge.append((s, t)) + edge = newedge + else: + while edge: + newedge = [] + for (x, y) in edge: + for (s, t) in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)): + try: + p = pixel[s, t] + except IndexError: + pass + else: + if p != value and p != border: + pixel[s, t] = value + newedge.append((s, t)) + edge = newedge diff --git a/pyPackages/pillowarmv7l/PIL/ImageDraw2.py b/pyPackages/pillowarmv7l/PIL/ImageDraw2.py new file mode 100644 index 0000000..c967a20 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/ImageDraw2.py @@ -0,0 +1,111 @@ +# +# The Python Imaging Library +# $Id$ +# +# WCK-style drawing interface operations +# +# History: +# 2003-12-07 fl created +# 2005-05-15 fl updated; added to PIL as ImageDraw2 +# 2005-05-15 fl added text support +# 2005-05-20 fl added arc/chord/pieslice support +# +# Copyright (c) 2003-2005 by Secret Labs AB +# Copyright (c) 2003-2005 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image, ImageColor, ImageDraw, ImageFont, ImagePath + + +class Pen: + def __init__(self, color, width=1, opacity=255): + self.color = ImageColor.getrgb(color) + self.width = width + + +class Brush: + def __init__(self, color, opacity=255): + self.color = ImageColor.getrgb(color) + + +class Font: + def __init__(self, color, file, size=12): + # FIXME: add support for bitmap fonts + self.color = ImageColor.getrgb(color) + self.font = ImageFont.truetype(file, size) + + +class Draw: + + def __init__(self, image, size=None, color=None): + if not hasattr(image, "im"): + image = Image.new(image, size, color) + self.draw = ImageDraw.Draw(image) + self.image = image + self.transform = None + + def flush(self): + return self.image + + def render(self, op, xy, pen, brush=None): + # handle color arguments + outline = fill = None + width = 1 + if isinstance(pen, Pen): + outline = pen.color + width = pen.width + elif isinstance(brush, Pen): + outline = brush.color + width = brush.width + if isinstance(brush, Brush): + fill = brush.color + elif isinstance(pen, Brush): + fill = pen.color + # handle transformation + if self.transform: + xy = ImagePath.Path(xy) + xy.transform(self.transform) + # render the item + if op == "line": + self.draw.line(xy, fill=outline, width=width) + else: + getattr(self.draw, op)(xy, fill=fill, outline=outline) + + def settransform(self, offset): + (xoffset, yoffset) = offset + self.transform = (1, 0, xoffset, 0, 1, yoffset) + + def arc(self, xy, start, end, *options): + self.render("arc", xy, start, end, *options) + + def chord(self, xy, start, end, *options): + self.render("chord", xy, start, end, *options) + + def ellipse(self, xy, *options): + self.render("ellipse", xy, *options) + + def line(self, xy, *options): + self.render("line", xy, *options) + + def pieslice(self, xy, start, end, *options): + self.render("pieslice", xy, start, end, *options) + + def polygon(self, xy, *options): + self.render("polygon", xy, *options) + + def rectangle(self, xy, *options): + self.render("rectangle", xy, *options) + + def symbol(self, xy, symbol, *options): + raise NotImplementedError("not in this version") + + def text(self, xy, text, font): + if self.transform: + xy = ImagePath.Path(xy) + xy.transform(self.transform) + self.draw.text(xy, text, font=font.font, fill=font.color) + + def textsize(self, text, font): + return self.draw.textsize(text, font=font.font) diff --git a/pyPackages/pillowarmv7l/PIL/ImageEnhance.py b/pyPackages/pillowarmv7l/PIL/ImageEnhance.py new file mode 100644 index 0000000..a196d5b --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/ImageEnhance.py @@ -0,0 +1,99 @@ +# +# The Python Imaging Library. +# $Id$ +# +# image enhancement classes +# +# For a background, see "Image Processing By Interpolation and +# Extrapolation", Paul Haeberli and Douglas Voorhies. Available +# at http://www.graficaobscura.com/interp/index.html +# +# History: +# 1996-03-23 fl Created +# 2009-06-16 fl Fixed mean calculation +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image, ImageFilter, ImageStat + + +class _Enhance: + + def enhance(self, factor): + """ + Returns an enhanced image. + + :param factor: A floating point value controlling the enhancement. + Factor 1.0 always returns a copy of the original image, + lower factors mean less color (brightness, contrast, + etc), and higher values more. There are no restrictions + on this value. + :rtype: :py:class:`~PIL.Image.Image` + """ + return Image.blend(self.degenerate, self.image, factor) + + +class Color(_Enhance): + """Adjust image color balance. + + This class can be used to adjust the colour balance of an image, in + a manner similar to the controls on a colour TV set. An enhancement + factor of 0.0 gives a black and white image. A factor of 1.0 gives + the original image. + """ + def __init__(self, image): + self.image = image + self.intermediate_mode = 'L' + if 'A' in image.getbands(): + self.intermediate_mode = 'LA' + + self.degenerate = image.convert(self.intermediate_mode).convert(image.mode) + +class Contrast(_Enhance): + """Adjust image contrast. + + This class can be used to control the contrast of an image, similar + to the contrast control on a TV set. An enhancement factor of 0.0 + gives a solid grey image. A factor of 1.0 gives the original image. + """ + def __init__(self, image): + self.image = image + mean = int(ImageStat.Stat(image.convert("L")).mean[0] + 0.5) + self.degenerate = Image.new("L", image.size, mean).convert(image.mode) + + if 'A' in image.getbands(): + self.degenerate.putalpha(image.split()[-1]) + + +class Brightness(_Enhance): + """Adjust image brightness. + + This class can be used to control the brighntess of an image. An + enhancement factor of 0.0 gives a black image. A factor of 1.0 gives the + original image. + """ + def __init__(self, image): + self.image = image + self.degenerate = Image.new(image.mode, image.size, 0) + + if 'A' in image.getbands(): + self.degenerate.putalpha(image.split()[-1]) + + +class Sharpness(_Enhance): + """Adjust image sharpness. + + This class can be used to adjust the sharpness of an image. An + enhancement factor of 0.0 gives a blurred image, a factor of 1.0 gives the + original image, and a factor of 2.0 gives a sharpened image. + """ + def __init__(self, image): + self.image = image + self.degenerate = image.filter(ImageFilter.SMOOTH) + + if 'A' in image.getbands(): + self.degenerate.putalpha(image.split()[-1]) diff --git a/pyPackages/pillowarmv7l/PIL/ImageFile.py b/pyPackages/pillowarmv7l/PIL/ImageFile.py new file mode 100644 index 0000000..82691af --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/ImageFile.py @@ -0,0 +1,523 @@ +# +# The Python Imaging Library. +# $Id$ +# +# base class for image file handlers +# +# history: +# 1995-09-09 fl Created +# 1996-03-11 fl Fixed load mechanism. +# 1996-04-15 fl Added pcx/xbm decoders. +# 1996-04-30 fl Added encoders. +# 1996-12-14 fl Added load helpers +# 1997-01-11 fl Use encode_to_file where possible +# 1997-08-27 fl Flush output in _save +# 1998-03-05 fl Use memory mapping for some modes +# 1999-02-04 fl Use memory mapping also for "I;16" and "I;16B" +# 1999-05-31 fl Added image parser +# 2000-10-12 fl Set readonly flag on memory-mapped images +# 2002-03-20 fl Use better messages for common decoder errors +# 2003-04-21 fl Fall back on mmap/map_buffer if map is not available +# 2003-10-30 fl Added StubImageFile class +# 2004-02-25 fl Made incremental parser more robust +# +# Copyright (c) 1997-2004 by Secret Labs AB +# Copyright (c) 1995-2004 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image +from PIL._util import isPath +import io +import os +import sys +import traceback + +MAXBLOCK = 65536 + +SAFEBLOCK = 1024*1024 + +LOAD_TRUNCATED_IMAGES = False + +ERRORS = { + -1: "image buffer overrun error", + -2: "decoding error", + -3: "unknown error", + -8: "bad configuration", + -9: "out of memory error" +} + + +def raise_ioerror(error): + try: + message = Image.core.getcodecstatus(error) + except AttributeError: + message = ERRORS.get(error) + if not message: + message = "decoder error %d" % error + raise IOError(message + " when reading image file") + + +# +# -------------------------------------------------------------------- +# Helpers + +def _tilesort(t): + # sort on offset + return t[2] + + +# +# -------------------------------------------------------------------- +# ImageFile base class + +class ImageFile(Image.Image): + "Base class for image file format handlers." + + def __init__(self, fp=None, filename=None): + Image.Image.__init__(self) + + self.tile = None + self.readonly = 1 # until we know better + + self.decoderconfig = () + self.decodermaxblock = MAXBLOCK + + if isPath(fp): + # filename + self.fp = open(fp, "rb") + self.filename = fp + else: + # stream + self.fp = fp + self.filename = filename + + try: + self._open() + except IndexError as v: # end of data + if Image.DEBUG > 1: + traceback.print_exc() + raise SyntaxError(v) + except TypeError as v: # end of data (ord) + if Image.DEBUG > 1: + traceback.print_exc() + raise SyntaxError(v) + except KeyError as v: # unsupported mode + if Image.DEBUG > 1: + traceback.print_exc() + raise SyntaxError(v) + except EOFError as v: # got header but not the first frame + if Image.DEBUG > 1: + traceback.print_exc() + raise SyntaxError(v) + + if not self.mode or self.size[0] <= 0: + raise SyntaxError("not identified by this driver") + + def draft(self, mode, size): + "Set draft mode" + + pass + + def verify(self): + "Check file integrity" + + # raise exception if something's wrong. must be called + # directly after open, and closes file when finished. + self.fp = None + + def load(self): + "Load image data based on tile list" + + pixel = Image.Image.load(self) + + if self.tile is None: + raise IOError("cannot load this image") + if not self.tile: + return pixel + + self.map = None + use_mmap = self.filename and len(self.tile) == 1 + # As of pypy 2.1.0, memory mapping was failing here. + use_mmap = use_mmap and not hasattr(sys, 'pypy_version_info') + + readonly = 0 + + # look for read/seek overrides + try: + read = self.load_read + # don't use mmap if there are custom read/seek functions + use_mmap = False + except AttributeError: + read = self.fp.read + + try: + seek = self.load_seek + use_mmap = False + except AttributeError: + seek = self.fp.seek + + if use_mmap: + # try memory mapping + d, e, o, a = self.tile[0] + if d == "raw" and a[0] == self.mode and a[0] in Image._MAPMODES: + try: + if hasattr(Image.core, "map"): + # use built-in mapper + self.map = Image.core.map(self.filename) + self.map.seek(o) + self.im = self.map.readimage( + self.mode, self.size, a[1], a[2] + ) + else: + # use mmap, if possible + import mmap + file = open(self.filename, "r+") + size = os.path.getsize(self.filename) + # FIXME: on Unix, use PROT_READ etc + self.map = mmap.mmap(file.fileno(), size) + self.im = Image.core.map_buffer( + self.map, self.size, d, e, o, a + ) + readonly = 1 + except (AttributeError, EnvironmentError, ImportError): + self.map = None + + self.load_prepare() + + if not self.map: + # sort tiles in file order + self.tile.sort(key=_tilesort) + + try: + # FIXME: This is a hack to handle TIFF's JpegTables tag. + prefix = self.tile_prefix + except AttributeError: + prefix = b"" + + for d, e, o, a in self.tile: + d = Image._getdecoder(self.mode, d, a, self.decoderconfig) + seek(o) + try: + d.setimage(self.im, e) + except ValueError: + continue + b = prefix + t = len(b) + while True: + try: + s = read(self.decodermaxblock) + except IndexError as ie: # truncated png/gif + if LOAD_TRUNCATED_IMAGES: + break + else: + raise IndexError(ie) + + if not s and not d.handles_eof: # truncated jpeg + self.tile = [] + + # JpegDecode needs to clean things up here either way + # If we don't destroy the decompressor, + # we have a memory leak. + d.cleanup() + + if LOAD_TRUNCATED_IMAGES: + break + else: + raise IOError("image file is truncated " + "(%d bytes not processed)" % len(b)) + + b = b + s + n, e = d.decode(b) + if n < 0: + break + b = b[n:] + t = t + n + # Need to cleanup here to prevent leaks in PyPy + d.cleanup() + + self.tile = [] + self.readonly = readonly + + self.fp = None # might be shared + + if not self.map and (not LOAD_TRUNCATED_IMAGES or t == 0) and e < 0: + # still raised if decoder fails to return anything + raise_ioerror(e) + + # post processing + if hasattr(self, "tile_post_rotate"): + # FIXME: This is a hack to handle rotated PCD's + self.im = self.im.rotate(self.tile_post_rotate) + self.size = self.im.size + + self.load_end() + + return Image.Image.load(self) + + def load_prepare(self): + # create image memory if necessary + if not self.im or\ + self.im.mode != self.mode or self.im.size != self.size: + self.im = Image.core.new(self.mode, self.size) + # create palette (optional) + if self.mode == "P": + Image.Image.load(self) + + def load_end(self): + # may be overridden + pass + + # may be defined for contained formats + # def load_seek(self, pos): + # pass + + # may be defined for blocked formats (e.g. PNG) + # def load_read(self, bytes): + # pass + + +class StubImageFile(ImageFile): + """ + Base class for stub image loaders. + + A stub loader is an image loader that can identify files of a + certain format, but relies on external code to load the file. + """ + + def _open(self): + raise NotImplementedError( + "StubImageFile subclass must implement _open" + ) + + def load(self): + loader = self._load() + if loader is None: + raise IOError("cannot find loader for this %s file" % self.format) + image = loader.load(self) + assert image is not None + # become the other object (!) + self.__class__ = image.__class__ + self.__dict__ = image.__dict__ + + def _load(self): + "(Hook) Find actual image loader." + raise NotImplementedError( + "StubImageFile subclass must implement _load" + ) + + +class Parser: + """ + Incremental image parser. This class implements the standard + feed/close consumer interface. + + In Python 2.x, this is an old-style class. + """ + incremental = None + image = None + data = None + decoder = None + finished = 0 + + def reset(self): + """ + (Consumer) Reset the parser. Note that you can only call this + method immediately after you've created a parser; parser + instances cannot be reused. + """ + assert self.data is None, "cannot reuse parsers" + + def feed(self, data): + """ + (Consumer) Feed data to the parser. + + :param data: A string buffer. + :exception IOError: If the parser failed to parse the image file. + """ + # collect data + + if self.finished: + return + + if self.data is None: + self.data = data + else: + self.data = self.data + data + + # parse what we have + if self.decoder: + + if self.offset > 0: + # skip header + skip = min(len(self.data), self.offset) + self.data = self.data[skip:] + self.offset = self.offset - skip + if self.offset > 0 or not self.data: + return + + n, e = self.decoder.decode(self.data) + + if n < 0: + # end of stream + self.data = None + self.finished = 1 + if e < 0: + # decoding error + self.image = None + raise_ioerror(e) + else: + # end of image + return + self.data = self.data[n:] + + elif self.image: + + # if we end up here with no decoder, this file cannot + # be incrementally parsed. wait until we've gotten all + # available data + pass + + else: + + # attempt to open this file + try: + try: + fp = io.BytesIO(self.data) + im = Image.open(fp) + finally: + fp.close() # explicitly close the virtual file + except IOError: + # traceback.print_exc() + pass # not enough data + else: + flag = hasattr(im, "load_seek") or hasattr(im, "load_read") + if flag or len(im.tile) != 1: + # custom load code, or multiple tiles + self.decode = None + else: + # initialize decoder + im.load_prepare() + d, e, o, a = im.tile[0] + im.tile = [] + self.decoder = Image._getdecoder( + im.mode, d, a, im.decoderconfig + ) + self.decoder.setimage(im.im, e) + + # calculate decoder offset + self.offset = o + if self.offset <= len(self.data): + self.data = self.data[self.offset:] + self.offset = 0 + + self.image = im + + def close(self): + """ + (Consumer) Close the stream. + + :returns: An image object. + :exception IOError: If the parser failed to parse the image file either + because it cannot be identified or cannot be + decoded. + """ + # finish decoding + if self.decoder: + # get rid of what's left in the buffers + self.feed(b"") + self.data = self.decoder = None + if not self.finished: + raise IOError("image was incomplete") + if not self.image: + raise IOError("cannot parse this image") + if self.data: + # incremental parsing not possible; reopen the file + # not that we have all data + try: + fp = io.BytesIO(self.data) + self.image = Image.open(fp) + finally: + self.image.load() + fp.close() # explicitly close the virtual file + return self.image + + +# -------------------------------------------------------------------- + +def _save(im, fp, tile, bufsize=0): + """Helper to save image based on tile list + + :param im: Image object. + :param fp: File object. + :param tile: Tile list. + :param bufsize: Optional buffer size + """ + + im.load() + if not hasattr(im, "encoderconfig"): + im.encoderconfig = () + tile.sort(key=_tilesort) + # FIXME: make MAXBLOCK a configuration parameter + # It would be great if we could have the encoder specify what it needs + # But, it would need at least the image size in most cases. RawEncode is + # a tricky case. + bufsize = max(MAXBLOCK, bufsize, im.size[0] * 4) # see RawEncode.c + try: + fh = fp.fileno() + fp.flush() + except (AttributeError, io.UnsupportedOperation): + # compress to Python file-compatible object + for e, b, o, a in tile: + e = Image._getencoder(im.mode, e, a, im.encoderconfig) + if o > 0: + fp.seek(o, 0) + e.setimage(im.im, b) + while True: + l, s, d = e.encode(bufsize) + fp.write(d) + if s: + break + if s < 0: + raise IOError("encoder error %d when writing image file" % s) + e.cleanup() + else: + # slight speedup: compress to real file object + for e, b, o, a in tile: + e = Image._getencoder(im.mode, e, a, im.encoderconfig) + if o > 0: + fp.seek(o, 0) + e.setimage(im.im, b) + s = e.encode_to_file(fh, bufsize) + if s < 0: + raise IOError("encoder error %d when writing image file" % s) + e.cleanup() + try: + fp.flush() + except: + pass + + +def _safe_read(fp, size): + """ + Reads large blocks in a safe way. Unlike fp.read(n), this function + doesn't trust the user. If the requested size is larger than + SAFEBLOCK, the file is read block by block. + + :param fp: File handle. Must implement a read method. + :param size: Number of bytes to read. + :returns: A string containing up to size bytes of data. + """ + if size <= 0: + return b"" + if size <= SAFEBLOCK: + return fp.read(size) + data = [] + while size > 0: + block = fp.read(min(size, SAFEBLOCK)) + if not block: + break + data.append(block) + size -= len(block) + return b"".join(data) diff --git a/pyPackages/pillowarmv7l/PIL/ImageFileIO.py b/pyPackages/pillowarmv7l/PIL/ImageFileIO.py new file mode 100644 index 0000000..e57d3f4 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/ImageFileIO.py @@ -0,0 +1,40 @@ +# +# The Python Imaging Library. +# $Id$ +# +# kludge to get basic ImageFileIO functionality +# +# History: +# 1998-08-06 fl Recreated +# +# Copyright (c) Secret Labs AB 1998-2002. +# +# See the README file for information on usage and redistribution. +# +""" +The **ImageFileIO** module can be used to read an image from a +socket, or any other stream device. + +Deprecated. New code should use the :class:`PIL.ImageFile.Parser` +class in the :mod:`PIL.ImageFile` module instead. + +.. seealso:: modules :class:`PIL.ImageFile.Parser` +""" + +from io import BytesIO + + +class ImageFileIO(BytesIO): + def __init__(self, fp): + """ + Adds buffering to a stream file object, in order to + provide **seek** and **tell** methods required + by the :func:`PIL.Image.Image.open` method. The stream object must + implement **read** and **close** methods. + + :param fp: Stream file handle. + + .. seealso:: modules :func:`PIL.Image.open` + """ + data = fp.read() + BytesIO.__init__(self, data) diff --git a/pyPackages/pillowarmv7l/PIL/ImageFilter.py b/pyPackages/pillowarmv7l/PIL/ImageFilter.py new file mode 100644 index 0000000..b468458 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/ImageFilter.py @@ -0,0 +1,275 @@ +# +# The Python Imaging Library. +# $Id$ +# +# standard filters +# +# History: +# 1995-11-27 fl Created +# 2002-06-08 fl Added rank and mode filters +# 2003-09-15 fl Fixed rank calculation in rank filter; added expand call +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1995-2002 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +from functools import reduce + + +class Filter(object): + pass + + +class Kernel(Filter): + """ + Create a convolution kernel. The current version only + supports 3x3 and 5x5 integer and floating point kernels. + + In the current version, kernels can only be applied to + "L" and "RGB" images. + + :param size: Kernel size, given as (width, height). In the current + version, this must be (3,3) or (5,5). + :param kernel: A sequence containing kernel weights. + :param scale: Scale factor. If given, the result for each pixel is + divided by this value. the default is the sum of the + kernel weights. + :param offset: Offset. If given, this value is added to the result, + after it has been divided by the scale factor. + """ + + def __init__(self, size, kernel, scale=None, offset=0): + if scale is None: + # default scale is sum of kernel + scale = reduce(lambda a, b: a+b, kernel) + if size[0] * size[1] != len(kernel): + raise ValueError("not enough coefficients in kernel") + self.filterargs = size, scale, offset, kernel + + def filter(self, image): + if image.mode == "P": + raise ValueError("cannot filter palette images") + return image.filter(*self.filterargs) + + +class BuiltinFilter(Kernel): + def __init__(self): + pass + + +class RankFilter(Filter): + """ + Create a rank filter. The rank filter sorts all pixels in + a window of the given size, and returns the **rank**'th value. + + :param size: The kernel size, in pixels. + :param rank: What pixel value to pick. Use 0 for a min filter, + ``size * size / 2`` for a median filter, ``size * size - 1`` + for a max filter, etc. + """ + name = "Rank" + + def __init__(self, size, rank): + self.size = size + self.rank = rank + + def filter(self, image): + if image.mode == "P": + raise ValueError("cannot filter palette images") + image = image.expand(self.size//2, self.size//2) + return image.rankfilter(self.size, self.rank) + + +class MedianFilter(RankFilter): + """ + Create a median filter. Picks the median pixel value in a window with the + given size. + + :param size: The kernel size, in pixels. + """ + name = "Median" + + def __init__(self, size=3): + self.size = size + self.rank = size*size//2 + + +class MinFilter(RankFilter): + """ + Create a min filter. Picks the lowest pixel value in a window with the + given size. + + :param size: The kernel size, in pixels. + """ + name = "Min" + + def __init__(self, size=3): + self.size = size + self.rank = 0 + + +class MaxFilter(RankFilter): + """ + Create a max filter. Picks the largest pixel value in a window with the + given size. + + :param size: The kernel size, in pixels. + """ + name = "Max" + + def __init__(self, size=3): + self.size = size + self.rank = size*size-1 + + +class ModeFilter(Filter): + """ + + Create a mode filter. Picks the most frequent pixel value in a box with the + given size. Pixel values that occur only once or twice are ignored; if no + pixel value occurs more than twice, the original pixel value is preserved. + + :param size: The kernel size, in pixels. + """ + name = "Mode" + + def __init__(self, size=3): + self.size = size + + def filter(self, image): + return image.modefilter(self.size) + + +class GaussianBlur(Filter): + """Gaussian blur filter. + + :param radius: Blur radius. + """ + name = "GaussianBlur" + + def __init__(self, radius=2): + self.radius = radius + + def filter(self, image): + return image.gaussian_blur(self.radius) + + +class UnsharpMask(Filter): + """Unsharp mask filter. + + See Wikipedia's entry on `digital unsharp masking`_ for an explanation of + the parameters. + + :param radius: Blur Radius + :param percent: Unsharp strength, in percent + :param threshold: Threshold controls the minimum brightness change that + will be sharpened + + .. _digital unsharp masking: https://en.wikipedia.org/wiki/Unsharp_masking#Digital_unsharp_masking + + """ + name = "UnsharpMask" + + def __init__(self, radius=2, percent=150, threshold=3): + self.radius = radius + self.percent = percent + self.threshold = threshold + + def filter(self, image): + return image.unsharp_mask(self.radius, self.percent, self.threshold) + + +class BLUR(BuiltinFilter): + name = "Blur" + filterargs = (5, 5), 16, 0, ( + 1, 1, 1, 1, 1, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 1, 1, 1, 1 + ) + + +class CONTOUR(BuiltinFilter): + name = "Contour" + filterargs = (3, 3), 1, 255, ( + -1, -1, -1, + -1, 8, -1, + -1, -1, -1 + ) + + +class DETAIL(BuiltinFilter): + name = "Detail" + filterargs = (3, 3), 6, 0, ( + 0, -1, 0, + -1, 10, -1, + 0, -1, 0 + ) + + +class EDGE_ENHANCE(BuiltinFilter): + name = "Edge-enhance" + filterargs = (3, 3), 2, 0, ( + -1, -1, -1, + -1, 10, -1, + -1, -1, -1 + ) + + +class EDGE_ENHANCE_MORE(BuiltinFilter): + name = "Edge-enhance More" + filterargs = (3, 3), 1, 0, ( + -1, -1, -1, + -1, 9, -1, + -1, -1, -1 + ) + + +class EMBOSS(BuiltinFilter): + name = "Emboss" + filterargs = (3, 3), 1, 128, ( + -1, 0, 0, + 0, 1, 0, + 0, 0, 0 + ) + + +class FIND_EDGES(BuiltinFilter): + name = "Find Edges" + filterargs = (3, 3), 1, 0, ( + -1, -1, -1, + -1, 8, -1, + -1, -1, -1 + ) + + +class SMOOTH(BuiltinFilter): + name = "Smooth" + filterargs = (3, 3), 13, 0, ( + 1, 1, 1, + 1, 5, 1, + 1, 1, 1 + ) + + +class SMOOTH_MORE(BuiltinFilter): + name = "Smooth More" + filterargs = (5, 5), 100, 0, ( + 1, 1, 1, 1, 1, + 1, 5, 5, 5, 1, + 1, 5, 44, 5, 1, + 1, 5, 5, 5, 1, + 1, 1, 1, 1, 1 + ) + + +class SHARPEN(BuiltinFilter): + name = "Sharpen" + filterargs = (3, 3), 16, 0, ( + -2, -2, -2, + -2, 32, -2, + -2, -2, -2 + ) diff --git a/pyPackages/pillowarmv7l/PIL/ImageFont.py b/pyPackages/pillowarmv7l/PIL/ImageFont.py new file mode 100644 index 0000000..afbae37 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/ImageFont.py @@ -0,0 +1,430 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PIL raster font management +# +# History: +# 1996-08-07 fl created (experimental) +# 1997-08-25 fl minor adjustments to handle fonts from pilfont 0.3 +# 1999-02-06 fl rewrote most font management stuff in C +# 1999-03-17 fl take pth files into account in load_path (from Richard Jones) +# 2001-02-17 fl added freetype support +# 2001-05-09 fl added TransposedFont wrapper class +# 2002-03-04 fl make sure we have a "L" or "1" font +# 2002-12-04 fl skip non-directory entries in the system path +# 2003-04-29 fl add embedded default font +# 2003-09-27 fl added support for truetype charmap encodings +# +# Todo: +# Adapt to PILFONT2 format (16-bit fonts, compressed, single file) +# +# Copyright (c) 1997-2003 by Secret Labs AB +# Copyright (c) 1996-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from __future__ import print_function + +from PIL import Image +from PIL._util import isDirectory, isPath +import os +import sys + +try: + import warnings +except ImportError: + warnings = None + + +class _imagingft_not_installed: + # module placeholder + def __getattr__(self, id): + raise ImportError("The _imagingft C module is not installed") + +try: + from PIL import _imagingft as core +except ImportError: + core = _imagingft_not_installed() + +# FIXME: add support for pilfont2 format (see FontFile.py) + +# -------------------------------------------------------------------- +# Font metrics format: +# "PILfont" LF +# fontdescriptor LF +# (optional) key=value... LF +# "DATA" LF +# binary data: 256*10*2 bytes (dx, dy, dstbox, srcbox) +# +# To place a character, cut out srcbox and paste at dstbox, +# relative to the character position. Then move the character +# position according to dx, dy. +# -------------------------------------------------------------------- + + +class ImageFont: + "PIL font wrapper" + + def _load_pilfont(self, filename): + + file = open(filename, "rb") + + for ext in (".png", ".gif", ".pbm"): + try: + fullname = os.path.splitext(filename)[0] + ext + image = Image.open(fullname) + except: + pass + else: + if image and image.mode in ("1", "L"): + break + else: + raise IOError("cannot find glyph data file") + + self.file = fullname + + return self._load_pilfont_data(file, image) + + def _load_pilfont_data(self, file, image): + + # read PILfont header + if file.readline() != b"PILfont\n": + raise SyntaxError("Not a PILfont file") + file.readline().split(b";") + self.info = [] # FIXME: should be a dictionary + while True: + s = file.readline() + if not s or s == b"DATA\n": + break + self.info.append(s) + + # read PILfont metrics + data = file.read(256*20) + + # check image + if image.mode not in ("1", "L"): + raise TypeError("invalid font image mode") + + image.load() + + self.font = Image.core.font(image.im, data) + + # delegate critical operations to internal type + self.getsize = self.font.getsize + self.getmask = self.font.getmask + + +## +# Wrapper for FreeType fonts. Application code should use the +# truetype factory function to create font objects. + +class FreeTypeFont: + "FreeType font wrapper (requires _imagingft service)" + + def __init__(self, font=None, size=10, index=0, encoding="", file=None): + # FIXME: use service provider instead + if file: + if warnings: + warnings.warn( + 'file parameter deprecated, ' + 'please use font parameter instead.', + DeprecationWarning) + font = file + + if isPath(font): + self.font = core.getfont(font, size, index, encoding) + else: + self.font_bytes = font.read() + self.font = core.getfont( + "", size, index, encoding, self.font_bytes) + + def getname(self): + return self.font.family, self.font.style + + def getmetrics(self): + return self.font.ascent, self.font.descent + + def getsize(self, text): + size, offset = self.font.getsize(text) + return (size[0] + offset[0], size[1] + offset[1]) + + def getoffset(self, text): + return self.font.getsize(text)[1] + + def getmask(self, text, mode=""): + return self.getmask2(text, mode)[0] + + def getmask2(self, text, mode="", fill=Image.core.fill): + size, offset = self.font.getsize(text) + im = fill("L", size, 0) + self.font.render(text, im.id, mode == "1") + return im, offset + +## +# Wrapper that creates a transposed font from any existing font +# object. +# +# @param font A font object. +# @param orientation An optional orientation. If given, this should +# be one of Image.FLIP_LEFT_RIGHT, Image.FLIP_TOP_BOTTOM, +# Image.ROTATE_90, Image.ROTATE_180, or Image.ROTATE_270. + + +class TransposedFont: + "Wrapper for writing rotated or mirrored text" + + def __init__(self, font, orientation=None): + self.font = font + self.orientation = orientation # any 'transpose' argument, or None + + def getsize(self, text): + w, h = self.font.getsize(text) + if self.orientation in (Image.ROTATE_90, Image.ROTATE_270): + return h, w + return w, h + + def getmask(self, text, mode=""): + im = self.font.getmask(text, mode) + if self.orientation is not None: + return im.transpose(self.orientation) + return im + + +def load(filename): + """ + Load a font file. This function loads a font object from the given + bitmap font file, and returns the corresponding font object. + + :param filename: Name of font file. + :return: A font object. + :exception IOError: If the file could not be read. + """ + f = ImageFont() + f._load_pilfont(filename) + return f + + +def truetype(font=None, size=10, index=0, encoding="", filename=None): + """ + Load a TrueType or OpenType font file, and create a font object. + This function loads a font object from the given file, and creates + a font object for a font of the given size. + + This function requires the _imagingft service. + + :param filename: A truetype font file. Under Windows, if the file + is not found in this filename, the loader also looks in + Windows :file:`fonts/` directory. + :param size: The requested size, in points. + :param index: Which font face to load (default is first available face). + :param encoding: Which font encoding to use (default is Unicode). Common + encodings are "unic" (Unicode), "symb" (Microsoft + Symbol), "ADOB" (Adobe Standard), "ADBE" (Adobe Expert), + and "armn" (Apple Roman). See the FreeType documentation + for more information. + :return: A font object. + :exception IOError: If the file could not be read. + """ + + if filename: + if warnings: + warnings.warn( + 'filename parameter deprecated, ' + 'please use font parameter instead.', + DeprecationWarning) + font = filename + + try: + return FreeTypeFont(font, size, index, encoding) + except IOError: + if font.endswith(".ttf"): + ttf_filename = font + else: + ttf_filename = "%s.ttf" % font + if sys.platform == "win32": + # check the windows font repository + # NOTE: must use uppercase WINDIR, to work around bugs in + # 1.5.2's os.environ.get() + windir = os.environ.get("WINDIR") + if windir: + filename = os.path.join(windir, "fonts", font) + return FreeTypeFont(filename, size, index, encoding) + elif sys.platform in ('linux', 'linux2'): + lindirs = os.environ.get("XDG_DATA_DIRS", "") + if not lindirs: + #According to the freedesktop spec, XDG_DATA_DIRS should + #default to /usr/share + lindirs = '/usr/share' + lindirs = lindirs.split(":") + for lindir in lindirs: + parentpath = os.path.join(lindir, "fonts") + for walkroot, walkdir, walkfilenames in os.walk(parentpath): + if ttf_filename in walkfilenames: + filepath = os.path.join(walkroot, ttf_filename) + return FreeTypeFont(filepath, size, index, encoding) + elif sys.platform == 'darwin': + macdirs = ['/Library/Fonts/', '/System/Library/Fonts/', os.path.expanduser('~/Library/Fonts/')] + for macdir in macdirs: + filepath = os.path.join(macdir, ttf_filename) + if os.path.exists(filepath): + return FreeTypeFont(filepath, size, index, encoding) + raise + + +def load_path(filename): + """ + Load font file. Same as :py:func:`~PIL.ImageFont.load`, but searches for a + bitmap font along the Python path. + + :param filename: Name of font file. + :return: A font object. + :exception IOError: If the file could not be read. + """ + for dir in sys.path: + if isDirectory(dir): + if not isinstance(filename, str): + if bytes is str: + filename = filename.encode("utf-8") + else: + filename = filename.decode("utf-8") + try: + return load(os.path.join(dir, filename)) + except IOError: + pass + raise IOError("cannot find font file") + + +def load_default(): + """Load a "better than nothing" default font. + + .. versionadded:: 1.1.4 + + :return: A font object. + """ + from io import BytesIO + import base64 + f = ImageFont() + f._load_pilfont_data( + # courB08 + BytesIO(base64.decodestring(b''' +UElMZm9udAo7Ozs7OzsxMDsKREFUQQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAA//8AAQAAAAAAAAABAAEA +BgAAAAH/+gADAAAAAQAAAAMABgAGAAAAAf/6AAT//QADAAAABgADAAYAAAAA//kABQABAAYAAAAL +AAgABgAAAAD/+AAFAAEACwAAABAACQAGAAAAAP/5AAUAAAAQAAAAFQAHAAYAAP////oABQAAABUA +AAAbAAYABgAAAAH/+QAE//wAGwAAAB4AAwAGAAAAAf/5AAQAAQAeAAAAIQAIAAYAAAAB//kABAAB +ACEAAAAkAAgABgAAAAD/+QAE//0AJAAAACgABAAGAAAAAP/6AAX//wAoAAAALQAFAAYAAAAB//8A +BAACAC0AAAAwAAMABgAAAAD//AAF//0AMAAAADUAAQAGAAAAAf//AAMAAAA1AAAANwABAAYAAAAB +//kABQABADcAAAA7AAgABgAAAAD/+QAFAAAAOwAAAEAABwAGAAAAAP/5AAYAAABAAAAARgAHAAYA +AAAA//kABQAAAEYAAABLAAcABgAAAAD/+QAFAAAASwAAAFAABwAGAAAAAP/5AAYAAABQAAAAVgAH +AAYAAAAA//kABQAAAFYAAABbAAcABgAAAAD/+QAFAAAAWwAAAGAABwAGAAAAAP/5AAUAAABgAAAA +ZQAHAAYAAAAA//kABQAAAGUAAABqAAcABgAAAAD/+QAFAAAAagAAAG8ABwAGAAAAAf/8AAMAAABv +AAAAcQAEAAYAAAAA//wAAwACAHEAAAB0AAYABgAAAAD/+gAE//8AdAAAAHgABQAGAAAAAP/7AAT/ +/gB4AAAAfAADAAYAAAAB//oABf//AHwAAACAAAUABgAAAAD/+gAFAAAAgAAAAIUABgAGAAAAAP/5 +AAYAAQCFAAAAiwAIAAYAAP////oABgAAAIsAAACSAAYABgAA////+gAFAAAAkgAAAJgABgAGAAAA +AP/6AAUAAACYAAAAnQAGAAYAAP////oABQAAAJ0AAACjAAYABgAA////+gAFAAAAowAAAKkABgAG +AAD////6AAUAAACpAAAArwAGAAYAAAAA//oABQAAAK8AAAC0AAYABgAA////+gAGAAAAtAAAALsA +BgAGAAAAAP/6AAQAAAC7AAAAvwAGAAYAAP////oABQAAAL8AAADFAAYABgAA////+gAGAAAAxQAA +AMwABgAGAAD////6AAUAAADMAAAA0gAGAAYAAP////oABQAAANIAAADYAAYABgAA////+gAGAAAA +2AAAAN8ABgAGAAAAAP/6AAUAAADfAAAA5AAGAAYAAP////oABQAAAOQAAADqAAYABgAAAAD/+gAF +AAEA6gAAAO8ABwAGAAD////6AAYAAADvAAAA9gAGAAYAAAAA//oABQAAAPYAAAD7AAYABgAA//// ++gAFAAAA+wAAAQEABgAGAAD////6AAYAAAEBAAABCAAGAAYAAP////oABgAAAQgAAAEPAAYABgAA +////+gAGAAABDwAAARYABgAGAAAAAP/6AAYAAAEWAAABHAAGAAYAAP////oABgAAARwAAAEjAAYA +BgAAAAD/+gAFAAABIwAAASgABgAGAAAAAf/5AAQAAQEoAAABKwAIAAYAAAAA//kABAABASsAAAEv +AAgABgAAAAH/+QAEAAEBLwAAATIACAAGAAAAAP/5AAX//AEyAAABNwADAAYAAAAAAAEABgACATcA +AAE9AAEABgAAAAH/+QAE//wBPQAAAUAAAwAGAAAAAP/7AAYAAAFAAAABRgAFAAYAAP////kABQAA +AUYAAAFMAAcABgAAAAD/+wAFAAABTAAAAVEABQAGAAAAAP/5AAYAAAFRAAABVwAHAAYAAAAA//sA +BQAAAVcAAAFcAAUABgAAAAD/+QAFAAABXAAAAWEABwAGAAAAAP/7AAYAAgFhAAABZwAHAAYAAP// +//kABQAAAWcAAAFtAAcABgAAAAD/+QAGAAABbQAAAXMABwAGAAAAAP/5AAQAAgFzAAABdwAJAAYA +AP////kABgAAAXcAAAF+AAcABgAAAAD/+QAGAAABfgAAAYQABwAGAAD////7AAUAAAGEAAABigAF +AAYAAP////sABQAAAYoAAAGQAAUABgAAAAD/+wAFAAABkAAAAZUABQAGAAD////7AAUAAgGVAAAB +mwAHAAYAAAAA//sABgACAZsAAAGhAAcABgAAAAD/+wAGAAABoQAAAacABQAGAAAAAP/7AAYAAAGn +AAABrQAFAAYAAAAA//kABgAAAa0AAAGzAAcABgAA////+wAGAAABswAAAboABQAGAAD////7AAUA +AAG6AAABwAAFAAYAAP////sABgAAAcAAAAHHAAUABgAAAAD/+wAGAAABxwAAAc0ABQAGAAD////7 +AAYAAgHNAAAB1AAHAAYAAAAA//sABQAAAdQAAAHZAAUABgAAAAH/+QAFAAEB2QAAAd0ACAAGAAAA +Av/6AAMAAQHdAAAB3gAHAAYAAAAA//kABAABAd4AAAHiAAgABgAAAAD/+wAF//0B4gAAAecAAgAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAB +//sAAwACAecAAAHpAAcABgAAAAD/+QAFAAEB6QAAAe4ACAAGAAAAAP/5AAYAAAHuAAAB9AAHAAYA +AAAA//oABf//AfQAAAH5AAUABgAAAAD/+QAGAAAB+QAAAf8ABwAGAAAAAv/5AAMAAgH/AAACAAAJ +AAYAAAAA//kABQABAgAAAAIFAAgABgAAAAH/+gAE//sCBQAAAggAAQAGAAAAAP/5AAYAAAIIAAAC +DgAHAAYAAAAB//kABf/+Ag4AAAISAAUABgAA////+wAGAAACEgAAAhkABQAGAAAAAP/7AAX//gIZ +AAACHgADAAYAAAAA//wABf/9Ah4AAAIjAAEABgAAAAD/+QAHAAACIwAAAioABwAGAAAAAP/6AAT/ ++wIqAAACLgABAAYAAAAA//kABP/8Ai4AAAIyAAMABgAAAAD/+gAFAAACMgAAAjcABgAGAAAAAf/5 +AAT//QI3AAACOgAEAAYAAAAB//kABP/9AjoAAAI9AAQABgAAAAL/+QAE//sCPQAAAj8AAgAGAAD/ +///7AAYAAgI/AAACRgAHAAYAAAAA//kABgABAkYAAAJMAAgABgAAAAH//AAD//0CTAAAAk4AAQAG +AAAAAf//AAQAAgJOAAACUQADAAYAAAAB//kABP/9AlEAAAJUAAQABgAAAAH/+QAF//4CVAAAAlgA +BQAGAAD////7AAYAAAJYAAACXwAFAAYAAP////kABgAAAl8AAAJmAAcABgAA////+QAGAAACZgAA +Am0ABwAGAAD////5AAYAAAJtAAACdAAHAAYAAAAA//sABQACAnQAAAJ5AAcABgAA////9wAGAAAC +eQAAAoAACQAGAAD////3AAYAAAKAAAAChwAJAAYAAP////cABgAAAocAAAKOAAkABgAA////9wAG +AAACjgAAApUACQAGAAD////4AAYAAAKVAAACnAAIAAYAAP////cABgAAApwAAAKjAAkABgAA//// ++gAGAAACowAAAqoABgAGAAAAAP/6AAUAAgKqAAACrwAIAAYAAP////cABQAAAq8AAAK1AAkABgAA +////9wAFAAACtQAAArsACQAGAAD////3AAUAAAK7AAACwQAJAAYAAP////gABQAAAsEAAALHAAgA +BgAAAAD/9wAEAAACxwAAAssACQAGAAAAAP/3AAQAAALLAAACzwAJAAYAAAAA//cABAAAAs8AAALT +AAkABgAAAAD/+AAEAAAC0wAAAtcACAAGAAD////6AAUAAALXAAAC3QAGAAYAAP////cABgAAAt0A +AALkAAkABgAAAAD/9wAFAAAC5AAAAukACQAGAAAAAP/3AAUAAALpAAAC7gAJAAYAAAAA//cABQAA +Au4AAALzAAkABgAAAAD/9wAFAAAC8wAAAvgACQAGAAAAAP/4AAUAAAL4AAAC/QAIAAYAAAAA//oA +Bf//Av0AAAMCAAUABgAA////+gAGAAADAgAAAwkABgAGAAD////3AAYAAAMJAAADEAAJAAYAAP// +//cABgAAAxAAAAMXAAkABgAA////9wAGAAADFwAAAx4ACQAGAAD////4AAYAAAAAAAoABwASAAYA +AP////cABgAAAAcACgAOABMABgAA////+gAFAAAADgAKABQAEAAGAAD////6AAYAAAAUAAoAGwAQ +AAYAAAAA//gABgAAABsACgAhABIABgAAAAD/+AAGAAAAIQAKACcAEgAGAAAAAP/4AAYAAAAnAAoA +LQASAAYAAAAA//gABgAAAC0ACgAzABIABgAAAAD/+QAGAAAAMwAKADkAEQAGAAAAAP/3AAYAAAA5 +AAoAPwATAAYAAP////sABQAAAD8ACgBFAA8ABgAAAAD/+wAFAAIARQAKAEoAEQAGAAAAAP/4AAUA +AABKAAoATwASAAYAAAAA//gABQAAAE8ACgBUABIABgAAAAD/+AAFAAAAVAAKAFkAEgAGAAAAAP/5 +AAUAAABZAAoAXgARAAYAAAAA//gABgAAAF4ACgBkABIABgAAAAD/+AAGAAAAZAAKAGoAEgAGAAAA +AP/4AAYAAABqAAoAcAASAAYAAAAA//kABgAAAHAACgB2ABEABgAAAAD/+AAFAAAAdgAKAHsAEgAG +AAD////4AAYAAAB7AAoAggASAAYAAAAA//gABQAAAIIACgCHABIABgAAAAD/+AAFAAAAhwAKAIwA +EgAGAAAAAP/4AAUAAACMAAoAkQASAAYAAAAA//gABQAAAJEACgCWABIABgAAAAD/+QAFAAAAlgAK +AJsAEQAGAAAAAP/6AAX//wCbAAoAoAAPAAYAAAAA//oABQABAKAACgClABEABgAA////+AAGAAAA +pQAKAKwAEgAGAAD////4AAYAAACsAAoAswASAAYAAP////gABgAAALMACgC6ABIABgAA////+QAG +AAAAugAKAMEAEQAGAAD////4AAYAAgDBAAoAyAAUAAYAAP////kABQACAMgACgDOABMABgAA//// ++QAGAAIAzgAKANUAEw== +''')), Image.open(BytesIO(base64.decodestring(b''' +iVBORw0KGgoAAAANSUhEUgAAAx4AAAAUAQAAAAArMtZoAAAEwElEQVR4nABlAJr/AHVE4czCI/4u +Mc4b7vuds/xzjz5/3/7u/n9vMe7vnfH/9++vPn/xyf5zhxzjt8GHw8+2d83u8x27199/nxuQ6Od9 +M43/5z2I+9n9ZtmDBwMQECDRQw/eQIQohJXxpBCNVE6QCCAAAAD//wBlAJr/AgALyj1t/wINwq0g +LeNZUworuN1cjTPIzrTX6ofHWeo3v336qPzfEwRmBnHTtf95/fglZK5N0PDgfRTslpGBvz7LFc4F +IUXBWQGjQ5MGCx34EDFPwXiY4YbYxavpnhHFrk14CDAAAAD//wBlAJr/AgKqRooH2gAgPeggvUAA +Bu2WfgPoAwzRAABAAAAAAACQgLz/3Uv4Gv+gX7BJgDeeGP6AAAD1NMDzKHD7ANWr3loYbxsAD791 +NAADfcoIDyP44K/jv4Y63/Z+t98Ovt+ub4T48LAAAAD//wBlAJr/AuplMlADJAAAAGuAphWpqhMx +in0A/fRvAYBABPgBwBUgABBQ/sYAyv9g0bCHgOLoGAAAAAAAREAAwI7nr0ArYpow7aX8//9LaP/9 +SjdavWA8ePHeBIKB//81/83ndznOaXx379wAAAD//wBlAJr/AqDxW+D3AABAAbUh/QMnbQag/gAY +AYDAAACgtgD/gOqAAAB5IA/8AAAk+n9w0AAA8AAAmFRJuPo27ciC0cD5oeW4E7KA/wD3ECMAn2tt +y8PgwH8AfAxFzC0JzeAMtratAsC/ffwAAAD//wBlAJr/BGKAyCAA4AAAAvgeYTAwHd1kmQF5chkG +ABoMIHcL5xVpTfQbUqzlAAAErwAQBgAAEOClA5D9il08AEh/tUzdCBsXkbgACED+woQg8Si9VeqY +lODCn7lmF6NhnAEYgAAA/NMIAAAAAAD//2JgjLZgVGBg5Pv/Tvpc8hwGBjYGJADjHDrAwPzAjv/H +/Wf3PzCwtzcwHmBgYGcwbZz8wHaCAQMDOwMDQ8MCBgYOC3W7mp+f0w+wHOYxO3OG+e376hsMZjk3 +AAAAAP//YmCMY2A4wMAIN5e5gQETPD6AZisDAwMDgzSDAAPjByiHcQMDAwMDg1nOze1lByRu5/47 +c4859311AYNZzg0AAAAA//9iYGDBYihOIIMuwIjGL39/fwffA8b//xv/P2BPtzzHwCBjUQAAAAD/ +/yLFBrIBAAAA//9i1HhcwdhizX7u8NZNzyLbvT97bfrMf/QHI8evOwcSqGUJAAAA//9iYBB81iSw +pEE170Qrg5MIYydHqwdDQRMrAwcVrQAAAAD//2J4x7j9AAMDn8Q/BgYLBoaiAwwMjPdvMDBYM1Tv +oJodAAAAAP//Yqo/83+dxePWlxl3npsel9lvLfPcqlE9725C+acfVLMEAAAA//9i+s9gwCoaaGMR +evta/58PTEWzr21hufPjA8N+qlnBwAAAAAD//2JiWLci5v1+HmFXDqcnULE/MxgYGBj+f6CaJQAA +AAD//2Ji2FrkY3iYpYC5qDeGgeEMAwPDvwQBBoYvcTwOVLMEAAAA//9isDBgkP///0EOg9z35v// +Gc/eeW7BwPj5+QGZhANUswMAAAD//2JgqGBgYGBgqEMXlvhMPUsAAAAA//8iYDd1AAAAAP//AwDR +w7IkEbzhVQAAAABJRU5ErkJggg== +''')))) + return f + +# End of file diff --git a/pyPackages/pillowarmv7l/PIL/ImageGrab.py b/pyPackages/pillowarmv7l/PIL/ImageGrab.py new file mode 100644 index 0000000..ef01353 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/ImageGrab.py @@ -0,0 +1,52 @@ +# +# The Python Imaging Library +# $Id$ +# +# screen grabber (windows only) +# +# History: +# 2001-04-26 fl created +# 2001-09-17 fl use builtin driver, if present +# 2002-11-19 fl added grabclipboard support +# +# Copyright (c) 2001-2002 by Secret Labs AB +# Copyright (c) 2001-2002 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image + +import sys +if sys.platform != "win32": + raise ImportError("ImageGrab is Windows only") + +try: + # built-in driver (1.1.3 and later) + grabber = Image.core.grabscreen +except AttributeError: + # stand-alone driver (pil plus) + import _grabscreen + grabber = _grabscreen.grab + + +def grab(bbox=None): + size, data = grabber() + im = Image.frombytes( + "RGB", size, data, + # RGB, 32-bit line padding, origo in lower left corner + "raw", "BGR", (size[0]*3 + 3) & -4, -1 + ) + if bbox: + im = im.crop(bbox) + return im + + +def grabclipboard(): + debug = 0 # temporary interface + data = Image.core.grabclipboard(debug) + if isinstance(data, bytes): + from PIL import BmpImagePlugin + import io + return BmpImagePlugin.DibImageFile(io.BytesIO(data)) + return data diff --git a/pyPackages/pillowarmv7l/PIL/ImageMath.py b/pyPackages/pillowarmv7l/PIL/ImageMath.py new file mode 100644 index 0000000..4dcc512 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/ImageMath.py @@ -0,0 +1,270 @@ +# +# The Python Imaging Library +# $Id$ +# +# a simple math add-on for the Python Imaging Library +# +# History: +# 1999-02-15 fl Original PIL Plus release +# 2005-05-05 fl Simplified and cleaned up for PIL 1.1.6 +# 2005-09-12 fl Fixed int() and float() for Python 2.4.1 +# +# Copyright (c) 1999-2005 by Secret Labs AB +# Copyright (c) 2005 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image +from PIL import _imagingmath + +try: + import builtins +except ImportError: + import __builtin__ + builtins = __builtin__ + +VERBOSE = 0 + + +def _isconstant(v): + return isinstance(v, int) or isinstance(v, float) + + +class _Operand: + # wraps an image operand, providing standard operators + + def __init__(self, im): + self.im = im + + def __fixup(self, im1): + # convert image to suitable mode + if isinstance(im1, _Operand): + # argument was an image. + if im1.im.mode in ("1", "L"): + return im1.im.convert("I") + elif im1.im.mode in ("I", "F"): + return im1.im + else: + raise ValueError("unsupported mode: %s" % im1.im.mode) + else: + # argument was a constant + if _isconstant(im1) and self.im.mode in ("1", "L", "I"): + return Image.new("I", self.im.size, im1) + else: + return Image.new("F", self.im.size, im1) + + def apply(self, op, im1, im2=None, mode=None): + im1 = self.__fixup(im1) + if im2 is None: + # unary operation + out = Image.new(mode or im1.mode, im1.size, None) + im1.load() + try: + op = getattr(_imagingmath, op+"_"+im1.mode) + except AttributeError: + raise TypeError("bad operand type for '%s'" % op) + _imagingmath.unop(op, out.im.id, im1.im.id) + else: + # binary operation + im2 = self.__fixup(im2) + if im1.mode != im2.mode: + # convert both arguments to floating point + if im1.mode != "F": + im1 = im1.convert("F") + if im2.mode != "F": + im2 = im2.convert("F") + if im1.mode != im2.mode: + raise ValueError("mode mismatch") + if im1.size != im2.size: + # crop both arguments to a common size + size = (min(im1.size[0], im2.size[0]), + min(im1.size[1], im2.size[1])) + if im1.size != size: + im1 = im1.crop((0, 0) + size) + if im2.size != size: + im2 = im2.crop((0, 0) + size) + out = Image.new(mode or im1.mode, size, None) + else: + out = Image.new(mode or im1.mode, im1.size, None) + im1.load() + im2.load() + try: + op = getattr(_imagingmath, op+"_"+im1.mode) + except AttributeError: + raise TypeError("bad operand type for '%s'" % op) + _imagingmath.binop(op, out.im.id, im1.im.id, im2.im.id) + return _Operand(out) + + # unary operators + def __bool__(self): + # an image is "true" if it contains at least one non-zero pixel + return self.im.getbbox() is not None + + if bytes is str: + # Provide __nonzero__ for pre-Py3k + __nonzero__ = __bool__ + del __bool__ + + def __abs__(self): + return self.apply("abs", self) + + def __pos__(self): + return self + + def __neg__(self): + return self.apply("neg", self) + + # binary operators + def __add__(self, other): + return self.apply("add", self, other) + + def __radd__(self, other): + return self.apply("add", other, self) + + def __sub__(self, other): + return self.apply("sub", self, other) + + def __rsub__(self, other): + return self.apply("sub", other, self) + + def __mul__(self, other): + return self.apply("mul", self, other) + + def __rmul__(self, other): + return self.apply("mul", other, self) + + def __truediv__(self, other): + return self.apply("div", self, other) + + def __rtruediv__(self, other): + return self.apply("div", other, self) + + def __mod__(self, other): + return self.apply("mod", self, other) + + def __rmod__(self, other): + return self.apply("mod", other, self) + + def __pow__(self, other): + return self.apply("pow", self, other) + + def __rpow__(self, other): + return self.apply("pow", other, self) + + if bytes is str: + # Provide __div__ and __rdiv__ for pre-Py3k + __div__ = __truediv__ + __rdiv__ = __rtruediv__ + del __truediv__ + del __rtruediv__ + + # bitwise + def __invert__(self): + return self.apply("invert", self) + + def __and__(self, other): + return self.apply("and", self, other) + + def __rand__(self, other): + return self.apply("and", other, self) + + def __or__(self, other): + return self.apply("or", self, other) + + def __ror__(self, other): + return self.apply("or", other, self) + + def __xor__(self, other): + return self.apply("xor", self, other) + + def __rxor__(self, other): + return self.apply("xor", other, self) + + def __lshift__(self, other): + return self.apply("lshift", self, other) + + def __rshift__(self, other): + return self.apply("rshift", self, other) + + # logical + def __eq__(self, other): + return self.apply("eq", self, other) + + def __ne__(self, other): + return self.apply("ne", self, other) + + def __lt__(self, other): + return self.apply("lt", self, other) + + def __le__(self, other): + return self.apply("le", self, other) + + def __gt__(self, other): + return self.apply("gt", self, other) + + def __ge__(self, other): + return self.apply("ge", self, other) + + +# conversions +def imagemath_int(self): + return _Operand(self.im.convert("I")) + + +def imagemath_float(self): + return _Operand(self.im.convert("F")) + + +# logical +def imagemath_equal(self, other): + return self.apply("eq", self, other, mode="I") + + +def imagemath_notequal(self, other): + return self.apply("ne", self, other, mode="I") + + +def imagemath_min(self, other): + return self.apply("min", self, other) + + +def imagemath_max(self, other): + return self.apply("max", self, other) + + +def imagemath_convert(self, mode): + return _Operand(self.im.convert(mode)) + +ops = {} +for k, v in list(globals().items()): + if k[:10] == "imagemath_": + ops[k[10:]] = v + + +def eval(expression, _dict={}, **kw): + """ + Evaluates an image expression. + + :param expression: A string containing a Python-style expression. + :param options: Values to add to the evaluation context. You + can either use a dictionary, or one or more keyword + arguments. + :return: The evaluated expression. This is usually an image object, but can + also be an integer, a floating point value, or a pixel tuple, + depending on the expression. + """ + + # build execution namespace + args = ops.copy() + args.update(_dict) + args.update(kw) + for k, v in list(args.items()): + if hasattr(v, "im"): + args[k] = _Operand(v) + + out = builtins.eval(expression, args) + try: + return out.im + except AttributeError: + return out diff --git a/pyPackages/pillowarmv7l/PIL/ImageMode.py b/pyPackages/pillowarmv7l/PIL/ImageMode.py new file mode 100644 index 0000000..2950691 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/ImageMode.py @@ -0,0 +1,52 @@ +# +# The Python Imaging Library. +# $Id$ +# +# standard mode descriptors +# +# History: +# 2006-03-20 fl Added +# +# Copyright (c) 2006 by Secret Labs AB. +# Copyright (c) 2006 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +# mode descriptor cache +_modes = {} + + +## +# Wrapper for mode strings. + +class ModeDescriptor: + + def __init__(self, mode, bands, basemode, basetype): + self.mode = mode + self.bands = bands + self.basemode = basemode + self.basetype = basetype + + def __str__(self): + return self.mode + + +## +# Gets a mode descriptor for the given mode. + +def getmode(mode): + if not _modes: + # initialize mode cache + from PIL import Image + # core modes + for m, (basemode, basetype, bands) in Image._MODEINFO.items(): + _modes[m] = ModeDescriptor(m, bands, basemode, basetype) + # extra experimental modes + _modes["LA"] = ModeDescriptor("LA", ("L", "A"), "L", "L") + _modes["PA"] = ModeDescriptor("PA", ("P", "A"), "RGB", "L") + # mapping modes + _modes["I;16"] = ModeDescriptor("I;16", "I", "L", "L") + _modes["I;16L"] = ModeDescriptor("I;16L", "I", "L", "L") + _modes["I;16B"] = ModeDescriptor("I;16B", "I", "L", "L") + return _modes[mode] diff --git a/pyPackages/pillowarmv7l/PIL/ImageMorph.py b/pyPackages/pillowarmv7l/PIL/ImageMorph.py new file mode 100644 index 0000000..996eacb --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/ImageMorph.py @@ -0,0 +1,245 @@ +# A binary morphology add-on for the Python Imaging Library +# +# History: +# 2014-06-04 Initial version. +# +# Copyright (c) 2014 Dov Grobgeld + +from PIL import Image +from PIL import _imagingmorph +import re + +LUT_SIZE = 1 << 9 + + +class LutBuilder: + """A class for building a MorphLut from a descriptive language + + The input patterns is a list of a strings sequences like these:: + + 4:(... + .1. + 111)->1 + + (whitespaces including linebreaks are ignored). The option 4 + describes a series of symmetry operations (in this case a + 4-rotation), the pattern is described by: + + - . or X - Ignore + - 1 - Pixel is on + - 0 - Pixel is off + + The result of the operation is described after "->" string. + + The default is to return the current pixel value, which is + returned if no other match is found. + + Operations: + + - 4 - 4 way rotation + - N - Negate + - 1 - Dummy op for no other operation (an op must always be given) + - M - Mirroring + + Example:: + + lb = LutBuilder(patterns = ["4:(... .1. 111)->1"]) + lut = lb.build_lut() + + """ + def __init__(self, patterns=None, op_name=None): + if patterns is not None: + self.patterns = patterns + else: + self.patterns = [] + self.lut = None + if op_name is not None: + known_patterns = { + 'corner': ['1:(... ... ...)->0', + '4:(00. 01. ...)->1'], + 'dilation4': ['4:(... .0. .1.)->1'], + 'dilation8': ['4:(... .0. .1.)->1', + '4:(... .0. ..1)->1'], + 'erosion4': ['4:(... .1. .0.)->0'], + 'erosion8': ['4:(... .1. .0.)->0', + '4:(... .1. ..0)->0'], + 'edge': ['1:(... ... ...)->0', + '4:(.0. .1. ...)->1', + '4:(01. .1. ...)->1'] + } + if op_name not in known_patterns: + raise Exception('Unknown pattern '+op_name+'!') + + self.patterns = known_patterns[op_name] + + def add_patterns(self, patterns): + self.patterns += patterns + + def build_default_lut(self): + symbols = [0, 1] + m = 1 << 4 # pos of current pixel + self.lut = bytearray([symbols[(i & m) > 0] for i in range(LUT_SIZE)]) + + def get_lut(self): + return self.lut + + def _string_permute(self, pattern, permutation): + """string_permute takes a pattern and a permutation and returns the + string permuted according to the permutation list. + """ + assert(len(permutation) == 9) + return ''.join([pattern[p] for p in permutation]) + + def _pattern_permute(self, basic_pattern, options, basic_result): + """pattern_permute takes a basic pattern and its result and clones + the pattern according to the modifications described in the $options + parameter. It returns a list of all cloned patterns.""" + patterns = [(basic_pattern, basic_result)] + + # rotations + if '4' in options: + res = patterns[-1][1] + for i in range(4): + patterns.append( + (self._string_permute(patterns[-1][0], [6, 3, 0, + 7, 4, 1, + 8, 5, 2]), res)) + # mirror + if 'M' in options: + n = len(patterns) + for pattern, res in patterns[0:n]: + patterns.append( + (self._string_permute(pattern, [2, 1, 0, + 5, 4, 3, + 8, 7, 6]), res)) + + # negate + if 'N' in options: + n = len(patterns) + for pattern, res in patterns[0:n]: + # Swap 0 and 1 + pattern = (pattern + .replace('0', 'Z') + .replace('1', '0') + .replace('Z', '1')) + res = '%d' % (1-int(res)) + patterns.append((pattern, res)) + + return patterns + + def build_lut(self): + """Compile all patterns into a morphology lut. + + TBD :Build based on (file) morphlut:modify_lut + """ + self.build_default_lut() + patterns = [] + + # Parse and create symmetries of the patterns strings + for p in self.patterns: + m = re.search( + r'(\w*):?\s*\((.+?)\)\s*->\s*(\d)', p.replace('\n', '')) + if not m: + raise Exception('Syntax error in pattern "'+p+'"') + options = m.group(1) + pattern = m.group(2) + result = int(m.group(3)) + + # Get rid of spaces + pattern = pattern.replace(' ', '').replace('\n', '') + + patterns += self._pattern_permute(pattern, options, result) + +# # Debugging +# for p,r in patterns: +# print p,r +# print '--' + + # compile the patterns into regular expressions for speed + for i in range(len(patterns)): + p = patterns[i][0].replace('.', 'X').replace('X', '[01]') + p = re.compile(p) + patterns[i] = (p, patterns[i][1]) + + # Step through table and find patterns that match. + # Note that all the patterns are searched. The last one + # caught overrides + for i in range(LUT_SIZE): + # Build the bit pattern + bitpattern = bin(i)[2:] + bitpattern = ('0'*(9-len(bitpattern)) + bitpattern)[::-1] + + for p, r in patterns: + if p.match(bitpattern): + self.lut[i] = [0, 1][r] + + return self.lut + + +class MorphOp: + """A class for binary morphological operators""" + + def __init__(self, + lut=None, + op_name=None, + patterns=None): + """Create a binary morphological operator""" + self.lut = lut + if op_name is not None: + self.lut = LutBuilder(op_name=op_name).build_lut() + elif patterns is not None: + self.lut = LutBuilder(patterns=patterns).build_lut() + + def apply(self, image): + """Run a single morphological operation on an image + + Returns a tuple of the number of changed pixels and the + morphed image""" + if self.lut is None: + raise Exception('No operator loaded') + + outimage = Image.new(image.mode, image.size, None) + count = _imagingmorph.apply( + bytes(self.lut), image.im.id, outimage.im.id) + return count, outimage + + def match(self, image): + """Get a list of coordinates matching the morphological operation on + an image. + + Returns a list of tuples of (x,y) coordinates + of all matching pixels.""" + if self.lut is None: + raise Exception('No operator loaded') + + return _imagingmorph.match(bytes(self.lut), image.im.id) + + def get_on_pixels(self, image): + """Get a list of all turned on pixels in a binary image + + Returns a list of tuples of (x,y) coordinates + of all matching pixels.""" + + return _imagingmorph.get_on_pixels(image.im.id) + + def load_lut(self, filename): + """Load an operator from an mrl file""" + with open(filename, 'rb') as f: + self.lut = bytearray(f.read()) + + if len(self.lut) != 8192: + self.lut = None + raise Exception('Wrong size operator file!') + + def save_lut(self, filename): + """Save an operator to an mrl file""" + if self.lut is None: + raise Exception('No operator loaded') + with open(filename, 'wb') as f: + f.write(self.lut) + + def set_lut(self, lut): + """Set the lut from an external source""" + self.lut = lut + +# End of file diff --git a/pyPackages/pillowarmv7l/PIL/ImageOps.py b/pyPackages/pillowarmv7l/PIL/ImageOps.py new file mode 100644 index 0000000..a170687 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/ImageOps.py @@ -0,0 +1,462 @@ +# +# The Python Imaging Library. +# $Id$ +# +# standard image operations +# +# History: +# 2001-10-20 fl Created +# 2001-10-23 fl Added autocontrast operator +# 2001-12-18 fl Added Kevin's fit operator +# 2004-03-14 fl Fixed potential division by zero in equalize +# 2005-05-05 fl Fixed equalize for low number of values +# +# Copyright (c) 2001-2004 by Secret Labs AB +# Copyright (c) 2001-2004 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image +from PIL._util import isStringType +import operator +from functools import reduce + + +# +# helpers + +def _border(border): + if isinstance(border, tuple): + if len(border) == 2: + left, top = right, bottom = border + elif len(border) == 4: + left, top, right, bottom = border + else: + left = top = right = bottom = border + return left, top, right, bottom + + +def _color(color, mode): + if isStringType(color): + from PIL import ImageColor + color = ImageColor.getcolor(color, mode) + return color + + +def _lut(image, lut): + if image.mode == "P": + # FIXME: apply to lookup table, not image data + raise NotImplementedError("mode P support coming soon") + elif image.mode in ("L", "RGB"): + if image.mode == "RGB" and len(lut) == 256: + lut = lut + lut + lut + return image.point(lut) + else: + raise IOError("not supported for this image mode") + +# +# actions + + +def autocontrast(image, cutoff=0, ignore=None): + """ + Maximize (normalize) image contrast. This function calculates a + histogram of the input image, removes **cutoff** percent of the + lightest and darkest pixels from the histogram, and remaps the image + so that the darkest pixel becomes black (0), and the lightest + becomes white (255). + + :param image: The image to process. + :param cutoff: How many percent to cut off from the histogram. + :param ignore: The background pixel value (use None for no background). + :return: An image. + """ + histogram = image.histogram() + lut = [] + for layer in range(0, len(histogram), 256): + h = histogram[layer:layer+256] + if ignore is not None: + # get rid of outliers + try: + h[ignore] = 0 + except TypeError: + # assume sequence + for ix in ignore: + h[ix] = 0 + if cutoff: + # cut off pixels from both ends of the histogram + # get number of pixels + n = 0 + for ix in range(256): + n = n + h[ix] + # remove cutoff% pixels from the low end + cut = n * cutoff // 100 + for lo in range(256): + if cut > h[lo]: + cut = cut - h[lo] + h[lo] = 0 + else: + h[lo] -= cut + cut = 0 + if cut <= 0: + break + # remove cutoff% samples from the hi end + cut = n * cutoff // 100 + for hi in range(255, -1, -1): + if cut > h[hi]: + cut = cut - h[hi] + h[hi] = 0 + else: + h[hi] -= cut + cut = 0 + if cut <= 0: + break + # find lowest/highest samples after preprocessing + for lo in range(256): + if h[lo]: + break + for hi in range(255, -1, -1): + if h[hi]: + break + if hi <= lo: + # don't bother + lut.extend(list(range(256))) + else: + scale = 255.0 / (hi - lo) + offset = -lo * scale + for ix in range(256): + ix = int(ix * scale + offset) + if ix < 0: + ix = 0 + elif ix > 255: + ix = 255 + lut.append(ix) + return _lut(image, lut) + + +def colorize(image, black, white): + """ + Colorize grayscale image. The **black** and **white** + arguments should be RGB tuples; this function calculates a color + wedge mapping all black pixels in the source image to the first + color, and all white pixels to the second color. + + :param image: The image to colorize. + :param black: The color to use for black input pixels. + :param white: The color to use for white input pixels. + :return: An image. + """ + assert image.mode == "L" + black = _color(black, "RGB") + white = _color(white, "RGB") + red = [] + green = [] + blue = [] + for i in range(256): + red.append(black[0]+i*(white[0]-black[0])//255) + green.append(black[1]+i*(white[1]-black[1])//255) + blue.append(black[2]+i*(white[2]-black[2])//255) + image = image.convert("RGB") + return _lut(image, red + green + blue) + + +def crop(image, border=0): + """ + Remove border from image. The same amount of pixels are removed + from all four sides. This function works on all image modes. + + .. seealso:: :py:meth:`~PIL.Image.Image.crop` + + :param image: The image to crop. + :param border: The number of pixels to remove. + :return: An image. + """ + left, top, right, bottom = _border(border) + return image.crop( + (left, top, image.size[0]-right, image.size[1]-bottom) + ) + + +def deform(image, deformer, resample=Image.BILINEAR): + """ + Deform the image. + + :param image: The image to deform. + :param deformer: A deformer object. Any object that implements a + **getmesh** method can be used. + :param resample: What resampling filter to use. + :return: An image. + """ + return image.transform( + image.size, Image.MESH, deformer.getmesh(image), resample + ) + + +def equalize(image, mask=None): + """ + Equalize the image histogram. This function applies a non-linear + mapping to the input image, in order to create a uniform + distribution of grayscale values in the output image. + + :param image: The image to equalize. + :param mask: An optional mask. If given, only the pixels selected by + the mask are included in the analysis. + :return: An image. + """ + if image.mode == "P": + image = image.convert("RGB") + h = image.histogram(mask) + lut = [] + for b in range(0, len(h), 256): + histo = [_f for _f in h[b:b+256] if _f] + if len(histo) <= 1: + lut.extend(list(range(256))) + else: + step = (reduce(operator.add, histo) - histo[-1]) // 255 + if not step: + lut.extend(list(range(256))) + else: + n = step // 2 + for i in range(256): + lut.append(n // step) + n = n + h[i+b] + return _lut(image, lut) + + +def expand(image, border=0, fill=0): + """ + Add border to the image + + :param image: The image to expand. + :param border: Border width, in pixels. + :param fill: Pixel fill value (a color value). Default is 0 (black). + :return: An image. + """ + "Add border to image" + left, top, right, bottom = _border(border) + width = left + image.size[0] + right + height = top + image.size[1] + bottom + out = Image.new(image.mode, (width, height), _color(fill, image.mode)) + out.paste(image, (left, top)) + return out + + +def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)): + """ + Returns a sized and cropped version of the image, cropped to the + requested aspect ratio and size. + + This function was contributed by Kevin Cazabon. + + :param size: The requested output size in pixels, given as a + (width, height) tuple. + :param method: What resampling method to use. Default is + :py:attr:`PIL.Image.NEAREST`. + :param bleed: Remove a border around the outside of the image (from all + four edges. The value is a decimal percentage (use 0.01 for + one percent). The default value is 0 (no border). + :param centering: Control the cropping position. Use (0.5, 0.5) for + center cropping (e.g. if cropping the width, take 50% off + of the left side, and therefore 50% off the right side). + (0.0, 0.0) will crop from the top left corner (i.e. if + cropping the width, take all of the crop off of the right + side, and if cropping the height, take all of it off the + bottom). (1.0, 0.0) will crop from the bottom left + corner, etc. (i.e. if cropping the width, take all of the + crop off the left side, and if cropping the height take + none from the top, and therefore all off the bottom). + :return: An image. + """ + + # by Kevin Cazabon, Feb 17/2000 + # kevin@cazabon.com + # http://www.cazabon.com + + # ensure inputs are valid + if not isinstance(centering, list): + centering = [centering[0], centering[1]] + + if centering[0] > 1.0 or centering[0] < 0.0: + centering[0] = 0.50 + if centering[1] > 1.0 or centering[1] < 0.0: + centering[1] = 0.50 + + if bleed > 0.49999 or bleed < 0.0: + bleed = 0.0 + + # calculate the area to use for resizing and cropping, subtracting + # the 'bleed' around the edges + + # number of pixels to trim off on Top and Bottom, Left and Right + bleedPixels = ( + int((float(bleed) * float(image.size[0])) + 0.5), + int((float(bleed) * float(image.size[1])) + 0.5) + ) + + liveArea = (0, 0, image.size[0], image.size[1]) + if bleed > 0.0: + liveArea = ( + bleedPixels[0], bleedPixels[1], image.size[0] - bleedPixels[0] - 1, + image.size[1] - bleedPixels[1] - 1 + ) + + liveSize = (liveArea[2] - liveArea[0], liveArea[3] - liveArea[1]) + + # calculate the aspect ratio of the liveArea + liveAreaAspectRatio = float(liveSize[0])/float(liveSize[1]) + + # calculate the aspect ratio of the output image + aspectRatio = float(size[0]) / float(size[1]) + + # figure out if the sides or top/bottom will be cropped off + if liveAreaAspectRatio >= aspectRatio: + # liveArea is wider than what's needed, crop the sides + cropWidth = int((aspectRatio * float(liveSize[1])) + 0.5) + cropHeight = liveSize[1] + else: + # liveArea is taller than what's needed, crop the top and bottom + cropWidth = liveSize[0] + cropHeight = int((float(liveSize[0])/aspectRatio) + 0.5) + + # make the crop + leftSide = int(liveArea[0] + (float(liveSize[0]-cropWidth) * centering[0])) + if leftSide < 0: + leftSide = 0 + topSide = int(liveArea[1] + (float(liveSize[1]-cropHeight) * centering[1])) + if topSide < 0: + topSide = 0 + + out = image.crop( + (leftSide, topSide, leftSide + cropWidth, topSide + cropHeight) + ) + + # resize the image and return it + return out.resize(size, method) + + +def flip(image): + """ + Flip the image vertically (top to bottom). + + :param image: The image to flip. + :return: An image. + """ + return image.transpose(Image.FLIP_TOP_BOTTOM) + + +def grayscale(image): + """ + Convert the image to grayscale. + + :param image: The image to convert. + :return: An image. + """ + return image.convert("L") + + +def invert(image): + """ + Invert (negate) the image. + + :param image: The image to invert. + :return: An image. + """ + lut = [] + for i in range(256): + lut.append(255-i) + return _lut(image, lut) + + +def mirror(image): + """ + Flip image horizontally (left to right). + + :param image: The image to mirror. + :return: An image. + """ + return image.transpose(Image.FLIP_LEFT_RIGHT) + + +def posterize(image, bits): + """ + Reduce the number of bits for each color channel. + + :param image: The image to posterize. + :param bits: The number of bits to keep for each channel (1-8). + :return: An image. + """ + lut = [] + mask = ~(2**(8-bits)-1) + for i in range(256): + lut.append(i & mask) + return _lut(image, lut) + + +def solarize(image, threshold=128): + """ + Invert all pixel values above a threshold. + + :param image: The image to solarize. + :param threshold: All pixels above this greyscale level are inverted. + :return: An image. + """ + lut = [] + for i in range(256): + if i < threshold: + lut.append(i) + else: + lut.append(255-i) + return _lut(image, lut) + + +# -------------------------------------------------------------------- +# PIL USM components, from Kevin Cazabon. + +def gaussian_blur(im, radius=None): + """ PIL_usm.gblur(im, [radius])""" + + if radius is None: + radius = 5.0 + + im.load() + + return im.im.gaussian_blur(radius) + +gblur = gaussian_blur + + +def unsharp_mask(im, radius=None, percent=None, threshold=None): + """ PIL_usm.usm(im, [radius, percent, threshold])""" + + if radius is None: + radius = 5.0 + if percent is None: + percent = 150 + if threshold is None: + threshold = 3 + + im.load() + + return im.im.unsharp_mask(radius, percent, threshold) + +usm = unsharp_mask + + +def box_blur(image, radius): + """ + Blur the image by setting each pixel to the average value of the pixels + in a square box extending radius pixels in each direction. + Supports float radius of arbitrary size. Uses an optimized implementation + which runs in linear time relative to the size of the image + for any radius value. + + :param image: The image to blur. + :param radius: Size of the box in one direction. Radius 0 does not blur, + returns an identical image. Radius 1 takes 1 pixel + in each direction, i.e. 9 pixels in total. + :return: An image. + """ + image.load() + + return image._new(image.im.box_blur(radius)) diff --git a/pyPackages/pillowarmv7l/PIL/ImagePalette.py b/pyPackages/pillowarmv7l/PIL/ImagePalette.py new file mode 100644 index 0000000..62f8814 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/ImagePalette.py @@ -0,0 +1,235 @@ +# +# The Python Imaging Library. +# $Id$ +# +# image palette object +# +# History: +# 1996-03-11 fl Rewritten. +# 1997-01-03 fl Up and running. +# 1997-08-23 fl Added load hack +# 2001-04-16 fl Fixed randint shadow bug in random() +# +# Copyright (c) 1997-2001 by Secret Labs AB +# Copyright (c) 1996-1997 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import array +import warnings +from PIL import ImageColor + + +class ImagePalette: + "Color palette for palette mapped images" + + def __init__(self, mode="RGB", palette=None, size=0): + self.mode = mode + self.rawmode = None # if set, palette contains raw data + self.palette = palette or list(range(256))*len(self.mode) + self.colors = {} + self.dirty = None + if ((size == 0 and len(self.mode)*256 != len(self.palette)) or + (size != 0 and size != len(self.palette))): + raise ValueError("wrong palette size") + + def getdata(self): + """ + Get palette contents in format suitable # for the low-level + ``im.putpalette`` primitive. + + .. warning:: This method is experimental. + """ + if self.rawmode: + return self.rawmode, self.palette + return self.mode + ";L", self.tobytes() + + def tobytes(self): + """Convert palette to bytes. + + .. warning:: This method is experimental. + """ + if self.rawmode: + raise ValueError("palette contains raw palette data") + if isinstance(self.palette, bytes): + return self.palette + arr = array.array("B", self.palette) + if hasattr(arr, 'tobytes'): + # py3k has a tobytes, tostring is deprecated. + return arr.tobytes() + return arr.tostring() + + # Declare tostring as an alias for tobytes + tostring = tobytes + + def getcolor(self, color): + """Given an rgb tuple, allocate palette entry. + + .. warning:: This method is experimental. + """ + if self.rawmode: + raise ValueError("palette contains raw palette data") + if isinstance(color, tuple): + try: + return self.colors[color] + except KeyError: + # allocate new color slot + if isinstance(self.palette, bytes): + self.palette = [int(x) for x in self.palette] + index = len(self.colors) + if index >= 256: + raise ValueError("cannot allocate more than 256 colors") + self.colors[color] = index + self.palette[index] = color[0] + self.palette[index+256] = color[1] + self.palette[index+512] = color[2] + self.dirty = 1 + return index + else: + raise ValueError("unknown color specifier: %r" % color) + + def save(self, fp): + """Save palette to text file. + + .. warning:: This method is experimental. + """ + if self.rawmode: + raise ValueError("palette contains raw palette data") + if isinstance(fp, str): + fp = open(fp, "w") + fp.write("# Palette\n") + fp.write("# Mode: %s\n" % self.mode) + for i in range(256): + fp.write("%d" % i) + for j in range(i*len(self.mode), (i+1)*len(self.mode)): + try: + fp.write(" %d" % self.palette[j]) + except IndexError: + fp.write(" 0") + fp.write("\n") + fp.close() + + +# -------------------------------------------------------------------- +# Internal + +def raw(rawmode, data): + palette = ImagePalette() + palette.rawmode = rawmode + palette.palette = data + palette.dirty = 1 + return palette + + +# -------------------------------------------------------------------- +# Factories + +def _make_linear_lut(black, white): + warnings.warn( + '_make_linear_lut() is deprecated. ' + 'Please call make_linear_lut() instead.', + DeprecationWarning, + stacklevel=2 + ) + return make_linear_lut(black, white) + + +def _make_gamma_lut(exp): + warnings.warn( + '_make_gamma_lut() is deprecated. ' + 'Please call make_gamma_lut() instead.', + DeprecationWarning, + stacklevel=2 + ) + return make_gamma_lut(exp) + + +def make_linear_lut(black, white): + lut = [] + if black == 0: + for i in range(256): + lut.append(white*i//255) + else: + raise NotImplementedError # FIXME + return lut + + +def make_gamma_lut(exp): + lut = [] + for i in range(256): + lut.append(int(((i / 255.0) ** exp) * 255.0 + 0.5)) + return lut + + +def negative(mode="RGB"): + palette = list(range(256)) + palette.reverse() + return ImagePalette(mode, palette * len(mode)) + + +def random(mode="RGB"): + from random import randint + palette = [] + for i in range(256*len(mode)): + palette.append(randint(0, 255)) + return ImagePalette(mode, palette) + + +def sepia(white="#fff0c0"): + r, g, b = ImageColor.getrgb(white) + r = make_linear_lut(0, r) + g = make_linear_lut(0, g) + b = make_linear_lut(0, b) + return ImagePalette("RGB", r + g + b) + + +def wedge(mode="RGB"): + return ImagePalette(mode, list(range(256)) * len(mode)) + + +def load(filename): + + # FIXME: supports GIMP gradients only + + fp = open(filename, "rb") + + lut = None + + if not lut: + try: + from PIL import GimpPaletteFile + fp.seek(0) + p = GimpPaletteFile.GimpPaletteFile(fp) + lut = p.getpalette() + except (SyntaxError, ValueError): + # import traceback + # traceback.print_exc() + pass + + if not lut: + try: + from PIL import GimpGradientFile + fp.seek(0) + p = GimpGradientFile.GimpGradientFile(fp) + lut = p.getpalette() + except (SyntaxError, ValueError): + # import traceback + # traceback.print_exc() + pass + + if not lut: + try: + from PIL import PaletteFile + fp.seek(0) + p = PaletteFile.PaletteFile(fp) + lut = p.getpalette() + except (SyntaxError, ValueError): + import traceback + traceback.print_exc() + pass + + if not lut: + raise IOError("cannot load palette") + + return lut # data, rawmode diff --git a/pyPackages/pillowarmv7l/PIL/ImagePath.py b/pyPackages/pillowarmv7l/PIL/ImagePath.py new file mode 100644 index 0000000..656d5ce --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/ImagePath.py @@ -0,0 +1,66 @@ +# +# The Python Imaging Library +# $Id$ +# +# path interface +# +# History: +# 1996-11-04 fl Created +# 2002-04-14 fl Added documentation stub class +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image + + +# the Python class below is overridden by the C implementation. + + +class Path: + + def __init__(self, xy): + pass + + ## + # Compacts the path, by removing points that are close to each + # other. This method modifies the path in place. + + def compact(self, distance=2): + pass + + ## + # Gets the bounding box. + + def getbbox(self): + pass + + ## + # Maps the path through a function. + + def map(self, function): + pass + + ## + # Converts the path to Python list. + # + # @param flat By default, this function returns a list of 2-tuples + # [(x, y), ...]. If this argument is true, it returns a flat + # list [x, y, ...] instead. + # @return A list of coordinates. + + def tolist(self, flat=0): + pass + + ## + # Transforms the path. + + def transform(self, matrix): + pass + + +# override with C implementation +Path = Image.core.path diff --git a/pyPackages/pillowarmv7l/PIL/ImageQt.py b/pyPackages/pillowarmv7l/PIL/ImageQt.py new file mode 100644 index 0000000..22ee2ea --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/ImageQt.py @@ -0,0 +1,98 @@ +# +# The Python Imaging Library. +# $Id$ +# +# a simple Qt image interface. +# +# history: +# 2006-06-03 fl: created +# 2006-06-04 fl: inherit from QImage instead of wrapping it +# 2006-06-05 fl: removed toimage helper; move string support to ImageQt +# 2013-11-13 fl: add support for Qt5 (aurelien.ballier@cyclonit.com) +# +# Copyright (c) 2006 by Secret Labs AB +# Copyright (c) 2006 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image +from PIL._util import isPath +import sys + +if 'PyQt4.QtGui' not in sys.modules: + try: + from PyQt5.QtGui import QImage, qRgba + except: + try: + from PyQt4.QtGui import QImage, qRgba + except: + from PySide.QtGui import QImage, qRgba + +else: #PyQt4 is used + from PyQt4.QtGui import QImage, qRgba + +## +# (Internal) Turns an RGB color into a Qt compatible color integer. + +def rgb(r, g, b, a=255): + # use qRgb to pack the colors, and then turn the resulting long + # into a negative integer with the same bitpattern. + return (qRgba(r, g, b, a) & 0xffffffff) + + +## +# An PIL image wrapper for Qt. This is a subclass of PyQt4's QImage +# class. +# +# @param im A PIL Image object, or a file name (given either as Python +# string or a PyQt string object). + +class ImageQt(QImage): + + def __init__(self, im): + + data = None + colortable = None + + # handle filename, if given instead of image name + if hasattr(im, "toUtf8"): + # FIXME - is this really the best way to do this? + im = unicode(im.toUtf8(), "utf-8") + if isPath(im): + im = Image.open(im) + + if im.mode == "1": + format = QImage.Format_Mono + elif im.mode == "L": + format = QImage.Format_Indexed8 + colortable = [] + for i in range(256): + colortable.append(rgb(i, i, i)) + elif im.mode == "P": + format = QImage.Format_Indexed8 + colortable = [] + palette = im.getpalette() + for i in range(0, len(palette), 3): + colortable.append(rgb(*palette[i:i+3])) + elif im.mode == "RGB": + data = im.tobytes("raw", "BGRX") + format = QImage.Format_RGB32 + elif im.mode == "RGBA": + try: + data = im.tobytes("raw", "BGRA") + except SystemError: + # workaround for earlier versions + r, g, b, a = im.split() + im = Image.merge("RGBA", (b, g, r, a)) + format = QImage.Format_ARGB32 + else: + raise ValueError("unsupported image mode %r" % im.mode) + + # must keep a reference, or Qt will crash! + self.__data = data or im.tobytes() + + QImage.__init__(self, self.__data, im.size[0], im.size[1], format) + + if colortable: + self.setColorTable(colortable) diff --git a/pyPackages/pillowarmv7l/PIL/ImageSequence.py b/pyPackages/pillowarmv7l/PIL/ImageSequence.py new file mode 100644 index 0000000..dd01e29 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/ImageSequence.py @@ -0,0 +1,42 @@ +# +# The Python Imaging Library. +# $Id$ +# +# sequence support classes +# +# history: +# 1997-02-20 fl Created +# +# Copyright (c) 1997 by Secret Labs AB. +# Copyright (c) 1997 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +## + + +class Iterator: + """ + This class implements an iterator object that can be used to loop + over an image sequence. + + You can use the ``[]`` operator to access elements by index. This operator + will raise an :py:exc:`IndexError` if you try to access a nonexistent + frame. + + :param im: An image object. + """ + + def __init__(self, im): + if not hasattr(im, "seek"): + raise AttributeError("im must have seek method") + self.im = im + + def __getitem__(self, ix): + try: + if ix: + self.im.seek(ix) + return self.im + except EOFError: + raise IndexError # end of sequence diff --git a/pyPackages/pillowarmv7l/PIL/ImageShow.py b/pyPackages/pillowarmv7l/PIL/ImageShow.py new file mode 100644 index 0000000..9527dbf --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/ImageShow.py @@ -0,0 +1,179 @@ +# +# The Python Imaging Library. +# $Id$ +# +# im.show() drivers +# +# History: +# 2008-04-06 fl Created +# +# Copyright (c) Secret Labs AB 2008. +# +# See the README file for information on usage and redistribution. +# + +from __future__ import print_function + +from PIL import Image +import os +import sys + +if sys.version_info >= (3, 3): + from shlex import quote +else: + from pipes import quote + +_viewers = [] + + +def register(viewer, order=1): + try: + if issubclass(viewer, Viewer): + viewer = viewer() + except TypeError: + pass # raised if viewer wasn't a class + if order > 0: + _viewers.append(viewer) + elif order < 0: + _viewers.insert(0, viewer) + + +## +# Displays a given image. +# +# @param image An image object. +# @param title Optional title. Not all viewers can display the title. +# @param **options Additional viewer options. +# @return True if a suitable viewer was found, false otherwise. + +def show(image, title=None, **options): + for viewer in _viewers: + if viewer.show(image, title=title, **options): + return 1 + return 0 + + +## +# Base class for viewers. + +class Viewer: + + # main api + + def show(self, image, **options): + + # save temporary image to disk + if image.mode[:4] == "I;16": + # @PIL88 @PIL101 + # "I;16" isn't an 'official' mode, but we still want to + # provide a simple way to show 16-bit images. + base = "L" + # FIXME: auto-contrast if max() > 255? + else: + base = Image.getmodebase(image.mode) + if base != image.mode and image.mode != "1": + image = image.convert(base) + + return self.show_image(image, **options) + + # hook methods + + format = None + + def get_format(self, image): + # return format name, or None to save as PGM/PPM + return self.format + + def get_command(self, file, **options): + raise NotImplementedError + + def save_image(self, image): + # save to temporary file, and return filename + return image._dump(format=self.get_format(image)) + + def show_image(self, image, **options): + # display given image + return self.show_file(self.save_image(image), **options) + + def show_file(self, file, **options): + # display given file + os.system(self.get_command(file, **options)) + return 1 + +# -------------------------------------------------------------------- + +if sys.platform == "win32": + + class WindowsViewer(Viewer): + format = "BMP" + + def get_command(self, file, **options): + return ('start "Pillow" /WAIT "%s" ' + '&& ping -n 2 127.0.0.1 >NUL ' + '&& del /f "%s"' % (file, file)) + + register(WindowsViewer) + +elif sys.platform == "darwin": + + class MacViewer(Viewer): + format = "BMP" + + def get_command(self, file, **options): + # on darwin open returns immediately resulting in the temp + # file removal while app is opening + command = "open -a /Applications/Preview.app" + command = "(%s %s; sleep 20; rm -f %s)&" % (command, quote(file), + quote(file)) + return command + + register(MacViewer) + +else: + + # unixoids + + def which(executable): + path = os.environ.get("PATH") + if not path: + return None + for dirname in path.split(os.pathsep): + filename = os.path.join(dirname, executable) + if os.path.isfile(filename): + # FIXME: make sure it's executable + return filename + return None + + class UnixViewer(Viewer): + def show_file(self, file, **options): + command, executable = self.get_command_ex(file, **options) + command = "(%s %s; rm -f %s)&" % (command, quote(file), + quote(file)) + os.system(command) + return 1 + + # implementations + + class DisplayViewer(UnixViewer): + def get_command_ex(self, file, **options): + command = executable = "display" + return command, executable + + if which("display"): + register(DisplayViewer) + + class XVViewer(UnixViewer): + def get_command_ex(self, file, title=None, **options): + # note: xv is pretty outdated. most modern systems have + # imagemagick's display command instead. + command = executable = "xv" + if title: + command += " -name %s" % quote(title) + return command, executable + + if which("xv"): + register(XVViewer) + +if __name__ == "__main__": + # usage: python ImageShow.py imagefile [title] + print(show(Image.open(sys.argv[1]), *sys.argv[2:])) diff --git a/pyPackages/pillowarmv7l/PIL/ImageStat.py b/pyPackages/pillowarmv7l/PIL/ImageStat.py new file mode 100644 index 0000000..7e023c6 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/ImageStat.py @@ -0,0 +1,147 @@ +# +# The Python Imaging Library. +# $Id$ +# +# global image statistics +# +# History: +# 1996-04-05 fl Created +# 1997-05-21 fl Added mask; added rms, var, stddev attributes +# 1997-08-05 fl Added median +# 1998-07-05 hk Fixed integer overflow error +# +# Notes: +# This class shows how to implement delayed evaluation of attributes. +# To get a certain value, simply access the corresponding attribute. +# The __getattr__ dispatcher takes care of the rest. +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996-97. +# +# See the README file for information on usage and redistribution. +# + +import math +import operator +from functools import reduce + + +class Stat: + + def __init__(self, image_or_list, mask=None): + try: + if mask: + self.h = image_or_list.histogram(mask) + else: + self.h = image_or_list.histogram() + except AttributeError: + self.h = image_or_list # assume it to be a histogram list + if not isinstance(self.h, list): + raise TypeError("first argument must be image or list") + self.bands = list(range(len(self.h) // 256)) + + def __getattr__(self, id): + "Calculate missing attribute" + if id[:4] == "_get": + raise AttributeError(id) + # calculate missing attribute + v = getattr(self, "_get" + id)() + setattr(self, id, v) + return v + + def _getextrema(self): + "Get min/max values for each band in the image" + + def minmax(histogram): + n = 255 + x = 0 + for i in range(256): + if histogram[i]: + n = min(n, i) + x = max(x, i) + return n, x # returns (255, 0) if there's no data in the histogram + + v = [] + for i in range(0, len(self.h), 256): + v.append(minmax(self.h[i:])) + return v + + def _getcount(self): + "Get total number of pixels in each layer" + + v = [] + for i in range(0, len(self.h), 256): + v.append(reduce(operator.add, self.h[i:i+256])) + return v + + def _getsum(self): + "Get sum of all pixels in each layer" + + v = [] + for i in range(0, len(self.h), 256): + sum = 0.0 + for j in range(256): + sum += j * self.h[i + j] + v.append(sum) + return v + + def _getsum2(self): + "Get squared sum of all pixels in each layer" + + v = [] + for i in range(0, len(self.h), 256): + sum2 = 0.0 + for j in range(256): + sum2 += (j ** 2) * float(self.h[i + j]) + v.append(sum2) + return v + + def _getmean(self): + "Get average pixel level for each layer" + + v = [] + for i in self.bands: + v.append(self.sum[i] / self.count[i]) + return v + + def _getmedian(self): + "Get median pixel level for each layer" + + v = [] + for i in self.bands: + s = 0 + l = self.count[i]//2 + b = i * 256 + for j in range(256): + s = s + self.h[b+j] + if s > l: + break + v.append(j) + return v + + def _getrms(self): + "Get RMS for each layer" + + v = [] + for i in self.bands: + v.append(math.sqrt(self.sum2[i] / self.count[i])) + return v + + def _getvar(self): + "Get variance for each layer" + + v = [] + for i in self.bands: + n = self.count[i] + v.append((self.sum2[i]-(self.sum[i]**2.0)/n)/n) + return v + + def _getstddev(self): + "Get standard deviation for each layer" + + v = [] + for i in self.bands: + v.append(math.sqrt(self.var[i])) + return v + +Global = Stat # compatibility diff --git a/pyPackages/pillowarmv7l/PIL/ImageTk.py b/pyPackages/pillowarmv7l/PIL/ImageTk.py new file mode 100644 index 0000000..5fb5ecf --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/ImageTk.py @@ -0,0 +1,292 @@ +# +# The Python Imaging Library. +# $Id$ +# +# a Tk display interface +# +# History: +# 96-04-08 fl Created +# 96-09-06 fl Added getimage method +# 96-11-01 fl Rewritten, removed image attribute and crop method +# 97-05-09 fl Use PyImagingPaste method instead of image type +# 97-05-12 fl Minor tweaks to match the IFUNC95 interface +# 97-05-17 fl Support the "pilbitmap" booster patch +# 97-06-05 fl Added file= and data= argument to image constructors +# 98-03-09 fl Added width and height methods to Image classes +# 98-07-02 fl Use default mode for "P" images without palette attribute +# 98-07-02 fl Explicitly destroy Tkinter image objects +# 99-07-24 fl Support multiple Tk interpreters (from Greg Couch) +# 99-07-26 fl Automatically hook into Tkinter (if possible) +# 99-08-15 fl Hook uses _imagingtk instead of _imaging +# +# Copyright (c) 1997-1999 by Secret Labs AB +# Copyright (c) 1996-1997 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +try: + import tkinter +except ImportError: + import Tkinter + tkinter = Tkinter + del Tkinter + +from PIL import Image + + +# -------------------------------------------------------------------- +# Check for Tkinter interface hooks + +_pilbitmap_ok = None + + +def _pilbitmap_check(): + global _pilbitmap_ok + if _pilbitmap_ok is None: + try: + im = Image.new("1", (1, 1)) + tkinter.BitmapImage(data="PIL:%d" % im.im.id) + _pilbitmap_ok = 1 + except tkinter.TclError: + _pilbitmap_ok = 0 + return _pilbitmap_ok + + +# -------------------------------------------------------------------- +# PhotoImage + +class PhotoImage: + """ + A Tkinter-compatible photo image. This can be used + everywhere Tkinter expects an image object. If the image is an RGBA + image, pixels having alpha 0 are treated as transparent. + + The constructor takes either a PIL image, or a mode and a size. + Alternatively, you can use the **file** or **data** options to initialize + the photo image object. + + :param image: Either a PIL image, or a mode string. If a mode string is + used, a size must also be given. + :param size: If the first argument is a mode string, this defines the size + of the image. + :keyword file: A filename to load the image from (using + ``Image.open(file)``). + :keyword data: An 8-bit string containing image data (as loaded from an + image file). + """ + + def __init__(self, image=None, size=None, **kw): + + # Tk compatibility: file or data + if image is None: + if "file" in kw: + image = Image.open(kw["file"]) + del kw["file"] + elif "data" in kw: + from io import BytesIO + image = Image.open(BytesIO(kw["data"])) + del kw["data"] + + if hasattr(image, "mode") and hasattr(image, "size"): + # got an image instead of a mode + mode = image.mode + if mode == "P": + # palette mapped data + image.load() + try: + mode = image.palette.mode + except AttributeError: + mode = "RGB" # default + size = image.size + kw["width"], kw["height"] = size + else: + mode = image + image = None + + if mode not in ["1", "L", "RGB", "RGBA"]: + mode = Image.getmodebase(mode) + + self.__mode = mode + self.__size = size + self.__photo = tkinter.PhotoImage(**kw) + self.tk = self.__photo.tk + if image: + self.paste(image) + + def __del__(self): + name = self.__photo.name + self.__photo.name = None + try: + self.__photo.tk.call("image", "delete", name) + except: + pass # ignore internal errors + + def __str__(self): + """ + Get the Tkinter photo image identifier. This method is automatically + called by Tkinter whenever a PhotoImage object is passed to a Tkinter + method. + + :return: A Tkinter photo image identifier (a string). + """ + return str(self.__photo) + + def width(self): + """ + Get the width of the image. + + :return: The width, in pixels. + """ + return self.__size[0] + + def height(self): + """ + Get the height of the image. + + :return: The height, in pixels. + """ + return self.__size[1] + + def paste(self, im, box=None): + """ + Paste a PIL image into the photo image. Note that this can + be very slow if the photo image is displayed. + + :param im: A PIL image. The size must match the target region. If the + mode does not match, the image is converted to the mode of + the bitmap image. + :param box: A 4-tuple defining the left, upper, right, and lower pixel + coordinate. If None is given instead of a tuple, all of + the image is assumed. + """ + + # convert to blittable + im.load() + image = im.im + if image.isblock() and im.mode == self.__mode: + block = image + else: + block = image.new_block(self.__mode, im.size) + image.convert2(block, image) # convert directly between buffers + + tk = self.__photo.tk + + try: + tk.call("PyImagingPhoto", self.__photo, block.id) + except tkinter.TclError: + # activate Tkinter hook + try: + from PIL import _imagingtk + try: + _imagingtk.tkinit(tk.interpaddr(), 1) + except AttributeError: + _imagingtk.tkinit(id(tk), 0) + tk.call("PyImagingPhoto", self.__photo, block.id) + except (ImportError, AttributeError, tkinter.TclError): + raise # configuration problem; cannot attach to Tkinter + +# -------------------------------------------------------------------- +# BitmapImage + + +class BitmapImage: + """ + + A Tkinter-compatible bitmap image. This can be used everywhere Tkinter + expects an image object. + + The given image must have mode "1". Pixels having value 0 are treated as + transparent. Options, if any, are passed on to Tkinter. The most commonly + used option is **foreground**, which is used to specify the color for the + non-transparent parts. See the Tkinter documentation for information on + how to specify colours. + + :param image: A PIL image. + """ + + def __init__(self, image=None, **kw): + + # Tk compatibility: file or data + if image is None: + if "file" in kw: + image = Image.open(kw["file"]) + del kw["file"] + elif "data" in kw: + from io import BytesIO + image = Image.open(BytesIO(kw["data"])) + del kw["data"] + + self.__mode = image.mode + self.__size = image.size + + if _pilbitmap_check(): + # fast way (requires the pilbitmap booster patch) + image.load() + kw["data"] = "PIL:%d" % image.im.id + self.__im = image # must keep a reference + else: + # slow but safe way + kw["data"] = image.tobitmap() + self.__photo = tkinter.BitmapImage(**kw) + + def __del__(self): + name = self.__photo.name + self.__photo.name = None + try: + self.__photo.tk.call("image", "delete", name) + except: + pass # ignore internal errors + + def width(self): + """ + Get the width of the image. + + :return: The width, in pixels. + """ + return self.__size[0] + + def height(self): + """ + Get the height of the image. + + :return: The height, in pixels. + """ + return self.__size[1] + + def __str__(self): + """ + Get the Tkinter bitmap image identifier. This method is automatically + called by Tkinter whenever a BitmapImage object is passed to a Tkinter + method. + + :return: A Tkinter bitmap image identifier (a string). + """ + return str(self.__photo) + + +def getimage(photo): + """Copies the contents of a PhotoImage to a PIL image memory.""" + photo.tk.call("PyImagingPhotoGet", photo) + + +# -------------------------------------------------------------------- +# Helper for the Image.show method. + +def _show(image, title): + + class UI(tkinter.Label): + def __init__(self, master, im): + if im.mode == "1": + self.image = BitmapImage(im, foreground="white", master=master) + else: + self.image = PhotoImage(im, master=master) + tkinter.Label.__init__(self, master, image=self.image, + bg="black", bd=0) + + if not tkinter._default_root: + raise IOError("tkinter not initialized") + top = tkinter.Toplevel() + if title: + top.title(title) + UI(top, image).pack() diff --git a/pyPackages/pillowarmv7l/PIL/ImageTransform.py b/pyPackages/pillowarmv7l/PIL/ImageTransform.py new file mode 100644 index 0000000..81f9050 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/ImageTransform.py @@ -0,0 +1,103 @@ +# +# The Python Imaging Library. +# $Id$ +# +# transform wrappers +# +# History: +# 2002-04-08 fl Created +# +# Copyright (c) 2002 by Secret Labs AB +# Copyright (c) 2002 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image + + +class Transform(Image.ImageTransformHandler): + def __init__(self, data): + self.data = data + + def getdata(self): + return self.method, self.data + + def transform(self, size, image, **options): + # can be overridden + method, data = self.getdata() + return image.transform(size, method, data, **options) + + +## +# Define an affine image transform. +#

+# This function takes a 6-tuple (a, b, c, d, e, f) which +# contain the first two rows from an affine transform matrix. For +# each pixel (x, y) in the output image, the new value is +# taken from a position (a x + b y + c, +# d x + e y + f) in the input image, rounded to +# nearest pixel. +#

+# This function can be used to scale, translate, rotate, and shear the +# original image. +# +# @def AffineTransform(matrix) +# @param matrix A 6-tuple (a, b, c, d, e, f) containing +# the first two rows from an affine transform matrix. +# @see Image#Image.transform + + +class AffineTransform(Transform): + method = Image.AFFINE + + +## +# Define a transform to extract a subregion from an image. +#

+# Maps a rectangle (defined by two corners) from the image to a +# rectangle of the given size. The resulting image will contain +# data sampled from between the corners, such that (x0, y0) +# in the input image will end up at (0,0) in the output image, +# and (x1, y1) at size. +#

+# This method can be used to crop, stretch, shrink, or mirror an +# arbitrary rectangle in the current image. It is slightly slower than +# crop, but about as fast as a corresponding resize +# operation. +# +# @def ExtentTransform(bbox) +# @param bbox A 4-tuple (x0, y0, x1, y1) which specifies +# two points in the input image's coordinate system. +# @see Image#Image.transform + +class ExtentTransform(Transform): + method = Image.EXTENT + + +## +# Define an quad image transform. +#

+# Maps a quadrilateral (a region defined by four corners) from the +# image to a rectangle of the given size. +# +# @def QuadTransform(xy) +# @param xy An 8-tuple (x0, y0, x1, y1, x2, y2, y3, y3) which +# contain the upper left, lower left, lower right, and upper right +# corner of the source quadrilateral. +# @see Image#Image.transform + +class QuadTransform(Transform): + method = Image.QUAD + + +## +# Define an mesh image transform. A mesh transform consists of one +# or more individual quad transforms. +# +# @def MeshTransform(data) +# @param data A list of (bbox, quad) tuples. +# @see Image#Image.transform + +class MeshTransform(Transform): + method = Image.MESH diff --git a/pyPackages/pillowarmv7l/PIL/ImageWin.py b/pyPackages/pillowarmv7l/PIL/ImageWin.py new file mode 100644 index 0000000..300d118 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/ImageWin.py @@ -0,0 +1,251 @@ +# +# The Python Imaging Library. +# $Id$ +# +# a Windows DIB display interface +# +# History: +# 1996-05-20 fl Created +# 1996-09-20 fl Fixed subregion exposure +# 1997-09-21 fl Added draw primitive (for tzPrint) +# 2003-05-21 fl Added experimental Window/ImageWindow classes +# 2003-09-05 fl Added fromstring/tostring methods +# +# Copyright (c) Secret Labs AB 1997-2003. +# Copyright (c) Fredrik Lundh 1996-2003. +# +# See the README file for information on usage and redistribution. +# + +import warnings +from PIL import Image + + +class HDC: + """ + Wraps an HDC integer. The resulting object can be passed to the + :py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose` + methods. + """ + def __init__(self, dc): + self.dc = dc + + def __int__(self): + return self.dc + + +class HWND: + """ + Wraps an HWND integer. The resulting object can be passed to the + :py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose` + methods, instead of a DC. + """ + def __init__(self, wnd): + self.wnd = wnd + + def __int__(self): + return self.wnd + + +class Dib: + """ + A Windows bitmap with the given mode and size. The mode can be one of "1", + "L", "P", or "RGB". + + If the display requires a palette, this constructor creates a suitable + palette and associates it with the image. For an "L" image, 128 greylevels + are allocated. For an "RGB" image, a 6x6x6 colour cube is used, together + with 20 greylevels. + + To make sure that palettes work properly under Windows, you must call the + **palette** method upon certain events from Windows. + + :param image: Either a PIL image, or a mode string. If a mode string is + used, a size must also be given. The mode can be one of "1", + "L", "P", or "RGB". + :param size: If the first argument is a mode string, this + defines the size of the image. + """ + + def __init__(self, image, size=None): + if hasattr(image, "mode") and hasattr(image, "size"): + mode = image.mode + size = image.size + else: + mode = image + image = None + if mode not in ["1", "L", "P", "RGB"]: + mode = Image.getmodebase(mode) + self.image = Image.core.display(mode, size) + self.mode = mode + self.size = size + if image: + self.paste(image) + + def expose(self, handle): + """ + Copy the bitmap contents to a device context. + + :param handle: Device context (HDC), cast to a Python integer, or an + HDC or HWND instance. In PythonWin, you can use the + :py:meth:`CDC.GetHandleAttrib` to get a suitable handle. + """ + if isinstance(handle, HWND): + dc = self.image.getdc(handle) + try: + result = self.image.expose(dc) + finally: + self.image.releasedc(handle, dc) + else: + result = self.image.expose(handle) + return result + + def draw(self, handle, dst, src=None): + """ + Same as expose, but allows you to specify where to draw the image, and + what part of it to draw. + + The destination and source areas are given as 4-tuple rectangles. If + the source is omitted, the entire image is copied. If the source and + the destination have different sizes, the image is resized as + necessary. + """ + if not src: + src = (0, 0) + self.size + if isinstance(handle, HWND): + dc = self.image.getdc(handle) + try: + result = self.image.draw(dc, dst, src) + finally: + self.image.releasedc(handle, dc) + else: + result = self.image.draw(handle, dst, src) + return result + + def query_palette(self, handle): + """ + Installs the palette associated with the image in the given device + context. + + This method should be called upon **QUERYNEWPALETTE** and + **PALETTECHANGED** events from Windows. If this method returns a + non-zero value, one or more display palette entries were changed, and + the image should be redrawn. + + :param handle: Device context (HDC), cast to a Python integer, or an + HDC or HWND instance. + :return: A true value if one or more entries were changed (this + indicates that the image should be redrawn). + """ + if isinstance(handle, HWND): + handle = self.image.getdc(handle) + try: + result = self.image.query_palette(handle) + finally: + self.image.releasedc(handle, handle) + else: + result = self.image.query_palette(handle) + return result + + def paste(self, im, box=None): + """ + Paste a PIL image into the bitmap image. + + :param im: A PIL image. The size must match the target region. + If the mode does not match, the image is converted to the + mode of the bitmap image. + :param box: A 4-tuple defining the left, upper, right, and + lower pixel coordinate. If None is given instead of a + tuple, all of the image is assumed. + """ + im.load() + if self.mode != im.mode: + im = im.convert(self.mode) + if box: + self.image.paste(im.im, box) + else: + self.image.paste(im.im) + + def frombytes(self, buffer): + """ + Load display memory contents from byte data. + + :param buffer: A buffer containing display data (usually + data returned from tobytes) + """ + return self.image.frombytes(buffer) + + def tobytes(self): + """ + Copy display memory contents to bytes object. + + :return: A bytes object containing display data. + """ + return self.image.tobytes() + + ## + # Deprecated aliases to frombytes & tobytes. + + def fromstring(self, *args, **kw): + warnings.warn( + 'fromstring() is deprecated. Please call frombytes() instead.', + DeprecationWarning, + stacklevel=2 + ) + return self.frombytes(*args, **kw) + + def tostring(self): + warnings.warn( + 'tostring() is deprecated. Please call tobytes() instead.', + DeprecationWarning, + stacklevel=2 + ) + return self.tobytes() + + +## +# Create a Window with the given title size. + +class Window: + + def __init__(self, title="PIL", width=None, height=None): + self.hwnd = Image.core.createwindow( + title, self.__dispatcher, width or 0, height or 0 + ) + + def __dispatcher(self, action, *args): + return getattr(self, "ui_handle_" + action)(*args) + + def ui_handle_clear(self, dc, x0, y0, x1, y1): + pass + + def ui_handle_damage(self, x0, y0, x1, y1): + pass + + def ui_handle_destroy(self): + pass + + def ui_handle_repair(self, dc, x0, y0, x1, y1): + pass + + def ui_handle_resize(self, width, height): + pass + + def mainloop(self): + Image.core.eventloop() + + +## +# Create an image window which displays the given image. + +class ImageWindow(Window): + + def __init__(self, image, title="PIL"): + if not isinstance(image, Dib): + image = Dib(image) + self.image = image + width, height = image.size + Window.__init__(self, title, width=width, height=height) + + def ui_handle_repair(self, dc, x0, y0, x1, y1): + self.image.draw(dc, (x0, y0, x1, y1)) diff --git a/pyPackages/pillowarmv7l/PIL/ImtImagePlugin.py b/pyPackages/pillowarmv7l/PIL/ImtImagePlugin.py new file mode 100644 index 0000000..f512eb8 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/ImtImagePlugin.py @@ -0,0 +1,94 @@ +# +# The Python Imaging Library. +# $Id$ +# +# IM Tools support for PIL +# +# history: +# 1996-05-27 fl Created (read 8-bit images only) +# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.2) +# +# Copyright (c) Secret Labs AB 1997-2001. +# Copyright (c) Fredrik Lundh 1996-2001. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.2" + +import re + +from PIL import Image, ImageFile + +# +# -------------------------------------------------------------------- + +field = re.compile(br"([a-z]*) ([^ \r\n]*)") + + +## +# Image plugin for IM Tools images. + +class ImtImageFile(ImageFile.ImageFile): + + format = "IMT" + format_description = "IM Tools" + + def _open(self): + + # Quick rejection: if there's not a LF among the first + # 100 bytes, this is (probably) not a text header. + + if b"\n" not in self.fp.read(100): + raise SyntaxError("not an IM file") + self.fp.seek(0) + + xsize = ysize = 0 + + while True: + + s = self.fp.read(1) + if not s: + break + + if s == b'\x0C': + + # image data begins + self.tile = [("raw", (0, 0)+self.size, + self.fp.tell(), + (self.mode, 0, 1))] + + break + + else: + + # read key/value pair + # FIXME: dangerous, may read whole file + s = s + self.fp.readline() + if len(s) == 1 or len(s) > 100: + break + if s[0] == b"*": + continue # comment + + m = field.match(s) + if not m: + break + k, v = m.group(1, 2) + if k == "width": + xsize = int(v) + self.size = xsize, ysize + elif k == "height": + ysize = int(v) + self.size = xsize, ysize + elif k == "pixel" and v == "n8": + self.mode = "L" + + +# +# -------------------------------------------------------------------- + +Image.register_open("IMT", ImtImageFile) + +# +# no extension registered (".im" is simply too common) diff --git a/pyPackages/pillowarmv7l/PIL/IptcImagePlugin.py b/pyPackages/pillowarmv7l/PIL/IptcImagePlugin.py new file mode 100644 index 0000000..dc86075 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/IptcImagePlugin.py @@ -0,0 +1,268 @@ +# +# The Python Imaging Library. +# $Id$ +# +# IPTC/NAA file handling +# +# history: +# 1995-10-01 fl Created +# 1998-03-09 fl Cleaned up and added to PIL +# 2002-06-18 fl Added getiptcinfo helper +# +# Copyright (c) Secret Labs AB 1997-2002. +# Copyright (c) Fredrik Lundh 1995. +# +# See the README file for information on usage and redistribution. +# + +from __future__ import print_function + +__version__ = "0.3" + + +from PIL import Image, ImageFile, _binary +import os +import tempfile + +i8 = _binary.i8 +i16 = _binary.i16be +i32 = _binary.i32be +o8 = _binary.o8 + +COMPRESSION = { + 1: "raw", + 5: "jpeg" +} + +PAD = o8(0) * 4 + + +# +# Helpers + +def i(c): + return i32((PAD + c)[-4:]) + + +def dump(c): + for i in c: + print("%02x" % i8(i), end=' ') + print() + + +## +# Image plugin for IPTC/NAA datastreams. To read IPTC/NAA fields +# from TIFF and JPEG files, use the getiptcinfo function. + +class IptcImageFile(ImageFile.ImageFile): + + format = "IPTC" + format_description = "IPTC/NAA" + + def getint(self, key): + return i(self.info[key]) + + def field(self): + # + # get a IPTC field header + s = self.fp.read(5) + if not len(s): + return None, 0 + + tag = i8(s[1]), i8(s[2]) + + # syntax + if i8(s[0]) != 0x1C or tag[0] < 1 or tag[0] > 9: + raise SyntaxError("invalid IPTC/NAA file") + + # field size + size = i8(s[3]) + if size > 132: + raise IOError("illegal field length in IPTC/NAA file") + elif size == 128: + size = 0 + elif size > 128: + size = i(self.fp.read(size-128)) + else: + size = i16(s[3:]) + + return tag, size + + def _open(self): + + # load descriptive fields + while True: + offset = self.fp.tell() + tag, size = self.field() + if not tag or tag == (8, 10): + break + if size: + tagdata = self.fp.read(size) + else: + tagdata = None + if tag in list(self.info.keys()): + if isinstance(self.info[tag], list): + self.info[tag].append(tagdata) + else: + self.info[tag] = [self.info[tag], tagdata] + else: + self.info[tag] = tagdata + + # print tag, self.info[tag] + + # mode + layers = i8(self.info[(3, 60)][0]) + component = i8(self.info[(3, 60)][1]) + if (3, 65) in self.info: + id = i8(self.info[(3, 65)][0])-1 + else: + id = 0 + if layers == 1 and not component: + self.mode = "L" + elif layers == 3 and component: + self.mode = "RGB"[id] + elif layers == 4 and component: + self.mode = "CMYK"[id] + + # size + self.size = self.getint((3, 20)), self.getint((3, 30)) + + # compression + try: + compression = COMPRESSION[self.getint((3, 120))] + except KeyError: + raise IOError("Unknown IPTC image compression") + + # tile + if tag == (8, 10): + self.tile = [("iptc", (compression, offset), + (0, 0, self.size[0], self.size[1]))] + + def load(self): + + if len(self.tile) != 1 or self.tile[0][0] != "iptc": + return ImageFile.ImageFile.load(self) + + type, tile, box = self.tile[0] + + encoding, offset = tile + + self.fp.seek(offset) + + # Copy image data to temporary file + o_fd, outfile = tempfile.mkstemp(text=False) + o = os.fdopen(o_fd) + if encoding == "raw": + # To simplify access to the extracted file, + # prepend a PPM header + o.write("P5\n%d %d\n255\n" % self.size) + while True: + type, size = self.field() + if type != (8, 10): + break + while size > 0: + s = self.fp.read(min(size, 8192)) + if not s: + break + o.write(s) + size -= len(s) + o.close() + + try: + try: + # fast + self.im = Image.core.open_ppm(outfile) + except: + # slightly slower + im = Image.open(outfile) + im.load() + self.im = im.im + finally: + try: + os.unlink(outfile) + except: + pass + + +Image.register_open("IPTC", IptcImageFile) + +Image.register_extension("IPTC", ".iim") + + +## +# Get IPTC information from TIFF, JPEG, or IPTC file. +# +# @param im An image containing IPTC data. +# @return A dictionary containing IPTC information, or None if +# no IPTC information block was found. + +def getiptcinfo(im): + + from PIL import TiffImagePlugin, JpegImagePlugin + import io + + data = None + + if isinstance(im, IptcImageFile): + # return info dictionary right away + return im.info + + elif isinstance(im, JpegImagePlugin.JpegImageFile): + # extract the IPTC/NAA resource + try: + app = im.app["APP13"] + if app[:14] == b"Photoshop 3.0\x00": + app = app[14:] + # parse the image resource block + offset = 0 + while app[offset:offset+4] == b"8BIM": + offset += 4 + # resource code + code = JpegImagePlugin.i16(app, offset) + offset += 2 + # resource name (usually empty) + name_len = i8(app[offset]) + name = app[offset+1:offset+1+name_len] + offset = 1 + offset + name_len + if offset & 1: + offset += 1 + # resource data block + size = JpegImagePlugin.i32(app, offset) + offset += 4 + if code == 0x0404: + # 0x0404 contains IPTC/NAA data + data = app[offset:offset+size] + break + offset = offset + size + if offset & 1: + offset += 1 + except (AttributeError, KeyError): + pass + + elif isinstance(im, TiffImagePlugin.TiffImageFile): + # get raw data from the IPTC/NAA tag (PhotoShop tags the data + # as 4-byte integers, so we cannot use the get method...) + try: + data = im.tag.tagdata[TiffImagePlugin.IPTC_NAA_CHUNK] + except (AttributeError, KeyError): + pass + + if data is None: + return None # no properties + + # create an IptcImagePlugin object without initializing it + class FakeImage: + pass + im = FakeImage() + im.__class__ = IptcImageFile + + # parse the IPTC information chunk + im.info = {} + im.fp = io.BytesIO(data) + + try: + im._open() + except (IndexError, KeyError): + pass # expected failure + + return im.info diff --git a/pyPackages/pillowarmv7l/PIL/Jpeg2KImagePlugin.py b/pyPackages/pillowarmv7l/PIL/Jpeg2KImagePlugin.py new file mode 100644 index 0000000..446699f --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/Jpeg2KImagePlugin.py @@ -0,0 +1,277 @@ +# +# The Python Imaging Library +# $Id$ +# +# JPEG2000 file handling +# +# History: +# 2014-03-12 ajh Created +# +# Copyright (c) 2014 Coriolis Systems Limited +# Copyright (c) 2014 Alastair Houghton +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.1" + +from PIL import Image, ImageFile +import struct +import os +import io + + +def _parse_codestream(fp): + """Parse the JPEG 2000 codestream to extract the size and component + count from the SIZ marker segment, returning a PIL (size, mode) tuple.""" + + hdr = fp.read(2) + lsiz = struct.unpack('>H', hdr)[0] + siz = hdr + fp.read(lsiz - 2) + lsiz, rsiz, xsiz, ysiz, xosiz, yosiz, xtsiz, ytsiz, \ + xtosiz, ytosiz, csiz \ + = struct.unpack('>HHIIIIIIIIH', siz[:38]) + ssiz = [None]*csiz + xrsiz = [None]*csiz + yrsiz = [None]*csiz + for i in range(csiz): + ssiz[i], xrsiz[i], yrsiz[i] \ + = struct.unpack('>BBB', siz[36 + 3 * i:39 + 3 * i]) + + size = (xsiz - xosiz, ysiz - yosiz) + if csiz == 1: + if (yrsiz[0] & 0x7f) > 8: + mode = 'I;16' + else: + mode = 'L' + elif csiz == 2: + mode = 'LA' + elif csiz == 3: + mode = 'RGB' + elif csiz == 4: + mode = 'RGBA' + else: + mode = None + + return (size, mode) + + +def _parse_jp2_header(fp): + """Parse the JP2 header box to extract size, component count and + color space information, returning a PIL (size, mode) tuple.""" + + # Find the JP2 header box + header = None + while True: + lbox, tbox = struct.unpack('>I4s', fp.read(8)) + if lbox == 1: + lbox = struct.unpack('>Q', fp.read(8))[0] + hlen = 16 + else: + hlen = 8 + + if lbox < hlen: + raise SyntaxError('Invalid JP2 header length') + + if tbox == b'jp2h': + header = fp.read(lbox - hlen) + break + else: + fp.seek(lbox - hlen, os.SEEK_CUR) + + if header is None: + raise SyntaxError('could not find JP2 header') + + size = None + mode = None + bpc = None + + hio = io.BytesIO(header) + while True: + lbox, tbox = struct.unpack('>I4s', hio.read(8)) + if lbox == 1: + lbox = struct.unpack('>Q', hio.read(8))[0] + hlen = 16 + else: + hlen = 8 + + content = hio.read(lbox - hlen) + + if tbox == b'ihdr': + height, width, nc, bpc, c, unkc, ipr \ + = struct.unpack('>IIHBBBB', content) + size = (width, height) + if unkc: + if nc == 1 and (bpc & 0x7f) > 8: + mode = 'I;16' + elif nc == 1: + mode = 'L' + elif nc == 2: + mode = 'LA' + elif nc == 3: + mode = 'RGB' + elif nc == 4: + mode = 'RGBA' + break + elif tbox == b'colr': + meth, prec, approx = struct.unpack('>BBB', content[:3]) + if meth == 1: + cs = struct.unpack('>I', content[3:7])[0] + if cs == 16: # sRGB + if nc == 1 and (bpc & 0x7f) > 8: + mode = 'I;16' + elif nc == 1: + mode = 'L' + elif nc == 3: + mode = 'RGB' + elif nc == 4: + mode = 'RGBA' + break + elif cs == 17: # grayscale + if nc == 1 and (bpc & 0x7f) > 8: + mode = 'I;16' + elif nc == 1: + mode = 'L' + elif nc == 2: + mode = 'LA' + break + elif cs == 18: # sYCC + if nc == 3: + mode = 'RGB' + elif nc == 4: + mode = 'RGBA' + break + + return (size, mode) + +## +# Image plugin for JPEG2000 images. + + +class Jpeg2KImageFile(ImageFile.ImageFile): + format = "JPEG2000" + format_description = "JPEG 2000 (ISO 15444)" + + def _open(self): + sig = self.fp.read(4) + if sig == b'\xff\x4f\xff\x51': + self.codec = "j2k" + self.size, self.mode = _parse_codestream(self.fp) + else: + sig = sig + self.fp.read(8) + + if sig == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a': + self.codec = "jp2" + self.size, self.mode = _parse_jp2_header(self.fp) + else: + raise SyntaxError('not a JPEG 2000 file') + + if self.size is None or self.mode is None: + raise SyntaxError('unable to determine size/mode') + + self.reduce = 0 + self.layers = 0 + + fd = -1 + length = -1 + + try: + fd = self.fp.fileno() + length = os.fstat(fd).st_size + except: + fd = -1 + try: + pos = self.fp.tell() + self.fp.seek(0, 2) + length = self.fp.tell() + self.fp.seek(pos, 0) + except: + length = -1 + + self.tile = [('jpeg2k', (0, 0) + self.size, 0, + (self.codec, self.reduce, self.layers, fd, length))] + + def load(self): + if self.reduce: + power = 1 << self.reduce + adjust = power >> 1 + self.size = (int((self.size[0] + adjust) / power), + int((self.size[1] + adjust) / power)) + + if self.tile: + # Update the reduce and layers settings + t = self.tile[0] + t3 = (t[3][0], self.reduce, self.layers, t[3][3], t[3][4]) + self.tile = [(t[0], (0, 0) + self.size, t[2], t3)] + + ImageFile.ImageFile.load(self) + + +def _accept(prefix): + return (prefix[:4] == b'\xff\x4f\xff\x51' + or prefix[:12] == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a') + + +# ------------------------------------------------------------ +# Save support + +def _save(im, fp, filename): + if filename.endswith('.j2k'): + kind = 'j2k' + else: + kind = 'jp2' + + # Get the keyword arguments + info = im.encoderinfo + + offset = info.get('offset', None) + tile_offset = info.get('tile_offset', None) + tile_size = info.get('tile_size', None) + quality_mode = info.get('quality_mode', 'rates') + quality_layers = info.get('quality_layers', None) + num_resolutions = info.get('num_resolutions', 0) + cblk_size = info.get('codeblock_size', None) + precinct_size = info.get('precinct_size', None) + irreversible = info.get('irreversible', False) + progression = info.get('progression', 'LRCP') + cinema_mode = info.get('cinema_mode', 'no') + fd = -1 + + if hasattr(fp, "fileno"): + try: + fd = fp.fileno() + except: + fd = -1 + + im.encoderconfig = ( + offset, + tile_offset, + tile_size, + quality_mode, + quality_layers, + num_resolutions, + cblk_size, + precinct_size, + irreversible, + progression, + cinema_mode, + fd + ) + + ImageFile._save(im, fp, [('jpeg2k', (0, 0)+im.size, 0, kind)]) + +# ------------------------------------------------------------ +# Registry stuff + +Image.register_open('JPEG2000', Jpeg2KImageFile, _accept) +Image.register_save('JPEG2000', _save) + +Image.register_extension('JPEG2000', '.jp2') +Image.register_extension('JPEG2000', '.j2k') +Image.register_extension('JPEG2000', '.jpc') +Image.register_extension('JPEG2000', '.jpf') +Image.register_extension('JPEG2000', '.jpx') +Image.register_extension('JPEG2000', '.j2c') + +Image.register_mime('JPEG2000', 'image/jp2') +Image.register_mime('JPEG2000', 'image/jpx') diff --git a/pyPackages/pillowarmv7l/PIL/JpegImagePlugin.py b/pyPackages/pillowarmv7l/PIL/JpegImagePlugin.py new file mode 100644 index 0000000..9dd79d5 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/JpegImagePlugin.py @@ -0,0 +1,738 @@ +# +# The Python Imaging Library. +# $Id$ +# +# JPEG (JFIF) file handling +# +# See "Digital Compression and Coding of Continous-Tone Still Images, +# Part 1, Requirements and Guidelines" (CCITT T.81 / ISO 10918-1) +# +# History: +# 1995-09-09 fl Created +# 1995-09-13 fl Added full parser +# 1996-03-25 fl Added hack to use the IJG command line utilities +# 1996-05-05 fl Workaround Photoshop 2.5 CMYK polarity bug +# 1996-05-28 fl Added draft support, JFIF version (0.1) +# 1996-12-30 fl Added encoder options, added progression property (0.2) +# 1997-08-27 fl Save mode 1 images as BW (0.3) +# 1998-07-12 fl Added YCbCr to draft and save methods (0.4) +# 1998-10-19 fl Don't hang on files using 16-bit DQT's (0.4.1) +# 2001-04-16 fl Extract DPI settings from JFIF files (0.4.2) +# 2002-07-01 fl Skip pad bytes before markers; identify Exif files (0.4.3) +# 2003-04-25 fl Added experimental EXIF decoder (0.5) +# 2003-06-06 fl Added experimental EXIF GPSinfo decoder +# 2003-09-13 fl Extract COM markers +# 2009-09-06 fl Added icc_profile support (from Florian Hoech) +# 2009-03-06 fl Changed CMYK handling; always use Adobe polarity (0.6) +# 2009-03-08 fl Added subsampling support (from Justin Huff). +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1995-1996 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.6" + +import array +import struct +import io +from struct import unpack +from PIL import Image, ImageFile, TiffImagePlugin, _binary +from PIL.JpegPresets import presets +from PIL._util import isStringType + +i8 = _binary.i8 +o8 = _binary.o8 +i16 = _binary.i16be +i32 = _binary.i32be + + +# +# Parser + +def Skip(self, marker): + n = i16(self.fp.read(2))-2 + ImageFile._safe_read(self.fp, n) + + +def APP(self, marker): + # + # Application marker. Store these in the APP dictionary. + # Also look for well-known application markers. + + n = i16(self.fp.read(2))-2 + s = ImageFile._safe_read(self.fp, n) + + app = "APP%d" % (marker & 15) + + self.app[app] = s # compatibility + self.applist.append((app, s)) + + if marker == 0xFFE0 and s[:4] == b"JFIF": + # extract JFIF information + self.info["jfif"] = version = i16(s, 5) # version + self.info["jfif_version"] = divmod(version, 256) + # extract JFIF properties + try: + jfif_unit = i8(s[7]) + jfif_density = i16(s, 8), i16(s, 10) + except: + pass + else: + if jfif_unit == 1: + self.info["dpi"] = jfif_density + self.info["jfif_unit"] = jfif_unit + self.info["jfif_density"] = jfif_density + elif marker == 0xFFE1 and s[:5] == b"Exif\0": + # extract Exif information (incomplete) + self.info["exif"] = s # FIXME: value will change + elif marker == 0xFFE2 and s[:5] == b"FPXR\0": + # extract FlashPix information (incomplete) + self.info["flashpix"] = s # FIXME: value will change + elif marker == 0xFFE2 and s[:12] == b"ICC_PROFILE\0": + # Since an ICC profile can be larger than the maximum size of + # a JPEG marker (64K), we need provisions to split it into + # multiple markers. The format defined by the ICC specifies + # one or more APP2 markers containing the following data: + # Identifying string ASCII "ICC_PROFILE\0" (12 bytes) + # Marker sequence number 1, 2, etc (1 byte) + # Number of markers Total of APP2's used (1 byte) + # Profile data (remainder of APP2 data) + # Decoders should use the marker sequence numbers to + # reassemble the profile, rather than assuming that the APP2 + # markers appear in the correct sequence. + self.icclist.append(s) + elif marker == 0xFFEE and s[:5] == b"Adobe": + self.info["adobe"] = i16(s, 5) + # extract Adobe custom properties + try: + adobe_transform = i8(s[1]) + except: + pass + else: + self.info["adobe_transform"] = adobe_transform + elif marker == 0xFFE2 and s[:4] == b"MPF\0": + # extract MPO information + self.info["mp"] = s[4:] + # offset is current location minus buffer size + # plus constant header size + self.info["mpoffset"] = self.fp.tell() - n + 4 + + +def COM(self, marker): + # + # Comment marker. Store these in the APP dictionary. + n = i16(self.fp.read(2))-2 + s = ImageFile._safe_read(self.fp, n) + + self.app["COM"] = s # compatibility + self.applist.append(("COM", s)) + + +def SOF(self, marker): + # + # Start of frame marker. Defines the size and mode of the + # image. JPEG is colour blind, so we use some simple + # heuristics to map the number of layers to an appropriate + # mode. Note that this could be made a bit brighter, by + # looking for JFIF and Adobe APP markers. + + n = i16(self.fp.read(2))-2 + s = ImageFile._safe_read(self.fp, n) + self.size = i16(s[3:]), i16(s[1:]) + + self.bits = i8(s[0]) + if self.bits != 8: + raise SyntaxError("cannot handle %d-bit layers" % self.bits) + + self.layers = i8(s[5]) + if self.layers == 1: + self.mode = "L" + elif self.layers == 3: + self.mode = "RGB" + elif self.layers == 4: + self.mode = "CMYK" + else: + raise SyntaxError("cannot handle %d-layer images" % self.layers) + + if marker in [0xFFC2, 0xFFC6, 0xFFCA, 0xFFCE]: + self.info["progressive"] = self.info["progression"] = 1 + + if self.icclist: + # fixup icc profile + self.icclist.sort() # sort by sequence number + if i8(self.icclist[0][13]) == len(self.icclist): + profile = [] + for p in self.icclist: + profile.append(p[14:]) + icc_profile = b"".join(profile) + else: + icc_profile = None # wrong number of fragments + self.info["icc_profile"] = icc_profile + self.icclist = None + + for i in range(6, len(s), 3): + t = s[i:i+3] + # 4-tuples: id, vsamp, hsamp, qtable + self.layer.append((t[0], i8(t[1])//16, i8(t[1]) & 15, i8(t[2]))) + + +def DQT(self, marker): + # + # Define quantization table. Support baseline 8-bit tables + # only. Note that there might be more than one table in + # each marker. + + # FIXME: The quantization tables can be used to estimate the + # compression quality. + + n = i16(self.fp.read(2))-2 + s = ImageFile._safe_read(self.fp, n) + while len(s): + if len(s) < 65: + raise SyntaxError("bad quantization table marker") + v = i8(s[0]) + if v//16 == 0: + self.quantization[v & 15] = array.array("b", s[1:65]) + s = s[65:] + else: + return # FIXME: add code to read 16-bit tables! + # raise SyntaxError, "bad quantization table element size" + + +# +# JPEG marker table + +MARKER = { + 0xFFC0: ("SOF0", "Baseline DCT", SOF), + 0xFFC1: ("SOF1", "Extended Sequential DCT", SOF), + 0xFFC2: ("SOF2", "Progressive DCT", SOF), + 0xFFC3: ("SOF3", "Spatial lossless", SOF), + 0xFFC4: ("DHT", "Define Huffman table", Skip), + 0xFFC5: ("SOF5", "Differential sequential DCT", SOF), + 0xFFC6: ("SOF6", "Differential progressive DCT", SOF), + 0xFFC7: ("SOF7", "Differential spatial", SOF), + 0xFFC8: ("JPG", "Extension", None), + 0xFFC9: ("SOF9", "Extended sequential DCT (AC)", SOF), + 0xFFCA: ("SOF10", "Progressive DCT (AC)", SOF), + 0xFFCB: ("SOF11", "Spatial lossless DCT (AC)", SOF), + 0xFFCC: ("DAC", "Define arithmetic coding conditioning", Skip), + 0xFFCD: ("SOF13", "Differential sequential DCT (AC)", SOF), + 0xFFCE: ("SOF14", "Differential progressive DCT (AC)", SOF), + 0xFFCF: ("SOF15", "Differential spatial (AC)", SOF), + 0xFFD0: ("RST0", "Restart 0", None), + 0xFFD1: ("RST1", "Restart 1", None), + 0xFFD2: ("RST2", "Restart 2", None), + 0xFFD3: ("RST3", "Restart 3", None), + 0xFFD4: ("RST4", "Restart 4", None), + 0xFFD5: ("RST5", "Restart 5", None), + 0xFFD6: ("RST6", "Restart 6", None), + 0xFFD7: ("RST7", "Restart 7", None), + 0xFFD8: ("SOI", "Start of image", None), + 0xFFD9: ("EOI", "End of image", None), + 0xFFDA: ("SOS", "Start of scan", Skip), + 0xFFDB: ("DQT", "Define quantization table", DQT), + 0xFFDC: ("DNL", "Define number of lines", Skip), + 0xFFDD: ("DRI", "Define restart interval", Skip), + 0xFFDE: ("DHP", "Define hierarchical progression", SOF), + 0xFFDF: ("EXP", "Expand reference component", Skip), + 0xFFE0: ("APP0", "Application segment 0", APP), + 0xFFE1: ("APP1", "Application segment 1", APP), + 0xFFE2: ("APP2", "Application segment 2", APP), + 0xFFE3: ("APP3", "Application segment 3", APP), + 0xFFE4: ("APP4", "Application segment 4", APP), + 0xFFE5: ("APP5", "Application segment 5", APP), + 0xFFE6: ("APP6", "Application segment 6", APP), + 0xFFE7: ("APP7", "Application segment 7", APP), + 0xFFE8: ("APP8", "Application segment 8", APP), + 0xFFE9: ("APP9", "Application segment 9", APP), + 0xFFEA: ("APP10", "Application segment 10", APP), + 0xFFEB: ("APP11", "Application segment 11", APP), + 0xFFEC: ("APP12", "Application segment 12", APP), + 0xFFED: ("APP13", "Application segment 13", APP), + 0xFFEE: ("APP14", "Application segment 14", APP), + 0xFFEF: ("APP15", "Application segment 15", APP), + 0xFFF0: ("JPG0", "Extension 0", None), + 0xFFF1: ("JPG1", "Extension 1", None), + 0xFFF2: ("JPG2", "Extension 2", None), + 0xFFF3: ("JPG3", "Extension 3", None), + 0xFFF4: ("JPG4", "Extension 4", None), + 0xFFF5: ("JPG5", "Extension 5", None), + 0xFFF6: ("JPG6", "Extension 6", None), + 0xFFF7: ("JPG7", "Extension 7", None), + 0xFFF8: ("JPG8", "Extension 8", None), + 0xFFF9: ("JPG9", "Extension 9", None), + 0xFFFA: ("JPG10", "Extension 10", None), + 0xFFFB: ("JPG11", "Extension 11", None), + 0xFFFC: ("JPG12", "Extension 12", None), + 0xFFFD: ("JPG13", "Extension 13", None), + 0xFFFE: ("COM", "Comment", COM) +} + + +def _accept(prefix): + return prefix[0:1] == b"\377" + + +## +# Image plugin for JPEG and JFIF images. + +class JpegImageFile(ImageFile.ImageFile): + + format = "JPEG" + format_description = "JPEG (ISO 10918)" + + def _open(self): + + s = self.fp.read(1) + + if i8(s[0]) != 255: + raise SyntaxError("not a JPEG file") + + # Create attributes + self.bits = self.layers = 0 + + # JPEG specifics (internal) + self.layer = [] + self.huffman_dc = {} + self.huffman_ac = {} + self.quantization = {} + self.app = {} # compatibility + self.applist = [] + self.icclist = [] + + while True: + + i = i8(s) + if i == 0xFF: + s = s + self.fp.read(1) + i = i16(s) + else: + # Skip non-0xFF junk + s = b"\xff" + continue + + if i in MARKER: + name, description, handler = MARKER[i] + # print hex(i), name, description + if handler is not None: + handler(self, i) + if i == 0xFFDA: # start of scan + rawmode = self.mode + if self.mode == "CMYK": + rawmode = "CMYK;I" # assume adobe conventions + self.tile = [("jpeg", (0, 0) + self.size, 0, + (rawmode, ""))] + # self.__offset = self.fp.tell() + break + s = self.fp.read(1) + elif i == 0 or i == 0xFFFF: + # padded marker or junk; move on + s = b"\xff" + else: + raise SyntaxError("no marker found") + + def draft(self, mode, size): + + if len(self.tile) != 1: + return + + d, e, o, a = self.tile[0] + scale = 0 + + if a[0] == "RGB" and mode in ["L", "YCbCr"]: + self.mode = mode + a = mode, "" + + if size: + scale = max(self.size[0] // size[0], self.size[1] // size[1]) + for s in [8, 4, 2, 1]: + if scale >= s: + break + e = e[0], e[1], (e[2]-e[0]+s-1)//s+e[0], (e[3]-e[1]+s-1)//s+e[1] + self.size = ((self.size[0]+s-1)//s, (self.size[1]+s-1)//s) + scale = s + + self.tile = [(d, e, o, a)] + self.decoderconfig = (scale, 0) + + return self + + def load_djpeg(self): + + # ALTERNATIVE: handle JPEGs via the IJG command line utilities + + import subprocess + import tempfile + import os + f, path = tempfile.mkstemp() + os.close(f) + if os.path.exists(self.filename): + subprocess.check_call(["djpeg", "-outfile", path, self.filename]) + else: + raise ValueError("Invalid Filename") + + try: + self.im = Image.core.open_ppm(path) + finally: + try: + os.unlink(path) + except: + pass + + self.mode = self.im.mode + self.size = self.im.size + + self.tile = [] + + def _getexif(self): + return _getexif(self) + + def _getmp(self): + return _getmp(self) + + +def _fixup(value): + # Helper function for _getexif() and _getmp() + if len(value) == 1: + return value[0] + return value + + +def _getexif(self): + # Extract EXIF information. This method is highly experimental, + # and is likely to be replaced with something better in a future + # version. + + # The EXIF record consists of a TIFF file embedded in a JPEG + # application marker (!). + try: + data = self.info["exif"] + except KeyError: + return None + file = io.BytesIO(data[6:]) + head = file.read(8) + exif = {} + # process dictionary + info = TiffImagePlugin.ImageFileDirectory(head) + info.load(file) + for key, value in info.items(): + exif[key] = _fixup(value) + # get exif extension + try: + file.seek(exif[0x8769]) + except KeyError: + pass + else: + info = TiffImagePlugin.ImageFileDirectory(head) + info.load(file) + for key, value in info.items(): + exif[key] = _fixup(value) + # get gpsinfo extension + try: + file.seek(exif[0x8825]) + except KeyError: + pass + else: + info = TiffImagePlugin.ImageFileDirectory(head) + info.load(file) + exif[0x8825] = gps = {} + for key, value in info.items(): + gps[key] = _fixup(value) + return exif + + +def _getmp(self): + # Extract MP information. This method was inspired by the "highly + # experimental" _getexif version that's been in use for years now, + # itself based on the ImageFileDirectory class in the TIFF plug-in. + + # The MP record essentially consists of a TIFF file embedded in a JPEG + # application marker. + try: + data = self.info["mp"] + except KeyError: + return None + file = io.BytesIO(data) + head = file.read(8) + endianness = '>' if head[:4] == b'\x4d\x4d\x00\x2a' else '<' + mp = {} + # process dictionary + info = TiffImagePlugin.ImageFileDirectory(head) + info.load(file) + for key, value in info.items(): + mp[key] = _fixup(value) + # it's an error not to have a number of images + try: + quant = mp[0xB001] + except KeyError: + raise SyntaxError("malformed MP Index (no number of images)") + # get MP entries + try: + mpentries = [] + for entrynum in range(0, quant): + rawmpentry = mp[0xB002][entrynum * 16:(entrynum + 1) * 16] + unpackedentry = unpack('{0}LLLHH'.format(endianness), rawmpentry) + labels = ('Attribute', 'Size', 'DataOffset', 'EntryNo1', + 'EntryNo2') + mpentry = dict(zip(labels, unpackedentry)) + mpentryattr = { + 'DependentParentImageFlag': bool(mpentry['Attribute'] & + (1 << 31)), + 'DependentChildImageFlag': bool(mpentry['Attribute'] & + (1 << 30)), + 'RepresentativeImageFlag': bool(mpentry['Attribute'] & + (1 << 29)), + 'Reserved': (mpentry['Attribute'] & (3 << 27)) >> 27, + 'ImageDataFormat': (mpentry['Attribute'] & (7 << 24)) >> 24, + 'MPType': mpentry['Attribute'] & 0x00FFFFFF + } + if mpentryattr['ImageDataFormat'] == 0: + mpentryattr['ImageDataFormat'] = 'JPEG' + else: + raise SyntaxError("unsupported picture format in MPO") + mptypemap = { + 0x000000: 'Undefined', + 0x010001: 'Large Thumbnail (VGA Equivalent)', + 0x010002: 'Large Thumbnail (Full HD Equivalent)', + 0x020001: 'Multi-Frame Image (Panorama)', + 0x020002: 'Multi-Frame Image: (Disparity)', + 0x020003: 'Multi-Frame Image: (Multi-Angle)', + 0x030000: 'Baseline MP Primary Image' + } + mpentryattr['MPType'] = mptypemap.get(mpentryattr['MPType'], + 'Unknown') + mpentry['Attribute'] = mpentryattr + mpentries.append(mpentry) + mp[0xB002] = mpentries + except KeyError: + raise SyntaxError("malformed MP Index (bad MP Entry)") + # Next we should try and parse the individual image unique ID list; + # we don't because I've never seen this actually used in a real MPO + # file and so can't test it. + return mp + + +# -------------------------------------------------------------------- +# stuff to save JPEG files + +RAWMODE = { + "1": "L", + "L": "L", + "RGB": "RGB", + "RGBA": "RGB", + "RGBX": "RGB", + "CMYK": "CMYK;I", # assume adobe conventions + "YCbCr": "YCbCr", +} + +zigzag_index = ( 0, 1, 5, 6, 14, 15, 27, 28, + 2, 4, 7, 13, 16, 26, 29, 42, + 3, 8, 12, 17, 25, 30, 41, 43, + 9, 11, 18, 24, 31, 40, 44, 53, + 10, 19, 23, 32, 39, 45, 52, 54, + 20, 22, 33, 38, 46, 51, 55, 60, + 21, 34, 37, 47, 50, 56, 59, 61, + 35, 36, 48, 49, 57, 58, 62, 63) + +samplings = {(1, 1, 1, 1, 1, 1): 0, + (2, 1, 1, 1, 1, 1): 1, + (2, 2, 1, 1, 1, 1): 2, + } + + +def convert_dict_qtables(qtables): + qtables = [qtables[key] for key in range(len(qtables)) if key in qtables] + for idx, table in enumerate(qtables): + qtables[idx] = [table[i] for i in zigzag_index] + return qtables + + +def get_sampling(im): + # There's no subsampling when image have only 1 layer + # (grayscale images) or when they are CMYK (4 layers), + # so set subsampling to default value. + # + # NOTE: currently Pillow can't encode JPEG to YCCK format. + # If YCCK support is added in the future, subsampling code will have + # to be updated (here and in JpegEncode.c) to deal with 4 layers. + if not hasattr(im, 'layers') or im.layers in (1, 4): + return -1 + sampling = im.layer[0][1:3] + im.layer[1][1:3] + im.layer[2][1:3] + return samplings.get(sampling, -1) + + +def _save(im, fp, filename): + + try: + rawmode = RAWMODE[im.mode] + except KeyError: + raise IOError("cannot write mode %s as JPEG" % im.mode) + + info = im.encoderinfo + + dpi = info.get("dpi", (0, 0)) + + quality = info.get("quality", 0) + subsampling = info.get("subsampling", -1) + qtables = info.get("qtables") + + if quality == "keep": + quality = 0 + subsampling = "keep" + qtables = "keep" + elif quality in presets: + preset = presets[quality] + quality = 0 + subsampling = preset.get('subsampling', -1) + qtables = preset.get('quantization') + elif not isinstance(quality, int): + raise ValueError("Invalid quality setting") + else: + if subsampling in presets: + subsampling = presets[subsampling].get('subsampling', -1) + if isStringType(qtables) and qtables in presets: + qtables = presets[qtables].get('quantization') + + if subsampling == "4:4:4": + subsampling = 0 + elif subsampling == "4:2:2": + subsampling = 1 + elif subsampling == "4:1:1": + subsampling = 2 + elif subsampling == "keep": + if im.format != "JPEG": + raise ValueError( + "Cannot use 'keep' when original image is not a JPEG") + subsampling = get_sampling(im) + + def validate_qtables(qtables): + if qtables is None: + return qtables + if isStringType(qtables): + try: + lines = [int(num) for line in qtables.splitlines() + for num in line.split('#', 1)[0].split()] + except ValueError: + raise ValueError("Invalid quantization table") + else: + qtables = [lines[s:s+64] for s in range(0, len(lines), 64)] + if isinstance(qtables, (tuple, list, dict)): + if isinstance(qtables, dict): + qtables = convert_dict_qtables(qtables) + elif isinstance(qtables, tuple): + qtables = list(qtables) + if not (0 < len(qtables) < 5): + raise ValueError("None or too many quantization tables") + for idx, table in enumerate(qtables): + try: + if len(table) != 64: + raise + table = array.array('b', table) + except TypeError: + raise ValueError("Invalid quantization table") + else: + qtables[idx] = list(table) + return qtables + + if qtables == "keep": + if im.format != "JPEG": + raise ValueError( + "Cannot use 'keep' when original image is not a JPEG") + qtables = getattr(im, "quantization", None) + qtables = validate_qtables(qtables) + + extra = b"" + + icc_profile = info.get("icc_profile") + if icc_profile: + ICC_OVERHEAD_LEN = 14 + MAX_BYTES_IN_MARKER = 65533 + MAX_DATA_BYTES_IN_MARKER = MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN + markers = [] + while icc_profile: + markers.append(icc_profile[:MAX_DATA_BYTES_IN_MARKER]) + icc_profile = icc_profile[MAX_DATA_BYTES_IN_MARKER:] + i = 1 + for marker in markers: + size = struct.pack(">H", 2 + ICC_OVERHEAD_LEN + len(marker)) + extra += (b"\xFF\xE2" + size + b"ICC_PROFILE\0" + o8(i) + + o8(len(markers)) + marker) + i += 1 + + # get keyword arguments + im.encoderconfig = ( + quality, + # "progressive" is the official name, but older documentation + # says "progression" + # FIXME: issue a warning if the wrong form is used (post-1.1.7) + "progressive" in info or "progression" in info, + info.get("smooth", 0), + "optimize" in info, + info.get("streamtype", 0), + dpi[0], dpi[1], + subsampling, + qtables, + extra, + info.get("exif", b"") + ) + + # if we optimize, libjpeg needs a buffer big enough to hold the whole image + # in a shot. Guessing on the size, at im.size bytes. (raw pizel size is + # channels*size, this is a value that's been used in a django patch. + # https://github.com/jdriscoll/django-imagekit/issues/50 + bufsize = 0 + if "optimize" in info or "progressive" in info or "progression" in info: + if quality >= 95: + bufsize = 2 * im.size[0] * im.size[1] + else: + bufsize = im.size[0] * im.size[1] + + # The exif info needs to be written as one block, + APP1, + one spare byte. + # Ensure that our buffer is big enough + bufsize = max(ImageFile.MAXBLOCK, bufsize, len(info.get("exif", b"")) + 5) + + ImageFile._save(im, fp, [("jpeg", (0, 0)+im.size, 0, rawmode)], bufsize) + + +def _save_cjpeg(im, fp, filename): + # ALTERNATIVE: handle JPEGs via the IJG command line utilities. + import os + import subprocess + tempfile = im._dump() + subprocess.check_call(["cjpeg", "-outfile", filename, tempfile]) + try: + os.unlink(file) + except: + pass + + +## +# Factory for making JPEG and MPO instances +def jpeg_factory(fp=None, filename=None): + im = JpegImageFile(fp, filename) + mpheader = im._getmp() + try: + if mpheader[45057] > 1: + # It's actually an MPO + from .MpoImagePlugin import MpoImageFile + im = MpoImageFile(fp, filename) + except (TypeError, IndexError): + # It is really a JPEG + pass + return im + + +# -------------------------------------------------------------------q- +# Registry stuff + +Image.register_open("JPEG", jpeg_factory, _accept) +Image.register_save("JPEG", _save) + +Image.register_extension("JPEG", ".jfif") +Image.register_extension("JPEG", ".jpe") +Image.register_extension("JPEG", ".jpg") +Image.register_extension("JPEG", ".jpeg") + +Image.register_mime("JPEG", "image/jpeg") diff --git a/pyPackages/pillowarmv7l/PIL/JpegPresets.py b/pyPackages/pillowarmv7l/PIL/JpegPresets.py new file mode 100644 index 0000000..6ca46d0 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/JpegPresets.py @@ -0,0 +1,241 @@ +""" +JPEG quality settings equivalent to the Photoshop settings. + +More presets can be added to the presets dict if needed. + +Can be use when saving JPEG file. + +To apply the preset, specify:: + + quality="preset_name" + +To apply only the quantization table:: + + qtables="preset_name" + +To apply only the subsampling setting:: + + subsampling="preset_name" + +Example:: + + im.save("image_name.jpg", quality="web_high") + + +Subsampling +----------- + +Subsampling is the practice of encoding images by implementing less resolution +for chroma information than for luma information. +(ref.: http://en.wikipedia.org/wiki/Chroma_subsampling) + +Possible subsampling values are 0, 1 and 2 that correspond to 4:4:4, 4:2:2 and +4:1:1 (or 4:2:0?). + +You can get the subsampling of a JPEG with the +`JpegImagePlugin.get_subsampling(im)` function. + + +Quantization tables +------------------- + +They are values use by the DCT (Discrete cosine transform) to remove +*unnecessary* information from the image (the lossy part of the compression). +(ref.: http://en.wikipedia.org/wiki/Quantization_matrix#Quantization_matrices, +http://en.wikipedia.org/wiki/JPEG#Quantization) + +You can get the quantization tables of a JPEG with:: + + im.quantization + +This will return a dict with a number of arrays. You can pass this dict +directly as the qtables argument when saving a JPEG. + +The tables format between im.quantization and quantization in presets differ in +3 ways: + +1. The base container of the preset is a list with sublists instead of dict. + dict[0] -> list[0], dict[1] -> list[1], ... +2. Each table in a preset is a list instead of an array. +3. The zigzag order is remove in the preset (needed by libjpeg >= 6a). + +You can convert the dict format to the preset format with the +`JpegImagePlugin.convert_dict_qtables(dict_qtables)` function. + +Libjpeg ref.: http://www.jpegcameras.com/libjpeg/libjpeg-3.html + +""" + +presets = { + 'web_low': {'subsampling': 2, # "4:1:1" + 'quantization': [ + [20, 16, 25, 39, 50, 46, 62, 68, + 16, 18, 23, 38, 38, 53, 65, 68, + 25, 23, 31, 38, 53, 65, 68, 68, + 39, 38, 38, 53, 65, 68, 68, 68, + 50, 38, 53, 65, 68, 68, 68, 68, + 46, 53, 65, 68, 68, 68, 68, 68, + 62, 65, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68], + [21, 25, 32, 38, 54, 68, 68, 68, + 25, 28, 24, 38, 54, 68, 68, 68, + 32, 24, 32, 43, 66, 68, 68, 68, + 38, 38, 43, 53, 68, 68, 68, 68, + 54, 54, 66, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68] + ]}, + 'web_medium': {'subsampling': 2, # "4:1:1" + 'quantization': [ + [16, 11, 11, 16, 23, 27, 31, 30, + 11, 12, 12, 15, 20, 23, 23, 30, + 11, 12, 13, 16, 23, 26, 35, 47, + 16, 15, 16, 23, 26, 37, 47, 64, + 23, 20, 23, 26, 39, 51, 64, 64, + 27, 23, 26, 37, 51, 64, 64, 64, + 31, 23, 35, 47, 64, 64, 64, 64, + 30, 30, 47, 64, 64, 64, 64, 64], + [17, 15, 17, 21, 20, 26, 38, 48, + 15, 19, 18, 17, 20, 26, 35, 43, + 17, 18, 20, 22, 26, 30, 46, 53, + 21, 17, 22, 28, 30, 39, 53, 64, + 20, 20, 26, 30, 39, 48, 64, 64, + 26, 26, 30, 39, 48, 63, 64, 64, + 38, 35, 46, 53, 64, 64, 64, 64, + 48, 43, 53, 64, 64, 64, 64, 64] + ]}, + 'web_high': {'subsampling': 0, # "4:4:4" + 'quantization': [ + [ 6, 4, 4, 6, 9, 11, 12, 16, + 4, 5, 5, 6, 8, 10, 12, 12, + 4, 5, 5, 6, 10, 12, 14, 19, + 6, 6, 6, 11, 12, 15, 19, 28, + 9, 8, 10, 12, 16, 20, 27, 31, + 11, 10, 12, 15, 20, 27, 31, 31, + 12, 12, 14, 19, 27, 31, 31, 31, + 16, 12, 19, 28, 31, 31, 31, 31], + [ 7, 7, 13, 24, 26, 31, 31, 31, + 7, 12, 16, 21, 31, 31, 31, 31, + 13, 16, 17, 31, 31, 31, 31, 31, + 24, 21, 31, 31, 31, 31, 31, 31, + 26, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31] + ]}, + 'web_very_high': {'subsampling': 0, # "4:4:4" + 'quantization': [ + [ 2, 2, 2, 2, 3, 4, 5, 6, + 2, 2, 2, 2, 3, 4, 5, 6, + 2, 2, 2, 2, 4, 5, 7, 9, + 2, 2, 2, 4, 5, 7, 9, 12, + 3, 3, 4, 5, 8, 10, 12, 12, + 4, 4, 5, 7, 10, 12, 12, 12, + 5, 5, 7, 9, 12, 12, 12, 12, + 6, 6, 9, 12, 12, 12, 12, 12], + [ 3, 3, 5, 9, 13, 15, 15, 15, + 3, 4, 6, 11, 14, 12, 12, 12, + 5, 6, 9, 14, 12, 12, 12, 12, + 9, 11, 14, 12, 12, 12, 12, 12, + 13, 14, 12, 12, 12, 12, 12, 12, + 15, 12, 12, 12, 12, 12, 12, 12, + 15, 12, 12, 12, 12, 12, 12, 12, + 15, 12, 12, 12, 12, 12, 12, 12] + ]}, + 'web_maximum': {'subsampling': 0, # "4:4:4" + 'quantization': [ + [ 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2, + 1, 1, 1, 1, 1, 1, 2, 2, + 1, 1, 1, 1, 1, 2, 2, 3, + 1, 1, 1, 1, 2, 2, 3, 3, + 1, 1, 1, 2, 2, 3, 3, 3, + 1, 1, 2, 2, 3, 3, 3, 3], + [ 1, 1, 1, 2, 2, 3, 3, 3, + 1, 1, 1, 2, 3, 3, 3, 3, + 1, 1, 1, 3, 3, 3, 3, 3, + 2, 2, 3, 3, 3, 3, 3, 3, + 2, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3] + ]}, + 'low': {'subsampling': 2, # "4:1:1" + 'quantization': [ + [18, 14, 14, 21, 30, 35, 34, 17, + 14, 16, 16, 19, 26, 23, 12, 12, + 14, 16, 17, 21, 23, 12, 12, 12, + 21, 19, 21, 23, 12, 12, 12, 12, + 30, 26, 23, 12, 12, 12, 12, 12, + 35, 23, 12, 12, 12, 12, 12, 12, + 34, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12], + [20, 19, 22, 27, 20, 20, 17, 17, + 19, 25, 23, 14, 14, 12, 12, 12, + 22, 23, 14, 14, 12, 12, 12, 12, + 27, 14, 14, 12, 12, 12, 12, 12, + 20, 14, 12, 12, 12, 12, 12, 12, + 20, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12] + ]}, + 'medium': {'subsampling': 2, # "4:1:1" + 'quantization': [ + [12, 8, 8, 12, 17, 21, 24, 17, + 8, 9, 9, 11, 15, 19, 12, 12, + 8, 9, 10, 12, 19, 12, 12, 12, + 12, 11, 12, 21, 12, 12, 12, 12, + 17, 15, 19, 12, 12, 12, 12, 12, + 21, 19, 12, 12, 12, 12, 12, 12, + 24, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12], + [13, 11, 13, 16, 20, 20, 17, 17, + 11, 14, 14, 14, 14, 12, 12, 12, + 13, 14, 14, 14, 12, 12, 12, 12, + 16, 14, 14, 12, 12, 12, 12, 12, + 20, 14, 12, 12, 12, 12, 12, 12, + 20, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12] + ]}, + 'high': {'subsampling': 0, # "4:4:4" + 'quantization': [ + [ 6, 4, 4, 6, 9, 11, 12, 16, + 4, 5, 5, 6, 8, 10, 12, 12, + 4, 5, 5, 6, 10, 12, 12, 12, + 6, 6, 6, 11, 12, 12, 12, 12, + 9, 8, 10, 12, 12, 12, 12, 12, + 11, 10, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, + 16, 12, 12, 12, 12, 12, 12, 12], + [ 7, 7, 13, 24, 20, 20, 17, 17, + 7, 12, 16, 14, 14, 12, 12, 12, + 13, 16, 14, 14, 12, 12, 12, 12, + 24, 14, 14, 12, 12, 12, 12, 12, + 20, 14, 12, 12, 12, 12, 12, 12, + 20, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12] + ]}, + 'maximum': {'subsampling': 0, # "4:4:4" + 'quantization': [ + [ 2, 2, 2, 2, 3, 4, 5, 6, + 2, 2, 2, 2, 3, 4, 5, 6, + 2, 2, 2, 2, 4, 5, 7, 9, + 2, 2, 2, 4, 5, 7, 9, 12, + 3, 3, 4, 5, 8, 10, 12, 12, + 4, 4, 5, 7, 10, 12, 12, 12, + 5, 5, 7, 9, 12, 12, 12, 12, + 6, 6, 9, 12, 12, 12, 12, 12], + [ 3, 3, 5, 9, 13, 15, 15, 15, + 3, 4, 6, 10, 14, 12, 12, 12, + 5, 6, 9, 14, 12, 12, 12, 12, + 9, 10, 14, 12, 12, 12, 12, 12, + 13, 14, 12, 12, 12, 12, 12, 12, + 15, 12, 12, 12, 12, 12, 12, 12, + 15, 12, 12, 12, 12, 12, 12, 12, + 15, 12, 12, 12, 12, 12, 12, 12] + ]}, +} diff --git a/pyPackages/pillowarmv7l/PIL/McIdasImagePlugin.py b/pyPackages/pillowarmv7l/PIL/McIdasImagePlugin.py new file mode 100644 index 0000000..c3f255f --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/McIdasImagePlugin.py @@ -0,0 +1,74 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Basic McIdas support for PIL +# +# History: +# 1997-05-05 fl Created (8-bit images only) +# 2009-03-08 fl Added 16/32-bit support. +# +# Thanks to Richard Jones and Craig Swank for specs and samples. +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.2" + +import struct +from PIL import Image, ImageFile + + +def _accept(s): + return s[:8] == b"\x00\x00\x00\x00\x00\x00\x00\x04" + + +## +# Image plugin for McIdas area images. + +class McIdasImageFile(ImageFile.ImageFile): + + format = "MCIDAS" + format_description = "McIdas area file" + + def _open(self): + + # parse area file directory + s = self.fp.read(256) + if not _accept(s) or len(s) != 256: + raise SyntaxError("not an McIdas area file") + + self.area_descriptor_raw = s + self.area_descriptor = w = [0] + list(struct.unpack("!64i", s)) + + # get mode + if w[11] == 1: + mode = rawmode = "L" + elif w[11] == 2: + # FIXME: add memory map support + mode = "I" + rawmode = "I;16B" + elif w[11] == 4: + # FIXME: add memory map support + mode = "I" + rawmode = "I;32B" + else: + raise SyntaxError("unsupported McIdas format") + + self.mode = mode + self.size = w[10], w[9] + + offset = w[34] + w[15] + stride = w[15] + w[10]*w[11]*w[14] + + self.tile = [("raw", (0, 0) + self.size, offset, (rawmode, stride, 1))] + +# -------------------------------------------------------------------- +# registry + +Image.register_open("MCIDAS", McIdasImageFile, _accept) + +# no default extension diff --git a/pyPackages/pillowarmv7l/PIL/MicImagePlugin.py b/pyPackages/pillowarmv7l/PIL/MicImagePlugin.py new file mode 100644 index 0000000..cdfaf3e --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/MicImagePlugin.py @@ -0,0 +1,96 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Microsoft Image Composer support for PIL +# +# Notes: +# uses TiffImagePlugin.py to read the actual image streams +# +# History: +# 97-01-20 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.1" + + +from PIL import Image, TiffImagePlugin +from PIL.OleFileIO import * + + +# +# -------------------------------------------------------------------- + + +def _accept(prefix): + return prefix[:8] == MAGIC + + +## +# Image plugin for Microsoft's Image Composer file format. + +class MicImageFile(TiffImagePlugin.TiffImageFile): + + format = "MIC" + format_description = "Microsoft Image Composer" + + def _open(self): + + # read the OLE directory and see if this is a likely + # to be a Microsoft Image Composer file + + try: + self.ole = OleFileIO(self.fp) + except IOError: + raise SyntaxError("not an MIC file; invalid OLE file") + + # find ACI subfiles with Image members (maybe not the + # best way to identify MIC files, but what the... ;-) + + self.images = [] + for file in self.ole.listdir(): + if file[1:] and file[0][-4:] == ".ACI" and file[1] == "Image": + self.images.append(file) + + # if we didn't find any images, this is probably not + # an MIC file. + if not self.images: + raise SyntaxError("not an MIC file; no image entries") + + self.__fp = self.fp + self.frame = 0 + + if len(self.images) > 1: + self.category = Image.CONTAINER + + self.seek(0) + + def seek(self, frame): + + try: + filename = self.images[frame] + except IndexError: + raise EOFError("no such frame") + + self.fp = self.ole.openstream(filename) + + TiffImagePlugin.TiffImageFile._open(self) + + self.frame = frame + + def tell(self): + + return self.frame + +# +# -------------------------------------------------------------------- + +Image.register_open("MIC", MicImageFile, _accept) + +Image.register_extension("MIC", ".mic") diff --git a/pyPackages/pillowarmv7l/PIL/MpegImagePlugin.py b/pyPackages/pillowarmv7l/PIL/MpegImagePlugin.py new file mode 100644 index 0000000..9aca58f --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/MpegImagePlugin.py @@ -0,0 +1,85 @@ +# +# The Python Imaging Library. +# $Id$ +# +# MPEG file handling +# +# History: +# 95-09-09 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1995. +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.1" + +from PIL import Image, ImageFile +from PIL._binary import i8 + + +# +# Bitstream parser + +class BitStream: + + def __init__(self, fp): + self.fp = fp + self.bits = 0 + self.bitbuffer = 0 + + def next(self): + return i8(self.fp.read(1)) + + def peek(self, bits): + while self.bits < bits: + c = self.next() + if c < 0: + self.bits = 0 + continue + self.bitbuffer = (self.bitbuffer << 8) + c + self.bits += 8 + return self.bitbuffer >> (self.bits - bits) & (1 << bits) - 1 + + def skip(self, bits): + while self.bits < bits: + self.bitbuffer = (self.bitbuffer << 8) + i8(self.fp.read(1)) + self.bits += 8 + self.bits = self.bits - bits + + def read(self, bits): + v = self.peek(bits) + self.bits = self.bits - bits + return v + + +## +# Image plugin for MPEG streams. This plugin can identify a stream, +# but it cannot read it. + +class MpegImageFile(ImageFile.ImageFile): + + format = "MPEG" + format_description = "MPEG" + + def _open(self): + + s = BitStream(self.fp) + + if s.read(32) != 0x1B3: + raise SyntaxError("not an MPEG file") + + self.mode = "RGB" + self.size = s.read(12), s.read(12) + + +# -------------------------------------------------------------------- +# Registry stuff + +Image.register_open("MPEG", MpegImageFile) + +Image.register_extension("MPEG", ".mpg") +Image.register_extension("MPEG", ".mpeg") + +Image.register_mime("MPEG", "video/mpeg") diff --git a/pyPackages/pillowarmv7l/PIL/MpoImagePlugin.py b/pyPackages/pillowarmv7l/PIL/MpoImagePlugin.py new file mode 100644 index 0000000..c345fd3 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/MpoImagePlugin.py @@ -0,0 +1,90 @@ +# +# The Python Imaging Library. +# $Id$ +# +# MPO file handling +# +# See "Multi-Picture Format" (CIPA DC-007-Translation 2009, Standard of the +# Camera & Imaging Products Association) +# +# The multi-picture object combines multiple JPEG images (with a modified EXIF +# data format) into a single file. While it can theoretically be used much like +# a GIF animation, it is commonly used to represent 3D photographs and is (as +# of this writing) the most commonly used format by 3D cameras. +# +# History: +# 2014-03-13 Feneric Created +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.1" + +from PIL import Image, JpegImagePlugin + + +def _accept(prefix): + return JpegImagePlugin._accept(prefix) + + +def _save(im, fp, filename): + # Note that we can only save the current frame at present + return JpegImagePlugin._save(im, fp, filename) + + +## +# Image plugin for MPO images. + +class MpoImageFile(JpegImagePlugin.JpegImageFile): + + format = "MPO" + format_description = "MPO (CIPA DC-007)" + + def _open(self): + self.fp.seek(0) # prep the fp in order to pass the JPEG test + JpegImagePlugin.JpegImageFile._open(self) + self.mpinfo = self._getmp() + self.__framecount = self.mpinfo[0xB001] + self.__mpoffsets = [mpent['DataOffset'] + self.info['mpoffset'] + for mpent in self.mpinfo[0xB002]] + self.__mpoffsets[0] = 0 + # Note that the following assertion will only be invalid if something + # gets broken within JpegImagePlugin. + assert self.__framecount == len(self.__mpoffsets) + del self.info['mpoffset'] # no longer needed + self.__fp = self.fp # FIXME: hack + self.__fp.seek(self.__mpoffsets[0]) # get ready to read first frame + self.__frame = 0 + self.offset = 0 + # for now we can only handle reading and individual frame extraction + self.readonly = 1 + + def load_seek(self, pos): + self.__fp.seek(pos) + + def seek(self, frame): + if frame < 0 or frame >= self.__framecount: + raise EOFError("no more images in MPO file") + else: + self.fp = self.__fp + self.offset = self.__mpoffsets[frame] + self.tile = [ + ("jpeg", (0, 0) + self.size, self.offset, (self.mode, "")) + ] + self.__frame = frame + + def tell(self): + return self.__frame + + +# -------------------------------------------------------------------q- +# Registry stuff + +# Note that since MPO shares a factory with JPEG, we do not need to do a +# separate registration for it here. +# Image.register_open("MPO", JpegImagePlugin.jpeg_factory, _accept) +Image.register_save("MPO", _save) + +Image.register_extension("MPO", ".mpo") + +Image.register_mime("MPO", "image/mpo") diff --git a/pyPackages/pillowarmv7l/PIL/MspImagePlugin.py b/pyPackages/pillowarmv7l/PIL/MspImagePlugin.py new file mode 100644 index 0000000..4753be7 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/MspImagePlugin.py @@ -0,0 +1,104 @@ +# +# The Python Imaging Library. +# $Id$ +# +# MSP file handling +# +# This is the format used by the Paint program in Windows 1 and 2. +# +# History: +# 95-09-05 fl Created +# 97-01-03 fl Read/write MSP images +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1995-97. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.1" + +from PIL import Image, ImageFile, _binary + + +# +# read MSP files + +i16 = _binary.i16le + + +def _accept(prefix): + return prefix[:4] in [b"DanM", b"LinS"] + + +## +# Image plugin for Windows MSP images. This plugin supports both +# uncompressed (Windows 1.0). + +class MspImageFile(ImageFile.ImageFile): + + format = "MSP" + format_description = "Windows Paint" + + def _open(self): + + # Header + s = self.fp.read(32) + if s[:4] not in [b"DanM", b"LinS"]: + raise SyntaxError("not an MSP file") + + # Header checksum + sum = 0 + for i in range(0, 32, 2): + sum = sum ^ i16(s[i:i+2]) + if sum != 0: + raise SyntaxError("bad MSP checksum") + + self.mode = "1" + self.size = i16(s[4:]), i16(s[6:]) + + if s[:4] == b"DanM": + self.tile = [("raw", (0, 0)+self.size, 32, ("1", 0, 1))] + else: + self.tile = [("msp", (0, 0)+self.size, 32+2*self.size[1], None)] + +# +# write MSP files (uncompressed only) + +o16 = _binary.o16le + + +def _save(im, fp, filename): + + if im.mode != "1": + raise IOError("cannot write mode %s as MSP" % im.mode) + + # create MSP header + header = [0] * 16 + + header[0], header[1] = i16(b"Da"), i16(b"nM") # version 1 + header[2], header[3] = im.size + header[4], header[5] = 1, 1 + header[6], header[7] = 1, 1 + header[8], header[9] = im.size + + sum = 0 + for h in header: + sum = sum ^ h + header[12] = sum # FIXME: is this the right field? + + # header + for h in header: + fp.write(o16(h)) + + # image body + ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 32, ("1", 0, 1))]) + +# +# registry + +Image.register_open("MSP", MspImageFile, _accept) +Image.register_save("MSP", _save) + +Image.register_extension("MSP", ".msp") diff --git a/pyPackages/pillowarmv7l/PIL/OleFileIO-README.md b/pyPackages/pillowarmv7l/PIL/OleFileIO-README.md new file mode 100644 index 0000000..11a0e90 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/OleFileIO-README.md @@ -0,0 +1,351 @@ +OleFileIO_PL +============ + +[OleFileIO_PL](http://www.decalage.info/python/olefileio) is a Python module to parse and read [Microsoft OLE2 files (also called Structured Storage, Compound File Binary Format or Compound Document File Format)](http://en.wikipedia.org/wiki/Compound_File_Binary_Format), such as Microsoft Office documents, Image Composer and FlashPix files, Outlook messages, StickyNotes, several Microscopy file formats ... + +This is an improved version of the OleFileIO module from [PIL](http://www.pythonware.com/products/pil/index.htm), the excellent Python Imaging Library, created and maintained by Fredrik Lundh. The API is still compatible with PIL, but since 2005 I have improved the internal implementation significantly, with new features, bugfixes and a more robust design. + +As far as I know, this module is now the most complete and robust Python implementation to read MS OLE2 files, portable on several operating systems. (please tell me if you know other similar Python modules) + +OleFileIO_PL can be used as an independent module or with PIL. The goal is to have it integrated into [Pillow](http://python-pillow.github.io/), the friendly fork of PIL. + +OleFileIO\_PL is mostly meant for developers. If you are looking for tools to analyze OLE files or to extract data, then please also check [python-oletools](http://www.decalage.info/python/oletools), which are built upon OleFileIO_PL. + +News +---- + +Follow all updates and news on Twitter: + +- **2014-02-04 v0.30**: now compatible with Python 3.x, thanks to Martin Panter who did most of the hard work. +- 2013-07-24 v0.26: added methods to parse stream/storage timestamps, improved listdir to include storages, fixed parsing of direntry timestamps +- 2013-05-27 v0.25: improved metadata extraction, properties parsing and exception handling, fixed [issue #12](https://bitbucket.org/decalage/olefileio_pl/issue/12/error-when-converting-timestamps-in-ole) +- 2013-05-07 v0.24: new features to extract metadata (get\_metadata method and OleMetadata class), improved getproperties to convert timestamps to Python datetime +- 2012-10-09: published [python-oletools](http://www.decalage.info/python/oletools), a package of analysis tools based on OleFileIO_PL +- 2012-09-11 v0.23: added support for file-like objects, fixed [issue #8](https://bitbucket.org/decalage/olefileio_pl/issue/8/bug-with-file-object) +- 2012-02-17 v0.22: fixed issues #7 (bug in getproperties) and #2 (added close method) +- 2011-10-20: code hosted on bitbucket to ease contributions and bug tracking +- 2010-01-24 v0.21: fixed support for big-endian CPUs, such as PowerPC Macs. +- 2009-12-11 v0.20: small bugfix in OleFileIO.open when filename is not plain str. +- 2009-12-10 v0.19: fixed support for 64 bits platforms (thanks to Ben G. and Martijn for reporting the bug) +- see changelog in source code for more info. + +Download +-------- + +The archive is available on [the project page](https://bitbucket.org/decalage/olefileio_pl/downloads). + +Features +-------- + +- Parse and read any OLE file such as Microsoft Office 97-2003 legacy document formats (Word .doc, Excel .xls, PowerPoint .ppt, Visio .vsd, Project .mpp), Image Composer and FlashPix files, Outlook messages, StickyNotes, Zeiss AxioVision ZVI files, Olympus FluoView OIB files, ... +- List all the streams and storages contained in an OLE file +- Open streams as files +- Parse and read property streams, containing metadata of the file +- Portable, pure Python module, no dependency + + +Main improvements over the original version of OleFileIO in PIL: +---------------------------------------------------------------- + +- Compatible with Python 3.x and 2.6+ +- Many bug fixes +- Support for files larger than 6.8MB +- Support for 64 bits platforms and big-endian CPUs +- Robust: many checks to detect malformed files +- Runtime option to choose if malformed files should be parsed or raise exceptions +- Improved API +- Metadata extraction, stream/storage timestamps (e.g. for document forensics) +- Can open file-like objects +- Added setup.py and install.bat to ease installation +- More convenient slash-based syntax for stream paths + + + +How to use this module +---------------------- + +OleFileIO_PL can be used as an independent module or with PIL. The main functions and methods are explained below. + +For more information, see also the file **OleFileIO_PL.html**, sample code at the end of the module itself, and docstrings within the code. + +### About the structure of OLE files ### + +An OLE file can be seen as a mini file system or a Zip archive: It contains **streams** of data that look like files embedded within the OLE file. Each stream has a name. For example, the main stream of a MS Word document containing its text is named "WordDocument". + +An OLE file can also contain **storages**. A storage is a folder that contains streams or other storages. For example, a MS Word document with VBA macros has a storage called "Macros". + +Special streams can contain **properties**. A property is a specific value that can be used to store information such as the metadata of a document (title, author, creation date, etc). Property stream names usually start with the character '\x05'. + +For example, a typical MS Word document may look like this: + + \x05DocumentSummaryInformation (stream) + \x05SummaryInformation (stream) + WordDocument (stream) + Macros (storage) + PROJECT (stream) + PROJECTwm (stream) + VBA (storage) + Module1 (stream) + ThisDocument (stream) + _VBA_PROJECT (stream) + dir (stream) + ObjectPool (storage) + + + +### Import OleFileIO_PL ### + + :::python + import OleFileIO_PL + +As of version 0.30, the code has been changed to be compatible with Python 3.x. As a consequence, compatibility with Python 2.5 or older is not provided anymore. However, a copy of v0.26 is available as OleFileIO_PL2.py. If your application needs to be compatible with Python 2.5 or older, you may use the following code to load the old version when needed: + + :::python + try: + import OleFileIO_PL + except: + import OleFileIO_PL2 as OleFileIO_PL + +If you think OleFileIO_PL should stay compatible with Python 2.5 or older, please [contact me](http://decalage.info/contact). + + +### Test if a file is an OLE container ### + +Use isOleFile to check if the first bytes of the file contain the Magic for OLE files, before opening it. isOleFile returns True if it is an OLE file, False otherwise (new in v0.16). + + :::python + assert OleFileIO_PL.isOleFile('myfile.doc') + + +### Open an OLE file from disk ### + +Create an OleFileIO object with the file path as parameter: + + :::python + ole = OleFileIO_PL.OleFileIO('myfile.doc') + +### Open an OLE file from a file-like object ### + +This is useful if the file is not on disk, e.g. already stored in a string or as a file-like object. + + :::python + ole = OleFileIO_PL.OleFileIO(f) + +For example the code below reads a file into a string, then uses BytesIO to turn it into a file-like object. + + :::python + data = open('myfile.doc', 'rb').read() + f = io.BytesIO(data) # or StringIO.StringIO for Python 2.x + ole = OleFileIO_PL.OleFileIO(f) + +### How to handle malformed OLE files ### + +By default, the parser is configured to be as robust and permissive as possible, allowing to parse most malformed OLE files. Only fatal errors will raise an exception. It is possible to tell the parser to be more strict in order to raise exceptions for files that do not fully conform to the OLE specifications, using the raise_defect option (new in v0.14): + + :::python + ole = OleFileIO_PL.OleFileIO('myfile.doc', raise_defects=DEFECT_INCORRECT) + +When the parsing is done, the list of non-fatal issues detected is available as a list in the parsing_issues attribute of the OleFileIO object (new in 0.25): + + :::python + print('Non-fatal issues raised during parsing:') + if ole.parsing_issues: + for exctype, msg in ole.parsing_issues: + print('- %s: %s' % (exctype.__name__, msg)) + else: + print('None') + + +### Syntax for stream and storage path ### + +Two different syntaxes are allowed for methods that need or return the path of streams and storages: + +1) Either a **list of strings** including all the storages from the root up to the stream/storage name. For example a stream called "WordDocument" at the root will have ['WordDocument'] as full path. A stream called "ThisDocument" located in the storage "Macros/VBA" will be ['Macros', 'VBA', 'ThisDocument']. This is the original syntax from PIL. While hard to read and not very convenient, this syntax works in all cases. + +2) Or a **single string with slashes** to separate storage and stream names (similar to the Unix path syntax). The previous examples would be 'WordDocument' and 'Macros/VBA/ThisDocument'. This syntax is easier, but may fail if a stream or storage name contains a slash. (new in v0.15) + +Both are case-insensitive. + +Switching between the two is easy: + + :::python + slash_path = '/'.join(list_path) + list_path = slash_path.split('/') + + +### Get the list of streams ### + +listdir() returns a list of all the streams contained in the OLE file, including those stored in storages. Each stream is listed itself as a list, as described above. + + :::python + print(ole.listdir()) + +Sample result: + + :::python + [['\x01CompObj'], ['\x05DocumentSummaryInformation'], ['\x05SummaryInformation'] + , ['1Table'], ['Macros', 'PROJECT'], ['Macros', 'PROJECTwm'], ['Macros', 'VBA', + 'Module1'], ['Macros', 'VBA', 'ThisDocument'], ['Macros', 'VBA', '_VBA_PROJECT'] + , ['Macros', 'VBA', 'dir'], ['ObjectPool'], ['WordDocument']] + +As an option it is possible to choose if storages should also be listed, with or without streams (new in v0.26): + + :::python + ole.listdir (streams=False, storages=True) + + +### Test if known streams/storages exist: ### + +exists(path) checks if a given stream or storage exists in the OLE file (new in v0.16). + + :::python + if ole.exists('worddocument'): + print("This is a Word document.") + if ole.exists('macros/vba'): + print("This document seems to contain VBA macros.") + + +### Read data from a stream ### + +openstream(path) opens a stream as a file-like object. + +The following example extracts the "Pictures" stream from a PPT file: + + :::python + pics = ole.openstream('Pictures') + data = pics.read() + + +### Get information about a stream/storage ### + +Several methods can provide the size, type and timestamps of a given stream/storage: + +get_size(path) returns the size of a stream in bytes (new in v0.16): + + :::python + s = ole.get_size('WordDocument') + +get_type(path) returns the type of a stream/storage, as one of the following constants: STGTY\_STREAM for a stream, STGTY\_STORAGE for a storage, STGTY\_ROOT for the root entry, and False for a non existing path (new in v0.15). + + :::python + t = ole.get_type('WordDocument') + +get\_ctime(path) and get\_mtime(path) return the creation and modification timestamps of a stream/storage, as a Python datetime object with UTC timezone. Please note that these timestamps are only present if the application that created the OLE file explicitly stored them, which is rarely the case. When not present, these methods return None (new in v0.26). + + :::python + c = ole.get_ctime('WordDocument') + m = ole.get_mtime('WordDocument') + +The root storage is a special case: You can get its creation and modification timestamps using the OleFileIO.root attribute (new in v0.26): + + :::python + c = ole.root.getctime() + m = ole.root.getmtime() + +### Extract metadata ### + +get_metadata() will check if standard property streams exist, parse all the properties they contain, and return an OleMetadata object with the found properties as attributes (new in v0.24). + + :::python + meta = ole.get_metadata() + print('Author:', meta.author) + print('Title:', meta.title) + print('Creation date:', meta.create_time) + # print all metadata: + meta.dump() + +Available attributes include: + + codepage, title, subject, author, keywords, comments, template, + last_saved_by, revision_number, total_edit_time, last_printed, create_time, + last_saved_time, num_pages, num_words, num_chars, thumbnail, + creating_application, security, codepage_doc, category, presentation_target, + bytes, lines, paragraphs, slides, notes, hidden_slides, mm_clips, + scale_crop, heading_pairs, titles_of_parts, manager, company, links_dirty, + chars_with_spaces, unused, shared_doc, link_base, hlinks, hlinks_changed, + version, dig_sig, content_type, content_status, language, doc_version + +See the source code of the OleMetadata class for more information. + + +### Parse a property stream ### + +get\_properties(path) can be used to parse any property stream that is not handled by get\_metadata. It returns a dictionary indexed by integers. Each integer is the index of the property, pointing to its value. For example in the standard property stream '\x05SummaryInformation', the document title is property #2, and the subject is #3. + + :::python + p = ole.getproperties('specialprops') + +By default as in the original PIL version, timestamp properties are converted into a number of seconds since Jan 1,1601. With the option convert\_time, you can obtain more convenient Python datetime objects (UTC timezone). If some time properties should not be converted (such as total editing time in '\x05SummaryInformation'), the list of indexes can be passed as no_conversion (new in v0.25): + + :::python + p = ole.getproperties('specialprops', convert_time=True, no_conversion=[10]) + + +### Close the OLE file ### + +Unless your application is a simple script that terminates after processing an OLE file, do not forget to close each OleFileIO object after parsing to close the file on disk. (new in v0.22) + + :::python + ole.close() + +### Use OleFileIO_PL as a script ### + +OleFileIO_PL can also be used as a script from the command-line to display the structure of an OLE file and its metadata, for example: + + OleFileIO_PL.py myfile.doc + +You can use the option -c to check that all streams can be read fully, and -d to generate very verbose debugging information. + +## Real-life examples ## + +A real-life example: [using OleFileIO_PL for malware analysis and forensics](http://blog.gregback.net/2011/03/using-remnux-for-forensic-puzzle-6/). + +See also [this paper](https://computer-forensics.sans.org/community/papers/gcfa/grow-forensic-tools-taxonomy-python-libraries-helpful-forensic-analysis_6879) about python tools for forensics, which features OleFileIO_PL. + +About Python 2 and 3 +-------------------- + +OleFileIO\_PL used to support only Python 2.x. As of version 0.30, the code has been changed to be compatible with Python 3.x. As a consequence, compatibility with Python 2.5 or older is not provided anymore. However, a copy of v0.26 is available as OleFileIO_PL2.py. See above the "import" section for a workaround. + +If you think OleFileIO_PL should stay compatible with Python 2.5 or older, please [contact me](http://decalage.info/contact). + +How to contribute +----------------- + +The code is available in [a Mercurial repository on bitbucket](https://bitbucket.org/decalage/olefileio_pl). You may use it to submit enhancements or to report any issue. + +If you would like to help us improve this module, or simply provide feedback, please [contact me](http://decalage.info/contact). You can help in many ways: + +- test this module on different platforms / Python versions +- find and report bugs +- improve documentation, code samples, docstrings +- write unittest test cases +- provide tricky malformed files + +How to report bugs +------------------ + +To report a bug, for example a normal file which is not parsed correctly, please use the [issue reporting page](https://bitbucket.org/decalage/olefileio_pl/issues?status=new&status=open), or if you prefer to do it privately, use this [contact form](http://decalage.info/contact). Please provide all the information about the context and how to reproduce the bug. + +If possible please join the debugging output of OleFileIO_PL. For this, launch the following command : + + OleFileIO_PL.py -d -c file >debug.txt + +License +------- + +OleFileIO_PL is open-source. + +OleFileIO_PL changes are Copyright (c) 2005-2014 by Philippe Lagadec. + +The Python Imaging Library (PIL) is + +- Copyright (c) 1997-2005 by Secret Labs AB + +- Copyright (c) 1995-2005 by Fredrik Lundh + +By obtaining, using, and/or copying this software and/or its associated documentation, you agree that you have read, understood, and will comply with the following terms and conditions: + +Permission to use, copy, modify, and distribute this software and its associated documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appears in all copies, and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Secret Labs AB or the author not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. + +SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/pyPackages/pillowarmv7l/PIL/OleFileIO.py b/pyPackages/pillowarmv7l/PIL/OleFileIO.py new file mode 100644 index 0000000..c1cc5d5 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/OleFileIO.py @@ -0,0 +1,2089 @@ +#!/usr/bin/env python +## OleFileIO_PL: +## Module to read Microsoft OLE2 files (also called Structured Storage or +## Microsoft Compound Document File Format), such as Microsoft Office +## documents, Image Composer and FlashPix files, Outlook messages, ... +## This version is compatible with Python 2.6+ and 3.x + +## version 0.30 2014-02-04 Philippe Lagadec - http://www.decalage.info + +## Project website: http://www.decalage.info/python/olefileio + +## Improved version of the OleFileIO module from PIL library v1.1.6 +## See: http://www.pythonware.com/products/pil/index.htm + +## The Python Imaging Library (PIL) is + +## Copyright (c) 1997-2005 by Secret Labs AB +## Copyright (c) 1995-2005 by Fredrik Lundh + +## OleFileIO_PL changes are Copyright (c) 2005-2014 by Philippe Lagadec + +## See source code and LICENSE.txt for information on usage and redistribution. + +## WARNING: THIS IS (STILL) WORK IN PROGRESS. + + +# Starting with OleFileIO_PL v0.30, only Python 2.6+ and 3.x is supported +# This import enables print() as a function rather than a keyword +# (main requirement to be compatible with Python 3.x) +# The comment on the line below should be printed on Python 2.5 or older: +from __future__ import print_function # This version of OleFileIO_PL requires Python 2.6+ or 3.x. + + +__author__ = "Philippe Lagadec, Fredrik Lundh (Secret Labs AB)" +__date__ = "2014-02-04" +__version__ = '0.30' + +#--- LICENSE ------------------------------------------------------------------ + +# OleFileIO_PL is an improved version of the OleFileIO module from the +# Python Imaging Library (PIL). + +# OleFileIO_PL changes are Copyright (c) 2005-2014 by Philippe Lagadec +# +# The Python Imaging Library (PIL) is +# Copyright (c) 1997-2005 by Secret Labs AB +# Copyright (c) 1995-2005 by Fredrik Lundh +# +# By obtaining, using, and/or copying this software and/or its associated +# documentation, you agree that you have read, understood, and will comply with +# the following terms and conditions: +# +# Permission to use, copy, modify, and distribute this software and its +# associated documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appears in all copies, and that both +# that copyright notice and this permission notice appear in supporting +# documentation, and that the name of Secret Labs AB or the author(s) not be used +# in advertising or publicity pertaining to distribution of the software +# without specific, written prior permission. +# +# SECRET LABS AB AND THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +# SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +# IN NO EVENT SHALL SECRET LABS AB OR THE AUTHORS BE LIABLE FOR ANY SPECIAL, +# INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. + +#----------------------------------------------------------------------------- +# CHANGELOG: (only OleFileIO_PL changes compared to PIL 1.1.6) +# 2005-05-11 v0.10 PL: - a few fixes for Python 2.4 compatibility +# (all changes flagged with [PL]) +# 2006-02-22 v0.11 PL: - a few fixes for some Office 2003 documents which raise +# exceptions in _OleStream.__init__() +# 2006-06-09 v0.12 PL: - fixes for files above 6.8MB (DIFAT in loadfat) +# - added some constants +# - added header values checks +# - added some docstrings +# - getsect: bugfix in case sectors >512 bytes +# - getsect: added conformity checks +# - DEBUG_MODE constant to activate debug display +# 2007-09-04 v0.13 PL: - improved/translated (lots of) comments +# - updated license +# - converted tabs to 4 spaces +# 2007-11-19 v0.14 PL: - added OleFileIO._raise_defect() to adapt sensitivity +# - improved _unicode() to use Python 2.x unicode support +# - fixed bug in _OleDirectoryEntry +# 2007-11-25 v0.15 PL: - added safety checks to detect FAT loops +# - fixed _OleStream which didn't check stream size +# - added/improved many docstrings and comments +# - moved helper functions _unicode and _clsid out of +# OleFileIO class +# - improved OleFileIO._find() to add Unix path syntax +# - OleFileIO._find() is now case-insensitive +# - added get_type() and get_rootentry_name() +# - rewritten loaddirectory and _OleDirectoryEntry +# 2007-11-27 v0.16 PL: - added _OleDirectoryEntry.kids_dict +# - added detection of duplicate filenames in storages +# - added detection of duplicate references to streams +# - added get_size() and exists() to _OleDirectoryEntry +# - added isOleFile to check header before parsing +# - added __all__ list to control public keywords in pydoc +# 2007-12-04 v0.17 PL: - added _load_direntry to fix a bug in loaddirectory +# - improved _unicode(), added workarounds for Python <2.3 +# - added set_debug_mode and -d option to set debug mode +# - fixed bugs in OleFileIO.open and _OleDirectoryEntry +# - added safety check in main for large or binary +# properties +# - allow size>0 for storages for some implementations +# 2007-12-05 v0.18 PL: - fixed several bugs in handling of FAT, MiniFAT and +# streams +# - added option '-c' in main to check all streams +# 2009-12-10 v0.19 PL: - bugfix for 32 bit arrays on 64 bits platforms +# (thanks to Ben G. and Martijn for reporting the bug) +# 2009-12-11 v0.20 PL: - bugfix in OleFileIO.open when filename is not plain str +# 2010-01-22 v0.21 PL: - added support for big-endian CPUs such as PowerPC Macs +# 2012-02-16 v0.22 PL: - fixed bug in getproperties, patch by chuckleberryfinn +# (https://bitbucket.org/decalage/olefileio_pl/issue/7) +# - added close method to OleFileIO (fixed issue #2) +# 2012-07-25 v0.23 PL: - added support for file-like objects (patch by mete0r_kr) +# 2013-05-05 v0.24 PL: - getproperties: added conversion from filetime to python +# datetime +# - main: displays properties with date format +# - new class OleMetadata to parse standard properties +# - added get_metadata method +# 2013-05-07 v0.24 PL: - a few improvements in OleMetadata +# 2013-05-24 v0.25 PL: - getproperties: option to not convert some timestamps +# - OleMetaData: total_edit_time is now a number of seconds, +# not a timestamp +# - getproperties: added support for VT_BOOL, VT_INT, V_UINT +# - getproperties: filter out null chars from strings +# - getproperties: raise non-fatal defects instead of +# exceptions when properties cannot be parsed properly +# 2013-05-27 PL: - getproperties: improved exception handling +# - _raise_defect: added option to set exception type +# - all non-fatal issues are now recorded, and displayed +# when run as a script +# 2013-07-11 v0.26 PL: - added methods to get modification and creation times +# of a directory entry or a storage/stream +# - fixed parsing of direntry timestamps +# 2013-07-24 PL: - new options in listdir to list storages and/or streams +# 2014-02-04 v0.30 PL: - upgraded code to support Python 3.x by Martin Panter +# - several fixes for Python 2.6 (xrange, MAGIC) +# - reused i32 from Pillow's _binary + +#----------------------------------------------------------------------------- +# TODO (for version 1.0): +# + isOleFile should accept file-like objects like open +# + fix how all the methods handle unicode str and/or bytes as arguments +# + add path attrib to _OleDirEntry, set it once and for all in init or +# append_kids (then listdir/_list can be simplified) +# - TESTS with Linux, MacOSX, Python 1.5.2, various files, PIL, ... +# - add underscore to each private method, to avoid their display in +# pydoc/epydoc documentation - Remove it for classes to be documented +# - replace all raised exceptions with _raise_defect (at least in OleFileIO) +# - merge code from _OleStream and OleFileIO.getsect to read sectors +# (maybe add a class for FAT and MiniFAT ?) +# - add method to check all streams (follow sectors chains without storing all +# stream in memory, and report anomalies) +# - use _OleDirectoryEntry.kids_dict to improve _find and _list ? +# - fix Unicode names handling (find some way to stay compatible with Py1.5.2) +# => if possible avoid converting names to Latin-1 +# - review DIFAT code: fix handling of DIFSECT blocks in FAT (not stop) +# - rewrite OleFileIO.getproperties +# - improve docstrings to show more sample uses +# - see also original notes and FIXME below +# - remove all obsolete FIXMEs +# - OleMetadata: fix version attrib according to +# http://msdn.microsoft.com/en-us/library/dd945671%28v=office.12%29.aspx + +# IDEAS: +# - in OleFileIO._open and _OleStream, use size=None instead of 0x7FFFFFFF for +# streams with unknown size +# - use arrays of int instead of long integers for FAT/MiniFAT, to improve +# performance and reduce memory usage ? (possible issue with values >2^31) +# - provide tests with unittest (may need write support to create samples) +# - move all debug code (and maybe dump methods) to a separate module, with +# a class which inherits OleFileIO ? +# - fix docstrings to follow epydoc format +# - add support for 4K sectors ? +# - add support for big endian byte order ? +# - create a simple OLE explorer with wxPython + +# FUTURE EVOLUTIONS to add write support: +# 1) add ability to write a stream back on disk from BytesIO (same size, no +# change in FAT/MiniFAT). +# 2) rename a stream/storage if it doesn't change the RB tree +# 3) use rbtree module to update the red-black tree + any rename +# 4) remove a stream/storage: free sectors in FAT/MiniFAT +# 5) allocate new sectors in FAT/MiniFAT +# 6) create new storage/stream +#----------------------------------------------------------------------------- + +# +# THIS IS WORK IN PROGRESS +# +# The Python Imaging Library +# $Id$ +# +# stuff to deal with OLE2 Structured Storage files. this module is +# used by PIL to read Image Composer and FlashPix files, but can also +# be used to read other files of this type. +# +# History: +# 1997-01-20 fl Created +# 1997-01-22 fl Fixed 64-bit portability quirk +# 2003-09-09 fl Fixed typo in OleFileIO.loadfat (noted by Daniel Haertle) +# 2004-02-29 fl Changed long hex constants to signed integers +# +# Notes: +# FIXME: sort out sign problem (eliminate long hex constants) +# FIXME: change filename to use "a/b/c" instead of ["a", "b", "c"] +# FIXME: provide a glob mechanism function (using fnmatchcase) +# +# Literature: +# +# "FlashPix Format Specification, Appendix A", Kodak and Microsoft, +# September 1996. +# +# Quotes: +# +# "If this document and functionality of the Software conflict, +# the actual functionality of the Software represents the correct +# functionality" -- Microsoft, in the OLE format specification +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + +#------------------------------------------------------------------------------ + + +import io +import sys +import struct, array, os.path, datetime + +#[PL] Define explicitly the public API to avoid private objects in pydoc: +__all__ = ['OleFileIO', 'isOleFile', 'MAGIC'] + +# For Python 3.x, need to redefine long as int: +if str is not bytes: + long = int + +# Need to make sure we use xrange both on Python 2 and 3.x: +try: + # on Python 2 we need xrange: + iterrange = xrange +except: + # no xrange, for Python 3 it was renamed as range: + iterrange = range + +#[PL] workaround to fix an issue with array item size on 64 bits systems: +if array.array('L').itemsize == 4: + # on 32 bits platforms, long integers in an array are 32 bits: + UINT32 = 'L' +elif array.array('I').itemsize == 4: + # on 64 bits platforms, integers in an array are 32 bits: + UINT32 = 'I' +else: + raise ValueError('Need to fix a bug with 32 bit arrays, please contact author...') + + +#[PL] These workarounds were inspired from the Path module +# (see http://www.jorendorff.com/articles/python/path/) +#TODO: test with old Python versions + +# Pre-2.3 workaround for basestring. +try: + basestring +except NameError: + try: + # is Unicode supported (Python >2.0 or >1.6 ?) + basestring = (str, unicode) + except NameError: + basestring = str + +#[PL] Experimental setting: if True, OLE filenames will be kept in Unicode +# if False (default PIL behaviour), all filenames are converted to Latin-1. +KEEP_UNICODE_NAMES = False + +#[PL] DEBUG display mode: False by default, use set_debug_mode() or "-d" on +# command line to change it. +DEBUG_MODE = False +def debug_print(msg): + print(msg) +def debug_pass(msg): + pass +debug = debug_pass + +def set_debug_mode(debug_mode): + """ + Set debug mode on or off, to control display of debugging messages. + mode: True or False + """ + global DEBUG_MODE, debug + DEBUG_MODE = debug_mode + if debug_mode: + debug = debug_print + else: + debug = debug_pass + +MAGIC = b'\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1' + +#[PL]: added constants for Sector IDs (from AAF specifications) +MAXREGSECT = 0xFFFFFFFA; # maximum SECT +DIFSECT = 0xFFFFFFFC; # (-4) denotes a DIFAT sector in a FAT +FATSECT = 0xFFFFFFFD; # (-3) denotes a FAT sector in a FAT +ENDOFCHAIN = 0xFFFFFFFE; # (-2) end of a virtual stream chain +FREESECT = 0xFFFFFFFF; # (-1) unallocated sector + +#[PL]: added constants for Directory Entry IDs (from AAF specifications) +MAXREGSID = 0xFFFFFFFA; # maximum directory entry ID +NOSTREAM = 0xFFFFFFFF; # (-1) unallocated directory entry + +#[PL] object types in storage (from AAF specifications) +STGTY_EMPTY = 0 # empty directory entry (according to OpenOffice.org doc) +STGTY_STORAGE = 1 # element is a storage object +STGTY_STREAM = 2 # element is a stream object +STGTY_LOCKBYTES = 3 # element is an ILockBytes object +STGTY_PROPERTY = 4 # element is an IPropertyStorage object +STGTY_ROOT = 5 # element is a root storage + + +# +# -------------------------------------------------------------------- +# property types + +VT_EMPTY=0; VT_NULL=1; VT_I2=2; VT_I4=3; VT_R4=4; VT_R8=5; VT_CY=6; +VT_DATE=7; VT_BSTR=8; VT_DISPATCH=9; VT_ERROR=10; VT_BOOL=11; +VT_VARIANT=12; VT_UNKNOWN=13; VT_DECIMAL=14; VT_I1=16; VT_UI1=17; +VT_UI2=18; VT_UI4=19; VT_I8=20; VT_UI8=21; VT_INT=22; VT_UINT=23; +VT_VOID=24; VT_HRESULT=25; VT_PTR=26; VT_SAFEARRAY=27; VT_CARRAY=28; +VT_USERDEFINED=29; VT_LPSTR=30; VT_LPWSTR=31; VT_FILETIME=64; +VT_BLOB=65; VT_STREAM=66; VT_STORAGE=67; VT_STREAMED_OBJECT=68; +VT_STORED_OBJECT=69; VT_BLOB_OBJECT=70; VT_CF=71; VT_CLSID=72; +VT_VECTOR=0x1000; + +# map property id to name (for debugging purposes) + +VT = {} +for keyword, var in list(vars().items()): + if keyword[:3] == "VT_": + VT[var] = keyword + +# +# -------------------------------------------------------------------- +# Some common document types (root.clsid fields) + +WORD_CLSID = "00020900-0000-0000-C000-000000000046" +#TODO: check Excel, PPT, ... + +#[PL]: Defect levels to classify parsing errors - see OleFileIO._raise_defect() +DEFECT_UNSURE = 10 # a case which looks weird, but not sure it's a defect +DEFECT_POTENTIAL = 20 # a potential defect +DEFECT_INCORRECT = 30 # an error according to specifications, but parsing + # can go on +DEFECT_FATAL = 40 # an error which cannot be ignored, parsing is + # impossible + +#[PL] add useful constants to __all__: +for key in list(vars().keys()): + if key.startswith('STGTY_') or key.startswith('DEFECT_'): + __all__.append(key) + + +#--- FUNCTIONS ---------------------------------------------------------------- + +def isOleFile (filename): + """ + Test if file is an OLE container (according to its header). + + :param filename: file name or path (str, unicode) + :returns: True if OLE, False otherwise. + """ + f = open(filename, 'rb') + header = f.read(len(MAGIC)) + if header == MAGIC: + return True + else: + return False + + +if bytes is str: + # version for Python 2.x + def i8(c): + return ord(c) +else: + # version for Python 3.x + def i8(c): + return c if c.__class__ is int else c[0] + + +#TODO: replace i16 and i32 with more readable struct.unpack equivalent? + +def i16(c, o = 0): + """ + Converts a 2-bytes (16 bits) string to an integer. + + :param c: string containing bytes to convert + :param o: offset of bytes to convert in string + """ + return i8(c[o]) | (i8(c[o+1])<<8) + + +def i32(c, o = 0): + """ + Converts a 4-bytes (32 bits) string to an integer. + + :param c: string containing bytes to convert + :param o: offset of bytes to convert in string + """ +## return int(ord(c[o])+(ord(c[o+1])<<8)+(ord(c[o+2])<<16)+(ord(c[o+3])<<24)) +## # [PL]: added int() because "<<" gives long int since Python 2.4 + # copied from Pillow's _binary: + return i8(c[o]) | (i8(c[o+1])<<8) | (i8(c[o+2])<<16) | (i8(c[o+3])<<24) + + +def _clsid(clsid): + """ + Converts a CLSID to a human-readable string. + + :param clsid: string of length 16. + """ + assert len(clsid) == 16 + # if clsid is only made of null bytes, return an empty string: + # (PL: why not simply return the string with zeroes?) + if not clsid.strip(b"\0"): + return "" + return (("%08X-%04X-%04X-%02X%02X-" + "%02X" * 6) % + ((i32(clsid, 0), i16(clsid, 4), i16(clsid, 6)) + + tuple(map(i8, clsid[8:16])))) + + + +# UNICODE support: +# (necessary to handle storages/streams names which use Unicode) + +def _unicode(s, errors='replace'): + """ + Map unicode string to Latin 1. (Python with Unicode support) + + :param s: UTF-16LE unicode string to convert to Latin-1 + :param errors: 'replace', 'ignore' or 'strict'. + """ + #TODO: test if it OleFileIO works with Unicode strings, instead of + # converting to Latin-1. + try: + # First the string is converted to plain Unicode: + # (assuming it is encoded as UTF-16 little-endian) + u = s.decode('UTF-16LE', errors) + if bytes is not str or KEEP_UNICODE_NAMES: + return u + else: + # Second the unicode string is converted to Latin-1 + return u.encode('latin_1', errors) + except: + # there was an error during Unicode to Latin-1 conversion: + raise IOError('incorrect Unicode name') + + +def filetime2datetime(filetime): + """ + convert FILETIME (64 bits int) to Python datetime.datetime + """ + # TODO: manage exception when microseconds is too large + # inspired from http://code.activestate.com/recipes/511425-filetime-to-datetime/ + _FILETIME_null_date = datetime.datetime(1601, 1, 1, 0, 0, 0) + #debug('timedelta days=%d' % (filetime//(10*1000000*3600*24))) + return _FILETIME_null_date + datetime.timedelta(microseconds=filetime//10) + + + +#=== CLASSES ================================================================== + +class OleMetadata: + """ + class to parse and store metadata from standard properties of OLE files. + + Available attributes: + codepage, title, subject, author, keywords, comments, template, + last_saved_by, revision_number, total_edit_time, last_printed, create_time, + last_saved_time, num_pages, num_words, num_chars, thumbnail, + creating_application, security, codepage_doc, category, presentation_target, + bytes, lines, paragraphs, slides, notes, hidden_slides, mm_clips, + scale_crop, heading_pairs, titles_of_parts, manager, company, links_dirty, + chars_with_spaces, unused, shared_doc, link_base, hlinks, hlinks_changed, + version, dig_sig, content_type, content_status, language, doc_version + + Note: an attribute is set to None when not present in the properties of the + OLE file. + + References for SummaryInformation stream: + - http://msdn.microsoft.com/en-us/library/dd942545.aspx + - http://msdn.microsoft.com/en-us/library/dd925819%28v=office.12%29.aspx + - http://msdn.microsoft.com/en-us/library/windows/desktop/aa380376%28v=vs.85%29.aspx + - http://msdn.microsoft.com/en-us/library/aa372045.aspx + - http://sedna-soft.de/summary-information-stream/ + - http://poi.apache.org/apidocs/org/apache/poi/hpsf/SummaryInformation.html + + References for DocumentSummaryInformation stream: + - http://msdn.microsoft.com/en-us/library/dd945671%28v=office.12%29.aspx + - http://msdn.microsoft.com/en-us/library/windows/desktop/aa380374%28v=vs.85%29.aspx + - http://poi.apache.org/apidocs/org/apache/poi/hpsf/DocumentSummaryInformation.html + + new in version 0.25 + """ + + # attribute names for SummaryInformation stream properties: + # (ordered by property id, starting at 1) + SUMMARY_ATTRIBS = ['codepage', 'title', 'subject', 'author', 'keywords', 'comments', + 'template', 'last_saved_by', 'revision_number', 'total_edit_time', + 'last_printed', 'create_time', 'last_saved_time', 'num_pages', + 'num_words', 'num_chars', 'thumbnail', 'creating_application', + 'security'] + + # attribute names for DocumentSummaryInformation stream properties: + # (ordered by property id, starting at 1) + DOCSUM_ATTRIBS = ['codepage_doc', 'category', 'presentation_target', 'bytes', 'lines', 'paragraphs', + 'slides', 'notes', 'hidden_slides', 'mm_clips', + 'scale_crop', 'heading_pairs', 'titles_of_parts', 'manager', + 'company', 'links_dirty', 'chars_with_spaces', 'unused', 'shared_doc', + 'link_base', 'hlinks', 'hlinks_changed', 'version', 'dig_sig', + 'content_type', 'content_status', 'language', 'doc_version'] + + def __init__(self): + """ + Constructor for OleMetadata + All attributes are set to None by default + """ + # properties from SummaryInformation stream + self.codepage = None + self.title = None + self.subject = None + self.author = None + self.keywords = None + self.comments = None + self.template = None + self.last_saved_by = None + self.revision_number = None + self.total_edit_time = None + self.last_printed = None + self.create_time = None + self.last_saved_time = None + self.num_pages = None + self.num_words = None + self.num_chars = None + self.thumbnail = None + self.creating_application = None + self.security = None + # properties from DocumentSummaryInformation stream + self.codepage_doc = None + self.category = None + self.presentation_target = None + self.bytes = None + self.lines = None + self.paragraphs = None + self.slides = None + self.notes = None + self.hidden_slides = None + self.mm_clips = None + self.scale_crop = None + self.heading_pairs = None + self.titles_of_parts = None + self.manager = None + self.company = None + self.links_dirty = None + self.chars_with_spaces = None + self.unused = None + self.shared_doc = None + self.link_base = None + self.hlinks = None + self.hlinks_changed = None + self.version = None + self.dig_sig = None + self.content_type = None + self.content_status = None + self.language = None + self.doc_version = None + + + def parse_properties(self, olefile): + """ + Parse standard properties of an OLE file, from the streams + "\x05SummaryInformation" and "\x05DocumentSummaryInformation", + if present. + Properties are converted to strings, integers or python datetime objects. + If a property is not present, its value is set to None. + """ + # first set all attributes to None: + for attrib in (self.SUMMARY_ATTRIBS + self.DOCSUM_ATTRIBS): + setattr(self, attrib, None) + if olefile.exists("\x05SummaryInformation"): + # get properties from the stream: + # (converting timestamps to python datetime, except total_edit_time, + # which is property #10) + props = olefile.getproperties("\x05SummaryInformation", + convert_time=True, no_conversion=[10]) + # store them into this object's attributes: + for i in range(len(self.SUMMARY_ATTRIBS)): + # ids for standards properties start at 0x01, until 0x13 + value = props.get(i+1, None) + setattr(self, self.SUMMARY_ATTRIBS[i], value) + if olefile.exists("\x05DocumentSummaryInformation"): + # get properties from the stream: + props = olefile.getproperties("\x05DocumentSummaryInformation", + convert_time=True) + # store them into this object's attributes: + for i in range(len(self.DOCSUM_ATTRIBS)): + # ids for standards properties start at 0x01, until 0x13 + value = props.get(i+1, None) + setattr(self, self.DOCSUM_ATTRIBS[i], value) + + def dump(self): + """ + Dump all metadata, for debugging purposes. + """ + print('Properties from SummaryInformation stream:') + for prop in self.SUMMARY_ATTRIBS: + value = getattr(self, prop) + print('- %s: %s' % (prop, repr(value))) + print('Properties from DocumentSummaryInformation stream:') + for prop in self.DOCSUM_ATTRIBS: + value = getattr(self, prop) + print('- %s: %s' % (prop, repr(value))) + + +#--- _OleStream --------------------------------------------------------------- + +class _OleStream(io.BytesIO): + """ + OLE2 Stream + + Returns a read-only file object which can be used to read + the contents of a OLE stream (instance of the BytesIO class). + To open a stream, use the openstream method in the OleFile class. + + This function can be used with either ordinary streams, + or ministreams, depending on the offset, sectorsize, and + fat table arguments. + + Attributes: + - size: actual size of data stream, after it was opened. + """ + + # FIXME: should store the list of sects obtained by following + # the fat chain, and load new sectors on demand instead of + # loading it all in one go. + + def __init__(self, fp, sect, size, offset, sectorsize, fat, filesize): + """ + Constructor for _OleStream class. + + :param fp : file object, the OLE container or the MiniFAT stream + :param sect : sector index of first sector in the stream + :param size : total size of the stream + :param offset : offset in bytes for the first FAT or MiniFAT sector + :param sectorsize: size of one sector + :param fat : array/list of sector indexes (FAT or MiniFAT) + :param filesize : size of OLE file (for debugging) + :returns : a BytesIO instance containing the OLE stream + """ + debug('_OleStream.__init__:') + debug(' sect=%d (%X), size=%d, offset=%d, sectorsize=%d, len(fat)=%d, fp=%s' + %(sect,sect,size,offset,sectorsize,len(fat), repr(fp))) + #[PL] To detect malformed documents with FAT loops, we compute the + # expected number of sectors in the stream: + unknown_size = False + if size==0x7FFFFFFF: + # this is the case when called from OleFileIO._open(), and stream + # size is not known in advance (for example when reading the + # Directory stream). Then we can only guess maximum size: + size = len(fat)*sectorsize + # and we keep a record that size was unknown: + unknown_size = True + debug(' stream with UNKNOWN SIZE') + nb_sectors = (size + (sectorsize-1)) // sectorsize + debug('nb_sectors = %d' % nb_sectors) + # This number should (at least) be less than the total number of + # sectors in the given FAT: + if nb_sectors > len(fat): + raise IOError('malformed OLE document, stream too large') + # optimization(?): data is first a list of strings, and join() is called + # at the end to concatenate all in one string. + # (this may not be really useful with recent Python versions) + data = [] + # if size is zero, then first sector index should be ENDOFCHAIN: + if size == 0 and sect != ENDOFCHAIN: + debug('size == 0 and sect != ENDOFCHAIN:') + raise IOError('incorrect OLE sector index for empty stream') + #[PL] A fixed-length for loop is used instead of an undefined while + # loop to avoid DoS attacks: + for i in range(nb_sectors): + # Sector index may be ENDOFCHAIN, but only if size was unknown + if sect == ENDOFCHAIN: + if unknown_size: + break + else: + # else this means that the stream is smaller than declared: + debug('sect=ENDOFCHAIN before expected size') + raise IOError('incomplete OLE stream') + # sector index should be within FAT: + if sect<0 or sect>=len(fat): + debug('sect=%d (%X) / len(fat)=%d' % (sect, sect, len(fat))) + debug('i=%d / nb_sectors=%d' %(i, nb_sectors)) +## tmp_data = b"".join(data) +## f = open('test_debug.bin', 'wb') +## f.write(tmp_data) +## f.close() +## debug('data read so far: %d bytes' % len(tmp_data)) + raise IOError('incorrect OLE FAT, sector index out of range') + #TODO: merge this code with OleFileIO.getsect() ? + #TODO: check if this works with 4K sectors: + try: + fp.seek(offset + sectorsize * sect) + except: + debug('sect=%d, seek=%d, filesize=%d' % + (sect, offset+sectorsize*sect, filesize)) + raise IOError('OLE sector index out of range') + sector_data = fp.read(sectorsize) + # [PL] check if there was enough data: + # Note: if sector is the last of the file, sometimes it is not a + # complete sector (of 512 or 4K), so we may read less than + # sectorsize. + if len(sector_data)!=sectorsize and sect!=(len(fat)-1): + debug('sect=%d / len(fat)=%d, seek=%d / filesize=%d, len read=%d' % + (sect, len(fat), offset+sectorsize*sect, filesize, len(sector_data))) + debug('seek+len(read)=%d' % (offset+sectorsize*sect+len(sector_data))) + raise IOError('incomplete OLE sector') + data.append(sector_data) + # jump to next sector in the FAT: + try: + sect = fat[sect] + except IndexError: + # [PL] if pointer is out of the FAT an exception is raised + raise IOError('incorrect OLE FAT, sector index out of range') + #[PL] Last sector should be a "end of chain" marker: + if sect != ENDOFCHAIN: + raise IOError('incorrect last sector index in OLE stream') + data = b"".join(data) + # Data is truncated to the actual stream size: + if len(data) >= size: + data = data[:size] + # actual stream size is stored for future use: + self.size = size + elif unknown_size: + # actual stream size was not known, now we know the size of read + # data: + self.size = len(data) + else: + # read data is less than expected: + debug('len(data)=%d, size=%d' % (len(data), size)) + raise IOError('OLE stream size is less than declared') + # when all data is read in memory, BytesIO constructor is called + io.BytesIO.__init__(self, data) + # Then the _OleStream object can be used as a read-only file object. + + +#--- _OleDirectoryEntry ------------------------------------------------------- + +class _OleDirectoryEntry: + + """ + OLE2 Directory Entry + """ + #[PL] parsing code moved from OleFileIO.loaddirectory + + # struct to parse directory entries: + # <: little-endian byte order, standard sizes + # (note: this should guarantee that Q returns a 64 bits int) + # 64s: string containing entry name in unicode (max 31 chars) + null char + # H: uint16, number of bytes used in name buffer, including null = (len+1)*2 + # B: uint8, dir entry type (between 0 and 5) + # B: uint8, color: 0=black, 1=red + # I: uint32, index of left child node in the red-black tree, NOSTREAM if none + # I: uint32, index of right child node in the red-black tree, NOSTREAM if none + # I: uint32, index of child root node if it is a storage, else NOSTREAM + # 16s: CLSID, unique identifier (only used if it is a storage) + # I: uint32, user flags + # Q (was 8s): uint64, creation timestamp or zero + # Q (was 8s): uint64, modification timestamp or zero + # I: uint32, SID of first sector if stream or ministream, SID of 1st sector + # of stream containing ministreams if root entry, 0 otherwise + # I: uint32, total stream size in bytes if stream (low 32 bits), 0 otherwise + # I: uint32, total stream size in bytes if stream (high 32 bits), 0 otherwise + STRUCT_DIRENTRY = '<64sHBBIII16sIQQIII' + # size of a directory entry: 128 bytes + DIRENTRY_SIZE = 128 + assert struct.calcsize(STRUCT_DIRENTRY) == DIRENTRY_SIZE + + + def __init__(self, entry, sid, olefile): + """ + Constructor for an _OleDirectoryEntry object. + Parses a 128-bytes entry from the OLE Directory stream. + + :param entry : string (must be 128 bytes long) + :param sid : index of this directory entry in the OLE file directory + :param olefile: OleFileIO containing this directory entry + """ + self.sid = sid + # ref to olefile is stored for future use + self.olefile = olefile + # kids is a list of children entries, if this entry is a storage: + # (list of _OleDirectoryEntry objects) + self.kids = [] + # kids_dict is a dictionary of children entries, indexed by their + # name in lowercase: used to quickly find an entry, and to detect + # duplicates + self.kids_dict = {} + # flag used to detect if the entry is referenced more than once in + # directory: + self.used = False + # decode DirEntry + ( + name, + namelength, + self.entry_type, + self.color, + self.sid_left, + self.sid_right, + self.sid_child, + clsid, + self.dwUserFlags, + self.createTime, + self.modifyTime, + self.isectStart, + sizeLow, + sizeHigh + ) = struct.unpack(_OleDirectoryEntry.STRUCT_DIRENTRY, entry) + if self.entry_type not in [STGTY_ROOT, STGTY_STORAGE, STGTY_STREAM, STGTY_EMPTY]: + olefile._raise_defect(DEFECT_INCORRECT, 'unhandled OLE storage type') + # only first directory entry can (and should) be root: + if self.entry_type == STGTY_ROOT and sid != 0: + olefile._raise_defect(DEFECT_INCORRECT, 'duplicate OLE root entry') + if sid == 0 and self.entry_type != STGTY_ROOT: + olefile._raise_defect(DEFECT_INCORRECT, 'incorrect OLE root entry') + #debug (struct.unpack(fmt_entry, entry[:len_entry])) + # name should be at most 31 unicode characters + null character, + # so 64 bytes in total (31*2 + 2): + if namelength>64: + olefile._raise_defect(DEFECT_INCORRECT, 'incorrect DirEntry name length') + # if exception not raised, namelength is set to the maximum value: + namelength = 64 + # only characters without ending null char are kept: + name = name[:(namelength-2)] + # name is converted from unicode to Latin-1: + self.name = _unicode(name) + + debug('DirEntry SID=%d: %s' % (self.sid, repr(self.name))) + debug(' - type: %d' % self.entry_type) + debug(' - sect: %d' % self.isectStart) + debug(' - SID left: %d, right: %d, child: %d' % (self.sid_left, + self.sid_right, self.sid_child)) + + # sizeHigh is only used for 4K sectors, it should be zero for 512 bytes + # sectors, BUT apparently some implementations set it as 0xFFFFFFFF, 1 + # or some other value so it cannot be raised as a defect in general: + if olefile.sectorsize == 512: + if sizeHigh != 0 and sizeHigh != 0xFFFFFFFF: + debug('sectorsize=%d, sizeLow=%d, sizeHigh=%d (%X)' % + (olefile.sectorsize, sizeLow, sizeHigh, sizeHigh)) + olefile._raise_defect(DEFECT_UNSURE, 'incorrect OLE stream size') + self.size = sizeLow + else: + self.size = sizeLow + (long(sizeHigh)<<32) + debug(' - size: %d (sizeLow=%d, sizeHigh=%d)' % (self.size, sizeLow, sizeHigh)) + + self.clsid = _clsid(clsid) + # a storage should have a null size, BUT some implementations such as + # Word 8 for Mac seem to allow non-null values => Potential defect: + if self.entry_type == STGTY_STORAGE and self.size != 0: + olefile._raise_defect(DEFECT_POTENTIAL, 'OLE storage with size>0') + # check if stream is not already referenced elsewhere: + if self.entry_type in (STGTY_ROOT, STGTY_STREAM) and self.size>0: + if self.size < olefile.minisectorcutoff \ + and self.entry_type==STGTY_STREAM: # only streams can be in MiniFAT + # ministream object + minifat = True + else: + minifat = False + olefile._check_duplicate_stream(self.isectStart, minifat) + + + + def build_storage_tree(self): + """ + Read and build the red-black tree attached to this _OleDirectoryEntry + object, if it is a storage. + Note that this method builds a tree of all subentries, so it should + only be called for the root object once. + """ + debug('build_storage_tree: SID=%d - %s - sid_child=%d' + % (self.sid, repr(self.name), self.sid_child)) + if self.sid_child != NOSTREAM: + # if child SID is not NOSTREAM, then this entry is a storage. + # Let's walk through the tree of children to fill the kids list: + self.append_kids(self.sid_child) + + # Note from OpenOffice documentation: the safest way is to + # recreate the tree because some implementations may store broken + # red-black trees... + + # in the OLE file, entries are sorted on (length, name). + # for convenience, we sort them on name instead: + # (see rich comparison methods in this class) + self.kids.sort() + + + def append_kids(self, child_sid): + """ + Walk through red-black tree of children of this directory entry to add + all of them to the kids list. (recursive method) + + child_sid : index of child directory entry to use, or None when called + first time for the root. (only used during recursion) + """ + #[PL] this method was added to use simple recursion instead of a complex + # algorithm. + # if this is not a storage or a leaf of the tree, nothing to do: + if child_sid == NOSTREAM: + return + # check if child SID is in the proper range: + if child_sid<0 or child_sid>=len(self.olefile.direntries): + self.olefile._raise_defect(DEFECT_FATAL, 'OLE DirEntry index out of range') + # get child direntry: + child = self.olefile._load_direntry(child_sid) #direntries[child_sid] + debug('append_kids: child_sid=%d - %s - sid_left=%d, sid_right=%d, sid_child=%d' + % (child.sid, repr(child.name), child.sid_left, child.sid_right, child.sid_child)) + # the directory entries are organized as a red-black tree. + # (cf. Wikipedia for details) + # First walk through left side of the tree: + self.append_kids(child.sid_left) + # Check if its name is not already used (case-insensitive): + name_lower = child.name.lower() + if name_lower in self.kids_dict: + self.olefile._raise_defect(DEFECT_INCORRECT, + "Duplicate filename in OLE storage") + # Then the child_sid _OleDirectoryEntry object is appended to the + # kids list and dictionary: + self.kids.append(child) + self.kids_dict[name_lower] = child + # Check if kid was not already referenced in a storage: + if child.used: + self.olefile._raise_defect(DEFECT_INCORRECT, + 'OLE Entry referenced more than once') + child.used = True + # Finally walk through right side of the tree: + self.append_kids(child.sid_right) + # Afterwards build kid's own tree if it's also a storage: + child.build_storage_tree() + + + def __eq__(self, other): + "Compare entries by name" + return self.name == other.name + + def __lt__(self, other): + "Compare entries by name" + return self.name < other.name + + def __ne__(self, other): + return not self.__eq__(other) + + def __le__(self, other): + return self.__eq__(other) or self.__lt__(other) + + # Reflected __lt__() and __le__() will be used for __gt__() and __ge__() + + #TODO: replace by the same function as MS implementation ? + # (order by name length first, then case-insensitive order) + + + def dump(self, tab = 0): + "Dump this entry, and all its subentries (for debug purposes only)" + TYPES = ["(invalid)", "(storage)", "(stream)", "(lockbytes)", + "(property)", "(root)"] + print(" "*tab + repr(self.name), TYPES[self.entry_type], end=' ') + if self.entry_type in (STGTY_STREAM, STGTY_ROOT): + print(self.size, "bytes", end=' ') + print() + if self.entry_type in (STGTY_STORAGE, STGTY_ROOT) and self.clsid: + print(" "*tab + "{%s}" % self.clsid) + + for kid in self.kids: + kid.dump(tab + 2) + + + def getmtime(self): + """ + Return modification time of a directory entry. + + :returns: None if modification time is null, a python datetime object + otherwise (UTC timezone) + + new in version 0.26 + """ + if self.modifyTime == 0: + return None + return filetime2datetime(self.modifyTime) + + + def getctime(self): + """ + Return creation time of a directory entry. + + :returns: None if modification time is null, a python datetime object + otherwise (UTC timezone) + + new in version 0.26 + """ + if self.createTime == 0: + return None + return filetime2datetime(self.createTime) + + +#--- OleFileIO ---------------------------------------------------------------- + +class OleFileIO: + """ + OLE container object + + This class encapsulates the interface to an OLE 2 structured + storage file. Use the :py:meth:`~PIL.OleFileIO.OleFileIO.listdir` and + :py:meth:`~PIL.OleFileIO.OleFileIO.openstream` methods to + access the contents of this file. + + Object names are given as a list of strings, one for each subentry + level. The root entry should be omitted. For example, the following + code extracts all image streams from a Microsoft Image Composer file:: + + ole = OleFileIO("fan.mic") + + for entry in ole.listdir(): + if entry[1:2] == "Image": + fin = ole.openstream(entry) + fout = open(entry[0:1], "wb") + while True: + s = fin.read(8192) + if not s: + break + fout.write(s) + + You can use the viewer application provided with the Python Imaging + Library to view the resulting files (which happens to be standard + TIFF files). + """ + + def __init__(self, filename = None, raise_defects=DEFECT_FATAL): + """ + Constructor for OleFileIO class. + + :param filename: file to open. + :param raise_defects: minimal level for defects to be raised as exceptions. + (use DEFECT_FATAL for a typical application, DEFECT_INCORRECT for a + security-oriented application, see source code for details) + """ + # minimal level for defects to be raised as exceptions: + self._raise_defects_level = raise_defects + # list of defects/issues not raised as exceptions: + # tuples of (exception type, message) + self.parsing_issues = [] + if filename: + self.open(filename) + + + def _raise_defect(self, defect_level, message, exception_type=IOError): + """ + This method should be called for any defect found during file parsing. + It may raise an IOError exception according to the minimal level chosen + for the OleFileIO object. + + :param defect_level: defect level, possible values are: + DEFECT_UNSURE : a case which looks weird, but not sure it's a defect + DEFECT_POTENTIAL : a potential defect + DEFECT_INCORRECT : an error according to specifications, but parsing can go on + DEFECT_FATAL : an error which cannot be ignored, parsing is impossible + :param message: string describing the defect, used with raised exception. + :param exception_type: exception class to be raised, IOError by default + """ + # added by [PL] + if defect_level >= self._raise_defects_level: + raise exception_type(message) + else: + # just record the issue, no exception raised: + self.parsing_issues.append((exception_type, message)) + + + def open(self, filename): + """ + Open an OLE2 file. + Reads the header, FAT and directory. + + :param filename: string-like or file-like object + """ + #[PL] check if filename is a string-like or file-like object: + # (it is better to check for a read() method) + if hasattr(filename, 'read'): + # file-like object + self.fp = filename + else: + # string-like object: filename of file on disk + #TODO: if larger than 1024 bytes, this could be the actual data => BytesIO + self.fp = open(filename, "rb") + # old code fails if filename is not a plain string: + #if isinstance(filename, (bytes, basestring)): + # self.fp = open(filename, "rb") + #else: + # self.fp = filename + # obtain the filesize by using seek and tell, which should work on most + # file-like objects: + #TODO: do it above, using getsize with filename when possible? + #TODO: fix code to fail with clear exception when filesize cannot be obtained + self.fp.seek(0, os.SEEK_END) + try: + filesize = self.fp.tell() + finally: + self.fp.seek(0) + self._filesize = filesize + + # lists of streams in FAT and MiniFAT, to detect duplicate references + # (list of indexes of first sectors of each stream) + self._used_streams_fat = [] + self._used_streams_minifat = [] + + header = self.fp.read(512) + + if len(header) != 512 or header[:8] != MAGIC: + self._raise_defect(DEFECT_FATAL, "not an OLE2 structured storage file") + + # [PL] header structure according to AAF specifications: + ##Header + ##struct StructuredStorageHeader { // [offset from start (bytes), length (bytes)] + ##BYTE _abSig[8]; // [00H,08] {0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, + ## // 0x1a, 0xe1} for current version + ##CLSID _clsid; // [08H,16] reserved must be zero (WriteClassStg/ + ## // GetClassFile uses root directory class id) + ##USHORT _uMinorVersion; // [18H,02] minor version of the format: 33 is + ## // written by reference implementation + ##USHORT _uDllVersion; // [1AH,02] major version of the dll/format: 3 for + ## // 512-byte sectors, 4 for 4 KB sectors + ##USHORT _uByteOrder; // [1CH,02] 0xFFFE: indicates Intel byte-ordering + ##USHORT _uSectorShift; // [1EH,02] size of sectors in power-of-two; + ## // typically 9 indicating 512-byte sectors + ##USHORT _uMiniSectorShift; // [20H,02] size of mini-sectors in power-of-two; + ## // typically 6 indicating 64-byte mini-sectors + ##USHORT _usReserved; // [22H,02] reserved, must be zero + ##ULONG _ulReserved1; // [24H,04] reserved, must be zero + ##FSINDEX _csectDir; // [28H,04] must be zero for 512-byte sectors, + ## // number of SECTs in directory chain for 4 KB + ## // sectors + ##FSINDEX _csectFat; // [2CH,04] number of SECTs in the FAT chain + ##SECT _sectDirStart; // [30H,04] first SECT in the directory chain + ##DFSIGNATURE _signature; // [34H,04] signature used for transactions; must + ## // be zero. The reference implementation + ## // does not support transactions + ##ULONG _ulMiniSectorCutoff; // [38H,04] maximum size for a mini stream; + ## // typically 4096 bytes + ##SECT _sectMiniFatStart; // [3CH,04] first SECT in the MiniFAT chain + ##FSINDEX _csectMiniFat; // [40H,04] number of SECTs in the MiniFAT chain + ##SECT _sectDifStart; // [44H,04] first SECT in the DIFAT chain + ##FSINDEX _csectDif; // [48H,04] number of SECTs in the DIFAT chain + ##SECT _sectFat[109]; // [4CH,436] the SECTs of first 109 FAT sectors + ##}; + + # [PL] header decoding: + # '<' indicates little-endian byte ordering for Intel (cf. struct module help) + fmt_header = '<8s16sHHHHHHLLLLLLLLLL' + header_size = struct.calcsize(fmt_header) + debug( "fmt_header size = %d, +FAT = %d" % (header_size, header_size + 109*4) ) + header1 = header[:header_size] + ( + self.Sig, + self.clsid, + self.MinorVersion, + self.DllVersion, + self.ByteOrder, + self.SectorShift, + self.MiniSectorShift, + self.Reserved, self.Reserved1, + self.csectDir, + self.csectFat, + self.sectDirStart, + self.signature, + self.MiniSectorCutoff, + self.MiniFatStart, + self.csectMiniFat, + self.sectDifStart, + self.csectDif + ) = struct.unpack(fmt_header, header1) + debug( struct.unpack(fmt_header, header1)) + + if self.Sig != MAGIC: + # OLE signature should always be present + self._raise_defect(DEFECT_FATAL, "incorrect OLE signature") + if self.clsid != bytearray(16): + # according to AAF specs, CLSID should always be zero + self._raise_defect(DEFECT_INCORRECT, "incorrect CLSID in OLE header") + debug( "MinorVersion = %d" % self.MinorVersion ) + debug( "DllVersion = %d" % self.DllVersion ) + if self.DllVersion not in [3, 4]: + # version 3: usual format, 512 bytes per sector + # version 4: large format, 4K per sector + self._raise_defect(DEFECT_INCORRECT, "incorrect DllVersion in OLE header") + debug( "ByteOrder = %X" % self.ByteOrder ) + if self.ByteOrder != 0xFFFE: + # For now only common little-endian documents are handled correctly + self._raise_defect(DEFECT_FATAL, "incorrect ByteOrder in OLE header") + # TODO: add big-endian support for documents created on Mac ? + self.SectorSize = 2**self.SectorShift + debug( "SectorSize = %d" % self.SectorSize ) + if self.SectorSize not in [512, 4096]: + self._raise_defect(DEFECT_INCORRECT, "incorrect SectorSize in OLE header") + if (self.DllVersion==3 and self.SectorSize!=512) \ + or (self.DllVersion==4 and self.SectorSize!=4096): + self._raise_defect(DEFECT_INCORRECT, "SectorSize does not match DllVersion in OLE header") + self.MiniSectorSize = 2**self.MiniSectorShift + debug( "MiniSectorSize = %d" % self.MiniSectorSize ) + if self.MiniSectorSize not in [64]: + self._raise_defect(DEFECT_INCORRECT, "incorrect MiniSectorSize in OLE header") + if self.Reserved != 0 or self.Reserved1 != 0: + self._raise_defect(DEFECT_INCORRECT, "incorrect OLE header (non-null reserved bytes)") + debug( "csectDir = %d" % self.csectDir ) + if self.SectorSize==512 and self.csectDir!=0: + self._raise_defect(DEFECT_INCORRECT, "incorrect csectDir in OLE header") + debug( "csectFat = %d" % self.csectFat ) + debug( "sectDirStart = %X" % self.sectDirStart ) + debug( "signature = %d" % self.signature ) + # Signature should be zero, BUT some implementations do not follow this + # rule => only a potential defect: + if self.signature != 0: + self._raise_defect(DEFECT_POTENTIAL, "incorrect OLE header (signature>0)") + debug( "MiniSectorCutoff = %d" % self.MiniSectorCutoff ) + debug( "MiniFatStart = %X" % self.MiniFatStart ) + debug( "csectMiniFat = %d" % self.csectMiniFat ) + debug( "sectDifStart = %X" % self.sectDifStart ) + debug( "csectDif = %d" % self.csectDif ) + + # calculate the number of sectors in the file + # (-1 because header doesn't count) + self.nb_sect = ( (filesize + self.SectorSize-1) // self.SectorSize) - 1 + debug( "Number of sectors in the file: %d" % self.nb_sect ) + + # file clsid (probably never used, so we don't store it) + clsid = _clsid(header[8:24]) + self.sectorsize = self.SectorSize #1 << i16(header, 30) + self.minisectorsize = self.MiniSectorSize #1 << i16(header, 32) + self.minisectorcutoff = self.MiniSectorCutoff # i32(header, 56) + + # check known streams for duplicate references (these are always in FAT, + # never in MiniFAT): + self._check_duplicate_stream(self.sectDirStart) + # check MiniFAT only if it is not empty: + if self.csectMiniFat: + self._check_duplicate_stream(self.MiniFatStart) + # check DIFAT only if it is not empty: + if self.csectDif: + self._check_duplicate_stream(self.sectDifStart) + + # Load file allocation tables + self.loadfat(header) + # Load direcory. This sets both the direntries list (ordered by sid) + # and the root (ordered by hierarchy) members. + self.loaddirectory(self.sectDirStart)#i32(header, 48)) + self.ministream = None + self.minifatsect = self.MiniFatStart #i32(header, 60) + + + def close(self): + """ + close the OLE file, to release the file object + """ + self.fp.close() + + + def _check_duplicate_stream(self, first_sect, minifat=False): + """ + Checks if a stream has not been already referenced elsewhere. + This method should only be called once for each known stream, and only + if stream size is not null. + :param first_sect: index of first sector of the stream in FAT + :param minifat: if True, stream is located in the MiniFAT, else in the FAT + """ + if minifat: + debug('_check_duplicate_stream: sect=%d in MiniFAT' % first_sect) + used_streams = self._used_streams_minifat + else: + debug('_check_duplicate_stream: sect=%d in FAT' % first_sect) + # some values can be safely ignored (not a real stream): + if first_sect in (DIFSECT,FATSECT,ENDOFCHAIN,FREESECT): + return + used_streams = self._used_streams_fat + #TODO: would it be more efficient using a dict or hash values, instead + # of a list of long ? + if first_sect in used_streams: + self._raise_defect(DEFECT_INCORRECT, 'Stream referenced twice') + else: + used_streams.append(first_sect) + + + def dumpfat(self, fat, firstindex=0): + "Displays a part of FAT in human-readable form for debugging purpose" + # [PL] added only for debug + if not DEBUG_MODE: + return + # dictionary to convert special FAT values in human-readable strings + VPL=8 # valeurs par ligne (8+1 * 8+1 = 81) + fatnames = { + FREESECT: "..free..", + ENDOFCHAIN: "[ END. ]", + FATSECT: "FATSECT ", + DIFSECT: "DIFSECT " + } + nbsect = len(fat) + nlines = (nbsect+VPL-1)//VPL + print("index", end=" ") + for i in range(VPL): + print("%8X" % i, end=" ") + print() + for l in range(nlines): + index = l*VPL + print("%8X:" % (firstindex+index), end=" ") + for i in range(index, index+VPL): + if i>=nbsect: + break + sect = fat[i] + if sect in fatnames: + nom = fatnames[sect] + else: + if sect == i+1: + nom = " --->" + else: + nom = "%8X" % sect + print(nom, end=" ") + print() + + + def dumpsect(self, sector, firstindex=0): + "Displays a sector in a human-readable form, for debugging purpose." + if not DEBUG_MODE: + return + VPL=8 # number of values per line (8+1 * 8+1 = 81) + tab = array.array(UINT32, sector) + nbsect = len(tab) + nlines = (nbsect+VPL-1)//VPL + print("index", end=" ") + for i in range(VPL): + print("%8X" % i, end=" ") + print() + for l in range(nlines): + index = l*VPL + print("%8X:" % (firstindex+index), end=" ") + for i in range(index, index+VPL): + if i>=nbsect: + break + sect = tab[i] + nom = "%8X" % sect + print(nom, end=" ") + print() + + def sect2array(self, sect): + """ + convert a sector to an array of 32 bits unsigned integers, + swapping bytes on big endian CPUs such as PowerPC (old Macs) + """ + a = array.array(UINT32, sect) + # if CPU is big endian, swap bytes: + if sys.byteorder == 'big': + a.byteswap() + return a + + + def loadfat_sect(self, sect): + """ + Adds the indexes of the given sector to the FAT + + :param sect: string containing the first FAT sector, or array of long integers + :returns: index of last FAT sector. + """ + # a FAT sector is an array of ulong integers. + if isinstance(sect, array.array): + # if sect is already an array it is directly used + fat1 = sect + else: + # if it's a raw sector, it is parsed in an array + fat1 = self.sect2array(sect) + self.dumpsect(sect) + # The FAT is a sector chain starting at the first index of itself. + for isect in fat1: + #print("isect = %X" % isect) + if isect == ENDOFCHAIN or isect == FREESECT: + # the end of the sector chain has been reached + break + # read the FAT sector + s = self.getsect(isect) + # parse it as an array of 32 bits integers, and add it to the + # global FAT array + nextfat = self.sect2array(s) + self.fat = self.fat + nextfat + return isect + + + def loadfat(self, header): + """ + Load the FAT table. + """ + # The header contains a sector numbers + # for the first 109 FAT sectors. Additional sectors are + # described by DIF blocks + + sect = header[76:512] + debug( "len(sect)=%d, so %d integers" % (len(sect), len(sect)//4) ) + #fat = [] + # [PL] FAT is an array of 32 bits unsigned ints, it's more effective + # to use an array than a list in Python. + # It's initialized as empty first: + self.fat = array.array(UINT32) + self.loadfat_sect(sect) + #self.dumpfat(self.fat) +## for i in range(0, len(sect), 4): +## ix = i32(sect, i) +## #[PL] if ix == -2 or ix == -1: # ix == 0xFFFFFFFE or ix == 0xFFFFFFFF: +## if ix == 0xFFFFFFFE or ix == 0xFFFFFFFF: +## break +## s = self.getsect(ix) +## #fat = fat + [i32(s, i) for i in range(0, len(s), 4)] +## fat = fat + array.array(UINT32, s) + if self.csectDif != 0: + # [PL] There's a DIFAT because file is larger than 6.8MB + # some checks just in case: + if self.csectFat <= 109: + # there must be at least 109 blocks in header and the rest in + # DIFAT, so number of sectors must be >109. + self._raise_defect(DEFECT_INCORRECT, 'incorrect DIFAT, not enough sectors') + if self.sectDifStart >= self.nb_sect: + # initial DIFAT block index must be valid + self._raise_defect(DEFECT_FATAL, 'incorrect DIFAT, first index out of range') + debug( "DIFAT analysis..." ) + # We compute the necessary number of DIFAT sectors : + # (each DIFAT sector = 127 pointers + 1 towards next DIFAT sector) + nb_difat = (self.csectFat-109 + 126)//127 + debug( "nb_difat = %d" % nb_difat ) + if self.csectDif != nb_difat: + raise IOError('incorrect DIFAT') + isect_difat = self.sectDifStart + for i in iterrange(nb_difat): + debug( "DIFAT block %d, sector %X" % (i, isect_difat) ) + #TODO: check if corresponding FAT SID = DIFSECT + sector_difat = self.getsect(isect_difat) + difat = self.sect2array(sector_difat) + self.dumpsect(sector_difat) + self.loadfat_sect(difat[:127]) + # last DIFAT pointer is next DIFAT sector: + isect_difat = difat[127] + debug( "next DIFAT sector: %X" % isect_difat ) + # checks: + if isect_difat not in [ENDOFCHAIN, FREESECT]: + # last DIFAT pointer value must be ENDOFCHAIN or FREESECT + raise IOError('incorrect end of DIFAT') +## if len(self.fat) != self.csectFat: +## # FAT should contain csectFat blocks +## print("FAT length: %d instead of %d" % (len(self.fat), self.csectFat)) +## raise IOError('incorrect DIFAT') + # since FAT is read from fixed-size sectors, it may contain more values + # than the actual number of sectors in the file. + # Keep only the relevant sector indexes: + if len(self.fat) > self.nb_sect: + debug('len(fat)=%d, shrunk to nb_sect=%d' % (len(self.fat), self.nb_sect)) + self.fat = self.fat[:self.nb_sect] + debug('\nFAT:') + self.dumpfat(self.fat) + + + def loadminifat(self): + """ + Load the MiniFAT table. + """ + # MiniFAT is stored in a standard sub-stream, pointed to by a header + # field. + # NOTE: there are two sizes to take into account for this stream: + # 1) Stream size is calculated according to the number of sectors + # declared in the OLE header. This allocated stream may be more than + # needed to store the actual sector indexes. + # (self.csectMiniFat is the number of sectors of size self.SectorSize) + stream_size = self.csectMiniFat * self.SectorSize + # 2) Actually used size is calculated by dividing the MiniStream size + # (given by root entry size) by the size of mini sectors, *4 for + # 32 bits indexes: + nb_minisectors = (self.root.size + self.MiniSectorSize-1) // self.MiniSectorSize + used_size = nb_minisectors * 4 + debug('loadminifat(): minifatsect=%d, nb FAT sectors=%d, used_size=%d, stream_size=%d, nb MiniSectors=%d' % + (self.minifatsect, self.csectMiniFat, used_size, stream_size, nb_minisectors)) + if used_size > stream_size: + # This is not really a problem, but may indicate a wrong implementation: + self._raise_defect(DEFECT_INCORRECT, 'OLE MiniStream is larger than MiniFAT') + # In any case, first read stream_size: + s = self._open(self.minifatsect, stream_size, force_FAT=True).read() + #[PL] Old code replaced by an array: + #self.minifat = [i32(s, i) for i in range(0, len(s), 4)] + self.minifat = self.sect2array(s) + # Then shrink the array to used size, to avoid indexes out of MiniStream: + debug('MiniFAT shrunk from %d to %d sectors' % (len(self.minifat), nb_minisectors)) + self.minifat = self.minifat[:nb_minisectors] + debug('loadminifat(): len=%d' % len(self.minifat)) + debug('\nMiniFAT:') + self.dumpfat(self.minifat) + + def getsect(self, sect): + """ + Read given sector from file on disk. + + :param sect: sector index + :returns: a string containing the sector data. + """ + # [PL] this original code was wrong when sectors are 4KB instead of + # 512 bytes: + #self.fp.seek(512 + self.sectorsize * sect) + #[PL]: added safety checks: + #print("getsect(%X)" % sect) + try: + self.fp.seek(self.sectorsize * (sect+1)) + except: + debug('getsect(): sect=%X, seek=%d, filesize=%d' % + (sect, self.sectorsize*(sect+1), self._filesize)) + self._raise_defect(DEFECT_FATAL, 'OLE sector index out of range') + sector = self.fp.read(self.sectorsize) + if len(sector) != self.sectorsize: + debug('getsect(): sect=%X, read=%d, sectorsize=%d' % + (sect, len(sector), self.sectorsize)) + self._raise_defect(DEFECT_FATAL, 'incomplete OLE sector') + return sector + + + def loaddirectory(self, sect): + """ + Load the directory. + + :param sect: sector index of directory stream. + """ + # The directory is stored in a standard + # substream, independent of its size. + + # open directory stream as a read-only file: + # (stream size is not known in advance) + self.directory_fp = self._open(sect) + + #[PL] to detect malformed documents and avoid DoS attacks, the maximum + # number of directory entries can be calculated: + max_entries = self.directory_fp.size // 128 + debug('loaddirectory: size=%d, max_entries=%d' % + (self.directory_fp.size, max_entries)) + + # Create list of directory entries + #self.direntries = [] + # We start with a list of "None" object + self.direntries = [None] * max_entries +## for sid in iterrange(max_entries): +## entry = fp.read(128) +## if not entry: +## break +## self.direntries.append(_OleDirectoryEntry(entry, sid, self)) + # load root entry: + root_entry = self._load_direntry(0) + # Root entry is the first entry: + self.root = self.direntries[0] + # read and build all storage trees, starting from the root: + self.root.build_storage_tree() + + + def _load_direntry (self, sid): + """ + Load a directory entry from the directory. + This method should only be called once for each storage/stream when + loading the directory. + + :param sid: index of storage/stream in the directory. + :returns: a _OleDirectoryEntry object + :exception IOError: if the entry has always been referenced. + """ + # check if SID is OK: + if sid<0 or sid>=len(self.direntries): + self._raise_defect(DEFECT_FATAL, "OLE directory index out of range") + # check if entry was already referenced: + if self.direntries[sid] is not None: + self._raise_defect(DEFECT_INCORRECT, + "double reference for OLE stream/storage") + # if exception not raised, return the object + return self.direntries[sid] + self.directory_fp.seek(sid * 128) + entry = self.directory_fp.read(128) + self.direntries[sid] = _OleDirectoryEntry(entry, sid, self) + return self.direntries[sid] + + + def dumpdirectory(self): + """ + Dump directory (for debugging only) + """ + self.root.dump() + + + def _open(self, start, size = 0x7FFFFFFF, force_FAT=False): + """ + Open a stream, either in FAT or MiniFAT according to its size. + (openstream helper) + + :param start: index of first sector + :param size: size of stream (or nothing if size is unknown) + :param force_FAT: if False (default), stream will be opened in FAT or MiniFAT + according to size. If True, it will always be opened in FAT. + """ + debug('OleFileIO.open(): sect=%d, size=%d, force_FAT=%s' % + (start, size, str(force_FAT))) + # stream size is compared to the MiniSectorCutoff threshold: + if size < self.minisectorcutoff and not force_FAT: + # ministream object + if not self.ministream: + # load MiniFAT if it wasn't already done: + self.loadminifat() + # The first sector index of the miniFAT stream is stored in the + # root directory entry: + size_ministream = self.root.size + debug('Opening MiniStream: sect=%d, size=%d' % + (self.root.isectStart, size_ministream)) + self.ministream = self._open(self.root.isectStart, + size_ministream, force_FAT=True) + return _OleStream(self.ministream, start, size, 0, + self.minisectorsize, self.minifat, + self.ministream.size) + else: + # standard stream + return _OleStream(self.fp, start, size, 512, + self.sectorsize, self.fat, self._filesize) + + + def _list(self, files, prefix, node, streams=True, storages=False): + """ + (listdir helper) + :param files: list of files to fill in + :param prefix: current location in storage tree (list of names) + :param node: current node (_OleDirectoryEntry object) + :param streams: bool, include streams if True (True by default) - new in v0.26 + :param storages: bool, include storages if True (False by default) - new in v0.26 + (note: the root storage is never included) + """ + prefix = prefix + [node.name] + for entry in node.kids: + if entry.kids: + # this is a storage + if storages: + # add it to the list + files.append(prefix[1:] + [entry.name]) + # check its kids + self._list(files, prefix, entry, streams, storages) + else: + # this is a stream + if streams: + # add it to the list + files.append(prefix[1:] + [entry.name]) + + + def listdir(self, streams=True, storages=False): + """ + Return a list of streams stored in this file + + :param streams: bool, include streams if True (True by default) - new in v0.26 + :param storages: bool, include storages if True (False by default) - new in v0.26 + (note: the root storage is never included) + """ + files = [] + self._list(files, [], self.root, streams, storages) + return files + + + def _find(self, filename): + """ + Returns directory entry of given filename. (openstream helper) + Note: this method is case-insensitive. + + :param filename: path of stream in storage tree (except root entry), either: + + - a string using Unix path syntax, for example: + 'storage_1/storage_1.2/stream' + - a list of storage filenames, path to the desired stream/storage. + Example: ['storage_1', 'storage_1.2', 'stream'] + :returns: sid of requested filename + raise IOError if file not found + """ + + # if filename is a string instead of a list, split it on slashes to + # convert to a list: + if isinstance(filename, basestring): + filename = filename.split('/') + # walk across storage tree, following given path: + node = self.root + for name in filename: + for kid in node.kids: + if kid.name.lower() == name.lower(): + break + else: + raise IOError("file not found") + node = kid + return node.sid + + + def openstream(self, filename): + """ + Open a stream as a read-only file object (BytesIO). + + :param filename: path of stream in storage tree (except root entry), either: + + - a string using Unix path syntax, for example: + 'storage_1/storage_1.2/stream' + - a list of storage filenames, path to the desired stream/storage. + Example: ['storage_1', 'storage_1.2', 'stream'] + + :returns: file object (read-only) + :exception IOError: if filename not found, or if this is not a stream. + """ + sid = self._find(filename) + entry = self.direntries[sid] + if entry.entry_type != STGTY_STREAM: + raise IOError("this file is not a stream") + return self._open(entry.isectStart, entry.size) + + + def get_type(self, filename): + """ + Test if given filename exists as a stream or a storage in the OLE + container, and return its type. + + :param filename: path of stream in storage tree. (see openstream for syntax) + :returns: False if object does not exist, its entry type (>0) otherwise: + + - STGTY_STREAM: a stream + - STGTY_STORAGE: a storage + - STGTY_ROOT: the root entry + """ + try: + sid = self._find(filename) + entry = self.direntries[sid] + return entry.entry_type + except: + return False + + + def getmtime(self, filename): + """ + Return modification time of a stream/storage. + + :param filename: path of stream/storage in storage tree. (see openstream for + syntax) + :returns: None if modification time is null, a python datetime object + otherwise (UTC timezone) + + new in version 0.26 + """ + sid = self._find(filename) + entry = self.direntries[sid] + return entry.getmtime() + + + def getctime(self, filename): + """ + Return creation time of a stream/storage. + + :param filename: path of stream/storage in storage tree. (see openstream for + syntax) + :returns: None if creation time is null, a python datetime object + otherwise (UTC timezone) + + new in version 0.26 + """ + sid = self._find(filename) + entry = self.direntries[sid] + return entry.getctime() + + + def exists(self, filename): + """ + Test if given filename exists as a stream or a storage in the OLE + container. + + :param filename: path of stream in storage tree. (see openstream for syntax) + :returns: True if object exist, else False. + """ + try: + sid = self._find(filename) + return True + except: + return False + + + def get_size(self, filename): + """ + Return size of a stream in the OLE container, in bytes. + + :param filename: path of stream in storage tree (see openstream for syntax) + :returns: size in bytes (long integer) + :exception IOError: if file not found + :exception TypeError: if this is not a stream + """ + sid = self._find(filename) + entry = self.direntries[sid] + if entry.entry_type != STGTY_STREAM: + #TODO: Should it return zero instead of raising an exception ? + raise TypeError('object is not an OLE stream') + return entry.size + + + def get_rootentry_name(self): + """ + Return root entry name. Should usually be 'Root Entry' or 'R' in most + implementations. + """ + return self.root.name + + + def getproperties(self, filename, convert_time=False, no_conversion=None): + """ + Return properties described in substream. + + :param filename: path of stream in storage tree (see openstream for syntax) + :param convert_time: bool, if True timestamps will be converted to Python datetime + :param no_conversion: None or list of int, timestamps not to be converted + (for example total editing time is not a real timestamp) + :returns: a dictionary of values indexed by id (integer) + """ + # make sure no_conversion is a list, just to simplify code below: + if no_conversion == None: + no_conversion = [] + # stream path as a string to report exceptions: + streampath = filename + if not isinstance(streampath, str): + streampath = '/'.join(streampath) + + fp = self.openstream(filename) + + data = {} + + try: + # header + s = fp.read(28) + clsid = _clsid(s[8:24]) + + # format id + s = fp.read(20) + fmtid = _clsid(s[:16]) + fp.seek(i32(s, 16)) + + # get section + s = b"****" + fp.read(i32(fp.read(4))-4) + # number of properties: + num_props = i32(s, 4) + except BaseException as exc: + # catch exception while parsing property header, and only raise + # a DEFECT_INCORRECT then return an empty dict, because this is not + # a fatal error when parsing the whole file + msg = 'Error while parsing properties header in stream %s: %s' % ( + repr(streampath), exc) + self._raise_defect(DEFECT_INCORRECT, msg, type(exc)) + return data + + for i in range(num_props): + try: + id = 0 # just in case of an exception + id = i32(s, 8+i*8) + offset = i32(s, 12+i*8) + type = i32(s, offset) + + debug ('property id=%d: type=%d offset=%X' % (id, type, offset)) + + # test for common types first (should perhaps use + # a dictionary instead?) + + if type == VT_I2: # 16-bit signed integer + value = i16(s, offset+4) + if value >= 32768: + value = value - 65536 + elif type == VT_UI2: # 2-byte unsigned integer + value = i16(s, offset+4) + elif type in (VT_I4, VT_INT, VT_ERROR): + # VT_I4: 32-bit signed integer + # VT_ERROR: HRESULT, similar to 32-bit signed integer, + # see http://msdn.microsoft.com/en-us/library/cc230330.aspx + value = i32(s, offset+4) + elif type in (VT_UI4, VT_UINT): # 4-byte unsigned integer + value = i32(s, offset+4) # FIXME + elif type in (VT_BSTR, VT_LPSTR): + # CodePageString, see http://msdn.microsoft.com/en-us/library/dd942354.aspx + # size is a 32 bits integer, including the null terminator, and + # possibly trailing or embedded null chars + #TODO: if codepage is unicode, the string should be converted as such + count = i32(s, offset+4) + value = s[offset+8:offset+8+count-1] + # remove all null chars: + value = value.replace(b'\x00', b'') + elif type == VT_BLOB: + # binary large object (BLOB) + # see http://msdn.microsoft.com/en-us/library/dd942282.aspx + count = i32(s, offset+4) + value = s[offset+8:offset+8+count] + elif type == VT_LPWSTR: + # UnicodeString + # see http://msdn.microsoft.com/en-us/library/dd942313.aspx + # "the string should NOT contain embedded or additional trailing + # null characters." + count = i32(s, offset+4) + value = _unicode(s[offset+8:offset+8+count*2]) + elif type == VT_FILETIME: + value = long(i32(s, offset+4)) + (long(i32(s, offset+8))<<32) + # FILETIME is a 64-bit int: "number of 100ns periods + # since Jan 1,1601". + if convert_time and id not in no_conversion: + debug('Converting property #%d to python datetime, value=%d=%fs' + %(id, value, float(value)/10000000)) + # convert FILETIME to Python datetime.datetime + # inspired from http://code.activestate.com/recipes/511425-filetime-to-datetime/ + _FILETIME_null_date = datetime.datetime(1601, 1, 1, 0, 0, 0) + debug('timedelta days=%d' % (value//(10*1000000*3600*24))) + value = _FILETIME_null_date + datetime.timedelta(microseconds=value//10) + else: + # legacy code kept for backward compatibility: returns a + # number of seconds since Jan 1,1601 + value = value // 10000000 # seconds + elif type == VT_UI1: # 1-byte unsigned integer + value = i8(s[offset+4]) + elif type == VT_CLSID: + value = _clsid(s[offset+4:offset+20]) + elif type == VT_CF: + # PropertyIdentifier or ClipboardData?? + # see http://msdn.microsoft.com/en-us/library/dd941945.aspx + count = i32(s, offset+4) + value = s[offset+8:offset+8+count] + elif type == VT_BOOL: + # VARIANT_BOOL, 16 bits bool, 0x0000=Fals, 0xFFFF=True + # see http://msdn.microsoft.com/en-us/library/cc237864.aspx + value = bool(i16(s, offset+4)) + else: + value = None # everything else yields "None" + debug ('property id=%d: type=%d not implemented in parser yet' % (id, type)) + + # missing: VT_EMPTY, VT_NULL, VT_R4, VT_R8, VT_CY, VT_DATE, + # VT_DECIMAL, VT_I1, VT_I8, VT_UI8, + # see http://msdn.microsoft.com/en-us/library/dd942033.aspx + + # FIXME: add support for VT_VECTOR + # VT_VECTOR is a 32 uint giving the number of items, followed by + # the items in sequence. The VT_VECTOR value is combined with the + # type of items, e.g. VT_VECTOR|VT_BSTR + # see http://msdn.microsoft.com/en-us/library/dd942011.aspx + + #print("%08x" % id, repr(value), end=" ") + #print("(%s)" % VT[i32(s, offset) & 0xFFF]) + + data[id] = value + except BaseException as exc: + # catch exception while parsing each property, and only raise + # a DEFECT_INCORRECT, because parsing can go on + msg = 'Error while parsing property id %d in stream %s: %s' % ( + id, repr(streampath), exc) + self._raise_defect(DEFECT_INCORRECT, msg, type(exc)) + + return data + + def get_metadata(self): + """ + Parse standard properties streams, return an OleMetadata object + containing all the available metadata. + (also stored in the metadata attribute of the OleFileIO object) + + new in version 0.25 + """ + self.metadata = OleMetadata() + self.metadata.parse_properties(self) + return self.metadata + +# +# -------------------------------------------------------------------- +# This script can be used to dump the directory of any OLE2 structured +# storage file. + +if __name__ == "__main__": + + import sys + + # [PL] display quick usage info if launched from command-line + if len(sys.argv) <= 1: + print(__doc__) + print(""" +Launched from command line, this script parses OLE files and prints info. + +Usage: OleFileIO_PL.py [-d] [-c] [file2 ...] + +Options: +-d : debug mode (display a lot of debug information, for developers only) +-c : check all streams (for debugging purposes) +""") + sys.exit() + + check_streams = False + for filename in sys.argv[1:]: +## try: + # OPTIONS: + if filename == '-d': + # option to switch debug mode on: + set_debug_mode(True) + continue + if filename == '-c': + # option to switch check streams mode on: + check_streams = True + continue + + ole = OleFileIO(filename)#, raise_defects=DEFECT_INCORRECT) + print("-" * 68) + print(filename) + print("-" * 68) + ole.dumpdirectory() + for streamname in ole.listdir(): + if streamname[-1][0] == "\005": + print(streamname, ": properties") + props = ole.getproperties(streamname, convert_time=True) + props = sorted(props.items()) + for k, v in props: + #[PL]: avoid to display too large or binary values: + if isinstance(v, (basestring, bytes)): + if len(v) > 50: + v = v[:50] + if isinstance(v, bytes): + # quick and dirty binary check: + for c in (1,2,3,4,5,6,7,11,12,14,15,16,17,18,19,20, + 21,22,23,24,25,26,27,28,29,30,31): + if c in bytearray(v): + v = '(binary data)' + break + print(" ", k, v) + + if check_streams: + # Read all streams to check if there are errors: + print('\nChecking streams...') + for streamname in ole.listdir(): + # print name using repr() to convert binary chars to \xNN: + print('-', repr('/'.join(streamname)),'-', end=' ') + st_type = ole.get_type(streamname) + if st_type == STGTY_STREAM: + print('size %d' % ole.get_size(streamname)) + # just try to read stream in memory: + ole.openstream(streamname) + else: + print('NOT a stream : type=%d' % st_type) + print() + +## for streamname in ole.listdir(): +## # print name using repr() to convert binary chars to \xNN: +## print('-', repr('/'.join(streamname)),'-', end=' ') +## print(ole.getmtime(streamname)) +## print() + + print('Modification/Creation times of all directory entries:') + for entry in ole.direntries: + if entry is not None: + print('- %s: mtime=%s ctime=%s' % (entry.name, + entry.getmtime(), entry.getctime())) + print() + + # parse and display metadata: + meta = ole.get_metadata() + meta.dump() + print() + #[PL] Test a few new methods: + root = ole.get_rootentry_name() + print('Root entry name: "%s"' % root) + if ole.exists('worddocument'): + print("This is a Word document.") + print("type of stream 'WordDocument':", ole.get_type('worddocument')) + print("size :", ole.get_size('worddocument')) + if ole.exists('macros/vba'): + print("This document may contain VBA macros.") + + # print parsing issues: + print('\nNon-fatal issues raised during parsing:') + if ole.parsing_issues: + for exctype, msg in ole.parsing_issues: + print('- %s: %s' % (exctype.__name__, msg)) + else: + print('None') +## except IOError as v: +## print("***", "cannot read", file, "-", v) diff --git a/pyPackages/pillowarmv7l/PIL/PSDraw.py b/pyPackages/pillowarmv7l/PIL/PSDraw.py new file mode 100644 index 0000000..6187e40 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/PSDraw.py @@ -0,0 +1,237 @@ +# +# The Python Imaging Library +# $Id$ +# +# simple postscript graphics interface +# +# History: +# 1996-04-20 fl Created +# 1999-01-10 fl Added gsave/grestore to image method +# 2005-05-04 fl Fixed floating point issue in image (from Eric Etheridge) +# +# Copyright (c) 1997-2005 by Secret Labs AB. All rights reserved. +# Copyright (c) 1996 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +from __future__ import print_function + +from PIL import EpsImagePlugin + + +## +# Simple Postscript graphics interface. + +class PSDraw: + """ + Sets up printing to the given file. If **file** is omitted, + :py:attr:`sys.stdout` is assumed. + """ + + def __init__(self, fp=None): + if not fp: + import sys + fp = sys.stdout + self.fp = fp + + def _fp_write(self, to_write): + if bytes is str: + self.fp.write(to_write) + else: + self.fp.write(bytes(to_write, 'UTF-8')) + + def begin_document(self, id=None): + """Set up printing of a document. (Write Postscript DSC header.)""" + # FIXME: incomplete + self._fp_write("%!PS-Adobe-3.0\n" + "save\n" + "/showpage { } def\n" + "%%EndComments\n" + "%%BeginDocument\n") + # self.fp_write(ERROR_PS) # debugging! + self._fp_write(EDROFF_PS) + self._fp_write(VDI_PS) + self._fp_write("%%EndProlog\n") + self.isofont = {} + + def end_document(self): + """Ends printing. (Write Postscript DSC footer.)""" + self._fp_write("%%EndDocument\n" + "restore showpage\n" + "%%End\n") + if hasattr(self.fp, "flush"): + self.fp.flush() + + def setfont(self, font, size): + """ + Selects which font to use. + + :param font: A Postscript font name + :param size: Size in points. + """ + if font not in self.isofont: + # reencode font + self._fp_write("/PSDraw-%s ISOLatin1Encoding /%s E\n" % + (font, font)) + self.isofont[font] = 1 + # rough + self._fp_write("/F0 %d /PSDraw-%s F\n" % (size, font)) + + def line(self, xy0, xy1): + """ + Draws a line between the two points. Coordinates are given in + Postscript point coordinates (72 points per inch, (0, 0) is the lower + left corner of the page). + """ + xy = xy0 + xy1 + self._fp_write("%d %d %d %d Vl\n" % xy) + + def rectangle(self, box): + """ + Draws a rectangle. + + :param box: A 4-tuple of integers whose order and function is currently + undocumented. + + Hint: the tuple is passed into this format string: + + .. code-block:: python + + %d %d M %d %d 0 Vr\n + """ + self._fp_write("%d %d M %d %d 0 Vr\n" % box) + + def text(self, xy, text): + """ + Draws text at the given position. You must use + :py:meth:`~PIL.PSDraw.PSDraw.setfont` before calling this method. + """ + text = "\\(".join(text.split("(")) + text = "\\)".join(text.split(")")) + xy = xy + (text,) + self._fp_write("%d %d M (%s) S\n" % xy) + + def image(self, box, im, dpi=None): + """Draw a PIL image, centered in the given box.""" + # default resolution depends on mode + if not dpi: + if im.mode == "1": + dpi = 200 # fax + else: + dpi = 100 # greyscale + # image size (on paper) + x = float(im.size[0] * 72) / dpi + y = float(im.size[1] * 72) / dpi + # max allowed size + xmax = float(box[2] - box[0]) + ymax = float(box[3] - box[1]) + if x > xmax: + y = y * xmax / x + x = xmax + if y > ymax: + x = x * ymax / y + y = ymax + dx = (xmax - x) / 2 + box[0] + dy = (ymax - y) / 2 + box[1] + self._fp_write("gsave\n%f %f translate\n" % (dx, dy)) + if (x, y) != im.size: + # EpsImagePlugin._save prints the image at (0,0,xsize,ysize) + sx = x / im.size[0] + sy = y / im.size[1] + self._fp_write("%f %f scale\n" % (sx, sy)) + EpsImagePlugin._save(im, self.fp, None, 0) + self._fp_write("\ngrestore\n") + +# -------------------------------------------------------------------- +# Postscript driver + +# +# EDROFF.PS -- Postscript driver for Edroff 2 +# +# History: +# 94-01-25 fl: created (edroff 2.04) +# +# Copyright (c) Fredrik Lundh 1994. +# + +EDROFF_PS = """\ +/S { show } bind def +/P { moveto show } bind def +/M { moveto } bind def +/X { 0 rmoveto } bind def +/Y { 0 exch rmoveto } bind def +/E { findfont + dup maxlength dict begin + { + 1 index /FID ne { def } { pop pop } ifelse + } forall + /Encoding exch def + dup /FontName exch def + currentdict end definefont pop +} bind def +/F { findfont exch scalefont dup setfont + [ exch /setfont cvx ] cvx bind def +} bind def +""" + +# +# VDI.PS -- Postscript driver for VDI meta commands +# +# History: +# 94-01-25 fl: created (edroff 2.04) +# +# Copyright (c) Fredrik Lundh 1994. +# + +VDI_PS = """\ +/Vm { moveto } bind def +/Va { newpath arcn stroke } bind def +/Vl { moveto lineto stroke } bind def +/Vc { newpath 0 360 arc closepath } bind def +/Vr { exch dup 0 rlineto + exch dup neg 0 exch rlineto + exch neg 0 rlineto + 0 exch rlineto + 100 div setgray fill 0 setgray } bind def +/Tm matrix def +/Ve { Tm currentmatrix pop + translate scale newpath 0 0 .5 0 360 arc closepath + Tm setmatrix +} bind def +/Vf { currentgray exch setgray fill setgray } bind def +""" + +# +# ERROR.PS -- Error handler +# +# History: +# 89-11-21 fl: created (pslist 1.10) +# + +ERROR_PS = """\ +/landscape false def +/errorBUF 200 string def +/errorNL { currentpoint 10 sub exch pop 72 exch moveto } def +errordict begin /handleerror { + initmatrix /Courier findfont 10 scalefont setfont + newpath 72 720 moveto $error begin /newerror false def + (PostScript Error) show errorNL errorNL + (Error: ) show + /errorname load errorBUF cvs show errorNL errorNL + (Command: ) show + /command load dup type /stringtype ne { errorBUF cvs } if show + errorNL errorNL + (VMstatus: ) show + vmstatus errorBUF cvs show ( bytes available, ) show + errorBUF cvs show ( bytes used at level ) show + errorBUF cvs show errorNL errorNL + (Operand stargck: ) show errorNL /ostargck load { + dup type /stringtype ne { errorBUF cvs } if 72 0 rmoveto show errorNL + } forall errorNL + (Execution stargck: ) show errorNL /estargck load { + dup type /stringtype ne { errorBUF cvs } if 72 0 rmoveto show errorNL + } forall + end showpage +} def end +""" diff --git a/pyPackages/pillowarmv7l/PIL/PaletteFile.py b/pyPackages/pillowarmv7l/PIL/PaletteFile.py new file mode 100644 index 0000000..37ba4cb --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/PaletteFile.py @@ -0,0 +1,55 @@ +# +# Python Imaging Library +# $Id$ +# +# stuff to read simple, teragon-style palette files +# +# History: +# 97-08-23 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + +from PIL._binary import o8 + + +## +# File handler for Teragon-style palette files. + +class PaletteFile: + + rawmode = "RGB" + + def __init__(self, fp): + + self.palette = [(i, i, i) for i in range(256)] + + while True: + + s = fp.readline() + + if not s: + break + if s[0:1] == b"#": + continue + if len(s) > 100: + raise SyntaxError("bad palette file") + + v = [int(x) for x in s.split()] + try: + [i, r, g, b] = v + except ValueError: + [i, r] = v + g = b = r + + if 0 <= i <= 255: + self.palette[i] = o8(r) + o8(g) + o8(b) + + self.palette = b"".join(self.palette) + + def getpalette(self): + + return self.palette, self.rawmode diff --git a/pyPackages/pillowarmv7l/PIL/PalmImagePlugin.py b/pyPackages/pillowarmv7l/PIL/PalmImagePlugin.py new file mode 100644 index 0000000..bba1de8 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/PalmImagePlugin.py @@ -0,0 +1,240 @@ +# +# The Python Imaging Library. +# $Id$ +# + +## +# Image plugin for Palm pixmap images (output only). +## + +__version__ = "1.0" + +from PIL import Image, ImageFile, _binary + +_Palm8BitColormapValues = ( + (255, 255, 255), (255, 204, 255), (255, 153, 255), (255, 102, 255), + (255, 51, 255), (255, 0, 255), (255, 255, 204), (255, 204, 204), + (255, 153, 204), (255, 102, 204), (255, 51, 204), (255, 0, 204), + (255, 255, 153), (255, 204, 153), (255, 153, 153), (255, 102, 153), + (255, 51, 153), (255, 0, 153), (204, 255, 255), (204, 204, 255), + (204, 153, 255), (204, 102, 255), (204, 51, 255), (204, 0, 255), + (204, 255, 204), (204, 204, 204), (204, 153, 204), (204, 102, 204), + (204, 51, 204), (204, 0, 204), (204, 255, 153), (204, 204, 153), + (204, 153, 153), (204, 102, 153), (204, 51, 153), (204, 0, 153), + (153, 255, 255), (153, 204, 255), (153, 153, 255), (153, 102, 255), + (153, 51, 255), (153, 0, 255), (153, 255, 204), (153, 204, 204), + (153, 153, 204), (153, 102, 204), (153, 51, 204), (153, 0, 204), + (153, 255, 153), (153, 204, 153), (153, 153, 153), (153, 102, 153), + (153, 51, 153), (153, 0, 153), (102, 255, 255), (102, 204, 255), + (102, 153, 255), (102, 102, 255), (102, 51, 255), (102, 0, 255), + (102, 255, 204), (102, 204, 204), (102, 153, 204), (102, 102, 204), + (102, 51, 204), (102, 0, 204), (102, 255, 153), (102, 204, 153), + (102, 153, 153), (102, 102, 153), (102, 51, 153), (102, 0, 153), + ( 51, 255, 255), ( 51, 204, 255), ( 51, 153, 255), ( 51, 102, 255), + ( 51, 51, 255), ( 51, 0, 255), ( 51, 255, 204), ( 51, 204, 204), + ( 51, 153, 204), ( 51, 102, 204), ( 51, 51, 204), ( 51, 0, 204), + ( 51, 255, 153), ( 51, 204, 153), ( 51, 153, 153), ( 51, 102, 153), + ( 51, 51, 153), ( 51, 0, 153), ( 0, 255, 255), ( 0, 204, 255), + ( 0, 153, 255), ( 0, 102, 255), ( 0, 51, 255), ( 0, 0, 255), + ( 0, 255, 204), ( 0, 204, 204), ( 0, 153, 204), ( 0, 102, 204), + ( 0, 51, 204), ( 0, 0, 204), ( 0, 255, 153), ( 0, 204, 153), + ( 0, 153, 153), ( 0, 102, 153), ( 0, 51, 153), ( 0, 0, 153), + (255, 255, 102), (255, 204, 102), (255, 153, 102), (255, 102, 102), + (255, 51, 102), (255, 0, 102), (255, 255, 51), (255, 204, 51), + (255, 153, 51), (255, 102, 51), (255, 51, 51), (255, 0, 51), + (255, 255, 0), (255, 204, 0), (255, 153, 0), (255, 102, 0), + (255, 51, 0), (255, 0, 0), (204, 255, 102), (204, 204, 102), + (204, 153, 102), (204, 102, 102), (204, 51, 102), (204, 0, 102), + (204, 255, 51), (204, 204, 51), (204, 153, 51), (204, 102, 51), + (204, 51, 51), (204, 0, 51), (204, 255, 0), (204, 204, 0), + (204, 153, 0), (204, 102, 0), (204, 51, 0), (204, 0, 0), + (153, 255, 102), (153, 204, 102), (153, 153, 102), (153, 102, 102), + (153, 51, 102), (153, 0, 102), (153, 255, 51), (153, 204, 51), + (153, 153, 51), (153, 102, 51), (153, 51, 51), (153, 0, 51), + (153, 255, 0), (153, 204, 0), (153, 153, 0), (153, 102, 0), + (153, 51, 0), (153, 0, 0), (102, 255, 102), (102, 204, 102), + (102, 153, 102), (102, 102, 102), (102, 51, 102), (102, 0, 102), + (102, 255, 51), (102, 204, 51), (102, 153, 51), (102, 102, 51), + (102, 51, 51), (102, 0, 51), (102, 255, 0), (102, 204, 0), + (102, 153, 0), (102, 102, 0), (102, 51, 0), (102, 0, 0), + ( 51, 255, 102), ( 51, 204, 102), ( 51, 153, 102), ( 51, 102, 102), + ( 51, 51, 102), ( 51, 0, 102), ( 51, 255, 51), ( 51, 204, 51), + ( 51, 153, 51), ( 51, 102, 51), ( 51, 51, 51), ( 51, 0, 51), + ( 51, 255, 0), ( 51, 204, 0), ( 51, 153, 0), ( 51, 102, 0), + ( 51, 51, 0), ( 51, 0, 0), ( 0, 255, 102), ( 0, 204, 102), + ( 0, 153, 102), ( 0, 102, 102), ( 0, 51, 102), ( 0, 0, 102), + ( 0, 255, 51), ( 0, 204, 51), ( 0, 153, 51), ( 0, 102, 51), + ( 0, 51, 51), ( 0, 0, 51), ( 0, 255, 0), ( 0, 204, 0), + ( 0, 153, 0), ( 0, 102, 0), ( 0, 51, 0), ( 17, 17, 17), + ( 34, 34, 34), ( 68, 68, 68), ( 85, 85, 85), (119, 119, 119), + (136, 136, 136), (170, 170, 170), (187, 187, 187), (221, 221, 221), + (238, 238, 238), (192, 192, 192), (128, 0, 0), (128, 0, 128), + ( 0, 128, 0), ( 0, 128, 128), ( 0, 0, 0), ( 0, 0, 0), + ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), + ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), + ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), + ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), + ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), + ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0)) + + +# so build a prototype image to be used for palette resampling +def build_prototype_image(): + image = Image.new("L", (1, len(_Palm8BitColormapValues),)) + image.putdata(list(range(len(_Palm8BitColormapValues)))) + palettedata = () + for i in range(len(_Palm8BitColormapValues)): + palettedata = palettedata + _Palm8BitColormapValues[i] + for i in range(256 - len(_Palm8BitColormapValues)): + palettedata = palettedata + (0, 0, 0) + image.putpalette(palettedata) + return image + +Palm8BitColormapImage = build_prototype_image() + +# OK, we now have in Palm8BitColormapImage, +# a "P"-mode image with the right palette +# +# -------------------------------------------------------------------- + +_FLAGS = { + "custom-colormap": 0x4000, + "is-compressed": 0x8000, + "has-transparent": 0x2000, + } + +_COMPRESSION_TYPES = { + "none": 0xFF, + "rle": 0x01, + "scanline": 0x00, + } + +o8 = _binary.o8 +o16b = _binary.o16be + + +# +# -------------------------------------------------------------------- + +## +# (Internal) Image save plugin for the Palm format. + +def _save(im, fp, filename, check=0): + + if im.mode == "P": + + # we assume this is a color Palm image with the standard colormap, + # unless the "info" dict has a "custom-colormap" field + + rawmode = "P" + bpp = 8 + version = 1 + + elif (im.mode == "L" and + "bpp" in im.encoderinfo and + im.encoderinfo["bpp"] in (1, 2, 4)): + + # this is 8-bit grayscale, so we shift it to get the high-order bits, + # and invert it because + # Palm does greyscale from white (0) to black (1) + bpp = im.encoderinfo["bpp"] + im = im.point( + lambda x, shift=8-bpp, maxval=(1 << bpp)-1: maxval - (x >> shift)) + # we ignore the palette here + im.mode = "P" + rawmode = "P;" + str(bpp) + version = 1 + + elif im.mode == "L" and "bpp" in im.info and im.info["bpp"] in (1, 2, 4): + + # here we assume that even though the inherent mode is 8-bit grayscale, + # only the lower bpp bits are significant. + # We invert them to match the Palm. + bpp = im.info["bpp"] + im = im.point(lambda x, maxval=(1 << bpp)-1: maxval - (x & maxval)) + # we ignore the palette here + im.mode = "P" + rawmode = "P;" + str(bpp) + version = 1 + + elif im.mode == "1": + + # monochrome -- write it inverted, as is the Palm standard + rawmode = "1;I" + bpp = 1 + version = 0 + + else: + + raise IOError("cannot write mode %s as Palm" % im.mode) + + if check: + return check + + # + # make sure image data is available + im.load() + + # write header + + cols = im.size[0] + rows = im.size[1] + + rowbytes = int((cols + (16//bpp - 1)) / (16 // bpp)) * 2 + transparent_index = 0 + compression_type = _COMPRESSION_TYPES["none"] + + flags = 0 + if im.mode == "P" and "custom-colormap" in im.info: + flags = flags & _FLAGS["custom-colormap"] + colormapsize = 4 * 256 + 2 + colormapmode = im.palette.mode + colormap = im.getdata().getpalette() + else: + colormapsize = 0 + + if "offset" in im.info: + offset = (rowbytes * rows + 16 + 3 + colormapsize) // 4 + else: + offset = 0 + + fp.write(o16b(cols) + o16b(rows) + o16b(rowbytes) + o16b(flags)) + fp.write(o8(bpp)) + fp.write(o8(version)) + fp.write(o16b(offset)) + fp.write(o8(transparent_index)) + fp.write(o8(compression_type)) + fp.write(o16b(0)) # reserved by Palm + + # now write colormap if necessary + + if colormapsize > 0: + fp.write(o16b(256)) + for i in range(256): + fp.write(o8(i)) + if colormapmode == 'RGB': + fp.write( + o8(colormap[3 * i]) + + o8(colormap[3 * i + 1]) + + o8(colormap[3 * i + 2])) + elif colormapmode == 'RGBA': + fp.write( + o8(colormap[4 * i]) + + o8(colormap[4 * i + 1]) + + o8(colormap[4 * i + 2])) + + # now convert data to raw form + ImageFile._save( + im, fp, [("raw", (0, 0)+im.size, 0, (rawmode, rowbytes, 1))]) + + fp.flush() + + +# +# -------------------------------------------------------------------- + +Image.register_save("Palm", _save) + +Image.register_extension("Palm", ".palm") + +Image.register_mime("Palm", "image/palm") diff --git a/pyPackages/pillowarmv7l/PIL/PcdImagePlugin.py b/pyPackages/pillowarmv7l/PIL/PcdImagePlugin.py new file mode 100644 index 0000000..5ce7aa4 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/PcdImagePlugin.py @@ -0,0 +1,79 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PCD file handling +# +# History: +# 96-05-10 fl Created +# 96-05-27 fl Added draft mode (128x192, 256x384) +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.1" + + +from PIL import Image, ImageFile, _binary + +i8 = _binary.i8 + + +## +# Image plugin for PhotoCD images. This plugin only reads the 768x512 +# image from the file; higher resolutions are encoded in a proprietary +# encoding. + +class PcdImageFile(ImageFile.ImageFile): + + format = "PCD" + format_description = "Kodak PhotoCD" + + def _open(self): + + # rough + self.fp.seek(2048) + s = self.fp.read(2048) + + if s[:4] != b"PCD_": + raise SyntaxError("not a PCD file") + + orientation = i8(s[1538]) & 3 + if orientation == 1: + self.tile_post_rotate = 90 # hack + elif orientation == 3: + self.tile_post_rotate = -90 + + self.mode = "RGB" + self.size = 768, 512 # FIXME: not correct for rotated images! + self.tile = [("pcd", (0, 0)+self.size, 96*2048, None)] + + def draft(self, mode, size): + + if len(self.tile) != 1: + return + + d, e, o, a = self.tile[0] + + if size: + scale = max(self.size[0] / size[0], self.size[1] / size[1]) + for s, o in [(4, 0*2048), (2, 0*2048), (1, 96*2048)]: + if scale >= s: + break + # e = e[0], e[1], (e[2]-e[0]+s-1)/s+e[0], (e[3]-e[1]+s-1)/s+e[1] + # self.size = ((self.size[0]+s-1)/s, (self.size[1]+s-1)/s) + + self.tile = [(d, e, o, a)] + + return self + +# +# registry + +Image.register_open("PCD", PcdImageFile) + +Image.register_extension("PCD", ".pcd") diff --git a/pyPackages/pillowarmv7l/PIL/PcfFontFile.py b/pyPackages/pillowarmv7l/PIL/PcfFontFile.py new file mode 100644 index 0000000..c19a1c5 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/PcfFontFile.py @@ -0,0 +1,252 @@ +# +# THIS IS WORK IN PROGRESS +# +# The Python Imaging Library +# $Id$ +# +# portable compiled font file parser +# +# history: +# 1997-08-19 fl created +# 2003-09-13 fl fixed loading of unicode fonts +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1997-2003 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image +from PIL import FontFile +from PIL import _binary + +# -------------------------------------------------------------------- +# declarations + +PCF_MAGIC = 0x70636601 # "\x01fcp" + +PCF_PROPERTIES = (1 << 0) +PCF_ACCELERATORS = (1 << 1) +PCF_METRICS = (1 << 2) +PCF_BITMAPS = (1 << 3) +PCF_INK_METRICS = (1 << 4) +PCF_BDF_ENCODINGS = (1 << 5) +PCF_SWIDTHS = (1 << 6) +PCF_GLYPH_NAMES = (1 << 7) +PCF_BDF_ACCELERATORS = (1 << 8) + +BYTES_PER_ROW = [ + lambda bits: ((bits+7) >> 3), + lambda bits: ((bits+15) >> 3) & ~1, + lambda bits: ((bits+31) >> 3) & ~3, + lambda bits: ((bits+63) >> 3) & ~7, +] + +i8 = _binary.i8 +l16 = _binary.i16le +l32 = _binary.i32le +b16 = _binary.i16be +b32 = _binary.i32be + + +def sz(s, o): + return s[o:s.index(b"\0", o)] + + +## +# Font file plugin for the X11 PCF format. + +class PcfFontFile(FontFile.FontFile): + + name = "name" + + def __init__(self, fp): + + magic = l32(fp.read(4)) + if magic != PCF_MAGIC: + raise SyntaxError("not a PCF file") + + FontFile.FontFile.__init__(self) + + count = l32(fp.read(4)) + self.toc = {} + for i in range(count): + type = l32(fp.read(4)) + self.toc[type] = l32(fp.read(4)), l32(fp.read(4)), l32(fp.read(4)) + + self.fp = fp + + self.info = self._load_properties() + + metrics = self._load_metrics() + bitmaps = self._load_bitmaps(metrics) + encoding = self._load_encoding() + + # + # create glyph structure + + for ch in range(256): + ix = encoding[ch] + if ix is not None: + x, y, l, r, w, a, d, f = metrics[ix] + glyph = (w, 0), (l, d-y, x+l, d), (0, 0, x, y), bitmaps[ix] + self.glyph[ch] = glyph + + def _getformat(self, tag): + + format, size, offset = self.toc[tag] + + fp = self.fp + fp.seek(offset) + + format = l32(fp.read(4)) + + if format & 4: + i16, i32 = b16, b32 + else: + i16, i32 = l16, l32 + + return fp, format, i16, i32 + + def _load_properties(self): + + # + # font properties + + properties = {} + + fp, format, i16, i32 = self._getformat(PCF_PROPERTIES) + + nprops = i32(fp.read(4)) + + # read property description + p = [] + for i in range(nprops): + p.append((i32(fp.read(4)), i8(fp.read(1)), i32(fp.read(4)))) + if nprops & 3: + fp.seek(4 - (nprops & 3), 1) # pad + + data = fp.read(i32(fp.read(4))) + + for k, s, v in p: + k = sz(data, k) + if s: + v = sz(data, v) + properties[k] = v + + return properties + + def _load_metrics(self): + + # + # font metrics + + metrics = [] + + fp, format, i16, i32 = self._getformat(PCF_METRICS) + + append = metrics.append + + if (format & 0xff00) == 0x100: + + # "compressed" metrics + for i in range(i16(fp.read(2))): + left = i8(fp.read(1)) - 128 + right = i8(fp.read(1)) - 128 + width = i8(fp.read(1)) - 128 + ascent = i8(fp.read(1)) - 128 + descent = i8(fp.read(1)) - 128 + xsize = right - left + ysize = ascent + descent + append( + (xsize, ysize, left, right, width, + ascent, descent, 0) + ) + + else: + + # "jumbo" metrics + for i in range(i32(fp.read(4))): + left = i16(fp.read(2)) + right = i16(fp.read(2)) + width = i16(fp.read(2)) + ascent = i16(fp.read(2)) + descent = i16(fp.read(2)) + attributes = i16(fp.read(2)) + xsize = right - left + ysize = ascent + descent + append( + (xsize, ysize, left, right, width, + ascent, descent, attributes) + ) + + return metrics + + def _load_bitmaps(self, metrics): + + # + # bitmap data + + bitmaps = [] + + fp, format, i16, i32 = self._getformat(PCF_BITMAPS) + + nbitmaps = i32(fp.read(4)) + + if nbitmaps != len(metrics): + raise IOError("Wrong number of bitmaps") + + offsets = [] + for i in range(nbitmaps): + offsets.append(i32(fp.read(4))) + + bitmapSizes = [] + for i in range(4): + bitmapSizes.append(i32(fp.read(4))) + + byteorder = format & 4 # non-zero => MSB + bitorder = format & 8 # non-zero => MSB + padindex = format & 3 + + bitmapsize = bitmapSizes[padindex] + offsets.append(bitmapsize) + + data = fp.read(bitmapsize) + + pad = BYTES_PER_ROW[padindex] + mode = "1;R" + if bitorder: + mode = "1" + + for i in range(nbitmaps): + x, y, l, r, w, a, d, f = metrics[i] + b, e = offsets[i], offsets[i+1] + bitmaps.append( + Image.frombytes("1", (x, y), data[b:e], "raw", mode, pad(x)) + ) + + return bitmaps + + def _load_encoding(self): + + # map character code to bitmap index + encoding = [None] * 256 + + fp, format, i16, i32 = self._getformat(PCF_BDF_ENCODINGS) + + firstCol, lastCol = i16(fp.read(2)), i16(fp.read(2)) + firstRow, lastRow = i16(fp.read(2)), i16(fp.read(2)) + + default = i16(fp.read(2)) + + nencoding = (lastCol - firstCol + 1) * (lastRow - firstRow + 1) + + for i in range(nencoding): + encodingOffset = i16(fp.read(2)) + if encodingOffset != 0xFFFF: + try: + encoding[i+firstCol] = encodingOffset + except IndexError: + break # only load ISO-8859-1 glyphs + + return encoding diff --git a/pyPackages/pillowarmv7l/PIL/PcxImagePlugin.py b/pyPackages/pillowarmv7l/PIL/PcxImagePlugin.py new file mode 100644 index 0000000..0765f09 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/PcxImagePlugin.py @@ -0,0 +1,186 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PCX file handling +# +# This format was originally used by ZSoft's popular PaintBrush +# program for the IBM PC. It is also supported by many MS-DOS and +# Windows applications, including the Windows PaintBrush program in +# Windows 3. +# +# history: +# 1995-09-01 fl Created +# 1996-05-20 fl Fixed RGB support +# 1997-01-03 fl Fixed 2-bit and 4-bit support +# 1999-02-03 fl Fixed 8-bit support (broken in 1.0b1) +# 1999-02-07 fl Added write support +# 2002-06-09 fl Made 2-bit and 4-bit support a bit more robust +# 2002-07-30 fl Seek from to current position, not beginning of file +# 2003-06-03 fl Extract DPI settings (info["dpi"]) +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1995-2003 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.6" + +from PIL import Image, ImageFile, ImagePalette, _binary + +i8 = _binary.i8 +i16 = _binary.i16le +o8 = _binary.o8 + + +def _accept(prefix): + return i8(prefix[0]) == 10 and i8(prefix[1]) in [0, 2, 3, 5] + + +## +# Image plugin for Paintbrush images. + +class PcxImageFile(ImageFile.ImageFile): + + format = "PCX" + format_description = "Paintbrush" + + def _open(self): + + # header + s = self.fp.read(128) + if not _accept(s): + raise SyntaxError("not a PCX file") + + # image + bbox = i16(s, 4), i16(s, 6), i16(s, 8)+1, i16(s, 10)+1 + if bbox[2] <= bbox[0] or bbox[3] <= bbox[1]: + raise SyntaxError("bad PCX image size") + if Image.DEBUG: + print ("BBox: %s %s %s %s" % bbox) + + # format + version = i8(s[1]) + bits = i8(s[3]) + planes = i8(s[65]) + stride = i16(s, 66) + if Image.DEBUG: + print ("PCX version %s, bits %s, planes %s, stride %s" % + (version, bits, planes, stride)) + + self.info["dpi"] = i16(s, 12), i16(s, 14) + + if bits == 1 and planes == 1: + mode = rawmode = "1" + + elif bits == 1 and planes in (2, 4): + mode = "P" + rawmode = "P;%dL" % planes + self.palette = ImagePalette.raw("RGB", s[16:64]) + + elif version == 5 and bits == 8 and planes == 1: + mode = rawmode = "L" + # FIXME: hey, this doesn't work with the incremental loader !!! + self.fp.seek(-769, 2) + s = self.fp.read(769) + if len(s) == 769 and i8(s[0]) == 12: + # check if the palette is linear greyscale + for i in range(256): + if s[i*3+1:i*3+4] != o8(i)*3: + mode = rawmode = "P" + break + if mode == "P": + self.palette = ImagePalette.raw("RGB", s[1:]) + self.fp.seek(128) + + elif version == 5 and bits == 8 and planes == 3: + mode = "RGB" + rawmode = "RGB;L" + + else: + raise IOError("unknown PCX mode") + + self.mode = mode + self.size = bbox[2]-bbox[0], bbox[3]-bbox[1] + + bbox = (0, 0) + self.size + if Image.DEBUG: + print ("size: %sx%s" % self.size) + + self.tile = [("pcx", bbox, self.fp.tell(), (rawmode, planes * stride))] + +# -------------------------------------------------------------------- +# save PCX files + +SAVE = { + # mode: (version, bits, planes, raw mode) + "1": (2, 1, 1, "1"), + "L": (5, 8, 1, "L"), + "P": (5, 8, 1, "P"), + "RGB": (5, 8, 3, "RGB;L"), +} + +o16 = _binary.o16le + + +def _save(im, fp, filename, check=0): + + try: + version, bits, planes, rawmode = SAVE[im.mode] + except KeyError: + raise ValueError("Cannot save %s images as PCX" % im.mode) + + if check: + return check + + # bytes per plane + stride = (im.size[0] * bits + 7) // 8 + # stride should be even + stride += stride % 2 + # Stride needs to be kept in sync with the PcxEncode.c version. + # Ideally it should be passed in in the state, but the bytes value + # gets overwritten. + + if Image.DEBUG: + print ("PcxImagePlugin._save: xwidth: %d, bits: %d, stride: %d" % ( + im.size[0], bits, stride)) + + # under windows, we could determine the current screen size with + # "Image.core.display_mode()[1]", but I think that's overkill... + + screen = im.size + + dpi = 100, 100 + + # PCX header + fp.write( + o8(10) + o8(version) + o8(1) + o8(bits) + o16(0) + + o16(0) + o16(im.size[0]-1) + o16(im.size[1]-1) + o16(dpi[0]) + + o16(dpi[1]) + b"\0"*24 + b"\xFF"*24 + b"\0" + o8(planes) + + o16(stride) + o16(1) + o16(screen[0]) + o16(screen[1]) + + b"\0"*54 + ) + + assert fp.tell() == 128 + + ImageFile._save(im, fp, [("pcx", (0, 0)+im.size, 0, + (rawmode, bits*planes))]) + + if im.mode == "P": + # colour palette + fp.write(o8(12)) + fp.write(im.im.getpalette("RGB", "RGB")) # 768 bytes + elif im.mode == "L": + # greyscale palette + fp.write(o8(12)) + for i in range(256): + fp.write(o8(i)*3) + +# -------------------------------------------------------------------- +# registry + +Image.register_open("PCX", PcxImageFile, _accept) +Image.register_save("PCX", _save) + +Image.register_extension("PCX", ".pcx") diff --git a/pyPackages/pillowarmv7l/PIL/PdfImagePlugin.py b/pyPackages/pillowarmv7l/PIL/PdfImagePlugin.py new file mode 100644 index 0000000..5113f09 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/PdfImagePlugin.py @@ -0,0 +1,238 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PDF (Acrobat) file handling +# +# History: +# 1996-07-16 fl Created +# 1997-01-18 fl Fixed header +# 2004-02-21 fl Fixes for 1/L/CMYK images, etc. +# 2004-02-24 fl Fixes for 1 and P images. +# +# Copyright (c) 1997-2004 by Secret Labs AB. All rights reserved. +# Copyright (c) 1996-1997 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +## +# Image plugin for PDF images (output only). +## + +__version__ = "0.4" + +from PIL import Image, ImageFile +from PIL._binary import i8 +import io + + +# +# -------------------------------------------------------------------- + +# object ids: +# 1. catalogue +# 2. pages +# 3. image +# 4. page +# 5. page contents + +def _obj(fp, obj, **dict): + fp.write("%d 0 obj\n" % obj) + if dict: + fp.write("<<\n") + for k, v in dict.items(): + if v is not None: + fp.write("/%s %s\n" % (k, v)) + fp.write(">>\n") + + +def _endobj(fp): + fp.write("endobj\n") + + +## +# (Internal) Image save plugin for the PDF format. + +def _save(im, fp, filename): + resolution = im.encoderinfo.get("resolution", 72.0) + + # + # make sure image data is available + im.load() + + xref = [0]*(5+1) # placeholders + + class TextWriter: + def __init__(self, fp): + self.fp = fp + + def __getattr__(self, name): + return getattr(self.fp, name) + + def write(self, value): + self.fp.write(value.encode('latin-1')) + + fp = TextWriter(fp) + + fp.write("%PDF-1.2\n") + fp.write("% created by PIL PDF driver " + __version__ + "\n") + + # + # Get image characteristics + + width, height = im.size + + # FIXME: Should replace ASCIIHexDecode with RunLengthDecode (packbits) + # or LZWDecode (tiff/lzw compression). Note that PDF 1.2 also supports + # Flatedecode (zip compression). + + bits = 8 + params = None + + if im.mode == "1": + filter = "/ASCIIHexDecode" + colorspace = "/DeviceGray" + procset = "/ImageB" # grayscale + bits = 1 + elif im.mode == "L": + filter = "/DCTDecode" + # params = "<< /Predictor 15 /Columns %d >>" % (width-2) + colorspace = "/DeviceGray" + procset = "/ImageB" # grayscale + elif im.mode == "P": + filter = "/ASCIIHexDecode" + colorspace = "[ /Indexed /DeviceRGB 255 <" + palette = im.im.getpalette("RGB") + for i in range(256): + r = i8(palette[i*3]) + g = i8(palette[i*3+1]) + b = i8(palette[i*3+2]) + colorspace += "%02x%02x%02x " % (r, g, b) + colorspace += "> ]" + procset = "/ImageI" # indexed color + elif im.mode == "RGB": + filter = "/DCTDecode" + colorspace = "/DeviceRGB" + procset = "/ImageC" # color images + elif im.mode == "CMYK": + filter = "/DCTDecode" + colorspace = "/DeviceCMYK" + procset = "/ImageC" # color images + else: + raise ValueError("cannot save mode %s" % im.mode) + + # + # catalogue + + xref[1] = fp.tell() + _obj( + fp, 1, + Type="/Catalog", + Pages="2 0 R") + _endobj(fp) + + # + # pages + + xref[2] = fp.tell() + _obj( + fp, 2, + Type="/Pages", + Count=1, + Kids="[4 0 R]") + _endobj(fp) + + # + # image + + op = io.BytesIO() + + if filter == "/ASCIIHexDecode": + if bits == 1: + # FIXME: the hex encoder doesn't support packed 1-bit + # images; do things the hard way... + data = im.tobytes("raw", "1") + im = Image.new("L", (len(data), 1), None) + im.putdata(data) + ImageFile._save(im, op, [("hex", (0, 0)+im.size, 0, im.mode)]) + elif filter == "/DCTDecode": + Image.SAVE["JPEG"](im, op, filename) + elif filter == "/FlateDecode": + ImageFile._save(im, op, [("zip", (0, 0)+im.size, 0, im.mode)]) + elif filter == "/RunLengthDecode": + ImageFile._save(im, op, [("packbits", (0, 0)+im.size, 0, im.mode)]) + else: + raise ValueError("unsupported PDF filter (%s)" % filter) + + xref[3] = fp.tell() + _obj( + fp, 3, + Type="/XObject", + Subtype="/Image", + Width=width, # * 72.0 / resolution, + Height=height, # * 72.0 / resolution, + Length=len(op.getvalue()), + Filter=filter, + BitsPerComponent=bits, + DecodeParams=params, + ColorSpace=colorspace) + + fp.write("stream\n") + fp.fp.write(op.getvalue()) + fp.write("\nendstream\n") + + _endobj(fp) + + # + # page + + xref[4] = fp.tell() + _obj(fp, 4) + fp.write( + "<<\n/Type /Page\n/Parent 2 0 R\n" + "/Resources <<\n/ProcSet [ /PDF %s ]\n" + "/XObject << /image 3 0 R >>\n>>\n" + "/MediaBox [ 0 0 %d %d ]\n/Contents 5 0 R\n>>\n" % ( + procset, + int(width * 72.0 / resolution), + int(height * 72.0 / resolution))) + _endobj(fp) + + # + # page contents + + op = TextWriter(io.BytesIO()) + + op.write( + "q %d 0 0 %d 0 0 cm /image Do Q\n" % ( + int(width * 72.0 / resolution), + int(height * 72.0 / resolution))) + + xref[5] = fp.tell() + _obj(fp, 5, Length=len(op.fp.getvalue())) + + fp.write("stream\n") + fp.fp.write(op.fp.getvalue()) + fp.write("\nendstream\n") + + _endobj(fp) + + # + # trailer + startxref = fp.tell() + fp.write("xref\n0 %d\n0000000000 65535 f \n" % len(xref)) + for x in xref[1:]: + fp.write("%010d 00000 n \n" % x) + fp.write("trailer\n<<\n/Size %d\n/Root 1 0 R\n>>\n" % len(xref)) + fp.write("startxref\n%d\n%%%%EOF\n" % startxref) + fp.flush() + +# +# -------------------------------------------------------------------- + +Image.register_save("PDF", _save) + +Image.register_extension("PDF", ".pdf") + +Image.register_mime("PDF", "application/pdf") diff --git a/pyPackages/pillowarmv7l/PIL/PixarImagePlugin.py b/pyPackages/pillowarmv7l/PIL/PixarImagePlugin.py new file mode 100644 index 0000000..ebf4c8c --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/PixarImagePlugin.py @@ -0,0 +1,69 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PIXAR raster support for PIL +# +# history: +# 97-01-29 fl Created +# +# notes: +# This is incomplete; it is based on a few samples created with +# Photoshop 2.5 and 3.0, and a summary description provided by +# Greg Coats . Hopefully, "L" and +# "RGBA" support will be added in future versions. +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.1" + +from PIL import Image, ImageFile, _binary + +# +# helpers + +i16 = _binary.i16le +i32 = _binary.i32le + + +## +# Image plugin for PIXAR raster images. + +class PixarImageFile(ImageFile.ImageFile): + + format = "PIXAR" + format_description = "PIXAR raster image" + + def _open(self): + + # assuming a 4-byte magic label (FIXME: add "_accept" hook) + s = self.fp.read(4) + if s != b"\200\350\000\000": + raise SyntaxError("not a PIXAR file") + + # read rest of header + s = s + self.fp.read(508) + + self.size = i16(s[418:420]), i16(s[416:418]) + + # get channel/depth descriptions + mode = i16(s[424:426]), i16(s[426:428]) + + if mode == (14, 2): + self.mode = "RGB" + # FIXME: to be continued... + + # create tile descriptor (assuming "dumped") + self.tile = [("raw", (0, 0)+self.size, 1024, (self.mode, 0, 1))] + +# +# -------------------------------------------------------------------- + +Image.register_open("PIXAR", PixarImageFile) + +# +# FIXME: what's the standard extension? diff --git a/pyPackages/pillowarmv7l/PIL/PngImagePlugin.py b/pyPackages/pillowarmv7l/PIL/PngImagePlugin.py new file mode 100644 index 0000000..7a9becd --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/PngImagePlugin.py @@ -0,0 +1,811 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PNG support code +# +# See "PNG (Portable Network Graphics) Specification, version 1.0; +# W3C Recommendation", 1996-10-01, Thomas Boutell (ed.). +# +# history: +# 1996-05-06 fl Created (couldn't resist it) +# 1996-12-14 fl Upgraded, added read and verify support (0.2) +# 1996-12-15 fl Separate PNG stream parser +# 1996-12-29 fl Added write support, added getchunks +# 1996-12-30 fl Eliminated circular references in decoder (0.3) +# 1998-07-12 fl Read/write 16-bit images as mode I (0.4) +# 2001-02-08 fl Added transparency support (from Zircon) (0.5) +# 2001-04-16 fl Don't close data source in "open" method (0.6) +# 2004-02-24 fl Don't even pretend to support interlaced files (0.7) +# 2004-08-31 fl Do basic sanity check on chunk identifiers (0.8) +# 2004-09-20 fl Added PngInfo chunk container +# 2004-12-18 fl Added DPI read support (based on code by Niki Spahiev) +# 2008-08-13 fl Added tRNS support for RGB images +# 2009-03-06 fl Support for preserving ICC profiles (by Florian Hoech) +# 2009-03-08 fl Added zTXT support (from Lowell Alleman) +# 2009-03-29 fl Read interlaced PNG files (from Conrado Porto Lopes Gouvua) +# +# Copyright (c) 1997-2009 by Secret Labs AB +# Copyright (c) 1996 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from __future__ import print_function + +__version__ = "0.9" + +import re + +from PIL import Image, ImageFile, ImagePalette, _binary +import zlib + +i8 = _binary.i8 +i16 = _binary.i16be +i32 = _binary.i32be + +is_cid = re.compile(b"\w\w\w\w").match + + +_MAGIC = b"\211PNG\r\n\032\n" + + +_MODES = { + # supported bits/color combinations, and corresponding modes/rawmodes + (1, 0): ("1", "1"), + (2, 0): ("L", "L;2"), + (4, 0): ("L", "L;4"), + (8, 0): ("L", "L"), + (16, 0): ("I", "I;16B"), + (8, 2): ("RGB", "RGB"), + (16, 2): ("RGB", "RGB;16B"), + (1, 3): ("P", "P;1"), + (2, 3): ("P", "P;2"), + (4, 3): ("P", "P;4"), + (8, 3): ("P", "P"), + (8, 4): ("LA", "LA"), + (16, 4): ("RGBA", "LA;16B"), # LA;16B->LA not yet available + (8, 6): ("RGBA", "RGBA"), + (16, 6): ("RGBA", "RGBA;16B"), +} + + +_simple_palette = re.compile(b'^\xff+\x00\xff*$') + +# Maximum decompressed size for a iTXt or zTXt chunk. +# Eliminates decompression bombs where compressed chunks can expand 1000x +MAX_TEXT_CHUNK = ImageFile.SAFEBLOCK +# Set the maximum total text chunk size. +MAX_TEXT_MEMORY = 64 * MAX_TEXT_CHUNK + +def _safe_zlib_decompress(s): + dobj = zlib.decompressobj() + plaintext = dobj.decompress(s, MAX_TEXT_CHUNK) + if dobj.unconsumed_tail: + raise ValueError("Decompressed Data Too Large") + return plaintext + + +# -------------------------------------------------------------------- +# Support classes. Suitable for PNG and related formats like MNG etc. + +class ChunkStream: + + def __init__(self, fp): + + self.fp = fp + self.queue = [] + + if not hasattr(Image.core, "crc32"): + self.crc = self.crc_skip + + def read(self): + "Fetch a new chunk. Returns header information." + + if self.queue: + cid, pos, length = self.queue[-1] + del self.queue[-1] + self.fp.seek(pos) + else: + s = self.fp.read(8) + cid = s[4:] + pos = self.fp.tell() + length = i32(s) + + if not is_cid(cid): + raise SyntaxError("broken PNG file (chunk %s)" % repr(cid)) + + return cid, pos, length + + def close(self): + self.queue = self.crc = self.fp = None + + def push(self, cid, pos, length): + + self.queue.append((cid, pos, length)) + + def call(self, cid, pos, length): + "Call the appropriate chunk handler" + + if Image.DEBUG: + print("STREAM", cid, pos, length) + return getattr(self, "chunk_" + cid.decode('ascii'))(pos, length) + + def crc(self, cid, data): + "Read and verify checksum" + + crc1 = Image.core.crc32(data, Image.core.crc32(cid)) + crc2 = i16(self.fp.read(2)), i16(self.fp.read(2)) + if crc1 != crc2: + raise SyntaxError("broken PNG file" + "(bad header checksum in %s)" % cid) + + def crc_skip(self, cid, data): + "Read checksum. Used if the C module is not present" + + self.fp.read(4) + + def verify(self, endchunk=b"IEND"): + + # Simple approach; just calculate checksum for all remaining + # blocks. Must be called directly after open. + + cids = [] + + while True: + cid, pos, length = self.read() + if cid == endchunk: + break + self.crc(cid, ImageFile._safe_read(self.fp, length)) + cids.append(cid) + + return cids + + +class iTXt(str): + """ + Subclass of string to allow iTXt chunks to look like strings while + keeping their extra information + + """ + @staticmethod + def __new__(cls, text, lang, tkey): + """ + :param value: value for this key + :param lang: language code + :param tkey: UTF-8 version of the key name + """ + + self = str.__new__(cls, text) + self.lang = lang + self.tkey = tkey + return self + + +class PngInfo: + """ + PNG chunk container (for use with save(pnginfo=)) + + """ + + def __init__(self): + self.chunks = [] + + def add(self, cid, data): + """Appends an arbitrary chunk. Use with caution. + + :param cid: a byte string, 4 bytes long. + :param data: a byte string of the encoded data + + """ + + self.chunks.append((cid, data)) + + def add_itxt(self, key, value, lang="", tkey="", zip=False): + """Appends an iTXt chunk. + + :param key: latin-1 encodable text key name + :param value: value for this key + :param lang: language code + :param tkey: UTF-8 version of the key name + :param zip: compression flag + + """ + + if not isinstance(key, bytes): + key = key.encode("latin-1", "strict") + if not isinstance(value, bytes): + value = value.encode("utf-8", "strict") + if not isinstance(lang, bytes): + lang = lang.encode("utf-8", "strict") + if not isinstance(tkey, bytes): + tkey = tkey.encode("utf-8", "strict") + + if zip: + self.add(b"iTXt", key + b"\0\x01\0" + lang + b"\0" + tkey + b"\0" + + zlib.compress(value)) + else: + self.add(b"iTXt", key + b"\0\0\0" + lang + b"\0" + tkey + b"\0" + + value) + + def add_text(self, key, value, zip=0): + """Appends a text chunk. + + :param key: latin-1 encodable text key name + :param value: value for this key, text or an + :py:class:`PIL.PngImagePlugin.iTXt` instance + :param zip: compression flag + + """ + if isinstance(value, iTXt): + return self.add_itxt(key, value, value.lang, value.tkey, bool(zip)) + + # The tEXt chunk stores latin-1 text + if not isinstance(value, bytes): + try: + value = value.encode('latin-1', 'strict') + except UnicodeError: + return self.add_itxt(key, value, zip=bool(zip)) + + if not isinstance(key, bytes): + key = key.encode('latin-1', 'strict') + + if zip: + self.add(b"zTXt", key + b"\0\0" + zlib.compress(value)) + else: + self.add(b"tEXt", key + b"\0" + value) + + +# -------------------------------------------------------------------- +# PNG image stream (IHDR/IEND) + +class PngStream(ChunkStream): + + def __init__(self, fp): + + ChunkStream.__init__(self, fp) + + # local copies of Image attributes + self.im_info = {} + self.im_text = {} + self.im_size = (0, 0) + self.im_mode = None + self.im_tile = None + self.im_palette = None + + self.text_memory = 0 + + def check_text_memory(self, chunklen): + self.text_memory += chunklen + if self.text_memory > MAX_TEXT_MEMORY: + raise ValueError("Too much memory used in text chunks: %s>MAX_TEXT_MEMORY" % + self.text_memory) + + def chunk_iCCP(self, pos, length): + + # ICC profile + s = ImageFile._safe_read(self.fp, length) + # according to PNG spec, the iCCP chunk contains: + # Profile name 1-79 bytes (character string) + # Null separator 1 byte (null character) + # Compression method 1 byte (0) + # Compressed profile n bytes (zlib with deflate compression) + i = s.find(b"\0") + if Image.DEBUG: + print("iCCP profile name", s[:i]) + print("Compression method", i8(s[i])) + comp_method = i8(s[i]) + if comp_method != 0: + raise SyntaxError("Unknown compression method %s in iCCP chunk" % + comp_method) + try: + icc_profile = _safe_zlib_decompress(s[i+2:]) + except zlib.error: + icc_profile = None # FIXME + self.im_info["icc_profile"] = icc_profile + return s + + def chunk_IHDR(self, pos, length): + + # image header + s = ImageFile._safe_read(self.fp, length) + self.im_size = i32(s), i32(s[4:]) + try: + self.im_mode, self.im_rawmode = _MODES[(i8(s[8]), i8(s[9]))] + except: + pass + if i8(s[12]): + self.im_info["interlace"] = 1 + if i8(s[11]): + raise SyntaxError("unknown filter category") + return s + + def chunk_IDAT(self, pos, length): + + # image data + self.im_tile = [("zip", (0, 0)+self.im_size, pos, self.im_rawmode)] + self.im_idat = length + raise EOFError + + def chunk_IEND(self, pos, length): + + # end of PNG image + raise EOFError + + def chunk_PLTE(self, pos, length): + + # palette + s = ImageFile._safe_read(self.fp, length) + if self.im_mode == "P": + self.im_palette = "RGB", s + return s + + def chunk_tRNS(self, pos, length): + + # transparency + s = ImageFile._safe_read(self.fp, length) + if self.im_mode == "P": + if _simple_palette.match(s): + i = s.find(b"\0") + if i >= 0: + self.im_info["transparency"] = i + else: + self.im_info["transparency"] = s + elif self.im_mode == "L": + self.im_info["transparency"] = i16(s) + elif self.im_mode == "RGB": + self.im_info["transparency"] = i16(s), i16(s[2:]), i16(s[4:]) + return s + + def chunk_gAMA(self, pos, length): + + # gamma setting + s = ImageFile._safe_read(self.fp, length) + self.im_info["gamma"] = i32(s) / 100000.0 + return s + + def chunk_pHYs(self, pos, length): + + # pixels per unit + s = ImageFile._safe_read(self.fp, length) + px, py = i32(s), i32(s[4:]) + unit = i8(s[8]) + if unit == 1: # meter + dpi = int(px * 0.0254 + 0.5), int(py * 0.0254 + 0.5) + self.im_info["dpi"] = dpi + elif unit == 0: + self.im_info["aspect"] = px, py + return s + + def chunk_tEXt(self, pos, length): + + # text + s = ImageFile._safe_read(self.fp, length) + try: + k, v = s.split(b"\0", 1) + except ValueError: + # fallback for broken tEXt tags + k = s + v = b"" + if k: + if bytes is not str: + k = k.decode('latin-1', 'strict') + v = v.decode('latin-1', 'replace') + + self.im_info[k] = self.im_text[k] = v + self.check_text_memory(len(v)) + + return s + + def chunk_zTXt(self, pos, length): + + # compressed text + s = ImageFile._safe_read(self.fp, length) + try: + k, v = s.split(b"\0", 1) + except ValueError: + k = s + v = b"" + if v: + comp_method = i8(v[0]) + else: + comp_method = 0 + if comp_method != 0: + raise SyntaxError("Unknown compression method %s in zTXt chunk" % + comp_method) + try: + v = _safe_zlib_decompress(v[1:]) + except zlib.error: + v = b"" + + if k: + if bytes is not str: + k = k.decode('latin-1', 'strict') + v = v.decode('latin-1', 'replace') + + self.im_info[k] = self.im_text[k] = v + self.check_text_memory(len(v)) + + return s + + def chunk_iTXt(self, pos, length): + + # international text + r = s = ImageFile._safe_read(self.fp, length) + try: + k, r = r.split(b"\0", 1) + except ValueError: + return s + if len(r) < 2: + return s + cf, cm, r = i8(r[0]), i8(r[1]), r[2:] + try: + lang, tk, v = r.split(b"\0", 2) + except ValueError: + return s + if cf != 0: + if cm == 0: + try: + v = _safe_zlib_decompress(v) + except zlib.error: + return s + else: + return s + if bytes is not str: + try: + k = k.decode("latin-1", "strict") + lang = lang.decode("utf-8", "strict") + tk = tk.decode("utf-8", "strict") + v = v.decode("utf-8", "strict") + except UnicodeError: + return s + + self.im_info[k] = self.im_text[k] = iTXt(v, lang, tk) + self.check_text_memory(len(v)) + + return s + + +# -------------------------------------------------------------------- +# PNG reader + +def _accept(prefix): + return prefix[:8] == _MAGIC + + +## +# Image plugin for PNG images. + +class PngImageFile(ImageFile.ImageFile): + + format = "PNG" + format_description = "Portable network graphics" + + def _open(self): + + if self.fp.read(8) != _MAGIC: + raise SyntaxError("not a PNG file") + + # + # Parse headers up to the first IDAT chunk + + self.png = PngStream(self.fp) + + while True: + + # + # get next chunk + + cid, pos, length = self.png.read() + + try: + s = self.png.call(cid, pos, length) + except EOFError: + break + except AttributeError: + if Image.DEBUG: + print(cid, pos, length, "(unknown)") + s = ImageFile._safe_read(self.fp, length) + + self.png.crc(cid, s) + + # + # Copy relevant attributes from the PngStream. An alternative + # would be to let the PngStream class modify these attributes + # directly, but that introduces circular references which are + # difficult to break if things go wrong in the decoder... + # (believe me, I've tried ;-) + + self.mode = self.png.im_mode + self.size = self.png.im_size + self.info = self.png.im_info + self.text = self.png.im_text # experimental + self.tile = self.png.im_tile + + if self.png.im_palette: + rawmode, data = self.png.im_palette + self.palette = ImagePalette.raw(rawmode, data) + + self.__idat = length # used by load_read() + + def verify(self): + "Verify PNG file" + + if self.fp is None: + raise RuntimeError("verify must be called directly after open") + + # back up to beginning of IDAT block + self.fp.seek(self.tile[0][2] - 8) + + self.png.verify() + self.png.close() + + self.fp = None + + def load_prepare(self): + "internal: prepare to read PNG file" + + if self.info.get("interlace"): + self.decoderconfig = self.decoderconfig + (1,) + + ImageFile.ImageFile.load_prepare(self) + + def load_read(self, read_bytes): + "internal: read more image data" + + while self.__idat == 0: + # end of chunk, skip forward to next one + + self.fp.read(4) # CRC + + cid, pos, length = self.png.read() + + if cid not in [b"IDAT", b"DDAT"]: + self.png.push(cid, pos, length) + return b"" + + self.__idat = length # empty chunks are allowed + + # read more data from this chunk + if read_bytes <= 0: + read_bytes = self.__idat + else: + read_bytes = min(read_bytes, self.__idat) + + self.__idat = self.__idat - read_bytes + + return self.fp.read(read_bytes) + + def load_end(self): + "internal: finished reading image data" + + self.png.close() + self.png = None + + +# -------------------------------------------------------------------- +# PNG writer + +o8 = _binary.o8 +o16 = _binary.o16be +o32 = _binary.o32be + +_OUTMODES = { + # supported PIL modes, and corresponding rawmodes/bits/color combinations + "1": ("1", b'\x01\x00'), + "L;1": ("L;1", b'\x01\x00'), + "L;2": ("L;2", b'\x02\x00'), + "L;4": ("L;4", b'\x04\x00'), + "L": ("L", b'\x08\x00'), + "LA": ("LA", b'\x08\x04'), + "I": ("I;16B", b'\x10\x00'), + "P;1": ("P;1", b'\x01\x03'), + "P;2": ("P;2", b'\x02\x03'), + "P;4": ("P;4", b'\x04\x03'), + "P": ("P", b'\x08\x03'), + "RGB": ("RGB", b'\x08\x02'), + "RGBA": ("RGBA", b'\x08\x06'), +} + + +def putchunk(fp, cid, *data): + "Write a PNG chunk (including CRC field)" + + data = b"".join(data) + + fp.write(o32(len(data)) + cid) + fp.write(data) + hi, lo = Image.core.crc32(data, Image.core.crc32(cid)) + fp.write(o16(hi) + o16(lo)) + + +class _idat: + # wrap output from the encoder in IDAT chunks + + def __init__(self, fp, chunk): + self.fp = fp + self.chunk = chunk + + def write(self, data): + self.chunk(self.fp, b"IDAT", data) + + +def _save(im, fp, filename, chunk=putchunk, check=0): + # save an image to disk (called by the save method) + + mode = im.mode + + if mode == "P": + + # + # attempt to minimize storage requirements for palette images + if "bits" in im.encoderinfo: + # number of bits specified by user + colors = 1 << im.encoderinfo["bits"] + else: + # check palette contents + if im.palette: + colors = max(min(len(im.palette.getdata()[1])//3, 256), 2) + else: + colors = 256 + + if colors <= 2: + bits = 1 + elif colors <= 4: + bits = 2 + elif colors <= 16: + bits = 4 + else: + bits = 8 + if bits != 8: + mode = "%s;%d" % (mode, bits) + + # encoder options + if "dictionary" in im.encoderinfo: + dictionary = im.encoderinfo["dictionary"] + else: + dictionary = b"" + + im.encoderconfig = ("optimize" in im.encoderinfo, + im.encoderinfo.get("compress_level", -1), + im.encoderinfo.get("compress_type", -1), + dictionary) + + # get the corresponding PNG mode + try: + rawmode, mode = _OUTMODES[mode] + except KeyError: + raise IOError("cannot write mode %s as PNG" % mode) + + if check: + return check + + # + # write minimal PNG file + + fp.write(_MAGIC) + + chunk(fp, b"IHDR", + o32(im.size[0]), o32(im.size[1]), # 0: size + mode, # 8: depth/type + b'\0', # 10: compression + b'\0', # 11: filter category + b'\0') # 12: interlace flag + + if im.mode == "P": + palette_byte_number = (2 ** bits) * 3 + palette_bytes = im.im.getpalette("RGB")[:palette_byte_number] + while len(palette_bytes) < palette_byte_number: + palette_bytes += b'\0' + chunk(fp, b"PLTE", palette_bytes) + + transparency = im.encoderinfo.get('transparency', + im.info.get('transparency', None)) + + if transparency or transparency == 0: + if im.mode == "P": + # limit to actual palette size + alpha_bytes = 2**bits + if isinstance(transparency, bytes): + chunk(fp, b"tRNS", transparency[:alpha_bytes]) + else: + transparency = max(0, min(255, transparency)) + alpha = b'\xFF' * transparency + b'\0' + chunk(fp, b"tRNS", alpha[:alpha_bytes]) + elif im.mode == "L": + transparency = max(0, min(65535, transparency)) + chunk(fp, b"tRNS", o16(transparency)) + elif im.mode == "RGB": + red, green, blue = transparency + chunk(fp, b"tRNS", o16(red) + o16(green) + o16(blue)) + else: + if "transparency" in im.encoderinfo: + # don't bother with transparency if it's an RGBA + # and it's in the info dict. It's probably just stale. + raise IOError("cannot use transparency for this mode") + else: + if im.mode == "P" and im.im.getpalettemode() == "RGBA": + alpha = im.im.getpalette("RGBA", "A") + alpha_bytes = 2**bits + chunk(fp, b"tRNS", alpha[:alpha_bytes]) + + if 0: + # FIXME: to be supported some day + chunk(fp, b"gAMA", o32(int(gamma * 100000.0))) + + dpi = im.encoderinfo.get("dpi") + if dpi: + chunk(fp, b"pHYs", + o32(int(dpi[0] / 0.0254 + 0.5)), + o32(int(dpi[1] / 0.0254 + 0.5)), + b'\x01') + + info = im.encoderinfo.get("pnginfo") + if info: + for cid, data in info.chunks: + chunk(fp, cid, data) + + # ICC profile writing support -- 2008-06-06 Florian Hoech + if im.info.get("icc_profile"): + # ICC profile + # according to PNG spec, the iCCP chunk contains: + # Profile name 1-79 bytes (character string) + # Null separator 1 byte (null character) + # Compression method 1 byte (0) + # Compressed profile n bytes (zlib with deflate compression) + name = b"ICC Profile" + data = name + b"\0\0" + zlib.compress(im.info["icc_profile"]) + chunk(fp, b"iCCP", data) + + ImageFile._save(im, _idat(fp, chunk), + [("zip", (0, 0)+im.size, 0, rawmode)]) + + chunk(fp, b"IEND", b"") + + try: + fp.flush() + except: + pass + + +# -------------------------------------------------------------------- +# PNG chunk converter + +def getchunks(im, **params): + """Return a list of PNG chunks representing this image.""" + + class collector: + data = [] + + def write(self, data): + pass + + def append(self, chunk): + self.data.append(chunk) + + def append(fp, cid, *data): + data = b"".join(data) + hi, lo = Image.core.crc32(data, Image.core.crc32(cid)) + crc = o16(hi) + o16(lo) + fp.append((cid, data, crc)) + + fp = collector() + + try: + im.encoderinfo = params + _save(im, fp, None, append) + finally: + del im.encoderinfo + + return fp.data + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open("PNG", PngImageFile, _accept) +Image.register_save("PNG", _save) + +Image.register_extension("PNG", ".png") + +Image.register_mime("PNG", "image/png") diff --git a/pyPackages/pillowarmv7l/PIL/PpmImagePlugin.py b/pyPackages/pillowarmv7l/PIL/PpmImagePlugin.py new file mode 100644 index 0000000..9548324 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/PpmImagePlugin.py @@ -0,0 +1,172 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PPM support for PIL +# +# History: +# 96-03-24 fl Created +# 98-03-06 fl Write RGBA images (as RGB, that is) +# +# Copyright (c) Secret Labs AB 1997-98. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.2" + +import string + +from PIL import Image, ImageFile + +# +# -------------------------------------------------------------------- + +b_whitespace = string.whitespace +try: + import locale + locale_lang, locale_enc = locale.getlocale() + if locale_enc is None: + locale_lang, locale_enc = locale.getdefaultlocale() + b_whitespace = b_whitespace.decode(locale_enc) +except: + pass +b_whitespace = b_whitespace.encode('ascii', 'ignore') + +MODES = { + # standard + b"P4": "1", + b"P5": "L", + b"P6": "RGB", + # extensions + b"P0CMYK": "CMYK", + # PIL extensions (for test purposes only) + b"PyP": "P", + b"PyRGBA": "RGBA", + b"PyCMYK": "CMYK" +} + + +def _accept(prefix): + return prefix[0:1] == b"P" and prefix[1] in b"0456y" + + +## +# Image plugin for PBM, PGM, and PPM images. + +class PpmImageFile(ImageFile.ImageFile): + + format = "PPM" + format_description = "Pbmplus image" + + def _token(self, s=b""): + while True: # read until next whitespace + c = self.fp.read(1) + if not c or c in b_whitespace: + break + if c > b'\x79': + raise ValueError("Expected ASCII value, found binary") + s = s + c + if (len(s) > 9): + raise ValueError("Expected int, got > 9 digits") + return s + + def _open(self): + + # check magic + s = self.fp.read(1) + if s != b"P": + raise SyntaxError("not a PPM file") + mode = MODES[self._token(s)] + + if mode == "1": + self.mode = "1" + rawmode = "1;I" + else: + self.mode = rawmode = mode + + for ix in range(3): + while True: + while True: + s = self.fp.read(1) + if s not in b_whitespace: + break + if s != b"#": + break + s = self.fp.readline() + s = int(self._token(s)) + if ix == 0: + xsize = s + elif ix == 1: + ysize = s + if mode == "1": + break + elif ix == 2: + # maxgrey + if s > 255: + if not mode == 'L': + raise ValueError("Too many colors for band: %s" % s) + if s < 2**16: + self.mode = 'I' + rawmode = 'I;16B' + else: + self.mode = 'I' + rawmode = 'I;32B' + + self.size = xsize, ysize + self.tile = [("raw", + (0, 0, xsize, ysize), + self.fp.tell(), + (rawmode, 0, 1))] + + # ALTERNATIVE: load via builtin debug function + # self.im = Image.core.open_ppm(self.filename) + # self.mode = self.im.mode + # self.size = self.im.size + + +# +# -------------------------------------------------------------------- + +def _save(im, fp, filename): + if im.mode == "1": + rawmode, head = "1;I", b"P4" + elif im.mode == "L": + rawmode, head = "L", b"P5" + elif im.mode == "I": + if im.getextrema()[1] < 2**16: + rawmode, head = "I;16B", b"P5" + else: + rawmode, head = "I;32B", b"P5" + elif im.mode == "RGB": + rawmode, head = "RGB", b"P6" + elif im.mode == "RGBA": + rawmode, head = "RGB", b"P6" + else: + raise IOError("cannot write mode %s as PPM" % im.mode) + fp.write(head + ("\n%d %d\n" % im.size).encode('ascii')) + if head == b"P6": + fp.write(b"255\n") + if head == b"P5": + if rawmode == "L": + fp.write(b"255\n") + elif rawmode == "I;16B": + fp.write(b"65535\n") + elif rawmode == "I;32B": + fp.write(b"2147483648\n") + ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0, (rawmode, 0, 1))]) + + # ALTERNATIVE: save via builtin debug function + # im._dump(filename) + +# +# -------------------------------------------------------------------- + +Image.register_open("PPM", PpmImageFile, _accept) +Image.register_save("PPM", _save) + +Image.register_extension("PPM", ".pbm") +Image.register_extension("PPM", ".pgm") +Image.register_extension("PPM", ".ppm") diff --git a/pyPackages/pillowarmv7l/PIL/PsdImagePlugin.py b/pyPackages/pillowarmv7l/PIL/PsdImagePlugin.py new file mode 100644 index 0000000..02c94a8 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/PsdImagePlugin.py @@ -0,0 +1,304 @@ +# +# The Python Imaging Library +# $Id$ +# +# Adobe PSD 2.5/3.0 file handling +# +# History: +# 1995-09-01 fl Created +# 1997-01-03 fl Read most PSD images +# 1997-01-18 fl Fixed P and CMYK support +# 2001-10-21 fl Added seek/tell support (for layers) +# +# Copyright (c) 1997-2001 by Secret Labs AB. +# Copyright (c) 1995-2001 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.4" + +from PIL import Image, ImageFile, ImagePalette, _binary + +MODES = { + # (photoshop mode, bits) -> (pil mode, required channels) + (0, 1): ("1", 1), + (0, 8): ("L", 1), + (1, 8): ("L", 1), + (2, 8): ("P", 1), + (3, 8): ("RGB", 3), + (4, 8): ("CMYK", 4), + (7, 8): ("L", 1), # FIXME: multilayer + (8, 8): ("L", 1), # duotone + (9, 8): ("LAB", 3) +} + +# +# helpers + +i8 = _binary.i8 +i16 = _binary.i16be +i32 = _binary.i32be + + +# --------------------------------------------------------------------. +# read PSD images + +def _accept(prefix): + return prefix[:4] == b"8BPS" + + +## +# Image plugin for Photoshop images. + +class PsdImageFile(ImageFile.ImageFile): + + format = "PSD" + format_description = "Adobe Photoshop" + + def _open(self): + + read = self.fp.read + + # + # header + + s = read(26) + if s[:4] != b"8BPS" or i16(s[4:]) != 1: + raise SyntaxError("not a PSD file") + + psd_bits = i16(s[22:]) + psd_channels = i16(s[12:]) + psd_mode = i16(s[24:]) + + mode, channels = MODES[(psd_mode, psd_bits)] + + if channels > psd_channels: + raise IOError("not enough channels") + + self.mode = mode + self.size = i32(s[18:]), i32(s[14:]) + + # + # color mode data + + size = i32(read(4)) + if size: + data = read(size) + if mode == "P" and size == 768: + self.palette = ImagePalette.raw("RGB;L", data) + + # + # image resources + + self.resources = [] + + size = i32(read(4)) + if size: + # load resources + end = self.fp.tell() + size + while self.fp.tell() < end: + signature = read(4) + id = i16(read(2)) + name = read(i8(read(1))) + if not (len(name) & 1): + read(1) # padding + data = read(i32(read(4))) + if (len(data) & 1): + read(1) # padding + self.resources.append((id, name, data)) + if id == 1039: # ICC profile + self.info["icc_profile"] = data + + # + # layer and mask information + + self.layers = [] + + size = i32(read(4)) + if size: + end = self.fp.tell() + size + size = i32(read(4)) + if size: + self.layers = _layerinfo(self.fp) + self.fp.seek(end) + + # + # image descriptor + + self.tile = _maketile(self.fp, mode, (0, 0) + self.size, channels) + + # keep the file open + self._fp = self.fp + self.frame = 0 + + def seek(self, layer): + # seek to given layer (1..max) + if layer == self.frame: + return + try: + if layer <= 0: + raise IndexError + name, mode, bbox, tile = self.layers[layer-1] + self.mode = mode + self.tile = tile + self.frame = layer + self.fp = self._fp + return name, bbox + except IndexError: + raise EOFError("no such layer") + + def tell(self): + # return layer number (0=image, 1..max=layers) + return self.frame + + def load_prepare(self): + # create image memory if necessary + if not self.im or\ + self.im.mode != self.mode or self.im.size != self.size: + self.im = Image.core.fill(self.mode, self.size, 0) + # create palette (optional) + if self.mode == "P": + Image.Image.load(self) + + +def _layerinfo(file): + # read layerinfo block + layers = [] + read = file.read + for i in range(abs(i16(read(2)))): + + # bounding box + y0 = i32(read(4)) + x0 = i32(read(4)) + y1 = i32(read(4)) + x1 = i32(read(4)) + + # image info + info = [] + mode = [] + types = list(range(i16(read(2)))) + if len(types) > 4: + continue + + for i in types: + type = i16(read(2)) + + if type == 65535: + m = "A" + else: + m = "RGBA"[type] + + mode.append(m) + size = i32(read(4)) + info.append((m, size)) + + # figure out the image mode + mode.sort() + if mode == ["R"]: + mode = "L" + elif mode == ["B", "G", "R"]: + mode = "RGB" + elif mode == ["A", "B", "G", "R"]: + mode = "RGBA" + else: + mode = None # unknown + + # skip over blend flags and extra information + filler = read(12) + name = "" + size = i32(read(4)) + combined = 0 + if size: + length = i32(read(4)) + if length: + mask_y = i32(read(4)) + mask_x = i32(read(4)) + mask_h = i32(read(4)) - mask_y + mask_w = i32(read(4)) - mask_x + file.seek(length - 16, 1) + combined += length + 4 + + length = i32(read(4)) + if length: + file.seek(length, 1) + combined += length + 4 + + length = i8(read(1)) + if length: + # Don't know the proper encoding, + # Latin-1 should be a good guess + name = read(length).decode('latin-1', 'replace') + combined += length + 1 + + file.seek(size - combined, 1) + layers.append((name, mode, (x0, y0, x1, y1))) + + # get tiles + i = 0 + for name, mode, bbox in layers: + tile = [] + for m in mode: + t = _maketile(file, m, bbox, 1) + if t: + tile.extend(t) + layers[i] = name, mode, bbox, tile + i += 1 + + return layers + + +def _maketile(file, mode, bbox, channels): + + tile = None + read = file.read + + compression = i16(read(2)) + + xsize = bbox[2] - bbox[0] + ysize = bbox[3] - bbox[1] + + offset = file.tell() + + if compression == 0: + # + # raw compression + tile = [] + for channel in range(channels): + layer = mode[channel] + if mode == "CMYK": + layer += ";I" + tile.append(("raw", bbox, offset, layer)) + offset = offset + xsize*ysize + + elif compression == 1: + # + # packbits compression + i = 0 + tile = [] + bytecount = read(channels * ysize * 2) + offset = file.tell() + for channel in range(channels): + layer = mode[channel] + if mode == "CMYK": + layer += ";I" + tile.append( + ("packbits", bbox, offset, layer) + ) + for y in range(ysize): + offset = offset + i16(bytecount[i:i+2]) + i += 2 + + file.seek(offset) + + if offset & 1: + read(1) # padding + + return tile + +# -------------------------------------------------------------------- +# registry + +Image.register_open("PSD", PsdImageFile, _accept) + +Image.register_extension("PSD", ".psd") diff --git a/pyPackages/pillowarmv7l/PIL/PyAccess.py b/pyPackages/pillowarmv7l/PIL/PyAccess.py new file mode 100644 index 0000000..2882905 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/PyAccess.py @@ -0,0 +1,315 @@ +# +# The Python Imaging Library +# Pillow fork +# +# Python implementation of the PixelAccess Object +# +# Copyright (c) 1997-2009 by Secret Labs AB. All rights reserved. +# Copyright (c) 1995-2009 by Fredrik Lundh. +# Copyright (c) 2013 Eric Soroos +# +# See the README file for information on usage and redistribution +# + +# Notes: +# +# * Implements the pixel access object following Access. +# * Does not implement the line functions, as they don't appear to be used +# * Taking only the tuple form, which is used from python. +# * Fill.c uses the integer form, but it's still going to use the old +# Access.c implementation. +# + +from __future__ import print_function + +from cffi import FFI +import sys + +DEBUG = 0 + +defs = """ +struct Pixel_RGBA { + unsigned char r,g,b,a; +}; +struct Pixel_I16 { + unsigned char l,r; +}; +""" +ffi = FFI() +ffi.cdef(defs) + + +class PyAccess(object): + + def __init__(self, img, readonly=False): + vals = dict(img.im.unsafe_ptrs) + self.readonly = readonly + self.image8 = ffi.cast('unsigned char **', vals['image8']) + self.image32 = ffi.cast('int **', vals['image32']) + self.image = ffi.cast('unsigned char **', vals['image']) + self.xsize = vals['xsize'] + self.ysize = vals['ysize'] + + if DEBUG: + print (vals) + self._post_init() + + def _post_init(): + pass + + def __setitem__(self, xy, color): + """ + Modifies the pixel at x,y. The color is given as a single + numerical value for single band images, and a tuple for + multi-band images + + :param xy: The pixel coordinate, given as (x, y). + :param value: The pixel value. + """ + if self.readonly: + raise ValueError('Attempt to putpixel a read only image') + (x, y) = self.check_xy(xy) + return self.set_pixel(x, y, color) + + def __getitem__(self, xy): + """ + Returns the pixel at x,y. The pixel is returned as a single + value for single band images or a tuple for multiple band + images + + :param xy: The pixel coordinate, given as (x, y). + :returns: a pixel value for single band images, a tuple of + pixel values for multiband images. + """ + + (x, y) = self.check_xy(xy) + return self.get_pixel(x, y) + + putpixel = __setitem__ + getpixel = __getitem__ + + def check_xy(self, xy): + (x, y) = xy + if not (0 <= x < self.xsize and 0 <= y < self.ysize): + raise ValueError('pixel location out of range') + return xy + + +class _PyAccess32_2(PyAccess): + """ PA, LA, stored in first and last bytes of a 32 bit word """ + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) + + def get_pixel(self, x, y): + pixel = self.pixels[y][x] + return (pixel.r, pixel.a) + + def set_pixel(self, x, y, color): + pixel = self.pixels[y][x] + # tuple + pixel.r = min(color[0], 255) + pixel.a = min(color[1], 255) + + +class _PyAccess32_3(PyAccess): + """ RGB and friends, stored in the first three bytes of a 32 bit word """ + + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) + + def get_pixel(self, x, y): + pixel = self.pixels[y][x] + return (pixel.r, pixel.g, pixel.b) + + def set_pixel(self, x, y, color): + pixel = self.pixels[y][x] + # tuple + pixel.r = min(color[0], 255) + pixel.g = min(color[1], 255) + pixel.b = min(color[2], 255) + + +class _PyAccess32_4(PyAccess): + """ RGBA etc, all 4 bytes of a 32 bit word """ + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) + + def get_pixel(self, x, y): + pixel = self.pixels[y][x] + return (pixel.r, pixel.g, pixel.b, pixel.a) + + def set_pixel(self, x, y, color): + pixel = self.pixels[y][x] + # tuple + pixel.r = min(color[0], 255) + pixel.g = min(color[1], 255) + pixel.b = min(color[2], 255) + pixel.a = min(color[3], 255) + + +class _PyAccess8(PyAccess): + """ 1, L, P, 8 bit images stored as uint8 """ + def _post_init(self, *args, **kwargs): + self.pixels = self.image8 + + def get_pixel(self, x, y): + return self.pixels[y][x] + + def set_pixel(self, x, y, color): + try: + # integer + self.pixels[y][x] = min(color, 255) + except: + # tuple + self.pixels[y][x] = min(color[0], 255) + + +class _PyAccessI16_N(PyAccess): + """ I;16 access, native bitendian without conversion """ + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast('unsigned short **', self.image) + + def get_pixel(self, x, y): + return self.pixels[y][x] + + def set_pixel(self, x, y, color): + try: + # integer + self.pixels[y][x] = min(color, 65535) + except: + # tuple + self.pixels[y][x] = min(color[0], 65535) + + +class _PyAccessI16_L(PyAccess): + """ I;16L access, with conversion """ + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast('struct Pixel_I16 **', self.image) + + def get_pixel(self, x, y): + pixel = self.pixels[y][x] + return pixel.l + pixel.r * 256 + + def set_pixel(self, x, y, color): + pixel = self.pixels[y][x] + try: + color = min(color, 65535) + except: + color = min(color[0], 65535) + + pixel.l = color & 0xFF + pixel.r = color >> 8 + + +class _PyAccessI16_B(PyAccess): + """ I;16B access, with conversion """ + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast('struct Pixel_I16 **', self.image) + + def get_pixel(self, x, y): + pixel = self.pixels[y][x] + return pixel.l * 256 + pixel.r + + def set_pixel(self, x, y, color): + pixel = self.pixels[y][x] + try: + color = min(color, 65535) + except: + color = min(color[0], 65535) + + pixel.l = color >> 8 + pixel.r = color & 0xFF + + +class _PyAccessI32_N(PyAccess): + """ Signed Int32 access, native endian """ + def _post_init(self, *args, **kwargs): + self.pixels = self.image32 + + def get_pixel(self, x, y): + return self.pixels[y][x] + + def set_pixel(self, x, y, color): + self.pixels[y][x] = color + + +class _PyAccessI32_Swap(PyAccess): + """ I;32L/B access, with byteswapping conversion """ + def _post_init(self, *args, **kwargs): + self.pixels = self.image32 + + def reverse(self, i): + orig = ffi.new('int *', i) + chars = ffi.cast('unsigned char *', orig) + chars[0], chars[1], chars[2], chars[3] = chars[3], chars[2], \ + chars[1], chars[0] + return ffi.cast('int *', chars)[0] + + def get_pixel(self, x, y): + return self.reverse(self.pixels[y][x]) + + def set_pixel(self, x, y, color): + self.pixels[y][x] = self.reverse(color) + + +class _PyAccessF(PyAccess): + """ 32 bit float access """ + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast('float **', self.image32) + + def get_pixel(self, x, y): + return self.pixels[y][x] + + def set_pixel(self, x, y, color): + try: + # not a tuple + self.pixels[y][x] = color + except: + # tuple + self.pixels[y][x] = color[0] + + +mode_map = {'1': _PyAccess8, + 'L': _PyAccess8, + 'P': _PyAccess8, + 'LA': _PyAccess32_2, + 'PA': _PyAccess32_2, + 'RGB': _PyAccess32_3, + 'LAB': _PyAccess32_3, + 'HSV': _PyAccess32_3, + 'YCbCr': _PyAccess32_3, + 'RGBA': _PyAccess32_4, + 'RGBa': _PyAccess32_4, + 'RGBX': _PyAccess32_4, + 'CMYK': _PyAccess32_4, + 'F': _PyAccessF, + 'I': _PyAccessI32_N, + } + +if sys.byteorder == 'little': + mode_map['I;16'] = _PyAccessI16_N + mode_map['I;16L'] = _PyAccessI16_N + mode_map['I;16B'] = _PyAccessI16_B + + mode_map['I;32L'] = _PyAccessI32_N + mode_map['I;32B'] = _PyAccessI32_Swap +else: + mode_map['I;16'] = _PyAccessI16_L + mode_map['I;16L'] = _PyAccessI16_L + mode_map['I;16B'] = _PyAccessI16_N + + mode_map['I;32L'] = _PyAccessI32_Swap + mode_map['I;32B'] = _PyAccessI32_N + + +def new(img, readonly=False): + access_type = mode_map.get(img.mode, None) + if not access_type: + if DEBUG: + print("PyAccess Not Implemented: %s" % img.mode) + return None + if DEBUG: + print("New PyAccess: %s" % img.mode) + return access_type(img, readonly) + +# End of file diff --git a/pyPackages/pillowarmv7l/PIL/SgiImagePlugin.py b/pyPackages/pillowarmv7l/PIL/SgiImagePlugin.py new file mode 100644 index 0000000..2b8fcd8 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/SgiImagePlugin.py @@ -0,0 +1,91 @@ +# +# The Python Imaging Library. +# $Id$ +# +# SGI image file handling +# +# See "The SGI Image File Format (Draft version 0.97)", Paul Haeberli. +# +# +# History: +# 1995-09-10 fl Created +# +# Copyright (c) 2008 by Karsten Hiddemann. +# Copyright (c) 1997 by Secret Labs AB. +# Copyright (c) 1995 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.2" + + +from PIL import Image, ImageFile, _binary + +i8 = _binary.i8 +i16 = _binary.i16be +i32 = _binary.i32be + + +def _accept(prefix): + return i16(prefix) == 474 + + +## +# Image plugin for SGI images. + +class SgiImageFile(ImageFile.ImageFile): + + format = "SGI" + format_description = "SGI Image File Format" + + def _open(self): + + # HEAD + s = self.fp.read(512) + if i16(s) != 474: + raise ValueError("Not an SGI image file") + + # relevant header entries + compression = i8(s[2]) + + # bytes, dimension, zsize + layout = i8(s[3]), i16(s[4:]), i16(s[10:]) + + # determine mode from bytes/zsize + if layout == (1, 2, 1) or layout == (1, 1, 1): + self.mode = "L" + elif layout == (1, 3, 3): + self.mode = "RGB" + elif layout == (1, 3, 4): + self.mode = "RGBA" + else: + raise ValueError("Unsupported SGI image mode") + + # size + self.size = i16(s[6:]), i16(s[8:]) + + # decoder info + if compression == 0: + offset = 512 + pagesize = self.size[0]*self.size[1]*layout[0] + self.tile = [] + for layer in self.mode: + self.tile.append( + ("raw", (0, 0)+self.size, offset, (layer, 0, -1))) + offset = offset + pagesize + elif compression == 1: + raise ValueError("SGI RLE encoding not supported") + +# +# registry + +Image.register_open("SGI", SgiImageFile, _accept) + +Image.register_extension("SGI", ".bw") +Image.register_extension("SGI", ".rgb") +Image.register_extension("SGI", ".rgba") +Image.register_extension("SGI", ".sgi") + +# End of file diff --git a/pyPackages/pillowarmv7l/PIL/SpiderImagePlugin.py b/pyPackages/pillowarmv7l/PIL/SpiderImagePlugin.py new file mode 100644 index 0000000..306b348 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/SpiderImagePlugin.py @@ -0,0 +1,312 @@ +# +# The Python Imaging Library. +# +# SPIDER image file handling +# +# History: +# 2004-08-02 Created BB +# 2006-03-02 added save method +# 2006-03-13 added support for stack images +# +# Copyright (c) 2004 by Health Research Inc. (HRI) RENSSELAER, NY 12144. +# Copyright (c) 2004 by William Baxter. +# Copyright (c) 2004 by Secret Labs AB. +# Copyright (c) 2004 by Fredrik Lundh. +# + +## +# Image plugin for the Spider image format. This format is is used +# by the SPIDER software, in processing image data from electron +# microscopy and tomography. +## + +# +# SpiderImagePlugin.py +# +# The Spider image format is used by SPIDER software, in processing +# image data from electron microscopy and tomography. +# +# Spider home page: +# http://www.wadsworth.org/spider_doc/spider/docs/spider.html +# +# Details about the Spider image format: +# http://www.wadsworth.org/spider_doc/spider/docs/image_doc.html +# + +from __future__ import print_function + +from PIL import Image, ImageFile +import os +import struct +import sys + + +def isInt(f): + try: + i = int(f) + if f-i == 0: + return 1 + else: + return 0 + except: + return 0 + +iforms = [1, 3, -11, -12, -21, -22] + + +# There is no magic number to identify Spider files, so just check a +# series of header locations to see if they have reasonable values. +# Returns no.of bytes in the header, if it is a valid Spider header, +# otherwise returns 0 + +def isSpiderHeader(t): + h = (99,) + t # add 1 value so can use spider header index start=1 + # header values 1,2,5,12,13,22,23 should be integers + for i in [1, 2, 5, 12, 13, 22, 23]: + if not isInt(h[i]): + return 0 + # check iform + iform = int(h[5]) + if iform not in iforms: + return 0 + # check other header values + labrec = int(h[13]) # no. records in file header + labbyt = int(h[22]) # total no. of bytes in header + lenbyt = int(h[23]) # record length in bytes + # print "labrec = %d, labbyt = %d, lenbyt = %d" % (labrec,labbyt,lenbyt) + if labbyt != (labrec * lenbyt): + return 0 + # looks like a valid header + return labbyt + + +def isSpiderImage(filename): + fp = open(filename, 'rb') + f = fp.read(92) # read 23 * 4 bytes + fp.close() + t = struct.unpack('>23f', f) # try big-endian first + hdrlen = isSpiderHeader(t) + if hdrlen == 0: + t = struct.unpack('<23f', f) # little-endian + hdrlen = isSpiderHeader(t) + return hdrlen + + +class SpiderImageFile(ImageFile.ImageFile): + + format = "SPIDER" + format_description = "Spider 2D image" + + def _open(self): + # check header + n = 27 * 4 # read 27 float values + f = self.fp.read(n) + + try: + self.bigendian = 1 + t = struct.unpack('>27f', f) # try big-endian first + hdrlen = isSpiderHeader(t) + if hdrlen == 0: + self.bigendian = 0 + t = struct.unpack('<27f', f) # little-endian + hdrlen = isSpiderHeader(t) + if hdrlen == 0: + raise SyntaxError("not a valid Spider file") + except struct.error: + raise SyntaxError("not a valid Spider file") + + h = (99,) + t # add 1 value : spider header index starts at 1 + iform = int(h[5]) + if iform != 1: + raise SyntaxError("not a Spider 2D image") + + self.size = int(h[12]), int(h[2]) # size in pixels (width, height) + self.istack = int(h[24]) + self.imgnumber = int(h[27]) + + if self.istack == 0 and self.imgnumber == 0: + # stk=0, img=0: a regular 2D image + offset = hdrlen + self.nimages = 1 + elif self.istack > 0 and self.imgnumber == 0: + # stk>0, img=0: Opening the stack for the first time + self.imgbytes = int(h[12]) * int(h[2]) * 4 + self.hdrlen = hdrlen + self.nimages = int(h[26]) + # Point to the first image in the stack + offset = hdrlen * 2 + self.imgnumber = 1 + elif self.istack == 0 and self.imgnumber > 0: + # stk=0, img>0: an image within the stack + offset = hdrlen + self.stkoffset + self.istack = 2 # So Image knows it's still a stack + else: + raise SyntaxError("inconsistent stack header values") + + if self.bigendian: + self.rawmode = "F;32BF" + else: + self.rawmode = "F;32F" + self.mode = "F" + + self.tile = [ + ("raw", (0, 0) + self.size, offset, + (self.rawmode, 0, 1))] + self.__fp = self.fp # FIXME: hack + + # 1st image index is zero (although SPIDER imgnumber starts at 1) + def tell(self): + if self.imgnumber < 1: + return 0 + else: + return self.imgnumber - 1 + + def seek(self, frame): + if self.istack == 0: + return + if frame >= self.nimages: + raise EOFError("attempt to seek past end of file") + self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes) + self.fp = self.__fp + self.fp.seek(self.stkoffset) + self._open() + + # returns a byte image after rescaling to 0..255 + def convert2byte(self, depth=255): + (min, max) = self.getextrema() + m = 1 + if max != min: + m = depth / (max-min) + b = -m * min + return self.point(lambda i, m=m, b=b: i * m + b).convert("L") + + # returns a ImageTk.PhotoImage object, after rescaling to 0..255 + def tkPhotoImage(self): + from PIL import ImageTk + return ImageTk.PhotoImage(self.convert2byte(), palette=256) + + +# -------------------------------------------------------------------- +# Image series + +# given a list of filenames, return a list of images +def loadImageSeries(filelist=None): + " create a list of Image.images for use in montage " + if filelist is None or len(filelist) < 1: + return + + imglist = [] + for img in filelist: + if not os.path.exists(img): + print("unable to find %s" % img) + continue + try: + im = Image.open(img).convert2byte() + except: + if not isSpiderImage(img): + print(img + " is not a Spider image file") + continue + im.info['filename'] = img + imglist.append(im) + return imglist + + +# -------------------------------------------------------------------- +# For saving images in Spider format + +def makeSpiderHeader(im): + nsam, nrow = im.size + lenbyt = nsam * 4 # There are labrec records in the header + labrec = 1024 / lenbyt + if 1024 % lenbyt != 0: + labrec += 1 + labbyt = labrec * lenbyt + hdr = [] + nvalues = int(labbyt / 4) + for i in range(nvalues): + hdr.append(0.0) + + if len(hdr) < 23: + return [] + + # NB these are Fortran indices + hdr[1] = 1.0 # nslice (=1 for an image) + hdr[2] = float(nrow) # number of rows per slice + hdr[5] = 1.0 # iform for 2D image + hdr[12] = float(nsam) # number of pixels per line + hdr[13] = float(labrec) # number of records in file header + hdr[22] = float(labbyt) # total number of bytes in header + hdr[23] = float(lenbyt) # record length in bytes + + # adjust for Fortran indexing + hdr = hdr[1:] + hdr.append(0.0) + # pack binary data into a string + hdrstr = [] + for v in hdr: + hdrstr.append(struct.pack('f', v)) + return hdrstr + + +def _save(im, fp, filename): + if im.mode[0] != "F": + im = im.convert('F') + + hdr = makeSpiderHeader(im) + if len(hdr) < 256: + raise IOError("Error creating Spider header") + + # write the SPIDER header + try: + fp = open(filename, 'wb') + except: + raise IOError("Unable to open %s for writing" % filename) + fp.writelines(hdr) + + rawmode = "F;32NF" # 32-bit native floating point + ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0, (rawmode, 0, 1))]) + + fp.close() + + +def _save_spider(im, fp, filename): + # get the filename extension and register it with Image + fn, ext = os.path.splitext(filename) + Image.register_extension("SPIDER", ext) + _save(im, fp, filename) + +# -------------------------------------------------------------------- + +Image.register_open("SPIDER", SpiderImageFile) +Image.register_save("SPIDER", _save_spider) + +if __name__ == "__main__": + + if not sys.argv[1:]: + print("Syntax: python SpiderImagePlugin.py Spiderimage [outfile]") + sys.exit() + + filename = sys.argv[1] + if not isSpiderImage(filename): + print("input image must be in Spider format") + sys.exit() + + outfile = "" + if len(sys.argv[1:]) > 1: + outfile = sys.argv[2] + + im = Image.open(filename) + print("image: " + str(im)) + print("format: " + str(im.format)) + print("size: " + str(im.size)) + print("mode: " + str(im.mode)) + print("max, min: ", end=' ') + print(im.getextrema()) + + if outfile != "": + # perform some image operation + im = im.transpose(Image.FLIP_LEFT_RIGHT) + print( + "saving a flipped version of %s as %s " % + (os.path.basename(filename), outfile)) + im.save(outfile, "SPIDER") diff --git a/pyPackages/pillowarmv7l/PIL/SunImagePlugin.py b/pyPackages/pillowarmv7l/PIL/SunImagePlugin.py new file mode 100644 index 0000000..e0a7aa6 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/SunImagePlugin.py @@ -0,0 +1,83 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Sun image file handling +# +# History: +# 1995-09-10 fl Created +# 1996-05-28 fl Fixed 32-bit alignment +# 1998-12-29 fl Import ImagePalette module +# 2001-12-18 fl Fixed palette loading (from Jean-Claude Rimbault) +# +# Copyright (c) 1997-2001 by Secret Labs AB +# Copyright (c) 1995-1996 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.3" + + +from PIL import Image, ImageFile, ImagePalette, _binary + +i16 = _binary.i16be +i32 = _binary.i32be + + +def _accept(prefix): + return i32(prefix) == 0x59a66a95 + + +## +# Image plugin for Sun raster files. + +class SunImageFile(ImageFile.ImageFile): + + format = "SUN" + format_description = "Sun Raster File" + + def _open(self): + + # HEAD + s = self.fp.read(32) + if i32(s) != 0x59a66a95: + raise SyntaxError("not an SUN raster file") + + offset = 32 + + self.size = i32(s[4:8]), i32(s[8:12]) + + depth = i32(s[12:16]) + if depth == 1: + self.mode, rawmode = "1", "1;I" + elif depth == 8: + self.mode = rawmode = "L" + elif depth == 24: + self.mode, rawmode = "RGB", "BGR" + else: + raise SyntaxError("unsupported mode") + + compression = i32(s[20:24]) + + if i32(s[24:28]) != 0: + length = i32(s[28:32]) + offset = offset + length + self.palette = ImagePalette.raw("RGB;L", self.fp.read(length)) + if self.mode == "L": + self.mode = rawmode = "P" + + stride = (((self.size[0] * depth + 7) // 8) + 3) & (~3) + + if compression == 1: + self.tile = [("raw", (0, 0)+self.size, offset, (rawmode, stride))] + elif compression == 2: + self.tile = [("sun_rle", (0, 0)+self.size, offset, rawmode)] + +# +# registry + +Image.register_open("SUN", SunImageFile, _accept) + +Image.register_extension("SUN", ".ras") diff --git a/pyPackages/pillowarmv7l/PIL/TarIO.py b/pyPackages/pillowarmv7l/PIL/TarIO.py new file mode 100644 index 0000000..4e5115b --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/TarIO.py @@ -0,0 +1,57 @@ +# +# The Python Imaging Library. +# $Id$ +# +# read files from within a tar file +# +# History: +# 95-06-18 fl Created +# 96-05-28 fl Open files in binary mode +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1995-96. +# +# See the README file for information on usage and redistribution. +# + +from PIL import ContainerIO + + +## +# A file object that provides read access to a given member of a TAR +# file. + +class TarIO(ContainerIO.ContainerIO): + + ## + # Create file object. + # + # @param tarfile Name of TAR file. + # @param file Name of member file. + + def __init__(self, tarfile, file): + + fh = open(tarfile, "rb") + + while True: + + s = fh.read(512) + if len(s) != 512: + raise IOError("unexpected end of tar file") + + name = s[:100].decode('utf-8') + i = name.find('\0') + if i == 0: + raise IOError("cannot find subfile") + if i > 0: + name = name[:i] + + size = int(s[124:135], 8) + + if file == name: + break + + fh.seek((size + 511) & (~511), 1) + + # Open region + ContainerIO.ContainerIO.__init__(self, fh, fh.tell(), size) diff --git a/pyPackages/pillowarmv7l/PIL/TgaImagePlugin.py b/pyPackages/pillowarmv7l/PIL/TgaImagePlugin.py new file mode 100644 index 0000000..46eafe8 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/TgaImagePlugin.py @@ -0,0 +1,199 @@ +# +# The Python Imaging Library. +# $Id$ +# +# TGA file handling +# +# History: +# 95-09-01 fl created (reads 24-bit files only) +# 97-01-04 fl support more TGA versions, including compressed images +# 98-07-04 fl fixed orientation and alpha layer bugs +# 98-09-11 fl fixed orientation for runlength decoder +# +# Copyright (c) Secret Labs AB 1997-98. +# Copyright (c) Fredrik Lundh 1995-97. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.3" + +from PIL import Image, ImageFile, ImagePalette, _binary + + +# +# -------------------------------------------------------------------- +# Read RGA file + +i8 = _binary.i8 +i16 = _binary.i16le +i32 = _binary.i32le + + +MODES = { + # map imagetype/depth to rawmode + (1, 8): "P", + (3, 1): "1", + (3, 8): "L", + (2, 16): "BGR;5", + (2, 24): "BGR", + (2, 32): "BGRA", +} + + +## +# Image plugin for Targa files. + +class TgaImageFile(ImageFile.ImageFile): + + format = "TGA" + format_description = "Targa" + + def _open(self): + + # process header + s = self.fp.read(18) + + idlen = i8(s[0]) + + colormaptype = i8(s[1]) + imagetype = i8(s[2]) + + depth = i8(s[16]) + + flags = i8(s[17]) + + self.size = i16(s[12:]), i16(s[14:]) + + # validate header fields + if colormaptype not in (0, 1) or\ + self.size[0] <= 0 or self.size[1] <= 0 or\ + depth not in (1, 8, 16, 24, 32): + raise SyntaxError("not a TGA file") + + # image mode + if imagetype in (3, 11): + self.mode = "L" + if depth == 1: + self.mode = "1" # ??? + elif imagetype in (1, 9): + self.mode = "P" + elif imagetype in (2, 10): + self.mode = "RGB" + if depth == 32: + self.mode = "RGBA" + else: + raise SyntaxError("unknown TGA mode") + + # orientation + orientation = flags & 0x30 + if orientation == 0x20: + orientation = 1 + elif not orientation: + orientation = -1 + else: + raise SyntaxError("unknown TGA orientation") + + self.info["orientation"] = orientation + + if imagetype & 8: + self.info["compression"] = "tga_rle" + + if idlen: + self.info["id_section"] = self.fp.read(idlen) + + if colormaptype: + # read palette + start, size, mapdepth = i16(s[3:]), i16(s[5:]), i16(s[7:]) + if mapdepth == 16: + self.palette = ImagePalette.raw( + "BGR;16", b"\0"*2*start + self.fp.read(2*size)) + elif mapdepth == 24: + self.palette = ImagePalette.raw( + "BGR", b"\0"*3*start + self.fp.read(3*size)) + elif mapdepth == 32: + self.palette = ImagePalette.raw( + "BGRA", b"\0"*4*start + self.fp.read(4*size)) + + # setup tile descriptor + try: + rawmode = MODES[(imagetype & 7, depth)] + if imagetype & 8: + # compressed + self.tile = [("tga_rle", (0, 0)+self.size, + self.fp.tell(), (rawmode, orientation, depth))] + else: + self.tile = [("raw", (0, 0)+self.size, + self.fp.tell(), (rawmode, 0, orientation))] + except KeyError: + pass # cannot decode + +# +# -------------------------------------------------------------------- +# Write TGA file + +o8 = _binary.o8 +o16 = _binary.o16le +o32 = _binary.o32le + +SAVE = { + "1": ("1", 1, 0, 3), + "L": ("L", 8, 0, 3), + "P": ("P", 8, 1, 1), + "RGB": ("BGR", 24, 0, 2), + "RGBA": ("BGRA", 32, 0, 2), +} + + +def _save(im, fp, filename, check=0): + + try: + rawmode, bits, colormaptype, imagetype = SAVE[im.mode] + except KeyError: + raise IOError("cannot write mode %s as TGA" % im.mode) + + if check: + return check + + if colormaptype: + colormapfirst, colormaplength, colormapentry = 0, 256, 24 + else: + colormapfirst, colormaplength, colormapentry = 0, 0, 0 + + if im.mode == "RGBA": + flags = 8 + else: + flags = 0 + + orientation = im.info.get("orientation", -1) + if orientation > 0: + flags = flags | 0x20 + + fp.write(b"\000" + + o8(colormaptype) + + o8(imagetype) + + o16(colormapfirst) + + o16(colormaplength) + + o8(colormapentry) + + o16(0) + + o16(0) + + o16(im.size[0]) + + o16(im.size[1]) + + o8(bits) + + o8(flags)) + + if colormaptype: + fp.write(im.im.getpalette("RGB", "BGR")) + + ImageFile._save( + im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, 0, orientation))]) + +# +# -------------------------------------------------------------------- +# Registry + +Image.register_open("TGA", TgaImageFile) +Image.register_save("TGA", _save) + +Image.register_extension("TGA", ".tga") diff --git a/pyPackages/pillowarmv7l/PIL/TiffImagePlugin.py b/pyPackages/pillowarmv7l/PIL/TiffImagePlugin.py new file mode 100644 index 0000000..a533c27 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/TiffImagePlugin.py @@ -0,0 +1,1225 @@ +# +# The Python Imaging Library. +# $Id$ +# +# TIFF file handling +# +# TIFF is a flexible, if somewhat aged, image file format originally +# defined by Aldus. Although TIFF supports a wide variety of pixel +# layouts and compression methods, the name doesn't really stand for +# "thousands of incompatible file formats," it just feels that way. +# +# To read TIFF data from a stream, the stream must be seekable. For +# progressive decoding, make sure to use TIFF files where the tag +# directory is placed first in the file. +# +# History: +# 1995-09-01 fl Created +# 1996-05-04 fl Handle JPEGTABLES tag +# 1996-05-18 fl Fixed COLORMAP support +# 1997-01-05 fl Fixed PREDICTOR support +# 1997-08-27 fl Added support for rational tags (from Perry Stoll) +# 1998-01-10 fl Fixed seek/tell (from Jan Blom) +# 1998-07-15 fl Use private names for internal variables +# 1999-06-13 fl Rewritten for PIL 1.0 (1.0) +# 2000-10-11 fl Additional fixes for Python 2.0 (1.1) +# 2001-04-17 fl Fixed rewind support (seek to frame 0) (1.2) +# 2001-05-12 fl Added write support for more tags (from Greg Couch) (1.3) +# 2001-12-18 fl Added workaround for broken Matrox library +# 2002-01-18 fl Don't mess up if photometric tag is missing (D. Alan Stewart) +# 2003-05-19 fl Check FILLORDER tag +# 2003-09-26 fl Added RGBa support +# 2004-02-24 fl Added DPI support; fixed rational write support +# 2005-02-07 fl Added workaround for broken Corel Draw 10 files +# 2006-01-09 fl Added support for float/double tags (from Russell Nelson) +# +# Copyright (c) 1997-2006 by Secret Labs AB. All rights reserved. +# Copyright (c) 1995-1997 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from __future__ import print_function + +__version__ = "1.3.5" + +from PIL import Image, ImageFile +from PIL import ImagePalette +from PIL import _binary +from PIL._util import isStringType + +import warnings +import array +import sys +import collections +import itertools +import os +import io + +# Set these to true to force use of libtiff for reading or writing. +READ_LIBTIFF = False +WRITE_LIBTIFF = False + +II = b"II" # little-endian (Intel style) +MM = b"MM" # big-endian (Motorola style) + +i8 = _binary.i8 +o8 = _binary.o8 + +if sys.byteorder == "little": + native_prefix = II +else: + native_prefix = MM + +# +# -------------------------------------------------------------------- +# Read TIFF files + +il16 = _binary.i16le +il32 = _binary.i32le +ol16 = _binary.o16le +ol32 = _binary.o32le + +ib16 = _binary.i16be +ib32 = _binary.i32be +ob16 = _binary.o16be +ob32 = _binary.o32be + +# a few tag names, just to make the code below a bit more readable +IMAGEWIDTH = 256 +IMAGELENGTH = 257 +BITSPERSAMPLE = 258 +COMPRESSION = 259 +PHOTOMETRIC_INTERPRETATION = 262 +FILLORDER = 266 +IMAGEDESCRIPTION = 270 +STRIPOFFSETS = 273 +SAMPLESPERPIXEL = 277 +ROWSPERSTRIP = 278 +STRIPBYTECOUNTS = 279 +X_RESOLUTION = 282 +Y_RESOLUTION = 283 +PLANAR_CONFIGURATION = 284 +RESOLUTION_UNIT = 296 +SOFTWARE = 305 +DATE_TIME = 306 +ARTIST = 315 +PREDICTOR = 317 +COLORMAP = 320 +TILEOFFSETS = 324 +EXTRASAMPLES = 338 +SAMPLEFORMAT = 339 +JPEGTABLES = 347 +COPYRIGHT = 33432 +IPTC_NAA_CHUNK = 33723 # newsphoto properties +PHOTOSHOP_CHUNK = 34377 # photoshop properties +ICCPROFILE = 34675 +EXIFIFD = 34665 +XMP = 700 + +# https://github.com/fiji/ImageJA/blob/master/src/main/java/ij/io/TiffDecoder.java +IMAGEJ_META_DATA_BYTE_COUNTS = 50838 +IMAGEJ_META_DATA = 50839 + +COMPRESSION_INFO = { + # Compression => pil compression name + 1: "raw", + 2: "tiff_ccitt", + 3: "group3", + 4: "group4", + 5: "tiff_lzw", + 6: "tiff_jpeg", # obsolete + 7: "jpeg", + 8: "tiff_adobe_deflate", + 32771: "tiff_raw_16", # 16-bit padding + 32773: "packbits", + 32809: "tiff_thunderscan", + 32946: "tiff_deflate", + 34676: "tiff_sgilog", + 34677: "tiff_sgilog24", +} + +COMPRESSION_INFO_REV = dict([(v, k) for (k, v) in COMPRESSION_INFO.items()]) + +OPEN_INFO = { + # (ByteOrder, PhotoInterpretation, SampleFormat, FillOrder, BitsPerSample, + # ExtraSamples) => mode, rawmode + (II, 0, 1, 1, (1,), ()): ("1", "1;I"), + (II, 0, 1, 2, (1,), ()): ("1", "1;IR"), + (II, 0, 1, 1, (8,), ()): ("L", "L;I"), + (II, 0, 1, 2, (8,), ()): ("L", "L;IR"), + (II, 0, 3, 1, (32,), ()): ("F", "F;32F"), + (II, 1, 1, 1, (1,), ()): ("1", "1"), + (II, 1, 1, 1, (4,), ()): ("L", "L;4"), + (II, 1, 1, 2, (1,), ()): ("1", "1;R"), + (II, 1, 1, 1, (8,), ()): ("L", "L"), + (II, 1, 1, 1, (8, 8), (2,)): ("LA", "LA"), + (II, 1, 1, 2, (8,), ()): ("L", "L;R"), + (II, 1, 1, 1, (12,), ()): ("I;16", "I;12"), + (II, 1, 1, 1, (16,), ()): ("I;16", "I;16"), + (II, 1, 2, 1, (16,), ()): ("I;16S", "I;16S"), + (II, 1, 1, 1, (32,), ()): ("I", "I;32N"), + (II, 1, 2, 1, (32,), ()): ("I", "I;32S"), + (II, 1, 3, 1, (32,), ()): ("F", "F;32F"), + (II, 2, 1, 1, (8, 8, 8), ()): ("RGB", "RGB"), + (II, 2, 1, 2, (8, 8, 8), ()): ("RGB", "RGB;R"), + (II, 2, 1, 1, (8, 8, 8, 8), ()): ("RGBA", "RGBA"), # missing ExtraSamples + (II, 2, 1, 1, (8, 8, 8, 8), (0,)): ("RGBX", "RGBX"), + (II, 2, 1, 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"), + (II, 2, 1, 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"), + (II, 2, 1, 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10 + (II, 3, 1, 1, (1,), ()): ("P", "P;1"), + (II, 3, 1, 2, (1,), ()): ("P", "P;1R"), + (II, 3, 1, 1, (2,), ()): ("P", "P;2"), + (II, 3, 1, 2, (2,), ()): ("P", "P;2R"), + (II, 3, 1, 1, (4,), ()): ("P", "P;4"), + (II, 3, 1, 2, (4,), ()): ("P", "P;4R"), + (II, 3, 1, 1, (8,), ()): ("P", "P"), + (II, 3, 1, 1, (8, 8), (2,)): ("PA", "PA"), + (II, 3, 1, 2, (8,), ()): ("P", "P;R"), + (II, 5, 1, 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"), + (II, 6, 1, 1, (8, 8, 8), ()): ("YCbCr", "YCbCr"), + (II, 8, 1, 1, (8, 8, 8), ()): ("LAB", "LAB"), + + (MM, 0, 1, 1, (1,), ()): ("1", "1;I"), + (MM, 0, 1, 2, (1,), ()): ("1", "1;IR"), + (MM, 0, 1, 1, (8,), ()): ("L", "L;I"), + (MM, 0, 1, 2, (8,), ()): ("L", "L;IR"), + (MM, 1, 1, 1, (1,), ()): ("1", "1"), + (MM, 1, 1, 2, (1,), ()): ("1", "1;R"), + (MM, 1, 1, 1, (8,), ()): ("L", "L"), + (MM, 1, 1, 1, (8, 8), (2,)): ("LA", "LA"), + (MM, 1, 1, 2, (8,), ()): ("L", "L;R"), + (MM, 1, 1, 1, (16,), ()): ("I;16B", "I;16B"), + (MM, 1, 2, 1, (16,), ()): ("I;16BS", "I;16BS"), + (MM, 1, 2, 1, (32,), ()): ("I;32BS", "I;32BS"), + (MM, 1, 3, 1, (32,), ()): ("F", "F;32BF"), + (MM, 2, 1, 1, (8, 8, 8), ()): ("RGB", "RGB"), + (MM, 2, 1, 2, (8, 8, 8), ()): ("RGB", "RGB;R"), + (MM, 2, 1, 1, (8, 8, 8, 8), (0,)): ("RGBX", "RGBX"), + (MM, 2, 1, 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"), + (MM, 2, 1, 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"), + (MM, 2, 1, 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10 + (MM, 3, 1, 1, (1,), ()): ("P", "P;1"), + (MM, 3, 1, 2, (1,), ()): ("P", "P;1R"), + (MM, 3, 1, 1, (2,), ()): ("P", "P;2"), + (MM, 3, 1, 2, (2,), ()): ("P", "P;2R"), + (MM, 3, 1, 1, (4,), ()): ("P", "P;4"), + (MM, 3, 1, 2, (4,), ()): ("P", "P;4R"), + (MM, 3, 1, 1, (8,), ()): ("P", "P"), + (MM, 3, 1, 1, (8, 8), (2,)): ("PA", "PA"), + (MM, 3, 1, 2, (8,), ()): ("P", "P;R"), + (MM, 5, 1, 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"), + (MM, 6, 1, 1, (8, 8, 8), ()): ("YCbCr", "YCbCr"), + (MM, 8, 1, 1, (8, 8, 8), ()): ("LAB", "LAB"), + +} + +PREFIXES = [b"MM\000\052", b"II\052\000", b"II\xBC\000"] + + +def _accept(prefix): + return prefix[:4] in PREFIXES + + +## +# Wrapper for TIFF IFDs. + +class ImageFileDirectory(collections.MutableMapping): + """ This class represents a TIFF tag directory. To speed things + up, we don't decode tags unless they're asked for. + + Exposes a dictionary interface of the tags in the directory + ImageFileDirectory[key] = value + value = ImageFileDirectory[key] + + Also contains a dictionary of tag types as read from the tiff + image file, 'ImageFileDirectory.tagtype' + + + Data Structures: + 'public' + * self.tagtype = {} Key: numerical tiff tag number + Value: integer corresponding to the data type from + `TiffTags.TYPES` + + 'internal' + * self.tags = {} Key: numerical tiff tag number + Value: Decoded data, Generally a tuple. + * If set from __setval__ -- always a tuple + * Numeric types -- always a tuple + * String type -- not a tuple, returned as string + * Undefined data -- not a tuple, returned as bytes + * Byte -- not a tuple, returned as byte. + * self.tagdata = {} Key: numerical tiff tag number + Value: undecoded byte string from file + + + Tags will be found in either self.tags or self.tagdata, but + not both. The union of the two should contain all the tags + from the Tiff image file. External classes shouldn't + reference these unless they're really sure what they're doing. + """ + + def __init__(self, prefix=II): + """ + :prefix: 'II'|'MM' tiff endianness + """ + self.prefix = prefix[:2] + if self.prefix == MM: + self.i16, self.i32 = ib16, ib32 + self.o16, self.o32 = ob16, ob32 + elif self.prefix == II: + self.i16, self.i32 = il16, il32 + self.o16, self.o32 = ol16, ol32 + else: + raise SyntaxError("not a TIFF IFD") + self.reset() + + def reset(self): + #: Tags is an incomplete dictionary of the tags of the image. + #: For a complete dictionary, use the as_dict method. + self.tags = {} + self.tagdata = {} + self.tagtype = {} # added 2008-06-05 by Florian Hoech + self.next = None + self.offset = None + + def __str__(self): + return str(self.as_dict()) + + def as_dict(self): + """Return a dictionary of the image's tags.""" + return dict(self.items()) + + def named(self): + """ + Returns the complete tag dictionary, with named tags where posible. + """ + from PIL import TiffTags + result = {} + for tag_code, value in self.items(): + tag_name = TiffTags.TAGS.get(tag_code, tag_code) + result[tag_name] = value + return result + + # dictionary API + + def __len__(self): + return len(self.tagdata) + len(self.tags) + + def __getitem__(self, tag): + try: + return self.tags[tag] + except KeyError: + data = self.tagdata[tag] # unpack on the fly + type = self.tagtype[tag] + size, handler = self.load_dispatch[type] + self.tags[tag] = data = handler(self, data) + del self.tagdata[tag] + return data + + def getscalar(self, tag, default=None): + try: + value = self[tag] + if len(value) != 1: + if tag == SAMPLEFORMAT: + # work around broken (?) matrox library + # (from Ted Wright, via Bob Klimek) + raise KeyError # use default + raise ValueError("not a scalar") + return value[0] + except KeyError: + if default is None: + raise + return default + + def __contains__(self, tag): + return tag in self.tags or tag in self.tagdata + + if bytes is str: + def has_key(self, tag): + return tag in self + + def __setitem__(self, tag, value): + # tags are tuples for integers + # tags are not tuples for byte, string, and undefined data. + # see load_* + if not isinstance(value, tuple): + value = (value,) + self.tags[tag] = value + + def __delitem__(self, tag): + self.tags.pop(tag, self.tagdata.pop(tag, None)) + + def __iter__(self): + return itertools.chain(self.tags.__iter__(), self.tagdata.__iter__()) + + def items(self): + keys = list(self.__iter__()) + values = [self[key] for key in keys] + return zip(keys, values) + + # load primitives + + load_dispatch = {} + + def load_byte(self, data): + return data + load_dispatch[1] = (1, load_byte) + + def load_string(self, data): + if data[-1:] == b'\0': + data = data[:-1] + return data.decode('latin-1', 'replace') + load_dispatch[2] = (1, load_string) + + def load_short(self, data): + l = [] + for i in range(0, len(data), 2): + l.append(self.i16(data, i)) + return tuple(l) + load_dispatch[3] = (2, load_short) + + def load_long(self, data): + l = [] + for i in range(0, len(data), 4): + l.append(self.i32(data, i)) + return tuple(l) + load_dispatch[4] = (4, load_long) + + def load_rational(self, data): + l = [] + for i in range(0, len(data), 8): + l.append((self.i32(data, i), self.i32(data, i+4))) + return tuple(l) + load_dispatch[5] = (8, load_rational) + + def load_float(self, data): + a = array.array("f", data) + if self.prefix != native_prefix: + a.byteswap() + return tuple(a) + load_dispatch[11] = (4, load_float) + + def load_double(self, data): + a = array.array("d", data) + if self.prefix != native_prefix: + a.byteswap() + return tuple(a) + load_dispatch[12] = (8, load_double) + + def load_undefined(self, data): + # Untyped data + return data + load_dispatch[7] = (1, load_undefined) + + def load(self, fp): + # load tag dictionary + + self.reset() + self.offset = fp.tell() + + i16 = self.i16 + i32 = self.i32 + + for i in range(i16(fp.read(2))): + + ifd = fp.read(12) + + tag, typ = i16(ifd), i16(ifd, 2) + + if Image.DEBUG: + from PIL import TiffTags + tagname = TiffTags.TAGS.get(tag, "unknown") + typname = TiffTags.TYPES.get(typ, "unknown") + print("tag: %s (%d)" % (tagname, tag), end=' ') + print("- type: %s (%d)" % (typname, typ), end=' ') + + try: + dispatch = self.load_dispatch[typ] + except KeyError: + if Image.DEBUG: + print("- unsupported type", typ) + continue # ignore unsupported type + + size, handler = dispatch + + size = size * i32(ifd, 4) + + # Get and expand tag value + if size > 4: + here = fp.tell() + if Image.DEBUG: + print("Tag Location: %s" % here) + fp.seek(i32(ifd, 8)) + if Image.DEBUG: + print("Data Location: %s" % fp.tell()) + data = ImageFile._safe_read(fp, size) + fp.seek(here) + else: + data = ifd[8:8+size] + + if len(data) != size: + warnings.warn("Possibly corrupt EXIF data. " + "Expecting to read %d bytes but only got %d. " + "Skipping tag %s" % (size, len(data), tag)) + continue + + self.tagdata[tag] = data + self.tagtype[tag] = typ + + if Image.DEBUG: + if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, + ICCPROFILE, XMP): + print("- value: " % size) + else: + print("- value:", self[tag]) + + self.next = i32(fp.read(4)) + + # save primitives + + def save(self, fp): + + o16 = self.o16 + o32 = self.o32 + + fp.write(o16(len(self.tags))) + + # always write in ascending tag order + tags = sorted(self.tags.items()) + + directory = [] + append = directory.append + + offset = fp.tell() + len(self.tags) * 12 + 4 + + stripoffsets = None + + # pass 1: convert tags to binary format + for tag, value in tags: + + typ = None + + if tag in self.tagtype: + typ = self.tagtype[tag] + + if Image.DEBUG: + print ("Tag %s, Type: %s, Value: %s" % (tag, typ, value)) + + if typ == 1: + # byte data + if isinstance(value, tuple): + data = value = value[-1] + else: + data = value + elif typ == 7: + # untyped data + data = value = b"".join(value) + elif isStringType(value[0]): + # string data + if isinstance(value, tuple): + value = value[-1] + typ = 2 + # was b'\0'.join(str), which led to \x00a\x00b sorts + # of strings which I don't see in in the wild tiffs + # and doesn't match the tiff spec: 8-bit byte that + # contains a 7-bit ASCII code; the last byte must be + # NUL (binary zero). Also, I don't think this was well + # excersized before. + data = value = b"" + value.encode('ascii', 'replace') + b"\0" + else: + # integer data + if tag == STRIPOFFSETS: + stripoffsets = len(directory) + typ = 4 # to avoid catch-22 + elif tag in (X_RESOLUTION, Y_RESOLUTION) or typ == 5: + # identify rational data fields + typ = 5 + if isinstance(value[0], tuple): + # long name for flatten + value = tuple(itertools.chain.from_iterable(value)) + elif not typ: + typ = 3 + for v in value: + if v >= 65536: + typ = 4 + if typ == 3: + data = b"".join(map(o16, value)) + else: + data = b"".join(map(o32, value)) + + if Image.DEBUG: + from PIL import TiffTags + tagname = TiffTags.TAGS.get(tag, "unknown") + typname = TiffTags.TYPES.get(typ, "unknown") + print("save: %s (%d)" % (tagname, tag), end=' ') + print("- type: %s (%d)" % (typname, typ), end=' ') + if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, + ICCPROFILE, XMP): + size = len(data) + print("- value: " % size) + else: + print("- value:", value) + + # figure out if data fits into the directory + if len(data) == 4: + append((tag, typ, len(value), data, b"")) + elif len(data) < 4: + append((tag, typ, len(value), data + (4-len(data))*b"\0", b"")) + else: + count = len(value) + if typ == 5: + count = count // 2 # adjust for rational data field + + append((tag, typ, count, o32(offset), data)) + offset += len(data) + if offset & 1: + offset += 1 # word padding + + # update strip offset data to point beyond auxiliary data + if stripoffsets is not None: + tag, typ, count, value, data = directory[stripoffsets] + assert not data, "multistrip support not yet implemented" + value = o32(self.i32(value) + offset) + directory[stripoffsets] = tag, typ, count, value, data + + # pass 2: write directory to file + for tag, typ, count, value, data in directory: + if Image.DEBUG > 1: + print(tag, typ, count, repr(value), repr(data)) + fp.write(o16(tag) + o16(typ) + o32(count) + value) + + # -- overwrite here for multi-page -- + fp.write(b"\0\0\0\0") # end of directory + + # pass 3: write auxiliary data to file + for tag, typ, count, value, data in directory: + fp.write(data) + if len(data) & 1: + fp.write(b"\0") + + return offset + + +## +# Image plugin for TIFF files. + +class TiffImageFile(ImageFile.ImageFile): + + format = "TIFF" + format_description = "Adobe TIFF" + + def _open(self): + "Open the first image in a TIFF file" + + # Header + ifh = self.fp.read(8) + + if ifh[:4] not in PREFIXES: + raise SyntaxError("not a TIFF file") + + # image file directory (tag dictionary) + self.tag = self.ifd = ImageFileDirectory(ifh[:2]) + + # setup frame pointers + self.__first = self.__next = self.ifd.i32(ifh, 4) + self.__frame = -1 + self.__fp = self.fp + + if Image.DEBUG: + print ("*** TiffImageFile._open ***") + print ("- __first:", self.__first) + print ("- ifh: ", ifh) + + # and load the first frame + self._seek(0) + + def seek(self, frame): + "Select a given frame as current image" + if frame < 0: + frame = 0 + self._seek(frame) + # Create a new core image object on second and + # subsequent frames in the image. Image may be + # different size/mode. + Image._decompression_bomb_check(self.size) + self.im = Image.core.new(self.mode, self.size) + + def tell(self): + "Return the current frame number" + return self._tell() + + def _seek(self, frame): + self.fp = self.__fp + if frame < self.__frame: + # rewind file + self.__frame = -1 + self.__next = self.__first + while self.__frame < frame: + if not self.__next: + raise EOFError("no more images in TIFF file") + if Image.DEBUG: + print("Seeking to frame %s, on frame %s, __next %s, location: %s" % + (frame, self.__frame, self.__next, self.fp.tell())) + # reset python3 buffered io handle in case fp + # was passed to libtiff, invalidating the buffer + self.fp.tell() + self.fp.seek(self.__next) + if Image.DEBUG: + print("Loading tags, location: %s" % self.fp.tell()) + self.tag.load(self.fp) + self.__next = self.tag.next + self.__frame += 1 + self._setup() + + def _tell(self): + return self.__frame + + def _decoder(self, rawmode, layer, tile=None): + "Setup decoder contexts" + + args = None + if rawmode == "RGB" and self._planar_configuration == 2: + rawmode = rawmode[layer] + compression = self._compression + if compression == "raw": + args = (rawmode, 0, 1) + elif compression == "jpeg": + args = rawmode, "" + if JPEGTABLES in self.tag: + # Hack to handle abbreviated JPEG headers + self.tile_prefix = self.tag[JPEGTABLES] + elif compression == "packbits": + args = rawmode + elif compression == "tiff_lzw": + args = rawmode + if 317 in self.tag: + # Section 14: Differencing Predictor + self.decoderconfig = (self.tag[PREDICTOR][0],) + + if ICCPROFILE in self.tag: + self.info['icc_profile'] = self.tag[ICCPROFILE] + + return args + + def _load_libtiff(self): + """ Overload method triggered when we detect a compressed tiff + Calls out to libtiff """ + + pixel = Image.Image.load(self) + + if self.tile is None: + raise IOError("cannot load this image") + if not self.tile: + return pixel + + self.load_prepare() + + if not len(self.tile) == 1: + raise IOError("Not exactly one tile") + + # (self._compression, (extents tuple), + # 0, (rawmode, self._compression, fp)) + ignored, extents, ignored_2, args = self.tile[0] + args = args + (self.ifd.offset,) + decoder = Image._getdecoder(self.mode, 'libtiff', args, + self.decoderconfig) + try: + decoder.setimage(self.im, extents) + except ValueError: + raise IOError("Couldn't set the image") + + if hasattr(self.fp, "getvalue"): + # We've got a stringio like thing passed in. Yay for all in memory. + # The decoder needs the entire file in one shot, so there's not + # a lot we can do here other than give it the entire file. + # unless we could do something like get the address of the + # underlying string for stringio. + # + # Rearranging for supporting byteio items, since they have a fileno + # that returns an IOError if there's no underlying fp. Easier to + # dea. with here by reordering. + if Image.DEBUG: + print ("have getvalue. just sending in a string from getvalue") + n, err = decoder.decode(self.fp.getvalue()) + elif hasattr(self.fp, "fileno"): + # we've got a actual file on disk, pass in the fp. + if Image.DEBUG: + print ("have fileno, calling fileno version of the decoder.") + self.fp.seek(0) + # 4 bytes, otherwise the trace might error out + n, err = decoder.decode(b"fpfp") + else: + # we have something else. + if Image.DEBUG: + print ("don't have fileno or getvalue. just reading") + # UNDONE -- so much for that buffer size thing. + n, err = decoder.decode(self.fp.read()) + + self.tile = [] + self.readonly = 0 + # libtiff closed the fp in a, we need to close self.fp, if possible + if hasattr(self.fp, 'close'): + if not self.__next: + self.fp.close() + self.fp = None # might be shared + + if err < 0: + raise IOError(err) + + self.load_end() + + return Image.Image.load(self) + + def _setup(self): + "Setup this image object based on current tags" + + if 0xBC01 in self.tag: + raise IOError("Windows Media Photo files not yet supported") + + getscalar = self.tag.getscalar + + # extract relevant tags + self._compression = COMPRESSION_INFO[getscalar(COMPRESSION, 1)] + self._planar_configuration = getscalar(PLANAR_CONFIGURATION, 1) + + # photometric is a required tag, but not everyone is reading + # the specification + photo = getscalar(PHOTOMETRIC_INTERPRETATION, 0) + + fillorder = getscalar(FILLORDER, 1) + + if Image.DEBUG: + print("*** Summary ***") + print("- compression:", self._compression) + print("- photometric_interpretation:", photo) + print("- planar_configuration:", self._planar_configuration) + print("- fill_order:", fillorder) + + # size + xsize = getscalar(IMAGEWIDTH) + ysize = getscalar(IMAGELENGTH) + self.size = xsize, ysize + + if Image.DEBUG: + print("- size:", self.size) + + format = getscalar(SAMPLEFORMAT, 1) + + # mode: check photometric interpretation and bits per pixel + key = ( + self.tag.prefix, photo, format, fillorder, + self.tag.get(BITSPERSAMPLE, (1,)), + self.tag.get(EXTRASAMPLES, ()) + ) + if Image.DEBUG: + print("format key:", key) + try: + self.mode, rawmode = OPEN_INFO[key] + except KeyError: + if Image.DEBUG: + print("- unsupported format") + raise SyntaxError("unknown pixel mode") + + if Image.DEBUG: + print("- raw mode:", rawmode) + print("- pil mode:", self.mode) + + self.info["compression"] = self._compression + + xres = getscalar(X_RESOLUTION, (1, 1)) + yres = getscalar(Y_RESOLUTION, (1, 1)) + + if xres and not isinstance(xres, tuple): + xres = (xres, 1.) + if yres and not isinstance(yres, tuple): + yres = (yres, 1.) + if xres and yres: + xres = xres[0] / (xres[1] or 1) + yres = yres[0] / (yres[1] or 1) + resunit = getscalar(RESOLUTION_UNIT, 1) + if resunit == 2: # dots per inch + self.info["dpi"] = xres, yres + elif resunit == 3: # dots per centimeter. convert to dpi + self.info["dpi"] = xres * 2.54, yres * 2.54 + else: # No absolute unit of measurement + self.info["resolution"] = xres, yres + + # build tile descriptors + x = y = l = 0 + self.tile = [] + if STRIPOFFSETS in self.tag: + # striped image + offsets = self.tag[STRIPOFFSETS] + h = getscalar(ROWSPERSTRIP, ysize) + w = self.size[0] + if READ_LIBTIFF or self._compression in ["tiff_ccitt", "group3", + "group4", "tiff_jpeg", + "tiff_adobe_deflate", + "tiff_thunderscan", + "tiff_deflate", + "tiff_sgilog", + "tiff_sgilog24", + "tiff_raw_16"]: + # if Image.DEBUG: + # print "Activating g4 compression for whole file" + + # Decoder expects entire file as one tile. + # There's a buffer size limit in load (64k) + # so large g4 images will fail if we use that + # function. + # + # Setup the one tile for the whole image, then + # replace the existing load function with our + # _load_libtiff function. + + self.load = self._load_libtiff + + # To be nice on memory footprint, if there's a + # file descriptor, use that instead of reading + # into a string in python. + + # libtiff closes the file descriptor, so pass in a dup. + try: + fp = hasattr(self.fp, "fileno") and \ + os.dup(self.fp.fileno()) + # flush the file descriptor, prevents error on pypy 2.4+ + # should also eliminate the need for fp.tell for py3 + # in _seek + self.fp.flush() + except IOError: + # io.BytesIO have a fileno, but returns an IOError if + # it doesn't use a file descriptor. + fp = False + + # libtiff handles the fillmode for us, so 1;IR should + # actually be 1;I. Including the R double reverses the + # bits, so stripes of the image are reversed. See + # https://github.com/python-pillow/Pillow/issues/279 + if fillorder == 2: + key = ( + self.tag.prefix, photo, format, 1, + self.tag.get(BITSPERSAMPLE, (1,)), + self.tag.get(EXTRASAMPLES, ()) + ) + if Image.DEBUG: + print("format key:", key) + # this should always work, since all the + # fillorder==2 modes have a corresponding + # fillorder=1 mode + self.mode, rawmode = OPEN_INFO[key] + # libtiff always returns the bytes in native order. + # we're expecting image byte order. So, if the rawmode + # contains I;16, we need to convert from native to image + # byte order. + if self.mode in ('I;16B', 'I;16') and 'I;16' in rawmode: + rawmode = 'I;16N' + + # Offset in the tile tuple is 0, we go from 0,0 to + # w,h, and we only do this once -- eds + a = (rawmode, self._compression, fp) + self.tile.append( + (self._compression, + (0, 0, w, ysize), + 0, a)) + a = None + + else: + for i in range(len(offsets)): + a = self._decoder(rawmode, l, i) + self.tile.append( + (self._compression, + (0, min(y, ysize), w, min(y+h, ysize)), + offsets[i], a)) + if Image.DEBUG: + print ("tiles: ", self.tile) + y = y + h + if y >= self.size[1]: + x = y = 0 + l += 1 + a = None + elif TILEOFFSETS in self.tag: + # tiled image + w = getscalar(322) + h = getscalar(323) + a = None + for o in self.tag[TILEOFFSETS]: + if not a: + a = self._decoder(rawmode, l) + # FIXME: this doesn't work if the image size + # is not a multiple of the tile size... + self.tile.append( + (self._compression, + (x, y, x+w, y+h), + o, a)) + x = x + w + if x >= self.size[0]: + x, y = 0, y + h + if y >= self.size[1]: + x = y = 0 + l += 1 + a = None + else: + if Image.DEBUG: + print("- unsupported data organization") + raise SyntaxError("unknown data organization") + + # fixup palette descriptor + + if self.mode == "P": + palette = [o8(a // 256) for a in self.tag[COLORMAP]] + self.palette = ImagePalette.raw("RGB;L", b"".join(palette)) +# +# -------------------------------------------------------------------- +# Write TIFF files + +# little endian is default except for image modes with +# explict big endian byte-order + +SAVE_INFO = { + # mode => rawmode, byteorder, photometrics, + # sampleformat, bitspersample, extra + "1": ("1", II, 1, 1, (1,), None), + "L": ("L", II, 1, 1, (8,), None), + "LA": ("LA", II, 1, 1, (8, 8), 2), + "P": ("P", II, 3, 1, (8,), None), + "PA": ("PA", II, 3, 1, (8, 8), 2), + "I": ("I;32S", II, 1, 2, (32,), None), + "I;16": ("I;16", II, 1, 1, (16,), None), + "I;16S": ("I;16S", II, 1, 2, (16,), None), + "F": ("F;32F", II, 1, 3, (32,), None), + "RGB": ("RGB", II, 2, 1, (8, 8, 8), None), + "RGBX": ("RGBX", II, 2, 1, (8, 8, 8, 8), 0), + "RGBA": ("RGBA", II, 2, 1, (8, 8, 8, 8), 2), + "CMYK": ("CMYK", II, 5, 1, (8, 8, 8, 8), None), + "YCbCr": ("YCbCr", II, 6, 1, (8, 8, 8), None), + "LAB": ("LAB", II, 8, 1, (8, 8, 8), None), + + "I;32BS": ("I;32BS", MM, 1, 2, (32,), None), + "I;16B": ("I;16B", MM, 1, 1, (16,), None), + "I;16BS": ("I;16BS", MM, 1, 2, (16,), None), + "F;32BF": ("F;32BF", MM, 1, 3, (32,), None), +} + + +def _cvt_res(value): + # convert value to TIFF rational number -- (numerator, denominator) + if isinstance(value, collections.Sequence): + assert(len(value) % 2 == 0) + return value + if isinstance(value, int): + return (value, 1) + value = float(value) + return (int(value * 65536), 65536) + + +def _save(im, fp, filename): + + try: + rawmode, prefix, photo, format, bits, extra = SAVE_INFO[im.mode] + except KeyError: + raise IOError("cannot write mode %s as TIFF" % im.mode) + + ifd = ImageFileDirectory(prefix) + + compression = im.encoderinfo.get('compression', im.info.get('compression', + 'raw')) + + libtiff = WRITE_LIBTIFF or compression != 'raw' + + # required for color libtiff images + ifd[PLANAR_CONFIGURATION] = getattr(im, '_planar_configuration', 1) + + # -- multi-page -- skip TIFF header on subsequent pages + if not libtiff and fp.tell() == 0: + # tiff header (write via IFD to get everything right) + # PIL always starts the first IFD at offset 8 + fp.write(ifd.prefix + ifd.o16(42) + ifd.o32(8)) + + ifd[IMAGEWIDTH] = im.size[0] + ifd[IMAGELENGTH] = im.size[1] + + # write any arbitrary tags passed in as an ImageFileDirectory + info = im.encoderinfo.get("tiffinfo", {}) + if Image.DEBUG: + print("Tiffinfo Keys: %s" % info.keys) + keys = list(info.keys()) + for key in keys: + ifd[key] = info.get(key) + try: + ifd.tagtype[key] = info.tagtype[key] + except: + pass # might not be an IFD, Might not have populated type + + # additions written by Greg Couch, gregc@cgl.ucsf.edu + # inspired by image-sig posting from Kevin Cazabon, kcazabon@home.com + if hasattr(im, 'tag'): + # preserve tags from original TIFF image file + for key in (RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION, + IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, XMP): + if key in im.tag: + ifd[key] = im.tag[key] + ifd.tagtype[key] = im.tag.tagtype.get(key, None) + + # preserve ICC profile (should also work when saving other formats + # which support profiles as TIFF) -- 2008-06-06 Florian Hoech + if "icc_profile" in im.info: + ifd[ICCPROFILE] = im.info["icc_profile"] + + for key, name, cvt in [ + (IMAGEDESCRIPTION, "description", lambda x: x), + (X_RESOLUTION, "resolution", _cvt_res), + (Y_RESOLUTION, "resolution", _cvt_res), + (X_RESOLUTION, "x_resolution", _cvt_res), + (Y_RESOLUTION, "y_resolution", _cvt_res), + (RESOLUTION_UNIT, "resolution_unit", + lambda x: {"inch": 2, "cm": 3, "centimeter": 3}.get(x, 1)), + (SOFTWARE, "software", lambda x: x), + (DATE_TIME, "date_time", lambda x: x), + (ARTIST, "artist", lambda x: x), + (COPYRIGHT, "copyright", lambda x: x)]: + name_with_spaces = name.replace("_", " ") + if "_" in name and name_with_spaces in im.encoderinfo: + warnings.warn("%r is deprecated; use %r instead" % + (name_with_spaces, name), DeprecationWarning) + ifd[key] = cvt(im.encoderinfo[name.replace("_", " ")]) + if name in im.encoderinfo: + ifd[key] = cvt(im.encoderinfo[name]) + + dpi = im.encoderinfo.get("dpi") + if dpi: + ifd[RESOLUTION_UNIT] = 2 + ifd[X_RESOLUTION] = _cvt_res(dpi[0]) + ifd[Y_RESOLUTION] = _cvt_res(dpi[1]) + + if bits != (1,): + ifd[BITSPERSAMPLE] = bits + if len(bits) != 1: + ifd[SAMPLESPERPIXEL] = len(bits) + if extra is not None: + ifd[EXTRASAMPLES] = extra + if format != 1: + ifd[SAMPLEFORMAT] = format + + ifd[PHOTOMETRIC_INTERPRETATION] = photo + + if im.mode == "P": + lut = im.im.getpalette("RGB", "RGB;L") + ifd[COLORMAP] = tuple(i8(v) * 256 for v in lut) + + # data orientation + stride = len(bits) * ((im.size[0]*bits[0]+7)//8) + ifd[ROWSPERSTRIP] = im.size[1] + ifd[STRIPBYTECOUNTS] = stride * im.size[1] + ifd[STRIPOFFSETS] = 0 # this is adjusted by IFD writer + # no compression by default: + ifd[COMPRESSION] = COMPRESSION_INFO_REV.get(compression, 1) + + if libtiff: + if Image.DEBUG: + print ("Saving using libtiff encoder") + print (ifd.items()) + _fp = 0 + if hasattr(fp, "fileno"): + try: + fp.seek(0) + _fp = os.dup(fp.fileno()) + except io.UnsupportedOperation: + pass + + # ICC Profile crashes. + blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS, ROWSPERSTRIP, ICCPROFILE] + atts = {} + # bits per sample is a single short in the tiff directory, not a list. + atts[BITSPERSAMPLE] = bits[0] + # Merge the ones that we have with (optional) more bits from + # the original file, e.g x,y resolution so that we can + # save(load('')) == original file. + for k, v in itertools.chain(ifd.items(), + getattr(im, 'ifd', {}).items()): + if k not in atts and k not in blocklist: + if type(v[0]) == tuple and len(v) > 1: + # A tuple of more than one rational tuples + # flatten to floats, + # following tiffcp.c->cpTag->TIFF_RATIONAL + atts[k] = [float(elt[0])/float(elt[1]) for elt in v] + continue + if type(v[0]) == tuple and len(v) == 1: + # A tuple of one rational tuples + # flatten to floats, + # following tiffcp.c->cpTag->TIFF_RATIONAL + atts[k] = float(v[0][0])/float(v[0][1]) + continue + if (type(v) == tuple and + (len(v) > 2 or + (len(v) == 2 and v[1] == 0))): + # List of ints? + # Avoid divide by zero in next if-clause + if type(v[0]) in (int, float): + atts[k] = list(v) + continue + if type(v) == tuple and len(v) == 2: + # one rational tuple + # flatten to float, + # following tiffcp.c->cpTag->TIFF_RATIONAL + atts[k] = float(v[0])/float(v[1]) + continue + if type(v) == tuple and len(v) == 1: + v = v[0] + # drop through + if isStringType(v): + atts[k] = bytes(v.encode('ascii', 'replace')) + b"\0" + continue + else: + # int or similar + atts[k] = v + + if Image.DEBUG: + print (atts) + + # libtiff always expects the bytes in native order. + # we're storing image byte order. So, if the rawmode + # contains I;16, we need to convert from native to image + # byte order. + if im.mode in ('I;16B', 'I;16'): + rawmode = 'I;16N' + + a = (rawmode, compression, _fp, filename, atts) + # print (im.mode, compression, a, im.encoderconfig) + e = Image._getencoder(im.mode, 'libtiff', a, im.encoderconfig) + e.setimage(im.im, (0, 0)+im.size) + while True: + # undone, change to self.decodermaxblock: + l, s, d = e.encode(16*1024) + if not _fp: + fp.write(d) + if s: + break + if s < 0: + raise IOError("encoder error %d when writing image file" % s) + + else: + offset = ifd.save(fp) + + ImageFile._save(im, fp, [ + ("raw", (0, 0)+im.size, offset, (rawmode, stride, 1)) + ]) + + # -- helper for multi-page save -- + if "_debug_multipage" in im.encoderinfo: + # just to access o32 and o16 (using correct byte order) + im._debug_multipage = ifd + +# +# -------------------------------------------------------------------- +# Register + +Image.register_open("TIFF", TiffImageFile, _accept) +Image.register_save("TIFF", _save) + +Image.register_extension("TIFF", ".tif") +Image.register_extension("TIFF", ".tiff") + +Image.register_mime("TIFF", "image/tiff") diff --git a/pyPackages/pillowarmv7l/PIL/TiffTags.py b/pyPackages/pillowarmv7l/PIL/TiffTags.py new file mode 100644 index 0000000..d15aa7e --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/TiffTags.py @@ -0,0 +1,307 @@ +# +# The Python Imaging Library. +# $Id$ +# +# TIFF tags +# +# This module provides clear-text names for various well-known +# TIFF tags. the TIFF codec works just fine without it. +# +# Copyright (c) Secret Labs AB 1999. +# +# See the README file for information on usage and redistribution. +# + +## +# This module provides constants and clear-text names for various +# well-known TIFF tags. +## + +## +# Map tag numbers (or tag number, tag value tuples) to tag names. + +TAGS = { + + 254: "NewSubfileType", + 255: "SubfileType", + 256: "ImageWidth", + 257: "ImageLength", + 258: "BitsPerSample", + + 259: "Compression", + (259, 1): "Uncompressed", + (259, 2): "CCITT 1d", + (259, 3): "Group 3 Fax", + (259, 4): "Group 4 Fax", + (259, 5): "LZW", + (259, 6): "JPEG", + (259, 32773): "PackBits", + + 262: "PhotometricInterpretation", + (262, 0): "WhiteIsZero", + (262, 1): "BlackIsZero", + (262, 2): "RGB", + (262, 3): "RGB Palette", + (262, 4): "Transparency Mask", + (262, 5): "CMYK", + (262, 6): "YCbCr", + (262, 8): "CieLAB", + (262, 32803): "CFA", # TIFF/EP, Adobe DNG + (262, 32892): "LinearRaw", # Adobe DNG + + 263: "Thresholding", + 264: "CellWidth", + 265: "CellHeight", + 266: "FillOrder", + 269: "DocumentName", + + 270: "ImageDescription", + 271: "Make", + 272: "Model", + 273: "StripOffsets", + 274: "Orientation", + 277: "SamplesPerPixel", + 278: "RowsPerStrip", + 279: "StripByteCounts", + + 280: "MinSampleValue", + 281: "MaxSampleValue", + 282: "XResolution", + 283: "YResolution", + 284: "PlanarConfiguration", + (284, 1): "Contigous", + (284, 2): "Separate", + + 285: "PageName", + 286: "XPosition", + 287: "YPosition", + 288: "FreeOffsets", + 289: "FreeByteCounts", + + 290: "GrayResponseUnit", + 291: "GrayResponseCurve", + 292: "T4Options", + 293: "T6Options", + 296: "ResolutionUnit", + 297: "PageNumber", + + 301: "TransferFunction", + 305: "Software", + 306: "DateTime", + + 315: "Artist", + 316: "HostComputer", + 317: "Predictor", + 318: "WhitePoint", + 319: "PrimaryChromaticies", + + 320: "ColorMap", + 321: "HalftoneHints", + 322: "TileWidth", + 323: "TileLength", + 324: "TileOffsets", + 325: "TileByteCounts", + + 332: "InkSet", + 333: "InkNames", + 334: "NumberOfInks", + 336: "DotRange", + 337: "TargetPrinter", + 338: "ExtraSamples", + 339: "SampleFormat", + + 340: "SMinSampleValue", + 341: "SMaxSampleValue", + 342: "TransferRange", + + 347: "JPEGTables", + + # obsolete JPEG tags + 512: "JPEGProc", + 513: "JPEGInterchangeFormat", + 514: "JPEGInterchangeFormatLength", + 515: "JPEGRestartInterval", + 517: "JPEGLosslessPredictors", + 518: "JPEGPointTransforms", + 519: "JPEGQTables", + 520: "JPEGDCTables", + 521: "JPEGACTables", + + 529: "YCbCrCoefficients", + 530: "YCbCrSubSampling", + 531: "YCbCrPositioning", + 532: "ReferenceBlackWhite", + + # XMP + 700: "XMP", + + 33432: "Copyright", + + # various extensions (should check specs for "official" names) + 33723: "IptcNaaInfo", + 34377: "PhotoshopInfo", + + # Exif IFD + 34665: "ExifIFD", + + # ICC Profile + 34675: "ICCProfile", + + # Additional Exif Info + 33434: "ExposureTime", + 33437: "FNumber", + 34850: "ExposureProgram", + 34852: "SpectralSensitivity", + 34853: "GPSInfoIFD", + 34855: "ISOSpeedRatings", + 34856: "OECF", + 34864: "SensitivityType", + 34865: "StandardOutputSensitivity", + 34866: "RecommendedExposureIndex", + 34867: "ISOSpeed", + 34868: "ISOSpeedLatitudeyyy", + 34869: "ISOSpeedLatitudezzz", + 36864: "ExifVersion", + 36867: "DateTimeOriginal", + 36868: "DateTImeDigitized", + 37121: "ComponentsConfiguration", + 37122: "CompressedBitsPerPixel", + 37377: "ShutterSpeedValue", + 37378: "ApertureValue", + 37379: "BrightnessValue", + 37380: "ExposureBiasValue", + 37381: "MaxApertureValue", + 37382: "SubjectDistance", + 37383: "MeteringMode", + 37384: "LightSource", + 37385: "Flash", + 37386: "FocalLength", + 37396: "SubjectArea", + 37500: "MakerNote", + 37510: "UserComment", + 37520: "SubSec", + 37521: "SubSecTimeOriginal", + 37522: "SubsecTimeDigitized", + 40960: "FlashPixVersion", + 40961: "ColorSpace", + 40962: "PixelXDimension", + 40963: "PixelYDimension", + 40964: "RelatedSoundFile", + 40965: "InteroperabilityIFD", + 41483: "FlashEnergy", + 41484: "SpatialFrequencyResponse", + 41486: "FocalPlaneXResolution", + 41487: "FocalPlaneYResolution", + 41488: "FocalPlaneResolutionUnit", + 41492: "SubjectLocation", + 41493: "ExposureIndex", + 41495: "SensingMethod", + 41728: "FileSource", + 41729: "SceneType", + 41730: "CFAPattern", + 41985: "CustomRendered", + 41986: "ExposureMode", + 41987: "WhiteBalance", + 41988: "DigitalZoomRatio", + 41989: "FocalLengthIn35mmFilm", + 41990: "SceneCaptureType", + 41991: "GainControl", + 41992: "Contrast", + 41993: "Saturation", + 41994: "Sharpness", + 41995: "DeviceSettingDescription", + 41996: "SubjectDistanceRange", + 42016: "ImageUniqueID", + 42032: "CameraOwnerName", + 42033: "BodySerialNumber", + 42034: "LensSpecification", + 42035: "LensMake", + 42036: "LensModel", + 42037: "LensSerialNumber", + 42240: "Gamma", + + # MP Info + 45056: "MPFVersion", + 45057: "NumberOfImages", + 45058: "MPEntry", + 45059: "ImageUIDList", + 45060: "TotalFrames", + 45313: "MPIndividualNum", + 45569: "PanOrientation", + 45570: "PanOverlap_H", + 45571: "PanOverlap_V", + 45572: "BaseViewpointNum", + 45573: "ConvergenceAngle", + 45574: "BaselineLength", + 45575: "VerticalDivergence", + 45576: "AxisDistance_X", + 45577: "AxisDistance_Y", + 45578: "AxisDistance_Z", + 45579: "YawAngle", + 45580: "PitchAngle", + 45581: "RollAngle", + + # Adobe DNG + 50706: "DNGVersion", + 50707: "DNGBackwardVersion", + 50708: "UniqueCameraModel", + 50709: "LocalizedCameraModel", + 50710: "CFAPlaneColor", + 50711: "CFALayout", + 50712: "LinearizationTable", + 50713: "BlackLevelRepeatDim", + 50714: "BlackLevel", + 50715: "BlackLevelDeltaH", + 50716: "BlackLevelDeltaV", + 50717: "WhiteLevel", + 50718: "DefaultScale", + 50719: "DefaultCropOrigin", + 50720: "DefaultCropSize", + 50778: "CalibrationIlluminant1", + 50779: "CalibrationIlluminant2", + 50721: "ColorMatrix1", + 50722: "ColorMatrix2", + 50723: "CameraCalibration1", + 50724: "CameraCalibration2", + 50725: "ReductionMatrix1", + 50726: "ReductionMatrix2", + 50727: "AnalogBalance", + 50728: "AsShotNeutral", + 50729: "AsShotWhiteXY", + 50730: "BaselineExposure", + 50731: "BaselineNoise", + 50732: "BaselineSharpness", + 50733: "BayerGreenSplit", + 50734: "LinearResponseLimit", + 50735: "CameraSerialNumber", + 50736: "LensInfo", + 50737: "ChromaBlurRadius", + 50738: "AntiAliasStrength", + 50740: "DNGPrivateData", + 50741: "MakerNoteSafety", + 50780: "BestQualityScale", + + # ImageJ + 50838: "ImageJMetaDataByteCounts", # private tag registered with Adobe + 50839: "ImageJMetaData", # private tag registered with Adobe +} + +## +# Map type numbers to type names. + +TYPES = { + + 1: "byte", + 2: "ascii", + 3: "short", + 4: "long", + 5: "rational", + 6: "signed byte", + 7: "undefined", + 8: "signed short", + 9: "signed long", + 10: "signed rational", + 11: "float", + 12: "double", + +} diff --git a/pyPackages/pillowarmv7l/PIL/WalImageFile.py b/pyPackages/pillowarmv7l/PIL/WalImageFile.py new file mode 100644 index 0000000..fc2bb30 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/WalImageFile.py @@ -0,0 +1,131 @@ +# The Python Imaging Library. +# $Id$ +# +# WAL file handling +# +# History: +# 2003-04-23 fl created +# +# Copyright (c) 2003 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +# NOTE: This format cannot be automatically recognized, so the reader +# is not registered for use with Image.open(). To open a WAL file, use +# the WalImageFile.open() function instead. + +# This reader is based on the specification available from: +# http://www.flipcode.com/archives/Quake_2_BSP_File_Format.shtml +# and has been tested with a few sample files found using google. + +from __future__ import print_function + +from PIL import Image, _binary + +try: + import builtins +except ImportError: + import __builtin__ + builtins = __builtin__ + +i32 = _binary.i32le + + +## +# Load texture from a Quake2 WAL texture file. +#

+# By default, a Quake2 standard palette is attached to the texture. +# To override the palette, use the putpalette method. +# +# @param filename WAL file name, or an opened file handle. +# @return An image instance. + +def open(filename): + # FIXME: modify to return a WalImageFile instance instead of + # plain Image object ? + + if hasattr(filename, "read"): + fp = filename + else: + fp = builtins.open(filename, "rb") + + # read header fields + header = fp.read(32+24+32+12) + size = i32(header, 32), i32(header, 36) + offset = i32(header, 40) + + # load pixel data + fp.seek(offset) + + im = Image.frombytes("P", size, fp.read(size[0] * size[1])) + im.putpalette(quake2palette) + + im.format = "WAL" + im.format_description = "Quake2 Texture" + + # strings are null-terminated + im.info["name"] = header[:32].split(b"\0", 1)[0] + next_name = header[56:56+32].split(b"\0", 1)[0] + if next_name: + im.info["next_name"] = next_name + + return im + + +quake2palette = ( + # default palette taken from piffo 0.93 by Hans Häggström + b"\x01\x01\x01\x0b\x0b\x0b\x12\x12\x12\x17\x17\x17\x1b\x1b\x1b\x1e" + b"\x1e\x1e\x22\x22\x22\x26\x26\x26\x29\x29\x29\x2c\x2c\x2c\x2f\x2f" + b"\x2f\x32\x32\x32\x35\x35\x35\x37\x37\x37\x3a\x3a\x3a\x3c\x3c\x3c" + b"\x24\x1e\x13\x22\x1c\x12\x20\x1b\x12\x1f\x1a\x10\x1d\x19\x10\x1b" + b"\x17\x0f\x1a\x16\x0f\x18\x14\x0d\x17\x13\x0d\x16\x12\x0d\x14\x10" + b"\x0b\x13\x0f\x0b\x10\x0d\x0a\x0f\x0b\x0a\x0d\x0b\x07\x0b\x0a\x07" + b"\x23\x23\x26\x22\x22\x25\x22\x20\x23\x21\x1f\x22\x20\x1e\x20\x1f" + b"\x1d\x1e\x1d\x1b\x1c\x1b\x1a\x1a\x1a\x19\x19\x18\x17\x17\x17\x16" + b"\x16\x14\x14\x14\x13\x13\x13\x10\x10\x10\x0f\x0f\x0f\x0d\x0d\x0d" + b"\x2d\x28\x20\x29\x24\x1c\x27\x22\x1a\x25\x1f\x17\x38\x2e\x1e\x31" + b"\x29\x1a\x2c\x25\x17\x26\x20\x14\x3c\x30\x14\x37\x2c\x13\x33\x28" + b"\x12\x2d\x24\x10\x28\x1f\x0f\x22\x1a\x0b\x1b\x14\x0a\x13\x0f\x07" + b"\x31\x1a\x16\x30\x17\x13\x2e\x16\x10\x2c\x14\x0d\x2a\x12\x0b\x27" + b"\x0f\x0a\x25\x0f\x07\x21\x0d\x01\x1e\x0b\x01\x1c\x0b\x01\x1a\x0b" + b"\x01\x18\x0a\x01\x16\x0a\x01\x13\x0a\x01\x10\x07\x01\x0d\x07\x01" + b"\x29\x23\x1e\x27\x21\x1c\x26\x20\x1b\x25\x1f\x1a\x23\x1d\x19\x21" + b"\x1c\x18\x20\x1b\x17\x1e\x19\x16\x1c\x18\x14\x1b\x17\x13\x19\x14" + b"\x10\x17\x13\x0f\x14\x10\x0d\x12\x0f\x0b\x0f\x0b\x0a\x0b\x0a\x07" + b"\x26\x1a\x0f\x23\x19\x0f\x20\x17\x0f\x1c\x16\x0f\x19\x13\x0d\x14" + b"\x10\x0b\x10\x0d\x0a\x0b\x0a\x07\x33\x22\x1f\x35\x29\x26\x37\x2f" + b"\x2d\x39\x35\x34\x37\x39\x3a\x33\x37\x39\x30\x34\x36\x2b\x31\x34" + b"\x27\x2e\x31\x22\x2b\x2f\x1d\x28\x2c\x17\x25\x2a\x0f\x20\x26\x0d" + b"\x1e\x25\x0b\x1c\x22\x0a\x1b\x20\x07\x19\x1e\x07\x17\x1b\x07\x14" + b"\x18\x01\x12\x16\x01\x0f\x12\x01\x0b\x0d\x01\x07\x0a\x01\x01\x01" + b"\x2c\x21\x21\x2a\x1f\x1f\x29\x1d\x1d\x27\x1c\x1c\x26\x1a\x1a\x24" + b"\x18\x18\x22\x17\x17\x21\x16\x16\x1e\x13\x13\x1b\x12\x12\x18\x10" + b"\x10\x16\x0d\x0d\x12\x0b\x0b\x0d\x0a\x0a\x0a\x07\x07\x01\x01\x01" + b"\x2e\x30\x29\x2d\x2e\x27\x2b\x2c\x26\x2a\x2a\x24\x28\x29\x23\x27" + b"\x27\x21\x26\x26\x1f\x24\x24\x1d\x22\x22\x1c\x1f\x1f\x1a\x1c\x1c" + b"\x18\x19\x19\x16\x17\x17\x13\x13\x13\x10\x0f\x0f\x0d\x0b\x0b\x0a" + b"\x30\x1e\x1b\x2d\x1c\x19\x2c\x1a\x17\x2a\x19\x14\x28\x17\x13\x26" + b"\x16\x10\x24\x13\x0f\x21\x12\x0d\x1f\x10\x0b\x1c\x0f\x0a\x19\x0d" + b"\x0a\x16\x0b\x07\x12\x0a\x07\x0f\x07\x01\x0a\x01\x01\x01\x01\x01" + b"\x28\x29\x38\x26\x27\x36\x25\x26\x34\x24\x24\x31\x22\x22\x2f\x20" + b"\x21\x2d\x1e\x1f\x2a\x1d\x1d\x27\x1b\x1b\x25\x19\x19\x21\x17\x17" + b"\x1e\x14\x14\x1b\x13\x12\x17\x10\x0f\x13\x0d\x0b\x0f\x0a\x07\x07" + b"\x2f\x32\x29\x2d\x30\x26\x2b\x2e\x24\x29\x2c\x21\x27\x2a\x1e\x25" + b"\x28\x1c\x23\x26\x1a\x21\x25\x18\x1e\x22\x14\x1b\x1f\x10\x19\x1c" + b"\x0d\x17\x1a\x0a\x13\x17\x07\x10\x13\x01\x0d\x0f\x01\x0a\x0b\x01" + b"\x01\x3f\x01\x13\x3c\x0b\x1b\x39\x10\x20\x35\x14\x23\x31\x17\x23" + b"\x2d\x18\x23\x29\x18\x3f\x3f\x3f\x3f\x3f\x39\x3f\x3f\x31\x3f\x3f" + b"\x2a\x3f\x3f\x20\x3f\x3f\x14\x3f\x3c\x12\x3f\x39\x0f\x3f\x35\x0b" + b"\x3f\x32\x07\x3f\x2d\x01\x3d\x2a\x01\x3b\x26\x01\x39\x21\x01\x37" + b"\x1d\x01\x34\x1a\x01\x32\x16\x01\x2f\x12\x01\x2d\x0f\x01\x2a\x0b" + b"\x01\x27\x07\x01\x23\x01\x01\x1d\x01\x01\x17\x01\x01\x10\x01\x01" + b"\x3d\x01\x01\x19\x19\x3f\x3f\x01\x01\x01\x01\x3f\x16\x16\x13\x10" + b"\x10\x0f\x0d\x0d\x0b\x3c\x2e\x2a\x36\x27\x20\x30\x21\x18\x29\x1b" + b"\x10\x3c\x39\x37\x37\x32\x2f\x31\x2c\x28\x2b\x26\x21\x30\x22\x20" +) + +if __name__ == "__main__": + im = open("../hacks/sample.wal") + print(im.info, im.mode, im.size) + im.save("../out.png") diff --git a/pyPackages/pillowarmv7l/PIL/WebPImagePlugin.py b/pyPackages/pillowarmv7l/PIL/WebPImagePlugin.py new file mode 100644 index 0000000..78a7a53 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/WebPImagePlugin.py @@ -0,0 +1,80 @@ +from PIL import Image +from PIL import ImageFile +from io import BytesIO +from PIL import _webp + + +_VALID_WEBP_MODES = { + "RGB": True, + "RGBA": True, + } + +_VP8_MODES_BY_IDENTIFIER = { + b"VP8 ": "RGB", + b"VP8X": "RGBA", + b"VP8L": "RGBA", # lossless + } + + +def _accept(prefix): + is_riff_file_format = prefix[:4] == b"RIFF" + is_webp_file = prefix[8:12] == b"WEBP" + is_valid_vp8_mode = prefix[12:16] in _VP8_MODES_BY_IDENTIFIER + + return is_riff_file_format and is_webp_file and is_valid_vp8_mode + + +class WebPImageFile(ImageFile.ImageFile): + + format = "WEBP" + format_description = "WebP image" + + def _open(self): + data, width, height, self.mode, icc_profile, exif = \ + _webp.WebPDecode(self.fp.read()) + + if icc_profile: + self.info["icc_profile"] = icc_profile + if exif: + self.info["exif"] = exif + + self.size = width, height + self.fp = BytesIO(data) + self.tile = [("raw", (0, 0) + self.size, 0, self.mode)] + + def _getexif(self): + from PIL.JpegImagePlugin import _getexif + return _getexif(self) + + +def _save(im, fp, filename): + image_mode = im.mode + if im.mode not in _VALID_WEBP_MODES: + raise IOError("cannot write mode %s as WEBP" % image_mode) + + lossless = im.encoderinfo.get("lossless", False) + quality = im.encoderinfo.get("quality", 80) + icc_profile = im.encoderinfo.get("icc_profile", "") + exif = im.encoderinfo.get("exif", "") + + data = _webp.WebPEncode( + im.tobytes(), + im.size[0], + im.size[1], + lossless, + float(quality), + im.mode, + icc_profile, + exif + ) + if data is None: + raise IOError("cannot write file as WEBP (encoder returned None)") + + fp.write(data) + + +Image.register_open("WEBP", WebPImageFile, _accept) +Image.register_save("WEBP", _save) + +Image.register_extension("WEBP", ".webp") +Image.register_mime("WEBP", "image/webp") diff --git a/pyPackages/pillowarmv7l/PIL/WmfImagePlugin.py b/pyPackages/pillowarmv7l/PIL/WmfImagePlugin.py new file mode 100644 index 0000000..6146c15 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/WmfImagePlugin.py @@ -0,0 +1,173 @@ +# +# The Python Imaging Library +# $Id$ +# +# WMF stub codec +# +# history: +# 1996-12-14 fl Created +# 2004-02-22 fl Turned into a stub driver +# 2004-02-23 fl Added EMF support +# +# Copyright (c) Secret Labs AB 1997-2004. All rights reserved. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.2" + +from PIL import Image, ImageFile, _binary + +_handler = None + +if str != bytes: + long = int + + +## +# Install application-specific WMF image handler. +# +# @param handler Handler object. + +def register_handler(handler): + global _handler + _handler = handler + +if hasattr(Image.core, "drawwmf"): + # install default handler (windows only) + + class WmfHandler: + + def open(self, im): + im.mode = "RGB" + self.bbox = im.info["wmf_bbox"] + + def load(self, im): + im.fp.seek(0) # rewind + return Image.frombytes( + "RGB", im.size, + Image.core.drawwmf(im.fp.read(), im.size, self.bbox), + "raw", "BGR", (im.size[0]*3 + 3) & -4, -1 + ) + + register_handler(WmfHandler()) + +# -------------------------------------------------------------------- + +word = _binary.i16le + + +def short(c, o=0): + v = word(c, o) + if v >= 32768: + v -= 65536 + return v + +dword = _binary.i32le + + +# +# -------------------------------------------------------------------- +# Read WMF file + +def _accept(prefix): + return ( + prefix[:6] == b"\xd7\xcd\xc6\x9a\x00\x00" or + prefix[:4] == b"\x01\x00\x00\x00" + ) + + +## +# Image plugin for Windows metafiles. + +class WmfStubImageFile(ImageFile.StubImageFile): + + format = "WMF" + format_description = "Windows Metafile" + + def _open(self): + + # check placable header + s = self.fp.read(80) + + if s[:6] == b"\xd7\xcd\xc6\x9a\x00\x00": + + # placeable windows metafile + + # get units per inch + inch = word(s, 14) + + # get bounding box + x0 = short(s, 6) + y0 = short(s, 8) + x1 = short(s, 10) + y1 = short(s, 12) + + # normalize size to 72 dots per inch + size = (x1 - x0) * 72 // inch, (y1 - y0) * 72 // inch + + self.info["wmf_bbox"] = x0, y0, x1, y1 + + self.info["dpi"] = 72 + + # print self.mode, self.size, self.info + + # sanity check (standard metafile header) + if s[22:26] != b"\x01\x00\t\x00": + raise SyntaxError("Unsupported WMF file format") + + elif dword(s) == 1 and s[40:44] == b" EMF": + # enhanced metafile + + # get bounding box + x0 = dword(s, 8) + y0 = dword(s, 12) + x1 = dword(s, 16) + y1 = dword(s, 20) + + # get frame (in 0.01 millimeter units) + frame = dword(s, 24), dword(s, 28), dword(s, 32), dword(s, 36) + + # normalize size to 72 dots per inch + size = x1 - x0, y1 - y0 + + # calculate dots per inch from bbox and frame + xdpi = 2540 * (x1 - y0) // (frame[2] - frame[0]) + ydpi = 2540 * (y1 - y0) // (frame[3] - frame[1]) + + self.info["wmf_bbox"] = x0, y0, x1, y1 + + if xdpi == ydpi: + self.info["dpi"] = xdpi + else: + self.info["dpi"] = xdpi, ydpi + + else: + raise SyntaxError("Unsupported file format") + + self.mode = "RGB" + self.size = size + + loader = self._load() + if loader: + loader.open(self) + + def _load(self): + return _handler + + +def _save(im, fp, filename): + if _handler is None or not hasattr("_handler", "save"): + raise IOError("WMF save handler not installed") + _handler.save(im, fp, filename) + +# +# -------------------------------------------------------------------- +# Registry stuff + +Image.register_open(WmfStubImageFile.format, WmfStubImageFile, _accept) +Image.register_save(WmfStubImageFile.format, _save) + +Image.register_extension(WmfStubImageFile.format, ".wmf") +Image.register_extension(WmfStubImageFile.format, ".emf") diff --git a/pyPackages/pillowarmv7l/PIL/XVThumbImagePlugin.py b/pyPackages/pillowarmv7l/PIL/XVThumbImagePlugin.py new file mode 100644 index 0000000..5cf1386 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/XVThumbImagePlugin.py @@ -0,0 +1,75 @@ +# +# The Python Imaging Library. +# $Id$ +# +# XV Thumbnail file handler by Charles E. "Gene" Cash +# (gcash@magicnet.net) +# +# see xvcolor.c and xvbrowse.c in the sources to John Bradley's XV, +# available from ftp://ftp.cis.upenn.edu/pub/xv/ +# +# history: +# 98-08-15 cec created (b/w only) +# 98-12-09 cec added color palette +# 98-12-28 fl added to PIL (with only a few very minor modifications) +# +# To do: +# FIXME: make save work (this requires quantization support) +# + +__version__ = "0.1" + +from PIL import Image, ImageFile, ImagePalette, _binary + +o8 = _binary.o8 + +# standard color palette for thumbnails (RGB332) +PALETTE = b"" +for r in range(8): + for g in range(8): + for b in range(4): + PALETTE = PALETTE + (o8((r*255)//7)+o8((g*255)//7)+o8((b*255)//3)) + + +## +# Image plugin for XV thumbnail images. + +class XVThumbImageFile(ImageFile.ImageFile): + + format = "XVThumb" + format_description = "XV thumbnail image" + + def _open(self): + + # check magic + s = self.fp.read(6) + if s != b"P7 332": + raise SyntaxError("not an XV thumbnail file") + + # Skip to beginning of next line + self.fp.readline() + + # skip info comments + while True: + s = self.fp.readline() + if not s: + raise SyntaxError("Unexpected EOF reading XV thumbnail file") + if s[0] != b'#': + break + + # parse header line (already read) + s = s.strip().split() + + self.mode = "P" + self.size = int(s[0:1]), int(s[1:2]) + + self.palette = ImagePalette.raw("RGB", PALETTE) + + self.tile = [ + ("raw", (0, 0)+self.size, + self.fp.tell(), (self.mode, 0, 1) + )] + +# -------------------------------------------------------------------- + +Image.register_open("XVThumb", XVThumbImageFile) diff --git a/pyPackages/pillowarmv7l/PIL/XbmImagePlugin.py b/pyPackages/pillowarmv7l/PIL/XbmImagePlugin.py new file mode 100644 index 0000000..604ba15 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/XbmImagePlugin.py @@ -0,0 +1,96 @@ +# +# The Python Imaging Library. +# $Id$ +# +# XBM File handling +# +# History: +# 1995-09-08 fl Created +# 1996-11-01 fl Added save support +# 1997-07-07 fl Made header parser more tolerant +# 1997-07-22 fl Fixed yet another parser bug +# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.4) +# 2001-05-13 fl Added hotspot handling (based on code from Bernhard Herzog) +# 2004-02-24 fl Allow some whitespace before first #define +# +# Copyright (c) 1997-2004 by Secret Labs AB +# Copyright (c) 1996-1997 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.6" + +import re +from PIL import Image, ImageFile + +# XBM header +xbm_head = re.compile( + b"\s*#define[ \t]+[^_]*_width[ \t]+(?P[0-9]+)[\r\n]+" + b"#define[ \t]+[^_]*_height[ \t]+(?P[0-9]+)[\r\n]+" + b"(?P" + b"#define[ \t]+[^_]*_x_hot[ \t]+(?P[0-9]+)[\r\n]+" + b"#define[ \t]+[^_]*_y_hot[ \t]+(?P[0-9]+)[\r\n]+" + b")?" + b"[\\000-\\377]*_bits\\[\\]" +) + + +def _accept(prefix): + return prefix.lstrip()[:7] == b"#define" + + +## +# Image plugin for X11 bitmaps. + +class XbmImageFile(ImageFile.ImageFile): + + format = "XBM" + format_description = "X11 Bitmap" + + def _open(self): + + m = xbm_head.match(self.fp.read(512)) + + if m: + + xsize = int(m.group("width")) + ysize = int(m.group("height")) + + if m.group("hotspot"): + self.info["hotspot"] = ( + int(m.group("xhot")), int(m.group("yhot")) + ) + + self.mode = "1" + self.size = xsize, ysize + + self.tile = [("xbm", (0, 0)+self.size, m.end(), None)] + + +def _save(im, fp, filename): + + if im.mode != "1": + raise IOError("cannot write mode %s as XBM" % im.mode) + + fp.write(("#define im_width %d\n" % im.size[0]).encode('ascii')) + fp.write(("#define im_height %d\n" % im.size[1]).encode('ascii')) + + hotspot = im.encoderinfo.get("hotspot") + if hotspot: + fp.write(("#define im_x_hot %d\n" % hotspot[0]).encode('ascii')) + fp.write(("#define im_y_hot %d\n" % hotspot[1]).encode('ascii')) + + fp.write(b"static char im_bits[] = {\n") + + ImageFile._save(im, fp, [("xbm", (0, 0)+im.size, 0, None)]) + + fp.write(b"};\n") + + +Image.register_open("XBM", XbmImageFile, _accept) +Image.register_save("XBM", _save) + +Image.register_extension("XBM", ".xbm") + +Image.register_mime("XBM", "image/xbm") diff --git a/pyPackages/pillowarmv7l/PIL/XpmImagePlugin.py b/pyPackages/pillowarmv7l/PIL/XpmImagePlugin.py new file mode 100644 index 0000000..5175808 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/XpmImagePlugin.py @@ -0,0 +1,131 @@ +# +# The Python Imaging Library. +# $Id$ +# +# XPM File handling +# +# History: +# 1996-12-29 fl Created +# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.7) +# +# Copyright (c) Secret Labs AB 1997-2001. +# Copyright (c) Fredrik Lundh 1996-2001. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.2" + + +import re +from PIL import Image, ImageFile, ImagePalette +from PIL._binary import i8, o8 + +# XPM header +xpm_head = re.compile(b"\"([0-9]*) ([0-9]*) ([0-9]*) ([0-9]*)") + + +def _accept(prefix): + return prefix[:9] == b"/* XPM */" + + +## +# Image plugin for X11 pixel maps. + +class XpmImageFile(ImageFile.ImageFile): + + format = "XPM" + format_description = "X11 Pixel Map" + + def _open(self): + + if not _accept(self.fp.read(9)): + raise SyntaxError("not an XPM file") + + # skip forward to next string + while True: + s = self.fp.readline() + if not s: + raise SyntaxError("broken XPM file") + m = xpm_head.match(s) + if m: + break + + self.size = int(m.group(1)), int(m.group(2)) + + pal = int(m.group(3)) + bpp = int(m.group(4)) + + if pal > 256 or bpp != 1: + raise ValueError("cannot read this XPM file") + + # + # load palette description + + palette = [b"\0\0\0"] * 256 + + for i in range(pal): + + s = self.fp.readline() + if s[-2:] == b'\r\n': + s = s[:-2] + elif s[-1:] in b'\r\n': + s = s[:-1] + + c = i8(s[1]) + s = s[2:-2].split() + + for i in range(0, len(s), 2): + + if s[i] == b"c": + + # process colour key + rgb = s[i+1] + if rgb == b"None": + self.info["transparency"] = c + elif rgb[0:1] == b"#": + # FIXME: handle colour names (see ImagePalette.py) + rgb = int(rgb[1:], 16) + palette[c] = (o8((rgb >> 16) & 255) + + o8((rgb >> 8) & 255) + + o8(rgb & 255)) + else: + # unknown colour + raise ValueError("cannot read this XPM file") + break + + else: + + # missing colour key + raise ValueError("cannot read this XPM file") + + self.mode = "P" + self.palette = ImagePalette.raw("RGB", b"".join(palette)) + + self.tile = [("raw", (0, 0)+self.size, self.fp.tell(), ("P", 0, 1))] + + def load_read(self, bytes): + + # + # load all image data in one chunk + + xsize, ysize = self.size + + s = [None] * ysize + + for i in range(ysize): + s[i] = self.fp.readline()[1:xsize+1].ljust(xsize) + + self.fp = None + + return b"".join(s) + +# +# Registry + +Image.register_open("XPM", XpmImageFile, _accept) + +Image.register_extension("XPM", ".xpm") + +Image.register_mime("XPM", "image/xpm") diff --git a/pyPackages/pillowarmv7l/PIL/__init__.py b/pyPackages/pillowarmv7l/PIL/__init__.py new file mode 100644 index 0000000..4d5d7b3 --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/__init__.py @@ -0,0 +1,58 @@ +# +# The Python Imaging Library. +# $Id$ +# +# package placeholder +# +# Copyright (c) 1999 by Secret Labs AB. +# +# See the README file for information on usage and redistribution. +# + +# ;-) + +VERSION = '1.1.7' # PIL version +PILLOW_VERSION = '2.7.0' # Pillow + +_plugins = ['BmpImagePlugin', + 'BufrStubImagePlugin', + 'CurImagePlugin', + 'DcxImagePlugin', + 'EpsImagePlugin', + 'FitsStubImagePlugin', + 'FliImagePlugin', + 'FpxImagePlugin', + 'GbrImagePlugin', + 'GifImagePlugin', + 'GribStubImagePlugin', + 'Hdf5StubImagePlugin', + 'IcnsImagePlugin', + 'IcoImagePlugin', + 'ImImagePlugin', + 'ImtImagePlugin', + 'IptcImagePlugin', + 'JpegImagePlugin', + 'Jpeg2KImagePlugin', + 'McIdasImagePlugin', + 'MicImagePlugin', + 'MpegImagePlugin', + 'MpoImagePlugin', + 'MspImagePlugin', + 'PalmImagePlugin', + 'PcdImagePlugin', + 'PcxImagePlugin', + 'PdfImagePlugin', + 'PixarImagePlugin', + 'PngImagePlugin', + 'PpmImagePlugin', + 'PsdImagePlugin', + 'SgiImagePlugin', + 'SpiderImagePlugin', + 'SunImagePlugin', + 'TgaImagePlugin', + 'TiffImagePlugin', + 'WebPImagePlugin', + 'WmfImagePlugin', + 'XbmImagePlugin', + 'XpmImagePlugin', + 'XVThumbImagePlugin'] diff --git a/pyPackages/pillowarmv7l/PIL/_binary.py b/pyPackages/pillowarmv7l/PIL/_binary.py new file mode 100644 index 0000000..51ce45a --- /dev/null +++ b/pyPackages/pillowarmv7l/PIL/_binary.py @@ -0,0 +1,76 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Binary input/output support routines. +# +# Copyright (c) 1997-2003 by Secret Labs AB +# Copyright (c) 1995-2003 by Fredrik Lundh +# Copyright (c) 2012 by Brian Crowell +# +# See the README file for information on usage and redistribution. +# + +if bytes is str: + def i8(c): + return ord(c) + + def o8(i): + return chr(i & 255) +else: + def i8(c): + return c if c.__class__ is int else c[0] + + def o8(i): + return bytes((i & 255,)) + + +# Input, le = little endian, be = big endian +# TODO: replace with more readable struct.unpack equivalent +def i16le(c, o=0): + """ + Converts a 2-bytes (16 bits) string to an integer. + + c: string containing bytes to convert + o: offset of bytes to convert in string + """ + return i8(c[o]) | (i8(c[o+1]) << 8) + + +def i32le(c, o=0): + """ + Converts a 4-bytes (32 bits) string to an integer. + + c: string containing bytes to convert + o: offset of bytes to convert in string + """ + return (i8(c[o]) | (i8(c[o+1]) << 8) | (i8(c[o+2]) << 16) | + (i8(c[o+3]) << 24)) + + +def i16be(c, o=0): + return (i8(c[o]) << 8) | i8(c[o+1]) + + +def i32be(c, o=0): + return ((i8(c[o]) << 24) | (i8(c[o+1]) << 16) | + (i8(c[o+2]) << 8) | i8(c[o+3])) + + +# Output, le = little endian, be = big endian +def o16le(i): + return o8(i) + o8(i >> 8) + + +def o32le(i): + return o8(i) + o8(i >> 8) + o8(i >> 16) + o8(i >> 24) + + +def o16be(i): + return o8(i >> 8) + o8(i) + + +def o32be(i): + return o8(i >> 24) + o8(i >> 16) + o8(i >> 8) + o8(i) + +# End of file diff --git a/pyPackages/pillowarmv7l/PIL/_imaging.cpython-34m.so b/pyPackages/pillowarmv7l/PIL/_imaging.cpython-34m.so new file mode 100644 index 0000000000000000000000000000000000000000..7b60c0c0189f853102adb8feee3f0044320a2f94 GIT binary patch literal 796425 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ŠK(O4A*=X+gl+4$2jr&%fKh;)Od^w%EvL8w} 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 z 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 z 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

fG&b=ag`JYOfJi*-`^T9g*)l|gM~ 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 !S}s5o&Kk_2ie)`OAaO8iRo6B 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%0As>G-d~?QmesBQ%^;I0$Gs%tQg@dEwKI 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%0AOQ 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 BIr#tp literal 0 HcmV?d00001 diff --git a/pyPackages/pillowarmv7l/PIL/_imagingmath.cpython-34m.so b/pyPackages/pillowarmv7l/PIL/_imagingmath.cpython-34m.so new file mode 100644 index 0000000000000000000000000000000000000000..c79b13fc0cf77c2c2a60e7dd97199d6a4d268c0f GIT binary patch literal 46560 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 ADF6Tf literal 0 HcmV?d00001 diff --git a/pyPackages/pillowarmv7l/PIL/_imagingmorph.cpython-34m.so b/pyPackages/pillowarmv7l/PIL/_imagingmorph.cpython-34m.so new file mode 100644 index 0000000000000000000000000000000000000000..c3364dcf7e7802595bf9a49c80387fed74f0d94c GIT binary patch literal 24279 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Əu? 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 convert to format (default is given by extension)") + print() + print(" -g convert to greyscale") + print(" -p convert to palette image (using standard palette)") + print(" -r convert to rgb") + print() + print(" -o optimize output (trade speed for size)") + print(" -q set compression quality (0-100, JPEG only)") + print() + print(" -f list supported file formats") + sys.exit(1) + +if len(sys.argv) == 1: + usage() + +try: + opt, argv = getopt.getopt(sys.argv[1:], "c:dfgopq:r") +except getopt.error as v: + print(v) + sys.exit(1) + +format = None +convert = None + +options = { } + +for o, a in opt: + + if o == "-f": + Image.init() + id = sorted(Image.ID) + print("Supported formats (* indicates output format):") + for i in id: + if i in Image.SAVE: + print(i+"*", end=' ') + else: + print(i, end=' ') + sys.exit(1) + + elif o == "-c": + format = a + + if o == "-g": + convert = "L" + elif o == "-p": + convert = "P" + elif o == "-r": + convert = "RGB" + + elif o == "-o": + options["optimize"] = 1 + elif o == "-q": + options["quality"] = string.atoi(a) + +if len(argv) != 2: + usage() + +try: + im = Image.open(argv[0]) + if convert and im.mode != convert: + im.draft(convert, im.size) + im = im.convert(convert) + if format: + im.save(argv[1], format, **options) + else: + im.save(argv[1], **options) +except: + print("cannot convert image", end=' ') + print("(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1])) diff --git a/pyPackages/pillowarmv7l/Pillow-2.7.0.data/scripts/pildriver.py b/pyPackages/pillowarmv7l/Pillow-2.7.0.data/scripts/pildriver.py new file mode 100644 index 0000000..e382171 --- /dev/null +++ b/pyPackages/pillowarmv7l/Pillow-2.7.0.data/scripts/pildriver.py @@ -0,0 +1,528 @@ +#!python +"""PILdriver, an image-processing calculator using PIL. + +An instance of class PILDriver is essentially a software stack machine +(Polish-notation interpreter) for sequencing PIL image +transformations. The state of the instance is the interpreter stack. + +The only method one will normally invoke after initialization is the +`execute' method. This takes an argument list of tokens, pushes them +onto the instance's stack, and then tries to clear the stack by +successive evaluation of PILdriver operators. Any part of the stack +not cleaned off persists and is part of the evaluation context for +the next call of the execute method. + +PILDriver doesn't catch any exceptions, on the theory that these +are actually diagnostic information that should be interpreted by +the calling code. + +When called as a script, the command-line arguments are passed to +a PILDriver instance. If there are no command-line arguments, the +module runs an interactive interpreter, each line of which is split into +space-separated tokens and passed to the execute method. + +In the method descriptions below, a first line beginning with the string +`usage:' means this method can be invoked with the token that follows +it. Following <>-enclosed arguments describe how the method interprets +the entries on the stack. Each argument specification begins with a +type specification: either `int', `float', `string', or `image'. + +All operations consume their arguments off the stack (use `dup' to +keep copies around). Use `verbose 1' to see the stack state displayed +before each operation. + +Usage examples: + + `show crop 0 0 200 300 open test.png' loads test.png, crops out a portion +of its upper-left-hand corner and displays the cropped portion. + + `save rotated.png rotate 30 open test.tiff' loads test.tiff, rotates it +30 degrees, and saves the result as rotated.png (in PNG format). +""" +# by Eric S. Raymond +# $Id$ + +# TO DO: +# 1. Add PILFont capabilities, once that's documented. +# 2. Add PILDraw operations. +# 3. Add support for composing and decomposing multiple-image files. +# + +from __future__ import print_function + +from PIL import Image + +class PILDriver: + + verbose = 0 + + def do_verbose(self): + """usage: verbose + + Set verbosity flag from top of stack. + """ + self.verbose = int(self.do_pop()) + + # The evaluation stack (internal only) + + stack = [] # Stack of pending operations + + def push(self, item): + "Push an argument onto the evaluation stack." + self.stack = [item] + self.stack + + def top(self): + "Return the top-of-stack element." + return self.stack[0] + + # Stack manipulation (callable) + + def do_clear(self): + """usage: clear + + Clear the stack. + """ + self.stack = [] + + def do_pop(self): + """usage: pop + + Discard the top element on the stack. + """ + top = self.stack[0] + self.stack = self.stack[1:] + return top + + def do_dup(self): + """usage: dup + + Duplicate the top-of-stack item. + """ + if hasattr(self, 'format'): # If it's an image, do a real copy + dup = self.stack[0].copy() + else: + dup = self.stack[0] + self.stack = [dup] + self.stack + + def do_swap(self): + """usage: swap + + Swap the top-of-stack item with the next one down. + """ + self.stack = [self.stack[1], self.stack[0]] + self.stack[2:] + + # Image module functions (callable) + + def do_new(self): + """usage: new : + + Create and push a greyscale image of given size and color. + """ + xsize = int(self.do_pop()) + ysize = int(self.do_pop()) + color = int(self.do_pop()) + self.push(Image.new("L", (xsize, ysize), color)) + + def do_open(self): + """usage: open + + Open the indicated image, read it, push the image on the stack. + """ + self.push(Image.open(self.do_pop())) + + def do_blend(self): + """usage: blend + + Replace two images and an alpha with the blended image. + """ + image1 = self.do_pop() + image2 = self.do_pop() + alpha = float(self.do_pop()) + self.push(Image.blend(image1, image2, alpha)) + + def do_composite(self): + """usage: composite + + Replace two images and a mask with their composite. + """ + image1 = self.do_pop() + image2 = self.do_pop() + mask = self.do_pop() + self.push(Image.composite(image1, image2, mask)) + + def do_merge(self): + """usage: merge [ [ []]] + + Merge top-of stack images in a way described by the mode. + """ + mode = self.do_pop() + bandlist = [] + for band in mode: + bandlist.append(self.do_pop()) + self.push(Image.merge(mode, bandlist)) + + # Image class methods + + def do_convert(self): + """usage: convert + + Convert the top image to the given mode. + """ + mode = self.do_pop() + image = self.do_pop() + self.push(image.convert(mode)) + + def do_copy(self): + """usage: copy + + Make and push a true copy of the top image. + """ + self.dup() + + def do_crop(self): + """usage: crop + + Crop and push a rectangular region from the current image. + """ + left = int(self.do_pop()) + upper = int(self.do_pop()) + right = int(self.do_pop()) + lower = int(self.do_pop()) + image = self.do_pop() + self.push(image.crop((left, upper, right, lower))) + + def do_draft(self): + """usage: draft + + Configure the loader for a given mode and size. + """ + mode = self.do_pop() + xsize = int(self.do_pop()) + ysize = int(self.do_pop()) + self.push(self.draft(mode, (xsize, ysize))) + + def do_filter(self): + """usage: filter + + Process the top image with the given filter. + """ + from PIL import ImageFilter + filter = eval("ImageFilter." + self.do_pop().upper()) + image = self.do_pop() + self.push(image.filter(filter)) + + def do_getbbox(self): + """usage: getbbox + + Push left, upper, right, and lower pixel coordinates of the top image. + """ + bounding_box = self.do_pop().getbbox() + self.push(bounding_box[3]) + self.push(bounding_box[2]) + self.push(bounding_box[1]) + self.push(bounding_box[0]) + + def do_getextrema(self): + """usage: extrema + + Push minimum and maximum pixel values of the top image. + """ + extrema = self.do_pop().extrema() + self.push(extrema[1]) + self.push(extrema[0]) + + def do_offset(self): + """usage: offset + + Offset the pixels in the top image. + """ + xoff = int(self.do_pop()) + yoff = int(self.do_pop()) + image = self.do_pop() + self.push(image.offset(xoff, yoff)) + + def do_paste(self): + """usage: paste + + Paste figure image into ground with upper left at given offsets. + """ + figure = self.do_pop() + xoff = int(self.do_pop()) + yoff = int(self.do_pop()) + ground = self.do_pop() + if figure.mode == "RGBA": + ground.paste(figure, (xoff, yoff), figure) + else: + ground.paste(figure, (xoff, yoff)) + self.push(ground) + + def do_resize(self): + """usage: resize + + Resize the top image. + """ + ysize = int(self.do_pop()) + xsize = int(self.do_pop()) + image = self.do_pop() + self.push(image.resize((xsize, ysize))) + + def do_rotate(self): + """usage: rotate + + Rotate image through a given angle + """ + angle = int(self.do_pop()) + image = self.do_pop() + self.push(image.rotate(angle)) + + def do_save(self): + """usage: save + + Save image with default options. + """ + filename = self.do_pop() + image = self.do_pop() + image.save(filename) + + def do_save2(self): + """usage: save2 + + Save image with specified options. + """ + filename = self.do_pop() + options = self.do_pop() + image = self.do_pop() + image.save(filename, None, options) + + def do_show(self): + """usage: show + + Display and pop the top image. + """ + self.do_pop().show() + + def do_thumbnail(self): + """usage: thumbnail + + Modify the top image in the stack to contain a thumbnail of itself. + """ + ysize = int(self.do_pop()) + xsize = int(self.do_pop()) + self.top().thumbnail((xsize, ysize)) + + def do_transpose(self): + """usage: transpose + + Transpose the top image. + """ + transpose = self.do_pop().upper() + image = self.do_pop() + self.push(image.transpose(transpose)) + + # Image attributes + + def do_format(self): + """usage: format + + Push the format of the top image onto the stack. + """ + self.push(self.do_pop().format) + + def do_mode(self): + """usage: mode + + Push the mode of the top image onto the stack. + """ + self.push(self.do_pop().mode) + + def do_size(self): + """usage: size + + Push the image size on the stack as (y, x). + """ + size = self.do_pop().size + self.push(size[0]) + self.push(size[1]) + + # ImageChops operations + + def do_invert(self): + """usage: invert + + Invert the top image. + """ + from PIL import ImageChops + self.push(ImageChops.invert(self.do_pop())) + + def do_lighter(self): + """usage: lighter + + Pop the two top images, push an image of the lighter pixels of both. + """ + from PIL import ImageChops + image1 = self.do_pop() + image2 = self.do_pop() + self.push(ImageChops.lighter(image1, image2)) + + def do_darker(self): + """usage: darker + + Pop the two top images, push an image of the darker pixels of both. + """ + from PIL import ImageChops + image1 = self.do_pop() + image2 = self.do_pop() + self.push(ImageChops.darker(image1, image2)) + + def do_difference(self): + """usage: difference + + Pop the two top images, push the difference image + """ + from PIL import ImageChops + image1 = self.do_pop() + image2 = self.do_pop() + self.push(ImageChops.difference(image1, image2)) + + def do_multiply(self): + """usage: multiply + + Pop the two top images, push the multiplication image. + """ + from PIL import ImageChops + image1 = self.do_pop() + image2 = self.do_pop() + self.push(ImageChops.multiply(image1, image2)) + + def do_screen(self): + """usage: screen + + Pop the two top images, superimpose their inverted versions. + """ + from PIL import ImageChops + image2 = self.do_pop() + image1 = self.do_pop() + self.push(ImageChops.screen(image1, image2)) + + def do_add(self): + """usage: add + + Pop the two top images, produce the scaled sum with offset. + """ + from PIL import ImageChops + image1 = self.do_pop() + image2 = self.do_pop() + scale = float(self.do_pop()) + offset = int(self.do_pop()) + self.push(ImageChops.add(image1, image2, scale, offset)) + + def do_subtract(self): + """usage: subtract + + Pop the two top images, produce the scaled difference with offset. + """ + from PIL import ImageChops + image1 = self.do_pop() + image2 = self.do_pop() + scale = float(self.do_pop()) + offset = int(self.do_pop()) + self.push(ImageChops.subtract(image1, image2, scale, offset)) + + # ImageEnhance classes + + def do_color(self): + """usage: color + + Enhance color in the top image. + """ + from PIL import ImageEnhance + factor = float(self.do_pop()) + image = self.do_pop() + enhancer = ImageEnhance.Color(image) + self.push(enhancer.enhance(factor)) + + def do_contrast(self): + """usage: contrast + + Enhance contrast in the top image. + """ + from PIL import ImageEnhance + factor = float(self.do_pop()) + image = self.do_pop() + enhancer = ImageEnhance.Contrast(image) + self.push(enhancer.enhance(factor)) + + def do_brightness(self): + """usage: brightness + + Enhance brightness in the top image. + """ + from PIL import ImageEnhance + factor = float(self.do_pop()) + image = self.do_pop() + enhancer = ImageEnhance.Brightness(image) + self.push(enhancer.enhance(factor)) + + def do_sharpness(self): + """usage: sharpness + + Enhance sharpness in the top image. + """ + from PIL import ImageEnhance + factor = float(self.do_pop()) + image = self.do_pop() + enhancer = ImageEnhance.Sharpness(image) + self.push(enhancer.enhance(factor)) + + # The interpreter loop + + def execute(self, list): + "Interpret a list of PILDriver commands." + list.reverse() + while len(list) > 0: + self.push(list[0]) + list = list[1:] + if self.verbose: + print("Stack: " + repr(self.stack)) + top = self.top() + if not isinstance(top, str): + continue + funcname = "do_" + top + if not hasattr(self, funcname): + continue + else: + self.do_pop() + func = getattr(self, funcname) + func() + +if __name__ == '__main__': + import sys + try: + import readline + except ImportError: + pass # not available on all platforms + + # If we see command-line arguments, interpret them as a stack state + # and execute. Otherwise go interactive. + + driver = PILDriver() + if len(sys.argv[1:]) > 0: + driver.execute(sys.argv[1:]) + else: + print("PILDriver says hello.") + while True: + try: + if sys.version_info[0] >= 3: + line = input('pildriver> ') + else: + line = raw_input('pildriver> ') + except EOFError: + print("\nPILDriver says goodbye.") + break + driver.execute(line.split()) + print(driver.stack) + +# The following sets edit modes for GNU EMACS +# Local Variables: +# mode:python +# End: diff --git a/pyPackages/pillowarmv7l/Pillow-2.7.0.data/scripts/pilfile.py b/pyPackages/pillowarmv7l/Pillow-2.7.0.data/scripts/pilfile.py new file mode 100644 index 0000000..a842db1 --- /dev/null +++ b/pyPackages/pillowarmv7l/Pillow-2.7.0.data/scripts/pilfile.py @@ -0,0 +1,95 @@ +#!python +# +# The Python Imaging Library. +# $Id$ +# +# a utility to identify image files +# +# this script identifies image files, extracting size and +# pixel mode information for known file formats. Note that +# you don't need the PIL C extension to use this module. +# +# History: +# 0.0 1995-09-01 fl Created +# 0.1 1996-05-18 fl Modified options, added debugging mode +# 0.2 1996-12-29 fl Added verify mode +# 0.3 1999-06-05 fl Don't mess up on class exceptions (1.5.2 and later) +# 0.4 2003-09-30 fl Expand wildcards on Windows; robustness tweaks +# + +from __future__ import print_function + +import site +import getopt, glob, sys + +from PIL import Image + +if len(sys.argv) == 1: + print("PIL File 0.4/2003-09-30 -- identify image files") + print("Usage: pilfile [option] files...") + print("Options:") + print(" -f list supported file formats") + print(" -i show associated info and tile data") + print(" -v verify file headers") + print(" -q quiet, don't warn for unidentified/missing/broken files") + sys.exit(1) + +try: + opt, args = getopt.getopt(sys.argv[1:], "fqivD") +except getopt.error as v: + print(v) + sys.exit(1) + +verbose = quiet = verify = 0 + +for o, a in opt: + if o == "-f": + Image.init() + id = sorted(Image.ID) + print("Supported formats:") + for i in id: + print(i, end=' ') + sys.exit(1) + elif o == "-i": + verbose = 1 + elif o == "-q": + quiet = 1 + elif o == "-v": + verify = 1 + elif o == "-D": + Image.DEBUG += 1 + +def globfix(files): + # expand wildcards where necessary + if sys.platform == "win32": + out = [] + for file in files: + if glob.has_magic(file): + out.extend(glob.glob(file)) + else: + out.append(file) + return out + return files + +for file in globfix(args): + try: + im = Image.open(file) + print("%s:" % file, im.format, "%dx%d" % im.size, im.mode, end=' ') + if verbose: + print(im.info, im.tile, end=' ') + print() + if verify: + try: + im.verify() + except: + if not quiet: + print("failed to verify image", end=' ') + print("(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1])) + except IOError as v: + if not quiet: + print(file, "failed:", v) + except: + import traceback + if not quiet: + print(file, "failed:", "unexpected error") + traceback.print_exc(file=sys.stdout) diff --git a/pyPackages/pillowarmv7l/Pillow-2.7.0.data/scripts/pilfont.py b/pyPackages/pillowarmv7l/Pillow-2.7.0.data/scripts/pilfont.py new file mode 100644 index 0000000..f99a776 --- /dev/null +++ b/pyPackages/pillowarmv7l/Pillow-2.7.0.data/scripts/pilfont.py @@ -0,0 +1,56 @@ +#!python +# +# The Python Imaging Library +# $Id$ +# +# PIL raster font compiler +# +# history: +# 1997-08-25 fl created +# 2002-03-10 fl use "from PIL import" +# + +from __future__ import print_function + +VERSION = "0.4" + +import glob, sys + +# drivers +from PIL import BdfFontFile +from PIL import PcfFontFile + +if len(sys.argv) <= 1: + print("PILFONT", VERSION, "-- PIL font compiler.") + print() + print("Usage: pilfont fontfiles...") + print() + print("Convert given font files to the PIL raster font format.") + print("This version of pilfont supports X BDF and PCF fonts.") + sys.exit(1) + +files = [] +for f in sys.argv[1:]: + files = files + glob.glob(f) + +for f in files: + + print(f + "...", end=' ') + + try: + + fp = open(f, "rb") + + try: + p = PcfFontFile.PcfFontFile(fp) + except SyntaxError: + fp.seek(0) + p = BdfFontFile.BdfFontFile(fp) + + p.save(f) + + except (SyntaxError, IOError): + print("failed") + + else: + print("OK") diff --git a/pyPackages/pillowarmv7l/Pillow-2.7.0.data/scripts/pilprint.py b/pyPackages/pillowarmv7l/Pillow-2.7.0.data/scripts/pilprint.py new file mode 100644 index 0000000..01469b7 --- /dev/null +++ b/pyPackages/pillowarmv7l/Pillow-2.7.0.data/scripts/pilprint.py @@ -0,0 +1,95 @@ +#!python +# +# The Python Imaging Library. +# $Id$ +# +# print image files to postscript printer +# +# History: +# 0.1 1996-04-20 fl Created +# 0.2 1996-10-04 fl Use draft mode when converting. +# 0.3 2003-05-06 fl Fixed a typo or two. +# + +from __future__ import print_function + +VERSION = "pilprint 0.3/2003-05-05" + +from PIL import Image +from PIL import PSDraw + +letter = ( 1.0*72, 1.0*72, 7.5*72, 10.0*72 ) + +def description(file, image): + import os + title = os.path.splitext(os.path.split(file)[1])[0] + format = " (%dx%d " + if image.format: + format = " (" + image.format + " %dx%d " + return title + format % image.size + image.mode + ")" + +import getopt, os, sys + +if len(sys.argv) == 1: + print("PIL Print 0.2a1/96-10-04 -- print image files") + print("Usage: pilprint files...") + print("Options:") + print(" -c colour printer (default is monochrome)") + print(" -p print via lpr (default is stdout)") + print(" -P same as -p but use given printer") + sys.exit(1) + +try: + opt, argv = getopt.getopt(sys.argv[1:], "cdpP:") +except getopt.error as v: + print(v) + sys.exit(1) + +printer = None # print to stdout +monochrome = 1 # reduce file size for most common case + +for o, a in opt: + if o == "-d": + # debug: show available drivers + Image.init() + print(Image.ID) + sys.exit(1) + elif o == "-c": + # colour printer + monochrome = 0 + elif o == "-p": + # default printer channel + printer = "lpr" + elif o == "-P": + # printer channel + printer = "lpr -P%s" % a + +for file in argv: + try: + + im = Image.open(file) + + title = description(file, im) + + if monochrome and im.mode not in ["1", "L"]: + im.draft("L", im.size) + im = im.convert("L") + + if printer: + fp = os.popen(printer, "w") + else: + fp = sys.stdout + + ps = PSDraw.PSDraw(fp) + + ps.begin_document() + ps.setfont("Helvetica-Narrow-Bold", 18) + ps.text((letter[0], letter[3]+24), title) + ps.setfont("Helvetica-Narrow-Bold", 8) + ps.text((letter[0], letter[1]-30), VERSION) + ps.image(letter, im) + ps.end_document() + + except: + print("cannot print image", end=' ') + print("(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1])) diff --git a/pyPackages/pillowarmv7l/Pillow-2.7.0.dist-info/DESCRIPTION.rst b/pyPackages/pillowarmv7l/Pillow-2.7.0.dist-info/DESCRIPTION.rst new file mode 100644 index 0000000..7dd5388 --- /dev/null +++ b/pyPackages/pillowarmv7l/Pillow-2.7.0.dist-info/DESCRIPTION.rst @@ -0,0 +1,28 @@ +Pillow +====== + +*Python Imaging Library (Fork)* + +Pillow is the "friendly" PIL fork by `Alex Clark and Contributors `_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. For more information, please `read the documentation `_, `check the changelog `_ and `find out how to contribute `_. + +.. image:: https://travis-ci.org/python-pillow/Pillow.svg?branch=master + :target: https://travis-ci.org/python-pillow/Pillow + :alt: Travis CI build status + +.. image:: https://pypip.in/v/Pillow/badge.png + :target: https://pypi.python.org/pypi/Pillow/ + :alt: Latest PyPI version + +.. image:: https://pypip.in/d/Pillow/badge.png + :target: https://pypi.python.org/pypi/Pillow/ + :alt: Number of PyPI downloads + +.. image:: https://coveralls.io/repos/python-pillow/Pillow/badge.png?branch=master + :target: https://coveralls.io/r/python-pillow/Pillow?branch=master + :alt: Code coverage + +.. image:: https://landscape.io/github/python-pillow/Pillow/master/landscape.png + :target: https://landscape.io/github/python-pillow/Pillow/master + :alt: Code health + + diff --git a/pyPackages/pillowarmv7l/Pillow-2.7.0.dist-info/METADATA b/pyPackages/pillowarmv7l/Pillow-2.7.0.dist-info/METADATA new file mode 100644 index 0000000..e7bd2c0 --- /dev/null +++ b/pyPackages/pillowarmv7l/Pillow-2.7.0.dist-info/METADATA @@ -0,0 +1,53 @@ +Metadata-Version: 2.0 +Name: Pillow +Version: 2.7.0 +Summary: Python Imaging Library (Fork) +Home-page: http://python-pillow.github.io/ +Author: Alex Clark (fork author) +Author-email: aclark@aclark.net +License: Standard PIL License +Keywords: Imaging +Platform: UNKNOWN +Classifier: Development Status :: 6 - Mature +Classifier: Topic :: Multimedia :: Graphics +Classifier: Topic :: Multimedia :: Graphics :: Capture :: Digital Camera +Classifier: Topic :: Multimedia :: Graphics :: Capture :: Scanners +Classifier: Topic :: Multimedia :: Graphics :: Capture :: Screen Capture +Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion +Classifier: Topic :: Multimedia :: Graphics :: Viewers +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.2 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 + +Pillow +====== + +*Python Imaging Library (Fork)* + +Pillow is the "friendly" PIL fork by `Alex Clark and Contributors `_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. For more information, please `read the documentation `_, `check the changelog `_ and `find out how to contribute `_. + +.. image:: https://travis-ci.org/python-pillow/Pillow.svg?branch=master + :target: https://travis-ci.org/python-pillow/Pillow + :alt: Travis CI build status + +.. image:: https://pypip.in/v/Pillow/badge.png + :target: https://pypi.python.org/pypi/Pillow/ + :alt: Latest PyPI version + +.. image:: https://pypip.in/d/Pillow/badge.png + :target: https://pypi.python.org/pypi/Pillow/ + :alt: Number of PyPI downloads + +.. image:: https://coveralls.io/repos/python-pillow/Pillow/badge.png?branch=master + :target: https://coveralls.io/r/python-pillow/Pillow?branch=master + :alt: Code coverage + +.. image:: https://landscape.io/github/python-pillow/Pillow/master/landscape.png + :target: https://landscape.io/github/python-pillow/Pillow/master + :alt: Code health + + diff --git a/pyPackages/pillowarmv7l/Pillow-2.7.0.dist-info/RECORD b/pyPackages/pillowarmv7l/Pillow-2.7.0.dist-info/RECORD new file mode 100644 index 0000000..a838b41 --- /dev/null +++ b/pyPackages/pillowarmv7l/Pillow-2.7.0.dist-info/RECORD @@ -0,0 +1,102 @@ +Pillow-2.7.0.dist-info/RECORD,, +Pillow-2.7.0.dist-info/WHEEL,sha256=rZT4o2iTq1qUJckPTBekqwFlcebNQH4el-PBBI-IQ7U,104 +Pillow-2.7.0.dist-info/metadata.json,sha256=zUnTxXSJpbs7YQN81tL8MDaAZ6-ubNRLHw6m179weko,1118 +Pillow-2.7.0.dist-info/DESCRIPTION.rst,sha256=RSre6zfamxsnhcS6Iac9VAHXMWDJkygzBNUQ1jwhIME,1302 +Pillow-2.7.0.dist-info/METADATA,sha256=ZrV7kb6DNUniGPNco9mnWWZh_4sMMlwA-r9T-D0r1WE,2336 +Pillow-2.7.0.dist-info/top_level.txt,sha256=riZqrk-hyZqh5f1Z0Zwii3dKfxEsByhu9cU9IODF-NY,4 +Pillow-2.7.0.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 +Pillow-2.7.0.data/scripts/pilfont.py,sha256=EVkStUuvSfevmB5H5Bfb6C8H4TEoQIDeZwMPVrRTFKI,1011 +Pillow-2.7.0.data/scripts/pilprint.py,sha256=bkf07hViRRXCSoWSqH9ML0fdeu0v9jmcyaWBAwiDnLg,2366 +Pillow-2.7.0.data/scripts/pilconvert.py,sha256=tSl6H017Ji7usF45X3YXQT-Lm-h6Aa5grvFROGch544,2320 +Pillow-2.7.0.data/scripts/pildriver.py,sha256=y_M5ezjhP0XKt1s0FlOlQq_QrdcLrMiYuHbh9B9QgDM,15587 +Pillow-2.7.0.data/scripts/pilfile.py,sha256=NYrWQ8LYlXFGvIPd9aT37z0thTOan-4OjD1O1-hO8LU,2565 +PIL/GribStubImagePlugin.py,sha256=BrMgUAZs1bfvNcgphqlDG0Bbvd5PY-pHEn_1zGmVHSo,1505 +PIL/WmfImagePlugin.py,sha256=hJSN7dK1rqz9hlmwyNwBmRAEDPu5MsZ2lguayd0J7WQ,4139 +PIL/XVThumbImagePlugin.py,sha256=sNyLCE1Y4ayyiH7mkJ2UYULNn-D61x_fPh_IJmwJ_Uc,1845 +PIL/ImageMorph.py,sha256=2smSgIxQYKwBDUlfWou2jygjZXDgDhcJcaa7AEmsRkY,7962 +PIL/_util.py,sha256=b3m5R7BosKxQ1NJud7vuxdmFouSfSYoaJ2P7WqlGtcE,553 +PIL/PaletteFile.py,sha256=wleoMhAafS87ZSWktV2FzOZKnAtHdDrtruEtj_Bnzf0,1105 +PIL/PcdImagePlugin.py,sha256=wx-mpL8jhCfkuXm5QW6SwOrX3yNzFIe60SzwEUEj7ww,1815 +PIL/ImtImagePlugin.py,sha256=hleP-mZH7EF2JKdsmi4rVZrdMCJLJl_nRYtmOw7IyyU,2222 +PIL/IcoImagePlugin.py,sha256=v3aBbHDCTLfOAP7ZnupTV5rQJ06etROv89Khv0ttwTg,9206 +PIL/ImImagePlugin.py,sha256=dCEPB2MuhPZCPewKoirr4G-0Nea9ZWBEn43ib_Dbxz0,10104 +PIL/FpxImagePlugin.py,sha256=acs5bKxAm5SqEaoUTsRNilZXw_af2ErBGNFF7peuSdI,6265 +PIL/BdfFontFile.py,sha256=EVmwabLMhOVnPBsUH4b0VOI_iBmBbbedeBWS20B7f2c,3355 +PIL/PixarImagePlugin.py,sha256=ruRFUTTWjp-Tw_xl6FKaWIDQYwguL6Z-2CasW6DZWRM,1614 +PIL/ImageDraw.py,sha256=fP89EuWkq9cZmWEzJl7qmVtH2RWq7xNwtqB0t5k8kSo,11667 +PIL/ImageOps.py,sha256=XPgQSyrypOQofZ800t72KezVPSdBxY5GclIjsfJEZu8,13923 +PIL/XbmImagePlugin.py,sha256=p0FBa5uS9eT5gNI08w7xainD1yxcus_SrpTPv2yuRkg,2455 +PIL/PcfFontFile.py,sha256=VmqdiYNtLI-c7hTBCqVuYkqWv40erTpON9OPNLDWuWg,6192 +PIL/MpegImagePlugin.py,sha256=GtV_O-3QhjtjJEs8f0wCYIpe33G68Udx4qYIfC7kAeg,1815 +PIL/FliImagePlugin.py,sha256=NiwSiYqbAdNG0v6ywhKkbZj17jZ_rgeuRADwUxI1n44,3460 +PIL/ImageMath.py,sha256=nJHWoTV4WOVXmZrNq-spzf18FYYPHaGjxBcWdvx--rE,7454 +PIL/PalmImagePlugin.py,sha256=IZR0mKEx8jkoJp_9rfqUJV61_QHEZ1iEoMBhyFC_5W0,9234 +PIL/MspImagePlugin.py,sha256=zJo5Ja5zkurZD0PRlqn-qqG3tRzIjqRgq_mCA4euEA8,2181 +PIL/ContainerIO.py,sha256=6N-rxsLN6xAFMWxOfwrnimYYYOC0P6ppYcM2r1Hp8Kw,2597 +PIL/GdImageFile.py,sha256=uP7YPhguHBzbBwOvNIONcpZPYnI06kULMuz-TW8SZnM,2182 +PIL/ImageMode.py,sha256=YDdvRTzi4Es9SlpK_emwR-ZbZW4pLqXhbAdUgy6c0Kw,1298 +PIL/ImageFilter.py,sha256=YbnrIBef2ebvcHMmBDZNpGAWPk92ir9lBZTJVDmdvUI,6620 +PIL/ImagePath.py,sha256=3AjcTwrReCInTyHlXxOddwn8JDyrkC2jczyi6r_xyfY,1231 +PIL/GifImagePlugin.py,sha256=cYofhFJj2_4i9GBk3LyLXx6-rVsMX3F7uAi9-3S_qU8,16887 +PIL/PyAccess.py,sha256=Cy7luHHMi0KiT0WgV4QGYDoM6ZP-6Rt65PnHDOPGTz8,8645 +PIL/ImageColor.py,sha256=3CAnIw4NwIZ5W24_fPV4FTTd-0aU1GDMuoKN5VyU_z8,7981 +PIL/XpmImagePlugin.py,sha256=Un9E6V5sQyaAHmc0B1JttqLNlUQHTyqJCvpK2E2nl2Y,3089 +PIL/PpmImagePlugin.py,sha256=sjgUZxXH4q3suPypI7teKbxs6jHTpv5aLWRny25s-UA,4581 +PIL/ImageShow.py,sha256=gaVDMpYEPsp1oDtb5tBOyx9fXmg46VtoHGroyGSyL24,4787 +PIL/TarIO.py,sha256=5TS3r1DEi4pCHCTW2Xqcwh1ff0oTgcm2bQRdZZx1Ftc,1223 +PIL/ImageTk.py,sha256=dAqzvvyPEUXtFROC_7mmc-ySfioUbZAN_Lip8scEy9Y,9092 +PIL/ImageFont.py,sha256=nNRIzt982UWS38TUG9QRDktW156oolUrUohe5Ckhfcw,18789 +PIL/SunImagePlugin.py,sha256=LLLdNIMhE2vclGvEUgES_R5zmWPVtHvctiQSKIa8RY4,1944 +PIL/Image.py,sha256=d7yGVMuDdJq68HJKhwJdqic9M8EYCpLpXG3xykrEEA4,80214 +PIL/ImagePalette.py,sha256=ApHiA8ZTaXT3OvKNi_44z8frdP9HLNjkQlNGHeeeVlk,6397 +PIL/__init__.py,sha256=-hmODD6O4QCii4i08pZzccu9rwDrFkOtPDvbpq_jAtU,1554 +PIL/Jpeg2KImagePlugin.py,sha256=BXjgwpeio6NZst1jo_fuEsDgGxTkJ50T5K6LTk5-Tik,7747 +PIL/ImageWin.py,sha256=2ERIZhzJ5d1w5aHcyeO0ONUQyGgDi_JHZ43WZW4HHf8,7668 +PIL/SpiderImagePlugin.py,sha256=izK7YOGjidy14zaMvkMvILMg3tSPZSRFUw-btGX75wI,9202 +PIL/ImageStat.py,sha256=XhzAmXbvcTKpYrT9KYrnoeytUBormN1e2tMo1LOVkNg,3826 +PIL/ImageDraw2.py,sha256=-yad95PlntI3f-biWPMJ5CfAtSSrXVMppVDdtBU0A6M,3199 +PIL/ImageCms.py,sha256=ljjLvs4e-53M9hztfKhZ5ajPUZKksWU5v2GPC8iZG-A,37131 +PIL/SgiImagePlugin.py,sha256=8UyPCPeaKe4LdPHl7XciZyyv-_1nMzicbF9G9FBYTsM,2111 +PIL/GimpGradientFile.py,sha256=xYXgu2wU7TKyUWxQ2zjjEeVLwfPylNjyzB0-AhkKZjs,3339 +PIL/MpoImagePlugin.py,sha256=jRYN6JB9ldSQdCTyG6u_8lfvgkqHuBAdzYiZe1ZCa68,2793 +PIL/BmpImagePlugin.py,sha256=QtlMM5K1zf9_HyLWCzL_1tHSghdFdYl543qbuZ4KFS8,7435 +PIL/TiffTags.py,sha256=M3lhOlg4o7hO6V4U_4RI9SrHCVZY3S4tUyb1U1YQ72o,7452 +PIL/EpsImagePlugin.py,sha256=V3errmq2KxdhPdfCntPgBfRThWcCMUSwPhKtxk8BgTk,12396 +PIL/BufrStubImagePlugin.py,sha256=bnQDTRwjOe-uDVkmO8KuW6-DsN2-eNL7zWh7aCdeOeY,1504 +PIL/IcnsImagePlugin.py,sha256=OK7J1Dt7StQl3XdKk3XH2d31u04-JFuCY3l9gqEETHo,9201 +PIL/ImageFile.py,sha256=eSCBDBNMBHawIPuADxBGtqkSfbf1RJYA94pFcNbrpXk,16025 +PIL/ImageQt.py,sha256=2WxO5mFqLUffgg-J9AecZVMJTqTG6uEZqserdCyDPUo,2888 +PIL/ImageFileIO.py,sha256=8F1GcVY9rYgzrmJJpb0ga2ATj9arscdUOO8CVZ4OAhA,1022 +PIL/WalImageFile.py,sha256=Ty1vo2r6Nzxytin_FKAjuPQg1_veSpr8MNJB1aa8vfo,5506 +PIL/PcxImagePlugin.py,sha256=ol1EeBMhrjPRUqlrlWaTS0HPXbvVDFXrhwqpY0qwFCE,5285 +PIL/Hdf5StubImagePlugin.py,sha256=AthIydrNqzKj_68Pa29b08fKKMu089A_Ef_G49aLLbo,1549 +PIL/FontFile.py,sha256=Irugt01KYj5fz5GeIcA1Gt1vk8mdpfbpe10R7T_dFGs,2825 +PIL/JpegImagePlugin.py,sha256=gp9S9vDTV4r8b0u7zcK9SeeOwMiP8RMUHD-gzgEe-V8,25018 +PIL/PdfImagePlugin.py,sha256=ByPn-u7rgGnBkl9KE-D2Cbpe1GTbBU5iVOJEdf06m4g,5807 +PIL/ImageTransform.py,sha256=rksN-m5SPqnTuINfQt_4Pkm_Qbfef78Lx-qK1CteeE0,2876 +PIL/IptcImagePlugin.py,sha256=CIGSzoYj7wl0AGIS4jkS4BBd-fJDbdBxtee_jwbGXIQ,6964 +PIL/McIdasImagePlugin.py,sha256=LJIKViAbq0IiN4E3M0XKFS2YGrEIifNq5lstCaqgmMA,1755 +PIL/ImageSequence.py,sha256=lnB9kn5jJ6Q227sJfpfUsEOnY49AcVt1Qh7kaEEZdLA,939 +PIL/_binary.py,sha256=j9qHnEQhqutp-bo6Rp0xplvkLjLdgb_Z6R8JeSLZzVQ,1575 +PIL/PSDraw.py,sha256=g0nun40mCBb9MYHqanG90Kkr8hDVc1PyW1uPltoiRrY,6872 +PIL/OleFileIO.py,sha256=I9xRyB2SnMv2U6gGza-mVi0BME88WBSXcvRWnEw2tU4,89467 +PIL/GbrImagePlugin.py,sha256=Ak2MaIQXd4JYoFaB08epg0LK6CNJj85NRuagZu2_cEo,1581 +PIL/GimpPaletteFile.py,sha256=qhHZPMXNRGHcAZuF45jG2qqz_VvDc8Jnv_UEE-Qergo,1333 +PIL/JpegPresets.py,sha256=rl9QjI3re1Ej50gYZCFekdYmndmBHRyyeRDuOvGp4kw,12335 +PIL/PsdImagePlugin.py,sha256=th2ZytaMH97XaBh9sAyq_0NrHuNHpEsXdH-j_Vi2w8k,7424 +PIL/TgaImagePlugin.py,sha256=O53Fq965kUToz5e-W9a8Jamc9_FDcoh9D3SZOjShKUk,4969 +PIL/DcxImagePlugin.py,sha256=rUnOlhFJKwDsD76b4K7vzvsc0pbMiKBsH8P25Kmwr0Q,1803 +PIL/PngImagePlugin.py,sha256=QWV9OUoVtdKF1VmQANlOs-DB1UJx421yERZi5Hs9BhQ,23339 +PIL/WebPImagePlugin.py,sha256=1GoMv5wCH8egdyhLu17FSBOha9WNQ_RvOjpgtfwY8K0,1961 +PIL/ImageEnhance.py,sha256=wYnG1q_AAOZGGoaBxcBSyOWgOytQhiBsJc97Sbk1BMI,3176 +PIL/CurImagePlugin.py,sha256=uyQuhu7gNcR4QHtPt9Hm2Lv89X9QB6ZscSaqc2eo-nc,1943 +PIL/ImageChops.py,sha256=jZCVJwGjIpbe4tKudbxpsLVvel4FvujQcd3HbFdtLCM,6184 +PIL/ExifTags.py,sha256=klIomCR-gXvHbeRRPmFg-PFJWEWw3DO_ZFPjmnoiPiA,5092 +PIL/FitsStubImagePlugin.py,sha256=Gebku65jfMMtEhblt3gy_RMGFx9kROq4PPAof_wo-lc,1653 +PIL/MicImagePlugin.py,sha256=r-O0lEDmS9ELkX2TzToViGcSUol_xJb41qYQpr9M8Hs,2174 +PIL/TiffImagePlugin.py,sha256=t1KBkSpXbdjjZvPBmYhVmRbyfQ4QVi8cI17LQOKPcAw,43236 +PIL/ImageGrab.py,sha256=ag9826OLUE6e6nhF0MccPXkfoPta6DxBuRoFE7fP-vs,1218 +PIL/OleFileIO-README.md,sha256=7BhnoybuHsGmbaGvQt4-juROtNpIzQZ9UvrXoEsJv0w,17799 +PIL/_imaging.cpython-34m.so,sha256=T676GYbDnHf_xL_3sEkzRBJrBz_4thFo7ce7rJA1N_A,796425 +PIL/_imagingmath.cpython-34m.so,sha256=t6yReDrjbeAl8U7T79H3JHjlu_ZV5JhfC_h8xjRJIxA,46560 +PIL/_imagingmorph.cpython-34m.so,sha256=SV1Z7hs2Y9ihmY-8kOOMu_aEBkTcMrpeY4uvt_m9mBQ,24279 diff --git a/pyPackages/pillowarmv7l/Pillow-2.7.0.dist-info/WHEEL b/pyPackages/pillowarmv7l/Pillow-2.7.0.dist-info/WHEEL new file mode 100644 index 0000000..6a4587c --- /dev/null +++ b/pyPackages/pillowarmv7l/Pillow-2.7.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.24.0) +Root-Is-Purelib: false +Tag: cp34-cp34m-linux_armv7l + diff --git a/pyPackages/pillowarmv7l/Pillow-2.7.0.dist-info/metadata.json b/pyPackages/pillowarmv7l/Pillow-2.7.0.dist-info/metadata.json new file mode 100644 index 0000000..796eed6 --- /dev/null +++ b/pyPackages/pillowarmv7l/Pillow-2.7.0.dist-info/metadata.json @@ -0,0 +1 @@ +{"classifiers": ["Development Status :: 6 - Mature", "Topic :: Multimedia :: Graphics", "Topic :: Multimedia :: Graphics :: Capture :: Digital Camera", "Topic :: Multimedia :: Graphics :: Capture :: Scanners", "Topic :: Multimedia :: Graphics :: Capture :: Screen Capture", "Topic :: Multimedia :: Graphics :: Graphics Conversion", "Topic :: Multimedia :: Graphics :: Viewers", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4"], "version": "2.7.0", "license": "Standard PIL License", "extensions": {"python.details": {"project_urls": {"Home": "http://python-pillow.github.io/"}, "document_names": {"description": "DESCRIPTION.rst"}, "contacts": [{"name": "Alex Clark (fork author)", "role": "author", "email": "aclark@aclark.net"}]}}, "keywords": ["Imaging"], "generator": "bdist_wheel (0.24.0)", "name": "Pillow", "metadata_version": "2.0", "summary": "Python Imaging Library (Fork)"} \ No newline at end of file diff --git a/pyPackages/pillowarmv7l/Pillow-2.7.0.dist-info/top_level.txt b/pyPackages/pillowarmv7l/Pillow-2.7.0.dist-info/top_level.txt new file mode 100644 index 0000000..b338169 --- /dev/null +++ b/pyPackages/pillowarmv7l/Pillow-2.7.0.dist-info/top_level.txt @@ -0,0 +1 @@ +PIL diff --git a/pyPackages/pillowarmv7l/Pillow-2.7.0.dist-info/zip-safe b/pyPackages/pillowarmv7l/Pillow-2.7.0.dist-info/zip-safe new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/pyPackages/pillowarmv7l/Pillow-2.7.0.dist-info/zip-safe @@ -0,0 +1 @@ + diff --git a/pyPackages/pillowarmv7l/__init__.py b/pyPackages/pillowarmv7l/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pyPackages/pillowx86/PIL/BdfFontFile.py b/pyPackages/pillowx86/PIL/BdfFontFile.py new file mode 100644 index 0000000..c528124 --- /dev/null +++ b/pyPackages/pillowx86/PIL/BdfFontFile.py @@ -0,0 +1,133 @@ +# +# The Python Imaging Library +# $Id$ +# +# bitmap distribution font (bdf) file parser +# +# history: +# 1996-05-16 fl created (as bdf2pil) +# 1997-08-25 fl converted to FontFile driver +# 2001-05-25 fl removed bogus __init__ call +# 2002-11-20 fl robustification (from Kevin Cazabon, Dmitry Vasiliev) +# 2003-04-22 fl more robustification (from Graham Dumpleton) +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1997-2003 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image +from PIL import FontFile + + +# -------------------------------------------------------------------- +# parse X Bitmap Distribution Format (BDF) +# -------------------------------------------------------------------- + +bdf_slant = { + "R": "Roman", + "I": "Italic", + "O": "Oblique", + "RI": "Reverse Italic", + "RO": "Reverse Oblique", + "OT": "Other" +} + +bdf_spacing = { + "P": "Proportional", + "M": "Monospaced", + "C": "Cell" +} + + +def bdf_char(f): + # skip to STARTCHAR + while True: + s = f.readline() + if not s: + return None + if s[:9] == b"STARTCHAR": + break + id = s[9:].strip().decode('ascii') + + # load symbol properties + props = {} + while True: + s = f.readline() + if not s or s[:6] == b"BITMAP": + break + i = s.find(b" ") + props[s[:i].decode('ascii')] = s[i+1:-1].decode('ascii') + + # load bitmap + bitmap = [] + while True: + s = f.readline() + if not s or s[:7] == b"ENDCHAR": + break + bitmap.append(s[:-1]) + bitmap = b"".join(bitmap) + + [x, y, l, d] = [int(s) for s in props["BBX"].split()] + [dx, dy] = [int(s) for s in props["DWIDTH"].split()] + + bbox = (dx, dy), (l, -d-y, x+l, -d), (0, 0, x, y) + + try: + im = Image.frombytes("1", (x, y), bitmap, "hex", "1") + except ValueError: + # deal with zero-width characters + im = Image.new("1", (x, y)) + + return id, int(props["ENCODING"]), bbox, im + + +## +# Font file plugin for the X11 BDF format. + +class BdfFontFile(FontFile.FontFile): + + def __init__(self, fp): + + FontFile.FontFile.__init__(self) + + s = fp.readline() + if s[:13] != b"STARTFONT 2.1": + raise SyntaxError("not a valid BDF file") + + props = {} + comments = [] + + while True: + s = fp.readline() + if not s or s[:13] == b"ENDPROPERTIES": + break + i = s.find(b" ") + 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')) + + font = props["FONT"].split("-") + + font[4] = bdf_slant[font[4].upper()] + font[11] = bdf_spacing[font[11].upper()] + + # ascent = int(props["FONT_ASCENT"]) + # descent = int(props["FONT_DESCENT"]) + + # fontname = ";".join(font[1:]) + + # print "#", fontname + # for i in comments: + # print "#", i + + font = [] + while True: + c = bdf_char(fp) + if not c: + break + id, ch, (xy, dst, src), im = c + if 0 <= ch < len(self.glyph): + self.glyph[ch] = xy, dst, src, im diff --git a/pyPackages/pillowx86/PIL/BmpImagePlugin.py b/pyPackages/pillowx86/PIL/BmpImagePlugin.py new file mode 100644 index 0000000..917d43b --- /dev/null +++ b/pyPackages/pillowx86/PIL/BmpImagePlugin.py @@ -0,0 +1,264 @@ +# +# The Python Imaging Library. +# $Id$ +# +# BMP file handler +# +# Windows (and OS/2) native bitmap storage format. +# +# history: +# 1995-09-01 fl Created +# 1996-04-30 fl Added save +# 1997-08-27 fl Fixed save of 1-bit images +# 1998-03-06 fl Load P images as L where possible +# 1998-07-03 fl Load P images as 1 where possible +# 1998-12-29 fl Handle small palettes +# 2002-12-30 fl Fixed load of 1-bit palette images +# 2003-04-21 fl Fixed load of 1-bit monochrome images +# 2003-04-23 fl Added limited support for BI_BITFIELDS compression +# +# Copyright (c) 1997-2003 by Secret Labs AB +# Copyright (c) 1995-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.7" + + +from PIL import Image, ImageFile, ImagePalette, _binary +import math + +i8 = _binary.i8 +i16 = _binary.i16le +i32 = _binary.i32le +o8 = _binary.o8 +o16 = _binary.o16le +o32 = _binary.o32le + +# +# -------------------------------------------------------------------- +# Read BMP file + +BIT2MODE = { + # bits => mode, rawmode + 1: ("P", "P;1"), + 4: ("P", "P;4"), + 8: ("P", "P"), + 16: ("RGB", "BGR;15"), + 24: ("RGB", "BGR"), + 32: ("RGB", "BGRX") +} + + +def _accept(prefix): + return prefix[:2] == b"BM" + + +## +# Image plugin for the Windows BMP format. + +class BmpImageFile(ImageFile.ImageFile): + + format = "BMP" + format_description = "Windows Bitmap" + + def _bitmap(self, header=0, offset=0): + if header: + self.fp.seek(header) + + read = self.fp.read + + # CORE/INFO + s = read(4) + s = s + ImageFile._safe_read(self.fp, i32(s)-4) + + if len(s) == 12: + + # OS/2 1.0 CORE + bits = i16(s[10:]) + self.size = i16(s[4:]), i16(s[6:]) + compression = 0 + lutsize = 3 + colors = 0 + direction = -1 + + elif len(s) in [40, 64, 108, 124]: + + # WIN 3.1 or OS/2 2.0 INFO + bits = i16(s[14:]) + self.size = i32(s[4:]), i32(s[8:]) + compression = i32(s[16:]) + pxperm = (i32(s[24:]), i32(s[28:])) # Pixels per meter + lutsize = 4 + colors = i32(s[32:]) + direction = -1 + if i8(s[11]) == 0xff: + # upside-down storage + self.size = self.size[0], 2**32 - self.size[1] + direction = 0 + + self.info["dpi"] = tuple(map(lambda x: math.ceil(x / 39.3701), + pxperm)) + + else: + raise IOError("Unsupported BMP header type (%d)" % len(s)) + + if (self.size[0]*self.size[1]) > 2**31: + # Prevent DOS for > 2gb images + raise IOError("Unsupported BMP Size: (%dx%d)" % self.size) + + if not colors: + colors = 1 << bits + + # MODE + try: + self.mode, rawmode = BIT2MODE[bits] + except KeyError: + raise IOError("Unsupported BMP pixel depth (%d)" % bits) + + if compression == 3: + # BI_BITFIELDS compression + mask = i32(read(4)), i32(read(4)), i32(read(4)) + if bits == 32 and mask == (0xff0000, 0x00ff00, 0x0000ff): + rawmode = "BGRX" + elif bits == 16 and mask == (0x00f800, 0x0007e0, 0x00001f): + rawmode = "BGR;16" + elif bits == 16 and mask == (0x007c00, 0x0003e0, 0x00001f): + rawmode = "BGR;15" + else: + # print bits, map(hex, mask) + raise IOError("Unsupported BMP bitfields layout") + elif compression != 0: + raise IOError("Unsupported BMP compression (%d)" % compression) + + # LUT + if self.mode == "P": + palette = [] + greyscale = 1 + if colors == 2: + indices = (0, 255) + elif colors > 2**16 or colors <= 0: # We're reading a i32. + raise IOError("Unsupported BMP Palette size (%d)" % colors) + else: + indices = list(range(colors)) + for i in indices: + rgb = read(lutsize)[:3] + if rgb != o8(i)*3: + greyscale = 0 + palette.append(rgb) + if greyscale: + if colors == 2: + self.mode = rawmode = "1" + else: + self.mode = rawmode = "L" + else: + self.mode = "P" + self.palette = ImagePalette.raw( + "BGR", b"".join(palette) + ) + + if not offset: + offset = self.fp.tell() + + self.tile = [("raw", + (0, 0) + self.size, + offset, + (rawmode, ((self.size[0]*bits+31) >> 3) & (~3), + direction))] + + self.info["compression"] = compression + + def _open(self): + + # HEAD + s = self.fp.read(14) + if s[:2] != b"BM": + raise SyntaxError("Not a BMP file") + offset = i32(s[10:]) + + self._bitmap(offset=offset) + + +class DibImageFile(BmpImageFile): + + format = "DIB" + format_description = "Windows Bitmap" + + def _open(self): + self._bitmap() + +# +# -------------------------------------------------------------------- +# Write BMP file + +SAVE = { + "1": ("1", 1, 2), + "L": ("L", 8, 256), + "P": ("P", 8, 256), + "RGB": ("BGR", 24, 0), +} + + +def _save(im, fp, filename, check=0): + try: + rawmode, bits, colors = SAVE[im.mode] + except KeyError: + raise IOError("cannot write mode %s as BMP" % im.mode) + + if check: + return check + + info = im.encoderinfo + + dpi = info.get("dpi", (96, 96)) + + # 1 meter == 39.3701 inches + ppm = tuple(map(lambda x: int(x * 39.3701), dpi)) + + stride = ((im.size[0]*bits+7)//8+3) & (~3) + header = 40 # or 64 for OS/2 version 2 + offset = 14 + header + colors * 4 + image = stride * im.size[1] + + # bitmap header + fp.write(b"BM" + # file type (magic) + o32(offset+image) + # file size + o32(0) + # reserved + o32(offset)) # 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(b"\0" * (header - 40)) # padding (for OS/2 format) + + if im.mode == "1": + for i in (0, 255): + fp.write(o8(i) * 4) + elif im.mode == "L": + for i in range(256): + fp.write(o8(i) * 4) + elif im.mode == "P": + fp.write(im.im.getpalette("RGB", "BGRX")) + + ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0, + (rawmode, stride, -1))]) + +# +# -------------------------------------------------------------------- +# Registry + +Image.register_open(BmpImageFile.format, BmpImageFile, _accept) +Image.register_save(BmpImageFile.format, _save) + +Image.register_extension(BmpImageFile.format, ".bmp") diff --git a/pyPackages/pillowx86/PIL/BufrStubImagePlugin.py b/pyPackages/pillowx86/PIL/BufrStubImagePlugin.py new file mode 100644 index 0000000..45ee547 --- /dev/null +++ b/pyPackages/pillowx86/PIL/BufrStubImagePlugin.py @@ -0,0 +1,72 @@ +# +# The Python Imaging Library +# $Id$ +# +# BUFR stub adapter +# +# Copyright (c) 1996-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image, ImageFile + +_handler = None + + +## +# Install application-specific BUFR image handler. +# +# @param handler Handler object. + +def register_handler(handler): + global _handler + _handler = handler + + +# -------------------------------------------------------------------- +# Image adapter + +def _accept(prefix): + return prefix[:4] == b"BUFR" or prefix[:4] == b"ZCZC" + + +class BufrStubImageFile(ImageFile.StubImageFile): + + format = "BUFR" + format_description = "BUFR" + + def _open(self): + + offset = self.fp.tell() + + if not _accept(self.fp.read(8)): + raise SyntaxError("Not a BUFR file") + + self.fp.seek(offset) + + # make something up + self.mode = "F" + self.size = 1, 1 + + loader = self._load() + if loader: + loader.open(self) + + def _load(self): + return _handler + + +def _save(im, fp, filename): + if _handler is None or not hasattr("_handler", "save"): + raise IOError("BUFR save handler not installed") + _handler.save(im, fp, filename) + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(BufrStubImageFile.format, BufrStubImageFile, _accept) +Image.register_save(BufrStubImageFile.format, _save) + +Image.register_extension(BufrStubImageFile.format, ".bufr") diff --git a/pyPackages/pillowx86/PIL/ContainerIO.py b/pyPackages/pillowx86/PIL/ContainerIO.py new file mode 100644 index 0000000..dcedcd6 --- /dev/null +++ b/pyPackages/pillowx86/PIL/ContainerIO.py @@ -0,0 +1,117 @@ +# +# The Python Imaging Library. +# $Id$ +# +# a class to read from a container file +# +# History: +# 1995-06-18 fl Created +# 1995-09-07 fl Added readline(), readlines() +# +# Copyright (c) 1997-2001 by Secret Labs AB +# Copyright (c) 1995 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +## +# A file object that provides read access to a part of an existing +# file (for example a TAR file). + + +class ContainerIO: + + ## + # Create file object. + # + # @param file Existing file. + # @param offset Start of region, in bytes. + # @param length Size of region, in bytes. + + def __init__(self, file, offset, length): + self.fh = file + self.pos = 0 + self.offset = offset + self.length = length + self.fh.seek(offset) + + ## + # Always false. + + def isatty(self): + return 0 + + ## + # Move file pointer. + # + # @param offset Offset in bytes. + # @param mode Starting position. Use 0 for beginning of region, 1 + # for current offset, and 2 for end of region. You cannot move + # the pointer outside the defined region. + + def seek(self, offset, mode=0): + if mode == 1: + self.pos = self.pos + offset + elif mode == 2: + self.pos = self.length + offset + else: + self.pos = offset + # clamp + self.pos = max(0, min(self.pos, self.length)) + self.fh.seek(self.offset + self.pos) + + ## + # Get current file pointer. + # + # @return Offset from start of region, in bytes. + + def tell(self): + return self.pos + + ## + # Read data. + # + # @def read(bytes=0) + # @param bytes Number of bytes to read. If omitted or zero, + # read until end of region. + # @return An 8-bit string. + + def read(self, n=0): + if n: + n = min(n, self.length - self.pos) + else: + n = self.length - self.pos + if not n: # EOF + return "" + self.pos = self.pos + n + return self.fh.read(n) + + ## + # Read a line of text. + # + # @return An 8-bit string. + + def readline(self): + s = "" + while True: + c = self.read(1) + if not c: + break + s = s + c + if c == "\n": + break + return s + + ## + # Read multiple lines of text. + # + # @return A list of 8-bit strings. + + def readlines(self): + l = [] + while True: + s = self.readline() + if not s: + break + l.append(s) + return l diff --git a/pyPackages/pillowx86/PIL/CurImagePlugin.py b/pyPackages/pillowx86/PIL/CurImagePlugin.py new file mode 100644 index 0000000..0178957 --- /dev/null +++ b/pyPackages/pillowx86/PIL/CurImagePlugin.py @@ -0,0 +1,87 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Windows Cursor support for PIL +# +# notes: +# uses BmpImagePlugin.py to read the bitmap data. +# +# history: +# 96-05-27 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.1" + +from PIL import Image, BmpImagePlugin, _binary + + +# +# -------------------------------------------------------------------- + +i8 = _binary.i8 +i16 = _binary.i16le +i32 = _binary.i32le + + +def _accept(prefix): + return prefix[:4] == b"\0\0\2\0" + + +## +# Image plugin for Windows Cursor files. + +class CurImageFile(BmpImagePlugin.BmpImageFile): + + format = "CUR" + format_description = "Windows Cursor" + + def _open(self): + + offset = self.fp.tell() + + # check magic + s = self.fp.read(6) + if not _accept(s): + raise SyntaxError("not a CUR file") + + # pick the largest cursor in the file + m = b"" + for i in range(i16(s[4:])): + s = self.fp.read(16) + if not m: + m = s + elif i8(s[0]) > i8(m[0]) and i8(s[1]) > i8(m[1]): + m = s + # print "width", i8(s[0]) + # print "height", i8(s[1]) + # print "colors", i8(s[2]) + # print "reserved", i8(s[3]) + # print "hotspot x", i16(s[4:]) + # print "hotspot y", i16(s[6:]) + # print "bytes", i32(s[8:]) + # print "offset", i32(s[12:]) + + # load as bitmap + self._bitmap(i32(m[12:]) + offset) + + # patch up the bitmap height + 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 + + return + + +# +# -------------------------------------------------------------------- + +Image.register_open("CUR", CurImageFile, _accept) + +Image.register_extension("CUR", ".cur") diff --git a/pyPackages/pillowx86/PIL/DcxImagePlugin.py b/pyPackages/pillowx86/PIL/DcxImagePlugin.py new file mode 100644 index 0000000..0940b39 --- /dev/null +++ b/pyPackages/pillowx86/PIL/DcxImagePlugin.py @@ -0,0 +1,79 @@ +# +# The Python Imaging Library. +# $Id$ +# +# DCX file handling +# +# DCX is a container file format defined by Intel, commonly used +# for fax applications. Each DCX file consists of a directory +# (a list of file offsets) followed by a set of (usually 1-bit) +# PCX files. +# +# History: +# 1995-09-09 fl Created +# 1996-03-20 fl Properly derived from PcxImageFile. +# 1998-07-15 fl Renamed offset attribute to avoid name clash +# 2002-07-30 fl Fixed file handling +# +# Copyright (c) 1997-98 by Secret Labs AB. +# Copyright (c) 1995-96 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.2" + +from PIL import Image, _binary + +from PIL.PcxImagePlugin import PcxImageFile + +MAGIC = 0x3ADE68B1 # QUIZ: what's this value, then? + +i32 = _binary.i32le + + +def _accept(prefix): + return i32(prefix) == MAGIC + + +## +# Image plugin for the Intel DCX format. + +class DcxImageFile(PcxImageFile): + + format = "DCX" + format_description = "Intel DCX" + + def _open(self): + + # Header + s = self.fp.read(4) + if i32(s) != MAGIC: + raise SyntaxError("not a DCX file") + + # Component directory + self._offset = [] + for i in range(1024): + offset = i32(self.fp.read(4)) + if not offset: + break + self._offset.append(offset) + + self.__fp = self.fp + self.seek(0) + + def seek(self, frame): + if frame >= len(self._offset): + raise EOFError("attempt to seek outside DCX directory") + self.frame = frame + self.fp = self.__fp + self.fp.seek(self._offset[frame]) + PcxImageFile._open(self) + + def tell(self): + return self.frame + + +Image.register_open("DCX", DcxImageFile, _accept) + +Image.register_extension("DCX", ".dcx") diff --git a/pyPackages/pillowx86/PIL/EpsImagePlugin.py b/pyPackages/pillowx86/PIL/EpsImagePlugin.py new file mode 100644 index 0000000..d077ca1 --- /dev/null +++ b/pyPackages/pillowx86/PIL/EpsImagePlugin.py @@ -0,0 +1,424 @@ +# +# The Python Imaging Library. +# $Id$ +# +# EPS file handling +# +# History: +# 1995-09-01 fl Created (0.1) +# 1996-05-18 fl Don't choke on "atend" fields, Ghostscript interface (0.2) +# 1996-08-22 fl Don't choke on floating point BoundingBox values +# 1996-08-23 fl Handle files from Macintosh (0.3) +# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.4) +# 2003-09-07 fl Check gs.close status (from Federico Di Gregorio) (0.5) +# 2014-05-07 e Handling of EPS with binary preview and fixed resolution +# resizing +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1995-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.5" + +import re +import io +from PIL import Image, ImageFile, _binary + +# +# -------------------------------------------------------------------- + +i32 = _binary.i32le +o32 = _binary.o32le + +split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$") +field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$") + +gs_windows_binary = None +import sys +if sys.platform.startswith('win'): + import shutil + if hasattr(shutil, 'which'): + which = shutil.which + else: + # Python < 3.3 + import distutils.spawn + which = distutils.spawn.find_executable + for binary in ('gswin32c', 'gswin64c', 'gs'): + if which(binary) is not None: + gs_windows_binary = binary + break + else: + gs_windows_binary = False + + +def has_ghostscript(): + if gs_windows_binary: + return True + if not sys.platform.startswith('win'): + import subprocess + try: + gs = subprocess.Popen(['gs', '--version'], stdout=subprocess.PIPE) + gs.stdout.read() + return True + except OSError: + # no ghostscript + pass + return False + + +def Ghostscript(tile, size, fp, scale=1): + """Render an image using Ghostscript""" + + # Unpack decoder tile + decoder, tile, offset, data = tile[0] + length, bbox = data + + # Hack to support hi-res rendering + scale = int(scale) or 1 + # orig_size = size + # 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]))) + # print("Ghostscript", scale, size, orig_size, bbox, orig_bbox, res) + + import os + import subprocess + import tempfile + + out_fd, outfile = tempfile.mkstemp() + os.close(out_fd) + + infile_temp = None + if hasattr(fp, 'name') and os.path.exists(fp.name): + infile = fp.name + else: + in_fd, infile_temp = tempfile.mkstemp() + os.close(in_fd) + infile = infile_temp + + # ignore length and offset! + # ghostscript can read it + # copy whole file to read in ghostscript + with open(infile_temp, 'wb') as f: + # fetch length of fp + fp.seek(0, 2) + fsize = fp.tell() + # ensure start position + # go back + fp.seek(0) + lengthfile = fsize + while lengthfile > 0: + 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) + "-dNOPAUSE -dSAFER", # don't pause between pages, + # safe mode + "-sDEVICE=ppmraw", # ppm driver + "-sOutputFile=%s" % outfile, # output file + "-c", "%d %d translate" % (-bbox[0], -bbox[1]), + # adjust for image origin + "-f", infile, # input file + ] + + if gs_windows_binary is not None: + if not gs_windows_binary: + raise WindowsError('Unable to locate Ghostscript on paths') + command[0] = gs_windows_binary + + # push data through ghostscript + try: + gs = subprocess.Popen(command, stdin=subprocess.PIPE, + stdout=subprocess.PIPE) + gs.stdin.close() + status = gs.wait() + if status: + raise IOError("gs failed (status %d)" % status) + im = Image.core.open_ppm(outfile) + finally: + try: + os.unlink(outfile) + if infile_temp: + os.unlink(infile_temp) + except: + pass + + return im + + +class PSFile: + """ + Wrapper for bytesio object that treats either CR or LF as end of line. + """ + def __init__(self, fp): + self.fp = fp + self.char = None + + def seek(self, offset, whence=0): + self.char = None + self.fp.seek(offset, whence) + + def readline(self): + s = self.char or b"" + self.char = None + + c = self.fp.read(1) + while c not in b"\r\n": + s = s + c + c = self.fp.read(1) + + self.char = self.fp.read(1) + # line endings can be 1 or 2 of \r \n, in either order + if self.char in b"\r\n": + self.char = None + + return s.decode('latin-1') + + +def _accept(prefix): + return prefix[:4] == b"%!PS" or i32(prefix) == 0xC6D3D0C5 + +## +# Image plugin for Encapsulated Postscript. This plugin supports only +# a few variants of this format. + + +class EpsImageFile(ImageFile.ImageFile): + """EPS File Parser for the Python Imaging Library""" + + format = "EPS" + format_description = "Encapsulated Postscript" + + mode_map = {1: "L", 2: "LAB", 3: "RGB"} + + def _open(self): + (length, offset) = self._find_offset(self.fp) + + # Rewrap the open file pointer in something that will + # convert line endings and decode to latin-1. + try: + if bytes is str: + # Python2, no encoding conversion necessary + fp = open(self.fp.name, "Ur") + else: + # Python3, can use bare open command. + fp = open(self.fp.name, "Ur", encoding='latin-1') + except: + # Expect this for bytesio/stringio + fp = PSFile(self.fp) + + # go to offset - start of "%!PS" + fp.seek(offset) + + box = None + + self.mode = "RGB" + self.size = 1, 1 # FIXME: huh? + + # + # Load EPS header + + s = fp.readline().strip('\r\n') + + while s: + if len(s) > 255: + raise SyntaxError("not an EPS file") + + try: + m = split.match(s) + except re.error as v: + raise SyntaxError("not an EPS file") + + if m: + k, v = m.group(1, 2) + self.info[k] = v + if k == "BoundingBox": + try: + # Note: The DSC spec says that BoundingBox + # fields should be integers, but some drivers + # put floating point values there anyway. + box = [int(float(s)) for s in v.split()] + self.size = box[2] - box[0], box[3] - box[1] + self.tile = [("eps", (0, 0) + self.size, offset, + (length, box))] + except: + pass + + else: + m = field.match(s) + if m: + k = m.group(1) + + if k == "EndComments": + break + if k[:8] == "PS-Adobe": + self.info[k[:8]] = k[9:] + else: + self.info[k] = "" + elif s[0] == '%': + # handle non-DSC Postscript comments that some + # tools mistakenly put in the Comments section + pass + else: + raise IOError("bad EPS header") + + s = fp.readline().strip('\r\n') + + if s[0] != "%": + break + + # + # Scan for an "ImageData" descriptor + + while s[0] == "%": + + if len(s) > 255: + raise SyntaxError("not an EPS file") + + if s[:11] == "%ImageData:": + # Encoded bitmapped image. + [x, y, bi, mo, z3, z4, en, id] = s[11:].split(None, 7) + + if int(bi) != 8: + break + try: + self.mode = self.mode_map[int(mo)] + except: + break + + self.size = int(x), int(y) + return + + s = fp.readline().strip('\r\n') + if not s: + break + + if not box: + raise IOError("cannot determine EPS bounding box") + + def _find_offset(self, fp): + + s = fp.read(160) + + if s[:4] == b"%!PS": + # for HEAD without binary preview + fp.seek(0, 2) + length = fp.tell() + offset = 0 + elif i32(s[0:4]) == 0xC6D3D0C5: + # FIX for: Some EPS file not handled correctly / issue #302 + # EPS can contain binary data + # or start directly with latin coding + # more info see: + # http://partners.adobe.com/public/developer/en/ps/5002.EPSF_Spec.pdf + offset = i32(s[4:8]) + length = i32(s[8:12]) + else: + raise SyntaxError("not an EPS file") + + return (length, offset) + + def load(self, scale=1): + # Load EPS via Ghostscript + if not self.tile: + return + self.im = Ghostscript(self.tile, self.size, self.fp, scale) + self.mode = self.im.mode + self.size = self.im.size + self.tile = [] + + def load_seek(self, *args, **kwargs): + # we can't incrementally load, so force ImageFile.parser to + # use our custom load method by defining this method. + pass + + +# +# -------------------------------------------------------------------- + +def _save(im, fp, filename, eps=1): + """EPS Writer for the Python Imaging Library.""" + + # + # make sure image data is available + im.load() + + # + # determine postscript image mode + if im.mode == "L": + operator = (8, 1, "image") + elif im.mode == "RGB": + operator = (8, 3, "false 3 colorimage") + elif im.mode == "CMYK": + operator = (8, 4, "false 4 colorimage") + else: + raise ValueError("image mode is not supported") + + class NoCloseStream: + def __init__(self, fp): + self.fp = fp + + def __getattr__(self, name): + return getattr(self.fp, name) + + def close(self): + pass + + base_fp = fp + fp = NoCloseStream(fp) + if sys.version_info[0] > 2: + fp = io.TextIOWrapper(fp, encoding='latin-1') + + if eps: + # + # write EPS header + fp.write("%!PS-Adobe-3.0 EPSF-3.0\n") + fp.write("%%Creator: PIL 0.1 EpsEncode\n") + # fp.write("%%CreationDate: %s"...) + fp.write("%%%%BoundingBox: 0 0 %d %d\n" % im.size) + fp.write("%%Pages: 1\n") + 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) + + # + # image header + fp.write("gsave\n") + fp.write("10 dict begin\n") + fp.write("/buf %d string def\n" % (im.size[0] * operator[1])) + fp.write("%d %d scale\n" % im.size) + fp.write("%d %d 8\n" % im.size) # <= bits + fp.write("[%d 0 0 -%d 0 %d]\n" % (im.size[0], im.size[1], im.size[1])) + fp.write("{ currentfile buf readhexstring pop } bind\n") + fp.write(operator[2] + "\n") + fp.flush() + + ImageFile._save(im, base_fp, [("eps", (0, 0)+im.size, 0, None)]) + + fp.write("\n%%%%EndBinary\n") + fp.write("grestore end\n") + fp.flush() + +# +# -------------------------------------------------------------------- + +Image.register_open(EpsImageFile.format, EpsImageFile, _accept) + +Image.register_save(EpsImageFile.format, _save) + +Image.register_extension(EpsImageFile.format, ".ps") +Image.register_extension(EpsImageFile.format, ".eps") + +Image.register_mime(EpsImageFile.format, "application/postscript") diff --git a/pyPackages/pillowx86/PIL/ExifTags.py b/pyPackages/pillowx86/PIL/ExifTags.py new file mode 100644 index 0000000..52e145f --- /dev/null +++ b/pyPackages/pillowx86/PIL/ExifTags.py @@ -0,0 +1,193 @@ +# +# The Python Imaging Library. +# $Id$ +# +# EXIF tags +# +# Copyright (c) 2003 by Secret Labs AB +# +# See the README file for information on usage and redistribution. +# + +## +# This module provides constants and clear-text names for various +# well-known EXIF tags. +## + +## +# Maps EXIF tags to tag names. + +TAGS = { + + # possibly incomplete + 0x00fe: "NewSubfileType", + 0x00ff: "SubfileType", + 0x0100: "ImageWidth", + 0x0101: "ImageLength", + 0x0102: "BitsPerSample", + 0x0103: "Compression", + 0x0106: "PhotometricInterpretation", + 0x0107: "Threshholding", + 0x0108: "CellWidth", + 0x0109: "CellLenght", + 0x010a: "FillOrder", + 0x010d: "DocumentName", + 0x011d: "PageName", + 0x010e: "ImageDescription", + 0x010f: "Make", + 0x0110: "Model", + 0x0111: "StripOffsets", + 0x0112: "Orientation", + 0x0115: "SamplesPerPixel", + 0x0116: "RowsPerStrip", + 0x0117: "StripByteConunts", + 0x0118: "MinSampleValue", + 0x0119: "MaxSampleValue", + 0x011a: "XResolution", + 0x011b: "YResolution", + 0x011c: "PlanarConfiguration", + 0x0120: "FreeOffsets", + 0x0121: "FreeByteCounts", + 0x0122: "GrayResponseUnit", + 0x0123: "GrayResponseCurve", + 0x0128: "ResolutionUnit", + 0x012d: "TransferFunction", + 0x0131: "Software", + 0x0132: "DateTime", + 0x013b: "Artist", + 0x013c: "HostComputer", + 0x013e: "WhitePoint", + 0x013f: "PrimaryChromaticities", + 0x0140: "ColorMap", + 0x0152: "ExtraSamples", + 0x0201: "JpegIFOffset", + 0x0202: "JpegIFByteCount", + 0x0211: "YCbCrCoefficients", + 0x0212: "YCbCrSubSampling", + 0x0213: "YCbCrPositioning", + 0x0214: "ReferenceBlackWhite", + 0x1000: "RelatedImageFileFormat", + 0x1001: "RelatedImageWidth", + 0x1002: "RelatedImageLength", + 0x828d: "CFARepeatPatternDim", + 0x828e: "CFAPattern", + 0x828f: "BatteryLevel", + 0x8298: "Copyright", + 0x829a: "ExposureTime", + 0x829d: "FNumber", + 0x8769: "ExifOffset", + 0x8773: "InterColorProfile", + 0x8822: "ExposureProgram", + 0x8824: "SpectralSensitivity", + 0x8825: "GPSInfo", + 0x8827: "ISOSpeedRatings", + 0x8828: "OECF", + 0x8829: "Interlace", + 0x882a: "TimeZoneOffset", + 0x882b: "SelfTimerMode", + 0x9000: "ExifVersion", + 0x9003: "DateTimeOriginal", + 0x9004: "DateTimeDigitized", + 0x9101: "ComponentsConfiguration", + 0x9102: "CompressedBitsPerPixel", + 0x9201: "ShutterSpeedValue", + 0x9202: "ApertureValue", + 0x9203: "BrightnessValue", + 0x9204: "ExposureBiasValue", + 0x9205: "MaxApertureValue", + 0x9206: "SubjectDistance", + 0x9207: "MeteringMode", + 0x9208: "LightSource", + 0x9209: "Flash", + 0x920a: "FocalLength", + 0x920b: "FlashEnergy", + 0x920c: "SpatialFrequencyResponse", + 0x920d: "Noise", + 0x9211: "ImageNumber", + 0x9212: "SecurityClassification", + 0x9213: "ImageHistory", + 0x9214: "SubjectLocation", + 0x9215: "ExposureIndex", + 0x9216: "TIFF/EPStandardID", + 0x927c: "MakerNote", + 0x9286: "UserComment", + 0x9290: "SubsecTime", + 0x9291: "SubsecTimeOriginal", + 0x9292: "SubsecTimeDigitized", + 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", + +} + +## +# Maps EXIF GPS tags to tag names. + +GPSTAGS = { + 0: "GPSVersionID", + 1: "GPSLatitudeRef", + 2: "GPSLatitude", + 3: "GPSLongitudeRef", + 4: "GPSLongitude", + 5: "GPSAltitudeRef", + 6: "GPSAltitude", + 7: "GPSTimeStamp", + 8: "GPSSatellites", + 9: "GPSStatus", + 10: "GPSMeasureMode", + 11: "GPSDOP", + 12: "GPSSpeedRef", + 13: "GPSSpeed", + 14: "GPSTrackRef", + 15: "GPSTrack", + 16: "GPSImgDirectionRef", + 17: "GPSImgDirection", + 18: "GPSMapDatum", + 19: "GPSDestLatitudeRef", + 20: "GPSDestLatitude", + 21: "GPSDestLongitudeRef", + 22: "GPSDestLongitude", + 23: "GPSDestBearingRef", + 24: "GPSDestBearing", + 25: "GPSDestDistanceRef", + 26: "GPSDestDistance", + 27: "GPSProcessingMethod", + 28: "GPSAreaInformation", + 29: "GPSDateStamp", + 30: "GPSDifferential", + 31: "GPSHPositioningError", +} diff --git a/pyPackages/pillowx86/PIL/FitsStubImagePlugin.py b/pyPackages/pillowx86/PIL/FitsStubImagePlugin.py new file mode 100644 index 0000000..0b851ae --- /dev/null +++ b/pyPackages/pillowx86/PIL/FitsStubImagePlugin.py @@ -0,0 +1,73 @@ +# +# The Python Imaging Library +# $Id$ +# +# FITS stub adapter +# +# Copyright (c) 1998-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image, ImageFile + +_handler = None + +## +# Install application-specific FITS image handler. +# +# @param handler Handler object. + +def register_handler(handler): + global _handler + _handler = handler + +# -------------------------------------------------------------------- +# Image adapter + +def _accept(prefix): + return prefix[:6] == b"SIMPLE" + +class FITSStubImageFile(ImageFile.StubImageFile): + + format = "FITS" + format_description = "FITS" + + def _open(self): + + offset = self.fp.tell() + + if not _accept(self.fp.read(6)): + raise SyntaxError("Not a FITS file") + + # FIXME: add more sanity checks here; mandatory header items + # include SIMPLE, BITPIX, NAXIS, etc. + + self.fp.seek(offset) + + # make something up + self.mode = "F" + self.size = 1, 1 + + loader = self._load() + if loader: + loader.open(self) + + def _load(self): + return _handler + + +def _save(im, fp, filename): + if _handler is None or not hasattr("_handler", "save"): + raise IOError("FITS save handler not installed") + _handler.save(im, fp, filename) + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(FITSStubImageFile.format, FITSStubImageFile, _accept) +Image.register_save(FITSStubImageFile.format, _save) + +Image.register_extension(FITSStubImageFile.format, ".fit") +Image.register_extension(FITSStubImageFile.format, ".fits") diff --git a/pyPackages/pillowx86/PIL/FliImagePlugin.py b/pyPackages/pillowx86/PIL/FliImagePlugin.py new file mode 100644 index 0000000..4ecaccd --- /dev/null +++ b/pyPackages/pillowx86/PIL/FliImagePlugin.py @@ -0,0 +1,143 @@ +# +# The Python Imaging Library. +# $Id$ +# +# FLI/FLC file handling. +# +# History: +# 95-09-01 fl Created +# 97-01-03 fl Fixed parser, setup decoder tile +# 98-07-15 fl Renamed offset attribute to avoid name clash +# +# Copyright (c) Secret Labs AB 1997-98. +# Copyright (c) Fredrik Lundh 1995-97. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.2" + +from PIL import Image, ImageFile, ImagePalette, _binary + +i8 = _binary.i8 +i16 = _binary.i16le +i32 = _binary.i32le +o8 = _binary.o8 + + +# +# decoder + +def _accept(prefix): + return i16(prefix[4:6]) in [0xAF11, 0xAF12] + + +## +# Image plugin for the FLI/FLC animation format. Use the seek +# method to load individual frames. + +class FliImageFile(ImageFile.ImageFile): + + format = "FLI" + format_description = "Autodesk FLI/FLC Animation" + + def _open(self): + + # 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 + raise SyntaxError("not an FLI/FLC file") + + # image characteristics + self.mode = "P" + self.size = i16(s[8:10]), i16(s[10:12]) + + # animation speed + duration = i32(s[16:20]) + if magic == 0xAF11: + duration = (duration * 1000) / 70 + self.info["duration"] = duration + + # look for palette + palette = [(a, a, a) for a in range(256)] + + s = self.fp.read(16) + + self.__offset = 128 + + if i16(s[4:6]) == 0xF100: + # prefix chunk; ignore it + self.__offset = self.__offset + i32(s) + s = self.fp.read(16) + + if i16(s[4:6]) == 0xF1FA: + # look for palette chunk + s = self.fp.read(6) + if i16(s[4:6]) == 11: + self._palette(palette, 2) + elif i16(s[4:6]) == 4: + self._palette(palette, 0) + + 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 + self.frame = -1 + self.__fp = self.fp + + self.seek(0) + + def _palette(self, palette, shift): + # load palette + + i = 0 + for e in range(i16(self.fp.read(2))): + s = self.fp.read(2) + i = i + i8(s[0]) + n = i8(s[1]) + if n == 0: + n = 256 + 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 + palette[i] = (r, g, b) + i += 1 + + def seek(self, frame): + + if frame != self.frame + 1: + raise ValueError("cannot seek to frame %d" % frame) + self.frame = frame + + # move to next frame + self.fp = self.__fp + self.fp.seek(self.__offset) + + s = self.fp.read(4) + if not s: + raise EOFError + + framesize = i32(s) + + self.decodermaxblock = framesize + self.tile = [("fli", (0, 0)+self.size, self.__offset, None)] + + self.__offset = self.__offset + framesize + + def tell(self): + + return self.frame + +# +# registry + +Image.register_open("FLI", FliImageFile, _accept) + +Image.register_extension("FLI", ".fli") +Image.register_extension("FLI", ".flc") diff --git a/pyPackages/pillowx86/PIL/FontFile.py b/pyPackages/pillowx86/PIL/FontFile.py new file mode 100644 index 0000000..26ddff0 --- /dev/null +++ b/pyPackages/pillowx86/PIL/FontFile.py @@ -0,0 +1,119 @@ +# +# The Python Imaging Library +# $Id$ +# +# base class for raster font file parsers +# +# history: +# 1997-06-05 fl created +# 1997-08-19 fl restrict image width +# +# Copyright (c) 1997-1998 by Secret Labs AB +# Copyright (c) 1997-1998 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import os +from PIL import Image, _binary + +try: + import zlib +except ImportError: + zlib = None + +WIDTH = 800 + + +def puti16(fp, values): + # write network order (big-endian) 16-bit sequence + for v in values: + if v < 0: + v += 65536 + fp.write(_binary.o16be(v)) + + +## +# Base class for raster font file handlers. + +class FontFile: + + bitmap = None + + def __init__(self): + + self.info = {} + self.glyph = [None] * 256 + + def __getitem__(self, ix): + return self.glyph[ix] + + def compile(self): + "Create metrics and bitmap" + + if self.bitmap: + return + + # create bitmap large enough to hold all data + h = w = maxwidth = 0 + lines = 1 + for glyph in self: + if glyph: + d, dst, src, im = glyph + h = max(h, src[3] - src[1]) + w = w + (src[2] - src[0]) + if w > WIDTH: + lines += 1 + w = (src[2] - src[0]) + maxwidth = max(maxwidth, w) + + xsize = maxwidth + ysize = lines * h + + if xsize == 0 and ysize == 0: + return "" + + self.ysize = h + + # paste glyphs into bitmap + self.bitmap = Image.new("1", (xsize, ysize)) + self.metrics = [None] * 256 + x = y = 0 + for i in range(256): + glyph = self[i] + if glyph: + d, dst, src, im = glyph + xx, yy = src[2] - src[0], src[3] - src[1] + x0, y0 = x, y + x = x + xx + if x > WIDTH: + x, y = 0, y + h + x0, y0 = x, y + x = xx + s = src[0] + x0, src[1] + y0, src[2] + x0, src[3] + y0 + self.bitmap.paste(im.crop(src), s) + # print chr(i), dst, s + self.metrics[i] = d, dst, s + + def save(self, filename): + "Save font" + + self.compile() + + # font data + self.bitmap.save(os.path.splitext(filename)[0] + ".pbm", "PNG") + + # font metrics + fp = open(os.path.splitext(filename)[0] + ".pil", "wb") + fp.write(b"PILfont\n") + fp.write((";;;;;;%d;\n" % self.ysize).encode('ascii')) # HACK!!! + fp.write(b"DATA\n") + for id in range(256): + m = self.metrics[id] + if not m: + puti16(fp, [0] * 10) + else: + puti16(fp, m[0] + m[1] + m[2]) + fp.close() + +# End of file diff --git a/pyPackages/pillowx86/PIL/FpxImagePlugin.py b/pyPackages/pillowx86/PIL/FpxImagePlugin.py new file mode 100644 index 0000000..7e1d09d --- /dev/null +++ b/pyPackages/pillowx86/PIL/FpxImagePlugin.py @@ -0,0 +1,227 @@ +# +# THIS IS WORK IN PROGRESS +# +# The Python Imaging Library. +# $Id$ +# +# FlashPix support for PIL +# +# History: +# 97-01-25 fl Created (reads uncompressed RGB images only) +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.1" + + +from PIL import Image, ImageFile +from PIL.OleFileIO import * + + +# we map from colour field tuples to (mode, rawmode) descriptors +MODES = { + # opacity + (0x00007ffe): ("A", "L"), + # monochrome + (0x00010000,): ("L", "L"), + (0x00018000, 0x00017ffe): ("RGBA", "LA"), + # photo YCC + (0x00020000, 0x00020001, 0x00020002): ("RGB", "YCC;P"), + (0x00028000, 0x00028001, 0x00028002, 0x00027ffe): ("RGBA", "YCCA;P"), + # standard RGB (NIFRGB) + (0x00030000, 0x00030001, 0x00030002): ("RGB", "RGB"), + (0x00038000, 0x00038001, 0x00038002, 0x00037ffe): ("RGBA", "RGBA"), +} + + +# +# -------------------------------------------------------------------- + +def _accept(prefix): + return prefix[:8] == MAGIC + + +## +# Image plugin for the FlashPix images. + +class FpxImageFile(ImageFile.ImageFile): + + format = "FPX" + format_description = "FlashPix" + + def _open(self): + # + # read the OLE directory and see if this is a likely + # to be a FlashPix file + + try: + self.ole = OleFileIO(self.fp) + except IOError: + raise SyntaxError("not an FPX file; invalid OLE file") + + if self.ole.root.clsid != "56616700-C154-11CE-8553-00AA00A1F95B": + raise SyntaxError("not an FPX file; bad root CLSID") + + self._open_index(1) + + def _open_index(self, index=1): + # + # get the Image Contents Property Set + + prop = self.ole.getproperties([ + "Data Object Store %06d" % index, + "\005Image Contents" + ]) + + # size (highest resolution) + + self.size = prop[0x1000002], prop[0x1000003] + + size = max(self.size) + i = 1 + while size > 64: + size = size / 2 + i += 1 + self.maxid = i - 1 + + # mode. instead of using a single field for this, flashpix + # requires you to specify the mode for each channel in each + # resolution subimage, and leaves it to the decoder to make + # sure that they all match. for now, we'll cheat and assume + # that this is always the case. + + id = self.maxid << 16 + + s = prop[0x2000002 | id] + + colors = [] + for i in range(i32(s, 4)): + # note: for now, we ignore the "uncalibrated" flag + colors.append(i32(s, 8+i*4) & 0x7fffffff) + + self.mode, self.rawmode = MODES[tuple(colors)] + + # load JPEG tables, if any + self.jpeg = {} + for i in range(256): + id = 0x3000001 | (i << 16) + if id in prop: + self.jpeg[i] = prop[id] + + # print len(self.jpeg), "tables loaded" + + self._open_subimage(1, self.maxid) + + def _open_subimage(self, index=1, subimage=0): + # + # setup tile descriptors for a given subimage + + stream = [ + "Data Object Store %06d" % index, + "Resolution %04d" % subimage, + "Subimage 0000 Header" + ] + + fp = self.ole.openstream(stream) + + # skip prefix + p = fp.read(28) + + # header stream + s = fp.read(36) + + size = i32(s, 4), i32(s, 8) + tilecount = i32(s, 12) + tilesize = i32(s, 16), i32(s, 20) + channels = i32(s, 24) + offset = i32(s, 28) + length = i32(s, 32) + + # print size, self.mode, self.rawmode + + if size != self.size: + raise IOError("subimage mismatch") + + # get tile descriptors + fp.seek(28 + offset) + s = fp.read(i32(s, 12) * length) + + x = y = 0 + xsize, ysize = size + xtile, ytile = tilesize + self.tile = [] + + for i in range(0, len(s), length): + + compression = i32(s, i+8) + + if compression == 0: + self.tile.append(("raw", (x, y, x+xtile, y+ytile), + i32(s, i) + 28, (self.rawmode))) + + elif compression == 1: + + # FIXME: the fill decoder is not implemented + self.tile.append(("fill", (x, y, x+xtile, y+ytile), + i32(s, i) + 28, (self.rawmode, s[12:16]))) + + elif compression == 2: + + internal_color_conversion = i8(s[14]) + jpeg_tables = i8(s[15]) + rawmode = self.rawmode + + if internal_color_conversion: + # The image is stored as usual (usually YCbCr). + if rawmode == "RGBA": + # For "RGBA", data is stored as YCbCrA based on + # negative RGB. The following trick works around + # this problem : + jpegmode, rawmode = "YCbCrK", "CMYK" + else: + jpegmode = None # let the decoder decide + + else: + # The image is stored as defined by rawmode + jpegmode = rawmode + + self.tile.append(("jpeg", (x, y, x+xtile, y+ytile), + i32(s, i) + 28, (rawmode, jpegmode))) + + # FIXME: jpeg tables are tile dependent; the prefix + # data must be placed in the tile descriptor itself! + + if jpeg_tables: + self.tile_prefix = self.jpeg[jpeg_tables] + + else: + raise IOError("unknown/invalid compression") + + x = x + xtile + if x >= xsize: + x, y = 0, y + ytile + if y >= ysize: + break # isn't really required + + self.stream = stream + self.fp = None + + def load(self): + + if not self.fp: + self.fp = self.ole.openstream(self.stream[:2] + + ["Subimage 0000 Data"]) + + ImageFile.ImageFile.load(self) + +# +# -------------------------------------------------------------------- + +Image.register_open("FPX", FpxImageFile, _accept) + +Image.register_extension("FPX", ".fpx") diff --git a/pyPackages/pillowx86/PIL/GbrImagePlugin.py b/pyPackages/pillowx86/PIL/GbrImagePlugin.py new file mode 100644 index 0000000..0e68632 --- /dev/null +++ b/pyPackages/pillowx86/PIL/GbrImagePlugin.py @@ -0,0 +1,71 @@ +# +# The Python Imaging Library +# $Id$ +# +# load a GIMP brush file +# +# History: +# 96-03-14 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image, ImageFile, _binary + +i32 = _binary.i32be + + +def _accept(prefix): + return i32(prefix) >= 20 and i32(prefix[4:8]) == 1 + + +## +# Image plugin for the GIMP brush format. + +class GbrImageFile(ImageFile.ImageFile): + + format = "GBR" + format_description = "GIMP brush file" + + def _open(self): + + header_size = i32(self.fp.read(4)) + version = i32(self.fp.read(4)) + if header_size < 20 or version != 1: + raise SyntaxError("not a GIMP brush") + + width = i32(self.fp.read(4)) + height = i32(self.fp.read(4)) + bytes = i32(self.fp.read(4)) + if width <= 0 or height <= 0 or bytes != 1: + raise SyntaxError("not a GIMP brush") + + comment = self.fp.read(header_size - 20)[:-1] + + self.mode = "L" + self.size = width, height + + self.info["comment"] = comment + + # Since the brush is so small, we read the data immediately + self.data = self.fp.read(width * height) + + def load(self): + + if not self.data: + return + + # create an image out of the brush data block + self.im = Image.core.new(self.mode, self.size) + self.im.frombytes(self.data) + self.data = b"" + +# +# registry + +Image.register_open("GBR", GbrImageFile, _accept) + +Image.register_extension("GBR", ".gbr") diff --git a/pyPackages/pillowx86/PIL/GdImageFile.py b/pyPackages/pillowx86/PIL/GdImageFile.py new file mode 100644 index 0000000..080153a --- /dev/null +++ b/pyPackages/pillowx86/PIL/GdImageFile.py @@ -0,0 +1,92 @@ +# +# The Python Imaging Library. +# $Id$ +# +# GD file handling +# +# History: +# 1996-04-12 fl Created +# +# Copyright (c) 1997 by Secret Labs AB. +# Copyright (c) 1996 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + + +# NOTE: This format cannot be automatically recognized, so the +# class is not registered for use with Image.open(). To open a +# gd file, use the GdImageFile.open() function instead. + +# THE GD FORMAT IS NOT DESIGNED FOR DATA INTERCHANGE. This +# implementation is provided for convenience and demonstrational +# purposes only. + + +__version__ = "0.1" + +from PIL import ImageFile, ImagePalette, _binary +from PIL._util import isPath + +try: + import builtins +except ImportError: + import __builtin__ + builtins = __builtin__ + +i16 = _binary.i16be + + +## +# Image plugin for the GD uncompressed format. Note that this format +# is not supported by the standard Image.open function. To use +# this plugin, you have to import the GdImageFile module and +# use the GdImageFile.open function. + +class GdImageFile(ImageFile.ImageFile): + + format = "GD" + format_description = "GD uncompressed images" + + def _open(self): + + # Header + s = self.fp.read(775) + + self.mode = "L" # FIXME: "P" + self.size = i16(s[0:2]), i16(s[2:4]) + + # transparency index + tindex = i16(s[5:7]) + if tindex < 256: + self.info["transparent"] = tindex + + self.palette = ImagePalette.raw("RGB", s[7:]) + + self.tile = [("raw", (0, 0)+self.size, 775, ("L", 0, -1))] + + +## +# Load texture from a GD image file. +# +# @param filename GD file name, or an opened file handle. +# @param mode Optional mode. In this version, if the mode argument +# is given, it must be "r". +# @return An image instance. +# @exception IOError If the image could not be read. + +def open(fp, mode="r"): + + if mode != "r": + raise ValueError("bad mode") + + if isPath(fp): + filename = fp + fp = builtins.open(fp, "rb") + else: + filename = "" + + try: + return GdImageFile(fp, filename) + except SyntaxError: + raise IOError("cannot identify this image file") diff --git a/pyPackages/pillowx86/PIL/GifImagePlugin.py b/pyPackages/pillowx86/PIL/GifImagePlugin.py new file mode 100644 index 0000000..55aece3 --- /dev/null +++ b/pyPackages/pillowx86/PIL/GifImagePlugin.py @@ -0,0 +1,544 @@ +# +# The Python Imaging Library. +# $Id$ +# +# GIF file handling +# +# History: +# 1995-09-01 fl Created +# 1996-12-14 fl Added interlace support +# 1996-12-30 fl Added animation support +# 1997-01-05 fl Added write support, fixed local colour map bug +# 1997-02-23 fl Make sure to load raster data in getdata() +# 1997-07-05 fl Support external decoder (0.4) +# 1998-07-09 fl Handle all modes when saving (0.5) +# 1998-07-15 fl Renamed offset attribute to avoid name clash +# 2001-04-16 fl Added rewind support (seek to frame 0) (0.6) +# 2001-04-17 fl Added palette optimization (0.7) +# 2002-06-06 fl Added transparency support for save (0.8) +# 2004-02-24 fl Disable interlacing for small images +# +# Copyright (c) 1997-2004 by Secret Labs AB +# Copyright (c) 1995-2004 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.9" + + +from PIL import Image, ImageFile, ImagePalette, _binary + + +# -------------------------------------------------------------------- +# Helpers + +i8 = _binary.i8 +i16 = _binary.i16le +o8 = _binary.o8 +o16 = _binary.o16le + + +# -------------------------------------------------------------------- +# Identify/read GIF files + +def _accept(prefix): + return prefix[:6] in [b"GIF87a", b"GIF89a"] + + +## +# Image plugin for GIF images. This plugin supports both GIF87 and +# GIF89 images. + +class GifImageFile(ImageFile.ImageFile): + + format = "GIF" + format_description = "Compuserve GIF" + global_palette = None + + def data(self): + s = self.fp.read(1) + if s and i8(s): + return self.fp.read(i8(s)) + return None + + def _open(self): + + # Screen + s = self.fp.read(13) + if s[:6] not in [b"GIF87a", b"GIF89a"]: + raise SyntaxError("not a GIF file") + + self.info["version"] = s[:6] + self.size = i16(s[6:]), i16(s[8:]) + self.tile = [] + flags = i8(s[10]) + bits = (flags & 7) + 1 + + if flags & 128: + # get global palette + self.info["background"] = i8(s[11]) + # check if palette contains colour indices + p = self.fp.read(3 << bits) + for i in range(0, len(p), 3): + if not (i//3 == i8(p[i]) == i8(p[i+1]) == i8(p[i+2])): + p = ImagePalette.raw("RGB", p) + self.global_palette = self.palette = p + break + + self.__fp = self.fp # FIXME: hack + self.__rewind = self.fp.tell() + self.seek(0) # get ready to read first frame + + def seek(self, frame): + + if frame == 0: + # rewind + self.__offset = 0 + self.dispose = None + self.dispose_extent = [0, 0, 0, 0] # x0, y0, x1, y1 + self.__frame = -1 + self.__fp.seek(self.__rewind) + self._prev_im = None + self.disposal_method = 0 + else: + # ensure that the previous frame was loaded + if not self.im: + self.load() + + if frame != self.__frame + 1: + raise ValueError("cannot seek to frame %d" % frame) + self.__frame = frame + + self.tile = [] + + self.fp = self.__fp + if self.__offset: + # backup to last frame + self.fp.seek(self.__offset) + while self.data(): + pass + self.__offset = 0 + + if self.dispose: + self.im.paste(self.dispose, self.dispose_extent) + + from copy import copy + self.palette = copy(self.global_palette) + + while True: + + s = self.fp.read(1) + if not s or s == b";": + break + + elif s == b"!": + # + # extensions + # + s = self.fp.read(1) + block = self.data() + if i8(s) == 249: + # + # graphic control extension + # + flags = i8(block[0]) + if flags & 1: + self.info["transparency"] = i8(block[3]) + self.info["duration"] = i16(block[1:3]) * 10 + + # disposal method - find the value of bits 4 - 6 + dispose_bits = 0b00011100 & flags + dispose_bits = dispose_bits >> 2 + if dispose_bits: + # only set the dispose if it is not + # unspecified. I'm not sure if this is + # correct, but it seems to prevent the last + # frame from looking odd for some animations + self.disposal_method = dispose_bits + elif i8(s) == 255: + # + # application extension + # + self.info["extension"] = block, self.fp.tell() + if block[:11] == b"NETSCAPE2.0": + block = self.data() + if len(block) >= 3 and i8(block[0]) == 1: + self.info["loop"] = i16(block[1:3]) + while self.data(): + pass + + elif s == b",": + # + # local image + # + s = self.fp.read(9) + + # extent + x0, y0 = i16(s[0:]), i16(s[2:]) + x1, y1 = x0 + i16(s[4:]), y0 + i16(s[6:]) + self.dispose_extent = x0, y0, x1, y1 + flags = i8(s[8]) + + interlace = (flags & 64) != 0 + + if flags & 128: + bits = (flags & 7) + 1 + self.palette =\ + ImagePalette.raw("RGB", self.fp.read(3 << bits)) + + # image data + bits = i8(self.fp.read(1)) + self.__offset = self.fp.tell() + self.tile = [("gif", + (x0, y0, x1, y1), + self.__offset, + (bits, interlace))] + break + + else: + pass + # raise IOError, "illegal GIF tag `%x`" % i8(s) + + try: + if self.disposal_method < 2: + # do not dispose or none specified + self.dispose = None + elif self.disposal_method == 2: + # replace with background colour + self.dispose = Image.core.fill("P", self.size, + self.info["background"]) + else: + # replace with previous contents + if self.im: + self.dispose = self.im.copy() + + # only dispose the extent in this frame + if self.dispose: + self.dispose = self.dispose.crop(self.dispose_extent) + except (AttributeError, KeyError): + pass + + if not self.tile: + # self.__fp = None + raise EOFError("no more images in GIF file") + + self.mode = "L" + if self.palette: + self.mode = "P" + + def tell(self): + return self.__frame + + def load_end(self): + ImageFile.ImageFile.load_end(self) + + # if the disposal method is 'do not dispose', transparent + # pixels should show the content of the previous frame + if self._prev_im and self.disposal_method == 1: + # we do this by pasting the updated area onto the previous + # frame which we then use as the current image content + updated = self.im.crop(self.dispose_extent) + self._prev_im.paste(updated, self.dispose_extent, + updated.convert('RGBA')) + self.im = self._prev_im + self._prev_im = self.im.copy() + +# -------------------------------------------------------------------- +# Write GIF files + +try: + import _imaging_gif +except ImportError: + _imaging_gif = None + +RAWMODE = { + "1": "L", + "L": "L", + "P": "P", +} + + +def _save(im, fp, filename): + + if _imaging_gif: + # call external driver + try: + _imaging_gif.save(im, fp, filename) + return + except IOError: + pass # write uncompressed file + + if im.mode in RAWMODE: + imOut = im + else: + # convert on the fly (EXPERIMENTAL -- I'm not sure PIL + # should automatically convert images on save...) + if Image.getmodebase(im.mode) == "RGB": + palette_size = 256 + if im.palette: + palette_size = len(im.palette.getdata()[1]) // 3 + imOut = im.convert("P", palette=1, colors=palette_size) + else: + imOut = im.convert("L") + + # header + try: + palette = im.encoderinfo["palette"] + except KeyError: + palette = None + im.encoderinfo["optimize"] = im.encoderinfo.get("optimize", True) + + header, usedPaletteColors = getheader(imOut, palette, im.encoderinfo) + for s in header: + fp.write(s) + + flags = 0 + + try: + interlace = im.encoderinfo["interlace"] + except KeyError: + interlace = 1 + + # workaround for @PIL153 + if min(im.size) < 16: + interlace = 0 + + if interlace: + flags = flags | 64 + + try: + transparency = im.encoderinfo["transparency"] + except KeyError: + pass + else: + transparency = int(transparency) + # optimize the block away if transparent color is not used + transparentColorExists = True + # adjust the transparency index after optimize + if usedPaletteColors is not None and len(usedPaletteColors) < 256: + for i in range(len(usedPaletteColors)): + if usedPaletteColors[i] == transparency: + transparency = i + transparentColorExists = True + break + else: + transparentColorExists = False + + # transparency extension block + if transparentColorExists: + fp.write(b"!" + + o8(249) + # extension intro + o8(4) + # length + o8(1) + # transparency info present + o16(0) + # duration + o8(transparency) # transparency index + + o8(0)) + + # local image header + fp.write(b"," + + o16(0) + o16(0) + # bounding box + o16(im.size[0]) + # size + o16(im.size[1]) + + o8(flags) + # flags + o8(8)) # bits + + imOut.encoderconfig = (8, interlace) + ImageFile._save(imOut, fp, [("gif", (0, 0)+im.size, 0, + RAWMODE[imOut.mode])]) + + fp.write(b"\0") # end of image data + + fp.write(b";") # end of file + + try: + fp.flush() + except: + pass + + +def _save_netpbm(im, fp, filename): + + # + # If you need real GIF compression and/or RGB quantization, you + # can use the external NETPBM/PBMPLUS utilities. See comments + # below for information on how to enable this. + + import os + from subprocess import Popen, check_call, PIPE, CalledProcessError + import tempfile + file = im._dump() + + if im.mode != "RGB": + with open(filename, 'wb') as f: + stderr = tempfile.TemporaryFile() + check_call(["ppmtogif", file], stdout=f, stderr=stderr) + else: + with open(filename, 'wb') as f: + + # Pipe ppmquant output into ppmtogif + # "ppmquant 256 %s | ppmtogif > %s" % (file, filename) + quant_cmd = ["ppmquant", "256", file] + togif_cmd = ["ppmtogif"] + stderr = tempfile.TemporaryFile() + quant_proc = Popen(quant_cmd, stdout=PIPE, stderr=stderr) + stderr = tempfile.TemporaryFile() + togif_proc = Popen(togif_cmd, stdin=quant_proc.stdout, stdout=f, + stderr=stderr) + + # Allow ppmquant to receive SIGPIPE if ppmtogif exits + quant_proc.stdout.close() + + retcode = quant_proc.wait() + if retcode: + raise CalledProcessError(retcode, quant_cmd) + + retcode = togif_proc.wait() + if retcode: + raise CalledProcessError(retcode, togif_cmd) + + try: + os.unlink(file) + except: + pass + + +# -------------------------------------------------------------------- +# GIF utilities + +def getheader(im, palette=None, info=None): + """Return a list of strings representing a GIF header""" + + optimize = info and info.get("optimize", 0) + + # Header Block + # http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp + header = [ + b"GIF87a" + # signature + version + o16(im.size[0]) + # canvas width + o16(im.size[1]) # canvas height + ] + + if im.mode == "P": + if palette and isinstance(palette, bytes): + sourcePalette = palette[:768] + else: + sourcePalette = im.im.getpalette("RGB")[:768] + else: # L-mode + if palette and isinstance(palette, bytes): + sourcePalette = palette[:768] + else: + sourcePalette = bytearray([i//3 for i in range(768)]) + + usedPaletteColors = paletteBytes = None + + if im.mode in ("P", "L") and optimize: + usedPaletteColors = [] + + # check which colors are used + i = 0 + for count in im.histogram(): + if count: + usedPaletteColors.append(i) + i += 1 + + # create the new palette if not every color is used + if len(usedPaletteColors) < 256: + paletteBytes = b"" + newPositions = {} + + i = 0 + # pick only the used colors from the palette + for oldPosition in usedPaletteColors: + paletteBytes += sourcePalette[oldPosition*3:oldPosition*3+3] + newPositions[oldPosition] = i + i += 1 + + # replace the palette color id of all pixel with the new id + imageBytes = bytearray(im.tobytes()) + for i in range(len(imageBytes)): + imageBytes[i] = newPositions[imageBytes[i]] + im.frombytes(bytes(imageBytes)) + newPaletteBytes = (paletteBytes + + (768 - len(paletteBytes)) * b'\x00') + im.putpalette(newPaletteBytes) + im.palette = ImagePalette.ImagePalette("RGB", palette=paletteBytes, + size=len(paletteBytes)) + + if not paletteBytes: + paletteBytes = sourcePalette + + # Logical Screen Descriptor + # calculate the palette size for the header + import math + colorTableSize = int(math.ceil(math.log(len(paletteBytes)//3, 2)))-1 + if colorTableSize < 0: + colorTableSize = 0 + # size of global color table + global color table flag + header.append(o8(colorTableSize + 128)) + # background + reserved/aspect + header.append(o8(0) + o8(0)) + # end of Logical Screen Descriptor + + # add the missing amount of bytes + # the palette has to be 2< 0: + paletteBytes += o8(0) * 3 * actualTargetSizeDiff + + # Header + Logical Screen Descriptor + Global Color Table + header.append(paletteBytes) + return header, usedPaletteColors + + +def getdata(im, offset=(0, 0), **params): + """Return a list of strings representing this image. + The first string is a local image header, the rest contains + encoded image data.""" + + class collector: + data = [] + + def write(self, data): + self.data.append(data) + + im.load() # make sure raster data is available + + fp = collector() + + try: + im.encoderinfo = params + + # local image header + fp.write(b"," + + o16(offset[0]) + # offset + o16(offset[1]) + + o16(im.size[0]) + # size + o16(im.size[1]) + + o8(0) + # flags + o8(8)) # bits + + ImageFile._save(im, fp, [("gif", (0, 0)+im.size, 0, RAWMODE[im.mode])]) + + fp.write(b"\0") # end of image data + + finally: + del im.encoderinfo + + return fp.data + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(GifImageFile.format, GifImageFile, _accept) +Image.register_save(GifImageFile.format, _save) +Image.register_extension(GifImageFile.format, ".gif") +Image.register_mime(GifImageFile.format, "image/gif") + +# +# Uncomment the following line if you wish to use NETPBM/PBMPLUS +# instead of the built-in "uncompressed" GIF encoder + +# Image.register_save(GifImageFile.format, _save_netpbm) diff --git a/pyPackages/pillowx86/PIL/GimpGradientFile.py b/pyPackages/pillowx86/PIL/GimpGradientFile.py new file mode 100644 index 0000000..696f425 --- /dev/null +++ b/pyPackages/pillowx86/PIL/GimpGradientFile.py @@ -0,0 +1,137 @@ +# +# Python Imaging Library +# $Id$ +# +# stuff to read (and render) GIMP gradient files +# +# History: +# 97-08-23 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + +from math import pi, log, sin, sqrt +from PIL._binary import o8 + +# -------------------------------------------------------------------- +# Stuff to translate curve segments to palette values (derived from +# the corresponding code in GIMP, written by Federico Mena Quintero. +# See the GIMP distribution for more information.) +# + +EPSILON = 1e-10 + + +def linear(middle, pos): + if pos <= middle: + if middle < EPSILON: + return 0.0 + else: + return 0.5 * pos / middle + else: + pos = pos - middle + middle = 1.0 - middle + if middle < EPSILON: + return 1.0 + else: + return 0.5 + 0.5 * pos / middle + + +def curved(middle, pos): + return pos ** (log(0.5) / log(max(middle, EPSILON))) + + +def sine(middle, pos): + return (sin((-pi / 2.0) + pi * linear(middle, pos)) + 1.0) / 2.0 + + +def sphere_increasing(middle, pos): + return sqrt(1.0 - (linear(middle, pos) - 1.0) ** 2) + + +def sphere_decreasing(middle, pos): + return 1.0 - sqrt(1.0 - linear(middle, pos) ** 2) + +SEGMENTS = [linear, curved, sine, sphere_increasing, sphere_decreasing] + + +class GradientFile: + + gradient = None + + def getpalette(self, entries=256): + + palette = [] + + ix = 0 + x0, x1, xm, rgb0, rgb1, segment = self.gradient[ix] + + for i in range(entries): + + x = i / float(entries-1) + + while x1 < x: + ix += 1 + x0, x1, xm, rgb0, rgb1, segment = self.gradient[ix] + + w = x1 - x0 + + if w < EPSILON: + scale = segment(0.5, 0.5) + else: + scale = segment((xm - x0) / w, (x - x0) / w) + + # expand to RGBA + r = o8(int(255 * ((rgb1[0] - rgb0[0]) * scale + rgb0[0]) + 0.5)) + g = o8(int(255 * ((rgb1[1] - rgb0[1]) * scale + rgb0[1]) + 0.5)) + b = o8(int(255 * ((rgb1[2] - rgb0[2]) * scale + rgb0[2]) + 0.5)) + a = o8(int(255 * ((rgb1[3] - rgb0[3]) * scale + rgb0[3]) + 0.5)) + + # add to palette + palette.append(r + g + b + a) + + return b"".join(palette), "RGBA" + + +## +# File handler for GIMP's gradient format. + +class GimpGradientFile(GradientFile): + + def __init__(self, fp): + + if fp.readline()[:13] != b"GIMP Gradient": + raise SyntaxError("not a GIMP gradient file") + + line = fp.readline() + + # GIMP 1.2 gradient files don't contain a name, but GIMP 1.3 files do + if line.startswith(b"Name: "): + line = fp.readline().strip() + + count = int(line) + + gradient = [] + + for i in range(count): + + s = fp.readline().split() + w = [float(x) for x in s[:11]] + + x0, x1 = w[0], w[2] + xm = w[1] + rgb0 = w[3:7] + rgb1 = w[7:11] + + segment = SEGMENTS[int(s[11])] + cspace = int(s[12]) + + if cspace != 0: + raise IOError("cannot handle HSV colour space") + + gradient.append((x0, x1, xm, rgb0, rgb1, segment)) + + self.gradient = gradient diff --git a/pyPackages/pillowx86/PIL/GimpPaletteFile.py b/pyPackages/pillowx86/PIL/GimpPaletteFile.py new file mode 100644 index 0000000..a066c80 --- /dev/null +++ b/pyPackages/pillowx86/PIL/GimpPaletteFile.py @@ -0,0 +1,62 @@ +# +# Python Imaging Library +# $Id$ +# +# stuff to read GIMP palette files +# +# History: +# 1997-08-23 fl Created +# 2004-09-07 fl Support GIMP 2.0 palette files. +# +# Copyright (c) Secret Labs AB 1997-2004. All rights reserved. +# Copyright (c) Fredrik Lundh 1997-2004. +# +# See the README file for information on usage and redistribution. +# + +import re +from PIL._binary import o8 + + +## +# File handler for GIMP's palette format. + +class GimpPaletteFile: + + rawmode = "RGB" + + def __init__(self, fp): + + self.palette = [o8(i)*3 for i in range(256)] + + if fp.readline()[:12] != b"GIMP Palette": + raise SyntaxError("not a GIMP palette file") + + i = 0 + + while i <= 255: + + s = fp.readline() + + if not s: + break + # skip fields and comment lines + if re.match(b"\w+:|#", s): + continue + if len(s) > 100: + raise SyntaxError("bad palette file") + + v = tuple(map(int, s.split()[:3])) + if len(v) != 3: + raise ValueError("bad palette entry") + + if 0 <= i <= 255: + self.palette[i] = o8(v[0]) + o8(v[1]) + o8(v[2]) + + i += 1 + + self.palette = b"".join(self.palette) + + def getpalette(self): + + return self.palette, self.rawmode diff --git a/pyPackages/pillowx86/PIL/GribStubImagePlugin.py b/pyPackages/pillowx86/PIL/GribStubImagePlugin.py new file mode 100644 index 0000000..8ffad81 --- /dev/null +++ b/pyPackages/pillowx86/PIL/GribStubImagePlugin.py @@ -0,0 +1,72 @@ +# +# The Python Imaging Library +# $Id$ +# +# GRIB stub adapter +# +# Copyright (c) 1996-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image, ImageFile + +_handler = None + + +## +# Install application-specific GRIB image handler. +# +# @param handler Handler object. + +def register_handler(handler): + global _handler + _handler = handler + + +# -------------------------------------------------------------------- +# Image adapter + +def _accept(prefix): + return prefix[0:4] == b"GRIB" and prefix[7] == b'\x01' + + +class GribStubImageFile(ImageFile.StubImageFile): + + format = "GRIB" + format_description = "GRIB" + + def _open(self): + + offset = self.fp.tell() + + if not _accept(self.fp.read(8)): + raise SyntaxError("Not a GRIB file") + + self.fp.seek(offset) + + # make something up + self.mode = "F" + self.size = 1, 1 + + loader = self._load() + if loader: + loader.open(self) + + def _load(self): + return _handler + + +def _save(im, fp, filename): + if _handler is None or not hasattr("_handler", "save"): + raise IOError("GRIB save handler not installed") + _handler.save(im, fp, filename) + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(GribStubImageFile.format, GribStubImageFile, _accept) +Image.register_save(GribStubImageFile.format, _save) + +Image.register_extension(GribStubImageFile.format, ".grib") diff --git a/pyPackages/pillowx86/PIL/Hdf5StubImagePlugin.py b/pyPackages/pillowx86/PIL/Hdf5StubImagePlugin.py new file mode 100644 index 0000000..f7945be --- /dev/null +++ b/pyPackages/pillowx86/PIL/Hdf5StubImagePlugin.py @@ -0,0 +1,73 @@ +# +# The Python Imaging Library +# $Id$ +# +# HDF5 stub adapter +# +# Copyright (c) 2000-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image, ImageFile + +_handler = None + + +## +# Install application-specific HDF5 image handler. +# +# @param handler Handler object. + +def register_handler(handler): + global _handler + _handler = handler + + +# -------------------------------------------------------------------- +# Image adapter + +def _accept(prefix): + return prefix[:8] == b"\x89HDF\r\n\x1a\n" + + +class HDF5StubImageFile(ImageFile.StubImageFile): + + format = "HDF5" + format_description = "HDF5" + + def _open(self): + + offset = self.fp.tell() + + if not _accept(self.fp.read(8)): + raise SyntaxError("Not an HDF file") + + self.fp.seek(offset) + + # make something up + self.mode = "F" + self.size = 1, 1 + + loader = self._load() + if loader: + loader.open(self) + + def _load(self): + return _handler + + +def _save(im, fp, filename): + if _handler is None or not hasattr("_handler", "save"): + raise IOError("HDF5 save handler not installed") + _handler.save(im, fp, filename) + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(HDF5StubImageFile.format, HDF5StubImageFile, _accept) +Image.register_save(HDF5StubImageFile.format, _save) + +Image.register_extension(HDF5StubImageFile.format, ".h5") +Image.register_extension(HDF5StubImageFile.format, ".hdf") diff --git a/pyPackages/pillowx86/PIL/IcnsImagePlugin.py b/pyPackages/pillowx86/PIL/IcnsImagePlugin.py new file mode 100644 index 0000000..e88b849 --- /dev/null +++ b/pyPackages/pillowx86/PIL/IcnsImagePlugin.py @@ -0,0 +1,311 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Mac OS X icns file decoder, based on icns.py by Bob Ippolito. +# +# history: +# 2004-10-09 fl Turned into a PIL plugin; removed 2.3 dependencies. +# +# Copyright (c) 2004 by Bob Ippolito. +# Copyright (c) 2004 by Secret Labs. +# Copyright (c) 2004 by Fredrik Lundh. +# Copyright (c) 2014 by Alastair Houghton. +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image, ImageFile, PngImagePlugin, _binary +import io +import struct + +enable_jpeg2k = hasattr(Image.core, 'jp2klib_version') +if enable_jpeg2k: + from PIL import Jpeg2KImagePlugin + +i8 = _binary.i8 + +HEADERSIZE = 8 + + +def nextheader(fobj): + return struct.unpack('>4sI', fobj.read(HEADERSIZE)) + + +def read_32t(fobj, start_length, size): + # The 128x128 icon seems to have an extra header for some reason. + (start, length) = start_length + fobj.seek(start) + sig = fobj.read(4) + if sig != b'\x00\x00\x00\x00': + raise SyntaxError('Unknown signature, expecting 0x00000000') + return read_32(fobj, (start + 4, length - 4), size) + + +def read_32(fobj, start_length, size): + """ + Read a 32bit RGB icon resource. Seems to be either uncompressed or + an RLE packbits-like scheme. + """ + (start, length) = start_length + fobj.seek(start) + pixel_size = (size[0] * size[2], size[1] * size[2]) + sizesq = pixel_size[0] * pixel_size[1] + if length == sizesq * 3: + # uncompressed ("RGBRGBGB") + indata = fobj.read(length) + im = Image.frombuffer("RGB", pixel_size, indata, "raw", "RGB", 0, 1) + else: + # decode image + im = Image.new("RGB", pixel_size, None) + for band_ix in range(3): + data = [] + bytesleft = sizesq + while bytesleft > 0: + byte = fobj.read(1) + if not byte: + break + byte = i8(byte) + if byte & 0x80: + blocksize = byte - 125 + byte = fobj.read(1) + for i in range(blocksize): + data.append(byte) + else: + blocksize = byte + 1 + data.append(fobj.read(blocksize)) + bytesleft -= blocksize + if bytesleft <= 0: + break + if bytesleft != 0: + raise SyntaxError( + "Error reading channel [%r left]" % bytesleft + ) + band = Image.frombuffer( + "L", pixel_size, b"".join(data), "raw", "L", 0, 1 + ) + im.im.putband(band.im, band_ix) + return {"RGB": im} + + +def read_mk(fobj, start_length, size): + # Alpha masks seem to be uncompressed + (start, length) = start_length + fobj.seek(start) + pixel_size = (size[0] * size[2], size[1] * size[2]) + sizesq = pixel_size[0] * pixel_size[1] + band = Image.frombuffer( + "L", pixel_size, fobj.read(sizesq), "raw", "L", 0, 1 + ) + return {"A": band} + + +def read_png_or_jpeg2000(fobj, start_length, size): + (start, length) = start_length + fobj.seek(start) + sig = fobj.read(12) + if sig[:8] == b'\x89PNG\x0d\x0a\x1a\x0a': + fobj.seek(start) + im = PngImagePlugin.PngImageFile(fobj) + return {"RGBA": im} + elif sig[:4] == b'\xff\x4f\xff\x51' \ + or sig[:4] == b'\x0d\x0a\x87\x0a' \ + or sig == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a': + if not enable_jpeg2k: + raise ValueError('Unsupported icon subimage format (rebuild PIL ' + 'with JPEG 2000 support to fix this)') + # j2k, jpc or j2c + fobj.seek(start) + jp2kstream = fobj.read(length) + f = io.BytesIO(jp2kstream) + im = Jpeg2KImagePlugin.Jpeg2KImageFile(f) + if im.mode != 'RGBA': + im = im.convert('RGBA') + return {"RGBA": im} + else: + raise ValueError('Unsupported icon subimage format') + + +class IcnsFile: + + SIZES = { + (512, 512, 2): [ + (b'ic10', read_png_or_jpeg2000), + ], + (512, 512, 1): [ + (b'ic09', read_png_or_jpeg2000), + ], + (256, 256, 2): [ + (b'ic14', read_png_or_jpeg2000), + ], + (256, 256, 1): [ + (b'ic08', read_png_or_jpeg2000), + ], + (128, 128, 2): [ + (b'ic13', read_png_or_jpeg2000), + ], + (128, 128, 1): [ + (b'ic07', read_png_or_jpeg2000), + (b'it32', read_32t), + (b't8mk', read_mk), + ], + (64, 64, 1): [ + (b'icp6', read_png_or_jpeg2000), + ], + (32, 32, 2): [ + (b'ic12', read_png_or_jpeg2000), + ], + (48, 48, 1): [ + (b'ih32', read_32), + (b'h8mk', read_mk), + ], + (32, 32, 1): [ + (b'icp5', read_png_or_jpeg2000), + (b'il32', read_32), + (b'l8mk', read_mk), + ], + (16, 16, 2): [ + (b'ic11', read_png_or_jpeg2000), + ], + (16, 16, 1): [ + (b'icp4', read_png_or_jpeg2000), + (b'is32', read_32), + (b's8mk', read_mk), + ], + } + + def __init__(self, fobj): + """ + fobj is a file-like object as an icns resource + """ + # signature : (start, length) + self.dct = dct = {} + self.fobj = fobj + sig, filesize = nextheader(fobj) + if sig != b'icns': + raise SyntaxError('not an icns file') + i = HEADERSIZE + while i < filesize: + sig, blocksize = nextheader(fobj) + if blocksize <= 0: + raise SyntaxError('invalid block header') + i += HEADERSIZE + blocksize -= HEADERSIZE + dct[sig] = (i, blocksize) + fobj.seek(blocksize, 1) + i += blocksize + + def itersizes(self): + sizes = [] + for size, fmts in self.SIZES.items(): + for (fmt, reader) in fmts: + if fmt in self.dct: + sizes.append(size) + break + return sizes + + def bestsize(self): + sizes = self.itersizes() + if not sizes: + raise SyntaxError("No 32bit icon resources found") + return max(sizes) + + def dataforsize(self, size): + """ + Get an icon resource as {channel: array}. Note that + the arrays are bottom-up like windows bitmaps and will likely + need to be flipped or transposed in some way. + """ + dct = {} + for code, reader in self.SIZES[size]: + desc = self.dct.get(code) + if desc is not None: + dct.update(reader(self.fobj, desc, size)) + return dct + + def getimage(self, size=None): + if size is None: + size = self.bestsize() + if len(size) == 2: + size = (size[0], size[1], 1) + channels = self.dataforsize(size) + + im = channels.get('RGBA', None) + if im: + return im + + im = channels.get("RGB").copy() + try: + im.putalpha(channels["A"]) + except KeyError: + pass + return im + + +## +# Image plugin for Mac OS icons. + +class IcnsImageFile(ImageFile.ImageFile): + """ + PIL read-only image support for Mac OS .icns files. + Chooses the best resolution, but will possibly load + a different size image if you mutate the size attribute + before calling 'load'. + + The info dictionary has a key 'sizes' that is a list + of sizes that the icns file has. + """ + + format = "ICNS" + format_description = "Mac OS icns resource" + + def _open(self): + self.icns = IcnsFile(self.fp) + self.mode = 'RGBA' + self.best_size = self.icns.bestsize() + self.size = (self.best_size[0] * self.best_size[2], + self.best_size[1] * self.best_size[2]) + self.info['sizes'] = self.icns.itersizes() + # Just use this to see if it's loaded or not yet. + self.tile = ('',) + + def load(self): + if len(self.size) == 3: + self.best_size = self.size + self.size = (self.best_size[0] * self.best_size[2], + self.best_size[1] * self.best_size[2]) + + Image.Image.load(self) + if not self.tile: + return + self.load_prepare() + # This is likely NOT the best way to do it, but whatever. + im = self.icns.getimage(self.best_size) + + # If this is a PNG or JPEG 2000, it won't be loaded yet + im.load() + + self.im = im.im + self.mode = im.mode + self.size = im.size + self.fp = None + self.icns = None + self.tile = () + self.load_end() + +Image.register_open("ICNS", IcnsImageFile, lambda x: x[:4] == b'icns') +Image.register_extension("ICNS", '.icns') + +if __name__ == '__main__': + import os + import sys + imf = IcnsImageFile(open(sys.argv[1], 'rb')) + for size in imf.info['sizes']: + imf.size = size + imf.load() + im = imf.im + im.save('out-%s-%s-%s.png' % size) + im = Image.open(open(sys.argv[1], "rb")) + im.save("out.png") + if sys.platform == 'windows': + os.startfile("out.png") diff --git a/pyPackages/pillowx86/PIL/IcoImagePlugin.py b/pyPackages/pillowx86/PIL/IcoImagePlugin.py new file mode 100644 index 0000000..db8cec0 --- /dev/null +++ b/pyPackages/pillowx86/PIL/IcoImagePlugin.py @@ -0,0 +1,284 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Windows Icon support for PIL +# +# History: +# 96-05-27 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + +# This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis +# . +# https://code.google.com/p/casadebender/wiki/Win32IconImagePlugin +# +# Icon format references: +# * http://en.wikipedia.org/wiki/ICO_(file_format) +# * http://msdn.microsoft.com/en-us/library/ms997538.aspx + + +__version__ = "0.1" + +import struct +from io import BytesIO + +from PIL import Image, ImageFile, BmpImagePlugin, PngImagePlugin, _binary +from math import log, ceil + +# +# -------------------------------------------------------------------- + +i8 = _binary.i8 +i16 = _binary.i16le +i32 = _binary.i32le + +_MAGIC = b"\0\0\1\0" + + +def _save(im, fp, filename): + fp.write(_MAGIC) # (2+2) + sizes = im.encoderinfo.get("sizes", + [(16, 16), (24, 24), (32, 32), (48, 48), + (64, 64), (128, 128), (255, 255)]) + width, height = im.size + filter(lambda x: False if (x[0] > width or x[1] > height or + x[0] > 255 or x[1] > 255) else True, sizes) + sizes = sorted(sizes, key=lambda x: x[0]) + fp.write(struct.pack("H", len(sizes))) # idCount(2) + offset = fp.tell() + len(sizes)*16 + for size in sizes: + width, height = size + fp.write(struct.pack("B", width)) # bWidth(1) + fp.write(struct.pack("B", height)) # bHeight(1) + fp.write(b"\0") # bColorCount(1) + fp.write(b"\0") # bReserved(1) + fp.write(b"\0\0") # wPlanes(2) + fp.write(struct.pack("H", 32)) # wBitCount(2) + + image_io = BytesIO() + tmp = im.copy() + tmp.thumbnail(size, Image.LANCZOS) + tmp.save(image_io, "png") + image_io.seek(0) + image_bytes = image_io.read() + bytes_len = len(image_bytes) + fp.write(struct.pack("I", bytes_len)) # dwBytesInRes(4) + fp.write(struct.pack("I", offset)) # dwImageOffset(4) + current = fp.tell() + fp.seek(offset) + fp.write(image_bytes) + offset = offset + bytes_len + fp.seek(current) + + +def _accept(prefix): + return prefix[:4] == _MAGIC + + +class IcoFile: + def __init__(self, buf): + """ + Parse image from file-like object containing ico file data + """ + + # check magic + s = buf.read(6) + if not _accept(s): + raise SyntaxError("not an ICO file") + + self.buf = buf + self.entry = [] + + # Number of items in file + self.nb_items = i16(s[4:]) + + # Get headers for each item + for i in range(self.nb_items): + s = buf.read(16) + + icon_header = { + 'width': i8(s[0]), + 'height': i8(s[1]), + 'nb_color': i8(s[2]), # No. of colors in image (0 if >=8bpp) + 'reserved': i8(s[3]), + 'planes': i16(s[4:]), + 'bpp': i16(s[6:]), + 'size': i32(s[8:]), + 'offset': i32(s[12:]) + } + + # See Wikipedia + for j in ('width', 'height'): + if not icon_header[j]: + icon_header[j] = 256 + + # See Wikipedia notes about color depth. + # We need this just to differ images with equal sizes + icon_header['color_depth'] = (icon_header['bpp'] or + (icon_header['nb_color'] != 0 and + ceil(log(icon_header['nb_color'], + 2))) or 256) + + icon_header['dim'] = (icon_header['width'], icon_header['height']) + icon_header['square'] = (icon_header['width'] * + icon_header['height']) + + self.entry.append(icon_header) + + self.entry = sorted(self.entry, key=lambda x: x['color_depth']) + # ICO images are usually squares + # self.entry = sorted(self.entry, key=lambda x: x['width']) + self.entry = sorted(self.entry, key=lambda x: x['square']) + self.entry.reverse() + + def sizes(self): + """ + Get a list of all available icon sizes and color depths. + """ + return set((h['width'], h['height']) for h in self.entry) + + def getimage(self, size, bpp=False): + """ + Get an image from the icon + """ + for (i, h) in enumerate(self.entry): + if size == h['dim'] and (bpp is False or bpp == h['color_depth']): + return self.frame(i) + return self.frame(0) + + def frame(self, idx): + """ + Get an image from frame idx + """ + + header = self.entry[idx] + + self.buf.seek(header['offset']) + data = self.buf.read(8) + self.buf.seek(header['offset']) + + if data[:8] == PngImagePlugin._MAGIC: + # png frame + im = PngImagePlugin.PngImageFile(self.buf) + else: + # XOR + AND mask bmp frame + im = BmpImagePlugin.DibImageFile(self.buf) + + # change tile dimension to only encompass XOR image + im.size = (im.size[0], int(im.size[1] / 2)) + d, e, o, a = im.tile[0] + im.tile[0] = d, (0, 0) + im.size, o, a + + # figure out where AND mask image starts + mode = a[0] + bpp = 8 + for k in BmpImagePlugin.BIT2MODE.keys(): + if mode == BmpImagePlugin.BIT2MODE[k][1]: + bpp = k + break + + if 32 == bpp: + # 32-bit color depth icon image allows semitransparent areas + # PIL's DIB format ignores transparency bits, recover them. + # The DIB is packed in BGRX byte order where X is the alpha + # channel. + + # Back up to start of bmp data + self.buf.seek(o) + # extract every 4th byte (eg. 3,7,11,15,...) + alpha_bytes = self.buf.read(im.size[0] * im.size[1] * 4)[3::4] + + # convert to an 8bpp grayscale image + mask = Image.frombuffer( + 'L', # 8bpp + im.size, # (w, h) + alpha_bytes, # source chars + 'raw', # raw decoder + ('L', 0, -1) # 8bpp inverted, unpadded, reversed + ) + else: + # get AND image from end of bitmap + w = im.size[0] + if (w % 32) > 0: + # bitmap row data is aligned to word boundaries + w += 32 - (im.size[0] % 32) + + # the total mask data is + # padded row size * height / bits per char + + and_mask_offset = o + int(im.size[0] * im.size[1] * + (bpp / 8.0)) + total_bytes = int((w * im.size[1]) / 8) + + self.buf.seek(and_mask_offset) + maskData = self.buf.read(total_bytes) + + # convert raw data to image + mask = Image.frombuffer( + '1', # 1 bpp + im.size, # (w, h) + maskData, # source chars + 'raw', # raw decoder + ('1;I', int(w/8), -1) # 1bpp inverted, padded, reversed + ) + + # now we have two images, im is XOR image and mask is AND image + + # apply mask image as alpha channel + im = im.convert('RGBA') + im.putalpha(mask) + + return im + + +## +# Image plugin for Windows Icon files. + +class IcoImageFile(ImageFile.ImageFile): + """ + PIL read-only image support for Microsoft Windows .ico files. + + By default the largest resolution image in the file will be loaded. This + can be changed by altering the 'size' attribute before calling 'load'. + + The info dictionary has a key 'sizes' that is a list of the sizes available + in the icon file. + + Handles classic, XP and Vista icon formats. + + This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis + . + https://code.google.com/p/casadebender/wiki/Win32IconImagePlugin + """ + format = "ICO" + format_description = "Windows Icon" + + def _open(self): + self.ico = IcoFile(self.fp) + self.info['sizes'] = self.ico.sizes() + self.size = self.ico.entry[0]['dim'] + self.load() + + def load(self): + im = self.ico.getimage(self.size) + # if tile is PNG, it won't really be loaded yet + im.load() + self.im = im.im + self.mode = im.mode + self.size = im.size + + def load_seek(self): + # Flage the ImageFile.Parser so that it + # just does all the decode at the end. + pass +# +# -------------------------------------------------------------------- + +Image.register_open("ICO", IcoImageFile, _accept) +Image.register_save("ICO", _save) +Image.register_extension("ICO", ".ico") diff --git a/pyPackages/pillowx86/PIL/ImImagePlugin.py b/pyPackages/pillowx86/PIL/ImImagePlugin.py new file mode 100644 index 0000000..4266f83 --- /dev/null +++ b/pyPackages/pillowx86/PIL/ImImagePlugin.py @@ -0,0 +1,347 @@ +# +# The Python Imaging Library. +# $Id$ +# +# IFUNC IM file handling for PIL +# +# history: +# 1995-09-01 fl Created. +# 1997-01-03 fl Save palette images +# 1997-01-08 fl Added sequence support +# 1997-01-23 fl Added P and RGB save support +# 1997-05-31 fl Read floating point images +# 1997-06-22 fl Save floating point images +# 1997-08-27 fl Read and save 1-bit images +# 1998-06-25 fl Added support for RGB+LUT images +# 1998-07-02 fl Added support for YCC images +# 1998-07-15 fl Renamed offset attribute to avoid name clash +# 1998-12-29 fl Added I;16 support +# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.7) +# 2003-09-26 fl Added LA/PA support +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1995-2001 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.7" + +import re +from PIL import Image, ImageFile, ImagePalette +from PIL._binary import i8 + + +# -------------------------------------------------------------------- +# Standard tags + +COMMENT = "Comment" +DATE = "Date" +EQUIPMENT = "Digitalization equipment" +FRAMES = "File size (no of images)" +LUT = "Lut" +NAME = "Name" +SCALE = "Scale (x,y)" +SIZE = "Image size (x*y)" +MODE = "Image type" + +TAGS = {COMMENT: 0, DATE: 0, EQUIPMENT: 0, FRAMES: 0, LUT: 0, NAME: 0, + SCALE: 0, SIZE: 0, MODE: 0} + +OPEN = { + # ifunc93/p3cfunc formats + "0 1 image": ("1", "1"), + "L 1 image": ("1", "1"), + "Greyscale image": ("L", "L"), + "Grayscale image": ("L", "L"), + "RGB image": ("RGB", "RGB;L"), + "RLB image": ("RGB", "RLB"), + "RYB image": ("RGB", "RLB"), + "B1 image": ("1", "1"), + "B2 image": ("P", "P;2"), + "B4 image": ("P", "P;4"), + "X 24 image": ("RGB", "RGB"), + "L 32 S image": ("I", "I;32"), + "L 32 F image": ("F", "F;32"), + # old p3cfunc formats + "RGB3 image": ("RGB", "RGB;T"), + "RYB3 image": ("RGB", "RYB;T"), + # extensions + "LA image": ("LA", "LA;L"), + "RGBA image": ("RGBA", "RGBA;L"), + "RGBX image": ("RGBX", "RGBX;L"), + "CMYK image": ("CMYK", "CMYK;L"), + "YCC image": ("YCbCr", "YCbCr;L"), +} + +# ifunc95 extensions +for i in ["8", "8S", "16", "16S", "32", "32F"]: + OPEN["L %s image" % i] = ("F", "F;%s" % i) + OPEN["L*%s image" % i] = ("F", "F;%s" % i) +for i in ["16", "16L", "16B"]: + OPEN["L %s image" % i] = ("I;%s" % i, "I;%s" % i) + OPEN["L*%s image" % i] = ("I;%s" % i, "I;%s" % i) +for i in ["32S"]: + OPEN["L %s image" % i] = ("I", "I;%s" % i) + OPEN["L*%s image" % i] = ("I", "I;%s" % i) +for i in range(2, 33): + OPEN["L*%s image" % i] = ("F", "F;%s" % i) + + +# -------------------------------------------------------------------- +# Read IM directory + +split = re.compile(br"^([A-Za-z][^:]*):[ \t]*(.*)[ \t]*$") + + +def number(s): + try: + return int(s) + except ValueError: + return float(s) + + +## +# Image plugin for the IFUNC IM file format. + +class ImImageFile(ImageFile.ImageFile): + + format = "IM" + format_description = "IFUNC Image Memory" + + def _open(self): + + # Quick rejection: if there's not an LF among the first + # 100 bytes, this is (probably) not a text header. + + if b"\n" not in self.fp.read(100): + raise SyntaxError("not an IM file") + self.fp.seek(0) + + n = 0 + + # Default values + self.info[MODE] = "L" + self.info[SIZE] = (512, 512) + self.info[FRAMES] = 1 + + self.rawmode = "L" + + while True: + + s = self.fp.read(1) + + # Some versions of IFUNC uses \n\r instead of \r\n... + if s == b"\r": + continue + + if not s or s == b'\0' or s == b'\x1A': + break + + # FIXME: this may read whole file if not a text file + s = s + self.fp.readline() + + if len(s) > 100: + raise SyntaxError("not an IM file") + + if s[-2:] == b'\r\n': + s = s[:-2] + elif s[-1:] == b'\n': + s = s[:-1] + + try: + m = split.match(s) + except re.error as v: + raise SyntaxError("not an IM file") + + if m: + + k, v = m.group(1, 2) + + # Don't know if this is the correct encoding, + # but a decent guess (I guess) + k = k.decode('latin-1', 'replace') + v = v.decode('latin-1', 'replace') + + # Convert value as appropriate + if k in [FRAMES, SCALE, SIZE]: + v = v.replace("*", ",") + v = tuple(map(number, v.split(","))) + if len(v) == 1: + v = v[0] + elif k == MODE and v in OPEN: + v, self.rawmode = OPEN[v] + + # Add to dictionary. Note that COMMENT tags are + # combined into a list of strings. + if k == COMMENT: + if k in self.info: + self.info[k].append(v) + else: + self.info[k] = [v] + else: + self.info[k] = v + + if k in TAGS: + n += 1 + + else: + + raise SyntaxError("Syntax error in IM header: " + + s.decode('ascii', 'replace')) + + if not n: + raise SyntaxError("Not an IM file") + + # Basic attributes + self.size = self.info[SIZE] + self.mode = self.info[MODE] + + # Skip forward to start of image data + while s and s[0:1] != b'\x1A': + s = self.fp.read(1) + if not s: + raise SyntaxError("File truncated") + + if LUT in self.info: + # convert lookup table to palette or lut attribute + palette = self.fp.read(768) + greyscale = 1 # greyscale palette + linear = 1 # linear greyscale palette + for i in range(256): + if palette[i] == palette[i+256] == palette[i+512]: + if i8(palette[i]) != i: + linear = 0 + else: + greyscale = 0 + if self.mode == "L" or self.mode == "LA": + if greyscale: + if not linear: + self.lut = [i8(c) for c in palette[:256]] + else: + if self.mode == "L": + self.mode = self.rawmode = "P" + elif self.mode == "LA": + self.mode = self.rawmode = "PA" + self.palette = ImagePalette.raw("RGB;L", palette) + elif self.mode == "RGB": + if not greyscale or not linear: + self.lut = [i8(c) for c in palette] + + self.frame = 0 + + self.__offset = offs = self.fp.tell() + + self.__fp = self.fp # FIXME: hack + + if self.rawmode[:2] == "F;": + + # ifunc95 formats + try: + # use bit decoder (if necessary) + bits = int(self.rawmode[2:]) + if bits not in [8, 16, 32]: + self.tile = [("bit", (0, 0)+self.size, offs, + (bits, 8, 3, 0, -1))] + return + except ValueError: + pass + + if self.rawmode in ["RGB;T", "RYB;T"]: + # Old LabEye/3PC files. Would be very surprised if anyone + # ever stumbled upon such a file ;-) + size = self.size[0] * self.size[1] + self.tile = [("raw", (0, 0)+self.size, offs, ("G", 0, -1)), + ("raw", (0, 0)+self.size, offs+size, ("R", 0, -1)), + ("raw", (0, 0)+self.size, offs+2*size, ("B", 0, -1))] + else: + # LabEye/IFUNC files + self.tile = [("raw", (0, 0)+self.size, offs, + (self.rawmode, 0, -1))] + + def seek(self, frame): + + if frame < 0 or frame >= self.info[FRAMES]: + raise EOFError("seek outside sequence") + + if self.frame == frame: + return + + self.frame = frame + + if self.mode == "1": + bits = 1 + else: + bits = 8 * len(self.mode) + + size = ((self.size[0] * bits + 7) // 8) * self.size[1] + offs = self.__offset + frame * size + + self.fp = self.__fp + + self.tile = [("raw", (0, 0)+self.size, offs, (self.rawmode, 0, -1))] + + def tell(self): + + return self.frame + +# +# -------------------------------------------------------------------- +# Save IM files + +SAVE = { + # mode: (im type, raw mode) + "1": ("0 1", "1"), + "L": ("Greyscale", "L"), + "LA": ("LA", "LA;L"), + "P": ("Greyscale", "P"), + "PA": ("LA", "PA;L"), + "I": ("L 32S", "I;32S"), + "I;16": ("L 16", "I;16"), + "I;16L": ("L 16L", "I;16L"), + "I;16B": ("L 16B", "I;16B"), + "F": ("L 32F", "F;32F"), + "RGB": ("RGB", "RGB;L"), + "RGBA": ("RGBA", "RGBA;L"), + "RGBX": ("RGBX", "RGBX;L"), + "CMYK": ("CMYK", "CMYK;L"), + "YCbCr": ("YCC", "YCbCr;L") +} + + +def _save(im, fp, filename, check=0): + + try: + type, rawmode = SAVE[im.mode] + except KeyError: + raise ValueError("Cannot save %s images as IM" % im.mode) + + try: + frames = im.encoderinfo["frames"] + except KeyError: + frames = 1 + + if check: + return check + + fp.write(("Image type: %s image\r\n" % type).encode('ascii')) + if filename: + fp.write(("Name: %s\r\n" % filename).encode('ascii')) + fp.write(("Image size (x*y): %d*%d\r\n" % im.size).encode('ascii')) + fp.write(("File size (no of images): %d\r\n" % frames).encode('ascii')) + if im.mode == "P": + fp.write(b"Lut: 1\r\n") + fp.write(b"\000" * (511-fp.tell()) + b"\032") + if im.mode == "P": + fp.write(im.im.getpalette("RGB", "RGB;L")) # 768 bytes + ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0, (rawmode, 0, -1))]) + +# +# -------------------------------------------------------------------- +# Registry + +Image.register_open("IM", ImImageFile) +Image.register_save("IM", _save) + +Image.register_extension("IM", ".im") diff --git a/pyPackages/pillowx86/PIL/Image.py b/pyPackages/pillowx86/PIL/Image.py new file mode 100644 index 0000000..98e3644 --- /dev/null +++ b/pyPackages/pillowx86/PIL/Image.py @@ -0,0 +1,2467 @@ +# +# The Python Imaging Library. +# $Id$ +# +# the Image class wrapper +# +# partial release history: +# 1995-09-09 fl Created +# 1996-03-11 fl PIL release 0.0 (proof of concept) +# 1996-04-30 fl PIL release 0.1b1 +# 1999-07-28 fl PIL release 1.0 final +# 2000-06-07 fl PIL release 1.1 +# 2000-10-20 fl PIL release 1.1.1 +# 2001-05-07 fl PIL release 1.1.2 +# 2002-03-15 fl PIL release 1.1.3 +# 2003-05-10 fl PIL release 1.1.4 +# 2005-03-28 fl PIL release 1.1.5 +# 2006-12-02 fl PIL release 1.1.6 +# 2009-11-15 fl PIL release 1.1.7 +# +# Copyright (c) 1997-2009 by Secret Labs AB. All rights reserved. +# Copyright (c) 1995-2009 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +from __future__ import print_function + +from PIL import VERSION, PILLOW_VERSION, _plugins + +import warnings + + +class DecompressionBombWarning(RuntimeWarning): + pass + + +class _imaging_not_installed: + # module placeholder + def __getattr__(self, id): + raise ImportError("The _imaging C module is not installed") + + +# Limit to around a quarter gigabyte for a 24 bit (3 bpp) image +MAX_IMAGE_PIXELS = int(1024 * 1024 * 1024 / 4 / 3) + +try: + # give Tk a chance to set up the environment, in case we're + # using an _imaging module linked against libtcl/libtk (use + # __import__ to hide this from naive packagers; we don't really + # depend on Tk unless ImageTk is used, and that module already + # imports Tkinter) + __import__("FixTk") +except ImportError: + pass + +try: + # If the _imaging C module is not present, Pillow will not load. + # Note that other modules should not refer to _imaging directly; + # import Image and use the Image.core variable instead. + # Also note that Image.core is not a publicly documented interface, + # and should be considered private and subject to change. + from PIL import _imaging as core + if PILLOW_VERSION != getattr(core, 'PILLOW_VERSION', None): + raise ImportError("The _imaging extension was built for another " + " version of Pillow or PIL") + +except ImportError as v: + core = _imaging_not_installed() + # Explanations for ways that we know we might have an import error + if str(v).startswith("Module use of python"): + # The _imaging C module is present, but not compiled for + # the right version (windows only). Print a warning, if + # possible. + warnings.warn( + "The _imaging extension was built for another version " + "of Python.", + RuntimeWarning + ) + elif str(v).startswith("The _imaging extension"): + warnings.warn(str(v), RuntimeWarning) + elif "Symbol not found: _PyUnicodeUCS2_FromString" in str(v): + warnings.warn( + "The _imaging extension was built for Python with UCS2 support; " + "recompile PIL or build Python --without-wide-unicode. ", + RuntimeWarning + ) + elif "Symbol not found: _PyUnicodeUCS4_FromString" in str(v): + warnings.warn( + "The _imaging extension was built for Python with UCS4 support; " + "recompile PIL or build Python --with-wide-unicode. ", + RuntimeWarning + ) + # Fail here anyway. Don't let people run with a mostly broken Pillow. + # see docs/porting-pil-to-pillow.rst + raise + +try: + import builtins +except ImportError: + import __builtin__ + builtins = __builtin__ + +from PIL import ImageMode +from PIL._binary import i8 +from PIL._util import isPath +from PIL._util import isStringType +from PIL._util import deferred_error + +import os +import sys + +# type stuff +import collections +import numbers + +# works everywhere, win for pypy, not cpython +USE_CFFI_ACCESS = hasattr(sys, 'pypy_version_info') +try: + import cffi + HAS_CFFI = True +except: + HAS_CFFI = False + + +def isImageType(t): + """ + Checks if an object is an image object. + + .. warning:: + + This function is for internal use only. + + :param t: object to check if it's an image + :returns: True if the object is an image + """ + return hasattr(t, "im") + +# +# Debug level + +DEBUG = 0 + +# +# Constants (also defined in _imagingmodule.c!) + +NONE = 0 + +# transpose +FLIP_LEFT_RIGHT = 0 +FLIP_TOP_BOTTOM = 1 +ROTATE_90 = 2 +ROTATE_180 = 3 +ROTATE_270 = 4 +TRANSPOSE = 5 + +# transforms +AFFINE = 0 +EXTENT = 1 +PERSPECTIVE = 2 +QUAD = 3 +MESH = 4 + +# resampling filters +NEAREST = NONE = 0 +LANCZOS = ANTIALIAS = 1 +BILINEAR = LINEAR = 2 +BICUBIC = CUBIC = 3 + +# dithers +NONE = 0 +NEAREST = 0 +ORDERED = 1 # Not yet implemented +RASTERIZE = 2 # Not yet implemented +FLOYDSTEINBERG = 3 # default + +# palettes/quantizers +WEB = 0 +ADAPTIVE = 1 + +MEDIANCUT = 0 +MAXCOVERAGE = 1 +FASTOCTREE = 2 + +# categories +NORMAL = 0 +SEQUENCE = 1 +CONTAINER = 2 + +if hasattr(core, 'DEFAULT_STRATEGY'): + DEFAULT_STRATEGY = core.DEFAULT_STRATEGY + FILTERED = core.FILTERED + HUFFMAN_ONLY = core.HUFFMAN_ONLY + RLE = core.RLE + FIXED = core.FIXED + + +# -------------------------------------------------------------------- +# Registries + +ID = [] +OPEN = {} +MIME = {} +SAVE = {} +EXTENSION = {} + +# -------------------------------------------------------------------- +# Modes supported by this version + +_MODEINFO = { + # NOTE: this table will be removed in future versions. use + # getmode* functions or ImageMode descriptors instead. + + # official modes + "1": ("L", "L", ("1",)), + "L": ("L", "L", ("L",)), + "I": ("L", "I", ("I",)), + "F": ("L", "F", ("F",)), + "P": ("RGB", "L", ("P",)), + "RGB": ("RGB", "L", ("R", "G", "B")), + "RGBX": ("RGB", "L", ("R", "G", "B", "X")), + "RGBA": ("RGB", "L", ("R", "G", "B", "A")), + "CMYK": ("RGB", "L", ("C", "M", "Y", "K")), + "YCbCr": ("RGB", "L", ("Y", "Cb", "Cr")), + "LAB": ("RGB", "L", ("L", "A", "B")), + "HSV": ("RGB", "L", ("H", "S", "V")), + + # Experimental modes include I;16, I;16L, I;16B, RGBa, BGR;15, and + # BGR;24. Use these modes only if you know exactly what you're + # doing... + +} + +if sys.byteorder == 'little': + _ENDIAN = '<' +else: + _ENDIAN = '>' + +_MODE_CONV = { + # official modes + "1": ('|b1', None), # broken + "L": ('|u1', None), + "I": (_ENDIAN + 'i4', None), + "F": (_ENDIAN + 'f4', None), + "P": ('|u1', None), + "RGB": ('|u1', 3), + "RGBX": ('|u1', 4), + "RGBA": ('|u1', 4), + "CMYK": ('|u1', 4), + "YCbCr": ('|u1', 3), + "LAB": ('|u1', 3), # UNDONE - unsigned |u1i1i1 + # I;16 == I;16L, and I;32 == I;32L + "I;16": ('u2', None), + "I;16L": ('i2', None), + "I;16LS": ('u4', None), + "I;32L": ('i4', None), + "I;32LS": ('= 1: + return + + try: + from PIL import BmpImagePlugin + except ImportError: + pass + try: + from PIL import GifImagePlugin + except ImportError: + pass + try: + from PIL import JpegImagePlugin + except ImportError: + pass + try: + from PIL import PpmImagePlugin + except ImportError: + pass + try: + from PIL import PngImagePlugin + except ImportError: + pass +# try: +# import TiffImagePlugin +# except ImportError: +# pass + + _initialized = 1 + + +def init(): + """ + Explicitly initializes the Python Imaging Library. This function + loads all available file format drivers. + """ + + global _initialized + if _initialized >= 2: + return 0 + + for plugin in _plugins: + try: + if DEBUG: + print ("Importing %s" % plugin) + __import__("PIL.%s" % plugin, globals(), locals(), []) + except ImportError: + if DEBUG: + print("Image: failed to import", end=' ') + print(plugin, ":", sys.exc_info()[1]) + + if OPEN or SAVE: + _initialized = 2 + return 1 + + +# -------------------------------------------------------------------- +# Codec factories (used by tobytes/frombytes and ImageFile.load) + +def _getdecoder(mode, decoder_name, args, extra=()): + + # tweak arguments + if args is None: + args = () + elif not isinstance(args, tuple): + args = (args,) + + try: + # get decoder + decoder = getattr(core, decoder_name + "_decoder") + # print(decoder, mode, args + extra) + return decoder(mode, *args + extra) + except AttributeError: + raise IOError("decoder %s not available" % decoder_name) + + +def _getencoder(mode, encoder_name, args, extra=()): + + # tweak arguments + if args is None: + args = () + elif not isinstance(args, tuple): + args = (args,) + + try: + # get encoder + encoder = getattr(core, encoder_name + "_encoder") + # print(encoder, mode, args + extra) + return encoder(mode, *args + extra) + except AttributeError: + raise IOError("encoder %s not available" % encoder_name) + + +# -------------------------------------------------------------------- +# Simple expression analyzer + +def coerce_e(value): + return value if isinstance(value, _E) else _E(value) + + +class _E: + def __init__(self, data): + self.data = data + + def __add__(self, other): + return _E((self.data, "__add__", coerce_e(other).data)) + + def __mul__(self, other): + return _E((self.data, "__mul__", coerce_e(other).data)) + + +def _getscaleoffset(expr): + stub = ["stub"] + data = expr(_E(stub)).data + try: + (a, b, c) = data # simplified syntax + if (a is stub and b == "__mul__" and isinstance(c, numbers.Number)): + return c, 0.0 + if a is stub and b == "__add__" and isinstance(c, numbers.Number): + return 1.0, c + except TypeError: + pass + try: + ((a, b, c), d, e) = data # full syntax + if (a is stub and b == "__mul__" and isinstance(c, numbers.Number) and + d == "__add__" and isinstance(e, numbers.Number)): + return c, e + except TypeError: + pass + raise ValueError("illegal expression") + + +# -------------------------------------------------------------------- +# Implementation wrapper + +class Image: + """ + This class represents an image object. To create + :py:class:`~PIL.Image.Image` objects, use the appropriate factory + functions. There's hardly ever any reason to call the Image constructor + directly. + + * :py:func:`~PIL.Image.open` + * :py:func:`~PIL.Image.new` + * :py:func:`~PIL.Image.frombytes` + """ + format = None + format_description = None + + def __init__(self): + # FIXME: take "new" parameters / other image? + # FIXME: turn mode and size into delegating properties? + self.im = None + self.mode = "" + self.size = (0, 0) + self.palette = None + self.info = {} + self.category = NORMAL + self.readonly = 0 + self.pyaccess = None + + def _new(self, im): + new = Image() + new.im = im + new.mode = im.mode + new.size = im.size + new.palette = self.palette + if im.mode == "P" and not new.palette: + from PIL import ImagePalette + new.palette = ImagePalette.ImagePalette() + try: + new.info = self.info.copy() + except AttributeError: + # fallback (pre-1.5.2) + new.info = {} + for k, v in self.info: + new.info[k] = v + return new + + _makeself = _new # compatibility + + # Context Manager Support + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + + def close(self): + """ + Closes the file pointer, if possible. + + This operation will destroy the image core and release its memory. + The image data will be unusable afterward. + + This function is only required to close images that have not + had their file read and closed by the + :py:meth:`~PIL.Image.Image.load` method. + """ + try: + self.fp.close() + except Exception as msg: + if DEBUG: + print ("Error closing: %s" % msg) + + # Instead of simply setting to None, we're setting up a + # deferred error that will better explain that the core image + # object is gone. + self.im = deferred_error(ValueError("Operation on closed image")) + + def _copy(self): + self.load() + self.im = self.im.copy() + self.pyaccess = None + self.readonly = 0 + + def _dump(self, file=None, format=None): + import tempfile + suffix = '' + if format: + suffix = '.'+format + if not file: + f, file = tempfile.mkstemp(suffix) + os.close(f) + + self.load() + if not format or format == "PPM": + self.im.save_ppm(file) + else: + if not file.endswith(format): + file = file + "." + format + self.save(file, format) + return file + + def __eq__(self, other): + if self.__class__.__name__ != other.__class__.__name__: + return False + a = (self.mode == other.mode) + b = (self.size == other.size) + c = (self.getpalette() == other.getpalette()) + d = (self.info == other.info) + e = (self.category == other.category) + f = (self.readonly == other.readonly) + g = (self.tobytes() == other.tobytes()) + return a and b and c and d and e and f and g + + def __ne__(self, other): + eq = (self == other) + return not eq + + def __repr__(self): + return "<%s.%s image mode=%s size=%dx%d at 0x%X>" % ( + self.__class__.__module__, self.__class__.__name__, + self.mode, self.size[0], self.size[1], + id(self) + ) + + def __getattr__(self, name): + if name == "__array_interface__": + # numpy array interface support + new = {} + shape, typestr = _conv_type_shape(self) + new['shape'] = shape + new['typestr'] = typestr + new['data'] = self.tobytes() + return new + raise AttributeError(name) + + def __getstate__(self): + return [ + self.info, + self.mode, + self.size, + self.getpalette(), + self.tobytes()] + + def __setstate__(self, state): + Image.__init__(self) + self.tile = [] + info, mode, size, palette, data = state + self.info = info + self.mode = mode + self.size = size + self.im = core.new(mode, size) + if mode in ("L", "P"): + self.putpalette(palette) + self.frombytes(data) + + def tobytes(self, encoder_name="raw", *args): + """ + Return image as a bytes object + + :param encoder_name: What encoder to use. The default is to + use the standard "raw" encoder. + :param args: Extra arguments to the encoder. + :rtype: A bytes object. + """ + + # may pass tuple instead of argument list + if len(args) == 1 and isinstance(args[0], tuple): + args = args[0] + + if encoder_name == "raw" and args == (): + args = self.mode + + self.load() + + # unpack data + e = _getencoder(self.mode, encoder_name, args) + e.setimage(self.im) + + bufsize = max(65536, self.size[0] * 4) # see RawEncode.c + + data = [] + while True: + l, s, d = e.encode(bufsize) + data.append(d) + if s: + break + if s < 0: + raise RuntimeError("encoder error %d in tobytes" % s) + + return b"".join(data) + + # Declare tostring as alias to tobytes + def tostring(self, *args, **kw): + """Deprecated alias to tobytes. + + .. deprecated:: 2.0 + """ + warnings.warn( + 'tostring() is deprecated. Please call tobytes() instead.', + DeprecationWarning, + stacklevel=2, + ) + return self.tobytes(*args, **kw) + + def tobitmap(self, name="image"): + """ + Returns the image converted to an X11 bitmap. + + .. note:: This method only works for mode "1" images. + + :param name: The name prefix to use for the bitmap variables. + :returns: A string containing an X11 bitmap. + :raises ValueError: If the mode is not "1" + """ + + self.load() + if self.mode != "1": + raise ValueError("not a bitmap") + data = self.tobytes("xbm") + return b"".join([ + ("#define %s_width %d\n" % (name, self.size[0])).encode('ascii'), + ("#define %s_height %d\n" % (name, self.size[1])).encode('ascii'), + ("static char %s_bits[] = {\n" % name).encode('ascii'), data, b"};" + ]) + + def frombytes(self, data, decoder_name="raw", *args): + """ + Loads this image with pixel data from a bytes object. + + This method is similar to the :py:func:`~PIL.Image.frombytes` function, + but loads data into this image instead of creating a new image object. + """ + + # may pass tuple instead of argument list + if len(args) == 1 and isinstance(args[0], tuple): + args = args[0] + + # default format + if decoder_name == "raw" and args == (): + args = self.mode + + # unpack data + d = _getdecoder(self.mode, decoder_name, args) + d.setimage(self.im) + s = d.decode(data) + + if s[0] >= 0: + raise ValueError("not enough image data") + if s[1] != 0: + raise ValueError("cannot decode image data") + + def fromstring(self, *args, **kw): + """Deprecated alias to frombytes. + + .. deprecated:: 2.0 + """ + warnings.warn( + 'fromstring() is deprecated. Please call frombytes() instead.', + DeprecationWarning) + return self.frombytes(*args, **kw) + + def load(self): + """ + Allocates storage for the image and loads the pixel data. In + normal cases, you don't need to call this method, since the + Image class automatically loads an opened image when it is + accessed for the first time. This method will close the file + associated with the image. + + :returns: An image access object. + :rtype: :ref:`PixelAccess` or :py:class:`PIL.PyAccess` + """ + if self.im and self.palette and self.palette.dirty: + # realize palette + self.im.putpalette(*self.palette.getdata()) + self.palette.dirty = 0 + self.palette.mode = "RGB" + self.palette.rawmode = None + if "transparency" in self.info: + if isinstance(self.info["transparency"], int): + self.im.putpalettealpha(self.info["transparency"], 0) + else: + self.im.putpalettealphas(self.info["transparency"]) + self.palette.mode = "RGBA" + + if self.im: + if HAS_CFFI and USE_CFFI_ACCESS: + if self.pyaccess: + return self.pyaccess + from PIL import PyAccess + self.pyaccess = PyAccess.new(self, self.readonly) + if self.pyaccess: + return self.pyaccess + return self.im.pixel_access(self.readonly) + + def verify(self): + """ + Verifies the contents of a file. For data read from a file, this + method attempts to determine if the file is broken, without + actually decoding the image data. If this method finds any + problems, it raises suitable exceptions. If you need to load + the image after using this method, you must reopen the image + file. + """ + pass + + def convert(self, mode=None, matrix=None, dither=None, + palette=WEB, colors=256): + """ + Returns a converted copy of this image. For the "P" mode, this + method translates pixels through the palette. If mode is + omitted, a mode is chosen so that all information in the image + and the palette can be represented without a palette. + + The current version supports all possible conversions between + "L", "RGB" and "CMYK." The **matrix** argument only supports "L" + and "RGB". + + When translating a color image to black and white (mode "L"), + the library uses the ITU-R 601-2 luma transform:: + + L = R * 299/1000 + G * 587/1000 + B * 114/1000 + + The default method of converting a greyscale ("L") or "RGB" + image into a bilevel (mode "1") image uses Floyd-Steinberg + dither to approximate the original image luminosity levels. If + dither is NONE, all non-zero values are set to 255 (white). To + use other thresholds, use the :py:meth:`~PIL.Image.Image.point` + method. + + :param mode: The requested mode. See: :ref:`concept-modes`. + :param matrix: An optional conversion matrix. If given, this + should be 4- or 16-tuple containing floating point values. + :param dither: Dithering method, used when converting from + mode "RGB" to "P" or from "RGB" or "L" to "1". + Available methods are NONE or FLOYDSTEINBERG (default). + :param palette: Palette to use when converting from mode "RGB" + to "P". Available palettes are WEB or ADAPTIVE. + :param colors: Number of colors to use for the ADAPTIVE palette. + Defaults to 256. + :rtype: :py:class:`~PIL.Image.Image` + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + if not mode: + # determine default mode + if self.mode == "P": + self.load() + if self.palette: + mode = self.palette.mode + else: + mode = "RGB" + else: + return self.copy() + + self.load() + + if matrix: + # matrix conversion + if mode not in ("L", "RGB"): + raise ValueError("illegal conversion") + im = self.im.convert_matrix(mode, matrix) + return self._new(im) + + if mode == "P" and self.mode == "RGBA": + return self.quantize(colors) + + trns = None + delete_trns = False + # transparency handling + if "transparency" in self.info and \ + self.info['transparency'] is not None: + if self.mode in ('L', 'RGB') and mode == 'RGBA': + # Use transparent conversion to promote from transparent + # color to an alpha channel. + return self._new(self.im.convert_transparent( + mode, self.info['transparency'])) + elif self.mode in ('L', 'RGB', 'P') and mode in ('L', 'RGB', 'P'): + t = self.info['transparency'] + if isinstance(t, bytes): + # Dragons. This can't be represented by a single color + warnings.warn('Palette images with Transparency ' + + ' expressed in bytes should be converted ' + + 'to RGBA images') + delete_trns = True + else: + # get the new transparency color. + # use existing conversions + trns_im = Image()._new(core.new(self.mode, (1, 1))) + if self.mode == 'P': + trns_im.putpalette(self.palette) + trns_im.putpixel((0, 0), t) + + if mode in ('L', 'RGB'): + trns_im = trns_im.convert(mode) + else: + # can't just retrieve the palette number, got to do it + # after quantization. + trns_im = trns_im.convert('RGB') + trns = trns_im.getpixel((0, 0)) + + elif self.mode == 'P' and mode == 'RGBA': + t = self.info['transparency'] + delete_trns = True + + if isinstance(t, bytes): + self.im.putpalettealphas(t) + elif isinstance(t, int): + self.im.putpalettealpha(t,0) + else: + raise ValueError("Transparency for P mode should" + + " be bytes or int") + + + if mode == "P" and palette == ADAPTIVE: + im = self.im.quantize(colors) + new = self._new(im) + from PIL import ImagePalette + new.palette = ImagePalette.raw("RGB", new.im.getpalette("RGB")) + if delete_trns: + # This could possibly happen if we requantize to fewer colors. + # The transparency would be totally off in that case. + del(new.info['transparency']) + if trns is not None: + try: + new.info['transparency'] = new.palette.getcolor(trns) + except: + # if we can't make a transparent color, don't leave the old + # transparency hanging around to mess us up. + del(new.info['transparency']) + warnings.warn("Couldn't allocate palette entry " + + "for transparency") + return new + + # colorspace conversion + if dither is None: + dither = FLOYDSTEINBERG + + try: + im = self.im.convert(mode, dither) + except ValueError: + try: + # normalize source image and try again + im = self.im.convert(getmodebase(self.mode)) + im = im.convert(mode, dither) + except KeyError: + raise ValueError("illegal conversion") + + new_im = self._new(im) + if delete_trns: + # crash fail if we leave a bytes transparency in an rgb/l mode. + del(new_im.info['transparency']) + if trns is not None: + if new_im.mode == 'P': + try: + new_im.info['transparency'] = new_im.palette.getcolor(trns) + except: + del(new_im.info['transparency']) + warnings.warn("Couldn't allocate palette entry " + + "for transparency") + else: + new_im.info['transparency'] = trns + return new_im + + def quantize(self, colors=256, method=None, kmeans=0, palette=None): + """ + Convert the image to 'P' mode with the specified number + of colors. + + :param colors: The desired number of colors, <= 256 + :param method: 0 = median cut + 1 = maximum coverage + 2 = fast octree + :param kmeans: Integer + :param palette: Quantize to the :py:class:`PIL.ImagingPalette` palette. + :returns: A new image + + """ + + self.load() + + if method is None: + # defaults: + method = 0 + if self.mode == 'RGBA': + method = 2 + + if self.mode == 'RGBA' and method != 2: + # Caller specified an invalid mode. + raise ValueError('Fast Octree (method == 2) is the ' + + ' only valid method for quantizing RGBA images') + + if palette: + # use palette from reference image + palette.load() + if palette.mode != "P": + raise ValueError("bad mode for palette image") + if self.mode != "RGB" and self.mode != "L": + raise ValueError( + "only RGB or L mode images can be quantized to a palette" + ) + im = self.im.convert("P", 1, palette.im) + return self._makeself(im) + + im = self.im.quantize(colors, method, kmeans) + return self._new(im) + + def copy(self): + """ + Copies this image. Use this method if you wish to paste things + into an image, but still retain the original. + + :rtype: :py:class:`~PIL.Image.Image` + :returns: An :py:class:`~PIL.Image.Image` object. + """ + self.load() + im = self.im.copy() + return self._new(im) + + def crop(self, box=None): + """ + Returns a rectangular region from this image. The box is a + 4-tuple defining the left, upper, right, and lower pixel + coordinate. + + This is a lazy operation. Changes to the source image may or + may not be reflected in the cropped image. To break the + connection, call the :py:meth:`~PIL.Image.Image.load` method on + the cropped copy. + + :param box: The crop rectangle, as a (left, upper, right, lower)-tuple. + :rtype: :py:class:`~PIL.Image.Image` + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + self.load() + if box is None: + return self.copy() + + # lazy operation + return _ImageCrop(self, box) + + def draft(self, mode, size): + """ + Configures the image file loader so it returns a version of the + image that as closely as possible matches the given mode and + size. For example, you can use this method to convert a color + JPEG to greyscale while loading it, or to extract a 128x192 + version from a PCD file. + + Note that this method modifies the :py:class:`~PIL.Image.Image` object + in place. If the image has already been loaded, this method has no + effect. + + :param mode: The requested mode. + :param size: The requested size. + """ + pass + + def _expand(self, xmargin, ymargin=None): + if ymargin is None: + ymargin = xmargin + self.load() + return self._new(self.im.expand(xmargin, ymargin, 0)) + + def filter(self, filter): + """ + Filters this image using the given filter. For a list of + available filters, see the :py:mod:`~PIL.ImageFilter` module. + + :param filter: Filter kernel. + :returns: An :py:class:`~PIL.Image.Image` object. """ + + self.load() + + if isinstance(filter, collections.Callable): + filter = filter() + if not hasattr(filter, "filter"): + raise TypeError("filter argument should be ImageFilter.Filter " + + "instance or class") + + if self.im.bands == 1: + return self._new(filter.filter(self.im)) + # fix to handle multiband images since _imaging doesn't + ims = [] + for c in range(self.im.bands): + ims.append(self._new(filter.filter(self.im.getband(c)))) + return merge(self.mode, ims) + + def getbands(self): + """ + Returns a tuple containing the name of each band in this image. + For example, **getbands** on an RGB image returns ("R", "G", "B"). + + :returns: A tuple containing band names. + :rtype: tuple + """ + return ImageMode.getmode(self.mode).bands + + def getbbox(self): + """ + Calculates the bounding box of the non-zero regions in the + image. + + :returns: The bounding box is returned as a 4-tuple defining the + left, upper, right, and lower pixel coordinate. If the image + is completely empty, this method returns None. + + """ + + self.load() + return self.im.getbbox() + + def getcolors(self, maxcolors=256): + """ + Returns a list of colors used in this image. + + :param maxcolors: Maximum number of colors. If this number is + exceeded, this method returns None. The default limit is + 256 colors. + :returns: An unsorted list of (count, pixel) values. + """ + + self.load() + if self.mode in ("1", "L", "P"): + h = self.im.histogram() + out = [] + for i in range(256): + if h[i]: + out.append((h[i], i)) + if len(out) > maxcolors: + return None + return out + return self.im.getcolors(maxcolors) + + def getdata(self, band=None): + """ + Returns the contents of this image as a sequence object + containing pixel values. The sequence object is flattened, so + that values for line one follow directly after the values of + line zero, and so on. + + Note that the sequence object returned by this method is an + internal PIL data type, which only supports certain sequence + operations. To convert it to an ordinary sequence (e.g. for + printing), use **list(im.getdata())**. + + :param band: What band to return. The default is to return + all bands. To return a single band, pass in the index + value (e.g. 0 to get the "R" band from an "RGB" image). + :returns: A sequence-like object. + """ + + self.load() + if band is not None: + return self.im.getband(band) + return self.im # could be abused + + def getextrema(self): + """ + Gets the the minimum and maximum pixel values for each band in + the image. + + :returns: For a single-band image, a 2-tuple containing the + minimum and maximum pixel value. For a multi-band image, + a tuple containing one 2-tuple for each band. + """ + + self.load() + if self.im.bands > 1: + extrema = [] + for i in range(self.im.bands): + extrema.append(self.im.getband(i).getextrema()) + return tuple(extrema) + return self.im.getextrema() + + def getim(self): + """ + Returns a capsule that points to the internal image memory. + + :returns: A capsule object. + """ + + self.load() + return self.im.ptr + + def getpalette(self): + """ + Returns the image palette as a list. + + :returns: A list of color values [r, g, b, ...], or None if the + image has no palette. + """ + + self.load() + try: + if bytes is str: + return [i8(c) for c in self.im.getpalette()] + else: + return list(self.im.getpalette()) + except ValueError: + return None # no palette + + def getpixel(self, xy): + """ + Returns the pixel value at a given position. + + :param xy: The coordinate, given as (x, y). + :returns: The pixel value. If the image is a multi-layer image, + this method returns a tuple. + """ + + self.load() + if self.pyaccess: + return self.pyaccess.getpixel(xy) + return self.im.getpixel(xy) + + def getprojection(self): + """ + Get projection to x and y axes + + :returns: Two sequences, indicating where there are non-zero + pixels along the X-axis and the Y-axis, respectively. + """ + + self.load() + x, y = self.im.getprojection() + return [i8(c) for c in x], [i8(c) for c in y] + + def histogram(self, mask=None, extrema=None): + """ + Returns a histogram for the image. The histogram is returned as + a list of pixel counts, one for each pixel value in the source + image. If the image has more than one band, the histograms for + all bands are concatenated (for example, the histogram for an + "RGB" image contains 768 values). + + A bilevel image (mode "1") is treated as a greyscale ("L") image + by this method. + + If a mask is provided, the method returns a histogram for those + parts of the image where the mask image is non-zero. The mask + image must have the same size as the image, and be either a + bi-level image (mode "1") or a greyscale image ("L"). + + :param mask: An optional mask. + :returns: A list containing pixel counts. + """ + self.load() + if mask: + mask.load() + return self.im.histogram((0, 0), mask.im) + if self.mode in ("I", "F"): + if extrema is None: + extrema = self.getextrema() + return self.im.histogram(extrema) + return self.im.histogram() + + def offset(self, xoffset, yoffset=None): + """ + .. deprecated:: 2.0 + + .. note:: New code should use :py:func:`PIL.ImageChops.offset`. + + Returns a copy of the image where the data has been offset by the given + distances. Data wraps around the edges. If **yoffset** is omitted, it + is assumed to be equal to **xoffset**. + + :param xoffset: The horizontal distance. + :param yoffset: The vertical distance. If omitted, both + distances are set to the same value. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + if warnings: + warnings.warn( + "'offset' is deprecated; use 'ImageChops.offset' instead", + DeprecationWarning, stacklevel=2 + ) + from PIL import ImageChops + return ImageChops.offset(self, xoffset, yoffset) + + def paste(self, im, box=None, mask=None): + """ + Pastes another image into this image. The box argument is either + a 2-tuple giving the upper left corner, a 4-tuple defining the + left, upper, right, and lower pixel coordinate, or None (same as + (0, 0)). If a 4-tuple is given, the size of the pasted image + must match the size of the region. + + If the modes don't match, the pasted image is converted to the mode of + this image (see the :py:meth:`~PIL.Image.Image.convert` method for + details). + + Instead of an image, the source can be a integer or tuple + containing pixel values. The method then fills the region + with the given color. When creating RGB images, you can + also use color strings as supported by the ImageColor module. + + If a mask is given, this method updates only the regions + indicated by the mask. You can use either "1", "L" or "RGBA" + images (in the latter case, the alpha band is used as mask). + Where the mask is 255, the given image is copied as is. Where + the mask is 0, the current value is preserved. Intermediate + values can be used for transparency effects. + + Note that if you paste an "RGBA" image, the alpha band is + ignored. You can work around this by using the same image as + both source image and mask. + + :param im: Source image or pixel value (integer or tuple). + :param box: An optional 4-tuple giving the region to paste into. + If a 2-tuple is used instead, it's treated as the upper left + corner. If omitted or None, the source is pasted into the + upper left corner. + + If an image is given as the second argument and there is no + third, the box defaults to (0, 0), and the second argument + is interpreted as a mask image. + :param mask: An optional mask image. + """ + + if isImageType(box) and mask is None: + # abbreviated paste(im, mask) syntax + mask = box + box = None + + if box is None: + # cover all of self + box = (0, 0) + self.size + + if len(box) == 2: + # lower left corner given; get size from image or mask + if isImageType(im): + size = im.size + elif isImageType(mask): + size = mask.size + else: + # FIXME: use self.size here? + raise ValueError( + "cannot determine region size; use 4-item box" + ) + box = box + (box[0]+size[0], box[1]+size[1]) + + if isStringType(im): + from PIL import ImageColor + im = ImageColor.getcolor(im, self.mode) + + elif isImageType(im): + im.load() + if self.mode != im.mode: + if self.mode != "RGB" or im.mode not in ("RGBA", "RGBa"): + # should use an adapter for this! + im = im.convert(self.mode) + im = im.im + + self.load() + if self.readonly: + self._copy() + + if mask: + mask.load() + self.im.paste(im, box, mask.im) + else: + self.im.paste(im, box) + + def point(self, lut, mode=None): + """ + Maps this image through a lookup table or function. + + :param lut: A lookup table, containing 256 (or 65336 if + self.mode=="I" and mode == "L") values per band in the + image. A function can be used instead, it should take a + single argument. The function is called once for each + possible pixel value, and the resulting table is applied to + all bands of the image. + :param mode: Output mode (default is same as input). In the + current version, this can only be used if the source image + has mode "L" or "P", and the output has mode "1" or the + source image mode is "I" and the output mode is "L". + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + self.load() + + if isinstance(lut, ImagePointHandler): + return lut.point(self) + + if callable(lut): + # if it isn't a list, it should be a function + if self.mode in ("I", "I;16", "F"): + # check if the function can be used with point_transform + # UNDONE wiredfool -- I think this prevents us from ever doing + # a gamma function point transform on > 8bit images. + scale, offset = _getscaleoffset(lut) + return self._new(self.im.point_transform(scale, offset)) + # for other modes, convert the function to a table + lut = [lut(i) for i in range(256)] * self.im.bands + + if self.mode == "F": + # FIXME: _imaging returns a confusing error message for this case + raise ValueError("point operation not supported for this mode") + + return self._new(self.im.point(lut, mode)) + + def putalpha(self, alpha): + """ + Adds or replaces the alpha layer in this image. If the image + does not have an alpha layer, it's converted to "LA" or "RGBA". + The new layer must be either "L" or "1". + + :param alpha: The new alpha layer. This can either be an "L" or "1" + image having the same size as this image, or an integer or + other color value. + """ + + self.load() + if self.readonly: + self._copy() + + if self.mode not in ("LA", "RGBA"): + # attempt to promote self to a matching alpha mode + try: + mode = getmodebase(self.mode) + "A" + try: + self.im.setmode(mode) + self.pyaccess = None + except (AttributeError, ValueError): + # do things the hard way + im = self.im.convert(mode) + if im.mode not in ("LA", "RGBA"): + raise ValueError # sanity check + self.im = im + self.pyaccess = None + self.mode = self.im.mode + except (KeyError, ValueError): + raise ValueError("illegal image mode") + + if self.mode == "LA": + band = 1 + else: + band = 3 + + if isImageType(alpha): + # alpha layer + if alpha.mode not in ("1", "L"): + raise ValueError("illegal image mode") + alpha.load() + if alpha.mode == "1": + alpha = alpha.convert("L") + else: + # constant alpha + try: + self.im.fillband(band, alpha) + except (AttributeError, ValueError): + # do things the hard way + alpha = new("L", self.size, alpha) + else: + return + + self.im.putband(alpha.im, band) + + def putdata(self, data, scale=1.0, offset=0.0): + """ + Copies pixel data to this image. This method copies data from a + sequence object into the image, starting at the upper left + corner (0, 0), and continuing until either the image or the + sequence ends. The scale and offset values are used to adjust + the sequence values: **pixel = value*scale + offset**. + + :param data: A sequence object. + :param scale: An optional scale value. The default is 1.0. + :param offset: An optional offset value. The default is 0.0. + """ + + self.load() + if self.readonly: + self._copy() + + self.im.putdata(data, scale, offset) + + def putpalette(self, data, rawmode="RGB"): + """ + Attaches a palette to this image. The image must be a "P" or + "L" image, and the palette sequence must contain 768 integer + values, where each group of three values represent the red, + green, and blue values for the corresponding pixel + index. Instead of an integer sequence, you can use an 8-bit + string. + + :param data: A palette sequence (either a list or a string). + """ + from PIL import ImagePalette + + if self.mode not in ("L", "P"): + raise ValueError("illegal image mode") + self.load() + if isinstance(data, ImagePalette.ImagePalette): + palette = ImagePalette.raw(data.rawmode, data.palette) + else: + if not isinstance(data, bytes): + if bytes is str: + data = "".join(chr(x) for x in data) + else: + data = bytes(data) + palette = ImagePalette.raw(rawmode, data) + self.mode = "P" + self.palette = palette + self.palette.mode = "RGB" + self.load() # install new palette + + def putpixel(self, xy, value): + """ + Modifies the pixel at the given position. The color is given as + a single numerical value for single-band images, and a tuple for + multi-band images. + + Note that this method is relatively slow. For more extensive changes, + use :py:meth:`~PIL.Image.Image.paste` or the :py:mod:`~PIL.ImageDraw` + module instead. + + See: + + * :py:meth:`~PIL.Image.Image.paste` + * :py:meth:`~PIL.Image.Image.putdata` + * :py:mod:`~PIL.ImageDraw` + + :param xy: The pixel coordinate, given as (x, y). + :param value: The pixel value. + """ + + self.load() + if self.readonly: + self._copy() + self.pyaccess = None + self.load() + + if self.pyaccess: + return self.pyaccess.putpixel(xy, value) + return self.im.putpixel(xy, value) + + def resize(self, size, resample=NEAREST): + """ + Returns a resized copy of this image. + + :param size: The requested size in pixels, as a 2-tuple: + (width, height). + :param resample: An optional resampling filter. This can be + one of :py:attr:`PIL.Image.NEAREST` (use nearest neighbour), + :py:attr:`PIL.Image.BILINEAR` (linear interpolation), + :py:attr:`PIL.Image.BICUBIC` (cubic spline interpolation), or + :py:attr:`PIL.Image.LANCZOS` (a high-quality downsampling filter). + If omitted, or if the image has mode "1" or "P", it is + set :py:attr:`PIL.Image.NEAREST`. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + if resample not in (NEAREST, BILINEAR, BICUBIC, LANCZOS): + raise ValueError("unknown resampling filter") + + self.load() + + size=tuple(size) + if self.size == size: + return self._new(self.im) + + if self.mode in ("1", "P"): + resample = NEAREST + + if self.mode == 'RGBA': + return self.convert('RGBa').resize(size, resample).convert('RGBA') + + return self._new(self.im.resize(size, resample)) + + def rotate(self, angle, resample=NEAREST, expand=0): + """ + Returns a rotated copy of this image. This method returns a + copy of this image, rotated the given number of degrees counter + clockwise around its centre. + + :param angle: In degrees counter clockwise. + :param resample: An optional resampling filter. This can be + one of :py:attr:`PIL.Image.NEAREST` (use nearest neighbour), + :py:attr:`PIL.Image.BILINEAR` (linear interpolation in a 2x2 + environment), or :py:attr:`PIL.Image.BICUBIC` + (cubic spline interpolation in a 4x4 environment). + If omitted, or if the image has mode "1" or "P", it is + set :py:attr:`PIL.Image.NEAREST`. + :param expand: Optional expansion flag. If true, expands the output + image to make it large enough to hold the entire rotated image. + If false or omitted, make the output image the same size as the + input image. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + if expand: + import math + angle = -angle * math.pi / 180 + matrix = [ + math.cos(angle), math.sin(angle), 0.0, + -math.sin(angle), math.cos(angle), 0.0 + ] + + def transform(x, y, matrix=matrix): + (a, b, c, d, e, f) = matrix + return a*x + b*y + c, d*x + e*y + f + + # calculate output size + w, h = self.size + xx = [] + yy = [] + for x, y in ((0, 0), (w, 0), (w, h), (0, h)): + x, y = transform(x, y) + xx.append(x) + yy.append(y) + w = int(math.ceil(max(xx)) - math.floor(min(xx))) + h = int(math.ceil(max(yy)) - math.floor(min(yy))) + + # adjust center + x, y = transform(w / 2.0, h / 2.0) + matrix[2] = self.size[0] / 2.0 - x + matrix[5] = self.size[1] / 2.0 - y + + return self.transform((w, h), AFFINE, matrix, resample) + + if resample not in (NEAREST, BILINEAR, BICUBIC): + raise ValueError("unknown resampling filter") + + self.load() + + if self.mode in ("1", "P"): + resample = NEAREST + + return self._new(self.im.rotate(angle, resample)) + + def save(self, fp, format=None, **params): + """ + Saves this image under the given filename. If no format is + specified, the format to use is determined from the filename + extension, if possible. + + Keyword options can be used to provide additional instructions + to the writer. If a writer doesn't recognise an option, it is + silently ignored. The available options are described in the + :doc:`image format documentation + <../handbook/image-file-formats>` for each writer. + + You can use a file object instead of a filename. In this case, + you must always specify the format. The file object must + implement the ``seek``, ``tell``, and ``write`` + methods, and be opened in binary mode. + + :param fp: File name or file object. + :param format: Optional format override. If omitted, the + format to use is determined from the filename extension. + If a file object was used instead of a filename, this + parameter should always be used. + :param options: Extra parameters to the image writer. + :returns: None + :exception KeyError: If the output format could not be determined + from the file name. Use the format option to solve this. + :exception IOError: If the file could not be written. The file + may have been created, and may contain partial data. + """ + + if isPath(fp): + filename = fp + else: + if hasattr(fp, "name") and isPath(fp.name): + filename = fp.name + else: + filename = "" + + # may mutate self! + self.load() + + self.encoderinfo = params + self.encoderconfig = () + + preinit() + + ext = os.path.splitext(filename)[1].lower() + + if not format: + try: + format = EXTENSION[ext] + except KeyError: + init() + try: + format = EXTENSION[ext] + except KeyError: + raise KeyError(ext) # unknown extension + + try: + save_handler = SAVE[format.upper()] + except KeyError: + init() + save_handler = SAVE[format.upper()] # unknown format + + if isPath(fp): + fp = builtins.open(fp, "wb") + close = 1 + else: + close = 0 + + try: + save_handler(self, fp, filename) + finally: + # do what we can to clean up + if close: + fp.close() + + def seek(self, frame): + """ + Seeks to the given frame in this sequence file. If you seek + beyond the end of the sequence, the method raises an + **EOFError** exception. When a sequence file is opened, the + library automatically seeks to frame 0. + + Note that in the current version of the library, most sequence + formats only allows you to seek to the next frame. + + See :py:meth:`~PIL.Image.Image.tell`. + + :param frame: Frame number, starting at 0. + :exception EOFError: If the call attempts to seek beyond the end + of the sequence. + """ + + # overridden by file handlers + if frame != 0: + raise EOFError + + def show(self, title=None, command=None): + """ + Displays this image. This method is mainly intended for + debugging purposes. + + On Unix platforms, this method saves the image to a temporary + PPM file, and calls the **xv** utility. + + On Windows, it saves the image to a temporary BMP file, and uses + the standard BMP display utility to show it (usually Paint). + + :param title: Optional title to use for the image window, + where possible. + :param command: command used to show the image + """ + + _show(self, title=title, command=command) + + def split(self): + """ + Split this image into individual bands. This method returns a + tuple of individual image bands from an image. For example, + splitting an "RGB" image creates three new images each + containing a copy of one of the original bands (red, green, + blue). + + :returns: A tuple containing bands. + """ + + self.load() + if self.im.bands == 1: + ims = [self.copy()] + else: + ims = [] + for i in range(self.im.bands): + ims.append(self._new(self.im.getband(i))) + return tuple(ims) + + def tell(self): + """ + Returns the current frame number. See :py:meth:`~PIL.Image.Image.seek`. + + :returns: Frame number, starting with 0. + """ + return 0 + + def thumbnail(self, size, resample=BICUBIC): + """ + Make this image into a thumbnail. This method modifies the + image to contain a thumbnail version of itself, no larger than + the given size. This method calculates an appropriate thumbnail + size to preserve the aspect of the image, calls the + :py:meth:`~PIL.Image.Image.draft` method to configure the file reader + (where applicable), and finally resizes the image. + + Note that this function modifies the :py:class:`~PIL.Image.Image` + object in place. If you need to use the full resolution image as well, + apply this method to a :py:meth:`~PIL.Image.Image.copy` of the original + image. + + :param size: Requested size. + :param resample: Optional resampling filter. This can be one + of :py:attr:`PIL.Image.NEAREST`, :py:attr:`PIL.Image.BILINEAR`, + :py:attr:`PIL.Image.BICUBIC`, or :py:attr:`PIL.Image.LANCZOS`. + If omitted, it defaults to :py:attr:`PIL.Image.BICUBIC`. + (was :py:attr:`PIL.Image.NEAREST` prior to version 2.5.0) + :returns: None + """ + + # preserve aspect ratio + x, y = self.size + if x > size[0]: + y = int(max(y * size[0] / x, 1)) + x = int(size[0]) + if y > size[1]: + x = int(max(x * size[1] / y, 1)) + y = int(size[1]) + size = x, y + + if size == self.size: + return + + self.draft(None, size) + + im = self.resize(size, resample) + + self.im = im.im + self.mode = im.mode + self.size = size + + self.readonly = 0 + self.pyaccess = None + + # FIXME: the different tranform methods need further explanation + # instead of bloating the method docs, add a separate chapter. + def transform(self, size, method, data=None, resample=NEAREST, fill=1): + """ + Transforms this image. This method creates a new image with the + given size, and the same mode as the original, and copies data + to the new image using the given transform. + + :param size: The output size. + :param method: The transformation method. This is one of + :py:attr:`PIL.Image.EXTENT` (cut out a rectangular subregion), + :py:attr:`PIL.Image.AFFINE` (affine transform), + :py:attr:`PIL.Image.PERSPECTIVE` (perspective transform), + :py:attr:`PIL.Image.QUAD` (map a quadrilateral to a rectangle), or + :py:attr:`PIL.Image.MESH` (map a number of source quadrilaterals + in one operation). + :param data: Extra data to the transformation method. + :param resample: Optional resampling filter. It can be one of + :py:attr:`PIL.Image.NEAREST` (use nearest neighbour), + :py:attr:`PIL.Image.BILINEAR` (linear interpolation in a 2x2 + environment), or :py:attr:`PIL.Image.BICUBIC` (cubic spline + interpolation in a 4x4 environment). If omitted, or if the image + has mode "1" or "P", it is set to :py:attr:`PIL.Image.NEAREST`. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + if self.mode == 'RGBA': + return self.convert('RGBa').transform( + size, method, data, resample, fill).convert('RGBA') + + if isinstance(method, ImageTransformHandler): + return method.transform(size, self, resample=resample, fill=fill) + if hasattr(method, "getdata"): + # compatibility w. old-style transform objects + method, data = method.getdata() + if data is None: + raise ValueError("missing method data") + + im = new(self.mode, size, None) + if method == MESH: + # list of quads + for box, quad in data: + im.__transformer(box, self, QUAD, quad, resample, fill) + else: + im.__transformer((0, 0)+size, self, method, data, resample, fill) + + return im + + def __transformer(self, box, image, method, data, + resample=NEAREST, fill=1): + + # FIXME: this should be turned into a lazy operation (?) + + w = box[2]-box[0] + h = box[3]-box[1] + + if method == AFFINE: + # change argument order to match implementation + data = (data[2], data[0], data[1], + data[5], data[3], data[4]) + elif method == EXTENT: + # convert extent to an affine transform + x0, y0, x1, y1 = data + xs = float(x1 - x0) / w + ys = float(y1 - y0) / h + method = AFFINE + data = (x0 + xs/2, xs, 0, y0 + ys/2, 0, ys) + elif method == PERSPECTIVE: + # change argument order to match implementation + data = (data[2], data[0], data[1], + data[5], data[3], data[4], + data[6], data[7]) + elif method == QUAD: + # quadrilateral warp. data specifies the four corners + # given as NW, SW, SE, and NE. + nw = data[0:2] + sw = data[2:4] + se = data[4:6] + ne = data[6:8] + x0, y0 = nw + As = 1.0 / w + At = 1.0 / h + data = (x0, (ne[0]-x0)*As, (sw[0]-x0)*At, + (se[0]-sw[0]-ne[0]+x0)*As*At, + y0, (ne[1]-y0)*As, (sw[1]-y0)*At, + (se[1]-sw[1]-ne[1]+y0)*As*At) + else: + raise ValueError("unknown transformation method") + + if resample not in (NEAREST, BILINEAR, BICUBIC): + raise ValueError("unknown resampling filter") + + image.load() + + self.load() + + if image.mode in ("1", "P"): + resample = NEAREST + + self.im.transform2(box, image.im, method, data, resample, fill) + + def transpose(self, method): + """ + Transpose image (flip or rotate in 90 degree steps) + + :param method: One of :py:attr:`PIL.Image.FLIP_LEFT_RIGHT`, + :py:attr:`PIL.Image.FLIP_TOP_BOTTOM`, :py:attr:`PIL.Image.ROTATE_90`, + :py:attr:`PIL.Image.ROTATE_180`, :py:attr:`PIL.Image.ROTATE_270` or + :py:attr:`PIL.Image.TRANSPOSE`. + :returns: Returns a flipped or rotated copy of this image. + """ + + self.load() + return self._new(self.im.transpose(method)) + + def effect_spread(self, distance): + """ + Randomly spread pixels in an image. + + :param distance: Distance to spread pixels. + """ + self.load() + im = self.im.effect_spread(distance) + return self._new(im) + + +# -------------------------------------------------------------------- +# Lazy operations + +class _ImageCrop(Image): + + def __init__(self, im, box): + + Image.__init__(self) + + x0, y0, x1, y1 = box + if x1 < x0: + x1 = x0 + if y1 < y0: + y1 = y0 + + self.mode = im.mode + self.size = x1-x0, y1-y0 + + self.__crop = x0, y0, x1, y1 + + self.im = im.im + + def load(self): + + # lazy evaluation! + if self.__crop: + self.im = self.im.crop(self.__crop) + self.__crop = None + + if self.im: + return self.im.pixel_access(self.readonly) + + # FIXME: future versions should optimize crop/paste + # sequences! + + +# -------------------------------------------------------------------- +# Abstract handlers. + +class ImagePointHandler: + # used as a mixin by point transforms (for use with im.point) + pass + + +class ImageTransformHandler: + # used as a mixin by geometry transforms (for use with im.transform) + pass + + +# -------------------------------------------------------------------- +# Factories + +# +# Debugging + +def _wedge(): + "Create greyscale wedge (for debugging only)" + + return Image()._new(core.wedge("L")) + + +def new(mode, size, color=0): + """ + Creates a new image with the given mode and size. + + :param mode: The mode to use for the new image. See: + :ref:`concept-modes`. + :param size: A 2-tuple, containing (width, height) in pixels. + :param color: What color to use for the image. Default is black. + If given, this should be a single integer or floating point value + for single-band modes, and a tuple for multi-band modes (one value + per band). When creating RGB images, you can also use color + strings as supported by the ImageColor module. If the color is + None, the image is not initialised. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + if color is None: + # don't initialize + return Image()._new(core.new(mode, size)) + + if isStringType(color): + # css3-style specifier + + from PIL import ImageColor + color = ImageColor.getcolor(color, mode) + + return Image()._new(core.fill(mode, size, color)) + + +def frombytes(mode, size, data, decoder_name="raw", *args): + """ + Creates a copy of an image memory from pixel data in a buffer. + + In its simplest form, this function takes three arguments + (mode, size, and unpacked pixel data). + + You can also use any pixel decoder supported by PIL. For more + information on available decoders, see the section + **Writing Your Own File Decoder**. + + Note that this function decodes pixel data only, not entire images. + If you have an entire image in a string, wrap it in a + :py:class:`~io.BytesIO` object, and use :py:func:`~PIL.Image.open` to load + it. + + :param mode: The image mode. See: :ref:`concept-modes`. + :param size: The image size. + :param data: A byte buffer containing raw data for the given mode. + :param decoder_name: What decoder to use. + :param args: Additional parameters for the given decoder. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + # may pass tuple instead of argument list + if len(args) == 1 and isinstance(args[0], tuple): + args = args[0] + + if decoder_name == "raw" and args == (): + args = mode + + im = new(mode, size) + im.frombytes(data, decoder_name, args) + return im + + +def fromstring(*args, **kw): + """Deprecated alias to frombytes. + + .. deprecated:: 2.0 + """ + warnings.warn( + 'fromstring() is deprecated. Please call frombytes() instead.', + DeprecationWarning, + stacklevel=2 + ) + return frombytes(*args, **kw) + + +def frombuffer(mode, size, data, decoder_name="raw", *args): + """ + Creates an image memory referencing pixel data in a byte buffer. + + This function is similar to :py:func:`~PIL.Image.frombytes`, but uses data + in the byte buffer, where possible. This means that changes to the + original buffer object are reflected in this image). Not all modes can + share memory; supported modes include "L", "RGBX", "RGBA", and "CMYK". + + Note that this function decodes pixel data only, not entire images. + If you have an entire image file in a string, wrap it in a + **BytesIO** object, and use :py:func:`~PIL.Image.open` to load it. + + In the current version, the default parameters used for the "raw" decoder + differs from that used for :py:func:`~PIL.Image.fromstring`. This is a + bug, and will probably be fixed in a future release. The current release + issues a warning if you do this; to disable the warning, you should provide + the full set of parameters. See below for details. + + :param mode: The image mode. See: :ref:`concept-modes`. + :param size: The image size. + :param data: A bytes or other buffer object containing raw + data for the given mode. + :param decoder_name: What decoder to use. + :param args: Additional parameters for the given decoder. For the + default encoder ("raw"), it's recommended that you provide the + full set of parameters:: + + frombuffer(mode, size, data, "raw", mode, 0, 1) + + :returns: An :py:class:`~PIL.Image.Image` object. + + .. versionadded:: 1.1.4 + """ + + # may pass tuple instead of argument list + if len(args) == 1 and isinstance(args[0], tuple): + args = args[0] + + if decoder_name == "raw": + if args == (): + if warnings: + warnings.warn( + "the frombuffer defaults may change in a future release; " + "for portability, change the call to read:\n" + " frombuffer(mode, size, data, 'raw', mode, 0, 1)", + RuntimeWarning, stacklevel=2 + ) + args = mode, 0, -1 # may change to (mode, 0, 1) post-1.1.6 + if args[0] in _MAPMODES: + im = new(mode, (1, 1)) + im = im._new( + core.map_buffer(data, size, decoder_name, None, 0, args) + ) + im.readonly = 1 + return im + + return frombytes(mode, size, data, decoder_name, args) + + +def fromarray(obj, mode=None): + """ + Creates an image memory from an object exporting the array interface + (using the buffer protocol). + + If obj is not contiguous, then the tobytes method is called + and :py:func:`~PIL.Image.frombuffer` is used. + + :param obj: Object with array interface + :param mode: Mode to use (will be determined from type if None) + See: :ref:`concept-modes`. + :returns: An image object. + + .. versionadded:: 1.1.6 + """ + arr = obj.__array_interface__ + shape = arr['shape'] + ndim = len(shape) + try: + strides = arr['strides'] + except KeyError: + strides = None + if mode is None: + try: + typekey = (1, 1) + shape[2:], arr['typestr'] + mode, rawmode = _fromarray_typemap[typekey] + except KeyError: + # print typekey + raise TypeError("Cannot handle this data type") + else: + rawmode = mode + if mode in ["1", "L", "I", "P", "F"]: + ndmax = 2 + elif mode == "RGB": + ndmax = 3 + else: + ndmax = 4 + if ndim > ndmax: + raise ValueError("Too many dimensions: %d > %d." % (ndim, ndmax)) + + size = shape[1], shape[0] + if strides is not None: + if hasattr(obj, 'tobytes'): + obj = obj.tobytes() + else: + obj = obj.tostring() + + return frombuffer(mode, size, obj, "raw", rawmode, 0, 1) + +_fromarray_typemap = { + # (shape, typestr) => mode, rawmode + # first two members of shape are set to one + # ((1, 1), "|b1"): ("1", "1"), # broken + ((1, 1), "|u1"): ("L", "L"), + ((1, 1), "|i1"): ("I", "I;8"), + ((1, 1), "i2"): ("I", "I;16B"), + ((1, 1), "i4"): ("I", "I;32B"), + ((1, 1), "f4"): ("F", "F;32BF"), + ((1, 1), "f8"): ("F", "F;64BF"), + ((1, 1, 3), "|u1"): ("RGB", "RGB"), + ((1, 1, 4), "|u1"): ("RGBA", "RGBA"), + } + +# shortcuts +_fromarray_typemap[((1, 1), _ENDIAN + "i4")] = ("I", "I") +_fromarray_typemap[((1, 1), _ENDIAN + "f4")] = ("F", "F") + + +def _decompression_bomb_check(size): + if MAX_IMAGE_PIXELS is None: + return + + pixels = size[0] * size[1] + + if pixels > MAX_IMAGE_PIXELS: + warnings.warn( + "Image size (%d pixels) exceeds limit of %d pixels, " + "could be decompression bomb DOS attack." % + (pixels, MAX_IMAGE_PIXELS), + DecompressionBombWarning) + + +def open(fp, mode="r"): + """ + Opens and identifies the given image file. + + This is a lazy operation; this function identifies the file, but + the file remains open and the actual image data is not read from + the file until you try to process the data (or call the + :py:meth:`~PIL.Image.Image.load` method). See + :py:func:`~PIL.Image.new`. + + :param file: A filename (string) or a file object. The file object + must implement :py:meth:`~file.read`, :py:meth:`~file.seek`, and + :py:meth:`~file.tell` methods, and be opened in binary mode. + :param mode: The mode. If given, this argument must be "r". + :returns: An :py:class:`~PIL.Image.Image` object. + :exception IOError: If the file cannot be found, or the image cannot be + opened and identified. + """ + + if mode != "r": + raise ValueError("bad mode %r" % mode) + + if isPath(fp): + filename = fp + fp = builtins.open(fp, "rb") + else: + filename = "" + + prefix = fp.read(16) + + preinit() + + for i in ID: + try: + factory, accept = OPEN[i] + if not accept or accept(prefix): + fp.seek(0) + im = factory(fp, filename) + _decompression_bomb_check(im.size) + return im + except (SyntaxError, IndexError, TypeError): + # import traceback + # traceback.print_exc() + pass + + if init(): + + for i in ID: + try: + factory, accept = OPEN[i] + if not accept or accept(prefix): + fp.seek(0) + im = factory(fp, filename) + _decompression_bomb_check(im.size) + return im + except (SyntaxError, IndexError, TypeError): + # import traceback + # traceback.print_exc() + pass + + raise IOError("cannot identify image file %r" + % (filename if filename else fp)) + + +# +# Image processing. + +def alpha_composite(im1, im2): + """ + Alpha composite im2 over im1. + + :param im1: The first image. + :param im2: The second image. Must have the same mode and size as + the first image. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + im1.load() + im2.load() + return im1._new(core.alpha_composite(im1.im, im2.im)) + + +def blend(im1, im2, alpha): + """ + Creates a new image by interpolating between two input images, using + a constant alpha.:: + + out = image1 * (1.0 - alpha) + image2 * alpha + + :param im1: The first image. + :param im2: The second image. Must have the same mode and size as + the first image. + :param alpha: The interpolation alpha factor. If alpha is 0.0, a + copy of the first image is returned. If alpha is 1.0, a copy of + the second image is returned. There are no restrictions on the + alpha value. If necessary, the result is clipped to fit into + the allowed output range. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + im1.load() + im2.load() + return im1._new(core.blend(im1.im, im2.im, alpha)) + + +def composite(image1, image2, mask): + """ + Create composite image by blending images using a transparency mask. + + :param image1: The first image. + :param image2: The second image. Must have the same mode and + size as the first image. + :param mask: A mask image. This image can have mode + "1", "L", or "RGBA", and must have the same size as the + other two images. + """ + + image = image2.copy() + image.paste(image1, None, mask) + return image + + +def eval(image, *args): + """ + Applies the function (which should take one argument) to each pixel + in the given image. If the image has more than one band, the same + function is applied to each band. Note that the function is + evaluated once for each possible pixel value, so you cannot use + random components or other generators. + + :param image: The input image. + :param function: A function object, taking one integer argument. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + return image.point(args[0]) + + +def merge(mode, bands): + """ + Merge a set of single band images into a new multiband image. + + :param mode: The mode to use for the output image. See: + :ref:`concept-modes`. + :param bands: A sequence containing one single-band image for + each band in the output image. All bands must have the + same size. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + if getmodebands(mode) != len(bands) or "*" in mode: + raise ValueError("wrong number of bands") + for im in bands[1:]: + if im.mode != getmodetype(mode): + raise ValueError("mode mismatch") + if im.size != bands[0].size: + raise ValueError("size mismatch") + im = core.new(mode, bands[0].size) + for i in range(getmodebands(mode)): + bands[i].load() + im.putband(bands[i].im, i) + return bands[0]._new(im) + + +# -------------------------------------------------------------------- +# Plugin registry + +def register_open(id, factory, accept=None): + """ + Register an image file plugin. This function should not be used + in application code. + + :param id: An image format identifier. + :param factory: An image file factory method. + :param accept: An optional function that can be used to quickly + reject images having another format. + """ + id = id.upper() + ID.append(id) + OPEN[id] = factory, accept + + +def register_mime(id, mimetype): + """ + Registers an image MIME type. This function should not be used + in application code. + + :param id: An image format identifier. + :param mimetype: The image MIME type for this format. + """ + MIME[id.upper()] = mimetype + + +def register_save(id, driver): + """ + Registers an image save function. This function should not be + used in application code. + + :param id: An image format identifier. + :param driver: A function to save images in this format. + """ + SAVE[id.upper()] = driver + + +def register_extension(id, extension): + """ + Registers an image extension. This function should not be + used in application code. + + :param id: An image format identifier. + :param extension: An extension used for this format. + """ + EXTENSION[extension.lower()] = id.upper() + + +# -------------------------------------------------------------------- +# Simple display support. User code may override this. + +def _show(image, **options): + # override me, as necessary + _showxv(image, **options) + + +def _showxv(image, title=None, **options): + from PIL import ImageShow + ImageShow.show(image, title, **options) + + +# -------------------------------------------------------------------- +# Effects + +def effect_mandelbrot(size, extent, quality): + """ + Generate a Mandelbrot set covering the given extent. + + :param size: The requested size in pixels, as a 2-tuple: + (width, height). + :param extent: The extent to cover, as a 4-tuple: + (x0, y0, x1, y2). + :param quality: Quality. + """ + return Image()._new(core.effect_mandelbrot(size, extent, quality)) + + +def effect_noise(size, sigma): + """ + Generate Gaussian noise centered around 128. + + :param size: The requested size in pixels, as a 2-tuple: + (width, height). + :param sigma: Standard deviation of noise. + """ + return Image()._new(core.effect_noise(size, sigma)) + +# End of file diff --git a/pyPackages/pillowx86/PIL/ImageChops.py b/pyPackages/pillowx86/PIL/ImageChops.py new file mode 100644 index 0000000..ba5350e --- /dev/null +++ b/pyPackages/pillowx86/PIL/ImageChops.py @@ -0,0 +1,283 @@ +# +# The Python Imaging Library. +# $Id$ +# +# standard channel operations +# +# History: +# 1996-03-24 fl Created +# 1996-08-13 fl Added logical operations (for "1" images) +# 2000-10-12 fl Added offset method (from Image.py) +# +# Copyright (c) 1997-2000 by Secret Labs AB +# Copyright (c) 1996-2000 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image + + +def constant(image, value): + """Fill a channel with a given grey level. + + :rtype: :py:class:`~PIL.Image.Image` + """ + + return Image.new("L", image.size, value) + + +def duplicate(image): + """Copy a channel. Alias for :py:meth:`PIL.Image.Image.copy`. + + :rtype: :py:class:`~PIL.Image.Image` + """ + + return image.copy() + + +def invert(image): + """ + Invert an image (channel). + + .. code-block:: python + + out = MAX - image + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image.load() + return image._new(image.im.chop_invert()) + + +def lighter(image1, image2): + """ + Compares the two images, pixel by pixel, and returns a new image containing + the lighter values. + + .. code-block:: python + + out = max(image1, image2) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_lighter(image2.im)) + + +def darker(image1, image2): + """ + Compares the two images, pixel by pixel, and returns a new image + containing the darker values. + + .. code-block:: python + + out = min(image1, image2) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_darker(image2.im)) + + +def difference(image1, image2): + """ + Returns the absolute value of the pixel-by-pixel difference between the two + images. + + .. code-block:: python + + out = abs(image1 - image2) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_difference(image2.im)) + + +def multiply(image1, image2): + """ + Superimposes two images on top of each other. + + If you multiply an image with a solid black image, the result is black. If + you multiply with a solid white image, the image is unaffected. + + .. code-block:: python + + out = image1 * image2 / MAX + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_multiply(image2.im)) + + +def screen(image1, image2): + """ + Superimposes two inverted images on top of each other. + + .. code-block:: python + + out = MAX - ((MAX - image1) * (MAX - image2) / MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_screen(image2.im)) + + +def add(image1, image2, scale=1.0, offset=0): + """ + Adds two images, dividing the result by scale and adding the + offset. If omitted, scale defaults to 1.0, and offset to 0.0. + + .. code-block:: python + + out = ((image1 + image2) / scale + offset) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_add(image2.im, scale, offset)) + + +def subtract(image1, image2, scale=1.0, offset=0): + """ + Subtracts two images, dividing the result by scale and adding the + offset. If omitted, scale defaults to 1.0, and offset to 0.0. + + .. code-block:: python + + out = ((image1 - image2) / scale + offset) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_subtract(image2.im, scale, offset)) + + +def add_modulo(image1, image2): + """Add two images, without clipping the result. + + .. code-block:: python + + out = ((image1 + image2) % MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_add_modulo(image2.im)) + + +def subtract_modulo(image1, image2): + """Subtract two images, without clipping the result. + + .. code-block:: python + + out = ((image1 - image2) % MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_subtract_modulo(image2.im)) + + +def logical_and(image1, image2): + """Logical AND between two images. + + .. code-block:: python + + out = ((image1 and image2) % MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_and(image2.im)) + + +def logical_or(image1, image2): + """Logical OR between two images. + + .. code-block:: python + + out = ((image1 or image2) % MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_or(image2.im)) + + +def logical_xor(image1, image2): + """Logical XOR between two images. + + .. code-block:: python + + out = ((bool(image1) != bool(image2)) % MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_xor(image2.im)) + + +def blend(image1, image2, alpha): + """Blend images using constant transparency weight. Alias for + :py:meth:`PIL.Image.Image.blend`. + + :rtype: :py:class:`~PIL.Image.Image` + """ + + return Image.blend(image1, image2, alpha) + + +def composite(image1, image2, mask): + """Create composite using transparency mask. Alias for + :py:meth:`PIL.Image.Image.composite`. + + :rtype: :py:class:`~PIL.Image.Image` + """ + + return Image.composite(image1, image2, mask) + + +def offset(image, xoffset, yoffset=None): + """Returns a copy of the image where data has been offset by the given + distances. Data wraps around the edges. If **yoffset** is omitted, it + is assumed to be equal to **xoffset**. + + :param xoffset: The horizontal distance. + :param yoffset: The vertical distance. If omitted, both + distances are set to the same value. + :rtype: :py:class:`~PIL.Image.Image` + """ + + if yoffset is None: + yoffset = xoffset + image.load() + return image._new(image.im.offset(xoffset, yoffset)) diff --git a/pyPackages/pillowx86/PIL/ImageCms.py b/pyPackages/pillowx86/PIL/ImageCms.py new file mode 100644 index 0000000..ed219f7 --- /dev/null +++ b/pyPackages/pillowx86/PIL/ImageCms.py @@ -0,0 +1,972 @@ +# The Python Imaging Library. +# $Id$ + +# Optional color managment support, based on Kevin Cazabon's PyCMS +# library. + +# History: + +# 2009-03-08 fl Added to PIL. + +# Copyright (C) 2002-2003 Kevin Cazabon +# Copyright (c) 2009 by Fredrik Lundh +# Copyright (c) 2013 by Eric Soroos + +# See the README file for information on usage and redistribution. See +# below for the original description. + +from __future__ import print_function + +DESCRIPTION = """ +pyCMS + + a Python / PIL interface to the littleCMS ICC Color Management System + Copyright (C) 2002-2003 Kevin Cazabon + kevin@cazabon.com + http://www.cazabon.com + + pyCMS home page: http://www.cazabon.com/pyCMS + littleCMS home page: http://www.littlecms.com + (littleCMS is Copyright (C) 1998-2001 Marti Maria) + + Originally released under LGPL. Graciously donated to PIL in + March 2009, for distribution under the standard PIL license + + The pyCMS.py module provides a "clean" interface between Python/PIL and + pyCMSdll, taking care of some of the more complex handling of the direct + pyCMSdll functions, as well as error-checking and making sure that all + relevant data is kept together. + + While it is possible to call pyCMSdll functions directly, it's not highly + recommended. + + Version History: + + 1.0.0 pil Oct 2013 Port to LCMS 2. + + 0.1.0 pil mod March 10, 2009 + + Renamed display profile to proof profile. The proof + profile is the profile of the device that is being + simulated, not the profile of the device which is + actually used to display/print the final simulation + (that'd be the output profile) - also see LCMSAPI.txt + input colorspace -> using 'renderingIntent' -> proof + colorspace -> using 'proofRenderingIntent' -> output + colorspace + + Added LCMS FLAGS support. + Added FLAGS["SOFTPROOFING"] as default flag for + buildProofTransform (otherwise the proof profile/intent + would be ignored). + + 0.1.0 pil March 2009 - added to PIL, as PIL.ImageCms + + 0.0.2 alpha Jan 6, 2002 + + Added try/except statements arount type() checks of + potential CObjects... Python won't let you use type() + on them, and raises a TypeError (stupid, if you ask + me!) + + Added buildProofTransformFromOpenProfiles() function. + Additional fixes in DLL, see DLL code for details. + + 0.0.1 alpha first public release, Dec. 26, 2002 + + Known to-do list with current version (of Python interface, not pyCMSdll): + + none + +""" + +VERSION = "1.0.0 pil" + +# --------------------------------------------------------------------. + +from PIL import Image +try: + from PIL import _imagingcms +except ImportError as ex: + # Allow error import for doc purposes, but error out when accessing + # anything in core. + from _util import deferred_error + _imagingcms = deferred_error(ex) +from PIL._util import isStringType + +core = _imagingcms + +# +# intent/direction values + +INTENT_PERCEPTUAL = 0 +INTENT_RELATIVE_COLORIMETRIC = 1 +INTENT_SATURATION = 2 +INTENT_ABSOLUTE_COLORIMETRIC = 3 + +DIRECTION_INPUT = 0 +DIRECTION_OUTPUT = 1 +DIRECTION_PROOF = 2 + +# +# flags + +FLAGS = { + "MATRIXINPUT": 1, + "MATRIXOUTPUT": 2, + "MATRIXONLY": (1 | 2), + "NOWHITEONWHITEFIXUP": 4, # Don't hot fix scum dot + # Don't create prelinearization tables on precalculated transforms + # (internal use): + "NOPRELINEARIZATION": 16, + "GUESSDEVICECLASS": 32, # Guess device class (for transform2devicelink) + "NOTCACHE": 64, # Inhibit 1-pixel cache + "NOTPRECALC": 256, + "NULLTRANSFORM": 512, # Don't transform anyway + "HIGHRESPRECALC": 1024, # Use more memory to give better accurancy + "LOWRESPRECALC": 2048, # Use less memory to minimize resouces + "WHITEBLACKCOMPENSATION": 8192, + "BLACKPOINTCOMPENSATION": 8192, + "GAMUTCHECK": 4096, # Out of Gamut alarm + "SOFTPROOFING": 16384, # Do softproofing + "PRESERVEBLACK": 32768, # Black preservation + "NODEFAULTRESOURCEDEF": 16777216, # CRD special + "GRIDPOINTS": lambda n: ((n) & 0xFF) << 16 # Gridpoints +} + +_MAX_FLAG = 0 +for flag in FLAGS.values(): + if isinstance(flag, int): + _MAX_FLAG = _MAX_FLAG | flag + + +# --------------------------------------------------------------------. +# Experimental PIL-level API +# --------------------------------------------------------------------. + +## +# Profile. + +class ImageCmsProfile: + + def __init__(self, profile): + """ + :param profile: Either a string representing a filename, + a file like object containing a profile or a + low-level profile object + + """ + + if isStringType(profile): + self._set(core.profile_open(profile), profile) + elif hasattr(profile, "read"): + self._set(core.profile_frombytes(profile.read())) + else: + self._set(profile) # assume it's already a profile + + def _set(self, profile, filename=None): + self.profile = profile + self.filename = filename + if profile: + self.product_name = None # profile.product_name + self.product_info = None # profile.product_info + else: + self.product_name = None + self.product_info = None + + def tobytes(self): + """ + Returns the profile in a format suitable for embedding in + saved images. + + :returns: a bytes object containing the ICC profile. + """ + + return core.profile_tobytes(self.profile) + + +class ImageCmsTransform(Image.ImagePointHandler): + + # Transform. This can be used with the procedural API, or with the + # standard Image.point() method. + # + # Will return the output profile in the output.info['icc_profile']. + + def __init__(self, input, output, input_mode, output_mode, + intent=INTENT_PERCEPTUAL, proof=None, + proof_intent=INTENT_ABSOLUTE_COLORIMETRIC, flags=0): + if proof is None: + self.transform = core.buildTransform( + input.profile, output.profile, + input_mode, output_mode, + intent, + flags + ) + else: + self.transform = core.buildProofTransform( + input.profile, output.profile, proof.profile, + input_mode, output_mode, + intent, proof_intent, + flags + ) + # Note: inputMode and outputMode are for pyCMS compatibility only + self.input_mode = self.inputMode = input_mode + self.output_mode = self.outputMode = output_mode + + self.output_profile = output + + def point(self, im): + return self.apply(im) + + def apply(self, im, imOut=None): + im.load() + if imOut is None: + imOut = Image.new(self.output_mode, im.size, None) + self.transform.apply(im.im.id, imOut.im.id) + imOut.info['icc_profile'] = self.output_profile.tobytes() + return imOut + + def apply_in_place(self, im): + im.load() + if im.mode != self.output_mode: + raise ValueError("mode mismatch") # wrong output mode + self.transform.apply(im.im.id, im.im.id) + im.info['icc_profile'] = self.output_profile.tobytes() + return im + + +def get_display_profile(handle=None): + """ (experimental) Fetches the profile for the current display device. + :returns: None if the profile is not known. + """ + + import sys + if sys.platform == "win32": + from PIL import ImageWin + if isinstance(handle, ImageWin.HDC): + profile = core.get_display_profile_win32(handle, 1) + else: + profile = core.get_display_profile_win32(handle or 0) + else: + try: + get = _imagingcms.get_display_profile + except AttributeError: + return None + else: + profile = get() + return ImageCmsProfile(profile) + + +# --------------------------------------------------------------------. +# pyCMS compatible layer +# --------------------------------------------------------------------. + +class PyCMSError(Exception): + + """ (pyCMS) Exception class. + This is used for all errors in the pyCMS API. """ + pass + + +def profileToProfile( + im, inputProfile, outputProfile, renderingIntent=INTENT_PERCEPTUAL, + outputMode=None, inPlace=0, flags=0): + """ + (pyCMS) Applies an ICC transformation to a given image, mapping from + inputProfile to outputProfile. + + If the input or output profiles specified are not valid filenames, a + PyCMSError will be raised. If inPlace == TRUE and outputMode != im.mode, + a PyCMSError will be raised. If an error occurs during application of + the profiles, a PyCMSError will be raised. If outputMode is not a mode + supported by the outputProfile (or by pyCMS), a PyCMSError will be + raised. + + This function applies an ICC transformation to im from inputProfile's + color space to outputProfile's color space using the specified rendering + intent to decide how to handle out-of-gamut colors. + + OutputMode can be used to specify that a color mode conversion is to + be done using these profiles, but the specified profiles must be able + to handle that mode. I.e., if converting im from RGB to CMYK using + profiles, the input profile must handle RGB data, and the output + profile must handle CMYK data. + + :param im: An open PIL image object (i.e. Image.new(...) or + Image.open(...), etc.) + :param inputProfile: String, as a valid filename path to the ICC input + profile you wish to use for this image, or a profile object + :param outputProfile: String, as a valid filename path to the ICC output + profile you wish to use for this image, or a profile object + :param renderingIntent: Integer (0-3) specifying the rendering intent you + wish to use for the transform + + INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) + INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) + INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) + INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) + + see the pyCMS documentation for details on rendering intents and what + they do. + :param outputMode: A valid PIL mode for the output image (i.e. "RGB", + "CMYK", etc.). Note: if rendering the image "inPlace", outputMode + MUST be the same mode as the input, or omitted completely. If + omitted, the outputMode will be the same as the mode of the input + image (im.mode) + :param inPlace: Boolean (1 = True, None or 0 = False). If True, the + original image is modified in-place, and None is returned. If False + (default), a new Image object is returned with the transform applied. + :param flags: Integer (0-...) specifying additional flags + :returns: Either None or a new PIL image object, depending on value of + inPlace + :exception PyCMSError: + """ + + if outputMode is None: + outputMode = im.mode + + if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3): + raise PyCMSError("renderingIntent must be an integer between 0 and 3") + + if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): + raise PyCMSError( + "flags must be an integer between 0 and %s" + _MAX_FLAG) + + try: + if not isinstance(inputProfile, ImageCmsProfile): + inputProfile = ImageCmsProfile(inputProfile) + if not isinstance(outputProfile, ImageCmsProfile): + outputProfile = ImageCmsProfile(outputProfile) + transform = ImageCmsTransform( + inputProfile, outputProfile, im.mode, outputMode, + renderingIntent, flags=flags + ) + if inPlace: + transform.apply_in_place(im) + imOut = None + else: + imOut = transform.apply(im) + except (IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + + return imOut + + +def getOpenProfile(profileFilename): + """ + (pyCMS) Opens an ICC profile file. + + The PyCMSProfile object can be passed back into pyCMS for use in creating + transforms and such (as in ImageCms.buildTransformFromOpenProfiles()). + + If profileFilename is not a vaild filename for an ICC profile, a PyCMSError + will be raised. + + :param profileFilename: String, as a valid filename path to the ICC profile + you wish to open, or a file-like object. + :returns: A CmsProfile class object. + :exception PyCMSError: + """ + + try: + return ImageCmsProfile(profileFilename) + except (IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + + +def buildTransform( + inputProfile, outputProfile, inMode, outMode, + renderingIntent=INTENT_PERCEPTUAL, flags=0): + """ + (pyCMS) Builds an ICC transform mapping from the inputProfile to the + outputProfile. Use applyTransform to apply the transform to a given + image. + + If the input or output profiles specified are not valid filenames, a + PyCMSError will be raised. If an error occurs during creation of the + transform, a PyCMSError will be raised. + + If inMode or outMode are not a mode supported by the outputProfile (or + by pyCMS), a PyCMSError will be raised. + + This function builds and returns an ICC transform from the inputProfile + to the outputProfile using the renderingIntent to determine what to do + with out-of-gamut colors. It will ONLY work for converting images that + are in inMode to images that are in outMode color format (PIL mode, + i.e. "RGB", "RGBA", "CMYK", etc.). + + Building the transform is a fair part of the overhead in + ImageCms.profileToProfile(), so if you're planning on converting multiple + images using the same input/output settings, this can save you time. + Once you have a transform object, it can be used with + ImageCms.applyProfile() to convert images without the need to re-compute + the lookup table for the transform. + + The reason pyCMS returns a class object rather than a handle directly + to the transform is that it needs to keep track of the PIL input/output + modes that the transform is meant for. These attributes are stored in + the "inMode" and "outMode" attributes of the object (which can be + manually overridden if you really want to, but I don't know of any + time that would be of use, or would even work). + + :param inputProfile: String, as a valid filename path to the ICC input + profile you wish to use for this transform, or a profile object + :param outputProfile: String, as a valid filename path to the ICC output + profile you wish to use for this transform, or a profile object + :param inMode: String, as a valid PIL mode that the appropriate profile + also supports (i.e. "RGB", "RGBA", "CMYK", etc.) + :param outMode: String, as a valid PIL mode that the appropriate profile + also supports (i.e. "RGB", "RGBA", "CMYK", etc.) + :param renderingIntent: Integer (0-3) specifying the rendering intent you + wish to use for the transform + + INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) + INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) + INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) + INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) + + see the pyCMS documentation for details on rendering intents and what + they do. + :param flags: Integer (0-...) specifying additional flags + :returns: A CmsTransform class object. + :exception PyCMSError: + """ + + if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3): + raise PyCMSError("renderingIntent must be an integer between 0 and 3") + + if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): + raise PyCMSError( + "flags must be an integer between 0 and %s" + _MAX_FLAG) + + try: + if not isinstance(inputProfile, ImageCmsProfile): + inputProfile = ImageCmsProfile(inputProfile) + if not isinstance(outputProfile, ImageCmsProfile): + outputProfile = ImageCmsProfile(outputProfile) + return ImageCmsTransform( + inputProfile, outputProfile, inMode, outMode, + renderingIntent, flags=flags) + except (IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + + +def buildProofTransform( + inputProfile, outputProfile, proofProfile, inMode, outMode, + renderingIntent=INTENT_PERCEPTUAL, + proofRenderingIntent=INTENT_ABSOLUTE_COLORIMETRIC, + flags=FLAGS["SOFTPROOFING"]): + """ + (pyCMS) Builds an ICC transform mapping from the inputProfile to the + outputProfile, but tries to simulate the result that would be + obtained on the proofProfile device. + + If the input, output, or proof profiles specified are not valid + filenames, a PyCMSError will be raised. + + If an error occurs during creation of the transform, a PyCMSError will + be raised. + + If inMode or outMode are not a mode supported by the outputProfile + (or by pyCMS), a PyCMSError will be raised. + + This function builds and returns an ICC transform from the inputProfile + to the outputProfile, but tries to simulate the result that would be + obtained on the proofProfile device using renderingIntent and + proofRenderingIntent to determine what to do with out-of-gamut + colors. This is known as "soft-proofing". It will ONLY work for + converting images that are in inMode to images that are in outMode + color format (PIL mode, i.e. "RGB", "RGBA", "CMYK", etc.). + + Usage of the resulting transform object is exactly the same as with + ImageCms.buildTransform(). + + Proof profiling is generally used when using an output device to get a + good idea of what the final printed/displayed image would look like on + the proofProfile device when it's quicker and easier to use the + output device for judging color. Generally, this means that the + output device is a monitor, or a dye-sub printer (etc.), and the simulated + device is something more expensive, complicated, or time consuming + (making it difficult to make a real print for color judgement purposes). + + Soft-proofing basically functions by adjusting the colors on the + output device to match the colors of the device being simulated. However, + when the simulated device has a much wider gamut than the output + device, you may obtain marginal results. + + :param inputProfile: String, as a valid filename path to the ICC input + profile you wish to use for this transform, or a profile object + :param outputProfile: String, as a valid filename path to the ICC output + (monitor, usually) profile you wish to use for this transform, or a + profile object + :param proofProfile: String, as a valid filename path to the ICC proof + profile you wish to use for this transform, or a profile object + :param inMode: String, as a valid PIL mode that the appropriate profile + also supports (i.e. "RGB", "RGBA", "CMYK", etc.) + :param outMode: String, as a valid PIL mode that the appropriate profile + also supports (i.e. "RGB", "RGBA", "CMYK", etc.) + :param renderingIntent: Integer (0-3) specifying the rendering intent you + wish to use for the input->proof (simulated) transform + + INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) + INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) + INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) + INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) + + see the pyCMS documentation for details on rendering intents and what + they do. + :param proofRenderingIntent: Integer (0-3) specifying the rendering intent you + wish to use for proof->output transform + + INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) + INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) + INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) + INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) + + see the pyCMS documentation for details on rendering intents and what + they do. + :param flags: Integer (0-...) specifying additional flags + :returns: A CmsTransform class object. + :exception PyCMSError: + """ + + if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3): + raise PyCMSError("renderingIntent must be an integer between 0 and 3") + + if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): + raise PyCMSError( + "flags must be an integer between 0 and %s" + _MAX_FLAG) + + try: + if not isinstance(inputProfile, ImageCmsProfile): + inputProfile = ImageCmsProfile(inputProfile) + if not isinstance(outputProfile, ImageCmsProfile): + outputProfile = ImageCmsProfile(outputProfile) + if not isinstance(proofProfile, ImageCmsProfile): + proofProfile = ImageCmsProfile(proofProfile) + return ImageCmsTransform( + inputProfile, outputProfile, inMode, outMode, renderingIntent, + proofProfile, proofRenderingIntent, flags) + except (IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + +buildTransformFromOpenProfiles = buildTransform +buildProofTransformFromOpenProfiles = buildProofTransform + + +def applyTransform(im, transform, inPlace=0): + """ + (pyCMS) Applies a transform to a given image. + + If im.mode != transform.inMode, a PyCMSError is raised. + + If inPlace == TRUE and transform.inMode != transform.outMode, a + PyCMSError is raised. + + If im.mode, transfer.inMode, or transfer.outMode is not supported by + pyCMSdll or the profiles you used for the transform, a PyCMSError is + raised. + + If an error occurs while the transform is being applied, a PyCMSError + is raised. + + This function applies a pre-calculated transform (from + ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles()) + to an image. The transform can be used for multiple images, saving + considerable calcuation time if doing the same conversion multiple times. + + If you want to modify im in-place instead of receiving a new image as + the return value, set inPlace to TRUE. This can only be done if + transform.inMode and transform.outMode are the same, because we can't + change the mode in-place (the buffer sizes for some modes are + different). The default behavior is to return a new Image object of + the same dimensions in mode transform.outMode. + + :param im: A PIL Image object, and im.mode must be the same as the inMode + supported by the transform. + :param transform: A valid CmsTransform class object + :param inPlace: Bool (1 == True, 0 or None == False). If True, im is + modified in place and None is returned, if False, a new Image object + with the transform applied is returned (and im is not changed). The + default is False. + :returns: Either None, or a new PIL Image object, depending on the value of + inPlace. The profile will be returned in the image's info['icc_profile']. + :exception PyCMSError: + """ + + try: + if inPlace: + transform.apply_in_place(im) + imOut = None + else: + imOut = transform.apply(im) + except (TypeError, ValueError) as v: + raise PyCMSError(v) + + return imOut + + +def createProfile(colorSpace, colorTemp=-1): + """ + (pyCMS) Creates a profile. + + If colorSpace not in ["LAB", "XYZ", "sRGB"], a PyCMSError is raised + + If using LAB and colorTemp != a positive integer, a PyCMSError is raised. + + If an error occurs while creating the profile, a PyCMSError is raised. + + Use this function to create common profiles on-the-fly instead of + having to supply a profile on disk and knowing the path to it. It + returns a normal CmsProfile object that can be passed to + ImageCms.buildTransformFromOpenProfiles() to create a transform to apply + to images. + + :param colorSpace: String, the color space of the profile you wish to + create. + Currently only "LAB", "XYZ", and "sRGB" are supported. + :param colorTemp: Positive integer for the white point for the profile, in + degrees Kelvin (i.e. 5000, 6500, 9600, etc.). The default is for D50 + illuminant if omitted (5000k). colorTemp is ONLY applied to LAB + profiles, and is ignored for XYZ and sRGB. + :returns: A CmsProfile class object + :exception PyCMSError: + """ + + if colorSpace not in ["LAB", "XYZ", "sRGB"]: + raise PyCMSError( + "Color space not supported for on-the-fly profile creation (%s)" + % colorSpace) + + if colorSpace == "LAB": + try: + colorTemp = float(colorTemp) + except: + raise PyCMSError( + "Color temperature must be numeric, \"%s\" not valid" + % colorTemp) + + try: + return core.createProfile(colorSpace, colorTemp) + except (TypeError, ValueError) as v: + raise PyCMSError(v) + + +def getProfileName(profile): + """ + + (pyCMS) Gets the internal product name for the given profile. + + If profile isn't a valid CmsProfile object or filename to a profile, + a PyCMSError is raised If an error occurs while trying to obtain the + name tag, a PyCMSError is raised. + + Use this function to obtain the INTERNAL name of the profile (stored + in an ICC tag in the profile itself), usually the one used when the + profile was originally created. Sometimes this tag also contains + additional information supplied by the creator. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: A string containing the internal name of the profile as stored + in an ICC tag. + :exception PyCMSError: + """ + + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + # do it in python, not c. + # // name was "%s - %s" (model, manufacturer) || Description , + # // but if the Model and Manufacturer were the same or the model + # // was long, Just the model, in 1.x + model = profile.profile.product_model + manufacturer = profile.profile.product_manufacturer + + if not (model or manufacturer): + return profile.profile.product_description + "\n" + if not manufacturer or len(model) > 30: + return model + "\n" + return "%s - %s\n" % (model, manufacturer) + + except (AttributeError, IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + + +def getProfileInfo(profile): + """ + (pyCMS) Gets the internal product information for the given profile. + + If profile isn't a valid CmsProfile object or filename to a profile, + a PyCMSError is raised. + + If an error occurs while trying to obtain the info tag, a PyCMSError + is raised + + Use this function to obtain the information stored in the profile's + info tag. This often contains details about the profile, and how it + was created, as supplied by the creator. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: A string containing the internal profile information stored in + an ICC tag. + :exception PyCMSError: + """ + + try: + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + # add an extra newline to preserve pyCMS compatibility + # Python, not C. the white point bits weren't working well, + # so skipping. + # // info was description \r\n\r\n copyright \r\n\r\n K007 tag \r\n\r\n whitepoint + description = profile.profile.product_description + cpright = profile.profile.product_copyright + arr = [] + for elt in (description, cpright): + if elt: + arr.append(elt) + return "\r\n\r\n".join(arr) + "\r\n\r\n" + + except (AttributeError, IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + + +def getProfileCopyright(profile): + """ + (pyCMS) Gets the copyright for the given profile. + + If profile isn't a valid CmsProfile object or filename to a profile, + a PyCMSError is raised. + + If an error occurs while trying to obtain the copyright tag, a PyCMSError + is raised + + Use this function to obtain the information stored in the profile's + copyright tag. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: A string containing the internal profile information stored in + an ICC tag. + :exception PyCMSError: + """ + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return profile.profile.product_copyright + "\n" + except (AttributeError, IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + + +def getProfileManufacturer(profile): + """ + (pyCMS) Gets the manufacturer for the given profile. + + If profile isn't a valid CmsProfile object or filename to a profile, + a PyCMSError is raised. + + If an error occurs while trying to obtain the manufacturer tag, a + PyCMSError is raised + + Use this function to obtain the information stored in the profile's + manufacturer tag. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: A string containing the internal profile information stored in + an ICC tag. + :exception PyCMSError: + """ + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return profile.profile.product_manufacturer + "\n" + except (AttributeError, IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + + +def getProfileModel(profile): + """ + (pyCMS) Gets the model for the given profile. + + If profile isn't a valid CmsProfile object or filename to a profile, + a PyCMSError is raised. + + If an error occurs while trying to obtain the model tag, a PyCMSError + is raised + + Use this function to obtain the information stored in the profile's + model tag. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: A string containing the internal profile information stored in + an ICC tag. + :exception PyCMSError: + """ + + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return profile.profile.product_model + "\n" + except (AttributeError, IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + + +def getProfileDescription(profile): + """ + (pyCMS) Gets the description for the given profile. + + If profile isn't a valid CmsProfile object or filename to a profile, + a PyCMSError is raised. + + If an error occurs while trying to obtain the description tag, a PyCMSError + is raised + + Use this function to obtain the information stored in the profile's + description tag. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: A string containing the internal profile information stored in an + ICC tag. + :exception PyCMSError: + """ + + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return profile.profile.product_description + "\n" + except (AttributeError, IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + + +def getDefaultIntent(profile): + """ + (pyCMS) Gets the default intent name for the given profile. + + If profile isn't a valid CmsProfile object or filename to a profile, + a PyCMSError is raised. + + If an error occurs while trying to obtain the default intent, a + PyCMSError is raised. + + Use this function to determine the default (and usually best optomized) + rendering intent for this profile. Most profiles support multiple + rendering intents, but are intended mostly for one type of conversion. + If you wish to use a different intent than returned, use + ImageCms.isIntentSupported() to verify it will work first. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: Integer 0-3 specifying the default rendering intent for this + profile. + + INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) + INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) + INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) + INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) + + see the pyCMS documentation for details on rendering intents and what + they do. + :exception PyCMSError: + """ + + try: + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return profile.profile.rendering_intent + except (AttributeError, IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + + +def isIntentSupported(profile, intent, direction): + """ + (pyCMS) Checks if a given intent is supported. + + Use this function to verify that you can use your desired + renderingIntent with profile, and that profile can be used for the + input/output/proof profile as you desire. + + Some profiles are created specifically for one "direction", can cannot + be used for others. Some profiles can only be used for certain + rendering intents... so it's best to either verify this before trying + to create a transform with them (using this function), or catch the + potential PyCMSError that will occur if they don't support the modes + you select. + + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :param intent: Integer (0-3) specifying the rendering intent you wish to + use with this profile + + INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) + INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) + INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) + INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) + + see the pyCMS documentation for details on rendering intents and what + they do. + :param direction: Integer specifing if the profile is to be used for input, + output, or proof + + INPUT = 0 (or use ImageCms.DIRECTION_INPUT) + OUTPUT = 1 (or use ImageCms.DIRECTION_OUTPUT) + PROOF = 2 (or use ImageCms.DIRECTION_PROOF) + + :returns: 1 if the intent/direction are supported, -1 if they are not. + :exception PyCMSError: + """ + + try: + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + # FIXME: I get different results for the same data w. different + # compilers. Bug in LittleCMS or in the binding? + if profile.profile.is_intent_supported(intent, direction): + return 1 + else: + return -1 + except (AttributeError, IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + + +def versions(): + """ + (pyCMS) Fetches versions. + """ + + import sys + return ( + VERSION, core.littlecms_version, + sys.version.split()[0], Image.VERSION + ) + +# -------------------------------------------------------------------- + +if __name__ == "__main__": + # create a cheap manual from the __doc__ strings for the functions above + + from PIL import ImageCms + print(__doc__) + + for f in dir(ImageCms): + doc = None + try: + exec("doc = %s.__doc__" % (f)) + if "pyCMS" in doc: + # so we don't get the __doc__ string for imported modules + print("=" * 80) + print("%s" % f) + print(doc) + except (AttributeError, TypeError): + pass + +# End of file diff --git a/pyPackages/pillowx86/PIL/ImageColor.py b/pyPackages/pillowx86/PIL/ImageColor.py new file mode 100644 index 0000000..fc95e6d --- /dev/null +++ b/pyPackages/pillowx86/PIL/ImageColor.py @@ -0,0 +1,279 @@ +# +# The Python Imaging Library +# $Id$ +# +# map CSS3-style colour description strings to RGB +# +# History: +# 2002-10-24 fl Added support for CSS-style color strings +# 2002-12-15 fl Added RGBA support +# 2004-03-27 fl Fixed remaining int() problems for Python 1.5.2 +# 2004-07-19 fl Fixed gray/grey spelling issues +# 2009-03-05 fl Fixed rounding error in grayscale calculation +# +# Copyright (c) 2002-2004 by Secret Labs AB +# Copyright (c) 2002-2004 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image +import re + + +def getrgb(color): + """ + Convert a color string to an RGB tuple. If the string cannot be parsed, + this function raises a :py:exc:`ValueError` exception. + + .. versionadded:: 1.1.4 + + :param color: A color string + :return: ``(red, green, blue[, alpha])`` + """ + try: + rgb = colormap[color] + except KeyError: + try: + # fall back on case-insensitive lookup + rgb = colormap[color.lower()] + except KeyError: + rgb = None + # found color in cache + if rgb: + if isinstance(rgb, tuple): + return rgb + colormap[color] = rgb = getrgb(rgb) + return rgb + # check for known string formats + m = re.match("#\w\w\w$", color) + if m: + return ( + int(color[1]*2, 16), + int(color[2]*2, 16), + int(color[3]*2, 16) + ) + m = re.match("#\w\w\w\w\w\w$", color) + if m: + return ( + int(color[1:3], 16), + int(color[3:5], 16), + int(color[5:7], 16) + ) + m = re.match("rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color) + if m: + return ( + int(m.group(1)), + int(m.group(2)), + int(m.group(3)) + ) + m = re.match("rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color) + if m: + return ( + int((int(m.group(1)) * 255) / 100.0 + 0.5), + int((int(m.group(2)) * 255) / 100.0 + 0.5), + int((int(m.group(3)) * 255) / 100.0 + 0.5) + ) + m = re.match("hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color) + if m: + from colorsys import hls_to_rgb + rgb = hls_to_rgb( + float(m.group(1)) / 360.0, + float(m.group(3)) / 100.0, + float(m.group(2)) / 100.0, + ) + return ( + int(rgb[0] * 255 + 0.5), + int(rgb[1] * 255 + 0.5), + int(rgb[2] * 255 + 0.5) + ) + m = re.match("rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", + color) + if m: + return ( + int(m.group(1)), + int(m.group(2)), + int(m.group(3)), + int(m.group(4)) + ) + raise ValueError("unknown color specifier: %r" % color) + + +def getcolor(color, mode): + """ + Same as :py:func:`~PIL.ImageColor.getrgb`, but converts the RGB value to a + greyscale value if the mode is not color or a palette image. If the string + cannot be parsed, this function raises a :py:exc:`ValueError` exception. + + .. versionadded:: 1.1.4 + + :param color: A color string + :return: ``(graylevel [, alpha]) or (red, green, blue[, alpha])`` + """ + # same as getrgb, but converts the result to the given mode + color, alpha = getrgb(color), 255 + if len(color) == 4: + color, alpha = color[0:3], color[3] + + if Image.getmodebase(mode) == "L": + r, g, b = color + color = (r*299 + g*587 + b*114)//1000 + if mode[-1] == 'A': + return (color, alpha) + else: + if mode[-1] == 'A': + return color + (alpha,) + return color + +colormap = { + # X11 colour table (from "CSS3 module: Color working draft"), with + # gray/grey spelling issues fixed. This is a superset of HTML 4.0 + # colour names used in CSS 1. + "aliceblue": "#f0f8ff", + "antiquewhite": "#faebd7", + "aqua": "#00ffff", + "aquamarine": "#7fffd4", + "azure": "#f0ffff", + "beige": "#f5f5dc", + "bisque": "#ffe4c4", + "black": "#000000", + "blanchedalmond": "#ffebcd", + "blue": "#0000ff", + "blueviolet": "#8a2be2", + "brown": "#a52a2a", + "burlywood": "#deb887", + "cadetblue": "#5f9ea0", + "chartreuse": "#7fff00", + "chocolate": "#d2691e", + "coral": "#ff7f50", + "cornflowerblue": "#6495ed", + "cornsilk": "#fff8dc", + "crimson": "#dc143c", + "cyan": "#00ffff", + "darkblue": "#00008b", + "darkcyan": "#008b8b", + "darkgoldenrod": "#b8860b", + "darkgray": "#a9a9a9", + "darkgrey": "#a9a9a9", + "darkgreen": "#006400", + "darkkhaki": "#bdb76b", + "darkmagenta": "#8b008b", + "darkolivegreen": "#556b2f", + "darkorange": "#ff8c00", + "darkorchid": "#9932cc", + "darkred": "#8b0000", + "darksalmon": "#e9967a", + "darkseagreen": "#8fbc8f", + "darkslateblue": "#483d8b", + "darkslategray": "#2f4f4f", + "darkslategrey": "#2f4f4f", + "darkturquoise": "#00ced1", + "darkviolet": "#9400d3", + "deeppink": "#ff1493", + "deepskyblue": "#00bfff", + "dimgray": "#696969", + "dimgrey": "#696969", + "dodgerblue": "#1e90ff", + "firebrick": "#b22222", + "floralwhite": "#fffaf0", + "forestgreen": "#228b22", + "fuchsia": "#ff00ff", + "gainsboro": "#dcdcdc", + "ghostwhite": "#f8f8ff", + "gold": "#ffd700", + "goldenrod": "#daa520", + "gray": "#808080", + "grey": "#808080", + "green": "#008000", + "greenyellow": "#adff2f", + "honeydew": "#f0fff0", + "hotpink": "#ff69b4", + "indianred": "#cd5c5c", + "indigo": "#4b0082", + "ivory": "#fffff0", + "khaki": "#f0e68c", + "lavender": "#e6e6fa", + "lavenderblush": "#fff0f5", + "lawngreen": "#7cfc00", + "lemonchiffon": "#fffacd", + "lightblue": "#add8e6", + "lightcoral": "#f08080", + "lightcyan": "#e0ffff", + "lightgoldenrodyellow": "#fafad2", + "lightgreen": "#90ee90", + "lightgray": "#d3d3d3", + "lightgrey": "#d3d3d3", + "lightpink": "#ffb6c1", + "lightsalmon": "#ffa07a", + "lightseagreen": "#20b2aa", + "lightskyblue": "#87cefa", + "lightslategray": "#778899", + "lightslategrey": "#778899", + "lightsteelblue": "#b0c4de", + "lightyellow": "#ffffe0", + "lime": "#00ff00", + "limegreen": "#32cd32", + "linen": "#faf0e6", + "magenta": "#ff00ff", + "maroon": "#800000", + "mediumaquamarine": "#66cdaa", + "mediumblue": "#0000cd", + "mediumorchid": "#ba55d3", + "mediumpurple": "#9370db", + "mediumseagreen": "#3cb371", + "mediumslateblue": "#7b68ee", + "mediumspringgreen": "#00fa9a", + "mediumturquoise": "#48d1cc", + "mediumvioletred": "#c71585", + "midnightblue": "#191970", + "mintcream": "#f5fffa", + "mistyrose": "#ffe4e1", + "moccasin": "#ffe4b5", + "navajowhite": "#ffdead", + "navy": "#000080", + "oldlace": "#fdf5e6", + "olive": "#808000", + "olivedrab": "#6b8e23", + "orange": "#ffa500", + "orangered": "#ff4500", + "orchid": "#da70d6", + "palegoldenrod": "#eee8aa", + "palegreen": "#98fb98", + "paleturquoise": "#afeeee", + "palevioletred": "#db7093", + "papayawhip": "#ffefd5", + "peachpuff": "#ffdab9", + "peru": "#cd853f", + "pink": "#ffc0cb", + "plum": "#dda0dd", + "powderblue": "#b0e0e6", + "purple": "#800080", + "red": "#ff0000", + "rosybrown": "#bc8f8f", + "royalblue": "#4169e1", + "saddlebrown": "#8b4513", + "salmon": "#fa8072", + "sandybrown": "#f4a460", + "seagreen": "#2e8b57", + "seashell": "#fff5ee", + "sienna": "#a0522d", + "silver": "#c0c0c0", + "skyblue": "#87ceeb", + "slateblue": "#6a5acd", + "slategray": "#708090", + "slategrey": "#708090", + "snow": "#fffafa", + "springgreen": "#00ff7f", + "steelblue": "#4682b4", + "tan": "#d2b48c", + "teal": "#008080", + "thistle": "#d8bfd8", + "tomato": "#ff6347", + "turquoise": "#40e0d0", + "violet": "#ee82ee", + "wheat": "#f5deb3", + "white": "#ffffff", + "whitesmoke": "#f5f5f5", + "yellow": "#ffff00", + "yellowgreen": "#9acd32", +} diff --git a/pyPackages/pillowx86/PIL/ImageDraw.py b/pyPackages/pillowx86/PIL/ImageDraw.py new file mode 100644 index 0000000..a2a75d1 --- /dev/null +++ b/pyPackages/pillowx86/PIL/ImageDraw.py @@ -0,0 +1,383 @@ +# +# The Python Imaging Library +# $Id$ +# +# drawing interface operations +# +# History: +# 1996-04-13 fl Created (experimental) +# 1996-08-07 fl Filled polygons, ellipses. +# 1996-08-13 fl Added text support +# 1998-06-28 fl Handle I and F images +# 1998-12-29 fl Added arc; use arc primitive to draw ellipses +# 1999-01-10 fl Added shape stuff (experimental) +# 1999-02-06 fl Added bitmap support +# 1999-02-11 fl Changed all primitives to take options +# 1999-02-20 fl Fixed backwards compatibility +# 2000-10-12 fl Copy on write, when necessary +# 2001-02-18 fl Use default ink for bitmap/text also in fill mode +# 2002-10-24 fl Added support for CSS-style color strings +# 2002-12-10 fl Added experimental support for RGBA-on-RGB drawing +# 2002-12-11 fl Refactored low-level drawing API (work in progress) +# 2004-08-26 fl Made Draw() a factory function, added getdraw() support +# 2004-09-04 fl Added width support to line primitive +# 2004-09-10 fl Added font mode handling +# 2006-06-19 fl Added font bearing support (getmask2) +# +# Copyright (c) 1997-2006 by Secret Labs AB +# Copyright (c) 1996-2006 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import numbers + +from PIL import Image, ImageColor +from PIL._util import isStringType + +try: + import warnings +except ImportError: + warnings = None + + +## +# A simple 2D drawing interface for PIL images. +#

+# Application code should use the Draw factory, instead of +# directly. + +class ImageDraw: + + ## + # Create a drawing instance. + # + # @param im The image to draw in. + # @param mode Optional mode to use for color values. For RGB + # images, this argument can be RGB or RGBA (to blend the + # drawing into the image). For all other modes, this argument + # must be the same as the image mode. If omitted, the mode + # defaults to the mode of the image. + + def __init__(self, im, mode=None): + im.load() + if im.readonly: + im._copy() # make it writeable + blend = 0 + if mode is None: + mode = im.mode + if mode != im.mode: + if mode == "RGBA" and im.mode == "RGB": + blend = 1 + else: + raise ValueError("mode mismatch") + if mode == "P": + self.palette = im.palette + else: + self.palette = None + self.im = im.im + self.draw = Image.core.draw(self.im, blend) + self.mode = mode + if mode in ("I", "F"): + self.ink = self.draw.draw_ink(1, mode) + else: + self.ink = self.draw.draw_ink(-1, mode) + if mode in ("1", "P", "I", "F"): + # FIXME: fix Fill2 to properly support matte for I+F images + self.fontmode = "1" + else: + self.fontmode = "L" # aliasing is okay for other modes + self.fill = 0 + self.font = None + + ## + # Set the default pen color. + + def setink(self, ink): + # compatibility + if warnings: + warnings.warn( + "'setink' is deprecated; use keyword arguments instead", + DeprecationWarning, stacklevel=2 + ) + if isStringType(ink): + ink = ImageColor.getcolor(ink, self.mode) + if self.palette and not isinstance(ink, numbers.Number): + ink = self.palette.getcolor(ink) + self.ink = self.draw.draw_ink(ink, self.mode) + + ## + # Set the default background color. + + def setfill(self, onoff): + # compatibility + if warnings: + warnings.warn( + "'setfill' is deprecated; use keyword arguments instead", + DeprecationWarning, stacklevel=2 + ) + self.fill = onoff + + ## + # Set the default font. + + def setfont(self, font): + # compatibility + self.font = font + + ## + # Get the current default font. + + def getfont(self): + if not self.font: + # FIXME: should add a font repository + from PIL import ImageFont + self.font = ImageFont.load_default() + return self.font + + def _getink(self, ink, fill=None): + if ink is None and fill is None: + if self.fill: + fill = self.ink + else: + ink = self.ink + else: + if ink is not None: + if isStringType(ink): + ink = ImageColor.getcolor(ink, self.mode) + if self.palette and not isinstance(ink, numbers.Number): + ink = self.palette.getcolor(ink) + ink = self.draw.draw_ink(ink, self.mode) + if fill is not None: + if isStringType(fill): + fill = ImageColor.getcolor(fill, self.mode) + if self.palette and not isinstance(fill, numbers.Number): + fill = self.palette.getcolor(fill) + fill = self.draw.draw_ink(fill, self.mode) + return ink, fill + + ## + # Draw an arc. + + def arc(self, xy, start, end, fill=None): + ink, fill = self._getink(fill) + if ink is not None: + self.draw.draw_arc(xy, start, end, ink) + + ## + # Draw a bitmap. + + def bitmap(self, xy, bitmap, fill=None): + bitmap.load() + ink, fill = self._getink(fill) + if ink is None: + ink = fill + if ink is not None: + self.draw.draw_bitmap(xy, bitmap.im, ink) + + ## + # Draw a chord. + + def chord(self, xy, start, end, fill=None, outline=None): + ink, fill = self._getink(outline, fill) + if fill is not None: + self.draw.draw_chord(xy, start, end, fill, 1) + if ink is not None: + self.draw.draw_chord(xy, start, end, ink, 0) + + ## + # Draw an ellipse. + + def ellipse(self, xy, fill=None, outline=None): + ink, fill = self._getink(outline, fill) + if fill is not None: + self.draw.draw_ellipse(xy, fill, 1) + if ink is not None: + self.draw.draw_ellipse(xy, ink, 0) + + ## + # Draw a line, or a connected sequence of line segments. + + def line(self, xy, fill=None, width=0): + ink, fill = self._getink(fill) + if ink is not None: + self.draw.draw_lines(xy, ink, width) + + ## + # (Experimental) Draw a shape. + + def shape(self, shape, fill=None, outline=None): + # experimental + shape.close() + ink, fill = self._getink(outline, fill) + if fill is not None: + self.draw.draw_outline(shape, fill, 1) + if ink is not None: + self.draw.draw_outline(shape, ink, 0) + + ## + # Draw a pieslice. + + def pieslice(self, xy, start, end, fill=None, outline=None): + ink, fill = self._getink(outline, fill) + if fill is not None: + self.draw.draw_pieslice(xy, start, end, fill, 1) + if ink is not None: + self.draw.draw_pieslice(xy, start, end, ink, 0) + + ## + # Draw one or more individual pixels. + + def point(self, xy, fill=None): + ink, fill = self._getink(fill) + if ink is not None: + self.draw.draw_points(xy, ink) + + ## + # Draw a polygon. + + def polygon(self, xy, fill=None, outline=None): + ink, fill = self._getink(outline, fill) + if fill is not None: + self.draw.draw_polygon(xy, fill, 1) + if ink is not None: + self.draw.draw_polygon(xy, ink, 0) + + ## + # Draw a rectangle. + + def rectangle(self, xy, fill=None, outline=None): + ink, fill = self._getink(outline, fill) + if fill is not None: + self.draw.draw_rectangle(xy, fill, 1) + if ink is not None: + self.draw.draw_rectangle(xy, ink, 0) + + ## + # Draw text. + + def text(self, xy, text, fill=None, font=None, anchor=None): + ink, fill = self._getink(fill) + if font is None: + font = self.getfont() + if ink is None: + ink = fill + if ink is not None: + try: + mask, offset = font.getmask2(text, self.fontmode) + xy = xy[0] + offset[0], xy[1] + offset[1] + except AttributeError: + try: + mask = font.getmask(text, self.fontmode) + except TypeError: + mask = font.getmask(text) + self.draw.draw_bitmap(xy, mask, ink) + + ## + # Get the size of a given string, in pixels. + + def textsize(self, text, font=None): + if font is None: + font = self.getfont() + return font.getsize(text) + + +## +# A simple 2D drawing interface for PIL images. +# +# @param im The image to draw in. +# @param mode Optional mode to use for color values. For RGB +# images, this argument can be RGB or RGBA (to blend the +# drawing into the image). For all other modes, this argument +# must be the same as the image mode. If omitted, the mode +# defaults to the mode of the image. + +def Draw(im, mode=None): + try: + return im.getdraw(mode) + except AttributeError: + return ImageDraw(im, mode) + +# experimental access to the outline API +try: + Outline = Image.core.outline +except: + Outline = None + + +## +# (Experimental) A more advanced 2D drawing interface for PIL images, +# based on the WCK interface. +# +# @param im The image to draw in. +# @param hints An optional list of hints. +# @return A (drawing context, drawing resource factory) tuple. + +def getdraw(im=None, hints=None): + # FIXME: this needs more work! + # FIXME: come up with a better 'hints' scheme. + handler = None + if not hints or "nicest" in hints: + try: + from PIL import _imagingagg as handler + except ImportError: + pass + if handler is None: + from PIL import ImageDraw2 as handler + if im: + im = handler.Draw(im) + return im, handler + + +## +# (experimental) Fills a bounded region with a given color. +# +# @param image Target image. +# @param xy Seed position (a 2-item coordinate tuple). +# @param value Fill color. +# @param border Optional border value. If given, the region consists of +# pixels with a color different from the border color. If not given, +# the region consists of pixels having the same color as the seed +# pixel. + +def floodfill(image, xy, value, border=None): + "Fill bounded region." + # based on an implementation by Eric S. Raymond + pixel = image.load() + x, y = xy + try: + background = pixel[x, y] + if background == value: + return # seed point already has fill color + pixel[x, y] = value + except IndexError: + return # seed point outside image + edge = [(x, y)] + if border is None: + while edge: + newedge = [] + for (x, y) in edge: + for (s, t) in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)): + try: + p = pixel[s, t] + except IndexError: + pass + else: + if p == background: + pixel[s, t] = value + newedge.append((s, t)) + edge = newedge + else: + while edge: + newedge = [] + for (x, y) in edge: + for (s, t) in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)): + try: + p = pixel[s, t] + except IndexError: + pass + else: + if p != value and p != border: + pixel[s, t] = value + newedge.append((s, t)) + edge = newedge diff --git a/pyPackages/pillowx86/PIL/ImageDraw2.py b/pyPackages/pillowx86/PIL/ImageDraw2.py new file mode 100644 index 0000000..c967a20 --- /dev/null +++ b/pyPackages/pillowx86/PIL/ImageDraw2.py @@ -0,0 +1,111 @@ +# +# The Python Imaging Library +# $Id$ +# +# WCK-style drawing interface operations +# +# History: +# 2003-12-07 fl created +# 2005-05-15 fl updated; added to PIL as ImageDraw2 +# 2005-05-15 fl added text support +# 2005-05-20 fl added arc/chord/pieslice support +# +# Copyright (c) 2003-2005 by Secret Labs AB +# Copyright (c) 2003-2005 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image, ImageColor, ImageDraw, ImageFont, ImagePath + + +class Pen: + def __init__(self, color, width=1, opacity=255): + self.color = ImageColor.getrgb(color) + self.width = width + + +class Brush: + def __init__(self, color, opacity=255): + self.color = ImageColor.getrgb(color) + + +class Font: + def __init__(self, color, file, size=12): + # FIXME: add support for bitmap fonts + self.color = ImageColor.getrgb(color) + self.font = ImageFont.truetype(file, size) + + +class Draw: + + def __init__(self, image, size=None, color=None): + if not hasattr(image, "im"): + image = Image.new(image, size, color) + self.draw = ImageDraw.Draw(image) + self.image = image + self.transform = None + + def flush(self): + return self.image + + def render(self, op, xy, pen, brush=None): + # handle color arguments + outline = fill = None + width = 1 + if isinstance(pen, Pen): + outline = pen.color + width = pen.width + elif isinstance(brush, Pen): + outline = brush.color + width = brush.width + if isinstance(brush, Brush): + fill = brush.color + elif isinstance(pen, Brush): + fill = pen.color + # handle transformation + if self.transform: + xy = ImagePath.Path(xy) + xy.transform(self.transform) + # render the item + if op == "line": + self.draw.line(xy, fill=outline, width=width) + else: + getattr(self.draw, op)(xy, fill=fill, outline=outline) + + def settransform(self, offset): + (xoffset, yoffset) = offset + self.transform = (1, 0, xoffset, 0, 1, yoffset) + + def arc(self, xy, start, end, *options): + self.render("arc", xy, start, end, *options) + + def chord(self, xy, start, end, *options): + self.render("chord", xy, start, end, *options) + + def ellipse(self, xy, *options): + self.render("ellipse", xy, *options) + + def line(self, xy, *options): + self.render("line", xy, *options) + + def pieslice(self, xy, start, end, *options): + self.render("pieslice", xy, start, end, *options) + + def polygon(self, xy, *options): + self.render("polygon", xy, *options) + + def rectangle(self, xy, *options): + self.render("rectangle", xy, *options) + + def symbol(self, xy, symbol, *options): + raise NotImplementedError("not in this version") + + def text(self, xy, text, font): + if self.transform: + xy = ImagePath.Path(xy) + xy.transform(self.transform) + self.draw.text(xy, text, font=font.font, fill=font.color) + + def textsize(self, text, font): + return self.draw.textsize(text, font=font.font) diff --git a/pyPackages/pillowx86/PIL/ImageEnhance.py b/pyPackages/pillowx86/PIL/ImageEnhance.py new file mode 100644 index 0000000..a196d5b --- /dev/null +++ b/pyPackages/pillowx86/PIL/ImageEnhance.py @@ -0,0 +1,99 @@ +# +# The Python Imaging Library. +# $Id$ +# +# image enhancement classes +# +# For a background, see "Image Processing By Interpolation and +# Extrapolation", Paul Haeberli and Douglas Voorhies. Available +# at http://www.graficaobscura.com/interp/index.html +# +# History: +# 1996-03-23 fl Created +# 2009-06-16 fl Fixed mean calculation +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image, ImageFilter, ImageStat + + +class _Enhance: + + def enhance(self, factor): + """ + Returns an enhanced image. + + :param factor: A floating point value controlling the enhancement. + Factor 1.0 always returns a copy of the original image, + lower factors mean less color (brightness, contrast, + etc), and higher values more. There are no restrictions + on this value. + :rtype: :py:class:`~PIL.Image.Image` + """ + return Image.blend(self.degenerate, self.image, factor) + + +class Color(_Enhance): + """Adjust image color balance. + + This class can be used to adjust the colour balance of an image, in + a manner similar to the controls on a colour TV set. An enhancement + factor of 0.0 gives a black and white image. A factor of 1.0 gives + the original image. + """ + def __init__(self, image): + self.image = image + self.intermediate_mode = 'L' + if 'A' in image.getbands(): + self.intermediate_mode = 'LA' + + self.degenerate = image.convert(self.intermediate_mode).convert(image.mode) + +class Contrast(_Enhance): + """Adjust image contrast. + + This class can be used to control the contrast of an image, similar + to the contrast control on a TV set. An enhancement factor of 0.0 + gives a solid grey image. A factor of 1.0 gives the original image. + """ + def __init__(self, image): + self.image = image + mean = int(ImageStat.Stat(image.convert("L")).mean[0] + 0.5) + self.degenerate = Image.new("L", image.size, mean).convert(image.mode) + + if 'A' in image.getbands(): + self.degenerate.putalpha(image.split()[-1]) + + +class Brightness(_Enhance): + """Adjust image brightness. + + This class can be used to control the brighntess of an image. An + enhancement factor of 0.0 gives a black image. A factor of 1.0 gives the + original image. + """ + def __init__(self, image): + self.image = image + self.degenerate = Image.new(image.mode, image.size, 0) + + if 'A' in image.getbands(): + self.degenerate.putalpha(image.split()[-1]) + + +class Sharpness(_Enhance): + """Adjust image sharpness. + + This class can be used to adjust the sharpness of an image. An + enhancement factor of 0.0 gives a blurred image, a factor of 1.0 gives the + original image, and a factor of 2.0 gives a sharpened image. + """ + def __init__(self, image): + self.image = image + self.degenerate = image.filter(ImageFilter.SMOOTH) + + if 'A' in image.getbands(): + self.degenerate.putalpha(image.split()[-1]) diff --git a/pyPackages/pillowx86/PIL/ImageFile.py b/pyPackages/pillowx86/PIL/ImageFile.py new file mode 100644 index 0000000..82691af --- /dev/null +++ b/pyPackages/pillowx86/PIL/ImageFile.py @@ -0,0 +1,523 @@ +# +# The Python Imaging Library. +# $Id$ +# +# base class for image file handlers +# +# history: +# 1995-09-09 fl Created +# 1996-03-11 fl Fixed load mechanism. +# 1996-04-15 fl Added pcx/xbm decoders. +# 1996-04-30 fl Added encoders. +# 1996-12-14 fl Added load helpers +# 1997-01-11 fl Use encode_to_file where possible +# 1997-08-27 fl Flush output in _save +# 1998-03-05 fl Use memory mapping for some modes +# 1999-02-04 fl Use memory mapping also for "I;16" and "I;16B" +# 1999-05-31 fl Added image parser +# 2000-10-12 fl Set readonly flag on memory-mapped images +# 2002-03-20 fl Use better messages for common decoder errors +# 2003-04-21 fl Fall back on mmap/map_buffer if map is not available +# 2003-10-30 fl Added StubImageFile class +# 2004-02-25 fl Made incremental parser more robust +# +# Copyright (c) 1997-2004 by Secret Labs AB +# Copyright (c) 1995-2004 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image +from PIL._util import isPath +import io +import os +import sys +import traceback + +MAXBLOCK = 65536 + +SAFEBLOCK = 1024*1024 + +LOAD_TRUNCATED_IMAGES = False + +ERRORS = { + -1: "image buffer overrun error", + -2: "decoding error", + -3: "unknown error", + -8: "bad configuration", + -9: "out of memory error" +} + + +def raise_ioerror(error): + try: + message = Image.core.getcodecstatus(error) + except AttributeError: + message = ERRORS.get(error) + if not message: + message = "decoder error %d" % error + raise IOError(message + " when reading image file") + + +# +# -------------------------------------------------------------------- +# Helpers + +def _tilesort(t): + # sort on offset + return t[2] + + +# +# -------------------------------------------------------------------- +# ImageFile base class + +class ImageFile(Image.Image): + "Base class for image file format handlers." + + def __init__(self, fp=None, filename=None): + Image.Image.__init__(self) + + self.tile = None + self.readonly = 1 # until we know better + + self.decoderconfig = () + self.decodermaxblock = MAXBLOCK + + if isPath(fp): + # filename + self.fp = open(fp, "rb") + self.filename = fp + else: + # stream + self.fp = fp + self.filename = filename + + try: + self._open() + except IndexError as v: # end of data + if Image.DEBUG > 1: + traceback.print_exc() + raise SyntaxError(v) + except TypeError as v: # end of data (ord) + if Image.DEBUG > 1: + traceback.print_exc() + raise SyntaxError(v) + except KeyError as v: # unsupported mode + if Image.DEBUG > 1: + traceback.print_exc() + raise SyntaxError(v) + except EOFError as v: # got header but not the first frame + if Image.DEBUG > 1: + traceback.print_exc() + raise SyntaxError(v) + + if not self.mode or self.size[0] <= 0: + raise SyntaxError("not identified by this driver") + + def draft(self, mode, size): + "Set draft mode" + + pass + + def verify(self): + "Check file integrity" + + # raise exception if something's wrong. must be called + # directly after open, and closes file when finished. + self.fp = None + + def load(self): + "Load image data based on tile list" + + pixel = Image.Image.load(self) + + if self.tile is None: + raise IOError("cannot load this image") + if not self.tile: + return pixel + + self.map = None + use_mmap = self.filename and len(self.tile) == 1 + # As of pypy 2.1.0, memory mapping was failing here. + use_mmap = use_mmap and not hasattr(sys, 'pypy_version_info') + + readonly = 0 + + # look for read/seek overrides + try: + read = self.load_read + # don't use mmap if there are custom read/seek functions + use_mmap = False + except AttributeError: + read = self.fp.read + + try: + seek = self.load_seek + use_mmap = False + except AttributeError: + seek = self.fp.seek + + if use_mmap: + # try memory mapping + d, e, o, a = self.tile[0] + if d == "raw" and a[0] == self.mode and a[0] in Image._MAPMODES: + try: + if hasattr(Image.core, "map"): + # use built-in mapper + self.map = Image.core.map(self.filename) + self.map.seek(o) + self.im = self.map.readimage( + self.mode, self.size, a[1], a[2] + ) + else: + # use mmap, if possible + import mmap + file = open(self.filename, "r+") + size = os.path.getsize(self.filename) + # FIXME: on Unix, use PROT_READ etc + self.map = mmap.mmap(file.fileno(), size) + self.im = Image.core.map_buffer( + self.map, self.size, d, e, o, a + ) + readonly = 1 + except (AttributeError, EnvironmentError, ImportError): + self.map = None + + self.load_prepare() + + if not self.map: + # sort tiles in file order + self.tile.sort(key=_tilesort) + + try: + # FIXME: This is a hack to handle TIFF's JpegTables tag. + prefix = self.tile_prefix + except AttributeError: + prefix = b"" + + for d, e, o, a in self.tile: + d = Image._getdecoder(self.mode, d, a, self.decoderconfig) + seek(o) + try: + d.setimage(self.im, e) + except ValueError: + continue + b = prefix + t = len(b) + while True: + try: + s = read(self.decodermaxblock) + except IndexError as ie: # truncated png/gif + if LOAD_TRUNCATED_IMAGES: + break + else: + raise IndexError(ie) + + if not s and not d.handles_eof: # truncated jpeg + self.tile = [] + + # JpegDecode needs to clean things up here either way + # If we don't destroy the decompressor, + # we have a memory leak. + d.cleanup() + + if LOAD_TRUNCATED_IMAGES: + break + else: + raise IOError("image file is truncated " + "(%d bytes not processed)" % len(b)) + + b = b + s + n, e = d.decode(b) + if n < 0: + break + b = b[n:] + t = t + n + # Need to cleanup here to prevent leaks in PyPy + d.cleanup() + + self.tile = [] + self.readonly = readonly + + self.fp = None # might be shared + + if not self.map and (not LOAD_TRUNCATED_IMAGES or t == 0) and e < 0: + # still raised if decoder fails to return anything + raise_ioerror(e) + + # post processing + if hasattr(self, "tile_post_rotate"): + # FIXME: This is a hack to handle rotated PCD's + self.im = self.im.rotate(self.tile_post_rotate) + self.size = self.im.size + + self.load_end() + + return Image.Image.load(self) + + def load_prepare(self): + # create image memory if necessary + if not self.im or\ + self.im.mode != self.mode or self.im.size != self.size: + self.im = Image.core.new(self.mode, self.size) + # create palette (optional) + if self.mode == "P": + Image.Image.load(self) + + def load_end(self): + # may be overridden + pass + + # may be defined for contained formats + # def load_seek(self, pos): + # pass + + # may be defined for blocked formats (e.g. PNG) + # def load_read(self, bytes): + # pass + + +class StubImageFile(ImageFile): + """ + Base class for stub image loaders. + + A stub loader is an image loader that can identify files of a + certain format, but relies on external code to load the file. + """ + + def _open(self): + raise NotImplementedError( + "StubImageFile subclass must implement _open" + ) + + def load(self): + loader = self._load() + if loader is None: + raise IOError("cannot find loader for this %s file" % self.format) + image = loader.load(self) + assert image is not None + # become the other object (!) + self.__class__ = image.__class__ + self.__dict__ = image.__dict__ + + def _load(self): + "(Hook) Find actual image loader." + raise NotImplementedError( + "StubImageFile subclass must implement _load" + ) + + +class Parser: + """ + Incremental image parser. This class implements the standard + feed/close consumer interface. + + In Python 2.x, this is an old-style class. + """ + incremental = None + image = None + data = None + decoder = None + finished = 0 + + def reset(self): + """ + (Consumer) Reset the parser. Note that you can only call this + method immediately after you've created a parser; parser + instances cannot be reused. + """ + assert self.data is None, "cannot reuse parsers" + + def feed(self, data): + """ + (Consumer) Feed data to the parser. + + :param data: A string buffer. + :exception IOError: If the parser failed to parse the image file. + """ + # collect data + + if self.finished: + return + + if self.data is None: + self.data = data + else: + self.data = self.data + data + + # parse what we have + if self.decoder: + + if self.offset > 0: + # skip header + skip = min(len(self.data), self.offset) + self.data = self.data[skip:] + self.offset = self.offset - skip + if self.offset > 0 or not self.data: + return + + n, e = self.decoder.decode(self.data) + + if n < 0: + # end of stream + self.data = None + self.finished = 1 + if e < 0: + # decoding error + self.image = None + raise_ioerror(e) + else: + # end of image + return + self.data = self.data[n:] + + elif self.image: + + # if we end up here with no decoder, this file cannot + # be incrementally parsed. wait until we've gotten all + # available data + pass + + else: + + # attempt to open this file + try: + try: + fp = io.BytesIO(self.data) + im = Image.open(fp) + finally: + fp.close() # explicitly close the virtual file + except IOError: + # traceback.print_exc() + pass # not enough data + else: + flag = hasattr(im, "load_seek") or hasattr(im, "load_read") + if flag or len(im.tile) != 1: + # custom load code, or multiple tiles + self.decode = None + else: + # initialize decoder + im.load_prepare() + d, e, o, a = im.tile[0] + im.tile = [] + self.decoder = Image._getdecoder( + im.mode, d, a, im.decoderconfig + ) + self.decoder.setimage(im.im, e) + + # calculate decoder offset + self.offset = o + if self.offset <= len(self.data): + self.data = self.data[self.offset:] + self.offset = 0 + + self.image = im + + def close(self): + """ + (Consumer) Close the stream. + + :returns: An image object. + :exception IOError: If the parser failed to parse the image file either + because it cannot be identified or cannot be + decoded. + """ + # finish decoding + if self.decoder: + # get rid of what's left in the buffers + self.feed(b"") + self.data = self.decoder = None + if not self.finished: + raise IOError("image was incomplete") + if not self.image: + raise IOError("cannot parse this image") + if self.data: + # incremental parsing not possible; reopen the file + # not that we have all data + try: + fp = io.BytesIO(self.data) + self.image = Image.open(fp) + finally: + self.image.load() + fp.close() # explicitly close the virtual file + return self.image + + +# -------------------------------------------------------------------- + +def _save(im, fp, tile, bufsize=0): + """Helper to save image based on tile list + + :param im: Image object. + :param fp: File object. + :param tile: Tile list. + :param bufsize: Optional buffer size + """ + + im.load() + if not hasattr(im, "encoderconfig"): + im.encoderconfig = () + tile.sort(key=_tilesort) + # FIXME: make MAXBLOCK a configuration parameter + # It would be great if we could have the encoder specify what it needs + # But, it would need at least the image size in most cases. RawEncode is + # a tricky case. + bufsize = max(MAXBLOCK, bufsize, im.size[0] * 4) # see RawEncode.c + try: + fh = fp.fileno() + fp.flush() + except (AttributeError, io.UnsupportedOperation): + # compress to Python file-compatible object + for e, b, o, a in tile: + e = Image._getencoder(im.mode, e, a, im.encoderconfig) + if o > 0: + fp.seek(o, 0) + e.setimage(im.im, b) + while True: + l, s, d = e.encode(bufsize) + fp.write(d) + if s: + break + if s < 0: + raise IOError("encoder error %d when writing image file" % s) + e.cleanup() + else: + # slight speedup: compress to real file object + for e, b, o, a in tile: + e = Image._getencoder(im.mode, e, a, im.encoderconfig) + if o > 0: + fp.seek(o, 0) + e.setimage(im.im, b) + s = e.encode_to_file(fh, bufsize) + if s < 0: + raise IOError("encoder error %d when writing image file" % s) + e.cleanup() + try: + fp.flush() + except: + pass + + +def _safe_read(fp, size): + """ + Reads large blocks in a safe way. Unlike fp.read(n), this function + doesn't trust the user. If the requested size is larger than + SAFEBLOCK, the file is read block by block. + + :param fp: File handle. Must implement a read method. + :param size: Number of bytes to read. + :returns: A string containing up to size bytes of data. + """ + if size <= 0: + return b"" + if size <= SAFEBLOCK: + return fp.read(size) + data = [] + while size > 0: + block = fp.read(min(size, SAFEBLOCK)) + if not block: + break + data.append(block) + size -= len(block) + return b"".join(data) diff --git a/pyPackages/pillowx86/PIL/ImageFileIO.py b/pyPackages/pillowx86/PIL/ImageFileIO.py new file mode 100644 index 0000000..e57d3f4 --- /dev/null +++ b/pyPackages/pillowx86/PIL/ImageFileIO.py @@ -0,0 +1,40 @@ +# +# The Python Imaging Library. +# $Id$ +# +# kludge to get basic ImageFileIO functionality +# +# History: +# 1998-08-06 fl Recreated +# +# Copyright (c) Secret Labs AB 1998-2002. +# +# See the README file for information on usage and redistribution. +# +""" +The **ImageFileIO** module can be used to read an image from a +socket, or any other stream device. + +Deprecated. New code should use the :class:`PIL.ImageFile.Parser` +class in the :mod:`PIL.ImageFile` module instead. + +.. seealso:: modules :class:`PIL.ImageFile.Parser` +""" + +from io import BytesIO + + +class ImageFileIO(BytesIO): + def __init__(self, fp): + """ + Adds buffering to a stream file object, in order to + provide **seek** and **tell** methods required + by the :func:`PIL.Image.Image.open` method. The stream object must + implement **read** and **close** methods. + + :param fp: Stream file handle. + + .. seealso:: modules :func:`PIL.Image.open` + """ + data = fp.read() + BytesIO.__init__(self, data) diff --git a/pyPackages/pillowx86/PIL/ImageFilter.py b/pyPackages/pillowx86/PIL/ImageFilter.py new file mode 100644 index 0000000..b468458 --- /dev/null +++ b/pyPackages/pillowx86/PIL/ImageFilter.py @@ -0,0 +1,275 @@ +# +# The Python Imaging Library. +# $Id$ +# +# standard filters +# +# History: +# 1995-11-27 fl Created +# 2002-06-08 fl Added rank and mode filters +# 2003-09-15 fl Fixed rank calculation in rank filter; added expand call +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1995-2002 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +from functools import reduce + + +class Filter(object): + pass + + +class Kernel(Filter): + """ + Create a convolution kernel. The current version only + supports 3x3 and 5x5 integer and floating point kernels. + + In the current version, kernels can only be applied to + "L" and "RGB" images. + + :param size: Kernel size, given as (width, height). In the current + version, this must be (3,3) or (5,5). + :param kernel: A sequence containing kernel weights. + :param scale: Scale factor. If given, the result for each pixel is + divided by this value. the default is the sum of the + kernel weights. + :param offset: Offset. If given, this value is added to the result, + after it has been divided by the scale factor. + """ + + def __init__(self, size, kernel, scale=None, offset=0): + if scale is None: + # default scale is sum of kernel + scale = reduce(lambda a, b: a+b, kernel) + if size[0] * size[1] != len(kernel): + raise ValueError("not enough coefficients in kernel") + self.filterargs = size, scale, offset, kernel + + def filter(self, image): + if image.mode == "P": + raise ValueError("cannot filter palette images") + return image.filter(*self.filterargs) + + +class BuiltinFilter(Kernel): + def __init__(self): + pass + + +class RankFilter(Filter): + """ + Create a rank filter. The rank filter sorts all pixels in + a window of the given size, and returns the **rank**'th value. + + :param size: The kernel size, in pixels. + :param rank: What pixel value to pick. Use 0 for a min filter, + ``size * size / 2`` for a median filter, ``size * size - 1`` + for a max filter, etc. + """ + name = "Rank" + + def __init__(self, size, rank): + self.size = size + self.rank = rank + + def filter(self, image): + if image.mode == "P": + raise ValueError("cannot filter palette images") + image = image.expand(self.size//2, self.size//2) + return image.rankfilter(self.size, self.rank) + + +class MedianFilter(RankFilter): + """ + Create a median filter. Picks the median pixel value in a window with the + given size. + + :param size: The kernel size, in pixels. + """ + name = "Median" + + def __init__(self, size=3): + self.size = size + self.rank = size*size//2 + + +class MinFilter(RankFilter): + """ + Create a min filter. Picks the lowest pixel value in a window with the + given size. + + :param size: The kernel size, in pixels. + """ + name = "Min" + + def __init__(self, size=3): + self.size = size + self.rank = 0 + + +class MaxFilter(RankFilter): + """ + Create a max filter. Picks the largest pixel value in a window with the + given size. + + :param size: The kernel size, in pixels. + """ + name = "Max" + + def __init__(self, size=3): + self.size = size + self.rank = size*size-1 + + +class ModeFilter(Filter): + """ + + Create a mode filter. Picks the most frequent pixel value in a box with the + given size. Pixel values that occur only once or twice are ignored; if no + pixel value occurs more than twice, the original pixel value is preserved. + + :param size: The kernel size, in pixels. + """ + name = "Mode" + + def __init__(self, size=3): + self.size = size + + def filter(self, image): + return image.modefilter(self.size) + + +class GaussianBlur(Filter): + """Gaussian blur filter. + + :param radius: Blur radius. + """ + name = "GaussianBlur" + + def __init__(self, radius=2): + self.radius = radius + + def filter(self, image): + return image.gaussian_blur(self.radius) + + +class UnsharpMask(Filter): + """Unsharp mask filter. + + See Wikipedia's entry on `digital unsharp masking`_ for an explanation of + the parameters. + + :param radius: Blur Radius + :param percent: Unsharp strength, in percent + :param threshold: Threshold controls the minimum brightness change that + will be sharpened + + .. _digital unsharp masking: https://en.wikipedia.org/wiki/Unsharp_masking#Digital_unsharp_masking + + """ + name = "UnsharpMask" + + def __init__(self, radius=2, percent=150, threshold=3): + self.radius = radius + self.percent = percent + self.threshold = threshold + + def filter(self, image): + return image.unsharp_mask(self.radius, self.percent, self.threshold) + + +class BLUR(BuiltinFilter): + name = "Blur" + filterargs = (5, 5), 16, 0, ( + 1, 1, 1, 1, 1, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 1, 1, 1, 1 + ) + + +class CONTOUR(BuiltinFilter): + name = "Contour" + filterargs = (3, 3), 1, 255, ( + -1, -1, -1, + -1, 8, -1, + -1, -1, -1 + ) + + +class DETAIL(BuiltinFilter): + name = "Detail" + filterargs = (3, 3), 6, 0, ( + 0, -1, 0, + -1, 10, -1, + 0, -1, 0 + ) + + +class EDGE_ENHANCE(BuiltinFilter): + name = "Edge-enhance" + filterargs = (3, 3), 2, 0, ( + -1, -1, -1, + -1, 10, -1, + -1, -1, -1 + ) + + +class EDGE_ENHANCE_MORE(BuiltinFilter): + name = "Edge-enhance More" + filterargs = (3, 3), 1, 0, ( + -1, -1, -1, + -1, 9, -1, + -1, -1, -1 + ) + + +class EMBOSS(BuiltinFilter): + name = "Emboss" + filterargs = (3, 3), 1, 128, ( + -1, 0, 0, + 0, 1, 0, + 0, 0, 0 + ) + + +class FIND_EDGES(BuiltinFilter): + name = "Find Edges" + filterargs = (3, 3), 1, 0, ( + -1, -1, -1, + -1, 8, -1, + -1, -1, -1 + ) + + +class SMOOTH(BuiltinFilter): + name = "Smooth" + filterargs = (3, 3), 13, 0, ( + 1, 1, 1, + 1, 5, 1, + 1, 1, 1 + ) + + +class SMOOTH_MORE(BuiltinFilter): + name = "Smooth More" + filterargs = (5, 5), 100, 0, ( + 1, 1, 1, 1, 1, + 1, 5, 5, 5, 1, + 1, 5, 44, 5, 1, + 1, 5, 5, 5, 1, + 1, 1, 1, 1, 1 + ) + + +class SHARPEN(BuiltinFilter): + name = "Sharpen" + filterargs = (3, 3), 16, 0, ( + -2, -2, -2, + -2, 32, -2, + -2, -2, -2 + ) diff --git a/pyPackages/pillowx86/PIL/ImageFont.py b/pyPackages/pillowx86/PIL/ImageFont.py new file mode 100644 index 0000000..afbae37 --- /dev/null +++ b/pyPackages/pillowx86/PIL/ImageFont.py @@ -0,0 +1,430 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PIL raster font management +# +# History: +# 1996-08-07 fl created (experimental) +# 1997-08-25 fl minor adjustments to handle fonts from pilfont 0.3 +# 1999-02-06 fl rewrote most font management stuff in C +# 1999-03-17 fl take pth files into account in load_path (from Richard Jones) +# 2001-02-17 fl added freetype support +# 2001-05-09 fl added TransposedFont wrapper class +# 2002-03-04 fl make sure we have a "L" or "1" font +# 2002-12-04 fl skip non-directory entries in the system path +# 2003-04-29 fl add embedded default font +# 2003-09-27 fl added support for truetype charmap encodings +# +# Todo: +# Adapt to PILFONT2 format (16-bit fonts, compressed, single file) +# +# Copyright (c) 1997-2003 by Secret Labs AB +# Copyright (c) 1996-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from __future__ import print_function + +from PIL import Image +from PIL._util import isDirectory, isPath +import os +import sys + +try: + import warnings +except ImportError: + warnings = None + + +class _imagingft_not_installed: + # module placeholder + def __getattr__(self, id): + raise ImportError("The _imagingft C module is not installed") + +try: + from PIL import _imagingft as core +except ImportError: + core = _imagingft_not_installed() + +# FIXME: add support for pilfont2 format (see FontFile.py) + +# -------------------------------------------------------------------- +# Font metrics format: +# "PILfont" LF +# fontdescriptor LF +# (optional) key=value... LF +# "DATA" LF +# binary data: 256*10*2 bytes (dx, dy, dstbox, srcbox) +# +# To place a character, cut out srcbox and paste at dstbox, +# relative to the character position. Then move the character +# position according to dx, dy. +# -------------------------------------------------------------------- + + +class ImageFont: + "PIL font wrapper" + + def _load_pilfont(self, filename): + + file = open(filename, "rb") + + for ext in (".png", ".gif", ".pbm"): + try: + fullname = os.path.splitext(filename)[0] + ext + image = Image.open(fullname) + except: + pass + else: + if image and image.mode in ("1", "L"): + break + else: + raise IOError("cannot find glyph data file") + + self.file = fullname + + return self._load_pilfont_data(file, image) + + def _load_pilfont_data(self, file, image): + + # read PILfont header + if file.readline() != b"PILfont\n": + raise SyntaxError("Not a PILfont file") + file.readline().split(b";") + self.info = [] # FIXME: should be a dictionary + while True: + s = file.readline() + if not s or s == b"DATA\n": + break + self.info.append(s) + + # read PILfont metrics + data = file.read(256*20) + + # check image + if image.mode not in ("1", "L"): + raise TypeError("invalid font image mode") + + image.load() + + self.font = Image.core.font(image.im, data) + + # delegate critical operations to internal type + self.getsize = self.font.getsize + self.getmask = self.font.getmask + + +## +# Wrapper for FreeType fonts. Application code should use the +# truetype factory function to create font objects. + +class FreeTypeFont: + "FreeType font wrapper (requires _imagingft service)" + + def __init__(self, font=None, size=10, index=0, encoding="", file=None): + # FIXME: use service provider instead + if file: + if warnings: + warnings.warn( + 'file parameter deprecated, ' + 'please use font parameter instead.', + DeprecationWarning) + font = file + + if isPath(font): + self.font = core.getfont(font, size, index, encoding) + else: + self.font_bytes = font.read() + self.font = core.getfont( + "", size, index, encoding, self.font_bytes) + + def getname(self): + return self.font.family, self.font.style + + def getmetrics(self): + return self.font.ascent, self.font.descent + + def getsize(self, text): + size, offset = self.font.getsize(text) + return (size[0] + offset[0], size[1] + offset[1]) + + def getoffset(self, text): + return self.font.getsize(text)[1] + + def getmask(self, text, mode=""): + return self.getmask2(text, mode)[0] + + def getmask2(self, text, mode="", fill=Image.core.fill): + size, offset = self.font.getsize(text) + im = fill("L", size, 0) + self.font.render(text, im.id, mode == "1") + return im, offset + +## +# Wrapper that creates a transposed font from any existing font +# object. +# +# @param font A font object. +# @param orientation An optional orientation. If given, this should +# be one of Image.FLIP_LEFT_RIGHT, Image.FLIP_TOP_BOTTOM, +# Image.ROTATE_90, Image.ROTATE_180, or Image.ROTATE_270. + + +class TransposedFont: + "Wrapper for writing rotated or mirrored text" + + def __init__(self, font, orientation=None): + self.font = font + self.orientation = orientation # any 'transpose' argument, or None + + def getsize(self, text): + w, h = self.font.getsize(text) + if self.orientation in (Image.ROTATE_90, Image.ROTATE_270): + return h, w + return w, h + + def getmask(self, text, mode=""): + im = self.font.getmask(text, mode) + if self.orientation is not None: + return im.transpose(self.orientation) + return im + + +def load(filename): + """ + Load a font file. This function loads a font object from the given + bitmap font file, and returns the corresponding font object. + + :param filename: Name of font file. + :return: A font object. + :exception IOError: If the file could not be read. + """ + f = ImageFont() + f._load_pilfont(filename) + return f + + +def truetype(font=None, size=10, index=0, encoding="", filename=None): + """ + Load a TrueType or OpenType font file, and create a font object. + This function loads a font object from the given file, and creates + a font object for a font of the given size. + + This function requires the _imagingft service. + + :param filename: A truetype font file. Under Windows, if the file + is not found in this filename, the loader also looks in + Windows :file:`fonts/` directory. + :param size: The requested size, in points. + :param index: Which font face to load (default is first available face). + :param encoding: Which font encoding to use (default is Unicode). Common + encodings are "unic" (Unicode), "symb" (Microsoft + Symbol), "ADOB" (Adobe Standard), "ADBE" (Adobe Expert), + and "armn" (Apple Roman). See the FreeType documentation + for more information. + :return: A font object. + :exception IOError: If the file could not be read. + """ + + if filename: + if warnings: + warnings.warn( + 'filename parameter deprecated, ' + 'please use font parameter instead.', + DeprecationWarning) + font = filename + + try: + return FreeTypeFont(font, size, index, encoding) + except IOError: + if font.endswith(".ttf"): + ttf_filename = font + else: + ttf_filename = "%s.ttf" % font + if sys.platform == "win32": + # check the windows font repository + # NOTE: must use uppercase WINDIR, to work around bugs in + # 1.5.2's os.environ.get() + windir = os.environ.get("WINDIR") + if windir: + filename = os.path.join(windir, "fonts", font) + return FreeTypeFont(filename, size, index, encoding) + elif sys.platform in ('linux', 'linux2'): + lindirs = os.environ.get("XDG_DATA_DIRS", "") + if not lindirs: + #According to the freedesktop spec, XDG_DATA_DIRS should + #default to /usr/share + lindirs = '/usr/share' + lindirs = lindirs.split(":") + for lindir in lindirs: + parentpath = os.path.join(lindir, "fonts") + for walkroot, walkdir, walkfilenames in os.walk(parentpath): + if ttf_filename in walkfilenames: + filepath = os.path.join(walkroot, ttf_filename) + return FreeTypeFont(filepath, size, index, encoding) + elif sys.platform == 'darwin': + macdirs = ['/Library/Fonts/', '/System/Library/Fonts/', os.path.expanduser('~/Library/Fonts/')] + for macdir in macdirs: + filepath = os.path.join(macdir, ttf_filename) + if os.path.exists(filepath): + return FreeTypeFont(filepath, size, index, encoding) + raise + + +def load_path(filename): + """ + Load font file. Same as :py:func:`~PIL.ImageFont.load`, but searches for a + bitmap font along the Python path. + + :param filename: Name of font file. + :return: A font object. + :exception IOError: If the file could not be read. + """ + for dir in sys.path: + if isDirectory(dir): + if not isinstance(filename, str): + if bytes is str: + filename = filename.encode("utf-8") + else: + filename = filename.decode("utf-8") + try: + return load(os.path.join(dir, filename)) + except IOError: + pass + raise IOError("cannot find font file") + + +def load_default(): + """Load a "better than nothing" default font. + + .. versionadded:: 1.1.4 + + :return: A font object. + """ + from io import BytesIO + import base64 + f = ImageFont() + f._load_pilfont_data( + # courB08 + BytesIO(base64.decodestring(b''' +UElMZm9udAo7Ozs7OzsxMDsKREFUQQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAA//8AAQAAAAAAAAABAAEA +BgAAAAH/+gADAAAAAQAAAAMABgAGAAAAAf/6AAT//QADAAAABgADAAYAAAAA//kABQABAAYAAAAL +AAgABgAAAAD/+AAFAAEACwAAABAACQAGAAAAAP/5AAUAAAAQAAAAFQAHAAYAAP////oABQAAABUA +AAAbAAYABgAAAAH/+QAE//wAGwAAAB4AAwAGAAAAAf/5AAQAAQAeAAAAIQAIAAYAAAAB//kABAAB +ACEAAAAkAAgABgAAAAD/+QAE//0AJAAAACgABAAGAAAAAP/6AAX//wAoAAAALQAFAAYAAAAB//8A +BAACAC0AAAAwAAMABgAAAAD//AAF//0AMAAAADUAAQAGAAAAAf//AAMAAAA1AAAANwABAAYAAAAB +//kABQABADcAAAA7AAgABgAAAAD/+QAFAAAAOwAAAEAABwAGAAAAAP/5AAYAAABAAAAARgAHAAYA +AAAA//kABQAAAEYAAABLAAcABgAAAAD/+QAFAAAASwAAAFAABwAGAAAAAP/5AAYAAABQAAAAVgAH +AAYAAAAA//kABQAAAFYAAABbAAcABgAAAAD/+QAFAAAAWwAAAGAABwAGAAAAAP/5AAUAAABgAAAA +ZQAHAAYAAAAA//kABQAAAGUAAABqAAcABgAAAAD/+QAFAAAAagAAAG8ABwAGAAAAAf/8AAMAAABv +AAAAcQAEAAYAAAAA//wAAwACAHEAAAB0AAYABgAAAAD/+gAE//8AdAAAAHgABQAGAAAAAP/7AAT/ +/gB4AAAAfAADAAYAAAAB//oABf//AHwAAACAAAUABgAAAAD/+gAFAAAAgAAAAIUABgAGAAAAAP/5 +AAYAAQCFAAAAiwAIAAYAAP////oABgAAAIsAAACSAAYABgAA////+gAFAAAAkgAAAJgABgAGAAAA +AP/6AAUAAACYAAAAnQAGAAYAAP////oABQAAAJ0AAACjAAYABgAA////+gAFAAAAowAAAKkABgAG +AAD////6AAUAAACpAAAArwAGAAYAAAAA//oABQAAAK8AAAC0AAYABgAA////+gAGAAAAtAAAALsA +BgAGAAAAAP/6AAQAAAC7AAAAvwAGAAYAAP////oABQAAAL8AAADFAAYABgAA////+gAGAAAAxQAA +AMwABgAGAAD////6AAUAAADMAAAA0gAGAAYAAP////oABQAAANIAAADYAAYABgAA////+gAGAAAA +2AAAAN8ABgAGAAAAAP/6AAUAAADfAAAA5AAGAAYAAP////oABQAAAOQAAADqAAYABgAAAAD/+gAF +AAEA6gAAAO8ABwAGAAD////6AAYAAADvAAAA9gAGAAYAAAAA//oABQAAAPYAAAD7AAYABgAA//// ++gAFAAAA+wAAAQEABgAGAAD////6AAYAAAEBAAABCAAGAAYAAP////oABgAAAQgAAAEPAAYABgAA +////+gAGAAABDwAAARYABgAGAAAAAP/6AAYAAAEWAAABHAAGAAYAAP////oABgAAARwAAAEjAAYA +BgAAAAD/+gAFAAABIwAAASgABgAGAAAAAf/5AAQAAQEoAAABKwAIAAYAAAAA//kABAABASsAAAEv +AAgABgAAAAH/+QAEAAEBLwAAATIACAAGAAAAAP/5AAX//AEyAAABNwADAAYAAAAAAAEABgACATcA +AAE9AAEABgAAAAH/+QAE//wBPQAAAUAAAwAGAAAAAP/7AAYAAAFAAAABRgAFAAYAAP////kABQAA +AUYAAAFMAAcABgAAAAD/+wAFAAABTAAAAVEABQAGAAAAAP/5AAYAAAFRAAABVwAHAAYAAAAA//sA +BQAAAVcAAAFcAAUABgAAAAD/+QAFAAABXAAAAWEABwAGAAAAAP/7AAYAAgFhAAABZwAHAAYAAP// +//kABQAAAWcAAAFtAAcABgAAAAD/+QAGAAABbQAAAXMABwAGAAAAAP/5AAQAAgFzAAABdwAJAAYA +AP////kABgAAAXcAAAF+AAcABgAAAAD/+QAGAAABfgAAAYQABwAGAAD////7AAUAAAGEAAABigAF +AAYAAP////sABQAAAYoAAAGQAAUABgAAAAD/+wAFAAABkAAAAZUABQAGAAD////7AAUAAgGVAAAB +mwAHAAYAAAAA//sABgACAZsAAAGhAAcABgAAAAD/+wAGAAABoQAAAacABQAGAAAAAP/7AAYAAAGn +AAABrQAFAAYAAAAA//kABgAAAa0AAAGzAAcABgAA////+wAGAAABswAAAboABQAGAAD////7AAUA +AAG6AAABwAAFAAYAAP////sABgAAAcAAAAHHAAUABgAAAAD/+wAGAAABxwAAAc0ABQAGAAD////7 +AAYAAgHNAAAB1AAHAAYAAAAA//sABQAAAdQAAAHZAAUABgAAAAH/+QAFAAEB2QAAAd0ACAAGAAAA +Av/6AAMAAQHdAAAB3gAHAAYAAAAA//kABAABAd4AAAHiAAgABgAAAAD/+wAF//0B4gAAAecAAgAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAB +//sAAwACAecAAAHpAAcABgAAAAD/+QAFAAEB6QAAAe4ACAAGAAAAAP/5AAYAAAHuAAAB9AAHAAYA +AAAA//oABf//AfQAAAH5AAUABgAAAAD/+QAGAAAB+QAAAf8ABwAGAAAAAv/5AAMAAgH/AAACAAAJ +AAYAAAAA//kABQABAgAAAAIFAAgABgAAAAH/+gAE//sCBQAAAggAAQAGAAAAAP/5AAYAAAIIAAAC +DgAHAAYAAAAB//kABf/+Ag4AAAISAAUABgAA////+wAGAAACEgAAAhkABQAGAAAAAP/7AAX//gIZ +AAACHgADAAYAAAAA//wABf/9Ah4AAAIjAAEABgAAAAD/+QAHAAACIwAAAioABwAGAAAAAP/6AAT/ ++wIqAAACLgABAAYAAAAA//kABP/8Ai4AAAIyAAMABgAAAAD/+gAFAAACMgAAAjcABgAGAAAAAf/5 +AAT//QI3AAACOgAEAAYAAAAB//kABP/9AjoAAAI9AAQABgAAAAL/+QAE//sCPQAAAj8AAgAGAAD/ +///7AAYAAgI/AAACRgAHAAYAAAAA//kABgABAkYAAAJMAAgABgAAAAH//AAD//0CTAAAAk4AAQAG +AAAAAf//AAQAAgJOAAACUQADAAYAAAAB//kABP/9AlEAAAJUAAQABgAAAAH/+QAF//4CVAAAAlgA +BQAGAAD////7AAYAAAJYAAACXwAFAAYAAP////kABgAAAl8AAAJmAAcABgAA////+QAGAAACZgAA +Am0ABwAGAAD////5AAYAAAJtAAACdAAHAAYAAAAA//sABQACAnQAAAJ5AAcABgAA////9wAGAAAC +eQAAAoAACQAGAAD////3AAYAAAKAAAAChwAJAAYAAP////cABgAAAocAAAKOAAkABgAA////9wAG +AAACjgAAApUACQAGAAD////4AAYAAAKVAAACnAAIAAYAAP////cABgAAApwAAAKjAAkABgAA//// ++gAGAAACowAAAqoABgAGAAAAAP/6AAUAAgKqAAACrwAIAAYAAP////cABQAAAq8AAAK1AAkABgAA +////9wAFAAACtQAAArsACQAGAAD////3AAUAAAK7AAACwQAJAAYAAP////gABQAAAsEAAALHAAgA +BgAAAAD/9wAEAAACxwAAAssACQAGAAAAAP/3AAQAAALLAAACzwAJAAYAAAAA//cABAAAAs8AAALT +AAkABgAAAAD/+AAEAAAC0wAAAtcACAAGAAD////6AAUAAALXAAAC3QAGAAYAAP////cABgAAAt0A +AALkAAkABgAAAAD/9wAFAAAC5AAAAukACQAGAAAAAP/3AAUAAALpAAAC7gAJAAYAAAAA//cABQAA +Au4AAALzAAkABgAAAAD/9wAFAAAC8wAAAvgACQAGAAAAAP/4AAUAAAL4AAAC/QAIAAYAAAAA//oA +Bf//Av0AAAMCAAUABgAA////+gAGAAADAgAAAwkABgAGAAD////3AAYAAAMJAAADEAAJAAYAAP// +//cABgAAAxAAAAMXAAkABgAA////9wAGAAADFwAAAx4ACQAGAAD////4AAYAAAAAAAoABwASAAYA +AP////cABgAAAAcACgAOABMABgAA////+gAFAAAADgAKABQAEAAGAAD////6AAYAAAAUAAoAGwAQ +AAYAAAAA//gABgAAABsACgAhABIABgAAAAD/+AAGAAAAIQAKACcAEgAGAAAAAP/4AAYAAAAnAAoA +LQASAAYAAAAA//gABgAAAC0ACgAzABIABgAAAAD/+QAGAAAAMwAKADkAEQAGAAAAAP/3AAYAAAA5 +AAoAPwATAAYAAP////sABQAAAD8ACgBFAA8ABgAAAAD/+wAFAAIARQAKAEoAEQAGAAAAAP/4AAUA +AABKAAoATwASAAYAAAAA//gABQAAAE8ACgBUABIABgAAAAD/+AAFAAAAVAAKAFkAEgAGAAAAAP/5 +AAUAAABZAAoAXgARAAYAAAAA//gABgAAAF4ACgBkABIABgAAAAD/+AAGAAAAZAAKAGoAEgAGAAAA +AP/4AAYAAABqAAoAcAASAAYAAAAA//kABgAAAHAACgB2ABEABgAAAAD/+AAFAAAAdgAKAHsAEgAG +AAD////4AAYAAAB7AAoAggASAAYAAAAA//gABQAAAIIACgCHABIABgAAAAD/+AAFAAAAhwAKAIwA +EgAGAAAAAP/4AAUAAACMAAoAkQASAAYAAAAA//gABQAAAJEACgCWABIABgAAAAD/+QAFAAAAlgAK +AJsAEQAGAAAAAP/6AAX//wCbAAoAoAAPAAYAAAAA//oABQABAKAACgClABEABgAA////+AAGAAAA +pQAKAKwAEgAGAAD////4AAYAAACsAAoAswASAAYAAP////gABgAAALMACgC6ABIABgAA////+QAG +AAAAugAKAMEAEQAGAAD////4AAYAAgDBAAoAyAAUAAYAAP////kABQACAMgACgDOABMABgAA//// ++QAGAAIAzgAKANUAEw== +''')), Image.open(BytesIO(base64.decodestring(b''' +iVBORw0KGgoAAAANSUhEUgAAAx4AAAAUAQAAAAArMtZoAAAEwElEQVR4nABlAJr/AHVE4czCI/4u +Mc4b7vuds/xzjz5/3/7u/n9vMe7vnfH/9++vPn/xyf5zhxzjt8GHw8+2d83u8x27199/nxuQ6Od9 +M43/5z2I+9n9ZtmDBwMQECDRQw/eQIQohJXxpBCNVE6QCCAAAAD//wBlAJr/AgALyj1t/wINwq0g +LeNZUworuN1cjTPIzrTX6ofHWeo3v336qPzfEwRmBnHTtf95/fglZK5N0PDgfRTslpGBvz7LFc4F +IUXBWQGjQ5MGCx34EDFPwXiY4YbYxavpnhHFrk14CDAAAAD//wBlAJr/AgKqRooH2gAgPeggvUAA +Bu2WfgPoAwzRAABAAAAAAACQgLz/3Uv4Gv+gX7BJgDeeGP6AAAD1NMDzKHD7ANWr3loYbxsAD791 +NAADfcoIDyP44K/jv4Y63/Z+t98Ovt+ub4T48LAAAAD//wBlAJr/AuplMlADJAAAAGuAphWpqhMx +in0A/fRvAYBABPgBwBUgABBQ/sYAyv9g0bCHgOLoGAAAAAAAREAAwI7nr0ArYpow7aX8//9LaP/9 +SjdavWA8ePHeBIKB//81/83ndznOaXx379wAAAD//wBlAJr/AqDxW+D3AABAAbUh/QMnbQag/gAY +AYDAAACgtgD/gOqAAAB5IA/8AAAk+n9w0AAA8AAAmFRJuPo27ciC0cD5oeW4E7KA/wD3ECMAn2tt +y8PgwH8AfAxFzC0JzeAMtratAsC/ffwAAAD//wBlAJr/BGKAyCAA4AAAAvgeYTAwHd1kmQF5chkG +ABoMIHcL5xVpTfQbUqzlAAAErwAQBgAAEOClA5D9il08AEh/tUzdCBsXkbgACED+woQg8Si9VeqY +lODCn7lmF6NhnAEYgAAA/NMIAAAAAAD//2JgjLZgVGBg5Pv/Tvpc8hwGBjYGJADjHDrAwPzAjv/H +/Wf3PzCwtzcwHmBgYGcwbZz8wHaCAQMDOwMDQ8MCBgYOC3W7mp+f0w+wHOYxO3OG+e376hsMZjk3 +AAAAAP//YmCMY2A4wMAIN5e5gQETPD6AZisDAwMDgzSDAAPjByiHcQMDAwMDg1nOze1lByRu5/47 +c4859311AYNZzg0AAAAA//9iYGDBYihOIIMuwIjGL39/fwffA8b//xv/P2BPtzzHwCBjUQAAAAD/ +/yLFBrIBAAAA//9i1HhcwdhizX7u8NZNzyLbvT97bfrMf/QHI8evOwcSqGUJAAAA//9iYBB81iSw +pEE170Qrg5MIYydHqwdDQRMrAwcVrQAAAAD//2J4x7j9AAMDn8Q/BgYLBoaiAwwMjPdvMDBYM1Tv +oJodAAAAAP//Yqo/83+dxePWlxl3npsel9lvLfPcqlE9725C+acfVLMEAAAA//9i+s9gwCoaaGMR +evta/58PTEWzr21hufPjA8N+qlnBwAAAAAD//2JiWLci5v1+HmFXDqcnULE/MxgYGBj+f6CaJQAA +AAD//2Ji2FrkY3iYpYC5qDeGgeEMAwPDvwQBBoYvcTwOVLMEAAAA//9isDBgkP///0EOg9z35v// +Gc/eeW7BwPj5+QGZhANUswMAAAD//2JgqGBgYGBgqEMXlvhMPUsAAAAA//8iYDd1AAAAAP//AwDR +w7IkEbzhVQAAAABJRU5ErkJggg== +''')))) + return f + +# End of file diff --git a/pyPackages/pillowx86/PIL/ImageGrab.py b/pyPackages/pillowx86/PIL/ImageGrab.py new file mode 100644 index 0000000..ef01353 --- /dev/null +++ b/pyPackages/pillowx86/PIL/ImageGrab.py @@ -0,0 +1,52 @@ +# +# The Python Imaging Library +# $Id$ +# +# screen grabber (windows only) +# +# History: +# 2001-04-26 fl created +# 2001-09-17 fl use builtin driver, if present +# 2002-11-19 fl added grabclipboard support +# +# Copyright (c) 2001-2002 by Secret Labs AB +# Copyright (c) 2001-2002 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image + +import sys +if sys.platform != "win32": + raise ImportError("ImageGrab is Windows only") + +try: + # built-in driver (1.1.3 and later) + grabber = Image.core.grabscreen +except AttributeError: + # stand-alone driver (pil plus) + import _grabscreen + grabber = _grabscreen.grab + + +def grab(bbox=None): + size, data = grabber() + im = Image.frombytes( + "RGB", size, data, + # RGB, 32-bit line padding, origo in lower left corner + "raw", "BGR", (size[0]*3 + 3) & -4, -1 + ) + if bbox: + im = im.crop(bbox) + return im + + +def grabclipboard(): + debug = 0 # temporary interface + data = Image.core.grabclipboard(debug) + if isinstance(data, bytes): + from PIL import BmpImagePlugin + import io + return BmpImagePlugin.DibImageFile(io.BytesIO(data)) + return data diff --git a/pyPackages/pillowx86/PIL/ImageMath.py b/pyPackages/pillowx86/PIL/ImageMath.py new file mode 100644 index 0000000..4dcc512 --- /dev/null +++ b/pyPackages/pillowx86/PIL/ImageMath.py @@ -0,0 +1,270 @@ +# +# The Python Imaging Library +# $Id$ +# +# a simple math add-on for the Python Imaging Library +# +# History: +# 1999-02-15 fl Original PIL Plus release +# 2005-05-05 fl Simplified and cleaned up for PIL 1.1.6 +# 2005-09-12 fl Fixed int() and float() for Python 2.4.1 +# +# Copyright (c) 1999-2005 by Secret Labs AB +# Copyright (c) 2005 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image +from PIL import _imagingmath + +try: + import builtins +except ImportError: + import __builtin__ + builtins = __builtin__ + +VERBOSE = 0 + + +def _isconstant(v): + return isinstance(v, int) or isinstance(v, float) + + +class _Operand: + # wraps an image operand, providing standard operators + + def __init__(self, im): + self.im = im + + def __fixup(self, im1): + # convert image to suitable mode + if isinstance(im1, _Operand): + # argument was an image. + if im1.im.mode in ("1", "L"): + return im1.im.convert("I") + elif im1.im.mode in ("I", "F"): + return im1.im + else: + raise ValueError("unsupported mode: %s" % im1.im.mode) + else: + # argument was a constant + if _isconstant(im1) and self.im.mode in ("1", "L", "I"): + return Image.new("I", self.im.size, im1) + else: + return Image.new("F", self.im.size, im1) + + def apply(self, op, im1, im2=None, mode=None): + im1 = self.__fixup(im1) + if im2 is None: + # unary operation + out = Image.new(mode or im1.mode, im1.size, None) + im1.load() + try: + op = getattr(_imagingmath, op+"_"+im1.mode) + except AttributeError: + raise TypeError("bad operand type for '%s'" % op) + _imagingmath.unop(op, out.im.id, im1.im.id) + else: + # binary operation + im2 = self.__fixup(im2) + if im1.mode != im2.mode: + # convert both arguments to floating point + if im1.mode != "F": + im1 = im1.convert("F") + if im2.mode != "F": + im2 = im2.convert("F") + if im1.mode != im2.mode: + raise ValueError("mode mismatch") + if im1.size != im2.size: + # crop both arguments to a common size + size = (min(im1.size[0], im2.size[0]), + min(im1.size[1], im2.size[1])) + if im1.size != size: + im1 = im1.crop((0, 0) + size) + if im2.size != size: + im2 = im2.crop((0, 0) + size) + out = Image.new(mode or im1.mode, size, None) + else: + out = Image.new(mode or im1.mode, im1.size, None) + im1.load() + im2.load() + try: + op = getattr(_imagingmath, op+"_"+im1.mode) + except AttributeError: + raise TypeError("bad operand type for '%s'" % op) + _imagingmath.binop(op, out.im.id, im1.im.id, im2.im.id) + return _Operand(out) + + # unary operators + def __bool__(self): + # an image is "true" if it contains at least one non-zero pixel + return self.im.getbbox() is not None + + if bytes is str: + # Provide __nonzero__ for pre-Py3k + __nonzero__ = __bool__ + del __bool__ + + def __abs__(self): + return self.apply("abs", self) + + def __pos__(self): + return self + + def __neg__(self): + return self.apply("neg", self) + + # binary operators + def __add__(self, other): + return self.apply("add", self, other) + + def __radd__(self, other): + return self.apply("add", other, self) + + def __sub__(self, other): + return self.apply("sub", self, other) + + def __rsub__(self, other): + return self.apply("sub", other, self) + + def __mul__(self, other): + return self.apply("mul", self, other) + + def __rmul__(self, other): + return self.apply("mul", other, self) + + def __truediv__(self, other): + return self.apply("div", self, other) + + def __rtruediv__(self, other): + return self.apply("div", other, self) + + def __mod__(self, other): + return self.apply("mod", self, other) + + def __rmod__(self, other): + return self.apply("mod", other, self) + + def __pow__(self, other): + return self.apply("pow", self, other) + + def __rpow__(self, other): + return self.apply("pow", other, self) + + if bytes is str: + # Provide __div__ and __rdiv__ for pre-Py3k + __div__ = __truediv__ + __rdiv__ = __rtruediv__ + del __truediv__ + del __rtruediv__ + + # bitwise + def __invert__(self): + return self.apply("invert", self) + + def __and__(self, other): + return self.apply("and", self, other) + + def __rand__(self, other): + return self.apply("and", other, self) + + def __or__(self, other): + return self.apply("or", self, other) + + def __ror__(self, other): + return self.apply("or", other, self) + + def __xor__(self, other): + return self.apply("xor", self, other) + + def __rxor__(self, other): + return self.apply("xor", other, self) + + def __lshift__(self, other): + return self.apply("lshift", self, other) + + def __rshift__(self, other): + return self.apply("rshift", self, other) + + # logical + def __eq__(self, other): + return self.apply("eq", self, other) + + def __ne__(self, other): + return self.apply("ne", self, other) + + def __lt__(self, other): + return self.apply("lt", self, other) + + def __le__(self, other): + return self.apply("le", self, other) + + def __gt__(self, other): + return self.apply("gt", self, other) + + def __ge__(self, other): + return self.apply("ge", self, other) + + +# conversions +def imagemath_int(self): + return _Operand(self.im.convert("I")) + + +def imagemath_float(self): + return _Operand(self.im.convert("F")) + + +# logical +def imagemath_equal(self, other): + return self.apply("eq", self, other, mode="I") + + +def imagemath_notequal(self, other): + return self.apply("ne", self, other, mode="I") + + +def imagemath_min(self, other): + return self.apply("min", self, other) + + +def imagemath_max(self, other): + return self.apply("max", self, other) + + +def imagemath_convert(self, mode): + return _Operand(self.im.convert(mode)) + +ops = {} +for k, v in list(globals().items()): + if k[:10] == "imagemath_": + ops[k[10:]] = v + + +def eval(expression, _dict={}, **kw): + """ + Evaluates an image expression. + + :param expression: A string containing a Python-style expression. + :param options: Values to add to the evaluation context. You + can either use a dictionary, or one or more keyword + arguments. + :return: The evaluated expression. This is usually an image object, but can + also be an integer, a floating point value, or a pixel tuple, + depending on the expression. + """ + + # build execution namespace + args = ops.copy() + args.update(_dict) + args.update(kw) + for k, v in list(args.items()): + if hasattr(v, "im"): + args[k] = _Operand(v) + + out = builtins.eval(expression, args) + try: + return out.im + except AttributeError: + return out diff --git a/pyPackages/pillowx86/PIL/ImageMode.py b/pyPackages/pillowx86/PIL/ImageMode.py new file mode 100644 index 0000000..2950691 --- /dev/null +++ b/pyPackages/pillowx86/PIL/ImageMode.py @@ -0,0 +1,52 @@ +# +# The Python Imaging Library. +# $Id$ +# +# standard mode descriptors +# +# History: +# 2006-03-20 fl Added +# +# Copyright (c) 2006 by Secret Labs AB. +# Copyright (c) 2006 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +# mode descriptor cache +_modes = {} + + +## +# Wrapper for mode strings. + +class ModeDescriptor: + + def __init__(self, mode, bands, basemode, basetype): + self.mode = mode + self.bands = bands + self.basemode = basemode + self.basetype = basetype + + def __str__(self): + return self.mode + + +## +# Gets a mode descriptor for the given mode. + +def getmode(mode): + if not _modes: + # initialize mode cache + from PIL import Image + # core modes + for m, (basemode, basetype, bands) in Image._MODEINFO.items(): + _modes[m] = ModeDescriptor(m, bands, basemode, basetype) + # extra experimental modes + _modes["LA"] = ModeDescriptor("LA", ("L", "A"), "L", "L") + _modes["PA"] = ModeDescriptor("PA", ("P", "A"), "RGB", "L") + # mapping modes + _modes["I;16"] = ModeDescriptor("I;16", "I", "L", "L") + _modes["I;16L"] = ModeDescriptor("I;16L", "I", "L", "L") + _modes["I;16B"] = ModeDescriptor("I;16B", "I", "L", "L") + return _modes[mode] diff --git a/pyPackages/pillowx86/PIL/ImageMorph.py b/pyPackages/pillowx86/PIL/ImageMorph.py new file mode 100644 index 0000000..996eacb --- /dev/null +++ b/pyPackages/pillowx86/PIL/ImageMorph.py @@ -0,0 +1,245 @@ +# A binary morphology add-on for the Python Imaging Library +# +# History: +# 2014-06-04 Initial version. +# +# Copyright (c) 2014 Dov Grobgeld + +from PIL import Image +from PIL import _imagingmorph +import re + +LUT_SIZE = 1 << 9 + + +class LutBuilder: + """A class for building a MorphLut from a descriptive language + + The input patterns is a list of a strings sequences like these:: + + 4:(... + .1. + 111)->1 + + (whitespaces including linebreaks are ignored). The option 4 + describes a series of symmetry operations (in this case a + 4-rotation), the pattern is described by: + + - . or X - Ignore + - 1 - Pixel is on + - 0 - Pixel is off + + The result of the operation is described after "->" string. + + The default is to return the current pixel value, which is + returned if no other match is found. + + Operations: + + - 4 - 4 way rotation + - N - Negate + - 1 - Dummy op for no other operation (an op must always be given) + - M - Mirroring + + Example:: + + lb = LutBuilder(patterns = ["4:(... .1. 111)->1"]) + lut = lb.build_lut() + + """ + def __init__(self, patterns=None, op_name=None): + if patterns is not None: + self.patterns = patterns + else: + self.patterns = [] + self.lut = None + if op_name is not None: + known_patterns = { + 'corner': ['1:(... ... ...)->0', + '4:(00. 01. ...)->1'], + 'dilation4': ['4:(... .0. .1.)->1'], + 'dilation8': ['4:(... .0. .1.)->1', + '4:(... .0. ..1)->1'], + 'erosion4': ['4:(... .1. .0.)->0'], + 'erosion8': ['4:(... .1. .0.)->0', + '4:(... .1. ..0)->0'], + 'edge': ['1:(... ... ...)->0', + '4:(.0. .1. ...)->1', + '4:(01. .1. ...)->1'] + } + if op_name not in known_patterns: + raise Exception('Unknown pattern '+op_name+'!') + + self.patterns = known_patterns[op_name] + + def add_patterns(self, patterns): + self.patterns += patterns + + def build_default_lut(self): + symbols = [0, 1] + m = 1 << 4 # pos of current pixel + self.lut = bytearray([symbols[(i & m) > 0] for i in range(LUT_SIZE)]) + + def get_lut(self): + return self.lut + + def _string_permute(self, pattern, permutation): + """string_permute takes a pattern and a permutation and returns the + string permuted according to the permutation list. + """ + assert(len(permutation) == 9) + return ''.join([pattern[p] for p in permutation]) + + def _pattern_permute(self, basic_pattern, options, basic_result): + """pattern_permute takes a basic pattern and its result and clones + the pattern according to the modifications described in the $options + parameter. It returns a list of all cloned patterns.""" + patterns = [(basic_pattern, basic_result)] + + # rotations + if '4' in options: + res = patterns[-1][1] + for i in range(4): + patterns.append( + (self._string_permute(patterns[-1][0], [6, 3, 0, + 7, 4, 1, + 8, 5, 2]), res)) + # mirror + if 'M' in options: + n = len(patterns) + for pattern, res in patterns[0:n]: + patterns.append( + (self._string_permute(pattern, [2, 1, 0, + 5, 4, 3, + 8, 7, 6]), res)) + + # negate + if 'N' in options: + n = len(patterns) + for pattern, res in patterns[0:n]: + # Swap 0 and 1 + pattern = (pattern + .replace('0', 'Z') + .replace('1', '0') + .replace('Z', '1')) + res = '%d' % (1-int(res)) + patterns.append((pattern, res)) + + return patterns + + def build_lut(self): + """Compile all patterns into a morphology lut. + + TBD :Build based on (file) morphlut:modify_lut + """ + self.build_default_lut() + patterns = [] + + # Parse and create symmetries of the patterns strings + for p in self.patterns: + m = re.search( + r'(\w*):?\s*\((.+?)\)\s*->\s*(\d)', p.replace('\n', '')) + if not m: + raise Exception('Syntax error in pattern "'+p+'"') + options = m.group(1) + pattern = m.group(2) + result = int(m.group(3)) + + # Get rid of spaces + pattern = pattern.replace(' ', '').replace('\n', '') + + patterns += self._pattern_permute(pattern, options, result) + +# # Debugging +# for p,r in patterns: +# print p,r +# print '--' + + # compile the patterns into regular expressions for speed + for i in range(len(patterns)): + p = patterns[i][0].replace('.', 'X').replace('X', '[01]') + p = re.compile(p) + patterns[i] = (p, patterns[i][1]) + + # Step through table and find patterns that match. + # Note that all the patterns are searched. The last one + # caught overrides + for i in range(LUT_SIZE): + # Build the bit pattern + bitpattern = bin(i)[2:] + bitpattern = ('0'*(9-len(bitpattern)) + bitpattern)[::-1] + + for p, r in patterns: + if p.match(bitpattern): + self.lut[i] = [0, 1][r] + + return self.lut + + +class MorphOp: + """A class for binary morphological operators""" + + def __init__(self, + lut=None, + op_name=None, + patterns=None): + """Create a binary morphological operator""" + self.lut = lut + if op_name is not None: + self.lut = LutBuilder(op_name=op_name).build_lut() + elif patterns is not None: + self.lut = LutBuilder(patterns=patterns).build_lut() + + def apply(self, image): + """Run a single morphological operation on an image + + Returns a tuple of the number of changed pixels and the + morphed image""" + if self.lut is None: + raise Exception('No operator loaded') + + outimage = Image.new(image.mode, image.size, None) + count = _imagingmorph.apply( + bytes(self.lut), image.im.id, outimage.im.id) + return count, outimage + + def match(self, image): + """Get a list of coordinates matching the morphological operation on + an image. + + Returns a list of tuples of (x,y) coordinates + of all matching pixels.""" + if self.lut is None: + raise Exception('No operator loaded') + + return _imagingmorph.match(bytes(self.lut), image.im.id) + + def get_on_pixels(self, image): + """Get a list of all turned on pixels in a binary image + + Returns a list of tuples of (x,y) coordinates + of all matching pixels.""" + + return _imagingmorph.get_on_pixels(image.im.id) + + def load_lut(self, filename): + """Load an operator from an mrl file""" + with open(filename, 'rb') as f: + self.lut = bytearray(f.read()) + + if len(self.lut) != 8192: + self.lut = None + raise Exception('Wrong size operator file!') + + def save_lut(self, filename): + """Save an operator to an mrl file""" + if self.lut is None: + raise Exception('No operator loaded') + with open(filename, 'wb') as f: + f.write(self.lut) + + def set_lut(self, lut): + """Set the lut from an external source""" + self.lut = lut + +# End of file diff --git a/pyPackages/pillowx86/PIL/ImageOps.py b/pyPackages/pillowx86/PIL/ImageOps.py new file mode 100644 index 0000000..a170687 --- /dev/null +++ b/pyPackages/pillowx86/PIL/ImageOps.py @@ -0,0 +1,462 @@ +# +# The Python Imaging Library. +# $Id$ +# +# standard image operations +# +# History: +# 2001-10-20 fl Created +# 2001-10-23 fl Added autocontrast operator +# 2001-12-18 fl Added Kevin's fit operator +# 2004-03-14 fl Fixed potential division by zero in equalize +# 2005-05-05 fl Fixed equalize for low number of values +# +# Copyright (c) 2001-2004 by Secret Labs AB +# Copyright (c) 2001-2004 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image +from PIL._util import isStringType +import operator +from functools import reduce + + +# +# helpers + +def _border(border): + if isinstance(border, tuple): + if len(border) == 2: + left, top = right, bottom = border + elif len(border) == 4: + left, top, right, bottom = border + else: + left = top = right = bottom = border + return left, top, right, bottom + + +def _color(color, mode): + if isStringType(color): + from PIL import ImageColor + color = ImageColor.getcolor(color, mode) + return color + + +def _lut(image, lut): + if image.mode == "P": + # FIXME: apply to lookup table, not image data + raise NotImplementedError("mode P support coming soon") + elif image.mode in ("L", "RGB"): + if image.mode == "RGB" and len(lut) == 256: + lut = lut + lut + lut + return image.point(lut) + else: + raise IOError("not supported for this image mode") + +# +# actions + + +def autocontrast(image, cutoff=0, ignore=None): + """ + Maximize (normalize) image contrast. This function calculates a + histogram of the input image, removes **cutoff** percent of the + lightest and darkest pixels from the histogram, and remaps the image + so that the darkest pixel becomes black (0), and the lightest + becomes white (255). + + :param image: The image to process. + :param cutoff: How many percent to cut off from the histogram. + :param ignore: The background pixel value (use None for no background). + :return: An image. + """ + histogram = image.histogram() + lut = [] + for layer in range(0, len(histogram), 256): + h = histogram[layer:layer+256] + if ignore is not None: + # get rid of outliers + try: + h[ignore] = 0 + except TypeError: + # assume sequence + for ix in ignore: + h[ix] = 0 + if cutoff: + # cut off pixels from both ends of the histogram + # get number of pixels + n = 0 + for ix in range(256): + n = n + h[ix] + # remove cutoff% pixels from the low end + cut = n * cutoff // 100 + for lo in range(256): + if cut > h[lo]: + cut = cut - h[lo] + h[lo] = 0 + else: + h[lo] -= cut + cut = 0 + if cut <= 0: + break + # remove cutoff% samples from the hi end + cut = n * cutoff // 100 + for hi in range(255, -1, -1): + if cut > h[hi]: + cut = cut - h[hi] + h[hi] = 0 + else: + h[hi] -= cut + cut = 0 + if cut <= 0: + break + # find lowest/highest samples after preprocessing + for lo in range(256): + if h[lo]: + break + for hi in range(255, -1, -1): + if h[hi]: + break + if hi <= lo: + # don't bother + lut.extend(list(range(256))) + else: + scale = 255.0 / (hi - lo) + offset = -lo * scale + for ix in range(256): + ix = int(ix * scale + offset) + if ix < 0: + ix = 0 + elif ix > 255: + ix = 255 + lut.append(ix) + return _lut(image, lut) + + +def colorize(image, black, white): + """ + Colorize grayscale image. The **black** and **white** + arguments should be RGB tuples; this function calculates a color + wedge mapping all black pixels in the source image to the first + color, and all white pixels to the second color. + + :param image: The image to colorize. + :param black: The color to use for black input pixels. + :param white: The color to use for white input pixels. + :return: An image. + """ + assert image.mode == "L" + black = _color(black, "RGB") + white = _color(white, "RGB") + red = [] + green = [] + blue = [] + for i in range(256): + red.append(black[0]+i*(white[0]-black[0])//255) + green.append(black[1]+i*(white[1]-black[1])//255) + blue.append(black[2]+i*(white[2]-black[2])//255) + image = image.convert("RGB") + return _lut(image, red + green + blue) + + +def crop(image, border=0): + """ + Remove border from image. The same amount of pixels are removed + from all four sides. This function works on all image modes. + + .. seealso:: :py:meth:`~PIL.Image.Image.crop` + + :param image: The image to crop. + :param border: The number of pixels to remove. + :return: An image. + """ + left, top, right, bottom = _border(border) + return image.crop( + (left, top, image.size[0]-right, image.size[1]-bottom) + ) + + +def deform(image, deformer, resample=Image.BILINEAR): + """ + Deform the image. + + :param image: The image to deform. + :param deformer: A deformer object. Any object that implements a + **getmesh** method can be used. + :param resample: What resampling filter to use. + :return: An image. + """ + return image.transform( + image.size, Image.MESH, deformer.getmesh(image), resample + ) + + +def equalize(image, mask=None): + """ + Equalize the image histogram. This function applies a non-linear + mapping to the input image, in order to create a uniform + distribution of grayscale values in the output image. + + :param image: The image to equalize. + :param mask: An optional mask. If given, only the pixels selected by + the mask are included in the analysis. + :return: An image. + """ + if image.mode == "P": + image = image.convert("RGB") + h = image.histogram(mask) + lut = [] + for b in range(0, len(h), 256): + histo = [_f for _f in h[b:b+256] if _f] + if len(histo) <= 1: + lut.extend(list(range(256))) + else: + step = (reduce(operator.add, histo) - histo[-1]) // 255 + if not step: + lut.extend(list(range(256))) + else: + n = step // 2 + for i in range(256): + lut.append(n // step) + n = n + h[i+b] + return _lut(image, lut) + + +def expand(image, border=0, fill=0): + """ + Add border to the image + + :param image: The image to expand. + :param border: Border width, in pixels. + :param fill: Pixel fill value (a color value). Default is 0 (black). + :return: An image. + """ + "Add border to image" + left, top, right, bottom = _border(border) + width = left + image.size[0] + right + height = top + image.size[1] + bottom + out = Image.new(image.mode, (width, height), _color(fill, image.mode)) + out.paste(image, (left, top)) + return out + + +def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)): + """ + Returns a sized and cropped version of the image, cropped to the + requested aspect ratio and size. + + This function was contributed by Kevin Cazabon. + + :param size: The requested output size in pixels, given as a + (width, height) tuple. + :param method: What resampling method to use. Default is + :py:attr:`PIL.Image.NEAREST`. + :param bleed: Remove a border around the outside of the image (from all + four edges. The value is a decimal percentage (use 0.01 for + one percent). The default value is 0 (no border). + :param centering: Control the cropping position. Use (0.5, 0.5) for + center cropping (e.g. if cropping the width, take 50% off + of the left side, and therefore 50% off the right side). + (0.0, 0.0) will crop from the top left corner (i.e. if + cropping the width, take all of the crop off of the right + side, and if cropping the height, take all of it off the + bottom). (1.0, 0.0) will crop from the bottom left + corner, etc. (i.e. if cropping the width, take all of the + crop off the left side, and if cropping the height take + none from the top, and therefore all off the bottom). + :return: An image. + """ + + # by Kevin Cazabon, Feb 17/2000 + # kevin@cazabon.com + # http://www.cazabon.com + + # ensure inputs are valid + if not isinstance(centering, list): + centering = [centering[0], centering[1]] + + if centering[0] > 1.0 or centering[0] < 0.0: + centering[0] = 0.50 + if centering[1] > 1.0 or centering[1] < 0.0: + centering[1] = 0.50 + + if bleed > 0.49999 or bleed < 0.0: + bleed = 0.0 + + # calculate the area to use for resizing and cropping, subtracting + # the 'bleed' around the edges + + # number of pixels to trim off on Top and Bottom, Left and Right + bleedPixels = ( + int((float(bleed) * float(image.size[0])) + 0.5), + int((float(bleed) * float(image.size[1])) + 0.5) + ) + + liveArea = (0, 0, image.size[0], image.size[1]) + if bleed > 0.0: + liveArea = ( + bleedPixels[0], bleedPixels[1], image.size[0] - bleedPixels[0] - 1, + image.size[1] - bleedPixels[1] - 1 + ) + + liveSize = (liveArea[2] - liveArea[0], liveArea[3] - liveArea[1]) + + # calculate the aspect ratio of the liveArea + liveAreaAspectRatio = float(liveSize[0])/float(liveSize[1]) + + # calculate the aspect ratio of the output image + aspectRatio = float(size[0]) / float(size[1]) + + # figure out if the sides or top/bottom will be cropped off + if liveAreaAspectRatio >= aspectRatio: + # liveArea is wider than what's needed, crop the sides + cropWidth = int((aspectRatio * float(liveSize[1])) + 0.5) + cropHeight = liveSize[1] + else: + # liveArea is taller than what's needed, crop the top and bottom + cropWidth = liveSize[0] + cropHeight = int((float(liveSize[0])/aspectRatio) + 0.5) + + # make the crop + leftSide = int(liveArea[0] + (float(liveSize[0]-cropWidth) * centering[0])) + if leftSide < 0: + leftSide = 0 + topSide = int(liveArea[1] + (float(liveSize[1]-cropHeight) * centering[1])) + if topSide < 0: + topSide = 0 + + out = image.crop( + (leftSide, topSide, leftSide + cropWidth, topSide + cropHeight) + ) + + # resize the image and return it + return out.resize(size, method) + + +def flip(image): + """ + Flip the image vertically (top to bottom). + + :param image: The image to flip. + :return: An image. + """ + return image.transpose(Image.FLIP_TOP_BOTTOM) + + +def grayscale(image): + """ + Convert the image to grayscale. + + :param image: The image to convert. + :return: An image. + """ + return image.convert("L") + + +def invert(image): + """ + Invert (negate) the image. + + :param image: The image to invert. + :return: An image. + """ + lut = [] + for i in range(256): + lut.append(255-i) + return _lut(image, lut) + + +def mirror(image): + """ + Flip image horizontally (left to right). + + :param image: The image to mirror. + :return: An image. + """ + return image.transpose(Image.FLIP_LEFT_RIGHT) + + +def posterize(image, bits): + """ + Reduce the number of bits for each color channel. + + :param image: The image to posterize. + :param bits: The number of bits to keep for each channel (1-8). + :return: An image. + """ + lut = [] + mask = ~(2**(8-bits)-1) + for i in range(256): + lut.append(i & mask) + return _lut(image, lut) + + +def solarize(image, threshold=128): + """ + Invert all pixel values above a threshold. + + :param image: The image to solarize. + :param threshold: All pixels above this greyscale level are inverted. + :return: An image. + """ + lut = [] + for i in range(256): + if i < threshold: + lut.append(i) + else: + lut.append(255-i) + return _lut(image, lut) + + +# -------------------------------------------------------------------- +# PIL USM components, from Kevin Cazabon. + +def gaussian_blur(im, radius=None): + """ PIL_usm.gblur(im, [radius])""" + + if radius is None: + radius = 5.0 + + im.load() + + return im.im.gaussian_blur(radius) + +gblur = gaussian_blur + + +def unsharp_mask(im, radius=None, percent=None, threshold=None): + """ PIL_usm.usm(im, [radius, percent, threshold])""" + + if radius is None: + radius = 5.0 + if percent is None: + percent = 150 + if threshold is None: + threshold = 3 + + im.load() + + return im.im.unsharp_mask(radius, percent, threshold) + +usm = unsharp_mask + + +def box_blur(image, radius): + """ + Blur the image by setting each pixel to the average value of the pixels + in a square box extending radius pixels in each direction. + Supports float radius of arbitrary size. Uses an optimized implementation + which runs in linear time relative to the size of the image + for any radius value. + + :param image: The image to blur. + :param radius: Size of the box in one direction. Radius 0 does not blur, + returns an identical image. Radius 1 takes 1 pixel + in each direction, i.e. 9 pixels in total. + :return: An image. + """ + image.load() + + return image._new(image.im.box_blur(radius)) diff --git a/pyPackages/pillowx86/PIL/ImagePalette.py b/pyPackages/pillowx86/PIL/ImagePalette.py new file mode 100644 index 0000000..62f8814 --- /dev/null +++ b/pyPackages/pillowx86/PIL/ImagePalette.py @@ -0,0 +1,235 @@ +# +# The Python Imaging Library. +# $Id$ +# +# image palette object +# +# History: +# 1996-03-11 fl Rewritten. +# 1997-01-03 fl Up and running. +# 1997-08-23 fl Added load hack +# 2001-04-16 fl Fixed randint shadow bug in random() +# +# Copyright (c) 1997-2001 by Secret Labs AB +# Copyright (c) 1996-1997 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import array +import warnings +from PIL import ImageColor + + +class ImagePalette: + "Color palette for palette mapped images" + + def __init__(self, mode="RGB", palette=None, size=0): + self.mode = mode + self.rawmode = None # if set, palette contains raw data + self.palette = palette or list(range(256))*len(self.mode) + self.colors = {} + self.dirty = None + if ((size == 0 and len(self.mode)*256 != len(self.palette)) or + (size != 0 and size != len(self.palette))): + raise ValueError("wrong palette size") + + def getdata(self): + """ + Get palette contents in format suitable # for the low-level + ``im.putpalette`` primitive. + + .. warning:: This method is experimental. + """ + if self.rawmode: + return self.rawmode, self.palette + return self.mode + ";L", self.tobytes() + + def tobytes(self): + """Convert palette to bytes. + + .. warning:: This method is experimental. + """ + if self.rawmode: + raise ValueError("palette contains raw palette data") + if isinstance(self.palette, bytes): + return self.palette + arr = array.array("B", self.palette) + if hasattr(arr, 'tobytes'): + # py3k has a tobytes, tostring is deprecated. + return arr.tobytes() + return arr.tostring() + + # Declare tostring as an alias for tobytes + tostring = tobytes + + def getcolor(self, color): + """Given an rgb tuple, allocate palette entry. + + .. warning:: This method is experimental. + """ + if self.rawmode: + raise ValueError("palette contains raw palette data") + if isinstance(color, tuple): + try: + return self.colors[color] + except KeyError: + # allocate new color slot + if isinstance(self.palette, bytes): + self.palette = [int(x) for x in self.palette] + index = len(self.colors) + if index >= 256: + raise ValueError("cannot allocate more than 256 colors") + self.colors[color] = index + self.palette[index] = color[0] + self.palette[index+256] = color[1] + self.palette[index+512] = color[2] + self.dirty = 1 + return index + else: + raise ValueError("unknown color specifier: %r" % color) + + def save(self, fp): + """Save palette to text file. + + .. warning:: This method is experimental. + """ + if self.rawmode: + raise ValueError("palette contains raw palette data") + if isinstance(fp, str): + fp = open(fp, "w") + fp.write("# Palette\n") + fp.write("# Mode: %s\n" % self.mode) + for i in range(256): + fp.write("%d" % i) + for j in range(i*len(self.mode), (i+1)*len(self.mode)): + try: + fp.write(" %d" % self.palette[j]) + except IndexError: + fp.write(" 0") + fp.write("\n") + fp.close() + + +# -------------------------------------------------------------------- +# Internal + +def raw(rawmode, data): + palette = ImagePalette() + palette.rawmode = rawmode + palette.palette = data + palette.dirty = 1 + return palette + + +# -------------------------------------------------------------------- +# Factories + +def _make_linear_lut(black, white): + warnings.warn( + '_make_linear_lut() is deprecated. ' + 'Please call make_linear_lut() instead.', + DeprecationWarning, + stacklevel=2 + ) + return make_linear_lut(black, white) + + +def _make_gamma_lut(exp): + warnings.warn( + '_make_gamma_lut() is deprecated. ' + 'Please call make_gamma_lut() instead.', + DeprecationWarning, + stacklevel=2 + ) + return make_gamma_lut(exp) + + +def make_linear_lut(black, white): + lut = [] + if black == 0: + for i in range(256): + lut.append(white*i//255) + else: + raise NotImplementedError # FIXME + return lut + + +def make_gamma_lut(exp): + lut = [] + for i in range(256): + lut.append(int(((i / 255.0) ** exp) * 255.0 + 0.5)) + return lut + + +def negative(mode="RGB"): + palette = list(range(256)) + palette.reverse() + return ImagePalette(mode, palette * len(mode)) + + +def random(mode="RGB"): + from random import randint + palette = [] + for i in range(256*len(mode)): + palette.append(randint(0, 255)) + return ImagePalette(mode, palette) + + +def sepia(white="#fff0c0"): + r, g, b = ImageColor.getrgb(white) + r = make_linear_lut(0, r) + g = make_linear_lut(0, g) + b = make_linear_lut(0, b) + return ImagePalette("RGB", r + g + b) + + +def wedge(mode="RGB"): + return ImagePalette(mode, list(range(256)) * len(mode)) + + +def load(filename): + + # FIXME: supports GIMP gradients only + + fp = open(filename, "rb") + + lut = None + + if not lut: + try: + from PIL import GimpPaletteFile + fp.seek(0) + p = GimpPaletteFile.GimpPaletteFile(fp) + lut = p.getpalette() + except (SyntaxError, ValueError): + # import traceback + # traceback.print_exc() + pass + + if not lut: + try: + from PIL import GimpGradientFile + fp.seek(0) + p = GimpGradientFile.GimpGradientFile(fp) + lut = p.getpalette() + except (SyntaxError, ValueError): + # import traceback + # traceback.print_exc() + pass + + if not lut: + try: + from PIL import PaletteFile + fp.seek(0) + p = PaletteFile.PaletteFile(fp) + lut = p.getpalette() + except (SyntaxError, ValueError): + import traceback + traceback.print_exc() + pass + + if not lut: + raise IOError("cannot load palette") + + return lut # data, rawmode diff --git a/pyPackages/pillowx86/PIL/ImagePath.py b/pyPackages/pillowx86/PIL/ImagePath.py new file mode 100644 index 0000000..656d5ce --- /dev/null +++ b/pyPackages/pillowx86/PIL/ImagePath.py @@ -0,0 +1,66 @@ +# +# The Python Imaging Library +# $Id$ +# +# path interface +# +# History: +# 1996-11-04 fl Created +# 2002-04-14 fl Added documentation stub class +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image + + +# the Python class below is overridden by the C implementation. + + +class Path: + + def __init__(self, xy): + pass + + ## + # Compacts the path, by removing points that are close to each + # other. This method modifies the path in place. + + def compact(self, distance=2): + pass + + ## + # Gets the bounding box. + + def getbbox(self): + pass + + ## + # Maps the path through a function. + + def map(self, function): + pass + + ## + # Converts the path to Python list. + # + # @param flat By default, this function returns a list of 2-tuples + # [(x, y), ...]. If this argument is true, it returns a flat + # list [x, y, ...] instead. + # @return A list of coordinates. + + def tolist(self, flat=0): + pass + + ## + # Transforms the path. + + def transform(self, matrix): + pass + + +# override with C implementation +Path = Image.core.path diff --git a/pyPackages/pillowx86/PIL/ImageQt.py b/pyPackages/pillowx86/PIL/ImageQt.py new file mode 100644 index 0000000..22ee2ea --- /dev/null +++ b/pyPackages/pillowx86/PIL/ImageQt.py @@ -0,0 +1,98 @@ +# +# The Python Imaging Library. +# $Id$ +# +# a simple Qt image interface. +# +# history: +# 2006-06-03 fl: created +# 2006-06-04 fl: inherit from QImage instead of wrapping it +# 2006-06-05 fl: removed toimage helper; move string support to ImageQt +# 2013-11-13 fl: add support for Qt5 (aurelien.ballier@cyclonit.com) +# +# Copyright (c) 2006 by Secret Labs AB +# Copyright (c) 2006 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image +from PIL._util import isPath +import sys + +if 'PyQt4.QtGui' not in sys.modules: + try: + from PyQt5.QtGui import QImage, qRgba + except: + try: + from PyQt4.QtGui import QImage, qRgba + except: + from PySide.QtGui import QImage, qRgba + +else: #PyQt4 is used + from PyQt4.QtGui import QImage, qRgba + +## +# (Internal) Turns an RGB color into a Qt compatible color integer. + +def rgb(r, g, b, a=255): + # use qRgb to pack the colors, and then turn the resulting long + # into a negative integer with the same bitpattern. + return (qRgba(r, g, b, a) & 0xffffffff) + + +## +# An PIL image wrapper for Qt. This is a subclass of PyQt4's QImage +# class. +# +# @param im A PIL Image object, or a file name (given either as Python +# string or a PyQt string object). + +class ImageQt(QImage): + + def __init__(self, im): + + data = None + colortable = None + + # handle filename, if given instead of image name + if hasattr(im, "toUtf8"): + # FIXME - is this really the best way to do this? + im = unicode(im.toUtf8(), "utf-8") + if isPath(im): + im = Image.open(im) + + if im.mode == "1": + format = QImage.Format_Mono + elif im.mode == "L": + format = QImage.Format_Indexed8 + colortable = [] + for i in range(256): + colortable.append(rgb(i, i, i)) + elif im.mode == "P": + format = QImage.Format_Indexed8 + colortable = [] + palette = im.getpalette() + for i in range(0, len(palette), 3): + colortable.append(rgb(*palette[i:i+3])) + elif im.mode == "RGB": + data = im.tobytes("raw", "BGRX") + format = QImage.Format_RGB32 + elif im.mode == "RGBA": + try: + data = im.tobytes("raw", "BGRA") + except SystemError: + # workaround for earlier versions + r, g, b, a = im.split() + im = Image.merge("RGBA", (b, g, r, a)) + format = QImage.Format_ARGB32 + else: + raise ValueError("unsupported image mode %r" % im.mode) + + # must keep a reference, or Qt will crash! + self.__data = data or im.tobytes() + + QImage.__init__(self, self.__data, im.size[0], im.size[1], format) + + if colortable: + self.setColorTable(colortable) diff --git a/pyPackages/pillowx86/PIL/ImageSequence.py b/pyPackages/pillowx86/PIL/ImageSequence.py new file mode 100644 index 0000000..dd01e29 --- /dev/null +++ b/pyPackages/pillowx86/PIL/ImageSequence.py @@ -0,0 +1,42 @@ +# +# The Python Imaging Library. +# $Id$ +# +# sequence support classes +# +# history: +# 1997-02-20 fl Created +# +# Copyright (c) 1997 by Secret Labs AB. +# Copyright (c) 1997 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +## + + +class Iterator: + """ + This class implements an iterator object that can be used to loop + over an image sequence. + + You can use the ``[]`` operator to access elements by index. This operator + will raise an :py:exc:`IndexError` if you try to access a nonexistent + frame. + + :param im: An image object. + """ + + def __init__(self, im): + if not hasattr(im, "seek"): + raise AttributeError("im must have seek method") + self.im = im + + def __getitem__(self, ix): + try: + if ix: + self.im.seek(ix) + return self.im + except EOFError: + raise IndexError # end of sequence diff --git a/pyPackages/pillowx86/PIL/ImageShow.py b/pyPackages/pillowx86/PIL/ImageShow.py new file mode 100644 index 0000000..9527dbf --- /dev/null +++ b/pyPackages/pillowx86/PIL/ImageShow.py @@ -0,0 +1,179 @@ +# +# The Python Imaging Library. +# $Id$ +# +# im.show() drivers +# +# History: +# 2008-04-06 fl Created +# +# Copyright (c) Secret Labs AB 2008. +# +# See the README file for information on usage and redistribution. +# + +from __future__ import print_function + +from PIL import Image +import os +import sys + +if sys.version_info >= (3, 3): + from shlex import quote +else: + from pipes import quote + +_viewers = [] + + +def register(viewer, order=1): + try: + if issubclass(viewer, Viewer): + viewer = viewer() + except TypeError: + pass # raised if viewer wasn't a class + if order > 0: + _viewers.append(viewer) + elif order < 0: + _viewers.insert(0, viewer) + + +## +# Displays a given image. +# +# @param image An image object. +# @param title Optional title. Not all viewers can display the title. +# @param **options Additional viewer options. +# @return True if a suitable viewer was found, false otherwise. + +def show(image, title=None, **options): + for viewer in _viewers: + if viewer.show(image, title=title, **options): + return 1 + return 0 + + +## +# Base class for viewers. + +class Viewer: + + # main api + + def show(self, image, **options): + + # save temporary image to disk + if image.mode[:4] == "I;16": + # @PIL88 @PIL101 + # "I;16" isn't an 'official' mode, but we still want to + # provide a simple way to show 16-bit images. + base = "L" + # FIXME: auto-contrast if max() > 255? + else: + base = Image.getmodebase(image.mode) + if base != image.mode and image.mode != "1": + image = image.convert(base) + + return self.show_image(image, **options) + + # hook methods + + format = None + + def get_format(self, image): + # return format name, or None to save as PGM/PPM + return self.format + + def get_command(self, file, **options): + raise NotImplementedError + + def save_image(self, image): + # save to temporary file, and return filename + return image._dump(format=self.get_format(image)) + + def show_image(self, image, **options): + # display given image + return self.show_file(self.save_image(image), **options) + + def show_file(self, file, **options): + # display given file + os.system(self.get_command(file, **options)) + return 1 + +# -------------------------------------------------------------------- + +if sys.platform == "win32": + + class WindowsViewer(Viewer): + format = "BMP" + + def get_command(self, file, **options): + return ('start "Pillow" /WAIT "%s" ' + '&& ping -n 2 127.0.0.1 >NUL ' + '&& del /f "%s"' % (file, file)) + + register(WindowsViewer) + +elif sys.platform == "darwin": + + class MacViewer(Viewer): + format = "BMP" + + def get_command(self, file, **options): + # on darwin open returns immediately resulting in the temp + # file removal while app is opening + command = "open -a /Applications/Preview.app" + command = "(%s %s; sleep 20; rm -f %s)&" % (command, quote(file), + quote(file)) + return command + + register(MacViewer) + +else: + + # unixoids + + def which(executable): + path = os.environ.get("PATH") + if not path: + return None + for dirname in path.split(os.pathsep): + filename = os.path.join(dirname, executable) + if os.path.isfile(filename): + # FIXME: make sure it's executable + return filename + return None + + class UnixViewer(Viewer): + def show_file(self, file, **options): + command, executable = self.get_command_ex(file, **options) + command = "(%s %s; rm -f %s)&" % (command, quote(file), + quote(file)) + os.system(command) + return 1 + + # implementations + + class DisplayViewer(UnixViewer): + def get_command_ex(self, file, **options): + command = executable = "display" + return command, executable + + if which("display"): + register(DisplayViewer) + + class XVViewer(UnixViewer): + def get_command_ex(self, file, title=None, **options): + # note: xv is pretty outdated. most modern systems have + # imagemagick's display command instead. + command = executable = "xv" + if title: + command += " -name %s" % quote(title) + return command, executable + + if which("xv"): + register(XVViewer) + +if __name__ == "__main__": + # usage: python ImageShow.py imagefile [title] + print(show(Image.open(sys.argv[1]), *sys.argv[2:])) diff --git a/pyPackages/pillowx86/PIL/ImageStat.py b/pyPackages/pillowx86/PIL/ImageStat.py new file mode 100644 index 0000000..7e023c6 --- /dev/null +++ b/pyPackages/pillowx86/PIL/ImageStat.py @@ -0,0 +1,147 @@ +# +# The Python Imaging Library. +# $Id$ +# +# global image statistics +# +# History: +# 1996-04-05 fl Created +# 1997-05-21 fl Added mask; added rms, var, stddev attributes +# 1997-08-05 fl Added median +# 1998-07-05 hk Fixed integer overflow error +# +# Notes: +# This class shows how to implement delayed evaluation of attributes. +# To get a certain value, simply access the corresponding attribute. +# The __getattr__ dispatcher takes care of the rest. +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996-97. +# +# See the README file for information on usage and redistribution. +# + +import math +import operator +from functools import reduce + + +class Stat: + + def __init__(self, image_or_list, mask=None): + try: + if mask: + self.h = image_or_list.histogram(mask) + else: + self.h = image_or_list.histogram() + except AttributeError: + self.h = image_or_list # assume it to be a histogram list + if not isinstance(self.h, list): + raise TypeError("first argument must be image or list") + self.bands = list(range(len(self.h) // 256)) + + def __getattr__(self, id): + "Calculate missing attribute" + if id[:4] == "_get": + raise AttributeError(id) + # calculate missing attribute + v = getattr(self, "_get" + id)() + setattr(self, id, v) + return v + + def _getextrema(self): + "Get min/max values for each band in the image" + + def minmax(histogram): + n = 255 + x = 0 + for i in range(256): + if histogram[i]: + n = min(n, i) + x = max(x, i) + return n, x # returns (255, 0) if there's no data in the histogram + + v = [] + for i in range(0, len(self.h), 256): + v.append(minmax(self.h[i:])) + return v + + def _getcount(self): + "Get total number of pixels in each layer" + + v = [] + for i in range(0, len(self.h), 256): + v.append(reduce(operator.add, self.h[i:i+256])) + return v + + def _getsum(self): + "Get sum of all pixels in each layer" + + v = [] + for i in range(0, len(self.h), 256): + sum = 0.0 + for j in range(256): + sum += j * self.h[i + j] + v.append(sum) + return v + + def _getsum2(self): + "Get squared sum of all pixels in each layer" + + v = [] + for i in range(0, len(self.h), 256): + sum2 = 0.0 + for j in range(256): + sum2 += (j ** 2) * float(self.h[i + j]) + v.append(sum2) + return v + + def _getmean(self): + "Get average pixel level for each layer" + + v = [] + for i in self.bands: + v.append(self.sum[i] / self.count[i]) + return v + + def _getmedian(self): + "Get median pixel level for each layer" + + v = [] + for i in self.bands: + s = 0 + l = self.count[i]//2 + b = i * 256 + for j in range(256): + s = s + self.h[b+j] + if s > l: + break + v.append(j) + return v + + def _getrms(self): + "Get RMS for each layer" + + v = [] + for i in self.bands: + v.append(math.sqrt(self.sum2[i] / self.count[i])) + return v + + def _getvar(self): + "Get variance for each layer" + + v = [] + for i in self.bands: + n = self.count[i] + v.append((self.sum2[i]-(self.sum[i]**2.0)/n)/n) + return v + + def _getstddev(self): + "Get standard deviation for each layer" + + v = [] + for i in self.bands: + v.append(math.sqrt(self.var[i])) + return v + +Global = Stat # compatibility diff --git a/pyPackages/pillowx86/PIL/ImageTk.py b/pyPackages/pillowx86/PIL/ImageTk.py new file mode 100644 index 0000000..5fb5ecf --- /dev/null +++ b/pyPackages/pillowx86/PIL/ImageTk.py @@ -0,0 +1,292 @@ +# +# The Python Imaging Library. +# $Id$ +# +# a Tk display interface +# +# History: +# 96-04-08 fl Created +# 96-09-06 fl Added getimage method +# 96-11-01 fl Rewritten, removed image attribute and crop method +# 97-05-09 fl Use PyImagingPaste method instead of image type +# 97-05-12 fl Minor tweaks to match the IFUNC95 interface +# 97-05-17 fl Support the "pilbitmap" booster patch +# 97-06-05 fl Added file= and data= argument to image constructors +# 98-03-09 fl Added width and height methods to Image classes +# 98-07-02 fl Use default mode for "P" images without palette attribute +# 98-07-02 fl Explicitly destroy Tkinter image objects +# 99-07-24 fl Support multiple Tk interpreters (from Greg Couch) +# 99-07-26 fl Automatically hook into Tkinter (if possible) +# 99-08-15 fl Hook uses _imagingtk instead of _imaging +# +# Copyright (c) 1997-1999 by Secret Labs AB +# Copyright (c) 1996-1997 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +try: + import tkinter +except ImportError: + import Tkinter + tkinter = Tkinter + del Tkinter + +from PIL import Image + + +# -------------------------------------------------------------------- +# Check for Tkinter interface hooks + +_pilbitmap_ok = None + + +def _pilbitmap_check(): + global _pilbitmap_ok + if _pilbitmap_ok is None: + try: + im = Image.new("1", (1, 1)) + tkinter.BitmapImage(data="PIL:%d" % im.im.id) + _pilbitmap_ok = 1 + except tkinter.TclError: + _pilbitmap_ok = 0 + return _pilbitmap_ok + + +# -------------------------------------------------------------------- +# PhotoImage + +class PhotoImage: + """ + A Tkinter-compatible photo image. This can be used + everywhere Tkinter expects an image object. If the image is an RGBA + image, pixels having alpha 0 are treated as transparent. + + The constructor takes either a PIL image, or a mode and a size. + Alternatively, you can use the **file** or **data** options to initialize + the photo image object. + + :param image: Either a PIL image, or a mode string. If a mode string is + used, a size must also be given. + :param size: If the first argument is a mode string, this defines the size + of the image. + :keyword file: A filename to load the image from (using + ``Image.open(file)``). + :keyword data: An 8-bit string containing image data (as loaded from an + image file). + """ + + def __init__(self, image=None, size=None, **kw): + + # Tk compatibility: file or data + if image is None: + if "file" in kw: + image = Image.open(kw["file"]) + del kw["file"] + elif "data" in kw: + from io import BytesIO + image = Image.open(BytesIO(kw["data"])) + del kw["data"] + + if hasattr(image, "mode") and hasattr(image, "size"): + # got an image instead of a mode + mode = image.mode + if mode == "P": + # palette mapped data + image.load() + try: + mode = image.palette.mode + except AttributeError: + mode = "RGB" # default + size = image.size + kw["width"], kw["height"] = size + else: + mode = image + image = None + + if mode not in ["1", "L", "RGB", "RGBA"]: + mode = Image.getmodebase(mode) + + self.__mode = mode + self.__size = size + self.__photo = tkinter.PhotoImage(**kw) + self.tk = self.__photo.tk + if image: + self.paste(image) + + def __del__(self): + name = self.__photo.name + self.__photo.name = None + try: + self.__photo.tk.call("image", "delete", name) + except: + pass # ignore internal errors + + def __str__(self): + """ + Get the Tkinter photo image identifier. This method is automatically + called by Tkinter whenever a PhotoImage object is passed to a Tkinter + method. + + :return: A Tkinter photo image identifier (a string). + """ + return str(self.__photo) + + def width(self): + """ + Get the width of the image. + + :return: The width, in pixels. + """ + return self.__size[0] + + def height(self): + """ + Get the height of the image. + + :return: The height, in pixels. + """ + return self.__size[1] + + def paste(self, im, box=None): + """ + Paste a PIL image into the photo image. Note that this can + be very slow if the photo image is displayed. + + :param im: A PIL image. The size must match the target region. If the + mode does not match, the image is converted to the mode of + the bitmap image. + :param box: A 4-tuple defining the left, upper, right, and lower pixel + coordinate. If None is given instead of a tuple, all of + the image is assumed. + """ + + # convert to blittable + im.load() + image = im.im + if image.isblock() and im.mode == self.__mode: + block = image + else: + block = image.new_block(self.__mode, im.size) + image.convert2(block, image) # convert directly between buffers + + tk = self.__photo.tk + + try: + tk.call("PyImagingPhoto", self.__photo, block.id) + except tkinter.TclError: + # activate Tkinter hook + try: + from PIL import _imagingtk + try: + _imagingtk.tkinit(tk.interpaddr(), 1) + except AttributeError: + _imagingtk.tkinit(id(tk), 0) + tk.call("PyImagingPhoto", self.__photo, block.id) + except (ImportError, AttributeError, tkinter.TclError): + raise # configuration problem; cannot attach to Tkinter + +# -------------------------------------------------------------------- +# BitmapImage + + +class BitmapImage: + """ + + A Tkinter-compatible bitmap image. This can be used everywhere Tkinter + expects an image object. + + The given image must have mode "1". Pixels having value 0 are treated as + transparent. Options, if any, are passed on to Tkinter. The most commonly + used option is **foreground**, which is used to specify the color for the + non-transparent parts. See the Tkinter documentation for information on + how to specify colours. + + :param image: A PIL image. + """ + + def __init__(self, image=None, **kw): + + # Tk compatibility: file or data + if image is None: + if "file" in kw: + image = Image.open(kw["file"]) + del kw["file"] + elif "data" in kw: + from io import BytesIO + image = Image.open(BytesIO(kw["data"])) + del kw["data"] + + self.__mode = image.mode + self.__size = image.size + + if _pilbitmap_check(): + # fast way (requires the pilbitmap booster patch) + image.load() + kw["data"] = "PIL:%d" % image.im.id + self.__im = image # must keep a reference + else: + # slow but safe way + kw["data"] = image.tobitmap() + self.__photo = tkinter.BitmapImage(**kw) + + def __del__(self): + name = self.__photo.name + self.__photo.name = None + try: + self.__photo.tk.call("image", "delete", name) + except: + pass # ignore internal errors + + def width(self): + """ + Get the width of the image. + + :return: The width, in pixels. + """ + return self.__size[0] + + def height(self): + """ + Get the height of the image. + + :return: The height, in pixels. + """ + return self.__size[1] + + def __str__(self): + """ + Get the Tkinter bitmap image identifier. This method is automatically + called by Tkinter whenever a BitmapImage object is passed to a Tkinter + method. + + :return: A Tkinter bitmap image identifier (a string). + """ + return str(self.__photo) + + +def getimage(photo): + """Copies the contents of a PhotoImage to a PIL image memory.""" + photo.tk.call("PyImagingPhotoGet", photo) + + +# -------------------------------------------------------------------- +# Helper for the Image.show method. + +def _show(image, title): + + class UI(tkinter.Label): + def __init__(self, master, im): + if im.mode == "1": + self.image = BitmapImage(im, foreground="white", master=master) + else: + self.image = PhotoImage(im, master=master) + tkinter.Label.__init__(self, master, image=self.image, + bg="black", bd=0) + + if not tkinter._default_root: + raise IOError("tkinter not initialized") + top = tkinter.Toplevel() + if title: + top.title(title) + UI(top, image).pack() diff --git a/pyPackages/pillowx86/PIL/ImageTransform.py b/pyPackages/pillowx86/PIL/ImageTransform.py new file mode 100644 index 0000000..81f9050 --- /dev/null +++ b/pyPackages/pillowx86/PIL/ImageTransform.py @@ -0,0 +1,103 @@ +# +# The Python Imaging Library. +# $Id$ +# +# transform wrappers +# +# History: +# 2002-04-08 fl Created +# +# Copyright (c) 2002 by Secret Labs AB +# Copyright (c) 2002 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image + + +class Transform(Image.ImageTransformHandler): + def __init__(self, data): + self.data = data + + def getdata(self): + return self.method, self.data + + def transform(self, size, image, **options): + # can be overridden + method, data = self.getdata() + return image.transform(size, method, data, **options) + + +## +# Define an affine image transform. +#

+# This function takes a 6-tuple (a, b, c, d, e, f) which +# contain the first two rows from an affine transform matrix. For +# each pixel (x, y) in the output image, the new value is +# taken from a position (a x + b y + c, +# d x + e y + f) in the input image, rounded to +# nearest pixel. +#

+# This function can be used to scale, translate, rotate, and shear the +# original image. +# +# @def AffineTransform(matrix) +# @param matrix A 6-tuple (a, b, c, d, e, f) containing +# the first two rows from an affine transform matrix. +# @see Image#Image.transform + + +class AffineTransform(Transform): + method = Image.AFFINE + + +## +# Define a transform to extract a subregion from an image. +#

+# Maps a rectangle (defined by two corners) from the image to a +# rectangle of the given size. The resulting image will contain +# data sampled from between the corners, such that (x0, y0) +# in the input image will end up at (0,0) in the output image, +# and (x1, y1) at size. +#

+# This method can be used to crop, stretch, shrink, or mirror an +# arbitrary rectangle in the current image. It is slightly slower than +# crop, but about as fast as a corresponding resize +# operation. +# +# @def ExtentTransform(bbox) +# @param bbox A 4-tuple (x0, y0, x1, y1) which specifies +# two points in the input image's coordinate system. +# @see Image#Image.transform + +class ExtentTransform(Transform): + method = Image.EXTENT + + +## +# Define an quad image transform. +#

+# Maps a quadrilateral (a region defined by four corners) from the +# image to a rectangle of the given size. +# +# @def QuadTransform(xy) +# @param xy An 8-tuple (x0, y0, x1, y1, x2, y2, y3, y3) which +# contain the upper left, lower left, lower right, and upper right +# corner of the source quadrilateral. +# @see Image#Image.transform + +class QuadTransform(Transform): + method = Image.QUAD + + +## +# Define an mesh image transform. A mesh transform consists of one +# or more individual quad transforms. +# +# @def MeshTransform(data) +# @param data A list of (bbox, quad) tuples. +# @see Image#Image.transform + +class MeshTransform(Transform): + method = Image.MESH diff --git a/pyPackages/pillowx86/PIL/ImageWin.py b/pyPackages/pillowx86/PIL/ImageWin.py new file mode 100644 index 0000000..300d118 --- /dev/null +++ b/pyPackages/pillowx86/PIL/ImageWin.py @@ -0,0 +1,251 @@ +# +# The Python Imaging Library. +# $Id$ +# +# a Windows DIB display interface +# +# History: +# 1996-05-20 fl Created +# 1996-09-20 fl Fixed subregion exposure +# 1997-09-21 fl Added draw primitive (for tzPrint) +# 2003-05-21 fl Added experimental Window/ImageWindow classes +# 2003-09-05 fl Added fromstring/tostring methods +# +# Copyright (c) Secret Labs AB 1997-2003. +# Copyright (c) Fredrik Lundh 1996-2003. +# +# See the README file for information on usage and redistribution. +# + +import warnings +from PIL import Image + + +class HDC: + """ + Wraps an HDC integer. The resulting object can be passed to the + :py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose` + methods. + """ + def __init__(self, dc): + self.dc = dc + + def __int__(self): + return self.dc + + +class HWND: + """ + Wraps an HWND integer. The resulting object can be passed to the + :py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose` + methods, instead of a DC. + """ + def __init__(self, wnd): + self.wnd = wnd + + def __int__(self): + return self.wnd + + +class Dib: + """ + A Windows bitmap with the given mode and size. The mode can be one of "1", + "L", "P", or "RGB". + + If the display requires a palette, this constructor creates a suitable + palette and associates it with the image. For an "L" image, 128 greylevels + are allocated. For an "RGB" image, a 6x6x6 colour cube is used, together + with 20 greylevels. + + To make sure that palettes work properly under Windows, you must call the + **palette** method upon certain events from Windows. + + :param image: Either a PIL image, or a mode string. If a mode string is + used, a size must also be given. The mode can be one of "1", + "L", "P", or "RGB". + :param size: If the first argument is a mode string, this + defines the size of the image. + """ + + def __init__(self, image, size=None): + if hasattr(image, "mode") and hasattr(image, "size"): + mode = image.mode + size = image.size + else: + mode = image + image = None + if mode not in ["1", "L", "P", "RGB"]: + mode = Image.getmodebase(mode) + self.image = Image.core.display(mode, size) + self.mode = mode + self.size = size + if image: + self.paste(image) + + def expose(self, handle): + """ + Copy the bitmap contents to a device context. + + :param handle: Device context (HDC), cast to a Python integer, or an + HDC or HWND instance. In PythonWin, you can use the + :py:meth:`CDC.GetHandleAttrib` to get a suitable handle. + """ + if isinstance(handle, HWND): + dc = self.image.getdc(handle) + try: + result = self.image.expose(dc) + finally: + self.image.releasedc(handle, dc) + else: + result = self.image.expose(handle) + return result + + def draw(self, handle, dst, src=None): + """ + Same as expose, but allows you to specify where to draw the image, and + what part of it to draw. + + The destination and source areas are given as 4-tuple rectangles. If + the source is omitted, the entire image is copied. If the source and + the destination have different sizes, the image is resized as + necessary. + """ + if not src: + src = (0, 0) + self.size + if isinstance(handle, HWND): + dc = self.image.getdc(handle) + try: + result = self.image.draw(dc, dst, src) + finally: + self.image.releasedc(handle, dc) + else: + result = self.image.draw(handle, dst, src) + return result + + def query_palette(self, handle): + """ + Installs the palette associated with the image in the given device + context. + + This method should be called upon **QUERYNEWPALETTE** and + **PALETTECHANGED** events from Windows. If this method returns a + non-zero value, one or more display palette entries were changed, and + the image should be redrawn. + + :param handle: Device context (HDC), cast to a Python integer, or an + HDC or HWND instance. + :return: A true value if one or more entries were changed (this + indicates that the image should be redrawn). + """ + if isinstance(handle, HWND): + handle = self.image.getdc(handle) + try: + result = self.image.query_palette(handle) + finally: + self.image.releasedc(handle, handle) + else: + result = self.image.query_palette(handle) + return result + + def paste(self, im, box=None): + """ + Paste a PIL image into the bitmap image. + + :param im: A PIL image. The size must match the target region. + If the mode does not match, the image is converted to the + mode of the bitmap image. + :param box: A 4-tuple defining the left, upper, right, and + lower pixel coordinate. If None is given instead of a + tuple, all of the image is assumed. + """ + im.load() + if self.mode != im.mode: + im = im.convert(self.mode) + if box: + self.image.paste(im.im, box) + else: + self.image.paste(im.im) + + def frombytes(self, buffer): + """ + Load display memory contents from byte data. + + :param buffer: A buffer containing display data (usually + data returned from tobytes) + """ + return self.image.frombytes(buffer) + + def tobytes(self): + """ + Copy display memory contents to bytes object. + + :return: A bytes object containing display data. + """ + return self.image.tobytes() + + ## + # Deprecated aliases to frombytes & tobytes. + + def fromstring(self, *args, **kw): + warnings.warn( + 'fromstring() is deprecated. Please call frombytes() instead.', + DeprecationWarning, + stacklevel=2 + ) + return self.frombytes(*args, **kw) + + def tostring(self): + warnings.warn( + 'tostring() is deprecated. Please call tobytes() instead.', + DeprecationWarning, + stacklevel=2 + ) + return self.tobytes() + + +## +# Create a Window with the given title size. + +class Window: + + def __init__(self, title="PIL", width=None, height=None): + self.hwnd = Image.core.createwindow( + title, self.__dispatcher, width or 0, height or 0 + ) + + def __dispatcher(self, action, *args): + return getattr(self, "ui_handle_" + action)(*args) + + def ui_handle_clear(self, dc, x0, y0, x1, y1): + pass + + def ui_handle_damage(self, x0, y0, x1, y1): + pass + + def ui_handle_destroy(self): + pass + + def ui_handle_repair(self, dc, x0, y0, x1, y1): + pass + + def ui_handle_resize(self, width, height): + pass + + def mainloop(self): + Image.core.eventloop() + + +## +# Create an image window which displays the given image. + +class ImageWindow(Window): + + def __init__(self, image, title="PIL"): + if not isinstance(image, Dib): + image = Dib(image) + self.image = image + width, height = image.size + Window.__init__(self, title, width=width, height=height) + + def ui_handle_repair(self, dc, x0, y0, x1, y1): + self.image.draw(dc, (x0, y0, x1, y1)) diff --git a/pyPackages/pillowx86/PIL/ImtImagePlugin.py b/pyPackages/pillowx86/PIL/ImtImagePlugin.py new file mode 100644 index 0000000..f512eb8 --- /dev/null +++ b/pyPackages/pillowx86/PIL/ImtImagePlugin.py @@ -0,0 +1,94 @@ +# +# The Python Imaging Library. +# $Id$ +# +# IM Tools support for PIL +# +# history: +# 1996-05-27 fl Created (read 8-bit images only) +# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.2) +# +# Copyright (c) Secret Labs AB 1997-2001. +# Copyright (c) Fredrik Lundh 1996-2001. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.2" + +import re + +from PIL import Image, ImageFile + +# +# -------------------------------------------------------------------- + +field = re.compile(br"([a-z]*) ([^ \r\n]*)") + + +## +# Image plugin for IM Tools images. + +class ImtImageFile(ImageFile.ImageFile): + + format = "IMT" + format_description = "IM Tools" + + def _open(self): + + # Quick rejection: if there's not a LF among the first + # 100 bytes, this is (probably) not a text header. + + if b"\n" not in self.fp.read(100): + raise SyntaxError("not an IM file") + self.fp.seek(0) + + xsize = ysize = 0 + + while True: + + s = self.fp.read(1) + if not s: + break + + if s == b'\x0C': + + # image data begins + self.tile = [("raw", (0, 0)+self.size, + self.fp.tell(), + (self.mode, 0, 1))] + + break + + else: + + # read key/value pair + # FIXME: dangerous, may read whole file + s = s + self.fp.readline() + if len(s) == 1 or len(s) > 100: + break + if s[0] == b"*": + continue # comment + + m = field.match(s) + if not m: + break + k, v = m.group(1, 2) + if k == "width": + xsize = int(v) + self.size = xsize, ysize + elif k == "height": + ysize = int(v) + self.size = xsize, ysize + elif k == "pixel" and v == "n8": + self.mode = "L" + + +# +# -------------------------------------------------------------------- + +Image.register_open("IMT", ImtImageFile) + +# +# no extension registered (".im" is simply too common) diff --git a/pyPackages/pillowx86/PIL/IptcImagePlugin.py b/pyPackages/pillowx86/PIL/IptcImagePlugin.py new file mode 100644 index 0000000..dc86075 --- /dev/null +++ b/pyPackages/pillowx86/PIL/IptcImagePlugin.py @@ -0,0 +1,268 @@ +# +# The Python Imaging Library. +# $Id$ +# +# IPTC/NAA file handling +# +# history: +# 1995-10-01 fl Created +# 1998-03-09 fl Cleaned up and added to PIL +# 2002-06-18 fl Added getiptcinfo helper +# +# Copyright (c) Secret Labs AB 1997-2002. +# Copyright (c) Fredrik Lundh 1995. +# +# See the README file for information on usage and redistribution. +# + +from __future__ import print_function + +__version__ = "0.3" + + +from PIL import Image, ImageFile, _binary +import os +import tempfile + +i8 = _binary.i8 +i16 = _binary.i16be +i32 = _binary.i32be +o8 = _binary.o8 + +COMPRESSION = { + 1: "raw", + 5: "jpeg" +} + +PAD = o8(0) * 4 + + +# +# Helpers + +def i(c): + return i32((PAD + c)[-4:]) + + +def dump(c): + for i in c: + print("%02x" % i8(i), end=' ') + print() + + +## +# Image plugin for IPTC/NAA datastreams. To read IPTC/NAA fields +# from TIFF and JPEG files, use the getiptcinfo function. + +class IptcImageFile(ImageFile.ImageFile): + + format = "IPTC" + format_description = "IPTC/NAA" + + def getint(self, key): + return i(self.info[key]) + + def field(self): + # + # get a IPTC field header + s = self.fp.read(5) + if not len(s): + return None, 0 + + tag = i8(s[1]), i8(s[2]) + + # syntax + if i8(s[0]) != 0x1C or tag[0] < 1 or tag[0] > 9: + raise SyntaxError("invalid IPTC/NAA file") + + # field size + size = i8(s[3]) + if size > 132: + raise IOError("illegal field length in IPTC/NAA file") + elif size == 128: + size = 0 + elif size > 128: + size = i(self.fp.read(size-128)) + else: + size = i16(s[3:]) + + return tag, size + + def _open(self): + + # load descriptive fields + while True: + offset = self.fp.tell() + tag, size = self.field() + if not tag or tag == (8, 10): + break + if size: + tagdata = self.fp.read(size) + else: + tagdata = None + if tag in list(self.info.keys()): + if isinstance(self.info[tag], list): + self.info[tag].append(tagdata) + else: + self.info[tag] = [self.info[tag], tagdata] + else: + self.info[tag] = tagdata + + # print tag, self.info[tag] + + # mode + layers = i8(self.info[(3, 60)][0]) + component = i8(self.info[(3, 60)][1]) + if (3, 65) in self.info: + id = i8(self.info[(3, 65)][0])-1 + else: + id = 0 + if layers == 1 and not component: + self.mode = "L" + elif layers == 3 and component: + self.mode = "RGB"[id] + elif layers == 4 and component: + self.mode = "CMYK"[id] + + # size + self.size = self.getint((3, 20)), self.getint((3, 30)) + + # compression + try: + compression = COMPRESSION[self.getint((3, 120))] + except KeyError: + raise IOError("Unknown IPTC image compression") + + # tile + if tag == (8, 10): + self.tile = [("iptc", (compression, offset), + (0, 0, self.size[0], self.size[1]))] + + def load(self): + + if len(self.tile) != 1 or self.tile[0][0] != "iptc": + return ImageFile.ImageFile.load(self) + + type, tile, box = self.tile[0] + + encoding, offset = tile + + self.fp.seek(offset) + + # Copy image data to temporary file + o_fd, outfile = tempfile.mkstemp(text=False) + o = os.fdopen(o_fd) + if encoding == "raw": + # To simplify access to the extracted file, + # prepend a PPM header + o.write("P5\n%d %d\n255\n" % self.size) + while True: + type, size = self.field() + if type != (8, 10): + break + while size > 0: + s = self.fp.read(min(size, 8192)) + if not s: + break + o.write(s) + size -= len(s) + o.close() + + try: + try: + # fast + self.im = Image.core.open_ppm(outfile) + except: + # slightly slower + im = Image.open(outfile) + im.load() + self.im = im.im + finally: + try: + os.unlink(outfile) + except: + pass + + +Image.register_open("IPTC", IptcImageFile) + +Image.register_extension("IPTC", ".iim") + + +## +# Get IPTC information from TIFF, JPEG, or IPTC file. +# +# @param im An image containing IPTC data. +# @return A dictionary containing IPTC information, or None if +# no IPTC information block was found. + +def getiptcinfo(im): + + from PIL import TiffImagePlugin, JpegImagePlugin + import io + + data = None + + if isinstance(im, IptcImageFile): + # return info dictionary right away + return im.info + + elif isinstance(im, JpegImagePlugin.JpegImageFile): + # extract the IPTC/NAA resource + try: + app = im.app["APP13"] + if app[:14] == b"Photoshop 3.0\x00": + app = app[14:] + # parse the image resource block + offset = 0 + while app[offset:offset+4] == b"8BIM": + offset += 4 + # resource code + code = JpegImagePlugin.i16(app, offset) + offset += 2 + # resource name (usually empty) + name_len = i8(app[offset]) + name = app[offset+1:offset+1+name_len] + offset = 1 + offset + name_len + if offset & 1: + offset += 1 + # resource data block + size = JpegImagePlugin.i32(app, offset) + offset += 4 + if code == 0x0404: + # 0x0404 contains IPTC/NAA data + data = app[offset:offset+size] + break + offset = offset + size + if offset & 1: + offset += 1 + except (AttributeError, KeyError): + pass + + elif isinstance(im, TiffImagePlugin.TiffImageFile): + # get raw data from the IPTC/NAA tag (PhotoShop tags the data + # as 4-byte integers, so we cannot use the get method...) + try: + data = im.tag.tagdata[TiffImagePlugin.IPTC_NAA_CHUNK] + except (AttributeError, KeyError): + pass + + if data is None: + return None # no properties + + # create an IptcImagePlugin object without initializing it + class FakeImage: + pass + im = FakeImage() + im.__class__ = IptcImageFile + + # parse the IPTC information chunk + im.info = {} + im.fp = io.BytesIO(data) + + try: + im._open() + except (IndexError, KeyError): + pass # expected failure + + return im.info diff --git a/pyPackages/pillowx86/PIL/Jpeg2KImagePlugin.py b/pyPackages/pillowx86/PIL/Jpeg2KImagePlugin.py new file mode 100644 index 0000000..446699f --- /dev/null +++ b/pyPackages/pillowx86/PIL/Jpeg2KImagePlugin.py @@ -0,0 +1,277 @@ +# +# The Python Imaging Library +# $Id$ +# +# JPEG2000 file handling +# +# History: +# 2014-03-12 ajh Created +# +# Copyright (c) 2014 Coriolis Systems Limited +# Copyright (c) 2014 Alastair Houghton +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.1" + +from PIL import Image, ImageFile +import struct +import os +import io + + +def _parse_codestream(fp): + """Parse the JPEG 2000 codestream to extract the size and component + count from the SIZ marker segment, returning a PIL (size, mode) tuple.""" + + hdr = fp.read(2) + lsiz = struct.unpack('>H', hdr)[0] + siz = hdr + fp.read(lsiz - 2) + lsiz, rsiz, xsiz, ysiz, xosiz, yosiz, xtsiz, ytsiz, \ + xtosiz, ytosiz, csiz \ + = struct.unpack('>HHIIIIIIIIH', siz[:38]) + ssiz = [None]*csiz + xrsiz = [None]*csiz + yrsiz = [None]*csiz + for i in range(csiz): + ssiz[i], xrsiz[i], yrsiz[i] \ + = struct.unpack('>BBB', siz[36 + 3 * i:39 + 3 * i]) + + size = (xsiz - xosiz, ysiz - yosiz) + if csiz == 1: + if (yrsiz[0] & 0x7f) > 8: + mode = 'I;16' + else: + mode = 'L' + elif csiz == 2: + mode = 'LA' + elif csiz == 3: + mode = 'RGB' + elif csiz == 4: + mode = 'RGBA' + else: + mode = None + + return (size, mode) + + +def _parse_jp2_header(fp): + """Parse the JP2 header box to extract size, component count and + color space information, returning a PIL (size, mode) tuple.""" + + # Find the JP2 header box + header = None + while True: + lbox, tbox = struct.unpack('>I4s', fp.read(8)) + if lbox == 1: + lbox = struct.unpack('>Q', fp.read(8))[0] + hlen = 16 + else: + hlen = 8 + + if lbox < hlen: + raise SyntaxError('Invalid JP2 header length') + + if tbox == b'jp2h': + header = fp.read(lbox - hlen) + break + else: + fp.seek(lbox - hlen, os.SEEK_CUR) + + if header is None: + raise SyntaxError('could not find JP2 header') + + size = None + mode = None + bpc = None + + hio = io.BytesIO(header) + while True: + lbox, tbox = struct.unpack('>I4s', hio.read(8)) + if lbox == 1: + lbox = struct.unpack('>Q', hio.read(8))[0] + hlen = 16 + else: + hlen = 8 + + content = hio.read(lbox - hlen) + + if tbox == b'ihdr': + height, width, nc, bpc, c, unkc, ipr \ + = struct.unpack('>IIHBBBB', content) + size = (width, height) + if unkc: + if nc == 1 and (bpc & 0x7f) > 8: + mode = 'I;16' + elif nc == 1: + mode = 'L' + elif nc == 2: + mode = 'LA' + elif nc == 3: + mode = 'RGB' + elif nc == 4: + mode = 'RGBA' + break + elif tbox == b'colr': + meth, prec, approx = struct.unpack('>BBB', content[:3]) + if meth == 1: + cs = struct.unpack('>I', content[3:7])[0] + if cs == 16: # sRGB + if nc == 1 and (bpc & 0x7f) > 8: + mode = 'I;16' + elif nc == 1: + mode = 'L' + elif nc == 3: + mode = 'RGB' + elif nc == 4: + mode = 'RGBA' + break + elif cs == 17: # grayscale + if nc == 1 and (bpc & 0x7f) > 8: + mode = 'I;16' + elif nc == 1: + mode = 'L' + elif nc == 2: + mode = 'LA' + break + elif cs == 18: # sYCC + if nc == 3: + mode = 'RGB' + elif nc == 4: + mode = 'RGBA' + break + + return (size, mode) + +## +# Image plugin for JPEG2000 images. + + +class Jpeg2KImageFile(ImageFile.ImageFile): + format = "JPEG2000" + format_description = "JPEG 2000 (ISO 15444)" + + def _open(self): + sig = self.fp.read(4) + if sig == b'\xff\x4f\xff\x51': + self.codec = "j2k" + self.size, self.mode = _parse_codestream(self.fp) + else: + sig = sig + self.fp.read(8) + + if sig == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a': + self.codec = "jp2" + self.size, self.mode = _parse_jp2_header(self.fp) + else: + raise SyntaxError('not a JPEG 2000 file') + + if self.size is None or self.mode is None: + raise SyntaxError('unable to determine size/mode') + + self.reduce = 0 + self.layers = 0 + + fd = -1 + length = -1 + + try: + fd = self.fp.fileno() + length = os.fstat(fd).st_size + except: + fd = -1 + try: + pos = self.fp.tell() + self.fp.seek(0, 2) + length = self.fp.tell() + self.fp.seek(pos, 0) + except: + length = -1 + + self.tile = [('jpeg2k', (0, 0) + self.size, 0, + (self.codec, self.reduce, self.layers, fd, length))] + + def load(self): + if self.reduce: + power = 1 << self.reduce + adjust = power >> 1 + self.size = (int((self.size[0] + adjust) / power), + int((self.size[1] + adjust) / power)) + + if self.tile: + # Update the reduce and layers settings + t = self.tile[0] + t3 = (t[3][0], self.reduce, self.layers, t[3][3], t[3][4]) + self.tile = [(t[0], (0, 0) + self.size, t[2], t3)] + + ImageFile.ImageFile.load(self) + + +def _accept(prefix): + return (prefix[:4] == b'\xff\x4f\xff\x51' + or prefix[:12] == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a') + + +# ------------------------------------------------------------ +# Save support + +def _save(im, fp, filename): + if filename.endswith('.j2k'): + kind = 'j2k' + else: + kind = 'jp2' + + # Get the keyword arguments + info = im.encoderinfo + + offset = info.get('offset', None) + tile_offset = info.get('tile_offset', None) + tile_size = info.get('tile_size', None) + quality_mode = info.get('quality_mode', 'rates') + quality_layers = info.get('quality_layers', None) + num_resolutions = info.get('num_resolutions', 0) + cblk_size = info.get('codeblock_size', None) + precinct_size = info.get('precinct_size', None) + irreversible = info.get('irreversible', False) + progression = info.get('progression', 'LRCP') + cinema_mode = info.get('cinema_mode', 'no') + fd = -1 + + if hasattr(fp, "fileno"): + try: + fd = fp.fileno() + except: + fd = -1 + + im.encoderconfig = ( + offset, + tile_offset, + tile_size, + quality_mode, + quality_layers, + num_resolutions, + cblk_size, + precinct_size, + irreversible, + progression, + cinema_mode, + fd + ) + + ImageFile._save(im, fp, [('jpeg2k', (0, 0)+im.size, 0, kind)]) + +# ------------------------------------------------------------ +# Registry stuff + +Image.register_open('JPEG2000', Jpeg2KImageFile, _accept) +Image.register_save('JPEG2000', _save) + +Image.register_extension('JPEG2000', '.jp2') +Image.register_extension('JPEG2000', '.j2k') +Image.register_extension('JPEG2000', '.jpc') +Image.register_extension('JPEG2000', '.jpf') +Image.register_extension('JPEG2000', '.jpx') +Image.register_extension('JPEG2000', '.j2c') + +Image.register_mime('JPEG2000', 'image/jp2') +Image.register_mime('JPEG2000', 'image/jpx') diff --git a/pyPackages/pillowx86/PIL/JpegImagePlugin.py b/pyPackages/pillowx86/PIL/JpegImagePlugin.py new file mode 100644 index 0000000..9dd79d5 --- /dev/null +++ b/pyPackages/pillowx86/PIL/JpegImagePlugin.py @@ -0,0 +1,738 @@ +# +# The Python Imaging Library. +# $Id$ +# +# JPEG (JFIF) file handling +# +# See "Digital Compression and Coding of Continous-Tone Still Images, +# Part 1, Requirements and Guidelines" (CCITT T.81 / ISO 10918-1) +# +# History: +# 1995-09-09 fl Created +# 1995-09-13 fl Added full parser +# 1996-03-25 fl Added hack to use the IJG command line utilities +# 1996-05-05 fl Workaround Photoshop 2.5 CMYK polarity bug +# 1996-05-28 fl Added draft support, JFIF version (0.1) +# 1996-12-30 fl Added encoder options, added progression property (0.2) +# 1997-08-27 fl Save mode 1 images as BW (0.3) +# 1998-07-12 fl Added YCbCr to draft and save methods (0.4) +# 1998-10-19 fl Don't hang on files using 16-bit DQT's (0.4.1) +# 2001-04-16 fl Extract DPI settings from JFIF files (0.4.2) +# 2002-07-01 fl Skip pad bytes before markers; identify Exif files (0.4.3) +# 2003-04-25 fl Added experimental EXIF decoder (0.5) +# 2003-06-06 fl Added experimental EXIF GPSinfo decoder +# 2003-09-13 fl Extract COM markers +# 2009-09-06 fl Added icc_profile support (from Florian Hoech) +# 2009-03-06 fl Changed CMYK handling; always use Adobe polarity (0.6) +# 2009-03-08 fl Added subsampling support (from Justin Huff). +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1995-1996 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.6" + +import array +import struct +import io +from struct import unpack +from PIL import Image, ImageFile, TiffImagePlugin, _binary +from PIL.JpegPresets import presets +from PIL._util import isStringType + +i8 = _binary.i8 +o8 = _binary.o8 +i16 = _binary.i16be +i32 = _binary.i32be + + +# +# Parser + +def Skip(self, marker): + n = i16(self.fp.read(2))-2 + ImageFile._safe_read(self.fp, n) + + +def APP(self, marker): + # + # Application marker. Store these in the APP dictionary. + # Also look for well-known application markers. + + n = i16(self.fp.read(2))-2 + s = ImageFile._safe_read(self.fp, n) + + app = "APP%d" % (marker & 15) + + self.app[app] = s # compatibility + self.applist.append((app, s)) + + if marker == 0xFFE0 and s[:4] == b"JFIF": + # extract JFIF information + self.info["jfif"] = version = i16(s, 5) # version + self.info["jfif_version"] = divmod(version, 256) + # extract JFIF properties + try: + jfif_unit = i8(s[7]) + jfif_density = i16(s, 8), i16(s, 10) + except: + pass + else: + if jfif_unit == 1: + self.info["dpi"] = jfif_density + self.info["jfif_unit"] = jfif_unit + self.info["jfif_density"] = jfif_density + elif marker == 0xFFE1 and s[:5] == b"Exif\0": + # extract Exif information (incomplete) + self.info["exif"] = s # FIXME: value will change + elif marker == 0xFFE2 and s[:5] == b"FPXR\0": + # extract FlashPix information (incomplete) + self.info["flashpix"] = s # FIXME: value will change + elif marker == 0xFFE2 and s[:12] == b"ICC_PROFILE\0": + # Since an ICC profile can be larger than the maximum size of + # a JPEG marker (64K), we need provisions to split it into + # multiple markers. The format defined by the ICC specifies + # one or more APP2 markers containing the following data: + # Identifying string ASCII "ICC_PROFILE\0" (12 bytes) + # Marker sequence number 1, 2, etc (1 byte) + # Number of markers Total of APP2's used (1 byte) + # Profile data (remainder of APP2 data) + # Decoders should use the marker sequence numbers to + # reassemble the profile, rather than assuming that the APP2 + # markers appear in the correct sequence. + self.icclist.append(s) + elif marker == 0xFFEE and s[:5] == b"Adobe": + self.info["adobe"] = i16(s, 5) + # extract Adobe custom properties + try: + adobe_transform = i8(s[1]) + except: + pass + else: + self.info["adobe_transform"] = adobe_transform + elif marker == 0xFFE2 and s[:4] == b"MPF\0": + # extract MPO information + self.info["mp"] = s[4:] + # offset is current location minus buffer size + # plus constant header size + self.info["mpoffset"] = self.fp.tell() - n + 4 + + +def COM(self, marker): + # + # Comment marker. Store these in the APP dictionary. + n = i16(self.fp.read(2))-2 + s = ImageFile._safe_read(self.fp, n) + + self.app["COM"] = s # compatibility + self.applist.append(("COM", s)) + + +def SOF(self, marker): + # + # Start of frame marker. Defines the size and mode of the + # image. JPEG is colour blind, so we use some simple + # heuristics to map the number of layers to an appropriate + # mode. Note that this could be made a bit brighter, by + # looking for JFIF and Adobe APP markers. + + n = i16(self.fp.read(2))-2 + s = ImageFile._safe_read(self.fp, n) + self.size = i16(s[3:]), i16(s[1:]) + + self.bits = i8(s[0]) + if self.bits != 8: + raise SyntaxError("cannot handle %d-bit layers" % self.bits) + + self.layers = i8(s[5]) + if self.layers == 1: + self.mode = "L" + elif self.layers == 3: + self.mode = "RGB" + elif self.layers == 4: + self.mode = "CMYK" + else: + raise SyntaxError("cannot handle %d-layer images" % self.layers) + + if marker in [0xFFC2, 0xFFC6, 0xFFCA, 0xFFCE]: + self.info["progressive"] = self.info["progression"] = 1 + + if self.icclist: + # fixup icc profile + self.icclist.sort() # sort by sequence number + if i8(self.icclist[0][13]) == len(self.icclist): + profile = [] + for p in self.icclist: + profile.append(p[14:]) + icc_profile = b"".join(profile) + else: + icc_profile = None # wrong number of fragments + self.info["icc_profile"] = icc_profile + self.icclist = None + + for i in range(6, len(s), 3): + t = s[i:i+3] + # 4-tuples: id, vsamp, hsamp, qtable + self.layer.append((t[0], i8(t[1])//16, i8(t[1]) & 15, i8(t[2]))) + + +def DQT(self, marker): + # + # Define quantization table. Support baseline 8-bit tables + # only. Note that there might be more than one table in + # each marker. + + # FIXME: The quantization tables can be used to estimate the + # compression quality. + + n = i16(self.fp.read(2))-2 + s = ImageFile._safe_read(self.fp, n) + while len(s): + if len(s) < 65: + raise SyntaxError("bad quantization table marker") + v = i8(s[0]) + if v//16 == 0: + self.quantization[v & 15] = array.array("b", s[1:65]) + s = s[65:] + else: + return # FIXME: add code to read 16-bit tables! + # raise SyntaxError, "bad quantization table element size" + + +# +# JPEG marker table + +MARKER = { + 0xFFC0: ("SOF0", "Baseline DCT", SOF), + 0xFFC1: ("SOF1", "Extended Sequential DCT", SOF), + 0xFFC2: ("SOF2", "Progressive DCT", SOF), + 0xFFC3: ("SOF3", "Spatial lossless", SOF), + 0xFFC4: ("DHT", "Define Huffman table", Skip), + 0xFFC5: ("SOF5", "Differential sequential DCT", SOF), + 0xFFC6: ("SOF6", "Differential progressive DCT", SOF), + 0xFFC7: ("SOF7", "Differential spatial", SOF), + 0xFFC8: ("JPG", "Extension", None), + 0xFFC9: ("SOF9", "Extended sequential DCT (AC)", SOF), + 0xFFCA: ("SOF10", "Progressive DCT (AC)", SOF), + 0xFFCB: ("SOF11", "Spatial lossless DCT (AC)", SOF), + 0xFFCC: ("DAC", "Define arithmetic coding conditioning", Skip), + 0xFFCD: ("SOF13", "Differential sequential DCT (AC)", SOF), + 0xFFCE: ("SOF14", "Differential progressive DCT (AC)", SOF), + 0xFFCF: ("SOF15", "Differential spatial (AC)", SOF), + 0xFFD0: ("RST0", "Restart 0", None), + 0xFFD1: ("RST1", "Restart 1", None), + 0xFFD2: ("RST2", "Restart 2", None), + 0xFFD3: ("RST3", "Restart 3", None), + 0xFFD4: ("RST4", "Restart 4", None), + 0xFFD5: ("RST5", "Restart 5", None), + 0xFFD6: ("RST6", "Restart 6", None), + 0xFFD7: ("RST7", "Restart 7", None), + 0xFFD8: ("SOI", "Start of image", None), + 0xFFD9: ("EOI", "End of image", None), + 0xFFDA: ("SOS", "Start of scan", Skip), + 0xFFDB: ("DQT", "Define quantization table", DQT), + 0xFFDC: ("DNL", "Define number of lines", Skip), + 0xFFDD: ("DRI", "Define restart interval", Skip), + 0xFFDE: ("DHP", "Define hierarchical progression", SOF), + 0xFFDF: ("EXP", "Expand reference component", Skip), + 0xFFE0: ("APP0", "Application segment 0", APP), + 0xFFE1: ("APP1", "Application segment 1", APP), + 0xFFE2: ("APP2", "Application segment 2", APP), + 0xFFE3: ("APP3", "Application segment 3", APP), + 0xFFE4: ("APP4", "Application segment 4", APP), + 0xFFE5: ("APP5", "Application segment 5", APP), + 0xFFE6: ("APP6", "Application segment 6", APP), + 0xFFE7: ("APP7", "Application segment 7", APP), + 0xFFE8: ("APP8", "Application segment 8", APP), + 0xFFE9: ("APP9", "Application segment 9", APP), + 0xFFEA: ("APP10", "Application segment 10", APP), + 0xFFEB: ("APP11", "Application segment 11", APP), + 0xFFEC: ("APP12", "Application segment 12", APP), + 0xFFED: ("APP13", "Application segment 13", APP), + 0xFFEE: ("APP14", "Application segment 14", APP), + 0xFFEF: ("APP15", "Application segment 15", APP), + 0xFFF0: ("JPG0", "Extension 0", None), + 0xFFF1: ("JPG1", "Extension 1", None), + 0xFFF2: ("JPG2", "Extension 2", None), + 0xFFF3: ("JPG3", "Extension 3", None), + 0xFFF4: ("JPG4", "Extension 4", None), + 0xFFF5: ("JPG5", "Extension 5", None), + 0xFFF6: ("JPG6", "Extension 6", None), + 0xFFF7: ("JPG7", "Extension 7", None), + 0xFFF8: ("JPG8", "Extension 8", None), + 0xFFF9: ("JPG9", "Extension 9", None), + 0xFFFA: ("JPG10", "Extension 10", None), + 0xFFFB: ("JPG11", "Extension 11", None), + 0xFFFC: ("JPG12", "Extension 12", None), + 0xFFFD: ("JPG13", "Extension 13", None), + 0xFFFE: ("COM", "Comment", COM) +} + + +def _accept(prefix): + return prefix[0:1] == b"\377" + + +## +# Image plugin for JPEG and JFIF images. + +class JpegImageFile(ImageFile.ImageFile): + + format = "JPEG" + format_description = "JPEG (ISO 10918)" + + def _open(self): + + s = self.fp.read(1) + + if i8(s[0]) != 255: + raise SyntaxError("not a JPEG file") + + # Create attributes + self.bits = self.layers = 0 + + # JPEG specifics (internal) + self.layer = [] + self.huffman_dc = {} + self.huffman_ac = {} + self.quantization = {} + self.app = {} # compatibility + self.applist = [] + self.icclist = [] + + while True: + + i = i8(s) + if i == 0xFF: + s = s + self.fp.read(1) + i = i16(s) + else: + # Skip non-0xFF junk + s = b"\xff" + continue + + if i in MARKER: + name, description, handler = MARKER[i] + # print hex(i), name, description + if handler is not None: + handler(self, i) + if i == 0xFFDA: # start of scan + rawmode = self.mode + if self.mode == "CMYK": + rawmode = "CMYK;I" # assume adobe conventions + self.tile = [("jpeg", (0, 0) + self.size, 0, + (rawmode, ""))] + # self.__offset = self.fp.tell() + break + s = self.fp.read(1) + elif i == 0 or i == 0xFFFF: + # padded marker or junk; move on + s = b"\xff" + else: + raise SyntaxError("no marker found") + + def draft(self, mode, size): + + if len(self.tile) != 1: + return + + d, e, o, a = self.tile[0] + scale = 0 + + if a[0] == "RGB" and mode in ["L", "YCbCr"]: + self.mode = mode + a = mode, "" + + if size: + scale = max(self.size[0] // size[0], self.size[1] // size[1]) + for s in [8, 4, 2, 1]: + if scale >= s: + break + e = e[0], e[1], (e[2]-e[0]+s-1)//s+e[0], (e[3]-e[1]+s-1)//s+e[1] + self.size = ((self.size[0]+s-1)//s, (self.size[1]+s-1)//s) + scale = s + + self.tile = [(d, e, o, a)] + self.decoderconfig = (scale, 0) + + return self + + def load_djpeg(self): + + # ALTERNATIVE: handle JPEGs via the IJG command line utilities + + import subprocess + import tempfile + import os + f, path = tempfile.mkstemp() + os.close(f) + if os.path.exists(self.filename): + subprocess.check_call(["djpeg", "-outfile", path, self.filename]) + else: + raise ValueError("Invalid Filename") + + try: + self.im = Image.core.open_ppm(path) + finally: + try: + os.unlink(path) + except: + pass + + self.mode = self.im.mode + self.size = self.im.size + + self.tile = [] + + def _getexif(self): + return _getexif(self) + + def _getmp(self): + return _getmp(self) + + +def _fixup(value): + # Helper function for _getexif() and _getmp() + if len(value) == 1: + return value[0] + return value + + +def _getexif(self): + # Extract EXIF information. This method is highly experimental, + # and is likely to be replaced with something better in a future + # version. + + # The EXIF record consists of a TIFF file embedded in a JPEG + # application marker (!). + try: + data = self.info["exif"] + except KeyError: + return None + file = io.BytesIO(data[6:]) + head = file.read(8) + exif = {} + # process dictionary + info = TiffImagePlugin.ImageFileDirectory(head) + info.load(file) + for key, value in info.items(): + exif[key] = _fixup(value) + # get exif extension + try: + file.seek(exif[0x8769]) + except KeyError: + pass + else: + info = TiffImagePlugin.ImageFileDirectory(head) + info.load(file) + for key, value in info.items(): + exif[key] = _fixup(value) + # get gpsinfo extension + try: + file.seek(exif[0x8825]) + except KeyError: + pass + else: + info = TiffImagePlugin.ImageFileDirectory(head) + info.load(file) + exif[0x8825] = gps = {} + for key, value in info.items(): + gps[key] = _fixup(value) + return exif + + +def _getmp(self): + # Extract MP information. This method was inspired by the "highly + # experimental" _getexif version that's been in use for years now, + # itself based on the ImageFileDirectory class in the TIFF plug-in. + + # The MP record essentially consists of a TIFF file embedded in a JPEG + # application marker. + try: + data = self.info["mp"] + except KeyError: + return None + file = io.BytesIO(data) + head = file.read(8) + endianness = '>' if head[:4] == b'\x4d\x4d\x00\x2a' else '<' + mp = {} + # process dictionary + info = TiffImagePlugin.ImageFileDirectory(head) + info.load(file) + for key, value in info.items(): + mp[key] = _fixup(value) + # it's an error not to have a number of images + try: + quant = mp[0xB001] + except KeyError: + raise SyntaxError("malformed MP Index (no number of images)") + # get MP entries + try: + mpentries = [] + for entrynum in range(0, quant): + rawmpentry = mp[0xB002][entrynum * 16:(entrynum + 1) * 16] + unpackedentry = unpack('{0}LLLHH'.format(endianness), rawmpentry) + labels = ('Attribute', 'Size', 'DataOffset', 'EntryNo1', + 'EntryNo2') + mpentry = dict(zip(labels, unpackedentry)) + mpentryattr = { + 'DependentParentImageFlag': bool(mpentry['Attribute'] & + (1 << 31)), + 'DependentChildImageFlag': bool(mpentry['Attribute'] & + (1 << 30)), + 'RepresentativeImageFlag': bool(mpentry['Attribute'] & + (1 << 29)), + 'Reserved': (mpentry['Attribute'] & (3 << 27)) >> 27, + 'ImageDataFormat': (mpentry['Attribute'] & (7 << 24)) >> 24, + 'MPType': mpentry['Attribute'] & 0x00FFFFFF + } + if mpentryattr['ImageDataFormat'] == 0: + mpentryattr['ImageDataFormat'] = 'JPEG' + else: + raise SyntaxError("unsupported picture format in MPO") + mptypemap = { + 0x000000: 'Undefined', + 0x010001: 'Large Thumbnail (VGA Equivalent)', + 0x010002: 'Large Thumbnail (Full HD Equivalent)', + 0x020001: 'Multi-Frame Image (Panorama)', + 0x020002: 'Multi-Frame Image: (Disparity)', + 0x020003: 'Multi-Frame Image: (Multi-Angle)', + 0x030000: 'Baseline MP Primary Image' + } + mpentryattr['MPType'] = mptypemap.get(mpentryattr['MPType'], + 'Unknown') + mpentry['Attribute'] = mpentryattr + mpentries.append(mpentry) + mp[0xB002] = mpentries + except KeyError: + raise SyntaxError("malformed MP Index (bad MP Entry)") + # Next we should try and parse the individual image unique ID list; + # we don't because I've never seen this actually used in a real MPO + # file and so can't test it. + return mp + + +# -------------------------------------------------------------------- +# stuff to save JPEG files + +RAWMODE = { + "1": "L", + "L": "L", + "RGB": "RGB", + "RGBA": "RGB", + "RGBX": "RGB", + "CMYK": "CMYK;I", # assume adobe conventions + "YCbCr": "YCbCr", +} + +zigzag_index = ( 0, 1, 5, 6, 14, 15, 27, 28, + 2, 4, 7, 13, 16, 26, 29, 42, + 3, 8, 12, 17, 25, 30, 41, 43, + 9, 11, 18, 24, 31, 40, 44, 53, + 10, 19, 23, 32, 39, 45, 52, 54, + 20, 22, 33, 38, 46, 51, 55, 60, + 21, 34, 37, 47, 50, 56, 59, 61, + 35, 36, 48, 49, 57, 58, 62, 63) + +samplings = {(1, 1, 1, 1, 1, 1): 0, + (2, 1, 1, 1, 1, 1): 1, + (2, 2, 1, 1, 1, 1): 2, + } + + +def convert_dict_qtables(qtables): + qtables = [qtables[key] for key in range(len(qtables)) if key in qtables] + for idx, table in enumerate(qtables): + qtables[idx] = [table[i] for i in zigzag_index] + return qtables + + +def get_sampling(im): + # There's no subsampling when image have only 1 layer + # (grayscale images) or when they are CMYK (4 layers), + # so set subsampling to default value. + # + # NOTE: currently Pillow can't encode JPEG to YCCK format. + # If YCCK support is added in the future, subsampling code will have + # to be updated (here and in JpegEncode.c) to deal with 4 layers. + if not hasattr(im, 'layers') or im.layers in (1, 4): + return -1 + sampling = im.layer[0][1:3] + im.layer[1][1:3] + im.layer[2][1:3] + return samplings.get(sampling, -1) + + +def _save(im, fp, filename): + + try: + rawmode = RAWMODE[im.mode] + except KeyError: + raise IOError("cannot write mode %s as JPEG" % im.mode) + + info = im.encoderinfo + + dpi = info.get("dpi", (0, 0)) + + quality = info.get("quality", 0) + subsampling = info.get("subsampling", -1) + qtables = info.get("qtables") + + if quality == "keep": + quality = 0 + subsampling = "keep" + qtables = "keep" + elif quality in presets: + preset = presets[quality] + quality = 0 + subsampling = preset.get('subsampling', -1) + qtables = preset.get('quantization') + elif not isinstance(quality, int): + raise ValueError("Invalid quality setting") + else: + if subsampling in presets: + subsampling = presets[subsampling].get('subsampling', -1) + if isStringType(qtables) and qtables in presets: + qtables = presets[qtables].get('quantization') + + if subsampling == "4:4:4": + subsampling = 0 + elif subsampling == "4:2:2": + subsampling = 1 + elif subsampling == "4:1:1": + subsampling = 2 + elif subsampling == "keep": + if im.format != "JPEG": + raise ValueError( + "Cannot use 'keep' when original image is not a JPEG") + subsampling = get_sampling(im) + + def validate_qtables(qtables): + if qtables is None: + return qtables + if isStringType(qtables): + try: + lines = [int(num) for line in qtables.splitlines() + for num in line.split('#', 1)[0].split()] + except ValueError: + raise ValueError("Invalid quantization table") + else: + qtables = [lines[s:s+64] for s in range(0, len(lines), 64)] + if isinstance(qtables, (tuple, list, dict)): + if isinstance(qtables, dict): + qtables = convert_dict_qtables(qtables) + elif isinstance(qtables, tuple): + qtables = list(qtables) + if not (0 < len(qtables) < 5): + raise ValueError("None or too many quantization tables") + for idx, table in enumerate(qtables): + try: + if len(table) != 64: + raise + table = array.array('b', table) + except TypeError: + raise ValueError("Invalid quantization table") + else: + qtables[idx] = list(table) + return qtables + + if qtables == "keep": + if im.format != "JPEG": + raise ValueError( + "Cannot use 'keep' when original image is not a JPEG") + qtables = getattr(im, "quantization", None) + qtables = validate_qtables(qtables) + + extra = b"" + + icc_profile = info.get("icc_profile") + if icc_profile: + ICC_OVERHEAD_LEN = 14 + MAX_BYTES_IN_MARKER = 65533 + MAX_DATA_BYTES_IN_MARKER = MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN + markers = [] + while icc_profile: + markers.append(icc_profile[:MAX_DATA_BYTES_IN_MARKER]) + icc_profile = icc_profile[MAX_DATA_BYTES_IN_MARKER:] + i = 1 + for marker in markers: + size = struct.pack(">H", 2 + ICC_OVERHEAD_LEN + len(marker)) + extra += (b"\xFF\xE2" + size + b"ICC_PROFILE\0" + o8(i) + + o8(len(markers)) + marker) + i += 1 + + # get keyword arguments + im.encoderconfig = ( + quality, + # "progressive" is the official name, but older documentation + # says "progression" + # FIXME: issue a warning if the wrong form is used (post-1.1.7) + "progressive" in info or "progression" in info, + info.get("smooth", 0), + "optimize" in info, + info.get("streamtype", 0), + dpi[0], dpi[1], + subsampling, + qtables, + extra, + info.get("exif", b"") + ) + + # if we optimize, libjpeg needs a buffer big enough to hold the whole image + # in a shot. Guessing on the size, at im.size bytes. (raw pizel size is + # channels*size, this is a value that's been used in a django patch. + # https://github.com/jdriscoll/django-imagekit/issues/50 + bufsize = 0 + if "optimize" in info or "progressive" in info or "progression" in info: + if quality >= 95: + bufsize = 2 * im.size[0] * im.size[1] + else: + bufsize = im.size[0] * im.size[1] + + # The exif info needs to be written as one block, + APP1, + one spare byte. + # Ensure that our buffer is big enough + bufsize = max(ImageFile.MAXBLOCK, bufsize, len(info.get("exif", b"")) + 5) + + ImageFile._save(im, fp, [("jpeg", (0, 0)+im.size, 0, rawmode)], bufsize) + + +def _save_cjpeg(im, fp, filename): + # ALTERNATIVE: handle JPEGs via the IJG command line utilities. + import os + import subprocess + tempfile = im._dump() + subprocess.check_call(["cjpeg", "-outfile", filename, tempfile]) + try: + os.unlink(file) + except: + pass + + +## +# Factory for making JPEG and MPO instances +def jpeg_factory(fp=None, filename=None): + im = JpegImageFile(fp, filename) + mpheader = im._getmp() + try: + if mpheader[45057] > 1: + # It's actually an MPO + from .MpoImagePlugin import MpoImageFile + im = MpoImageFile(fp, filename) + except (TypeError, IndexError): + # It is really a JPEG + pass + return im + + +# -------------------------------------------------------------------q- +# Registry stuff + +Image.register_open("JPEG", jpeg_factory, _accept) +Image.register_save("JPEG", _save) + +Image.register_extension("JPEG", ".jfif") +Image.register_extension("JPEG", ".jpe") +Image.register_extension("JPEG", ".jpg") +Image.register_extension("JPEG", ".jpeg") + +Image.register_mime("JPEG", "image/jpeg") diff --git a/pyPackages/pillowx86/PIL/JpegPresets.py b/pyPackages/pillowx86/PIL/JpegPresets.py new file mode 100644 index 0000000..6ca46d0 --- /dev/null +++ b/pyPackages/pillowx86/PIL/JpegPresets.py @@ -0,0 +1,241 @@ +""" +JPEG quality settings equivalent to the Photoshop settings. + +More presets can be added to the presets dict if needed. + +Can be use when saving JPEG file. + +To apply the preset, specify:: + + quality="preset_name" + +To apply only the quantization table:: + + qtables="preset_name" + +To apply only the subsampling setting:: + + subsampling="preset_name" + +Example:: + + im.save("image_name.jpg", quality="web_high") + + +Subsampling +----------- + +Subsampling is the practice of encoding images by implementing less resolution +for chroma information than for luma information. +(ref.: http://en.wikipedia.org/wiki/Chroma_subsampling) + +Possible subsampling values are 0, 1 and 2 that correspond to 4:4:4, 4:2:2 and +4:1:1 (or 4:2:0?). + +You can get the subsampling of a JPEG with the +`JpegImagePlugin.get_subsampling(im)` function. + + +Quantization tables +------------------- + +They are values use by the DCT (Discrete cosine transform) to remove +*unnecessary* information from the image (the lossy part of the compression). +(ref.: http://en.wikipedia.org/wiki/Quantization_matrix#Quantization_matrices, +http://en.wikipedia.org/wiki/JPEG#Quantization) + +You can get the quantization tables of a JPEG with:: + + im.quantization + +This will return a dict with a number of arrays. You can pass this dict +directly as the qtables argument when saving a JPEG. + +The tables format between im.quantization and quantization in presets differ in +3 ways: + +1. The base container of the preset is a list with sublists instead of dict. + dict[0] -> list[0], dict[1] -> list[1], ... +2. Each table in a preset is a list instead of an array. +3. The zigzag order is remove in the preset (needed by libjpeg >= 6a). + +You can convert the dict format to the preset format with the +`JpegImagePlugin.convert_dict_qtables(dict_qtables)` function. + +Libjpeg ref.: http://www.jpegcameras.com/libjpeg/libjpeg-3.html + +""" + +presets = { + 'web_low': {'subsampling': 2, # "4:1:1" + 'quantization': [ + [20, 16, 25, 39, 50, 46, 62, 68, + 16, 18, 23, 38, 38, 53, 65, 68, + 25, 23, 31, 38, 53, 65, 68, 68, + 39, 38, 38, 53, 65, 68, 68, 68, + 50, 38, 53, 65, 68, 68, 68, 68, + 46, 53, 65, 68, 68, 68, 68, 68, + 62, 65, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68], + [21, 25, 32, 38, 54, 68, 68, 68, + 25, 28, 24, 38, 54, 68, 68, 68, + 32, 24, 32, 43, 66, 68, 68, 68, + 38, 38, 43, 53, 68, 68, 68, 68, + 54, 54, 66, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68] + ]}, + 'web_medium': {'subsampling': 2, # "4:1:1" + 'quantization': [ + [16, 11, 11, 16, 23, 27, 31, 30, + 11, 12, 12, 15, 20, 23, 23, 30, + 11, 12, 13, 16, 23, 26, 35, 47, + 16, 15, 16, 23, 26, 37, 47, 64, + 23, 20, 23, 26, 39, 51, 64, 64, + 27, 23, 26, 37, 51, 64, 64, 64, + 31, 23, 35, 47, 64, 64, 64, 64, + 30, 30, 47, 64, 64, 64, 64, 64], + [17, 15, 17, 21, 20, 26, 38, 48, + 15, 19, 18, 17, 20, 26, 35, 43, + 17, 18, 20, 22, 26, 30, 46, 53, + 21, 17, 22, 28, 30, 39, 53, 64, + 20, 20, 26, 30, 39, 48, 64, 64, + 26, 26, 30, 39, 48, 63, 64, 64, + 38, 35, 46, 53, 64, 64, 64, 64, + 48, 43, 53, 64, 64, 64, 64, 64] + ]}, + 'web_high': {'subsampling': 0, # "4:4:4" + 'quantization': [ + [ 6, 4, 4, 6, 9, 11, 12, 16, + 4, 5, 5, 6, 8, 10, 12, 12, + 4, 5, 5, 6, 10, 12, 14, 19, + 6, 6, 6, 11, 12, 15, 19, 28, + 9, 8, 10, 12, 16, 20, 27, 31, + 11, 10, 12, 15, 20, 27, 31, 31, + 12, 12, 14, 19, 27, 31, 31, 31, + 16, 12, 19, 28, 31, 31, 31, 31], + [ 7, 7, 13, 24, 26, 31, 31, 31, + 7, 12, 16, 21, 31, 31, 31, 31, + 13, 16, 17, 31, 31, 31, 31, 31, + 24, 21, 31, 31, 31, 31, 31, 31, + 26, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31] + ]}, + 'web_very_high': {'subsampling': 0, # "4:4:4" + 'quantization': [ + [ 2, 2, 2, 2, 3, 4, 5, 6, + 2, 2, 2, 2, 3, 4, 5, 6, + 2, 2, 2, 2, 4, 5, 7, 9, + 2, 2, 2, 4, 5, 7, 9, 12, + 3, 3, 4, 5, 8, 10, 12, 12, + 4, 4, 5, 7, 10, 12, 12, 12, + 5, 5, 7, 9, 12, 12, 12, 12, + 6, 6, 9, 12, 12, 12, 12, 12], + [ 3, 3, 5, 9, 13, 15, 15, 15, + 3, 4, 6, 11, 14, 12, 12, 12, + 5, 6, 9, 14, 12, 12, 12, 12, + 9, 11, 14, 12, 12, 12, 12, 12, + 13, 14, 12, 12, 12, 12, 12, 12, + 15, 12, 12, 12, 12, 12, 12, 12, + 15, 12, 12, 12, 12, 12, 12, 12, + 15, 12, 12, 12, 12, 12, 12, 12] + ]}, + 'web_maximum': {'subsampling': 0, # "4:4:4" + 'quantization': [ + [ 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2, + 1, 1, 1, 1, 1, 1, 2, 2, + 1, 1, 1, 1, 1, 2, 2, 3, + 1, 1, 1, 1, 2, 2, 3, 3, + 1, 1, 1, 2, 2, 3, 3, 3, + 1, 1, 2, 2, 3, 3, 3, 3], + [ 1, 1, 1, 2, 2, 3, 3, 3, + 1, 1, 1, 2, 3, 3, 3, 3, + 1, 1, 1, 3, 3, 3, 3, 3, + 2, 2, 3, 3, 3, 3, 3, 3, + 2, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3] + ]}, + 'low': {'subsampling': 2, # "4:1:1" + 'quantization': [ + [18, 14, 14, 21, 30, 35, 34, 17, + 14, 16, 16, 19, 26, 23, 12, 12, + 14, 16, 17, 21, 23, 12, 12, 12, + 21, 19, 21, 23, 12, 12, 12, 12, + 30, 26, 23, 12, 12, 12, 12, 12, + 35, 23, 12, 12, 12, 12, 12, 12, + 34, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12], + [20, 19, 22, 27, 20, 20, 17, 17, + 19, 25, 23, 14, 14, 12, 12, 12, + 22, 23, 14, 14, 12, 12, 12, 12, + 27, 14, 14, 12, 12, 12, 12, 12, + 20, 14, 12, 12, 12, 12, 12, 12, + 20, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12] + ]}, + 'medium': {'subsampling': 2, # "4:1:1" + 'quantization': [ + [12, 8, 8, 12, 17, 21, 24, 17, + 8, 9, 9, 11, 15, 19, 12, 12, + 8, 9, 10, 12, 19, 12, 12, 12, + 12, 11, 12, 21, 12, 12, 12, 12, + 17, 15, 19, 12, 12, 12, 12, 12, + 21, 19, 12, 12, 12, 12, 12, 12, + 24, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12], + [13, 11, 13, 16, 20, 20, 17, 17, + 11, 14, 14, 14, 14, 12, 12, 12, + 13, 14, 14, 14, 12, 12, 12, 12, + 16, 14, 14, 12, 12, 12, 12, 12, + 20, 14, 12, 12, 12, 12, 12, 12, + 20, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12] + ]}, + 'high': {'subsampling': 0, # "4:4:4" + 'quantization': [ + [ 6, 4, 4, 6, 9, 11, 12, 16, + 4, 5, 5, 6, 8, 10, 12, 12, + 4, 5, 5, 6, 10, 12, 12, 12, + 6, 6, 6, 11, 12, 12, 12, 12, + 9, 8, 10, 12, 12, 12, 12, 12, + 11, 10, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, + 16, 12, 12, 12, 12, 12, 12, 12], + [ 7, 7, 13, 24, 20, 20, 17, 17, + 7, 12, 16, 14, 14, 12, 12, 12, + 13, 16, 14, 14, 12, 12, 12, 12, + 24, 14, 14, 12, 12, 12, 12, 12, + 20, 14, 12, 12, 12, 12, 12, 12, + 20, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12] + ]}, + 'maximum': {'subsampling': 0, # "4:4:4" + 'quantization': [ + [ 2, 2, 2, 2, 3, 4, 5, 6, + 2, 2, 2, 2, 3, 4, 5, 6, + 2, 2, 2, 2, 4, 5, 7, 9, + 2, 2, 2, 4, 5, 7, 9, 12, + 3, 3, 4, 5, 8, 10, 12, 12, + 4, 4, 5, 7, 10, 12, 12, 12, + 5, 5, 7, 9, 12, 12, 12, 12, + 6, 6, 9, 12, 12, 12, 12, 12], + [ 3, 3, 5, 9, 13, 15, 15, 15, + 3, 4, 6, 10, 14, 12, 12, 12, + 5, 6, 9, 14, 12, 12, 12, 12, + 9, 10, 14, 12, 12, 12, 12, 12, + 13, 14, 12, 12, 12, 12, 12, 12, + 15, 12, 12, 12, 12, 12, 12, 12, + 15, 12, 12, 12, 12, 12, 12, 12, + 15, 12, 12, 12, 12, 12, 12, 12] + ]}, +} diff --git a/pyPackages/pillowx86/PIL/McIdasImagePlugin.py b/pyPackages/pillowx86/PIL/McIdasImagePlugin.py new file mode 100644 index 0000000..c3f255f --- /dev/null +++ b/pyPackages/pillowx86/PIL/McIdasImagePlugin.py @@ -0,0 +1,74 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Basic McIdas support for PIL +# +# History: +# 1997-05-05 fl Created (8-bit images only) +# 2009-03-08 fl Added 16/32-bit support. +# +# Thanks to Richard Jones and Craig Swank for specs and samples. +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.2" + +import struct +from PIL import Image, ImageFile + + +def _accept(s): + return s[:8] == b"\x00\x00\x00\x00\x00\x00\x00\x04" + + +## +# Image plugin for McIdas area images. + +class McIdasImageFile(ImageFile.ImageFile): + + format = "MCIDAS" + format_description = "McIdas area file" + + def _open(self): + + # parse area file directory + s = self.fp.read(256) + if not _accept(s) or len(s) != 256: + raise SyntaxError("not an McIdas area file") + + self.area_descriptor_raw = s + self.area_descriptor = w = [0] + list(struct.unpack("!64i", s)) + + # get mode + if w[11] == 1: + mode = rawmode = "L" + elif w[11] == 2: + # FIXME: add memory map support + mode = "I" + rawmode = "I;16B" + elif w[11] == 4: + # FIXME: add memory map support + mode = "I" + rawmode = "I;32B" + else: + raise SyntaxError("unsupported McIdas format") + + self.mode = mode + self.size = w[10], w[9] + + offset = w[34] + w[15] + stride = w[15] + w[10]*w[11]*w[14] + + self.tile = [("raw", (0, 0) + self.size, offset, (rawmode, stride, 1))] + +# -------------------------------------------------------------------- +# registry + +Image.register_open("MCIDAS", McIdasImageFile, _accept) + +# no default extension diff --git a/pyPackages/pillowx86/PIL/MicImagePlugin.py b/pyPackages/pillowx86/PIL/MicImagePlugin.py new file mode 100644 index 0000000..cdfaf3e --- /dev/null +++ b/pyPackages/pillowx86/PIL/MicImagePlugin.py @@ -0,0 +1,96 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Microsoft Image Composer support for PIL +# +# Notes: +# uses TiffImagePlugin.py to read the actual image streams +# +# History: +# 97-01-20 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.1" + + +from PIL import Image, TiffImagePlugin +from PIL.OleFileIO import * + + +# +# -------------------------------------------------------------------- + + +def _accept(prefix): + return prefix[:8] == MAGIC + + +## +# Image plugin for Microsoft's Image Composer file format. + +class MicImageFile(TiffImagePlugin.TiffImageFile): + + format = "MIC" + format_description = "Microsoft Image Composer" + + def _open(self): + + # read the OLE directory and see if this is a likely + # to be a Microsoft Image Composer file + + try: + self.ole = OleFileIO(self.fp) + except IOError: + raise SyntaxError("not an MIC file; invalid OLE file") + + # find ACI subfiles with Image members (maybe not the + # best way to identify MIC files, but what the... ;-) + + self.images = [] + for file in self.ole.listdir(): + if file[1:] and file[0][-4:] == ".ACI" and file[1] == "Image": + self.images.append(file) + + # if we didn't find any images, this is probably not + # an MIC file. + if not self.images: + raise SyntaxError("not an MIC file; no image entries") + + self.__fp = self.fp + self.frame = 0 + + if len(self.images) > 1: + self.category = Image.CONTAINER + + self.seek(0) + + def seek(self, frame): + + try: + filename = self.images[frame] + except IndexError: + raise EOFError("no such frame") + + self.fp = self.ole.openstream(filename) + + TiffImagePlugin.TiffImageFile._open(self) + + self.frame = frame + + def tell(self): + + return self.frame + +# +# -------------------------------------------------------------------- + +Image.register_open("MIC", MicImageFile, _accept) + +Image.register_extension("MIC", ".mic") diff --git a/pyPackages/pillowx86/PIL/MpegImagePlugin.py b/pyPackages/pillowx86/PIL/MpegImagePlugin.py new file mode 100644 index 0000000..9aca58f --- /dev/null +++ b/pyPackages/pillowx86/PIL/MpegImagePlugin.py @@ -0,0 +1,85 @@ +# +# The Python Imaging Library. +# $Id$ +# +# MPEG file handling +# +# History: +# 95-09-09 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1995. +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.1" + +from PIL import Image, ImageFile +from PIL._binary import i8 + + +# +# Bitstream parser + +class BitStream: + + def __init__(self, fp): + self.fp = fp + self.bits = 0 + self.bitbuffer = 0 + + def next(self): + return i8(self.fp.read(1)) + + def peek(self, bits): + while self.bits < bits: + c = self.next() + if c < 0: + self.bits = 0 + continue + self.bitbuffer = (self.bitbuffer << 8) + c + self.bits += 8 + return self.bitbuffer >> (self.bits - bits) & (1 << bits) - 1 + + def skip(self, bits): + while self.bits < bits: + self.bitbuffer = (self.bitbuffer << 8) + i8(self.fp.read(1)) + self.bits += 8 + self.bits = self.bits - bits + + def read(self, bits): + v = self.peek(bits) + self.bits = self.bits - bits + return v + + +## +# Image plugin for MPEG streams. This plugin can identify a stream, +# but it cannot read it. + +class MpegImageFile(ImageFile.ImageFile): + + format = "MPEG" + format_description = "MPEG" + + def _open(self): + + s = BitStream(self.fp) + + if s.read(32) != 0x1B3: + raise SyntaxError("not an MPEG file") + + self.mode = "RGB" + self.size = s.read(12), s.read(12) + + +# -------------------------------------------------------------------- +# Registry stuff + +Image.register_open("MPEG", MpegImageFile) + +Image.register_extension("MPEG", ".mpg") +Image.register_extension("MPEG", ".mpeg") + +Image.register_mime("MPEG", "video/mpeg") diff --git a/pyPackages/pillowx86/PIL/MpoImagePlugin.py b/pyPackages/pillowx86/PIL/MpoImagePlugin.py new file mode 100644 index 0000000..c345fd3 --- /dev/null +++ b/pyPackages/pillowx86/PIL/MpoImagePlugin.py @@ -0,0 +1,90 @@ +# +# The Python Imaging Library. +# $Id$ +# +# MPO file handling +# +# See "Multi-Picture Format" (CIPA DC-007-Translation 2009, Standard of the +# Camera & Imaging Products Association) +# +# The multi-picture object combines multiple JPEG images (with a modified EXIF +# data format) into a single file. While it can theoretically be used much like +# a GIF animation, it is commonly used to represent 3D photographs and is (as +# of this writing) the most commonly used format by 3D cameras. +# +# History: +# 2014-03-13 Feneric Created +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.1" + +from PIL import Image, JpegImagePlugin + + +def _accept(prefix): + return JpegImagePlugin._accept(prefix) + + +def _save(im, fp, filename): + # Note that we can only save the current frame at present + return JpegImagePlugin._save(im, fp, filename) + + +## +# Image plugin for MPO images. + +class MpoImageFile(JpegImagePlugin.JpegImageFile): + + format = "MPO" + format_description = "MPO (CIPA DC-007)" + + def _open(self): + self.fp.seek(0) # prep the fp in order to pass the JPEG test + JpegImagePlugin.JpegImageFile._open(self) + self.mpinfo = self._getmp() + self.__framecount = self.mpinfo[0xB001] + self.__mpoffsets = [mpent['DataOffset'] + self.info['mpoffset'] + for mpent in self.mpinfo[0xB002]] + self.__mpoffsets[0] = 0 + # Note that the following assertion will only be invalid if something + # gets broken within JpegImagePlugin. + assert self.__framecount == len(self.__mpoffsets) + del self.info['mpoffset'] # no longer needed + self.__fp = self.fp # FIXME: hack + self.__fp.seek(self.__mpoffsets[0]) # get ready to read first frame + self.__frame = 0 + self.offset = 0 + # for now we can only handle reading and individual frame extraction + self.readonly = 1 + + def load_seek(self, pos): + self.__fp.seek(pos) + + def seek(self, frame): + if frame < 0 or frame >= self.__framecount: + raise EOFError("no more images in MPO file") + else: + self.fp = self.__fp + self.offset = self.__mpoffsets[frame] + self.tile = [ + ("jpeg", (0, 0) + self.size, self.offset, (self.mode, "")) + ] + self.__frame = frame + + def tell(self): + return self.__frame + + +# -------------------------------------------------------------------q- +# Registry stuff + +# Note that since MPO shares a factory with JPEG, we do not need to do a +# separate registration for it here. +# Image.register_open("MPO", JpegImagePlugin.jpeg_factory, _accept) +Image.register_save("MPO", _save) + +Image.register_extension("MPO", ".mpo") + +Image.register_mime("MPO", "image/mpo") diff --git a/pyPackages/pillowx86/PIL/MspImagePlugin.py b/pyPackages/pillowx86/PIL/MspImagePlugin.py new file mode 100644 index 0000000..4753be7 --- /dev/null +++ b/pyPackages/pillowx86/PIL/MspImagePlugin.py @@ -0,0 +1,104 @@ +# +# The Python Imaging Library. +# $Id$ +# +# MSP file handling +# +# This is the format used by the Paint program in Windows 1 and 2. +# +# History: +# 95-09-05 fl Created +# 97-01-03 fl Read/write MSP images +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1995-97. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.1" + +from PIL import Image, ImageFile, _binary + + +# +# read MSP files + +i16 = _binary.i16le + + +def _accept(prefix): + return prefix[:4] in [b"DanM", b"LinS"] + + +## +# Image plugin for Windows MSP images. This plugin supports both +# uncompressed (Windows 1.0). + +class MspImageFile(ImageFile.ImageFile): + + format = "MSP" + format_description = "Windows Paint" + + def _open(self): + + # Header + s = self.fp.read(32) + if s[:4] not in [b"DanM", b"LinS"]: + raise SyntaxError("not an MSP file") + + # Header checksum + sum = 0 + for i in range(0, 32, 2): + sum = sum ^ i16(s[i:i+2]) + if sum != 0: + raise SyntaxError("bad MSP checksum") + + self.mode = "1" + self.size = i16(s[4:]), i16(s[6:]) + + if s[:4] == b"DanM": + self.tile = [("raw", (0, 0)+self.size, 32, ("1", 0, 1))] + else: + self.tile = [("msp", (0, 0)+self.size, 32+2*self.size[1], None)] + +# +# write MSP files (uncompressed only) + +o16 = _binary.o16le + + +def _save(im, fp, filename): + + if im.mode != "1": + raise IOError("cannot write mode %s as MSP" % im.mode) + + # create MSP header + header = [0] * 16 + + header[0], header[1] = i16(b"Da"), i16(b"nM") # version 1 + header[2], header[3] = im.size + header[4], header[5] = 1, 1 + header[6], header[7] = 1, 1 + header[8], header[9] = im.size + + sum = 0 + for h in header: + sum = sum ^ h + header[12] = sum # FIXME: is this the right field? + + # header + for h in header: + fp.write(o16(h)) + + # image body + ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 32, ("1", 0, 1))]) + +# +# registry + +Image.register_open("MSP", MspImageFile, _accept) +Image.register_save("MSP", _save) + +Image.register_extension("MSP", ".msp") diff --git a/pyPackages/pillowx86/PIL/OleFileIO-README.md b/pyPackages/pillowx86/PIL/OleFileIO-README.md new file mode 100644 index 0000000..11a0e90 --- /dev/null +++ b/pyPackages/pillowx86/PIL/OleFileIO-README.md @@ -0,0 +1,351 @@ +OleFileIO_PL +============ + +[OleFileIO_PL](http://www.decalage.info/python/olefileio) is a Python module to parse and read [Microsoft OLE2 files (also called Structured Storage, Compound File Binary Format or Compound Document File Format)](http://en.wikipedia.org/wiki/Compound_File_Binary_Format), such as Microsoft Office documents, Image Composer and FlashPix files, Outlook messages, StickyNotes, several Microscopy file formats ... + +This is an improved version of the OleFileIO module from [PIL](http://www.pythonware.com/products/pil/index.htm), the excellent Python Imaging Library, created and maintained by Fredrik Lundh. The API is still compatible with PIL, but since 2005 I have improved the internal implementation significantly, with new features, bugfixes and a more robust design. + +As far as I know, this module is now the most complete and robust Python implementation to read MS OLE2 files, portable on several operating systems. (please tell me if you know other similar Python modules) + +OleFileIO_PL can be used as an independent module or with PIL. The goal is to have it integrated into [Pillow](http://python-pillow.github.io/), the friendly fork of PIL. + +OleFileIO\_PL is mostly meant for developers. If you are looking for tools to analyze OLE files or to extract data, then please also check [python-oletools](http://www.decalage.info/python/oletools), which are built upon OleFileIO_PL. + +News +---- + +Follow all updates and news on Twitter: + +- **2014-02-04 v0.30**: now compatible with Python 3.x, thanks to Martin Panter who did most of the hard work. +- 2013-07-24 v0.26: added methods to parse stream/storage timestamps, improved listdir to include storages, fixed parsing of direntry timestamps +- 2013-05-27 v0.25: improved metadata extraction, properties parsing and exception handling, fixed [issue #12](https://bitbucket.org/decalage/olefileio_pl/issue/12/error-when-converting-timestamps-in-ole) +- 2013-05-07 v0.24: new features to extract metadata (get\_metadata method and OleMetadata class), improved getproperties to convert timestamps to Python datetime +- 2012-10-09: published [python-oletools](http://www.decalage.info/python/oletools), a package of analysis tools based on OleFileIO_PL +- 2012-09-11 v0.23: added support for file-like objects, fixed [issue #8](https://bitbucket.org/decalage/olefileio_pl/issue/8/bug-with-file-object) +- 2012-02-17 v0.22: fixed issues #7 (bug in getproperties) and #2 (added close method) +- 2011-10-20: code hosted on bitbucket to ease contributions and bug tracking +- 2010-01-24 v0.21: fixed support for big-endian CPUs, such as PowerPC Macs. +- 2009-12-11 v0.20: small bugfix in OleFileIO.open when filename is not plain str. +- 2009-12-10 v0.19: fixed support for 64 bits platforms (thanks to Ben G. and Martijn for reporting the bug) +- see changelog in source code for more info. + +Download +-------- + +The archive is available on [the project page](https://bitbucket.org/decalage/olefileio_pl/downloads). + +Features +-------- + +- Parse and read any OLE file such as Microsoft Office 97-2003 legacy document formats (Word .doc, Excel .xls, PowerPoint .ppt, Visio .vsd, Project .mpp), Image Composer and FlashPix files, Outlook messages, StickyNotes, Zeiss AxioVision ZVI files, Olympus FluoView OIB files, ... +- List all the streams and storages contained in an OLE file +- Open streams as files +- Parse and read property streams, containing metadata of the file +- Portable, pure Python module, no dependency + + +Main improvements over the original version of OleFileIO in PIL: +---------------------------------------------------------------- + +- Compatible with Python 3.x and 2.6+ +- Many bug fixes +- Support for files larger than 6.8MB +- Support for 64 bits platforms and big-endian CPUs +- Robust: many checks to detect malformed files +- Runtime option to choose if malformed files should be parsed or raise exceptions +- Improved API +- Metadata extraction, stream/storage timestamps (e.g. for document forensics) +- Can open file-like objects +- Added setup.py and install.bat to ease installation +- More convenient slash-based syntax for stream paths + + + +How to use this module +---------------------- + +OleFileIO_PL can be used as an independent module or with PIL. The main functions and methods are explained below. + +For more information, see also the file **OleFileIO_PL.html**, sample code at the end of the module itself, and docstrings within the code. + +### About the structure of OLE files ### + +An OLE file can be seen as a mini file system or a Zip archive: It contains **streams** of data that look like files embedded within the OLE file. Each stream has a name. For example, the main stream of a MS Word document containing its text is named "WordDocument". + +An OLE file can also contain **storages**. A storage is a folder that contains streams or other storages. For example, a MS Word document with VBA macros has a storage called "Macros". + +Special streams can contain **properties**. A property is a specific value that can be used to store information such as the metadata of a document (title, author, creation date, etc). Property stream names usually start with the character '\x05'. + +For example, a typical MS Word document may look like this: + + \x05DocumentSummaryInformation (stream) + \x05SummaryInformation (stream) + WordDocument (stream) + Macros (storage) + PROJECT (stream) + PROJECTwm (stream) + VBA (storage) + Module1 (stream) + ThisDocument (stream) + _VBA_PROJECT (stream) + dir (stream) + ObjectPool (storage) + + + +### Import OleFileIO_PL ### + + :::python + import OleFileIO_PL + +As of version 0.30, the code has been changed to be compatible with Python 3.x. As a consequence, compatibility with Python 2.5 or older is not provided anymore. However, a copy of v0.26 is available as OleFileIO_PL2.py. If your application needs to be compatible with Python 2.5 or older, you may use the following code to load the old version when needed: + + :::python + try: + import OleFileIO_PL + except: + import OleFileIO_PL2 as OleFileIO_PL + +If you think OleFileIO_PL should stay compatible with Python 2.5 or older, please [contact me](http://decalage.info/contact). + + +### Test if a file is an OLE container ### + +Use isOleFile to check if the first bytes of the file contain the Magic for OLE files, before opening it. isOleFile returns True if it is an OLE file, False otherwise (new in v0.16). + + :::python + assert OleFileIO_PL.isOleFile('myfile.doc') + + +### Open an OLE file from disk ### + +Create an OleFileIO object with the file path as parameter: + + :::python + ole = OleFileIO_PL.OleFileIO('myfile.doc') + +### Open an OLE file from a file-like object ### + +This is useful if the file is not on disk, e.g. already stored in a string or as a file-like object. + + :::python + ole = OleFileIO_PL.OleFileIO(f) + +For example the code below reads a file into a string, then uses BytesIO to turn it into a file-like object. + + :::python + data = open('myfile.doc', 'rb').read() + f = io.BytesIO(data) # or StringIO.StringIO for Python 2.x + ole = OleFileIO_PL.OleFileIO(f) + +### How to handle malformed OLE files ### + +By default, the parser is configured to be as robust and permissive as possible, allowing to parse most malformed OLE files. Only fatal errors will raise an exception. It is possible to tell the parser to be more strict in order to raise exceptions for files that do not fully conform to the OLE specifications, using the raise_defect option (new in v0.14): + + :::python + ole = OleFileIO_PL.OleFileIO('myfile.doc', raise_defects=DEFECT_INCORRECT) + +When the parsing is done, the list of non-fatal issues detected is available as a list in the parsing_issues attribute of the OleFileIO object (new in 0.25): + + :::python + print('Non-fatal issues raised during parsing:') + if ole.parsing_issues: + for exctype, msg in ole.parsing_issues: + print('- %s: %s' % (exctype.__name__, msg)) + else: + print('None') + + +### Syntax for stream and storage path ### + +Two different syntaxes are allowed for methods that need or return the path of streams and storages: + +1) Either a **list of strings** including all the storages from the root up to the stream/storage name. For example a stream called "WordDocument" at the root will have ['WordDocument'] as full path. A stream called "ThisDocument" located in the storage "Macros/VBA" will be ['Macros', 'VBA', 'ThisDocument']. This is the original syntax from PIL. While hard to read and not very convenient, this syntax works in all cases. + +2) Or a **single string with slashes** to separate storage and stream names (similar to the Unix path syntax). The previous examples would be 'WordDocument' and 'Macros/VBA/ThisDocument'. This syntax is easier, but may fail if a stream or storage name contains a slash. (new in v0.15) + +Both are case-insensitive. + +Switching between the two is easy: + + :::python + slash_path = '/'.join(list_path) + list_path = slash_path.split('/') + + +### Get the list of streams ### + +listdir() returns a list of all the streams contained in the OLE file, including those stored in storages. Each stream is listed itself as a list, as described above. + + :::python + print(ole.listdir()) + +Sample result: + + :::python + [['\x01CompObj'], ['\x05DocumentSummaryInformation'], ['\x05SummaryInformation'] + , ['1Table'], ['Macros', 'PROJECT'], ['Macros', 'PROJECTwm'], ['Macros', 'VBA', + 'Module1'], ['Macros', 'VBA', 'ThisDocument'], ['Macros', 'VBA', '_VBA_PROJECT'] + , ['Macros', 'VBA', 'dir'], ['ObjectPool'], ['WordDocument']] + +As an option it is possible to choose if storages should also be listed, with or without streams (new in v0.26): + + :::python + ole.listdir (streams=False, storages=True) + + +### Test if known streams/storages exist: ### + +exists(path) checks if a given stream or storage exists in the OLE file (new in v0.16). + + :::python + if ole.exists('worddocument'): + print("This is a Word document.") + if ole.exists('macros/vba'): + print("This document seems to contain VBA macros.") + + +### Read data from a stream ### + +openstream(path) opens a stream as a file-like object. + +The following example extracts the "Pictures" stream from a PPT file: + + :::python + pics = ole.openstream('Pictures') + data = pics.read() + + +### Get information about a stream/storage ### + +Several methods can provide the size, type and timestamps of a given stream/storage: + +get_size(path) returns the size of a stream in bytes (new in v0.16): + + :::python + s = ole.get_size('WordDocument') + +get_type(path) returns the type of a stream/storage, as one of the following constants: STGTY\_STREAM for a stream, STGTY\_STORAGE for a storage, STGTY\_ROOT for the root entry, and False for a non existing path (new in v0.15). + + :::python + t = ole.get_type('WordDocument') + +get\_ctime(path) and get\_mtime(path) return the creation and modification timestamps of a stream/storage, as a Python datetime object with UTC timezone. Please note that these timestamps are only present if the application that created the OLE file explicitly stored them, which is rarely the case. When not present, these methods return None (new in v0.26). + + :::python + c = ole.get_ctime('WordDocument') + m = ole.get_mtime('WordDocument') + +The root storage is a special case: You can get its creation and modification timestamps using the OleFileIO.root attribute (new in v0.26): + + :::python + c = ole.root.getctime() + m = ole.root.getmtime() + +### Extract metadata ### + +get_metadata() will check if standard property streams exist, parse all the properties they contain, and return an OleMetadata object with the found properties as attributes (new in v0.24). + + :::python + meta = ole.get_metadata() + print('Author:', meta.author) + print('Title:', meta.title) + print('Creation date:', meta.create_time) + # print all metadata: + meta.dump() + +Available attributes include: + + codepage, title, subject, author, keywords, comments, template, + last_saved_by, revision_number, total_edit_time, last_printed, create_time, + last_saved_time, num_pages, num_words, num_chars, thumbnail, + creating_application, security, codepage_doc, category, presentation_target, + bytes, lines, paragraphs, slides, notes, hidden_slides, mm_clips, + scale_crop, heading_pairs, titles_of_parts, manager, company, links_dirty, + chars_with_spaces, unused, shared_doc, link_base, hlinks, hlinks_changed, + version, dig_sig, content_type, content_status, language, doc_version + +See the source code of the OleMetadata class for more information. + + +### Parse a property stream ### + +get\_properties(path) can be used to parse any property stream that is not handled by get\_metadata. It returns a dictionary indexed by integers. Each integer is the index of the property, pointing to its value. For example in the standard property stream '\x05SummaryInformation', the document title is property #2, and the subject is #3. + + :::python + p = ole.getproperties('specialprops') + +By default as in the original PIL version, timestamp properties are converted into a number of seconds since Jan 1,1601. With the option convert\_time, you can obtain more convenient Python datetime objects (UTC timezone). If some time properties should not be converted (such as total editing time in '\x05SummaryInformation'), the list of indexes can be passed as no_conversion (new in v0.25): + + :::python + p = ole.getproperties('specialprops', convert_time=True, no_conversion=[10]) + + +### Close the OLE file ### + +Unless your application is a simple script that terminates after processing an OLE file, do not forget to close each OleFileIO object after parsing to close the file on disk. (new in v0.22) + + :::python + ole.close() + +### Use OleFileIO_PL as a script ### + +OleFileIO_PL can also be used as a script from the command-line to display the structure of an OLE file and its metadata, for example: + + OleFileIO_PL.py myfile.doc + +You can use the option -c to check that all streams can be read fully, and -d to generate very verbose debugging information. + +## Real-life examples ## + +A real-life example: [using OleFileIO_PL for malware analysis and forensics](http://blog.gregback.net/2011/03/using-remnux-for-forensic-puzzle-6/). + +See also [this paper](https://computer-forensics.sans.org/community/papers/gcfa/grow-forensic-tools-taxonomy-python-libraries-helpful-forensic-analysis_6879) about python tools for forensics, which features OleFileIO_PL. + +About Python 2 and 3 +-------------------- + +OleFileIO\_PL used to support only Python 2.x. As of version 0.30, the code has been changed to be compatible with Python 3.x. As a consequence, compatibility with Python 2.5 or older is not provided anymore. However, a copy of v0.26 is available as OleFileIO_PL2.py. See above the "import" section for a workaround. + +If you think OleFileIO_PL should stay compatible with Python 2.5 or older, please [contact me](http://decalage.info/contact). + +How to contribute +----------------- + +The code is available in [a Mercurial repository on bitbucket](https://bitbucket.org/decalage/olefileio_pl). You may use it to submit enhancements or to report any issue. + +If you would like to help us improve this module, or simply provide feedback, please [contact me](http://decalage.info/contact). You can help in many ways: + +- test this module on different platforms / Python versions +- find and report bugs +- improve documentation, code samples, docstrings +- write unittest test cases +- provide tricky malformed files + +How to report bugs +------------------ + +To report a bug, for example a normal file which is not parsed correctly, please use the [issue reporting page](https://bitbucket.org/decalage/olefileio_pl/issues?status=new&status=open), or if you prefer to do it privately, use this [contact form](http://decalage.info/contact). Please provide all the information about the context and how to reproduce the bug. + +If possible please join the debugging output of OleFileIO_PL. For this, launch the following command : + + OleFileIO_PL.py -d -c file >debug.txt + +License +------- + +OleFileIO_PL is open-source. + +OleFileIO_PL changes are Copyright (c) 2005-2014 by Philippe Lagadec. + +The Python Imaging Library (PIL) is + +- Copyright (c) 1997-2005 by Secret Labs AB + +- Copyright (c) 1995-2005 by Fredrik Lundh + +By obtaining, using, and/or copying this software and/or its associated documentation, you agree that you have read, understood, and will comply with the following terms and conditions: + +Permission to use, copy, modify, and distribute this software and its associated documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appears in all copies, and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Secret Labs AB or the author not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. + +SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/pyPackages/pillowx86/PIL/OleFileIO.py b/pyPackages/pillowx86/PIL/OleFileIO.py new file mode 100644 index 0000000..c1cc5d5 --- /dev/null +++ b/pyPackages/pillowx86/PIL/OleFileIO.py @@ -0,0 +1,2089 @@ +#!/usr/bin/env python +## OleFileIO_PL: +## Module to read Microsoft OLE2 files (also called Structured Storage or +## Microsoft Compound Document File Format), such as Microsoft Office +## documents, Image Composer and FlashPix files, Outlook messages, ... +## This version is compatible with Python 2.6+ and 3.x + +## version 0.30 2014-02-04 Philippe Lagadec - http://www.decalage.info + +## Project website: http://www.decalage.info/python/olefileio + +## Improved version of the OleFileIO module from PIL library v1.1.6 +## See: http://www.pythonware.com/products/pil/index.htm + +## The Python Imaging Library (PIL) is + +## Copyright (c) 1997-2005 by Secret Labs AB +## Copyright (c) 1995-2005 by Fredrik Lundh + +## OleFileIO_PL changes are Copyright (c) 2005-2014 by Philippe Lagadec + +## See source code and LICENSE.txt for information on usage and redistribution. + +## WARNING: THIS IS (STILL) WORK IN PROGRESS. + + +# Starting with OleFileIO_PL v0.30, only Python 2.6+ and 3.x is supported +# This import enables print() as a function rather than a keyword +# (main requirement to be compatible with Python 3.x) +# The comment on the line below should be printed on Python 2.5 or older: +from __future__ import print_function # This version of OleFileIO_PL requires Python 2.6+ or 3.x. + + +__author__ = "Philippe Lagadec, Fredrik Lundh (Secret Labs AB)" +__date__ = "2014-02-04" +__version__ = '0.30' + +#--- LICENSE ------------------------------------------------------------------ + +# OleFileIO_PL is an improved version of the OleFileIO module from the +# Python Imaging Library (PIL). + +# OleFileIO_PL changes are Copyright (c) 2005-2014 by Philippe Lagadec +# +# The Python Imaging Library (PIL) is +# Copyright (c) 1997-2005 by Secret Labs AB +# Copyright (c) 1995-2005 by Fredrik Lundh +# +# By obtaining, using, and/or copying this software and/or its associated +# documentation, you agree that you have read, understood, and will comply with +# the following terms and conditions: +# +# Permission to use, copy, modify, and distribute this software and its +# associated documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appears in all copies, and that both +# that copyright notice and this permission notice appear in supporting +# documentation, and that the name of Secret Labs AB or the author(s) not be used +# in advertising or publicity pertaining to distribution of the software +# without specific, written prior permission. +# +# SECRET LABS AB AND THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +# SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +# IN NO EVENT SHALL SECRET LABS AB OR THE AUTHORS BE LIABLE FOR ANY SPECIAL, +# INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. + +#----------------------------------------------------------------------------- +# CHANGELOG: (only OleFileIO_PL changes compared to PIL 1.1.6) +# 2005-05-11 v0.10 PL: - a few fixes for Python 2.4 compatibility +# (all changes flagged with [PL]) +# 2006-02-22 v0.11 PL: - a few fixes for some Office 2003 documents which raise +# exceptions in _OleStream.__init__() +# 2006-06-09 v0.12 PL: - fixes for files above 6.8MB (DIFAT in loadfat) +# - added some constants +# - added header values checks +# - added some docstrings +# - getsect: bugfix in case sectors >512 bytes +# - getsect: added conformity checks +# - DEBUG_MODE constant to activate debug display +# 2007-09-04 v0.13 PL: - improved/translated (lots of) comments +# - updated license +# - converted tabs to 4 spaces +# 2007-11-19 v0.14 PL: - added OleFileIO._raise_defect() to adapt sensitivity +# - improved _unicode() to use Python 2.x unicode support +# - fixed bug in _OleDirectoryEntry +# 2007-11-25 v0.15 PL: - added safety checks to detect FAT loops +# - fixed _OleStream which didn't check stream size +# - added/improved many docstrings and comments +# - moved helper functions _unicode and _clsid out of +# OleFileIO class +# - improved OleFileIO._find() to add Unix path syntax +# - OleFileIO._find() is now case-insensitive +# - added get_type() and get_rootentry_name() +# - rewritten loaddirectory and _OleDirectoryEntry +# 2007-11-27 v0.16 PL: - added _OleDirectoryEntry.kids_dict +# - added detection of duplicate filenames in storages +# - added detection of duplicate references to streams +# - added get_size() and exists() to _OleDirectoryEntry +# - added isOleFile to check header before parsing +# - added __all__ list to control public keywords in pydoc +# 2007-12-04 v0.17 PL: - added _load_direntry to fix a bug in loaddirectory +# - improved _unicode(), added workarounds for Python <2.3 +# - added set_debug_mode and -d option to set debug mode +# - fixed bugs in OleFileIO.open and _OleDirectoryEntry +# - added safety check in main for large or binary +# properties +# - allow size>0 for storages for some implementations +# 2007-12-05 v0.18 PL: - fixed several bugs in handling of FAT, MiniFAT and +# streams +# - added option '-c' in main to check all streams +# 2009-12-10 v0.19 PL: - bugfix for 32 bit arrays on 64 bits platforms +# (thanks to Ben G. and Martijn for reporting the bug) +# 2009-12-11 v0.20 PL: - bugfix in OleFileIO.open when filename is not plain str +# 2010-01-22 v0.21 PL: - added support for big-endian CPUs such as PowerPC Macs +# 2012-02-16 v0.22 PL: - fixed bug in getproperties, patch by chuckleberryfinn +# (https://bitbucket.org/decalage/olefileio_pl/issue/7) +# - added close method to OleFileIO (fixed issue #2) +# 2012-07-25 v0.23 PL: - added support for file-like objects (patch by mete0r_kr) +# 2013-05-05 v0.24 PL: - getproperties: added conversion from filetime to python +# datetime +# - main: displays properties with date format +# - new class OleMetadata to parse standard properties +# - added get_metadata method +# 2013-05-07 v0.24 PL: - a few improvements in OleMetadata +# 2013-05-24 v0.25 PL: - getproperties: option to not convert some timestamps +# - OleMetaData: total_edit_time is now a number of seconds, +# not a timestamp +# - getproperties: added support for VT_BOOL, VT_INT, V_UINT +# - getproperties: filter out null chars from strings +# - getproperties: raise non-fatal defects instead of +# exceptions when properties cannot be parsed properly +# 2013-05-27 PL: - getproperties: improved exception handling +# - _raise_defect: added option to set exception type +# - all non-fatal issues are now recorded, and displayed +# when run as a script +# 2013-07-11 v0.26 PL: - added methods to get modification and creation times +# of a directory entry or a storage/stream +# - fixed parsing of direntry timestamps +# 2013-07-24 PL: - new options in listdir to list storages and/or streams +# 2014-02-04 v0.30 PL: - upgraded code to support Python 3.x by Martin Panter +# - several fixes for Python 2.6 (xrange, MAGIC) +# - reused i32 from Pillow's _binary + +#----------------------------------------------------------------------------- +# TODO (for version 1.0): +# + isOleFile should accept file-like objects like open +# + fix how all the methods handle unicode str and/or bytes as arguments +# + add path attrib to _OleDirEntry, set it once and for all in init or +# append_kids (then listdir/_list can be simplified) +# - TESTS with Linux, MacOSX, Python 1.5.2, various files, PIL, ... +# - add underscore to each private method, to avoid their display in +# pydoc/epydoc documentation - Remove it for classes to be documented +# - replace all raised exceptions with _raise_defect (at least in OleFileIO) +# - merge code from _OleStream and OleFileIO.getsect to read sectors +# (maybe add a class for FAT and MiniFAT ?) +# - add method to check all streams (follow sectors chains without storing all +# stream in memory, and report anomalies) +# - use _OleDirectoryEntry.kids_dict to improve _find and _list ? +# - fix Unicode names handling (find some way to stay compatible with Py1.5.2) +# => if possible avoid converting names to Latin-1 +# - review DIFAT code: fix handling of DIFSECT blocks in FAT (not stop) +# - rewrite OleFileIO.getproperties +# - improve docstrings to show more sample uses +# - see also original notes and FIXME below +# - remove all obsolete FIXMEs +# - OleMetadata: fix version attrib according to +# http://msdn.microsoft.com/en-us/library/dd945671%28v=office.12%29.aspx + +# IDEAS: +# - in OleFileIO._open and _OleStream, use size=None instead of 0x7FFFFFFF for +# streams with unknown size +# - use arrays of int instead of long integers for FAT/MiniFAT, to improve +# performance and reduce memory usage ? (possible issue with values >2^31) +# - provide tests with unittest (may need write support to create samples) +# - move all debug code (and maybe dump methods) to a separate module, with +# a class which inherits OleFileIO ? +# - fix docstrings to follow epydoc format +# - add support for 4K sectors ? +# - add support for big endian byte order ? +# - create a simple OLE explorer with wxPython + +# FUTURE EVOLUTIONS to add write support: +# 1) add ability to write a stream back on disk from BytesIO (same size, no +# change in FAT/MiniFAT). +# 2) rename a stream/storage if it doesn't change the RB tree +# 3) use rbtree module to update the red-black tree + any rename +# 4) remove a stream/storage: free sectors in FAT/MiniFAT +# 5) allocate new sectors in FAT/MiniFAT +# 6) create new storage/stream +#----------------------------------------------------------------------------- + +# +# THIS IS WORK IN PROGRESS +# +# The Python Imaging Library +# $Id$ +# +# stuff to deal with OLE2 Structured Storage files. this module is +# used by PIL to read Image Composer and FlashPix files, but can also +# be used to read other files of this type. +# +# History: +# 1997-01-20 fl Created +# 1997-01-22 fl Fixed 64-bit portability quirk +# 2003-09-09 fl Fixed typo in OleFileIO.loadfat (noted by Daniel Haertle) +# 2004-02-29 fl Changed long hex constants to signed integers +# +# Notes: +# FIXME: sort out sign problem (eliminate long hex constants) +# FIXME: change filename to use "a/b/c" instead of ["a", "b", "c"] +# FIXME: provide a glob mechanism function (using fnmatchcase) +# +# Literature: +# +# "FlashPix Format Specification, Appendix A", Kodak and Microsoft, +# September 1996. +# +# Quotes: +# +# "If this document and functionality of the Software conflict, +# the actual functionality of the Software represents the correct +# functionality" -- Microsoft, in the OLE format specification +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + +#------------------------------------------------------------------------------ + + +import io +import sys +import struct, array, os.path, datetime + +#[PL] Define explicitly the public API to avoid private objects in pydoc: +__all__ = ['OleFileIO', 'isOleFile', 'MAGIC'] + +# For Python 3.x, need to redefine long as int: +if str is not bytes: + long = int + +# Need to make sure we use xrange both on Python 2 and 3.x: +try: + # on Python 2 we need xrange: + iterrange = xrange +except: + # no xrange, for Python 3 it was renamed as range: + iterrange = range + +#[PL] workaround to fix an issue with array item size on 64 bits systems: +if array.array('L').itemsize == 4: + # on 32 bits platforms, long integers in an array are 32 bits: + UINT32 = 'L' +elif array.array('I').itemsize == 4: + # on 64 bits platforms, integers in an array are 32 bits: + UINT32 = 'I' +else: + raise ValueError('Need to fix a bug with 32 bit arrays, please contact author...') + + +#[PL] These workarounds were inspired from the Path module +# (see http://www.jorendorff.com/articles/python/path/) +#TODO: test with old Python versions + +# Pre-2.3 workaround for basestring. +try: + basestring +except NameError: + try: + # is Unicode supported (Python >2.0 or >1.6 ?) + basestring = (str, unicode) + except NameError: + basestring = str + +#[PL] Experimental setting: if True, OLE filenames will be kept in Unicode +# if False (default PIL behaviour), all filenames are converted to Latin-1. +KEEP_UNICODE_NAMES = False + +#[PL] DEBUG display mode: False by default, use set_debug_mode() or "-d" on +# command line to change it. +DEBUG_MODE = False +def debug_print(msg): + print(msg) +def debug_pass(msg): + pass +debug = debug_pass + +def set_debug_mode(debug_mode): + """ + Set debug mode on or off, to control display of debugging messages. + mode: True or False + """ + global DEBUG_MODE, debug + DEBUG_MODE = debug_mode + if debug_mode: + debug = debug_print + else: + debug = debug_pass + +MAGIC = b'\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1' + +#[PL]: added constants for Sector IDs (from AAF specifications) +MAXREGSECT = 0xFFFFFFFA; # maximum SECT +DIFSECT = 0xFFFFFFFC; # (-4) denotes a DIFAT sector in a FAT +FATSECT = 0xFFFFFFFD; # (-3) denotes a FAT sector in a FAT +ENDOFCHAIN = 0xFFFFFFFE; # (-2) end of a virtual stream chain +FREESECT = 0xFFFFFFFF; # (-1) unallocated sector + +#[PL]: added constants for Directory Entry IDs (from AAF specifications) +MAXREGSID = 0xFFFFFFFA; # maximum directory entry ID +NOSTREAM = 0xFFFFFFFF; # (-1) unallocated directory entry + +#[PL] object types in storage (from AAF specifications) +STGTY_EMPTY = 0 # empty directory entry (according to OpenOffice.org doc) +STGTY_STORAGE = 1 # element is a storage object +STGTY_STREAM = 2 # element is a stream object +STGTY_LOCKBYTES = 3 # element is an ILockBytes object +STGTY_PROPERTY = 4 # element is an IPropertyStorage object +STGTY_ROOT = 5 # element is a root storage + + +# +# -------------------------------------------------------------------- +# property types + +VT_EMPTY=0; VT_NULL=1; VT_I2=2; VT_I4=3; VT_R4=4; VT_R8=5; VT_CY=6; +VT_DATE=7; VT_BSTR=8; VT_DISPATCH=9; VT_ERROR=10; VT_BOOL=11; +VT_VARIANT=12; VT_UNKNOWN=13; VT_DECIMAL=14; VT_I1=16; VT_UI1=17; +VT_UI2=18; VT_UI4=19; VT_I8=20; VT_UI8=21; VT_INT=22; VT_UINT=23; +VT_VOID=24; VT_HRESULT=25; VT_PTR=26; VT_SAFEARRAY=27; VT_CARRAY=28; +VT_USERDEFINED=29; VT_LPSTR=30; VT_LPWSTR=31; VT_FILETIME=64; +VT_BLOB=65; VT_STREAM=66; VT_STORAGE=67; VT_STREAMED_OBJECT=68; +VT_STORED_OBJECT=69; VT_BLOB_OBJECT=70; VT_CF=71; VT_CLSID=72; +VT_VECTOR=0x1000; + +# map property id to name (for debugging purposes) + +VT = {} +for keyword, var in list(vars().items()): + if keyword[:3] == "VT_": + VT[var] = keyword + +# +# -------------------------------------------------------------------- +# Some common document types (root.clsid fields) + +WORD_CLSID = "00020900-0000-0000-C000-000000000046" +#TODO: check Excel, PPT, ... + +#[PL]: Defect levels to classify parsing errors - see OleFileIO._raise_defect() +DEFECT_UNSURE = 10 # a case which looks weird, but not sure it's a defect +DEFECT_POTENTIAL = 20 # a potential defect +DEFECT_INCORRECT = 30 # an error according to specifications, but parsing + # can go on +DEFECT_FATAL = 40 # an error which cannot be ignored, parsing is + # impossible + +#[PL] add useful constants to __all__: +for key in list(vars().keys()): + if key.startswith('STGTY_') or key.startswith('DEFECT_'): + __all__.append(key) + + +#--- FUNCTIONS ---------------------------------------------------------------- + +def isOleFile (filename): + """ + Test if file is an OLE container (according to its header). + + :param filename: file name or path (str, unicode) + :returns: True if OLE, False otherwise. + """ + f = open(filename, 'rb') + header = f.read(len(MAGIC)) + if header == MAGIC: + return True + else: + return False + + +if bytes is str: + # version for Python 2.x + def i8(c): + return ord(c) +else: + # version for Python 3.x + def i8(c): + return c if c.__class__ is int else c[0] + + +#TODO: replace i16 and i32 with more readable struct.unpack equivalent? + +def i16(c, o = 0): + """ + Converts a 2-bytes (16 bits) string to an integer. + + :param c: string containing bytes to convert + :param o: offset of bytes to convert in string + """ + return i8(c[o]) | (i8(c[o+1])<<8) + + +def i32(c, o = 0): + """ + Converts a 4-bytes (32 bits) string to an integer. + + :param c: string containing bytes to convert + :param o: offset of bytes to convert in string + """ +## return int(ord(c[o])+(ord(c[o+1])<<8)+(ord(c[o+2])<<16)+(ord(c[o+3])<<24)) +## # [PL]: added int() because "<<" gives long int since Python 2.4 + # copied from Pillow's _binary: + return i8(c[o]) | (i8(c[o+1])<<8) | (i8(c[o+2])<<16) | (i8(c[o+3])<<24) + + +def _clsid(clsid): + """ + Converts a CLSID to a human-readable string. + + :param clsid: string of length 16. + """ + assert len(clsid) == 16 + # if clsid is only made of null bytes, return an empty string: + # (PL: why not simply return the string with zeroes?) + if not clsid.strip(b"\0"): + return "" + return (("%08X-%04X-%04X-%02X%02X-" + "%02X" * 6) % + ((i32(clsid, 0), i16(clsid, 4), i16(clsid, 6)) + + tuple(map(i8, clsid[8:16])))) + + + +# UNICODE support: +# (necessary to handle storages/streams names which use Unicode) + +def _unicode(s, errors='replace'): + """ + Map unicode string to Latin 1. (Python with Unicode support) + + :param s: UTF-16LE unicode string to convert to Latin-1 + :param errors: 'replace', 'ignore' or 'strict'. + """ + #TODO: test if it OleFileIO works with Unicode strings, instead of + # converting to Latin-1. + try: + # First the string is converted to plain Unicode: + # (assuming it is encoded as UTF-16 little-endian) + u = s.decode('UTF-16LE', errors) + if bytes is not str or KEEP_UNICODE_NAMES: + return u + else: + # Second the unicode string is converted to Latin-1 + return u.encode('latin_1', errors) + except: + # there was an error during Unicode to Latin-1 conversion: + raise IOError('incorrect Unicode name') + + +def filetime2datetime(filetime): + """ + convert FILETIME (64 bits int) to Python datetime.datetime + """ + # TODO: manage exception when microseconds is too large + # inspired from http://code.activestate.com/recipes/511425-filetime-to-datetime/ + _FILETIME_null_date = datetime.datetime(1601, 1, 1, 0, 0, 0) + #debug('timedelta days=%d' % (filetime//(10*1000000*3600*24))) + return _FILETIME_null_date + datetime.timedelta(microseconds=filetime//10) + + + +#=== CLASSES ================================================================== + +class OleMetadata: + """ + class to parse and store metadata from standard properties of OLE files. + + Available attributes: + codepage, title, subject, author, keywords, comments, template, + last_saved_by, revision_number, total_edit_time, last_printed, create_time, + last_saved_time, num_pages, num_words, num_chars, thumbnail, + creating_application, security, codepage_doc, category, presentation_target, + bytes, lines, paragraphs, slides, notes, hidden_slides, mm_clips, + scale_crop, heading_pairs, titles_of_parts, manager, company, links_dirty, + chars_with_spaces, unused, shared_doc, link_base, hlinks, hlinks_changed, + version, dig_sig, content_type, content_status, language, doc_version + + Note: an attribute is set to None when not present in the properties of the + OLE file. + + References for SummaryInformation stream: + - http://msdn.microsoft.com/en-us/library/dd942545.aspx + - http://msdn.microsoft.com/en-us/library/dd925819%28v=office.12%29.aspx + - http://msdn.microsoft.com/en-us/library/windows/desktop/aa380376%28v=vs.85%29.aspx + - http://msdn.microsoft.com/en-us/library/aa372045.aspx + - http://sedna-soft.de/summary-information-stream/ + - http://poi.apache.org/apidocs/org/apache/poi/hpsf/SummaryInformation.html + + References for DocumentSummaryInformation stream: + - http://msdn.microsoft.com/en-us/library/dd945671%28v=office.12%29.aspx + - http://msdn.microsoft.com/en-us/library/windows/desktop/aa380374%28v=vs.85%29.aspx + - http://poi.apache.org/apidocs/org/apache/poi/hpsf/DocumentSummaryInformation.html + + new in version 0.25 + """ + + # attribute names for SummaryInformation stream properties: + # (ordered by property id, starting at 1) + SUMMARY_ATTRIBS = ['codepage', 'title', 'subject', 'author', 'keywords', 'comments', + 'template', 'last_saved_by', 'revision_number', 'total_edit_time', + 'last_printed', 'create_time', 'last_saved_time', 'num_pages', + 'num_words', 'num_chars', 'thumbnail', 'creating_application', + 'security'] + + # attribute names for DocumentSummaryInformation stream properties: + # (ordered by property id, starting at 1) + DOCSUM_ATTRIBS = ['codepage_doc', 'category', 'presentation_target', 'bytes', 'lines', 'paragraphs', + 'slides', 'notes', 'hidden_slides', 'mm_clips', + 'scale_crop', 'heading_pairs', 'titles_of_parts', 'manager', + 'company', 'links_dirty', 'chars_with_spaces', 'unused', 'shared_doc', + 'link_base', 'hlinks', 'hlinks_changed', 'version', 'dig_sig', + 'content_type', 'content_status', 'language', 'doc_version'] + + def __init__(self): + """ + Constructor for OleMetadata + All attributes are set to None by default + """ + # properties from SummaryInformation stream + self.codepage = None + self.title = None + self.subject = None + self.author = None + self.keywords = None + self.comments = None + self.template = None + self.last_saved_by = None + self.revision_number = None + self.total_edit_time = None + self.last_printed = None + self.create_time = None + self.last_saved_time = None + self.num_pages = None + self.num_words = None + self.num_chars = None + self.thumbnail = None + self.creating_application = None + self.security = None + # properties from DocumentSummaryInformation stream + self.codepage_doc = None + self.category = None + self.presentation_target = None + self.bytes = None + self.lines = None + self.paragraphs = None + self.slides = None + self.notes = None + self.hidden_slides = None + self.mm_clips = None + self.scale_crop = None + self.heading_pairs = None + self.titles_of_parts = None + self.manager = None + self.company = None + self.links_dirty = None + self.chars_with_spaces = None + self.unused = None + self.shared_doc = None + self.link_base = None + self.hlinks = None + self.hlinks_changed = None + self.version = None + self.dig_sig = None + self.content_type = None + self.content_status = None + self.language = None + self.doc_version = None + + + def parse_properties(self, olefile): + """ + Parse standard properties of an OLE file, from the streams + "\x05SummaryInformation" and "\x05DocumentSummaryInformation", + if present. + Properties are converted to strings, integers or python datetime objects. + If a property is not present, its value is set to None. + """ + # first set all attributes to None: + for attrib in (self.SUMMARY_ATTRIBS + self.DOCSUM_ATTRIBS): + setattr(self, attrib, None) + if olefile.exists("\x05SummaryInformation"): + # get properties from the stream: + # (converting timestamps to python datetime, except total_edit_time, + # which is property #10) + props = olefile.getproperties("\x05SummaryInformation", + convert_time=True, no_conversion=[10]) + # store them into this object's attributes: + for i in range(len(self.SUMMARY_ATTRIBS)): + # ids for standards properties start at 0x01, until 0x13 + value = props.get(i+1, None) + setattr(self, self.SUMMARY_ATTRIBS[i], value) + if olefile.exists("\x05DocumentSummaryInformation"): + # get properties from the stream: + props = olefile.getproperties("\x05DocumentSummaryInformation", + convert_time=True) + # store them into this object's attributes: + for i in range(len(self.DOCSUM_ATTRIBS)): + # ids for standards properties start at 0x01, until 0x13 + value = props.get(i+1, None) + setattr(self, self.DOCSUM_ATTRIBS[i], value) + + def dump(self): + """ + Dump all metadata, for debugging purposes. + """ + print('Properties from SummaryInformation stream:') + for prop in self.SUMMARY_ATTRIBS: + value = getattr(self, prop) + print('- %s: %s' % (prop, repr(value))) + print('Properties from DocumentSummaryInformation stream:') + for prop in self.DOCSUM_ATTRIBS: + value = getattr(self, prop) + print('- %s: %s' % (prop, repr(value))) + + +#--- _OleStream --------------------------------------------------------------- + +class _OleStream(io.BytesIO): + """ + OLE2 Stream + + Returns a read-only file object which can be used to read + the contents of a OLE stream (instance of the BytesIO class). + To open a stream, use the openstream method in the OleFile class. + + This function can be used with either ordinary streams, + or ministreams, depending on the offset, sectorsize, and + fat table arguments. + + Attributes: + - size: actual size of data stream, after it was opened. + """ + + # FIXME: should store the list of sects obtained by following + # the fat chain, and load new sectors on demand instead of + # loading it all in one go. + + def __init__(self, fp, sect, size, offset, sectorsize, fat, filesize): + """ + Constructor for _OleStream class. + + :param fp : file object, the OLE container or the MiniFAT stream + :param sect : sector index of first sector in the stream + :param size : total size of the stream + :param offset : offset in bytes for the first FAT or MiniFAT sector + :param sectorsize: size of one sector + :param fat : array/list of sector indexes (FAT or MiniFAT) + :param filesize : size of OLE file (for debugging) + :returns : a BytesIO instance containing the OLE stream + """ + debug('_OleStream.__init__:') + debug(' sect=%d (%X), size=%d, offset=%d, sectorsize=%d, len(fat)=%d, fp=%s' + %(sect,sect,size,offset,sectorsize,len(fat), repr(fp))) + #[PL] To detect malformed documents with FAT loops, we compute the + # expected number of sectors in the stream: + unknown_size = False + if size==0x7FFFFFFF: + # this is the case when called from OleFileIO._open(), and stream + # size is not known in advance (for example when reading the + # Directory stream). Then we can only guess maximum size: + size = len(fat)*sectorsize + # and we keep a record that size was unknown: + unknown_size = True + debug(' stream with UNKNOWN SIZE') + nb_sectors = (size + (sectorsize-1)) // sectorsize + debug('nb_sectors = %d' % nb_sectors) + # This number should (at least) be less than the total number of + # sectors in the given FAT: + if nb_sectors > len(fat): + raise IOError('malformed OLE document, stream too large') + # optimization(?): data is first a list of strings, and join() is called + # at the end to concatenate all in one string. + # (this may not be really useful with recent Python versions) + data = [] + # if size is zero, then first sector index should be ENDOFCHAIN: + if size == 0 and sect != ENDOFCHAIN: + debug('size == 0 and sect != ENDOFCHAIN:') + raise IOError('incorrect OLE sector index for empty stream') + #[PL] A fixed-length for loop is used instead of an undefined while + # loop to avoid DoS attacks: + for i in range(nb_sectors): + # Sector index may be ENDOFCHAIN, but only if size was unknown + if sect == ENDOFCHAIN: + if unknown_size: + break + else: + # else this means that the stream is smaller than declared: + debug('sect=ENDOFCHAIN before expected size') + raise IOError('incomplete OLE stream') + # sector index should be within FAT: + if sect<0 or sect>=len(fat): + debug('sect=%d (%X) / len(fat)=%d' % (sect, sect, len(fat))) + debug('i=%d / nb_sectors=%d' %(i, nb_sectors)) +## tmp_data = b"".join(data) +## f = open('test_debug.bin', 'wb') +## f.write(tmp_data) +## f.close() +## debug('data read so far: %d bytes' % len(tmp_data)) + raise IOError('incorrect OLE FAT, sector index out of range') + #TODO: merge this code with OleFileIO.getsect() ? + #TODO: check if this works with 4K sectors: + try: + fp.seek(offset + sectorsize * sect) + except: + debug('sect=%d, seek=%d, filesize=%d' % + (sect, offset+sectorsize*sect, filesize)) + raise IOError('OLE sector index out of range') + sector_data = fp.read(sectorsize) + # [PL] check if there was enough data: + # Note: if sector is the last of the file, sometimes it is not a + # complete sector (of 512 or 4K), so we may read less than + # sectorsize. + if len(sector_data)!=sectorsize and sect!=(len(fat)-1): + debug('sect=%d / len(fat)=%d, seek=%d / filesize=%d, len read=%d' % + (sect, len(fat), offset+sectorsize*sect, filesize, len(sector_data))) + debug('seek+len(read)=%d' % (offset+sectorsize*sect+len(sector_data))) + raise IOError('incomplete OLE sector') + data.append(sector_data) + # jump to next sector in the FAT: + try: + sect = fat[sect] + except IndexError: + # [PL] if pointer is out of the FAT an exception is raised + raise IOError('incorrect OLE FAT, sector index out of range') + #[PL] Last sector should be a "end of chain" marker: + if sect != ENDOFCHAIN: + raise IOError('incorrect last sector index in OLE stream') + data = b"".join(data) + # Data is truncated to the actual stream size: + if len(data) >= size: + data = data[:size] + # actual stream size is stored for future use: + self.size = size + elif unknown_size: + # actual stream size was not known, now we know the size of read + # data: + self.size = len(data) + else: + # read data is less than expected: + debug('len(data)=%d, size=%d' % (len(data), size)) + raise IOError('OLE stream size is less than declared') + # when all data is read in memory, BytesIO constructor is called + io.BytesIO.__init__(self, data) + # Then the _OleStream object can be used as a read-only file object. + + +#--- _OleDirectoryEntry ------------------------------------------------------- + +class _OleDirectoryEntry: + + """ + OLE2 Directory Entry + """ + #[PL] parsing code moved from OleFileIO.loaddirectory + + # struct to parse directory entries: + # <: little-endian byte order, standard sizes + # (note: this should guarantee that Q returns a 64 bits int) + # 64s: string containing entry name in unicode (max 31 chars) + null char + # H: uint16, number of bytes used in name buffer, including null = (len+1)*2 + # B: uint8, dir entry type (between 0 and 5) + # B: uint8, color: 0=black, 1=red + # I: uint32, index of left child node in the red-black tree, NOSTREAM if none + # I: uint32, index of right child node in the red-black tree, NOSTREAM if none + # I: uint32, index of child root node if it is a storage, else NOSTREAM + # 16s: CLSID, unique identifier (only used if it is a storage) + # I: uint32, user flags + # Q (was 8s): uint64, creation timestamp or zero + # Q (was 8s): uint64, modification timestamp or zero + # I: uint32, SID of first sector if stream or ministream, SID of 1st sector + # of stream containing ministreams if root entry, 0 otherwise + # I: uint32, total stream size in bytes if stream (low 32 bits), 0 otherwise + # I: uint32, total stream size in bytes if stream (high 32 bits), 0 otherwise + STRUCT_DIRENTRY = '<64sHBBIII16sIQQIII' + # size of a directory entry: 128 bytes + DIRENTRY_SIZE = 128 + assert struct.calcsize(STRUCT_DIRENTRY) == DIRENTRY_SIZE + + + def __init__(self, entry, sid, olefile): + """ + Constructor for an _OleDirectoryEntry object. + Parses a 128-bytes entry from the OLE Directory stream. + + :param entry : string (must be 128 bytes long) + :param sid : index of this directory entry in the OLE file directory + :param olefile: OleFileIO containing this directory entry + """ + self.sid = sid + # ref to olefile is stored for future use + self.olefile = olefile + # kids is a list of children entries, if this entry is a storage: + # (list of _OleDirectoryEntry objects) + self.kids = [] + # kids_dict is a dictionary of children entries, indexed by their + # name in lowercase: used to quickly find an entry, and to detect + # duplicates + self.kids_dict = {} + # flag used to detect if the entry is referenced more than once in + # directory: + self.used = False + # decode DirEntry + ( + name, + namelength, + self.entry_type, + self.color, + self.sid_left, + self.sid_right, + self.sid_child, + clsid, + self.dwUserFlags, + self.createTime, + self.modifyTime, + self.isectStart, + sizeLow, + sizeHigh + ) = struct.unpack(_OleDirectoryEntry.STRUCT_DIRENTRY, entry) + if self.entry_type not in [STGTY_ROOT, STGTY_STORAGE, STGTY_STREAM, STGTY_EMPTY]: + olefile._raise_defect(DEFECT_INCORRECT, 'unhandled OLE storage type') + # only first directory entry can (and should) be root: + if self.entry_type == STGTY_ROOT and sid != 0: + olefile._raise_defect(DEFECT_INCORRECT, 'duplicate OLE root entry') + if sid == 0 and self.entry_type != STGTY_ROOT: + olefile._raise_defect(DEFECT_INCORRECT, 'incorrect OLE root entry') + #debug (struct.unpack(fmt_entry, entry[:len_entry])) + # name should be at most 31 unicode characters + null character, + # so 64 bytes in total (31*2 + 2): + if namelength>64: + olefile._raise_defect(DEFECT_INCORRECT, 'incorrect DirEntry name length') + # if exception not raised, namelength is set to the maximum value: + namelength = 64 + # only characters without ending null char are kept: + name = name[:(namelength-2)] + # name is converted from unicode to Latin-1: + self.name = _unicode(name) + + debug('DirEntry SID=%d: %s' % (self.sid, repr(self.name))) + debug(' - type: %d' % self.entry_type) + debug(' - sect: %d' % self.isectStart) + debug(' - SID left: %d, right: %d, child: %d' % (self.sid_left, + self.sid_right, self.sid_child)) + + # sizeHigh is only used for 4K sectors, it should be zero for 512 bytes + # sectors, BUT apparently some implementations set it as 0xFFFFFFFF, 1 + # or some other value so it cannot be raised as a defect in general: + if olefile.sectorsize == 512: + if sizeHigh != 0 and sizeHigh != 0xFFFFFFFF: + debug('sectorsize=%d, sizeLow=%d, sizeHigh=%d (%X)' % + (olefile.sectorsize, sizeLow, sizeHigh, sizeHigh)) + olefile._raise_defect(DEFECT_UNSURE, 'incorrect OLE stream size') + self.size = sizeLow + else: + self.size = sizeLow + (long(sizeHigh)<<32) + debug(' - size: %d (sizeLow=%d, sizeHigh=%d)' % (self.size, sizeLow, sizeHigh)) + + self.clsid = _clsid(clsid) + # a storage should have a null size, BUT some implementations such as + # Word 8 for Mac seem to allow non-null values => Potential defect: + if self.entry_type == STGTY_STORAGE and self.size != 0: + olefile._raise_defect(DEFECT_POTENTIAL, 'OLE storage with size>0') + # check if stream is not already referenced elsewhere: + if self.entry_type in (STGTY_ROOT, STGTY_STREAM) and self.size>0: + if self.size < olefile.minisectorcutoff \ + and self.entry_type==STGTY_STREAM: # only streams can be in MiniFAT + # ministream object + minifat = True + else: + minifat = False + olefile._check_duplicate_stream(self.isectStart, minifat) + + + + def build_storage_tree(self): + """ + Read and build the red-black tree attached to this _OleDirectoryEntry + object, if it is a storage. + Note that this method builds a tree of all subentries, so it should + only be called for the root object once. + """ + debug('build_storage_tree: SID=%d - %s - sid_child=%d' + % (self.sid, repr(self.name), self.sid_child)) + if self.sid_child != NOSTREAM: + # if child SID is not NOSTREAM, then this entry is a storage. + # Let's walk through the tree of children to fill the kids list: + self.append_kids(self.sid_child) + + # Note from OpenOffice documentation: the safest way is to + # recreate the tree because some implementations may store broken + # red-black trees... + + # in the OLE file, entries are sorted on (length, name). + # for convenience, we sort them on name instead: + # (see rich comparison methods in this class) + self.kids.sort() + + + def append_kids(self, child_sid): + """ + Walk through red-black tree of children of this directory entry to add + all of them to the kids list. (recursive method) + + child_sid : index of child directory entry to use, or None when called + first time for the root. (only used during recursion) + """ + #[PL] this method was added to use simple recursion instead of a complex + # algorithm. + # if this is not a storage or a leaf of the tree, nothing to do: + if child_sid == NOSTREAM: + return + # check if child SID is in the proper range: + if child_sid<0 or child_sid>=len(self.olefile.direntries): + self.olefile._raise_defect(DEFECT_FATAL, 'OLE DirEntry index out of range') + # get child direntry: + child = self.olefile._load_direntry(child_sid) #direntries[child_sid] + debug('append_kids: child_sid=%d - %s - sid_left=%d, sid_right=%d, sid_child=%d' + % (child.sid, repr(child.name), child.sid_left, child.sid_right, child.sid_child)) + # the directory entries are organized as a red-black tree. + # (cf. Wikipedia for details) + # First walk through left side of the tree: + self.append_kids(child.sid_left) + # Check if its name is not already used (case-insensitive): + name_lower = child.name.lower() + if name_lower in self.kids_dict: + self.olefile._raise_defect(DEFECT_INCORRECT, + "Duplicate filename in OLE storage") + # Then the child_sid _OleDirectoryEntry object is appended to the + # kids list and dictionary: + self.kids.append(child) + self.kids_dict[name_lower] = child + # Check if kid was not already referenced in a storage: + if child.used: + self.olefile._raise_defect(DEFECT_INCORRECT, + 'OLE Entry referenced more than once') + child.used = True + # Finally walk through right side of the tree: + self.append_kids(child.sid_right) + # Afterwards build kid's own tree if it's also a storage: + child.build_storage_tree() + + + def __eq__(self, other): + "Compare entries by name" + return self.name == other.name + + def __lt__(self, other): + "Compare entries by name" + return self.name < other.name + + def __ne__(self, other): + return not self.__eq__(other) + + def __le__(self, other): + return self.__eq__(other) or self.__lt__(other) + + # Reflected __lt__() and __le__() will be used for __gt__() and __ge__() + + #TODO: replace by the same function as MS implementation ? + # (order by name length first, then case-insensitive order) + + + def dump(self, tab = 0): + "Dump this entry, and all its subentries (for debug purposes only)" + TYPES = ["(invalid)", "(storage)", "(stream)", "(lockbytes)", + "(property)", "(root)"] + print(" "*tab + repr(self.name), TYPES[self.entry_type], end=' ') + if self.entry_type in (STGTY_STREAM, STGTY_ROOT): + print(self.size, "bytes", end=' ') + print() + if self.entry_type in (STGTY_STORAGE, STGTY_ROOT) and self.clsid: + print(" "*tab + "{%s}" % self.clsid) + + for kid in self.kids: + kid.dump(tab + 2) + + + def getmtime(self): + """ + Return modification time of a directory entry. + + :returns: None if modification time is null, a python datetime object + otherwise (UTC timezone) + + new in version 0.26 + """ + if self.modifyTime == 0: + return None + return filetime2datetime(self.modifyTime) + + + def getctime(self): + """ + Return creation time of a directory entry. + + :returns: None if modification time is null, a python datetime object + otherwise (UTC timezone) + + new in version 0.26 + """ + if self.createTime == 0: + return None + return filetime2datetime(self.createTime) + + +#--- OleFileIO ---------------------------------------------------------------- + +class OleFileIO: + """ + OLE container object + + This class encapsulates the interface to an OLE 2 structured + storage file. Use the :py:meth:`~PIL.OleFileIO.OleFileIO.listdir` and + :py:meth:`~PIL.OleFileIO.OleFileIO.openstream` methods to + access the contents of this file. + + Object names are given as a list of strings, one for each subentry + level. The root entry should be omitted. For example, the following + code extracts all image streams from a Microsoft Image Composer file:: + + ole = OleFileIO("fan.mic") + + for entry in ole.listdir(): + if entry[1:2] == "Image": + fin = ole.openstream(entry) + fout = open(entry[0:1], "wb") + while True: + s = fin.read(8192) + if not s: + break + fout.write(s) + + You can use the viewer application provided with the Python Imaging + Library to view the resulting files (which happens to be standard + TIFF files). + """ + + def __init__(self, filename = None, raise_defects=DEFECT_FATAL): + """ + Constructor for OleFileIO class. + + :param filename: file to open. + :param raise_defects: minimal level for defects to be raised as exceptions. + (use DEFECT_FATAL for a typical application, DEFECT_INCORRECT for a + security-oriented application, see source code for details) + """ + # minimal level for defects to be raised as exceptions: + self._raise_defects_level = raise_defects + # list of defects/issues not raised as exceptions: + # tuples of (exception type, message) + self.parsing_issues = [] + if filename: + self.open(filename) + + + def _raise_defect(self, defect_level, message, exception_type=IOError): + """ + This method should be called for any defect found during file parsing. + It may raise an IOError exception according to the minimal level chosen + for the OleFileIO object. + + :param defect_level: defect level, possible values are: + DEFECT_UNSURE : a case which looks weird, but not sure it's a defect + DEFECT_POTENTIAL : a potential defect + DEFECT_INCORRECT : an error according to specifications, but parsing can go on + DEFECT_FATAL : an error which cannot be ignored, parsing is impossible + :param message: string describing the defect, used with raised exception. + :param exception_type: exception class to be raised, IOError by default + """ + # added by [PL] + if defect_level >= self._raise_defects_level: + raise exception_type(message) + else: + # just record the issue, no exception raised: + self.parsing_issues.append((exception_type, message)) + + + def open(self, filename): + """ + Open an OLE2 file. + Reads the header, FAT and directory. + + :param filename: string-like or file-like object + """ + #[PL] check if filename is a string-like or file-like object: + # (it is better to check for a read() method) + if hasattr(filename, 'read'): + # file-like object + self.fp = filename + else: + # string-like object: filename of file on disk + #TODO: if larger than 1024 bytes, this could be the actual data => BytesIO + self.fp = open(filename, "rb") + # old code fails if filename is not a plain string: + #if isinstance(filename, (bytes, basestring)): + # self.fp = open(filename, "rb") + #else: + # self.fp = filename + # obtain the filesize by using seek and tell, which should work on most + # file-like objects: + #TODO: do it above, using getsize with filename when possible? + #TODO: fix code to fail with clear exception when filesize cannot be obtained + self.fp.seek(0, os.SEEK_END) + try: + filesize = self.fp.tell() + finally: + self.fp.seek(0) + self._filesize = filesize + + # lists of streams in FAT and MiniFAT, to detect duplicate references + # (list of indexes of first sectors of each stream) + self._used_streams_fat = [] + self._used_streams_minifat = [] + + header = self.fp.read(512) + + if len(header) != 512 or header[:8] != MAGIC: + self._raise_defect(DEFECT_FATAL, "not an OLE2 structured storage file") + + # [PL] header structure according to AAF specifications: + ##Header + ##struct StructuredStorageHeader { // [offset from start (bytes), length (bytes)] + ##BYTE _abSig[8]; // [00H,08] {0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, + ## // 0x1a, 0xe1} for current version + ##CLSID _clsid; // [08H,16] reserved must be zero (WriteClassStg/ + ## // GetClassFile uses root directory class id) + ##USHORT _uMinorVersion; // [18H,02] minor version of the format: 33 is + ## // written by reference implementation + ##USHORT _uDllVersion; // [1AH,02] major version of the dll/format: 3 for + ## // 512-byte sectors, 4 for 4 KB sectors + ##USHORT _uByteOrder; // [1CH,02] 0xFFFE: indicates Intel byte-ordering + ##USHORT _uSectorShift; // [1EH,02] size of sectors in power-of-two; + ## // typically 9 indicating 512-byte sectors + ##USHORT _uMiniSectorShift; // [20H,02] size of mini-sectors in power-of-two; + ## // typically 6 indicating 64-byte mini-sectors + ##USHORT _usReserved; // [22H,02] reserved, must be zero + ##ULONG _ulReserved1; // [24H,04] reserved, must be zero + ##FSINDEX _csectDir; // [28H,04] must be zero for 512-byte sectors, + ## // number of SECTs in directory chain for 4 KB + ## // sectors + ##FSINDEX _csectFat; // [2CH,04] number of SECTs in the FAT chain + ##SECT _sectDirStart; // [30H,04] first SECT in the directory chain + ##DFSIGNATURE _signature; // [34H,04] signature used for transactions; must + ## // be zero. The reference implementation + ## // does not support transactions + ##ULONG _ulMiniSectorCutoff; // [38H,04] maximum size for a mini stream; + ## // typically 4096 bytes + ##SECT _sectMiniFatStart; // [3CH,04] first SECT in the MiniFAT chain + ##FSINDEX _csectMiniFat; // [40H,04] number of SECTs in the MiniFAT chain + ##SECT _sectDifStart; // [44H,04] first SECT in the DIFAT chain + ##FSINDEX _csectDif; // [48H,04] number of SECTs in the DIFAT chain + ##SECT _sectFat[109]; // [4CH,436] the SECTs of first 109 FAT sectors + ##}; + + # [PL] header decoding: + # '<' indicates little-endian byte ordering for Intel (cf. struct module help) + fmt_header = '<8s16sHHHHHHLLLLLLLLLL' + header_size = struct.calcsize(fmt_header) + debug( "fmt_header size = %d, +FAT = %d" % (header_size, header_size + 109*4) ) + header1 = header[:header_size] + ( + self.Sig, + self.clsid, + self.MinorVersion, + self.DllVersion, + self.ByteOrder, + self.SectorShift, + self.MiniSectorShift, + self.Reserved, self.Reserved1, + self.csectDir, + self.csectFat, + self.sectDirStart, + self.signature, + self.MiniSectorCutoff, + self.MiniFatStart, + self.csectMiniFat, + self.sectDifStart, + self.csectDif + ) = struct.unpack(fmt_header, header1) + debug( struct.unpack(fmt_header, header1)) + + if self.Sig != MAGIC: + # OLE signature should always be present + self._raise_defect(DEFECT_FATAL, "incorrect OLE signature") + if self.clsid != bytearray(16): + # according to AAF specs, CLSID should always be zero + self._raise_defect(DEFECT_INCORRECT, "incorrect CLSID in OLE header") + debug( "MinorVersion = %d" % self.MinorVersion ) + debug( "DllVersion = %d" % self.DllVersion ) + if self.DllVersion not in [3, 4]: + # version 3: usual format, 512 bytes per sector + # version 4: large format, 4K per sector + self._raise_defect(DEFECT_INCORRECT, "incorrect DllVersion in OLE header") + debug( "ByteOrder = %X" % self.ByteOrder ) + if self.ByteOrder != 0xFFFE: + # For now only common little-endian documents are handled correctly + self._raise_defect(DEFECT_FATAL, "incorrect ByteOrder in OLE header") + # TODO: add big-endian support for documents created on Mac ? + self.SectorSize = 2**self.SectorShift + debug( "SectorSize = %d" % self.SectorSize ) + if self.SectorSize not in [512, 4096]: + self._raise_defect(DEFECT_INCORRECT, "incorrect SectorSize in OLE header") + if (self.DllVersion==3 and self.SectorSize!=512) \ + or (self.DllVersion==4 and self.SectorSize!=4096): + self._raise_defect(DEFECT_INCORRECT, "SectorSize does not match DllVersion in OLE header") + self.MiniSectorSize = 2**self.MiniSectorShift + debug( "MiniSectorSize = %d" % self.MiniSectorSize ) + if self.MiniSectorSize not in [64]: + self._raise_defect(DEFECT_INCORRECT, "incorrect MiniSectorSize in OLE header") + if self.Reserved != 0 or self.Reserved1 != 0: + self._raise_defect(DEFECT_INCORRECT, "incorrect OLE header (non-null reserved bytes)") + debug( "csectDir = %d" % self.csectDir ) + if self.SectorSize==512 and self.csectDir!=0: + self._raise_defect(DEFECT_INCORRECT, "incorrect csectDir in OLE header") + debug( "csectFat = %d" % self.csectFat ) + debug( "sectDirStart = %X" % self.sectDirStart ) + debug( "signature = %d" % self.signature ) + # Signature should be zero, BUT some implementations do not follow this + # rule => only a potential defect: + if self.signature != 0: + self._raise_defect(DEFECT_POTENTIAL, "incorrect OLE header (signature>0)") + debug( "MiniSectorCutoff = %d" % self.MiniSectorCutoff ) + debug( "MiniFatStart = %X" % self.MiniFatStart ) + debug( "csectMiniFat = %d" % self.csectMiniFat ) + debug( "sectDifStart = %X" % self.sectDifStart ) + debug( "csectDif = %d" % self.csectDif ) + + # calculate the number of sectors in the file + # (-1 because header doesn't count) + self.nb_sect = ( (filesize + self.SectorSize-1) // self.SectorSize) - 1 + debug( "Number of sectors in the file: %d" % self.nb_sect ) + + # file clsid (probably never used, so we don't store it) + clsid = _clsid(header[8:24]) + self.sectorsize = self.SectorSize #1 << i16(header, 30) + self.minisectorsize = self.MiniSectorSize #1 << i16(header, 32) + self.minisectorcutoff = self.MiniSectorCutoff # i32(header, 56) + + # check known streams for duplicate references (these are always in FAT, + # never in MiniFAT): + self._check_duplicate_stream(self.sectDirStart) + # check MiniFAT only if it is not empty: + if self.csectMiniFat: + self._check_duplicate_stream(self.MiniFatStart) + # check DIFAT only if it is not empty: + if self.csectDif: + self._check_duplicate_stream(self.sectDifStart) + + # Load file allocation tables + self.loadfat(header) + # Load direcory. This sets both the direntries list (ordered by sid) + # and the root (ordered by hierarchy) members. + self.loaddirectory(self.sectDirStart)#i32(header, 48)) + self.ministream = None + self.minifatsect = self.MiniFatStart #i32(header, 60) + + + def close(self): + """ + close the OLE file, to release the file object + """ + self.fp.close() + + + def _check_duplicate_stream(self, first_sect, minifat=False): + """ + Checks if a stream has not been already referenced elsewhere. + This method should only be called once for each known stream, and only + if stream size is not null. + :param first_sect: index of first sector of the stream in FAT + :param minifat: if True, stream is located in the MiniFAT, else in the FAT + """ + if minifat: + debug('_check_duplicate_stream: sect=%d in MiniFAT' % first_sect) + used_streams = self._used_streams_minifat + else: + debug('_check_duplicate_stream: sect=%d in FAT' % first_sect) + # some values can be safely ignored (not a real stream): + if first_sect in (DIFSECT,FATSECT,ENDOFCHAIN,FREESECT): + return + used_streams = self._used_streams_fat + #TODO: would it be more efficient using a dict or hash values, instead + # of a list of long ? + if first_sect in used_streams: + self._raise_defect(DEFECT_INCORRECT, 'Stream referenced twice') + else: + used_streams.append(first_sect) + + + def dumpfat(self, fat, firstindex=0): + "Displays a part of FAT in human-readable form for debugging purpose" + # [PL] added only for debug + if not DEBUG_MODE: + return + # dictionary to convert special FAT values in human-readable strings + VPL=8 # valeurs par ligne (8+1 * 8+1 = 81) + fatnames = { + FREESECT: "..free..", + ENDOFCHAIN: "[ END. ]", + FATSECT: "FATSECT ", + DIFSECT: "DIFSECT " + } + nbsect = len(fat) + nlines = (nbsect+VPL-1)//VPL + print("index", end=" ") + for i in range(VPL): + print("%8X" % i, end=" ") + print() + for l in range(nlines): + index = l*VPL + print("%8X:" % (firstindex+index), end=" ") + for i in range(index, index+VPL): + if i>=nbsect: + break + sect = fat[i] + if sect in fatnames: + nom = fatnames[sect] + else: + if sect == i+1: + nom = " --->" + else: + nom = "%8X" % sect + print(nom, end=" ") + print() + + + def dumpsect(self, sector, firstindex=0): + "Displays a sector in a human-readable form, for debugging purpose." + if not DEBUG_MODE: + return + VPL=8 # number of values per line (8+1 * 8+1 = 81) + tab = array.array(UINT32, sector) + nbsect = len(tab) + nlines = (nbsect+VPL-1)//VPL + print("index", end=" ") + for i in range(VPL): + print("%8X" % i, end=" ") + print() + for l in range(nlines): + index = l*VPL + print("%8X:" % (firstindex+index), end=" ") + for i in range(index, index+VPL): + if i>=nbsect: + break + sect = tab[i] + nom = "%8X" % sect + print(nom, end=" ") + print() + + def sect2array(self, sect): + """ + convert a sector to an array of 32 bits unsigned integers, + swapping bytes on big endian CPUs such as PowerPC (old Macs) + """ + a = array.array(UINT32, sect) + # if CPU is big endian, swap bytes: + if sys.byteorder == 'big': + a.byteswap() + return a + + + def loadfat_sect(self, sect): + """ + Adds the indexes of the given sector to the FAT + + :param sect: string containing the first FAT sector, or array of long integers + :returns: index of last FAT sector. + """ + # a FAT sector is an array of ulong integers. + if isinstance(sect, array.array): + # if sect is already an array it is directly used + fat1 = sect + else: + # if it's a raw sector, it is parsed in an array + fat1 = self.sect2array(sect) + self.dumpsect(sect) + # The FAT is a sector chain starting at the first index of itself. + for isect in fat1: + #print("isect = %X" % isect) + if isect == ENDOFCHAIN or isect == FREESECT: + # the end of the sector chain has been reached + break + # read the FAT sector + s = self.getsect(isect) + # parse it as an array of 32 bits integers, and add it to the + # global FAT array + nextfat = self.sect2array(s) + self.fat = self.fat + nextfat + return isect + + + def loadfat(self, header): + """ + Load the FAT table. + """ + # The header contains a sector numbers + # for the first 109 FAT sectors. Additional sectors are + # described by DIF blocks + + sect = header[76:512] + debug( "len(sect)=%d, so %d integers" % (len(sect), len(sect)//4) ) + #fat = [] + # [PL] FAT is an array of 32 bits unsigned ints, it's more effective + # to use an array than a list in Python. + # It's initialized as empty first: + self.fat = array.array(UINT32) + self.loadfat_sect(sect) + #self.dumpfat(self.fat) +## for i in range(0, len(sect), 4): +## ix = i32(sect, i) +## #[PL] if ix == -2 or ix == -1: # ix == 0xFFFFFFFE or ix == 0xFFFFFFFF: +## if ix == 0xFFFFFFFE or ix == 0xFFFFFFFF: +## break +## s = self.getsect(ix) +## #fat = fat + [i32(s, i) for i in range(0, len(s), 4)] +## fat = fat + array.array(UINT32, s) + if self.csectDif != 0: + # [PL] There's a DIFAT because file is larger than 6.8MB + # some checks just in case: + if self.csectFat <= 109: + # there must be at least 109 blocks in header and the rest in + # DIFAT, so number of sectors must be >109. + self._raise_defect(DEFECT_INCORRECT, 'incorrect DIFAT, not enough sectors') + if self.sectDifStart >= self.nb_sect: + # initial DIFAT block index must be valid + self._raise_defect(DEFECT_FATAL, 'incorrect DIFAT, first index out of range') + debug( "DIFAT analysis..." ) + # We compute the necessary number of DIFAT sectors : + # (each DIFAT sector = 127 pointers + 1 towards next DIFAT sector) + nb_difat = (self.csectFat-109 + 126)//127 + debug( "nb_difat = %d" % nb_difat ) + if self.csectDif != nb_difat: + raise IOError('incorrect DIFAT') + isect_difat = self.sectDifStart + for i in iterrange(nb_difat): + debug( "DIFAT block %d, sector %X" % (i, isect_difat) ) + #TODO: check if corresponding FAT SID = DIFSECT + sector_difat = self.getsect(isect_difat) + difat = self.sect2array(sector_difat) + self.dumpsect(sector_difat) + self.loadfat_sect(difat[:127]) + # last DIFAT pointer is next DIFAT sector: + isect_difat = difat[127] + debug( "next DIFAT sector: %X" % isect_difat ) + # checks: + if isect_difat not in [ENDOFCHAIN, FREESECT]: + # last DIFAT pointer value must be ENDOFCHAIN or FREESECT + raise IOError('incorrect end of DIFAT') +## if len(self.fat) != self.csectFat: +## # FAT should contain csectFat blocks +## print("FAT length: %d instead of %d" % (len(self.fat), self.csectFat)) +## raise IOError('incorrect DIFAT') + # since FAT is read from fixed-size sectors, it may contain more values + # than the actual number of sectors in the file. + # Keep only the relevant sector indexes: + if len(self.fat) > self.nb_sect: + debug('len(fat)=%d, shrunk to nb_sect=%d' % (len(self.fat), self.nb_sect)) + self.fat = self.fat[:self.nb_sect] + debug('\nFAT:') + self.dumpfat(self.fat) + + + def loadminifat(self): + """ + Load the MiniFAT table. + """ + # MiniFAT is stored in a standard sub-stream, pointed to by a header + # field. + # NOTE: there are two sizes to take into account for this stream: + # 1) Stream size is calculated according to the number of sectors + # declared in the OLE header. This allocated stream may be more than + # needed to store the actual sector indexes. + # (self.csectMiniFat is the number of sectors of size self.SectorSize) + stream_size = self.csectMiniFat * self.SectorSize + # 2) Actually used size is calculated by dividing the MiniStream size + # (given by root entry size) by the size of mini sectors, *4 for + # 32 bits indexes: + nb_minisectors = (self.root.size + self.MiniSectorSize-1) // self.MiniSectorSize + used_size = nb_minisectors * 4 + debug('loadminifat(): minifatsect=%d, nb FAT sectors=%d, used_size=%d, stream_size=%d, nb MiniSectors=%d' % + (self.minifatsect, self.csectMiniFat, used_size, stream_size, nb_minisectors)) + if used_size > stream_size: + # This is not really a problem, but may indicate a wrong implementation: + self._raise_defect(DEFECT_INCORRECT, 'OLE MiniStream is larger than MiniFAT') + # In any case, first read stream_size: + s = self._open(self.minifatsect, stream_size, force_FAT=True).read() + #[PL] Old code replaced by an array: + #self.minifat = [i32(s, i) for i in range(0, len(s), 4)] + self.minifat = self.sect2array(s) + # Then shrink the array to used size, to avoid indexes out of MiniStream: + debug('MiniFAT shrunk from %d to %d sectors' % (len(self.minifat), nb_minisectors)) + self.minifat = self.minifat[:nb_minisectors] + debug('loadminifat(): len=%d' % len(self.minifat)) + debug('\nMiniFAT:') + self.dumpfat(self.minifat) + + def getsect(self, sect): + """ + Read given sector from file on disk. + + :param sect: sector index + :returns: a string containing the sector data. + """ + # [PL] this original code was wrong when sectors are 4KB instead of + # 512 bytes: + #self.fp.seek(512 + self.sectorsize * sect) + #[PL]: added safety checks: + #print("getsect(%X)" % sect) + try: + self.fp.seek(self.sectorsize * (sect+1)) + except: + debug('getsect(): sect=%X, seek=%d, filesize=%d' % + (sect, self.sectorsize*(sect+1), self._filesize)) + self._raise_defect(DEFECT_FATAL, 'OLE sector index out of range') + sector = self.fp.read(self.sectorsize) + if len(sector) != self.sectorsize: + debug('getsect(): sect=%X, read=%d, sectorsize=%d' % + (sect, len(sector), self.sectorsize)) + self._raise_defect(DEFECT_FATAL, 'incomplete OLE sector') + return sector + + + def loaddirectory(self, sect): + """ + Load the directory. + + :param sect: sector index of directory stream. + """ + # The directory is stored in a standard + # substream, independent of its size. + + # open directory stream as a read-only file: + # (stream size is not known in advance) + self.directory_fp = self._open(sect) + + #[PL] to detect malformed documents and avoid DoS attacks, the maximum + # number of directory entries can be calculated: + max_entries = self.directory_fp.size // 128 + debug('loaddirectory: size=%d, max_entries=%d' % + (self.directory_fp.size, max_entries)) + + # Create list of directory entries + #self.direntries = [] + # We start with a list of "None" object + self.direntries = [None] * max_entries +## for sid in iterrange(max_entries): +## entry = fp.read(128) +## if not entry: +## break +## self.direntries.append(_OleDirectoryEntry(entry, sid, self)) + # load root entry: + root_entry = self._load_direntry(0) + # Root entry is the first entry: + self.root = self.direntries[0] + # read and build all storage trees, starting from the root: + self.root.build_storage_tree() + + + def _load_direntry (self, sid): + """ + Load a directory entry from the directory. + This method should only be called once for each storage/stream when + loading the directory. + + :param sid: index of storage/stream in the directory. + :returns: a _OleDirectoryEntry object + :exception IOError: if the entry has always been referenced. + """ + # check if SID is OK: + if sid<0 or sid>=len(self.direntries): + self._raise_defect(DEFECT_FATAL, "OLE directory index out of range") + # check if entry was already referenced: + if self.direntries[sid] is not None: + self._raise_defect(DEFECT_INCORRECT, + "double reference for OLE stream/storage") + # if exception not raised, return the object + return self.direntries[sid] + self.directory_fp.seek(sid * 128) + entry = self.directory_fp.read(128) + self.direntries[sid] = _OleDirectoryEntry(entry, sid, self) + return self.direntries[sid] + + + def dumpdirectory(self): + """ + Dump directory (for debugging only) + """ + self.root.dump() + + + def _open(self, start, size = 0x7FFFFFFF, force_FAT=False): + """ + Open a stream, either in FAT or MiniFAT according to its size. + (openstream helper) + + :param start: index of first sector + :param size: size of stream (or nothing if size is unknown) + :param force_FAT: if False (default), stream will be opened in FAT or MiniFAT + according to size. If True, it will always be opened in FAT. + """ + debug('OleFileIO.open(): sect=%d, size=%d, force_FAT=%s' % + (start, size, str(force_FAT))) + # stream size is compared to the MiniSectorCutoff threshold: + if size < self.minisectorcutoff and not force_FAT: + # ministream object + if not self.ministream: + # load MiniFAT if it wasn't already done: + self.loadminifat() + # The first sector index of the miniFAT stream is stored in the + # root directory entry: + size_ministream = self.root.size + debug('Opening MiniStream: sect=%d, size=%d' % + (self.root.isectStart, size_ministream)) + self.ministream = self._open(self.root.isectStart, + size_ministream, force_FAT=True) + return _OleStream(self.ministream, start, size, 0, + self.minisectorsize, self.minifat, + self.ministream.size) + else: + # standard stream + return _OleStream(self.fp, start, size, 512, + self.sectorsize, self.fat, self._filesize) + + + def _list(self, files, prefix, node, streams=True, storages=False): + """ + (listdir helper) + :param files: list of files to fill in + :param prefix: current location in storage tree (list of names) + :param node: current node (_OleDirectoryEntry object) + :param streams: bool, include streams if True (True by default) - new in v0.26 + :param storages: bool, include storages if True (False by default) - new in v0.26 + (note: the root storage is never included) + """ + prefix = prefix + [node.name] + for entry in node.kids: + if entry.kids: + # this is a storage + if storages: + # add it to the list + files.append(prefix[1:] + [entry.name]) + # check its kids + self._list(files, prefix, entry, streams, storages) + else: + # this is a stream + if streams: + # add it to the list + files.append(prefix[1:] + [entry.name]) + + + def listdir(self, streams=True, storages=False): + """ + Return a list of streams stored in this file + + :param streams: bool, include streams if True (True by default) - new in v0.26 + :param storages: bool, include storages if True (False by default) - new in v0.26 + (note: the root storage is never included) + """ + files = [] + self._list(files, [], self.root, streams, storages) + return files + + + def _find(self, filename): + """ + Returns directory entry of given filename. (openstream helper) + Note: this method is case-insensitive. + + :param filename: path of stream in storage tree (except root entry), either: + + - a string using Unix path syntax, for example: + 'storage_1/storage_1.2/stream' + - a list of storage filenames, path to the desired stream/storage. + Example: ['storage_1', 'storage_1.2', 'stream'] + :returns: sid of requested filename + raise IOError if file not found + """ + + # if filename is a string instead of a list, split it on slashes to + # convert to a list: + if isinstance(filename, basestring): + filename = filename.split('/') + # walk across storage tree, following given path: + node = self.root + for name in filename: + for kid in node.kids: + if kid.name.lower() == name.lower(): + break + else: + raise IOError("file not found") + node = kid + return node.sid + + + def openstream(self, filename): + """ + Open a stream as a read-only file object (BytesIO). + + :param filename: path of stream in storage tree (except root entry), either: + + - a string using Unix path syntax, for example: + 'storage_1/storage_1.2/stream' + - a list of storage filenames, path to the desired stream/storage. + Example: ['storage_1', 'storage_1.2', 'stream'] + + :returns: file object (read-only) + :exception IOError: if filename not found, or if this is not a stream. + """ + sid = self._find(filename) + entry = self.direntries[sid] + if entry.entry_type != STGTY_STREAM: + raise IOError("this file is not a stream") + return self._open(entry.isectStart, entry.size) + + + def get_type(self, filename): + """ + Test if given filename exists as a stream or a storage in the OLE + container, and return its type. + + :param filename: path of stream in storage tree. (see openstream for syntax) + :returns: False if object does not exist, its entry type (>0) otherwise: + + - STGTY_STREAM: a stream + - STGTY_STORAGE: a storage + - STGTY_ROOT: the root entry + """ + try: + sid = self._find(filename) + entry = self.direntries[sid] + return entry.entry_type + except: + return False + + + def getmtime(self, filename): + """ + Return modification time of a stream/storage. + + :param filename: path of stream/storage in storage tree. (see openstream for + syntax) + :returns: None if modification time is null, a python datetime object + otherwise (UTC timezone) + + new in version 0.26 + """ + sid = self._find(filename) + entry = self.direntries[sid] + return entry.getmtime() + + + def getctime(self, filename): + """ + Return creation time of a stream/storage. + + :param filename: path of stream/storage in storage tree. (see openstream for + syntax) + :returns: None if creation time is null, a python datetime object + otherwise (UTC timezone) + + new in version 0.26 + """ + sid = self._find(filename) + entry = self.direntries[sid] + return entry.getctime() + + + def exists(self, filename): + """ + Test if given filename exists as a stream or a storage in the OLE + container. + + :param filename: path of stream in storage tree. (see openstream for syntax) + :returns: True if object exist, else False. + """ + try: + sid = self._find(filename) + return True + except: + return False + + + def get_size(self, filename): + """ + Return size of a stream in the OLE container, in bytes. + + :param filename: path of stream in storage tree (see openstream for syntax) + :returns: size in bytes (long integer) + :exception IOError: if file not found + :exception TypeError: if this is not a stream + """ + sid = self._find(filename) + entry = self.direntries[sid] + if entry.entry_type != STGTY_STREAM: + #TODO: Should it return zero instead of raising an exception ? + raise TypeError('object is not an OLE stream') + return entry.size + + + def get_rootentry_name(self): + """ + Return root entry name. Should usually be 'Root Entry' or 'R' in most + implementations. + """ + return self.root.name + + + def getproperties(self, filename, convert_time=False, no_conversion=None): + """ + Return properties described in substream. + + :param filename: path of stream in storage tree (see openstream for syntax) + :param convert_time: bool, if True timestamps will be converted to Python datetime + :param no_conversion: None or list of int, timestamps not to be converted + (for example total editing time is not a real timestamp) + :returns: a dictionary of values indexed by id (integer) + """ + # make sure no_conversion is a list, just to simplify code below: + if no_conversion == None: + no_conversion = [] + # stream path as a string to report exceptions: + streampath = filename + if not isinstance(streampath, str): + streampath = '/'.join(streampath) + + fp = self.openstream(filename) + + data = {} + + try: + # header + s = fp.read(28) + clsid = _clsid(s[8:24]) + + # format id + s = fp.read(20) + fmtid = _clsid(s[:16]) + fp.seek(i32(s, 16)) + + # get section + s = b"****" + fp.read(i32(fp.read(4))-4) + # number of properties: + num_props = i32(s, 4) + except BaseException as exc: + # catch exception while parsing property header, and only raise + # a DEFECT_INCORRECT then return an empty dict, because this is not + # a fatal error when parsing the whole file + msg = 'Error while parsing properties header in stream %s: %s' % ( + repr(streampath), exc) + self._raise_defect(DEFECT_INCORRECT, msg, type(exc)) + return data + + for i in range(num_props): + try: + id = 0 # just in case of an exception + id = i32(s, 8+i*8) + offset = i32(s, 12+i*8) + type = i32(s, offset) + + debug ('property id=%d: type=%d offset=%X' % (id, type, offset)) + + # test for common types first (should perhaps use + # a dictionary instead?) + + if type == VT_I2: # 16-bit signed integer + value = i16(s, offset+4) + if value >= 32768: + value = value - 65536 + elif type == VT_UI2: # 2-byte unsigned integer + value = i16(s, offset+4) + elif type in (VT_I4, VT_INT, VT_ERROR): + # VT_I4: 32-bit signed integer + # VT_ERROR: HRESULT, similar to 32-bit signed integer, + # see http://msdn.microsoft.com/en-us/library/cc230330.aspx + value = i32(s, offset+4) + elif type in (VT_UI4, VT_UINT): # 4-byte unsigned integer + value = i32(s, offset+4) # FIXME + elif type in (VT_BSTR, VT_LPSTR): + # CodePageString, see http://msdn.microsoft.com/en-us/library/dd942354.aspx + # size is a 32 bits integer, including the null terminator, and + # possibly trailing or embedded null chars + #TODO: if codepage is unicode, the string should be converted as such + count = i32(s, offset+4) + value = s[offset+8:offset+8+count-1] + # remove all null chars: + value = value.replace(b'\x00', b'') + elif type == VT_BLOB: + # binary large object (BLOB) + # see http://msdn.microsoft.com/en-us/library/dd942282.aspx + count = i32(s, offset+4) + value = s[offset+8:offset+8+count] + elif type == VT_LPWSTR: + # UnicodeString + # see http://msdn.microsoft.com/en-us/library/dd942313.aspx + # "the string should NOT contain embedded or additional trailing + # null characters." + count = i32(s, offset+4) + value = _unicode(s[offset+8:offset+8+count*2]) + elif type == VT_FILETIME: + value = long(i32(s, offset+4)) + (long(i32(s, offset+8))<<32) + # FILETIME is a 64-bit int: "number of 100ns periods + # since Jan 1,1601". + if convert_time and id not in no_conversion: + debug('Converting property #%d to python datetime, value=%d=%fs' + %(id, value, float(value)/10000000)) + # convert FILETIME to Python datetime.datetime + # inspired from http://code.activestate.com/recipes/511425-filetime-to-datetime/ + _FILETIME_null_date = datetime.datetime(1601, 1, 1, 0, 0, 0) + debug('timedelta days=%d' % (value//(10*1000000*3600*24))) + value = _FILETIME_null_date + datetime.timedelta(microseconds=value//10) + else: + # legacy code kept for backward compatibility: returns a + # number of seconds since Jan 1,1601 + value = value // 10000000 # seconds + elif type == VT_UI1: # 1-byte unsigned integer + value = i8(s[offset+4]) + elif type == VT_CLSID: + value = _clsid(s[offset+4:offset+20]) + elif type == VT_CF: + # PropertyIdentifier or ClipboardData?? + # see http://msdn.microsoft.com/en-us/library/dd941945.aspx + count = i32(s, offset+4) + value = s[offset+8:offset+8+count] + elif type == VT_BOOL: + # VARIANT_BOOL, 16 bits bool, 0x0000=Fals, 0xFFFF=True + # see http://msdn.microsoft.com/en-us/library/cc237864.aspx + value = bool(i16(s, offset+4)) + else: + value = None # everything else yields "None" + debug ('property id=%d: type=%d not implemented in parser yet' % (id, type)) + + # missing: VT_EMPTY, VT_NULL, VT_R4, VT_R8, VT_CY, VT_DATE, + # VT_DECIMAL, VT_I1, VT_I8, VT_UI8, + # see http://msdn.microsoft.com/en-us/library/dd942033.aspx + + # FIXME: add support for VT_VECTOR + # VT_VECTOR is a 32 uint giving the number of items, followed by + # the items in sequence. The VT_VECTOR value is combined with the + # type of items, e.g. VT_VECTOR|VT_BSTR + # see http://msdn.microsoft.com/en-us/library/dd942011.aspx + + #print("%08x" % id, repr(value), end=" ") + #print("(%s)" % VT[i32(s, offset) & 0xFFF]) + + data[id] = value + except BaseException as exc: + # catch exception while parsing each property, and only raise + # a DEFECT_INCORRECT, because parsing can go on + msg = 'Error while parsing property id %d in stream %s: %s' % ( + id, repr(streampath), exc) + self._raise_defect(DEFECT_INCORRECT, msg, type(exc)) + + return data + + def get_metadata(self): + """ + Parse standard properties streams, return an OleMetadata object + containing all the available metadata. + (also stored in the metadata attribute of the OleFileIO object) + + new in version 0.25 + """ + self.metadata = OleMetadata() + self.metadata.parse_properties(self) + return self.metadata + +# +# -------------------------------------------------------------------- +# This script can be used to dump the directory of any OLE2 structured +# storage file. + +if __name__ == "__main__": + + import sys + + # [PL] display quick usage info if launched from command-line + if len(sys.argv) <= 1: + print(__doc__) + print(""" +Launched from command line, this script parses OLE files and prints info. + +Usage: OleFileIO_PL.py [-d] [-c] [file2 ...] + +Options: +-d : debug mode (display a lot of debug information, for developers only) +-c : check all streams (for debugging purposes) +""") + sys.exit() + + check_streams = False + for filename in sys.argv[1:]: +## try: + # OPTIONS: + if filename == '-d': + # option to switch debug mode on: + set_debug_mode(True) + continue + if filename == '-c': + # option to switch check streams mode on: + check_streams = True + continue + + ole = OleFileIO(filename)#, raise_defects=DEFECT_INCORRECT) + print("-" * 68) + print(filename) + print("-" * 68) + ole.dumpdirectory() + for streamname in ole.listdir(): + if streamname[-1][0] == "\005": + print(streamname, ": properties") + props = ole.getproperties(streamname, convert_time=True) + props = sorted(props.items()) + for k, v in props: + #[PL]: avoid to display too large or binary values: + if isinstance(v, (basestring, bytes)): + if len(v) > 50: + v = v[:50] + if isinstance(v, bytes): + # quick and dirty binary check: + for c in (1,2,3,4,5,6,7,11,12,14,15,16,17,18,19,20, + 21,22,23,24,25,26,27,28,29,30,31): + if c in bytearray(v): + v = '(binary data)' + break + print(" ", k, v) + + if check_streams: + # Read all streams to check if there are errors: + print('\nChecking streams...') + for streamname in ole.listdir(): + # print name using repr() to convert binary chars to \xNN: + print('-', repr('/'.join(streamname)),'-', end=' ') + st_type = ole.get_type(streamname) + if st_type == STGTY_STREAM: + print('size %d' % ole.get_size(streamname)) + # just try to read stream in memory: + ole.openstream(streamname) + else: + print('NOT a stream : type=%d' % st_type) + print() + +## for streamname in ole.listdir(): +## # print name using repr() to convert binary chars to \xNN: +## print('-', repr('/'.join(streamname)),'-', end=' ') +## print(ole.getmtime(streamname)) +## print() + + print('Modification/Creation times of all directory entries:') + for entry in ole.direntries: + if entry is not None: + print('- %s: mtime=%s ctime=%s' % (entry.name, + entry.getmtime(), entry.getctime())) + print() + + # parse and display metadata: + meta = ole.get_metadata() + meta.dump() + print() + #[PL] Test a few new methods: + root = ole.get_rootentry_name() + print('Root entry name: "%s"' % root) + if ole.exists('worddocument'): + print("This is a Word document.") + print("type of stream 'WordDocument':", ole.get_type('worddocument')) + print("size :", ole.get_size('worddocument')) + if ole.exists('macros/vba'): + print("This document may contain VBA macros.") + + # print parsing issues: + print('\nNon-fatal issues raised during parsing:') + if ole.parsing_issues: + for exctype, msg in ole.parsing_issues: + print('- %s: %s' % (exctype.__name__, msg)) + else: + print('None') +## except IOError as v: +## print("***", "cannot read", file, "-", v) diff --git a/pyPackages/pillowx86/PIL/PSDraw.py b/pyPackages/pillowx86/PIL/PSDraw.py new file mode 100644 index 0000000..6187e40 --- /dev/null +++ b/pyPackages/pillowx86/PIL/PSDraw.py @@ -0,0 +1,237 @@ +# +# The Python Imaging Library +# $Id$ +# +# simple postscript graphics interface +# +# History: +# 1996-04-20 fl Created +# 1999-01-10 fl Added gsave/grestore to image method +# 2005-05-04 fl Fixed floating point issue in image (from Eric Etheridge) +# +# Copyright (c) 1997-2005 by Secret Labs AB. All rights reserved. +# Copyright (c) 1996 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +from __future__ import print_function + +from PIL import EpsImagePlugin + + +## +# Simple Postscript graphics interface. + +class PSDraw: + """ + Sets up printing to the given file. If **file** is omitted, + :py:attr:`sys.stdout` is assumed. + """ + + def __init__(self, fp=None): + if not fp: + import sys + fp = sys.stdout + self.fp = fp + + def _fp_write(self, to_write): + if bytes is str: + self.fp.write(to_write) + else: + self.fp.write(bytes(to_write, 'UTF-8')) + + def begin_document(self, id=None): + """Set up printing of a document. (Write Postscript DSC header.)""" + # FIXME: incomplete + self._fp_write("%!PS-Adobe-3.0\n" + "save\n" + "/showpage { } def\n" + "%%EndComments\n" + "%%BeginDocument\n") + # self.fp_write(ERROR_PS) # debugging! + self._fp_write(EDROFF_PS) + self._fp_write(VDI_PS) + self._fp_write("%%EndProlog\n") + self.isofont = {} + + def end_document(self): + """Ends printing. (Write Postscript DSC footer.)""" + self._fp_write("%%EndDocument\n" + "restore showpage\n" + "%%End\n") + if hasattr(self.fp, "flush"): + self.fp.flush() + + def setfont(self, font, size): + """ + Selects which font to use. + + :param font: A Postscript font name + :param size: Size in points. + """ + if font not in self.isofont: + # reencode font + self._fp_write("/PSDraw-%s ISOLatin1Encoding /%s E\n" % + (font, font)) + self.isofont[font] = 1 + # rough + self._fp_write("/F0 %d /PSDraw-%s F\n" % (size, font)) + + def line(self, xy0, xy1): + """ + Draws a line between the two points. Coordinates are given in + Postscript point coordinates (72 points per inch, (0, 0) is the lower + left corner of the page). + """ + xy = xy0 + xy1 + self._fp_write("%d %d %d %d Vl\n" % xy) + + def rectangle(self, box): + """ + Draws a rectangle. + + :param box: A 4-tuple of integers whose order and function is currently + undocumented. + + Hint: the tuple is passed into this format string: + + .. code-block:: python + + %d %d M %d %d 0 Vr\n + """ + self._fp_write("%d %d M %d %d 0 Vr\n" % box) + + def text(self, xy, text): + """ + Draws text at the given position. You must use + :py:meth:`~PIL.PSDraw.PSDraw.setfont` before calling this method. + """ + text = "\\(".join(text.split("(")) + text = "\\)".join(text.split(")")) + xy = xy + (text,) + self._fp_write("%d %d M (%s) S\n" % xy) + + def image(self, box, im, dpi=None): + """Draw a PIL image, centered in the given box.""" + # default resolution depends on mode + if not dpi: + if im.mode == "1": + dpi = 200 # fax + else: + dpi = 100 # greyscale + # image size (on paper) + x = float(im.size[0] * 72) / dpi + y = float(im.size[1] * 72) / dpi + # max allowed size + xmax = float(box[2] - box[0]) + ymax = float(box[3] - box[1]) + if x > xmax: + y = y * xmax / x + x = xmax + if y > ymax: + x = x * ymax / y + y = ymax + dx = (xmax - x) / 2 + box[0] + dy = (ymax - y) / 2 + box[1] + self._fp_write("gsave\n%f %f translate\n" % (dx, dy)) + if (x, y) != im.size: + # EpsImagePlugin._save prints the image at (0,0,xsize,ysize) + sx = x / im.size[0] + sy = y / im.size[1] + self._fp_write("%f %f scale\n" % (sx, sy)) + EpsImagePlugin._save(im, self.fp, None, 0) + self._fp_write("\ngrestore\n") + +# -------------------------------------------------------------------- +# Postscript driver + +# +# EDROFF.PS -- Postscript driver for Edroff 2 +# +# History: +# 94-01-25 fl: created (edroff 2.04) +# +# Copyright (c) Fredrik Lundh 1994. +# + +EDROFF_PS = """\ +/S { show } bind def +/P { moveto show } bind def +/M { moveto } bind def +/X { 0 rmoveto } bind def +/Y { 0 exch rmoveto } bind def +/E { findfont + dup maxlength dict begin + { + 1 index /FID ne { def } { pop pop } ifelse + } forall + /Encoding exch def + dup /FontName exch def + currentdict end definefont pop +} bind def +/F { findfont exch scalefont dup setfont + [ exch /setfont cvx ] cvx bind def +} bind def +""" + +# +# VDI.PS -- Postscript driver for VDI meta commands +# +# History: +# 94-01-25 fl: created (edroff 2.04) +# +# Copyright (c) Fredrik Lundh 1994. +# + +VDI_PS = """\ +/Vm { moveto } bind def +/Va { newpath arcn stroke } bind def +/Vl { moveto lineto stroke } bind def +/Vc { newpath 0 360 arc closepath } bind def +/Vr { exch dup 0 rlineto + exch dup neg 0 exch rlineto + exch neg 0 rlineto + 0 exch rlineto + 100 div setgray fill 0 setgray } bind def +/Tm matrix def +/Ve { Tm currentmatrix pop + translate scale newpath 0 0 .5 0 360 arc closepath + Tm setmatrix +} bind def +/Vf { currentgray exch setgray fill setgray } bind def +""" + +# +# ERROR.PS -- Error handler +# +# History: +# 89-11-21 fl: created (pslist 1.10) +# + +ERROR_PS = """\ +/landscape false def +/errorBUF 200 string def +/errorNL { currentpoint 10 sub exch pop 72 exch moveto } def +errordict begin /handleerror { + initmatrix /Courier findfont 10 scalefont setfont + newpath 72 720 moveto $error begin /newerror false def + (PostScript Error) show errorNL errorNL + (Error: ) show + /errorname load errorBUF cvs show errorNL errorNL + (Command: ) show + /command load dup type /stringtype ne { errorBUF cvs } if show + errorNL errorNL + (VMstatus: ) show + vmstatus errorBUF cvs show ( bytes available, ) show + errorBUF cvs show ( bytes used at level ) show + errorBUF cvs show errorNL errorNL + (Operand stargck: ) show errorNL /ostargck load { + dup type /stringtype ne { errorBUF cvs } if 72 0 rmoveto show errorNL + } forall errorNL + (Execution stargck: ) show errorNL /estargck load { + dup type /stringtype ne { errorBUF cvs } if 72 0 rmoveto show errorNL + } forall + end showpage +} def end +""" diff --git a/pyPackages/pillowx86/PIL/PaletteFile.py b/pyPackages/pillowx86/PIL/PaletteFile.py new file mode 100644 index 0000000..37ba4cb --- /dev/null +++ b/pyPackages/pillowx86/PIL/PaletteFile.py @@ -0,0 +1,55 @@ +# +# Python Imaging Library +# $Id$ +# +# stuff to read simple, teragon-style palette files +# +# History: +# 97-08-23 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + +from PIL._binary import o8 + + +## +# File handler for Teragon-style palette files. + +class PaletteFile: + + rawmode = "RGB" + + def __init__(self, fp): + + self.palette = [(i, i, i) for i in range(256)] + + while True: + + s = fp.readline() + + if not s: + break + if s[0:1] == b"#": + continue + if len(s) > 100: + raise SyntaxError("bad palette file") + + v = [int(x) for x in s.split()] + try: + [i, r, g, b] = v + except ValueError: + [i, r] = v + g = b = r + + if 0 <= i <= 255: + self.palette[i] = o8(r) + o8(g) + o8(b) + + self.palette = b"".join(self.palette) + + def getpalette(self): + + return self.palette, self.rawmode diff --git a/pyPackages/pillowx86/PIL/PalmImagePlugin.py b/pyPackages/pillowx86/PIL/PalmImagePlugin.py new file mode 100644 index 0000000..bba1de8 --- /dev/null +++ b/pyPackages/pillowx86/PIL/PalmImagePlugin.py @@ -0,0 +1,240 @@ +# +# The Python Imaging Library. +# $Id$ +# + +## +# Image plugin for Palm pixmap images (output only). +## + +__version__ = "1.0" + +from PIL import Image, ImageFile, _binary + +_Palm8BitColormapValues = ( + (255, 255, 255), (255, 204, 255), (255, 153, 255), (255, 102, 255), + (255, 51, 255), (255, 0, 255), (255, 255, 204), (255, 204, 204), + (255, 153, 204), (255, 102, 204), (255, 51, 204), (255, 0, 204), + (255, 255, 153), (255, 204, 153), (255, 153, 153), (255, 102, 153), + (255, 51, 153), (255, 0, 153), (204, 255, 255), (204, 204, 255), + (204, 153, 255), (204, 102, 255), (204, 51, 255), (204, 0, 255), + (204, 255, 204), (204, 204, 204), (204, 153, 204), (204, 102, 204), + (204, 51, 204), (204, 0, 204), (204, 255, 153), (204, 204, 153), + (204, 153, 153), (204, 102, 153), (204, 51, 153), (204, 0, 153), + (153, 255, 255), (153, 204, 255), (153, 153, 255), (153, 102, 255), + (153, 51, 255), (153, 0, 255), (153, 255, 204), (153, 204, 204), + (153, 153, 204), (153, 102, 204), (153, 51, 204), (153, 0, 204), + (153, 255, 153), (153, 204, 153), (153, 153, 153), (153, 102, 153), + (153, 51, 153), (153, 0, 153), (102, 255, 255), (102, 204, 255), + (102, 153, 255), (102, 102, 255), (102, 51, 255), (102, 0, 255), + (102, 255, 204), (102, 204, 204), (102, 153, 204), (102, 102, 204), + (102, 51, 204), (102, 0, 204), (102, 255, 153), (102, 204, 153), + (102, 153, 153), (102, 102, 153), (102, 51, 153), (102, 0, 153), + ( 51, 255, 255), ( 51, 204, 255), ( 51, 153, 255), ( 51, 102, 255), + ( 51, 51, 255), ( 51, 0, 255), ( 51, 255, 204), ( 51, 204, 204), + ( 51, 153, 204), ( 51, 102, 204), ( 51, 51, 204), ( 51, 0, 204), + ( 51, 255, 153), ( 51, 204, 153), ( 51, 153, 153), ( 51, 102, 153), + ( 51, 51, 153), ( 51, 0, 153), ( 0, 255, 255), ( 0, 204, 255), + ( 0, 153, 255), ( 0, 102, 255), ( 0, 51, 255), ( 0, 0, 255), + ( 0, 255, 204), ( 0, 204, 204), ( 0, 153, 204), ( 0, 102, 204), + ( 0, 51, 204), ( 0, 0, 204), ( 0, 255, 153), ( 0, 204, 153), + ( 0, 153, 153), ( 0, 102, 153), ( 0, 51, 153), ( 0, 0, 153), + (255, 255, 102), (255, 204, 102), (255, 153, 102), (255, 102, 102), + (255, 51, 102), (255, 0, 102), (255, 255, 51), (255, 204, 51), + (255, 153, 51), (255, 102, 51), (255, 51, 51), (255, 0, 51), + (255, 255, 0), (255, 204, 0), (255, 153, 0), (255, 102, 0), + (255, 51, 0), (255, 0, 0), (204, 255, 102), (204, 204, 102), + (204, 153, 102), (204, 102, 102), (204, 51, 102), (204, 0, 102), + (204, 255, 51), (204, 204, 51), (204, 153, 51), (204, 102, 51), + (204, 51, 51), (204, 0, 51), (204, 255, 0), (204, 204, 0), + (204, 153, 0), (204, 102, 0), (204, 51, 0), (204, 0, 0), + (153, 255, 102), (153, 204, 102), (153, 153, 102), (153, 102, 102), + (153, 51, 102), (153, 0, 102), (153, 255, 51), (153, 204, 51), + (153, 153, 51), (153, 102, 51), (153, 51, 51), (153, 0, 51), + (153, 255, 0), (153, 204, 0), (153, 153, 0), (153, 102, 0), + (153, 51, 0), (153, 0, 0), (102, 255, 102), (102, 204, 102), + (102, 153, 102), (102, 102, 102), (102, 51, 102), (102, 0, 102), + (102, 255, 51), (102, 204, 51), (102, 153, 51), (102, 102, 51), + (102, 51, 51), (102, 0, 51), (102, 255, 0), (102, 204, 0), + (102, 153, 0), (102, 102, 0), (102, 51, 0), (102, 0, 0), + ( 51, 255, 102), ( 51, 204, 102), ( 51, 153, 102), ( 51, 102, 102), + ( 51, 51, 102), ( 51, 0, 102), ( 51, 255, 51), ( 51, 204, 51), + ( 51, 153, 51), ( 51, 102, 51), ( 51, 51, 51), ( 51, 0, 51), + ( 51, 255, 0), ( 51, 204, 0), ( 51, 153, 0), ( 51, 102, 0), + ( 51, 51, 0), ( 51, 0, 0), ( 0, 255, 102), ( 0, 204, 102), + ( 0, 153, 102), ( 0, 102, 102), ( 0, 51, 102), ( 0, 0, 102), + ( 0, 255, 51), ( 0, 204, 51), ( 0, 153, 51), ( 0, 102, 51), + ( 0, 51, 51), ( 0, 0, 51), ( 0, 255, 0), ( 0, 204, 0), + ( 0, 153, 0), ( 0, 102, 0), ( 0, 51, 0), ( 17, 17, 17), + ( 34, 34, 34), ( 68, 68, 68), ( 85, 85, 85), (119, 119, 119), + (136, 136, 136), (170, 170, 170), (187, 187, 187), (221, 221, 221), + (238, 238, 238), (192, 192, 192), (128, 0, 0), (128, 0, 128), + ( 0, 128, 0), ( 0, 128, 128), ( 0, 0, 0), ( 0, 0, 0), + ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), + ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), + ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), + ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), + ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), + ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0)) + + +# so build a prototype image to be used for palette resampling +def build_prototype_image(): + image = Image.new("L", (1, len(_Palm8BitColormapValues),)) + image.putdata(list(range(len(_Palm8BitColormapValues)))) + palettedata = () + for i in range(len(_Palm8BitColormapValues)): + palettedata = palettedata + _Palm8BitColormapValues[i] + for i in range(256 - len(_Palm8BitColormapValues)): + palettedata = palettedata + (0, 0, 0) + image.putpalette(palettedata) + return image + +Palm8BitColormapImage = build_prototype_image() + +# OK, we now have in Palm8BitColormapImage, +# a "P"-mode image with the right palette +# +# -------------------------------------------------------------------- + +_FLAGS = { + "custom-colormap": 0x4000, + "is-compressed": 0x8000, + "has-transparent": 0x2000, + } + +_COMPRESSION_TYPES = { + "none": 0xFF, + "rle": 0x01, + "scanline": 0x00, + } + +o8 = _binary.o8 +o16b = _binary.o16be + + +# +# -------------------------------------------------------------------- + +## +# (Internal) Image save plugin for the Palm format. + +def _save(im, fp, filename, check=0): + + if im.mode == "P": + + # we assume this is a color Palm image with the standard colormap, + # unless the "info" dict has a "custom-colormap" field + + rawmode = "P" + bpp = 8 + version = 1 + + elif (im.mode == "L" and + "bpp" in im.encoderinfo and + im.encoderinfo["bpp"] in (1, 2, 4)): + + # this is 8-bit grayscale, so we shift it to get the high-order bits, + # and invert it because + # Palm does greyscale from white (0) to black (1) + bpp = im.encoderinfo["bpp"] + im = im.point( + lambda x, shift=8-bpp, maxval=(1 << bpp)-1: maxval - (x >> shift)) + # we ignore the palette here + im.mode = "P" + rawmode = "P;" + str(bpp) + version = 1 + + elif im.mode == "L" and "bpp" in im.info and im.info["bpp"] in (1, 2, 4): + + # here we assume that even though the inherent mode is 8-bit grayscale, + # only the lower bpp bits are significant. + # We invert them to match the Palm. + bpp = im.info["bpp"] + im = im.point(lambda x, maxval=(1 << bpp)-1: maxval - (x & maxval)) + # we ignore the palette here + im.mode = "P" + rawmode = "P;" + str(bpp) + version = 1 + + elif im.mode == "1": + + # monochrome -- write it inverted, as is the Palm standard + rawmode = "1;I" + bpp = 1 + version = 0 + + else: + + raise IOError("cannot write mode %s as Palm" % im.mode) + + if check: + return check + + # + # make sure image data is available + im.load() + + # write header + + cols = im.size[0] + rows = im.size[1] + + rowbytes = int((cols + (16//bpp - 1)) / (16 // bpp)) * 2 + transparent_index = 0 + compression_type = _COMPRESSION_TYPES["none"] + + flags = 0 + if im.mode == "P" and "custom-colormap" in im.info: + flags = flags & _FLAGS["custom-colormap"] + colormapsize = 4 * 256 + 2 + colormapmode = im.palette.mode + colormap = im.getdata().getpalette() + else: + colormapsize = 0 + + if "offset" in im.info: + offset = (rowbytes * rows + 16 + 3 + colormapsize) // 4 + else: + offset = 0 + + fp.write(o16b(cols) + o16b(rows) + o16b(rowbytes) + o16b(flags)) + fp.write(o8(bpp)) + fp.write(o8(version)) + fp.write(o16b(offset)) + fp.write(o8(transparent_index)) + fp.write(o8(compression_type)) + fp.write(o16b(0)) # reserved by Palm + + # now write colormap if necessary + + if colormapsize > 0: + fp.write(o16b(256)) + for i in range(256): + fp.write(o8(i)) + if colormapmode == 'RGB': + fp.write( + o8(colormap[3 * i]) + + o8(colormap[3 * i + 1]) + + o8(colormap[3 * i + 2])) + elif colormapmode == 'RGBA': + fp.write( + o8(colormap[4 * i]) + + o8(colormap[4 * i + 1]) + + o8(colormap[4 * i + 2])) + + # now convert data to raw form + ImageFile._save( + im, fp, [("raw", (0, 0)+im.size, 0, (rawmode, rowbytes, 1))]) + + fp.flush() + + +# +# -------------------------------------------------------------------- + +Image.register_save("Palm", _save) + +Image.register_extension("Palm", ".palm") + +Image.register_mime("Palm", "image/palm") diff --git a/pyPackages/pillowx86/PIL/PcdImagePlugin.py b/pyPackages/pillowx86/PIL/PcdImagePlugin.py new file mode 100644 index 0000000..5ce7aa4 --- /dev/null +++ b/pyPackages/pillowx86/PIL/PcdImagePlugin.py @@ -0,0 +1,79 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PCD file handling +# +# History: +# 96-05-10 fl Created +# 96-05-27 fl Added draft mode (128x192, 256x384) +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.1" + + +from PIL import Image, ImageFile, _binary + +i8 = _binary.i8 + + +## +# Image plugin for PhotoCD images. This plugin only reads the 768x512 +# image from the file; higher resolutions are encoded in a proprietary +# encoding. + +class PcdImageFile(ImageFile.ImageFile): + + format = "PCD" + format_description = "Kodak PhotoCD" + + def _open(self): + + # rough + self.fp.seek(2048) + s = self.fp.read(2048) + + if s[:4] != b"PCD_": + raise SyntaxError("not a PCD file") + + orientation = i8(s[1538]) & 3 + if orientation == 1: + self.tile_post_rotate = 90 # hack + elif orientation == 3: + self.tile_post_rotate = -90 + + self.mode = "RGB" + self.size = 768, 512 # FIXME: not correct for rotated images! + self.tile = [("pcd", (0, 0)+self.size, 96*2048, None)] + + def draft(self, mode, size): + + if len(self.tile) != 1: + return + + d, e, o, a = self.tile[0] + + if size: + scale = max(self.size[0] / size[0], self.size[1] / size[1]) + for s, o in [(4, 0*2048), (2, 0*2048), (1, 96*2048)]: + if scale >= s: + break + # e = e[0], e[1], (e[2]-e[0]+s-1)/s+e[0], (e[3]-e[1]+s-1)/s+e[1] + # self.size = ((self.size[0]+s-1)/s, (self.size[1]+s-1)/s) + + self.tile = [(d, e, o, a)] + + return self + +# +# registry + +Image.register_open("PCD", PcdImageFile) + +Image.register_extension("PCD", ".pcd") diff --git a/pyPackages/pillowx86/PIL/PcfFontFile.py b/pyPackages/pillowx86/PIL/PcfFontFile.py new file mode 100644 index 0000000..c19a1c5 --- /dev/null +++ b/pyPackages/pillowx86/PIL/PcfFontFile.py @@ -0,0 +1,252 @@ +# +# THIS IS WORK IN PROGRESS +# +# The Python Imaging Library +# $Id$ +# +# portable compiled font file parser +# +# history: +# 1997-08-19 fl created +# 2003-09-13 fl fixed loading of unicode fonts +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1997-2003 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +from PIL import Image +from PIL import FontFile +from PIL import _binary + +# -------------------------------------------------------------------- +# declarations + +PCF_MAGIC = 0x70636601 # "\x01fcp" + +PCF_PROPERTIES = (1 << 0) +PCF_ACCELERATORS = (1 << 1) +PCF_METRICS = (1 << 2) +PCF_BITMAPS = (1 << 3) +PCF_INK_METRICS = (1 << 4) +PCF_BDF_ENCODINGS = (1 << 5) +PCF_SWIDTHS = (1 << 6) +PCF_GLYPH_NAMES = (1 << 7) +PCF_BDF_ACCELERATORS = (1 << 8) + +BYTES_PER_ROW = [ + lambda bits: ((bits+7) >> 3), + lambda bits: ((bits+15) >> 3) & ~1, + lambda bits: ((bits+31) >> 3) & ~3, + lambda bits: ((bits+63) >> 3) & ~7, +] + +i8 = _binary.i8 +l16 = _binary.i16le +l32 = _binary.i32le +b16 = _binary.i16be +b32 = _binary.i32be + + +def sz(s, o): + return s[o:s.index(b"\0", o)] + + +## +# Font file plugin for the X11 PCF format. + +class PcfFontFile(FontFile.FontFile): + + name = "name" + + def __init__(self, fp): + + magic = l32(fp.read(4)) + if magic != PCF_MAGIC: + raise SyntaxError("not a PCF file") + + FontFile.FontFile.__init__(self) + + count = l32(fp.read(4)) + self.toc = {} + for i in range(count): + type = l32(fp.read(4)) + self.toc[type] = l32(fp.read(4)), l32(fp.read(4)), l32(fp.read(4)) + + self.fp = fp + + self.info = self._load_properties() + + metrics = self._load_metrics() + bitmaps = self._load_bitmaps(metrics) + encoding = self._load_encoding() + + # + # create glyph structure + + for ch in range(256): + ix = encoding[ch] + if ix is not None: + x, y, l, r, w, a, d, f = metrics[ix] + glyph = (w, 0), (l, d-y, x+l, d), (0, 0, x, y), bitmaps[ix] + self.glyph[ch] = glyph + + def _getformat(self, tag): + + format, size, offset = self.toc[tag] + + fp = self.fp + fp.seek(offset) + + format = l32(fp.read(4)) + + if format & 4: + i16, i32 = b16, b32 + else: + i16, i32 = l16, l32 + + return fp, format, i16, i32 + + def _load_properties(self): + + # + # font properties + + properties = {} + + fp, format, i16, i32 = self._getformat(PCF_PROPERTIES) + + nprops = i32(fp.read(4)) + + # read property description + p = [] + for i in range(nprops): + p.append((i32(fp.read(4)), i8(fp.read(1)), i32(fp.read(4)))) + if nprops & 3: + fp.seek(4 - (nprops & 3), 1) # pad + + data = fp.read(i32(fp.read(4))) + + for k, s, v in p: + k = sz(data, k) + if s: + v = sz(data, v) + properties[k] = v + + return properties + + def _load_metrics(self): + + # + # font metrics + + metrics = [] + + fp, format, i16, i32 = self._getformat(PCF_METRICS) + + append = metrics.append + + if (format & 0xff00) == 0x100: + + # "compressed" metrics + for i in range(i16(fp.read(2))): + left = i8(fp.read(1)) - 128 + right = i8(fp.read(1)) - 128 + width = i8(fp.read(1)) - 128 + ascent = i8(fp.read(1)) - 128 + descent = i8(fp.read(1)) - 128 + xsize = right - left + ysize = ascent + descent + append( + (xsize, ysize, left, right, width, + ascent, descent, 0) + ) + + else: + + # "jumbo" metrics + for i in range(i32(fp.read(4))): + left = i16(fp.read(2)) + right = i16(fp.read(2)) + width = i16(fp.read(2)) + ascent = i16(fp.read(2)) + descent = i16(fp.read(2)) + attributes = i16(fp.read(2)) + xsize = right - left + ysize = ascent + descent + append( + (xsize, ysize, left, right, width, + ascent, descent, attributes) + ) + + return metrics + + def _load_bitmaps(self, metrics): + + # + # bitmap data + + bitmaps = [] + + fp, format, i16, i32 = self._getformat(PCF_BITMAPS) + + nbitmaps = i32(fp.read(4)) + + if nbitmaps != len(metrics): + raise IOError("Wrong number of bitmaps") + + offsets = [] + for i in range(nbitmaps): + offsets.append(i32(fp.read(4))) + + bitmapSizes = [] + for i in range(4): + bitmapSizes.append(i32(fp.read(4))) + + byteorder = format & 4 # non-zero => MSB + bitorder = format & 8 # non-zero => MSB + padindex = format & 3 + + bitmapsize = bitmapSizes[padindex] + offsets.append(bitmapsize) + + data = fp.read(bitmapsize) + + pad = BYTES_PER_ROW[padindex] + mode = "1;R" + if bitorder: + mode = "1" + + for i in range(nbitmaps): + x, y, l, r, w, a, d, f = metrics[i] + b, e = offsets[i], offsets[i+1] + bitmaps.append( + Image.frombytes("1", (x, y), data[b:e], "raw", mode, pad(x)) + ) + + return bitmaps + + def _load_encoding(self): + + # map character code to bitmap index + encoding = [None] * 256 + + fp, format, i16, i32 = self._getformat(PCF_BDF_ENCODINGS) + + firstCol, lastCol = i16(fp.read(2)), i16(fp.read(2)) + firstRow, lastRow = i16(fp.read(2)), i16(fp.read(2)) + + default = i16(fp.read(2)) + + nencoding = (lastCol - firstCol + 1) * (lastRow - firstRow + 1) + + for i in range(nencoding): + encodingOffset = i16(fp.read(2)) + if encodingOffset != 0xFFFF: + try: + encoding[i+firstCol] = encodingOffset + except IndexError: + break # only load ISO-8859-1 glyphs + + return encoding diff --git a/pyPackages/pillowx86/PIL/PcxImagePlugin.py b/pyPackages/pillowx86/PIL/PcxImagePlugin.py new file mode 100644 index 0000000..0765f09 --- /dev/null +++ b/pyPackages/pillowx86/PIL/PcxImagePlugin.py @@ -0,0 +1,186 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PCX file handling +# +# This format was originally used by ZSoft's popular PaintBrush +# program for the IBM PC. It is also supported by many MS-DOS and +# Windows applications, including the Windows PaintBrush program in +# Windows 3. +# +# history: +# 1995-09-01 fl Created +# 1996-05-20 fl Fixed RGB support +# 1997-01-03 fl Fixed 2-bit and 4-bit support +# 1999-02-03 fl Fixed 8-bit support (broken in 1.0b1) +# 1999-02-07 fl Added write support +# 2002-06-09 fl Made 2-bit and 4-bit support a bit more robust +# 2002-07-30 fl Seek from to current position, not beginning of file +# 2003-06-03 fl Extract DPI settings (info["dpi"]) +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1995-2003 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.6" + +from PIL import Image, ImageFile, ImagePalette, _binary + +i8 = _binary.i8 +i16 = _binary.i16le +o8 = _binary.o8 + + +def _accept(prefix): + return i8(prefix[0]) == 10 and i8(prefix[1]) in [0, 2, 3, 5] + + +## +# Image plugin for Paintbrush images. + +class PcxImageFile(ImageFile.ImageFile): + + format = "PCX" + format_description = "Paintbrush" + + def _open(self): + + # header + s = self.fp.read(128) + if not _accept(s): + raise SyntaxError("not a PCX file") + + # image + bbox = i16(s, 4), i16(s, 6), i16(s, 8)+1, i16(s, 10)+1 + if bbox[2] <= bbox[0] or bbox[3] <= bbox[1]: + raise SyntaxError("bad PCX image size") + if Image.DEBUG: + print ("BBox: %s %s %s %s" % bbox) + + # format + version = i8(s[1]) + bits = i8(s[3]) + planes = i8(s[65]) + stride = i16(s, 66) + if Image.DEBUG: + print ("PCX version %s, bits %s, planes %s, stride %s" % + (version, bits, planes, stride)) + + self.info["dpi"] = i16(s, 12), i16(s, 14) + + if bits == 1 and planes == 1: + mode = rawmode = "1" + + elif bits == 1 and planes in (2, 4): + mode = "P" + rawmode = "P;%dL" % planes + self.palette = ImagePalette.raw("RGB", s[16:64]) + + elif version == 5 and bits == 8 and planes == 1: + mode = rawmode = "L" + # FIXME: hey, this doesn't work with the incremental loader !!! + self.fp.seek(-769, 2) + s = self.fp.read(769) + if len(s) == 769 and i8(s[0]) == 12: + # check if the palette is linear greyscale + for i in range(256): + if s[i*3+1:i*3+4] != o8(i)*3: + mode = rawmode = "P" + break + if mode == "P": + self.palette = ImagePalette.raw("RGB", s[1:]) + self.fp.seek(128) + + elif version == 5 and bits == 8 and planes == 3: + mode = "RGB" + rawmode = "RGB;L" + + else: + raise IOError("unknown PCX mode") + + self.mode = mode + self.size = bbox[2]-bbox[0], bbox[3]-bbox[1] + + bbox = (0, 0) + self.size + if Image.DEBUG: + print ("size: %sx%s" % self.size) + + self.tile = [("pcx", bbox, self.fp.tell(), (rawmode, planes * stride))] + +# -------------------------------------------------------------------- +# save PCX files + +SAVE = { + # mode: (version, bits, planes, raw mode) + "1": (2, 1, 1, "1"), + "L": (5, 8, 1, "L"), + "P": (5, 8, 1, "P"), + "RGB": (5, 8, 3, "RGB;L"), +} + +o16 = _binary.o16le + + +def _save(im, fp, filename, check=0): + + try: + version, bits, planes, rawmode = SAVE[im.mode] + except KeyError: + raise ValueError("Cannot save %s images as PCX" % im.mode) + + if check: + return check + + # bytes per plane + stride = (im.size[0] * bits + 7) // 8 + # stride should be even + stride += stride % 2 + # Stride needs to be kept in sync with the PcxEncode.c version. + # Ideally it should be passed in in the state, but the bytes value + # gets overwritten. + + if Image.DEBUG: + print ("PcxImagePlugin._save: xwidth: %d, bits: %d, stride: %d" % ( + im.size[0], bits, stride)) + + # under windows, we could determine the current screen size with + # "Image.core.display_mode()[1]", but I think that's overkill... + + screen = im.size + + dpi = 100, 100 + + # PCX header + fp.write( + o8(10) + o8(version) + o8(1) + o8(bits) + o16(0) + + o16(0) + o16(im.size[0]-1) + o16(im.size[1]-1) + o16(dpi[0]) + + o16(dpi[1]) + b"\0"*24 + b"\xFF"*24 + b"\0" + o8(planes) + + o16(stride) + o16(1) + o16(screen[0]) + o16(screen[1]) + + b"\0"*54 + ) + + assert fp.tell() == 128 + + ImageFile._save(im, fp, [("pcx", (0, 0)+im.size, 0, + (rawmode, bits*planes))]) + + if im.mode == "P": + # colour palette + fp.write(o8(12)) + fp.write(im.im.getpalette("RGB", "RGB")) # 768 bytes + elif im.mode == "L": + # greyscale palette + fp.write(o8(12)) + for i in range(256): + fp.write(o8(i)*3) + +# -------------------------------------------------------------------- +# registry + +Image.register_open("PCX", PcxImageFile, _accept) +Image.register_save("PCX", _save) + +Image.register_extension("PCX", ".pcx") diff --git a/pyPackages/pillowx86/PIL/PdfImagePlugin.py b/pyPackages/pillowx86/PIL/PdfImagePlugin.py new file mode 100644 index 0000000..5113f09 --- /dev/null +++ b/pyPackages/pillowx86/PIL/PdfImagePlugin.py @@ -0,0 +1,238 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PDF (Acrobat) file handling +# +# History: +# 1996-07-16 fl Created +# 1997-01-18 fl Fixed header +# 2004-02-21 fl Fixes for 1/L/CMYK images, etc. +# 2004-02-24 fl Fixes for 1 and P images. +# +# Copyright (c) 1997-2004 by Secret Labs AB. All rights reserved. +# Copyright (c) 1996-1997 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +## +# Image plugin for PDF images (output only). +## + +__version__ = "0.4" + +from PIL import Image, ImageFile +from PIL._binary import i8 +import io + + +# +# -------------------------------------------------------------------- + +# object ids: +# 1. catalogue +# 2. pages +# 3. image +# 4. page +# 5. page contents + +def _obj(fp, obj, **dict): + fp.write("%d 0 obj\n" % obj) + if dict: + fp.write("<<\n") + for k, v in dict.items(): + if v is not None: + fp.write("/%s %s\n" % (k, v)) + fp.write(">>\n") + + +def _endobj(fp): + fp.write("endobj\n") + + +## +# (Internal) Image save plugin for the PDF format. + +def _save(im, fp, filename): + resolution = im.encoderinfo.get("resolution", 72.0) + + # + # make sure image data is available + im.load() + + xref = [0]*(5+1) # placeholders + + class TextWriter: + def __init__(self, fp): + self.fp = fp + + def __getattr__(self, name): + return getattr(self.fp, name) + + def write(self, value): + self.fp.write(value.encode('latin-1')) + + fp = TextWriter(fp) + + fp.write("%PDF-1.2\n") + fp.write("% created by PIL PDF driver " + __version__ + "\n") + + # + # Get image characteristics + + width, height = im.size + + # FIXME: Should replace ASCIIHexDecode with RunLengthDecode (packbits) + # or LZWDecode (tiff/lzw compression). Note that PDF 1.2 also supports + # Flatedecode (zip compression). + + bits = 8 + params = None + + if im.mode == "1": + filter = "/ASCIIHexDecode" + colorspace = "/DeviceGray" + procset = "/ImageB" # grayscale + bits = 1 + elif im.mode == "L": + filter = "/DCTDecode" + # params = "<< /Predictor 15 /Columns %d >>" % (width-2) + colorspace = "/DeviceGray" + procset = "/ImageB" # grayscale + elif im.mode == "P": + filter = "/ASCIIHexDecode" + colorspace = "[ /Indexed /DeviceRGB 255 <" + palette = im.im.getpalette("RGB") + for i in range(256): + r = i8(palette[i*3]) + g = i8(palette[i*3+1]) + b = i8(palette[i*3+2]) + colorspace += "%02x%02x%02x " % (r, g, b) + colorspace += "> ]" + procset = "/ImageI" # indexed color + elif im.mode == "RGB": + filter = "/DCTDecode" + colorspace = "/DeviceRGB" + procset = "/ImageC" # color images + elif im.mode == "CMYK": + filter = "/DCTDecode" + colorspace = "/DeviceCMYK" + procset = "/ImageC" # color images + else: + raise ValueError("cannot save mode %s" % im.mode) + + # + # catalogue + + xref[1] = fp.tell() + _obj( + fp, 1, + Type="/Catalog", + Pages="2 0 R") + _endobj(fp) + + # + # pages + + xref[2] = fp.tell() + _obj( + fp, 2, + Type="/Pages", + Count=1, + Kids="[4 0 R]") + _endobj(fp) + + # + # image + + op = io.BytesIO() + + if filter == "/ASCIIHexDecode": + if bits == 1: + # FIXME: the hex encoder doesn't support packed 1-bit + # images; do things the hard way... + data = im.tobytes("raw", "1") + im = Image.new("L", (len(data), 1), None) + im.putdata(data) + ImageFile._save(im, op, [("hex", (0, 0)+im.size, 0, im.mode)]) + elif filter == "/DCTDecode": + Image.SAVE["JPEG"](im, op, filename) + elif filter == "/FlateDecode": + ImageFile._save(im, op, [("zip", (0, 0)+im.size, 0, im.mode)]) + elif filter == "/RunLengthDecode": + ImageFile._save(im, op, [("packbits", (0, 0)+im.size, 0, im.mode)]) + else: + raise ValueError("unsupported PDF filter (%s)" % filter) + + xref[3] = fp.tell() + _obj( + fp, 3, + Type="/XObject", + Subtype="/Image", + Width=width, # * 72.0 / resolution, + Height=height, # * 72.0 / resolution, + Length=len(op.getvalue()), + Filter=filter, + BitsPerComponent=bits, + DecodeParams=params, + ColorSpace=colorspace) + + fp.write("stream\n") + fp.fp.write(op.getvalue()) + fp.write("\nendstream\n") + + _endobj(fp) + + # + # page + + xref[4] = fp.tell() + _obj(fp, 4) + fp.write( + "<<\n/Type /Page\n/Parent 2 0 R\n" + "/Resources <<\n/ProcSet [ /PDF %s ]\n" + "/XObject << /image 3 0 R >>\n>>\n" + "/MediaBox [ 0 0 %d %d ]\n/Contents 5 0 R\n>>\n" % ( + procset, + int(width * 72.0 / resolution), + int(height * 72.0 / resolution))) + _endobj(fp) + + # + # page contents + + op = TextWriter(io.BytesIO()) + + op.write( + "q %d 0 0 %d 0 0 cm /image Do Q\n" % ( + int(width * 72.0 / resolution), + int(height * 72.0 / resolution))) + + xref[5] = fp.tell() + _obj(fp, 5, Length=len(op.fp.getvalue())) + + fp.write("stream\n") + fp.fp.write(op.fp.getvalue()) + fp.write("\nendstream\n") + + _endobj(fp) + + # + # trailer + startxref = fp.tell() + fp.write("xref\n0 %d\n0000000000 65535 f \n" % len(xref)) + for x in xref[1:]: + fp.write("%010d 00000 n \n" % x) + fp.write("trailer\n<<\n/Size %d\n/Root 1 0 R\n>>\n" % len(xref)) + fp.write("startxref\n%d\n%%%%EOF\n" % startxref) + fp.flush() + +# +# -------------------------------------------------------------------- + +Image.register_save("PDF", _save) + +Image.register_extension("PDF", ".pdf") + +Image.register_mime("PDF", "application/pdf") diff --git a/pyPackages/pillowx86/PIL/PixarImagePlugin.py b/pyPackages/pillowx86/PIL/PixarImagePlugin.py new file mode 100644 index 0000000..ebf4c8c --- /dev/null +++ b/pyPackages/pillowx86/PIL/PixarImagePlugin.py @@ -0,0 +1,69 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PIXAR raster support for PIL +# +# history: +# 97-01-29 fl Created +# +# notes: +# This is incomplete; it is based on a few samples created with +# Photoshop 2.5 and 3.0, and a summary description provided by +# Greg Coats . Hopefully, "L" and +# "RGBA" support will be added in future versions. +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.1" + +from PIL import Image, ImageFile, _binary + +# +# helpers + +i16 = _binary.i16le +i32 = _binary.i32le + + +## +# Image plugin for PIXAR raster images. + +class PixarImageFile(ImageFile.ImageFile): + + format = "PIXAR" + format_description = "PIXAR raster image" + + def _open(self): + + # assuming a 4-byte magic label (FIXME: add "_accept" hook) + s = self.fp.read(4) + if s != b"\200\350\000\000": + raise SyntaxError("not a PIXAR file") + + # read rest of header + s = s + self.fp.read(508) + + self.size = i16(s[418:420]), i16(s[416:418]) + + # get channel/depth descriptions + mode = i16(s[424:426]), i16(s[426:428]) + + if mode == (14, 2): + self.mode = "RGB" + # FIXME: to be continued... + + # create tile descriptor (assuming "dumped") + self.tile = [("raw", (0, 0)+self.size, 1024, (self.mode, 0, 1))] + +# +# -------------------------------------------------------------------- + +Image.register_open("PIXAR", PixarImageFile) + +# +# FIXME: what's the standard extension? diff --git a/pyPackages/pillowx86/PIL/PngImagePlugin.py b/pyPackages/pillowx86/PIL/PngImagePlugin.py new file mode 100644 index 0000000..7a9becd --- /dev/null +++ b/pyPackages/pillowx86/PIL/PngImagePlugin.py @@ -0,0 +1,811 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PNG support code +# +# See "PNG (Portable Network Graphics) Specification, version 1.0; +# W3C Recommendation", 1996-10-01, Thomas Boutell (ed.). +# +# history: +# 1996-05-06 fl Created (couldn't resist it) +# 1996-12-14 fl Upgraded, added read and verify support (0.2) +# 1996-12-15 fl Separate PNG stream parser +# 1996-12-29 fl Added write support, added getchunks +# 1996-12-30 fl Eliminated circular references in decoder (0.3) +# 1998-07-12 fl Read/write 16-bit images as mode I (0.4) +# 2001-02-08 fl Added transparency support (from Zircon) (0.5) +# 2001-04-16 fl Don't close data source in "open" method (0.6) +# 2004-02-24 fl Don't even pretend to support interlaced files (0.7) +# 2004-08-31 fl Do basic sanity check on chunk identifiers (0.8) +# 2004-09-20 fl Added PngInfo chunk container +# 2004-12-18 fl Added DPI read support (based on code by Niki Spahiev) +# 2008-08-13 fl Added tRNS support for RGB images +# 2009-03-06 fl Support for preserving ICC profiles (by Florian Hoech) +# 2009-03-08 fl Added zTXT support (from Lowell Alleman) +# 2009-03-29 fl Read interlaced PNG files (from Conrado Porto Lopes Gouvua) +# +# Copyright (c) 1997-2009 by Secret Labs AB +# Copyright (c) 1996 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from __future__ import print_function + +__version__ = "0.9" + +import re + +from PIL import Image, ImageFile, ImagePalette, _binary +import zlib + +i8 = _binary.i8 +i16 = _binary.i16be +i32 = _binary.i32be + +is_cid = re.compile(b"\w\w\w\w").match + + +_MAGIC = b"\211PNG\r\n\032\n" + + +_MODES = { + # supported bits/color combinations, and corresponding modes/rawmodes + (1, 0): ("1", "1"), + (2, 0): ("L", "L;2"), + (4, 0): ("L", "L;4"), + (8, 0): ("L", "L"), + (16, 0): ("I", "I;16B"), + (8, 2): ("RGB", "RGB"), + (16, 2): ("RGB", "RGB;16B"), + (1, 3): ("P", "P;1"), + (2, 3): ("P", "P;2"), + (4, 3): ("P", "P;4"), + (8, 3): ("P", "P"), + (8, 4): ("LA", "LA"), + (16, 4): ("RGBA", "LA;16B"), # LA;16B->LA not yet available + (8, 6): ("RGBA", "RGBA"), + (16, 6): ("RGBA", "RGBA;16B"), +} + + +_simple_palette = re.compile(b'^\xff+\x00\xff*$') + +# Maximum decompressed size for a iTXt or zTXt chunk. +# Eliminates decompression bombs where compressed chunks can expand 1000x +MAX_TEXT_CHUNK = ImageFile.SAFEBLOCK +# Set the maximum total text chunk size. +MAX_TEXT_MEMORY = 64 * MAX_TEXT_CHUNK + +def _safe_zlib_decompress(s): + dobj = zlib.decompressobj() + plaintext = dobj.decompress(s, MAX_TEXT_CHUNK) + if dobj.unconsumed_tail: + raise ValueError("Decompressed Data Too Large") + return plaintext + + +# -------------------------------------------------------------------- +# Support classes. Suitable for PNG and related formats like MNG etc. + +class ChunkStream: + + def __init__(self, fp): + + self.fp = fp + self.queue = [] + + if not hasattr(Image.core, "crc32"): + self.crc = self.crc_skip + + def read(self): + "Fetch a new chunk. Returns header information." + + if self.queue: + cid, pos, length = self.queue[-1] + del self.queue[-1] + self.fp.seek(pos) + else: + s = self.fp.read(8) + cid = s[4:] + pos = self.fp.tell() + length = i32(s) + + if not is_cid(cid): + raise SyntaxError("broken PNG file (chunk %s)" % repr(cid)) + + return cid, pos, length + + def close(self): + self.queue = self.crc = self.fp = None + + def push(self, cid, pos, length): + + self.queue.append((cid, pos, length)) + + def call(self, cid, pos, length): + "Call the appropriate chunk handler" + + if Image.DEBUG: + print("STREAM", cid, pos, length) + return getattr(self, "chunk_" + cid.decode('ascii'))(pos, length) + + def crc(self, cid, data): + "Read and verify checksum" + + crc1 = Image.core.crc32(data, Image.core.crc32(cid)) + crc2 = i16(self.fp.read(2)), i16(self.fp.read(2)) + if crc1 != crc2: + raise SyntaxError("broken PNG file" + "(bad header checksum in %s)" % cid) + + def crc_skip(self, cid, data): + "Read checksum. Used if the C module is not present" + + self.fp.read(4) + + def verify(self, endchunk=b"IEND"): + + # Simple approach; just calculate checksum for all remaining + # blocks. Must be called directly after open. + + cids = [] + + while True: + cid, pos, length = self.read() + if cid == endchunk: + break + self.crc(cid, ImageFile._safe_read(self.fp, length)) + cids.append(cid) + + return cids + + +class iTXt(str): + """ + Subclass of string to allow iTXt chunks to look like strings while + keeping their extra information + + """ + @staticmethod + def __new__(cls, text, lang, tkey): + """ + :param value: value for this key + :param lang: language code + :param tkey: UTF-8 version of the key name + """ + + self = str.__new__(cls, text) + self.lang = lang + self.tkey = tkey + return self + + +class PngInfo: + """ + PNG chunk container (for use with save(pnginfo=)) + + """ + + def __init__(self): + self.chunks = [] + + def add(self, cid, data): + """Appends an arbitrary chunk. Use with caution. + + :param cid: a byte string, 4 bytes long. + :param data: a byte string of the encoded data + + """ + + self.chunks.append((cid, data)) + + def add_itxt(self, key, value, lang="", tkey="", zip=False): + """Appends an iTXt chunk. + + :param key: latin-1 encodable text key name + :param value: value for this key + :param lang: language code + :param tkey: UTF-8 version of the key name + :param zip: compression flag + + """ + + if not isinstance(key, bytes): + key = key.encode("latin-1", "strict") + if not isinstance(value, bytes): + value = value.encode("utf-8", "strict") + if not isinstance(lang, bytes): + lang = lang.encode("utf-8", "strict") + if not isinstance(tkey, bytes): + tkey = tkey.encode("utf-8", "strict") + + if zip: + self.add(b"iTXt", key + b"\0\x01\0" + lang + b"\0" + tkey + b"\0" + + zlib.compress(value)) + else: + self.add(b"iTXt", key + b"\0\0\0" + lang + b"\0" + tkey + b"\0" + + value) + + def add_text(self, key, value, zip=0): + """Appends a text chunk. + + :param key: latin-1 encodable text key name + :param value: value for this key, text or an + :py:class:`PIL.PngImagePlugin.iTXt` instance + :param zip: compression flag + + """ + if isinstance(value, iTXt): + return self.add_itxt(key, value, value.lang, value.tkey, bool(zip)) + + # The tEXt chunk stores latin-1 text + if not isinstance(value, bytes): + try: + value = value.encode('latin-1', 'strict') + except UnicodeError: + return self.add_itxt(key, value, zip=bool(zip)) + + if not isinstance(key, bytes): + key = key.encode('latin-1', 'strict') + + if zip: + self.add(b"zTXt", key + b"\0\0" + zlib.compress(value)) + else: + self.add(b"tEXt", key + b"\0" + value) + + +# -------------------------------------------------------------------- +# PNG image stream (IHDR/IEND) + +class PngStream(ChunkStream): + + def __init__(self, fp): + + ChunkStream.__init__(self, fp) + + # local copies of Image attributes + self.im_info = {} + self.im_text = {} + self.im_size = (0, 0) + self.im_mode = None + self.im_tile = None + self.im_palette = None + + self.text_memory = 0 + + def check_text_memory(self, chunklen): + self.text_memory += chunklen + if self.text_memory > MAX_TEXT_MEMORY: + raise ValueError("Too much memory used in text chunks: %s>MAX_TEXT_MEMORY" % + self.text_memory) + + def chunk_iCCP(self, pos, length): + + # ICC profile + s = ImageFile._safe_read(self.fp, length) + # according to PNG spec, the iCCP chunk contains: + # Profile name 1-79 bytes (character string) + # Null separator 1 byte (null character) + # Compression method 1 byte (0) + # Compressed profile n bytes (zlib with deflate compression) + i = s.find(b"\0") + if Image.DEBUG: + print("iCCP profile name", s[:i]) + print("Compression method", i8(s[i])) + comp_method = i8(s[i]) + if comp_method != 0: + raise SyntaxError("Unknown compression method %s in iCCP chunk" % + comp_method) + try: + icc_profile = _safe_zlib_decompress(s[i+2:]) + except zlib.error: + icc_profile = None # FIXME + self.im_info["icc_profile"] = icc_profile + return s + + def chunk_IHDR(self, pos, length): + + # image header + s = ImageFile._safe_read(self.fp, length) + self.im_size = i32(s), i32(s[4:]) + try: + self.im_mode, self.im_rawmode = _MODES[(i8(s[8]), i8(s[9]))] + except: + pass + if i8(s[12]): + self.im_info["interlace"] = 1 + if i8(s[11]): + raise SyntaxError("unknown filter category") + return s + + def chunk_IDAT(self, pos, length): + + # image data + self.im_tile = [("zip", (0, 0)+self.im_size, pos, self.im_rawmode)] + self.im_idat = length + raise EOFError + + def chunk_IEND(self, pos, length): + + # end of PNG image + raise EOFError + + def chunk_PLTE(self, pos, length): + + # palette + s = ImageFile._safe_read(self.fp, length) + if self.im_mode == "P": + self.im_palette = "RGB", s + return s + + def chunk_tRNS(self, pos, length): + + # transparency + s = ImageFile._safe_read(self.fp, length) + if self.im_mode == "P": + if _simple_palette.match(s): + i = s.find(b"\0") + if i >= 0: + self.im_info["transparency"] = i + else: + self.im_info["transparency"] = s + elif self.im_mode == "L": + self.im_info["transparency"] = i16(s) + elif self.im_mode == "RGB": + self.im_info["transparency"] = i16(s), i16(s[2:]), i16(s[4:]) + return s + + def chunk_gAMA(self, pos, length): + + # gamma setting + s = ImageFile._safe_read(self.fp, length) + self.im_info["gamma"] = i32(s) / 100000.0 + return s + + def chunk_pHYs(self, pos, length): + + # pixels per unit + s = ImageFile._safe_read(self.fp, length) + px, py = i32(s), i32(s[4:]) + unit = i8(s[8]) + if unit == 1: # meter + dpi = int(px * 0.0254 + 0.5), int(py * 0.0254 + 0.5) + self.im_info["dpi"] = dpi + elif unit == 0: + self.im_info["aspect"] = px, py + return s + + def chunk_tEXt(self, pos, length): + + # text + s = ImageFile._safe_read(self.fp, length) + try: + k, v = s.split(b"\0", 1) + except ValueError: + # fallback for broken tEXt tags + k = s + v = b"" + if k: + if bytes is not str: + k = k.decode('latin-1', 'strict') + v = v.decode('latin-1', 'replace') + + self.im_info[k] = self.im_text[k] = v + self.check_text_memory(len(v)) + + return s + + def chunk_zTXt(self, pos, length): + + # compressed text + s = ImageFile._safe_read(self.fp, length) + try: + k, v = s.split(b"\0", 1) + except ValueError: + k = s + v = b"" + if v: + comp_method = i8(v[0]) + else: + comp_method = 0 + if comp_method != 0: + raise SyntaxError("Unknown compression method %s in zTXt chunk" % + comp_method) + try: + v = _safe_zlib_decompress(v[1:]) + except zlib.error: + v = b"" + + if k: + if bytes is not str: + k = k.decode('latin-1', 'strict') + v = v.decode('latin-1', 'replace') + + self.im_info[k] = self.im_text[k] = v + self.check_text_memory(len(v)) + + return s + + def chunk_iTXt(self, pos, length): + + # international text + r = s = ImageFile._safe_read(self.fp, length) + try: + k, r = r.split(b"\0", 1) + except ValueError: + return s + if len(r) < 2: + return s + cf, cm, r = i8(r[0]), i8(r[1]), r[2:] + try: + lang, tk, v = r.split(b"\0", 2) + except ValueError: + return s + if cf != 0: + if cm == 0: + try: + v = _safe_zlib_decompress(v) + except zlib.error: + return s + else: + return s + if bytes is not str: + try: + k = k.decode("latin-1", "strict") + lang = lang.decode("utf-8", "strict") + tk = tk.decode("utf-8", "strict") + v = v.decode("utf-8", "strict") + except UnicodeError: + return s + + self.im_info[k] = self.im_text[k] = iTXt(v, lang, tk) + self.check_text_memory(len(v)) + + return s + + +# -------------------------------------------------------------------- +# PNG reader + +def _accept(prefix): + return prefix[:8] == _MAGIC + + +## +# Image plugin for PNG images. + +class PngImageFile(ImageFile.ImageFile): + + format = "PNG" + format_description = "Portable network graphics" + + def _open(self): + + if self.fp.read(8) != _MAGIC: + raise SyntaxError("not a PNG file") + + # + # Parse headers up to the first IDAT chunk + + self.png = PngStream(self.fp) + + while True: + + # + # get next chunk + + cid, pos, length = self.png.read() + + try: + s = self.png.call(cid, pos, length) + except EOFError: + break + except AttributeError: + if Image.DEBUG: + print(cid, pos, length, "(unknown)") + s = ImageFile._safe_read(self.fp, length) + + self.png.crc(cid, s) + + # + # Copy relevant attributes from the PngStream. An alternative + # would be to let the PngStream class modify these attributes + # directly, but that introduces circular references which are + # difficult to break if things go wrong in the decoder... + # (believe me, I've tried ;-) + + self.mode = self.png.im_mode + self.size = self.png.im_size + self.info = self.png.im_info + self.text = self.png.im_text # experimental + self.tile = self.png.im_tile + + if self.png.im_palette: + rawmode, data = self.png.im_palette + self.palette = ImagePalette.raw(rawmode, data) + + self.__idat = length # used by load_read() + + def verify(self): + "Verify PNG file" + + if self.fp is None: + raise RuntimeError("verify must be called directly after open") + + # back up to beginning of IDAT block + self.fp.seek(self.tile[0][2] - 8) + + self.png.verify() + self.png.close() + + self.fp = None + + def load_prepare(self): + "internal: prepare to read PNG file" + + if self.info.get("interlace"): + self.decoderconfig = self.decoderconfig + (1,) + + ImageFile.ImageFile.load_prepare(self) + + def load_read(self, read_bytes): + "internal: read more image data" + + while self.__idat == 0: + # end of chunk, skip forward to next one + + self.fp.read(4) # CRC + + cid, pos, length = self.png.read() + + if cid not in [b"IDAT", b"DDAT"]: + self.png.push(cid, pos, length) + return b"" + + self.__idat = length # empty chunks are allowed + + # read more data from this chunk + if read_bytes <= 0: + read_bytes = self.__idat + else: + read_bytes = min(read_bytes, self.__idat) + + self.__idat = self.__idat - read_bytes + + return self.fp.read(read_bytes) + + def load_end(self): + "internal: finished reading image data" + + self.png.close() + self.png = None + + +# -------------------------------------------------------------------- +# PNG writer + +o8 = _binary.o8 +o16 = _binary.o16be +o32 = _binary.o32be + +_OUTMODES = { + # supported PIL modes, and corresponding rawmodes/bits/color combinations + "1": ("1", b'\x01\x00'), + "L;1": ("L;1", b'\x01\x00'), + "L;2": ("L;2", b'\x02\x00'), + "L;4": ("L;4", b'\x04\x00'), + "L": ("L", b'\x08\x00'), + "LA": ("LA", b'\x08\x04'), + "I": ("I;16B", b'\x10\x00'), + "P;1": ("P;1", b'\x01\x03'), + "P;2": ("P;2", b'\x02\x03'), + "P;4": ("P;4", b'\x04\x03'), + "P": ("P", b'\x08\x03'), + "RGB": ("RGB", b'\x08\x02'), + "RGBA": ("RGBA", b'\x08\x06'), +} + + +def putchunk(fp, cid, *data): + "Write a PNG chunk (including CRC field)" + + data = b"".join(data) + + fp.write(o32(len(data)) + cid) + fp.write(data) + hi, lo = Image.core.crc32(data, Image.core.crc32(cid)) + fp.write(o16(hi) + o16(lo)) + + +class _idat: + # wrap output from the encoder in IDAT chunks + + def __init__(self, fp, chunk): + self.fp = fp + self.chunk = chunk + + def write(self, data): + self.chunk(self.fp, b"IDAT", data) + + +def _save(im, fp, filename, chunk=putchunk, check=0): + # save an image to disk (called by the save method) + + mode = im.mode + + if mode == "P": + + # + # attempt to minimize storage requirements for palette images + if "bits" in im.encoderinfo: + # number of bits specified by user + colors = 1 << im.encoderinfo["bits"] + else: + # check palette contents + if im.palette: + colors = max(min(len(im.palette.getdata()[1])//3, 256), 2) + else: + colors = 256 + + if colors <= 2: + bits = 1 + elif colors <= 4: + bits = 2 + elif colors <= 16: + bits = 4 + else: + bits = 8 + if bits != 8: + mode = "%s;%d" % (mode, bits) + + # encoder options + if "dictionary" in im.encoderinfo: + dictionary = im.encoderinfo["dictionary"] + else: + dictionary = b"" + + im.encoderconfig = ("optimize" in im.encoderinfo, + im.encoderinfo.get("compress_level", -1), + im.encoderinfo.get("compress_type", -1), + dictionary) + + # get the corresponding PNG mode + try: + rawmode, mode = _OUTMODES[mode] + except KeyError: + raise IOError("cannot write mode %s as PNG" % mode) + + if check: + return check + + # + # write minimal PNG file + + fp.write(_MAGIC) + + chunk(fp, b"IHDR", + o32(im.size[0]), o32(im.size[1]), # 0: size + mode, # 8: depth/type + b'\0', # 10: compression + b'\0', # 11: filter category + b'\0') # 12: interlace flag + + if im.mode == "P": + palette_byte_number = (2 ** bits) * 3 + palette_bytes = im.im.getpalette("RGB")[:palette_byte_number] + while len(palette_bytes) < palette_byte_number: + palette_bytes += b'\0' + chunk(fp, b"PLTE", palette_bytes) + + transparency = im.encoderinfo.get('transparency', + im.info.get('transparency', None)) + + if transparency or transparency == 0: + if im.mode == "P": + # limit to actual palette size + alpha_bytes = 2**bits + if isinstance(transparency, bytes): + chunk(fp, b"tRNS", transparency[:alpha_bytes]) + else: + transparency = max(0, min(255, transparency)) + alpha = b'\xFF' * transparency + b'\0' + chunk(fp, b"tRNS", alpha[:alpha_bytes]) + elif im.mode == "L": + transparency = max(0, min(65535, transparency)) + chunk(fp, b"tRNS", o16(transparency)) + elif im.mode == "RGB": + red, green, blue = transparency + chunk(fp, b"tRNS", o16(red) + o16(green) + o16(blue)) + else: + if "transparency" in im.encoderinfo: + # don't bother with transparency if it's an RGBA + # and it's in the info dict. It's probably just stale. + raise IOError("cannot use transparency for this mode") + else: + if im.mode == "P" and im.im.getpalettemode() == "RGBA": + alpha = im.im.getpalette("RGBA", "A") + alpha_bytes = 2**bits + chunk(fp, b"tRNS", alpha[:alpha_bytes]) + + if 0: + # FIXME: to be supported some day + chunk(fp, b"gAMA", o32(int(gamma * 100000.0))) + + dpi = im.encoderinfo.get("dpi") + if dpi: + chunk(fp, b"pHYs", + o32(int(dpi[0] / 0.0254 + 0.5)), + o32(int(dpi[1] / 0.0254 + 0.5)), + b'\x01') + + info = im.encoderinfo.get("pnginfo") + if info: + for cid, data in info.chunks: + chunk(fp, cid, data) + + # ICC profile writing support -- 2008-06-06 Florian Hoech + if im.info.get("icc_profile"): + # ICC profile + # according to PNG spec, the iCCP chunk contains: + # Profile name 1-79 bytes (character string) + # Null separator 1 byte (null character) + # Compression method 1 byte (0) + # Compressed profile n bytes (zlib with deflate compression) + name = b"ICC Profile" + data = name + b"\0\0" + zlib.compress(im.info["icc_profile"]) + chunk(fp, b"iCCP", data) + + ImageFile._save(im, _idat(fp, chunk), + [("zip", (0, 0)+im.size, 0, rawmode)]) + + chunk(fp, b"IEND", b"") + + try: + fp.flush() + except: + pass + + +# -------------------------------------------------------------------- +# PNG chunk converter + +def getchunks(im, **params): + """Return a list of PNG chunks representing this image.""" + + class collector: + data = [] + + def write(self, data): + pass + + def append(self, chunk): + self.data.append(chunk) + + def append(fp, cid, *data): + data = b"".join(data) + hi, lo = Image.core.crc32(data, Image.core.crc32(cid)) + crc = o16(hi) + o16(lo) + fp.append((cid, data, crc)) + + fp = collector() + + try: + im.encoderinfo = params + _save(im, fp, None, append) + finally: + del im.encoderinfo + + return fp.data + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open("PNG", PngImageFile, _accept) +Image.register_save("PNG", _save) + +Image.register_extension("PNG", ".png") + +Image.register_mime("PNG", "image/png") diff --git a/pyPackages/pillowx86/PIL/PpmImagePlugin.py b/pyPackages/pillowx86/PIL/PpmImagePlugin.py new file mode 100644 index 0000000..9548324 --- /dev/null +++ b/pyPackages/pillowx86/PIL/PpmImagePlugin.py @@ -0,0 +1,172 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PPM support for PIL +# +# History: +# 96-03-24 fl Created +# 98-03-06 fl Write RGBA images (as RGB, that is) +# +# Copyright (c) Secret Labs AB 1997-98. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.2" + +import string + +from PIL import Image, ImageFile + +# +# -------------------------------------------------------------------- + +b_whitespace = string.whitespace +try: + import locale + locale_lang, locale_enc = locale.getlocale() + if locale_enc is None: + locale_lang, locale_enc = locale.getdefaultlocale() + b_whitespace = b_whitespace.decode(locale_enc) +except: + pass +b_whitespace = b_whitespace.encode('ascii', 'ignore') + +MODES = { + # standard + b"P4": "1", + b"P5": "L", + b"P6": "RGB", + # extensions + b"P0CMYK": "CMYK", + # PIL extensions (for test purposes only) + b"PyP": "P", + b"PyRGBA": "RGBA", + b"PyCMYK": "CMYK" +} + + +def _accept(prefix): + return prefix[0:1] == b"P" and prefix[1] in b"0456y" + + +## +# Image plugin for PBM, PGM, and PPM images. + +class PpmImageFile(ImageFile.ImageFile): + + format = "PPM" + format_description = "Pbmplus image" + + def _token(self, s=b""): + while True: # read until next whitespace + c = self.fp.read(1) + if not c or c in b_whitespace: + break + if c > b'\x79': + raise ValueError("Expected ASCII value, found binary") + s = s + c + if (len(s) > 9): + raise ValueError("Expected int, got > 9 digits") + return s + + def _open(self): + + # check magic + s = self.fp.read(1) + if s != b"P": + raise SyntaxError("not a PPM file") + mode = MODES[self._token(s)] + + if mode == "1": + self.mode = "1" + rawmode = "1;I" + else: + self.mode = rawmode = mode + + for ix in range(3): + while True: + while True: + s = self.fp.read(1) + if s not in b_whitespace: + break + if s != b"#": + break + s = self.fp.readline() + s = int(self._token(s)) + if ix == 0: + xsize = s + elif ix == 1: + ysize = s + if mode == "1": + break + elif ix == 2: + # maxgrey + if s > 255: + if not mode == 'L': + raise ValueError("Too many colors for band: %s" % s) + if s < 2**16: + self.mode = 'I' + rawmode = 'I;16B' + else: + self.mode = 'I' + rawmode = 'I;32B' + + self.size = xsize, ysize + self.tile = [("raw", + (0, 0, xsize, ysize), + self.fp.tell(), + (rawmode, 0, 1))] + + # ALTERNATIVE: load via builtin debug function + # self.im = Image.core.open_ppm(self.filename) + # self.mode = self.im.mode + # self.size = self.im.size + + +# +# -------------------------------------------------------------------- + +def _save(im, fp, filename): + if im.mode == "1": + rawmode, head = "1;I", b"P4" + elif im.mode == "L": + rawmode, head = "L", b"P5" + elif im.mode == "I": + if im.getextrema()[1] < 2**16: + rawmode, head = "I;16B", b"P5" + else: + rawmode, head = "I;32B", b"P5" + elif im.mode == "RGB": + rawmode, head = "RGB", b"P6" + elif im.mode == "RGBA": + rawmode, head = "RGB", b"P6" + else: + raise IOError("cannot write mode %s as PPM" % im.mode) + fp.write(head + ("\n%d %d\n" % im.size).encode('ascii')) + if head == b"P6": + fp.write(b"255\n") + if head == b"P5": + if rawmode == "L": + fp.write(b"255\n") + elif rawmode == "I;16B": + fp.write(b"65535\n") + elif rawmode == "I;32B": + fp.write(b"2147483648\n") + ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0, (rawmode, 0, 1))]) + + # ALTERNATIVE: save via builtin debug function + # im._dump(filename) + +# +# -------------------------------------------------------------------- + +Image.register_open("PPM", PpmImageFile, _accept) +Image.register_save("PPM", _save) + +Image.register_extension("PPM", ".pbm") +Image.register_extension("PPM", ".pgm") +Image.register_extension("PPM", ".ppm") diff --git a/pyPackages/pillowx86/PIL/PsdImagePlugin.py b/pyPackages/pillowx86/PIL/PsdImagePlugin.py new file mode 100644 index 0000000..02c94a8 --- /dev/null +++ b/pyPackages/pillowx86/PIL/PsdImagePlugin.py @@ -0,0 +1,304 @@ +# +# The Python Imaging Library +# $Id$ +# +# Adobe PSD 2.5/3.0 file handling +# +# History: +# 1995-09-01 fl Created +# 1997-01-03 fl Read most PSD images +# 1997-01-18 fl Fixed P and CMYK support +# 2001-10-21 fl Added seek/tell support (for layers) +# +# Copyright (c) 1997-2001 by Secret Labs AB. +# Copyright (c) 1995-2001 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.4" + +from PIL import Image, ImageFile, ImagePalette, _binary + +MODES = { + # (photoshop mode, bits) -> (pil mode, required channels) + (0, 1): ("1", 1), + (0, 8): ("L", 1), + (1, 8): ("L", 1), + (2, 8): ("P", 1), + (3, 8): ("RGB", 3), + (4, 8): ("CMYK", 4), + (7, 8): ("L", 1), # FIXME: multilayer + (8, 8): ("L", 1), # duotone + (9, 8): ("LAB", 3) +} + +# +# helpers + +i8 = _binary.i8 +i16 = _binary.i16be +i32 = _binary.i32be + + +# --------------------------------------------------------------------. +# read PSD images + +def _accept(prefix): + return prefix[:4] == b"8BPS" + + +## +# Image plugin for Photoshop images. + +class PsdImageFile(ImageFile.ImageFile): + + format = "PSD" + format_description = "Adobe Photoshop" + + def _open(self): + + read = self.fp.read + + # + # header + + s = read(26) + if s[:4] != b"8BPS" or i16(s[4:]) != 1: + raise SyntaxError("not a PSD file") + + psd_bits = i16(s[22:]) + psd_channels = i16(s[12:]) + psd_mode = i16(s[24:]) + + mode, channels = MODES[(psd_mode, psd_bits)] + + if channels > psd_channels: + raise IOError("not enough channels") + + self.mode = mode + self.size = i32(s[18:]), i32(s[14:]) + + # + # color mode data + + size = i32(read(4)) + if size: + data = read(size) + if mode == "P" and size == 768: + self.palette = ImagePalette.raw("RGB;L", data) + + # + # image resources + + self.resources = [] + + size = i32(read(4)) + if size: + # load resources + end = self.fp.tell() + size + while self.fp.tell() < end: + signature = read(4) + id = i16(read(2)) + name = read(i8(read(1))) + if not (len(name) & 1): + read(1) # padding + data = read(i32(read(4))) + if (len(data) & 1): + read(1) # padding + self.resources.append((id, name, data)) + if id == 1039: # ICC profile + self.info["icc_profile"] = data + + # + # layer and mask information + + self.layers = [] + + size = i32(read(4)) + if size: + end = self.fp.tell() + size + size = i32(read(4)) + if size: + self.layers = _layerinfo(self.fp) + self.fp.seek(end) + + # + # image descriptor + + self.tile = _maketile(self.fp, mode, (0, 0) + self.size, channels) + + # keep the file open + self._fp = self.fp + self.frame = 0 + + def seek(self, layer): + # seek to given layer (1..max) + if layer == self.frame: + return + try: + if layer <= 0: + raise IndexError + name, mode, bbox, tile = self.layers[layer-1] + self.mode = mode + self.tile = tile + self.frame = layer + self.fp = self._fp + return name, bbox + except IndexError: + raise EOFError("no such layer") + + def tell(self): + # return layer number (0=image, 1..max=layers) + return self.frame + + def load_prepare(self): + # create image memory if necessary + if not self.im or\ + self.im.mode != self.mode or self.im.size != self.size: + self.im = Image.core.fill(self.mode, self.size, 0) + # create palette (optional) + if self.mode == "P": + Image.Image.load(self) + + +def _layerinfo(file): + # read layerinfo block + layers = [] + read = file.read + for i in range(abs(i16(read(2)))): + + # bounding box + y0 = i32(read(4)) + x0 = i32(read(4)) + y1 = i32(read(4)) + x1 = i32(read(4)) + + # image info + info = [] + mode = [] + types = list(range(i16(read(2)))) + if len(types) > 4: + continue + + for i in types: + type = i16(read(2)) + + if type == 65535: + m = "A" + else: + m = "RGBA"[type] + + mode.append(m) + size = i32(read(4)) + info.append((m, size)) + + # figure out the image mode + mode.sort() + if mode == ["R"]: + mode = "L" + elif mode == ["B", "G", "R"]: + mode = "RGB" + elif mode == ["A", "B", "G", "R"]: + mode = "RGBA" + else: + mode = None # unknown + + # skip over blend flags and extra information + filler = read(12) + name = "" + size = i32(read(4)) + combined = 0 + if size: + length = i32(read(4)) + if length: + mask_y = i32(read(4)) + mask_x = i32(read(4)) + mask_h = i32(read(4)) - mask_y + mask_w = i32(read(4)) - mask_x + file.seek(length - 16, 1) + combined += length + 4 + + length = i32(read(4)) + if length: + file.seek(length, 1) + combined += length + 4 + + length = i8(read(1)) + if length: + # Don't know the proper encoding, + # Latin-1 should be a good guess + name = read(length).decode('latin-1', 'replace') + combined += length + 1 + + file.seek(size - combined, 1) + layers.append((name, mode, (x0, y0, x1, y1))) + + # get tiles + i = 0 + for name, mode, bbox in layers: + tile = [] + for m in mode: + t = _maketile(file, m, bbox, 1) + if t: + tile.extend(t) + layers[i] = name, mode, bbox, tile + i += 1 + + return layers + + +def _maketile(file, mode, bbox, channels): + + tile = None + read = file.read + + compression = i16(read(2)) + + xsize = bbox[2] - bbox[0] + ysize = bbox[3] - bbox[1] + + offset = file.tell() + + if compression == 0: + # + # raw compression + tile = [] + for channel in range(channels): + layer = mode[channel] + if mode == "CMYK": + layer += ";I" + tile.append(("raw", bbox, offset, layer)) + offset = offset + xsize*ysize + + elif compression == 1: + # + # packbits compression + i = 0 + tile = [] + bytecount = read(channels * ysize * 2) + offset = file.tell() + for channel in range(channels): + layer = mode[channel] + if mode == "CMYK": + layer += ";I" + tile.append( + ("packbits", bbox, offset, layer) + ) + for y in range(ysize): + offset = offset + i16(bytecount[i:i+2]) + i += 2 + + file.seek(offset) + + if offset & 1: + read(1) # padding + + return tile + +# -------------------------------------------------------------------- +# registry + +Image.register_open("PSD", PsdImageFile, _accept) + +Image.register_extension("PSD", ".psd") diff --git a/pyPackages/pillowx86/PIL/PyAccess.py b/pyPackages/pillowx86/PIL/PyAccess.py new file mode 100644 index 0000000..2882905 --- /dev/null +++ b/pyPackages/pillowx86/PIL/PyAccess.py @@ -0,0 +1,315 @@ +# +# The Python Imaging Library +# Pillow fork +# +# Python implementation of the PixelAccess Object +# +# Copyright (c) 1997-2009 by Secret Labs AB. All rights reserved. +# Copyright (c) 1995-2009 by Fredrik Lundh. +# Copyright (c) 2013 Eric Soroos +# +# See the README file for information on usage and redistribution +# + +# Notes: +# +# * Implements the pixel access object following Access. +# * Does not implement the line functions, as they don't appear to be used +# * Taking only the tuple form, which is used from python. +# * Fill.c uses the integer form, but it's still going to use the old +# Access.c implementation. +# + +from __future__ import print_function + +from cffi import FFI +import sys + +DEBUG = 0 + +defs = """ +struct Pixel_RGBA { + unsigned char r,g,b,a; +}; +struct Pixel_I16 { + unsigned char l,r; +}; +""" +ffi = FFI() +ffi.cdef(defs) + + +class PyAccess(object): + + def __init__(self, img, readonly=False): + vals = dict(img.im.unsafe_ptrs) + self.readonly = readonly + self.image8 = ffi.cast('unsigned char **', vals['image8']) + self.image32 = ffi.cast('int **', vals['image32']) + self.image = ffi.cast('unsigned char **', vals['image']) + self.xsize = vals['xsize'] + self.ysize = vals['ysize'] + + if DEBUG: + print (vals) + self._post_init() + + def _post_init(): + pass + + def __setitem__(self, xy, color): + """ + Modifies the pixel at x,y. The color is given as a single + numerical value for single band images, and a tuple for + multi-band images + + :param xy: The pixel coordinate, given as (x, y). + :param value: The pixel value. + """ + if self.readonly: + raise ValueError('Attempt to putpixel a read only image') + (x, y) = self.check_xy(xy) + return self.set_pixel(x, y, color) + + def __getitem__(self, xy): + """ + Returns the pixel at x,y. The pixel is returned as a single + value for single band images or a tuple for multiple band + images + + :param xy: The pixel coordinate, given as (x, y). + :returns: a pixel value for single band images, a tuple of + pixel values for multiband images. + """ + + (x, y) = self.check_xy(xy) + return self.get_pixel(x, y) + + putpixel = __setitem__ + getpixel = __getitem__ + + def check_xy(self, xy): + (x, y) = xy + if not (0 <= x < self.xsize and 0 <= y < self.ysize): + raise ValueError('pixel location out of range') + return xy + + +class _PyAccess32_2(PyAccess): + """ PA, LA, stored in first and last bytes of a 32 bit word """ + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) + + def get_pixel(self, x, y): + pixel = self.pixels[y][x] + return (pixel.r, pixel.a) + + def set_pixel(self, x, y, color): + pixel = self.pixels[y][x] + # tuple + pixel.r = min(color[0], 255) + pixel.a = min(color[1], 255) + + +class _PyAccess32_3(PyAccess): + """ RGB and friends, stored in the first three bytes of a 32 bit word """ + + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) + + def get_pixel(self, x, y): + pixel = self.pixels[y][x] + return (pixel.r, pixel.g, pixel.b) + + def set_pixel(self, x, y, color): + pixel = self.pixels[y][x] + # tuple + pixel.r = min(color[0], 255) + pixel.g = min(color[1], 255) + pixel.b = min(color[2], 255) + + +class _PyAccess32_4(PyAccess): + """ RGBA etc, all 4 bytes of a 32 bit word """ + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) + + def get_pixel(self, x, y): + pixel = self.pixels[y][x] + return (pixel.r, pixel.g, pixel.b, pixel.a) + + def set_pixel(self, x, y, color): + pixel = self.pixels[y][x] + # tuple + pixel.r = min(color[0], 255) + pixel.g = min(color[1], 255) + pixel.b = min(color[2], 255) + pixel.a = min(color[3], 255) + + +class _PyAccess8(PyAccess): + """ 1, L, P, 8 bit images stored as uint8 """ + def _post_init(self, *args, **kwargs): + self.pixels = self.image8 + + def get_pixel(self, x, y): + return self.pixels[y][x] + + def set_pixel(self, x, y, color): + try: + # integer + self.pixels[y][x] = min(color, 255) + except: + # tuple + self.pixels[y][x] = min(color[0], 255) + + +class _PyAccessI16_N(PyAccess): + """ I;16 access, native bitendian without conversion """ + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast('unsigned short **', self.image) + + def get_pixel(self, x, y): + return self.pixels[y][x] + + def set_pixel(self, x, y, color): + try: + # integer + self.pixels[y][x] = min(color, 65535) + except: + # tuple + self.pixels[y][x] = min(color[0], 65535) + + +class _PyAccessI16_L(PyAccess): + """ I;16L access, with conversion """ + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast('struct Pixel_I16 **', self.image) + + def get_pixel(self, x, y): + pixel = self.pixels[y][x] + return pixel.l + pixel.r * 256 + + def set_pixel(self, x, y, color): + pixel = self.pixels[y][x] + try: + color = min(color, 65535) + except: + color = min(color[0], 65535) + + pixel.l = color & 0xFF + pixel.r = color >> 8 + + +class _PyAccessI16_B(PyAccess): + """ I;16B access, with conversion """ + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast('struct Pixel_I16 **', self.image) + + def get_pixel(self, x, y): + pixel = self.pixels[y][x] + return pixel.l * 256 + pixel.r + + def set_pixel(self, x, y, color): + pixel = self.pixels[y][x] + try: + color = min(color, 65535) + except: + color = min(color[0], 65535) + + pixel.l = color >> 8 + pixel.r = color & 0xFF + + +class _PyAccessI32_N(PyAccess): + """ Signed Int32 access, native endian """ + def _post_init(self, *args, **kwargs): + self.pixels = self.image32 + + def get_pixel(self, x, y): + return self.pixels[y][x] + + def set_pixel(self, x, y, color): + self.pixels[y][x] = color + + +class _PyAccessI32_Swap(PyAccess): + """ I;32L/B access, with byteswapping conversion """ + def _post_init(self, *args, **kwargs): + self.pixels = self.image32 + + def reverse(self, i): + orig = ffi.new('int *', i) + chars = ffi.cast('unsigned char *', orig) + chars[0], chars[1], chars[2], chars[3] = chars[3], chars[2], \ + chars[1], chars[0] + return ffi.cast('int *', chars)[0] + + def get_pixel(self, x, y): + return self.reverse(self.pixels[y][x]) + + def set_pixel(self, x, y, color): + self.pixels[y][x] = self.reverse(color) + + +class _PyAccessF(PyAccess): + """ 32 bit float access """ + def _post_init(self, *args, **kwargs): + self.pixels = ffi.cast('float **', self.image32) + + def get_pixel(self, x, y): + return self.pixels[y][x] + + def set_pixel(self, x, y, color): + try: + # not a tuple + self.pixels[y][x] = color + except: + # tuple + self.pixels[y][x] = color[0] + + +mode_map = {'1': _PyAccess8, + 'L': _PyAccess8, + 'P': _PyAccess8, + 'LA': _PyAccess32_2, + 'PA': _PyAccess32_2, + 'RGB': _PyAccess32_3, + 'LAB': _PyAccess32_3, + 'HSV': _PyAccess32_3, + 'YCbCr': _PyAccess32_3, + 'RGBA': _PyAccess32_4, + 'RGBa': _PyAccess32_4, + 'RGBX': _PyAccess32_4, + 'CMYK': _PyAccess32_4, + 'F': _PyAccessF, + 'I': _PyAccessI32_N, + } + +if sys.byteorder == 'little': + mode_map['I;16'] = _PyAccessI16_N + mode_map['I;16L'] = _PyAccessI16_N + mode_map['I;16B'] = _PyAccessI16_B + + mode_map['I;32L'] = _PyAccessI32_N + mode_map['I;32B'] = _PyAccessI32_Swap +else: + mode_map['I;16'] = _PyAccessI16_L + mode_map['I;16L'] = _PyAccessI16_L + mode_map['I;16B'] = _PyAccessI16_N + + mode_map['I;32L'] = _PyAccessI32_Swap + mode_map['I;32B'] = _PyAccessI32_N + + +def new(img, readonly=False): + access_type = mode_map.get(img.mode, None) + if not access_type: + if DEBUG: + print("PyAccess Not Implemented: %s" % img.mode) + return None + if DEBUG: + print("New PyAccess: %s" % img.mode) + return access_type(img, readonly) + +# End of file diff --git a/pyPackages/pillowx86/PIL/SgiImagePlugin.py b/pyPackages/pillowx86/PIL/SgiImagePlugin.py new file mode 100644 index 0000000..2b8fcd8 --- /dev/null +++ b/pyPackages/pillowx86/PIL/SgiImagePlugin.py @@ -0,0 +1,91 @@ +# +# The Python Imaging Library. +# $Id$ +# +# SGI image file handling +# +# See "The SGI Image File Format (Draft version 0.97)", Paul Haeberli. +# +# +# History: +# 1995-09-10 fl Created +# +# Copyright (c) 2008 by Karsten Hiddemann. +# Copyright (c) 1997 by Secret Labs AB. +# Copyright (c) 1995 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.2" + + +from PIL import Image, ImageFile, _binary + +i8 = _binary.i8 +i16 = _binary.i16be +i32 = _binary.i32be + + +def _accept(prefix): + return i16(prefix) == 474 + + +## +# Image plugin for SGI images. + +class SgiImageFile(ImageFile.ImageFile): + + format = "SGI" + format_description = "SGI Image File Format" + + def _open(self): + + # HEAD + s = self.fp.read(512) + if i16(s) != 474: + raise ValueError("Not an SGI image file") + + # relevant header entries + compression = i8(s[2]) + + # bytes, dimension, zsize + layout = i8(s[3]), i16(s[4:]), i16(s[10:]) + + # determine mode from bytes/zsize + if layout == (1, 2, 1) or layout == (1, 1, 1): + self.mode = "L" + elif layout == (1, 3, 3): + self.mode = "RGB" + elif layout == (1, 3, 4): + self.mode = "RGBA" + else: + raise ValueError("Unsupported SGI image mode") + + # size + self.size = i16(s[6:]), i16(s[8:]) + + # decoder info + if compression == 0: + offset = 512 + pagesize = self.size[0]*self.size[1]*layout[0] + self.tile = [] + for layer in self.mode: + self.tile.append( + ("raw", (0, 0)+self.size, offset, (layer, 0, -1))) + offset = offset + pagesize + elif compression == 1: + raise ValueError("SGI RLE encoding not supported") + +# +# registry + +Image.register_open("SGI", SgiImageFile, _accept) + +Image.register_extension("SGI", ".bw") +Image.register_extension("SGI", ".rgb") +Image.register_extension("SGI", ".rgba") +Image.register_extension("SGI", ".sgi") + +# End of file diff --git a/pyPackages/pillowx86/PIL/SpiderImagePlugin.py b/pyPackages/pillowx86/PIL/SpiderImagePlugin.py new file mode 100644 index 0000000..306b348 --- /dev/null +++ b/pyPackages/pillowx86/PIL/SpiderImagePlugin.py @@ -0,0 +1,312 @@ +# +# The Python Imaging Library. +# +# SPIDER image file handling +# +# History: +# 2004-08-02 Created BB +# 2006-03-02 added save method +# 2006-03-13 added support for stack images +# +# Copyright (c) 2004 by Health Research Inc. (HRI) RENSSELAER, NY 12144. +# Copyright (c) 2004 by William Baxter. +# Copyright (c) 2004 by Secret Labs AB. +# Copyright (c) 2004 by Fredrik Lundh. +# + +## +# Image plugin for the Spider image format. This format is is used +# by the SPIDER software, in processing image data from electron +# microscopy and tomography. +## + +# +# SpiderImagePlugin.py +# +# The Spider image format is used by SPIDER software, in processing +# image data from electron microscopy and tomography. +# +# Spider home page: +# http://www.wadsworth.org/spider_doc/spider/docs/spider.html +# +# Details about the Spider image format: +# http://www.wadsworth.org/spider_doc/spider/docs/image_doc.html +# + +from __future__ import print_function + +from PIL import Image, ImageFile +import os +import struct +import sys + + +def isInt(f): + try: + i = int(f) + if f-i == 0: + return 1 + else: + return 0 + except: + return 0 + +iforms = [1, 3, -11, -12, -21, -22] + + +# There is no magic number to identify Spider files, so just check a +# series of header locations to see if they have reasonable values. +# Returns no.of bytes in the header, if it is a valid Spider header, +# otherwise returns 0 + +def isSpiderHeader(t): + h = (99,) + t # add 1 value so can use spider header index start=1 + # header values 1,2,5,12,13,22,23 should be integers + for i in [1, 2, 5, 12, 13, 22, 23]: + if not isInt(h[i]): + return 0 + # check iform + iform = int(h[5]) + if iform not in iforms: + return 0 + # check other header values + labrec = int(h[13]) # no. records in file header + labbyt = int(h[22]) # total no. of bytes in header + lenbyt = int(h[23]) # record length in bytes + # print "labrec = %d, labbyt = %d, lenbyt = %d" % (labrec,labbyt,lenbyt) + if labbyt != (labrec * lenbyt): + return 0 + # looks like a valid header + return labbyt + + +def isSpiderImage(filename): + fp = open(filename, 'rb') + f = fp.read(92) # read 23 * 4 bytes + fp.close() + t = struct.unpack('>23f', f) # try big-endian first + hdrlen = isSpiderHeader(t) + if hdrlen == 0: + t = struct.unpack('<23f', f) # little-endian + hdrlen = isSpiderHeader(t) + return hdrlen + + +class SpiderImageFile(ImageFile.ImageFile): + + format = "SPIDER" + format_description = "Spider 2D image" + + def _open(self): + # check header + n = 27 * 4 # read 27 float values + f = self.fp.read(n) + + try: + self.bigendian = 1 + t = struct.unpack('>27f', f) # try big-endian first + hdrlen = isSpiderHeader(t) + if hdrlen == 0: + self.bigendian = 0 + t = struct.unpack('<27f', f) # little-endian + hdrlen = isSpiderHeader(t) + if hdrlen == 0: + raise SyntaxError("not a valid Spider file") + except struct.error: + raise SyntaxError("not a valid Spider file") + + h = (99,) + t # add 1 value : spider header index starts at 1 + iform = int(h[5]) + if iform != 1: + raise SyntaxError("not a Spider 2D image") + + self.size = int(h[12]), int(h[2]) # size in pixels (width, height) + self.istack = int(h[24]) + self.imgnumber = int(h[27]) + + if self.istack == 0 and self.imgnumber == 0: + # stk=0, img=0: a regular 2D image + offset = hdrlen + self.nimages = 1 + elif self.istack > 0 and self.imgnumber == 0: + # stk>0, img=0: Opening the stack for the first time + self.imgbytes = int(h[12]) * int(h[2]) * 4 + self.hdrlen = hdrlen + self.nimages = int(h[26]) + # Point to the first image in the stack + offset = hdrlen * 2 + self.imgnumber = 1 + elif self.istack == 0 and self.imgnumber > 0: + # stk=0, img>0: an image within the stack + offset = hdrlen + self.stkoffset + self.istack = 2 # So Image knows it's still a stack + else: + raise SyntaxError("inconsistent stack header values") + + if self.bigendian: + self.rawmode = "F;32BF" + else: + self.rawmode = "F;32F" + self.mode = "F" + + self.tile = [ + ("raw", (0, 0) + self.size, offset, + (self.rawmode, 0, 1))] + self.__fp = self.fp # FIXME: hack + + # 1st image index is zero (although SPIDER imgnumber starts at 1) + def tell(self): + if self.imgnumber < 1: + return 0 + else: + return self.imgnumber - 1 + + def seek(self, frame): + if self.istack == 0: + return + if frame >= self.nimages: + raise EOFError("attempt to seek past end of file") + self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes) + self.fp = self.__fp + self.fp.seek(self.stkoffset) + self._open() + + # returns a byte image after rescaling to 0..255 + def convert2byte(self, depth=255): + (min, max) = self.getextrema() + m = 1 + if max != min: + m = depth / (max-min) + b = -m * min + return self.point(lambda i, m=m, b=b: i * m + b).convert("L") + + # returns a ImageTk.PhotoImage object, after rescaling to 0..255 + def tkPhotoImage(self): + from PIL import ImageTk + return ImageTk.PhotoImage(self.convert2byte(), palette=256) + + +# -------------------------------------------------------------------- +# Image series + +# given a list of filenames, return a list of images +def loadImageSeries(filelist=None): + " create a list of Image.images for use in montage " + if filelist is None or len(filelist) < 1: + return + + imglist = [] + for img in filelist: + if not os.path.exists(img): + print("unable to find %s" % img) + continue + try: + im = Image.open(img).convert2byte() + except: + if not isSpiderImage(img): + print(img + " is not a Spider image file") + continue + im.info['filename'] = img + imglist.append(im) + return imglist + + +# -------------------------------------------------------------------- +# For saving images in Spider format + +def makeSpiderHeader(im): + nsam, nrow = im.size + lenbyt = nsam * 4 # There are labrec records in the header + labrec = 1024 / lenbyt + if 1024 % lenbyt != 0: + labrec += 1 + labbyt = labrec * lenbyt + hdr = [] + nvalues = int(labbyt / 4) + for i in range(nvalues): + hdr.append(0.0) + + if len(hdr) < 23: + return [] + + # NB these are Fortran indices + hdr[1] = 1.0 # nslice (=1 for an image) + hdr[2] = float(nrow) # number of rows per slice + hdr[5] = 1.0 # iform for 2D image + hdr[12] = float(nsam) # number of pixels per line + hdr[13] = float(labrec) # number of records in file header + hdr[22] = float(labbyt) # total number of bytes in header + hdr[23] = float(lenbyt) # record length in bytes + + # adjust for Fortran indexing + hdr = hdr[1:] + hdr.append(0.0) + # pack binary data into a string + hdrstr = [] + for v in hdr: + hdrstr.append(struct.pack('f', v)) + return hdrstr + + +def _save(im, fp, filename): + if im.mode[0] != "F": + im = im.convert('F') + + hdr = makeSpiderHeader(im) + if len(hdr) < 256: + raise IOError("Error creating Spider header") + + # write the SPIDER header + try: + fp = open(filename, 'wb') + except: + raise IOError("Unable to open %s for writing" % filename) + fp.writelines(hdr) + + rawmode = "F;32NF" # 32-bit native floating point + ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0, (rawmode, 0, 1))]) + + fp.close() + + +def _save_spider(im, fp, filename): + # get the filename extension and register it with Image + fn, ext = os.path.splitext(filename) + Image.register_extension("SPIDER", ext) + _save(im, fp, filename) + +# -------------------------------------------------------------------- + +Image.register_open("SPIDER", SpiderImageFile) +Image.register_save("SPIDER", _save_spider) + +if __name__ == "__main__": + + if not sys.argv[1:]: + print("Syntax: python SpiderImagePlugin.py Spiderimage [outfile]") + sys.exit() + + filename = sys.argv[1] + if not isSpiderImage(filename): + print("input image must be in Spider format") + sys.exit() + + outfile = "" + if len(sys.argv[1:]) > 1: + outfile = sys.argv[2] + + im = Image.open(filename) + print("image: " + str(im)) + print("format: " + str(im.format)) + print("size: " + str(im.size)) + print("mode: " + str(im.mode)) + print("max, min: ", end=' ') + print(im.getextrema()) + + if outfile != "": + # perform some image operation + im = im.transpose(Image.FLIP_LEFT_RIGHT) + print( + "saving a flipped version of %s as %s " % + (os.path.basename(filename), outfile)) + im.save(outfile, "SPIDER") diff --git a/pyPackages/pillowx86/PIL/SunImagePlugin.py b/pyPackages/pillowx86/PIL/SunImagePlugin.py new file mode 100644 index 0000000..e0a7aa6 --- /dev/null +++ b/pyPackages/pillowx86/PIL/SunImagePlugin.py @@ -0,0 +1,83 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Sun image file handling +# +# History: +# 1995-09-10 fl Created +# 1996-05-28 fl Fixed 32-bit alignment +# 1998-12-29 fl Import ImagePalette module +# 2001-12-18 fl Fixed palette loading (from Jean-Claude Rimbault) +# +# Copyright (c) 1997-2001 by Secret Labs AB +# Copyright (c) 1995-1996 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.3" + + +from PIL import Image, ImageFile, ImagePalette, _binary + +i16 = _binary.i16be +i32 = _binary.i32be + + +def _accept(prefix): + return i32(prefix) == 0x59a66a95 + + +## +# Image plugin for Sun raster files. + +class SunImageFile(ImageFile.ImageFile): + + format = "SUN" + format_description = "Sun Raster File" + + def _open(self): + + # HEAD + s = self.fp.read(32) + if i32(s) != 0x59a66a95: + raise SyntaxError("not an SUN raster file") + + offset = 32 + + self.size = i32(s[4:8]), i32(s[8:12]) + + depth = i32(s[12:16]) + if depth == 1: + self.mode, rawmode = "1", "1;I" + elif depth == 8: + self.mode = rawmode = "L" + elif depth == 24: + self.mode, rawmode = "RGB", "BGR" + else: + raise SyntaxError("unsupported mode") + + compression = i32(s[20:24]) + + if i32(s[24:28]) != 0: + length = i32(s[28:32]) + offset = offset + length + self.palette = ImagePalette.raw("RGB;L", self.fp.read(length)) + if self.mode == "L": + self.mode = rawmode = "P" + + stride = (((self.size[0] * depth + 7) // 8) + 3) & (~3) + + if compression == 1: + self.tile = [("raw", (0, 0)+self.size, offset, (rawmode, stride))] + elif compression == 2: + self.tile = [("sun_rle", (0, 0)+self.size, offset, rawmode)] + +# +# registry + +Image.register_open("SUN", SunImageFile, _accept) + +Image.register_extension("SUN", ".ras") diff --git a/pyPackages/pillowx86/PIL/TarIO.py b/pyPackages/pillowx86/PIL/TarIO.py new file mode 100644 index 0000000..4e5115b --- /dev/null +++ b/pyPackages/pillowx86/PIL/TarIO.py @@ -0,0 +1,57 @@ +# +# The Python Imaging Library. +# $Id$ +# +# read files from within a tar file +# +# History: +# 95-06-18 fl Created +# 96-05-28 fl Open files in binary mode +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1995-96. +# +# See the README file for information on usage and redistribution. +# + +from PIL import ContainerIO + + +## +# A file object that provides read access to a given member of a TAR +# file. + +class TarIO(ContainerIO.ContainerIO): + + ## + # Create file object. + # + # @param tarfile Name of TAR file. + # @param file Name of member file. + + def __init__(self, tarfile, file): + + fh = open(tarfile, "rb") + + while True: + + s = fh.read(512) + if len(s) != 512: + raise IOError("unexpected end of tar file") + + name = s[:100].decode('utf-8') + i = name.find('\0') + if i == 0: + raise IOError("cannot find subfile") + if i > 0: + name = name[:i] + + size = int(s[124:135], 8) + + if file == name: + break + + fh.seek((size + 511) & (~511), 1) + + # Open region + ContainerIO.ContainerIO.__init__(self, fh, fh.tell(), size) diff --git a/pyPackages/pillowx86/PIL/TgaImagePlugin.py b/pyPackages/pillowx86/PIL/TgaImagePlugin.py new file mode 100644 index 0000000..46eafe8 --- /dev/null +++ b/pyPackages/pillowx86/PIL/TgaImagePlugin.py @@ -0,0 +1,199 @@ +# +# The Python Imaging Library. +# $Id$ +# +# TGA file handling +# +# History: +# 95-09-01 fl created (reads 24-bit files only) +# 97-01-04 fl support more TGA versions, including compressed images +# 98-07-04 fl fixed orientation and alpha layer bugs +# 98-09-11 fl fixed orientation for runlength decoder +# +# Copyright (c) Secret Labs AB 1997-98. +# Copyright (c) Fredrik Lundh 1995-97. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.3" + +from PIL import Image, ImageFile, ImagePalette, _binary + + +# +# -------------------------------------------------------------------- +# Read RGA file + +i8 = _binary.i8 +i16 = _binary.i16le +i32 = _binary.i32le + + +MODES = { + # map imagetype/depth to rawmode + (1, 8): "P", + (3, 1): "1", + (3, 8): "L", + (2, 16): "BGR;5", + (2, 24): "BGR", + (2, 32): "BGRA", +} + + +## +# Image plugin for Targa files. + +class TgaImageFile(ImageFile.ImageFile): + + format = "TGA" + format_description = "Targa" + + def _open(self): + + # process header + s = self.fp.read(18) + + idlen = i8(s[0]) + + colormaptype = i8(s[1]) + imagetype = i8(s[2]) + + depth = i8(s[16]) + + flags = i8(s[17]) + + self.size = i16(s[12:]), i16(s[14:]) + + # validate header fields + if colormaptype not in (0, 1) or\ + self.size[0] <= 0 or self.size[1] <= 0 or\ + depth not in (1, 8, 16, 24, 32): + raise SyntaxError("not a TGA file") + + # image mode + if imagetype in (3, 11): + self.mode = "L" + if depth == 1: + self.mode = "1" # ??? + elif imagetype in (1, 9): + self.mode = "P" + elif imagetype in (2, 10): + self.mode = "RGB" + if depth == 32: + self.mode = "RGBA" + else: + raise SyntaxError("unknown TGA mode") + + # orientation + orientation = flags & 0x30 + if orientation == 0x20: + orientation = 1 + elif not orientation: + orientation = -1 + else: + raise SyntaxError("unknown TGA orientation") + + self.info["orientation"] = orientation + + if imagetype & 8: + self.info["compression"] = "tga_rle" + + if idlen: + self.info["id_section"] = self.fp.read(idlen) + + if colormaptype: + # read palette + start, size, mapdepth = i16(s[3:]), i16(s[5:]), i16(s[7:]) + if mapdepth == 16: + self.palette = ImagePalette.raw( + "BGR;16", b"\0"*2*start + self.fp.read(2*size)) + elif mapdepth == 24: + self.palette = ImagePalette.raw( + "BGR", b"\0"*3*start + self.fp.read(3*size)) + elif mapdepth == 32: + self.palette = ImagePalette.raw( + "BGRA", b"\0"*4*start + self.fp.read(4*size)) + + # setup tile descriptor + try: + rawmode = MODES[(imagetype & 7, depth)] + if imagetype & 8: + # compressed + self.tile = [("tga_rle", (0, 0)+self.size, + self.fp.tell(), (rawmode, orientation, depth))] + else: + self.tile = [("raw", (0, 0)+self.size, + self.fp.tell(), (rawmode, 0, orientation))] + except KeyError: + pass # cannot decode + +# +# -------------------------------------------------------------------- +# Write TGA file + +o8 = _binary.o8 +o16 = _binary.o16le +o32 = _binary.o32le + +SAVE = { + "1": ("1", 1, 0, 3), + "L": ("L", 8, 0, 3), + "P": ("P", 8, 1, 1), + "RGB": ("BGR", 24, 0, 2), + "RGBA": ("BGRA", 32, 0, 2), +} + + +def _save(im, fp, filename, check=0): + + try: + rawmode, bits, colormaptype, imagetype = SAVE[im.mode] + except KeyError: + raise IOError("cannot write mode %s as TGA" % im.mode) + + if check: + return check + + if colormaptype: + colormapfirst, colormaplength, colormapentry = 0, 256, 24 + else: + colormapfirst, colormaplength, colormapentry = 0, 0, 0 + + if im.mode == "RGBA": + flags = 8 + else: + flags = 0 + + orientation = im.info.get("orientation", -1) + if orientation > 0: + flags = flags | 0x20 + + fp.write(b"\000" + + o8(colormaptype) + + o8(imagetype) + + o16(colormapfirst) + + o16(colormaplength) + + o8(colormapentry) + + o16(0) + + o16(0) + + o16(im.size[0]) + + o16(im.size[1]) + + o8(bits) + + o8(flags)) + + if colormaptype: + fp.write(im.im.getpalette("RGB", "BGR")) + + ImageFile._save( + im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, 0, orientation))]) + +# +# -------------------------------------------------------------------- +# Registry + +Image.register_open("TGA", TgaImageFile) +Image.register_save("TGA", _save) + +Image.register_extension("TGA", ".tga") diff --git a/pyPackages/pillowx86/PIL/TiffImagePlugin.py b/pyPackages/pillowx86/PIL/TiffImagePlugin.py new file mode 100644 index 0000000..a533c27 --- /dev/null +++ b/pyPackages/pillowx86/PIL/TiffImagePlugin.py @@ -0,0 +1,1225 @@ +# +# The Python Imaging Library. +# $Id$ +# +# TIFF file handling +# +# TIFF is a flexible, if somewhat aged, image file format originally +# defined by Aldus. Although TIFF supports a wide variety of pixel +# layouts and compression methods, the name doesn't really stand for +# "thousands of incompatible file formats," it just feels that way. +# +# To read TIFF data from a stream, the stream must be seekable. For +# progressive decoding, make sure to use TIFF files where the tag +# directory is placed first in the file. +# +# History: +# 1995-09-01 fl Created +# 1996-05-04 fl Handle JPEGTABLES tag +# 1996-05-18 fl Fixed COLORMAP support +# 1997-01-05 fl Fixed PREDICTOR support +# 1997-08-27 fl Added support for rational tags (from Perry Stoll) +# 1998-01-10 fl Fixed seek/tell (from Jan Blom) +# 1998-07-15 fl Use private names for internal variables +# 1999-06-13 fl Rewritten for PIL 1.0 (1.0) +# 2000-10-11 fl Additional fixes for Python 2.0 (1.1) +# 2001-04-17 fl Fixed rewind support (seek to frame 0) (1.2) +# 2001-05-12 fl Added write support for more tags (from Greg Couch) (1.3) +# 2001-12-18 fl Added workaround for broken Matrox library +# 2002-01-18 fl Don't mess up if photometric tag is missing (D. Alan Stewart) +# 2003-05-19 fl Check FILLORDER tag +# 2003-09-26 fl Added RGBa support +# 2004-02-24 fl Added DPI support; fixed rational write support +# 2005-02-07 fl Added workaround for broken Corel Draw 10 files +# 2006-01-09 fl Added support for float/double tags (from Russell Nelson) +# +# Copyright (c) 1997-2006 by Secret Labs AB. All rights reserved. +# Copyright (c) 1995-1997 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +from __future__ import print_function + +__version__ = "1.3.5" + +from PIL import Image, ImageFile +from PIL import ImagePalette +from PIL import _binary +from PIL._util import isStringType + +import warnings +import array +import sys +import collections +import itertools +import os +import io + +# Set these to true to force use of libtiff for reading or writing. +READ_LIBTIFF = False +WRITE_LIBTIFF = False + +II = b"II" # little-endian (Intel style) +MM = b"MM" # big-endian (Motorola style) + +i8 = _binary.i8 +o8 = _binary.o8 + +if sys.byteorder == "little": + native_prefix = II +else: + native_prefix = MM + +# +# -------------------------------------------------------------------- +# Read TIFF files + +il16 = _binary.i16le +il32 = _binary.i32le +ol16 = _binary.o16le +ol32 = _binary.o32le + +ib16 = _binary.i16be +ib32 = _binary.i32be +ob16 = _binary.o16be +ob32 = _binary.o32be + +# a few tag names, just to make the code below a bit more readable +IMAGEWIDTH = 256 +IMAGELENGTH = 257 +BITSPERSAMPLE = 258 +COMPRESSION = 259 +PHOTOMETRIC_INTERPRETATION = 262 +FILLORDER = 266 +IMAGEDESCRIPTION = 270 +STRIPOFFSETS = 273 +SAMPLESPERPIXEL = 277 +ROWSPERSTRIP = 278 +STRIPBYTECOUNTS = 279 +X_RESOLUTION = 282 +Y_RESOLUTION = 283 +PLANAR_CONFIGURATION = 284 +RESOLUTION_UNIT = 296 +SOFTWARE = 305 +DATE_TIME = 306 +ARTIST = 315 +PREDICTOR = 317 +COLORMAP = 320 +TILEOFFSETS = 324 +EXTRASAMPLES = 338 +SAMPLEFORMAT = 339 +JPEGTABLES = 347 +COPYRIGHT = 33432 +IPTC_NAA_CHUNK = 33723 # newsphoto properties +PHOTOSHOP_CHUNK = 34377 # photoshop properties +ICCPROFILE = 34675 +EXIFIFD = 34665 +XMP = 700 + +# https://github.com/fiji/ImageJA/blob/master/src/main/java/ij/io/TiffDecoder.java +IMAGEJ_META_DATA_BYTE_COUNTS = 50838 +IMAGEJ_META_DATA = 50839 + +COMPRESSION_INFO = { + # Compression => pil compression name + 1: "raw", + 2: "tiff_ccitt", + 3: "group3", + 4: "group4", + 5: "tiff_lzw", + 6: "tiff_jpeg", # obsolete + 7: "jpeg", + 8: "tiff_adobe_deflate", + 32771: "tiff_raw_16", # 16-bit padding + 32773: "packbits", + 32809: "tiff_thunderscan", + 32946: "tiff_deflate", + 34676: "tiff_sgilog", + 34677: "tiff_sgilog24", +} + +COMPRESSION_INFO_REV = dict([(v, k) for (k, v) in COMPRESSION_INFO.items()]) + +OPEN_INFO = { + # (ByteOrder, PhotoInterpretation, SampleFormat, FillOrder, BitsPerSample, + # ExtraSamples) => mode, rawmode + (II, 0, 1, 1, (1,), ()): ("1", "1;I"), + (II, 0, 1, 2, (1,), ()): ("1", "1;IR"), + (II, 0, 1, 1, (8,), ()): ("L", "L;I"), + (II, 0, 1, 2, (8,), ()): ("L", "L;IR"), + (II, 0, 3, 1, (32,), ()): ("F", "F;32F"), + (II, 1, 1, 1, (1,), ()): ("1", "1"), + (II, 1, 1, 1, (4,), ()): ("L", "L;4"), + (II, 1, 1, 2, (1,), ()): ("1", "1;R"), + (II, 1, 1, 1, (8,), ()): ("L", "L"), + (II, 1, 1, 1, (8, 8), (2,)): ("LA", "LA"), + (II, 1, 1, 2, (8,), ()): ("L", "L;R"), + (II, 1, 1, 1, (12,), ()): ("I;16", "I;12"), + (II, 1, 1, 1, (16,), ()): ("I;16", "I;16"), + (II, 1, 2, 1, (16,), ()): ("I;16S", "I;16S"), + (II, 1, 1, 1, (32,), ()): ("I", "I;32N"), + (II, 1, 2, 1, (32,), ()): ("I", "I;32S"), + (II, 1, 3, 1, (32,), ()): ("F", "F;32F"), + (II, 2, 1, 1, (8, 8, 8), ()): ("RGB", "RGB"), + (II, 2, 1, 2, (8, 8, 8), ()): ("RGB", "RGB;R"), + (II, 2, 1, 1, (8, 8, 8, 8), ()): ("RGBA", "RGBA"), # missing ExtraSamples + (II, 2, 1, 1, (8, 8, 8, 8), (0,)): ("RGBX", "RGBX"), + (II, 2, 1, 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"), + (II, 2, 1, 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"), + (II, 2, 1, 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10 + (II, 3, 1, 1, (1,), ()): ("P", "P;1"), + (II, 3, 1, 2, (1,), ()): ("P", "P;1R"), + (II, 3, 1, 1, (2,), ()): ("P", "P;2"), + (II, 3, 1, 2, (2,), ()): ("P", "P;2R"), + (II, 3, 1, 1, (4,), ()): ("P", "P;4"), + (II, 3, 1, 2, (4,), ()): ("P", "P;4R"), + (II, 3, 1, 1, (8,), ()): ("P", "P"), + (II, 3, 1, 1, (8, 8), (2,)): ("PA", "PA"), + (II, 3, 1, 2, (8,), ()): ("P", "P;R"), + (II, 5, 1, 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"), + (II, 6, 1, 1, (8, 8, 8), ()): ("YCbCr", "YCbCr"), + (II, 8, 1, 1, (8, 8, 8), ()): ("LAB", "LAB"), + + (MM, 0, 1, 1, (1,), ()): ("1", "1;I"), + (MM, 0, 1, 2, (1,), ()): ("1", "1;IR"), + (MM, 0, 1, 1, (8,), ()): ("L", "L;I"), + (MM, 0, 1, 2, (8,), ()): ("L", "L;IR"), + (MM, 1, 1, 1, (1,), ()): ("1", "1"), + (MM, 1, 1, 2, (1,), ()): ("1", "1;R"), + (MM, 1, 1, 1, (8,), ()): ("L", "L"), + (MM, 1, 1, 1, (8, 8), (2,)): ("LA", "LA"), + (MM, 1, 1, 2, (8,), ()): ("L", "L;R"), + (MM, 1, 1, 1, (16,), ()): ("I;16B", "I;16B"), + (MM, 1, 2, 1, (16,), ()): ("I;16BS", "I;16BS"), + (MM, 1, 2, 1, (32,), ()): ("I;32BS", "I;32BS"), + (MM, 1, 3, 1, (32,), ()): ("F", "F;32BF"), + (MM, 2, 1, 1, (8, 8, 8), ()): ("RGB", "RGB"), + (MM, 2, 1, 2, (8, 8, 8), ()): ("RGB", "RGB;R"), + (MM, 2, 1, 1, (8, 8, 8, 8), (0,)): ("RGBX", "RGBX"), + (MM, 2, 1, 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"), + (MM, 2, 1, 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"), + (MM, 2, 1, 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10 + (MM, 3, 1, 1, (1,), ()): ("P", "P;1"), + (MM, 3, 1, 2, (1,), ()): ("P", "P;1R"), + (MM, 3, 1, 1, (2,), ()): ("P", "P;2"), + (MM, 3, 1, 2, (2,), ()): ("P", "P;2R"), + (MM, 3, 1, 1, (4,), ()): ("P", "P;4"), + (MM, 3, 1, 2, (4,), ()): ("P", "P;4R"), + (MM, 3, 1, 1, (8,), ()): ("P", "P"), + (MM, 3, 1, 1, (8, 8), (2,)): ("PA", "PA"), + (MM, 3, 1, 2, (8,), ()): ("P", "P;R"), + (MM, 5, 1, 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"), + (MM, 6, 1, 1, (8, 8, 8), ()): ("YCbCr", "YCbCr"), + (MM, 8, 1, 1, (8, 8, 8), ()): ("LAB", "LAB"), + +} + +PREFIXES = [b"MM\000\052", b"II\052\000", b"II\xBC\000"] + + +def _accept(prefix): + return prefix[:4] in PREFIXES + + +## +# Wrapper for TIFF IFDs. + +class ImageFileDirectory(collections.MutableMapping): + """ This class represents a TIFF tag directory. To speed things + up, we don't decode tags unless they're asked for. + + Exposes a dictionary interface of the tags in the directory + ImageFileDirectory[key] = value + value = ImageFileDirectory[key] + + Also contains a dictionary of tag types as read from the tiff + image file, 'ImageFileDirectory.tagtype' + + + Data Structures: + 'public' + * self.tagtype = {} Key: numerical tiff tag number + Value: integer corresponding to the data type from + `TiffTags.TYPES` + + 'internal' + * self.tags = {} Key: numerical tiff tag number + Value: Decoded data, Generally a tuple. + * If set from __setval__ -- always a tuple + * Numeric types -- always a tuple + * String type -- not a tuple, returned as string + * Undefined data -- not a tuple, returned as bytes + * Byte -- not a tuple, returned as byte. + * self.tagdata = {} Key: numerical tiff tag number + Value: undecoded byte string from file + + + Tags will be found in either self.tags or self.tagdata, but + not both. The union of the two should contain all the tags + from the Tiff image file. External classes shouldn't + reference these unless they're really sure what they're doing. + """ + + def __init__(self, prefix=II): + """ + :prefix: 'II'|'MM' tiff endianness + """ + self.prefix = prefix[:2] + if self.prefix == MM: + self.i16, self.i32 = ib16, ib32 + self.o16, self.o32 = ob16, ob32 + elif self.prefix == II: + self.i16, self.i32 = il16, il32 + self.o16, self.o32 = ol16, ol32 + else: + raise SyntaxError("not a TIFF IFD") + self.reset() + + def reset(self): + #: Tags is an incomplete dictionary of the tags of the image. + #: For a complete dictionary, use the as_dict method. + self.tags = {} + self.tagdata = {} + self.tagtype = {} # added 2008-06-05 by Florian Hoech + self.next = None + self.offset = None + + def __str__(self): + return str(self.as_dict()) + + def as_dict(self): + """Return a dictionary of the image's tags.""" + return dict(self.items()) + + def named(self): + """ + Returns the complete tag dictionary, with named tags where posible. + """ + from PIL import TiffTags + result = {} + for tag_code, value in self.items(): + tag_name = TiffTags.TAGS.get(tag_code, tag_code) + result[tag_name] = value + return result + + # dictionary API + + def __len__(self): + return len(self.tagdata) + len(self.tags) + + def __getitem__(self, tag): + try: + return self.tags[tag] + except KeyError: + data = self.tagdata[tag] # unpack on the fly + type = self.tagtype[tag] + size, handler = self.load_dispatch[type] + self.tags[tag] = data = handler(self, data) + del self.tagdata[tag] + return data + + def getscalar(self, tag, default=None): + try: + value = self[tag] + if len(value) != 1: + if tag == SAMPLEFORMAT: + # work around broken (?) matrox library + # (from Ted Wright, via Bob Klimek) + raise KeyError # use default + raise ValueError("not a scalar") + return value[0] + except KeyError: + if default is None: + raise + return default + + def __contains__(self, tag): + return tag in self.tags or tag in self.tagdata + + if bytes is str: + def has_key(self, tag): + return tag in self + + def __setitem__(self, tag, value): + # tags are tuples for integers + # tags are not tuples for byte, string, and undefined data. + # see load_* + if not isinstance(value, tuple): + value = (value,) + self.tags[tag] = value + + def __delitem__(self, tag): + self.tags.pop(tag, self.tagdata.pop(tag, None)) + + def __iter__(self): + return itertools.chain(self.tags.__iter__(), self.tagdata.__iter__()) + + def items(self): + keys = list(self.__iter__()) + values = [self[key] for key in keys] + return zip(keys, values) + + # load primitives + + load_dispatch = {} + + def load_byte(self, data): + return data + load_dispatch[1] = (1, load_byte) + + def load_string(self, data): + if data[-1:] == b'\0': + data = data[:-1] + return data.decode('latin-1', 'replace') + load_dispatch[2] = (1, load_string) + + def load_short(self, data): + l = [] + for i in range(0, len(data), 2): + l.append(self.i16(data, i)) + return tuple(l) + load_dispatch[3] = (2, load_short) + + def load_long(self, data): + l = [] + for i in range(0, len(data), 4): + l.append(self.i32(data, i)) + return tuple(l) + load_dispatch[4] = (4, load_long) + + def load_rational(self, data): + l = [] + for i in range(0, len(data), 8): + l.append((self.i32(data, i), self.i32(data, i+4))) + return tuple(l) + load_dispatch[5] = (8, load_rational) + + def load_float(self, data): + a = array.array("f", data) + if self.prefix != native_prefix: + a.byteswap() + return tuple(a) + load_dispatch[11] = (4, load_float) + + def load_double(self, data): + a = array.array("d", data) + if self.prefix != native_prefix: + a.byteswap() + return tuple(a) + load_dispatch[12] = (8, load_double) + + def load_undefined(self, data): + # Untyped data + return data + load_dispatch[7] = (1, load_undefined) + + def load(self, fp): + # load tag dictionary + + self.reset() + self.offset = fp.tell() + + i16 = self.i16 + i32 = self.i32 + + for i in range(i16(fp.read(2))): + + ifd = fp.read(12) + + tag, typ = i16(ifd), i16(ifd, 2) + + if Image.DEBUG: + from PIL import TiffTags + tagname = TiffTags.TAGS.get(tag, "unknown") + typname = TiffTags.TYPES.get(typ, "unknown") + print("tag: %s (%d)" % (tagname, tag), end=' ') + print("- type: %s (%d)" % (typname, typ), end=' ') + + try: + dispatch = self.load_dispatch[typ] + except KeyError: + if Image.DEBUG: + print("- unsupported type", typ) + continue # ignore unsupported type + + size, handler = dispatch + + size = size * i32(ifd, 4) + + # Get and expand tag value + if size > 4: + here = fp.tell() + if Image.DEBUG: + print("Tag Location: %s" % here) + fp.seek(i32(ifd, 8)) + if Image.DEBUG: + print("Data Location: %s" % fp.tell()) + data = ImageFile._safe_read(fp, size) + fp.seek(here) + else: + data = ifd[8:8+size] + + if len(data) != size: + warnings.warn("Possibly corrupt EXIF data. " + "Expecting to read %d bytes but only got %d. " + "Skipping tag %s" % (size, len(data), tag)) + continue + + self.tagdata[tag] = data + self.tagtype[tag] = typ + + if Image.DEBUG: + if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, + ICCPROFILE, XMP): + print("- value: " % size) + else: + print("- value:", self[tag]) + + self.next = i32(fp.read(4)) + + # save primitives + + def save(self, fp): + + o16 = self.o16 + o32 = self.o32 + + fp.write(o16(len(self.tags))) + + # always write in ascending tag order + tags = sorted(self.tags.items()) + + directory = [] + append = directory.append + + offset = fp.tell() + len(self.tags) * 12 + 4 + + stripoffsets = None + + # pass 1: convert tags to binary format + for tag, value in tags: + + typ = None + + if tag in self.tagtype: + typ = self.tagtype[tag] + + if Image.DEBUG: + print ("Tag %s, Type: %s, Value: %s" % (tag, typ, value)) + + if typ == 1: + # byte data + if isinstance(value, tuple): + data = value = value[-1] + else: + data = value + elif typ == 7: + # untyped data + data = value = b"".join(value) + elif isStringType(value[0]): + # string data + if isinstance(value, tuple): + value = value[-1] + typ = 2 + # was b'\0'.join(str), which led to \x00a\x00b sorts + # of strings which I don't see in in the wild tiffs + # and doesn't match the tiff spec: 8-bit byte that + # contains a 7-bit ASCII code; the last byte must be + # NUL (binary zero). Also, I don't think this was well + # excersized before. + data = value = b"" + value.encode('ascii', 'replace') + b"\0" + else: + # integer data + if tag == STRIPOFFSETS: + stripoffsets = len(directory) + typ = 4 # to avoid catch-22 + elif tag in (X_RESOLUTION, Y_RESOLUTION) or typ == 5: + # identify rational data fields + typ = 5 + if isinstance(value[0], tuple): + # long name for flatten + value = tuple(itertools.chain.from_iterable(value)) + elif not typ: + typ = 3 + for v in value: + if v >= 65536: + typ = 4 + if typ == 3: + data = b"".join(map(o16, value)) + else: + data = b"".join(map(o32, value)) + + if Image.DEBUG: + from PIL import TiffTags + tagname = TiffTags.TAGS.get(tag, "unknown") + typname = TiffTags.TYPES.get(typ, "unknown") + print("save: %s (%d)" % (tagname, tag), end=' ') + print("- type: %s (%d)" % (typname, typ), end=' ') + if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, + ICCPROFILE, XMP): + size = len(data) + print("- value: " % size) + else: + print("- value:", value) + + # figure out if data fits into the directory + if len(data) == 4: + append((tag, typ, len(value), data, b"")) + elif len(data) < 4: + append((tag, typ, len(value), data + (4-len(data))*b"\0", b"")) + else: + count = len(value) + if typ == 5: + count = count // 2 # adjust for rational data field + + append((tag, typ, count, o32(offset), data)) + offset += len(data) + if offset & 1: + offset += 1 # word padding + + # update strip offset data to point beyond auxiliary data + if stripoffsets is not None: + tag, typ, count, value, data = directory[stripoffsets] + assert not data, "multistrip support not yet implemented" + value = o32(self.i32(value) + offset) + directory[stripoffsets] = tag, typ, count, value, data + + # pass 2: write directory to file + for tag, typ, count, value, data in directory: + if Image.DEBUG > 1: + print(tag, typ, count, repr(value), repr(data)) + fp.write(o16(tag) + o16(typ) + o32(count) + value) + + # -- overwrite here for multi-page -- + fp.write(b"\0\0\0\0") # end of directory + + # pass 3: write auxiliary data to file + for tag, typ, count, value, data in directory: + fp.write(data) + if len(data) & 1: + fp.write(b"\0") + + return offset + + +## +# Image plugin for TIFF files. + +class TiffImageFile(ImageFile.ImageFile): + + format = "TIFF" + format_description = "Adobe TIFF" + + def _open(self): + "Open the first image in a TIFF file" + + # Header + ifh = self.fp.read(8) + + if ifh[:4] not in PREFIXES: + raise SyntaxError("not a TIFF file") + + # image file directory (tag dictionary) + self.tag = self.ifd = ImageFileDirectory(ifh[:2]) + + # setup frame pointers + self.__first = self.__next = self.ifd.i32(ifh, 4) + self.__frame = -1 + self.__fp = self.fp + + if Image.DEBUG: + print ("*** TiffImageFile._open ***") + print ("- __first:", self.__first) + print ("- ifh: ", ifh) + + # and load the first frame + self._seek(0) + + def seek(self, frame): + "Select a given frame as current image" + if frame < 0: + frame = 0 + self._seek(frame) + # Create a new core image object on second and + # subsequent frames in the image. Image may be + # different size/mode. + Image._decompression_bomb_check(self.size) + self.im = Image.core.new(self.mode, self.size) + + def tell(self): + "Return the current frame number" + return self._tell() + + def _seek(self, frame): + self.fp = self.__fp + if frame < self.__frame: + # rewind file + self.__frame = -1 + self.__next = self.__first + while self.__frame < frame: + if not self.__next: + raise EOFError("no more images in TIFF file") + if Image.DEBUG: + print("Seeking to frame %s, on frame %s, __next %s, location: %s" % + (frame, self.__frame, self.__next, self.fp.tell())) + # reset python3 buffered io handle in case fp + # was passed to libtiff, invalidating the buffer + self.fp.tell() + self.fp.seek(self.__next) + if Image.DEBUG: + print("Loading tags, location: %s" % self.fp.tell()) + self.tag.load(self.fp) + self.__next = self.tag.next + self.__frame += 1 + self._setup() + + def _tell(self): + return self.__frame + + def _decoder(self, rawmode, layer, tile=None): + "Setup decoder contexts" + + args = None + if rawmode == "RGB" and self._planar_configuration == 2: + rawmode = rawmode[layer] + compression = self._compression + if compression == "raw": + args = (rawmode, 0, 1) + elif compression == "jpeg": + args = rawmode, "" + if JPEGTABLES in self.tag: + # Hack to handle abbreviated JPEG headers + self.tile_prefix = self.tag[JPEGTABLES] + elif compression == "packbits": + args = rawmode + elif compression == "tiff_lzw": + args = rawmode + if 317 in self.tag: + # Section 14: Differencing Predictor + self.decoderconfig = (self.tag[PREDICTOR][0],) + + if ICCPROFILE in self.tag: + self.info['icc_profile'] = self.tag[ICCPROFILE] + + return args + + def _load_libtiff(self): + """ Overload method triggered when we detect a compressed tiff + Calls out to libtiff """ + + pixel = Image.Image.load(self) + + if self.tile is None: + raise IOError("cannot load this image") + if not self.tile: + return pixel + + self.load_prepare() + + if not len(self.tile) == 1: + raise IOError("Not exactly one tile") + + # (self._compression, (extents tuple), + # 0, (rawmode, self._compression, fp)) + ignored, extents, ignored_2, args = self.tile[0] + args = args + (self.ifd.offset,) + decoder = Image._getdecoder(self.mode, 'libtiff', args, + self.decoderconfig) + try: + decoder.setimage(self.im, extents) + except ValueError: + raise IOError("Couldn't set the image") + + if hasattr(self.fp, "getvalue"): + # We've got a stringio like thing passed in. Yay for all in memory. + # The decoder needs the entire file in one shot, so there's not + # a lot we can do here other than give it the entire file. + # unless we could do something like get the address of the + # underlying string for stringio. + # + # Rearranging for supporting byteio items, since they have a fileno + # that returns an IOError if there's no underlying fp. Easier to + # dea. with here by reordering. + if Image.DEBUG: + print ("have getvalue. just sending in a string from getvalue") + n, err = decoder.decode(self.fp.getvalue()) + elif hasattr(self.fp, "fileno"): + # we've got a actual file on disk, pass in the fp. + if Image.DEBUG: + print ("have fileno, calling fileno version of the decoder.") + self.fp.seek(0) + # 4 bytes, otherwise the trace might error out + n, err = decoder.decode(b"fpfp") + else: + # we have something else. + if Image.DEBUG: + print ("don't have fileno or getvalue. just reading") + # UNDONE -- so much for that buffer size thing. + n, err = decoder.decode(self.fp.read()) + + self.tile = [] + self.readonly = 0 + # libtiff closed the fp in a, we need to close self.fp, if possible + if hasattr(self.fp, 'close'): + if not self.__next: + self.fp.close() + self.fp = None # might be shared + + if err < 0: + raise IOError(err) + + self.load_end() + + return Image.Image.load(self) + + def _setup(self): + "Setup this image object based on current tags" + + if 0xBC01 in self.tag: + raise IOError("Windows Media Photo files not yet supported") + + getscalar = self.tag.getscalar + + # extract relevant tags + self._compression = COMPRESSION_INFO[getscalar(COMPRESSION, 1)] + self._planar_configuration = getscalar(PLANAR_CONFIGURATION, 1) + + # photometric is a required tag, but not everyone is reading + # the specification + photo = getscalar(PHOTOMETRIC_INTERPRETATION, 0) + + fillorder = getscalar(FILLORDER, 1) + + if Image.DEBUG: + print("*** Summary ***") + print("- compression:", self._compression) + print("- photometric_interpretation:", photo) + print("- planar_configuration:", self._planar_configuration) + print("- fill_order:", fillorder) + + # size + xsize = getscalar(IMAGEWIDTH) + ysize = getscalar(IMAGELENGTH) + self.size = xsize, ysize + + if Image.DEBUG: + print("- size:", self.size) + + format = getscalar(SAMPLEFORMAT, 1) + + # mode: check photometric interpretation and bits per pixel + key = ( + self.tag.prefix, photo, format, fillorder, + self.tag.get(BITSPERSAMPLE, (1,)), + self.tag.get(EXTRASAMPLES, ()) + ) + if Image.DEBUG: + print("format key:", key) + try: + self.mode, rawmode = OPEN_INFO[key] + except KeyError: + if Image.DEBUG: + print("- unsupported format") + raise SyntaxError("unknown pixel mode") + + if Image.DEBUG: + print("- raw mode:", rawmode) + print("- pil mode:", self.mode) + + self.info["compression"] = self._compression + + xres = getscalar(X_RESOLUTION, (1, 1)) + yres = getscalar(Y_RESOLUTION, (1, 1)) + + if xres and not isinstance(xres, tuple): + xres = (xres, 1.) + if yres and not isinstance(yres, tuple): + yres = (yres, 1.) + if xres and yres: + xres = xres[0] / (xres[1] or 1) + yres = yres[0] / (yres[1] or 1) + resunit = getscalar(RESOLUTION_UNIT, 1) + if resunit == 2: # dots per inch + self.info["dpi"] = xres, yres + elif resunit == 3: # dots per centimeter. convert to dpi + self.info["dpi"] = xres * 2.54, yres * 2.54 + else: # No absolute unit of measurement + self.info["resolution"] = xres, yres + + # build tile descriptors + x = y = l = 0 + self.tile = [] + if STRIPOFFSETS in self.tag: + # striped image + offsets = self.tag[STRIPOFFSETS] + h = getscalar(ROWSPERSTRIP, ysize) + w = self.size[0] + if READ_LIBTIFF or self._compression in ["tiff_ccitt", "group3", + "group4", "tiff_jpeg", + "tiff_adobe_deflate", + "tiff_thunderscan", + "tiff_deflate", + "tiff_sgilog", + "tiff_sgilog24", + "tiff_raw_16"]: + # if Image.DEBUG: + # print "Activating g4 compression for whole file" + + # Decoder expects entire file as one tile. + # There's a buffer size limit in load (64k) + # so large g4 images will fail if we use that + # function. + # + # Setup the one tile for the whole image, then + # replace the existing load function with our + # _load_libtiff function. + + self.load = self._load_libtiff + + # To be nice on memory footprint, if there's a + # file descriptor, use that instead of reading + # into a string in python. + + # libtiff closes the file descriptor, so pass in a dup. + try: + fp = hasattr(self.fp, "fileno") and \ + os.dup(self.fp.fileno()) + # flush the file descriptor, prevents error on pypy 2.4+ + # should also eliminate the need for fp.tell for py3 + # in _seek + self.fp.flush() + except IOError: + # io.BytesIO have a fileno, but returns an IOError if + # it doesn't use a file descriptor. + fp = False + + # libtiff handles the fillmode for us, so 1;IR should + # actually be 1;I. Including the R double reverses the + # bits, so stripes of the image are reversed. See + # https://github.com/python-pillow/Pillow/issues/279 + if fillorder == 2: + key = ( + self.tag.prefix, photo, format, 1, + self.tag.get(BITSPERSAMPLE, (1,)), + self.tag.get(EXTRASAMPLES, ()) + ) + if Image.DEBUG: + print("format key:", key) + # this should always work, since all the + # fillorder==2 modes have a corresponding + # fillorder=1 mode + self.mode, rawmode = OPEN_INFO[key] + # libtiff always returns the bytes in native order. + # we're expecting image byte order. So, if the rawmode + # contains I;16, we need to convert from native to image + # byte order. + if self.mode in ('I;16B', 'I;16') and 'I;16' in rawmode: + rawmode = 'I;16N' + + # Offset in the tile tuple is 0, we go from 0,0 to + # w,h, and we only do this once -- eds + a = (rawmode, self._compression, fp) + self.tile.append( + (self._compression, + (0, 0, w, ysize), + 0, a)) + a = None + + else: + for i in range(len(offsets)): + a = self._decoder(rawmode, l, i) + self.tile.append( + (self._compression, + (0, min(y, ysize), w, min(y+h, ysize)), + offsets[i], a)) + if Image.DEBUG: + print ("tiles: ", self.tile) + y = y + h + if y >= self.size[1]: + x = y = 0 + l += 1 + a = None + elif TILEOFFSETS in self.tag: + # tiled image + w = getscalar(322) + h = getscalar(323) + a = None + for o in self.tag[TILEOFFSETS]: + if not a: + a = self._decoder(rawmode, l) + # FIXME: this doesn't work if the image size + # is not a multiple of the tile size... + self.tile.append( + (self._compression, + (x, y, x+w, y+h), + o, a)) + x = x + w + if x >= self.size[0]: + x, y = 0, y + h + if y >= self.size[1]: + x = y = 0 + l += 1 + a = None + else: + if Image.DEBUG: + print("- unsupported data organization") + raise SyntaxError("unknown data organization") + + # fixup palette descriptor + + if self.mode == "P": + palette = [o8(a // 256) for a in self.tag[COLORMAP]] + self.palette = ImagePalette.raw("RGB;L", b"".join(palette)) +# +# -------------------------------------------------------------------- +# Write TIFF files + +# little endian is default except for image modes with +# explict big endian byte-order + +SAVE_INFO = { + # mode => rawmode, byteorder, photometrics, + # sampleformat, bitspersample, extra + "1": ("1", II, 1, 1, (1,), None), + "L": ("L", II, 1, 1, (8,), None), + "LA": ("LA", II, 1, 1, (8, 8), 2), + "P": ("P", II, 3, 1, (8,), None), + "PA": ("PA", II, 3, 1, (8, 8), 2), + "I": ("I;32S", II, 1, 2, (32,), None), + "I;16": ("I;16", II, 1, 1, (16,), None), + "I;16S": ("I;16S", II, 1, 2, (16,), None), + "F": ("F;32F", II, 1, 3, (32,), None), + "RGB": ("RGB", II, 2, 1, (8, 8, 8), None), + "RGBX": ("RGBX", II, 2, 1, (8, 8, 8, 8), 0), + "RGBA": ("RGBA", II, 2, 1, (8, 8, 8, 8), 2), + "CMYK": ("CMYK", II, 5, 1, (8, 8, 8, 8), None), + "YCbCr": ("YCbCr", II, 6, 1, (8, 8, 8), None), + "LAB": ("LAB", II, 8, 1, (8, 8, 8), None), + + "I;32BS": ("I;32BS", MM, 1, 2, (32,), None), + "I;16B": ("I;16B", MM, 1, 1, (16,), None), + "I;16BS": ("I;16BS", MM, 1, 2, (16,), None), + "F;32BF": ("F;32BF", MM, 1, 3, (32,), None), +} + + +def _cvt_res(value): + # convert value to TIFF rational number -- (numerator, denominator) + if isinstance(value, collections.Sequence): + assert(len(value) % 2 == 0) + return value + if isinstance(value, int): + return (value, 1) + value = float(value) + return (int(value * 65536), 65536) + + +def _save(im, fp, filename): + + try: + rawmode, prefix, photo, format, bits, extra = SAVE_INFO[im.mode] + except KeyError: + raise IOError("cannot write mode %s as TIFF" % im.mode) + + ifd = ImageFileDirectory(prefix) + + compression = im.encoderinfo.get('compression', im.info.get('compression', + 'raw')) + + libtiff = WRITE_LIBTIFF or compression != 'raw' + + # required for color libtiff images + ifd[PLANAR_CONFIGURATION] = getattr(im, '_planar_configuration', 1) + + # -- multi-page -- skip TIFF header on subsequent pages + if not libtiff and fp.tell() == 0: + # tiff header (write via IFD to get everything right) + # PIL always starts the first IFD at offset 8 + fp.write(ifd.prefix + ifd.o16(42) + ifd.o32(8)) + + ifd[IMAGEWIDTH] = im.size[0] + ifd[IMAGELENGTH] = im.size[1] + + # write any arbitrary tags passed in as an ImageFileDirectory + info = im.encoderinfo.get("tiffinfo", {}) + if Image.DEBUG: + print("Tiffinfo Keys: %s" % info.keys) + keys = list(info.keys()) + for key in keys: + ifd[key] = info.get(key) + try: + ifd.tagtype[key] = info.tagtype[key] + except: + pass # might not be an IFD, Might not have populated type + + # additions written by Greg Couch, gregc@cgl.ucsf.edu + # inspired by image-sig posting from Kevin Cazabon, kcazabon@home.com + if hasattr(im, 'tag'): + # preserve tags from original TIFF image file + for key in (RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION, + IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, XMP): + if key in im.tag: + ifd[key] = im.tag[key] + ifd.tagtype[key] = im.tag.tagtype.get(key, None) + + # preserve ICC profile (should also work when saving other formats + # which support profiles as TIFF) -- 2008-06-06 Florian Hoech + if "icc_profile" in im.info: + ifd[ICCPROFILE] = im.info["icc_profile"] + + for key, name, cvt in [ + (IMAGEDESCRIPTION, "description", lambda x: x), + (X_RESOLUTION, "resolution", _cvt_res), + (Y_RESOLUTION, "resolution", _cvt_res), + (X_RESOLUTION, "x_resolution", _cvt_res), + (Y_RESOLUTION, "y_resolution", _cvt_res), + (RESOLUTION_UNIT, "resolution_unit", + lambda x: {"inch": 2, "cm": 3, "centimeter": 3}.get(x, 1)), + (SOFTWARE, "software", lambda x: x), + (DATE_TIME, "date_time", lambda x: x), + (ARTIST, "artist", lambda x: x), + (COPYRIGHT, "copyright", lambda x: x)]: + name_with_spaces = name.replace("_", " ") + if "_" in name and name_with_spaces in im.encoderinfo: + warnings.warn("%r is deprecated; use %r instead" % + (name_with_spaces, name), DeprecationWarning) + ifd[key] = cvt(im.encoderinfo[name.replace("_", " ")]) + if name in im.encoderinfo: + ifd[key] = cvt(im.encoderinfo[name]) + + dpi = im.encoderinfo.get("dpi") + if dpi: + ifd[RESOLUTION_UNIT] = 2 + ifd[X_RESOLUTION] = _cvt_res(dpi[0]) + ifd[Y_RESOLUTION] = _cvt_res(dpi[1]) + + if bits != (1,): + ifd[BITSPERSAMPLE] = bits + if len(bits) != 1: + ifd[SAMPLESPERPIXEL] = len(bits) + if extra is not None: + ifd[EXTRASAMPLES] = extra + if format != 1: + ifd[SAMPLEFORMAT] = format + + ifd[PHOTOMETRIC_INTERPRETATION] = photo + + if im.mode == "P": + lut = im.im.getpalette("RGB", "RGB;L") + ifd[COLORMAP] = tuple(i8(v) * 256 for v in lut) + + # data orientation + stride = len(bits) * ((im.size[0]*bits[0]+7)//8) + ifd[ROWSPERSTRIP] = im.size[1] + ifd[STRIPBYTECOUNTS] = stride * im.size[1] + ifd[STRIPOFFSETS] = 0 # this is adjusted by IFD writer + # no compression by default: + ifd[COMPRESSION] = COMPRESSION_INFO_REV.get(compression, 1) + + if libtiff: + if Image.DEBUG: + print ("Saving using libtiff encoder") + print (ifd.items()) + _fp = 0 + if hasattr(fp, "fileno"): + try: + fp.seek(0) + _fp = os.dup(fp.fileno()) + except io.UnsupportedOperation: + pass + + # ICC Profile crashes. + blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS, ROWSPERSTRIP, ICCPROFILE] + atts = {} + # bits per sample is a single short in the tiff directory, not a list. + atts[BITSPERSAMPLE] = bits[0] + # Merge the ones that we have with (optional) more bits from + # the original file, e.g x,y resolution so that we can + # save(load('')) == original file. + for k, v in itertools.chain(ifd.items(), + getattr(im, 'ifd', {}).items()): + if k not in atts and k not in blocklist: + if type(v[0]) == tuple and len(v) > 1: + # A tuple of more than one rational tuples + # flatten to floats, + # following tiffcp.c->cpTag->TIFF_RATIONAL + atts[k] = [float(elt[0])/float(elt[1]) for elt in v] + continue + if type(v[0]) == tuple and len(v) == 1: + # A tuple of one rational tuples + # flatten to floats, + # following tiffcp.c->cpTag->TIFF_RATIONAL + atts[k] = float(v[0][0])/float(v[0][1]) + continue + if (type(v) == tuple and + (len(v) > 2 or + (len(v) == 2 and v[1] == 0))): + # List of ints? + # Avoid divide by zero in next if-clause + if type(v[0]) in (int, float): + atts[k] = list(v) + continue + if type(v) == tuple and len(v) == 2: + # one rational tuple + # flatten to float, + # following tiffcp.c->cpTag->TIFF_RATIONAL + atts[k] = float(v[0])/float(v[1]) + continue + if type(v) == tuple and len(v) == 1: + v = v[0] + # drop through + if isStringType(v): + atts[k] = bytes(v.encode('ascii', 'replace')) + b"\0" + continue + else: + # int or similar + atts[k] = v + + if Image.DEBUG: + print (atts) + + # libtiff always expects the bytes in native order. + # we're storing image byte order. So, if the rawmode + # contains I;16, we need to convert from native to image + # byte order. + if im.mode in ('I;16B', 'I;16'): + rawmode = 'I;16N' + + a = (rawmode, compression, _fp, filename, atts) + # print (im.mode, compression, a, im.encoderconfig) + e = Image._getencoder(im.mode, 'libtiff', a, im.encoderconfig) + e.setimage(im.im, (0, 0)+im.size) + while True: + # undone, change to self.decodermaxblock: + l, s, d = e.encode(16*1024) + if not _fp: + fp.write(d) + if s: + break + if s < 0: + raise IOError("encoder error %d when writing image file" % s) + + else: + offset = ifd.save(fp) + + ImageFile._save(im, fp, [ + ("raw", (0, 0)+im.size, offset, (rawmode, stride, 1)) + ]) + + # -- helper for multi-page save -- + if "_debug_multipage" in im.encoderinfo: + # just to access o32 and o16 (using correct byte order) + im._debug_multipage = ifd + +# +# -------------------------------------------------------------------- +# Register + +Image.register_open("TIFF", TiffImageFile, _accept) +Image.register_save("TIFF", _save) + +Image.register_extension("TIFF", ".tif") +Image.register_extension("TIFF", ".tiff") + +Image.register_mime("TIFF", "image/tiff") diff --git a/pyPackages/pillowx86/PIL/TiffTags.py b/pyPackages/pillowx86/PIL/TiffTags.py new file mode 100644 index 0000000..d15aa7e --- /dev/null +++ b/pyPackages/pillowx86/PIL/TiffTags.py @@ -0,0 +1,307 @@ +# +# The Python Imaging Library. +# $Id$ +# +# TIFF tags +# +# This module provides clear-text names for various well-known +# TIFF tags. the TIFF codec works just fine without it. +# +# Copyright (c) Secret Labs AB 1999. +# +# See the README file for information on usage and redistribution. +# + +## +# This module provides constants and clear-text names for various +# well-known TIFF tags. +## + +## +# Map tag numbers (or tag number, tag value tuples) to tag names. + +TAGS = { + + 254: "NewSubfileType", + 255: "SubfileType", + 256: "ImageWidth", + 257: "ImageLength", + 258: "BitsPerSample", + + 259: "Compression", + (259, 1): "Uncompressed", + (259, 2): "CCITT 1d", + (259, 3): "Group 3 Fax", + (259, 4): "Group 4 Fax", + (259, 5): "LZW", + (259, 6): "JPEG", + (259, 32773): "PackBits", + + 262: "PhotometricInterpretation", + (262, 0): "WhiteIsZero", + (262, 1): "BlackIsZero", + (262, 2): "RGB", + (262, 3): "RGB Palette", + (262, 4): "Transparency Mask", + (262, 5): "CMYK", + (262, 6): "YCbCr", + (262, 8): "CieLAB", + (262, 32803): "CFA", # TIFF/EP, Adobe DNG + (262, 32892): "LinearRaw", # Adobe DNG + + 263: "Thresholding", + 264: "CellWidth", + 265: "CellHeight", + 266: "FillOrder", + 269: "DocumentName", + + 270: "ImageDescription", + 271: "Make", + 272: "Model", + 273: "StripOffsets", + 274: "Orientation", + 277: "SamplesPerPixel", + 278: "RowsPerStrip", + 279: "StripByteCounts", + + 280: "MinSampleValue", + 281: "MaxSampleValue", + 282: "XResolution", + 283: "YResolution", + 284: "PlanarConfiguration", + (284, 1): "Contigous", + (284, 2): "Separate", + + 285: "PageName", + 286: "XPosition", + 287: "YPosition", + 288: "FreeOffsets", + 289: "FreeByteCounts", + + 290: "GrayResponseUnit", + 291: "GrayResponseCurve", + 292: "T4Options", + 293: "T6Options", + 296: "ResolutionUnit", + 297: "PageNumber", + + 301: "TransferFunction", + 305: "Software", + 306: "DateTime", + + 315: "Artist", + 316: "HostComputer", + 317: "Predictor", + 318: "WhitePoint", + 319: "PrimaryChromaticies", + + 320: "ColorMap", + 321: "HalftoneHints", + 322: "TileWidth", + 323: "TileLength", + 324: "TileOffsets", + 325: "TileByteCounts", + + 332: "InkSet", + 333: "InkNames", + 334: "NumberOfInks", + 336: "DotRange", + 337: "TargetPrinter", + 338: "ExtraSamples", + 339: "SampleFormat", + + 340: "SMinSampleValue", + 341: "SMaxSampleValue", + 342: "TransferRange", + + 347: "JPEGTables", + + # obsolete JPEG tags + 512: "JPEGProc", + 513: "JPEGInterchangeFormat", + 514: "JPEGInterchangeFormatLength", + 515: "JPEGRestartInterval", + 517: "JPEGLosslessPredictors", + 518: "JPEGPointTransforms", + 519: "JPEGQTables", + 520: "JPEGDCTables", + 521: "JPEGACTables", + + 529: "YCbCrCoefficients", + 530: "YCbCrSubSampling", + 531: "YCbCrPositioning", + 532: "ReferenceBlackWhite", + + # XMP + 700: "XMP", + + 33432: "Copyright", + + # various extensions (should check specs for "official" names) + 33723: "IptcNaaInfo", + 34377: "PhotoshopInfo", + + # Exif IFD + 34665: "ExifIFD", + + # ICC Profile + 34675: "ICCProfile", + + # Additional Exif Info + 33434: "ExposureTime", + 33437: "FNumber", + 34850: "ExposureProgram", + 34852: "SpectralSensitivity", + 34853: "GPSInfoIFD", + 34855: "ISOSpeedRatings", + 34856: "OECF", + 34864: "SensitivityType", + 34865: "StandardOutputSensitivity", + 34866: "RecommendedExposureIndex", + 34867: "ISOSpeed", + 34868: "ISOSpeedLatitudeyyy", + 34869: "ISOSpeedLatitudezzz", + 36864: "ExifVersion", + 36867: "DateTimeOriginal", + 36868: "DateTImeDigitized", + 37121: "ComponentsConfiguration", + 37122: "CompressedBitsPerPixel", + 37377: "ShutterSpeedValue", + 37378: "ApertureValue", + 37379: "BrightnessValue", + 37380: "ExposureBiasValue", + 37381: "MaxApertureValue", + 37382: "SubjectDistance", + 37383: "MeteringMode", + 37384: "LightSource", + 37385: "Flash", + 37386: "FocalLength", + 37396: "SubjectArea", + 37500: "MakerNote", + 37510: "UserComment", + 37520: "SubSec", + 37521: "SubSecTimeOriginal", + 37522: "SubsecTimeDigitized", + 40960: "FlashPixVersion", + 40961: "ColorSpace", + 40962: "PixelXDimension", + 40963: "PixelYDimension", + 40964: "RelatedSoundFile", + 40965: "InteroperabilityIFD", + 41483: "FlashEnergy", + 41484: "SpatialFrequencyResponse", + 41486: "FocalPlaneXResolution", + 41487: "FocalPlaneYResolution", + 41488: "FocalPlaneResolutionUnit", + 41492: "SubjectLocation", + 41493: "ExposureIndex", + 41495: "SensingMethod", + 41728: "FileSource", + 41729: "SceneType", + 41730: "CFAPattern", + 41985: "CustomRendered", + 41986: "ExposureMode", + 41987: "WhiteBalance", + 41988: "DigitalZoomRatio", + 41989: "FocalLengthIn35mmFilm", + 41990: "SceneCaptureType", + 41991: "GainControl", + 41992: "Contrast", + 41993: "Saturation", + 41994: "Sharpness", + 41995: "DeviceSettingDescription", + 41996: "SubjectDistanceRange", + 42016: "ImageUniqueID", + 42032: "CameraOwnerName", + 42033: "BodySerialNumber", + 42034: "LensSpecification", + 42035: "LensMake", + 42036: "LensModel", + 42037: "LensSerialNumber", + 42240: "Gamma", + + # MP Info + 45056: "MPFVersion", + 45057: "NumberOfImages", + 45058: "MPEntry", + 45059: "ImageUIDList", + 45060: "TotalFrames", + 45313: "MPIndividualNum", + 45569: "PanOrientation", + 45570: "PanOverlap_H", + 45571: "PanOverlap_V", + 45572: "BaseViewpointNum", + 45573: "ConvergenceAngle", + 45574: "BaselineLength", + 45575: "VerticalDivergence", + 45576: "AxisDistance_X", + 45577: "AxisDistance_Y", + 45578: "AxisDistance_Z", + 45579: "YawAngle", + 45580: "PitchAngle", + 45581: "RollAngle", + + # Adobe DNG + 50706: "DNGVersion", + 50707: "DNGBackwardVersion", + 50708: "UniqueCameraModel", + 50709: "LocalizedCameraModel", + 50710: "CFAPlaneColor", + 50711: "CFALayout", + 50712: "LinearizationTable", + 50713: "BlackLevelRepeatDim", + 50714: "BlackLevel", + 50715: "BlackLevelDeltaH", + 50716: "BlackLevelDeltaV", + 50717: "WhiteLevel", + 50718: "DefaultScale", + 50719: "DefaultCropOrigin", + 50720: "DefaultCropSize", + 50778: "CalibrationIlluminant1", + 50779: "CalibrationIlluminant2", + 50721: "ColorMatrix1", + 50722: "ColorMatrix2", + 50723: "CameraCalibration1", + 50724: "CameraCalibration2", + 50725: "ReductionMatrix1", + 50726: "ReductionMatrix2", + 50727: "AnalogBalance", + 50728: "AsShotNeutral", + 50729: "AsShotWhiteXY", + 50730: "BaselineExposure", + 50731: "BaselineNoise", + 50732: "BaselineSharpness", + 50733: "BayerGreenSplit", + 50734: "LinearResponseLimit", + 50735: "CameraSerialNumber", + 50736: "LensInfo", + 50737: "ChromaBlurRadius", + 50738: "AntiAliasStrength", + 50740: "DNGPrivateData", + 50741: "MakerNoteSafety", + 50780: "BestQualityScale", + + # ImageJ + 50838: "ImageJMetaDataByteCounts", # private tag registered with Adobe + 50839: "ImageJMetaData", # private tag registered with Adobe +} + +## +# Map type numbers to type names. + +TYPES = { + + 1: "byte", + 2: "ascii", + 3: "short", + 4: "long", + 5: "rational", + 6: "signed byte", + 7: "undefined", + 8: "signed short", + 9: "signed long", + 10: "signed rational", + 11: "float", + 12: "double", + +} diff --git a/pyPackages/pillowx86/PIL/WalImageFile.py b/pyPackages/pillowx86/PIL/WalImageFile.py new file mode 100644 index 0000000..fc2bb30 --- /dev/null +++ b/pyPackages/pillowx86/PIL/WalImageFile.py @@ -0,0 +1,131 @@ +# The Python Imaging Library. +# $Id$ +# +# WAL file handling +# +# History: +# 2003-04-23 fl created +# +# Copyright (c) 2003 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +# NOTE: This format cannot be automatically recognized, so the reader +# is not registered for use with Image.open(). To open a WAL file, use +# the WalImageFile.open() function instead. + +# This reader is based on the specification available from: +# http://www.flipcode.com/archives/Quake_2_BSP_File_Format.shtml +# and has been tested with a few sample files found using google. + +from __future__ import print_function + +from PIL import Image, _binary + +try: + import builtins +except ImportError: + import __builtin__ + builtins = __builtin__ + +i32 = _binary.i32le + + +## +# Load texture from a Quake2 WAL texture file. +#

+# By default, a Quake2 standard palette is attached to the texture. +# To override the palette, use the putpalette method. +# +# @param filename WAL file name, or an opened file handle. +# @return An image instance. + +def open(filename): + # FIXME: modify to return a WalImageFile instance instead of + # plain Image object ? + + if hasattr(filename, "read"): + fp = filename + else: + fp = builtins.open(filename, "rb") + + # read header fields + header = fp.read(32+24+32+12) + size = i32(header, 32), i32(header, 36) + offset = i32(header, 40) + + # load pixel data + fp.seek(offset) + + im = Image.frombytes("P", size, fp.read(size[0] * size[1])) + im.putpalette(quake2palette) + + im.format = "WAL" + im.format_description = "Quake2 Texture" + + # strings are null-terminated + im.info["name"] = header[:32].split(b"\0", 1)[0] + next_name = header[56:56+32].split(b"\0", 1)[0] + if next_name: + im.info["next_name"] = next_name + + return im + + +quake2palette = ( + # default palette taken from piffo 0.93 by Hans Häggström + b"\x01\x01\x01\x0b\x0b\x0b\x12\x12\x12\x17\x17\x17\x1b\x1b\x1b\x1e" + b"\x1e\x1e\x22\x22\x22\x26\x26\x26\x29\x29\x29\x2c\x2c\x2c\x2f\x2f" + b"\x2f\x32\x32\x32\x35\x35\x35\x37\x37\x37\x3a\x3a\x3a\x3c\x3c\x3c" + b"\x24\x1e\x13\x22\x1c\x12\x20\x1b\x12\x1f\x1a\x10\x1d\x19\x10\x1b" + b"\x17\x0f\x1a\x16\x0f\x18\x14\x0d\x17\x13\x0d\x16\x12\x0d\x14\x10" + b"\x0b\x13\x0f\x0b\x10\x0d\x0a\x0f\x0b\x0a\x0d\x0b\x07\x0b\x0a\x07" + b"\x23\x23\x26\x22\x22\x25\x22\x20\x23\x21\x1f\x22\x20\x1e\x20\x1f" + b"\x1d\x1e\x1d\x1b\x1c\x1b\x1a\x1a\x1a\x19\x19\x18\x17\x17\x17\x16" + b"\x16\x14\x14\x14\x13\x13\x13\x10\x10\x10\x0f\x0f\x0f\x0d\x0d\x0d" + b"\x2d\x28\x20\x29\x24\x1c\x27\x22\x1a\x25\x1f\x17\x38\x2e\x1e\x31" + b"\x29\x1a\x2c\x25\x17\x26\x20\x14\x3c\x30\x14\x37\x2c\x13\x33\x28" + b"\x12\x2d\x24\x10\x28\x1f\x0f\x22\x1a\x0b\x1b\x14\x0a\x13\x0f\x07" + b"\x31\x1a\x16\x30\x17\x13\x2e\x16\x10\x2c\x14\x0d\x2a\x12\x0b\x27" + b"\x0f\x0a\x25\x0f\x07\x21\x0d\x01\x1e\x0b\x01\x1c\x0b\x01\x1a\x0b" + b"\x01\x18\x0a\x01\x16\x0a\x01\x13\x0a\x01\x10\x07\x01\x0d\x07\x01" + b"\x29\x23\x1e\x27\x21\x1c\x26\x20\x1b\x25\x1f\x1a\x23\x1d\x19\x21" + b"\x1c\x18\x20\x1b\x17\x1e\x19\x16\x1c\x18\x14\x1b\x17\x13\x19\x14" + b"\x10\x17\x13\x0f\x14\x10\x0d\x12\x0f\x0b\x0f\x0b\x0a\x0b\x0a\x07" + b"\x26\x1a\x0f\x23\x19\x0f\x20\x17\x0f\x1c\x16\x0f\x19\x13\x0d\x14" + b"\x10\x0b\x10\x0d\x0a\x0b\x0a\x07\x33\x22\x1f\x35\x29\x26\x37\x2f" + b"\x2d\x39\x35\x34\x37\x39\x3a\x33\x37\x39\x30\x34\x36\x2b\x31\x34" + b"\x27\x2e\x31\x22\x2b\x2f\x1d\x28\x2c\x17\x25\x2a\x0f\x20\x26\x0d" + b"\x1e\x25\x0b\x1c\x22\x0a\x1b\x20\x07\x19\x1e\x07\x17\x1b\x07\x14" + b"\x18\x01\x12\x16\x01\x0f\x12\x01\x0b\x0d\x01\x07\x0a\x01\x01\x01" + b"\x2c\x21\x21\x2a\x1f\x1f\x29\x1d\x1d\x27\x1c\x1c\x26\x1a\x1a\x24" + b"\x18\x18\x22\x17\x17\x21\x16\x16\x1e\x13\x13\x1b\x12\x12\x18\x10" + b"\x10\x16\x0d\x0d\x12\x0b\x0b\x0d\x0a\x0a\x0a\x07\x07\x01\x01\x01" + b"\x2e\x30\x29\x2d\x2e\x27\x2b\x2c\x26\x2a\x2a\x24\x28\x29\x23\x27" + b"\x27\x21\x26\x26\x1f\x24\x24\x1d\x22\x22\x1c\x1f\x1f\x1a\x1c\x1c" + b"\x18\x19\x19\x16\x17\x17\x13\x13\x13\x10\x0f\x0f\x0d\x0b\x0b\x0a" + b"\x30\x1e\x1b\x2d\x1c\x19\x2c\x1a\x17\x2a\x19\x14\x28\x17\x13\x26" + b"\x16\x10\x24\x13\x0f\x21\x12\x0d\x1f\x10\x0b\x1c\x0f\x0a\x19\x0d" + b"\x0a\x16\x0b\x07\x12\x0a\x07\x0f\x07\x01\x0a\x01\x01\x01\x01\x01" + b"\x28\x29\x38\x26\x27\x36\x25\x26\x34\x24\x24\x31\x22\x22\x2f\x20" + b"\x21\x2d\x1e\x1f\x2a\x1d\x1d\x27\x1b\x1b\x25\x19\x19\x21\x17\x17" + b"\x1e\x14\x14\x1b\x13\x12\x17\x10\x0f\x13\x0d\x0b\x0f\x0a\x07\x07" + b"\x2f\x32\x29\x2d\x30\x26\x2b\x2e\x24\x29\x2c\x21\x27\x2a\x1e\x25" + b"\x28\x1c\x23\x26\x1a\x21\x25\x18\x1e\x22\x14\x1b\x1f\x10\x19\x1c" + b"\x0d\x17\x1a\x0a\x13\x17\x07\x10\x13\x01\x0d\x0f\x01\x0a\x0b\x01" + b"\x01\x3f\x01\x13\x3c\x0b\x1b\x39\x10\x20\x35\x14\x23\x31\x17\x23" + b"\x2d\x18\x23\x29\x18\x3f\x3f\x3f\x3f\x3f\x39\x3f\x3f\x31\x3f\x3f" + b"\x2a\x3f\x3f\x20\x3f\x3f\x14\x3f\x3c\x12\x3f\x39\x0f\x3f\x35\x0b" + b"\x3f\x32\x07\x3f\x2d\x01\x3d\x2a\x01\x3b\x26\x01\x39\x21\x01\x37" + b"\x1d\x01\x34\x1a\x01\x32\x16\x01\x2f\x12\x01\x2d\x0f\x01\x2a\x0b" + b"\x01\x27\x07\x01\x23\x01\x01\x1d\x01\x01\x17\x01\x01\x10\x01\x01" + b"\x3d\x01\x01\x19\x19\x3f\x3f\x01\x01\x01\x01\x3f\x16\x16\x13\x10" + b"\x10\x0f\x0d\x0d\x0b\x3c\x2e\x2a\x36\x27\x20\x30\x21\x18\x29\x1b" + b"\x10\x3c\x39\x37\x37\x32\x2f\x31\x2c\x28\x2b\x26\x21\x30\x22\x20" +) + +if __name__ == "__main__": + im = open("../hacks/sample.wal") + print(im.info, im.mode, im.size) + im.save("../out.png") diff --git a/pyPackages/pillowx86/PIL/WebPImagePlugin.py b/pyPackages/pillowx86/PIL/WebPImagePlugin.py new file mode 100644 index 0000000..78a7a53 --- /dev/null +++ b/pyPackages/pillowx86/PIL/WebPImagePlugin.py @@ -0,0 +1,80 @@ +from PIL import Image +from PIL import ImageFile +from io import BytesIO +from PIL import _webp + + +_VALID_WEBP_MODES = { + "RGB": True, + "RGBA": True, + } + +_VP8_MODES_BY_IDENTIFIER = { + b"VP8 ": "RGB", + b"VP8X": "RGBA", + b"VP8L": "RGBA", # lossless + } + + +def _accept(prefix): + is_riff_file_format = prefix[:4] == b"RIFF" + is_webp_file = prefix[8:12] == b"WEBP" + is_valid_vp8_mode = prefix[12:16] in _VP8_MODES_BY_IDENTIFIER + + return is_riff_file_format and is_webp_file and is_valid_vp8_mode + + +class WebPImageFile(ImageFile.ImageFile): + + format = "WEBP" + format_description = "WebP image" + + def _open(self): + data, width, height, self.mode, icc_profile, exif = \ + _webp.WebPDecode(self.fp.read()) + + if icc_profile: + self.info["icc_profile"] = icc_profile + if exif: + self.info["exif"] = exif + + self.size = width, height + self.fp = BytesIO(data) + self.tile = [("raw", (0, 0) + self.size, 0, self.mode)] + + def _getexif(self): + from PIL.JpegImagePlugin import _getexif + return _getexif(self) + + +def _save(im, fp, filename): + image_mode = im.mode + if im.mode not in _VALID_WEBP_MODES: + raise IOError("cannot write mode %s as WEBP" % image_mode) + + lossless = im.encoderinfo.get("lossless", False) + quality = im.encoderinfo.get("quality", 80) + icc_profile = im.encoderinfo.get("icc_profile", "") + exif = im.encoderinfo.get("exif", "") + + data = _webp.WebPEncode( + im.tobytes(), + im.size[0], + im.size[1], + lossless, + float(quality), + im.mode, + icc_profile, + exif + ) + if data is None: + raise IOError("cannot write file as WEBP (encoder returned None)") + + fp.write(data) + + +Image.register_open("WEBP", WebPImageFile, _accept) +Image.register_save("WEBP", _save) + +Image.register_extension("WEBP", ".webp") +Image.register_mime("WEBP", "image/webp") diff --git a/pyPackages/pillowx86/PIL/WmfImagePlugin.py b/pyPackages/pillowx86/PIL/WmfImagePlugin.py new file mode 100644 index 0000000..6146c15 --- /dev/null +++ b/pyPackages/pillowx86/PIL/WmfImagePlugin.py @@ -0,0 +1,173 @@ +# +# The Python Imaging Library +# $Id$ +# +# WMF stub codec +# +# history: +# 1996-12-14 fl Created +# 2004-02-22 fl Turned into a stub driver +# 2004-02-23 fl Added EMF support +# +# Copyright (c) Secret Labs AB 1997-2004. All rights reserved. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.2" + +from PIL import Image, ImageFile, _binary + +_handler = None + +if str != bytes: + long = int + + +## +# Install application-specific WMF image handler. +# +# @param handler Handler object. + +def register_handler(handler): + global _handler + _handler = handler + +if hasattr(Image.core, "drawwmf"): + # install default handler (windows only) + + class WmfHandler: + + def open(self, im): + im.mode = "RGB" + self.bbox = im.info["wmf_bbox"] + + def load(self, im): + im.fp.seek(0) # rewind + return Image.frombytes( + "RGB", im.size, + Image.core.drawwmf(im.fp.read(), im.size, self.bbox), + "raw", "BGR", (im.size[0]*3 + 3) & -4, -1 + ) + + register_handler(WmfHandler()) + +# -------------------------------------------------------------------- + +word = _binary.i16le + + +def short(c, o=0): + v = word(c, o) + if v >= 32768: + v -= 65536 + return v + +dword = _binary.i32le + + +# +# -------------------------------------------------------------------- +# Read WMF file + +def _accept(prefix): + return ( + prefix[:6] == b"\xd7\xcd\xc6\x9a\x00\x00" or + prefix[:4] == b"\x01\x00\x00\x00" + ) + + +## +# Image plugin for Windows metafiles. + +class WmfStubImageFile(ImageFile.StubImageFile): + + format = "WMF" + format_description = "Windows Metafile" + + def _open(self): + + # check placable header + s = self.fp.read(80) + + if s[:6] == b"\xd7\xcd\xc6\x9a\x00\x00": + + # placeable windows metafile + + # get units per inch + inch = word(s, 14) + + # get bounding box + x0 = short(s, 6) + y0 = short(s, 8) + x1 = short(s, 10) + y1 = short(s, 12) + + # normalize size to 72 dots per inch + size = (x1 - x0) * 72 // inch, (y1 - y0) * 72 // inch + + self.info["wmf_bbox"] = x0, y0, x1, y1 + + self.info["dpi"] = 72 + + # print self.mode, self.size, self.info + + # sanity check (standard metafile header) + if s[22:26] != b"\x01\x00\t\x00": + raise SyntaxError("Unsupported WMF file format") + + elif dword(s) == 1 and s[40:44] == b" EMF": + # enhanced metafile + + # get bounding box + x0 = dword(s, 8) + y0 = dword(s, 12) + x1 = dword(s, 16) + y1 = dword(s, 20) + + # get frame (in 0.01 millimeter units) + frame = dword(s, 24), dword(s, 28), dword(s, 32), dword(s, 36) + + # normalize size to 72 dots per inch + size = x1 - x0, y1 - y0 + + # calculate dots per inch from bbox and frame + xdpi = 2540 * (x1 - y0) // (frame[2] - frame[0]) + ydpi = 2540 * (y1 - y0) // (frame[3] - frame[1]) + + self.info["wmf_bbox"] = x0, y0, x1, y1 + + if xdpi == ydpi: + self.info["dpi"] = xdpi + else: + self.info["dpi"] = xdpi, ydpi + + else: + raise SyntaxError("Unsupported file format") + + self.mode = "RGB" + self.size = size + + loader = self._load() + if loader: + loader.open(self) + + def _load(self): + return _handler + + +def _save(im, fp, filename): + if _handler is None or not hasattr("_handler", "save"): + raise IOError("WMF save handler not installed") + _handler.save(im, fp, filename) + +# +# -------------------------------------------------------------------- +# Registry stuff + +Image.register_open(WmfStubImageFile.format, WmfStubImageFile, _accept) +Image.register_save(WmfStubImageFile.format, _save) + +Image.register_extension(WmfStubImageFile.format, ".wmf") +Image.register_extension(WmfStubImageFile.format, ".emf") diff --git a/pyPackages/pillowx86/PIL/XVThumbImagePlugin.py b/pyPackages/pillowx86/PIL/XVThumbImagePlugin.py new file mode 100644 index 0000000..5cf1386 --- /dev/null +++ b/pyPackages/pillowx86/PIL/XVThumbImagePlugin.py @@ -0,0 +1,75 @@ +# +# The Python Imaging Library. +# $Id$ +# +# XV Thumbnail file handler by Charles E. "Gene" Cash +# (gcash@magicnet.net) +# +# see xvcolor.c and xvbrowse.c in the sources to John Bradley's XV, +# available from ftp://ftp.cis.upenn.edu/pub/xv/ +# +# history: +# 98-08-15 cec created (b/w only) +# 98-12-09 cec added color palette +# 98-12-28 fl added to PIL (with only a few very minor modifications) +# +# To do: +# FIXME: make save work (this requires quantization support) +# + +__version__ = "0.1" + +from PIL import Image, ImageFile, ImagePalette, _binary + +o8 = _binary.o8 + +# standard color palette for thumbnails (RGB332) +PALETTE = b"" +for r in range(8): + for g in range(8): + for b in range(4): + PALETTE = PALETTE + (o8((r*255)//7)+o8((g*255)//7)+o8((b*255)//3)) + + +## +# Image plugin for XV thumbnail images. + +class XVThumbImageFile(ImageFile.ImageFile): + + format = "XVThumb" + format_description = "XV thumbnail image" + + def _open(self): + + # check magic + s = self.fp.read(6) + if s != b"P7 332": + raise SyntaxError("not an XV thumbnail file") + + # Skip to beginning of next line + self.fp.readline() + + # skip info comments + while True: + s = self.fp.readline() + if not s: + raise SyntaxError("Unexpected EOF reading XV thumbnail file") + if s[0] != b'#': + break + + # parse header line (already read) + s = s.strip().split() + + self.mode = "P" + self.size = int(s[0:1]), int(s[1:2]) + + self.palette = ImagePalette.raw("RGB", PALETTE) + + self.tile = [ + ("raw", (0, 0)+self.size, + self.fp.tell(), (self.mode, 0, 1) + )] + +# -------------------------------------------------------------------- + +Image.register_open("XVThumb", XVThumbImageFile) diff --git a/pyPackages/pillowx86/PIL/XbmImagePlugin.py b/pyPackages/pillowx86/PIL/XbmImagePlugin.py new file mode 100644 index 0000000..604ba15 --- /dev/null +++ b/pyPackages/pillowx86/PIL/XbmImagePlugin.py @@ -0,0 +1,96 @@ +# +# The Python Imaging Library. +# $Id$ +# +# XBM File handling +# +# History: +# 1995-09-08 fl Created +# 1996-11-01 fl Added save support +# 1997-07-07 fl Made header parser more tolerant +# 1997-07-22 fl Fixed yet another parser bug +# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.4) +# 2001-05-13 fl Added hotspot handling (based on code from Bernhard Herzog) +# 2004-02-24 fl Allow some whitespace before first #define +# +# Copyright (c) 1997-2004 by Secret Labs AB +# Copyright (c) 1996-1997 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.6" + +import re +from PIL import Image, ImageFile + +# XBM header +xbm_head = re.compile( + b"\s*#define[ \t]+[^_]*_width[ \t]+(?P[0-9]+)[\r\n]+" + b"#define[ \t]+[^_]*_height[ \t]+(?P[0-9]+)[\r\n]+" + b"(?P" + b"#define[ \t]+[^_]*_x_hot[ \t]+(?P[0-9]+)[\r\n]+" + b"#define[ \t]+[^_]*_y_hot[ \t]+(?P[0-9]+)[\r\n]+" + b")?" + b"[\\000-\\377]*_bits\\[\\]" +) + + +def _accept(prefix): + return prefix.lstrip()[:7] == b"#define" + + +## +# Image plugin for X11 bitmaps. + +class XbmImageFile(ImageFile.ImageFile): + + format = "XBM" + format_description = "X11 Bitmap" + + def _open(self): + + m = xbm_head.match(self.fp.read(512)) + + if m: + + xsize = int(m.group("width")) + ysize = int(m.group("height")) + + if m.group("hotspot"): + self.info["hotspot"] = ( + int(m.group("xhot")), int(m.group("yhot")) + ) + + self.mode = "1" + self.size = xsize, ysize + + self.tile = [("xbm", (0, 0)+self.size, m.end(), None)] + + +def _save(im, fp, filename): + + if im.mode != "1": + raise IOError("cannot write mode %s as XBM" % im.mode) + + fp.write(("#define im_width %d\n" % im.size[0]).encode('ascii')) + fp.write(("#define im_height %d\n" % im.size[1]).encode('ascii')) + + hotspot = im.encoderinfo.get("hotspot") + if hotspot: + fp.write(("#define im_x_hot %d\n" % hotspot[0]).encode('ascii')) + fp.write(("#define im_y_hot %d\n" % hotspot[1]).encode('ascii')) + + fp.write(b"static char im_bits[] = {\n") + + ImageFile._save(im, fp, [("xbm", (0, 0)+im.size, 0, None)]) + + fp.write(b"};\n") + + +Image.register_open("XBM", XbmImageFile, _accept) +Image.register_save("XBM", _save) + +Image.register_extension("XBM", ".xbm") + +Image.register_mime("XBM", "image/xbm") diff --git a/pyPackages/pillowx86/PIL/XpmImagePlugin.py b/pyPackages/pillowx86/PIL/XpmImagePlugin.py new file mode 100644 index 0000000..5175808 --- /dev/null +++ b/pyPackages/pillowx86/PIL/XpmImagePlugin.py @@ -0,0 +1,131 @@ +# +# The Python Imaging Library. +# $Id$ +# +# XPM File handling +# +# History: +# 1996-12-29 fl Created +# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.7) +# +# Copyright (c) Secret Labs AB 1997-2001. +# Copyright (c) Fredrik Lundh 1996-2001. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.2" + + +import re +from PIL import Image, ImageFile, ImagePalette +from PIL._binary import i8, o8 + +# XPM header +xpm_head = re.compile(b"\"([0-9]*) ([0-9]*) ([0-9]*) ([0-9]*)") + + +def _accept(prefix): + return prefix[:9] == b"/* XPM */" + + +## +# Image plugin for X11 pixel maps. + +class XpmImageFile(ImageFile.ImageFile): + + format = "XPM" + format_description = "X11 Pixel Map" + + def _open(self): + + if not _accept(self.fp.read(9)): + raise SyntaxError("not an XPM file") + + # skip forward to next string + while True: + s = self.fp.readline() + if not s: + raise SyntaxError("broken XPM file") + m = xpm_head.match(s) + if m: + break + + self.size = int(m.group(1)), int(m.group(2)) + + pal = int(m.group(3)) + bpp = int(m.group(4)) + + if pal > 256 or bpp != 1: + raise ValueError("cannot read this XPM file") + + # + # load palette description + + palette = [b"\0\0\0"] * 256 + + for i in range(pal): + + s = self.fp.readline() + if s[-2:] == b'\r\n': + s = s[:-2] + elif s[-1:] in b'\r\n': + s = s[:-1] + + c = i8(s[1]) + s = s[2:-2].split() + + for i in range(0, len(s), 2): + + if s[i] == b"c": + + # process colour key + rgb = s[i+1] + if rgb == b"None": + self.info["transparency"] = c + elif rgb[0:1] == b"#": + # FIXME: handle colour names (see ImagePalette.py) + rgb = int(rgb[1:], 16) + palette[c] = (o8((rgb >> 16) & 255) + + o8((rgb >> 8) & 255) + + o8(rgb & 255)) + else: + # unknown colour + raise ValueError("cannot read this XPM file") + break + + else: + + # missing colour key + raise ValueError("cannot read this XPM file") + + self.mode = "P" + self.palette = ImagePalette.raw("RGB", b"".join(palette)) + + self.tile = [("raw", (0, 0)+self.size, self.fp.tell(), ("P", 0, 1))] + + def load_read(self, bytes): + + # + # load all image data in one chunk + + xsize, ysize = self.size + + s = [None] * ysize + + for i in range(ysize): + s[i] = self.fp.readline()[1:xsize+1].ljust(xsize) + + self.fp = None + + return b"".join(s) + +# +# Registry + +Image.register_open("XPM", XpmImageFile, _accept) + +Image.register_extension("XPM", ".xpm") + +Image.register_mime("XPM", "image/xpm") diff --git a/pyPackages/pillowx86/PIL/__init__.py b/pyPackages/pillowx86/PIL/__init__.py new file mode 100644 index 0000000..4d5d7b3 --- /dev/null +++ b/pyPackages/pillowx86/PIL/__init__.py @@ -0,0 +1,58 @@ +# +# The Python Imaging Library. +# $Id$ +# +# package placeholder +# +# Copyright (c) 1999 by Secret Labs AB. +# +# See the README file for information on usage and redistribution. +# + +# ;-) + +VERSION = '1.1.7' # PIL version +PILLOW_VERSION = '2.7.0' # Pillow + +_plugins = ['BmpImagePlugin', + 'BufrStubImagePlugin', + 'CurImagePlugin', + 'DcxImagePlugin', + 'EpsImagePlugin', + 'FitsStubImagePlugin', + 'FliImagePlugin', + 'FpxImagePlugin', + 'GbrImagePlugin', + 'GifImagePlugin', + 'GribStubImagePlugin', + 'Hdf5StubImagePlugin', + 'IcnsImagePlugin', + 'IcoImagePlugin', + 'ImImagePlugin', + 'ImtImagePlugin', + 'IptcImagePlugin', + 'JpegImagePlugin', + 'Jpeg2KImagePlugin', + 'McIdasImagePlugin', + 'MicImagePlugin', + 'MpegImagePlugin', + 'MpoImagePlugin', + 'MspImagePlugin', + 'PalmImagePlugin', + 'PcdImagePlugin', + 'PcxImagePlugin', + 'PdfImagePlugin', + 'PixarImagePlugin', + 'PngImagePlugin', + 'PpmImagePlugin', + 'PsdImagePlugin', + 'SgiImagePlugin', + 'SpiderImagePlugin', + 'SunImagePlugin', + 'TgaImagePlugin', + 'TiffImagePlugin', + 'WebPImagePlugin', + 'WmfImagePlugin', + 'XbmImagePlugin', + 'XpmImagePlugin', + 'XVThumbImagePlugin'] diff --git a/pyPackages/pillowx86/PIL/_binary.py b/pyPackages/pillowx86/PIL/_binary.py new file mode 100644 index 0000000..51ce45a --- /dev/null +++ b/pyPackages/pillowx86/PIL/_binary.py @@ -0,0 +1,76 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Binary input/output support routines. +# +# Copyright (c) 1997-2003 by Secret Labs AB +# Copyright (c) 1995-2003 by Fredrik Lundh +# Copyright (c) 2012 by Brian Crowell +# +# See the README file for information on usage and redistribution. +# + +if bytes is str: + def i8(c): + return ord(c) + + def o8(i): + return chr(i & 255) +else: + def i8(c): + return c if c.__class__ is int else c[0] + + def o8(i): + return bytes((i & 255,)) + + +# Input, le = little endian, be = big endian +# TODO: replace with more readable struct.unpack equivalent +def i16le(c, o=0): + """ + Converts a 2-bytes (16 bits) string to an integer. + + c: string containing bytes to convert + o: offset of bytes to convert in string + """ + return i8(c[o]) | (i8(c[o+1]) << 8) + + +def i32le(c, o=0): + """ + Converts a 4-bytes (32 bits) string to an integer. + + c: string containing bytes to convert + o: offset of bytes to convert in string + """ + return (i8(c[o]) | (i8(c[o+1]) << 8) | (i8(c[o+2]) << 16) | + (i8(c[o+3]) << 24)) + + +def i16be(c, o=0): + return (i8(c[o]) << 8) | i8(c[o+1]) + + +def i32be(c, o=0): + return ((i8(c[o]) << 24) | (i8(c[o+1]) << 16) | + (i8(c[o+2]) << 8) | i8(c[o+3])) + + +# Output, le = little endian, be = big endian +def o16le(i): + return o8(i) + o8(i >> 8) + + +def o32le(i): + return o8(i) + o8(i >> 8) + o8(i >> 16) + o8(i >> 24) + + +def o16be(i): + return o8(i >> 8) + o8(i) + + +def o32be(i): + return o8(i >> 24) + o8(i >> 16) + o8(i >> 8) + o8(i) + +# End of file diff --git a/pyPackages/pillowx86/PIL/_imaging.cpython-34m.so b/pyPackages/pillowx86/PIL/_imaging.cpython-34m.so new file mode 100644 index 0000000000000000000000000000000000000000..fb378f0b1b4250c515f62883f25b000f556c78d4 GIT binary patch literal 792244 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%0ABjI7#zGU@ues1cyK_E^={)n}F%q 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Π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 U*vDXwWF!CX5oA_e*-d8s9|+6cx&QzG literal 0 HcmV?d00001 diff --git a/pyPackages/pillowx86/PIL/_imagingmath.cpython-34m.so b/pyPackages/pillowx86/PIL/_imagingmath.cpython-34m.so new file mode 100644 index 0000000000000000000000000000000000000000..80126dd3733f9877af91b8c305cbef810bf38c1e GIT binary patch literal 43814 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
      n)K08pX6HH8A@rJLxbw#uSDzVjwd< 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 literal 0 HcmV?d00001 diff --git a/pyPackages/pillowx86/PIL/_util.py b/pyPackages/pillowx86/PIL/_util.py new file mode 100644 index 0000000..51c6f68 --- /dev/null +++ b/pyPackages/pillowx86/PIL/_util.py @@ -0,0 +1,27 @@ +import os + +if bytes is str: + def isStringType(t): + return isinstance(t, basestring) + + def isPath(f): + return isinstance(f, basestring) +else: + def isStringType(t): + return isinstance(t, str) + + def isPath(f): + return isinstance(f, (bytes, str)) + + +# Checks if an object is a string, and that it points to a directory. +def isDirectory(f): + return isPath(f) and os.path.isdir(f) + + +class deferred_error(object): + def __init__(self, ex): + self.ex = ex + + def __getattr__(self, elt): + raise self.ex diff --git a/pyPackages/pillowx86/Pillow-2.7.0.data/scripts/pilconvert.py b/pyPackages/pillowx86/Pillow-2.7.0.data/scripts/pilconvert.py new file mode 100644 index 0000000..74969bf --- /dev/null +++ b/pyPackages/pillowx86/Pillow-2.7.0.data/scripts/pilconvert.py @@ -0,0 +1,97 @@ +#!python +# +# The Python Imaging Library. +# $Id$ +# +# convert image files +# +# History: +# 0.1 96-04-20 fl Created +# 0.2 96-10-04 fl Use draft mode when converting images +# 0.3 96-12-30 fl Optimize output (PNG, JPEG) +# 0.4 97-01-18 fl Made optimize an option (PNG, JPEG) +# 0.5 98-12-30 fl Fixed -f option (from Anthony Baxter) +# + +from __future__ import print_function + +import site +import getopt, string, sys + +from PIL import Image + +def usage(): + print("PIL Convert 0.5/1998-12-30 -- convert image files") + print("Usage: pilconvert [option] infile outfile") + print() + print("Options:") + print() + print(" -c convert to format (default is given by extension)") + print() + print(" -g convert to greyscale") + print(" -p convert to palette image (using standard palette)") + print(" -r convert to rgb") + print() + print(" -o optimize output (trade speed for size)") + print(" -q set compression quality (0-100, JPEG only)") + print() + print(" -f list supported file formats") + sys.exit(1) + +if len(sys.argv) == 1: + usage() + +try: + opt, argv = getopt.getopt(sys.argv[1:], "c:dfgopq:r") +except getopt.error as v: + print(v) + sys.exit(1) + +format = None +convert = None + +options = { } + +for o, a in opt: + + if o == "-f": + Image.init() + id = sorted(Image.ID) + print("Supported formats (* indicates output format):") + for i in id: + if i in Image.SAVE: + print(i+"*", end=' ') + else: + print(i, end=' ') + sys.exit(1) + + elif o == "-c": + format = a + + if o == "-g": + convert = "L" + elif o == "-p": + convert = "P" + elif o == "-r": + convert = "RGB" + + elif o == "-o": + options["optimize"] = 1 + elif o == "-q": + options["quality"] = string.atoi(a) + +if len(argv) != 2: + usage() + +try: + im = Image.open(argv[0]) + if convert and im.mode != convert: + im.draft(convert, im.size) + im = im.convert(convert) + if format: + im.save(argv[1], format, **options) + else: + im.save(argv[1], **options) +except: + print("cannot convert image", end=' ') + print("(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1])) diff --git a/pyPackages/pillowx86/Pillow-2.7.0.data/scripts/pildriver.py b/pyPackages/pillowx86/Pillow-2.7.0.data/scripts/pildriver.py new file mode 100644 index 0000000..e382171 --- /dev/null +++ b/pyPackages/pillowx86/Pillow-2.7.0.data/scripts/pildriver.py @@ -0,0 +1,528 @@ +#!python +"""PILdriver, an image-processing calculator using PIL. + +An instance of class PILDriver is essentially a software stack machine +(Polish-notation interpreter) for sequencing PIL image +transformations. The state of the instance is the interpreter stack. + +The only method one will normally invoke after initialization is the +`execute' method. This takes an argument list of tokens, pushes them +onto the instance's stack, and then tries to clear the stack by +successive evaluation of PILdriver operators. Any part of the stack +not cleaned off persists and is part of the evaluation context for +the next call of the execute method. + +PILDriver doesn't catch any exceptions, on the theory that these +are actually diagnostic information that should be interpreted by +the calling code. + +When called as a script, the command-line arguments are passed to +a PILDriver instance. If there are no command-line arguments, the +module runs an interactive interpreter, each line of which is split into +space-separated tokens and passed to the execute method. + +In the method descriptions below, a first line beginning with the string +`usage:' means this method can be invoked with the token that follows +it. Following <>-enclosed arguments describe how the method interprets +the entries on the stack. Each argument specification begins with a +type specification: either `int', `float', `string', or `image'. + +All operations consume their arguments off the stack (use `dup' to +keep copies around). Use `verbose 1' to see the stack state displayed +before each operation. + +Usage examples: + + `show crop 0 0 200 300 open test.png' loads test.png, crops out a portion +of its upper-left-hand corner and displays the cropped portion. + + `save rotated.png rotate 30 open test.tiff' loads test.tiff, rotates it +30 degrees, and saves the result as rotated.png (in PNG format). +""" +# by Eric S. Raymond +# $Id$ + +# TO DO: +# 1. Add PILFont capabilities, once that's documented. +# 2. Add PILDraw operations. +# 3. Add support for composing and decomposing multiple-image files. +# + +from __future__ import print_function + +from PIL import Image + +class PILDriver: + + verbose = 0 + + def do_verbose(self): + """usage: verbose + + Set verbosity flag from top of stack. + """ + self.verbose = int(self.do_pop()) + + # The evaluation stack (internal only) + + stack = [] # Stack of pending operations + + def push(self, item): + "Push an argument onto the evaluation stack." + self.stack = [item] + self.stack + + def top(self): + "Return the top-of-stack element." + return self.stack[0] + + # Stack manipulation (callable) + + def do_clear(self): + """usage: clear + + Clear the stack. + """ + self.stack = [] + + def do_pop(self): + """usage: pop + + Discard the top element on the stack. + """ + top = self.stack[0] + self.stack = self.stack[1:] + return top + + def do_dup(self): + """usage: dup + + Duplicate the top-of-stack item. + """ + if hasattr(self, 'format'): # If it's an image, do a real copy + dup = self.stack[0].copy() + else: + dup = self.stack[0] + self.stack = [dup] + self.stack + + def do_swap(self): + """usage: swap + + Swap the top-of-stack item with the next one down. + """ + self.stack = [self.stack[1], self.stack[0]] + self.stack[2:] + + # Image module functions (callable) + + def do_new(self): + """usage: new : + + Create and push a greyscale image of given size and color. + """ + xsize = int(self.do_pop()) + ysize = int(self.do_pop()) + color = int(self.do_pop()) + self.push(Image.new("L", (xsize, ysize), color)) + + def do_open(self): + """usage: open + + Open the indicated image, read it, push the image on the stack. + """ + self.push(Image.open(self.do_pop())) + + def do_blend(self): + """usage: blend + + Replace two images and an alpha with the blended image. + """ + image1 = self.do_pop() + image2 = self.do_pop() + alpha = float(self.do_pop()) + self.push(Image.blend(image1, image2, alpha)) + + def do_composite(self): + """usage: composite + + Replace two images and a mask with their composite. + """ + image1 = self.do_pop() + image2 = self.do_pop() + mask = self.do_pop() + self.push(Image.composite(image1, image2, mask)) + + def do_merge(self): + """usage: merge [ [ []]] + + Merge top-of stack images in a way described by the mode. + """ + mode = self.do_pop() + bandlist = [] + for band in mode: + bandlist.append(self.do_pop()) + self.push(Image.merge(mode, bandlist)) + + # Image class methods + + def do_convert(self): + """usage: convert + + Convert the top image to the given mode. + """ + mode = self.do_pop() + image = self.do_pop() + self.push(image.convert(mode)) + + def do_copy(self): + """usage: copy + + Make and push a true copy of the top image. + """ + self.dup() + + def do_crop(self): + """usage: crop + + Crop and push a rectangular region from the current image. + """ + left = int(self.do_pop()) + upper = int(self.do_pop()) + right = int(self.do_pop()) + lower = int(self.do_pop()) + image = self.do_pop() + self.push(image.crop((left, upper, right, lower))) + + def do_draft(self): + """usage: draft + + Configure the loader for a given mode and size. + """ + mode = self.do_pop() + xsize = int(self.do_pop()) + ysize = int(self.do_pop()) + self.push(self.draft(mode, (xsize, ysize))) + + def do_filter(self): + """usage: filter + + Process the top image with the given filter. + """ + from PIL import ImageFilter + filter = eval("ImageFilter." + self.do_pop().upper()) + image = self.do_pop() + self.push(image.filter(filter)) + + def do_getbbox(self): + """usage: getbbox + + Push left, upper, right, and lower pixel coordinates of the top image. + """ + bounding_box = self.do_pop().getbbox() + self.push(bounding_box[3]) + self.push(bounding_box[2]) + self.push(bounding_box[1]) + self.push(bounding_box[0]) + + def do_getextrema(self): + """usage: extrema + + Push minimum and maximum pixel values of the top image. + """ + extrema = self.do_pop().extrema() + self.push(extrema[1]) + self.push(extrema[0]) + + def do_offset(self): + """usage: offset + + Offset the pixels in the top image. + """ + xoff = int(self.do_pop()) + yoff = int(self.do_pop()) + image = self.do_pop() + self.push(image.offset(xoff, yoff)) + + def do_paste(self): + """usage: paste + + Paste figure image into ground with upper left at given offsets. + """ + figure = self.do_pop() + xoff = int(self.do_pop()) + yoff = int(self.do_pop()) + ground = self.do_pop() + if figure.mode == "RGBA": + ground.paste(figure, (xoff, yoff), figure) + else: + ground.paste(figure, (xoff, yoff)) + self.push(ground) + + def do_resize(self): + """usage: resize + + Resize the top image. + """ + ysize = int(self.do_pop()) + xsize = int(self.do_pop()) + image = self.do_pop() + self.push(image.resize((xsize, ysize))) + + def do_rotate(self): + """usage: rotate + + Rotate image through a given angle + """ + angle = int(self.do_pop()) + image = self.do_pop() + self.push(image.rotate(angle)) + + def do_save(self): + """usage: save + + Save image with default options. + """ + filename = self.do_pop() + image = self.do_pop() + image.save(filename) + + def do_save2(self): + """usage: save2 + + Save image with specified options. + """ + filename = self.do_pop() + options = self.do_pop() + image = self.do_pop() + image.save(filename, None, options) + + def do_show(self): + """usage: show + + Display and pop the top image. + """ + self.do_pop().show() + + def do_thumbnail(self): + """usage: thumbnail + + Modify the top image in the stack to contain a thumbnail of itself. + """ + ysize = int(self.do_pop()) + xsize = int(self.do_pop()) + self.top().thumbnail((xsize, ysize)) + + def do_transpose(self): + """usage: transpose + + Transpose the top image. + """ + transpose = self.do_pop().upper() + image = self.do_pop() + self.push(image.transpose(transpose)) + + # Image attributes + + def do_format(self): + """usage: format + + Push the format of the top image onto the stack. + """ + self.push(self.do_pop().format) + + def do_mode(self): + """usage: mode + + Push the mode of the top image onto the stack. + """ + self.push(self.do_pop().mode) + + def do_size(self): + """usage: size + + Push the image size on the stack as (y, x). + """ + size = self.do_pop().size + self.push(size[0]) + self.push(size[1]) + + # ImageChops operations + + def do_invert(self): + """usage: invert + + Invert the top image. + """ + from PIL import ImageChops + self.push(ImageChops.invert(self.do_pop())) + + def do_lighter(self): + """usage: lighter + + Pop the two top images, push an image of the lighter pixels of both. + """ + from PIL import ImageChops + image1 = self.do_pop() + image2 = self.do_pop() + self.push(ImageChops.lighter(image1, image2)) + + def do_darker(self): + """usage: darker + + Pop the two top images, push an image of the darker pixels of both. + """ + from PIL import ImageChops + image1 = self.do_pop() + image2 = self.do_pop() + self.push(ImageChops.darker(image1, image2)) + + def do_difference(self): + """usage: difference + + Pop the two top images, push the difference image + """ + from PIL import ImageChops + image1 = self.do_pop() + image2 = self.do_pop() + self.push(ImageChops.difference(image1, image2)) + + def do_multiply(self): + """usage: multiply + + Pop the two top images, push the multiplication image. + """ + from PIL import ImageChops + image1 = self.do_pop() + image2 = self.do_pop() + self.push(ImageChops.multiply(image1, image2)) + + def do_screen(self): + """usage: screen + + Pop the two top images, superimpose their inverted versions. + """ + from PIL import ImageChops + image2 = self.do_pop() + image1 = self.do_pop() + self.push(ImageChops.screen(image1, image2)) + + def do_add(self): + """usage: add + + Pop the two top images, produce the scaled sum with offset. + """ + from PIL import ImageChops + image1 = self.do_pop() + image2 = self.do_pop() + scale = float(self.do_pop()) + offset = int(self.do_pop()) + self.push(ImageChops.add(image1, image2, scale, offset)) + + def do_subtract(self): + """usage: subtract + + Pop the two top images, produce the scaled difference with offset. + """ + from PIL import ImageChops + image1 = self.do_pop() + image2 = self.do_pop() + scale = float(self.do_pop()) + offset = int(self.do_pop()) + self.push(ImageChops.subtract(image1, image2, scale, offset)) + + # ImageEnhance classes + + def do_color(self): + """usage: color + + Enhance color in the top image. + """ + from PIL import ImageEnhance + factor = float(self.do_pop()) + image = self.do_pop() + enhancer = ImageEnhance.Color(image) + self.push(enhancer.enhance(factor)) + + def do_contrast(self): + """usage: contrast + + Enhance contrast in the top image. + """ + from PIL import ImageEnhance + factor = float(self.do_pop()) + image = self.do_pop() + enhancer = ImageEnhance.Contrast(image) + self.push(enhancer.enhance(factor)) + + def do_brightness(self): + """usage: brightness + + Enhance brightness in the top image. + """ + from PIL import ImageEnhance + factor = float(self.do_pop()) + image = self.do_pop() + enhancer = ImageEnhance.Brightness(image) + self.push(enhancer.enhance(factor)) + + def do_sharpness(self): + """usage: sharpness + + Enhance sharpness in the top image. + """ + from PIL import ImageEnhance + factor = float(self.do_pop()) + image = self.do_pop() + enhancer = ImageEnhance.Sharpness(image) + self.push(enhancer.enhance(factor)) + + # The interpreter loop + + def execute(self, list): + "Interpret a list of PILDriver commands." + list.reverse() + while len(list) > 0: + self.push(list[0]) + list = list[1:] + if self.verbose: + print("Stack: " + repr(self.stack)) + top = self.top() + if not isinstance(top, str): + continue + funcname = "do_" + top + if not hasattr(self, funcname): + continue + else: + self.do_pop() + func = getattr(self, funcname) + func() + +if __name__ == '__main__': + import sys + try: + import readline + except ImportError: + pass # not available on all platforms + + # If we see command-line arguments, interpret them as a stack state + # and execute. Otherwise go interactive. + + driver = PILDriver() + if len(sys.argv[1:]) > 0: + driver.execute(sys.argv[1:]) + else: + print("PILDriver says hello.") + while True: + try: + if sys.version_info[0] >= 3: + line = input('pildriver> ') + else: + line = raw_input('pildriver> ') + except EOFError: + print("\nPILDriver says goodbye.") + break + driver.execute(line.split()) + print(driver.stack) + +# The following sets edit modes for GNU EMACS +# Local Variables: +# mode:python +# End: diff --git a/pyPackages/pillowx86/Pillow-2.7.0.data/scripts/pilfile.py b/pyPackages/pillowx86/Pillow-2.7.0.data/scripts/pilfile.py new file mode 100644 index 0000000..a842db1 --- /dev/null +++ b/pyPackages/pillowx86/Pillow-2.7.0.data/scripts/pilfile.py @@ -0,0 +1,95 @@ +#!python +# +# The Python Imaging Library. +# $Id$ +# +# a utility to identify image files +# +# this script identifies image files, extracting size and +# pixel mode information for known file formats. Note that +# you don't need the PIL C extension to use this module. +# +# History: +# 0.0 1995-09-01 fl Created +# 0.1 1996-05-18 fl Modified options, added debugging mode +# 0.2 1996-12-29 fl Added verify mode +# 0.3 1999-06-05 fl Don't mess up on class exceptions (1.5.2 and later) +# 0.4 2003-09-30 fl Expand wildcards on Windows; robustness tweaks +# + +from __future__ import print_function + +import site +import getopt, glob, sys + +from PIL import Image + +if len(sys.argv) == 1: + print("PIL File 0.4/2003-09-30 -- identify image files") + print("Usage: pilfile [option] files...") + print("Options:") + print(" -f list supported file formats") + print(" -i show associated info and tile data") + print(" -v verify file headers") + print(" -q quiet, don't warn for unidentified/missing/broken files") + sys.exit(1) + +try: + opt, args = getopt.getopt(sys.argv[1:], "fqivD") +except getopt.error as v: + print(v) + sys.exit(1) + +verbose = quiet = verify = 0 + +for o, a in opt: + if o == "-f": + Image.init() + id = sorted(Image.ID) + print("Supported formats:") + for i in id: + print(i, end=' ') + sys.exit(1) + elif o == "-i": + verbose = 1 + elif o == "-q": + quiet = 1 + elif o == "-v": + verify = 1 + elif o == "-D": + Image.DEBUG += 1 + +def globfix(files): + # expand wildcards where necessary + if sys.platform == "win32": + out = [] + for file in files: + if glob.has_magic(file): + out.extend(glob.glob(file)) + else: + out.append(file) + return out + return files + +for file in globfix(args): + try: + im = Image.open(file) + print("%s:" % file, im.format, "%dx%d" % im.size, im.mode, end=' ') + if verbose: + print(im.info, im.tile, end=' ') + print() + if verify: + try: + im.verify() + except: + if not quiet: + print("failed to verify image", end=' ') + print("(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1])) + except IOError as v: + if not quiet: + print(file, "failed:", v) + except: + import traceback + if not quiet: + print(file, "failed:", "unexpected error") + traceback.print_exc(file=sys.stdout) diff --git a/pyPackages/pillowx86/Pillow-2.7.0.data/scripts/pilfont.py b/pyPackages/pillowx86/Pillow-2.7.0.data/scripts/pilfont.py new file mode 100644 index 0000000..f99a776 --- /dev/null +++ b/pyPackages/pillowx86/Pillow-2.7.0.data/scripts/pilfont.py @@ -0,0 +1,56 @@ +#!python +# +# The Python Imaging Library +# $Id$ +# +# PIL raster font compiler +# +# history: +# 1997-08-25 fl created +# 2002-03-10 fl use "from PIL import" +# + +from __future__ import print_function + +VERSION = "0.4" + +import glob, sys + +# drivers +from PIL import BdfFontFile +from PIL import PcfFontFile + +if len(sys.argv) <= 1: + print("PILFONT", VERSION, "-- PIL font compiler.") + print() + print("Usage: pilfont fontfiles...") + print() + print("Convert given font files to the PIL raster font format.") + print("This version of pilfont supports X BDF and PCF fonts.") + sys.exit(1) + +files = [] +for f in sys.argv[1:]: + files = files + glob.glob(f) + +for f in files: + + print(f + "...", end=' ') + + try: + + fp = open(f, "rb") + + try: + p = PcfFontFile.PcfFontFile(fp) + except SyntaxError: + fp.seek(0) + p = BdfFontFile.BdfFontFile(fp) + + p.save(f) + + except (SyntaxError, IOError): + print("failed") + + else: + print("OK") diff --git a/pyPackages/pillowx86/Pillow-2.7.0.data/scripts/pilprint.py b/pyPackages/pillowx86/Pillow-2.7.0.data/scripts/pilprint.py new file mode 100644 index 0000000..01469b7 --- /dev/null +++ b/pyPackages/pillowx86/Pillow-2.7.0.data/scripts/pilprint.py @@ -0,0 +1,95 @@ +#!python +# +# The Python Imaging Library. +# $Id$ +# +# print image files to postscript printer +# +# History: +# 0.1 1996-04-20 fl Created +# 0.2 1996-10-04 fl Use draft mode when converting. +# 0.3 2003-05-06 fl Fixed a typo or two. +# + +from __future__ import print_function + +VERSION = "pilprint 0.3/2003-05-05" + +from PIL import Image +from PIL import PSDraw + +letter = ( 1.0*72, 1.0*72, 7.5*72, 10.0*72 ) + +def description(file, image): + import os + title = os.path.splitext(os.path.split(file)[1])[0] + format = " (%dx%d " + if image.format: + format = " (" + image.format + " %dx%d " + return title + format % image.size + image.mode + ")" + +import getopt, os, sys + +if len(sys.argv) == 1: + print("PIL Print 0.2a1/96-10-04 -- print image files") + print("Usage: pilprint files...") + print("Options:") + print(" -c colour printer (default is monochrome)") + print(" -p print via lpr (default is stdout)") + print(" -P same as -p but use given printer") + sys.exit(1) + +try: + opt, argv = getopt.getopt(sys.argv[1:], "cdpP:") +except getopt.error as v: + print(v) + sys.exit(1) + +printer = None # print to stdout +monochrome = 1 # reduce file size for most common case + +for o, a in opt: + if o == "-d": + # debug: show available drivers + Image.init() + print(Image.ID) + sys.exit(1) + elif o == "-c": + # colour printer + monochrome = 0 + elif o == "-p": + # default printer channel + printer = "lpr" + elif o == "-P": + # printer channel + printer = "lpr -P%s" % a + +for file in argv: + try: + + im = Image.open(file) + + title = description(file, im) + + if monochrome and im.mode not in ["1", "L"]: + im.draft("L", im.size) + im = im.convert("L") + + if printer: + fp = os.popen(printer, "w") + else: + fp = sys.stdout + + ps = PSDraw.PSDraw(fp) + + ps.begin_document() + ps.setfont("Helvetica-Narrow-Bold", 18) + ps.text((letter[0], letter[3]+24), title) + ps.setfont("Helvetica-Narrow-Bold", 8) + ps.text((letter[0], letter[1]-30), VERSION) + ps.image(letter, im) + ps.end_document() + + except: + print("cannot print image", end=' ') + print("(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1])) diff --git a/pyPackages/pillowx86/Pillow-2.7.0.dist-info/DESCRIPTION.rst b/pyPackages/pillowx86/Pillow-2.7.0.dist-info/DESCRIPTION.rst new file mode 100644 index 0000000..7dd5388 --- /dev/null +++ b/pyPackages/pillowx86/Pillow-2.7.0.dist-info/DESCRIPTION.rst @@ -0,0 +1,28 @@ +Pillow +====== + +*Python Imaging Library (Fork)* + +Pillow is the "friendly" PIL fork by `Alex Clark and Contributors `_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. For more information, please `read the documentation `_, `check the changelog `_ and `find out how to contribute `_. + +.. image:: https://travis-ci.org/python-pillow/Pillow.svg?branch=master + :target: https://travis-ci.org/python-pillow/Pillow + :alt: Travis CI build status + +.. image:: https://pypip.in/v/Pillow/badge.png + :target: https://pypi.python.org/pypi/Pillow/ + :alt: Latest PyPI version + +.. image:: https://pypip.in/d/Pillow/badge.png + :target: https://pypi.python.org/pypi/Pillow/ + :alt: Number of PyPI downloads + +.. image:: https://coveralls.io/repos/python-pillow/Pillow/badge.png?branch=master + :target: https://coveralls.io/r/python-pillow/Pillow?branch=master + :alt: Code coverage + +.. image:: https://landscape.io/github/python-pillow/Pillow/master/landscape.png + :target: https://landscape.io/github/python-pillow/Pillow/master + :alt: Code health + + diff --git a/pyPackages/pillowx86/Pillow-2.7.0.dist-info/METADATA b/pyPackages/pillowx86/Pillow-2.7.0.dist-info/METADATA new file mode 100644 index 0000000..e7bd2c0 --- /dev/null +++ b/pyPackages/pillowx86/Pillow-2.7.0.dist-info/METADATA @@ -0,0 +1,53 @@ +Metadata-Version: 2.0 +Name: Pillow +Version: 2.7.0 +Summary: Python Imaging Library (Fork) +Home-page: http://python-pillow.github.io/ +Author: Alex Clark (fork author) +Author-email: aclark@aclark.net +License: Standard PIL License +Keywords: Imaging +Platform: UNKNOWN +Classifier: Development Status :: 6 - Mature +Classifier: Topic :: Multimedia :: Graphics +Classifier: Topic :: Multimedia :: Graphics :: Capture :: Digital Camera +Classifier: Topic :: Multimedia :: Graphics :: Capture :: Scanners +Classifier: Topic :: Multimedia :: Graphics :: Capture :: Screen Capture +Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion +Classifier: Topic :: Multimedia :: Graphics :: Viewers +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.2 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 + +Pillow +====== + +*Python Imaging Library (Fork)* + +Pillow is the "friendly" PIL fork by `Alex Clark and Contributors `_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. For more information, please `read the documentation `_, `check the changelog `_ and `find out how to contribute `_. + +.. image:: https://travis-ci.org/python-pillow/Pillow.svg?branch=master + :target: https://travis-ci.org/python-pillow/Pillow + :alt: Travis CI build status + +.. image:: https://pypip.in/v/Pillow/badge.png + :target: https://pypi.python.org/pypi/Pillow/ + :alt: Latest PyPI version + +.. image:: https://pypip.in/d/Pillow/badge.png + :target: https://pypi.python.org/pypi/Pillow/ + :alt: Number of PyPI downloads + +.. image:: https://coveralls.io/repos/python-pillow/Pillow/badge.png?branch=master + :target: https://coveralls.io/r/python-pillow/Pillow?branch=master + :alt: Code coverage + +.. image:: https://landscape.io/github/python-pillow/Pillow/master/landscape.png + :target: https://landscape.io/github/python-pillow/Pillow/master + :alt: Code health + + diff --git a/pyPackages/pillowx86/Pillow-2.7.0.dist-info/RECORD b/pyPackages/pillowx86/Pillow-2.7.0.dist-info/RECORD new file mode 100644 index 0000000..9ffa7c7 --- /dev/null +++ b/pyPackages/pillowx86/Pillow-2.7.0.dist-info/RECORD @@ -0,0 +1,102 @@ +Pillow-2.7.0.dist-info/RECORD,, +Pillow-2.7.0.dist-info/WHEEL,sha256=Xd1I1m9g3hNXAdmhtTAjlQP3etTF4WmvCkIUCzi0Gd0,102 +Pillow-2.7.0.dist-info/metadata.json,sha256=r7_LhfQ2Z8R52FVEvWBrQm_WTqrE7TOeX9PRmXlo2C4,1118 +Pillow-2.7.0.dist-info/DESCRIPTION.rst,sha256=RSre6zfamxsnhcS6Iac9VAHXMWDJkygzBNUQ1jwhIME,1302 +Pillow-2.7.0.dist-info/METADATA,sha256=ZrV7kb6DNUniGPNco9mnWWZh_4sMMlwA-r9T-D0r1WE,2336 +Pillow-2.7.0.dist-info/top_level.txt,sha256=riZqrk-hyZqh5f1Z0Zwii3dKfxEsByhu9cU9IODF-NY,4 +Pillow-2.7.0.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 +Pillow-2.7.0.data/scripts/pilfont.py,sha256=EVkStUuvSfevmB5H5Bfb6C8H4TEoQIDeZwMPVrRTFKI,1011 +Pillow-2.7.0.data/scripts/pilprint.py,sha256=bkf07hViRRXCSoWSqH9ML0fdeu0v9jmcyaWBAwiDnLg,2366 +Pillow-2.7.0.data/scripts/pilconvert.py,sha256=tSl6H017Ji7usF45X3YXQT-Lm-h6Aa5grvFROGch544,2320 +Pillow-2.7.0.data/scripts/pildriver.py,sha256=y_M5ezjhP0XKt1s0FlOlQq_QrdcLrMiYuHbh9B9QgDM,15587 +Pillow-2.7.0.data/scripts/pilfile.py,sha256=NYrWQ8LYlXFGvIPd9aT37z0thTOan-4OjD1O1-hO8LU,2565 +PIL/GribStubImagePlugin.py,sha256=BrMgUAZs1bfvNcgphqlDG0Bbvd5PY-pHEn_1zGmVHSo,1505 +PIL/WmfImagePlugin.py,sha256=hJSN7dK1rqz9hlmwyNwBmRAEDPu5MsZ2lguayd0J7WQ,4139 +PIL/XVThumbImagePlugin.py,sha256=sNyLCE1Y4ayyiH7mkJ2UYULNn-D61x_fPh_IJmwJ_Uc,1845 +PIL/ImageMorph.py,sha256=2smSgIxQYKwBDUlfWou2jygjZXDgDhcJcaa7AEmsRkY,7962 +PIL/_util.py,sha256=b3m5R7BosKxQ1NJud7vuxdmFouSfSYoaJ2P7WqlGtcE,553 +PIL/PaletteFile.py,sha256=wleoMhAafS87ZSWktV2FzOZKnAtHdDrtruEtj_Bnzf0,1105 +PIL/PcdImagePlugin.py,sha256=wx-mpL8jhCfkuXm5QW6SwOrX3yNzFIe60SzwEUEj7ww,1815 +PIL/ImtImagePlugin.py,sha256=hleP-mZH7EF2JKdsmi4rVZrdMCJLJl_nRYtmOw7IyyU,2222 +PIL/IcoImagePlugin.py,sha256=v3aBbHDCTLfOAP7ZnupTV5rQJ06etROv89Khv0ttwTg,9206 +PIL/ImImagePlugin.py,sha256=dCEPB2MuhPZCPewKoirr4G-0Nea9ZWBEn43ib_Dbxz0,10104 +PIL/FpxImagePlugin.py,sha256=acs5bKxAm5SqEaoUTsRNilZXw_af2ErBGNFF7peuSdI,6265 +PIL/BdfFontFile.py,sha256=EVmwabLMhOVnPBsUH4b0VOI_iBmBbbedeBWS20B7f2c,3355 +PIL/PixarImagePlugin.py,sha256=ruRFUTTWjp-Tw_xl6FKaWIDQYwguL6Z-2CasW6DZWRM,1614 +PIL/ImageDraw.py,sha256=fP89EuWkq9cZmWEzJl7qmVtH2RWq7xNwtqB0t5k8kSo,11667 +PIL/ImageOps.py,sha256=XPgQSyrypOQofZ800t72KezVPSdBxY5GclIjsfJEZu8,13923 +PIL/XbmImagePlugin.py,sha256=p0FBa5uS9eT5gNI08w7xainD1yxcus_SrpTPv2yuRkg,2455 +PIL/PcfFontFile.py,sha256=VmqdiYNtLI-c7hTBCqVuYkqWv40erTpON9OPNLDWuWg,6192 +PIL/MpegImagePlugin.py,sha256=GtV_O-3QhjtjJEs8f0wCYIpe33G68Udx4qYIfC7kAeg,1815 +PIL/FliImagePlugin.py,sha256=NiwSiYqbAdNG0v6ywhKkbZj17jZ_rgeuRADwUxI1n44,3460 +PIL/ImageMath.py,sha256=nJHWoTV4WOVXmZrNq-spzf18FYYPHaGjxBcWdvx--rE,7454 +PIL/PalmImagePlugin.py,sha256=IZR0mKEx8jkoJp_9rfqUJV61_QHEZ1iEoMBhyFC_5W0,9234 +PIL/MspImagePlugin.py,sha256=zJo5Ja5zkurZD0PRlqn-qqG3tRzIjqRgq_mCA4euEA8,2181 +PIL/ContainerIO.py,sha256=6N-rxsLN6xAFMWxOfwrnimYYYOC0P6ppYcM2r1Hp8Kw,2597 +PIL/GdImageFile.py,sha256=uP7YPhguHBzbBwOvNIONcpZPYnI06kULMuz-TW8SZnM,2182 +PIL/ImageMode.py,sha256=YDdvRTzi4Es9SlpK_emwR-ZbZW4pLqXhbAdUgy6c0Kw,1298 +PIL/ImageFilter.py,sha256=YbnrIBef2ebvcHMmBDZNpGAWPk92ir9lBZTJVDmdvUI,6620 +PIL/ImagePath.py,sha256=3AjcTwrReCInTyHlXxOddwn8JDyrkC2jczyi6r_xyfY,1231 +PIL/GifImagePlugin.py,sha256=cYofhFJj2_4i9GBk3LyLXx6-rVsMX3F7uAi9-3S_qU8,16887 +PIL/PyAccess.py,sha256=Cy7luHHMi0KiT0WgV4QGYDoM6ZP-6Rt65PnHDOPGTz8,8645 +PIL/ImageColor.py,sha256=3CAnIw4NwIZ5W24_fPV4FTTd-0aU1GDMuoKN5VyU_z8,7981 +PIL/XpmImagePlugin.py,sha256=Un9E6V5sQyaAHmc0B1JttqLNlUQHTyqJCvpK2E2nl2Y,3089 +PIL/PpmImagePlugin.py,sha256=sjgUZxXH4q3suPypI7teKbxs6jHTpv5aLWRny25s-UA,4581 +PIL/ImageShow.py,sha256=gaVDMpYEPsp1oDtb5tBOyx9fXmg46VtoHGroyGSyL24,4787 +PIL/TarIO.py,sha256=5TS3r1DEi4pCHCTW2Xqcwh1ff0oTgcm2bQRdZZx1Ftc,1223 +PIL/ImageTk.py,sha256=dAqzvvyPEUXtFROC_7mmc-ySfioUbZAN_Lip8scEy9Y,9092 +PIL/ImageFont.py,sha256=nNRIzt982UWS38TUG9QRDktW156oolUrUohe5Ckhfcw,18789 +PIL/SunImagePlugin.py,sha256=LLLdNIMhE2vclGvEUgES_R5zmWPVtHvctiQSKIa8RY4,1944 +PIL/Image.py,sha256=d7yGVMuDdJq68HJKhwJdqic9M8EYCpLpXG3xykrEEA4,80214 +PIL/ImagePalette.py,sha256=ApHiA8ZTaXT3OvKNi_44z8frdP9HLNjkQlNGHeeeVlk,6397 +PIL/__init__.py,sha256=-hmODD6O4QCii4i08pZzccu9rwDrFkOtPDvbpq_jAtU,1554 +PIL/Jpeg2KImagePlugin.py,sha256=BXjgwpeio6NZst1jo_fuEsDgGxTkJ50T5K6LTk5-Tik,7747 +PIL/ImageWin.py,sha256=2ERIZhzJ5d1w5aHcyeO0ONUQyGgDi_JHZ43WZW4HHf8,7668 +PIL/SpiderImagePlugin.py,sha256=izK7YOGjidy14zaMvkMvILMg3tSPZSRFUw-btGX75wI,9202 +PIL/ImageStat.py,sha256=XhzAmXbvcTKpYrT9KYrnoeytUBormN1e2tMo1LOVkNg,3826 +PIL/ImageDraw2.py,sha256=-yad95PlntI3f-biWPMJ5CfAtSSrXVMppVDdtBU0A6M,3199 +PIL/ImageCms.py,sha256=ljjLvs4e-53M9hztfKhZ5ajPUZKksWU5v2GPC8iZG-A,37131 +PIL/SgiImagePlugin.py,sha256=8UyPCPeaKe4LdPHl7XciZyyv-_1nMzicbF9G9FBYTsM,2111 +PIL/GimpGradientFile.py,sha256=xYXgu2wU7TKyUWxQ2zjjEeVLwfPylNjyzB0-AhkKZjs,3339 +PIL/MpoImagePlugin.py,sha256=jRYN6JB9ldSQdCTyG6u_8lfvgkqHuBAdzYiZe1ZCa68,2793 +PIL/BmpImagePlugin.py,sha256=QtlMM5K1zf9_HyLWCzL_1tHSghdFdYl543qbuZ4KFS8,7435 +PIL/TiffTags.py,sha256=M3lhOlg4o7hO6V4U_4RI9SrHCVZY3S4tUyb1U1YQ72o,7452 +PIL/EpsImagePlugin.py,sha256=V3errmq2KxdhPdfCntPgBfRThWcCMUSwPhKtxk8BgTk,12396 +PIL/BufrStubImagePlugin.py,sha256=bnQDTRwjOe-uDVkmO8KuW6-DsN2-eNL7zWh7aCdeOeY,1504 +PIL/IcnsImagePlugin.py,sha256=OK7J1Dt7StQl3XdKk3XH2d31u04-JFuCY3l9gqEETHo,9201 +PIL/ImageFile.py,sha256=eSCBDBNMBHawIPuADxBGtqkSfbf1RJYA94pFcNbrpXk,16025 +PIL/ImageQt.py,sha256=2WxO5mFqLUffgg-J9AecZVMJTqTG6uEZqserdCyDPUo,2888 +PIL/ImageFileIO.py,sha256=8F1GcVY9rYgzrmJJpb0ga2ATj9arscdUOO8CVZ4OAhA,1022 +PIL/WalImageFile.py,sha256=Ty1vo2r6Nzxytin_FKAjuPQg1_veSpr8MNJB1aa8vfo,5506 +PIL/PcxImagePlugin.py,sha256=ol1EeBMhrjPRUqlrlWaTS0HPXbvVDFXrhwqpY0qwFCE,5285 +PIL/Hdf5StubImagePlugin.py,sha256=AthIydrNqzKj_68Pa29b08fKKMu089A_Ef_G49aLLbo,1549 +PIL/FontFile.py,sha256=Irugt01KYj5fz5GeIcA1Gt1vk8mdpfbpe10R7T_dFGs,2825 +PIL/JpegImagePlugin.py,sha256=gp9S9vDTV4r8b0u7zcK9SeeOwMiP8RMUHD-gzgEe-V8,25018 +PIL/PdfImagePlugin.py,sha256=ByPn-u7rgGnBkl9KE-D2Cbpe1GTbBU5iVOJEdf06m4g,5807 +PIL/ImageTransform.py,sha256=rksN-m5SPqnTuINfQt_4Pkm_Qbfef78Lx-qK1CteeE0,2876 +PIL/IptcImagePlugin.py,sha256=CIGSzoYj7wl0AGIS4jkS4BBd-fJDbdBxtee_jwbGXIQ,6964 +PIL/McIdasImagePlugin.py,sha256=LJIKViAbq0IiN4E3M0XKFS2YGrEIifNq5lstCaqgmMA,1755 +PIL/ImageSequence.py,sha256=lnB9kn5jJ6Q227sJfpfUsEOnY49AcVt1Qh7kaEEZdLA,939 +PIL/_binary.py,sha256=j9qHnEQhqutp-bo6Rp0xplvkLjLdgb_Z6R8JeSLZzVQ,1575 +PIL/PSDraw.py,sha256=g0nun40mCBb9MYHqanG90Kkr8hDVc1PyW1uPltoiRrY,6872 +PIL/OleFileIO.py,sha256=I9xRyB2SnMv2U6gGza-mVi0BME88WBSXcvRWnEw2tU4,89467 +PIL/GbrImagePlugin.py,sha256=Ak2MaIQXd4JYoFaB08epg0LK6CNJj85NRuagZu2_cEo,1581 +PIL/GimpPaletteFile.py,sha256=qhHZPMXNRGHcAZuF45jG2qqz_VvDc8Jnv_UEE-Qergo,1333 +PIL/JpegPresets.py,sha256=rl9QjI3re1Ej50gYZCFekdYmndmBHRyyeRDuOvGp4kw,12335 +PIL/PsdImagePlugin.py,sha256=th2ZytaMH97XaBh9sAyq_0NrHuNHpEsXdH-j_Vi2w8k,7424 +PIL/TgaImagePlugin.py,sha256=O53Fq965kUToz5e-W9a8Jamc9_FDcoh9D3SZOjShKUk,4969 +PIL/DcxImagePlugin.py,sha256=rUnOlhFJKwDsD76b4K7vzvsc0pbMiKBsH8P25Kmwr0Q,1803 +PIL/PngImagePlugin.py,sha256=QWV9OUoVtdKF1VmQANlOs-DB1UJx421yERZi5Hs9BhQ,23339 +PIL/WebPImagePlugin.py,sha256=1GoMv5wCH8egdyhLu17FSBOha9WNQ_RvOjpgtfwY8K0,1961 +PIL/ImageEnhance.py,sha256=wYnG1q_AAOZGGoaBxcBSyOWgOytQhiBsJc97Sbk1BMI,3176 +PIL/CurImagePlugin.py,sha256=uyQuhu7gNcR4QHtPt9Hm2Lv89X9QB6ZscSaqc2eo-nc,1943 +PIL/ImageChops.py,sha256=jZCVJwGjIpbe4tKudbxpsLVvel4FvujQcd3HbFdtLCM,6184 +PIL/ExifTags.py,sha256=klIomCR-gXvHbeRRPmFg-PFJWEWw3DO_ZFPjmnoiPiA,5092 +PIL/FitsStubImagePlugin.py,sha256=Gebku65jfMMtEhblt3gy_RMGFx9kROq4PPAof_wo-lc,1653 +PIL/MicImagePlugin.py,sha256=r-O0lEDmS9ELkX2TzToViGcSUol_xJb41qYQpr9M8Hs,2174 +PIL/TiffImagePlugin.py,sha256=t1KBkSpXbdjjZvPBmYhVmRbyfQ4QVi8cI17LQOKPcAw,43236 +PIL/ImageGrab.py,sha256=ag9826OLUE6e6nhF0MccPXkfoPta6DxBuRoFE7fP-vs,1218 +PIL/OleFileIO-README.md,sha256=7BhnoybuHsGmbaGvQt4-juROtNpIzQZ9UvrXoEsJv0w,17799 +PIL/_imaging.cpython-34m.so,sha256=XU0qy3DvtC02gZU3zlC6eRBFmhG9VOiiH0ByJ44U2Hg,792244 +PIL/_imagingmath.cpython-34m.so,sha256=XMeSwaTX7oQ8DfVzo4jKe70nph79ysE7v7-_xMB4iQU,43814 +PIL/_imagingmorph.cpython-34m.so,sha256=WRwgLXUywU8suOp7hXDHJVAo6Uuo8P1JFL03Ygdi46I,22691 diff --git a/pyPackages/pillowx86/Pillow-2.7.0.dist-info/WHEEL b/pyPackages/pillowx86/Pillow-2.7.0.dist-info/WHEEL new file mode 100644 index 0000000..553e0d2 --- /dev/null +++ b/pyPackages/pillowx86/Pillow-2.7.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.24.0) +Root-Is-Purelib: false +Tag: cp34-cp34m-linux_i686 + diff --git a/pyPackages/pillowx86/Pillow-2.7.0.dist-info/metadata.json b/pyPackages/pillowx86/Pillow-2.7.0.dist-info/metadata.json new file mode 100644 index 0000000..25d7d24 --- /dev/null +++ b/pyPackages/pillowx86/Pillow-2.7.0.dist-info/metadata.json @@ -0,0 +1 @@ +{"metadata_version": "2.0", "license": "Standard PIL License", "name": "Pillow", "extensions": {"python.details": {"project_urls": {"Home": "http://python-pillow.github.io/"}, "contacts": [{"role": "author", "email": "aclark@aclark.net", "name": "Alex Clark (fork author)"}], "document_names": {"description": "DESCRIPTION.rst"}}}, "version": "2.7.0", "keywords": ["Imaging"], "summary": "Python Imaging Library (Fork)", "generator": "bdist_wheel (0.24.0)", "classifiers": ["Development Status :: 6 - Mature", "Topic :: Multimedia :: Graphics", "Topic :: Multimedia :: Graphics :: Capture :: Digital Camera", "Topic :: Multimedia :: Graphics :: Capture :: Scanners", "Topic :: Multimedia :: Graphics :: Capture :: Screen Capture", "Topic :: Multimedia :: Graphics :: Graphics Conversion", "Topic :: Multimedia :: Graphics :: Viewers", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4"]} \ No newline at end of file diff --git a/pyPackages/pillowx86/Pillow-2.7.0.dist-info/top_level.txt b/pyPackages/pillowx86/Pillow-2.7.0.dist-info/top_level.txt new file mode 100644 index 0000000..b338169 --- /dev/null +++ b/pyPackages/pillowx86/Pillow-2.7.0.dist-info/top_level.txt @@ -0,0 +1 @@ +PIL diff --git a/pyPackages/pillowx86/Pillow-2.7.0.dist-info/zip-safe b/pyPackages/pillowx86/Pillow-2.7.0.dist-info/zip-safe new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/pyPackages/pillowx86/Pillow-2.7.0.dist-info/zip-safe @@ -0,0 +1 @@ + diff --git a/qml/Cover.qml b/qml/Cover.qml new file mode 100644 index 0000000..7422e2c --- /dev/null +++ b/qml/Cover.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import Sailfish.Silica 1.0 + +CoverBackground { + CoverPlaceholder { + text: "Sailfish Scanner" + } +} diff --git a/qml/FileChooser.qml b/qml/FileChooser.qml new file mode 100644 index 0000000..768f27f --- /dev/null +++ b/qml/FileChooser.qml @@ -0,0 +1,204 @@ +/* + * FileChooser.qml + * Copyright (C) Damien Caliste 2013-2014 + * + * FileChooser.qml is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +import QtQuick 2.0 +import Sailfish.Silica 1.0 +import Qt.labs.folderlistmodel 1.0 + +Item { + id: chooser_item + property string selection + property string entry + property bool saveMode: false + property alias saveText: chooser_entry.text + + Row { + width: parent.width + anchors.top: parent.top + TextField { + id: chooser_entry + width: parent.width - chooser_adddir.width + placeholderText: "enter a new file name" + validator: RegExpValidator { regExp: /^[^/]+$/ } + inputMethodHints: Qt.ImhNoPredictiveText + EnterKey.text: "save" + EnterKey.onClicked: { if (acceptableInput) { chooser_item.entry = folderModel.folder + "/" + text } } + onFocusChanged: { if (focus) { selectAll() } } + } + IconButton { + id: chooser_adddir + icon.source: "image://theme/icon-m-folder" + enabled: chooser_entry.acceptableInput + onClicked: { } + } + visible: chooser_item.saveMode + } + Label { + width: parent.width + visible: chooser_item.saveMode + text: "or select an existing one" + font.pixelSize: Theme.fontSizeSmall + color: Theme.secondaryColor + horizontalAlignment: Text.AlignHCenter + } + Row { + id: chooser_head + height: Theme.itemSizeSmall + width: parent.width + IconButton { + id: chooser_back + icon.source: "image://theme/icon-header-back" + enabled: folderModel.dirname().length > 0 + onClicked: { folderModel.navigateUp() } + } + Button { + width: parent.width - chooser_back.width - chooser_options.width + text: folderModel.basename() + visible: folderModel.dirname().length > 0 + enabled: false + } + IconButton { + id: chooser_options + height: Theme.itemSizeSmall + icon.source: "image://theme/icon-m-levels" + onClicked: { chooser_controls.open = !chooser_controls.open } + } + } + } + + FolderListModel { + id: folderModel + folder: StandardPaths.documents + function basename() { + var url = folder.toString() + if (url == "file:///home/nemo") { + return "My Jolla" + } else if (url == "file:///sdcard") { + return "My Android" + } else { + return url.substring(url.lastIndexOf("/") + 1) + } + } + function dirname() { + var url = parentFolder.toString() + if (url == "file:///home") { + return "file:///media/sdcard" + } else if (url == "file:///media") { + return "file:///sdcard" + } else if (url == "file:///") { + return "file:///home/nemo" + } else { + return url + } + } + function navigateUp() { + var url = dirname() + folder = url + if (url == "file:///media") { + append({"fileName": "My Jolla", "filePath": "file:///home/nemo"}) + } + } + } + SilicaListView { + id: chooser_list + header: Item { + id: header + width: chooser_header.width + height: chooser_header.height + Component.onCompleted: chooser_header.parent = header + onHeightChanged: chooser_list.contentY = - height + } + anchors { + fill: parent + rightMargin: page.isPortrait ? 0 : chooser_controls.visibleSize + bottomMargin: page.isPortrait ? chooser_controls.visibleSize + Theme.paddingLarge: 0 + } + model: folderModel + Formatter { + id: formatter + } + ViewPlaceholder { + enabled: folderModel.count == 0 + text: "No files" + } + delegate: ListItem { + contentHeight: Theme.itemSizeSmall + Image { + id: chooser_icon + source: fileIsDir ? "image://theme/icon-m-folder" : "image://theme/icon-m-document" + //visible: fileIsDir + anchors.left: parent.left + anchors.leftMargin: Theme.paddingSmall + anchors.rightMargin: Theme.paddingSmall + anchors.verticalCenter: parent.verticalCenter + } + Label { + text: fileName + truncationMode: TruncationMode.Fade + font.pixelSize: Theme.fontSizeSmall + anchors.topMargin: Theme.paddingSmall + anchors.right: img_go.left + anchors.left: chooser_icon.right + color: highlighted ? Theme.highlightColor : Theme.primaryColor + } + Label { + font.pixelSize: Theme.fontSizeExtraSmall + text: formatter.formatDate(fileModified, Formatter.Timepoint) + " - " + formatter.formatFileSize(fileSize) + color: Theme.secondaryColor + anchors.right: img_go.left + anchors.bottom: parent.bottom + } + Image { + id: img_go + source: "image://theme/icon-m-right" + anchors.right: parent.right + anchors.leftMargin: Theme.paddingSmall + anchors.rightMargin: Theme.paddingSmall + anchors.verticalCenter: parent.verticalCenter + } + onClicked: { fileIsDir ? folderModel.folder = filePath : selection = filePath } + } +} + VerticalScrollDecorator { flickable: chooser_list } + DockedPanel { + id: chooser_controls + open: false + width: parent.width + height: parent.height + dock: Dock.Bottom + + Flow { + anchors.fill: parent + + anchors.centerIn: parent + ComboBox { + width: parent.width / 2 + label: "sort by" + menu: ContextMenu { + MenuItem { text: "name"; onClicked: { folderModel.sortField = FolderListModel.Name } } + MenuItem { text: "date"; onClicked: { folderModel.sortField = FolderListModel.Time } } + MenuItem { text: "type"; onClicked: { folderModel.sortField = FolderListModel.Type } } + } + } + TextSwitch { + width: page.isPortrait ? parent.width / 2 : parent.width + text: "reversed" + onCheckedChanged: { folderModel.sortReversed = checked } + } + } + } +} diff --git a/qml/FileChooser2.qml b/qml/FileChooser2.qml new file mode 100644 index 0000000..6b7cc46 --- /dev/null +++ b/qml/FileChooser2.qml @@ -0,0 +1,96 @@ +/* + * FileChooser.qml + * Copyright (C) Damien Caliste 2013-2014 + * + * FileChooser.qml is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +import QtQuick 2.0 +import Sailfish.Silica 1.0 +import Qt.labs.folderlistmodel 1.0 + +Item { + id: chooser_item + function getEntry() { + } + + Column { + id: chooser_header + width: parent.width + Label { + width: parent.width + text: "Picture Chooser" + font.pixelSize: Theme.fontSizeSmall + color: Theme.secondaryColor + horizontalAlignment: Text.AlignHCenter + } + Row { + id: chooser_head + width: parent.width + IconButton { + id: chooser_back + icon.source: "image://theme/icon-header-back" + onClicked: { folderModel.navigateUp() } + } + Label { + width: parent.width - chooser_back.width + text: folderModel.folder + } + } + } + FolderListModel { + id: folderModel + folder: Qt.resolvedUrl('~') + showDirsFirst: true + rootFolder: Qt.resolvedUrl('/') + function navigateUp(){ + if (folderModel.folder == "file:///") { + return + } else { + folderModel.folder = folderModel.parentFolder + } + } + function fileSelected(fileName,filePath){ + py.call("main.helloMyWorld",[filePath],function(ret){ + pageStack.push(Qt.resolvedUrl("ImageView.qml")) + }) + } + } + SilicaListView { + id: chooser_list + anchors.top: chooser_header.bottom + width: parent.width + anchors.bottom: parent.bottom + model: folderModel + Formatter { + id: formatter + } + delegate: ListItem { + id: listItems + Label { + anchors.fill: parent + anchors.topMargin: Theme.paddingSmall + color: fileIsDir ? Theme.primaryColor : Theme.secondaryColor + text: fileName + } + Label { + font.pixelSize: Theme.fontSizeExtraSmall + text: formatter.formatDate(fileModified, Formatter.Timepoint) + " - " + formatter.formatFileSize(fileSize) + color: Theme.secondaryColor + anchors.rightMargin: Theme.paddingSmall + anchors.bottom: parent.bottom + } + onClicked: { fileIsDir ? folderModel.folder = filePath : folderModel.fileSelected(fileName,filePath)} + } + } +} diff --git a/qml/ImageView.qml b/qml/ImageView.qml new file mode 100644 index 0000000..6a95db9 --- /dev/null +++ b/qml/ImageView.qml @@ -0,0 +1,10 @@ +import QtQuick 2.0 +import Sailfish.Silica 1.0 + +Page { + Image { + anchors.fill: parent + source: "image://python/loadedImage" + } + +} diff --git a/qml/Main.qml b/qml/Main.qml new file mode 100644 index 0000000..9938ff2 --- /dev/null +++ b/qml/Main.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import Sailfish.Silica 1.0 + +Page { + id: main + Button{ + } +} diff --git a/qml/sailfish-python.qml b/qml/sailfish-python.qml new file mode 100644 index 0000000..8c71c86 --- /dev/null +++ b/qml/sailfish-python.qml @@ -0,0 +1,24 @@ +import QtQuick 2.0 +import Sailfish.Silica 1.0 +import io.thp.pyotherside 1.3 + +ApplicationWindow { + id: root + cover: Qt.resolvedUrl("Cover.qml") + Python { + id: py + Component.onCompleted: { + py.addImportPath(Qt.resolvedUrl('../src')); + py.importModule_sync("os") + if (py.evaluate("os.uname().machine") == "armv7l"){ + py.addImportPath(Qt.resolvedUrl('../src/pyPackages/pillowarmv7l')); + } else { + py.addImportPath(Qt.resolvedUrl('../src/pyPackages/pillowx86')); + } + py.importModule('main',function(){ + py.call("helloworld",[]) + pageStack.push(Qt.resolvedUrl("Main.qml")) + }) + } + } +} diff --git a/renamep.py b/renamep.py new file mode 100755 index 0000000..6c9217a --- /dev/null +++ b/renamep.py @@ -0,0 +1,26 @@ +#!/usr/bin/python3 -u + +import os,re,argparse + +parser = argparse.ArgumentParser(description='namechange') +parser.add_argument('newname', metavar='name', type=str, + help='newname') +args=parser.parse_args() + +newname=args.newname +oldname=open("./appname.txt").read()[:-1] + +os.rename("./qml/"+oldname+".qml","./qml/"+newname+".qml") +os.rename("./dat/"+oldname+".desktop","./dat/"+newname+".desktop") +os.rename("./dat/"+oldname+".sh","./dat/"+newname+".sh") + +makefile=open("./Makefile","r") +maketxt=makefile.read() +makefile.close() +makefile=open("./Makefile","w") +makefile.write(re.sub("Appname:="+oldname,"Appname:="+newname,maketxt)) +makefile.close() + +namefile=open("./appname.txt","w") +namefile.write(newname+"\n") +namefile.close() diff --git a/sailfish-python-noarch.rpm b/sailfish-python-noarch.rpm new file mode 100644 index 0000000000000000000000000000000000000000..494f5942ea4de9635c2df162dd0ffb460bde67b2 GIT binary patch literal 10344 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