From ac039b6cf6c9c9aca4b552eed50f233b122508d2 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Fri, 13 Mar 2026 15:17:46 -0400 Subject: [PATCH] feat: Implement Universal Mirroring with vendorized dependencies and portable bundling --- voxvera/__init__.py | 9 +- voxvera/cli.py | 73 +- voxvera/vendor/InquirerPy/__init__.py | 2 + voxvera/vendor/InquirerPy/base/__init__.py | 15 + voxvera/vendor/InquirerPy/base/complex.py | 294 + voxvera/vendor/InquirerPy/base/control.py | 227 + voxvera/vendor/InquirerPy/base/list.py | 238 + voxvera/vendor/InquirerPy/base/simple.py | 378 + .../vendor/InquirerPy/containers/__init__.py | 1 + .../InquirerPy/containers/instruction.py | 38 + .../vendor/InquirerPy/containers/message.py | 42 + .../vendor/InquirerPy/containers/spinner.py | 108 + .../InquirerPy/containers/validation.py | 60 + voxvera/vendor/InquirerPy/enum.py | 7 + voxvera/vendor/InquirerPy/exceptions.py | 25 + voxvera/vendor/InquirerPy/inquirer.py | 17 + voxvera/vendor/InquirerPy/prompts/__init__.py | 11 + voxvera/vendor/InquirerPy/prompts/checkbox.py | 244 + voxvera/vendor/InquirerPy/prompts/confirm.py | 196 + voxvera/vendor/InquirerPy/prompts/expand.py | 458 + voxvera/vendor/InquirerPy/prompts/filepath.py | 192 + voxvera/vendor/InquirerPy/prompts/fuzzy.py | 680 + voxvera/vendor/InquirerPy/prompts/input.py | 253 + voxvera/vendor/InquirerPy/prompts/list.py | 368 + voxvera/vendor/InquirerPy/prompts/number.py | 616 + voxvera/vendor/InquirerPy/prompts/rawlist.py | 285 + voxvera/vendor/InquirerPy/prompts/secret.py | 134 + voxvera/vendor/InquirerPy/py.typed | 1 + voxvera/vendor/InquirerPy/resolver.py | 219 + voxvera/vendor/InquirerPy/separator.py | 23 + voxvera/vendor/InquirerPy/utils.py | 290 + voxvera/vendor/InquirerPy/validator.py | 165 + voxvera/vendor/PIL/AvifImagePlugin.py | 293 + voxvera/vendor/PIL/BdfFontFile.py | 122 + voxvera/vendor/PIL/BlpImagePlugin.py | 498 + voxvera/vendor/PIL/BmpImagePlugin.py | 517 + voxvera/vendor/PIL/BufrStubImagePlugin.py | 76 + voxvera/vendor/PIL/ContainerIO.py | 173 + voxvera/vendor/PIL/CurImagePlugin.py | 75 + voxvera/vendor/PIL/DcxImagePlugin.py | 84 + voxvera/vendor/PIL/DdsImagePlugin.py | 625 + voxvera/vendor/PIL/EpsImagePlugin.py | 481 + voxvera/vendor/PIL/ExifTags.py | 382 + voxvera/vendor/PIL/FitsImagePlugin.py | 152 + voxvera/vendor/PIL/FliImagePlugin.py | 184 + voxvera/vendor/PIL/FontFile.py | 134 + voxvera/vendor/PIL/FpxImagePlugin.py | 259 + voxvera/vendor/PIL/FtexImagePlugin.py | 115 + voxvera/vendor/PIL/GbrImagePlugin.py | 103 + voxvera/vendor/PIL/GdImageFile.py | 102 + voxvera/vendor/PIL/GifImagePlugin.py | 1217 ++ voxvera/vendor/PIL/GimpGradientFile.py | 153 + voxvera/vendor/PIL/GimpPaletteFile.py | 75 + voxvera/vendor/PIL/GribStubImagePlugin.py | 76 + voxvera/vendor/PIL/Hdf5StubImagePlugin.py | 76 + voxvera/vendor/PIL/IcnsImagePlugin.py | 402 + voxvera/vendor/PIL/IcoImagePlugin.py | 396 + voxvera/vendor/PIL/ImImagePlugin.py | 390 + voxvera/vendor/PIL/Image.py | 4246 +++++ voxvera/vendor/PIL/ImageChops.py | 311 + voxvera/vendor/PIL/ImageCms.py | 1076 ++ voxvera/vendor/PIL/ImageColor.py | 320 + voxvera/vendor/PIL/ImageDraw.py | 1036 ++ voxvera/vendor/PIL/ImageDraw2.py | 243 + voxvera/vendor/PIL/ImageEnhance.py | 113 + voxvera/vendor/PIL/ImageFile.py | 938 ++ voxvera/vendor/PIL/ImageFilter.py | 607 + voxvera/vendor/PIL/ImageFont.py | 1320 ++ voxvera/vendor/PIL/ImageGrab.py | 224 + voxvera/vendor/PIL/ImageMath.py | 314 + voxvera/vendor/PIL/ImageMode.py | 85 + voxvera/vendor/PIL/ImageMorph.py | 317 + voxvera/vendor/PIL/ImageOps.py | 746 + voxvera/vendor/PIL/ImagePalette.py | 287 + voxvera/vendor/PIL/ImagePath.py | 20 + voxvera/vendor/PIL/ImageQt.py | 219 + voxvera/vendor/PIL/ImageSequence.py | 88 + voxvera/vendor/PIL/ImageShow.py | 362 + voxvera/vendor/PIL/ImageStat.py | 167 + voxvera/vendor/PIL/ImageText.py | 320 + voxvera/vendor/PIL/ImageTk.py | 266 + voxvera/vendor/PIL/ImageTransform.py | 136 + voxvera/vendor/PIL/ImageWin.py | 247 + voxvera/vendor/PIL/ImtImagePlugin.py | 103 + voxvera/vendor/PIL/IptcImagePlugin.py | 233 + voxvera/vendor/PIL/Jpeg2KImagePlugin.py | 448 + voxvera/vendor/PIL/JpegImagePlugin.py | 895 + voxvera/vendor/PIL/JpegPresets.py | 242 + voxvera/vendor/PIL/McIdasImagePlugin.py | 78 + voxvera/vendor/PIL/MicImagePlugin.py | 103 + voxvera/vendor/PIL/MpegImagePlugin.py | 84 + voxvera/vendor/PIL/MpoImagePlugin.py | 204 + voxvera/vendor/PIL/MspImagePlugin.py | 200 + voxvera/vendor/PIL/PSDraw.py | 237 + voxvera/vendor/PIL/PaletteFile.py | 54 + voxvera/vendor/PIL/PalmImagePlugin.py | 217 + voxvera/vendor/PIL/PcdImagePlugin.py | 68 + voxvera/vendor/PIL/PcfFontFile.py | 258 + voxvera/vendor/PIL/PcxImagePlugin.py | 228 + voxvera/vendor/PIL/PdfImagePlugin.py | 311 + voxvera/vendor/PIL/PdfParser.py | 1075 ++ voxvera/vendor/PIL/PixarImagePlugin.py | 72 + voxvera/vendor/PIL/PngImagePlugin.py | 1564 ++ voxvera/vendor/PIL/PpmImagePlugin.py | 375 + voxvera/vendor/PIL/PsdImagePlugin.py | 334 + voxvera/vendor/PIL/QoiImagePlugin.py | 235 + voxvera/vendor/PIL/SgiImagePlugin.py | 231 + voxvera/vendor/PIL/SpiderImagePlugin.py | 332 + voxvera/vendor/PIL/SunImagePlugin.py | 145 + voxvera/vendor/PIL/TarIO.py | 61 + voxvera/vendor/PIL/TgaImagePlugin.py | 264 + voxvera/vendor/PIL/TiffImagePlugin.py | 2338 +++ voxvera/vendor/PIL/TiffTags.py | 566 + voxvera/vendor/PIL/WalImageFile.py | 128 + voxvera/vendor/PIL/WebPImagePlugin.py | 317 + voxvera/vendor/PIL/WmfImagePlugin.py | 188 + voxvera/vendor/PIL/XVThumbImagePlugin.py | 83 + voxvera/vendor/PIL/XbmImagePlugin.py | 98 + voxvera/vendor/PIL/XpmImagePlugin.py | 157 + voxvera/vendor/PIL/__init__.py | 87 + voxvera/vendor/PIL/__main__.py | 7 + .../PIL/_avif.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 87889 bytes voxvera/vendor/PIL/_avif.pyi | 3 + voxvera/vendor/PIL/_binary.py | 112 + voxvera/vendor/PIL/_deprecate.py | 72 + .../_imaging.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 3357545 bytes voxvera/vendor/PIL/_imaging.pyi | 31 + ...imagingcms.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 157841 bytes voxvera/vendor/PIL/_imagingcms.pyi | 143 + ..._imagingft.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 322977 bytes voxvera/vendor/PIL/_imagingft.pyi | 70 + ...magingmath.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 167536 bytes voxvera/vendor/PIL/_imagingmath.pyi | 3 + ...agingmorph.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 37040 bytes voxvera/vendor/PIL/_imagingmorph.pyi | 3 + ..._imagingtk.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 47000 bytes voxvera/vendor/PIL/_imagingtk.pyi | 3 + voxvera/vendor/PIL/_tkinter_finder.py | 20 + voxvera/vendor/PIL/_typing.py | 45 + voxvera/vendor/PIL/_util.py | 29 + voxvera/vendor/PIL/_version.py | 4 + .../PIL/_webp.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 108849 bytes voxvera/vendor/PIL/_webp.pyi | 3 + voxvera/vendor/PIL/features.py | 343 + voxvera/vendor/PIL/py.typed | 0 voxvera/vendor/PIL/report.py | 5 + voxvera/vendor/bin/htmlmin | 8 + voxvera/vendor/bin/markdown-it | 8 + voxvera/vendor/bin/pygmentize | 8 + voxvera/vendor/bin/qr | 8 + voxvera/vendor/htmlmin/__init__.py | 30 + voxvera/vendor/htmlmin/command.py | 175 + voxvera/vendor/htmlmin/decorator.py | 64 + voxvera/vendor/htmlmin/escape.py | 201 + voxvera/vendor/htmlmin/main.py | 191 + voxvera/vendor/htmlmin/middleware.py | 92 + voxvera/vendor/htmlmin/parser.py | 408 + .../vendor/htmlmin/python3html/__init__.py | 139 + voxvera/vendor/htmlmin/python3html/parser.py | 481 + voxvera/vendor/htmlmin/tests/__init__.py | 0 voxvera/vendor/htmlmin/tests/test_escape.py | 103 + voxvera/vendor/htmlmin/tests/tests.py | 617 + .../htmlmin2-0.1.13.dist-info/INSTALLER | 1 + .../vendor/htmlmin2-0.1.13.dist-info/LICENSE | 24 + .../vendor/htmlmin2-0.1.13.dist-info/METADATA | 27 + .../vendor/htmlmin2-0.1.13.dist-info/RECORD | 22 + .../htmlmin2-0.1.13.dist-info/REQUESTED | 0 .../vendor/htmlmin2-0.1.13.dist-info/WHEEL | 5 + .../entry_points.txt | 2 + .../htmlmin2-0.1.13.dist-info/top_level.txt | 1 + .../vendor/htmlmin2-0.1.13.dist-info/zip-safe | 1 + .../inquirerpy-0.3.4.dist-info/INSTALLER | 1 + .../vendor/inquirerpy-0.3.4.dist-info/LICENSE | 21 + .../inquirerpy-0.3.4.dist-info/METADATA | 191 + .../vendor/inquirerpy-0.3.4.dist-info/RECORD | 36 + .../inquirerpy-0.3.4.dist-info/REQUESTED | 0 .../vendor/inquirerpy-0.3.4.dist-info/WHEEL | 4 + .../vendor/jsmin-3.0.1.dist-info/INSTALLER | 1 + .../vendor/jsmin-3.0.1.dist-info/LICENSE.txt | 23 + voxvera/vendor/jsmin-3.0.1.dist-info/METADATA | 196 + voxvera/vendor/jsmin-3.0.1.dist-info/RECORD | 10 + .../vendor/jsmin-3.0.1.dist-info/REQUESTED | 0 voxvera/vendor/jsmin-3.0.1.dist-info/WHEEL | 5 + .../jsmin-3.0.1.dist-info/top_level.txt | 1 + voxvera/vendor/jsmin/__init__.py | 252 + voxvera/vendor/jsmin/__main__.py | 37 + voxvera/vendor/jsmin/test.py | 636 + voxvera/vendor/markdown_it/__init__.py | 6 + voxvera/vendor/markdown_it/_compat.py | 1 + voxvera/vendor/markdown_it/_punycode.py | 67 + voxvera/vendor/markdown_it/cli/__init__.py | 0 voxvera/vendor/markdown_it/cli/parse.py | 110 + voxvera/vendor/markdown_it/common/__init__.py | 0 voxvera/vendor/markdown_it/common/entities.py | 5 + .../vendor/markdown_it/common/html_blocks.py | 69 + voxvera/vendor/markdown_it/common/html_re.py | 39 + .../markdown_it/common/normalize_url.py | 81 + voxvera/vendor/markdown_it/common/utils.py | 313 + .../vendor/markdown_it/helpers/__init__.py | 6 + .../helpers/parse_link_destination.py | 83 + .../markdown_it/helpers/parse_link_label.py | 44 + .../markdown_it/helpers/parse_link_title.py | 75 + voxvera/vendor/markdown_it/main.py | 350 + voxvera/vendor/markdown_it/parser_block.py | 113 + voxvera/vendor/markdown_it/parser_core.py | 46 + voxvera/vendor/markdown_it/parser_inline.py | 148 + voxvera/vendor/markdown_it/port.yaml | 48 + .../vendor/markdown_it/presets/__init__.py | 28 + .../vendor/markdown_it/presets/commonmark.py | 75 + voxvera/vendor/markdown_it/presets/default.py | 36 + voxvera/vendor/markdown_it/presets/zero.py | 44 + voxvera/vendor/markdown_it/py.typed | 1 + voxvera/vendor/markdown_it/renderer.py | 336 + voxvera/vendor/markdown_it/ruler.py | 275 + .../markdown_it/rules_block/__init__.py | 27 + .../markdown_it/rules_block/blockquote.py | 299 + .../vendor/markdown_it/rules_block/code.py | 36 + .../vendor/markdown_it/rules_block/fence.py | 101 + .../vendor/markdown_it/rules_block/heading.py | 69 + voxvera/vendor/markdown_it/rules_block/hr.py | 56 + .../markdown_it/rules_block/html_block.py | 90 + .../markdown_it/rules_block/lheading.py | 86 + .../vendor/markdown_it/rules_block/list.py | 345 + .../markdown_it/rules_block/paragraph.py | 66 + .../markdown_it/rules_block/reference.py | 235 + .../markdown_it/rules_block/state_block.py | 261 + .../vendor/markdown_it/rules_block/table.py | 250 + .../vendor/markdown_it/rules_core/__init__.py | 19 + .../vendor/markdown_it/rules_core/block.py | 13 + .../vendor/markdown_it/rules_core/inline.py | 10 + .../vendor/markdown_it/rules_core/linkify.py | 149 + .../markdown_it/rules_core/normalize.py | 19 + .../markdown_it/rules_core/replacements.py | 127 + .../markdown_it/rules_core/smartquotes.py | 202 + .../markdown_it/rules_core/state_core.py | 25 + .../markdown_it/rules_core/text_join.py | 35 + .../markdown_it/rules_inline/__init__.py | 31 + .../markdown_it/rules_inline/autolink.py | 77 + .../markdown_it/rules_inline/backticks.py | 72 + .../markdown_it/rules_inline/balance_pairs.py | 138 + .../markdown_it/rules_inline/emphasis.py | 102 + .../vendor/markdown_it/rules_inline/entity.py | 53 + .../vendor/markdown_it/rules_inline/escape.py | 93 + .../rules_inline/fragments_join.py | 43 + .../markdown_it/rules_inline/html_inline.py | 43 + .../vendor/markdown_it/rules_inline/image.py | 148 + .../vendor/markdown_it/rules_inline/link.py | 149 + .../markdown_it/rules_inline/linkify.py | 62 + .../markdown_it/rules_inline/newline.py | 44 + .../markdown_it/rules_inline/state_inline.py | 165 + .../markdown_it/rules_inline/strikethrough.py | 127 + .../vendor/markdown_it/rules_inline/text.py | 62 + voxvera/vendor/markdown_it/token.py | 178 + voxvera/vendor/markdown_it/tree.py | 333 + voxvera/vendor/markdown_it/utils.py | 186 + .../markdown_it_py-4.0.0.dist-info/INSTALLER | 1 + .../markdown_it_py-4.0.0.dist-info/METADATA | 219 + .../markdown_it_py-4.0.0.dist-info/RECORD | 76 + .../markdown_it_py-4.0.0.dist-info/WHEEL | 4 + .../entry_points.txt | 3 + .../licenses/LICENSE | 21 + .../licenses/LICENSE.markdown-it | 22 + .../vendor/mdurl-0.1.2.dist-info/INSTALLER | 1 + voxvera/vendor/mdurl-0.1.2.dist-info/LICENSE | 46 + voxvera/vendor/mdurl-0.1.2.dist-info/METADATA | 32 + voxvera/vendor/mdurl-0.1.2.dist-info/RECORD | 12 + voxvera/vendor/mdurl-0.1.2.dist-info/WHEEL | 4 + voxvera/vendor/mdurl/__init__.py | 18 + voxvera/vendor/mdurl/_decode.py | 104 + voxvera/vendor/mdurl/_encode.py | 85 + voxvera/vendor/mdurl/_format.py | 27 + voxvera/vendor/mdurl/_parse.py | 304 + voxvera/vendor/mdurl/_url.py | 14 + voxvera/vendor/mdurl/py.typed | 1 + voxvera/vendor/pfzy-0.3.4.dist-info/INSTALLER | 1 + voxvera/vendor/pfzy-0.3.4.dist-info/LICENSE | 21 + voxvera/vendor/pfzy-0.3.4.dist-info/METADATA | 112 + voxvera/vendor/pfzy-0.3.4.dist-info/RECORD | 9 + voxvera/vendor/pfzy-0.3.4.dist-info/WHEEL | 4 + voxvera/vendor/pfzy/__init__.py | 2 + voxvera/vendor/pfzy/match.py | 111 + voxvera/vendor/pfzy/score.py | 307 + voxvera/vendor/pfzy/types.py | 5 + .../vendor/pillow-12.1.1.dist-info/INSTALLER | 1 + .../vendor/pillow-12.1.1.dist-info/METADATA | 175 + voxvera/vendor/pillow-12.1.1.dist-info/RECORD | 141 + .../vendor/pillow-12.1.1.dist-info/REQUESTED | 0 voxvera/vendor/pillow-12.1.1.dist-info/WHEEL | 6 + .../pillow-12.1.1.dist-info/licenses/LICENSE | 1523 ++ .../sboms/auditwheel.cdx.json | 1 + .../pillow-12.1.1.dist-info/top_level.txt | 1 + .../vendor/pillow-12.1.1.dist-info/zip-safe | 1 + .../pillow.libs/libXau-154567c4.so.6.0.0 | Bin 0 -> 22081 bytes .../pillow.libs/libavif-01e67780.so.16.3.0 | Bin 0 -> 5142057 bytes .../libbrotlicommon-c55a5f7a.so.1.2.0 | Bin 0 -> 144425 bytes .../libbrotlidec-b57ddf63.so.1.2.0 | Bin 0 -> 62337 bytes .../libfreetype-ee1c40c4.so.6.20.4 | Bin 0 -> 1463609 bytes .../libharfbuzz-0692f733.so.0.61230.0 | Bin 0 -> 932841 bytes .../pillow.libs/libjpeg-32d42e18.so.62.4.0 | Bin 0 -> 836273 bytes .../pillow.libs/liblcms2-cc10e42f.so.2.0.17 | Bin 0 -> 519073 bytes .../pillow.libs/liblzma-61b1002e.so.5.8.2 | Bin 0 -> 266369 bytes .../pillow.libs/libopenjp2-94e588ba.so.2.5.4 | Bin 0 -> 585849 bytes .../pillow.libs/libpng16-4a38ea05.so.16.53.0 | Bin 0 -> 278001 bytes .../pillow.libs/libsharpyuv-95d8a097.so.0.1.2 | Bin 0 -> 46113 bytes .../pillow.libs/libtiff-295fd75c.so.6.2.0 | Bin 0 -> 754729 bytes .../pillow.libs/libwebp-d8b9687f.so.7.2.0 | Bin 0 -> 731209 bytes .../libwebpdemux-747f2b49.so.2.0.17 | Bin 0 -> 30217 bytes .../pillow.libs/libwebpmux-7f11e5ce.so.3.1.2 | Bin 0 -> 58617 bytes .../pillow.libs/libxcb-64009ff3.so.1.1.0 | Bin 0 -> 251425 bytes .../pillow.libs/libzstd-761a17b6.so.1.5.7 | Bin 0 -> 1800497 bytes .../prompt_toolkit-3.0.52.dist-info/INSTALLER | 1 + .../prompt_toolkit-3.0.52.dist-info/METADATA | 172 + .../prompt_toolkit-3.0.52.dist-info/RECORD | 153 + .../prompt_toolkit-3.0.52.dist-info/WHEEL | 5 + .../licenses/AUTHORS.rst | 11 + .../licenses/LICENSE | 27 + .../top_level.txt | 1 + voxvera/vendor/prompt_toolkit/__init__.py | 54 + .../prompt_toolkit/application/__init__.py | 32 + .../prompt_toolkit/application/application.py | 1630 ++ .../prompt_toolkit/application/current.py | 195 + .../prompt_toolkit/application/dummy.py | 55 + .../application/run_in_terminal.py | 117 + voxvera/vendor/prompt_toolkit/auto_suggest.py | 177 + voxvera/vendor/prompt_toolkit/buffer.py | 2029 +++ voxvera/vendor/prompt_toolkit/cache.py | 127 + .../prompt_toolkit/clipboard/__init__.py | 17 + .../vendor/prompt_toolkit/clipboard/base.py | 109 + .../prompt_toolkit/clipboard/in_memory.py | 44 + .../prompt_toolkit/clipboard/pyperclip.py | 42 + .../prompt_toolkit/completion/__init__.py | 43 + .../vendor/prompt_toolkit/completion/base.py | 438 + .../prompt_toolkit/completion/deduplicate.py | 45 + .../prompt_toolkit/completion/filesystem.py | 118 + .../completion/fuzzy_completer.py | 213 + .../prompt_toolkit/completion/nested.py | 109 + .../completion/word_completer.py | 94 + .../vendor/prompt_toolkit/contrib/__init__.py | 0 .../contrib/completers/__init__.py | 5 + .../contrib/completers/system.py | 64 + .../contrib/regular_languages/__init__.py | 80 + .../contrib/regular_languages/compiler.py | 579 + .../contrib/regular_languages/completion.py | 100 + .../contrib/regular_languages/lexer.py | 94 + .../contrib/regular_languages/regex_parser.py | 279 + .../contrib/regular_languages/validation.py | 60 + .../prompt_toolkit/contrib/ssh/__init__.py | 8 + .../prompt_toolkit/contrib/ssh/server.py | 178 + .../prompt_toolkit/contrib/telnet/__init__.py | 7 + .../prompt_toolkit/contrib/telnet/log.py | 13 + .../prompt_toolkit/contrib/telnet/protocol.py | 209 + .../prompt_toolkit/contrib/telnet/server.py | 428 + .../vendor/prompt_toolkit/cursor_shapes.py | 117 + .../vendor/prompt_toolkit/data_structures.py | 18 + voxvera/vendor/prompt_toolkit/document.py | 1182 ++ voxvera/vendor/prompt_toolkit/enums.py | 19 + .../prompt_toolkit/eventloop/__init__.py | 31 + .../eventloop/async_generator.py | 125 + .../prompt_toolkit/eventloop/inputhook.py | 191 + .../vendor/prompt_toolkit/eventloop/utils.py | 101 + .../vendor/prompt_toolkit/eventloop/win32.py | 72 + .../vendor/prompt_toolkit/filters/__init__.py | 71 + voxvera/vendor/prompt_toolkit/filters/app.py | 419 + voxvera/vendor/prompt_toolkit/filters/base.py | 260 + voxvera/vendor/prompt_toolkit/filters/cli.py | 65 + .../vendor/prompt_toolkit/filters/utils.py | 41 + .../prompt_toolkit/formatted_text/__init__.py | 59 + .../prompt_toolkit/formatted_text/ansi.py | 302 + .../prompt_toolkit/formatted_text/base.py | 179 + .../prompt_toolkit/formatted_text/html.py | 145 + .../prompt_toolkit/formatted_text/pygments.py | 32 + .../prompt_toolkit/formatted_text/utils.py | 102 + voxvera/vendor/prompt_toolkit/history.py | 306 + .../vendor/prompt_toolkit/input/__init__.py | 14 + .../input/ansi_escape_sequences.py | 344 + voxvera/vendor/prompt_toolkit/input/base.py | 154 + .../vendor/prompt_toolkit/input/defaults.py | 79 + .../vendor/prompt_toolkit/input/posix_pipe.py | 118 + .../prompt_toolkit/input/posix_utils.py | 97 + .../vendor/prompt_toolkit/input/typeahead.py | 78 + voxvera/vendor/prompt_toolkit/input/vt100.py | 309 + .../prompt_toolkit/input/vt100_parser.py | 250 + voxvera/vendor/prompt_toolkit/input/win32.py | 904 + .../vendor/prompt_toolkit/input/win32_pipe.py | 156 + .../prompt_toolkit/key_binding/__init__.py | 22 + .../key_binding/bindings/__init__.py | 0 .../key_binding/bindings/auto_suggest.py | 66 + .../key_binding/bindings/basic.py | 257 + .../key_binding/bindings/completion.py | 206 + .../key_binding/bindings/cpr.py | 30 + .../key_binding/bindings/emacs.py | 563 + .../key_binding/bindings/focus.py | 26 + .../key_binding/bindings/mouse.py | 348 + .../key_binding/bindings/named_commands.py | 691 + .../key_binding/bindings/open_in_editor.py | 52 + .../key_binding/bindings/page_navigation.py | 85 + .../key_binding/bindings/scroll.py | 190 + .../key_binding/bindings/search.py | 96 + .../prompt_toolkit/key_binding/bindings/vi.py | 2233 +++ .../prompt_toolkit/key_binding/defaults.py | 63 + .../prompt_toolkit/key_binding/digraphs.py | 1378 ++ .../prompt_toolkit/key_binding/emacs_state.py | 36 + .../key_binding/key_bindings.py | 672 + .../key_binding/key_processor.py | 526 + .../prompt_toolkit/key_binding/vi_state.py | 107 + voxvera/vendor/prompt_toolkit/keys.py | 222 + .../vendor/prompt_toolkit/layout/__init__.py | 147 + .../prompt_toolkit/layout/containers.py | 2766 +++ .../vendor/prompt_toolkit/layout/controls.py | 956 ++ .../vendor/prompt_toolkit/layout/dimension.py | 216 + voxvera/vendor/prompt_toolkit/layout/dummy.py | 40 + .../vendor/prompt_toolkit/layout/layout.py | 412 + .../vendor/prompt_toolkit/layout/margins.py | 304 + voxvera/vendor/prompt_toolkit/layout/menus.py | 748 + .../prompt_toolkit/layout/mouse_handlers.py | 56 + .../prompt_toolkit/layout/processors.py | 1016 ++ .../vendor/prompt_toolkit/layout/screen.py | 323 + .../prompt_toolkit/layout/scrollable_pane.py | 494 + voxvera/vendor/prompt_toolkit/layout/utils.py | 80 + .../vendor/prompt_toolkit/lexers/__init__.py | 21 + voxvera/vendor/prompt_toolkit/lexers/base.py | 85 + .../vendor/prompt_toolkit/lexers/pygments.py | 328 + voxvera/vendor/prompt_toolkit/log.py | 13 + voxvera/vendor/prompt_toolkit/mouse_events.py | 85 + .../vendor/prompt_toolkit/output/__init__.py | 15 + voxvera/vendor/prompt_toolkit/output/base.py | 332 + .../prompt_toolkit/output/color_depth.py | 64 + .../vendor/prompt_toolkit/output/conemu.py | 65 + .../vendor/prompt_toolkit/output/defaults.py | 106 + .../prompt_toolkit/output/flush_stdout.py | 87 + .../prompt_toolkit/output/plain_text.py | 143 + voxvera/vendor/prompt_toolkit/output/vt100.py | 760 + voxvera/vendor/prompt_toolkit/output/win32.py | 684 + .../vendor/prompt_toolkit/output/windows10.py | 133 + voxvera/vendor/prompt_toolkit/patch_stdout.py | 297 + voxvera/vendor/prompt_toolkit/py.typed | 0 voxvera/vendor/prompt_toolkit/renderer.py | 820 + voxvera/vendor/prompt_toolkit/search.py | 226 + voxvera/vendor/prompt_toolkit/selection.py | 58 + .../prompt_toolkit/shortcuts/__init__.py | 49 + .../prompt_toolkit/shortcuts/choice_input.py | 311 + .../prompt_toolkit/shortcuts/dialogs.py | 330 + .../shortcuts/progress_bar/__init__.py | 33 + .../shortcuts/progress_bar/base.py | 449 + .../shortcuts/progress_bar/formatters.py | 431 + .../vendor/prompt_toolkit/shortcuts/prompt.py | 1538 ++ .../vendor/prompt_toolkit/shortcuts/utils.py | 239 + .../vendor/prompt_toolkit/styles/__init__.py | 67 + voxvera/vendor/prompt_toolkit/styles/base.py | 188 + .../vendor/prompt_toolkit/styles/defaults.py | 236 + .../prompt_toolkit/styles/named_colors.py | 162 + .../vendor/prompt_toolkit/styles/pygments.py | 70 + voxvera/vendor/prompt_toolkit/styles/style.py | 407 + .../styles/style_transformation.py | 374 + voxvera/vendor/prompt_toolkit/token.py | 9 + voxvera/vendor/prompt_toolkit/utils.py | 327 + voxvera/vendor/prompt_toolkit/validation.py | 192 + .../vendor/prompt_toolkit/widgets/__init__.py | 63 + voxvera/vendor/prompt_toolkit/widgets/base.py | 1080 ++ .../vendor/prompt_toolkit/widgets/dialogs.py | 108 + .../vendor/prompt_toolkit/widgets/menus.py | 374 + .../vendor/prompt_toolkit/widgets/toolbars.py | 370 + voxvera/vendor/prompt_toolkit/win32_types.py | 229 + .../pygments-2.19.2.dist-info/INSTALLER | 1 + .../vendor/pygments-2.19.2.dist-info/METADATA | 58 + .../vendor/pygments-2.19.2.dist-info/RECORD | 346 + .../vendor/pygments-2.19.2.dist-info/WHEEL | 4 + .../entry_points.txt | 2 + .../licenses/AUTHORS | 291 + .../licenses/LICENSE | 25 + voxvera/vendor/pygments/__init__.py | 82 + voxvera/vendor/pygments/__main__.py | 17 + voxvera/vendor/pygments/cmdline.py | 668 + voxvera/vendor/pygments/console.py | 70 + voxvera/vendor/pygments/filter.py | 70 + voxvera/vendor/pygments/filters/__init__.py | 940 ++ voxvera/vendor/pygments/formatter.py | 129 + .../vendor/pygments/formatters/__init__.py | 157 + .../vendor/pygments/formatters/_mapping.py | 23 + voxvera/vendor/pygments/formatters/bbcode.py | 108 + voxvera/vendor/pygments/formatters/groff.py | 170 + voxvera/vendor/pygments/formatters/html.py | 995 ++ voxvera/vendor/pygments/formatters/img.py | 686 + voxvera/vendor/pygments/formatters/irc.py | 154 + voxvera/vendor/pygments/formatters/latex.py | 518 + voxvera/vendor/pygments/formatters/other.py | 160 + .../vendor/pygments/formatters/pangomarkup.py | 83 + voxvera/vendor/pygments/formatters/rtf.py | 349 + voxvera/vendor/pygments/formatters/svg.py | 185 + .../vendor/pygments/formatters/terminal.py | 127 + .../vendor/pygments/formatters/terminal256.py | 338 + voxvera/vendor/pygments/lexer.py | 961 ++ voxvera/vendor/pygments/lexers/__init__.py | 362 + .../vendor/pygments/lexers/_ada_builtins.py | 103 + .../vendor/pygments/lexers/_asy_builtins.py | 1644 ++ .../vendor/pygments/lexers/_cl_builtins.py | 231 + .../vendor/pygments/lexers/_cocoa_builtins.py | 75 + .../pygments/lexers/_csound_builtins.py | 1780 ++ .../vendor/pygments/lexers/_css_builtins.py | 558 + .../pygments/lexers/_googlesql_builtins.py | 918 + .../vendor/pygments/lexers/_julia_builtins.py | 411 + .../vendor/pygments/lexers/_lasso_builtins.py | 5326 ++++++ .../pygments/lexers/_lilypond_builtins.py | 4932 ++++++ .../vendor/pygments/lexers/_lua_builtins.py | 285 + .../vendor/pygments/lexers/_luau_builtins.py | 62 + voxvera/vendor/pygments/lexers/_mapping.py | 602 + .../vendor/pygments/lexers/_mql_builtins.py | 1171 ++ .../vendor/pygments/lexers/_mysql_builtins.py | 1335 ++ .../pygments/lexers/_openedge_builtins.py | 2600 +++ .../vendor/pygments/lexers/_php_builtins.py | 3325 ++++ .../pygments/lexers/_postgres_builtins.py | 739 + .../vendor/pygments/lexers/_qlik_builtins.py | 666 + .../pygments/lexers/_scheme_builtins.py | 1609 ++ .../pygments/lexers/_scilab_builtins.py | 3093 ++++ .../pygments/lexers/_sourcemod_builtins.py | 1151 ++ .../vendor/pygments/lexers/_sql_builtins.py | 106 + .../vendor/pygments/lexers/_stan_builtins.py | 648 + .../vendor/pygments/lexers/_stata_builtins.py | 457 + .../vendor/pygments/lexers/_tsql_builtins.py | 1003 ++ .../vendor/pygments/lexers/_usd_builtins.py | 112 + .../pygments/lexers/_vbscript_builtins.py | 279 + .../vendor/pygments/lexers/_vim_builtins.py | 1938 +++ .../vendor/pygments/lexers/actionscript.py | 243 + voxvera/vendor/pygments/lexers/ada.py | 144 + voxvera/vendor/pygments/lexers/agile.py | 25 + voxvera/vendor/pygments/lexers/algebra.py | 299 + voxvera/vendor/pygments/lexers/ambient.py | 75 + voxvera/vendor/pygments/lexers/amdgpu.py | 54 + voxvera/vendor/pygments/lexers/ampl.py | 87 + voxvera/vendor/pygments/lexers/apdlexer.py | 593 + voxvera/vendor/pygments/lexers/apl.py | 103 + voxvera/vendor/pygments/lexers/archetype.py | 315 + voxvera/vendor/pygments/lexers/arrow.py | 116 + voxvera/vendor/pygments/lexers/arturo.py | 249 + voxvera/vendor/pygments/lexers/asc.py | 55 + voxvera/vendor/pygments/lexers/asm.py | 1051 ++ voxvera/vendor/pygments/lexers/asn1.py | 178 + voxvera/vendor/pygments/lexers/automation.py | 379 + voxvera/vendor/pygments/lexers/bare.py | 101 + voxvera/vendor/pygments/lexers/basic.py | 656 + voxvera/vendor/pygments/lexers/bdd.py | 57 + voxvera/vendor/pygments/lexers/berry.py | 99 + voxvera/vendor/pygments/lexers/bibtex.py | 159 + voxvera/vendor/pygments/lexers/blueprint.py | 173 + voxvera/vendor/pygments/lexers/boa.py | 97 + voxvera/vendor/pygments/lexers/bqn.py | 112 + voxvera/vendor/pygments/lexers/business.py | 625 + voxvera/vendor/pygments/lexers/c_cpp.py | 414 + voxvera/vendor/pygments/lexers/c_like.py | 738 + voxvera/vendor/pygments/lexers/capnproto.py | 74 + voxvera/vendor/pygments/lexers/carbon.py | 95 + voxvera/vendor/pygments/lexers/cddl.py | 172 + voxvera/vendor/pygments/lexers/chapel.py | 139 + voxvera/vendor/pygments/lexers/clean.py | 180 + voxvera/vendor/pygments/lexers/codeql.py | 80 + voxvera/vendor/pygments/lexers/comal.py | 81 + voxvera/vendor/pygments/lexers/compiled.py | 35 + voxvera/vendor/pygments/lexers/configs.py | 1433 ++ voxvera/vendor/pygments/lexers/console.py | 114 + voxvera/vendor/pygments/lexers/cplint.py | 43 + voxvera/vendor/pygments/lexers/crystal.py | 364 + voxvera/vendor/pygments/lexers/csound.py | 466 + voxvera/vendor/pygments/lexers/css.py | 602 + voxvera/vendor/pygments/lexers/d.py | 259 + voxvera/vendor/pygments/lexers/dalvik.py | 126 + voxvera/vendor/pygments/lexers/data.py | 763 + voxvera/vendor/pygments/lexers/dax.py | 135 + voxvera/vendor/pygments/lexers/devicetree.py | 108 + voxvera/vendor/pygments/lexers/diff.py | 169 + voxvera/vendor/pygments/lexers/dns.py | 109 + voxvera/vendor/pygments/lexers/dotnet.py | 873 + voxvera/vendor/pygments/lexers/dsls.py | 970 ++ voxvera/vendor/pygments/lexers/dylan.py | 279 + voxvera/vendor/pygments/lexers/ecl.py | 144 + voxvera/vendor/pygments/lexers/eiffel.py | 68 + voxvera/vendor/pygments/lexers/elm.py | 123 + voxvera/vendor/pygments/lexers/elpi.py | 175 + voxvera/vendor/pygments/lexers/email.py | 132 + voxvera/vendor/pygments/lexers/erlang.py | 526 + voxvera/vendor/pygments/lexers/esoteric.py | 300 + voxvera/vendor/pygments/lexers/ezhil.py | 76 + voxvera/vendor/pygments/lexers/factor.py | 363 + voxvera/vendor/pygments/lexers/fantom.py | 251 + voxvera/vendor/pygments/lexers/felix.py | 275 + voxvera/vendor/pygments/lexers/fift.py | 68 + voxvera/vendor/pygments/lexers/floscript.py | 81 + voxvera/vendor/pygments/lexers/forth.py | 178 + voxvera/vendor/pygments/lexers/fortran.py | 212 + voxvera/vendor/pygments/lexers/foxpro.py | 427 + voxvera/vendor/pygments/lexers/freefem.py | 893 + voxvera/vendor/pygments/lexers/func.py | 110 + voxvera/vendor/pygments/lexers/functional.py | 21 + voxvera/vendor/pygments/lexers/futhark.py | 105 + voxvera/vendor/pygments/lexers/gcodelexer.py | 35 + voxvera/vendor/pygments/lexers/gdscript.py | 189 + voxvera/vendor/pygments/lexers/gleam.py | 74 + voxvera/vendor/pygments/lexers/go.py | 97 + .../pygments/lexers/grammar_notation.py | 262 + voxvera/vendor/pygments/lexers/graph.py | 108 + voxvera/vendor/pygments/lexers/graphics.py | 794 + voxvera/vendor/pygments/lexers/graphql.py | 176 + voxvera/vendor/pygments/lexers/graphviz.py | 58 + voxvera/vendor/pygments/lexers/gsql.py | 103 + voxvera/vendor/pygments/lexers/hare.py | 73 + voxvera/vendor/pygments/lexers/haskell.py | 866 + voxvera/vendor/pygments/lexers/haxe.py | 935 ++ voxvera/vendor/pygments/lexers/hdl.py | 466 + voxvera/vendor/pygments/lexers/hexdump.py | 102 + voxvera/vendor/pygments/lexers/html.py | 670 + voxvera/vendor/pygments/lexers/idl.py | 284 + voxvera/vendor/pygments/lexers/igor.py | 435 + voxvera/vendor/pygments/lexers/inferno.py | 95 + voxvera/vendor/pygments/lexers/installers.py | 352 + voxvera/vendor/pygments/lexers/int_fiction.py | 1370 ++ voxvera/vendor/pygments/lexers/iolang.py | 61 + voxvera/vendor/pygments/lexers/j.py | 151 + voxvera/vendor/pygments/lexers/javascript.py | 1591 ++ voxvera/vendor/pygments/lexers/jmespath.py | 69 + voxvera/vendor/pygments/lexers/jslt.py | 94 + voxvera/vendor/pygments/lexers/json5.py | 83 + voxvera/vendor/pygments/lexers/jsonnet.py | 169 + voxvera/vendor/pygments/lexers/jsx.py | 100 + voxvera/vendor/pygments/lexers/julia.py | 294 + voxvera/vendor/pygments/lexers/jvm.py | 1802 ++ voxvera/vendor/pygments/lexers/kuin.py | 332 + voxvera/vendor/pygments/lexers/kusto.py | 93 + voxvera/vendor/pygments/lexers/ldap.py | 155 + voxvera/vendor/pygments/lexers/lean.py | 241 + voxvera/vendor/pygments/lexers/lilypond.py | 225 + voxvera/vendor/pygments/lexers/lisp.py | 3146 ++++ voxvera/vendor/pygments/lexers/macaulay2.py | 1814 ++ voxvera/vendor/pygments/lexers/make.py | 212 + voxvera/vendor/pygments/lexers/maple.py | 291 + voxvera/vendor/pygments/lexers/markup.py | 1654 ++ voxvera/vendor/pygments/lexers/math.py | 21 + voxvera/vendor/pygments/lexers/matlab.py | 3307 ++++ voxvera/vendor/pygments/lexers/maxima.py | 84 + voxvera/vendor/pygments/lexers/meson.py | 139 + voxvera/vendor/pygments/lexers/mime.py | 210 + voxvera/vendor/pygments/lexers/minecraft.py | 391 + voxvera/vendor/pygments/lexers/mips.py | 130 + voxvera/vendor/pygments/lexers/ml.py | 958 ++ voxvera/vendor/pygments/lexers/modeling.py | 366 + voxvera/vendor/pygments/lexers/modula2.py | 1579 ++ voxvera/vendor/pygments/lexers/mojo.py | 707 + voxvera/vendor/pygments/lexers/monte.py | 203 + voxvera/vendor/pygments/lexers/mosel.py | 447 + voxvera/vendor/pygments/lexers/ncl.py | 894 + voxvera/vendor/pygments/lexers/nimrod.py | 199 + voxvera/vendor/pygments/lexers/nit.py | 63 + voxvera/vendor/pygments/lexers/nix.py | 144 + voxvera/vendor/pygments/lexers/numbair.py | 63 + voxvera/vendor/pygments/lexers/oberon.py | 120 + voxvera/vendor/pygments/lexers/objective.py | 513 + voxvera/vendor/pygments/lexers/ooc.py | 84 + voxvera/vendor/pygments/lexers/openscad.py | 96 + voxvera/vendor/pygments/lexers/other.py | 41 + voxvera/vendor/pygments/lexers/parasail.py | 78 + voxvera/vendor/pygments/lexers/parsers.py | 798 + voxvera/vendor/pygments/lexers/pascal.py | 644 + voxvera/vendor/pygments/lexers/pawn.py | 202 + voxvera/vendor/pygments/lexers/pddl.py | 82 + voxvera/vendor/pygments/lexers/perl.py | 733 + voxvera/vendor/pygments/lexers/phix.py | 363 + voxvera/vendor/pygments/lexers/php.py | 334 + voxvera/vendor/pygments/lexers/pointless.py | 70 + voxvera/vendor/pygments/lexers/pony.py | 93 + voxvera/vendor/pygments/lexers/praat.py | 303 + voxvera/vendor/pygments/lexers/procfile.py | 41 + voxvera/vendor/pygments/lexers/prolog.py | 318 + voxvera/vendor/pygments/lexers/promql.py | 176 + voxvera/vendor/pygments/lexers/prql.py | 251 + voxvera/vendor/pygments/lexers/ptx.py | 119 + voxvera/vendor/pygments/lexers/python.py | 1201 ++ voxvera/vendor/pygments/lexers/q.py | 187 + voxvera/vendor/pygments/lexers/qlik.py | 117 + voxvera/vendor/pygments/lexers/qvt.py | 153 + voxvera/vendor/pygments/lexers/r.py | 196 + voxvera/vendor/pygments/lexers/rdf.py | 468 + voxvera/vendor/pygments/lexers/rebol.py | 419 + voxvera/vendor/pygments/lexers/rego.py | 57 + voxvera/vendor/pygments/lexers/resource.py | 83 + voxvera/vendor/pygments/lexers/ride.py | 138 + voxvera/vendor/pygments/lexers/rita.py | 42 + voxvera/vendor/pygments/lexers/rnc.py | 66 + voxvera/vendor/pygments/lexers/roboconf.py | 81 + .../vendor/pygments/lexers/robotframework.py | 551 + voxvera/vendor/pygments/lexers/ruby.py | 518 + voxvera/vendor/pygments/lexers/rust.py | 222 + voxvera/vendor/pygments/lexers/sas.py | 227 + voxvera/vendor/pygments/lexers/savi.py | 171 + voxvera/vendor/pygments/lexers/scdoc.py | 85 + voxvera/vendor/pygments/lexers/scripting.py | 1616 ++ voxvera/vendor/pygments/lexers/sgf.py | 59 + voxvera/vendor/pygments/lexers/shell.py | 902 + voxvera/vendor/pygments/lexers/sieve.py | 78 + voxvera/vendor/pygments/lexers/slash.py | 183 + voxvera/vendor/pygments/lexers/smalltalk.py | 194 + voxvera/vendor/pygments/lexers/smithy.py | 77 + voxvera/vendor/pygments/lexers/smv.py | 78 + voxvera/vendor/pygments/lexers/snobol.py | 82 + voxvera/vendor/pygments/lexers/solidity.py | 87 + voxvera/vendor/pygments/lexers/soong.py | 78 + voxvera/vendor/pygments/lexers/sophia.py | 102 + voxvera/vendor/pygments/lexers/special.py | 122 + voxvera/vendor/pygments/lexers/spice.py | 70 + voxvera/vendor/pygments/lexers/sql.py | 1109 ++ voxvera/vendor/pygments/lexers/srcinfo.py | 62 + voxvera/vendor/pygments/lexers/stata.py | 170 + .../vendor/pygments/lexers/supercollider.py | 94 + voxvera/vendor/pygments/lexers/tablegen.py | 177 + voxvera/vendor/pygments/lexers/tact.py | 303 + voxvera/vendor/pygments/lexers/tal.py | 77 + voxvera/vendor/pygments/lexers/tcl.py | 148 + voxvera/vendor/pygments/lexers/teal.py | 88 + voxvera/vendor/pygments/lexers/templates.py | 2355 +++ voxvera/vendor/pygments/lexers/teraterm.py | 325 + voxvera/vendor/pygments/lexers/testing.py | 209 + voxvera/vendor/pygments/lexers/text.py | 27 + voxvera/vendor/pygments/lexers/textedit.py | 205 + voxvera/vendor/pygments/lexers/textfmts.py | 436 + voxvera/vendor/pygments/lexers/theorem.py | 410 + voxvera/vendor/pygments/lexers/thingsdb.py | 140 + voxvera/vendor/pygments/lexers/tlb.py | 59 + voxvera/vendor/pygments/lexers/tls.py | 54 + voxvera/vendor/pygments/lexers/tnt.py | 270 + .../vendor/pygments/lexers/trafficscript.py | 51 + voxvera/vendor/pygments/lexers/typoscript.py | 216 + voxvera/vendor/pygments/lexers/typst.py | 160 + voxvera/vendor/pygments/lexers/ul4.py | 309 + voxvera/vendor/pygments/lexers/unicon.py | 413 + voxvera/vendor/pygments/lexers/urbi.py | 145 + voxvera/vendor/pygments/lexers/usd.py | 85 + voxvera/vendor/pygments/lexers/varnish.py | 189 + .../vendor/pygments/lexers/verification.py | 113 + voxvera/vendor/pygments/lexers/verifpal.py | 65 + voxvera/vendor/pygments/lexers/vip.py | 150 + voxvera/vendor/pygments/lexers/vyper.py | 140 + voxvera/vendor/pygments/lexers/web.py | 24 + voxvera/vendor/pygments/lexers/webassembly.py | 119 + voxvera/vendor/pygments/lexers/webidl.py | 298 + voxvera/vendor/pygments/lexers/webmisc.py | 1006 ++ voxvera/vendor/pygments/lexers/wgsl.py | 406 + voxvera/vendor/pygments/lexers/whiley.py | 115 + voxvera/vendor/pygments/lexers/wowtoc.py | 120 + voxvera/vendor/pygments/lexers/wren.py | 98 + voxvera/vendor/pygments/lexers/x10.py | 66 + voxvera/vendor/pygments/lexers/xorg.py | 38 + voxvera/vendor/pygments/lexers/yang.py | 103 + voxvera/vendor/pygments/lexers/yara.py | 69 + voxvera/vendor/pygments/lexers/zig.py | 125 + voxvera/vendor/pygments/modeline.py | 43 + voxvera/vendor/pygments/plugin.py | 72 + voxvera/vendor/pygments/regexopt.py | 91 + voxvera/vendor/pygments/scanner.py | 104 + voxvera/vendor/pygments/sphinxext.py | 247 + voxvera/vendor/pygments/style.py | 203 + voxvera/vendor/pygments/styles/__init__.py | 61 + voxvera/vendor/pygments/styles/_mapping.py | 54 + voxvera/vendor/pygments/styles/abap.py | 32 + voxvera/vendor/pygments/styles/algol.py | 65 + voxvera/vendor/pygments/styles/algol_nu.py | 65 + voxvera/vendor/pygments/styles/arduino.py | 100 + voxvera/vendor/pygments/styles/autumn.py | 67 + voxvera/vendor/pygments/styles/borland.py | 53 + voxvera/vendor/pygments/styles/bw.py | 52 + voxvera/vendor/pygments/styles/coffee.py | 80 + voxvera/vendor/pygments/styles/colorful.py | 83 + voxvera/vendor/pygments/styles/default.py | 76 + voxvera/vendor/pygments/styles/dracula.py | 90 + voxvera/vendor/pygments/styles/emacs.py | 75 + voxvera/vendor/pygments/styles/friendly.py | 76 + .../pygments/styles/friendly_grayscale.py | 80 + voxvera/vendor/pygments/styles/fruity.py | 47 + voxvera/vendor/pygments/styles/gh_dark.py | 113 + voxvera/vendor/pygments/styles/gruvbox.py | 118 + voxvera/vendor/pygments/styles/igor.py | 32 + voxvera/vendor/pygments/styles/inkpot.py | 72 + voxvera/vendor/pygments/styles/lightbulb.py | 110 + voxvera/vendor/pygments/styles/lilypond.py | 62 + voxvera/vendor/pygments/styles/lovelace.py | 100 + voxvera/vendor/pygments/styles/manni.py | 79 + voxvera/vendor/pygments/styles/material.py | 124 + voxvera/vendor/pygments/styles/monokai.py | 112 + voxvera/vendor/pygments/styles/murphy.py | 82 + voxvera/vendor/pygments/styles/native.py | 70 + voxvera/vendor/pygments/styles/nord.py | 156 + voxvera/vendor/pygments/styles/onedark.py | 63 + .../vendor/pygments/styles/paraiso_dark.py | 124 + .../vendor/pygments/styles/paraiso_light.py | 124 + voxvera/vendor/pygments/styles/pastie.py | 78 + voxvera/vendor/pygments/styles/perldoc.py | 73 + .../vendor/pygments/styles/rainbow_dash.py | 95 + voxvera/vendor/pygments/styles/rrt.py | 40 + voxvera/vendor/pygments/styles/sas.py | 46 + voxvera/vendor/pygments/styles/solarized.py | 144 + voxvera/vendor/pygments/styles/staroffice.py | 31 + voxvera/vendor/pygments/styles/stata_dark.py | 42 + voxvera/vendor/pygments/styles/stata_light.py | 42 + voxvera/vendor/pygments/styles/tango.py | 143 + voxvera/vendor/pygments/styles/trac.py | 66 + voxvera/vendor/pygments/styles/vim.py | 67 + voxvera/vendor/pygments/styles/vs.py | 41 + voxvera/vendor/pygments/styles/xcode.py | 53 + voxvera/vendor/pygments/styles/zenburn.py | 83 + voxvera/vendor/pygments/token.py | 214 + voxvera/vendor/pygments/unistring.py | 153 + voxvera/vendor/pygments/util.py | 324 + .../vendor/pypdf-6.8.0.dist-info/INSTALLER | 1 + voxvera/vendor/pypdf-6.8.0.dist-info/METADATA | 169 + voxvera/vendor/pypdf-6.8.0.dist-info/RECORD | 62 + .../vendor/pypdf-6.8.0.dist-info/REQUESTED | 0 voxvera/vendor/pypdf-6.8.0.dist-info/WHEEL | 4 + .../pypdf-6.8.0.dist-info/licenses/LICENSE | 29 + voxvera/vendor/pypdf/__init__.py | 48 + voxvera/vendor/pypdf/_cmap.py | 358 + voxvera/vendor/pypdf/_codecs/__init__.py | 59 + voxvera/vendor/pypdf/_codecs/_codecs.py | 281 + voxvera/vendor/pypdf/_codecs/adobe_glyphs.py | 13969 ++++++++++++++++ .../vendor/pypdf/_codecs/core_font_metrics.py | 4483 +++++ voxvera/vendor/pypdf/_codecs/pdfdoc.py | 264 + voxvera/vendor/pypdf/_codecs/std.py | 258 + voxvera/vendor/pypdf/_codecs/symbol.py | 260 + voxvera/vendor/pypdf/_codecs/zapfding.py | 261 + .../vendor/pypdf/_crypt_providers/__init__.py | 86 + .../vendor/pypdf/_crypt_providers/_base.py | 38 + .../pypdf/_crypt_providers/_cryptography.py | 118 + .../pypdf/_crypt_providers/_fallback.py | 93 + .../pypdf/_crypt_providers/_pycryptodome.py | 97 + voxvera/vendor/pypdf/_doc_common.py | 1460 ++ voxvera/vendor/pypdf/_encryption.py | 1182 ++ voxvera/vendor/pypdf/_font.py | 321 + voxvera/vendor/pypdf/_page.py | 2349 +++ voxvera/vendor/pypdf/_page_labels.py | 290 + voxvera/vendor/pypdf/_protocols.py | 86 + voxvera/vendor/pypdf/_reader.py | 1366 ++ .../vendor/pypdf/_text_extraction/__init__.py | 245 + .../_text_extraction/_layout_mode/__init__.py | 16 + .../_layout_mode/_fixed_width_page.py | 400 + .../_layout_mode/_text_state_manager.py | 221 + .../_layout_mode/_text_state_params.py | 135 + .../pypdf/_text_extraction/_text_extractor.py | 350 + voxvera/vendor/pypdf/_utils.py | 631 + voxvera/vendor/pypdf/_version.py | 1 + voxvera/vendor/pypdf/_writer.py | 3335 ++++ voxvera/vendor/pypdf/annotations/__init__.py | 42 + voxvera/vendor/pypdf/annotations/_base.py | 29 + .../pypdf/annotations/_markup_annotations.py | 351 + .../annotations/_non_markup_annotations.py | 106 + voxvera/vendor/pypdf/constants.py | 814 + voxvera/vendor/pypdf/errors.py | 74 + voxvera/vendor/pypdf/filters.py | 838 + voxvera/vendor/pypdf/generic/__init__.py | 115 + .../pypdf/generic/_appearance_stream.py | 589 + voxvera/vendor/pypdf/generic/_base.py | 942 ++ .../vendor/pypdf/generic/_data_structures.py | 1772 ++ voxvera/vendor/pypdf/generic/_files.py | 401 + voxvera/vendor/pypdf/generic/_fit.py | 174 + voxvera/vendor/pypdf/generic/_image_inline.py | 314 + .../vendor/pypdf/generic/_image_xobject.py | 584 + voxvera/vendor/pypdf/generic/_link.py | 135 + voxvera/vendor/pypdf/generic/_outline.py | 33 + voxvera/vendor/pypdf/generic/_rectangle.py | 132 + voxvera/vendor/pypdf/generic/_utils.py | 208 + voxvera/vendor/pypdf/generic/_viewerpref.py | 163 + voxvera/vendor/pypdf/pagerange.py | 200 + voxvera/vendor/pypdf/papersizes.py | 52 + voxvera/vendor/pypdf/py.typed | 0 voxvera/vendor/pypdf/types.py | 80 + voxvera/vendor/pypdf/xmp.py | 748 + voxvera/vendor/qrcode-8.2.dist-info/INSTALLER | 1 + voxvera/vendor/qrcode-8.2.dist-info/LICENSE | 48 + voxvera/vendor/qrcode-8.2.dist-info/METADATA | 668 + voxvera/vendor/qrcode-8.2.dist-info/RECORD | 42 + voxvera/vendor/qrcode-8.2.dist-info/REQUESTED | 0 voxvera/vendor/qrcode-8.2.dist-info/WHEEL | 4 + .../qrcode-8.2.dist-info/entry_points.txt | 3 + voxvera/vendor/qrcode/LUT.py | 223 + voxvera/vendor/qrcode/__init__.py | 30 + voxvera/vendor/qrcode/base.py | 313 + voxvera/vendor/qrcode/compat/__init__.py | 0 voxvera/vendor/qrcode/compat/etree.py | 4 + voxvera/vendor/qrcode/compat/png.py | 7 + voxvera/vendor/qrcode/console_scripts.py | 181 + voxvera/vendor/qrcode/constants.py | 5 + voxvera/vendor/qrcode/exceptions.py | 2 + voxvera/vendor/qrcode/image/__init__.py | 0 voxvera/vendor/qrcode/image/base.py | 164 + voxvera/vendor/qrcode/image/pil.py | 57 + voxvera/vendor/qrcode/image/pure.py | 56 + voxvera/vendor/qrcode/image/styledpil.py | 120 + .../vendor/qrcode/image/styles/__init__.py | 0 .../vendor/qrcode/image/styles/colormasks.py | 226 + .../image/styles/moduledrawers/__init__.py | 10 + .../qrcode/image/styles/moduledrawers/base.py | 33 + .../qrcode/image/styles/moduledrawers/pil.py | 265 + .../qrcode/image/styles/moduledrawers/svg.py | 139 + voxvera/vendor/qrcode/image/svg.py | 175 + voxvera/vendor/qrcode/main.py | 541 + voxvera/vendor/qrcode/release.py | 42 + voxvera/vendor/qrcode/tests/__init__.py | 0 voxvera/vendor/qrcode/tests/consts.py | 4 + voxvera/vendor/qrcode/tests/test_example.py | 13 + voxvera/vendor/qrcode/tests/test_qrcode.py | 271 + .../vendor/qrcode/tests/test_qrcode_pil.py | 157 + .../vendor/qrcode/tests/test_qrcode_pypng.py | 35 + .../vendor/qrcode/tests/test_qrcode_svg.py | 54 + voxvera/vendor/qrcode/tests/test_release.py | 43 + voxvera/vendor/qrcode/tests/test_script.py | 97 + voxvera/vendor/qrcode/tests/test_util.py | 11 + voxvera/vendor/qrcode/util.py | 584 + .../vendor/rich-14.3.3.dist-info/INSTALLER | 1 + voxvera/vendor/rich-14.3.3.dist-info/METADATA | 480 + voxvera/vendor/rich-14.3.3.dist-info/RECORD | 107 + .../vendor/rich-14.3.3.dist-info/REQUESTED | 0 voxvera/vendor/rich-14.3.3.dist-info/WHEEL | 4 + .../rich-14.3.3.dist-info/licenses/LICENSE | 19 + voxvera/vendor/rich/__init__.py | 177 + voxvera/vendor/rich/__main__.py | 245 + voxvera/vendor/rich/_emoji_codes.py | 3610 ++++ voxvera/vendor/rich/_emoji_replace.py | 32 + voxvera/vendor/rich/_export_format.py | 76 + voxvera/vendor/rich/_extension.py | 10 + voxvera/vendor/rich/_fileno.py | 24 + voxvera/vendor/rich/_inspect.py | 272 + voxvera/vendor/rich/_log_render.py | 94 + voxvera/vendor/rich/_loop.py | 43 + voxvera/vendor/rich/_null_file.py | 69 + voxvera/vendor/rich/_palettes.py | 309 + voxvera/vendor/rich/_pick.py | 17 + voxvera/vendor/rich/_ratio.py | 153 + voxvera/vendor/rich/_spinners.py | 482 + voxvera/vendor/rich/_stack.py | 16 + voxvera/vendor/rich/_timer.py | 19 + voxvera/vendor/rich/_unicode_data/__init__.py | 93 + .../vendor/rich/_unicode_data/_versions.py | 23 + .../rich/_unicode_data/unicode10-0-0.py | 611 + .../rich/_unicode_data/unicode11-0-0.py | 625 + .../rich/_unicode_data/unicode12-0-0.py | 637 + .../rich/_unicode_data/unicode12-1-0.py | 636 + .../rich/_unicode_data/unicode13-0-0.py | 648 + .../rich/_unicode_data/unicode14-0-0.py | 661 + .../rich/_unicode_data/unicode15-0-0.py | 671 + .../rich/_unicode_data/unicode15-1-0.py | 670 + .../rich/_unicode_data/unicode16-0-0.py | 683 + .../rich/_unicode_data/unicode17-0-0.py | 691 + .../vendor/rich/_unicode_data/unicode4-1-0.py | 425 + .../vendor/rich/_unicode_data/unicode5-0-0.py | 430 + .../vendor/rich/_unicode_data/unicode5-1-0.py | 433 + .../vendor/rich/_unicode_data/unicode5-2-0.py | 461 + .../vendor/rich/_unicode_data/unicode6-0-0.py | 469 + .../vendor/rich/_unicode_data/unicode6-1-0.py | 480 + .../vendor/rich/_unicode_data/unicode6-2-0.py | 480 + .../vendor/rich/_unicode_data/unicode6-3-0.py | 481 + .../vendor/rich/_unicode_data/unicode7-0-0.py | 507 + .../vendor/rich/_unicode_data/unicode8-0-0.py | 515 + .../vendor/rich/_unicode_data/unicode9-0-0.py | 598 + voxvera/vendor/rich/_win32_console.py | 661 + voxvera/vendor/rich/_windows.py | 71 + voxvera/vendor/rich/_windows_renderer.py | 56 + voxvera/vendor/rich/_wrap.py | 93 + voxvera/vendor/rich/abc.py | 33 + voxvera/vendor/rich/align.py | 320 + voxvera/vendor/rich/ansi.py | 241 + voxvera/vendor/rich/bar.py | 93 + voxvera/vendor/rich/box.py | 474 + voxvera/vendor/rich/cells.py | 352 + voxvera/vendor/rich/color.py | 621 + voxvera/vendor/rich/color_triplet.py | 38 + voxvera/vendor/rich/columns.py | 187 + voxvera/vendor/rich/console.py | 2684 +++ voxvera/vendor/rich/constrain.py | 37 + voxvera/vendor/rich/containers.py | 167 + voxvera/vendor/rich/control.py | 219 + voxvera/vendor/rich/default_styles.py | 195 + voxvera/vendor/rich/diagnose.py | 39 + voxvera/vendor/rich/emoji.py | 91 + voxvera/vendor/rich/errors.py | 34 + voxvera/vendor/rich/file_proxy.py | 57 + voxvera/vendor/rich/filesize.py | 88 + voxvera/vendor/rich/highlighter.py | 232 + voxvera/vendor/rich/json.py | 139 + voxvera/vendor/rich/jupyter.py | 101 + voxvera/vendor/rich/layout.py | 442 + voxvera/vendor/rich/live.py | 404 + voxvera/vendor/rich/live_render.py | 116 + voxvera/vendor/rich/logging.py | 297 + voxvera/vendor/rich/markdown.py | 793 + voxvera/vendor/rich/markup.py | 251 + voxvera/vendor/rich/measure.py | 151 + voxvera/vendor/rich/padding.py | 141 + voxvera/vendor/rich/pager.py | 34 + voxvera/vendor/rich/palette.py | 100 + voxvera/vendor/rich/panel.py | 317 + voxvera/vendor/rich/pretty.py | 1016 ++ voxvera/vendor/rich/progress.py | 1716 ++ voxvera/vendor/rich/progress_bar.py | 223 + voxvera/vendor/rich/prompt.py | 400 + voxvera/vendor/rich/protocol.py | 42 + voxvera/vendor/rich/py.typed | 0 voxvera/vendor/rich/region.py | 10 + voxvera/vendor/rich/repr.py | 149 + voxvera/vendor/rich/rule.py | 130 + voxvera/vendor/rich/scope.py | 92 + voxvera/vendor/rich/screen.py | 54 + voxvera/vendor/rich/segment.py | 783 + voxvera/vendor/rich/spinner.py | 132 + voxvera/vendor/rich/status.py | 131 + voxvera/vendor/rich/style.py | 792 + voxvera/vendor/rich/styled.py | 42 + voxvera/vendor/rich/syntax.py | 985 ++ voxvera/vendor/rich/table.py | 1015 ++ voxvera/vendor/rich/terminal_theme.py | 153 + voxvera/vendor/rich/text.py | 1363 ++ voxvera/vendor/rich/theme.py | 115 + voxvera/vendor/rich/themes.py | 5 + voxvera/vendor/rich/traceback.py | 924 + voxvera/vendor/rich/tree.py | 257 + .../vendor/wcwidth-0.6.0.dist-info/INSTALLER | 1 + .../vendor/wcwidth-0.6.0.dist-info/METADATA | 776 + voxvera/vendor/wcwidth-0.6.0.dist-info/RECORD | 21 + voxvera/vendor/wcwidth-0.6.0.dist-info/WHEEL | 4 + .../wcwidth-0.6.0.dist-info/licenses/LICENSE | 27 + voxvera/vendor/wcwidth/__init__.py | 43 + voxvera/vendor/wcwidth/bisearch.py | 29 + voxvera/vendor/wcwidth/control_codes.py | 46 + voxvera/vendor/wcwidth/escape_sequences.py | 69 + voxvera/vendor/wcwidth/grapheme.py | 428 + voxvera/vendor/wcwidth/py.typed | 0 voxvera/vendor/wcwidth/sgr_state.py | 338 + voxvera/vendor/wcwidth/table_ambiguous.py | 189 + voxvera/vendor/wcwidth/table_grapheme.py | 2294 +++ voxvera/vendor/wcwidth/table_mc.py | 206 + voxvera/vendor/wcwidth/table_vs16.py | 126 + voxvera/vendor/wcwidth/table_wide.py | 138 + voxvera/vendor/wcwidth/table_zero.py | 350 + voxvera/vendor/wcwidth/textwrap.py | 656 + voxvera/vendor/wcwidth/unicode_versions.py | 21 + voxvera/vendor/wcwidth/wcwidth.py | 1030 ++ 1039 files changed, 314776 insertions(+), 14 deletions(-) create mode 100644 voxvera/vendor/InquirerPy/__init__.py create mode 100644 voxvera/vendor/InquirerPy/base/__init__.py create mode 100644 voxvera/vendor/InquirerPy/base/complex.py create mode 100644 voxvera/vendor/InquirerPy/base/control.py create mode 100644 voxvera/vendor/InquirerPy/base/list.py create mode 100644 voxvera/vendor/InquirerPy/base/simple.py create mode 100644 voxvera/vendor/InquirerPy/containers/__init__.py create mode 100644 voxvera/vendor/InquirerPy/containers/instruction.py create mode 100644 voxvera/vendor/InquirerPy/containers/message.py create mode 100644 voxvera/vendor/InquirerPy/containers/spinner.py create mode 100644 voxvera/vendor/InquirerPy/containers/validation.py create mode 100644 voxvera/vendor/InquirerPy/enum.py create mode 100644 voxvera/vendor/InquirerPy/exceptions.py create mode 100644 voxvera/vendor/InquirerPy/inquirer.py create mode 100644 voxvera/vendor/InquirerPy/prompts/__init__.py create mode 100644 voxvera/vendor/InquirerPy/prompts/checkbox.py create mode 100644 voxvera/vendor/InquirerPy/prompts/confirm.py create mode 100644 voxvera/vendor/InquirerPy/prompts/expand.py create mode 100644 voxvera/vendor/InquirerPy/prompts/filepath.py create mode 100644 voxvera/vendor/InquirerPy/prompts/fuzzy.py create mode 100644 voxvera/vendor/InquirerPy/prompts/input.py create mode 100644 voxvera/vendor/InquirerPy/prompts/list.py create mode 100644 voxvera/vendor/InquirerPy/prompts/number.py create mode 100644 voxvera/vendor/InquirerPy/prompts/rawlist.py create mode 100644 voxvera/vendor/InquirerPy/prompts/secret.py create mode 100644 voxvera/vendor/InquirerPy/py.typed create mode 100644 voxvera/vendor/InquirerPy/resolver.py create mode 100644 voxvera/vendor/InquirerPy/separator.py create mode 100644 voxvera/vendor/InquirerPy/utils.py create mode 100644 voxvera/vendor/InquirerPy/validator.py create mode 100644 voxvera/vendor/PIL/AvifImagePlugin.py create mode 100644 voxvera/vendor/PIL/BdfFontFile.py create mode 100644 voxvera/vendor/PIL/BlpImagePlugin.py create mode 100644 voxvera/vendor/PIL/BmpImagePlugin.py create mode 100644 voxvera/vendor/PIL/BufrStubImagePlugin.py create mode 100644 voxvera/vendor/PIL/ContainerIO.py create mode 100644 voxvera/vendor/PIL/CurImagePlugin.py create mode 100644 voxvera/vendor/PIL/DcxImagePlugin.py create mode 100644 voxvera/vendor/PIL/DdsImagePlugin.py create mode 100644 voxvera/vendor/PIL/EpsImagePlugin.py create mode 100644 voxvera/vendor/PIL/ExifTags.py create mode 100644 voxvera/vendor/PIL/FitsImagePlugin.py create mode 100644 voxvera/vendor/PIL/FliImagePlugin.py create mode 100644 voxvera/vendor/PIL/FontFile.py create mode 100644 voxvera/vendor/PIL/FpxImagePlugin.py create mode 100644 voxvera/vendor/PIL/FtexImagePlugin.py create mode 100644 voxvera/vendor/PIL/GbrImagePlugin.py create mode 100644 voxvera/vendor/PIL/GdImageFile.py create mode 100644 voxvera/vendor/PIL/GifImagePlugin.py create mode 100644 voxvera/vendor/PIL/GimpGradientFile.py create mode 100644 voxvera/vendor/PIL/GimpPaletteFile.py create mode 100644 voxvera/vendor/PIL/GribStubImagePlugin.py create mode 100644 voxvera/vendor/PIL/Hdf5StubImagePlugin.py create mode 100644 voxvera/vendor/PIL/IcnsImagePlugin.py create mode 100644 voxvera/vendor/PIL/IcoImagePlugin.py create mode 100644 voxvera/vendor/PIL/ImImagePlugin.py create mode 100644 voxvera/vendor/PIL/Image.py create mode 100644 voxvera/vendor/PIL/ImageChops.py create mode 100644 voxvera/vendor/PIL/ImageCms.py create mode 100644 voxvera/vendor/PIL/ImageColor.py create mode 100644 voxvera/vendor/PIL/ImageDraw.py create mode 100644 voxvera/vendor/PIL/ImageDraw2.py create mode 100644 voxvera/vendor/PIL/ImageEnhance.py create mode 100644 voxvera/vendor/PIL/ImageFile.py create mode 100644 voxvera/vendor/PIL/ImageFilter.py create mode 100644 voxvera/vendor/PIL/ImageFont.py create mode 100644 voxvera/vendor/PIL/ImageGrab.py create mode 100644 voxvera/vendor/PIL/ImageMath.py create mode 100644 voxvera/vendor/PIL/ImageMode.py create mode 100644 voxvera/vendor/PIL/ImageMorph.py create mode 100644 voxvera/vendor/PIL/ImageOps.py create mode 100644 voxvera/vendor/PIL/ImagePalette.py create mode 100644 voxvera/vendor/PIL/ImagePath.py create mode 100644 voxvera/vendor/PIL/ImageQt.py create mode 100644 voxvera/vendor/PIL/ImageSequence.py create mode 100644 voxvera/vendor/PIL/ImageShow.py create mode 100644 voxvera/vendor/PIL/ImageStat.py create mode 100644 voxvera/vendor/PIL/ImageText.py create mode 100644 voxvera/vendor/PIL/ImageTk.py create mode 100644 voxvera/vendor/PIL/ImageTransform.py create mode 100644 voxvera/vendor/PIL/ImageWin.py create mode 100644 voxvera/vendor/PIL/ImtImagePlugin.py create mode 100644 voxvera/vendor/PIL/IptcImagePlugin.py create mode 100644 voxvera/vendor/PIL/Jpeg2KImagePlugin.py create mode 100644 voxvera/vendor/PIL/JpegImagePlugin.py create mode 100644 voxvera/vendor/PIL/JpegPresets.py create mode 100644 voxvera/vendor/PIL/McIdasImagePlugin.py create mode 100644 voxvera/vendor/PIL/MicImagePlugin.py create mode 100644 voxvera/vendor/PIL/MpegImagePlugin.py create mode 100644 voxvera/vendor/PIL/MpoImagePlugin.py create mode 100644 voxvera/vendor/PIL/MspImagePlugin.py create mode 100644 voxvera/vendor/PIL/PSDraw.py create mode 100644 voxvera/vendor/PIL/PaletteFile.py create mode 100644 voxvera/vendor/PIL/PalmImagePlugin.py create mode 100644 voxvera/vendor/PIL/PcdImagePlugin.py create mode 100644 voxvera/vendor/PIL/PcfFontFile.py create mode 100644 voxvera/vendor/PIL/PcxImagePlugin.py create mode 100644 voxvera/vendor/PIL/PdfImagePlugin.py create mode 100644 voxvera/vendor/PIL/PdfParser.py create mode 100644 voxvera/vendor/PIL/PixarImagePlugin.py create mode 100644 voxvera/vendor/PIL/PngImagePlugin.py create mode 100644 voxvera/vendor/PIL/PpmImagePlugin.py create mode 100644 voxvera/vendor/PIL/PsdImagePlugin.py create mode 100644 voxvera/vendor/PIL/QoiImagePlugin.py create mode 100644 voxvera/vendor/PIL/SgiImagePlugin.py create mode 100644 voxvera/vendor/PIL/SpiderImagePlugin.py create mode 100644 voxvera/vendor/PIL/SunImagePlugin.py create mode 100644 voxvera/vendor/PIL/TarIO.py create mode 100644 voxvera/vendor/PIL/TgaImagePlugin.py create mode 100644 voxvera/vendor/PIL/TiffImagePlugin.py create mode 100644 voxvera/vendor/PIL/TiffTags.py create mode 100644 voxvera/vendor/PIL/WalImageFile.py create mode 100644 voxvera/vendor/PIL/WebPImagePlugin.py create mode 100644 voxvera/vendor/PIL/WmfImagePlugin.py create mode 100644 voxvera/vendor/PIL/XVThumbImagePlugin.py create mode 100644 voxvera/vendor/PIL/XbmImagePlugin.py create mode 100644 voxvera/vendor/PIL/XpmImagePlugin.py create mode 100644 voxvera/vendor/PIL/__init__.py create mode 100644 voxvera/vendor/PIL/__main__.py create mode 100755 voxvera/vendor/PIL/_avif.cpython-312-x86_64-linux-gnu.so create mode 100644 voxvera/vendor/PIL/_avif.pyi create mode 100644 voxvera/vendor/PIL/_binary.py create mode 100644 voxvera/vendor/PIL/_deprecate.py create mode 100755 voxvera/vendor/PIL/_imaging.cpython-312-x86_64-linux-gnu.so create mode 100644 voxvera/vendor/PIL/_imaging.pyi create mode 100755 voxvera/vendor/PIL/_imagingcms.cpython-312-x86_64-linux-gnu.so create mode 100644 voxvera/vendor/PIL/_imagingcms.pyi create mode 100755 voxvera/vendor/PIL/_imagingft.cpython-312-x86_64-linux-gnu.so create mode 100644 voxvera/vendor/PIL/_imagingft.pyi create mode 100755 voxvera/vendor/PIL/_imagingmath.cpython-312-x86_64-linux-gnu.so create mode 100644 voxvera/vendor/PIL/_imagingmath.pyi create mode 100755 voxvera/vendor/PIL/_imagingmorph.cpython-312-x86_64-linux-gnu.so create mode 100644 voxvera/vendor/PIL/_imagingmorph.pyi create mode 100755 voxvera/vendor/PIL/_imagingtk.cpython-312-x86_64-linux-gnu.so create mode 100644 voxvera/vendor/PIL/_imagingtk.pyi create mode 100644 voxvera/vendor/PIL/_tkinter_finder.py create mode 100644 voxvera/vendor/PIL/_typing.py create mode 100644 voxvera/vendor/PIL/_util.py create mode 100644 voxvera/vendor/PIL/_version.py create mode 100755 voxvera/vendor/PIL/_webp.cpython-312-x86_64-linux-gnu.so create mode 100644 voxvera/vendor/PIL/_webp.pyi create mode 100644 voxvera/vendor/PIL/features.py create mode 100644 voxvera/vendor/PIL/py.typed create mode 100644 voxvera/vendor/PIL/report.py create mode 100755 voxvera/vendor/bin/htmlmin create mode 100755 voxvera/vendor/bin/markdown-it create mode 100755 voxvera/vendor/bin/pygmentize create mode 100755 voxvera/vendor/bin/qr create mode 100644 voxvera/vendor/htmlmin/__init__.py create mode 100644 voxvera/vendor/htmlmin/command.py create mode 100644 voxvera/vendor/htmlmin/decorator.py create mode 100644 voxvera/vendor/htmlmin/escape.py create mode 100644 voxvera/vendor/htmlmin/main.py create mode 100644 voxvera/vendor/htmlmin/middleware.py create mode 100644 voxvera/vendor/htmlmin/parser.py create mode 100644 voxvera/vendor/htmlmin/python3html/__init__.py create mode 100644 voxvera/vendor/htmlmin/python3html/parser.py create mode 100644 voxvera/vendor/htmlmin/tests/__init__.py create mode 100644 voxvera/vendor/htmlmin/tests/test_escape.py create mode 100644 voxvera/vendor/htmlmin/tests/tests.py create mode 100644 voxvera/vendor/htmlmin2-0.1.13.dist-info/INSTALLER create mode 100644 voxvera/vendor/htmlmin2-0.1.13.dist-info/LICENSE create mode 100644 voxvera/vendor/htmlmin2-0.1.13.dist-info/METADATA create mode 100644 voxvera/vendor/htmlmin2-0.1.13.dist-info/RECORD create mode 100644 voxvera/vendor/htmlmin2-0.1.13.dist-info/REQUESTED create mode 100644 voxvera/vendor/htmlmin2-0.1.13.dist-info/WHEEL create mode 100644 voxvera/vendor/htmlmin2-0.1.13.dist-info/entry_points.txt create mode 100644 voxvera/vendor/htmlmin2-0.1.13.dist-info/top_level.txt create mode 100644 voxvera/vendor/htmlmin2-0.1.13.dist-info/zip-safe create mode 100644 voxvera/vendor/inquirerpy-0.3.4.dist-info/INSTALLER create mode 100644 voxvera/vendor/inquirerpy-0.3.4.dist-info/LICENSE create mode 100644 voxvera/vendor/inquirerpy-0.3.4.dist-info/METADATA create mode 100644 voxvera/vendor/inquirerpy-0.3.4.dist-info/RECORD create mode 100644 voxvera/vendor/inquirerpy-0.3.4.dist-info/REQUESTED create mode 100644 voxvera/vendor/inquirerpy-0.3.4.dist-info/WHEEL create mode 100644 voxvera/vendor/jsmin-3.0.1.dist-info/INSTALLER create mode 100644 voxvera/vendor/jsmin-3.0.1.dist-info/LICENSE.txt create mode 100644 voxvera/vendor/jsmin-3.0.1.dist-info/METADATA create mode 100644 voxvera/vendor/jsmin-3.0.1.dist-info/RECORD create mode 100644 voxvera/vendor/jsmin-3.0.1.dist-info/REQUESTED create mode 100644 voxvera/vendor/jsmin-3.0.1.dist-info/WHEEL create mode 100644 voxvera/vendor/jsmin-3.0.1.dist-info/top_level.txt create mode 100644 voxvera/vendor/jsmin/__init__.py create mode 100644 voxvera/vendor/jsmin/__main__.py create mode 100644 voxvera/vendor/jsmin/test.py create mode 100644 voxvera/vendor/markdown_it/__init__.py create mode 100644 voxvera/vendor/markdown_it/_compat.py create mode 100644 voxvera/vendor/markdown_it/_punycode.py create mode 100644 voxvera/vendor/markdown_it/cli/__init__.py create mode 100644 voxvera/vendor/markdown_it/cli/parse.py create mode 100644 voxvera/vendor/markdown_it/common/__init__.py create mode 100644 voxvera/vendor/markdown_it/common/entities.py create mode 100644 voxvera/vendor/markdown_it/common/html_blocks.py create mode 100644 voxvera/vendor/markdown_it/common/html_re.py create mode 100644 voxvera/vendor/markdown_it/common/normalize_url.py create mode 100644 voxvera/vendor/markdown_it/common/utils.py create mode 100644 voxvera/vendor/markdown_it/helpers/__init__.py create mode 100644 voxvera/vendor/markdown_it/helpers/parse_link_destination.py create mode 100644 voxvera/vendor/markdown_it/helpers/parse_link_label.py create mode 100644 voxvera/vendor/markdown_it/helpers/parse_link_title.py create mode 100644 voxvera/vendor/markdown_it/main.py create mode 100644 voxvera/vendor/markdown_it/parser_block.py create mode 100644 voxvera/vendor/markdown_it/parser_core.py create mode 100644 voxvera/vendor/markdown_it/parser_inline.py create mode 100644 voxvera/vendor/markdown_it/port.yaml create mode 100644 voxvera/vendor/markdown_it/presets/__init__.py create mode 100644 voxvera/vendor/markdown_it/presets/commonmark.py create mode 100644 voxvera/vendor/markdown_it/presets/default.py create mode 100644 voxvera/vendor/markdown_it/presets/zero.py create mode 100644 voxvera/vendor/markdown_it/py.typed create mode 100644 voxvera/vendor/markdown_it/renderer.py create mode 100644 voxvera/vendor/markdown_it/ruler.py create mode 100644 voxvera/vendor/markdown_it/rules_block/__init__.py create mode 100644 voxvera/vendor/markdown_it/rules_block/blockquote.py create mode 100644 voxvera/vendor/markdown_it/rules_block/code.py create mode 100644 voxvera/vendor/markdown_it/rules_block/fence.py create mode 100644 voxvera/vendor/markdown_it/rules_block/heading.py create mode 100644 voxvera/vendor/markdown_it/rules_block/hr.py create mode 100644 voxvera/vendor/markdown_it/rules_block/html_block.py create mode 100644 voxvera/vendor/markdown_it/rules_block/lheading.py create mode 100644 voxvera/vendor/markdown_it/rules_block/list.py create mode 100644 voxvera/vendor/markdown_it/rules_block/paragraph.py create mode 100644 voxvera/vendor/markdown_it/rules_block/reference.py create mode 100644 voxvera/vendor/markdown_it/rules_block/state_block.py create mode 100644 voxvera/vendor/markdown_it/rules_block/table.py create mode 100644 voxvera/vendor/markdown_it/rules_core/__init__.py create mode 100644 voxvera/vendor/markdown_it/rules_core/block.py create mode 100644 voxvera/vendor/markdown_it/rules_core/inline.py create mode 100644 voxvera/vendor/markdown_it/rules_core/linkify.py create mode 100644 voxvera/vendor/markdown_it/rules_core/normalize.py create mode 100644 voxvera/vendor/markdown_it/rules_core/replacements.py create mode 100644 voxvera/vendor/markdown_it/rules_core/smartquotes.py create mode 100644 voxvera/vendor/markdown_it/rules_core/state_core.py create mode 100644 voxvera/vendor/markdown_it/rules_core/text_join.py create mode 100644 voxvera/vendor/markdown_it/rules_inline/__init__.py create mode 100644 voxvera/vendor/markdown_it/rules_inline/autolink.py create mode 100644 voxvera/vendor/markdown_it/rules_inline/backticks.py create mode 100644 voxvera/vendor/markdown_it/rules_inline/balance_pairs.py create mode 100644 voxvera/vendor/markdown_it/rules_inline/emphasis.py create mode 100644 voxvera/vendor/markdown_it/rules_inline/entity.py create mode 100644 voxvera/vendor/markdown_it/rules_inline/escape.py create mode 100644 voxvera/vendor/markdown_it/rules_inline/fragments_join.py create mode 100644 voxvera/vendor/markdown_it/rules_inline/html_inline.py create mode 100644 voxvera/vendor/markdown_it/rules_inline/image.py create mode 100644 voxvera/vendor/markdown_it/rules_inline/link.py create mode 100644 voxvera/vendor/markdown_it/rules_inline/linkify.py create mode 100644 voxvera/vendor/markdown_it/rules_inline/newline.py create mode 100644 voxvera/vendor/markdown_it/rules_inline/state_inline.py create mode 100644 voxvera/vendor/markdown_it/rules_inline/strikethrough.py create mode 100644 voxvera/vendor/markdown_it/rules_inline/text.py create mode 100644 voxvera/vendor/markdown_it/token.py create mode 100644 voxvera/vendor/markdown_it/tree.py create mode 100644 voxvera/vendor/markdown_it/utils.py create mode 100644 voxvera/vendor/markdown_it_py-4.0.0.dist-info/INSTALLER create mode 100644 voxvera/vendor/markdown_it_py-4.0.0.dist-info/METADATA create mode 100644 voxvera/vendor/markdown_it_py-4.0.0.dist-info/RECORD create mode 100644 voxvera/vendor/markdown_it_py-4.0.0.dist-info/WHEEL create mode 100644 voxvera/vendor/markdown_it_py-4.0.0.dist-info/entry_points.txt create mode 100644 voxvera/vendor/markdown_it_py-4.0.0.dist-info/licenses/LICENSE create mode 100644 voxvera/vendor/markdown_it_py-4.0.0.dist-info/licenses/LICENSE.markdown-it create mode 100644 voxvera/vendor/mdurl-0.1.2.dist-info/INSTALLER create mode 100644 voxvera/vendor/mdurl-0.1.2.dist-info/LICENSE create mode 100644 voxvera/vendor/mdurl-0.1.2.dist-info/METADATA create mode 100644 voxvera/vendor/mdurl-0.1.2.dist-info/RECORD create mode 100644 voxvera/vendor/mdurl-0.1.2.dist-info/WHEEL create mode 100644 voxvera/vendor/mdurl/__init__.py create mode 100644 voxvera/vendor/mdurl/_decode.py create mode 100644 voxvera/vendor/mdurl/_encode.py create mode 100644 voxvera/vendor/mdurl/_format.py create mode 100644 voxvera/vendor/mdurl/_parse.py create mode 100644 voxvera/vendor/mdurl/_url.py create mode 100644 voxvera/vendor/mdurl/py.typed create mode 100644 voxvera/vendor/pfzy-0.3.4.dist-info/INSTALLER create mode 100644 voxvera/vendor/pfzy-0.3.4.dist-info/LICENSE create mode 100644 voxvera/vendor/pfzy-0.3.4.dist-info/METADATA create mode 100644 voxvera/vendor/pfzy-0.3.4.dist-info/RECORD create mode 100644 voxvera/vendor/pfzy-0.3.4.dist-info/WHEEL create mode 100644 voxvera/vendor/pfzy/__init__.py create mode 100644 voxvera/vendor/pfzy/match.py create mode 100644 voxvera/vendor/pfzy/score.py create mode 100644 voxvera/vendor/pfzy/types.py create mode 100644 voxvera/vendor/pillow-12.1.1.dist-info/INSTALLER create mode 100644 voxvera/vendor/pillow-12.1.1.dist-info/METADATA create mode 100644 voxvera/vendor/pillow-12.1.1.dist-info/RECORD create mode 100644 voxvera/vendor/pillow-12.1.1.dist-info/REQUESTED create mode 100644 voxvera/vendor/pillow-12.1.1.dist-info/WHEEL create mode 100644 voxvera/vendor/pillow-12.1.1.dist-info/licenses/LICENSE create mode 100644 voxvera/vendor/pillow-12.1.1.dist-info/sboms/auditwheel.cdx.json create mode 100644 voxvera/vendor/pillow-12.1.1.dist-info/top_level.txt create mode 100644 voxvera/vendor/pillow-12.1.1.dist-info/zip-safe create mode 100755 voxvera/vendor/pillow.libs/libXau-154567c4.so.6.0.0 create mode 100755 voxvera/vendor/pillow.libs/libavif-01e67780.so.16.3.0 create mode 100755 voxvera/vendor/pillow.libs/libbrotlicommon-c55a5f7a.so.1.2.0 create mode 100755 voxvera/vendor/pillow.libs/libbrotlidec-b57ddf63.so.1.2.0 create mode 100755 voxvera/vendor/pillow.libs/libfreetype-ee1c40c4.so.6.20.4 create mode 100755 voxvera/vendor/pillow.libs/libharfbuzz-0692f733.so.0.61230.0 create mode 100755 voxvera/vendor/pillow.libs/libjpeg-32d42e18.so.62.4.0 create mode 100755 voxvera/vendor/pillow.libs/liblcms2-cc10e42f.so.2.0.17 create mode 100755 voxvera/vendor/pillow.libs/liblzma-61b1002e.so.5.8.2 create mode 100755 voxvera/vendor/pillow.libs/libopenjp2-94e588ba.so.2.5.4 create mode 100755 voxvera/vendor/pillow.libs/libpng16-4a38ea05.so.16.53.0 create mode 100755 voxvera/vendor/pillow.libs/libsharpyuv-95d8a097.so.0.1.2 create mode 100755 voxvera/vendor/pillow.libs/libtiff-295fd75c.so.6.2.0 create mode 100755 voxvera/vendor/pillow.libs/libwebp-d8b9687f.so.7.2.0 create mode 100755 voxvera/vendor/pillow.libs/libwebpdemux-747f2b49.so.2.0.17 create mode 100755 voxvera/vendor/pillow.libs/libwebpmux-7f11e5ce.so.3.1.2 create mode 100755 voxvera/vendor/pillow.libs/libxcb-64009ff3.so.1.1.0 create mode 100755 voxvera/vendor/pillow.libs/libzstd-761a17b6.so.1.5.7 create mode 100644 voxvera/vendor/prompt_toolkit-3.0.52.dist-info/INSTALLER create mode 100644 voxvera/vendor/prompt_toolkit-3.0.52.dist-info/METADATA create mode 100644 voxvera/vendor/prompt_toolkit-3.0.52.dist-info/RECORD create mode 100644 voxvera/vendor/prompt_toolkit-3.0.52.dist-info/WHEEL create mode 100644 voxvera/vendor/prompt_toolkit-3.0.52.dist-info/licenses/AUTHORS.rst create mode 100644 voxvera/vendor/prompt_toolkit-3.0.52.dist-info/licenses/LICENSE create mode 100644 voxvera/vendor/prompt_toolkit-3.0.52.dist-info/top_level.txt create mode 100644 voxvera/vendor/prompt_toolkit/__init__.py create mode 100644 voxvera/vendor/prompt_toolkit/application/__init__.py create mode 100644 voxvera/vendor/prompt_toolkit/application/application.py create mode 100644 voxvera/vendor/prompt_toolkit/application/current.py create mode 100644 voxvera/vendor/prompt_toolkit/application/dummy.py create mode 100644 voxvera/vendor/prompt_toolkit/application/run_in_terminal.py create mode 100644 voxvera/vendor/prompt_toolkit/auto_suggest.py create mode 100644 voxvera/vendor/prompt_toolkit/buffer.py create mode 100644 voxvera/vendor/prompt_toolkit/cache.py create mode 100644 voxvera/vendor/prompt_toolkit/clipboard/__init__.py create mode 100644 voxvera/vendor/prompt_toolkit/clipboard/base.py create mode 100644 voxvera/vendor/prompt_toolkit/clipboard/in_memory.py create mode 100644 voxvera/vendor/prompt_toolkit/clipboard/pyperclip.py create mode 100644 voxvera/vendor/prompt_toolkit/completion/__init__.py create mode 100644 voxvera/vendor/prompt_toolkit/completion/base.py create mode 100644 voxvera/vendor/prompt_toolkit/completion/deduplicate.py create mode 100644 voxvera/vendor/prompt_toolkit/completion/filesystem.py create mode 100644 voxvera/vendor/prompt_toolkit/completion/fuzzy_completer.py create mode 100644 voxvera/vendor/prompt_toolkit/completion/nested.py create mode 100644 voxvera/vendor/prompt_toolkit/completion/word_completer.py create mode 100644 voxvera/vendor/prompt_toolkit/contrib/__init__.py create mode 100644 voxvera/vendor/prompt_toolkit/contrib/completers/__init__.py create mode 100644 voxvera/vendor/prompt_toolkit/contrib/completers/system.py create mode 100644 voxvera/vendor/prompt_toolkit/contrib/regular_languages/__init__.py create mode 100644 voxvera/vendor/prompt_toolkit/contrib/regular_languages/compiler.py create mode 100644 voxvera/vendor/prompt_toolkit/contrib/regular_languages/completion.py create mode 100644 voxvera/vendor/prompt_toolkit/contrib/regular_languages/lexer.py create mode 100644 voxvera/vendor/prompt_toolkit/contrib/regular_languages/regex_parser.py create mode 100644 voxvera/vendor/prompt_toolkit/contrib/regular_languages/validation.py create mode 100644 voxvera/vendor/prompt_toolkit/contrib/ssh/__init__.py create mode 100644 voxvera/vendor/prompt_toolkit/contrib/ssh/server.py create mode 100644 voxvera/vendor/prompt_toolkit/contrib/telnet/__init__.py create mode 100644 voxvera/vendor/prompt_toolkit/contrib/telnet/log.py create mode 100644 voxvera/vendor/prompt_toolkit/contrib/telnet/protocol.py create mode 100644 voxvera/vendor/prompt_toolkit/contrib/telnet/server.py create mode 100644 voxvera/vendor/prompt_toolkit/cursor_shapes.py create mode 100644 voxvera/vendor/prompt_toolkit/data_structures.py create mode 100644 voxvera/vendor/prompt_toolkit/document.py create mode 100644 voxvera/vendor/prompt_toolkit/enums.py create mode 100644 voxvera/vendor/prompt_toolkit/eventloop/__init__.py create mode 100644 voxvera/vendor/prompt_toolkit/eventloop/async_generator.py create mode 100644 voxvera/vendor/prompt_toolkit/eventloop/inputhook.py create mode 100644 voxvera/vendor/prompt_toolkit/eventloop/utils.py create mode 100644 voxvera/vendor/prompt_toolkit/eventloop/win32.py create mode 100644 voxvera/vendor/prompt_toolkit/filters/__init__.py create mode 100644 voxvera/vendor/prompt_toolkit/filters/app.py create mode 100644 voxvera/vendor/prompt_toolkit/filters/base.py create mode 100644 voxvera/vendor/prompt_toolkit/filters/cli.py create mode 100644 voxvera/vendor/prompt_toolkit/filters/utils.py create mode 100644 voxvera/vendor/prompt_toolkit/formatted_text/__init__.py create mode 100644 voxvera/vendor/prompt_toolkit/formatted_text/ansi.py create mode 100644 voxvera/vendor/prompt_toolkit/formatted_text/base.py create mode 100644 voxvera/vendor/prompt_toolkit/formatted_text/html.py create mode 100644 voxvera/vendor/prompt_toolkit/formatted_text/pygments.py create mode 100644 voxvera/vendor/prompt_toolkit/formatted_text/utils.py create mode 100644 voxvera/vendor/prompt_toolkit/history.py create mode 100644 voxvera/vendor/prompt_toolkit/input/__init__.py create mode 100644 voxvera/vendor/prompt_toolkit/input/ansi_escape_sequences.py create mode 100644 voxvera/vendor/prompt_toolkit/input/base.py create mode 100644 voxvera/vendor/prompt_toolkit/input/defaults.py create mode 100644 voxvera/vendor/prompt_toolkit/input/posix_pipe.py create mode 100644 voxvera/vendor/prompt_toolkit/input/posix_utils.py create mode 100644 voxvera/vendor/prompt_toolkit/input/typeahead.py create mode 100644 voxvera/vendor/prompt_toolkit/input/vt100.py create mode 100644 voxvera/vendor/prompt_toolkit/input/vt100_parser.py create mode 100644 voxvera/vendor/prompt_toolkit/input/win32.py create mode 100644 voxvera/vendor/prompt_toolkit/input/win32_pipe.py create mode 100644 voxvera/vendor/prompt_toolkit/key_binding/__init__.py create mode 100644 voxvera/vendor/prompt_toolkit/key_binding/bindings/__init__.py create mode 100644 voxvera/vendor/prompt_toolkit/key_binding/bindings/auto_suggest.py create mode 100644 voxvera/vendor/prompt_toolkit/key_binding/bindings/basic.py create mode 100644 voxvera/vendor/prompt_toolkit/key_binding/bindings/completion.py create mode 100644 voxvera/vendor/prompt_toolkit/key_binding/bindings/cpr.py create mode 100644 voxvera/vendor/prompt_toolkit/key_binding/bindings/emacs.py create mode 100644 voxvera/vendor/prompt_toolkit/key_binding/bindings/focus.py create mode 100644 voxvera/vendor/prompt_toolkit/key_binding/bindings/mouse.py create mode 100644 voxvera/vendor/prompt_toolkit/key_binding/bindings/named_commands.py create mode 100644 voxvera/vendor/prompt_toolkit/key_binding/bindings/open_in_editor.py create mode 100644 voxvera/vendor/prompt_toolkit/key_binding/bindings/page_navigation.py create mode 100644 voxvera/vendor/prompt_toolkit/key_binding/bindings/scroll.py create mode 100644 voxvera/vendor/prompt_toolkit/key_binding/bindings/search.py create mode 100644 voxvera/vendor/prompt_toolkit/key_binding/bindings/vi.py create mode 100644 voxvera/vendor/prompt_toolkit/key_binding/defaults.py create mode 100644 voxvera/vendor/prompt_toolkit/key_binding/digraphs.py create mode 100644 voxvera/vendor/prompt_toolkit/key_binding/emacs_state.py create mode 100644 voxvera/vendor/prompt_toolkit/key_binding/key_bindings.py create mode 100644 voxvera/vendor/prompt_toolkit/key_binding/key_processor.py create mode 100644 voxvera/vendor/prompt_toolkit/key_binding/vi_state.py create mode 100644 voxvera/vendor/prompt_toolkit/keys.py create mode 100644 voxvera/vendor/prompt_toolkit/layout/__init__.py create mode 100644 voxvera/vendor/prompt_toolkit/layout/containers.py create mode 100644 voxvera/vendor/prompt_toolkit/layout/controls.py create mode 100644 voxvera/vendor/prompt_toolkit/layout/dimension.py create mode 100644 voxvera/vendor/prompt_toolkit/layout/dummy.py create mode 100644 voxvera/vendor/prompt_toolkit/layout/layout.py create mode 100644 voxvera/vendor/prompt_toolkit/layout/margins.py create mode 100644 voxvera/vendor/prompt_toolkit/layout/menus.py create mode 100644 voxvera/vendor/prompt_toolkit/layout/mouse_handlers.py create mode 100644 voxvera/vendor/prompt_toolkit/layout/processors.py create mode 100644 voxvera/vendor/prompt_toolkit/layout/screen.py create mode 100644 voxvera/vendor/prompt_toolkit/layout/scrollable_pane.py create mode 100644 voxvera/vendor/prompt_toolkit/layout/utils.py create mode 100644 voxvera/vendor/prompt_toolkit/lexers/__init__.py create mode 100644 voxvera/vendor/prompt_toolkit/lexers/base.py create mode 100644 voxvera/vendor/prompt_toolkit/lexers/pygments.py create mode 100644 voxvera/vendor/prompt_toolkit/log.py create mode 100644 voxvera/vendor/prompt_toolkit/mouse_events.py create mode 100644 voxvera/vendor/prompt_toolkit/output/__init__.py create mode 100644 voxvera/vendor/prompt_toolkit/output/base.py create mode 100644 voxvera/vendor/prompt_toolkit/output/color_depth.py create mode 100644 voxvera/vendor/prompt_toolkit/output/conemu.py create mode 100644 voxvera/vendor/prompt_toolkit/output/defaults.py create mode 100644 voxvera/vendor/prompt_toolkit/output/flush_stdout.py create mode 100644 voxvera/vendor/prompt_toolkit/output/plain_text.py create mode 100644 voxvera/vendor/prompt_toolkit/output/vt100.py create mode 100644 voxvera/vendor/prompt_toolkit/output/win32.py create mode 100644 voxvera/vendor/prompt_toolkit/output/windows10.py create mode 100644 voxvera/vendor/prompt_toolkit/patch_stdout.py create mode 100644 voxvera/vendor/prompt_toolkit/py.typed create mode 100644 voxvera/vendor/prompt_toolkit/renderer.py create mode 100644 voxvera/vendor/prompt_toolkit/search.py create mode 100644 voxvera/vendor/prompt_toolkit/selection.py create mode 100644 voxvera/vendor/prompt_toolkit/shortcuts/__init__.py create mode 100644 voxvera/vendor/prompt_toolkit/shortcuts/choice_input.py create mode 100644 voxvera/vendor/prompt_toolkit/shortcuts/dialogs.py create mode 100644 voxvera/vendor/prompt_toolkit/shortcuts/progress_bar/__init__.py create mode 100644 voxvera/vendor/prompt_toolkit/shortcuts/progress_bar/base.py create mode 100644 voxvera/vendor/prompt_toolkit/shortcuts/progress_bar/formatters.py create mode 100644 voxvera/vendor/prompt_toolkit/shortcuts/prompt.py create mode 100644 voxvera/vendor/prompt_toolkit/shortcuts/utils.py create mode 100644 voxvera/vendor/prompt_toolkit/styles/__init__.py create mode 100644 voxvera/vendor/prompt_toolkit/styles/base.py create mode 100644 voxvera/vendor/prompt_toolkit/styles/defaults.py create mode 100644 voxvera/vendor/prompt_toolkit/styles/named_colors.py create mode 100644 voxvera/vendor/prompt_toolkit/styles/pygments.py create mode 100644 voxvera/vendor/prompt_toolkit/styles/style.py create mode 100644 voxvera/vendor/prompt_toolkit/styles/style_transformation.py create mode 100644 voxvera/vendor/prompt_toolkit/token.py create mode 100644 voxvera/vendor/prompt_toolkit/utils.py create mode 100644 voxvera/vendor/prompt_toolkit/validation.py create mode 100644 voxvera/vendor/prompt_toolkit/widgets/__init__.py create mode 100644 voxvera/vendor/prompt_toolkit/widgets/base.py create mode 100644 voxvera/vendor/prompt_toolkit/widgets/dialogs.py create mode 100644 voxvera/vendor/prompt_toolkit/widgets/menus.py create mode 100644 voxvera/vendor/prompt_toolkit/widgets/toolbars.py create mode 100644 voxvera/vendor/prompt_toolkit/win32_types.py create mode 100644 voxvera/vendor/pygments-2.19.2.dist-info/INSTALLER create mode 100644 voxvera/vendor/pygments-2.19.2.dist-info/METADATA create mode 100644 voxvera/vendor/pygments-2.19.2.dist-info/RECORD create mode 100644 voxvera/vendor/pygments-2.19.2.dist-info/WHEEL create mode 100644 voxvera/vendor/pygments-2.19.2.dist-info/entry_points.txt create mode 100644 voxvera/vendor/pygments-2.19.2.dist-info/licenses/AUTHORS create mode 100644 voxvera/vendor/pygments-2.19.2.dist-info/licenses/LICENSE create mode 100644 voxvera/vendor/pygments/__init__.py create mode 100644 voxvera/vendor/pygments/__main__.py create mode 100644 voxvera/vendor/pygments/cmdline.py create mode 100644 voxvera/vendor/pygments/console.py create mode 100644 voxvera/vendor/pygments/filter.py create mode 100644 voxvera/vendor/pygments/filters/__init__.py create mode 100644 voxvera/vendor/pygments/formatter.py create mode 100644 voxvera/vendor/pygments/formatters/__init__.py create mode 100644 voxvera/vendor/pygments/formatters/_mapping.py create mode 100644 voxvera/vendor/pygments/formatters/bbcode.py create mode 100644 voxvera/vendor/pygments/formatters/groff.py create mode 100644 voxvera/vendor/pygments/formatters/html.py create mode 100644 voxvera/vendor/pygments/formatters/img.py create mode 100644 voxvera/vendor/pygments/formatters/irc.py create mode 100644 voxvera/vendor/pygments/formatters/latex.py create mode 100644 voxvera/vendor/pygments/formatters/other.py create mode 100644 voxvera/vendor/pygments/formatters/pangomarkup.py create mode 100644 voxvera/vendor/pygments/formatters/rtf.py create mode 100644 voxvera/vendor/pygments/formatters/svg.py create mode 100644 voxvera/vendor/pygments/formatters/terminal.py create mode 100644 voxvera/vendor/pygments/formatters/terminal256.py create mode 100644 voxvera/vendor/pygments/lexer.py create mode 100644 voxvera/vendor/pygments/lexers/__init__.py create mode 100644 voxvera/vendor/pygments/lexers/_ada_builtins.py create mode 100644 voxvera/vendor/pygments/lexers/_asy_builtins.py create mode 100644 voxvera/vendor/pygments/lexers/_cl_builtins.py create mode 100644 voxvera/vendor/pygments/lexers/_cocoa_builtins.py create mode 100644 voxvera/vendor/pygments/lexers/_csound_builtins.py create mode 100644 voxvera/vendor/pygments/lexers/_css_builtins.py create mode 100644 voxvera/vendor/pygments/lexers/_googlesql_builtins.py create mode 100644 voxvera/vendor/pygments/lexers/_julia_builtins.py create mode 100644 voxvera/vendor/pygments/lexers/_lasso_builtins.py create mode 100644 voxvera/vendor/pygments/lexers/_lilypond_builtins.py create mode 100644 voxvera/vendor/pygments/lexers/_lua_builtins.py create mode 100644 voxvera/vendor/pygments/lexers/_luau_builtins.py create mode 100644 voxvera/vendor/pygments/lexers/_mapping.py create mode 100644 voxvera/vendor/pygments/lexers/_mql_builtins.py create mode 100644 voxvera/vendor/pygments/lexers/_mysql_builtins.py create mode 100644 voxvera/vendor/pygments/lexers/_openedge_builtins.py create mode 100644 voxvera/vendor/pygments/lexers/_php_builtins.py create mode 100644 voxvera/vendor/pygments/lexers/_postgres_builtins.py create mode 100644 voxvera/vendor/pygments/lexers/_qlik_builtins.py create mode 100644 voxvera/vendor/pygments/lexers/_scheme_builtins.py create mode 100644 voxvera/vendor/pygments/lexers/_scilab_builtins.py create mode 100644 voxvera/vendor/pygments/lexers/_sourcemod_builtins.py create mode 100644 voxvera/vendor/pygments/lexers/_sql_builtins.py create mode 100644 voxvera/vendor/pygments/lexers/_stan_builtins.py create mode 100644 voxvera/vendor/pygments/lexers/_stata_builtins.py create mode 100644 voxvera/vendor/pygments/lexers/_tsql_builtins.py create mode 100644 voxvera/vendor/pygments/lexers/_usd_builtins.py create mode 100644 voxvera/vendor/pygments/lexers/_vbscript_builtins.py create mode 100644 voxvera/vendor/pygments/lexers/_vim_builtins.py create mode 100644 voxvera/vendor/pygments/lexers/actionscript.py create mode 100644 voxvera/vendor/pygments/lexers/ada.py create mode 100644 voxvera/vendor/pygments/lexers/agile.py create mode 100644 voxvera/vendor/pygments/lexers/algebra.py create mode 100644 voxvera/vendor/pygments/lexers/ambient.py create mode 100644 voxvera/vendor/pygments/lexers/amdgpu.py create mode 100644 voxvera/vendor/pygments/lexers/ampl.py create mode 100644 voxvera/vendor/pygments/lexers/apdlexer.py create mode 100644 voxvera/vendor/pygments/lexers/apl.py create mode 100644 voxvera/vendor/pygments/lexers/archetype.py create mode 100644 voxvera/vendor/pygments/lexers/arrow.py create mode 100644 voxvera/vendor/pygments/lexers/arturo.py create mode 100644 voxvera/vendor/pygments/lexers/asc.py create mode 100644 voxvera/vendor/pygments/lexers/asm.py create mode 100644 voxvera/vendor/pygments/lexers/asn1.py create mode 100644 voxvera/vendor/pygments/lexers/automation.py create mode 100644 voxvera/vendor/pygments/lexers/bare.py create mode 100644 voxvera/vendor/pygments/lexers/basic.py create mode 100644 voxvera/vendor/pygments/lexers/bdd.py create mode 100644 voxvera/vendor/pygments/lexers/berry.py create mode 100644 voxvera/vendor/pygments/lexers/bibtex.py create mode 100644 voxvera/vendor/pygments/lexers/blueprint.py create mode 100644 voxvera/vendor/pygments/lexers/boa.py create mode 100644 voxvera/vendor/pygments/lexers/bqn.py create mode 100644 voxvera/vendor/pygments/lexers/business.py create mode 100644 voxvera/vendor/pygments/lexers/c_cpp.py create mode 100644 voxvera/vendor/pygments/lexers/c_like.py create mode 100644 voxvera/vendor/pygments/lexers/capnproto.py create mode 100644 voxvera/vendor/pygments/lexers/carbon.py create mode 100644 voxvera/vendor/pygments/lexers/cddl.py create mode 100644 voxvera/vendor/pygments/lexers/chapel.py create mode 100644 voxvera/vendor/pygments/lexers/clean.py create mode 100644 voxvera/vendor/pygments/lexers/codeql.py create mode 100644 voxvera/vendor/pygments/lexers/comal.py create mode 100644 voxvera/vendor/pygments/lexers/compiled.py create mode 100644 voxvera/vendor/pygments/lexers/configs.py create mode 100644 voxvera/vendor/pygments/lexers/console.py create mode 100644 voxvera/vendor/pygments/lexers/cplint.py create mode 100644 voxvera/vendor/pygments/lexers/crystal.py create mode 100644 voxvera/vendor/pygments/lexers/csound.py create mode 100644 voxvera/vendor/pygments/lexers/css.py create mode 100644 voxvera/vendor/pygments/lexers/d.py create mode 100644 voxvera/vendor/pygments/lexers/dalvik.py create mode 100644 voxvera/vendor/pygments/lexers/data.py create mode 100644 voxvera/vendor/pygments/lexers/dax.py create mode 100644 voxvera/vendor/pygments/lexers/devicetree.py create mode 100644 voxvera/vendor/pygments/lexers/diff.py create mode 100644 voxvera/vendor/pygments/lexers/dns.py create mode 100644 voxvera/vendor/pygments/lexers/dotnet.py create mode 100644 voxvera/vendor/pygments/lexers/dsls.py create mode 100644 voxvera/vendor/pygments/lexers/dylan.py create mode 100644 voxvera/vendor/pygments/lexers/ecl.py create mode 100644 voxvera/vendor/pygments/lexers/eiffel.py create mode 100644 voxvera/vendor/pygments/lexers/elm.py create mode 100644 voxvera/vendor/pygments/lexers/elpi.py create mode 100644 voxvera/vendor/pygments/lexers/email.py create mode 100644 voxvera/vendor/pygments/lexers/erlang.py create mode 100644 voxvera/vendor/pygments/lexers/esoteric.py create mode 100644 voxvera/vendor/pygments/lexers/ezhil.py create mode 100644 voxvera/vendor/pygments/lexers/factor.py create mode 100644 voxvera/vendor/pygments/lexers/fantom.py create mode 100644 voxvera/vendor/pygments/lexers/felix.py create mode 100644 voxvera/vendor/pygments/lexers/fift.py create mode 100644 voxvera/vendor/pygments/lexers/floscript.py create mode 100644 voxvera/vendor/pygments/lexers/forth.py create mode 100644 voxvera/vendor/pygments/lexers/fortran.py create mode 100644 voxvera/vendor/pygments/lexers/foxpro.py create mode 100644 voxvera/vendor/pygments/lexers/freefem.py create mode 100644 voxvera/vendor/pygments/lexers/func.py create mode 100644 voxvera/vendor/pygments/lexers/functional.py create mode 100644 voxvera/vendor/pygments/lexers/futhark.py create mode 100644 voxvera/vendor/pygments/lexers/gcodelexer.py create mode 100644 voxvera/vendor/pygments/lexers/gdscript.py create mode 100644 voxvera/vendor/pygments/lexers/gleam.py create mode 100644 voxvera/vendor/pygments/lexers/go.py create mode 100644 voxvera/vendor/pygments/lexers/grammar_notation.py create mode 100644 voxvera/vendor/pygments/lexers/graph.py create mode 100644 voxvera/vendor/pygments/lexers/graphics.py create mode 100644 voxvera/vendor/pygments/lexers/graphql.py create mode 100644 voxvera/vendor/pygments/lexers/graphviz.py create mode 100644 voxvera/vendor/pygments/lexers/gsql.py create mode 100644 voxvera/vendor/pygments/lexers/hare.py create mode 100644 voxvera/vendor/pygments/lexers/haskell.py create mode 100644 voxvera/vendor/pygments/lexers/haxe.py create mode 100644 voxvera/vendor/pygments/lexers/hdl.py create mode 100644 voxvera/vendor/pygments/lexers/hexdump.py create mode 100644 voxvera/vendor/pygments/lexers/html.py create mode 100644 voxvera/vendor/pygments/lexers/idl.py create mode 100644 voxvera/vendor/pygments/lexers/igor.py create mode 100644 voxvera/vendor/pygments/lexers/inferno.py create mode 100644 voxvera/vendor/pygments/lexers/installers.py create mode 100644 voxvera/vendor/pygments/lexers/int_fiction.py create mode 100644 voxvera/vendor/pygments/lexers/iolang.py create mode 100644 voxvera/vendor/pygments/lexers/j.py create mode 100644 voxvera/vendor/pygments/lexers/javascript.py create mode 100644 voxvera/vendor/pygments/lexers/jmespath.py create mode 100644 voxvera/vendor/pygments/lexers/jslt.py create mode 100644 voxvera/vendor/pygments/lexers/json5.py create mode 100644 voxvera/vendor/pygments/lexers/jsonnet.py create mode 100644 voxvera/vendor/pygments/lexers/jsx.py create mode 100644 voxvera/vendor/pygments/lexers/julia.py create mode 100644 voxvera/vendor/pygments/lexers/jvm.py create mode 100644 voxvera/vendor/pygments/lexers/kuin.py create mode 100644 voxvera/vendor/pygments/lexers/kusto.py create mode 100644 voxvera/vendor/pygments/lexers/ldap.py create mode 100644 voxvera/vendor/pygments/lexers/lean.py create mode 100644 voxvera/vendor/pygments/lexers/lilypond.py create mode 100644 voxvera/vendor/pygments/lexers/lisp.py create mode 100644 voxvera/vendor/pygments/lexers/macaulay2.py create mode 100644 voxvera/vendor/pygments/lexers/make.py create mode 100644 voxvera/vendor/pygments/lexers/maple.py create mode 100644 voxvera/vendor/pygments/lexers/markup.py create mode 100644 voxvera/vendor/pygments/lexers/math.py create mode 100644 voxvera/vendor/pygments/lexers/matlab.py create mode 100644 voxvera/vendor/pygments/lexers/maxima.py create mode 100644 voxvera/vendor/pygments/lexers/meson.py create mode 100644 voxvera/vendor/pygments/lexers/mime.py create mode 100644 voxvera/vendor/pygments/lexers/minecraft.py create mode 100644 voxvera/vendor/pygments/lexers/mips.py create mode 100644 voxvera/vendor/pygments/lexers/ml.py create mode 100644 voxvera/vendor/pygments/lexers/modeling.py create mode 100644 voxvera/vendor/pygments/lexers/modula2.py create mode 100644 voxvera/vendor/pygments/lexers/mojo.py create mode 100644 voxvera/vendor/pygments/lexers/monte.py create mode 100644 voxvera/vendor/pygments/lexers/mosel.py create mode 100644 voxvera/vendor/pygments/lexers/ncl.py create mode 100644 voxvera/vendor/pygments/lexers/nimrod.py create mode 100644 voxvera/vendor/pygments/lexers/nit.py create mode 100644 voxvera/vendor/pygments/lexers/nix.py create mode 100644 voxvera/vendor/pygments/lexers/numbair.py create mode 100644 voxvera/vendor/pygments/lexers/oberon.py create mode 100644 voxvera/vendor/pygments/lexers/objective.py create mode 100644 voxvera/vendor/pygments/lexers/ooc.py create mode 100644 voxvera/vendor/pygments/lexers/openscad.py create mode 100644 voxvera/vendor/pygments/lexers/other.py create mode 100644 voxvera/vendor/pygments/lexers/parasail.py create mode 100644 voxvera/vendor/pygments/lexers/parsers.py create mode 100644 voxvera/vendor/pygments/lexers/pascal.py create mode 100644 voxvera/vendor/pygments/lexers/pawn.py create mode 100644 voxvera/vendor/pygments/lexers/pddl.py create mode 100644 voxvera/vendor/pygments/lexers/perl.py create mode 100644 voxvera/vendor/pygments/lexers/phix.py create mode 100644 voxvera/vendor/pygments/lexers/php.py create mode 100644 voxvera/vendor/pygments/lexers/pointless.py create mode 100644 voxvera/vendor/pygments/lexers/pony.py create mode 100644 voxvera/vendor/pygments/lexers/praat.py create mode 100644 voxvera/vendor/pygments/lexers/procfile.py create mode 100644 voxvera/vendor/pygments/lexers/prolog.py create mode 100644 voxvera/vendor/pygments/lexers/promql.py create mode 100644 voxvera/vendor/pygments/lexers/prql.py create mode 100644 voxvera/vendor/pygments/lexers/ptx.py create mode 100644 voxvera/vendor/pygments/lexers/python.py create mode 100644 voxvera/vendor/pygments/lexers/q.py create mode 100644 voxvera/vendor/pygments/lexers/qlik.py create mode 100644 voxvera/vendor/pygments/lexers/qvt.py create mode 100644 voxvera/vendor/pygments/lexers/r.py create mode 100644 voxvera/vendor/pygments/lexers/rdf.py create mode 100644 voxvera/vendor/pygments/lexers/rebol.py create mode 100644 voxvera/vendor/pygments/lexers/rego.py create mode 100644 voxvera/vendor/pygments/lexers/resource.py create mode 100644 voxvera/vendor/pygments/lexers/ride.py create mode 100644 voxvera/vendor/pygments/lexers/rita.py create mode 100644 voxvera/vendor/pygments/lexers/rnc.py create mode 100644 voxvera/vendor/pygments/lexers/roboconf.py create mode 100644 voxvera/vendor/pygments/lexers/robotframework.py create mode 100644 voxvera/vendor/pygments/lexers/ruby.py create mode 100644 voxvera/vendor/pygments/lexers/rust.py create mode 100644 voxvera/vendor/pygments/lexers/sas.py create mode 100644 voxvera/vendor/pygments/lexers/savi.py create mode 100644 voxvera/vendor/pygments/lexers/scdoc.py create mode 100644 voxvera/vendor/pygments/lexers/scripting.py create mode 100644 voxvera/vendor/pygments/lexers/sgf.py create mode 100644 voxvera/vendor/pygments/lexers/shell.py create mode 100644 voxvera/vendor/pygments/lexers/sieve.py create mode 100644 voxvera/vendor/pygments/lexers/slash.py create mode 100644 voxvera/vendor/pygments/lexers/smalltalk.py create mode 100644 voxvera/vendor/pygments/lexers/smithy.py create mode 100644 voxvera/vendor/pygments/lexers/smv.py create mode 100644 voxvera/vendor/pygments/lexers/snobol.py create mode 100644 voxvera/vendor/pygments/lexers/solidity.py create mode 100644 voxvera/vendor/pygments/lexers/soong.py create mode 100644 voxvera/vendor/pygments/lexers/sophia.py create mode 100644 voxvera/vendor/pygments/lexers/special.py create mode 100644 voxvera/vendor/pygments/lexers/spice.py create mode 100644 voxvera/vendor/pygments/lexers/sql.py create mode 100644 voxvera/vendor/pygments/lexers/srcinfo.py create mode 100644 voxvera/vendor/pygments/lexers/stata.py create mode 100644 voxvera/vendor/pygments/lexers/supercollider.py create mode 100644 voxvera/vendor/pygments/lexers/tablegen.py create mode 100644 voxvera/vendor/pygments/lexers/tact.py create mode 100644 voxvera/vendor/pygments/lexers/tal.py create mode 100644 voxvera/vendor/pygments/lexers/tcl.py create mode 100644 voxvera/vendor/pygments/lexers/teal.py create mode 100644 voxvera/vendor/pygments/lexers/templates.py create mode 100644 voxvera/vendor/pygments/lexers/teraterm.py create mode 100644 voxvera/vendor/pygments/lexers/testing.py create mode 100644 voxvera/vendor/pygments/lexers/text.py create mode 100644 voxvera/vendor/pygments/lexers/textedit.py create mode 100644 voxvera/vendor/pygments/lexers/textfmts.py create mode 100644 voxvera/vendor/pygments/lexers/theorem.py create mode 100644 voxvera/vendor/pygments/lexers/thingsdb.py create mode 100644 voxvera/vendor/pygments/lexers/tlb.py create mode 100644 voxvera/vendor/pygments/lexers/tls.py create mode 100644 voxvera/vendor/pygments/lexers/tnt.py create mode 100644 voxvera/vendor/pygments/lexers/trafficscript.py create mode 100644 voxvera/vendor/pygments/lexers/typoscript.py create mode 100644 voxvera/vendor/pygments/lexers/typst.py create mode 100644 voxvera/vendor/pygments/lexers/ul4.py create mode 100644 voxvera/vendor/pygments/lexers/unicon.py create mode 100644 voxvera/vendor/pygments/lexers/urbi.py create mode 100644 voxvera/vendor/pygments/lexers/usd.py create mode 100644 voxvera/vendor/pygments/lexers/varnish.py create mode 100644 voxvera/vendor/pygments/lexers/verification.py create mode 100644 voxvera/vendor/pygments/lexers/verifpal.py create mode 100644 voxvera/vendor/pygments/lexers/vip.py create mode 100644 voxvera/vendor/pygments/lexers/vyper.py create mode 100644 voxvera/vendor/pygments/lexers/web.py create mode 100644 voxvera/vendor/pygments/lexers/webassembly.py create mode 100644 voxvera/vendor/pygments/lexers/webidl.py create mode 100644 voxvera/vendor/pygments/lexers/webmisc.py create mode 100644 voxvera/vendor/pygments/lexers/wgsl.py create mode 100644 voxvera/vendor/pygments/lexers/whiley.py create mode 100644 voxvera/vendor/pygments/lexers/wowtoc.py create mode 100644 voxvera/vendor/pygments/lexers/wren.py create mode 100644 voxvera/vendor/pygments/lexers/x10.py create mode 100644 voxvera/vendor/pygments/lexers/xorg.py create mode 100644 voxvera/vendor/pygments/lexers/yang.py create mode 100644 voxvera/vendor/pygments/lexers/yara.py create mode 100644 voxvera/vendor/pygments/lexers/zig.py create mode 100644 voxvera/vendor/pygments/modeline.py create mode 100644 voxvera/vendor/pygments/plugin.py create mode 100644 voxvera/vendor/pygments/regexopt.py create mode 100644 voxvera/vendor/pygments/scanner.py create mode 100644 voxvera/vendor/pygments/sphinxext.py create mode 100644 voxvera/vendor/pygments/style.py create mode 100644 voxvera/vendor/pygments/styles/__init__.py create mode 100644 voxvera/vendor/pygments/styles/_mapping.py create mode 100644 voxvera/vendor/pygments/styles/abap.py create mode 100644 voxvera/vendor/pygments/styles/algol.py create mode 100644 voxvera/vendor/pygments/styles/algol_nu.py create mode 100644 voxvera/vendor/pygments/styles/arduino.py create mode 100644 voxvera/vendor/pygments/styles/autumn.py create mode 100644 voxvera/vendor/pygments/styles/borland.py create mode 100644 voxvera/vendor/pygments/styles/bw.py create mode 100644 voxvera/vendor/pygments/styles/coffee.py create mode 100644 voxvera/vendor/pygments/styles/colorful.py create mode 100644 voxvera/vendor/pygments/styles/default.py create mode 100644 voxvera/vendor/pygments/styles/dracula.py create mode 100644 voxvera/vendor/pygments/styles/emacs.py create mode 100644 voxvera/vendor/pygments/styles/friendly.py create mode 100644 voxvera/vendor/pygments/styles/friendly_grayscale.py create mode 100644 voxvera/vendor/pygments/styles/fruity.py create mode 100644 voxvera/vendor/pygments/styles/gh_dark.py create mode 100644 voxvera/vendor/pygments/styles/gruvbox.py create mode 100644 voxvera/vendor/pygments/styles/igor.py create mode 100644 voxvera/vendor/pygments/styles/inkpot.py create mode 100644 voxvera/vendor/pygments/styles/lightbulb.py create mode 100644 voxvera/vendor/pygments/styles/lilypond.py create mode 100644 voxvera/vendor/pygments/styles/lovelace.py create mode 100644 voxvera/vendor/pygments/styles/manni.py create mode 100644 voxvera/vendor/pygments/styles/material.py create mode 100644 voxvera/vendor/pygments/styles/monokai.py create mode 100644 voxvera/vendor/pygments/styles/murphy.py create mode 100644 voxvera/vendor/pygments/styles/native.py create mode 100644 voxvera/vendor/pygments/styles/nord.py create mode 100644 voxvera/vendor/pygments/styles/onedark.py create mode 100644 voxvera/vendor/pygments/styles/paraiso_dark.py create mode 100644 voxvera/vendor/pygments/styles/paraiso_light.py create mode 100644 voxvera/vendor/pygments/styles/pastie.py create mode 100644 voxvera/vendor/pygments/styles/perldoc.py create mode 100644 voxvera/vendor/pygments/styles/rainbow_dash.py create mode 100644 voxvera/vendor/pygments/styles/rrt.py create mode 100644 voxvera/vendor/pygments/styles/sas.py create mode 100644 voxvera/vendor/pygments/styles/solarized.py create mode 100644 voxvera/vendor/pygments/styles/staroffice.py create mode 100644 voxvera/vendor/pygments/styles/stata_dark.py create mode 100644 voxvera/vendor/pygments/styles/stata_light.py create mode 100644 voxvera/vendor/pygments/styles/tango.py create mode 100644 voxvera/vendor/pygments/styles/trac.py create mode 100644 voxvera/vendor/pygments/styles/vim.py create mode 100644 voxvera/vendor/pygments/styles/vs.py create mode 100644 voxvera/vendor/pygments/styles/xcode.py create mode 100644 voxvera/vendor/pygments/styles/zenburn.py create mode 100644 voxvera/vendor/pygments/token.py create mode 100644 voxvera/vendor/pygments/unistring.py create mode 100644 voxvera/vendor/pygments/util.py create mode 100644 voxvera/vendor/pypdf-6.8.0.dist-info/INSTALLER create mode 100644 voxvera/vendor/pypdf-6.8.0.dist-info/METADATA create mode 100644 voxvera/vendor/pypdf-6.8.0.dist-info/RECORD create mode 100644 voxvera/vendor/pypdf-6.8.0.dist-info/REQUESTED create mode 100644 voxvera/vendor/pypdf-6.8.0.dist-info/WHEEL create mode 100644 voxvera/vendor/pypdf-6.8.0.dist-info/licenses/LICENSE create mode 100644 voxvera/vendor/pypdf/__init__.py create mode 100644 voxvera/vendor/pypdf/_cmap.py create mode 100644 voxvera/vendor/pypdf/_codecs/__init__.py create mode 100644 voxvera/vendor/pypdf/_codecs/_codecs.py create mode 100644 voxvera/vendor/pypdf/_codecs/adobe_glyphs.py create mode 100644 voxvera/vendor/pypdf/_codecs/core_font_metrics.py create mode 100644 voxvera/vendor/pypdf/_codecs/pdfdoc.py create mode 100644 voxvera/vendor/pypdf/_codecs/std.py create mode 100644 voxvera/vendor/pypdf/_codecs/symbol.py create mode 100644 voxvera/vendor/pypdf/_codecs/zapfding.py create mode 100644 voxvera/vendor/pypdf/_crypt_providers/__init__.py create mode 100644 voxvera/vendor/pypdf/_crypt_providers/_base.py create mode 100644 voxvera/vendor/pypdf/_crypt_providers/_cryptography.py create mode 100644 voxvera/vendor/pypdf/_crypt_providers/_fallback.py create mode 100644 voxvera/vendor/pypdf/_crypt_providers/_pycryptodome.py create mode 100644 voxvera/vendor/pypdf/_doc_common.py create mode 100644 voxvera/vendor/pypdf/_encryption.py create mode 100644 voxvera/vendor/pypdf/_font.py create mode 100644 voxvera/vendor/pypdf/_page.py create mode 100644 voxvera/vendor/pypdf/_page_labels.py create mode 100644 voxvera/vendor/pypdf/_protocols.py create mode 100644 voxvera/vendor/pypdf/_reader.py create mode 100644 voxvera/vendor/pypdf/_text_extraction/__init__.py create mode 100644 voxvera/vendor/pypdf/_text_extraction/_layout_mode/__init__.py create mode 100644 voxvera/vendor/pypdf/_text_extraction/_layout_mode/_fixed_width_page.py create mode 100644 voxvera/vendor/pypdf/_text_extraction/_layout_mode/_text_state_manager.py create mode 100644 voxvera/vendor/pypdf/_text_extraction/_layout_mode/_text_state_params.py create mode 100644 voxvera/vendor/pypdf/_text_extraction/_text_extractor.py create mode 100644 voxvera/vendor/pypdf/_utils.py create mode 100644 voxvera/vendor/pypdf/_version.py create mode 100644 voxvera/vendor/pypdf/_writer.py create mode 100644 voxvera/vendor/pypdf/annotations/__init__.py create mode 100644 voxvera/vendor/pypdf/annotations/_base.py create mode 100644 voxvera/vendor/pypdf/annotations/_markup_annotations.py create mode 100644 voxvera/vendor/pypdf/annotations/_non_markup_annotations.py create mode 100644 voxvera/vendor/pypdf/constants.py create mode 100644 voxvera/vendor/pypdf/errors.py create mode 100644 voxvera/vendor/pypdf/filters.py create mode 100644 voxvera/vendor/pypdf/generic/__init__.py create mode 100644 voxvera/vendor/pypdf/generic/_appearance_stream.py create mode 100644 voxvera/vendor/pypdf/generic/_base.py create mode 100644 voxvera/vendor/pypdf/generic/_data_structures.py create mode 100644 voxvera/vendor/pypdf/generic/_files.py create mode 100644 voxvera/vendor/pypdf/generic/_fit.py create mode 100644 voxvera/vendor/pypdf/generic/_image_inline.py create mode 100644 voxvera/vendor/pypdf/generic/_image_xobject.py create mode 100644 voxvera/vendor/pypdf/generic/_link.py create mode 100644 voxvera/vendor/pypdf/generic/_outline.py create mode 100644 voxvera/vendor/pypdf/generic/_rectangle.py create mode 100644 voxvera/vendor/pypdf/generic/_utils.py create mode 100644 voxvera/vendor/pypdf/generic/_viewerpref.py create mode 100644 voxvera/vendor/pypdf/pagerange.py create mode 100644 voxvera/vendor/pypdf/papersizes.py create mode 100644 voxvera/vendor/pypdf/py.typed create mode 100644 voxvera/vendor/pypdf/types.py create mode 100644 voxvera/vendor/pypdf/xmp.py create mode 100644 voxvera/vendor/qrcode-8.2.dist-info/INSTALLER create mode 100644 voxvera/vendor/qrcode-8.2.dist-info/LICENSE create mode 100644 voxvera/vendor/qrcode-8.2.dist-info/METADATA create mode 100644 voxvera/vendor/qrcode-8.2.dist-info/RECORD create mode 100644 voxvera/vendor/qrcode-8.2.dist-info/REQUESTED create mode 100644 voxvera/vendor/qrcode-8.2.dist-info/WHEEL create mode 100644 voxvera/vendor/qrcode-8.2.dist-info/entry_points.txt create mode 100644 voxvera/vendor/qrcode/LUT.py create mode 100644 voxvera/vendor/qrcode/__init__.py create mode 100644 voxvera/vendor/qrcode/base.py create mode 100644 voxvera/vendor/qrcode/compat/__init__.py create mode 100644 voxvera/vendor/qrcode/compat/etree.py create mode 100644 voxvera/vendor/qrcode/compat/png.py create mode 100755 voxvera/vendor/qrcode/console_scripts.py create mode 100644 voxvera/vendor/qrcode/constants.py create mode 100644 voxvera/vendor/qrcode/exceptions.py create mode 100644 voxvera/vendor/qrcode/image/__init__.py create mode 100644 voxvera/vendor/qrcode/image/base.py create mode 100644 voxvera/vendor/qrcode/image/pil.py create mode 100644 voxvera/vendor/qrcode/image/pure.py create mode 100644 voxvera/vendor/qrcode/image/styledpil.py create mode 100644 voxvera/vendor/qrcode/image/styles/__init__.py create mode 100644 voxvera/vendor/qrcode/image/styles/colormasks.py create mode 100644 voxvera/vendor/qrcode/image/styles/moduledrawers/__init__.py create mode 100644 voxvera/vendor/qrcode/image/styles/moduledrawers/base.py create mode 100644 voxvera/vendor/qrcode/image/styles/moduledrawers/pil.py create mode 100644 voxvera/vendor/qrcode/image/styles/moduledrawers/svg.py create mode 100644 voxvera/vendor/qrcode/image/svg.py create mode 100644 voxvera/vendor/qrcode/main.py create mode 100644 voxvera/vendor/qrcode/release.py create mode 100644 voxvera/vendor/qrcode/tests/__init__.py create mode 100644 voxvera/vendor/qrcode/tests/consts.py create mode 100644 voxvera/vendor/qrcode/tests/test_example.py create mode 100644 voxvera/vendor/qrcode/tests/test_qrcode.py create mode 100644 voxvera/vendor/qrcode/tests/test_qrcode_pil.py create mode 100644 voxvera/vendor/qrcode/tests/test_qrcode_pypng.py create mode 100644 voxvera/vendor/qrcode/tests/test_qrcode_svg.py create mode 100644 voxvera/vendor/qrcode/tests/test_release.py create mode 100644 voxvera/vendor/qrcode/tests/test_script.py create mode 100644 voxvera/vendor/qrcode/tests/test_util.py create mode 100644 voxvera/vendor/qrcode/util.py create mode 100644 voxvera/vendor/rich-14.3.3.dist-info/INSTALLER create mode 100644 voxvera/vendor/rich-14.3.3.dist-info/METADATA create mode 100644 voxvera/vendor/rich-14.3.3.dist-info/RECORD create mode 100644 voxvera/vendor/rich-14.3.3.dist-info/REQUESTED create mode 100644 voxvera/vendor/rich-14.3.3.dist-info/WHEEL create mode 100644 voxvera/vendor/rich-14.3.3.dist-info/licenses/LICENSE create mode 100644 voxvera/vendor/rich/__init__.py create mode 100644 voxvera/vendor/rich/__main__.py create mode 100644 voxvera/vendor/rich/_emoji_codes.py create mode 100644 voxvera/vendor/rich/_emoji_replace.py create mode 100644 voxvera/vendor/rich/_export_format.py create mode 100644 voxvera/vendor/rich/_extension.py create mode 100644 voxvera/vendor/rich/_fileno.py create mode 100644 voxvera/vendor/rich/_inspect.py create mode 100644 voxvera/vendor/rich/_log_render.py create mode 100644 voxvera/vendor/rich/_loop.py create mode 100644 voxvera/vendor/rich/_null_file.py create mode 100644 voxvera/vendor/rich/_palettes.py create mode 100644 voxvera/vendor/rich/_pick.py create mode 100644 voxvera/vendor/rich/_ratio.py create mode 100644 voxvera/vendor/rich/_spinners.py create mode 100644 voxvera/vendor/rich/_stack.py create mode 100644 voxvera/vendor/rich/_timer.py create mode 100644 voxvera/vendor/rich/_unicode_data/__init__.py create mode 100644 voxvera/vendor/rich/_unicode_data/_versions.py create mode 100644 voxvera/vendor/rich/_unicode_data/unicode10-0-0.py create mode 100644 voxvera/vendor/rich/_unicode_data/unicode11-0-0.py create mode 100644 voxvera/vendor/rich/_unicode_data/unicode12-0-0.py create mode 100644 voxvera/vendor/rich/_unicode_data/unicode12-1-0.py create mode 100644 voxvera/vendor/rich/_unicode_data/unicode13-0-0.py create mode 100644 voxvera/vendor/rich/_unicode_data/unicode14-0-0.py create mode 100644 voxvera/vendor/rich/_unicode_data/unicode15-0-0.py create mode 100644 voxvera/vendor/rich/_unicode_data/unicode15-1-0.py create mode 100644 voxvera/vendor/rich/_unicode_data/unicode16-0-0.py create mode 100644 voxvera/vendor/rich/_unicode_data/unicode17-0-0.py create mode 100644 voxvera/vendor/rich/_unicode_data/unicode4-1-0.py create mode 100644 voxvera/vendor/rich/_unicode_data/unicode5-0-0.py create mode 100644 voxvera/vendor/rich/_unicode_data/unicode5-1-0.py create mode 100644 voxvera/vendor/rich/_unicode_data/unicode5-2-0.py create mode 100644 voxvera/vendor/rich/_unicode_data/unicode6-0-0.py create mode 100644 voxvera/vendor/rich/_unicode_data/unicode6-1-0.py create mode 100644 voxvera/vendor/rich/_unicode_data/unicode6-2-0.py create mode 100644 voxvera/vendor/rich/_unicode_data/unicode6-3-0.py create mode 100644 voxvera/vendor/rich/_unicode_data/unicode7-0-0.py create mode 100644 voxvera/vendor/rich/_unicode_data/unicode8-0-0.py create mode 100644 voxvera/vendor/rich/_unicode_data/unicode9-0-0.py create mode 100644 voxvera/vendor/rich/_win32_console.py create mode 100644 voxvera/vendor/rich/_windows.py create mode 100644 voxvera/vendor/rich/_windows_renderer.py create mode 100644 voxvera/vendor/rich/_wrap.py create mode 100644 voxvera/vendor/rich/abc.py create mode 100644 voxvera/vendor/rich/align.py create mode 100644 voxvera/vendor/rich/ansi.py create mode 100644 voxvera/vendor/rich/bar.py create mode 100644 voxvera/vendor/rich/box.py create mode 100644 voxvera/vendor/rich/cells.py create mode 100644 voxvera/vendor/rich/color.py create mode 100644 voxvera/vendor/rich/color_triplet.py create mode 100644 voxvera/vendor/rich/columns.py create mode 100644 voxvera/vendor/rich/console.py create mode 100644 voxvera/vendor/rich/constrain.py create mode 100644 voxvera/vendor/rich/containers.py create mode 100644 voxvera/vendor/rich/control.py create mode 100644 voxvera/vendor/rich/default_styles.py create mode 100644 voxvera/vendor/rich/diagnose.py create mode 100644 voxvera/vendor/rich/emoji.py create mode 100644 voxvera/vendor/rich/errors.py create mode 100644 voxvera/vendor/rich/file_proxy.py create mode 100644 voxvera/vendor/rich/filesize.py create mode 100644 voxvera/vendor/rich/highlighter.py create mode 100644 voxvera/vendor/rich/json.py create mode 100644 voxvera/vendor/rich/jupyter.py create mode 100644 voxvera/vendor/rich/layout.py create mode 100644 voxvera/vendor/rich/live.py create mode 100644 voxvera/vendor/rich/live_render.py create mode 100644 voxvera/vendor/rich/logging.py create mode 100644 voxvera/vendor/rich/markdown.py create mode 100644 voxvera/vendor/rich/markup.py create mode 100644 voxvera/vendor/rich/measure.py create mode 100644 voxvera/vendor/rich/padding.py create mode 100644 voxvera/vendor/rich/pager.py create mode 100644 voxvera/vendor/rich/palette.py create mode 100644 voxvera/vendor/rich/panel.py create mode 100644 voxvera/vendor/rich/pretty.py create mode 100644 voxvera/vendor/rich/progress.py create mode 100644 voxvera/vendor/rich/progress_bar.py create mode 100644 voxvera/vendor/rich/prompt.py create mode 100644 voxvera/vendor/rich/protocol.py create mode 100644 voxvera/vendor/rich/py.typed create mode 100644 voxvera/vendor/rich/region.py create mode 100644 voxvera/vendor/rich/repr.py create mode 100644 voxvera/vendor/rich/rule.py create mode 100644 voxvera/vendor/rich/scope.py create mode 100644 voxvera/vendor/rich/screen.py create mode 100644 voxvera/vendor/rich/segment.py create mode 100644 voxvera/vendor/rich/spinner.py create mode 100644 voxvera/vendor/rich/status.py create mode 100644 voxvera/vendor/rich/style.py create mode 100644 voxvera/vendor/rich/styled.py create mode 100644 voxvera/vendor/rich/syntax.py create mode 100644 voxvera/vendor/rich/table.py create mode 100644 voxvera/vendor/rich/terminal_theme.py create mode 100644 voxvera/vendor/rich/text.py create mode 100644 voxvera/vendor/rich/theme.py create mode 100644 voxvera/vendor/rich/themes.py create mode 100644 voxvera/vendor/rich/traceback.py create mode 100644 voxvera/vendor/rich/tree.py create mode 100644 voxvera/vendor/wcwidth-0.6.0.dist-info/INSTALLER create mode 100644 voxvera/vendor/wcwidth-0.6.0.dist-info/METADATA create mode 100644 voxvera/vendor/wcwidth-0.6.0.dist-info/RECORD create mode 100644 voxvera/vendor/wcwidth-0.6.0.dist-info/WHEEL create mode 100644 voxvera/vendor/wcwidth-0.6.0.dist-info/licenses/LICENSE create mode 100644 voxvera/vendor/wcwidth/__init__.py create mode 100644 voxvera/vendor/wcwidth/bisearch.py create mode 100644 voxvera/vendor/wcwidth/control_codes.py create mode 100644 voxvera/vendor/wcwidth/escape_sequences.py create mode 100644 voxvera/vendor/wcwidth/grapheme.py create mode 100644 voxvera/vendor/wcwidth/py.typed create mode 100644 voxvera/vendor/wcwidth/sgr_state.py create mode 100644 voxvera/vendor/wcwidth/table_ambiguous.py create mode 100644 voxvera/vendor/wcwidth/table_grapheme.py create mode 100644 voxvera/vendor/wcwidth/table_mc.py create mode 100644 voxvera/vendor/wcwidth/table_vs16.py create mode 100644 voxvera/vendor/wcwidth/table_wide.py create mode 100644 voxvera/vendor/wcwidth/table_zero.py create mode 100644 voxvera/vendor/wcwidth/textwrap.py create mode 100644 voxvera/vendor/wcwidth/unicode_versions.py create mode 100644 voxvera/vendor/wcwidth/wcwidth.py diff --git a/voxvera/__init__.py b/voxvera/__init__.py index ee5434a..4b823c2 100644 --- a/voxvera/__init__.py +++ b/voxvera/__init__.py @@ -1 +1,8 @@ -"""VoxVera command line utilities.""" +import sys +import os +from pathlib import Path + +# Add the vendor directory to sys.path so we can run without pip install +vendor_dir = Path(__file__).parent / "vendor" +if vendor_dir.exists(): + sys.path.insert(0, str(vendor_dir)) diff --git a/voxvera/cli.py b/voxvera/cli.py index 18862f9..bfaac89 100644 --- a/voxvera/cli.py +++ b/voxvera/cli.py @@ -310,6 +310,27 @@ def copy_template(name: str) -> str: return str(dest) +def bundle_portable(dest_zip: Path): + """Bundle the entire VoxVera source, vendor libs, and Tor binaries into a ZIP.""" + with zipfile.ZipFile(dest_zip, 'w', zipfile.ZIP_DEFLATED) as zipf: + # Include the voxvera/ source directory + for root, dirs, files in os.walk(ROOT): + if "__pycache__" in root or "host" in root: + continue + for file in files: + file_path = Path(root) / file + arcname = file_path.relative_to(ROOT.parent) + zipf.write(file_path, arcname) + + # Include the run scripts from project root + for script in ["voxvera-run.sh", "voxvera-install.sh", "README.md", "requirements.txt"]: + script_path = ROOT.parent / script + if script_path.exists(): + zipf.write(script_path, script) + + print(f"Portable bundle created at {dest_zip}") + + def build_assets( config_path: str, download_path: str | None = None ): @@ -331,7 +352,6 @@ def build_assets( code = Path(lp).stem with open(lp, "r", encoding="utf-8") as lf: l_data = json.load(lf) - # Only include web and meta to keep HTML size small all_locales[code] = { "meta": l_data.get("meta", {}), "web": l_data.get("web", {}) @@ -339,7 +359,7 @@ def build_assets( html = html.replace("{{locales}}", json.dumps(all_locales)) - # Statically replace {{placeholders}} with config values for Tor JS-disabled compatibility + # Statically replace {{placeholders}} for Tor JS-disabled compatibility for key, value in data.items(): html = html.replace(f"{{{{{key}}}}}", str(value)) @@ -347,20 +367,21 @@ def build_assets( dest = ROOT / "host" / folder_name os.makedirs(dest, exist_ok=True) + # Update download link to point to our viral portable bundle + html = html.replace("download/download.zip", "download/voxvera-portable.zip") + with open(dest / "index.html", "w") as fh: fh.write(html) + + # Create the viral portable bundle + download_dir = dest / "download" + os.makedirs(download_dir, exist_ok=True) + bundle_portable(download_dir / "voxvera-portable.zip") + if download_path: - os.makedirs(dest / "download", exist_ok=True) - shutil.copy(download_path, dest / "download" / "download.zip") - # Only copy config if it's not already in the destination - if Path(config_path) != dest / "config.json": - shutil.copy(config_path, dest / "config.json") - # Copy QR codes only if they exist (may not be generated if URLs not set) - for qr_file in ["qrcode-content.png", "qrcode-tear-offs.png"]: - qr_src = Path(src_dir) / qr_file - if qr_src.exists(): - shutil.copy(qr_src, dest / qr_file) - print(f"Flyer files created under {dest}") + shutil.copy(download_path, download_dir / "extra-content.zip") + + # ... rest of the copies ... def get_tor_ports(): @@ -620,6 +641,29 @@ def import_multiple_sites(source_dir: str = None): print("\nImport multiple complete.") +def vendorize(): + """Download all dependencies into voxvera/vendor/ for portable use.""" + vendor_dir = ROOT / "vendor" + req_file = ROOT.parent / "requirements.txt" + if not req_file.exists(): + print("Error: requirements.txt not found.") + return + + os.makedirs(vendor_dir, exist_ok=True) + print(f"Vendorizing dependencies into {vendor_dir}...") + + try: + subprocess.check_call([ + sys.executable, "-m", "pip", "install", + "-r", str(req_file), + "--target", str(vendor_dir), + "--no-compile" + ]) + print("Vendorization complete.") + except subprocess.CalledProcessError as e: + print(f"Error during vendorization: {e}") + + def batch_import_configs(): import glob @@ -914,6 +958,7 @@ def main(argv=None): help="Skip interactive prompts and use existing config", ) sub.add_parser("check", help="Check for required external dependencies") + sub.add_parser("vendorize", help="Download all dependencies into voxvera/vendor/ for portability") sub.add_parser("manage", help="Manage VoxVera servers interactively") args = parser.parse_args(argv) @@ -986,6 +1031,8 @@ def main(argv=None): serve(config_path) elif args.command == "check": check_deps() + elif args.command == "vendorize": + vendorize() else: parser.print_help() diff --git a/voxvera/vendor/InquirerPy/__init__.py b/voxvera/vendor/InquirerPy/__init__.py new file mode 100644 index 0000000..4d3f794 --- /dev/null +++ b/voxvera/vendor/InquirerPy/__init__.py @@ -0,0 +1,2 @@ +from InquirerPy.resolver import prompt, prompt_async +from InquirerPy.utils import get_style diff --git a/voxvera/vendor/InquirerPy/base/__init__.py b/voxvera/vendor/InquirerPy/base/__init__.py new file mode 100644 index 0000000..fb6d176 --- /dev/null +++ b/voxvera/vendor/InquirerPy/base/__init__.py @@ -0,0 +1,15 @@ +"""Module contains base class for prompts. + +BaseSimplePrompt ← InputPrompt ← SecretPrompt ... + ↑ +BaseComplexPrompt + ↑ +BaseListPrompt ← FuzzyPrompt + ↑ +ListPrompt ← ExpandPrompt ... +""" + +from .complex import BaseComplexPrompt, FakeDocument +from .control import Choice, InquirerPyUIListControl +from .list import BaseListPrompt +from .simple import BaseSimplePrompt diff --git a/voxvera/vendor/InquirerPy/base/complex.py b/voxvera/vendor/InquirerPy/base/complex.py new file mode 100644 index 0000000..89c9f73 --- /dev/null +++ b/voxvera/vendor/InquirerPy/base/complex.py @@ -0,0 +1,294 @@ +"""Contains the interface class :class:`.BaseComplexPrompt` for more complex prompts and the mocked document class :class:`.FakeDocument`.""" +import shutil +from dataclasses import dataclass +from typing import Any, Callable, List, Optional, Tuple, Union + +from prompt_toolkit.application import Application +from prompt_toolkit.enums import EditingMode +from prompt_toolkit.filters.base import Condition, FilterOrBool +from prompt_toolkit.key_binding.key_bindings import KeyHandlerCallable +from prompt_toolkit.keys import Keys + +from InquirerPy.base.simple import BaseSimplePrompt +from InquirerPy.enum import INQUIRERPY_KEYBOARD_INTERRUPT +from InquirerPy.utils import ( + InquirerPySessionResult, + InquirerPyStyle, + InquirerPyValidate, +) + + +@dataclass +class FakeDocument: + """A fake `prompt_toolkit` document class. + + Work around to allow non-buffer type :class:`~prompt_toolkit.layout.UIControl` to use + :class:`~prompt_toolkit.validation.Validator`. + + Args: + text: Content to be validated. + cursor_position: Fake cursor position. + """ + + text: str + cursor_position: int = 0 + + +class BaseComplexPrompt(BaseSimplePrompt): + """A base class to create a more complex prompt that will involve :class:`~prompt_toolkit.application.Application`. + + Note: + This class does not create :class:`~prompt_toolkit.layout.Layout` nor :class:`~prompt_toolkit.application.Application`, + it only contains the necessary attributes and helper functions to be consumed. + + Note: + Use :class:`~InquirerPy.base.BaseListPrompt` to create a complex list prompt which involves multiple choices. It has + more methods and helper function implemented. + + See Also: + :class:`~InquirerPy.base.BaseListPrompt` + :class:`~InquirerPy.prompts.fuzzy.FuzzyPrompt` + """ + + def __init__( + self, + message: Union[str, Callable[[InquirerPySessionResult], str]], + style: Optional[InquirerPyStyle] = None, + border: bool = False, + vi_mode: bool = False, + qmark: str = "?", + amark: str = "?", + instruction: str = "", + long_instruction: str = "", + transformer: Optional[Callable[[Any], Any]] = None, + filter: Optional[Callable[[Any], Any]] = None, + validate: Optional[InquirerPyValidate] = None, + invalid_message: str = "Invalid input", + wrap_lines: bool = True, + raise_keyboard_interrupt: bool = True, + mandatory: bool = True, + mandatory_message: str = "Mandatory prompt", + session_result: Optional[InquirerPySessionResult] = None, + ) -> None: + super().__init__( + message=message, + style=style, + vi_mode=vi_mode, + qmark=qmark, + amark=amark, + instruction=instruction, + transformer=transformer, + filter=filter, + invalid_message=invalid_message, + validate=validate, + wrap_lines=wrap_lines, + raise_keyboard_interrupt=raise_keyboard_interrupt, + mandatory=mandatory, + mandatory_message=mandatory_message, + session_result=session_result, + ) + self._invalid_message = invalid_message + self._rendered = False + self._invalid = False + self._loading = False + self._application: Application + self._long_instruction = long_instruction + self._border = border + self._height_offset = 2 # prev prompt result + current prompt question + if self._border: + self._height_offset += 2 + if self._long_instruction: + self._height_offset += 1 + self._validation_window_bottom_offset = 0 if not self._long_instruction else 1 + if self._wrap_lines: + self._validation_window_bottom_offset += ( + self.extra_long_instruction_line_count + ) + + self._is_vim_edit = Condition(lambda: self._editing_mode == EditingMode.VI) + self._is_invalid = Condition(lambda: self._invalid) + self._is_displaying_long_instruction = Condition( + lambda: self._long_instruction != "" + ) + + def _redraw(self) -> None: + """Redraw the application UI.""" + self._application.invalidate() + + def register_kb( + self, *keys: Union[Keys, str], filter: FilterOrBool = True + ) -> Callable[[KeyHandlerCallable], KeyHandlerCallable]: + """Decorate keybinding registration function. + + Ensure that the `invalid` state is cleared on next keybinding entered. + """ + kb_dec = super().register_kb(*keys, filter=filter) + + def decorator(func: KeyHandlerCallable) -> KeyHandlerCallable: + @kb_dec + def executable(event): + if self._invalid: + self._invalid = False + func(event) + + return executable + + return decorator + + def _exception_handler(self, _, context) -> None: + """Set exception handler for the event loop. + + Skip the question and raise exception. + + Args: + loop: Current event loop. + context: Exception context. + """ + self._status["answered"] = True + self._status["result"] = INQUIRERPY_KEYBOARD_INTERRUPT + self._status["skipped"] = True + self._application.exit(exception=context["exception"]) + + def _after_render(self, app: Optional[Application]) -> None: + """Run after the :class:`~prompt_toolkit.application.Application` is rendered/updated. + + Since this function is fired up on each render, adding a check on `self._rendered` to + process logics that should only run once. + + Set event loop exception handler here, since its guaranteed that the event loop is running + in `_after_render`. + """ + if not self._rendered: + self._rendered = True + + self._keybinding_factory() + self._on_rendered(app) + + def _set_error(self, message: str) -> None: + """Set error message and set invalid state. + + Args: + message: Error message to display. + """ + self._invalid_message = message + self._invalid = True + + def _get_error_message(self) -> List[Tuple[str, str]]: + """Obtain the error message dynamically. + + Returns: + FormattedText in list of tuple format. + """ + return [ + ( + "class:validation-toolbar", + self._invalid_message, + ) + ] + + def _on_rendered(self, _: Optional[Application]) -> None: + """Run once after the UI is rendered. Acts like `ComponentDidMount`.""" + pass + + def _get_prompt_message(self) -> List[Tuple[str, str]]: + """Get the prompt message to display. + + Returns: + Formatted text in list of tuple format. + """ + pre_answer = ( + "class:instruction", + " %s " % self.instruction if self.instruction else " ", + ) + post_answer = ("class:answer", " %s" % self.status["result"]) + return super()._get_prompt_message(pre_answer, post_answer) + + def _run(self) -> Any: + """Run the application.""" + return self.application.run() + + async def _run_async(self) -> None: + """Run the application asynchronously.""" + return await self.application.run_async() + + @property + def application(self) -> Application: + """Get the application. + + :class:`.BaseComplexPrompt` requires :attr:`.BaseComplexPrompt._application` to be defined since this class + doesn't implement :class:`~prompt_toolkit.layout.Layout` and :class:`~prompt_toolkit.application.Application`. + + Raises: + NotImplementedError: When `self._application` is not defined. + """ + if not self._application: + raise NotImplementedError + return self._application + + @application.setter + def application(self, value: Application) -> None: + self._application = value + + @property + def height_offset(self) -> int: + """int: Height offset to apply.""" + if not self._wrap_lines: + return self._height_offset + return self.extra_line_count + self._height_offset + + @property + def total_message_length(self) -> int: + """int: Total length of the message.""" + total_message_length = 0 + if self._qmark: + total_message_length += len(self._qmark) + total_message_length += 1 # Extra space if qmark is present + total_message_length += len(str(self._message)) + total_message_length += 1 # Extra space between message and instruction + total_message_length += len(str(self._instruction)) + if self._instruction: + total_message_length += 1 # Extra space behind the instruction + return total_message_length + + @property + def extra_message_line_count(self) -> int: + """int: Get the extra lines created caused by line wrapping. + + Minus 1 on the totoal message length as we only want the extra line. + 24 // 24 will equal to 1 however we only want the value to be 1 when we have 25 char + which will create an extra line. + """ + term_width, _ = shutil.get_terminal_size() + return (self.total_message_length - 1) // term_width + + @property + def extra_long_instruction_line_count(self) -> int: + """int: Get the extra lines created caused by line wrapping. + + See Also: + :attr:`.BaseComplexPrompt.extra_message_line_count` + """ + if self._long_instruction: + term_width, _ = shutil.get_terminal_size() + return (len(self._long_instruction) - 1) // term_width + else: + return 0 + + @property + def extra_line_count(self) -> int: + """Get the extra lines created caused by line wrapping. + + Used mainly to calculate how much additional offset should be applied when getting + the height. + + Returns: + Total extra lines created due to line wrapping. + """ + result = 0 + + # message wrap + result += self.extra_message_line_count + # long instruction wrap + result += self.extra_long_instruction_line_count + + return result diff --git a/voxvera/vendor/InquirerPy/base/control.py b/voxvera/vendor/InquirerPy/base/control.py new file mode 100644 index 0000000..69cdc38 --- /dev/null +++ b/voxvera/vendor/InquirerPy/base/control.py @@ -0,0 +1,227 @@ +"""Contains the content control class :class:`.InquirerPyUIListControl`.""" +from abc import abstractmethod +from dataclasses import asdict, dataclass +from typing import Any, Callable, Dict, List, Optional, Tuple, cast + +from prompt_toolkit.layout.controls import FormattedTextControl + +from InquirerPy.exceptions import InvalidArgument, RequiredKeyNotFound +from InquirerPy.separator import Separator +from InquirerPy.utils import InquirerPyListChoices, InquirerPySessionResult + +__all__ = ["Choice", "InquirerPyUIListControl"] + + +@dataclass +class Choice: + """Class to create choices for list type prompts. + + A simple dataclass that can be used as an alternate to using :class:`dict` + when working with choices. + + Args: + value: The value of the choice when user selects this choice. + name: The value that should be presented to the user prior/after selection of the choice. + This value is optional, if not provided, it will fallback to the string representation of `value`. + enabled: Indicates if the choice should be pre-selected. + This only has effects when the prompt has `multiselect` enabled. + """ + + value: Any + name: Optional[str] = None + enabled: bool = False + + def __post_init__(self): + """Assign strinify value to name if not present.""" + if self.name is None: + self.name = str(self.value) + + +class InquirerPyUIListControl(FormattedTextControl): + """A base class to create :class:`~prompt_toolkit.layout.UIControl` to display list type contents. + + Args: + choices(InquirerPyListChoices): List of choices to display as the content. + Can also be a callable or async callable that returns a list of choices. + default: Default value, this will affect the cursor position. + multiselect: Indicate if the current prompt has `multiselect` enabled. + session_result: Current session result. + """ + + def __init__( + self, + choices: InquirerPyListChoices, + default: Any = None, + multiselect: bool = False, + session_result: Optional[InquirerPySessionResult] = None, + ) -> None: + self._session_result = session_result or {} + self._selected_choice_index: int = 0 + self._choice_func = None + self._multiselect = multiselect + self._default = ( + default + if not isinstance(default, Callable) + else cast(Callable, default)(self._session_result) + ) + self._raw_choices = ( + choices + if not isinstance(choices, Callable) + else cast(Callable, choices)(self._session_result) + ) + self._choices = self._get_choices(self._raw_choices, self._default) + self._safety_check() + self._format_choices() + super().__init__(self._get_formatted_choices) + + def _get_choices(self, choices: List[Any], default: Any) -> List[Dict[str, Any]]: + """Process the raw user input choices and format it into dictionary. + + Args: + choices: List of chices to display. + default: Default value, this will affect the :attr:`.InquirerPyUIListControl.selected_choice_index` + + Returns: + List of choices. + + Raises: + RequiredKeyNotFound: When the provided choice is missing the `name` or `value` key. + """ + processed_choices: List[Dict[str, Any]] = [] + try: + for index, choice in enumerate(choices, start=0): + if isinstance(choice, dict): + if choice["value"] == default: + self.selected_choice_index = index + processed_choices.append( + { + "name": str(choice["name"]), + "value": choice["value"], + "enabled": choice.get("enabled", False) + if self._multiselect + else False, + } + ) + elif isinstance(choice, Separator): + if self.selected_choice_index == index: + self.selected_choice_index = ( + self.selected_choice_index + 1 + ) % len(choices) + processed_choices.append( + {"name": str(choice), "value": choice, "enabled": False} + ) + elif isinstance(choice, Choice): + dict_choice = asdict(choice) + if dict_choice["value"] == default: + self.selected_choice_index = index + if not self._multiselect: + dict_choice["enabled"] = False + processed_choices.append(dict_choice) + else: + if choice == default: + self.selected_choice_index = index + processed_choices.append( + {"name": str(choice), "value": choice, "enabled": False} + ) + except KeyError: + raise RequiredKeyNotFound( + "dictionary type of choice require a 'name' key and a 'value' key" + ) + return processed_choices + + @property + def selected_choice_index(self) -> int: + """int: Current highlighted index.""" + return self._selected_choice_index + + @selected_choice_index.setter + def selected_choice_index(self, value: int) -> None: + self._selected_choice_index = value + + @property + def choices(self) -> List[Dict[str, Any]]: + """List[Dict[str, Any]]: Get all processed choices.""" + return self._choices + + @choices.setter + def choices(self, value: List[Dict[str, Any]]) -> None: + self._choices = value + + def _safety_check(self) -> None: + """Validate processed choices. + + Check if the choices are empty or if it only contains :class:`~InquirerPy.separator.Separator`. + """ + if not self.choices: + raise InvalidArgument("argument choices cannot be empty") + should_proceed: bool = False + for choice in self.choices: + if not isinstance(choice["value"], Separator): + should_proceed = True + break + if not should_proceed: + raise InvalidArgument( + "argument choices should contain choices other than separator" + ) + + def _get_formatted_choices(self) -> List[Tuple[str, str]]: + """Get all choices in formatted text format. + + Returns: + List of choices in formatted text form. + """ + display_choices = [] + + for index, choice in enumerate(self.choices): + if index == self.selected_choice_index: + display_choices += self._get_hover_text(choice) + else: + display_choices += self._get_normal_text(choice) + display_choices.append(("", "\n")) + if display_choices: + display_choices.pop() + return display_choices + + def _format_choices(self) -> None: + """Perform post processing on the choices. + + Additional customisation to the choices after :meth:`.InquirerPyUIListControl._get_choices` call. + """ + pass + + @abstractmethod + def _get_hover_text(self, choice) -> List[Tuple[str, str]]: + """Generate the formatted text for hovered choice. + + Returns: + Formatted text in list of tuple format. + """ + pass + + @abstractmethod + def _get_normal_text(self, choice) -> List[Tuple[str, str]]: + """Generate the formatted text for non-hovered choices. + + Returns: + Formatted text in list of tuple format. + """ + pass + + @property + def choice_count(self) -> int: + """int: Total count of choices.""" + return len(self.choices) + + @property + def selection(self) -> Dict[str, Any]: + """Dict[str, Any]: Current selected choice.""" + return self.choices[self.selected_choice_index] + + @property + def loading(self) -> bool: + """bool: Indicate if the content control is loading.""" + return self._loading + + @loading.setter + def loading(self, value: bool) -> None: + self._loading = value diff --git a/voxvera/vendor/InquirerPy/base/list.py b/voxvera/vendor/InquirerPy/base/list.py new file mode 100644 index 0000000..77f06aa --- /dev/null +++ b/voxvera/vendor/InquirerPy/base/list.py @@ -0,0 +1,238 @@ +"""Contains the base class :class:`.BaseListPrompt` which can be used to create a prompt involving choices.""" +from abc import abstractmethod +from typing import Any, Callable, List, Optional + +from prompt_toolkit.filters.base import Condition +from prompt_toolkit.keys import Keys + +from InquirerPy.base.complex import BaseComplexPrompt +from InquirerPy.base.control import InquirerPyUIListControl +from InquirerPy.separator import Separator +from InquirerPy.utils import ( + InquirerPyKeybindings, + InquirerPyMessage, + InquirerPySessionResult, + InquirerPyStyle, + InquirerPyValidate, +) + + +class BaseListPrompt(BaseComplexPrompt): + """A base class to create a complex prompt involving choice selections (i.e. list) using `prompt_toolkit` Application. + + Note: + This class does not create :class:`~prompt_toolkit.layout.Layout` nor :class:`~prompt_toolkit.application.Application`, + it only contains the necessary attributes and helper functions to be consumed. + + See Also: + :class:`~InquirerPy.prompts.list.ListPrompt` + :class:`~InquirerPy.prompts.fuzzy.FuzzyPrompt` + """ + + def __init__( + self, + message: InquirerPyMessage, + style: Optional[InquirerPyStyle] = None, + vi_mode: bool = False, + qmark: str = "?", + amark: str = "?", + instruction: str = "", + long_instruction: str = "", + border: bool = False, + transformer: Optional[Callable[[Any], Any]] = None, + filter: Optional[Callable[[Any], Any]] = None, + validate: Optional[InquirerPyValidate] = None, + invalid_message: str = "Invalid input", + multiselect: bool = False, + keybindings: Optional[InquirerPyKeybindings] = None, + cycle: bool = True, + wrap_lines: bool = True, + raise_keyboard_interrupt: bool = True, + mandatory: bool = True, + mandatory_message: str = "Mandatory prompt", + session_result: Optional[InquirerPySessionResult] = None, + ) -> None: + super().__init__( + message=message, + style=style, + border=border, + vi_mode=vi_mode, + qmark=qmark, + amark=amark, + transformer=transformer, + filter=filter, + invalid_message=invalid_message, + validate=validate, + instruction=instruction, + long_instruction=long_instruction, + wrap_lines=wrap_lines, + raise_keyboard_interrupt=raise_keyboard_interrupt, + mandatory=mandatory, + mandatory_message=mandatory_message, + session_result=session_result, + ) + + self._content_control: InquirerPyUIListControl + self._multiselect = multiselect + self._is_multiselect = Condition(lambda: self._multiselect) + self._cycle = cycle + + if not keybindings: + keybindings = {} + + self.kb_maps = { + "down": [ + {"key": "down"}, + {"key": "c-n", "filter": ~self._is_vim_edit}, + {"key": "j", "filter": self._is_vim_edit}, + ], + "up": [ + {"key": "up"}, + {"key": "c-p", "filter": ~self._is_vim_edit}, + {"key": "k", "filter": self._is_vim_edit}, + ], + "toggle": [ + {"key": "space"}, + ], + "toggle-down": [ + {"key": Keys.Tab}, + ], + "toggle-up": [ + {"key": Keys.BackTab}, + ], + "toggle-all": [ + {"key": "alt-r"}, + {"key": "c-r"}, + ], + "toggle-all-true": [ + {"key": "alt-a"}, + {"key": "c-a"}, + ], + "toggle-all-false": [], + **keybindings, + } + + self.kb_func_lookup = { + "down": [{"func": self._handle_down}], + "up": [{"func": self._handle_up}], + "toggle": [{"func": self._handle_toggle_choice}], + "toggle-down": [ + {"func": self._handle_toggle_choice}, + {"func": self._handle_down}, + ], + "toggle-up": [ + {"func": self._handle_toggle_choice}, + {"func": self._handle_up}, + ], + "toggle-all": [{"func": self._handle_toggle_all}], + "toggle-all-true": [{"func": self._handle_toggle_all, "args": [True]}], + "toggle-all-false": [{"func": self._handle_toggle_all, "args": [False]}], + } + + @property + def content_control(self) -> InquirerPyUIListControl: + """Get the content controller object. + + Needs to be an instance of :class:`~InquirerPy.base.control.InquirerPyUIListControl`. + + Each :class:`.BaseComplexPrompt` requires a `content_control` to display custom + contents for the prompt. + + Raises: + NotImplementedError: When `self._content_control` is not found. + """ + if not self._content_control: + raise NotImplementedError + return self._content_control + + @content_control.setter + def content_control(self, value: InquirerPyUIListControl) -> None: + self._content_control = value + + @property + def result_name(self) -> Any: + """Get the result value that should be printed to the terminal. + + In multiselect scenario, return result as a list. + """ + if self._multiselect: + return [choice["name"] for choice in self.selected_choices] + else: + try: + return self.content_control.selection["name"] + except IndexError: + return "" + + @property + def result_value(self) -> Any: + """Get the result value that should return to the user. + + In multiselect scenario, return result as a list. + """ + if self._multiselect: + return [choice["value"] for choice in self.selected_choices] + else: + try: + return self.content_control.selection["value"] + except IndexError: + return "" + + @property + def selected_choices(self) -> List[Any]: + """List[Any]: Get all user selected choices.""" + + def filter_choice(choice): + return not isinstance(choice, Separator) and choice["enabled"] + + return list(filter(filter_choice, self.content_control.choices)) + + def _handle_down(self, _) -> bool: + """Handle event when user attempts to move down. + + Returns: + Boolean indicating if the action hits the cap. + """ + if self._cycle: + self.content_control.selected_choice_index = ( + self.content_control.selected_choice_index + 1 + ) % self.content_control.choice_count + return False + else: + self.content_control.selected_choice_index += 1 + if ( + self.content_control.selected_choice_index + >= self.content_control.choice_count + ): + self.content_control.selected_choice_index = ( + self.content_control.choice_count - 1 + ) + return True + return False + + def _handle_up(self, _) -> bool: + """Handle event when user attempts to move up. + + Returns: + Boolean indicating if the action hits the cap. + """ + if self._cycle: + self.content_control.selected_choice_index = ( + self.content_control.selected_choice_index - 1 + ) % self.content_control.choice_count + return False + else: + self.content_control.selected_choice_index -= 1 + if self.content_control.selected_choice_index < 0: + self.content_control.selected_choice_index = 0 + return True + return False + + @abstractmethod + def _handle_toggle_choice(self, event) -> None: + """Handle event when user attempting to toggle the state of the chocie.""" + pass + + @abstractmethod + def _handle_toggle_all(self, event, value: bool) -> None: + """Handle event when user attempting to alter the state of all choices.""" + pass diff --git a/voxvera/vendor/InquirerPy/base/simple.py b/voxvera/vendor/InquirerPy/base/simple.py new file mode 100644 index 0000000..de695bf --- /dev/null +++ b/voxvera/vendor/InquirerPy/base/simple.py @@ -0,0 +1,378 @@ +"""Contains the base class :class:`.BaseSimplePrompt`.""" +import os +import re +from abc import ABC, abstractmethod +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Dict, + List, + Optional, + Tuple, + Union, + cast, +) + +from prompt_toolkit.enums import EditingMode +from prompt_toolkit.filters.base import Condition, FilterOrBool +from prompt_toolkit.key_binding.key_bindings import KeyBindings, KeyHandlerCallable +from prompt_toolkit.keys import Keys +from prompt_toolkit.styles.style import Style +from prompt_toolkit.validation import Validator + +from InquirerPy.enum import INQUIRERPY_KEYBOARD_INTERRUPT +from InquirerPy.exceptions import RequiredKeyNotFound +from InquirerPy.utils import ( + InquirerPyMessage, + InquirerPySessionResult, + InquirerPyStyle, + InquirerPyValidate, + get_style, +) + +if TYPE_CHECKING: + from prompt_toolkit.key_binding.key_processor import KeyPressEvent + + +class BaseSimplePrompt(ABC): + """The base class to create a simple terminal input prompt. + + Note: + No actual :class:`~prompt_toolkit.application.Application` is created by this class. + This class only creates some common interface and attributes that can be easily used + by `prompt_toolkit`. + + To have a functional prompt, you'll at least have to implement the :meth:`.BaseSimplePrompt._run` + and :meth:`.BaseSimplePrompt._get_prompt_message`. + + See Also: + :class:`~InquirerPy.prompts.input.InputPrompt` + """ + + def __init__( + self, + message: InquirerPyMessage, + style: Optional[InquirerPyStyle] = None, + vi_mode: bool = False, + qmark: str = "?", + amark: str = "?", + instruction: str = "", + validate: Optional[InquirerPyValidate] = None, + invalid_message: str = "Invalid input", + transformer: Optional[Callable[[Any], Any]] = None, + filter: Optional[Callable[[Any], Any]] = None, + default: Any = "", + wrap_lines: bool = True, + raise_keyboard_interrupt: bool = True, + mandatory: bool = True, + mandatory_message: str = "Mandatory prompt", + session_result: Optional[InquirerPySessionResult] = None, + ) -> None: + self._mandatory = mandatory + self._mandatory_message = mandatory_message + self._result = session_result or {} + self._message = ( + message + if not isinstance(message, Callable) + else cast(Callable, message)(self._result) + ) + self._instruction = instruction + self._default = ( + default if not isinstance(default, Callable) else default(self._result) + ) + self._style = Style.from_dict(style.dict if style else get_style().dict) + self._qmark = qmark + self._amark = amark + self._status = {"answered": False, "result": None, "skipped": False} + self._kb = KeyBindings() + self._lexer = "class:input" + self._transformer = transformer + self._filter = filter + self._wrap_lines = wrap_lines + self._editing_mode = ( + EditingMode.VI + if vi_mode or bool(os.getenv("INQUIRERPY_VI_MODE", False)) + else EditingMode.EMACS + ) + if isinstance(validate, Validator): + self._validator = validate + else: + self._validator = Validator.from_callable( + validate if validate else lambda _: True, + invalid_message, + move_cursor_to_end=True, + ) + self._raise_kbi = not os.getenv( + "INQUIRERPY_NO_RAISE_KBI", not raise_keyboard_interrupt + ) + self._is_rasing_kbi = Condition(lambda: self._raise_kbi) + + self._kb_maps = { + "answer": [{"key": Keys.Enter}], + "interrupt": [ + {"key": "c-c", "filter": self._is_rasing_kbi}, + {"key": "c-d", "filter": ~self._is_rasing_kbi}, + ], + "skip": [{"key": "c-z"}, {"key": "c-c", "filter": ~self._is_rasing_kbi}], + } + self._kb_func_lookup = { + "answer": [{"func": self._handle_enter}], + "interrupt": [{"func": self._handle_interrupt}], + "skip": [{"func": self._handle_skip}], + } + + def _keybinding_factory(self): + """Register all keybindings in `self._kb_maps`. + + It's required to call this function at the end of prompt constructor if + it inherits from :class:`~InquirerPy.base.simple.BaseSimplePrompt` or + :class:`~InquirerPy.base.complex.BaseComplexPrompt`. + """ + + def _factory(keys, filter, action): + if action not in self.kb_func_lookup: + raise RequiredKeyNotFound(f"keybinding action {action} not found") + if not isinstance(keys, list): + keys = [keys] + + @self.register_kb(*keys, filter=filter) + def _(event): + for method in self.kb_func_lookup[action]: + method["func"](event, *method.get("args", [])) + + for key, item in self.kb_maps.items(): + if not isinstance(item, list): + item = [item] + for kb in item: + _factory(kb["key"], kb.get("filter", Condition(lambda: True)), key) + + @abstractmethod + def _set_error(self, message: str) -> None: + """Set the error message for the prompt. + + Args: + message: Error message to set. + """ + pass + + def _handle_skip(self, event: Optional["KeyPressEvent"]) -> None: + """Handle the event when attempting to skip a prompt. + + Skip the prompt if the `_mandatory` field is False, otherwise + show an error message that the prompt cannot be skipped. + """ + if not self._mandatory: + self.status["answered"] = True + self.status["skipped"] = True + self.status["result"] = None + if event: + event.app.exit(result=None) + else: + self._set_error(message=self._mandatory_message) + + def _handle_interrupt(self, event: Optional["KeyPressEvent"]) -> None: + """Handle the event when a KeyboardInterrupt signal is sent.""" + self.status["answered"] = True + self.status["result"] = INQUIRERPY_KEYBOARD_INTERRUPT + self.status["skipped"] = True + if event: + event.app.exit(result=INQUIRERPY_KEYBOARD_INTERRUPT) + + @abstractmethod + def _handle_enter(self, event: Optional["KeyPressEvent"]) -> None: + """Handle the event when user attempt to answer the question.""" + pass + + @property + def status(self) -> Dict[str, Any]: + """Dict[str, Any]: Get current prompt status. + + The status contains 3 keys: "answered" and "result". + answered: If the current prompt is answered. + result: The result of the user answer. + skipped: If the prompt is skipped. + """ + return self._status + + @status.setter + def status(self, value) -> None: + self._status = value + + def register_kb( + self, *keys: Union[Keys, str], filter: FilterOrBool = True, **kwargs + ) -> Callable[[KeyHandlerCallable], KeyHandlerCallable]: + """Keybinding registration decorator. + + This decorator wraps around the :meth:`prompt_toolkit.key_binding.KeyBindings.add` with + added feature to process `alt` realted keybindings. + + By default, `prompt_toolkit` doesn't process `alt` related keybindings, + it requires `alt-ANY` to `escape` + `ANY`. + + Args: + keys: The keys to bind that can trigger the function. + filter: :class:`~prompt_toolkit.filter.Condition` to indicate if this keybinding should be active. + + Returns: + A decorator that should be applied to the function thats intended to be active when the keys + are pressed. + + Examples: + >>> @self.register_kb("alt-j") + ... def test(event): + ... pass + """ + alt_pattern = re.compile(r"^alt-(.*)") + + def decorator(func: KeyHandlerCallable) -> KeyHandlerCallable: + formatted_keys = [] + for key in keys: + match = alt_pattern.match(key) + if match: + formatted_keys.append("escape") + formatted_keys.append(match.group(1)) + else: + formatted_keys.append(key) + + @self._kb.add(*formatted_keys, filter=filter, **kwargs) + def executable(event) -> None: + func(event) + + return executable + + return decorator + + @abstractmethod + def _get_prompt_message( + self, pre_answer: Tuple[str, str], post_answer: Tuple[str, str] + ) -> List[Tuple[str, str]]: + """Get the question message in formatted text form to display in the prompt. + + This function is mainly used to render the question message dynamically based + on the current status (answered or not answered) of the prompt. + + Note: + The function requires implementation when inheriting :class:`.BaseSimplePrompt`. + You should call `super()._get_prompt_message(pre_answer, post_answer)` in + the implemented `_get_prompt_message`. + + Args: + pre_answer: The message to display before the question is answered. + post_answer: The information to display after the question is answered. + + Returns: + Formatted text in list of tuple format. + """ + display_message = [] + if self.status["skipped"]: + display_message.append(("class:skipped", self._qmark)) + display_message.append( + ("class:skipped", "%s%s " % (" " if self._qmark else "", self._message)) + ) + elif self.status["answered"]: + display_message.append(("class:answermark", self._amark)) + display_message.append( + ( + "class:answered_question", + "%s%s" % (" " if self._amark else "", self._message), + ) + ) + display_message.append( + post_answer + if not self._transformer + else ( + "class:answer", + " %s" % self._transformer(self.status["result"]), + ) + ) + else: + display_message.append(("class:questionmark", self._qmark)) + display_message.append( + ( + "class:question", + "%s%s" % (" " if self._qmark else "", self._message), + ) + ) + display_message.append(pre_answer) + return display_message + + @abstractmethod + def _run(self) -> Any: + """Abstractmethod to enforce a run function is implemented. + + All prompt instance requires a `_run` call to initialise and run an instance of + `PromptSession` or `Application`. + """ + pass + + @abstractmethod + async def _run_async(self) -> Any: + """Abstractmethod to enforce a run function is implemented. + + All prompt instance requires a `_run_async` call to initialise and run an instance of + `PromptSession` or `Application`. + """ + pass + + def execute(self, raise_keyboard_interrupt: Optional[bool] = None) -> Any: + """Run the prompt and get the result. + + Args: + raise_keyboard_interrupt: **Deprecated**. Set this parameter on the prompt initialisation instead. + + Returns: + Value of the user answer. Types varies depending on the prompt. + + Raises: + KeyboardInterrupt: When `ctrl-c` is pressed and `raise_keyboard_interrupt` is True. + """ + result = self._run() + if raise_keyboard_interrupt is not None: + self._raise_kbi = not os.getenv( + "INQUIRERPY_NO_RAISE_KBI", not raise_keyboard_interrupt + ) + if result == INQUIRERPY_KEYBOARD_INTERRUPT: + raise KeyboardInterrupt + if not self._filter: + return result + return self._filter(result) + + async def execute_async(self) -> None: + """Run the prompt asynchronously and get the result. + + Returns: + Value of the user answer. Types varies depending on the prompt. + + Raises: + KeyboardInterrupt: When `ctrl-c` is pressed and `raise_keyboard_interrupt` is True. + """ + result = await self._run_async() + if result == INQUIRERPY_KEYBOARD_INTERRUPT: + raise KeyboardInterrupt + if not self._filter: + return result + return self._filter(result) + + @property + def instruction(self) -> str: + """str: Instruction to display next to question.""" + return self._instruction + + @property + def kb_maps(self) -> Dict[str, Any]: + """Dict[str, Any]: Keybinding mappings.""" + return self._kb_maps + + @kb_maps.setter + def kb_maps(self, value: Dict[str, Any]) -> None: + self._kb_maps = {**self._kb_maps, **value} + + @property + def kb_func_lookup(self) -> Dict[str, Any]: + """Dict[str, Any]: Keybinding function lookup mappings..""" + return self._kb_func_lookup + + @kb_func_lookup.setter + def kb_func_lookup(self, value: Dict[str, Any]) -> None: + self._kb_func_lookup = {**self._kb_func_lookup, **value} diff --git a/voxvera/vendor/InquirerPy/containers/__init__.py b/voxvera/vendor/InquirerPy/containers/__init__.py new file mode 100644 index 0000000..4877158 --- /dev/null +++ b/voxvera/vendor/InquirerPy/containers/__init__.py @@ -0,0 +1 @@ +from .spinner import SpinnerWindow diff --git a/voxvera/vendor/InquirerPy/containers/instruction.py b/voxvera/vendor/InquirerPy/containers/instruction.py new file mode 100644 index 0000000..9ce0c5f --- /dev/null +++ b/voxvera/vendor/InquirerPy/containers/instruction.py @@ -0,0 +1,38 @@ +"""Module contains :class:`.InstructionWindow` which can be used to display long instructions.""" + +from typing import TYPE_CHECKING + +from prompt_toolkit.layout.containers import ConditionalContainer, Window +from prompt_toolkit.layout.controls import FormattedTextControl + +if TYPE_CHECKING: + from prompt_toolkit.filters.base import FilterOrBool + from prompt_toolkit.formatted_text.base import AnyFormattedText + + +class InstructionWindow(ConditionalContainer): + """Conditional `prompt_toolkit` :class:`~prompt_toolkit.layout.Window` that displays long instructions. + + Args: + message: Long instructions to display. + filter: Condition to display the instruction window. + """ + + def __init__(self, message: str, filter: "FilterOrBool", **kwargs) -> None: + self._message = message + super().__init__( + Window( + FormattedTextControl(text=self._get_message), + dont_extend_height=True, + **kwargs + ), + filter=filter, + ) + + def _get_message(self) -> "AnyFormattedText": + """Get long instruction to display. + + Returns: + FormattedText in list of tuple format. + """ + return [("class:long_instruction", self._message)] diff --git a/voxvera/vendor/InquirerPy/containers/message.py b/voxvera/vendor/InquirerPy/containers/message.py new file mode 100644 index 0000000..04b50c9 --- /dev/null +++ b/voxvera/vendor/InquirerPy/containers/message.py @@ -0,0 +1,42 @@ +"""Module contains the main message window :class:`~prompt_toolkit.container.Container`.""" + +from typing import TYPE_CHECKING + +from prompt_toolkit.layout.containers import ConditionalContainer, Window +from prompt_toolkit.layout.controls import FormattedTextControl +from prompt_toolkit.layout.dimension import LayoutDimension + +if TYPE_CHECKING: + from prompt_toolkit.filters.base import FilterOrBool + from prompt_toolkit.formatted_text.base import AnyFormattedText + + +class MessageWindow(ConditionalContainer): + """Main window to display question to the user. + + Args: + message: The message to display in the terminal. + filter: Condition that this message window should be displayed. + Use a loading condition to only display this window while its not loading. + wrap_lines: Enable line wrapping if the message is too long. + show_cursor: Display cursor. + """ + + def __init__( + self, + message: "AnyFormattedText", + filter: "FilterOrBool", + wrap_lines: bool = True, + show_cursor: bool = True, + **kwargs + ) -> None: + super().__init__( + content=Window( + height=LayoutDimension.exact(1) if not wrap_lines else None, + content=FormattedTextControl(message, show_cursor=show_cursor), + wrap_lines=wrap_lines, + dont_extend_height=True, + **kwargs + ), + filter=filter, + ) diff --git a/voxvera/vendor/InquirerPy/containers/spinner.py b/voxvera/vendor/InquirerPy/containers/spinner.py new file mode 100644 index 0000000..47be0a0 --- /dev/null +++ b/voxvera/vendor/InquirerPy/containers/spinner.py @@ -0,0 +1,108 @@ +"""Module contains spinner related resources. + +Note: + The spinner is not a standalone spinner to run in the terminal + but rather a `prompt_toolkit` :class:`~prompt_toolkit.layout.Window` that displays a spinner. + + Use library such as `yaspin `_ if you need a plain spinner. +""" +import asyncio +from typing import TYPE_CHECKING, Callable, List, NamedTuple, Optional, Tuple, Union + +from prompt_toolkit.filters.utils import to_filter +from prompt_toolkit.layout.containers import ConditionalContainer, Window +from prompt_toolkit.layout.controls import FormattedTextControl + +if TYPE_CHECKING: + from prompt_toolkit.filters.base import Filter + +__all__ = ["SPINNERS", "SpinnerWindow"] + + +class SPINNERS(NamedTuple): + """Presets of spinner patterns. + + See Also: + https://github.com/pavdmyt/yaspin/blob/master/yaspin/data/spinners.json + + This only contains some basic ones thats ready to use. For more patterns, checkout the + URL above. + + Examples: + >>> from InquirerPy import inquirer + >>> from InquirerPy.spinner import SPINNERS + >>> inquirer.select(message="", choices=lambda _: [1, 2, 3], spinner_pattern=SPINNERS.dots) + """ + + dots = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"] + dots2 = ["⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"] + line = ["-", "\\", "|", "/"] + line2 = ["⠂", "-", "–", "—", "–", "-"] + pipe = ["┤", "┘", "┴", "└", "├", "┌", "┬", "┐"] + star = ["✶", "✸", "✹", "✺", "✹", "✷"] + star2 = ["+", "x", "*"] + flip = ["_", "_", "_", "-", "`", "`", "'", "´", "-", "_", "_", "_"] + hamburger = ["☱", "☲", "☴"] + grow_vertical = ["▁", "▃", "▄", "▅", "▆", "▇", "▆", "▅", "▄", "▃"] + grow_horizontal = ["▏", "▎", "▍", "▌", "▋", "▊", "▉", "▊", "▋", "▌", "▍", "▎"] + box_bounce = ["▖", "▘", "▝", "▗"] + triangle = ["◢", "◣", "◤", "◥"] + arc = ["◜", "◠", "◝", "◞", "◡", "◟"] + circle = ["◡", "⊙", "◠"] + + +class SpinnerWindow(ConditionalContainer): + """Conditional `prompt_toolkit` :class:`~prompt_toolkit.layout.Window` that displays a spinner. + + Args: + loading: A :class:`~prompt_toolkit.filters.Condition` to indicate if the spinner should be visible. + redraw: A redraw function (i.e. :meth:`~prompt_toolkit.application.Application.invalidate`) to refresh the UI. + pattern: List of pattern to display as the spinner. + delay: Spinner refresh frequency. + text: Loading text to display. + """ + + def __init__( + self, + loading: "Filter", + redraw: Callable[[], None], + pattern: Optional[Union[List[str], SPINNERS]] = None, + delay: float = 0.1, + text: str = "", + ) -> None: + self._loading = to_filter(loading) + self._spinning = False + self._redraw = redraw + self._pattern = pattern or SPINNERS.line + self._char = self._pattern[0] + self._delay = delay + self._text = text or "Loading ..." + + super().__init__( + content=Window(content=FormattedTextControl(text=self._get_text)), + filter=self._loading, + ) + + def _get_text(self) -> List[Tuple[str, str]]: + """Dynamically get the text for the :class:`~prompt_toolkit.layout.Window`. + + Returns: + Formatted text. + """ + return [ + ("class:spinner_pattern", self._char), + ("", " "), + ("class:spinner_text", self._text), + ] + + async def start(self) -> None: + """Start the spinner.""" + if self._spinning: + return + self._spinning = True + while self._loading(): + for char in self._pattern: + await asyncio.sleep(self._delay) + self._char = char + self._redraw() + self._spinning = False diff --git a/voxvera/vendor/InquirerPy/containers/validation.py b/voxvera/vendor/InquirerPy/containers/validation.py new file mode 100644 index 0000000..32dca31 --- /dev/null +++ b/voxvera/vendor/InquirerPy/containers/validation.py @@ -0,0 +1,60 @@ +"""Module contains :class:`.ValidationWindow` which can be used to display error.""" + +from typing import Optional + +from prompt_toolkit.filters.base import FilterOrBool +from prompt_toolkit.formatted_text.base import AnyFormattedText +from prompt_toolkit.layout.containers import ConditionalContainer, Float, Window +from prompt_toolkit.layout.controls import FormattedTextControl + + +class ValidationWindow(ConditionalContainer): + """Conditional `prompt_toolkit` :class:`~prompt_toolkit.layout.Window` that displays error. + + Args: + invalid_message: Error message to display when error occured. + filter: Condition to display the error window. + """ + + def __init__( + self, invalid_message: AnyFormattedText, filter: FilterOrBool, **kwargs + ) -> None: + super().__init__( + Window( + FormattedTextControl(invalid_message), dont_extend_height=True, **kwargs + ), + filter=filter, + ) + + +class ValidationFloat(Float): + """:class:`~prompt_toolkit.layout.Float` wrapper around :class:`.ValidationWindow`. + + Args: + invalid_message: Error message to display when error occured. + filter: Condition to display the error window. + left: Distance to left. + right: Distance to right. + bottom: Distance to bottom. + top: Distance to top. + """ + + def __init__( + self, + invalid_message: AnyFormattedText, + filter: FilterOrBool, + left: Optional[int] = None, + right: Optional[int] = None, + bottom: Optional[int] = None, + top: Optional[int] = None, + **kwargs + ) -> None: + super().__init__( + content=ValidationWindow( + invalid_message=invalid_message, filter=filter, **kwargs + ), + left=left, + right=right, + bottom=bottom, + top=top, + ) diff --git a/voxvera/vendor/InquirerPy/enum.py b/voxvera/vendor/InquirerPy/enum.py new file mode 100644 index 0000000..71d46bf --- /dev/null +++ b/voxvera/vendor/InquirerPy/enum.py @@ -0,0 +1,7 @@ +"""Module contains common constants.""" +INQUIRERPY_KEYBOARD_INTERRUPT: str = "INQUIRERPY_KEYBOARD_INTERRUPT" + +INQUIRERPY_POINTER_SEQUENCE: str = "\u276f" +INQUIRERPY_FILL_CIRCLE_SEQUENCE: str = "\u25c9" +INQUIRERPY_EMPTY_CIRCLE_SEQUENCE: str = "\u25cb" +INQUIRERPY_QMARK_SEQUENCE: str = "\u003f" diff --git a/voxvera/vendor/InquirerPy/exceptions.py b/voxvera/vendor/InquirerPy/exceptions.py new file mode 100644 index 0000000..6048192 --- /dev/null +++ b/voxvera/vendor/InquirerPy/exceptions.py @@ -0,0 +1,25 @@ +"""Module contains exceptions that will be raised by `InquirerPy`.""" + + +class InvalidArgument(Exception): + """Provided argument is invalid. + + Args: + message: Exception message. + """ + + def __init__(self, message: str = "invalid argument"): + self._message = message + super().__init__(self._message) + + +class RequiredKeyNotFound(Exception): + """Missing required keys in dictionary. + + Args: + message: Exception message. + """ + + def __init__(self, message="required key not found"): + self.message = message + super().__init__(self.message) diff --git a/voxvera/vendor/InquirerPy/inquirer.py b/voxvera/vendor/InquirerPy/inquirer.py new file mode 100644 index 0000000..3553a3f --- /dev/null +++ b/voxvera/vendor/InquirerPy/inquirer.py @@ -0,0 +1,17 @@ +"""Servers as another entry point for `InquirerPy`. + +See Also: + :ref:`index:Alternate Syntax`. + +`inquirer` directly interact with individual prompt classes. It’s more flexible, easier to customise and also provides IDE type hintings/completions. +""" +from InquirerPy.prompts import CheckboxPrompt as checkbox +from InquirerPy.prompts import ConfirmPrompt as confirm +from InquirerPy.prompts import ExpandPrompt as expand +from InquirerPy.prompts import FilePathPrompt as filepath +from InquirerPy.prompts import FuzzyPrompt as fuzzy +from InquirerPy.prompts import InputPrompt as text +from InquirerPy.prompts import ListPrompt as select +from InquirerPy.prompts import NumberPrompt as number +from InquirerPy.prompts import RawlistPrompt as rawlist +from InquirerPy.prompts import SecretPrompt as secret diff --git a/voxvera/vendor/InquirerPy/prompts/__init__.py b/voxvera/vendor/InquirerPy/prompts/__init__.py new file mode 100644 index 0000000..a04cf64 --- /dev/null +++ b/voxvera/vendor/InquirerPy/prompts/__init__.py @@ -0,0 +1,11 @@ +"""Module contains import of all prompts classes.""" +from InquirerPy.prompts.checkbox import CheckboxPrompt +from InquirerPy.prompts.confirm import ConfirmPrompt +from InquirerPy.prompts.expand import ExpandPrompt +from InquirerPy.prompts.filepath import FilePathPrompt +from InquirerPy.prompts.fuzzy import FuzzyPrompt +from InquirerPy.prompts.input import InputPrompt +from InquirerPy.prompts.list import ListPrompt +from InquirerPy.prompts.number import NumberPrompt +from InquirerPy.prompts.rawlist import RawlistPrompt +from InquirerPy.prompts.secret import SecretPrompt diff --git a/voxvera/vendor/InquirerPy/prompts/checkbox.py b/voxvera/vendor/InquirerPy/prompts/checkbox.py new file mode 100644 index 0000000..60f3bde --- /dev/null +++ b/voxvera/vendor/InquirerPy/prompts/checkbox.py @@ -0,0 +1,244 @@ +"""Module contains the class to create a checkbox prompt.""" +from typing import Any, Callable, List, Optional, Tuple, Union + +from prompt_toolkit.validation import ValidationError + +from InquirerPy.base import FakeDocument, InquirerPyUIListControl +from InquirerPy.enum import ( + INQUIRERPY_EMPTY_CIRCLE_SEQUENCE, + INQUIRERPY_FILL_CIRCLE_SEQUENCE, + INQUIRERPY_POINTER_SEQUENCE, +) +from InquirerPy.prompts.list import ListPrompt +from InquirerPy.separator import Separator +from InquirerPy.utils import ( + InquirerPyKeybindings, + InquirerPyListChoices, + InquirerPyMessage, + InquirerPySessionResult, + InquirerPyStyle, + InquirerPyValidate, +) + +__all__ = ["CheckboxPrompt"] + + +class InquirerPyCheckboxControl(InquirerPyUIListControl): + """An :class:`~prompt_toolkit.layout.UIControl` class that displays a list of choices. + + Reference the parameter definition in :class:`.CheckboxPrompt`. + """ + + def __init__( + self, + choices: InquirerPyListChoices, + default: Any, + pointer: str, + enabled_symbol: str, + disabled_symbol: str, + session_result: Optional[InquirerPySessionResult], + ) -> None: + """Initialise required attributes and call base class.""" + self._pointer = pointer + self._enabled_symbol = enabled_symbol + self._disabled_symbol = disabled_symbol + super().__init__( + choices=choices, + default=default, + session_result=session_result, + multiselect=True, + ) + + def _format_choices(self) -> None: + pass + + def _get_hover_text(self, choice) -> List[Tuple[str, str]]: + display_choices = [] + display_choices.append(("class:pointer", self._pointer)) + if self._pointer: + display_choices.append(("", " ")) + if not isinstance(choice["value"], Separator): + display_choices.append( + ( + "class:checkbox", + self._enabled_symbol + if choice["enabled"] + else self._disabled_symbol, + ) + ) + if self._enabled_symbol and self._disabled_symbol: + display_choices.append(("", " ")) + display_choices.append(("[SetCursorPosition]", "")) + display_choices.append(("class:pointer", choice["name"])) + return display_choices + + def _get_normal_text(self, choice) -> List[Tuple[str, str]]: + display_choices = [] + display_choices.append(("", len(self._pointer) * " ")) + if self._pointer: + display_choices.append(("", " ")) + if not isinstance(choice["value"], Separator): + display_choices.append( + ( + "class:checkbox", + self._enabled_symbol + if choice["enabled"] + else self._disabled_symbol, + ) + ) + if self._enabled_symbol and self._disabled_symbol: + display_choices.append(("", " ")) + display_choices.append(("", choice["name"])) + else: + display_choices.append(("class:separator", choice["name"])) + return display_choices + + +class CheckboxPrompt(ListPrompt): + """Create a prompt which displays a list of checkboxes to toggle. + + A wrapper class around :class:`~prompt_toolkit.application.Application`. + + User can toggle on/off on each checkbox. + + Works very similar to :class:`~InquirerPy.prompts.list.ListPrompt` with `multiselect` enabled, + the main difference is visual/UI and also when not toggling anything, the result will be empty. + + Args: + message: The question to ask the user. + Refer to :ref:`pages/dynamic:message` documentation for more details. + choices: List of choices to display and select. + Refer to :ref:`pages/dynamic:choices` documentation for more details. + style: An :class:`InquirerPyStyle` instance. + Refer to :ref:`Style ` documentation for more details. + vi_mode: Use vim keybinding for the prompt. + Refer to :ref:`pages/kb:Keybindings` documentation for more details. + default: Set the default value of the prompt. + This will be used to determine which choice is highlighted (current selection), + The default value should be the value of one of the choices. + Refer to :ref:`pages/dynamic:default` documentation for more details. + separator: Separator symbol. Custom symbol that will be used as a separator between the choice index number and the choices. + qmark: Question mark symbol. Custom symbol that will be displayed infront of the question before its answered. + amark: Answer mark symbol. Custom symbol that will be displayed infront of the question after its answered. + pointer: Pointer symbol. Customer symbol that will be used to indicate the current choice selection. + enabled_symbol: Checkbox ticked symbol. Custom symbol which indicate the checkbox is ticked. + disabled_symbol: Checkbox not ticked symbol. Custom symbol which indicate the checkbox is not ticked. + instruction: Short instruction to display next to the question. + long_instruction: Long instructions to display at the bottom of the prompt. + validate: Add validation to user input. + The main use case for this prompt would be when `multiselect` is True, you can enforce a min/max selection. + Refer to :ref:`pages/validator:Validator` documentation for more details. + invalid_message: Error message to display when user input is invalid. + Refer to :ref:`pages/validator:Validator` documentation for more details. + transformer: A function which performs additional transformation on the value that gets printed to the terminal. + Different than `filter` parameter, this is only visual effect and won’t affect the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`. + Refer to :ref:`pages/dynamic:transformer` documentation for more details. + filter: A function which performs additional transformation on the result. + This affects the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`. + Refer to :ref:`pages/dynamic:filter` documentation for more details. + height: Preferred height of the prompt. + Refer to :ref:`pages/height:Height` documentation for more details. + max_height: Max height of the prompt. + Refer to :ref:`pages/height:Height` documentation for more details. + border: Create border around the choice window. + keybindings: Customise the builtin keybindings. + Refer to :ref:`pages/kb:Keybindings` for more details. + show_cursor: Display cursor at the end of the prompt. + Set to False to hide the cursor. + cycle: Return to top item if hit bottom during navigation or vice versa. + wrap_lines: Soft wrap question lines when question exceeds the terminal width. + raise_keyboard_interrupt: Raise the :class:`KeyboardInterrupt` exception when `ctrl-c` is pressed. If false, the result + will be `None` and the question is skiped. + mandatory: Indicate if the prompt is mandatory. If True, then the question cannot be skipped. + mandatory_message: Error message to show when user attempts to skip mandatory prompt. + session_result: Used internally for :ref:`index:Classic Syntax (PyInquirer)`. + + Examples: + >>> from InquirerPy import inquirer + >>> result = inquirer.checkbox(message="Select:", choices=[1, 2, 3]).execute() + >>> print(result) + [1] + """ + + def __init__( + self, + message: InquirerPyMessage, + choices: InquirerPyListChoices, + default: Any = None, + style: Optional[InquirerPyStyle] = None, + vi_mode: bool = False, + qmark: str = "?", + amark: str = "?", + pointer: str = INQUIRERPY_POINTER_SEQUENCE, + enabled_symbol: str = INQUIRERPY_FILL_CIRCLE_SEQUENCE, + disabled_symbol: str = INQUIRERPY_EMPTY_CIRCLE_SEQUENCE, + border: bool = False, + instruction: str = "", + long_instruction: str = "", + transformer: Optional[Callable[[Any], Any]] = None, + filter: Optional[Callable[[Any], Any]] = None, + height: Optional[Union[int, str]] = None, + max_height: Optional[Union[int, str]] = None, + validate: Optional[InquirerPyValidate] = None, + invalid_message: str = "Invalid input", + keybindings: Optional[InquirerPyKeybindings] = None, + show_cursor: bool = True, + cycle: bool = True, + wrap_lines: bool = True, + raise_keyboard_interrupt: bool = True, + mandatory: bool = True, + mandatory_message: str = "Mandatory prompt", + session_result: Optional[InquirerPySessionResult] = None, + ) -> None: + self.content_control = InquirerPyCheckboxControl( + choices=choices, + default=default, + pointer=pointer, + enabled_symbol=enabled_symbol, + disabled_symbol=disabled_symbol, + session_result=session_result, + ) + super().__init__( + message=message, + choices=choices, + style=style, + border=border, + vi_mode=vi_mode, + qmark=qmark, + amark=amark, + instruction=instruction, + long_instruction=long_instruction, + transformer=transformer, + filter=filter, + height=height, + max_height=max_height, + validate=validate, + invalid_message=invalid_message, + multiselect=True, + keybindings=keybindings, + show_cursor=show_cursor, + cycle=cycle, + wrap_lines=wrap_lines, + raise_keyboard_interrupt=raise_keyboard_interrupt, + mandatory=mandatory, + mandatory_message=mandatory_message, + session_result=session_result, + ) + + def _handle_enter(self, event) -> None: + """Override this method to force empty array result. + + When user does not select anything, exit with empty list. + + Args: + event: Keypress event. + """ + try: + fake_document = FakeDocument(self.result_value) + self._validator.validate(fake_document) # type: ignore + except ValidationError: + self._invalid = True + else: + self.status["answered"] = True + self.status["result"] = self.result_name + event.app.exit(result=self.result_value) diff --git a/voxvera/vendor/InquirerPy/prompts/confirm.py b/voxvera/vendor/InquirerPy/prompts/confirm.py new file mode 100644 index 0000000..7cd71f7 --- /dev/null +++ b/voxvera/vendor/InquirerPy/prompts/confirm.py @@ -0,0 +1,196 @@ +"""Module contains the class to create a confirm prompt.""" +from typing import TYPE_CHECKING, Any, Callable, List, Optional, Tuple + +from prompt_toolkit.buffer import ValidationState +from prompt_toolkit.keys import Keys +from prompt_toolkit.shortcuts import PromptSession +from prompt_toolkit.validation import ValidationError + +from InquirerPy.base import BaseSimplePrompt +from InquirerPy.exceptions import InvalidArgument +from InquirerPy.utils import ( + InquirerPyDefault, + InquirerPyKeybindings, + InquirerPyMessage, + InquirerPySessionResult, + InquirerPyStyle, +) + +if TYPE_CHECKING: + from prompt_toolkit.input.base import Input + from prompt_toolkit.key_binding.key_processor import KeyPressEvent + from prompt_toolkit.output.base import Output + +__all__ = ["ConfirmPrompt"] + + +class ConfirmPrompt(BaseSimplePrompt): + """Create a prompt that provides 2 options (confirm/deny) and controlled via single keypress. + + A wrapper class around :class:`~prompt_toolkit.shortcuts.PromptSession`. + + Args: + message: The question to ask the user. + Refer to :ref:`pages/dynamic:message` documentation for more details. + style: An :class:`InquirerPyStyle` instance. + Refer to :ref:`Style ` documentation for more details. + vi_mode: Used for compatibility . + default: Set the default value of the prompt, should be either `True` or `False`. + This affects the value returned when user directly hit `enter` key. + Refer to :ref:`pages/dynamic:default` documentation for more details. + qmark: Question mark symbol. Custom symbol that will be displayed infront of the question before its answered. + amark: Answer mark symbol. Custom symbol that will be displayed infront of the question after its answered. + instruction: Short instruction to display next to the question. + long_instruction: Long instructions to display at the bottom of the prompt. + transformer: A function which performs additional transformation on the value that gets printed to the terminal. + Different than `filter` parameter, this is only visual effect and won’t affect the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`. + Refer to :ref:`pages/dynamic:transformer` documentation for more details. + filter: A function which performs additional transformation on the result. + This affects the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`. + Refer to :ref:`pages/dynamic:filter` documentation for more details. + keybindings: Customise the builtin keybindings. + Refer to :ref:`pages/kb:Keybindings` for more details. + wrap_lines: Soft wrap question lines when question exceeds the terminal width. + confirm_letter: Letter used to confirm the prompt. A keybinding will be created for this letter. + Default is `y` and pressing `y` will answer the prompt with value `True`. + reject_letter: Letter used to reject the prompt. A keybinding will be created for this letter. + Default is `n` and pressing `n` will answer the prompt with value `False`. + raise_keyboard_interrupt: Raise the :class:`KeyboardInterrupt` exception when `ctrl-c` is pressed. If false, the result + will be `None` and the question is skiped. + mandatory: Indicate if the prompt is mandatory. If True, then the question cannot be skipped. + mandatory_message: Error message to show when user attempts to skip mandatory prompt. + session_result: Used internally for :ref:`index:Classic Syntax (PyInquirer)`. + input: Used internally and will be removed in future updates. + output: Used internally and will be removed in future updates. + + Examples: + >>> from InquirerPy import inquirer + >>> result = inquirer.confirm(message="Confirm?").execute() + >>> print(result) + True + """ + + def __init__( + self, + message: InquirerPyMessage, + style: Optional[InquirerPyStyle] = None, + default: InquirerPyDefault = False, + vi_mode: bool = False, + qmark: str = "?", + amark: str = "?", + instruction: str = "", + long_instruction: str = "", + transformer: Optional[Callable[[bool], Any]] = None, + filter: Optional[Callable[[bool], Any]] = None, + keybindings: Optional[InquirerPyKeybindings] = None, + wrap_lines: bool = True, + confirm_letter: str = "y", + reject_letter: str = "n", + raise_keyboard_interrupt: bool = True, + mandatory: bool = True, + mandatory_message: str = "Mandatory prompt", + session_result: Optional[InquirerPySessionResult] = None, + input: Optional["Input"] = None, + output: Optional["Output"] = None, + ) -> None: + vi_mode = False + super().__init__( + message=message, + style=style, + vi_mode=vi_mode, + qmark=qmark, + amark=amark, + instruction=instruction, + transformer=transformer, + filter=filter, + default=default, + wrap_lines=wrap_lines, + raise_keyboard_interrupt=raise_keyboard_interrupt, + mandatory=mandatory, + mandatory_message=mandatory_message, + session_result=session_result, + ) + if not isinstance(self._default, bool): + raise InvalidArgument( + f"{type(self).__name__} argument default should be type of bool" + ) + self._confirm_letter = confirm_letter + self._reject_letter = reject_letter + + if not keybindings: + keybindings = {} + self.kb_maps = { + "confirm": [ + {"key": self._confirm_letter}, + {"key": self._confirm_letter.upper()}, + ], + "reject": [ + {"key": self._reject_letter}, + {"key": self._reject_letter.upper()}, + ], + "any": [{"key": Keys.Any}], + **keybindings, + } + self.kb_func_lookup = { + "confirm": [{"func": self._handle_confirm}], + "reject": [{"func": self._handle_reject}], + "any": [{"func": lambda _: None}], + } + self._keybinding_factory() + + self._session = PromptSession( + message=self._get_prompt_message, + key_bindings=self._kb, + style=self._style, + wrap_lines=self._wrap_lines, + bottom_toolbar=[("class:long_instruction", long_instruction)] + if long_instruction + else None, + input=input, + output=output, + ) + + def _set_error(self, message: str) -> None: + self._session.default_buffer.validation_state = ValidationState.INVALID + self._session.default_buffer.validation_error = ValidationError(message=message) + + def _handle_reject(self, event) -> None: + self._session.default_buffer.text = "" + self.status["answered"] = True + self.status["result"] = False + event.app.exit(result=False) + + def _handle_confirm(self, event) -> None: + self._session.default_buffer.text = "" + self.status["answered"] = True + self.status["result"] = True + event.app.exit(result=True) + + def _handle_enter(self, event: "KeyPressEvent") -> None: + self.status["answered"] = True + self.status["result"] = self._default + event.app.exit(result=self._default) + + def _get_prompt_message(self) -> List[Tuple[str, str]]: + """Get message to display infront of the input buffer. + + Returns: + Formatted text in list of tuple format. + """ + if not self.instruction: + pre_answer = ( + "class:instruction", + " (%s/%s) " % (self._confirm_letter.upper(), self._reject_letter) + if self._default + else " (%s/%s) " % (self._confirm_letter, self._reject_letter.upper()), + ) + else: + pre_answer = ("class:instruction", " %s " % self.instruction) + post_answer = ("class:answer", " Yes" if self.status["result"] else " No") + return super()._get_prompt_message(pre_answer, post_answer) + + def _run(self) -> bool: + return self._session.prompt() + + async def _run_async(self) -> Any: + return await self._session.prompt_async() diff --git a/voxvera/vendor/InquirerPy/prompts/expand.py b/voxvera/vendor/InquirerPy/prompts/expand.py new file mode 100644 index 0000000..e95f967 --- /dev/null +++ b/voxvera/vendor/InquirerPy/prompts/expand.py @@ -0,0 +1,458 @@ +"""Module contains the class to create an expand prompt.""" +from dataclasses import dataclass +from typing import Any, Callable, List, Optional, Tuple, Union + +from InquirerPy.base import BaseListPrompt, InquirerPyUIListControl +from InquirerPy.base.control import Choice +from InquirerPy.enum import INQUIRERPY_POINTER_SEQUENCE +from InquirerPy.exceptions import InvalidArgument, RequiredKeyNotFound +from InquirerPy.prompts.list import ListPrompt +from InquirerPy.separator import Separator +from InquirerPy.utils import ( + InquirerPyDefault, + InquirerPyKeybindings, + InquirerPyListChoices, + InquirerPyMessage, + InquirerPySessionResult, + InquirerPyStyle, + InquirerPyValidate, +) + +__all__ = ["ExpandPrompt", "ExpandHelp", "ExpandChoice"] + + +@dataclass +class ExpandHelp: + """Help choice for the :class:`.ExpandPrompt`. + + Args: + key: The key to bind to toggle the expansion of the prompt. + message: The help message. + """ + + key: str = "h" + message: str = "Help, list all choices" + + +@dataclass +class ExpandChoice(Choice): + """Choice class for :class:`.ExpandPrompt`. + + See Also: + :class:`~InquirerPy.base.control.Choice` + + Args: + value: The value of the choice when user selects this choice. + name: The value that should be presented to the user prior/after selection of the choice. + This value is optional, if not provided, it will fallback to the string representation of `value`. + enabled: Indicates if the choice should be pre-selected. + This only has effects when the prompt has `multiselect` enabled. + key: Char to bind to the choice. Pressing this value will jump to the choice, + If this value is missing, the first char of the `str(value)` will be used as the key. + """ + + key: Optional[str] = None + + def __post_init__(self): + """Assign stringify value to name and also create key using the first char of the value if not present.""" + super().__post_init__() + if self.key is None: + self.key = str(self.value)[0].lower() + + +class InquirerPyExpandControl(InquirerPyUIListControl): + """An :class:`~prompt_toolkit.layout.UIControl` class that displays a list of choices. + + Reference the parameter definition in :class:`.ExpandPrompt`. + """ + + def __init__( + self, + choices: InquirerPyListChoices, + default: Any, + pointer: str, + separator: str, + expand_help: ExpandHelp, + expand_pointer: str, + marker: str, + session_result: Optional[InquirerPySessionResult], + multiselect: bool, + marker_pl: str, + ) -> None: + self._pointer = pointer + self._separator = separator + self._expanded = False + self._expand_pointer = expand_pointer + self._marker = marker + self._marker_pl = marker_pl + self._expand_help = expand_help + super().__init__( + choices=choices, + default=default, + session_result=session_result, + multiselect=multiselect, + ) + + def _format_choices(self) -> None: + self._key_maps = {} + try: + count = 0 + separator_count = 0 + for raw_choice, choice in zip(self._raw_choices, self.choices): # type: ignore + if ( + not isinstance(raw_choice, dict) + and not isinstance(raw_choice, Separator) + and not isinstance(raw_choice, ExpandChoice) + ): + raise InvalidArgument( + "expand prompt argument choices requires each choice to be type of dictionary or Separator or ExpandChoice" + ) + if isinstance(raw_choice, Separator): + separator_count += 1 + else: + choice["key"] = ( + raw_choice.key + if isinstance(raw_choice, ExpandChoice) + else raw_choice["key"] + ) + self._key_maps[choice["key"]] = count + count += 1 + except KeyError: + raise RequiredKeyNotFound( + "expand prompt choice requires a key 'key' to exists" + ) + + self.choices.append( + { + "key": self._expand_help.key, + "value": self._expand_help, + "name": self._expand_help.message, + "enabled": False, + } + ) + self._key_maps[self._expand_help.key] = len(self.choices) - 1 + + first_valid_choice_index = 0 + while isinstance(self.choices[first_valid_choice_index]["value"], Separator): + first_valid_choice_index += 1 + if self.selected_choice_index == first_valid_choice_index: + for index, choice in enumerate(self.choices): + if isinstance(choice["value"], Separator): + continue + if choice["key"] == self._default: + self.selected_choice_index = index + break + + def _get_formatted_choices(self) -> List[Tuple[str, str]]: + """Override this parent class method as expand require visual switch of content. + + Two types of mode: + * non expand mode + * expand mode + """ + if self._expanded: + return super()._get_formatted_choices() + else: + display_choices = [] + display_choices.append(("class:pointer", self._expand_pointer)) + display_choices.append( + ("", self.choices[self.selected_choice_index]["name"]) + ) + return display_choices + + def _get_hover_text(self, choice) -> List[Tuple[str, str]]: + display_choices = [] + display_choices.append(("class:pointer", self._pointer)) + display_choices.append( + ( + "class:marker", + self._marker if choice["enabled"] else self._marker_pl, + ) + ) + if not isinstance(choice["value"], Separator): + display_choices.append( + ("class:pointer", "%s%s" % (choice["key"], self._separator)) + ) + display_choices.append(("[SetCursorPosition]", "")) + display_choices.append(("class:pointer", choice["name"])) + return display_choices + + def _get_normal_text(self, choice) -> List[Tuple[str, str]]: + display_choices = [] + display_choices.append(("", len(self._pointer) * " ")) + display_choices.append( + ( + "class:marker", + self._marker if choice["enabled"] else self._marker_pl, + ) + ) + if not isinstance(choice["value"], Separator): + display_choices.append(("", "%s%s" % (choice["key"], self._separator))) + display_choices.append(("", choice["name"])) + else: + display_choices.append(("class:separator", choice["name"])) + return display_choices + + +class ExpandPrompt(ListPrompt): + """Create a compact prompt with the ability to expand. + + A wrapper class around :class:`~prompt_toolkit.application.Application`. + + Contains a list of chocies binded to a shortcut letter. + The prompt can be expanded using `h` key. + + Args: + message: The question to ask the user. + Refer to :ref:`pages/dynamic:message` documentation for more details. + choices: List of choices to display and select. + Refer to :ref:`pages/prompts/expand:Choices` documentation for more details. + style: An :class:`InquirerPyStyle` instance. + Refer to :ref:`Style ` documentation for more details. + vi_mode: Use vim keybinding for the prompt. + Refer to :ref:`pages/kb:Keybindings` documentation for more details. + default: Set the default value of the prompt. + This will be used to determine which choice is highlighted (current selection), + The default value should the value of one of the choices. + For :class:`.ExpandPrompt` specifically, default value can also be a `choice["key"]` which is the shortcut key for the choice. + Refer to :ref:`pages/dynamic:default` documentation for more details. + separator: Separator symbol. Custom symbol that will be used as a separator between the choice index number and the choices. + help_msg: This parameter is DEPRECATED. Use expand_help instead. + expand_help: The help configuration for the prompt. Must be an instance of :class:`.ExpandHelp`. + If this value is None, the default help key will be binded to `h` and the default help message would be + "Help, List all choices." + expand_pointer: Pointer symbol before prompt expansion. Custom symbol that will be displayed to indicate the prompt is not expanded. + qmark: Question mark symbol. Custom symbol that will be displayed infront of the question before its answered. + amark: Answer mark symbol. Custom symbol that will be displayed infront of the question after its answered. + pointer: Pointer symbol. Customer symbol that will be used to indicate the current choice selection. + instruction: Short instruction to display next to the question. + long_instruction: Long instructions to display at the bottom of the prompt. + validate: Add validation to user input. + The main use case for this prompt would be when `multiselect` is True, you can enforce a min/max selection. + Refer to :ref:`pages/validator:Validator` documentation for more details. + invalid_message: Error message to display when user input is invalid. + Refer to :ref:`pages/validator:Validator` documentation for more details. + transformer: A function which performs additional transformation on the value that gets printed to the terminal. + Different than `filter` parameter, this is only visual effect and won’t affect the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`. + Refer to :ref:`pages/dynamic:transformer` documentation for more details. + filter: A function which performs additional transformation on the result. + This affects the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`. + Refer to :ref:`pages/dynamic:filter` documentation for more details. + height: Preferred height of the prompt. + Refer to :ref:`pages/height:Height` documentation for more details. + max_height: Max height of the prompt. + Refer to :ref:`pages/height:Height` documentation for more details. + multiselect: Enable multi-selection on choices. + You can use `validate` parameter to control min/max selections. + Setting to True will also change the result from a single value to a list of values. + marker: Marker Symbol. Custom symbol to indicate if a choice is selected. + This will take effects when `multiselect` is True. + marker_pl: Marker place holder when the choice is not selected. + This is empty space by default. + border: Create border around the choice window. + keybindings: Customise the builtin keybindings. + Refer to :ref:`pages/kb:Keybindings` for more details. + show_cursor: Display cursor at the end of the prompt. + Set to False to hide the cursor. + cycle: Return to top item if hit bottom during navigation or vice versa. + wrap_lines: Soft wrap question lines when question exceeds the terminal width. + raise_keyboard_interrupt: Raise the :class:`KeyboardInterrupt` exception when `ctrl-c` is pressed. If false, the result + will be `None` and the question is skiped. + mandatory: Indicate if the prompt is mandatory. If True, then the question cannot be skipped. + mandatory_message: Error message to show when user attempts to skip mandatory prompt. + session_result: Used internally for :ref:`index:Classic Syntax (PyInquirer)`. + + Examples: + >>> from InquirerPy import inquirer + >>> result = inquirer.expand(message="Select one:", choices[{"name": "1", "value": "1", "key": "a"}]).execute() + >>> print(result) + "1" + """ + + def __init__( + self, + message: InquirerPyMessage, + choices: InquirerPyListChoices, + default: InquirerPyDefault = "", + style: Optional[InquirerPyStyle] = None, + vi_mode: bool = False, + qmark: str = "?", + amark: str = "?", + pointer: str = " ", + separator: str = ") ", + help_msg: str = "Help, list all choices", + expand_help: Optional[ExpandHelp] = None, + expand_pointer: str = "%s " % INQUIRERPY_POINTER_SEQUENCE, + instruction: str = "", + long_instruction: str = "", + transformer: Optional[Callable[[Any], Any]] = None, + filter: Optional[Callable[[Any], Any]] = None, + height: Optional[Union[int, str]] = None, + max_height: Optional[Union[int, str]] = None, + multiselect: bool = False, + marker: str = INQUIRERPY_POINTER_SEQUENCE, + marker_pl: str = " ", + border: bool = False, + validate: Optional[InquirerPyValidate] = None, + invalid_message: str = "Invalid input", + keybindings: Optional[InquirerPyKeybindings] = None, + show_cursor: bool = True, + cycle: bool = True, + wrap_lines: bool = True, + raise_keyboard_interrupt: bool = True, + mandatory: bool = True, + mandatory_message: str = "Mandatory prompt", + session_result: Optional[InquirerPySessionResult] = None, + ) -> None: + if expand_help is None: + expand_help = ExpandHelp(message=help_msg) + self._expand_help = expand_help + self.content_control: InquirerPyExpandControl = InquirerPyExpandControl( + choices=choices, + default=default, + pointer=pointer, + separator=separator, + expand_help=expand_help, + expand_pointer=expand_pointer, + marker=marker, + marker_pl=marker_pl, + session_result=session_result, + multiselect=multiselect, + ) + super().__init__( + message=message, + choices=choices, + style=style, + border=border, + vi_mode=vi_mode, + qmark=qmark, + amark=amark, + instruction=instruction, + long_instruction=long_instruction, + transformer=transformer, + filter=filter, + height=height, + max_height=max_height, + validate=validate, + invalid_message=invalid_message, + multiselect=multiselect, + keybindings=keybindings, + show_cursor=show_cursor, + cycle=cycle, + wrap_lines=wrap_lines, + raise_keyboard_interrupt=raise_keyboard_interrupt, + mandatory=mandatory, + mandatory_message=mandatory_message, + session_result=session_result, + ) + + def _on_rendered(self, _) -> None: + """Override this method to apply custom keybindings. + + Needs to creat these kb in the callback due to `after_render` + retrieve the choices asynchronously. + """ + + def keybinding_factory(key): + @self.register_kb(key.lower()) + def keybinding(_) -> None: + if key == self._expand_help.key: + self.content_control._expanded = not self.content_control._expanded + else: + self.content_control.selected_choice_index = ( + self.content_control._key_maps[key] + ) + + return keybinding + + for choice in self.content_control.choices: + if not isinstance(choice["value"], Separator): + keybinding_factory(choice["key"]) + + def _handle_up(self, event) -> None: + """Handle the event when user attempt to move up. + + Overriding this method to skip the help choice. + """ + if not self.content_control._expanded: + return + while True: + cap = BaseListPrompt._handle_up(self, event) + if not isinstance( + self.content_control.selection["value"], Separator + ) and not isinstance(self.content_control.selection["value"], ExpandHelp): + break + else: + if cap and not self._cycle: + self._handle_down(event) + break + + def _handle_down(self, event) -> None: + """Handle the event when user attempt to move down. + + Overriding this method to skip the help choice. + """ + if not self.content_control._expanded: + return + while True: + cap = BaseListPrompt._handle_down(self, event) + if not isinstance( + self.content_control.selection["value"], Separator + ) and not isinstance(self.content_control.selection["value"], ExpandHelp): + break + elif ( + isinstance(self.content_control.selection["value"], ExpandHelp) + and not self._cycle + ): + self._handle_up(event) + break + else: + if cap and not self._cycle: + self._handle_up(event) + break + + @property + def instruction(self) -> str: + """Construct the instruction behind the question. + + If _instruction exists, use that. + + :return: The instruction text. + """ + return ( + "(%s)" % "".join(self.content_control._key_maps.keys()) + if not self._instruction + else self._instruction + ) + + def _get_prompt_message(self) -> List[Tuple[str, str]]: + """Return the formatted text to display in the prompt. + + Overriding this method to allow multiple formatted class to be displayed. + """ + display_message = super()._get_prompt_message() + if not self.status["answered"]: + display_message.append( + ("class:input", self.content_control.selection["key"]) + ) + return display_message + + def _handle_toggle_all(self, _, value: Optional[bool] = None) -> None: + """Override this method to ignore `ExpandHelp`. + + :param value: Specify a value to toggle. + """ + if not self.content_control._expanded: + return + for choice in self.content_control.choices: + if isinstance(choice["value"], Separator) or isinstance( + choice["value"], ExpandHelp + ): + continue + choice["enabled"] = value if value else not choice["enabled"] + + def _handle_toggle_choice(self, event) -> None: + """Override this method to ignore keypress when not expanded.""" + if not self.content_control._expanded: + return + super()._handle_toggle_choice(event) diff --git a/voxvera/vendor/InquirerPy/prompts/filepath.py b/voxvera/vendor/InquirerPy/prompts/filepath.py new file mode 100644 index 0000000..97cce2b --- /dev/null +++ b/voxvera/vendor/InquirerPy/prompts/filepath.py @@ -0,0 +1,192 @@ +"""Module contains the class to create filepath prompt and filepath completer class.""" +import os +from pathlib import Path +from typing import TYPE_CHECKING, Any, Callable, Generator, Optional + +from prompt_toolkit.completion import Completer, Completion +from prompt_toolkit.completion.base import ThreadedCompleter + +from InquirerPy.prompts.input import InputPrompt +from InquirerPy.utils import ( + InquirerPyDefault, + InquirerPyKeybindings, + InquirerPyMessage, + InquirerPySessionResult, + InquirerPyStyle, + InquirerPyValidate, +) + +if TYPE_CHECKING: + from prompt_toolkit.input.base import Input + from prompt_toolkit.output.base import Output + +__all__ = ["FilePathPrompt", "FilePathCompleter"] + + +class FilePathCompleter(Completer): + """An auto completion class which generates system filepath. + + See Also: + :class:`~prompt_toolkit.completion.Completer` + + Args: + only_directories: Only complete directories. + only_files: Only complete files. + """ + + def __init__(self, only_directories: bool = False, only_files: bool = False): + self._only_directories = only_directories + self._only_files = only_files + self._delimiter = "/" if os.name == "posix" else "\\" + + def get_completions( + self, document, complete_event + ) -> Generator[Completion, None, None]: + """Get a list of valid system paths.""" + if document.text == "~": + return + + validation = lambda file, doc_text: str(file).startswith(doc_text) + + if document.cursor_position == 0: + dirname = Path.cwd() + validation = lambda file, doc_text: True + elif document.text.startswith("~"): + dirname = Path(os.path.dirname(f"{Path.home()}{document.text[1:]}")) + validation = lambda file, doc_text: str(file).startswith( + f"{Path.home()}{doc_text[1:]}" + ) + elif document.text.startswith(f".{self._delimiter}"): + dirname = Path(os.path.dirname(document.text)) + validation = lambda file, doc_text: str(file).startswith(doc_text[2:]) + else: + dirname = Path(os.path.dirname(document.text)) + + for item in self._get_completion(document, dirname, validation): + yield item + + def _get_completion( + self, document, path, validation + ) -> Generator[Completion, None, None]: + if not path.is_dir(): + return + for file in path.iterdir(): + if self._only_directories and not file.is_dir(): + continue + if self._only_files and not file.is_file(): + continue + if validation(file, document.text): + file_name = file.name + display_name = file_name + if file.is_dir(): + display_name = f"{file_name}{self._delimiter}" + yield Completion( + file.name, + start_position=-1 * len(os.path.basename(document.text)), + display=display_name, + ) + + +class FilePathPrompt(InputPrompt): + """Create a prompt that provides auto completion for system filepaths. + + A wrapper class around :class:`~prompt_toolkit.shortcuts.PromptSession`. + + Args: + message: The question to ask the user. + Refer to :ref:`pages/dynamic:message` documentation for more details. + style: An :class:`InquirerPyStyle` instance. + Refer to :ref:`Style ` documentation for more details. + vi_mode: Use vim keybinding for the prompt. + Refer to :ref:`pages/kb:Keybindings` documentation for more details. + default: Set the default text value of the prompt. + Refer to :ref:`pages/dynamic:default` documentation for more details. + qmark: Question mark symbol. Custom symbol that will be displayed infront of the question before its answered. + amark: Answer mark symbol. Custom symbol that will be displayed infront of the question after its answered. + instruction: Short instruction to display next to the question. + long_instruction: Long instructions to display at the bottom of the prompt. + multicolumn_complete: Change the auto-completion UI to a multi column display. + validate: Add validation to user input. + Refer to :ref:`pages/validator:Validator` documentation for more details. + invalid_message: Error message to display when user input is invalid. + Refer to :ref:`pages/validator:Validator` documentation for more details. + transformer: A function which performs additional transformation on the value that gets printed to the terminal. + Different than `filter` parameter, this is only visual effect and won’t affect the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`. + Refer to :ref:`pages/dynamic:transformer` documentation for more details. + filter: A function which performs additional transformation on the result. + This affects the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`. + Refer to :ref:`pages/dynamic:filter` documentation for more details. + keybindings: Customise the builtin keybindings. + Refer to :ref:`pages/kb:Keybindings` for more details. + wrap_lines: Soft wrap question lines when question exceeds the terminal width. + only_directories: Only complete directories. + only_files: Only complete files. + raise_keyboard_interrupt: Raise the :class:`KeyboardInterrupt` exception when `ctrl-c` is pressed. If false, the result + will be `None` and the question is skiped. + mandatory: Indicate if the prompt is mandatory. If True, then the question cannot be skipped. + mandatory_message: Error message to show when user attempts to skip mandatory prompt. + session_result: Used internally for :ref:`index:Classic Syntax (PyInquirer)`. + input: Used internally and will be removed in future updates. + output: Used internally and will be removed in future updates. + + Examples: + >>> from InquirerPy import inquirer + >>> result = inquirer.filepath(message="Enter a path:").execute() + >>> print(result) + /home/ubuntu/README.md + """ + + def __init__( + self, + message: InquirerPyMessage, + style: Optional[InquirerPyStyle] = None, + vi_mode: bool = False, + default: InquirerPyDefault = "", + qmark: str = "?", + amark: str = "?", + instruction: str = "", + long_instruction: str = "", + multicolumn_complete: bool = False, + validate: Optional[InquirerPyValidate] = None, + invalid_message: str = "Invalid input", + only_directories: bool = False, + only_files: bool = False, + transformer: Optional[Callable[[str], Any]] = None, + filter: Optional[Callable[[str], Any]] = None, + keybindings: Optional[InquirerPyKeybindings] = None, + wrap_lines: bool = True, + raise_keyboard_interrupt: bool = True, + mandatory: bool = True, + mandatory_message: str = "Mandatory prompt", + session_result: Optional[InquirerPySessionResult] = None, + input: Optional["Input"] = None, + output: Optional["Output"] = None, + ) -> None: + super().__init__( + message=message, + style=style, + vi_mode=vi_mode, + default=default, + qmark=qmark, + amark=amark, + instruction=instruction, + long_instruction=long_instruction, + completer=ThreadedCompleter( + FilePathCompleter( + only_directories=only_directories, only_files=only_files + ) + ), + multicolumn_complete=multicolumn_complete, + validate=validate, + invalid_message=invalid_message, + transformer=transformer, + filter=filter, + keybindings=keybindings, + wrap_lines=wrap_lines, + raise_keyboard_interrupt=raise_keyboard_interrupt, + mandatory=mandatory, + mandatory_message=mandatory_message, + session_result=session_result, + input=input, + output=output, + ) diff --git a/voxvera/vendor/InquirerPy/prompts/fuzzy.py b/voxvera/vendor/InquirerPy/prompts/fuzzy.py new file mode 100644 index 0000000..8f1a5b6 --- /dev/null +++ b/voxvera/vendor/InquirerPy/prompts/fuzzy.py @@ -0,0 +1,680 @@ +"""Module contains the class to create a fuzzy prompt.""" +import asyncio +import math +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Dict, + List, + Optional, + Tuple, + Union, + cast, +) + +from pfzy import fuzzy_match +from pfzy.score import fzy_scorer, substr_scorer +from pfzy.types import HAYSTACKS +from prompt_toolkit.application.application import Application +from prompt_toolkit.buffer import Buffer +from prompt_toolkit.filters.cli import IsDone +from prompt_toolkit.layout.containers import ( + ConditionalContainer, + FloatContainer, + HSplit, + Window, +) +from prompt_toolkit.layout.controls import BufferControl, DummyControl +from prompt_toolkit.layout.dimension import Dimension, LayoutDimension +from prompt_toolkit.layout.layout import Layout +from prompt_toolkit.layout.processors import AfterInput, BeforeInput +from prompt_toolkit.lexers.base import SimpleLexer +from prompt_toolkit.validation import ValidationError +from prompt_toolkit.widgets.base import Frame + +from InquirerPy.base import FakeDocument, InquirerPyUIListControl +from InquirerPy.base.list import BaseListPrompt +from InquirerPy.containers.instruction import InstructionWindow +from InquirerPy.containers.message import MessageWindow +from InquirerPy.containers.validation import ValidationFloat +from InquirerPy.enum import INQUIRERPY_POINTER_SEQUENCE +from InquirerPy.exceptions import InvalidArgument +from InquirerPy.separator import Separator +from InquirerPy.utils import ( + InquirerPyDefault, + InquirerPyKeybindings, + InquirerPyListChoices, + InquirerPyMessage, + InquirerPySessionResult, + InquirerPyStyle, + InquirerPyValidate, + calculate_height, +) + +if TYPE_CHECKING: + from prompt_toolkit.key_binding.key_processor import KeyPressEvent + +__all__ = ["FuzzyPrompt"] + + +class InquirerPyFuzzyControl(InquirerPyUIListControl): + """An :class:`~prompt_toolkit.layout.UIControl` class that displays a list of choices. + + This only displays the chocies. The actual input buffer will be handled by a separate + :class:`~prompt_toolkit.layout.BufferControl`. + + Reference the parameter definition in :class:`.FuzzyPrompt`. + """ + + def __init__( + self, + choices: InquirerPyListChoices, + pointer: str, + marker: str, + current_text: Callable[[], str], + max_lines: int, + session_result: Optional[InquirerPySessionResult], + multiselect: bool, + marker_pl: str, + match_exact: bool, + ) -> None: + self._pointer = pointer + self._marker = marker + self._marker_pl = marker_pl + self._current_text = current_text + self._max_lines = max_lines if max_lines > 0 else 1 + self._scorer = fzy_scorer if not match_exact else substr_scorer + super().__init__( + choices=choices, + default=None, + session_result=session_result, + multiselect=multiselect, + ) + + def _format_choices(self) -> None: + for index, choice in enumerate(self.choices): + if isinstance(choice["value"], Separator): + raise InvalidArgument( + "fuzzy prompt argument choices should not contain Separator" + ) + choice["index"] = index + choice["indices"] = [] + self._filtered_choices = self.choices + self._first_line = 0 + self._last_line = min(self._max_lines, self.choice_count) + self._height = self._last_line - self._first_line + + def _get_hover_text(self, choice) -> List[Tuple[str, str]]: + """Get the current highlighted line of text. + + If in the middle of filtering, loop through the char and color + indices matched char into style class `class:fuzzy_match`. + + Returns: + FormattedText in list of tuple format. + """ + display_choices = [] + display_choices.append(("class:pointer", self._pointer)) + display_choices.append( + ( + "class:marker", + self._marker + if self.choices[choice["index"]]["enabled"] + else self._marker_pl, + ) + ) + display_choices.append(("[SetCursorPosition]", "")) + if not choice["indices"]: + display_choices.append(("class:pointer", choice["name"])) + else: + indices = set(choice["indices"]) + for index, char in enumerate(choice["name"]): + if index in indices: + display_choices.append(("class:fuzzy_match", char)) + else: + display_choices.append(("class:pointer", char)) + return display_choices + + def _get_normal_text(self, choice) -> List[Tuple[str, str]]: + """Get the line of text in `FormattedText`. + + If in the middle of filtering, loop through the char and color + indices matched char into `class:fuzzy_match`. + + Calculate spaces of pointer to make the choice equally align. + + Returns: + FormattedText in list of tuple format. + """ + display_choices = [] + display_choices.append(("class:pointer", len(self._pointer) * " ")) + display_choices.append( + ( + "class:marker", + self._marker + if self.choices[choice["index"]]["enabled"] + else self._marker_pl, + ) + ) + if not choice["indices"]: + display_choices.append(("", choice["name"])) + else: + indices = set(choice["indices"]) + for index, char in enumerate(choice["name"]): + if index in indices: + display_choices.append(("class:fuzzy_match", char)) + else: + display_choices.append(("", char)) + return display_choices + + def _get_formatted_choices(self) -> List[Tuple[str, str]]: + """Get all available choices in formatted text format. + + Overriding this method because `self.choice` will be the + full choice list. Using `self.filtered_choice` to get + a list of choice based on current_text. + + Returns: + FormattedText in list of tuple format. + """ + display_choices = [] + if self.choice_count == 0: + self._selected_choice_index = 0 + return display_choices + + if self._selected_choice_index < 0: + self._selected_choice_index = 0 + elif self._selected_choice_index >= self.choice_count: + self._selected_choice_index = self.choice_count - 1 + + if (self._last_line - self._first_line) < min(self.choice_count, self._height): + self._last_line = min(self.choice_count, self._height) + self._first_line = self._last_line - min(self.choice_count, self._height) + + if self._selected_choice_index <= self._first_line: + self._first_line = self._selected_choice_index + self._last_line = self._first_line + min(self._height, self.choice_count) + elif self._selected_choice_index >= self._last_line: + self._last_line = self._selected_choice_index + 1 + self._first_line = self._last_line - min(self._height, self.choice_count) + + if self._last_line > self.choice_count: + self._last_line = self.choice_count + self._first_line = self._last_line - min(self._height, self.choice_count) + if self._first_line < 0: + self._first_line = 0 + self._last_line = self._first_line + min(self._height, self.choice_count) + + for index in range(self._first_line, self._last_line): + if index == self.selected_choice_index: + display_choices += self._get_hover_text(self._filtered_choices[index]) + else: + display_choices += self._get_normal_text(self._filtered_choices[index]) + display_choices.append(("", "\n")) + if display_choices: + display_choices.pop() + return display_choices + + async def _filter_choices(self, wait_time: float) -> List[Dict[str, Any]]: + """Call to filter choices using fzy fuzzy match. + + Args: + wait_time: Additional time to wait before filtering the choice. + + Returns: + Filtered choices. + """ + if not self._current_text(): + for choice in self.choices: + choice["indices"] = [] + choices = self.choices + else: + await asyncio.sleep(wait_time) + choices = await fuzzy_match( + self._current_text(), + cast(HAYSTACKS, self.choices), + key="name", + scorer=self._scorer, + ) + return choices + + @property + def selection(self) -> Dict[str, Any]: + """Override this value since `self.choice` does not indicate the choice displayed. + + `self.filtered_choice` is the up to date choice displayed. + + Returns: + A dictionary of name and value for the current pointed choice. + """ + return self._filtered_choices[self.selected_choice_index] + + @property + def choice_count(self) -> int: + """int: Filtered choice count.""" + return len(self._filtered_choices) + + +class FuzzyPrompt(BaseListPrompt): + """Create a prompt that lists choices while also allowing fuzzy search like fzf. + + A wrapper class around :class:`~prompt_toolkit.application.Application`. + + Fuzzy search using :func:`pfzy.match.fuzzy_match` function. + + Override the default keybindings for up/down as j/k cannot be bind even if `editing_mode` is vim + due to the input buffer. + + Args: + message: The question to ask the user. + Refer to :ref:`pages/dynamic:message` documentation for more details. + choices: List of choices to display and select. + Refer to :ref:`pages/dynamic:choices` documentation for more details. + style: An :class:`InquirerPyStyle` instance. + Refer to :ref:`Style ` documentation for more details. + vi_mode: Use vim keybinding for the prompt. + Refer to :ref:`pages/kb:Keybindings` documentation for more details. + default: Set the default value in the search buffer. + Different than other list type prompts, the `default` parameter tries to replicate what fzf does and + add the value in `default` to search buffer so it starts searching immediatelly. + Refer to :ref:`pages/dynamic:default` documentation for more details. + qmark: Question mark symbol. Custom symbol that will be displayed infront of the question before its answered. + amark: Answer mark symbol. Custom symbol that will be displayed infront of the question after its answered. + pointer: Pointer symbol. Customer symbol that will be used to indicate the current choice selection. + instruction: Short instruction to display next to the question. + long_instruction: Long instructions to display at the bottom of the prompt. + validate: Add validation to user input. + The main use case for this prompt would be when `multiselect` is True, you can enforce a min/max selection. + Refer to :ref:`pages/validator:Validator` documentation for more details. + invalid_message: Error message to display when user input is invalid. + Refer to :ref:`pages/validator:Validator` documentation for more details. + transformer: A function which performs additional transformation on the value that gets printed to the terminal. + Different than `filter` parameter, this is only visual effect and won’t affect the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`. + Refer to :ref:`pages/dynamic:transformer` documentation for more details. + filter: A function which performs additional transformation on the result. + This affects the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`. + Refer to :ref:`pages/dynamic:filter` documentation for more details. + height: Preferred height of the prompt. + Refer to :ref:`pages/height:Height` documentation for more details. + max_height: Max height of the prompt. + Refer to :ref:`pages/height:Height` documentation for more details. + multiselect: Enable multi-selection on choices. + You can use `validate` parameter to control min/max selections. + Setting to True will also change the result from a single value to a list of values. + prompt: Input prompt symbol. Custom symbol to display infront of the input buffer to indicate for input. + border: Create border around the choice window. + info: Display choice information similar to fzf --info=inline next to the prompt. + match_exact: Use exact sub-string match instead of using fzy fuzzy match algorithm. + exact_symbol: Custom symbol to display in the info section when `info=True`. + marker: Marker Symbol. Custom symbol to indicate if a choice is selected. + This will take effects when `multiselect` is True. + marker_pl: Marker place holder when the choice is not selected. + This is empty space by default. + keybindings: Customise the builtin keybindings. + Refer to :ref:`pages/kb:Keybindings` for more details. + cycle: Return to top item if hit bottom during navigation or vice versa. + wrap_lines: Soft wrap question lines when question exceeds the terminal width. + raise_keyboard_interrupt: Raise the :class:`KeyboardInterrupt` exception when `ctrl-c` is pressed. If false, the result + will be `None` and the question is skiped. + mandatory: Indicate if the prompt is mandatory. If True, then the question cannot be skipped. + mandatory_message: Error message to show when user attempts to skip mandatory prompt. + session_result: Used internally for :ref:`index:Classic Syntax (PyInquirer)`. + + Examples: + >>> from InquirerPy import inquirer + >>> result = inquirer.fuzzy(message="Select one:", choices=[1, 2, 3]).execute() + >>> print(result) + 1 + """ + + def __init__( + self, + message: InquirerPyMessage, + choices: InquirerPyListChoices, + default: InquirerPyDefault = "", + pointer: str = INQUIRERPY_POINTER_SEQUENCE, + style: Optional[InquirerPyStyle] = None, + vi_mode: bool = False, + qmark: str = "?", + amark: str = "?", + transformer: Optional[Callable[[Any], Any]] = None, + filter: Optional[Callable[[Any], Any]] = None, + instruction: str = "", + long_instruction: str = "", + multiselect: bool = False, + prompt: str = INQUIRERPY_POINTER_SEQUENCE, + marker: str = INQUIRERPY_POINTER_SEQUENCE, + marker_pl: str = " ", + border: bool = False, + info: bool = True, + match_exact: bool = False, + exact_symbol: str = " E", + height: Optional[Union[str, int]] = None, + max_height: Optional[Union[str, int]] = None, + validate: Optional[InquirerPyValidate] = None, + invalid_message: str = "Invalid input", + keybindings: Optional[InquirerPyKeybindings] = None, + cycle: bool = True, + wrap_lines: bool = True, + raise_keyboard_interrupt: bool = True, + mandatory: bool = True, + mandatory_message: str = "Mandatory prompt", + session_result: Optional[InquirerPySessionResult] = None, + ) -> None: + if not keybindings: + keybindings = {} + self._prompt = prompt + self._info = info + self._task = None + self._rendered = False + self._exact_symbol = exact_symbol + + keybindings = { + "up": [{"key": "up"}, {"key": "c-p"}], + "down": [{"key": "down"}, {"key": "c-n"}], + "toggle": [], + "toggle-exact": [], + **keybindings, + } + super().__init__( + message=message, + style=style, + border=border, + vi_mode=vi_mode, + qmark=qmark, + amark=amark, + transformer=transformer, + filter=filter, + validate=validate, + invalid_message=invalid_message, + multiselect=multiselect, + instruction=instruction, + long_instruction=long_instruction, + keybindings=keybindings, + cycle=cycle, + wrap_lines=wrap_lines, + raise_keyboard_interrupt=raise_keyboard_interrupt, + mandatory=mandatory, + mandatory_message=mandatory_message, + session_result=session_result, + ) + self.kb_func_lookup = {"toggle-exact": [{"func": self._toggle_exact}]} + self._default = ( + default + if not isinstance(default, Callable) + else cast(Callable, default)(self._result) + ) + self._height_offset += 1 # search input + self._dimmension_height, self._dimmension_max_height = calculate_height( + height, max_height, height_offset=self.height_offset + ) + + self._content_control: InquirerPyFuzzyControl = InquirerPyFuzzyControl( + choices=choices, + pointer=pointer, + marker=marker, + current_text=self._get_current_text, + max_lines=self._dimmension_max_height, + session_result=session_result, + multiselect=multiselect, + marker_pl=marker_pl, + match_exact=match_exact, + ) + + self._buffer = Buffer(on_text_changed=self._on_text_changed) + input_window = Window( + height=LayoutDimension.exact(1), + content=BufferControl( + self._buffer, + [ + AfterInput(self._generate_after_input), + BeforeInput(self._generate_before_input), + ], + lexer=SimpleLexer("class:input"), + ), + ) + + choice_height_dimmension = lambda: Dimension( + max=self._dimmension_max_height, + preferred=self._dimmension_height, + min=self.content_control._height if self.content_control._height > 0 else 1, + ) + self.choice_window = Window( + content=self.content_control, + height=choice_height_dimmension, + dont_extend_height=True, + ) + + main_content_window = HSplit([input_window, self.choice_window]) + if self._border: + main_content_window = Frame(main_content_window) + self._layout = Layout( + FloatContainer( + content=HSplit( + [ + MessageWindow( + message=self._get_prompt_message, + filter=True, + wrap_lines=self._wrap_lines, + show_cursor=True, + ), + ConditionalContainer( + main_content_window, + filter=~IsDone(), + ), + ConditionalContainer( + Window(content=DummyControl()), + filter=~IsDone() & self._is_displaying_long_instruction, + ), + InstructionWindow( + message=self._long_instruction, + filter=~IsDone() & self._is_displaying_long_instruction, + wrap_lines=self._wrap_lines, + ), + ], + ), + floats=[ + ValidationFloat( + invalid_message=self._get_error_message, + filter=self._is_invalid & ~IsDone(), + wrap_lines=self._wrap_lines, + left=0, + bottom=self._validation_window_bottom_offset, + ), + ], + ) + ) + self._layout.focus(input_window) + + self._application = Application( + layout=self._layout, + style=self._style, + key_bindings=self._kb, + editing_mode=self._editing_mode, + after_render=self._after_render, + ) + + def _toggle_exact(self, _, value: Optional[bool] = None) -> None: + """Toggle matching algorithm. + + Switch between fzy fuzzy match or sub-string exact match. + + Args: + value: Specify the value to toggle. + """ + if value is not None: + self.content_control._scorer = fzy_scorer if not value else substr_scorer + else: + self.content_control._scorer = ( + fzy_scorer + if self.content_control._scorer == substr_scorer + else substr_scorer + ) + + def _on_rendered(self, _) -> None: + """Render callable choices and set the buffer default text. + + Setting buffer default text has to be after application is rendered and choice are loaded, + because `self._filter_choices` will use the event loop from `Application`. + """ + if self._default: + default_text = str(self._default) + self._buffer.text = default_text + self._buffer.cursor_position = len(default_text) + + def _handle_toggle_all(self, _, value: Optional[bool] = None) -> None: + """Toggle all choice `enabled` status. + + Args: + value: Specify the value to toggle. + """ + if not self._multiselect: + return + for choice in self.content_control._filtered_choices: + raw_choice = self.content_control.choices[choice["index"]] + if isinstance(raw_choice["value"], Separator): + continue + raw_choice["enabled"] = value if value else not raw_choice["enabled"] + + def _generate_after_input(self) -> List[Tuple[str, str]]: + """Virtual text displayed after the user input.""" + display_message = [] + if self._info: + display_message.append(("", " ")) + display_message.append( + ( + "class:fuzzy_info", + f"{self.content_control.choice_count}/{len(self.content_control.choices)}", + ) + ) + if self._multiselect: + display_message.append( + ("class:fuzzy_info", f" ({len(self.selected_choices)})") + ) + if self.content_control._scorer == substr_scorer: + display_message.append(("class:fuzzy_info", self._exact_symbol)) + return display_message + + def _generate_before_input(self) -> List[Tuple[str, str]]: + """Display prompt symbol as virtual text before user input.""" + display_message = [] + display_message.append(("class:fuzzy_prompt", "%s " % self._prompt)) + return display_message + + def _filter_callback(self, task): + """Redraw `self._application` when the filter task is finished.""" + if task.cancelled(): + return + self.content_control._filtered_choices = task.result() + self._application.invalidate() + + def _calculate_wait_time(self) -> float: + """Calculate wait time to smoother the application on big data set. + + Using digit of the choices lengeth to get wait time. + For digit greater than 6, using formula 2^(digit - 5) * 0.3 to increase the wait_time. + + Returns: + Desired wait time before running the filter. + """ + wait_table = { + 2: 0.05, + 3: 0.1, + 4: 0.2, + 5: 0.3, + } + digit = 1 + if len(self.content_control.choices) > 0: + digit = int(math.log10(len(self.content_control.choices))) + 1 + + if digit < 2: + return 0.0 + if digit in wait_table: + return wait_table[digit] + return wait_table[5] * (2 ** (digit - 5)) + + def _on_text_changed(self, _) -> None: + """Handle buffer text change event. + + 1. Check if there is current task running. + 2. Cancel if already has task, increase wait_time + 3. Create a filtered_choice task in asyncio event loop + 4. Add callback + + 1. Run a new filter on all choices. + 2. Re-calculate current selected_choice_index + if it exceeds the total filtered_choice. + 3. Avoid selected_choice_index less than zero, + this fix the issue of cursor lose when: + choice -> empty choice -> choice + + Don't need to create or check asyncio event loop, `prompt_toolkit` + application already has a event loop running. + """ + if self._invalid: + self._invalid = False + wait_time = self._calculate_wait_time() + if self._task and not self._task.done(): + self._task.cancel() + self._task = asyncio.create_task( + self.content_control._filter_choices(wait_time) + ) + self._task.add_done_callback(self._filter_callback) + + def _handle_toggle_choice(self, _) -> None: + """Handle tab event, alter the `selected` state of the choice.""" + if not self._multiselect: + return + current_selected_index = self.content_control.selection["index"] + self.content_control.choices[current_selected_index][ + "enabled" + ] = not self.content_control.choices[current_selected_index]["enabled"] + + def _handle_enter(self, event: "KeyPressEvent") -> None: + """Handle enter event. + + Validate the result first. + + In multiselect scenario, if no TAB is entered, then capture the current + highlighted choice and return the value in a list. + Otherwise, return all TAB choices as a list. + + In normal scenario, reutrn the current highlighted choice. + + If current UI contains no choice due to filter, return None. + """ + try: + fake_document = FakeDocument(self.result_value) + self._validator.validate(fake_document) # type: ignore + if self._multiselect: + self.status["answered"] = True + if not self.selected_choices: + self.status["result"] = [self.content_control.selection["name"]] + event.app.exit(result=[self.content_control.selection["value"]]) + else: + self.status["result"] = self.result_name + event.app.exit(result=self.result_value) + else: + self.status["answered"] = True + self.status["result"] = self.content_control.selection["name"] + event.app.exit(result=self.content_control.selection["value"]) + except ValidationError as e: + self._set_error(str(e)) + except IndexError: + self.status["answered"] = True + self.status["result"] = None if not self._multiselect else [] + event.app.exit(result=None if not self._multiselect else []) + + @property + def content_control(self) -> InquirerPyFuzzyControl: + """InquirerPyFuzzyControl: Override for type-hinting.""" + return cast(InquirerPyFuzzyControl, super().content_control) + + @content_control.setter + def content_control(self, value: InquirerPyFuzzyControl) -> None: + self._content_control = value + + def _get_current_text(self) -> str: + """Get current input buffer text.""" + return self._buffer.text diff --git a/voxvera/vendor/InquirerPy/prompts/input.py b/voxvera/vendor/InquirerPy/prompts/input.py new file mode 100644 index 0000000..1c79027 --- /dev/null +++ b/voxvera/vendor/InquirerPy/prompts/input.py @@ -0,0 +1,253 @@ +"""Module contains the class to create an input prompt.""" +from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple, Union + +from prompt_toolkit.buffer import ValidationState +from prompt_toolkit.completion import NestedCompleter +from prompt_toolkit.completion.base import Completer +from prompt_toolkit.filters.base import Condition +from prompt_toolkit.keys import Keys +from prompt_toolkit.lexers import SimpleLexer +from prompt_toolkit.shortcuts.prompt import CompleteStyle, PromptSession +from prompt_toolkit.validation import ValidationError + +from InquirerPy.base import BaseSimplePrompt +from InquirerPy.enum import INQUIRERPY_POINTER_SEQUENCE +from InquirerPy.exceptions import InvalidArgument +from InquirerPy.utils import ( + InquirerPyDefault, + InquirerPyKeybindings, + InquirerPyMessage, + InquirerPySessionResult, + InquirerPyStyle, + InquirerPyValidate, +) + +if TYPE_CHECKING: + from prompt_toolkit.input.base import Input + from prompt_toolkit.key_binding.key_processor import KeyPressEvent + from prompt_toolkit.output.base import Output + +__all__ = ["InputPrompt"] + + +class InputPrompt(BaseSimplePrompt): + """Create a text prompt that accepts user input. + + A wrapper class around :class:`~prompt_toolkit.shortcuts.PromptSession`. + + Args: + message: The question to ask the user. + Refer to :ref:`pages/dynamic:message` documentation for more details. + style: An :class:`InquirerPyStyle` instance. + Refer to :ref:`Style ` documentation for more details. + vi_mode: Use vim keybinding for the prompt. + Refer to :ref:`pages/kb:Keybindings` documentation for more details. + default: Set the default text value of the prompt. + Refer to :ref:`pages/dynamic:default` documentation for more details. + qmark: Question mark symbol. Custom symbol that will be displayed infront of the question before its answered. + amark: Answer mark symbol. Custom symbol that will be displayed infront of the question after its answered. + instruction: Short instruction to display next to the question. + long_instruction: Long instructions to display at the bottom of the prompt. + completer: Add auto completion to the prompt. + Refer to :ref:`pages/prompts/input:Auto Completion` documentation for more details. + multicolumn_complete: Change the auto-completion UI to a multi column display. + multiline: Enable multiline edit. While multiline edit is active, pressing `enter` won't complete the answer. + and will create a new line. Use `esc` followd by `enter` to complete the question. + validate: Add validation to user input. + Refer to :ref:`pages/validator:Validator` documentation for more details. + invalid_message: Error message to display when user input is invalid. + Refer to :ref:`pages/validator:Validator` documentation for more details. + transformer: A function which performs additional transformation on the value that gets printed to the terminal. + Different than `filter` parameter, this is only visual effect and won’t affect the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`. + Refer to :ref:`pages/dynamic:transformer` documentation for more details. + filter: A function which performs additional transformation on the result. + This affects the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`. + Refer to :ref:`pages/dynamic:filter` documentation for more details. + keybindings: Customise the builtin keybindings. + Refer to :ref:`pages/kb:Keybindings` for more details. + wrap_lines: Soft wrap question lines when question exceeds the terminal width. + raise_keyboard_interrupt: Raise the :class:`KeyboardInterrupt` exception when `ctrl-c` is pressed. If false, the result + will be `None` and the question is skiped. + is_password: Used internally for :class:`~InquirerPy.prompts.secret.SecretPrompt`. + mandatory: Indicate if the prompt is mandatory. If True, then the question cannot be skipped. + mandatory_message: Error message to show when user attempts to skip mandatory prompt. + session_result: Used internally for :ref:`index:Classic Syntax (PyInquirer)`. + input: Used internally and will be removed in future updates. + output: Used internally and will be removed in future updates. + + Examples: + >>> from InquirerPy import inquirer + >>> result = inquirer.text(message="Enter your name:").execute() + >>> print(f"Name: {result}") + Name: Michael + """ + + def __init__( + self, + message: InquirerPyMessage, + style: Optional[InquirerPyStyle] = None, + vi_mode: bool = False, + default: InquirerPyDefault = "", + qmark: str = "?", + amark: str = "?", + instruction: str = "", + long_instruction: str = "", + completer: Optional[Union[Dict[str, Optional[str]], "Completer"]] = None, + multicolumn_complete: bool = False, + multiline: bool = False, + validate: Optional[InquirerPyValidate] = None, + invalid_message: str = "Invalid input", + transformer: Optional[Callable[[str], Any]] = None, + filter: Optional[Callable[[str], Any]] = None, + keybindings: Optional[InquirerPyKeybindings] = None, + wrap_lines: bool = True, + raise_keyboard_interrupt: bool = True, + is_password: bool = False, + mandatory: bool = True, + mandatory_message: str = "Mandatory prompt", + session_result: Optional[InquirerPySessionResult] = None, + input: Optional["Input"] = None, + output: Optional["Output"] = None, + ) -> None: + super().__init__( + message, + style, + vi_mode=vi_mode, + qmark=qmark, + amark=amark, + instruction=instruction, + validate=validate, + invalid_message=invalid_message, + transformer=transformer, + filter=filter, + session_result=session_result, + default=default, + wrap_lines=wrap_lines, + mandatory=mandatory, + mandatory_message=mandatory_message, + raise_keyboard_interrupt=raise_keyboard_interrupt, + ) + if not isinstance(self._default, str): + raise InvalidArgument( + f"{type(self).__name__} argument 'default' should be type of str" + ) + self._completer = None + if isinstance(completer, dict): + self._completer = NestedCompleter.from_nested_dict(completer) + elif isinstance(completer, Completer): + self._completer = completer + self._multiline = multiline + self._complete_style = ( + CompleteStyle.COLUMN + if not multicolumn_complete + else CompleteStyle.MULTI_COLUMN + ) + + @Condition + def is_multiline(): + return self._multiline + + if not keybindings: + keybindings = {} + self.kb_maps = { + "answer": [ + {"key": Keys.Enter, "filter": ~is_multiline}, + {"key": [Keys.Escape, Keys.Enter], "filter": is_multiline}, + ], + "completion": [{"key": "c-space"}], + **keybindings, + } + self.kb_func_lookup = {"completion": [{"func": self._handle_completion}]} + self._keybinding_factory() + + self._session = PromptSession( + message=self._get_prompt_message, + key_bindings=self._kb, + style=self._style, + completer=self._completer, + validator=self._validator, + validate_while_typing=False, + input=input, + output=output, + editing_mode=self._editing_mode, + lexer=SimpleLexer(self._lexer), + is_password=is_password, + multiline=self._multiline, + complete_style=self._complete_style, + wrap_lines=wrap_lines, + bottom_toolbar=[("class:long_instruction", long_instruction)] + if long_instruction + else None, + ) + + def _set_error(self, message: str) -> None: + self._session.default_buffer.validation_state = ValidationState.INVALID + self._session.default_buffer.validation_error = ValidationError(message=message) + + def _handle_enter(self, event: "KeyPressEvent") -> None: + try: + self._session.validator.validate(self._session.default_buffer) # type: ignore + except ValidationError: + self._session.default_buffer.validate_and_handle() + else: + self.status["answered"] = True + self.status["result"] = self._session.default_buffer.text + self._session.default_buffer.text = "" + event.app.exit(result=self.status["result"]) + + def _handle_completion(self, event) -> None: + if self._completer is None: + return + buff = event.app.current_buffer + if buff.complete_state: + buff.complete_next() + else: + buff.start_completion(select_first=False) + + def _get_prompt_message( + self, + pre_answer: Optional[Tuple[str, str]] = None, + post_answer: Optional[Tuple[str, str]] = None, + ) -> List[Tuple[str, str]]: + """Get message to display infront of the input buffer. + + Args: + pre_answer: The formatted text to display before answering the question. + post_answer: The formatted text to display after answering the question. + + Returns: + Formatted text in list of tuple format. + """ + if not pre_answer: + if self._multiline and not self._instruction: + pre_answer = ("class:instruction", " ESC + Enter to finish input") + else: + pre_answer = ( + "class:instruction", + " %s " % self.instruction if self.instruction else " ", + ) + if not post_answer: + if self._multiline and self.status["result"]: + lines = self.status["result"].split("\n") + if len(lines) > 1: + number_of_chars = len("".join(lines[1:])) + lines[0] += "...[%s char%s]" % ( + number_of_chars, + "s" if number_of_chars > 1 else "", + ) + post_answer = ("class:answer", " %s" % lines[0]) + else: + post_answer = ("class:answer", " %s" % self.status["result"]) + + formatted_message = super()._get_prompt_message(pre_answer, post_answer) + if not self.status["answered"] and self._multiline: + formatted_message.append( + ("class:questionmark", "\n%s " % INQUIRERPY_POINTER_SEQUENCE) + ) + return formatted_message + + def _run(self) -> str: + return self._session.prompt(default=self._default) + + async def _run_async(self) -> Any: + return await self._session.prompt_async(default=self._default) diff --git a/voxvera/vendor/InquirerPy/prompts/list.py b/voxvera/vendor/InquirerPy/prompts/list.py new file mode 100644 index 0000000..acbda33 --- /dev/null +++ b/voxvera/vendor/InquirerPy/prompts/list.py @@ -0,0 +1,368 @@ +"""Module contains the class to create a list prompt.""" +import shutil +from typing import TYPE_CHECKING, Any, Callable, List, Optional, Tuple, Union + +from prompt_toolkit.application.application import Application +from prompt_toolkit.filters.cli import IsDone +from prompt_toolkit.layout.containers import ( + ConditionalContainer, + FloatContainer, + HSplit, + Window, +) +from prompt_toolkit.layout.controls import DummyControl +from prompt_toolkit.layout.dimension import Dimension +from prompt_toolkit.layout.layout import Layout +from prompt_toolkit.validation import ValidationError +from prompt_toolkit.widgets.base import Frame + +from InquirerPy.base import InquirerPyUIListControl +from InquirerPy.base.complex import FakeDocument +from InquirerPy.base.list import BaseListPrompt +from InquirerPy.containers.instruction import InstructionWindow +from InquirerPy.containers.message import MessageWindow +from InquirerPy.containers.validation import ValidationFloat +from InquirerPy.enum import INQUIRERPY_POINTER_SEQUENCE +from InquirerPy.separator import Separator +from InquirerPy.utils import ( + InquirerPyDefault, + InquirerPyKeybindings, + InquirerPyListChoices, + InquirerPyMessage, + InquirerPySessionResult, + InquirerPyStyle, + InquirerPyValidate, + calculate_height, +) + +if TYPE_CHECKING: + from prompt_toolkit.key_binding.key_processor import KeyPressEvent + +__all__ = ["ListPrompt"] + + +class InquirerPyListControl(InquirerPyUIListControl): + """An :class:`~prompt_toolkit.layout.UIControl` class that displays a list of choices. + + Reference the parameter definition in :class:`.ListPrompt`. + """ + + def __init__( + self, + choices: InquirerPyListChoices, + default: Any, + pointer: str, + marker: str, + session_result: Optional[InquirerPySessionResult], + multiselect: bool, + marker_pl: str, + ) -> None: + self._pointer: str = pointer + self._marker: str = marker + self._marker_pl: str = marker_pl + super().__init__( + choices=choices, + default=default, + session_result=session_result, + multiselect=multiselect, + ) + + def _get_hover_text(self, choice) -> List[Tuple[str, str]]: + display_choices = [] + display_choices.append(("class:pointer", self._pointer)) + display_choices.append( + ( + "class:marker", + self._marker if choice["enabled"] else self._marker_pl, + ) + ) + display_choices.append(("[SetCursorPosition]", "")) + display_choices.append(("class:pointer", choice["name"])) + return display_choices + + def _get_normal_text(self, choice) -> List[Tuple[str, str]]: + display_choices = [] + display_choices.append(("", len(self._pointer) * " ")) + display_choices.append( + ( + "class:marker", + self._marker if choice["enabled"] else self._marker_pl, + ) + ) + if not isinstance(choice["value"], Separator): + display_choices.append(("", choice["name"])) + else: + display_choices.append(("class:separator", choice["name"])) + return display_choices + + +class ListPrompt(BaseListPrompt): + """Create a prompt that displays a list of choices to select. + + A wrapper class around :class:`~prompt_toolkit.application.Application`. + + Args: + message: The question to ask the user. + Refer to :ref:`pages/dynamic:message` documentation for more details. + choices: List of choices to display and select. + Refer to :ref:`pages/dynamic:choices` documentation for more details. + style: An :class:`InquirerPyStyle` instance. + Refer to :ref:`Style ` documentation for more details. + vi_mode: Use vim keybinding for the prompt. + Refer to :ref:`pages/kb:Keybindings` documentation for more details. + default: Set the default value of the prompt. + This will be used to determine which choice is highlighted (current selection), + The default value should be the value of one of the choices. + Refer to :ref:`pages/dynamic:default` documentation for more details. + qmark: Question mark symbol. Custom symbol that will be displayed infront of the question before its answered. + amark: Answer mark symbol. Custom symbol that will be displayed infront of the question after its answered. + pointer: Pointer symbol. Customer symbol that will be used to indicate the current choice selection. + instruction: Short instruction to display next to the question. + long_instruction: Long instructions to display at the bottom of the prompt. + validate: Add validation to user input. + The main use case for this prompt would be when `multiselect` is True, you can enforce a min/max selection. + Refer to :ref:`pages/validator:Validator` documentation for more details. + invalid_message: Error message to display when user input is invalid. + Refer to :ref:`pages/validator:Validator` documentation for more details. + transformer: A function which performs additional transformation on the value that gets printed to the terminal. + Different than `filter` parameter, this is only visual effect and won’t affect the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`. + Refer to :ref:`pages/dynamic:transformer` documentation for more details. + filter: A function which performs additional transformation on the result. + This affects the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`. + Refer to :ref:`pages/dynamic:filter` documentation for more details. + height: Preferred height of the prompt. + Refer to :ref:`pages/height:Height` documentation for more details. + max_height: Max height of the prompt. + Refer to :ref:`pages/height:Height` documentation for more details. + multiselect: Enable multi-selection on choices. + You can use `validate` parameter to control min/max selections. + Setting to True will also change the result from a single value to a list of values. + marker: Marker Symbol. Custom symbol to indicate if a choice is selected. + This will take effects when `multiselect` is True. + marker_pl: Marker place holder when the choice is not selected. + This is empty space by default. + border: Create border around the choice window. + keybindings: Customise the builtin keybindings. + Refer to :ref:`pages/kb:Keybindings` for more details. + show_cursor: Display cursor at the end of the prompt. + Set to False to hide the cursor. + cycle: Return to top item if hit bottom during navigation or vice versa. + wrap_lines: Soft wrap question lines when question exceeds the terminal width. + raise_keyboard_interrupt: Raise the :class:`KeyboardInterrupt` exception when `ctrl-c` is pressed. If false, the result + will be `None` and the question is skiped. + mandatory: Indicate if the prompt is mandatory. If True, then the question cannot be skipped. + mandatory_message: Error message to show when user attempts to skip mandatory prompt. + session_result: Used internally for :ref:`index:Classic Syntax (PyInquirer)`. + + Examples: + >>> from InquirerPy import inquirer + >>> result = inquirer.select(message="Select one:", choices=[1, 2, 3]).execute() + >>> print(result) + 1 + """ + + def __init__( + self, + message: InquirerPyMessage, + choices: InquirerPyListChoices, + default: InquirerPyDefault = None, + style: Optional[InquirerPyStyle] = None, + vi_mode: bool = False, + qmark: str = "?", + amark: str = "?", + pointer: str = INQUIRERPY_POINTER_SEQUENCE, + instruction: str = "", + long_instruction: str = "", + transformer: Optional[Callable[[Any], Any]] = None, + filter: Optional[Callable[[Any], Any]] = None, + height: Optional[Union[int, str]] = None, + max_height: Optional[Union[int, str]] = None, + multiselect: bool = False, + marker: str = INQUIRERPY_POINTER_SEQUENCE, + marker_pl: str = " ", + border: bool = False, + validate: Optional[InquirerPyValidate] = None, + invalid_message: str = "Invalid input", + keybindings: Optional[InquirerPyKeybindings] = None, + show_cursor: bool = True, + cycle: bool = True, + wrap_lines: bool = True, + raise_keyboard_interrupt: bool = True, + mandatory: bool = True, + mandatory_message: str = "Mandatory prompt", + session_result: Optional[InquirerPySessionResult] = None, + ) -> None: + if not hasattr(self, "_content_control"): + self.content_control = InquirerPyListControl( + choices=choices, + default=default, + pointer=pointer, + marker=marker, + session_result=session_result, + multiselect=multiselect, + marker_pl=marker_pl, + ) + super().__init__( + message=message, + style=style, + border=border, + vi_mode=vi_mode, + qmark=qmark, + amark=amark, + instruction=instruction, + long_instruction=long_instruction, + transformer=transformer, + filter=filter, + validate=validate, + invalid_message=invalid_message, + multiselect=multiselect, + keybindings=keybindings, + cycle=cycle, + wrap_lines=wrap_lines, + raise_keyboard_interrupt=raise_keyboard_interrupt, + mandatory=mandatory, + mandatory_message=mandatory_message, + session_result=session_result, + ) + self._show_cursor = show_cursor + self._dimmension_height, self._dimmension_max_height = calculate_height( + height, max_height, height_offset=self.height_offset + ) + main_content_window = Window( + content=self.content_control, + height=Dimension( + max=self._dimmension_max_height, + preferred=self._dimmension_height, + ), + dont_extend_height=True, + ) + + if self._border: + main_content_window = Frame(main_content_window) + + self._layout = FloatContainer( + content=HSplit( + [ + MessageWindow( + message=self._get_prompt_message_with_cursor + if self._show_cursor + else self._get_prompt_message, + filter=True, + wrap_lines=self._wrap_lines, + show_cursor=self._show_cursor, + ), + ConditionalContainer(main_content_window, filter=~IsDone()), + ConditionalContainer( + Window(content=DummyControl()), + filter=~IsDone() & self._is_displaying_long_instruction, + ), + InstructionWindow( + message=self._long_instruction, + filter=~IsDone() & self._is_displaying_long_instruction, + wrap_lines=self._wrap_lines, + ), + ] + ), + floats=[ + ValidationFloat( + invalid_message=self._get_error_message, + filter=self._is_invalid & ~IsDone(), + wrap_lines=self._wrap_lines, + left=0, + bottom=self._validation_window_bottom_offset, + ), + ], + ) + + self.application = Application( + layout=Layout(self._layout), + style=self._style, + key_bindings=self._kb, + after_render=self._after_render, + ) + + def _get_prompt_message_with_cursor(self) -> List[Tuple[str, str]]: + """Obtain the prompt message to display and display cursor behind the message. + + This ensures that cursor is always at the end of the window. + """ + message = self._get_prompt_message() + message.append(("[SetCursorPosition]", "")) + message.append(("", " ")) # [SetCursorPosition] require char behind it + return message + + def _handle_toggle_choice(self, _) -> None: + """Toggle the `enabled` status of the choice.""" + if not self._multiselect: + return + self.content_control.selection["enabled"] = not self.content_control.selection[ + "enabled" + ] + + def _handle_toggle_all(self, _, value: Optional[bool] = None) -> None: + """Toggle all choice `enabled` status. + + Args: + value: Sepcify a value to toggle. + """ + if not self._multiselect: + return + for choice in self.content_control.choices: + if isinstance(choice["value"], Separator): + continue + choice["enabled"] = value if value else not choice["enabled"] + + def _handle_up(self, event) -> None: + """Handle the event when user attempt to move up.""" + while True: + cap = super()._handle_up(event) + if not isinstance(self.content_control.selection["value"], Separator): + break + else: + if cap and not self._cycle: + self._handle_down(event) + break + + def _handle_down(self, event) -> None: + """Handle the event when user attempt to move down.""" + while True: + cap = super()._handle_down(event) + if not isinstance(self.content_control.selection["value"], Separator): + break + else: + if cap and not self._cycle: + self._handle_up(event) + break + + def _handle_enter(self, event: "KeyPressEvent") -> None: + """Handle the event when user hit `enter` key. + + 1. Set the prompt state to answered. + 2. Set the result to the name of the selected choices. + 3. Exit the app with the value of the selected choices. + + In multiselect scenario, if nothing is selected, return the current highlighted choice. + """ + try: + fake_document = FakeDocument(self.result_value) + self._validator.validate(fake_document) # type: ignore + except ValidationError as e: + self._set_error(str(e)) + else: + self.status["answered"] = True + if self._multiselect and not self.selected_choices: + self.status["result"] = [self.content_control.selection["name"]] + event.app.exit(result=[self.content_control.selection["value"]]) + else: + self.status["result"] = self.result_name + event.app.exit(result=self.result_value) + + @property + def extra_message_line_count(self) -> int: + """int: Get extra lines created for message caused by line wrapping. + + Overriding it to count the cursor as well. + """ + cursor_offset = -1 if not self._show_cursor else 0 + term_width, _ = shutil.get_terminal_size() + return (self.total_message_length + cursor_offset) // term_width diff --git a/voxvera/vendor/InquirerPy/prompts/number.py b/voxvera/vendor/InquirerPy/prompts/number.py new file mode 100644 index 0000000..5efce26 --- /dev/null +++ b/voxvera/vendor/InquirerPy/prompts/number.py @@ -0,0 +1,616 @@ +"""Module contains the class to create a number prompt.""" +import re +from decimal import Decimal +from typing import TYPE_CHECKING, Any, Callable, Optional, Tuple, Union, cast + +from prompt_toolkit.application.application import Application +from prompt_toolkit.buffer import Buffer +from prompt_toolkit.filters.base import Condition +from prompt_toolkit.filters.cli import IsDone +from prompt_toolkit.keys import Keys +from prompt_toolkit.layout.containers import ( + ConditionalContainer, + HorizontalAlign, + HSplit, + VSplit, + Window, +) +from prompt_toolkit.layout.controls import ( + BufferControl, + DummyControl, + FormattedTextControl, +) +from prompt_toolkit.layout.dimension import Dimension, LayoutDimension +from prompt_toolkit.layout.layout import Layout +from prompt_toolkit.lexers.base import SimpleLexer +from prompt_toolkit.validation import ValidationError + +from InquirerPy.base.complex import BaseComplexPrompt, FakeDocument +from InquirerPy.containers.instruction import InstructionWindow +from InquirerPy.containers.validation import ValidationWindow +from InquirerPy.enum import INQUIRERPY_QMARK_SEQUENCE +from InquirerPy.exceptions import InvalidArgument +from InquirerPy.utils import ( + InquirerPyDefault, + InquirerPyKeybindings, + InquirerPyMessage, + InquirerPySessionResult, + InquirerPyStyle, + InquirerPyValidate, +) + +if TYPE_CHECKING: + from prompt_toolkit.key_binding.key_processor import KeyPressEvent + +__all__ = ["NumberPrompt"] + + +class NumberPrompt(BaseComplexPrompt): + """Create a input prompts that only takes number as input. + + A wrapper class around :class:`~prompt_toolkit.application.Application`. + + Args: + message: The question to ask the user. + Refer to :ref:`pages/dynamic:message` documentation for more details. + style: An :class:`InquirerPyStyle` instance. + Refer to :ref:`Style ` documentation for more details. + vi_mode: Use vim keybinding for the prompt. + Refer to :ref:`pages/kb:Keybindings` documentation for more details. + default: Set the default value of the prompt. + You can enter either the floating value or integer value as the default. + Refer to :ref:`pages/dynamic:default` documentation for more details. + float_allowed: Allow decimal input. This will change the prompt to have 2 input buffer, one for the + whole value and one for the integral value. + min_allowed: Set the minimum value of the prompt. When the input value goes below this value, it + will automatically reset to this value. + max_allowed: Set the maximum value of the prompt. When the inptu value goes above this value, it + will automatically reset to this value. + qmark: Question mark symbol. Custom symbol that will be displayed infront of the question before its answered. + amark: Answer mark symbol. Custom symbol that will be displayed infront of the question after its answered. + decimal_symbol: Decimal point symbol. Custom symbol to display as the decimal point. + replace_mode: Start each input buffer in replace mode if default value is 0. + When typing, it will replace the 0 with the new value. The replace mode will be disabled once the value + is changed. + instruction: Short instruction to display next to the question. + long_instruction: Long instructions to display at the bottom of the prompt. + validate: Add validation to user input. + Refer to :ref:`pages/validator:Validator` documentation for more details. + invalid_message: Error message to display when user input is invalid. + Refer to :ref:`pages/validator:Validator` documentation for more details. + invalid_message: Error message to display when user input is invalid. + Refer to :ref:`pages/validator:Validator` documentation for more details. + transformer: A function which performs additional transformation on the value that gets printed to the terminal. + Different than `filter` parameter, this is only visual effect and won’t affect the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`. + Refer to :ref:`pages/dynamic:transformer` documentation for more details. + filter: A function which performs additional transformation on the result. + This affects the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`. + Refer to :ref:`pages/dynamic:filter` documentation for more details. + keybindings: Customise the builtin keybindings. + Refer to :ref:`pages/kb:Keybindings` for more details. + wrap_lines: Soft wrap question lines when question exceeds the terminal width. + raise_keyboard_interrupt: Raise the :class:`KeyboardInterrupt` exception when `ctrl-c` is pressed. If false, the result + will be `None` and the question is skiped. + mandatory: Indicate if the prompt is mandatory. If True, then the question cannot be skipped. + mandatory_message: Error message to show when user attempts to skip mandatory prompt. + session_result: Used internally for :ref:`index:Classic Syntax (PyInquirer)`. + + Examples: + >>> from InquirerPy import inquirer + >>> result = inquirer.number(message="Enter number:").execute() + >>> print(result) + 0 + """ + + def __init__( + self, + message: InquirerPyMessage, + style: Optional[InquirerPyStyle] = None, + vi_mode: bool = False, + default: InquirerPyDefault = 0, + float_allowed: bool = False, + max_allowed: Optional[Union[int, float]] = None, + min_allowed: Optional[Union[int, float]] = None, + decimal_symbol: str = ". ", + replace_mode: bool = False, + qmark: str = INQUIRERPY_QMARK_SEQUENCE, + amark: str = "?", + instruction: str = "", + long_instruction: str = "", + validate: Optional[InquirerPyValidate] = None, + invalid_message: str = "Invalid input", + transformer: Optional[Callable[[str], Any]] = None, + filter: Optional[Callable[[str], Any]] = None, + keybindings: Optional[InquirerPyKeybindings] = None, + wrap_lines: bool = True, + raise_keyboard_interrupt: bool = True, + mandatory: bool = True, + mandatory_message: str = "Mandatory prompt", + session_result: Optional[InquirerPySessionResult] = None, + ) -> None: + super().__init__( + message=message, + style=style, + vi_mode=vi_mode, + qmark=qmark, + amark=amark, + transformer=transformer, + filter=filter, + invalid_message=invalid_message, + validate=validate, + instruction=instruction, + long_instruction=long_instruction, + wrap_lines=wrap_lines, + raise_keyboard_interrupt=raise_keyboard_interrupt, + mandatory=mandatory, + mandatory_message=mandatory_message, + session_result=session_result, + ) + + self._float = float_allowed + self._is_float = Condition(lambda: self._float) + self._max = max_allowed + self._min = min_allowed + self._value_error_message = "Remove any non-integer value" + self._decimal_symbol = decimal_symbol + self._whole_replace = False + self._integral_replace = False + self._replace_mode = replace_mode + + self._leading_zero_pattern = re.compile(r"^(0*)[0-9]+.*") + self._sn_pattern = re.compile(r"^.*E-.*") + self._no_default = False + + if default is None: + default = 0 + self._no_default = True + + if isinstance(default, Callable): + default = cast(Callable, default)(session_result) + if self._float: + default = Decimal(str(float(cast(int, default)))) + if self._float: + if not isinstance(default, float) and not isinstance(default, Decimal): + raise InvalidArgument( + f"{type(self).__name__} argument 'default' should return type of float or Decimal" + ) + elif not isinstance(default, int): + raise InvalidArgument( + f"{type(self).__name__} argument 'default' should return type of int" + ) + self._default = default + + if keybindings is None: + keybindings = {} + self.kb_maps = { + "down": [ + {"key": "down"}, + {"key": "c-n", "filter": ~self._is_vim_edit}, + {"key": "j", "filter": self._is_vim_edit}, + ], + "up": [ + {"key": "up"}, + {"key": "c-p", "filter": ~self._is_vim_edit}, + {"key": "k", "filter": self._is_vim_edit}, + ], + "left": [ + {"key": "left"}, + {"key": "c-b", "filter": ~self._is_vim_edit}, + {"key": "h", "filter": self._is_vim_edit}, + ], + "right": [ + {"key": "right"}, + {"key": "c-f", "filter": ~self._is_vim_edit}, + {"key": "l", "filter": self._is_vim_edit}, + ], + "dot": [{"key": "."}], + "focus": [{"key": Keys.Tab}, {"key": "s-tab"}], + "input": [{"key": str(i)} for i in range(10)], + "negative_toggle": [{"key": "-"}], + **keybindings, + } + self.kb_func_lookup = { + "down": [{"func": self._handle_down}], + "up": [{"func": self._handle_up}], + "left": [{"func": self._handle_left}], + "right": [{"func": self._handle_right}], + "focus": [{"func": self._handle_focus}], + "input": [{"func": self._handle_input}], + "negative_toggle": [{"func": self._handle_negative_toggle}], + "dot": [{"func": self._handle_dot}], + } + + @self.register_kb(Keys.Any) + def _(_): + pass + + self._whole_width = 1 + self._whole_buffer = Buffer( + on_text_changed=self._on_whole_text_change, + on_cursor_position_changed=self._on_cursor_position_change, + ) + + self._integral_width = 1 + self._integral_buffer = Buffer( + on_text_changed=self._on_integral_text_change, + on_cursor_position_changed=self._on_cursor_position_change, + ) + + self._whole_window = Window( + height=LayoutDimension.exact(1) if not self._wrap_lines else None, + content=BufferControl( + buffer=self._whole_buffer, + lexer=SimpleLexer("class:input"), + ), + width=lambda: Dimension( + min=self._whole_width, + max=self._whole_width, + preferred=self._whole_width, + ), + dont_extend_width=True, + ) + + self._integral_window = Window( + height=LayoutDimension.exact(1) if not self._wrap_lines else None, + content=BufferControl( + buffer=self._integral_buffer, + lexer=SimpleLexer("class:input"), + ), + width=lambda: Dimension( + min=self._integral_width, + max=self._integral_width, + preferred=self._integral_width, + ), + ) + + self._layout = Layout( + HSplit( + [ + VSplit( + [ + Window( + height=LayoutDimension.exact(1) + if not self._wrap_lines + else None, + content=FormattedTextControl(self._get_prompt_message), + wrap_lines=self._wrap_lines, + dont_extend_height=True, + dont_extend_width=True, + ), + ConditionalContainer(self._whole_window, filter=~IsDone()), + ConditionalContainer( + Window( + height=LayoutDimension.exact(1) + if not self._wrap_lines + else None, + content=FormattedTextControl( + [("", self._decimal_symbol)] + ), + wrap_lines=self._wrap_lines, + dont_extend_height=True, + dont_extend_width=True, + ), + filter=self._is_float & ~IsDone(), + ), + ConditionalContainer( + self._integral_window, filter=self._is_float & ~IsDone() + ), + ], + align=HorizontalAlign.LEFT, + ), + ConditionalContainer( + Window(content=DummyControl()), + filter=~IsDone() & self._is_displaying_long_instruction, + ), + ValidationWindow( + invalid_message=self._get_error_message, + filter=self._is_invalid & ~IsDone(), + wrap_lines=self._wrap_lines, + ), + InstructionWindow( + message=self._long_instruction, + filter=~IsDone() & self._is_displaying_long_instruction, + wrap_lines=self._wrap_lines, + ), + ] + ), + ) + + self.focus = self._whole_window + + self._application = Application( + layout=self._layout, + style=self._style, + key_bindings=self._kb, + after_render=self._after_render, + editing_mode=self._editing_mode, + ) + + def _fix_sn(self, value: str) -> Tuple[str, str]: + """Fix sciencetific notation format. + + Args: + value: Value to fix. + + Returns: + A tuple of whole buffer text and integral buffer text. + """ + left, right = value.split("E-") + whole_buffer_text = "0" + integral_buffer_text = f"{(int(right) - 1) * '0'}{left.replace('.', '')}" + return whole_buffer_text, integral_buffer_text + + def _on_rendered(self, _) -> None: + """Additional processing to adjust buffer content after render.""" + if self._no_default: + return + if not self._float: + self._whole_buffer.text = str(self._default) + self._integral_buffer.text = "0" + else: + if self._sn_pattern.match(str(self._default)) is None: + whole_buffer_text, integral_buffer_text = str(self._default).split(".") + else: + whole_buffer_text, integral_buffer_text = self._fix_sn( + str(self._default) + ) + self._integral_buffer.text = integral_buffer_text + self._whole_buffer.text = whole_buffer_text + self._whole_buffer.cursor_position = len(self._whole_buffer.text) + self._integral_buffer.cursor_position = len(self._integral_buffer.text) + if self._replace_mode: + # check to start replace mode if applicable + if self._whole_buffer.text == "0": + self._whole_replace = True + self._whole_buffer.cursor_position = 0 + if self._integral_buffer.text == "0": + self._integral_replace = True + self._integral_buffer.cursor_position = 0 + + def _handle_number(self, increment: bool) -> None: + """Handle number increment and decrement. + + Additional processing to handle leading zeros in integral buffer + as well as SN notation. + + Args: + increment: Indicate if the operation should increment or decrement. + """ + if self.buffer_replace: + self.buffer_replace = False + self.focus_buffer.cursor_position += 1 + try: + leading_zeros = "" + if self.focus_buffer == self._integral_buffer: + zeros = self._leading_zero_pattern.match(self._integral_buffer.text) + if zeros is not None: + leading_zeros = zeros.group(1) + current_text_len = len(self.focus_buffer.text) + if not self.focus_buffer.text: + next_text = "0" + next_text_len = 1 + else: + if not increment: + if ( + self.focus_buffer == self._integral_buffer + and int(self.focus_buffer.text) == 0 + ): + return + next_text = leading_zeros + str(int(self.focus_buffer.text) - 1) + else: + next_text = leading_zeros + str(int(self.focus_buffer.text) + 1) + next_text_len = len(next_text) + desired_position = ( + self.focus_buffer.cursor_position + next_text_len - current_text_len + ) + self.focus_buffer.cursor_position = desired_position + self.focus_buffer.text = next_text + if self.focus_buffer.cursor_position != desired_position: + self.focus_buffer.cursor_position = desired_position + except ValueError: + self._set_error(message=self._value_error_message) + + def _handle_down(self, _) -> None: + """Handle down key press.""" + self._handle_number(increment=False) + + def _handle_up(self, _) -> None: + """Handle up key press.""" + self._handle_number(increment=True) + + def _handle_left(self, _) -> None: + """Handle left key press. + + Move to the left by one cursor position and focus the whole window + if applicable. + """ + self.buffer_replace = False + if ( + self.focus == self._integral_window + and self.focus_buffer.cursor_position == 0 + ): + self.focus = self._whole_window + else: + self.focus_buffer.cursor_position -= 1 + + def _handle_right(self, _) -> None: + """Handle right key press. + + Move to the right by one cursor position and focus the integral window + if applicable. + """ + self.buffer_replace = False + if ( + self.focus == self._whole_window + and self.focus_buffer.cursor_position == len(self.focus_buffer.text) + and self._float + ): + self.focus = self._integral_window + else: + self.focus_buffer.cursor_position += 1 + + def _handle_enter(self, event: "KeyPressEvent") -> None: + """Handle enter event and answer/close the prompt.""" + if not self._float and not self._whole_buffer.text: + result = "" + elif ( + self._float + and not self._whole_buffer.text + and not self._integral_buffer.text + ): + result = "" + else: + result = str(self.value) + + try: + fake_document = FakeDocument(result) + self._validator.validate(fake_document) # type: ignore + except ValidationError as e: + self._set_error(str(e)) + else: + self.status["answered"] = True + self.status["result"] = result + event.app.exit(result=result) + + def _handle_dot(self, _) -> None: + """Focus the integral window if `float_allowed`.""" + self._handle_focus(_, self._integral_window) + + def _handle_focus(self, _, window: Optional[Window] = None) -> None: + """Focus either the integral window or whole window.""" + if not self._float: + return + if window is not None: + self.focus = window + return + if self.focus == self._whole_window: + self.focus = self._integral_window + else: + self.focus = self._whole_window + + def _handle_input(self, event: "KeyPressEvent") -> None: + """Handle user input of numbers. + + Buffer will start as replace mode if the value is zero, once + cursor is moved or content is changed, disable replace mode. + """ + if self.buffer_replace: + self.buffer_replace = False + self.focus_buffer.text = event.key_sequence[0].data + self.focus_buffer.cursor_position += 1 + else: + self.focus_buffer.insert_text(event.key_sequence[0].data) + + def _handle_negative_toggle(self, _) -> None: + """Toggle negativity of the prompt value. + + Force the `-` sign at the start. + """ + if self._whole_buffer.text == "-": + self._whole_buffer.text = "0" + return + if self._whole_buffer.text.startswith("-"): + move_cursor = self._whole_buffer.cursor_position < len( + self._whole_buffer.text + ) + self._whole_buffer.text = self._whole_buffer.text[1:] + if move_cursor: + self._whole_buffer.cursor_position -= 1 + else: + move_cursor = self._whole_buffer.cursor_position != 0 + self._whole_buffer.text = f"-{self._whole_buffer.text}" + if move_cursor: + self._whole_buffer.cursor_position += 1 + + def _on_whole_text_change(self, buffer: Buffer) -> None: + """Handle event of text changes in buffer.""" + self._whole_width = len(buffer.text) + 1 + self._on_text_change(buffer) + + def _on_integral_text_change(self, buffer: Buffer) -> None: + """Handle event of text changes in buffer.""" + self._integral_width = len(buffer.text) + 1 + self._on_text_change(buffer) + + def _on_text_change(self, buffer: Buffer) -> None: + """Disable replace mode and fix cursor position on text changes.""" + self.buffer_replace = False + if buffer.text and buffer.text != "-": + self.value = self.value + if buffer.text.startswith("-") and buffer.cursor_position == 0: + buffer.cursor_position = 1 + + def _on_cursor_position_change(self, buffer: Buffer) -> None: + """Fix cursor position on cursor movement.""" + if self.focus_buffer.text.startswith("-") and buffer.cursor_position == 0: + buffer.cursor_position = 1 + + @property + def buffer_replace(self) -> bool: + """bool: Current buffer replace mode.""" + if self.focus_buffer == self._whole_buffer: + return self._whole_replace + else: + return self._integral_replace + + @buffer_replace.setter + def buffer_replace(self, value) -> None: + if self.focus_buffer == self._whole_buffer: + self._whole_replace = value + else: + self._integral_replace = value + + @property + def focus_buffer(self) -> Buffer: + """Buffer: Current editable buffer.""" + if self.focus == self._whole_window: + return self._whole_buffer + else: + return self._integral_buffer + + @property + def focus(self) -> Window: + """Window: Current focused window.""" + return self._focus + + @focus.setter + def focus(self, value: Window) -> None: + self._focus = value + self._layout.focus(self._focus) + + @property + def value(self) -> Union[int, float, Decimal]: + """Union[int, float]: The actual value of the prompt, combining and transforming all input buffer values.""" + try: + if not self._float: + return int(self._whole_buffer.text) + else: + return Decimal( + f"{self._whole_buffer.text}.{self._integral_buffer.text if self._integral_buffer.text else 0}" + ) + except ValueError: + self._set_error(self._value_error_message) + return self._default + + @value.setter + def value(self, value: Union[int, float, Decimal]) -> None: + if self._min is not None: + value = max( + value, self._min if not self._float else Decimal(str(self._min)) + ) + if self._max is not None: + value = min( + value, self._max if not self._float else Decimal(str(self._max)) + ) + if not self._float: + self._whole_buffer.text = str(value) + else: + if self._sn_pattern.match(str(value)) is None: + whole_buffer_text, integral_buffer_text = str(value).split(".") + else: + whole_buffer_text, integral_buffer_text = self._fix_sn(str(value)) + + if self._whole_buffer.text: + self._whole_buffer.text = whole_buffer_text + if self._integral_buffer.text: + self._integral_buffer.text = integral_buffer_text diff --git a/voxvera/vendor/InquirerPy/prompts/rawlist.py b/voxvera/vendor/InquirerPy/prompts/rawlist.py new file mode 100644 index 0000000..0de466d --- /dev/null +++ b/voxvera/vendor/InquirerPy/prompts/rawlist.py @@ -0,0 +1,285 @@ +"""Module contains the class to create a rawlist prompt.""" +from typing import Any, Callable, List, Optional, Tuple, Union + +from InquirerPy.base import InquirerPyUIListControl +from InquirerPy.enum import INQUIRERPY_POINTER_SEQUENCE +from InquirerPy.exceptions import InvalidArgument +from InquirerPy.prompts.list import ListPrompt +from InquirerPy.separator import Separator +from InquirerPy.utils import ( + InquirerPyDefault, + InquirerPyKeybindings, + InquirerPyListChoices, + InquirerPyMessage, + InquirerPySessionResult, + InquirerPyStyle, + InquirerPyValidate, +) + +__all__ = ["RawlistPrompt"] + + +class InquirerPyRawlistControl(InquirerPyUIListControl): + """An :class:`~prompt_toolkit.layout.UIControl` class that displays a list of choices. + + Reference the parameter definition in :class:`.RawlistPrompt`. + """ + + def __init__( + self, + choices: InquirerPyListChoices, + default: Any, + pointer: str, + separator: str, + marker: str, + session_result: Optional[InquirerPySessionResult], + multiselect: bool, + marker_pl: str, + ) -> None: + self._pointer = pointer + self._separator = separator + self._marker = marker + self._marker_pl = marker_pl + super().__init__( + choices=choices, + default=default, + session_result=session_result, + multiselect=multiselect, + ) + + def _format_choices(self) -> None: + separator_count = 0 + for index, choice in enumerate(self.choices): + if isinstance(choice["value"], Separator): + separator_count += 1 + continue + choice["display_index"] = index + 1 - separator_count + choice["actual_index"] = index + + if self.choices: + first_valid_choice_index = 0 + while isinstance( + self.choices[first_valid_choice_index]["value"], Separator + ): + first_valid_choice_index += 1 + if self.selected_choice_index == first_valid_choice_index: + for choice in self.choices: + if isinstance(choice["value"], Separator): + continue + if choice["display_index"] == self._default: + self.selected_choice_index = choice["actual_index"] + break + + def _get_hover_text(self, choice) -> List[Tuple[str, str]]: + display_choices = [] + display_choices.append(("class:pointer", self._pointer)) + display_choices.append( + ( + "class:marker", + self._marker if choice["enabled"] else self._marker_pl, + ) + ) + if not isinstance(choice["value"], Separator): + display_choices.append( + ( + "class:pointer", + "%s%s" % (str(choice["display_index"]), self._separator), + ) + ) + display_choices.append(("[SetCursorPosition]", "")) + display_choices.append(("class:pointer", choice["name"])) + return display_choices + + def _get_normal_text(self, choice) -> List[Tuple[str, str]]: + display_choices = [] + display_choices.append(("", len(self._pointer) * " ")) + display_choices.append( + ( + "class:marker", + self._marker if choice["enabled"] else self._marker_pl, + ) + ) + if not isinstance(choice["value"], Separator): + display_choices.append( + ("", "%s%s" % (str(choice["display_index"]), self._separator)) + ) + display_choices.append(("", choice["name"])) + else: + display_choices.append(("class:separator", choice["name"])) + return display_choices + + +class RawlistPrompt(ListPrompt): + """Create a prompt that displays a list of choices with index number as shortcuts. + + A wrapper class around :class:`~prompt_toolkit.application.Application`. + + Each choice will have an index number infront of them with keybinding created for that index number. + Enables user to use number to jump to different choices using number. + + Args: + message: The question to ask the user. + Refer to :ref:`pages/dynamic:message` documentation for more details. + choices: List of choices to display and select. + Refer to :ref:`pages/prompts/rawlist:Choices` documentation for more details. + style: An :class:`InquirerPyStyle` instance. + Refer to :ref:`Style ` documentation for more details. + vi_mode: Use vim keybinding for the prompt. + Refer to :ref:`pages/kb:Keybindings` documentation for more details. + default: Set the default value of the prompt. + This will be used to determine which choice is highlighted (current selection), + The default value should be the value of one of the choices. + For :class:`.RawlistPrompt` specifically, default value can also be value between 0-9. + Refer to :ref:`pages/dynamic:default` documentation for more details. + separator: Separator symbol. Custom symbol that will be used as a separator between the choice index number and the choices. + qmark: Question mark symbol. Custom symbol that will be displayed infront of the question before its answered. + amark: Answer mark symbol. Custom symbol that will be displayed infront of the question after its answered. + pointer: Pointer symbol. Customer symbol that will be used to indicate the current choice selection. + instruction: Short instruction to display next to the question. + long_instruction: Long instructions to display at the bottom of the prompt. + validate: Add validation to user input. + The main use case for this prompt would be when `multiselect` is True, you can enforce a min/max selection. + Refer to :ref:`pages/validator:Validator` documentation for more details. + invalid_message: Error message to display when user input is invalid. + Refer to :ref:`pages/validator:Validator` documentation for more details. + transformer: A function which performs additional transformation on the value that gets printed to the terminal. + Different than `filter` parameter, this is only visual effect and won’t affect the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`. + Refer to :ref:`pages/dynamic:transformer` documentation for more details. + filter: A function which performs additional transformation on the result. + This affects the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`. + Refer to :ref:`pages/dynamic:filter` documentation for more details. + height: Preferred height of the prompt. + Refer to :ref:`pages/height:Height` documentation for more details. + max_height: Max height of the prompt. + Refer to :ref:`pages/height:Height` documentation for more details. + multiselect: Enable multi-selection on choices. + You can use `validate` parameter to control min/max selections. + Setting to True will also change the result from a single value to a list of values. + marker: Marker Symbol. Custom symbol to indicate if a choice is selected. + This will take effects when `multiselect` is True. + marker_pl: Marker place holder when the choice is not selected. + This is empty space by default. + border: Create border around the choice window. + keybindings: Customise the builtin keybindings. + Refer to :ref:`pages/kb:Keybindings` for more details. + show_cursor: Display cursor at the end of the prompt. + Set to False to hide the cursor. + cycle: Return to top item if hit bottom during navigation or vice versa. + wrap_lines: Soft wrap question lines when question exceeds the terminal width. + raise_keyboard_interrupt: Raise the :class:`KeyboardInterrupt` exception when `ctrl-c` is pressed. If false, the result + will be `None` and the question is skiped. + mandatory: Indicate if the prompt is mandatory. If True, then the question cannot be skipped. + mandatory_message: Error message to show when user attempts to skip mandatory prompt. + session_result: Used internally for :ref:`index:Classic Syntax (PyInquirer)`. + + Examples: + >>> from InquirerPy import inquirer + >>> result = inquirer.rawlist(message="Select one:", choices=[1, 2, 3]).execute() + >>> print(result) + 1 + """ + + def __init__( + self, + message: InquirerPyMessage, + choices: InquirerPyListChoices, + default: InquirerPyDefault = None, + separator: str = ") ", + style: Optional[InquirerPyStyle] = None, + vi_mode: bool = False, + qmark: str = "?", + amark: str = "?", + pointer: str = " ", + instruction: str = "", + long_instruction: str = "", + transformer: Optional[Callable[[Any], Any]] = None, + filter: Optional[Callable[[Any], Any]] = None, + height: Optional[Union[int, str]] = None, + max_height: Optional[Union[int, str]] = None, + multiselect: bool = False, + marker: str = INQUIRERPY_POINTER_SEQUENCE, + marker_pl: str = " ", + border: bool = False, + validate: Optional[InquirerPyValidate] = None, + invalid_message: str = "Invalid input", + keybindings: Optional[InquirerPyKeybindings] = None, + show_cursor: bool = True, + cycle: bool = True, + wrap_lines: bool = True, + raise_keyboard_interrupt: bool = True, + mandatory: bool = True, + mandatory_message: str = "Mandatory prompt", + session_result: Optional[InquirerPySessionResult] = None, + ) -> None: + self.content_control = InquirerPyRawlistControl( + choices=choices, + default=default, + pointer=pointer, + separator=separator, + marker=marker, + session_result=session_result, + multiselect=multiselect, + marker_pl=marker_pl, + ) + super().__init__( + message=message, + choices=choices, + style=style, + border=border, + vi_mode=vi_mode, + qmark=qmark, + amark=amark, + instruction=instruction, + long_instruction=long_instruction, + transformer=transformer, + filter=filter, + height=height, + max_height=max_height, + multiselect=multiselect, + validate=validate, + invalid_message=invalid_message, + keybindings=keybindings, + show_cursor=show_cursor, + cycle=cycle, + wrap_lines=wrap_lines, + raise_keyboard_interrupt=raise_keyboard_interrupt, + mandatory=mandatory, + mandatory_message=mandatory_message, + session_result=session_result, + ) + + def _on_rendered(self, _) -> None: + """Override this method to apply custom keybindings. + + Needs to creat these kb in the callback due to `after_render` + retrieve the choices asynchronously. + + Check if fetched choices exceed the limit of 9, raise + InvalidArgument exception. + """ + + def keybinding_factory(choice): + @self.register_kb(str(choice["display_index"])) + def keybinding(_) -> None: + self.content_control.selected_choice_index = int(choice["actual_index"]) + + return keybinding + + if self.content_control.choice_count >= 10: + raise InvalidArgument("rawlist argument choices cannot exceed length of 9") + + for choice in self.content_control.choices: + if not isinstance(choice["value"], Separator): + keybinding_factory(choice) + + def _get_prompt_message(self) -> List[Tuple[str, str]]: + """Return the formatted text to display in the prompt. + + Overriding this method to allow multiple formatted class to be displayed. + """ + display_message = super()._get_prompt_message() + if not self.status["answered"] and self.content_control.choices: + display_message.append( + ("class:input", str(self.content_control.selection["display_index"])) + ) + return display_message diff --git a/voxvera/vendor/InquirerPy/prompts/secret.py b/voxvera/vendor/InquirerPy/prompts/secret.py new file mode 100644 index 0000000..cf7f3b2 --- /dev/null +++ b/voxvera/vendor/InquirerPy/prompts/secret.py @@ -0,0 +1,134 @@ +"""Module contains the class to create a secret prompt.""" +from typing import TYPE_CHECKING, Any, Callable, List, Optional, Tuple + +from InquirerPy.prompts.input import InputPrompt +from InquirerPy.utils import ( + InquirerPyDefault, + InquirerPyKeybindings, + InquirerPyMessage, + InquirerPySessionResult, + InquirerPyStyle, + InquirerPyValidate, +) + +if TYPE_CHECKING: + from prompt_toolkit.input.base import Input + from prompt_toolkit.output.base import Output + +__all__ = ["SecretPrompt"] + + +class SecretPrompt(InputPrompt): + """Create a text prompt which transforms the input to asterisks while typing. + + A wrapper class around :class:`~prompt_toolkit.shortcuts.PromptSession`. + + Args: + message: The question to ask the user. + Refer to :ref:`pages/dynamic:message` documentation for more details. + style: An :class:`InquirerPyStyle` instance. + Refer to :ref:`Style ` documentation for more details. + vi_mode: Use vim keybinding for the prompt. + Refer to :ref:`pages/kb:Keybindings` documentation for more details. + default: Set the default text value of the prompt. + Refer to :ref:`pages/dynamic:default` documentation for more details. + qmark: Question mark symbol. Custom symbol that will be displayed infront of the question before its answered. + amark: Answer mark symbol. Custom symbol that will be displayed infront of the question after its answered. + instruction: Short instruction to display next to the question. + long_instruction: Long instructions to display at the bottom of the prompt. + validate: Add validation to user input. + Refer to :ref:`pages/validator:Validator` documentation for more details. + invalid_message: Error message to display when user input is invalid. + Refer to :ref:`pages/validator:Validator` documentation for more details. + transformer: A function which performs additional transformation on the value that gets printed to the terminal. + Different than `filter` parameter, this is only visual effect and won’t affect the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`. + Refer to :ref:`pages/dynamic:transformer` documentation for more details. + filter: A function which performs additional transformation on the result. + This affects the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`. + Refer to :ref:`pages/dynamic:filter` documentation for more details. + keybindings: Customise the builtin keybindings. + Refer to :ref:`pages/kb:Keybindings` for more details. + wrap_lines: Soft wrap question lines when question exceeds the terminal width. + raise_keyboard_interrupt: Raise the :class:`KeyboardInterrupt` exception when `ctrl-c` is pressed. If false, the result + will be `None` and the question is skiped. + mandatory: Indicate if the prompt is mandatory. If True, then the question cannot be skipped. + mandatory_message: Error message to show when user attempts to skip mandatory prompt. + session_result: Used internally for :ref:`index:Classic Syntax (PyInquirer)`. + input: Used internally and will be removed in future updates. + output: Used internally and will be removed in future updates. + + Examples: + >>> from InquirerPy import inquirer + >>> result = inquirer.secret(message="Password:").execute() + >>> print(f"Password: {result}") + Password: asdf123 + """ + + def __init__( + self, + message: InquirerPyMessage, + style: Optional[InquirerPyStyle] = None, + default: InquirerPyDefault = "", + qmark: str = "?", + amark: str = "?", + instruction: str = "", + long_instruction: str = "", + vi_mode: bool = False, + validate: Optional[InquirerPyValidate] = None, + invalid_message: str = "Invalid input", + transformer: Optional[Callable[[str], Any]] = None, + filter: Optional[Callable[[str], Any]] = None, + keybindings: Optional[InquirerPyKeybindings] = None, + wrap_lines: bool = True, + raise_keyboard_interrupt: bool = True, + mandatory: bool = True, + mandatory_message: str = "Mandatory prompt", + session_result: Optional[InquirerPySessionResult] = None, + input: Optional["Input"] = None, + output: Optional["Output"] = None, + ) -> None: + super().__init__( + message=message, + style=style, + vi_mode=vi_mode, + default=default, + qmark=qmark, + amark=amark, + instruction=instruction, + long_instruction=long_instruction, + validate=validate, + invalid_message=invalid_message, + is_password=True, + transformer=transformer, + filter=filter, + keybindings=keybindings, + wrap_lines=wrap_lines, + raise_keyboard_interrupt=raise_keyboard_interrupt, + mandatory=mandatory, + mandatory_message=mandatory_message, + session_result=session_result, + input=input, + output=output, + ) + + def _get_prompt_message(self) -> List[Tuple[str, str]]: + """Get message to display infront of the input buffer. + + Args: + pre_answer: The formatted text to display before answering the question. + post_answer: The formatted text to display after answering the question. + + Returns: + Formatted text in list of tuple format. + """ + pre_answer = ( + "class:instruction", + " %s " % self.instruction if self.instruction else " ", + ) + post_answer = ( + "class:answer", + "" + if not self.status["result"] + else " %s" % "".join(["*" for _ in self.status["result"]]), + ) + return super()._get_prompt_message(pre_answer, post_answer) diff --git a/voxvera/vendor/InquirerPy/py.typed b/voxvera/vendor/InquirerPy/py.typed new file mode 100644 index 0000000..e1d46c5 --- /dev/null +++ b/voxvera/vendor/InquirerPy/py.typed @@ -0,0 +1 @@ +# Marker file for PEP 561. The InquirerPy package uses inline types. diff --git a/voxvera/vendor/InquirerPy/resolver.py b/voxvera/vendor/InquirerPy/resolver.py new file mode 100644 index 0000000..ea409bc --- /dev/null +++ b/voxvera/vendor/InquirerPy/resolver.py @@ -0,0 +1,219 @@ +"""This module contains the classic entrypoint for creating prompts. + +A `PyInquirer `_ compatible entrypoint :func:`.prompt`. +""" +from typing import Any, Dict, List, Optional, Tuple, Union + +from InquirerPy.exceptions import InvalidArgument, RequiredKeyNotFound +from InquirerPy.prompts.checkbox import CheckboxPrompt +from InquirerPy.prompts.confirm import ConfirmPrompt +from InquirerPy.prompts.expand import ExpandPrompt +from InquirerPy.prompts.filepath import FilePathPrompt +from InquirerPy.prompts.fuzzy import FuzzyPrompt +from InquirerPy.prompts.input import InputPrompt +from InquirerPy.prompts.list import ListPrompt +from InquirerPy.prompts.number import NumberPrompt +from InquirerPy.prompts.rawlist import RawlistPrompt +from InquirerPy.prompts.secret import SecretPrompt +from InquirerPy.utils import ( + InquirerPyKeybindings, + InquirerPyQuestions, + InquirerPySessionResult, + get_style, +) + +__all__ = ["prompt", "prompt_async"] + +question_mapping = { + "confirm": ConfirmPrompt, + "filepath": FilePathPrompt, + "password": SecretPrompt, + "input": InputPrompt, + "list": ListPrompt, + "checkbox": CheckboxPrompt, + "rawlist": RawlistPrompt, + "expand": ExpandPrompt, + "fuzzy": FuzzyPrompt, + "number": NumberPrompt, +} + + +def _get_questions(questions: InquirerPyQuestions) -> List[Dict[str, Any]]: + """Process and validate questions. + + Args: + questions: List of questions to create prompt. + + Returns: + List of validated questions. + """ + if isinstance(questions, dict): + questions = [questions] + + if not isinstance(questions, list): + raise InvalidArgument("argument questions should be type of list or dictionary") + + return questions + + +def _get_question( + original_question: Dict[str, Any], result: InquirerPySessionResult, index: int +) -> Tuple[Optional[Dict[str, Any]], str, Union[str, int], str]: + """Get information from individual question. + + Args: + original_question: Original question dictionary. + result: Current prompt session result. + index: Question index. + + Returns: + A tuple containing question information in the order of + question dictionary, type of question, name of question, message of question. + """ + question = original_question.copy() + question_type = question.pop("type") + question_name = question.pop("name", index) + message = question.pop("message") + question_when = question.pop("when", None) + if question_when and not question_when(result): + result[question_name] = None + question = None + return question, question_type, question_name, message + + +async def prompt_async( + questions: InquirerPyQuestions, + style: Optional[Dict[str, str]] = None, + vi_mode: bool = False, + raise_keyboard_interrupt: bool = True, + keybindings: Optional[InquirerPyKeybindings] = None, + style_override: bool = True, +) -> InquirerPySessionResult: + """Classic syntax entrypoint to create a prompt session via asynchronous method. + + Refer to :func:`InquirerPy.resolver.prompt` for detailed documentations. + """ + result: InquirerPySessionResult = {} + if not keybindings: + keybindings = {} + + questions = _get_questions(questions=questions) + question_style = get_style(style, style_override) + + for index, original_question in enumerate(questions): + try: + question, question_type, question_name, message = _get_question( + original_question=original_question, result=result, index=index + ) + if question is None: + continue + args = { + "message": message, + "style": question_style, + "vi_mode": vi_mode, + "raise_keyboard_interrupt": raise_keyboard_interrupt, + "session_result": result, + "keybindings": {**keybindings, **question.pop("keybindings", {})}, + } + result[question_name] = await question_mapping[question_type]( + **args, **question + ).execute_async() + except KeyError: + raise RequiredKeyNotFound + + return result + + +def prompt( + questions: InquirerPyQuestions, + style: Optional[Dict[str, str]] = None, + vi_mode: bool = False, + raise_keyboard_interrupt: bool = True, + keybindings: Optional[InquirerPyKeybindings] = None, + style_override: bool = True, +) -> InquirerPySessionResult: + """Classic syntax entrypoint to create a prompt session. + + Resolve user provided list of questions, display prompts and get the results. + + Args: + questions: A list of :ref:`pages/prompt:question` to ask. Refer to documentation for more info. + style: A :class:`dict` containing the style specification for the prompt. Refer to :ref:`pages/style:Style` for more info. + vi_mode: Use vim keybindings for the prompt instead of the default emacs keybindings. + Refer to :ref:`pages/kb:Keybindings` for more info. + raise_keyboard_interrupt: Raise the :class:`KeyboardInterrupt` exception when `ctrl-c` is pressed. If false, the result + will be `None` and the question is skiped. + keybindings: List of custom :ref:`pages/kb:Keybindings` to apply. Refer to documentation for more info. + style_override: Override all default styles. When providing any style customisation, all default styles are removed when this is True. + + Returns: + A dictionary containing all of the question answers. The key is the name of the question and the value is the + user answer. If the `name` key is not present as part of the question, then the question index will be used + as the key. + + Raises: + RequiredKeyNotFound: When the question is missing required keys. + InvalidArgument: When the provided `questions` argument is not a type of :class:`list` nor :class:`dictionary`. + + Examples: + >>> from InquirerPy import prompt + >>> from InquirerPy.validator import NumberValidator + >>> questions = [ + ... { + ... "type": "input", + ... "message": "Enter your age:", + ... "validate": NumberValidator(), + ... "invalid_message": "Input should be number.", + ... "default": "18", + ... "name": "age", + ... "filter": lambda result: int(result), + ... "transformer": lambda result: "Adult" if int(result) >= 18 else "Youth", + ... }, + ... { + ... "type": "rawlist", + ... "message": "What drinks would you like to buy:", + ... "default": 2, + ... "choices": lambda result: ["Soda", "Cidr", "Water", "Milk"] + ... if result["age"] < 18 + ... else ["Wine", "Beer"], + ... "name": "drink", + ... }, + ... { + ... "type": "list", + ... "message": "Would you like a bag:", + ... "choices": ["Yes", "No"], + ... "when": lambda result: result["drink"] in {"Wine", "Beer"}, + ... }, + ... {"type": "confirm", "message": "Confirm?", "default": True}, + ... ] + >>> result = prompt(questions=questions) + """ + result: InquirerPySessionResult = {} + if not keybindings: + keybindings = {} + + questions = _get_questions(questions=questions) + question_style = get_style(style, style_override) + + for index, original_question in enumerate(questions): + try: + question, question_type, question_name, message = _get_question( + original_question=original_question, result=result, index=index + ) + if question is None: + continue + args = { + "message": message, + "style": question_style, + "vi_mode": vi_mode, + "raise_keyboard_interrupt": raise_keyboard_interrupt, + "session_result": result, + "keybindings": {**keybindings, **question.pop("keybindings", {})}, + } + result[question_name] = question_mapping[question_type]( + **args, **question + ).execute() + except KeyError: + raise RequiredKeyNotFound + + return result diff --git a/voxvera/vendor/InquirerPy/separator.py b/voxvera/vendor/InquirerPy/separator.py new file mode 100644 index 0000000..e6d0688 --- /dev/null +++ b/voxvera/vendor/InquirerPy/separator.py @@ -0,0 +1,23 @@ +"""Module contains the :class:`.Separator` class.""" + + +class Separator: + """A non selectable choice that can be used as part of the choices argument in list type prompts. + + It can be used to create some visual separations between choices in list type prompts. + + Args: + line: Content to display as the separator. + + Example: + >>> from InquirerPy import inquirer + >>> choices = [1, 2, Separator(), 3] + >>> inquirer.select(message="", choices=choices) + """ + + def __init__(self, line: str = 15 * "-") -> None: + self._line = line + + def __str__(self) -> str: + """Create string representation of `Separator`.""" + return self._line diff --git a/voxvera/vendor/InquirerPy/utils.py b/voxvera/vendor/InquirerPy/utils.py new file mode 100644 index 0000000..8e2b62f --- /dev/null +++ b/voxvera/vendor/InquirerPy/utils.py @@ -0,0 +1,290 @@ +"""Module contains shared utility functions and typing aliases.""" +import math +import os +import shutil +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Dict, + List, + NamedTuple, + Optional, + Tuple, + Union, +) + +from prompt_toolkit import print_formatted_text +from prompt_toolkit.application import run_in_terminal +from prompt_toolkit.application.current import get_app +from prompt_toolkit.formatted_text import FormattedText +from prompt_toolkit.styles import Style +from prompt_toolkit.validation import Validator + +from InquirerPy.exceptions import InvalidArgument + +if TYPE_CHECKING: + from prompt_toolkit.filters.base import FilterOrBool + + from InquirerPy.base.control import Choice + +__all__ = [ + "get_style", + "calculate_height", + "InquirerPyStyle", + "patched_print", + "color_print", +] + + +class InquirerPyStyle(NamedTuple): + """`InquirerPy` Style class. + + Used as a helper class to enforce the method `get_style` to be used + while also avoiding :class:`dict` to be passed into prompts. + + Note: + The class is an instance of :class:`typing.NamedTuple`. + + Warning: + You should not directly be using this class besides for type hinting + purposes. Obtain an instance of this class using :func:`.get_style`. + """ + + dict: Dict[str, str] + + +InquirerPySessionResult = Dict[Union[str, int], Optional[Union[str, bool, List[Any]]]] +InquirerPyChoice = Union[List[Any], List["Choice"], List[Dict[str, Any]]] +InquirerPyListChoices = Union[ + Callable[["InquirerPySessionResult"], InquirerPyChoice], + InquirerPyChoice, +] +InquirerPyValidate = Union[Callable[[Any], bool], "Validator"] +InquirerPyQuestions = Union[List[Dict[str, Any]], Dict[str, Any]] +InquirerPyMessage = Union[str, Callable[["InquirerPySessionResult"], str]] +InquirerPyDefault = Union[Any, Callable[["InquirerPySessionResult"], Any]] +InquirerPyKeybindings = Dict[ + str, List[Dict[str, Union[str, "FilterOrBool", List[str]]]] +] + + +def get_style( + style: Optional[Dict[str, str]] = None, style_override: bool = True +) -> InquirerPyStyle: + """Obtain an :class:`.InquirerPyStyle` instance which can be consumed by the `style` parameter in prompts. + + Tip: + This function supports ENV variables. + + For all the color ENV variable names, refer to the :ref:`ENV ` documentation. + + Note: + If no style is provided, then a default theme based on `one dark `_ + color palette is applied. + + Note: + Priority: style parameter -> ENV variable -> default style + + Args: + style: The dictionary of style classes and their colors, If nothing is passed, the style will be resolved to the :ref:`pages/style:Default Style`. + style_override: A boolean to determine if the supplied `style` parameter should be merged with the :ref:`pages/style:Default Style` or override them. + By default, the supplied style will overwrite the :ref:`pages/style:Default Style`. + + Returns: + An instance of :class:`.InquirerPyStyle`. + + Examples: + >>> from InquirerPy import get_style + >>> from InquirerPy import inquirer + >>> style = get_style({"questionmark": "#ffffff", "answer": "#000000"}, style_override=False) + >>> result = inquirer.confirm(message="Confirm?", style=style).execute() + """ + if not style_override or style is None: + if not style: + style = {} + result = { + "questionmark": os.getenv("INQUIRERPY_STYLE_QUESTIONMARK", "#e5c07b"), + "answermark": os.getenv("INQUIRERPY_STYLE_ANSWERMARK", "#e5c07b"), + "answer": os.getenv("INQUIRERPY_STYLE_ANSWER", "#61afef"), + "input": os.getenv("INQUIRERPY_STYLE_INPUT", "#98c379"), + "question": os.getenv("INQUIRERPY_STYLE_QUESTION", ""), + "answered_question": os.getenv("INQUIRERPY_STYLE_ANSWERED_QUESTION", ""), + "instruction": os.getenv("INQUIRERPY_STYLE_INSTRUCTION", "#abb2bf"), + "long_instruction": os.getenv( + "INQUIRERPY_STYLE_LONG_INSTRUCTION", "#abb2bf" + ), + "pointer": os.getenv("INQUIRERPY_STYLE_POINTER", "#61afef"), + "checkbox": os.getenv("INQUIRERPY_STYLE_CHECKBOX", "#98c379"), + "separator": os.getenv("INQUIRERPY_STYLE_SEPARATOR", ""), + "skipped": os.getenv("INQUIRERPY_STYLE_SKIPPED", "#5c6370"), + "validator": os.getenv("INQUIRERPY_STYLE_VALIDATOR", ""), + "marker": os.getenv("INQUIRERPY_STYLE_MARKER", "#e5c07b"), + "fuzzy_prompt": os.getenv("INQUIRERPY_STYLE_FUZZY_PROMPT", "#c678dd"), + "fuzzy_info": os.getenv("INQUIRERPY_STYLE_FUZZY_INFO", "#abb2bf"), + "fuzzy_border": os.getenv("INQUIRERPY_STYLE_FUZZY_BORDER", "#4b5263"), + "fuzzy_match": os.getenv("INQUIRERPY_STYLE_FUZZY_MATCH", "#c678dd"), + "spinner_pattern": os.getenv("INQUIRERPY_STYLE_SPINNER_PATTERN", "#e5c07b"), + "spinner_text": os.getenv("INQUIRERPY_STYLE_SPINNER_TEXT", ""), + **style, + } + else: + result = { + "questionmark": os.getenv("INQUIRERPY_STYLE_QUESTIONMARK", ""), + "answermark": os.getenv("INQUIRERPY_STYLE_ANSWERMARK", ""), + "answer": os.getenv("INQUIRERPY_STYLE_ANSWER", ""), + "input": os.getenv("INQUIRERPY_STYLE_INPUT", ""), + "question": os.getenv("INQUIRERPY_STYLE_QUESTION", ""), + "answered_question": os.getenv("INQUIRERPY_STYLE_ANSWERED_QUESTION", ""), + "instruction": os.getenv("INQUIRERPY_STYLE_INSTRUCTION", ""), + "long_instruction": os.getenv("INQUIRERPY_STYLE_LONG_INSTRUCTION", ""), + "pointer": os.getenv("INQUIRERPY_STYLE_POINTER", ""), + "checkbox": os.getenv("INQUIRERPY_STYLE_CHECKBOX", ""), + "separator": os.getenv("INQUIRERPY_STYLE_SEPARATOR", ""), + "skipped": os.getenv("INQUIRERPY_STYLE_SKIPPED", ""), + "validator": os.getenv("INQUIRERPY_STYLE_VALIDATOR", ""), + "marker": os.getenv("INQUIRERPY_STYLE_MARKER", ""), + "fuzzy_prompt": os.getenv("INQUIRERPY_STYLE_FUZZY_PROMPT", ""), + "fuzzy_info": os.getenv("INQUIRERPY_STYLE_FUZZY_INFO", ""), + "fuzzy_border": os.getenv("INQUIRERPY_STYLE_FUZZY_BORDER", ""), + "fuzzy_match": os.getenv("INQUIRERPY_STYLE_FUZZY_MATCH", ""), + "spinner_pattern": os.getenv("INQUIRERPY_STYLE_SPINNER_PATTERN", ""), + "spinner_text": os.getenv("INQUIRERPY_STYLE_SPINNER_TEXT", ""), + **style, + } + + if result.get("fuzzy_border"): + result["frame.border"] = result.pop("fuzzy_border") + if result.get("validator"): + result["validation-toolbar"] = result.pop("validator") + result["bottom-toolbar"] = "noreverse" + return InquirerPyStyle(result) + + +def calculate_height( + height: Optional[Union[int, str]], + max_height: Optional[Union[int, str]], + height_offset: int = 2, +) -> Tuple[Optional[int], int]: + """Calculate the `height` and `max_height` for the main question contents. + + Tip: + The parameter `height`/`max_height` can be specified by either a :class:`string` or :class:`int`. + + When `height`/`max_height` is :class:`str`: + It will set the height to a percentage based on the value provided. + You can optionally add the '%' sign which will be ignored while processing. + + Example: "60%" or "60" (60% of the current terminal visible lines) + + When `height`/`max_height` is :class:`int`: + It will set the height to exact number of lines based on the value provided. + + Example: 20 (20 lines in terminal) + + Note: + If `max_height` is not provided or is None, the default `max_height` will be configured to `70%` for + best visual presentation in the terminal. + + Args: + height: The desired height in either percentage as string or exact value as int. + max_height: Maximum acceptable height in either percentage as string or exact value as int. + height_offset: Height offset to apply to the height. + + Returns: + A :class:`tuple` with the first value being the desired height and the second value being + the maximum height. + + Raises: + InvalidArgument: The provided `height`/`max_height` is not able to to be converted to int. + + Examples: + >>> calculate_height(height="60%", max_height="100%") + """ + try: + _, term_lines = shutil.get_terminal_size() + term_lines = term_lines + if not height: + dimmension_height = None + else: + if isinstance(height, str): + height = height.replace("%", "") + height = int(height) + dimmension_height = ( + math.floor(term_lines * (height / 100)) - height_offset + ) + else: + dimmension_height = height + + if not max_height: + max_height = "70%" if not height else "100%" + if isinstance(max_height, str): + max_height = max_height.replace("%", "") + max_height = int(max_height) + dimmension_max_height = ( + math.floor(term_lines * (max_height / 100)) - height_offset + ) + else: + dimmension_max_height = max_height + + if dimmension_height and dimmension_height > dimmension_max_height: + dimmension_height = dimmension_max_height + if dimmension_height and dimmension_height <= 0: + dimmension_height = 1 + if dimmension_max_height <= 0: + dimmension_max_height = 1 + return dimmension_height, dimmension_max_height + + except ValueError: + raise InvalidArgument( + "prompt argument height/max_height needs to be type of an int or str" + ) + + +def patched_print(*values) -> None: + """Patched :func:`print` that can print values without interrupting the prompt. + + See Also: + :func:`print` + :func:`~prompt_toolkit.application.run_in_terminal` + + Args: + *values: Refer to :func:`print`. + + Examples: + >>> patched_print("Hello World") + """ + + def _print(): + print(*values) + + run_in_terminal(_print) + + +def color_print( + formatted_text: List[Tuple[str, str]], style: Optional[Dict[str, str]] = None +) -> None: + """Print colored text leveraging :func:`~prompt_toolkit.shortcuts.print_formatted_text`. + + This function automatically handles printing the text without interrupting the + current prompt. + + Args: + formatted_text: A list of formatted_text. + style: Style to apply to `formatted_text` in :class:`dictionary` form. + + Example: + >>> color_print(formatted_text=[("class:aa", "hello "), ("class:bb", "world")], style={"aa": "red", "bb": "blue"}) + >>> color_print([("red", "yes"), ("", " "), ("blue", "no")]) + """ + + def _print(): + print_formatted_text( + FormattedText(formatted_text), + style=Style.from_dict(style) if style else None, + ) + + if get_app().is_running: + run_in_terminal(_print) + else: + _print() diff --git a/voxvera/vendor/InquirerPy/validator.py b/voxvera/vendor/InquirerPy/validator.py new file mode 100644 index 0000000..aece4fd --- /dev/null +++ b/voxvera/vendor/InquirerPy/validator.py @@ -0,0 +1,165 @@ +"""Module contains pre-built validators.""" +import re +from pathlib import Path +from typing import Optional + +from prompt_toolkit.validation import ValidationError, Validator + +__all__ = [ + "PathValidator", + "EmptyInputValidator", + "PasswordValidator", + "NumberValidator", +] + + +class NumberValidator(Validator): + """:class:`~prompt_toolkit.validation.Validator` to validate if input is a number. + + Args: + message: Error message to display in the validatation toolbar when validation failed. + float_allowed: Allow input to contain floating number (with decimal). + """ + + def __init__( + self, message: str = "Input should be a number", float_allowed: bool = False + ) -> None: + self._message = message + self._float_allowed = float_allowed + + def validate(self, document) -> None: + """Check if user input is a valid number. + + This method is used internally by `prompt_toolkit `_. + + See Also: + https://python-prompt-toolkit.readthedocs.io/en/master/pages/asking_for_input.html?highlight=validator#input-validation + """ + try: + if self._float_allowed: + float(document.text) + else: + int(document.text) + except ValueError: + raise ValidationError( + message=self._message, cursor_position=document.cursor_position + ) + + +class PathValidator(Validator): + """:class:`~prompt_toolkit.validation.Validator` to validate if input is a valid filepath on the system. + + Args: + message: Error message to display in the validatation toolbar when validation failed. + is_file: Explicitly check if the input is a valid file on the system. + is_dir: Explicitly check if the input is a valid directory/folder on the system. + """ + + def __init__( + self, + message: str = "Input is not a valid path", + is_file: bool = False, + is_dir: bool = False, + ) -> None: + self._message = message + self._is_file = is_file + self._is_dir = is_dir + + def validate(self, document) -> None: + """Check if user input is a filepath that exists on the system based on conditions. + + This method is used internally by `prompt_toolkit `_. + + See Also: + https://python-prompt-toolkit.readthedocs.io/en/master/pages/asking_for_input.html?highlight=validator#input-validation + """ + path = Path(document.text).expanduser() + if self._is_file and not path.is_file(): + raise ValidationError( + message=self._message, + cursor_position=document.cursor_position, + ) + elif self._is_dir and not path.is_dir(): + raise ValidationError( + message=self._message, + cursor_position=document.cursor_position, + ) + elif not path.exists(): + raise ValidationError( + message=self._message, + cursor_position=document.cursor_position, + ) + + +class EmptyInputValidator(Validator): + """:class:`~prompt_toolkit.validation.Validator` to validate if the input is empty. + + Args: + message: Error message to display in the validatation toolbar when validation failed. + """ + + def __init__(self, message: str = "Input cannot be empty") -> None: + self._message = message + + def validate(self, document) -> None: + """Check if user input is empty. + + This method is used internally by `prompt_toolkit `_. + + See Also: + https://python-prompt-toolkit.readthedocs.io/en/master/pages/asking_for_input.html?highlight=validator#input-validation + """ + if not len(document.text) > 0: + raise ValidationError( + message=self._message, + cursor_position=document.cursor_position, + ) + + +class PasswordValidator(Validator): + """:class:`~prompt_toolkit.validation.Validator` to validate password compliance. + + Args: + message: Error message to display in the validatation toolbar when validation failed. + length: The minimum length of the password. + cap: Password should include at least one capital letter. + special: Password should include at least one special char "@$!%*#?&". + number: Password should include at least one number. + """ + + def __init__( + self, + message: str = "Input is not compliant with the password constraints", + length: Optional[int] = None, + cap: bool = False, + special: bool = False, + number: bool = False, + ) -> None: + password_pattern = r"^" + if cap: + password_pattern += r"(?=.*[A-Z])" + if special: + password_pattern += r"(?=.*[@$!%*#?&])" + if number: + password_pattern += r"(?=.*[0-9])" + password_pattern += r"." + if length: + password_pattern += r"{%s,}" % length + else: + password_pattern += r"*" + password_pattern += r"$" + self._re = re.compile(password_pattern) + self._message = message + + def validate(self, document) -> None: + """Check if user input is compliant with the specified password constraints. + + This method is used internally by `prompt_toolkit `_. + + See Also: + https://python-prompt-toolkit.readthedocs.io/en/master/pages/asking_for_input.html?highlight=validator#input-validation + """ + if not self._re.match(document.text): + raise ValidationError( + message=self._message, cursor_position=document.cursor_position + ) diff --git a/voxvera/vendor/PIL/AvifImagePlugin.py b/voxvera/vendor/PIL/AvifImagePlugin.py new file mode 100644 index 0000000..43c39a9 --- /dev/null +++ b/voxvera/vendor/PIL/AvifImagePlugin.py @@ -0,0 +1,293 @@ +from __future__ import annotations + +import os +from io import BytesIO +from typing import IO + +from . import ExifTags, Image, ImageFile + +try: + from . import _avif + + SUPPORTED = True +except ImportError: + SUPPORTED = False + +# Decoder options as module globals, until there is a way to pass parameters +# to Image.open (see https://github.com/python-pillow/Pillow/issues/569) +DECODE_CODEC_CHOICE = "auto" +DEFAULT_MAX_THREADS = 0 + + +def get_codec_version(codec_name: str) -> str | None: + versions = _avif.codec_versions() + for version in versions.split(", "): + if version.split(" [")[0] == codec_name: + return version.split(":")[-1].split(" ")[0] + return None + + +def _accept(prefix: bytes) -> bool | str: + if prefix[4:8] != b"ftyp": + return False + major_brand = prefix[8:12] + if major_brand in ( + # coding brands + b"avif", + b"avis", + # We accept files with AVIF container brands; we can't yet know if + # the ftyp box has the correct compatible brands, but if it doesn't + # then the plugin will raise a SyntaxError which Pillow will catch + # before moving on to the next plugin that accepts the file. + # + # Also, because this file might not actually be an AVIF file, we + # don't raise an error if AVIF support isn't properly compiled. + b"mif1", + b"msf1", + ): + if not SUPPORTED: + return ( + "image file could not be identified because AVIF support not installed" + ) + return True + return False + + +def _get_default_max_threads() -> int: + if DEFAULT_MAX_THREADS: + return DEFAULT_MAX_THREADS + if hasattr(os, "sched_getaffinity"): + return len(os.sched_getaffinity(0)) + else: + return os.cpu_count() or 1 + + +class AvifImageFile(ImageFile.ImageFile): + format = "AVIF" + format_description = "AVIF image" + __frame = -1 + + def _open(self) -> None: + if not SUPPORTED: + msg = "image file could not be opened because AVIF support not installed" + raise SyntaxError(msg) + + if DECODE_CODEC_CHOICE != "auto" and not _avif.decoder_codec_available( + DECODE_CODEC_CHOICE + ): + msg = "Invalid opening codec" + raise ValueError(msg) + + assert self.fp is not None + self._decoder = _avif.AvifDecoder( + self.fp.read(), + DECODE_CODEC_CHOICE, + _get_default_max_threads(), + ) + + # Get info from decoder + self._size, self.n_frames, self._mode, icc, exif, exif_orientation, xmp = ( + self._decoder.get_info() + ) + self.is_animated = self.n_frames > 1 + + if icc: + self.info["icc_profile"] = icc + if xmp: + self.info["xmp"] = xmp + + if exif_orientation != 1 or exif: + exif_data = Image.Exif() + if exif: + exif_data.load(exif) + original_orientation = exif_data.get(ExifTags.Base.Orientation, 1) + else: + original_orientation = 1 + if exif_orientation != original_orientation: + exif_data[ExifTags.Base.Orientation] = exif_orientation + exif = exif_data.tobytes() + if exif: + self.info["exif"] = exif + self.seek(0) + + def seek(self, frame: int) -> None: + if not self._seek_check(frame): + return + + # Set tile + self.__frame = frame + self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 0, self.mode)] + + def load(self) -> Image.core.PixelAccess | None: + if self.tile: + # We need to load the image data for this frame + data, timescale, pts_in_timescales, duration_in_timescales = ( + self._decoder.get_frame(self.__frame) + ) + self.info["timestamp"] = round(1000 * (pts_in_timescales / timescale)) + self.info["duration"] = round(1000 * (duration_in_timescales / timescale)) + + if self.fp and self._exclusive_fp: + self.fp.close() + self.fp = BytesIO(data) + + return super().load() + + def load_seek(self, pos: int) -> None: + pass + + def tell(self) -> int: + return self.__frame + + +def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + _save(im, fp, filename, save_all=True) + + +def _save( + im: Image.Image, fp: IO[bytes], filename: str | bytes, save_all: bool = False +) -> None: + info = im.encoderinfo.copy() + if save_all: + append_images = list(info.get("append_images", [])) + else: + append_images = [] + + total = 0 + for ims in [im] + append_images: + total += getattr(ims, "n_frames", 1) + + quality = info.get("quality", 75) + if not isinstance(quality, int) or quality < 0 or quality > 100: + msg = "Invalid quality setting" + raise ValueError(msg) + + duration = info.get("duration", 0) + subsampling = info.get("subsampling", "4:2:0") + speed = info.get("speed", 6) + max_threads = info.get("max_threads", _get_default_max_threads()) + codec = info.get("codec", "auto") + if codec != "auto" and not _avif.encoder_codec_available(codec): + msg = "Invalid saving codec" + raise ValueError(msg) + range_ = info.get("range", "full") + tile_rows_log2 = info.get("tile_rows", 0) + tile_cols_log2 = info.get("tile_cols", 0) + alpha_premultiplied = bool(info.get("alpha_premultiplied", False)) + autotiling = bool(info.get("autotiling", tile_rows_log2 == tile_cols_log2 == 0)) + + icc_profile = info.get("icc_profile", im.info.get("icc_profile")) + exif_orientation = 1 + if exif := info.get("exif"): + if isinstance(exif, Image.Exif): + exif_data = exif + else: + exif_data = Image.Exif() + exif_data.load(exif) + if ExifTags.Base.Orientation in exif_data: + exif_orientation = exif_data.pop(ExifTags.Base.Orientation) + exif = exif_data.tobytes() if exif_data else b"" + elif isinstance(exif, Image.Exif): + exif = exif_data.tobytes() + + xmp = info.get("xmp") + + if isinstance(xmp, str): + xmp = xmp.encode("utf-8") + + advanced = info.get("advanced") + if advanced is not None: + if isinstance(advanced, dict): + advanced = advanced.items() + try: + advanced = tuple(advanced) + except TypeError: + invalid = True + else: + invalid = any(not isinstance(v, tuple) or len(v) != 2 for v in advanced) + if invalid: + msg = ( + "advanced codec options must be a dict of key-value string " + "pairs or a series of key-value two-tuples" + ) + raise ValueError(msg) + + # Setup the AVIF encoder + enc = _avif.AvifEncoder( + im.size, + subsampling, + quality, + speed, + max_threads, + codec, + range_, + tile_rows_log2, + tile_cols_log2, + alpha_premultiplied, + autotiling, + icc_profile or b"", + exif or b"", + exif_orientation, + xmp or b"", + advanced, + ) + + # Add each frame + frame_idx = 0 + frame_duration = 0 + cur_idx = im.tell() + is_single_frame = total == 1 + try: + for ims in [im] + append_images: + # Get number of frames in this image + nfr = getattr(ims, "n_frames", 1) + + for idx in range(nfr): + ims.seek(idx) + + # Make sure image mode is supported + frame = ims + rawmode = ims.mode + if ims.mode not in {"RGB", "RGBA"}: + rawmode = "RGBA" if ims.has_transparency_data else "RGB" + frame = ims.convert(rawmode) + + # Update frame duration + if isinstance(duration, (list, tuple)): + frame_duration = duration[frame_idx] + else: + frame_duration = duration + + # Append the frame to the animation encoder + enc.add( + frame.tobytes("raw", rawmode), + frame_duration, + frame.size, + rawmode, + is_single_frame, + ) + + # Update frame index + frame_idx += 1 + + if not save_all: + break + + finally: + im.seek(cur_idx) + + # Get the final output from the encoder + data = enc.finish() + if data is None: + msg = "cannot write file as AVIF (encoder returned None)" + raise OSError(msg) + + fp.write(data) + + +Image.register_open(AvifImageFile.format, AvifImageFile, _accept) +if SUPPORTED: + Image.register_save(AvifImageFile.format, _save) + Image.register_save_all(AvifImageFile.format, _save_all) + Image.register_extensions(AvifImageFile.format, [".avif", ".avifs"]) + Image.register_mime(AvifImageFile.format, "image/avif") diff --git a/voxvera/vendor/PIL/BdfFontFile.py b/voxvera/vendor/PIL/BdfFontFile.py new file mode 100644 index 0000000..f175e2f --- /dev/null +++ b/voxvera/vendor/PIL/BdfFontFile.py @@ -0,0 +1,122 @@ +# +# 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. +# + +""" +Parse X Bitmap Distribution Format (BDF) +""" +from __future__ import annotations + +from typing import BinaryIO + +from . import FontFile, Image + + +def bdf_char( + f: BinaryIO, +) -> ( + tuple[ + str, + int, + tuple[tuple[int, int], tuple[int, int, int, int], tuple[int, int, int, int]], + Image.Image, + ] + | None +): + # skip to STARTCHAR + while True: + s = f.readline() + if not s: + return None + if s.startswith(b"STARTCHAR"): + break + id = s[9:].strip().decode("ascii") + + # load symbol properties + props = {} + while True: + s = f.readline() + if not s or s.startswith(b"BITMAP"): + break + i = s.find(b" ") + props[s[:i].decode("ascii")] = s[i + 1 : -1].decode("ascii") + + # load bitmap + bitmap = bytearray() + while True: + s = f.readline() + if not s or s.startswith(b"ENDCHAR"): + break + bitmap += s[:-1] + + # The word BBX + # followed by the width in x (BBw), height in y (BBh), + # and x and y displacement (BBxoff0, BByoff0) + # of the lower left corner from the origin of the character. + width, height, x_disp, y_disp = (int(p) for p in props["BBX"].split()) + + # The word DWIDTH + # followed by the width in x and y of the character in device pixels. + dwx, dwy = (int(p) for p in props["DWIDTH"].split()) + + bbox = ( + (dwx, dwy), + (x_disp, -y_disp - height, width + x_disp, -y_disp), + (0, 0, width, height), + ) + + try: + im = Image.frombytes("1", (width, height), bitmap, "hex", "1") + except ValueError: + # deal with zero-width characters + im = Image.new("1", (width, height)) + + return id, int(props["ENCODING"]), bbox, im + + +class BdfFontFile(FontFile.FontFile): + """Font file plugin for the X11 BDF format.""" + + def __init__(self, fp: BinaryIO) -> None: + super().__init__() + + s = fp.readline() + if not s.startswith(b"STARTFONT 2.1"): + msg = "not a valid BDF file" + raise SyntaxError(msg) + + props = {} + comments = [] + + while True: + s = fp.readline() + if not s or s.startswith(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")) + + 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/voxvera/vendor/PIL/BlpImagePlugin.py b/voxvera/vendor/PIL/BlpImagePlugin.py new file mode 100644 index 0000000..6bb92ed --- /dev/null +++ b/voxvera/vendor/PIL/BlpImagePlugin.py @@ -0,0 +1,498 @@ +""" +Blizzard Mipmap Format (.blp) +Jerome Leclanche + +The contents of this file are hereby released in the public domain (CC0) +Full text of the CC0 license: + https://creativecommons.org/publicdomain/zero/1.0/ + +BLP1 files, used mostly in Warcraft III, are not fully supported. +All types of BLP2 files used in World of Warcraft are supported. + +The BLP file structure consists of a header, up to 16 mipmaps of the +texture + +Texture sizes must be powers of two, though the two dimensions do +not have to be equal; 512x256 is valid, but 512x200 is not. +The first mipmap (mipmap #0) is the full size image; each subsequent +mipmap halves both dimensions. The final mipmap should be 1x1. + +BLP files come in many different flavours: +* JPEG-compressed (type == 0) - only supported for BLP1. +* RAW images (type == 1, encoding == 1). Each mipmap is stored as an + array of 8-bit values, one per pixel, left to right, top to bottom. + Each value is an index to the palette. +* DXT-compressed (type == 1, encoding == 2): +- DXT1 compression is used if alpha_encoding == 0. + - An additional alpha bit is used if alpha_depth == 1. + - DXT3 compression is used if alpha_encoding == 1. + - DXT5 compression is used if alpha_encoding == 7. +""" + +from __future__ import annotations + +import abc +import os +import struct +from enum import IntEnum +from io import BytesIO +from typing import IO + +from . import Image, ImageFile + + +class Format(IntEnum): + JPEG = 0 + + +class Encoding(IntEnum): + UNCOMPRESSED = 1 + DXT = 2 + UNCOMPRESSED_RAW_BGRA = 3 + + +class AlphaEncoding(IntEnum): + DXT1 = 0 + DXT3 = 1 + DXT5 = 7 + + +def unpack_565(i: int) -> tuple[int, int, int]: + return ((i >> 11) & 0x1F) << 3, ((i >> 5) & 0x3F) << 2, (i & 0x1F) << 3 + + +def decode_dxt1( + data: bytes, alpha: bool = False +) -> tuple[bytearray, bytearray, bytearray, bytearray]: + """ + input: one "row" of data (i.e. will produce 4*width pixels) + """ + + blocks = len(data) // 8 # number of blocks in row + ret = (bytearray(), bytearray(), bytearray(), bytearray()) + + for block_index in range(blocks): + # Decode next 8-byte block. + idx = block_index * 8 + color0, color1, bits = struct.unpack_from("> 2 + + a = 0xFF + if control == 0: + r, g, b = r0, g0, b0 + elif control == 1: + r, g, b = r1, g1, b1 + elif control == 2: + if color0 > color1: + r = (2 * r0 + r1) // 3 + g = (2 * g0 + g1) // 3 + b = (2 * b0 + b1) // 3 + else: + r = (r0 + r1) // 2 + g = (g0 + g1) // 2 + b = (b0 + b1) // 2 + elif control == 3: + if color0 > color1: + r = (2 * r1 + r0) // 3 + g = (2 * g1 + g0) // 3 + b = (2 * b1 + b0) // 3 + else: + r, g, b, a = 0, 0, 0, 0 + + if alpha: + ret[j].extend([r, g, b, a]) + else: + ret[j].extend([r, g, b]) + + return ret + + +def decode_dxt3(data: bytes) -> tuple[bytearray, bytearray, bytearray, bytearray]: + """ + input: one "row" of data (i.e. will produce 4*width pixels) + """ + + blocks = len(data) // 16 # number of blocks in row + ret = (bytearray(), bytearray(), bytearray(), bytearray()) + + for block_index in range(blocks): + idx = block_index * 16 + block = data[idx : idx + 16] + # Decode next 16-byte block. + bits = struct.unpack_from("<8B", block) + color0, color1 = struct.unpack_from(">= 4 + else: + high = True + a &= 0xF + a *= 17 # We get a value between 0 and 15 + + color_code = (code >> 2 * (4 * j + i)) & 0x03 + + if color_code == 0: + r, g, b = r0, g0, b0 + elif color_code == 1: + r, g, b = r1, g1, b1 + elif color_code == 2: + r = (2 * r0 + r1) // 3 + g = (2 * g0 + g1) // 3 + b = (2 * b0 + b1) // 3 + elif color_code == 3: + r = (2 * r1 + r0) // 3 + g = (2 * g1 + g0) // 3 + b = (2 * b1 + b0) // 3 + + ret[j].extend([r, g, b, a]) + + return ret + + +def decode_dxt5(data: bytes) -> tuple[bytearray, bytearray, bytearray, bytearray]: + """ + input: one "row" of data (i.e. will produce 4 * width pixels) + """ + + blocks = len(data) // 16 # number of blocks in row + ret = (bytearray(), bytearray(), bytearray(), bytearray()) + + for block_index in range(blocks): + idx = block_index * 16 + block = data[idx : idx + 16] + # Decode next 16-byte block. + a0, a1 = struct.unpack_from("> alphacode_index) & 0x07 + elif alphacode_index == 15: + alphacode = (alphacode2 >> 15) | ((alphacode1 << 1) & 0x06) + else: # alphacode_index >= 18 and alphacode_index <= 45 + alphacode = (alphacode1 >> (alphacode_index - 16)) & 0x07 + + if alphacode == 0: + a = a0 + elif alphacode == 1: + a = a1 + elif a0 > a1: + a = ((8 - alphacode) * a0 + (alphacode - 1) * a1) // 7 + elif alphacode == 6: + a = 0 + elif alphacode == 7: + a = 255 + else: + a = ((6 - alphacode) * a0 + (alphacode - 1) * a1) // 5 + + color_code = (code >> 2 * (4 * j + i)) & 0x03 + + if color_code == 0: + r, g, b = r0, g0, b0 + elif color_code == 1: + r, g, b = r1, g1, b1 + elif color_code == 2: + r = (2 * r0 + r1) // 3 + g = (2 * g0 + g1) // 3 + b = (2 * b0 + b1) // 3 + elif color_code == 3: + r = (2 * r1 + r0) // 3 + g = (2 * g1 + g0) // 3 + b = (2 * b1 + b0) // 3 + + ret[j].extend([r, g, b, a]) + + return ret + + +class BLPFormatError(NotImplementedError): + pass + + +def _accept(prefix: bytes) -> bool: + return prefix.startswith((b"BLP1", b"BLP2")) + + +class BlpImageFile(ImageFile.ImageFile): + """ + Blizzard Mipmap Format + """ + + format = "BLP" + format_description = "Blizzard Mipmap Format" + + def _open(self) -> None: + assert self.fp is not None + self.magic = self.fp.read(4) + if not _accept(self.magic): + msg = f"Bad BLP magic {repr(self.magic)}" + raise BLPFormatError(msg) + + compression = struct.unpack(" tuple[int, int]: + try: + self._read_header() + self._load() + except struct.error as e: + msg = "Truncated BLP file" + raise OSError(msg) from e + return -1, 0 + + @abc.abstractmethod + def _load(self) -> None: + pass + + def _read_header(self) -> None: + self._offsets = struct.unpack("<16I", self._safe_read(16 * 4)) + self._lengths = struct.unpack("<16I", self._safe_read(16 * 4)) + + def _safe_read(self, length: int) -> bytes: + assert self.fd is not None + return ImageFile._safe_read(self.fd, length) + + def _read_palette(self) -> list[tuple[int, int, int, int]]: + ret = [] + for i in range(256): + try: + b, g, r, a = struct.unpack("<4B", self._safe_read(4)) + except struct.error: + break + ret.append((b, g, r, a)) + return ret + + def _read_bgra( + self, palette: list[tuple[int, int, int, int]], alpha: bool + ) -> bytearray: + data = bytearray() + _data = BytesIO(self._safe_read(self._lengths[0])) + while True: + try: + (offset,) = struct.unpack(" None: + self._compression, self._encoding, alpha = self.args + + if self._compression == Format.JPEG: + self._decode_jpeg_stream() + + elif self._compression == 1: + if self._encoding in (4, 5): + palette = self._read_palette() + data = self._read_bgra(palette, alpha) + self.set_as_raw(data) + else: + msg = f"Unsupported BLP encoding {repr(self._encoding)}" + raise BLPFormatError(msg) + else: + msg = f"Unsupported BLP compression {repr(self._encoding)}" + raise BLPFormatError(msg) + + def _decode_jpeg_stream(self) -> None: + from .JpegImagePlugin import JpegImageFile + + (jpeg_header_size,) = struct.unpack(" None: + self._compression, self._encoding, alpha, self._alpha_encoding = self.args + + palette = self._read_palette() + + assert self.fd is not None + self.fd.seek(self._offsets[0]) + + if self._compression == 1: + # Uncompressed or DirectX compression + + if self._encoding == Encoding.UNCOMPRESSED: + data = self._read_bgra(palette, alpha) + + elif self._encoding == Encoding.DXT: + data = bytearray() + if self._alpha_encoding == AlphaEncoding.DXT1: + linesize = (self.state.xsize + 3) // 4 * 8 + for yb in range((self.state.ysize + 3) // 4): + for d in decode_dxt1(self._safe_read(linesize), alpha): + data += d + + elif self._alpha_encoding == AlphaEncoding.DXT3: + linesize = (self.state.xsize + 3) // 4 * 16 + for yb in range((self.state.ysize + 3) // 4): + for d in decode_dxt3(self._safe_read(linesize)): + data += d + + elif self._alpha_encoding == AlphaEncoding.DXT5: + linesize = (self.state.xsize + 3) // 4 * 16 + for yb in range((self.state.ysize + 3) // 4): + for d in decode_dxt5(self._safe_read(linesize)): + data += d + else: + msg = f"Unsupported alpha encoding {repr(self._alpha_encoding)}" + raise BLPFormatError(msg) + else: + msg = f"Unknown BLP encoding {repr(self._encoding)}" + raise BLPFormatError(msg) + + else: + msg = f"Unknown BLP compression {repr(self._compression)}" + raise BLPFormatError(msg) + + self.set_as_raw(data) + + +class BLPEncoder(ImageFile.PyEncoder): + _pushes_fd = True + + def _write_palette(self) -> bytes: + data = b"" + assert self.im is not None + palette = self.im.getpalette("RGBA", "RGBA") + for i in range(len(palette) // 4): + r, g, b, a = palette[i * 4 : (i + 1) * 4] + data += struct.pack("<4B", b, g, r, a) + while len(data) < 256 * 4: + data += b"\x00" * 4 + return data + + def encode(self, bufsize: int) -> tuple[int, int, bytes]: + palette_data = self._write_palette() + + offset = 20 + 16 * 4 * 2 + len(palette_data) + data = struct.pack("<16I", offset, *((0,) * 15)) + + assert self.im is not None + w, h = self.im.size + data += struct.pack("<16I", w * h, *((0,) * 15)) + + data += palette_data + + for y in range(h): + for x in range(w): + data += struct.pack(" None: + if im.mode != "P": + msg = "Unsupported BLP image mode" + raise ValueError(msg) + + magic = b"BLP1" if im.encoderinfo.get("blp_version") == "BLP1" else b"BLP2" + fp.write(magic) + + assert im.palette is not None + fp.write(struct.pack(" mode, rawmode + 1: ("P", "P;1"), + 4: ("P", "P;4"), + 8: ("P", "P"), + 16: ("RGB", "BGR;15"), + 24: ("RGB", "BGR"), + 32: ("RGB", "BGRX"), +} + +USE_RAW_ALPHA = False + + +def _accept(prefix: bytes) -> bool: + return prefix.startswith(b"BM") + + +def _dib_accept(prefix: bytes) -> bool: + return i32(prefix) in [12, 40, 52, 56, 64, 108, 124] + + +# ============================================================================= +# Image plugin for the Windows BMP format. +# ============================================================================= +class BmpImageFile(ImageFile.ImageFile): + """Image plugin for the Windows Bitmap format (BMP)""" + + # ------------------------------------------------------------- Description + format_description = "Windows Bitmap" + format = "BMP" + + # -------------------------------------------------- BMP Compression values + COMPRESSIONS = {"RAW": 0, "RLE8": 1, "RLE4": 2, "BITFIELDS": 3, "JPEG": 4, "PNG": 5} + for k, v in COMPRESSIONS.items(): + vars()[k] = v + + def _bitmap(self, header: int = 0, offset: int = 0) -> None: + """Read relevant info about the BMP""" + assert self.fp is not None + read, seek = self.fp.read, self.fp.seek + if header: + seek(header) + # read bmp header size @offset 14 (this is part of the header size) + file_info: dict[str, bool | int | tuple[int, ...]] = { + "header_size": i32(read(4)), + "direction": -1, + } + + # -------------------- If requested, read header at a specific position + # read the rest of the bmp header, without its size + assert isinstance(file_info["header_size"], int) + header_data = ImageFile._safe_read(self.fp, file_info["header_size"] - 4) + + # ------------------------------- Windows Bitmap v2, IBM OS/2 Bitmap v1 + # ----- This format has different offsets because of width/height types + # 12: BITMAPCOREHEADER/OS21XBITMAPHEADER + if file_info["header_size"] == 12: + file_info["width"] = i16(header_data, 0) + file_info["height"] = i16(header_data, 2) + file_info["planes"] = i16(header_data, 4) + file_info["bits"] = i16(header_data, 6) + file_info["compression"] = self.COMPRESSIONS["RAW"] + file_info["palette_padding"] = 3 + + # --------------------------------------------- Windows Bitmap v3 to v5 + # 40: BITMAPINFOHEADER + # 52: BITMAPV2HEADER + # 56: BITMAPV3HEADER + # 64: BITMAPCOREHEADER2/OS22XBITMAPHEADER + # 108: BITMAPV4HEADER + # 124: BITMAPV5HEADER + elif file_info["header_size"] in (40, 52, 56, 64, 108, 124): + file_info["y_flip"] = header_data[7] == 0xFF + file_info["direction"] = 1 if file_info["y_flip"] else -1 + file_info["width"] = i32(header_data, 0) + file_info["height"] = ( + i32(header_data, 4) + if not file_info["y_flip"] + else 2**32 - i32(header_data, 4) + ) + file_info["planes"] = i16(header_data, 8) + file_info["bits"] = i16(header_data, 10) + file_info["compression"] = i32(header_data, 12) + # byte size of pixel data + file_info["data_size"] = i32(header_data, 16) + file_info["pixels_per_meter"] = ( + i32(header_data, 20), + i32(header_data, 24), + ) + file_info["colors"] = i32(header_data, 28) + file_info["palette_padding"] = 4 + assert isinstance(file_info["pixels_per_meter"], tuple) + self.info["dpi"] = tuple(x / 39.3701 for x in file_info["pixels_per_meter"]) + if file_info["compression"] == self.COMPRESSIONS["BITFIELDS"]: + masks = ["r_mask", "g_mask", "b_mask"] + if len(header_data) >= 48: + if len(header_data) >= 52: + masks.append("a_mask") + else: + file_info["a_mask"] = 0x0 + for idx, mask in enumerate(masks): + file_info[mask] = i32(header_data, 36 + idx * 4) + else: + # 40 byte headers only have the three components in the + # bitfields masks, ref: + # https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx + # See also + # https://github.com/python-pillow/Pillow/issues/1293 + # There is a 4th component in the RGBQuad, in the alpha + # location, but it is listed as a reserved component, + # and it is not generally an alpha channel + file_info["a_mask"] = 0x0 + for mask in masks: + file_info[mask] = i32(read(4)) + assert isinstance(file_info["r_mask"], int) + assert isinstance(file_info["g_mask"], int) + assert isinstance(file_info["b_mask"], int) + assert isinstance(file_info["a_mask"], int) + file_info["rgb_mask"] = ( + file_info["r_mask"], + file_info["g_mask"], + file_info["b_mask"], + ) + file_info["rgba_mask"] = ( + file_info["r_mask"], + file_info["g_mask"], + file_info["b_mask"], + file_info["a_mask"], + ) + else: + msg = f"Unsupported BMP header type ({file_info['header_size']})" + raise OSError(msg) + + # ------------------ Special case : header is reported 40, which + # ---------------------- is shorter than real size for bpp >= 16 + assert isinstance(file_info["width"], int) + assert isinstance(file_info["height"], int) + self._size = file_info["width"], file_info["height"] + + # ------- If color count was not found in the header, compute from bits + assert isinstance(file_info["bits"], int) + file_info["colors"] = ( + file_info["colors"] + if file_info.get("colors", 0) + else (1 << file_info["bits"]) + ) + assert isinstance(file_info["colors"], int) + if offset == 14 + file_info["header_size"] and file_info["bits"] <= 8: + offset += 4 * file_info["colors"] + + # ---------------------- Check bit depth for unusual unsupported values + self._mode, raw_mode = BIT2MODE.get(file_info["bits"], ("", "")) + if not self.mode: + msg = f"Unsupported BMP pixel depth ({file_info['bits']})" + raise OSError(msg) + + # ---------------- Process BMP with Bitfields compression (not palette) + decoder_name = "raw" + if file_info["compression"] == self.COMPRESSIONS["BITFIELDS"]: + SUPPORTED: dict[int, list[tuple[int, ...]]] = { + 32: [ + (0xFF0000, 0xFF00, 0xFF, 0x0), + (0xFF000000, 0xFF0000, 0xFF00, 0x0), + (0xFF000000, 0xFF00, 0xFF, 0x0), + (0xFF000000, 0xFF0000, 0xFF00, 0xFF), + (0xFF, 0xFF00, 0xFF0000, 0xFF000000), + (0xFF0000, 0xFF00, 0xFF, 0xFF000000), + (0xFF000000, 0xFF00, 0xFF, 0xFF0000), + (0x0, 0x0, 0x0, 0x0), + ], + 24: [(0xFF0000, 0xFF00, 0xFF)], + 16: [(0xF800, 0x7E0, 0x1F), (0x7C00, 0x3E0, 0x1F)], + } + MASK_MODES = { + (32, (0xFF0000, 0xFF00, 0xFF, 0x0)): "BGRX", + (32, (0xFF000000, 0xFF0000, 0xFF00, 0x0)): "XBGR", + (32, (0xFF000000, 0xFF00, 0xFF, 0x0)): "BGXR", + (32, (0xFF000000, 0xFF0000, 0xFF00, 0xFF)): "ABGR", + (32, (0xFF, 0xFF00, 0xFF0000, 0xFF000000)): "RGBA", + (32, (0xFF0000, 0xFF00, 0xFF, 0xFF000000)): "BGRA", + (32, (0xFF000000, 0xFF00, 0xFF, 0xFF0000)): "BGAR", + (32, (0x0, 0x0, 0x0, 0x0)): "BGRA", + (24, (0xFF0000, 0xFF00, 0xFF)): "BGR", + (16, (0xF800, 0x7E0, 0x1F)): "BGR;16", + (16, (0x7C00, 0x3E0, 0x1F)): "BGR;15", + } + if file_info["bits"] in SUPPORTED: + if ( + file_info["bits"] == 32 + and file_info["rgba_mask"] in SUPPORTED[file_info["bits"]] + ): + assert isinstance(file_info["rgba_mask"], tuple) + raw_mode = MASK_MODES[(file_info["bits"], file_info["rgba_mask"])] + self._mode = "RGBA" if "A" in raw_mode else self.mode + elif ( + file_info["bits"] in (24, 16) + and file_info["rgb_mask"] in SUPPORTED[file_info["bits"]] + ): + assert isinstance(file_info["rgb_mask"], tuple) + raw_mode = MASK_MODES[(file_info["bits"], file_info["rgb_mask"])] + else: + msg = "Unsupported BMP bitfields layout" + raise OSError(msg) + else: + msg = "Unsupported BMP bitfields layout" + raise OSError(msg) + elif file_info["compression"] == self.COMPRESSIONS["RAW"]: + if file_info["bits"] == 32 and ( + header == 22 or USE_RAW_ALPHA # 32-bit .cur offset + ): + raw_mode, self._mode = "BGRA", "RGBA" + elif file_info["compression"] in ( + self.COMPRESSIONS["RLE8"], + self.COMPRESSIONS["RLE4"], + ): + decoder_name = "bmp_rle" + else: + msg = f"Unsupported BMP compression ({file_info['compression']})" + raise OSError(msg) + + # --------------- Once the header is processed, process the palette/LUT + if self.mode == "P": # Paletted for 1, 4 and 8 bit images + # ---------------------------------------------------- 1-bit images + if not (0 < file_info["colors"] <= 65536): + msg = f"Unsupported BMP Palette size ({file_info['colors']})" + raise OSError(msg) + else: + assert isinstance(file_info["palette_padding"], int) + padding = file_info["palette_padding"] + palette = read(padding * file_info["colors"]) + grayscale = True + indices = ( + (0, 255) + if file_info["colors"] == 2 + else list(range(file_info["colors"])) + ) + + # ----------------- Check if grayscale and ignore palette if so + for ind, val in enumerate(indices): + rgb = palette[ind * padding : ind * padding + 3] + if rgb != o8(val) * 3: + grayscale = False + + # ------- If all colors are gray, white or black, ditch palette + if grayscale: + self._mode = "1" if file_info["colors"] == 2 else "L" + raw_mode = self.mode + else: + self._mode = "P" + self.palette = ImagePalette.raw( + "BGRX" if padding == 4 else "BGR", palette + ) + + # ---------------------------- Finally set the tile data for the plugin + self.info["compression"] = file_info["compression"] + args: list[Any] = [raw_mode] + if decoder_name == "bmp_rle": + args.append(file_info["compression"] == self.COMPRESSIONS["RLE4"]) + else: + assert isinstance(file_info["width"], int) + args.append(((file_info["width"] * file_info["bits"] + 31) >> 3) & (~3)) + args.append(file_info["direction"]) + self.tile = [ + ImageFile._Tile( + decoder_name, + (0, 0, file_info["width"], file_info["height"]), + offset or self.fp.tell(), + tuple(args), + ) + ] + + def _open(self) -> None: + """Open file, check magic number and read header""" + # read 14 bytes: magic number, filesize, reserved, header final offset + assert self.fp is not None + head_data = self.fp.read(14) + # choke if the file does not have the required magic bytes + if not _accept(head_data): + msg = "Not a BMP file" + raise SyntaxError(msg) + # read the start position of the BMP image data (u32) + offset = i32(head_data, 10) + # load bitmap information (offset=raster info) + self._bitmap(offset=offset) + + +class BmpRleDecoder(ImageFile.PyDecoder): + _pulls_fd = True + + def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: + assert self.fd is not None + rle4 = self.args[1] + data = bytearray() + x = 0 + dest_length = self.state.xsize * self.state.ysize + while len(data) < dest_length: + pixels = self.fd.read(1) + byte = self.fd.read(1) + if not pixels or not byte: + break + num_pixels = pixels[0] + if num_pixels: + # encoded mode + if x + num_pixels > self.state.xsize: + # Too much data for row + num_pixels = max(0, self.state.xsize - x) + if rle4: + first_pixel = o8(byte[0] >> 4) + second_pixel = o8(byte[0] & 0x0F) + for index in range(num_pixels): + if index % 2 == 0: + data += first_pixel + else: + data += second_pixel + else: + data += byte * num_pixels + x += num_pixels + else: + if byte[0] == 0: + # end of line + while len(data) % self.state.xsize != 0: + data += b"\x00" + x = 0 + elif byte[0] == 1: + # end of bitmap + break + elif byte[0] == 2: + # delta + bytes_read = self.fd.read(2) + if len(bytes_read) < 2: + break + right, up = self.fd.read(2) + data += b"\x00" * (right + up * self.state.xsize) + x = len(data) % self.state.xsize + else: + # absolute mode + if rle4: + # 2 pixels per byte + byte_count = byte[0] // 2 + bytes_read = self.fd.read(byte_count) + for byte_read in bytes_read: + data += o8(byte_read >> 4) + data += o8(byte_read & 0x0F) + else: + byte_count = byte[0] + bytes_read = self.fd.read(byte_count) + data += bytes_read + if len(bytes_read) < byte_count: + break + x += byte[0] + + # align to 16-bit word boundary + if self.fd.tell() % 2 != 0: + self.fd.seek(1, os.SEEK_CUR) + rawmode = "L" if self.mode == "L" else "P" + self.set_as_raw(bytes(data), rawmode, (0, self.args[-1])) + return -1, 0 + + +# ============================================================================= +# Image plugin for the DIB format (BMP alias) +# ============================================================================= +class DibImageFile(BmpImageFile): + format = "DIB" + format_description = "Windows Bitmap" + + def _open(self) -> None: + self._bitmap() + + +# +# -------------------------------------------------------------------- +# Write BMP file + + +SAVE = { + "1": ("1", 1, 2), + "L": ("L", 8, 256), + "P": ("P", 8, 256), + "RGB": ("BGR", 24, 0), + "RGBA": ("BGRA", 32, 0), +} + + +def _dib_save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + _save(im, fp, filename, False) + + +def _save( + im: Image.Image, fp: IO[bytes], filename: str | bytes, bitmap_header: bool = True +) -> None: + try: + rawmode, bits, colors = SAVE[im.mode] + except KeyError as e: + msg = f"cannot write mode {im.mode} as BMP" + raise OSError(msg) from e + + info = im.encoderinfo + + dpi = info.get("dpi", (96, 96)) + + # 1 meter == 39.3701 inches + ppm = tuple(int(x * 39.3701 + 0.5) for x in dpi) + + stride = ((im.size[0] * bits + 7) // 8 + 3) & (~3) + header = 40 # or 64 for OS/2 version 2 + image = stride * im.size[1] + + if im.mode == "1": + palette = b"".join(o8(i) * 3 + b"\x00" for i in (0, 255)) + elif im.mode == "L": + palette = b"".join(o8(i) * 3 + b"\x00" for i in range(256)) + elif im.mode == "P": + palette = im.im.getpalette("RGB", "BGRX") + colors = len(palette) // 4 + else: + palette = None + + # bitmap header + if bitmap_header: + offset = 14 + header + colors * 4 + file_size = offset + image + if file_size > 2**32 - 1: + msg = "File size is too large for the BMP format" + raise ValueError(msg) + fp.write( + b"BM" # file type (magic) + + o32(file_size) # 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]) # resolution + + o32(ppm[1]) # resolution + + o32(colors) # colors used + + o32(colors) # colors important + ) + + fp.write(b"\0" * (header - 40)) # padding (for OS/2 format) + + if palette: + fp.write(palette) + + ImageFile._save( + im, fp, [ImageFile._Tile("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") + +Image.register_mime(BmpImageFile.format, "image/bmp") + +Image.register_decoder("bmp_rle", BmpRleDecoder) + +Image.register_open(DibImageFile.format, DibImageFile, _dib_accept) +Image.register_save(DibImageFile.format, _dib_save) + +Image.register_extension(DibImageFile.format, ".dib") + +Image.register_mime(DibImageFile.format, "image/bmp") diff --git a/voxvera/vendor/PIL/BufrStubImagePlugin.py b/voxvera/vendor/PIL/BufrStubImagePlugin.py new file mode 100644 index 0000000..264564d --- /dev/null +++ b/voxvera/vendor/PIL/BufrStubImagePlugin.py @@ -0,0 +1,76 @@ +# +# 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 __future__ import annotations + +import os +from typing import IO + +from . import Image, ImageFile + +_handler = None + + +def register_handler(handler: ImageFile.StubHandler | None) -> None: + """ + Install application-specific BUFR image handler. + + :param handler: Handler object. + """ + global _handler + _handler = handler + + +# -------------------------------------------------------------------- +# Image adapter + + +def _accept(prefix: bytes) -> bool: + return prefix.startswith((b"BUFR", b"ZCZC")) + + +class BufrStubImageFile(ImageFile.StubImageFile): + format = "BUFR" + format_description = "BUFR" + + def _open(self) -> None: + assert self.fp is not None + if not _accept(self.fp.read(4)): + msg = "Not a BUFR file" + raise SyntaxError(msg) + + self.fp.seek(-4, os.SEEK_CUR) + + # make something up + self._mode = "F" + self._size = 1, 1 + + loader = self._load() + if loader: + loader.open(self) + + def _load(self) -> ImageFile.StubHandler | None: + return _handler + + +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + if _handler is None or not hasattr(_handler, "save"): + msg = "BUFR save handler not installed" + raise OSError(msg) + _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/voxvera/vendor/PIL/ContainerIO.py b/voxvera/vendor/PIL/ContainerIO.py new file mode 100644 index 0000000..ec9e66c --- /dev/null +++ b/voxvera/vendor/PIL/ContainerIO.py @@ -0,0 +1,173 @@ +# +# 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. +# +from __future__ import annotations + +import io +from collections.abc import Iterable +from typing import IO, AnyStr, NoReturn + + +class ContainerIO(IO[AnyStr]): + """ + A file object that provides read access to a part of an existing + file (for example a TAR file). + """ + + def __init__(self, file: IO[AnyStr], offset: int, length: int) -> None: + """ + Create file object. + + :param file: Existing file. + :param offset: Start of region, in bytes. + :param length: Size of region, in bytes. + """ + self.fh: IO[AnyStr] = file + self.pos = 0 + self.offset = offset + self.length = length + self.fh.seek(offset) + + ## + # Always false. + + def isatty(self) -> bool: + return False + + def seekable(self) -> bool: + return True + + def seek(self, offset: int, mode: int = io.SEEK_SET) -> int: + """ + 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. + :returns: Offset from start of region, in bytes. + """ + 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) + return self.pos + + def tell(self) -> int: + """ + Get current file pointer. + + :returns: Offset from start of region, in bytes. + """ + return self.pos + + def readable(self) -> bool: + return True + + def read(self, n: int = -1) -> AnyStr: + """ + Read data. + + :param n: Number of bytes to read. If omitted, zero or negative, + read until end of region. + :returns: An 8-bit string. + """ + if n > 0: + n = min(n, self.length - self.pos) + else: + n = self.length - self.pos + if n <= 0: # EOF + return b"" if "b" in self.fh.mode else "" # type: ignore[return-value] + self.pos = self.pos + n + return self.fh.read(n) + + def readline(self, n: int = -1) -> AnyStr: + """ + Read a line of text. + + :param n: Number of bytes to read. If omitted, zero or negative, + read until end of line. + :returns: An 8-bit string. + """ + s: AnyStr = b"" if "b" in self.fh.mode else "" # type: ignore[assignment] + newline_character = b"\n" if "b" in self.fh.mode else "\n" + while True: + c = self.read(1) + if not c: + break + s = s + c + if c == newline_character or len(s) == n: + break + return s + + def readlines(self, n: int | None = -1) -> list[AnyStr]: + """ + Read multiple lines of text. + + :param n: Number of lines to read. If omitted, zero, negative or None, + read until end of region. + :returns: A list of 8-bit strings. + """ + lines = [] + while True: + s = self.readline() + if not s: + break + lines.append(s) + if len(lines) == n: + break + return lines + + def writable(self) -> bool: + return False + + def write(self, b: AnyStr) -> NoReturn: + raise NotImplementedError() + + def writelines(self, lines: Iterable[AnyStr]) -> NoReturn: + raise NotImplementedError() + + def truncate(self, size: int | None = None) -> int: + raise NotImplementedError() + + def __enter__(self) -> ContainerIO[AnyStr]: + return self + + def __exit__(self, *args: object) -> None: + self.close() + + def __iter__(self) -> ContainerIO[AnyStr]: + return self + + def __next__(self) -> AnyStr: + line = self.readline() + if not line: + msg = "end of region" + raise StopIteration(msg) + return line + + def fileno(self) -> int: + return self.fh.fileno() + + def flush(self) -> None: + self.fh.flush() + + def close(self) -> None: + self.fh.close() diff --git a/voxvera/vendor/PIL/CurImagePlugin.py b/voxvera/vendor/PIL/CurImagePlugin.py new file mode 100644 index 0000000..9c188e0 --- /dev/null +++ b/voxvera/vendor/PIL/CurImagePlugin.py @@ -0,0 +1,75 @@ +# +# 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. +# +from __future__ import annotations + +from . import BmpImagePlugin, Image +from ._binary import i16le as i16 +from ._binary import i32le as i32 + +# +# -------------------------------------------------------------------- + + +def _accept(prefix: bytes) -> bool: + return prefix.startswith(b"\0\0\2\0") + + +## +# Image plugin for Windows Cursor files. + + +class CurImageFile(BmpImagePlugin.BmpImageFile): + format = "CUR" + format_description = "Windows Cursor" + + def _open(self) -> None: + assert self.fp is not None + offset = self.fp.tell() + + # check magic + s = self.fp.read(6) + if not _accept(s): + msg = "not a CUR file" + raise SyntaxError(msg) + + # 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 s[0] > m[0] and s[1] > m[1]: + m = s + if not m: + msg = "No cursors were found" + raise TypeError(msg) + + # load as bitmap + self._bitmap(i32(m, 12) + offset) + + # patch up the bitmap height + self._size = self.size[0], self.size[1] // 2 + self.tile = [self.tile[0]._replace(extents=(0, 0) + self.size)] + + +# +# -------------------------------------------------------------------- + +Image.register_open(CurImageFile.format, CurImageFile, _accept) + +Image.register_extension(CurImageFile.format, ".cur") diff --git a/voxvera/vendor/PIL/DcxImagePlugin.py b/voxvera/vendor/PIL/DcxImagePlugin.py new file mode 100644 index 0000000..d3f456d --- /dev/null +++ b/voxvera/vendor/PIL/DcxImagePlugin.py @@ -0,0 +1,84 @@ +# +# 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. +# +from __future__ import annotations + +from . import Image +from ._binary import i32le as i32 +from ._util import DeferredError +from .PcxImagePlugin import PcxImageFile + +MAGIC = 0x3ADE68B1 # QUIZ: what's this value, then? + + +def _accept(prefix: bytes) -> bool: + return len(prefix) >= 4 and i32(prefix) == MAGIC + + +## +# Image plugin for the Intel DCX format. + + +class DcxImageFile(PcxImageFile): + format = "DCX" + format_description = "Intel DCX" + _close_exclusive_fp_after_loading = False + + def _open(self) -> None: + # Header + assert self.fp is not None + s = self.fp.read(4) + if not _accept(s): + msg = "not a DCX file" + raise SyntaxError(msg) + + # 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.frame = -1 + self.n_frames = len(self._offset) + self.is_animated = self.n_frames > 1 + self.seek(0) + + def seek(self, frame: int) -> None: + if not self._seek_check(frame): + return + if isinstance(self._fp, DeferredError): + raise self._fp.ex + self.frame = frame + self.fp = self._fp + self.fp.seek(self._offset[frame]) + PcxImageFile._open(self) + + def tell(self) -> int: + return self.frame + + +Image.register_open(DcxImageFile.format, DcxImageFile, _accept) + +Image.register_extension(DcxImageFile.format, ".dcx") diff --git a/voxvera/vendor/PIL/DdsImagePlugin.py b/voxvera/vendor/PIL/DdsImagePlugin.py new file mode 100644 index 0000000..312f602 --- /dev/null +++ b/voxvera/vendor/PIL/DdsImagePlugin.py @@ -0,0 +1,625 @@ +""" +A Pillow plugin for .dds files (S3TC-compressed aka DXTC) +Jerome Leclanche + +Documentation: +https://web.archive.org/web/20170802060935/http://oss.sgi.com/projects/ogl-sample/registry/EXT/texture_compression_s3tc.txt + +The contents of this file are hereby released in the public domain (CC0) +Full text of the CC0 license: +https://creativecommons.org/publicdomain/zero/1.0/ +""" + +from __future__ import annotations + +import struct +import sys +from enum import IntEnum, IntFlag +from typing import IO + +from . import Image, ImageFile, ImagePalette +from ._binary import i32le as i32 +from ._binary import o8 +from ._binary import o32le as o32 + +# Magic ("DDS ") +DDS_MAGIC = 0x20534444 + + +# DDS flags +class DDSD(IntFlag): + CAPS = 0x1 + HEIGHT = 0x2 + WIDTH = 0x4 + PITCH = 0x8 + PIXELFORMAT = 0x1000 + MIPMAPCOUNT = 0x20000 + LINEARSIZE = 0x80000 + DEPTH = 0x800000 + + +# DDS caps +class DDSCAPS(IntFlag): + COMPLEX = 0x8 + TEXTURE = 0x1000 + MIPMAP = 0x400000 + + +class DDSCAPS2(IntFlag): + CUBEMAP = 0x200 + CUBEMAP_POSITIVEX = 0x400 + CUBEMAP_NEGATIVEX = 0x800 + CUBEMAP_POSITIVEY = 0x1000 + CUBEMAP_NEGATIVEY = 0x2000 + CUBEMAP_POSITIVEZ = 0x4000 + CUBEMAP_NEGATIVEZ = 0x8000 + VOLUME = 0x200000 + + +# Pixel Format +class DDPF(IntFlag): + ALPHAPIXELS = 0x1 + ALPHA = 0x2 + FOURCC = 0x4 + PALETTEINDEXED8 = 0x20 + RGB = 0x40 + LUMINANCE = 0x20000 + + +# dxgiformat.h +class DXGI_FORMAT(IntEnum): + UNKNOWN = 0 + R32G32B32A32_TYPELESS = 1 + R32G32B32A32_FLOAT = 2 + R32G32B32A32_UINT = 3 + R32G32B32A32_SINT = 4 + R32G32B32_TYPELESS = 5 + R32G32B32_FLOAT = 6 + R32G32B32_UINT = 7 + R32G32B32_SINT = 8 + R16G16B16A16_TYPELESS = 9 + R16G16B16A16_FLOAT = 10 + R16G16B16A16_UNORM = 11 + R16G16B16A16_UINT = 12 + R16G16B16A16_SNORM = 13 + R16G16B16A16_SINT = 14 + R32G32_TYPELESS = 15 + R32G32_FLOAT = 16 + R32G32_UINT = 17 + R32G32_SINT = 18 + R32G8X24_TYPELESS = 19 + D32_FLOAT_S8X24_UINT = 20 + R32_FLOAT_X8X24_TYPELESS = 21 + X32_TYPELESS_G8X24_UINT = 22 + R10G10B10A2_TYPELESS = 23 + R10G10B10A2_UNORM = 24 + R10G10B10A2_UINT = 25 + R11G11B10_FLOAT = 26 + R8G8B8A8_TYPELESS = 27 + R8G8B8A8_UNORM = 28 + R8G8B8A8_UNORM_SRGB = 29 + R8G8B8A8_UINT = 30 + R8G8B8A8_SNORM = 31 + R8G8B8A8_SINT = 32 + R16G16_TYPELESS = 33 + R16G16_FLOAT = 34 + R16G16_UNORM = 35 + R16G16_UINT = 36 + R16G16_SNORM = 37 + R16G16_SINT = 38 + R32_TYPELESS = 39 + D32_FLOAT = 40 + R32_FLOAT = 41 + R32_UINT = 42 + R32_SINT = 43 + R24G8_TYPELESS = 44 + D24_UNORM_S8_UINT = 45 + R24_UNORM_X8_TYPELESS = 46 + X24_TYPELESS_G8_UINT = 47 + R8G8_TYPELESS = 48 + R8G8_UNORM = 49 + R8G8_UINT = 50 + R8G8_SNORM = 51 + R8G8_SINT = 52 + R16_TYPELESS = 53 + R16_FLOAT = 54 + D16_UNORM = 55 + R16_UNORM = 56 + R16_UINT = 57 + R16_SNORM = 58 + R16_SINT = 59 + R8_TYPELESS = 60 + R8_UNORM = 61 + R8_UINT = 62 + R8_SNORM = 63 + R8_SINT = 64 + A8_UNORM = 65 + R1_UNORM = 66 + R9G9B9E5_SHAREDEXP = 67 + R8G8_B8G8_UNORM = 68 + G8R8_G8B8_UNORM = 69 + BC1_TYPELESS = 70 + BC1_UNORM = 71 + BC1_UNORM_SRGB = 72 + BC2_TYPELESS = 73 + BC2_UNORM = 74 + BC2_UNORM_SRGB = 75 + BC3_TYPELESS = 76 + BC3_UNORM = 77 + BC3_UNORM_SRGB = 78 + BC4_TYPELESS = 79 + BC4_UNORM = 80 + BC4_SNORM = 81 + BC5_TYPELESS = 82 + BC5_UNORM = 83 + BC5_SNORM = 84 + B5G6R5_UNORM = 85 + B5G5R5A1_UNORM = 86 + B8G8R8A8_UNORM = 87 + B8G8R8X8_UNORM = 88 + R10G10B10_XR_BIAS_A2_UNORM = 89 + B8G8R8A8_TYPELESS = 90 + B8G8R8A8_UNORM_SRGB = 91 + B8G8R8X8_TYPELESS = 92 + B8G8R8X8_UNORM_SRGB = 93 + BC6H_TYPELESS = 94 + BC6H_UF16 = 95 + BC6H_SF16 = 96 + BC7_TYPELESS = 97 + BC7_UNORM = 98 + BC7_UNORM_SRGB = 99 + AYUV = 100 + Y410 = 101 + Y416 = 102 + NV12 = 103 + P010 = 104 + P016 = 105 + OPAQUE_420 = 106 + YUY2 = 107 + Y210 = 108 + Y216 = 109 + NV11 = 110 + AI44 = 111 + IA44 = 112 + P8 = 113 + A8P8 = 114 + B4G4R4A4_UNORM = 115 + P208 = 130 + V208 = 131 + V408 = 132 + SAMPLER_FEEDBACK_MIN_MIP_OPAQUE = 189 + SAMPLER_FEEDBACK_MIP_REGION_USED_OPAQUE = 190 + + +class D3DFMT(IntEnum): + UNKNOWN = 0 + R8G8B8 = 20 + A8R8G8B8 = 21 + X8R8G8B8 = 22 + R5G6B5 = 23 + X1R5G5B5 = 24 + A1R5G5B5 = 25 + A4R4G4B4 = 26 + R3G3B2 = 27 + A8 = 28 + A8R3G3B2 = 29 + X4R4G4B4 = 30 + A2B10G10R10 = 31 + A8B8G8R8 = 32 + X8B8G8R8 = 33 + G16R16 = 34 + A2R10G10B10 = 35 + A16B16G16R16 = 36 + A8P8 = 40 + P8 = 41 + L8 = 50 + A8L8 = 51 + A4L4 = 52 + V8U8 = 60 + L6V5U5 = 61 + X8L8V8U8 = 62 + Q8W8V8U8 = 63 + V16U16 = 64 + A2W10V10U10 = 67 + D16_LOCKABLE = 70 + D32 = 71 + D15S1 = 73 + D24S8 = 75 + D24X8 = 77 + D24X4S4 = 79 + D16 = 80 + D32F_LOCKABLE = 82 + D24FS8 = 83 + D32_LOCKABLE = 84 + S8_LOCKABLE = 85 + L16 = 81 + VERTEXDATA = 100 + INDEX16 = 101 + INDEX32 = 102 + Q16W16V16U16 = 110 + R16F = 111 + G16R16F = 112 + A16B16G16R16F = 113 + R32F = 114 + G32R32F = 115 + A32B32G32R32F = 116 + CxV8U8 = 117 + A1 = 118 + A2B10G10R10_XR_BIAS = 119 + BINARYBUFFER = 199 + + UYVY = i32(b"UYVY") + R8G8_B8G8 = i32(b"RGBG") + YUY2 = i32(b"YUY2") + G8R8_G8B8 = i32(b"GRGB") + DXT1 = i32(b"DXT1") + DXT2 = i32(b"DXT2") + DXT3 = i32(b"DXT3") + DXT4 = i32(b"DXT4") + DXT5 = i32(b"DXT5") + DX10 = i32(b"DX10") + BC4S = i32(b"BC4S") + BC4U = i32(b"BC4U") + BC5S = i32(b"BC5S") + BC5U = i32(b"BC5U") + ATI1 = i32(b"ATI1") + ATI2 = i32(b"ATI2") + MULTI2_ARGB8 = i32(b"MET1") + + +# Backward compatibility layer +module = sys.modules[__name__] +for item in DDSD: + assert item.name is not None + setattr(module, f"DDSD_{item.name}", item.value) +for item1 in DDSCAPS: + assert item1.name is not None + setattr(module, f"DDSCAPS_{item1.name}", item1.value) +for item2 in DDSCAPS2: + assert item2.name is not None + setattr(module, f"DDSCAPS2_{item2.name}", item2.value) +for item3 in DDPF: + assert item3.name is not None + setattr(module, f"DDPF_{item3.name}", item3.value) + +DDS_FOURCC = DDPF.FOURCC +DDS_RGB = DDPF.RGB +DDS_RGBA = DDPF.RGB | DDPF.ALPHAPIXELS +DDS_LUMINANCE = DDPF.LUMINANCE +DDS_LUMINANCEA = DDPF.LUMINANCE | DDPF.ALPHAPIXELS +DDS_ALPHA = DDPF.ALPHA +DDS_PAL8 = DDPF.PALETTEINDEXED8 + +DDS_HEADER_FLAGS_TEXTURE = DDSD.CAPS | DDSD.HEIGHT | DDSD.WIDTH | DDSD.PIXELFORMAT +DDS_HEADER_FLAGS_MIPMAP = DDSD.MIPMAPCOUNT +DDS_HEADER_FLAGS_VOLUME = DDSD.DEPTH +DDS_HEADER_FLAGS_PITCH = DDSD.PITCH +DDS_HEADER_FLAGS_LINEARSIZE = DDSD.LINEARSIZE + +DDS_HEIGHT = DDSD.HEIGHT +DDS_WIDTH = DDSD.WIDTH + +DDS_SURFACE_FLAGS_TEXTURE = DDSCAPS.TEXTURE +DDS_SURFACE_FLAGS_MIPMAP = DDSCAPS.COMPLEX | DDSCAPS.MIPMAP +DDS_SURFACE_FLAGS_CUBEMAP = DDSCAPS.COMPLEX + +DDS_CUBEMAP_POSITIVEX = DDSCAPS2.CUBEMAP | DDSCAPS2.CUBEMAP_POSITIVEX +DDS_CUBEMAP_NEGATIVEX = DDSCAPS2.CUBEMAP | DDSCAPS2.CUBEMAP_NEGATIVEX +DDS_CUBEMAP_POSITIVEY = DDSCAPS2.CUBEMAP | DDSCAPS2.CUBEMAP_POSITIVEY +DDS_CUBEMAP_NEGATIVEY = DDSCAPS2.CUBEMAP | DDSCAPS2.CUBEMAP_NEGATIVEY +DDS_CUBEMAP_POSITIVEZ = DDSCAPS2.CUBEMAP | DDSCAPS2.CUBEMAP_POSITIVEZ +DDS_CUBEMAP_NEGATIVEZ = DDSCAPS2.CUBEMAP | DDSCAPS2.CUBEMAP_NEGATIVEZ + +DXT1_FOURCC = D3DFMT.DXT1 +DXT3_FOURCC = D3DFMT.DXT3 +DXT5_FOURCC = D3DFMT.DXT5 + +DXGI_FORMAT_R8G8B8A8_TYPELESS = DXGI_FORMAT.R8G8B8A8_TYPELESS +DXGI_FORMAT_R8G8B8A8_UNORM = DXGI_FORMAT.R8G8B8A8_UNORM +DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = DXGI_FORMAT.R8G8B8A8_UNORM_SRGB +DXGI_FORMAT_BC5_TYPELESS = DXGI_FORMAT.BC5_TYPELESS +DXGI_FORMAT_BC5_UNORM = DXGI_FORMAT.BC5_UNORM +DXGI_FORMAT_BC5_SNORM = DXGI_FORMAT.BC5_SNORM +DXGI_FORMAT_BC6H_UF16 = DXGI_FORMAT.BC6H_UF16 +DXGI_FORMAT_BC6H_SF16 = DXGI_FORMAT.BC6H_SF16 +DXGI_FORMAT_BC7_TYPELESS = DXGI_FORMAT.BC7_TYPELESS +DXGI_FORMAT_BC7_UNORM = DXGI_FORMAT.BC7_UNORM +DXGI_FORMAT_BC7_UNORM_SRGB = DXGI_FORMAT.BC7_UNORM_SRGB + + +class DdsImageFile(ImageFile.ImageFile): + format = "DDS" + format_description = "DirectDraw Surface" + + def _open(self) -> None: + assert self.fp is not None + if not _accept(self.fp.read(4)): + msg = "not a DDS file" + raise SyntaxError(msg) + (header_size,) = struct.unpack(" None: + pass + + +class DdsRgbDecoder(ImageFile.PyDecoder): + _pulls_fd = True + + def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: + assert self.fd is not None + bitcount, masks = self.args + + # Some masks will be padded with zeros, e.g. R 0b11 G 0b1100 + # Calculate how many zeros each mask is padded with + mask_offsets = [] + # And the maximum value of each channel without the padding + mask_totals = [] + for mask in masks: + offset = 0 + if mask != 0: + while mask >> (offset + 1) << (offset + 1) == mask: + offset += 1 + mask_offsets.append(offset) + mask_totals.append(mask >> offset) + + data = bytearray() + bytecount = bitcount // 8 + dest_length = self.state.xsize * self.state.ysize * len(masks) + while len(data) < dest_length: + value = int.from_bytes(self.fd.read(bytecount), "little") + for i, mask in enumerate(masks): + masked_value = value & mask + # Remove the zero padding, and scale it to 8 bits + data += o8( + int(((masked_value >> mask_offsets[i]) / mask_totals[i]) * 255) + if mask_totals[i] + else 0 + ) + self.set_as_raw(data) + return -1, 0 + + +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + if im.mode not in ("RGB", "RGBA", "L", "LA"): + msg = f"cannot write mode {im.mode} as DDS" + raise OSError(msg) + + flags = DDSD.CAPS | DDSD.HEIGHT | DDSD.WIDTH | DDSD.PIXELFORMAT + bitcount = len(im.getbands()) * 8 + pixel_format = im.encoderinfo.get("pixel_format") + args: tuple[int] | str + if pixel_format: + codec_name = "bcn" + flags |= DDSD.LINEARSIZE + pitch = (im.width + 3) * 4 + rgba_mask = [0, 0, 0, 0] + pixel_flags = DDPF.FOURCC + if pixel_format == "DXT1": + fourcc = D3DFMT.DXT1 + args = (1,) + elif pixel_format == "DXT3": + fourcc = D3DFMT.DXT3 + args = (2,) + elif pixel_format == "DXT5": + fourcc = D3DFMT.DXT5 + args = (3,) + else: + fourcc = D3DFMT.DX10 + if pixel_format == "BC2": + args = (2,) + dxgi_format = DXGI_FORMAT.BC2_TYPELESS + elif pixel_format == "BC3": + args = (3,) + dxgi_format = DXGI_FORMAT.BC3_TYPELESS + elif pixel_format == "BC5": + args = (5,) + dxgi_format = DXGI_FORMAT.BC5_TYPELESS + if im.mode != "RGB": + msg = "only RGB mode can be written as BC5" + raise OSError(msg) + else: + msg = f"cannot write pixel format {pixel_format}" + raise OSError(msg) + else: + codec_name = "raw" + flags |= DDSD.PITCH + pitch = (im.width * bitcount + 7) // 8 + + alpha = im.mode[-1] == "A" + if im.mode[0] == "L": + pixel_flags = DDPF.LUMINANCE + args = im.mode + if alpha: + rgba_mask = [0x000000FF, 0x000000FF, 0x000000FF] + else: + rgba_mask = [0xFF000000, 0xFF000000, 0xFF000000] + else: + pixel_flags = DDPF.RGB + args = im.mode[::-1] + rgba_mask = [0x00FF0000, 0x0000FF00, 0x000000FF] + + if alpha: + r, g, b, a = im.split() + im = Image.merge("RGBA", (a, r, g, b)) + if alpha: + pixel_flags |= DDPF.ALPHAPIXELS + rgba_mask.append(0xFF000000 if alpha else 0) + + fourcc = D3DFMT.UNKNOWN + fp.write( + o32(DDS_MAGIC) + + struct.pack( + "<7I", + 124, # header size + flags, # flags + im.height, + im.width, + pitch, + 0, # depth + 0, # mipmaps + ) + + struct.pack("11I", *((0,) * 11)) # reserved + # pfsize, pfflags, fourcc, bitcount + + struct.pack("<4I", 32, pixel_flags, fourcc, bitcount) + + struct.pack("<4I", *rgba_mask) # dwRGBABitMask + + struct.pack("<5I", DDSCAPS.TEXTURE, 0, 0, 0, 0) + ) + if fourcc == D3DFMT.DX10: + fp.write( + # dxgi_format, 2D resource, misc, array size, straight alpha + struct.pack("<5I", dxgi_format, 3, 0, 0, 1) + ) + ImageFile._save(im, fp, [ImageFile._Tile(codec_name, (0, 0) + im.size, 0, args)]) + + +def _accept(prefix: bytes) -> bool: + return prefix.startswith(b"DDS ") + + +Image.register_open(DdsImageFile.format, DdsImageFile, _accept) +Image.register_decoder("dds_rgb", DdsRgbDecoder) +Image.register_save(DdsImageFile.format, _save) +Image.register_extension(DdsImageFile.format, ".dds") diff --git a/voxvera/vendor/PIL/EpsImagePlugin.py b/voxvera/vendor/PIL/EpsImagePlugin.py new file mode 100644 index 0000000..2effb81 --- /dev/null +++ b/voxvera/vendor/PIL/EpsImagePlugin.py @@ -0,0 +1,481 @@ +# +# 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. +# +from __future__ import annotations + +import io +import os +import re +import subprocess +import sys +import tempfile +from typing import IO + +from . import Image, ImageFile +from ._binary import i32le as i32 + +# -------------------------------------------------------------------- + + +split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$") +field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$") + +gs_binary: str | bool | None = None +gs_windows_binary = None + + +def has_ghostscript() -> bool: + global gs_binary, gs_windows_binary + if gs_binary is None: + if sys.platform.startswith("win"): + if gs_windows_binary is None: + import shutil + + for binary in ("gswin32c", "gswin64c", "gs"): + if shutil.which(binary) is not None: + gs_windows_binary = binary + break + else: + gs_windows_binary = False + gs_binary = gs_windows_binary + else: + try: + subprocess.check_call(["gs", "--version"], stdout=subprocess.DEVNULL) + gs_binary = "gs" + except OSError: + gs_binary = False + return gs_binary is not False + + +def Ghostscript( + tile: list[ImageFile._Tile], + size: tuple[int, int], + fp: IO[bytes], + scale: int = 1, + transparency: bool = False, +) -> Image.core.ImagingCore: + """Render an image using Ghostscript""" + global gs_binary + if not has_ghostscript(): + msg = "Unable to locate Ghostscript on paths" + raise OSError(msg) + assert isinstance(gs_binary, str) + + # Unpack decoder tile + args = tile[0].args + assert isinstance(args, tuple) + length, bbox = args + + # Hack to support hi-res rendering + scale = int(scale) or 1 + width = size[0] * scale + height = size[1] * scale + # resolution is dependent on bbox and size + res_x = 72.0 * width / (bbox[2] - bbox[0]) + res_y = 72.0 * height / (bbox[3] - bbox[1]) + + 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, io.SEEK_END) + 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) + + if transparency: + # "RGBA" + device = "pngalpha" + else: + # "pnmraw" automatically chooses between + # PBM ("1"), PGM ("L"), and PPM ("RGB"). + device = "pnmraw" + + # Build Ghostscript command + command = [ + gs_binary, + "-q", # quiet mode + f"-g{width:d}x{height:d}", # set output geometry (pixels) + f"-r{res_x:f}x{res_y:f}", # set input DPI (dots per inch) + "-dBATCH", # exit after processing + "-dNOPAUSE", # don't pause between pages + "-dSAFER", # safe mode + f"-sDEVICE={device}", + f"-sOutputFile={outfile}", # output file + # adjust for image origin + "-c", + f"{-bbox[0]} {-bbox[1]} translate", + "-f", + infile, # input file + # showpage (see https://bugs.ghostscript.com/show_bug.cgi?id=698272) + "-c", + "showpage", + ] + + # push data through Ghostscript + try: + startupinfo = None + if sys.platform.startswith("win"): + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + subprocess.check_call(command, startupinfo=startupinfo) + with Image.open(outfile) as out_im: + out_im.load() + return out_im.im.copy() + finally: + try: + os.unlink(outfile) + if infile_temp: + os.unlink(infile_temp) + except OSError: + pass + + +def _accept(prefix: bytes) -> bool: + return prefix.startswith(b"%!PS") or ( + len(prefix) >= 4 and 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", 4: "CMYK"} + + def _open(self) -> None: + assert self.fp is not None + (length, offset) = self._find_offset(self.fp) + + # go to offset - start of "%!PS" + self.fp.seek(offset) + + self._mode = "RGB" + + # When reading header comments, the first comment is used. + # When reading trailer comments, the last comment is used. + bounding_box: list[int] | None = None + imagedata_size: tuple[int, int] | None = None + + byte_arr = bytearray(255) + bytes_mv = memoryview(byte_arr) + bytes_read = 0 + reading_header_comments = True + reading_trailer_comments = False + trailer_reached = False + + def check_required_header_comments() -> None: + """ + The EPS specification requires that some headers exist. + This should be checked when the header comments formally end, + when image data starts, or when the file ends, whichever comes first. + """ + if "PS-Adobe" not in self.info: + msg = 'EPS header missing "%!PS-Adobe" comment' + raise SyntaxError(msg) + if "BoundingBox" not in self.info: + msg = 'EPS header missing "%%BoundingBox" comment' + raise SyntaxError(msg) + + def read_comment(s: str) -> bool: + nonlocal bounding_box, reading_trailer_comments + try: + m = split.match(s) + except re.error as e: + msg = "not an EPS file" + raise SyntaxError(msg) from e + + if not m: + return False + + k, v = m.group(1, 2) + self.info[k] = v + if k == "BoundingBox": + if v == "(atend)": + reading_trailer_comments = True + elif not bounding_box or (trailer_reached and reading_trailer_comments): + try: + # Note: The DSC spec says that BoundingBox + # fields should be integers, but some drivers + # put floating point values there anyway. + bounding_box = [int(float(i)) for i in v.split()] + except Exception: + pass + return True + + while True: + byte = self.fp.read(1) + if byte == b"": + # if we didn't read a byte we must be at the end of the file + if bytes_read == 0: + if reading_header_comments: + check_required_header_comments() + break + elif byte in b"\r\n": + # if we read a line ending character, ignore it and parse what + # we have already read. if we haven't read any other characters, + # continue reading + if bytes_read == 0: + continue + else: + # ASCII/hexadecimal lines in an EPS file must not exceed + # 255 characters, not including line ending characters + if bytes_read >= 255: + # only enforce this for lines starting with a "%", + # otherwise assume it's binary data + if byte_arr[0] == ord("%"): + msg = "not an EPS file" + raise SyntaxError(msg) + else: + if reading_header_comments: + check_required_header_comments() + reading_header_comments = False + # reset bytes_read so we can keep reading + # data until the end of the line + bytes_read = 0 + byte_arr[bytes_read] = byte[0] + bytes_read += 1 + continue + + if reading_header_comments: + # Load EPS header + + # if this line doesn't start with a "%", + # or does start with "%%EndComments", + # then we've reached the end of the header/comments + if byte_arr[0] != ord("%") or bytes_mv[:13] == b"%%EndComments": + check_required_header_comments() + reading_header_comments = False + continue + + s = str(bytes_mv[:bytes_read], "latin-1") + if not read_comment(s): + m = field.match(s) + if m: + k = m.group(1) + if k.startswith("PS-Adobe"): + self.info["PS-Adobe"] = 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: + msg = "bad EPS header" + raise OSError(msg) + elif bytes_mv[:11] == b"%ImageData:": + # Check for an "ImageData" descriptor + # https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577413_pgfId-1035096 + + # If we've already read an "ImageData" descriptor, + # don't read another one. + if imagedata_size: + bytes_read = 0 + continue + + # Values: + # columns + # rows + # bit depth (1 or 8) + # mode (1: L, 2: LAB, 3: RGB, 4: CMYK) + # number of padding channels + # block size (number of bytes per row per channel) + # binary/ascii (1: binary, 2: ascii) + # data start identifier (the image data follows after a single line + # consisting only of this quoted value) + image_data_values = byte_arr[11:bytes_read].split(None, 7) + columns, rows, bit_depth, mode_id = ( + int(value) for value in image_data_values[:4] + ) + + if bit_depth == 1: + self._mode = "1" + elif bit_depth == 8: + try: + self._mode = self.mode_map[mode_id] + except ValueError: + break + else: + break + + # Parse the columns and rows after checking the bit depth and mode + # in case the bit depth and/or mode are invalid. + imagedata_size = columns, rows + elif bytes_mv[:5] == b"%%EOF": + break + elif trailer_reached and reading_trailer_comments: + # Load EPS trailer + s = str(bytes_mv[:bytes_read], "latin-1") + read_comment(s) + elif bytes_mv[:9] == b"%%Trailer": + trailer_reached = True + elif bytes_mv[:14] == b"%%BeginBinary:": + bytecount = int(byte_arr[14:bytes_read]) + self.fp.seek(bytecount, os.SEEK_CUR) + bytes_read = 0 + + # A "BoundingBox" is always required, + # even if an "ImageData" descriptor size exists. + if not bounding_box: + msg = "cannot determine EPS bounding box" + raise OSError(msg) + + # An "ImageData" size takes precedence over the "BoundingBox". + self._size = imagedata_size or ( + bounding_box[2] - bounding_box[0], + bounding_box[3] - bounding_box[1], + ) + + self.tile = [ + ImageFile._Tile("eps", (0, 0) + self.size, offset, (length, bounding_box)) + ] + + def _find_offset(self, fp: IO[bytes]) -> tuple[int, int]: + s = fp.read(4) + + if s == b"%!PS": + # for HEAD without binary preview + fp.seek(0, io.SEEK_END) + length = fp.tell() + offset = 0 + elif i32(s) == 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: + # https://web.archive.org/web/20160528181353/http://partners.adobe.com/public/developer/en/ps/5002.EPSF_Spec.pdf + s = fp.read(8) + offset = i32(s) + length = i32(s, 4) + else: + msg = "not an EPS file" + raise SyntaxError(msg) + + return length, offset + + def load( + self, scale: int = 1, transparency: bool = False + ) -> Image.core.PixelAccess | None: + # Load EPS via Ghostscript + if self.tile: + assert self.fp is not None + self.im = Ghostscript(self.tile, self.size, self.fp, scale, transparency) + self._mode = self.im.mode + self._size = self.im.size + self.tile = [] + return Image.Image.load(self) + + def load_seek(self, pos: int) -> None: + # we can't incrementally load, so force ImageFile.parser to + # use our custom load method by defining this method. + pass + + +# -------------------------------------------------------------------- + + +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes, eps: int = 1) -> None: + """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, b"image") + elif im.mode == "RGB": + operator = (8, 3, b"false 3 colorimage") + elif im.mode == "CMYK": + operator = (8, 4, b"false 4 colorimage") + else: + msg = "image mode is not supported" + raise ValueError(msg) + + if eps: + # write EPS header + fp.write(b"%!PS-Adobe-3.0 EPSF-3.0\n") + fp.write(b"%%Creator: PIL 0.1 EpsEncode\n") + # fp.write("%%CreationDate: %s"...) + fp.write(b"%%%%BoundingBox: 0 0 %d %d\n" % im.size) + fp.write(b"%%Pages: 1\n") + fp.write(b"%%EndComments\n") + fp.write(b"%%Page: 1 1\n") + fp.write(b"%%ImageData: %d %d " % im.size) + fp.write(b'%d %d 0 1 1 "%s"\n' % operator) + + # image header + fp.write(b"gsave\n") + fp.write(b"10 dict begin\n") + fp.write(b"/buf %d string def\n" % (im.size[0] * operator[1])) + fp.write(b"%d %d scale\n" % im.size) + fp.write(b"%d %d 8\n" % im.size) # <= bits + fp.write(b"[%d 0 0 -%d 0 %d]\n" % (im.size[0], im.size[1], im.size[1])) + fp.write(b"{ currentfile buf readhexstring pop } bind\n") + fp.write(operator[2] + b"\n") + if hasattr(fp, "flush"): + fp.flush() + + ImageFile._save(im, fp, [ImageFile._Tile("eps", (0, 0) + im.size)]) + + fp.write(b"\n%%%%EndBinary\n") + fp.write(b"grestore end\n") + if hasattr(fp, "flush"): + fp.flush() + + +# -------------------------------------------------------------------- + + +Image.register_open(EpsImageFile.format, EpsImageFile, _accept) + +Image.register_save(EpsImageFile.format, _save) + +Image.register_extensions(EpsImageFile.format, [".ps", ".eps"]) + +Image.register_mime(EpsImageFile.format, "application/postscript") diff --git a/voxvera/vendor/PIL/ExifTags.py b/voxvera/vendor/PIL/ExifTags.py new file mode 100644 index 0000000..2280d5c --- /dev/null +++ b/voxvera/vendor/PIL/ExifTags.py @@ -0,0 +1,382 @@ +# +# 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. +""" +from __future__ import annotations + +from enum import IntEnum + + +class Base(IntEnum): + # possibly incomplete + InteropIndex = 0x0001 + ProcessingSoftware = 0x000B + NewSubfileType = 0x00FE + SubfileType = 0x00FF + ImageWidth = 0x0100 + ImageLength = 0x0101 + BitsPerSample = 0x0102 + Compression = 0x0103 + PhotometricInterpretation = 0x0106 + Thresholding = 0x0107 + CellWidth = 0x0108 + CellLength = 0x0109 + FillOrder = 0x010A + DocumentName = 0x010D + ImageDescription = 0x010E + Make = 0x010F + Model = 0x0110 + StripOffsets = 0x0111 + Orientation = 0x0112 + SamplesPerPixel = 0x0115 + RowsPerStrip = 0x0116 + StripByteCounts = 0x0117 + MinSampleValue = 0x0118 + MaxSampleValue = 0x0119 + XResolution = 0x011A + YResolution = 0x011B + PlanarConfiguration = 0x011C + PageName = 0x011D + FreeOffsets = 0x0120 + FreeByteCounts = 0x0121 + GrayResponseUnit = 0x0122 + GrayResponseCurve = 0x0123 + T4Options = 0x0124 + T6Options = 0x0125 + ResolutionUnit = 0x0128 + PageNumber = 0x0129 + TransferFunction = 0x012D + Software = 0x0131 + DateTime = 0x0132 + Artist = 0x013B + HostComputer = 0x013C + Predictor = 0x013D + WhitePoint = 0x013E + PrimaryChromaticities = 0x013F + ColorMap = 0x0140 + HalftoneHints = 0x0141 + TileWidth = 0x0142 + TileLength = 0x0143 + TileOffsets = 0x0144 + TileByteCounts = 0x0145 + SubIFDs = 0x014A + InkSet = 0x014C + InkNames = 0x014D + NumberOfInks = 0x014E + DotRange = 0x0150 + TargetPrinter = 0x0151 + ExtraSamples = 0x0152 + SampleFormat = 0x0153 + SMinSampleValue = 0x0154 + SMaxSampleValue = 0x0155 + TransferRange = 0x0156 + ClipPath = 0x0157 + XClipPathUnits = 0x0158 + YClipPathUnits = 0x0159 + Indexed = 0x015A + JPEGTables = 0x015B + OPIProxy = 0x015F + JPEGProc = 0x0200 + JpegIFOffset = 0x0201 + JpegIFByteCount = 0x0202 + JpegRestartInterval = 0x0203 + JpegLosslessPredictors = 0x0205 + JpegPointTransforms = 0x0206 + JpegQTables = 0x0207 + JpegDCTables = 0x0208 + JpegACTables = 0x0209 + YCbCrCoefficients = 0x0211 + YCbCrSubSampling = 0x0212 + YCbCrPositioning = 0x0213 + ReferenceBlackWhite = 0x0214 + XMLPacket = 0x02BC + RelatedImageFileFormat = 0x1000 + RelatedImageWidth = 0x1001 + RelatedImageLength = 0x1002 + Rating = 0x4746 + RatingPercent = 0x4749 + ImageID = 0x800D + CFARepeatPatternDim = 0x828D + BatteryLevel = 0x828F + Copyright = 0x8298 + ExposureTime = 0x829A + FNumber = 0x829D + IPTCNAA = 0x83BB + ImageResources = 0x8649 + ExifOffset = 0x8769 + InterColorProfile = 0x8773 + ExposureProgram = 0x8822 + SpectralSensitivity = 0x8824 + GPSInfo = 0x8825 + ISOSpeedRatings = 0x8827 + OECF = 0x8828 + Interlace = 0x8829 + TimeZoneOffset = 0x882A + SelfTimerMode = 0x882B + SensitivityType = 0x8830 + StandardOutputSensitivity = 0x8831 + RecommendedExposureIndex = 0x8832 + ISOSpeed = 0x8833 + ISOSpeedLatitudeyyy = 0x8834 + ISOSpeedLatitudezzz = 0x8835 + ExifVersion = 0x9000 + DateTimeOriginal = 0x9003 + DateTimeDigitized = 0x9004 + OffsetTime = 0x9010 + OffsetTimeOriginal = 0x9011 + OffsetTimeDigitized = 0x9012 + ComponentsConfiguration = 0x9101 + CompressedBitsPerPixel = 0x9102 + ShutterSpeedValue = 0x9201 + ApertureValue = 0x9202 + BrightnessValue = 0x9203 + ExposureBiasValue = 0x9204 + MaxApertureValue = 0x9205 + SubjectDistance = 0x9206 + MeteringMode = 0x9207 + LightSource = 0x9208 + Flash = 0x9209 + FocalLength = 0x920A + Noise = 0x920D + ImageNumber = 0x9211 + SecurityClassification = 0x9212 + ImageHistory = 0x9213 + TIFFEPStandardID = 0x9216 + MakerNote = 0x927C + UserComment = 0x9286 + SubsecTime = 0x9290 + SubsecTimeOriginal = 0x9291 + SubsecTimeDigitized = 0x9292 + AmbientTemperature = 0x9400 + Humidity = 0x9401 + Pressure = 0x9402 + WaterDepth = 0x9403 + Acceleration = 0x9404 + CameraElevationAngle = 0x9405 + XPTitle = 0x9C9B + XPComment = 0x9C9C + XPAuthor = 0x9C9D + XPKeywords = 0x9C9E + XPSubject = 0x9C9F + FlashPixVersion = 0xA000 + ColorSpace = 0xA001 + ExifImageWidth = 0xA002 + ExifImageHeight = 0xA003 + RelatedSoundFile = 0xA004 + ExifInteroperabilityOffset = 0xA005 + FlashEnergy = 0xA20B + SpatialFrequencyResponse = 0xA20C + FocalPlaneXResolution = 0xA20E + FocalPlaneYResolution = 0xA20F + FocalPlaneResolutionUnit = 0xA210 + SubjectLocation = 0xA214 + ExposureIndex = 0xA215 + SensingMethod = 0xA217 + FileSource = 0xA300 + SceneType = 0xA301 + CFAPattern = 0xA302 + CustomRendered = 0xA401 + ExposureMode = 0xA402 + WhiteBalance = 0xA403 + DigitalZoomRatio = 0xA404 + FocalLengthIn35mmFilm = 0xA405 + SceneCaptureType = 0xA406 + GainControl = 0xA407 + Contrast = 0xA408 + Saturation = 0xA409 + Sharpness = 0xA40A + DeviceSettingDescription = 0xA40B + SubjectDistanceRange = 0xA40C + ImageUniqueID = 0xA420 + CameraOwnerName = 0xA430 + BodySerialNumber = 0xA431 + LensSpecification = 0xA432 + LensMake = 0xA433 + LensModel = 0xA434 + LensSerialNumber = 0xA435 + CompositeImage = 0xA460 + CompositeImageCount = 0xA461 + CompositeImageExposureTimes = 0xA462 + Gamma = 0xA500 + PrintImageMatching = 0xC4A5 + DNGVersion = 0xC612 + DNGBackwardVersion = 0xC613 + UniqueCameraModel = 0xC614 + LocalizedCameraModel = 0xC615 + CFAPlaneColor = 0xC616 + CFALayout = 0xC617 + LinearizationTable = 0xC618 + BlackLevelRepeatDim = 0xC619 + BlackLevel = 0xC61A + BlackLevelDeltaH = 0xC61B + BlackLevelDeltaV = 0xC61C + WhiteLevel = 0xC61D + DefaultScale = 0xC61E + DefaultCropOrigin = 0xC61F + DefaultCropSize = 0xC620 + ColorMatrix1 = 0xC621 + ColorMatrix2 = 0xC622 + CameraCalibration1 = 0xC623 + CameraCalibration2 = 0xC624 + ReductionMatrix1 = 0xC625 + ReductionMatrix2 = 0xC626 + AnalogBalance = 0xC627 + AsShotNeutral = 0xC628 + AsShotWhiteXY = 0xC629 + BaselineExposure = 0xC62A + BaselineNoise = 0xC62B + BaselineSharpness = 0xC62C + BayerGreenSplit = 0xC62D + LinearResponseLimit = 0xC62E + CameraSerialNumber = 0xC62F + LensInfo = 0xC630 + ChromaBlurRadius = 0xC631 + AntiAliasStrength = 0xC632 + ShadowScale = 0xC633 + DNGPrivateData = 0xC634 + MakerNoteSafety = 0xC635 + CalibrationIlluminant1 = 0xC65A + CalibrationIlluminant2 = 0xC65B + BestQualityScale = 0xC65C + RawDataUniqueID = 0xC65D + OriginalRawFileName = 0xC68B + OriginalRawFileData = 0xC68C + ActiveArea = 0xC68D + MaskedAreas = 0xC68E + AsShotICCProfile = 0xC68F + AsShotPreProfileMatrix = 0xC690 + CurrentICCProfile = 0xC691 + CurrentPreProfileMatrix = 0xC692 + ColorimetricReference = 0xC6BF + CameraCalibrationSignature = 0xC6F3 + ProfileCalibrationSignature = 0xC6F4 + AsShotProfileName = 0xC6F6 + NoiseReductionApplied = 0xC6F7 + ProfileName = 0xC6F8 + ProfileHueSatMapDims = 0xC6F9 + ProfileHueSatMapData1 = 0xC6FA + ProfileHueSatMapData2 = 0xC6FB + ProfileToneCurve = 0xC6FC + ProfileEmbedPolicy = 0xC6FD + ProfileCopyright = 0xC6FE + ForwardMatrix1 = 0xC714 + ForwardMatrix2 = 0xC715 + PreviewApplicationName = 0xC716 + PreviewApplicationVersion = 0xC717 + PreviewSettingsName = 0xC718 + PreviewSettingsDigest = 0xC719 + PreviewColorSpace = 0xC71A + PreviewDateTime = 0xC71B + RawImageDigest = 0xC71C + OriginalRawFileDigest = 0xC71D + SubTileBlockSize = 0xC71E + RowInterleaveFactor = 0xC71F + ProfileLookTableDims = 0xC725 + ProfileLookTableData = 0xC726 + OpcodeList1 = 0xC740 + OpcodeList2 = 0xC741 + OpcodeList3 = 0xC74E + NoiseProfile = 0xC761 + + +"""Maps EXIF tags to tag names.""" +TAGS = { + **{i.value: i.name for i in Base}, + 0x920C: "SpatialFrequencyResponse", + 0x9214: "SubjectLocation", + 0x9215: "ExposureIndex", + 0x828E: "CFAPattern", + 0x920B: "FlashEnergy", + 0x9216: "TIFF/EPStandardID", +} + + +class GPS(IntEnum): + GPSVersionID = 0x00 + GPSLatitudeRef = 0x01 + GPSLatitude = 0x02 + GPSLongitudeRef = 0x03 + GPSLongitude = 0x04 + GPSAltitudeRef = 0x05 + GPSAltitude = 0x06 + GPSTimeStamp = 0x07 + GPSSatellites = 0x08 + GPSStatus = 0x09 + GPSMeasureMode = 0x0A + GPSDOP = 0x0B + GPSSpeedRef = 0x0C + GPSSpeed = 0x0D + GPSTrackRef = 0x0E + GPSTrack = 0x0F + GPSImgDirectionRef = 0x10 + GPSImgDirection = 0x11 + GPSMapDatum = 0x12 + GPSDestLatitudeRef = 0x13 + GPSDestLatitude = 0x14 + GPSDestLongitudeRef = 0x15 + GPSDestLongitude = 0x16 + GPSDestBearingRef = 0x17 + GPSDestBearing = 0x18 + GPSDestDistanceRef = 0x19 + GPSDestDistance = 0x1A + GPSProcessingMethod = 0x1B + GPSAreaInformation = 0x1C + GPSDateStamp = 0x1D + GPSDifferential = 0x1E + GPSHPositioningError = 0x1F + + +"""Maps EXIF GPS tags to tag names.""" +GPSTAGS = {i.value: i.name for i in GPS} + + +class Interop(IntEnum): + InteropIndex = 0x0001 + InteropVersion = 0x0002 + RelatedImageFileFormat = 0x1000 + RelatedImageWidth = 0x1001 + RelatedImageHeight = 0x1002 + + +class IFD(IntEnum): + Exif = 0x8769 + GPSInfo = 0x8825 + MakerNote = 0x927C + Makernote = 0x927C # Deprecated + Interop = 0xA005 + IFD1 = -1 + + +class LightSource(IntEnum): + Unknown = 0x00 + Daylight = 0x01 + Fluorescent = 0x02 + Tungsten = 0x03 + Flash = 0x04 + Fine = 0x09 + Cloudy = 0x0A + Shade = 0x0B + DaylightFluorescent = 0x0C + DayWhiteFluorescent = 0x0D + CoolWhiteFluorescent = 0x0E + WhiteFluorescent = 0x0F + StandardLightA = 0x11 + StandardLightB = 0x12 + StandardLightC = 0x13 + D55 = 0x14 + D65 = 0x15 + D75 = 0x16 + D50 = 0x17 + ISO = 0x18 + Other = 0xFF diff --git a/voxvera/vendor/PIL/FitsImagePlugin.py b/voxvera/vendor/PIL/FitsImagePlugin.py new file mode 100644 index 0000000..a3fdc0e --- /dev/null +++ b/voxvera/vendor/PIL/FitsImagePlugin.py @@ -0,0 +1,152 @@ +# +# The Python Imaging Library +# $Id$ +# +# FITS file handling +# +# Copyright (c) 1998-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# +from __future__ import annotations + +import gzip +import math + +from . import Image, ImageFile + + +def _accept(prefix: bytes) -> bool: + return prefix.startswith(b"SIMPLE") + + +class FitsImageFile(ImageFile.ImageFile): + format = "FITS" + format_description = "FITS" + + def _open(self) -> None: + assert self.fp is not None + + headers: dict[bytes, bytes] = {} + header_in_progress = False + decoder_name = "" + while True: + header = self.fp.read(80) + if not header: + msg = "Truncated FITS file" + raise OSError(msg) + keyword = header[:8].strip() + if keyword in (b"SIMPLE", b"XTENSION"): + header_in_progress = True + elif headers and not header_in_progress: + # This is now a data unit + break + elif keyword == b"END": + # Seek to the end of the header unit + self.fp.seek(math.ceil(self.fp.tell() / 2880) * 2880) + if not decoder_name: + decoder_name, offset, args = self._parse_headers(headers) + + header_in_progress = False + continue + + if decoder_name: + # Keep going to read past the headers + continue + + value = header[8:].split(b"/")[0].strip() + if value.startswith(b"="): + value = value[1:].strip() + if not headers and (not _accept(keyword) or value != b"T"): + msg = "Not a FITS file" + raise SyntaxError(msg) + headers[keyword] = value + + if not decoder_name: + msg = "No image data" + raise ValueError(msg) + + offset += self.fp.tell() - 80 + self.tile = [ImageFile._Tile(decoder_name, (0, 0) + self.size, offset, args)] + + def _get_size( + self, headers: dict[bytes, bytes], prefix: bytes + ) -> tuple[int, int] | None: + naxis = int(headers[prefix + b"NAXIS"]) + if naxis == 0: + return None + + if naxis == 1: + return 1, int(headers[prefix + b"NAXIS1"]) + else: + return int(headers[prefix + b"NAXIS1"]), int(headers[prefix + b"NAXIS2"]) + + def _parse_headers( + self, headers: dict[bytes, bytes] + ) -> tuple[str, int, tuple[str | int, ...]]: + prefix = b"" + decoder_name = "raw" + offset = 0 + if ( + headers.get(b"XTENSION") == b"'BINTABLE'" + and headers.get(b"ZIMAGE") == b"T" + and headers[b"ZCMPTYPE"] == b"'GZIP_1 '" + ): + no_prefix_size = self._get_size(headers, prefix) or (0, 0) + number_of_bits = int(headers[b"BITPIX"]) + offset = no_prefix_size[0] * no_prefix_size[1] * (number_of_bits // 8) + + prefix = b"Z" + decoder_name = "fits_gzip" + + size = self._get_size(headers, prefix) + if not size: + return "", 0, () + + self._size = size + + number_of_bits = int(headers[prefix + b"BITPIX"]) + if number_of_bits == 8: + self._mode = "L" + elif number_of_bits == 16: + self._mode = "I;16" + elif number_of_bits == 32: + self._mode = "I" + elif number_of_bits in (-32, -64): + self._mode = "F" + + args: tuple[str | int, ...] + if decoder_name == "raw": + args = (self.mode, 0, -1) + else: + args = (number_of_bits,) + return decoder_name, offset, args + + +class FitsGzipDecoder(ImageFile.PyDecoder): + _pulls_fd = True + + def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: + assert self.fd is not None + value = gzip.decompress(self.fd.read()) + + rows = [] + offset = 0 + number_of_bits = min(self.args[0] // 8, 4) + for y in range(self.state.ysize): + row = bytearray() + for x in range(self.state.xsize): + row += value[offset + (4 - number_of_bits) : offset + 4] + offset += 4 + rows.append(row) + self.set_as_raw(bytes([pixel for row in rows[::-1] for pixel in row])) + return -1, 0 + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(FitsImageFile.format, FitsImageFile, _accept) +Image.register_decoder("fits_gzip", FitsGzipDecoder) + +Image.register_extensions(FitsImageFile.format, [".fit", ".fits"]) diff --git a/voxvera/vendor/PIL/FliImagePlugin.py b/voxvera/vendor/PIL/FliImagePlugin.py new file mode 100644 index 0000000..da1e8e9 --- /dev/null +++ b/voxvera/vendor/PIL/FliImagePlugin.py @@ -0,0 +1,184 @@ +# +# 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. +# +from __future__ import annotations + +import os + +from . import Image, ImageFile, ImagePalette +from ._binary import i16le as i16 +from ._binary import i32le as i32 +from ._binary import o8 +from ._util import DeferredError + +# +# decoder + + +def _accept(prefix: bytes) -> bool: + return ( + len(prefix) >= 16 + and i16(prefix, 4) in [0xAF11, 0xAF12] + and i16(prefix, 14) in [0, 3] # flags + ) + + +## +# 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" + _close_exclusive_fp_after_loading = False + + def _open(self) -> None: + # HEAD + assert self.fp is not None + s = self.fp.read(128) + if not ( + _accept(s) + and s[20:22] == b"\x00" * 2 + and s[42:80] == b"\x00" * 38 + and s[88:] == b"\x00" * 40 + ): + msg = "not an FLI/FLC file" + raise SyntaxError(msg) + + # frames + self.n_frames = i16(s, 6) + self.is_animated = self.n_frames > 1 + + # image characteristics + self._mode = "P" + self._size = i16(s, 8), i16(s, 10) + + # animation speed + duration = i32(s, 16) + magic = i16(s, 4) + 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) == 0xF100: + # prefix chunk; ignore it + self.fp.seek(self.__offset + i32(s)) + s = self.fp.read(16) + + if i16(s, 4) == 0xF1FA: + # look for palette chunk + number_of_subchunks = i16(s, 6) + chunk_size: int | None = None + for _ in range(number_of_subchunks): + if chunk_size is not None: + self.fp.seek(chunk_size - 6, os.SEEK_CUR) + s = self.fp.read(6) + chunk_type = i16(s, 4) + if chunk_type in (4, 11): + self._palette(palette, 2 if chunk_type == 11 else 0) + break + chunk_size = i32(s) + if not chunk_size: + break + + self.palette = ImagePalette.raw( + "RGB", b"".join(o8(r) + o8(g) + o8(b) for (r, g, b) in palette) + ) + + # set things up to decode first frame + self.__frame = -1 + self._fp = self.fp + self.__rewind = self.fp.tell() + self.seek(0) + + def _palette(self, palette: list[tuple[int, int, int]], shift: int) -> None: + # load palette + + i = 0 + assert self.fp is not None + for e in range(i16(self.fp.read(2))): + s = self.fp.read(2) + i = i + s[0] + n = s[1] + if n == 0: + n = 256 + s = self.fp.read(n * 3) + for n in range(0, len(s), 3): + r = s[n] << shift + g = s[n + 1] << shift + b = s[n + 2] << shift + palette[i] = (r, g, b) + i += 1 + + def seek(self, frame: int) -> None: + if not self._seek_check(frame): + return + if frame < self.__frame: + self._seek(0) + + for f in range(self.__frame + 1, frame + 1): + self._seek(f) + + def _seek(self, frame: int) -> None: + if isinstance(self._fp, DeferredError): + raise self._fp.ex + if frame == 0: + self.__frame = -1 + self._fp.seek(self.__rewind) + self.__offset = 128 + else: + # ensure that the previous frame was loaded + self.load() + + if frame != self.__frame + 1: + msg = f"cannot seek to frame {frame}" + raise ValueError(msg) + self.__frame = frame + + # move to next frame + self.fp = self._fp + self.fp.seek(self.__offset) + + s = self.fp.read(4) + if not s: + msg = "missing frame size" + raise EOFError(msg) + + framesize = i32(s) + + self.decodermaxblock = framesize + self.tile = [ImageFile._Tile("fli", (0, 0) + self.size, self.__offset)] + + self.__offset += framesize + + def tell(self) -> int: + return self.__frame + + +# +# registry + +Image.register_open(FliImageFile.format, FliImageFile, _accept) + +Image.register_extensions(FliImageFile.format, [".fli", ".flc"]) diff --git a/voxvera/vendor/PIL/FontFile.py b/voxvera/vendor/PIL/FontFile.py new file mode 100644 index 0000000..1e0c1c1 --- /dev/null +++ b/voxvera/vendor/PIL/FontFile.py @@ -0,0 +1,134 @@ +# +# 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. +# +from __future__ import annotations + +import os +from typing import BinaryIO + +from . import Image, _binary + +WIDTH = 800 + + +def puti16( + fp: BinaryIO, values: tuple[int, int, int, int, int, int, int, int, int, int] +) -> None: + """Write network order (big-endian) 16-bit sequence""" + for v in values: + if v < 0: + v += 65536 + fp.write(_binary.o16be(v)) + + +class FontFile: + """Base class for raster font file handlers.""" + + bitmap: Image.Image | None = None + + def __init__(self) -> None: + self.info: dict[bytes, bytes | int] = {} + self.glyph: list[ + tuple[ + tuple[int, int], + tuple[int, int, int, int], + tuple[int, int, int, int], + Image.Image, + ] + | None + ] = [None] * 256 + + def __getitem__(self, ix: int) -> ( + tuple[ + tuple[int, int], + tuple[int, int, int, int], + tuple[int, int, int, int], + Image.Image, + ] + | None + ): + return self.glyph[ix] + + def compile(self) -> None: + """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.glyph: + 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: list[ + tuple[tuple[int, int], tuple[int, int, int, int], tuple[int, int, int, int]] + | None + ] = [None] * 256 + x = y = 0 + for i in range(256): + glyph = self[i] + if glyph: + d, dst, src, im = glyph + xx = src[2] - src[0] + 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) + self.metrics[i] = d, dst, s + + def save(self, filename: str) -> None: + """Save font""" + + self.compile() + + # font data + if not self.bitmap: + msg = "No bitmap created" + raise ValueError(msg) + self.bitmap.save(os.path.splitext(filename)[0] + ".pbm", "PNG") + + # font metrics + with open(os.path.splitext(filename)[0] + ".pil", "wb") as fp: + fp.write(b"PILfont\n") + fp.write(f";;;;;;{self.ysize};\n".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]) diff --git a/voxvera/vendor/PIL/FpxImagePlugin.py b/voxvera/vendor/PIL/FpxImagePlugin.py new file mode 100644 index 0000000..2979712 --- /dev/null +++ b/voxvera/vendor/PIL/FpxImagePlugin.py @@ -0,0 +1,259 @@ +# +# 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. +# +from __future__ import annotations + +import olefile + +from . import Image, ImageFile +from ._binary import i32le as i32 + +# 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: bytes) -> bool: + return prefix.startswith(olefile.MAGIC) + + +## +# Image plugin for the FlashPix images. + + +class FpxImageFile(ImageFile.ImageFile): + format = "FPX" + format_description = "FlashPix" + + def _open(self) -> None: + # + # read the OLE directory and see if this is a likely + # to be a FlashPix file + + assert self.fp is not None + try: + self.ole = olefile.OleFileIO(self.fp) + except OSError as e: + msg = "not an FPX file; invalid OLE file" + raise SyntaxError(msg) from e + + root = self.ole.root + if not root or root.clsid != "56616700-C154-11CE-8553-00AA00A1F95B": + msg = "not an FPX file; bad root CLSID" + raise SyntaxError(msg) + + self._open_index(1) + + def _open_index(self, index: int = 1) -> None: + # + # get the Image Contents Property Set + + prop = self.ole.getproperties( + [f"Data Object Store {index:06d}", "\005Image Contents"] + ) + + # size (highest resolution) + + assert isinstance(prop[0x1000002], int) + assert isinstance(prop[0x1000003], int) + 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] + + if not isinstance(s, bytes) or (bands := i32(s, 4)) > 4: + msg = "Invalid number of bands" + raise OSError(msg) + + # note: for now, we ignore the "uncalibrated" flag + colors = tuple(i32(s, 8 + i * 4) & 0x7FFFFFFF for i in range(bands)) + + self._mode, self.rawmode = MODES[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] + + self._open_subimage(1, self.maxid) + + def _open_subimage(self, index: int = 1, subimage: int = 0) -> None: + # + # setup tile descriptors for a given subimage + + stream = [ + f"Data Object Store {index:06d}", + f"Resolution {subimage:04d}", + "Subimage 0000 Header", + ] + + fp = self.ole.openstream(stream) + + # skip prefix + 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) + + if size != self.size: + msg = "subimage mismatch" + raise OSError(msg) + + # 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): + x1 = min(xsize, x + xtile) + y1 = min(ysize, y + ytile) + + compression = i32(s, i + 8) + + if compression == 0: + self.tile.append( + ImageFile._Tile( + "raw", + (x, y, x1, y1), + i32(s, i) + 28, + self.rawmode, + ) + ) + + elif compression == 1: + # FIXME: the fill decoder is not implemented + self.tile.append( + ImageFile._Tile( + "fill", + (x, y, x1, y1), + i32(s, i) + 28, + (self.rawmode, s[12:16]), + ) + ) + + elif compression == 2: + internal_color_conversion = s[14] + jpeg_tables = 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( + ImageFile._Tile( + "jpeg", + (x, y, x1, y1), + 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: + msg = "unknown/invalid compression" + raise OSError(msg) + + x = x + xtile + if x >= xsize: + x, y = 0, y + ytile + if y >= ysize: + break # isn't really required + + assert self.fp is not None + self.stream = stream + self._fp = self.fp + self.fp = None + + def load(self) -> Image.core.PixelAccess | None: + if not self.fp: + self.fp = self.ole.openstream(self.stream[:2] + ["Subimage 0000 Data"]) + + return ImageFile.ImageFile.load(self) + + def close(self) -> None: + self.ole.close() + super().close() + + def __exit__(self, *args: object) -> None: + self.ole.close() + super().__exit__() + + +# +# -------------------------------------------------------------------- + + +Image.register_open(FpxImageFile.format, FpxImageFile, _accept) + +Image.register_extension(FpxImageFile.format, ".fpx") diff --git a/voxvera/vendor/PIL/FtexImagePlugin.py b/voxvera/vendor/PIL/FtexImagePlugin.py new file mode 100644 index 0000000..e4d836c --- /dev/null +++ b/voxvera/vendor/PIL/FtexImagePlugin.py @@ -0,0 +1,115 @@ +""" +A Pillow loader for .ftc and .ftu files (FTEX) +Jerome Leclanche + +The contents of this file are hereby released in the public domain (CC0) +Full text of the CC0 license: + https://creativecommons.org/publicdomain/zero/1.0/ + +Independence War 2: Edge Of Chaos - Texture File Format - 16 October 2001 + +The textures used for 3D objects in Independence War 2: Edge Of Chaos are in a +packed custom format called FTEX. This file format uses file extensions FTC +and FTU. +* FTC files are compressed textures (using standard texture compression). +* FTU files are not compressed. +Texture File Format +The FTC and FTU texture files both use the same format. This +has the following structure: +{header} +{format_directory} +{data} +Where: +{header} = { + u32:magic, + u32:version, + u32:width, + u32:height, + u32:mipmap_count, + u32:format_count +} + +* The "magic" number is "FTEX". +* "width" and "height" are the dimensions of the texture. +* "mipmap_count" is the number of mipmaps in the texture. +* "format_count" is the number of texture formats (different versions of the +same texture) in this file. + +{format_directory} = format_count * { u32:format, u32:where } + +The format value is 0 for DXT1 compressed textures and 1 for 24-bit RGB +uncompressed textures. +The texture data for a format starts at the position "where" in the file. + +Each set of texture data in the file has the following structure: +{data} = format_count * { u32:mipmap_size, mipmap_size * { u8 } } +* "mipmap_size" is the number of bytes in that mip level. For compressed +textures this is the size of the texture data compressed with DXT1. For 24 bit +uncompressed textures, this is 3 * width * height. Following this are the image +bytes for that mipmap level. + +Note: All data is stored in little-Endian (Intel) byte order. +""" + +from __future__ import annotations + +import struct +from enum import IntEnum +from io import BytesIO + +from . import Image, ImageFile + +MAGIC = b"FTEX" + + +class Format(IntEnum): + DXT1 = 0 + UNCOMPRESSED = 1 + + +class FtexImageFile(ImageFile.ImageFile): + format = "FTEX" + format_description = "Texture File Format (IW2:EOC)" + + def _open(self) -> None: + assert self.fp is not None + if not _accept(self.fp.read(4)): + msg = "not an FTEX file" + raise SyntaxError(msg) + struct.unpack(" None: + pass + + +def _accept(prefix: bytes) -> bool: + return prefix.startswith(MAGIC) + + +Image.register_open(FtexImageFile.format, FtexImageFile, _accept) +Image.register_extensions(FtexImageFile.format, [".ftc", ".ftu"]) diff --git a/voxvera/vendor/PIL/GbrImagePlugin.py b/voxvera/vendor/PIL/GbrImagePlugin.py new file mode 100644 index 0000000..ec666c8 --- /dev/null +++ b/voxvera/vendor/PIL/GbrImagePlugin.py @@ -0,0 +1,103 @@ +# +# The Python Imaging Library +# +# load a GIMP brush file +# +# History: +# 96-03-14 fl Created +# 16-01-08 es Version 2 +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996. +# Copyright (c) Eric Soroos 2016. +# +# See the README file for information on usage and redistribution. +# +# +# See https://github.com/GNOME/gimp/blob/mainline/devel-docs/gbr.txt for +# format documentation. +# +# This code Interprets version 1 and 2 .gbr files. +# Version 1 files are obsolete, and should not be used for new +# brushes. +# Version 2 files are saved by GIMP v2.8 (at least) +# Version 3 files have a format specifier of 18 for 16bit floats in +# the color depth field. This is currently unsupported by Pillow. +from __future__ import annotations + +from . import Image, ImageFile +from ._binary import i32be as i32 + + +def _accept(prefix: bytes) -> bool: + return len(prefix) >= 8 and i32(prefix, 0) >= 20 and i32(prefix, 4) in (1, 2) + + +## +# Image plugin for the GIMP brush format. + + +class GbrImageFile(ImageFile.ImageFile): + format = "GBR" + format_description = "GIMP brush file" + + def _open(self) -> None: + assert self.fp is not None + header_size = i32(self.fp.read(4)) + if header_size < 20: + msg = "not a GIMP brush" + raise SyntaxError(msg) + version = i32(self.fp.read(4)) + if version not in (1, 2): + msg = f"Unsupported GIMP brush version: {version}" + raise SyntaxError(msg) + + width = i32(self.fp.read(4)) + height = i32(self.fp.read(4)) + color_depth = i32(self.fp.read(4)) + if width == 0 or height == 0: + msg = "not a GIMP brush" + raise SyntaxError(msg) + if color_depth not in (1, 4): + msg = f"Unsupported GIMP brush color depth: {color_depth}" + raise SyntaxError(msg) + + if version == 1: + comment_length = header_size - 20 + else: + comment_length = header_size - 28 + magic_number = self.fp.read(4) + if magic_number != b"GIMP": + msg = "not a GIMP brush, bad magic number" + raise SyntaxError(msg) + self.info["spacing"] = i32(self.fp.read(4)) + + self.info["comment"] = self.fp.read(comment_length)[:-1] + + if color_depth == 1: + self._mode = "L" + else: + self._mode = "RGBA" + + self._size = width, height + + # Image might not be small + Image._decompression_bomb_check(self.size) + + # Data is an uncompressed block of w * h * bytes/pixel + self._data_size = width * height * color_depth + + def load(self) -> Image.core.PixelAccess | None: + if self._im is None: + assert self.fp is not None + self.im = Image.core.new(self.mode, self.size) + self.frombytes(self.fp.read(self._data_size)) + return Image.Image.load(self) + + +# +# registry + + +Image.register_open(GbrImageFile.format, GbrImageFile, _accept) +Image.register_extension(GbrImageFile.format, ".gbr") diff --git a/voxvera/vendor/PIL/GdImageFile.py b/voxvera/vendor/PIL/GdImageFile.py new file mode 100644 index 0000000..891225c --- /dev/null +++ b/voxvera/vendor/PIL/GdImageFile.py @@ -0,0 +1,102 @@ +# +# 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 :py:func:`PIL.Image.open()`. To open a + gd file, use the :py:func:`PIL.GdImageFile.open()` function instead. + +.. warning:: + THE GD FORMAT IS NOT DESIGNED FOR DATA INTERCHANGE. This + implementation is provided for convenience and demonstrational + purposes only. +""" +from __future__ import annotations + +from typing import IO + +from . import ImageFile, ImagePalette, UnidentifiedImageError +from ._binary import i16be as i16 +from ._binary import i32be as i32 +from ._typing import StrOrBytesPath + + +class GdImageFile(ImageFile.ImageFile): + """ + Image plugin for the GD uncompressed format. Note that this format + is not supported by the standard :py:func:`PIL.Image.open()` function. To use + this plugin, you have to import the :py:mod:`PIL.GdImageFile` module and + use the :py:func:`PIL.GdImageFile.open()` function. + """ + + format = "GD" + format_description = "GD uncompressed images" + + def _open(self) -> None: + # Header + assert self.fp is not None + + s = self.fp.read(1037) + + if i16(s) not in [65534, 65535]: + msg = "Not a valid GD 2.x .gd file" + raise SyntaxError(msg) + + self._mode = "P" + self._size = i16(s, 2), i16(s, 4) + + true_color = s[6] + true_color_offset = 2 if true_color else 0 + + # transparency index + tindex = i32(s, 7 + true_color_offset) + if tindex < 256: + self.info["transparency"] = tindex + + self.palette = ImagePalette.raw( + "RGBX", s[7 + true_color_offset + 6 : 7 + true_color_offset + 6 + 256 * 4] + ) + + self.tile = [ + ImageFile._Tile( + "raw", + (0, 0) + self.size, + 7 + true_color_offset + 6 + 256 * 4, + "L", + ) + ] + + +def open(fp: StrOrBytesPath | IO[bytes], mode: str = "r") -> GdImageFile: + """ + Load texture from a GD image file. + + :param fp: 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". + :returns: An image instance. + :raises OSError: If the image could not be read. + """ + if mode != "r": + msg = "bad mode" + raise ValueError(msg) + + try: + return GdImageFile(fp) + except SyntaxError as e: + msg = "cannot identify this image file" + raise UnidentifiedImageError(msg) from e diff --git a/voxvera/vendor/PIL/GifImagePlugin.py b/voxvera/vendor/PIL/GifImagePlugin.py new file mode 100644 index 0000000..76a0d4a --- /dev/null +++ b/voxvera/vendor/PIL/GifImagePlugin.py @@ -0,0 +1,1217 @@ +# +# 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. +# +from __future__ import annotations + +import itertools +import math +import os +import subprocess +from enum import IntEnum +from functools import cached_property +from typing import Any, NamedTuple, cast + +from . import ( + Image, + ImageChops, + ImageFile, + ImageMath, + ImageOps, + ImagePalette, + ImageSequence, +) +from ._binary import i16le as i16 +from ._binary import o8 +from ._binary import o16le as o16 +from ._util import DeferredError + +TYPE_CHECKING = False +if TYPE_CHECKING: + from typing import IO, Literal + + from . import _imaging + from ._typing import Buffer + + +class LoadingStrategy(IntEnum): + """.. versionadded:: 9.1.0""" + + RGB_AFTER_FIRST = 0 + RGB_AFTER_DIFFERENT_PALETTE_ONLY = 1 + RGB_ALWAYS = 2 + + +#: .. versionadded:: 9.1.0 +LOADING_STRATEGY = LoadingStrategy.RGB_AFTER_FIRST + +# -------------------------------------------------------------------- +# Identify/read GIF files + + +def _accept(prefix: bytes) -> bool: + return prefix.startswith((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" + _close_exclusive_fp_after_loading = False + + global_palette = None + + def data(self) -> bytes | None: + assert self.fp is not None + s = self.fp.read(1) + if s and s[0]: + return self.fp.read(s[0]) + return None + + def _is_palette_needed(self, p: bytes) -> bool: + for i in range(0, len(p), 3): + if not (i // 3 == p[i] == p[i + 1] == p[i + 2]): + return True + return False + + def _open(self) -> None: + # Screen + assert self.fp is not None + s = self.fp.read(13) + if not _accept(s): + msg = "not a GIF file" + raise SyntaxError(msg) + + self.info["version"] = s[:6] + self._size = i16(s, 6), i16(s, 8) + flags = s[10] + bits = (flags & 7) + 1 + + if flags & 128: + # get global palette + self.info["background"] = s[11] + # check if palette contains colour indices + p = self.fp.read(3 << bits) + if self._is_palette_needed(p): + palette = ImagePalette.raw("RGB", p) + self.global_palette = self.palette = palette + + self._fp = self.fp # FIXME: hack + self.__rewind = self.fp.tell() + self._n_frames: int | None = None + self._seek(0) # get ready to read first frame + + @property + def n_frames(self) -> int: + if self._n_frames is None: + current = self.tell() + try: + while True: + self._seek(self.tell() + 1, False) + except EOFError: + self._n_frames = self.tell() + 1 + self.seek(current) + return self._n_frames + + @cached_property + def is_animated(self) -> bool: + if self._n_frames is not None: + return self._n_frames != 1 + + current = self.tell() + if current: + return True + + try: + self._seek(1, False) + is_animated = True + except EOFError: + is_animated = False + + self.seek(current) + return is_animated + + def seek(self, frame: int) -> None: + if not self._seek_check(frame): + return + if frame < self.__frame: + self._im = None + self._seek(0) + + last_frame = self.__frame + for f in range(self.__frame + 1, frame + 1): + try: + self._seek(f) + except EOFError as e: + self.seek(last_frame) + msg = "no more images in GIF file" + raise EOFError(msg) from e + + def _seek(self, frame: int, update_image: bool = True) -> None: + if isinstance(self._fp, DeferredError): + raise self._fp.ex + if frame == 0: + # rewind + self.__offset = 0 + self.dispose: _imaging.ImagingCore | None = None + self.__frame = -1 + self._fp.seek(self.__rewind) + self.disposal_method = 0 + if "comment" in self.info: + del self.info["comment"] + else: + # ensure that the previous frame was loaded + if self.tile and update_image: + self.load() + + if frame != self.__frame + 1: + msg = f"cannot seek to frame {frame}" + raise ValueError(msg) + + self.fp = self._fp + if self.__offset: + # backup to last frame + self.fp.seek(self.__offset) + while self.data(): + pass + self.__offset = 0 + + s = self.fp.read(1) + if not s or s == b";": + msg = "no more images in GIF file" + raise EOFError(msg) + + palette: ImagePalette.ImagePalette | Literal[False] | None = None + + info: dict[str, Any] = {} + frame_transparency = None + interlace = None + frame_dispose_extent = None + while True: + if not s: + 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 s[0] == 249 and block is not None: + # + # graphic control extension + # + flags = block[0] + if flags & 1: + frame_transparency = block[3] + info["duration"] = i16(block, 1) * 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 s[0] == 254: + # + # comment extension + # + comment = b"" + + # Read this comment block + while block: + comment += block + block = self.data() + + if "comment" in info: + # If multiple comment blocks in frame, separate with \n + info["comment"] += b"\n" + comment + else: + info["comment"] = comment + s = b"" + continue + elif s[0] == 255 and frame == 0 and block is not None: + # + # application extension + # + info["extension"] = block, self.fp.tell() + if block.startswith(b"NETSCAPE2.0"): + block = self.data() + if block and len(block) >= 3 and block[0] == 1: + self.info["loop"] = i16(block, 1) + 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) + if (x1 > self.size[0] or y1 > self.size[1]) and update_image: + self._size = max(x1, self.size[0]), max(y1, self.size[1]) + Image._decompression_bomb_check(self._size) + frame_dispose_extent = x0, y0, x1, y1 + flags = s[8] + + interlace = (flags & 64) != 0 + + if flags & 128: + bits = (flags & 7) + 1 + p = self.fp.read(3 << bits) + if self._is_palette_needed(p): + palette = ImagePalette.raw("RGB", p) + else: + palette = False + + # image data + bits = self.fp.read(1)[0] + self.__offset = self.fp.tell() + break + s = b"" + + if interlace is None: + msg = "image not found in GIF frame" + raise EOFError(msg) + + self.__frame = frame + if not update_image: + return + + self.tile = [] + + if self.dispose: + self.im.paste(self.dispose, self.dispose_extent) + + self._frame_palette = palette if palette is not None else self.global_palette + self._frame_transparency = frame_transparency + if frame == 0: + if self._frame_palette: + if LOADING_STRATEGY == LoadingStrategy.RGB_ALWAYS: + self._mode = "RGBA" if frame_transparency is not None else "RGB" + else: + self._mode = "P" + else: + self._mode = "L" + + if palette: + self.palette = palette + elif self.global_palette: + from copy import copy + + self.palette = copy(self.global_palette) + else: + self.palette = None + else: + if self.mode == "P": + if ( + LOADING_STRATEGY != LoadingStrategy.RGB_AFTER_DIFFERENT_PALETTE_ONLY + or palette + ): + if "transparency" in self.info: + self.im.putpalettealpha(self.info["transparency"], 0) + self.im = self.im.convert("RGBA", Image.Dither.FLOYDSTEINBERG) + self._mode = "RGBA" + del self.info["transparency"] + else: + self._mode = "RGB" + self.im = self.im.convert("RGB", Image.Dither.FLOYDSTEINBERG) + + def _rgb(color: int) -> tuple[int, int, int]: + if self._frame_palette: + if color * 3 + 3 > len(self._frame_palette.palette): + color = 0 + return cast( + tuple[int, int, int], + tuple(self._frame_palette.palette[color * 3 : color * 3 + 3]), + ) + else: + return (color, color, color) + + self.dispose = None + self.dispose_extent: tuple[int, int, int, int] | None = frame_dispose_extent + if self.dispose_extent and self.disposal_method >= 2: + try: + if self.disposal_method == 2: + # replace with background colour + + # only dispose the extent in this frame + x0, y0, x1, y1 = self.dispose_extent + dispose_size = (x1 - x0, y1 - y0) + + Image._decompression_bomb_check(dispose_size) + + # by convention, attempt to use transparency first + dispose_mode = "P" + color = self.info.get("transparency", frame_transparency) + if color is not None: + if self.mode in ("RGB", "RGBA"): + dispose_mode = "RGBA" + color = _rgb(color) + (0,) + else: + color = self.info.get("background", 0) + if self.mode in ("RGB", "RGBA"): + dispose_mode = "RGB" + color = _rgb(color) + self.dispose = Image.core.fill(dispose_mode, dispose_size, color) + else: + # replace with previous contents + if self._im is not None: + # only dispose the extent in this frame + self.dispose = self._crop(self.im, self.dispose_extent) + elif frame_transparency is not None: + x0, y0, x1, y1 = self.dispose_extent + dispose_size = (x1 - x0, y1 - y0) + + Image._decompression_bomb_check(dispose_size) + dispose_mode = "P" + color = frame_transparency + if self.mode in ("RGB", "RGBA"): + dispose_mode = "RGBA" + color = _rgb(frame_transparency) + (0,) + self.dispose = Image.core.fill( + dispose_mode, dispose_size, color + ) + except AttributeError: + pass + + if interlace is not None: + transparency = -1 + if frame_transparency is not None: + if frame == 0: + if LOADING_STRATEGY != LoadingStrategy.RGB_ALWAYS: + self.info["transparency"] = frame_transparency + elif self.mode not in ("RGB", "RGBA"): + transparency = frame_transparency + self.tile = [ + ImageFile._Tile( + "gif", + (x0, y0, x1, y1), + self.__offset, + (bits, interlace, transparency), + ) + ] + + if info.get("comment"): + self.info["comment"] = info["comment"] + for k in ["duration", "extension"]: + if k in info: + self.info[k] = info[k] + elif k in self.info: + del self.info[k] + + def load_prepare(self) -> None: + temp_mode = "P" if self._frame_palette else "L" + self._prev_im = None + if self.__frame == 0: + if self._frame_transparency is not None: + self.im = Image.core.fill( + temp_mode, self.size, self._frame_transparency + ) + elif self.mode in ("RGB", "RGBA"): + self._prev_im = self.im + if self._frame_palette: + self.im = Image.core.fill("P", self.size, self._frame_transparency or 0) + self.im.putpalette("RGB", *self._frame_palette.getdata()) + else: + self._im = None + if not self._prev_im and self._im is not None and self.size != self.im.size: + expanded_im = Image.core.fill(self.im.mode, self.size) + if self._frame_palette: + expanded_im.putpalette("RGB", *self._frame_palette.getdata()) + expanded_im.paste(self.im, (0, 0) + self.im.size) + + self.im = expanded_im + self._mode = temp_mode + self._frame_palette = None + + super().load_prepare() + + def load_end(self) -> None: + if self.__frame == 0: + if self.mode == "P" and LOADING_STRATEGY == LoadingStrategy.RGB_ALWAYS: + if self._frame_transparency is not None: + self.im.putpalettealpha(self._frame_transparency, 0) + self._mode = "RGBA" + else: + self._mode = "RGB" + self.im = self.im.convert(self.mode, Image.Dither.FLOYDSTEINBERG) + return + if not self._prev_im: + return + if self.size != self._prev_im.size: + if self._frame_transparency is not None: + expanded_im = Image.core.fill("RGBA", self.size) + else: + expanded_im = Image.core.fill("P", self.size) + expanded_im.putpalette("RGB", "RGB", self.im.getpalette()) + expanded_im = expanded_im.convert("RGB") + expanded_im.paste(self._prev_im, (0, 0) + self._prev_im.size) + + self._prev_im = expanded_im + assert self._prev_im is not None + if self._frame_transparency is not None: + if self.mode == "L": + frame_im = self.im.convert_transparent("LA", self._frame_transparency) + else: + self.im.putpalettealpha(self._frame_transparency, 0) + frame_im = self.im.convert("RGBA") + else: + frame_im = self.im.convert("RGB") + + assert self.dispose_extent is not None + frame_im = self._crop(frame_im, self.dispose_extent) + + self.im = self._prev_im + self._mode = self.im.mode + if frame_im.mode in ("LA", "RGBA"): + self.im.paste(frame_im, self.dispose_extent, frame_im) + else: + self.im.paste(frame_im, self.dispose_extent) + + def tell(self) -> int: + return self.__frame + + +# -------------------------------------------------------------------- +# Write GIF files + + +RAWMODE = {"1": "L", "L": "L", "P": "P"} + + +def _normalize_mode(im: Image.Image) -> Image.Image: + """ + Takes an image (or frame), returns an image in a mode that is appropriate + for saving in a Gif. + + It may return the original image, or it may return an image converted to + palette or 'L' mode. + + :param im: Image object + :returns: Image object + """ + if im.mode in RAWMODE: + im.load() + return im + if Image.getmodebase(im.mode) == "RGB": + im = im.convert("P", palette=Image.Palette.ADAPTIVE) + assert im.palette is not None + if im.palette.mode == "RGBA": + for rgba in im.palette.colors: + if rgba[3] == 0: + im.info["transparency"] = im.palette.colors[rgba] + break + return im + return im.convert("L") + + +_Palette = bytes | bytearray | list[int] | ImagePalette.ImagePalette + + +def _normalize_palette( + im: Image.Image, palette: _Palette | None, info: dict[str, Any] +) -> Image.Image: + """ + Normalizes the palette for image. + - Sets the palette to the incoming palette, if provided. + - Ensures that there's a palette for L mode images + - Optimizes the palette if necessary/desired. + + :param im: Image object + :param palette: bytes object containing the source palette, or .... + :param info: encoderinfo + :returns: Image object + """ + source_palette = None + if palette: + # a bytes palette + if isinstance(palette, (bytes, bytearray, list)): + source_palette = bytearray(palette[:768]) + if isinstance(palette, ImagePalette.ImagePalette): + source_palette = bytearray(palette.palette) + + if im.mode == "P": + if not source_palette: + im_palette = im.getpalette(None) + assert im_palette is not None + source_palette = bytearray(im_palette) + else: # L-mode + if not source_palette: + source_palette = bytearray(i // 3 for i in range(768)) + im.palette = ImagePalette.ImagePalette("RGB", palette=source_palette) + assert source_palette is not None + + if palette: + used_palette_colors: list[int | None] = [] + assert im.palette is not None + for i in range(0, len(source_palette), 3): + source_color = tuple(source_palette[i : i + 3]) + index = im.palette.colors.get(source_color) + if index in used_palette_colors: + index = None + used_palette_colors.append(index) + for i, index in enumerate(used_palette_colors): + if index is None: + for j in range(len(used_palette_colors)): + if j not in used_palette_colors: + used_palette_colors[i] = j + break + dest_map: list[int] = [] + for index in used_palette_colors: + assert index is not None + dest_map.append(index) + im = im.remap_palette(dest_map) + else: + optimized_palette_colors = _get_optimize(im, info) + if optimized_palette_colors is not None: + im = im.remap_palette(optimized_palette_colors, source_palette) + if "transparency" in info: + try: + info["transparency"] = optimized_palette_colors.index( + info["transparency"] + ) + except ValueError: + del info["transparency"] + return im + + assert im.palette is not None + im.palette.palette = source_palette + return im + + +def _write_single_frame( + im: Image.Image, + fp: IO[bytes], + palette: _Palette | None, +) -> None: + im_out = _normalize_mode(im) + for k, v in im_out.info.items(): + if isinstance(k, str): + im.encoderinfo.setdefault(k, v) + im_out = _normalize_palette(im_out, palette, im.encoderinfo) + + for s in _get_global_header(im_out, im.encoderinfo): + fp.write(s) + + # local image header + flags = 0 + if get_interlace(im): + flags = flags | 64 + _write_local_header(fp, im, (0, 0), flags) + + im_out.encoderconfig = (8, get_interlace(im)) + ImageFile._save( + im_out, fp, [ImageFile._Tile("gif", (0, 0) + im.size, 0, RAWMODE[im_out.mode])] + ) + + fp.write(b"\0") # end of image data + + +def _getbbox( + base_im: Image.Image, im_frame: Image.Image +) -> tuple[Image.Image, tuple[int, int, int, int] | None]: + palette_bytes = [ + bytes(im.palette.palette) if im.palette else b"" for im in (base_im, im_frame) + ] + if palette_bytes[0] != palette_bytes[1]: + im_frame = im_frame.convert("RGBA") + base_im = base_im.convert("RGBA") + delta = ImageChops.subtract_modulo(im_frame, base_im) + return delta, delta.getbbox(alpha_only=False) + + +class _Frame(NamedTuple): + im: Image.Image + bbox: tuple[int, int, int, int] | None + encoderinfo: dict[str, Any] + + +def _write_multiple_frames( + im: Image.Image, fp: IO[bytes], palette: _Palette | None +) -> bool: + duration = im.encoderinfo.get("duration") + disposal = im.encoderinfo.get("disposal", im.info.get("disposal")) + + im_frames: list[_Frame] = [] + previous_im: Image.Image | None = None + frame_count = 0 + background_im = None + for imSequence in itertools.chain([im], im.encoderinfo.get("append_images", [])): + for im_frame in ImageSequence.Iterator(imSequence): + # a copy is required here since seek can still mutate the image + im_frame = _normalize_mode(im_frame.copy()) + if frame_count == 0: + for k, v in im_frame.info.items(): + if k == "transparency": + continue + if isinstance(k, str): + im.encoderinfo.setdefault(k, v) + + encoderinfo = im.encoderinfo.copy() + if "transparency" in im_frame.info: + encoderinfo.setdefault("transparency", im_frame.info["transparency"]) + im_frame = _normalize_palette(im_frame, palette, encoderinfo) + if isinstance(duration, (list, tuple)): + encoderinfo["duration"] = duration[frame_count] + elif duration is None and "duration" in im_frame.info: + encoderinfo["duration"] = im_frame.info["duration"] + if isinstance(disposal, (list, tuple)): + encoderinfo["disposal"] = disposal[frame_count] + frame_count += 1 + + diff_frame = None + if im_frames and previous_im: + # delta frame + delta, bbox = _getbbox(previous_im, im_frame) + if not bbox: + # This frame is identical to the previous frame + if encoderinfo.get("duration"): + im_frames[-1].encoderinfo["duration"] += encoderinfo["duration"] + continue + if im_frames[-1].encoderinfo.get("disposal") == 2: + # To appear correctly in viewers using a convention, + # only consider transparency, and not background color + color = im.encoderinfo.get( + "transparency", im.info.get("transparency") + ) + if color is not None: + if background_im is None: + background = _get_background(im_frame, color) + background_im = Image.new("P", im_frame.size, background) + first_palette = im_frames[0].im.palette + assert first_palette is not None + background_im.putpalette(first_palette, first_palette.mode) + bbox = _getbbox(background_im, im_frame)[1] + else: + bbox = (0, 0) + im_frame.size + elif encoderinfo.get("optimize") and im_frame.mode != "1": + if "transparency" not in encoderinfo: + assert im_frame.palette is not None + try: + encoderinfo["transparency"] = ( + im_frame.palette._new_color_index(im_frame) + ) + except ValueError: + pass + if "transparency" in encoderinfo: + # When the delta is zero, fill the image with transparency + diff_frame = im_frame.copy() + fill = Image.new("P", delta.size, encoderinfo["transparency"]) + if delta.mode == "RGBA": + r, g, b, a = delta.split() + mask = ImageMath.lambda_eval( + lambda args: args["convert"]( + args["max"]( + args["max"]( + args["max"](args["r"], args["g"]), args["b"] + ), + args["a"], + ) + * 255, + "1", + ), + r=r, + g=g, + b=b, + a=a, + ) + else: + if delta.mode == "P": + # Convert to L without considering palette + delta_l = Image.new("L", delta.size) + delta_l.putdata(delta.get_flattened_data()) + delta = delta_l + mask = ImageMath.lambda_eval( + lambda args: args["convert"](args["im"] * 255, "1"), + im=delta, + ) + diff_frame.paste(fill, mask=ImageOps.invert(mask)) + else: + bbox = None + previous_im = im_frame + im_frames.append(_Frame(diff_frame or im_frame, bbox, encoderinfo)) + + if len(im_frames) == 1: + if "duration" in im.encoderinfo: + # Since multiple frames will not be written, use the combined duration + im.encoderinfo["duration"] = im_frames[0].encoderinfo["duration"] + return False + + for frame_data in im_frames: + im_frame = frame_data.im + if not frame_data.bbox: + # global header + for s in _get_global_header(im_frame, frame_data.encoderinfo): + fp.write(s) + offset = (0, 0) + else: + # compress difference + if not palette: + frame_data.encoderinfo["include_color_table"] = True + + if frame_data.bbox != (0, 0) + im_frame.size: + im_frame = im_frame.crop(frame_data.bbox) + offset = frame_data.bbox[:2] + _write_frame_data(fp, im_frame, offset, frame_data.encoderinfo) + return True + + +def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + _save(im, fp, filename, save_all=True) + + +def _save( + im: Image.Image, fp: IO[bytes], filename: str | bytes, save_all: bool = False +) -> None: + # header + if "palette" in im.encoderinfo or "palette" in im.info: + palette = im.encoderinfo.get("palette", im.info.get("palette")) + else: + palette = None + im.encoderinfo.setdefault("optimize", True) + + if not save_all or not _write_multiple_frames(im, fp, palette): + _write_single_frame(im, fp, palette) + + fp.write(b";") # end of file + + if hasattr(fp, "flush"): + fp.flush() + + +def get_interlace(im: Image.Image) -> int: + interlace = im.encoderinfo.get("interlace", 1) + + # workaround for @PIL153 + if min(im.size) < 16: + interlace = 0 + + return interlace + + +def _write_local_header( + fp: IO[bytes], im: Image.Image, offset: tuple[int, int], flags: int +) -> None: + try: + transparency = im.encoderinfo["transparency"] + except KeyError: + transparency = None + + if "duration" in im.encoderinfo: + duration = int(im.encoderinfo["duration"] / 10) + else: + duration = 0 + + disposal = int(im.encoderinfo.get("disposal", 0)) + + if transparency is not None or duration != 0 or disposal: + packed_flag = 1 if transparency is not None else 0 + packed_flag |= disposal << 2 + + fp.write( + b"!" + + o8(249) # extension intro + + o8(4) # length + + o8(packed_flag) # packed fields + + o16(duration) # duration + + o8(transparency or 0) # transparency index + + o8(0) + ) + + include_color_table = im.encoderinfo.get("include_color_table") + if include_color_table: + palette_bytes = _get_palette_bytes(im) + color_table_size = _get_color_table_size(palette_bytes) + if color_table_size: + flags = flags | 128 # local color table flag + flags = flags | color_table_size + + fp.write( + b"," + + o16(offset[0]) # offset + + o16(offset[1]) + + o16(im.size[0]) # size + + o16(im.size[1]) + + o8(flags) # flags + ) + if include_color_table and color_table_size: + fp.write(_get_header_palette(palette_bytes)) + fp.write(o8(8)) # bits + + +def _save_netpbm(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + # Unused by default. + # To use, uncomment the register_save call at the end of the file. + # + # 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. + tempfile = im._dump() + + try: + with open(filename, "wb") as f: + if im.mode != "RGB": + subprocess.check_call( + ["ppmtogif", tempfile], stdout=f, stderr=subprocess.DEVNULL + ) + else: + # Pipe ppmquant output into ppmtogif + # "ppmquant 256 %s | ppmtogif > %s" % (tempfile, filename) + quant_cmd = ["ppmquant", "256", tempfile] + togif_cmd = ["ppmtogif"] + quant_proc = subprocess.Popen( + quant_cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL + ) + togif_proc = subprocess.Popen( + togif_cmd, + stdin=quant_proc.stdout, + stdout=f, + stderr=subprocess.DEVNULL, + ) + + # Allow ppmquant to receive SIGPIPE if ppmtogif exits + assert quant_proc.stdout is not None + quant_proc.stdout.close() + + retcode = quant_proc.wait() + if retcode: + raise subprocess.CalledProcessError(retcode, quant_cmd) + + retcode = togif_proc.wait() + if retcode: + raise subprocess.CalledProcessError(retcode, togif_cmd) + finally: + try: + os.unlink(tempfile) + except OSError: + pass + + +# Force optimization so that we can test performance against +# cases where it took lots of memory and time previously. +_FORCE_OPTIMIZE = False + + +def _get_optimize(im: Image.Image, info: dict[str, Any]) -> list[int] | None: + """ + Palette optimization is a potentially expensive operation. + + This function determines if the palette should be optimized using + some heuristics, then returns the list of palette entries in use. + + :param im: Image object + :param info: encoderinfo + :returns: list of indexes of palette entries in use, or None + """ + if im.mode in ("P", "L") and info and info.get("optimize"): + # Potentially expensive operation. + + # The palette saves 3 bytes per color not used, but palette + # lengths are restricted to 3*(2**N) bytes. Max saving would + # be 768 -> 6 bytes if we went all the way down to 2 colors. + # * If we're over 128 colors, we can't save any space. + # * If there aren't any holes, it's not worth collapsing. + # * If we have a 'large' image, the palette is in the noise. + + # create the new palette if not every color is used + optimise = _FORCE_OPTIMIZE or im.mode == "L" + if optimise or im.width * im.height < 512 * 512: + # check which colors are used + used_palette_colors = [] + for i, count in enumerate(im.histogram()): + if count: + used_palette_colors.append(i) + + if optimise or max(used_palette_colors) >= len(used_palette_colors): + return used_palette_colors + + assert im.palette is not None + num_palette_colors = len(im.palette.palette) // Image.getmodebands( + im.palette.mode + ) + current_palette_size = 1 << (num_palette_colors - 1).bit_length() + if ( + # check that the palette would become smaller when saved + len(used_palette_colors) <= current_palette_size // 2 + # check that the palette is not already the smallest possible size + and current_palette_size > 2 + ): + return used_palette_colors + return None + + +def _get_color_table_size(palette_bytes: bytes) -> int: + # calculate the palette size for the header + if not palette_bytes: + return 0 + elif len(palette_bytes) < 9: + return 1 + else: + return math.ceil(math.log(len(palette_bytes) // 3, 2)) - 1 + + +def _get_header_palette(palette_bytes: bytes) -> bytes: + """ + Returns the palette, null padded to the next power of 2 (*3) bytes + suitable for direct inclusion in the GIF header + + :param palette_bytes: Unpadded palette bytes, in RGBRGB form + :returns: Null padded palette + """ + color_table_size = _get_color_table_size(palette_bytes) + + # add the missing amount of bytes + # the palette has to be 2< 0: + palette_bytes += o8(0) * 3 * actual_target_size_diff + return palette_bytes + + +def _get_palette_bytes(im: Image.Image) -> bytes: + """ + Gets the palette for inclusion in the gif header + + :param im: Image object + :returns: Bytes, len<=768 suitable for inclusion in gif header + """ + if not im.palette: + return b"" + + palette = bytes(im.palette.palette) + if im.palette.mode == "RGBA": + palette = b"".join(palette[i * 4 : i * 4 + 3] for i in range(len(palette) // 3)) + return palette + + +def _get_background( + im: Image.Image, + info_background: int | tuple[int, int, int] | tuple[int, int, int, int] | None, +) -> int: + background = 0 + if info_background: + if isinstance(info_background, tuple): + # WebPImagePlugin stores an RGBA value in info["background"] + # So it must be converted to the same format as GifImagePlugin's + # info["background"] - a global color table index + assert im.palette is not None + try: + background = im.palette.getcolor(info_background, im) + except ValueError as e: + if str(e) not in ( + # If all 256 colors are in use, + # then there is no need for the background color + "cannot allocate more than 256 colors", + # Ignore non-opaque WebP background + "cannot add non-opaque RGBA color to RGB palette", + ): + raise + else: + background = info_background + return background + + +def _get_global_header(im: Image.Image, info: dict[str, Any]) -> list[bytes]: + """Return a list of strings representing a GIF header""" + + # Header Block + # https://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp + + version = b"87a" + if im.info.get("version") == b"89a" or ( + info + and ( + "transparency" in info + or info.get("loop") is not None + or info.get("duration") + or info.get("comment") + ) + ): + version = b"89a" + + background = _get_background(im, info.get("background")) + + palette_bytes = _get_palette_bytes(im) + color_table_size = _get_color_table_size(palette_bytes) + + header = [ + b"GIF" # signature + + version # version + + o16(im.size[0]) # canvas width + + o16(im.size[1]), # canvas height + # Logical Screen Descriptor + # size of global color table + global color table flag + o8(color_table_size + 128), # packed fields + # background + reserved/aspect + o8(background) + o8(0), + # Global Color Table + _get_header_palette(palette_bytes), + ] + if info.get("loop") is not None: + header.append( + b"!" + + o8(255) # extension intro + + o8(11) + + b"NETSCAPE2.0" + + o8(3) + + o8(1) + + o16(info["loop"]) # number of loops + + o8(0) + ) + if info.get("comment"): + comment_block = b"!" + o8(254) # extension intro + + comment = info["comment"] + if isinstance(comment, str): + comment = comment.encode() + for i in range(0, len(comment), 255): + subblock = comment[i : i + 255] + comment_block += o8(len(subblock)) + subblock + + comment_block += o8(0) + header.append(comment_block) + return header + + +def _write_frame_data( + fp: IO[bytes], + im_frame: Image.Image, + offset: tuple[int, int], + params: dict[str, Any], +) -> None: + try: + im_frame.encoderinfo = params + + # local image header + _write_local_header(fp, im_frame, offset, 0) + + ImageFile._save( + im_frame, + fp, + [ImageFile._Tile("gif", (0, 0) + im_frame.size, 0, RAWMODE[im_frame.mode])], + ) + + fp.write(b"\0") # end of image data + finally: + del im_frame.encoderinfo + + +# -------------------------------------------------------------------- +# Legacy GIF utilities + + +def getheader( + im: Image.Image, palette: _Palette | None = None, info: dict[str, Any] | None = None +) -> tuple[list[bytes], list[int] | None]: + """ + Legacy Method to get Gif data from image. + + Warning:: May modify image data. + + :param im: Image object + :param palette: bytes object containing the source palette, or .... + :param info: encoderinfo + :returns: tuple of(list of header items, optimized palette) + + """ + if info is None: + info = {} + + used_palette_colors = _get_optimize(im, info) + + if "background" not in info and "background" in im.info: + info["background"] = im.info["background"] + + im_mod = _normalize_palette(im, palette, info) + im.palette = im_mod.palette + im.im = im_mod.im + header = _get_global_header(im, info) + + return header, used_palette_colors + + +def getdata( + im: Image.Image, offset: tuple[int, int] = (0, 0), **params: Any +) -> list[bytes]: + """ + Legacy Method + + Return a list of strings representing this image. + The first string is a local image header, the rest contains + encoded image data. + + To specify duration, add the time in milliseconds, + e.g. ``getdata(im_frame, duration=1000)`` + + :param im: Image object + :param offset: Tuple of (x, y) pixels. Defaults to (0, 0) + :param \\**params: e.g. duration or other encoder info parameters + :returns: List of bytes containing GIF encoded frame data + + """ + from io import BytesIO + + class Collector(BytesIO): + data = [] + + def write(self, data: Buffer) -> int: + self.data.append(data) + return len(data) + + im.load() # make sure raster data is available + + fp = Collector() + + _write_frame_data(fp, im, offset, params) + + return fp.data + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(GifImageFile.format, GifImageFile, _accept) +Image.register_save(GifImageFile.format, _save) +Image.register_save_all(GifImageFile.format, _save_all) +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/voxvera/vendor/PIL/GimpGradientFile.py b/voxvera/vendor/PIL/GimpGradientFile.py new file mode 100644 index 0000000..5f26918 --- /dev/null +++ b/voxvera/vendor/PIL/GimpGradientFile.py @@ -0,0 +1,153 @@ +# +# 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. +# + +""" +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.) +""" +from __future__ import annotations + +from math import log, pi, sin, sqrt + +from ._binary import o8 + +TYPE_CHECKING = False +if TYPE_CHECKING: + from collections.abc import Callable + from typing import IO + +EPSILON = 1e-10 +"""""" # Enable auto-doc for data member + + +def linear(middle: float, pos: float) -> float: + 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: float, pos: float) -> float: + return pos ** (log(0.5) / log(max(middle, EPSILON))) + + +def sine(middle: float, pos: float) -> float: + return (sin((-pi / 2.0) + pi * linear(middle, pos)) + 1.0) / 2.0 + + +def sphere_increasing(middle: float, pos: float) -> float: + return sqrt(1.0 - (linear(middle, pos) - 1.0) ** 2) + + +def sphere_decreasing(middle: float, pos: float) -> float: + return 1.0 - sqrt(1.0 - linear(middle, pos) ** 2) + + +SEGMENTS = [linear, curved, sine, sphere_increasing, sphere_decreasing] +"""""" # Enable auto-doc for data member + + +class GradientFile: + gradient: ( + list[ + tuple[ + float, + float, + float, + list[float], + list[float], + Callable[[float, float], float], + ] + ] + | None + ) = None + + def getpalette(self, entries: int = 256) -> tuple[bytes, str]: + assert self.gradient is not None + palette = [] + + ix = 0 + x0, x1, xm, rgb0, rgb1, segment = self.gradient[ix] + + for i in range(entries): + x = i / (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" + + +class GimpGradientFile(GradientFile): + """File handler for GIMP's gradient format.""" + + def __init__(self, fp: IO[bytes]) -> None: + if not fp.readline().startswith(b"GIMP Gradient"): + msg = "not a GIMP gradient file" + raise SyntaxError(msg) + + 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) + + self.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: + msg = "cannot handle HSV colour space" + raise OSError(msg) + + self.gradient.append((x0, x1, xm, rgb0, rgb1, segment)) diff --git a/voxvera/vendor/PIL/GimpPaletteFile.py b/voxvera/vendor/PIL/GimpPaletteFile.py new file mode 100644 index 0000000..016257d --- /dev/null +++ b/voxvera/vendor/PIL/GimpPaletteFile.py @@ -0,0 +1,75 @@ +# +# 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. +# +from __future__ import annotations + +import re +from io import BytesIO + +TYPE_CHECKING = False +if TYPE_CHECKING: + from typing import IO + + +class GimpPaletteFile: + """File handler for GIMP's palette format.""" + + rawmode = "RGB" + + def _read(self, fp: IO[bytes], limit: bool = True) -> None: + if not fp.readline().startswith(b"GIMP Palette"): + msg = "not a GIMP palette file" + raise SyntaxError(msg) + + palette: list[int] = [] + i = 0 + while True: + if limit and i == 256 + 3: + break + + i += 1 + s = fp.readline() + if not s: + break + + # skip fields and comment lines + if re.match(rb"\w+:|#", s): + continue + if limit and len(s) > 100: + msg = "bad palette file" + raise SyntaxError(msg) + + v = s.split(maxsplit=3) + if len(v) < 3: + msg = "bad palette entry" + raise ValueError(msg) + + palette += (int(v[i]) for i in range(3)) + if limit and len(palette) == 768: + break + + self.palette = bytes(palette) + + def __init__(self, fp: IO[bytes]) -> None: + self._read(fp) + + @classmethod + def frombytes(cls, data: bytes) -> GimpPaletteFile: + self = cls.__new__(cls) + self._read(BytesIO(data), False) + return self + + def getpalette(self) -> tuple[bytes, str]: + return self.palette, self.rawmode diff --git a/voxvera/vendor/PIL/GribStubImagePlugin.py b/voxvera/vendor/PIL/GribStubImagePlugin.py new file mode 100644 index 0000000..146a6fa --- /dev/null +++ b/voxvera/vendor/PIL/GribStubImagePlugin.py @@ -0,0 +1,76 @@ +# +# 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 __future__ import annotations + +import os +from typing import IO + +from . import Image, ImageFile + +_handler = None + + +def register_handler(handler: ImageFile.StubHandler | None) -> None: + """ + Install application-specific GRIB image handler. + + :param handler: Handler object. + """ + global _handler + _handler = handler + + +# -------------------------------------------------------------------- +# Image adapter + + +def _accept(prefix: bytes) -> bool: + return len(prefix) >= 8 and prefix.startswith(b"GRIB") and prefix[7] == 1 + + +class GribStubImageFile(ImageFile.StubImageFile): + format = "GRIB" + format_description = "GRIB" + + def _open(self) -> None: + assert self.fp is not None + if not _accept(self.fp.read(8)): + msg = "Not a GRIB file" + raise SyntaxError(msg) + + self.fp.seek(-8, os.SEEK_CUR) + + # make something up + self._mode = "F" + self._size = 1, 1 + + loader = self._load() + if loader: + loader.open(self) + + def _load(self) -> ImageFile.StubHandler | None: + return _handler + + +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + if _handler is None or not hasattr(_handler, "save"): + msg = "GRIB save handler not installed" + raise OSError(msg) + _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/voxvera/vendor/PIL/Hdf5StubImagePlugin.py b/voxvera/vendor/PIL/Hdf5StubImagePlugin.py new file mode 100644 index 0000000..1523e95 --- /dev/null +++ b/voxvera/vendor/PIL/Hdf5StubImagePlugin.py @@ -0,0 +1,76 @@ +# +# 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 __future__ import annotations + +import os +from typing import IO + +from . import Image, ImageFile + +_handler = None + + +def register_handler(handler: ImageFile.StubHandler | None) -> None: + """ + Install application-specific HDF5 image handler. + + :param handler: Handler object. + """ + global _handler + _handler = handler + + +# -------------------------------------------------------------------- +# Image adapter + + +def _accept(prefix: bytes) -> bool: + return prefix.startswith(b"\x89HDF\r\n\x1a\n") + + +class HDF5StubImageFile(ImageFile.StubImageFile): + format = "HDF5" + format_description = "HDF5" + + def _open(self) -> None: + assert self.fp is not None + if not _accept(self.fp.read(8)): + msg = "Not an HDF file" + raise SyntaxError(msg) + + self.fp.seek(-8, os.SEEK_CUR) + + # make something up + self._mode = "F" + self._size = 1, 1 + + loader = self._load() + if loader: + loader.open(self) + + def _load(self) -> ImageFile.StubHandler | None: + return _handler + + +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + if _handler is None or not hasattr(_handler, "save"): + msg = "HDF5 save handler not installed" + raise OSError(msg) + _handler.save(im, fp, filename) + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(HDF5StubImageFile.format, HDF5StubImageFile, _accept) +Image.register_save(HDF5StubImageFile.format, _save) + +Image.register_extensions(HDF5StubImageFile.format, [".h5", ".hdf"]) diff --git a/voxvera/vendor/PIL/IcnsImagePlugin.py b/voxvera/vendor/PIL/IcnsImagePlugin.py new file mode 100644 index 0000000..058861d --- /dev/null +++ b/voxvera/vendor/PIL/IcnsImagePlugin.py @@ -0,0 +1,402 @@ +# +# The Python Imaging Library. +# $Id$ +# +# macOS icns file decoder, based on icns.py by Bob Ippolito. +# +# history: +# 2004-10-09 fl Turned into a PIL plugin; removed 2.3 dependencies. +# 2020-04-04 Allow saving on all operating systems. +# +# Copyright (c) 2004 by Bob Ippolito. +# Copyright (c) 2004 by Secret Labs. +# Copyright (c) 2004 by Fredrik Lundh. +# Copyright (c) 2014 by Alastair Houghton. +# Copyright (c) 2020 by Pan Jing. +# +# See the README file for information on usage and redistribution. +# +from __future__ import annotations + +import io +import os +import struct +import sys +from typing import IO + +from . import Image, ImageFile, PngImagePlugin, features + +enable_jpeg2k = features.check_codec("jpg_2000") +if enable_jpeg2k: + from . import Jpeg2KImagePlugin + +MAGIC = b"icns" +HEADERSIZE = 8 + + +def nextheader(fobj: IO[bytes]) -> tuple[bytes, int]: + return struct.unpack(">4sI", fobj.read(HEADERSIZE)) + + +def read_32t( + fobj: IO[bytes], start_length: tuple[int, int], size: tuple[int, int, int] +) -> dict[str, Image.Image]: + # 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": + msg = "Unknown signature, expecting 0x00000000" + raise SyntaxError(msg) + return read_32(fobj, (start + 4, length - 4), size) + + +def read_32( + fobj: IO[bytes], start_length: tuple[int, int], size: tuple[int, int, int] +) -> dict[str, Image.Image]: + """ + 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_int = byte[0] + if byte_int & 0x80: + blocksize = byte_int - 125 + byte = fobj.read(1) + for i in range(blocksize): + data.append(byte) + else: + blocksize = byte_int + 1 + data.append(fobj.read(blocksize)) + bytesleft -= blocksize + if bytesleft <= 0: + break + if bytesleft != 0: + msg = f"Error reading channel [{repr(bytesleft)} left]" + raise SyntaxError(msg) + 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: IO[bytes], start_length: tuple[int, int], size: tuple[int, int, int] +) -> dict[str, Image.Image]: + # Alpha masks seem to be uncompressed + start = start_length[0] + 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: IO[bytes], start_length: tuple[int, int], size: tuple[int, int, int] +) -> dict[str, Image.Image]: + (start, length) = start_length + fobj.seek(start) + sig = fobj.read(12) + + im: Image.Image + if sig.startswith(b"\x89PNG\x0d\x0a\x1a\x0a"): + fobj.seek(start) + im = PngImagePlugin.PngImageFile(fobj) + Image._decompression_bomb_check(im.size) + return {"RGBA": im} + elif ( + sig.startswith((b"\xff\x4f\xff\x51", b"\x0d\x0a\x87\x0a")) + or sig == b"\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a" + ): + if not enable_jpeg2k: + msg = ( + "Unsupported icon subimage format (rebuild PIL " + "with JPEG 2000 support to fix this)" + ) + raise ValueError(msg) + # j2k, jpc or j2c + fobj.seek(start) + jp2kstream = fobj.read(length) + f = io.BytesIO(jp2kstream) + im = Jpeg2KImagePlugin.Jpeg2KImageFile(f) + Image._decompression_bomb_check(im.size) + if im.mode != "RGBA": + im = im.convert("RGBA") + return {"RGBA": im} + else: + msg = "Unsupported icon subimage format" + raise ValueError(msg) + + +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: IO[bytes]) -> None: + """ + fobj is a file-like object as an icns resource + """ + # signature : (start, length) + self.dct = {} + self.fobj = fobj + sig, filesize = nextheader(fobj) + if not _accept(sig): + msg = "not an icns file" + raise SyntaxError(msg) + i = HEADERSIZE + while i < filesize: + sig, blocksize = nextheader(fobj) + if blocksize <= 0: + msg = "invalid block header" + raise SyntaxError(msg) + i += HEADERSIZE + blocksize -= HEADERSIZE + self.dct[sig] = (i, blocksize) + fobj.seek(blocksize, io.SEEK_CUR) + i += blocksize + + def itersizes(self) -> list[tuple[int, int, int]]: + 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) -> tuple[int, int, int]: + sizes = self.itersizes() + if not sizes: + msg = "No 32bit icon resources found" + raise SyntaxError(msg) + return max(sizes) + + def dataforsize(self, size: tuple[int, int, int]) -> dict[str, Image.Image]: + """ + 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: tuple[int, int] | tuple[int, int, int] | None = None + ) -> Image.Image: + if size is None: + size = self.bestsize() + elif len(size) == 2: + size = (size[0], size[1], 1) + channels = self.dataforsize(size) + + im = channels.get("RGBA") + if im: + return im + + im = channels["RGB"].copy() + try: + im.putalpha(channels["A"]) + except KeyError: + pass + return im + + +## +# Image plugin for Mac OS icons. + + +class IcnsImageFile(ImageFile.ImageFile): + """ + PIL 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) -> None: + assert self.fp is not None + self.icns = IcnsFile(self.fp) + self._mode = "RGBA" + self.info["sizes"] = self.icns.itersizes() + self.best_size = self.icns.bestsize() + self.size = ( + self.best_size[0] * self.best_size[2], + self.best_size[1] * self.best_size[2], + ) + + @property + def size(self) -> tuple[int, int]: + return self._size + + @size.setter + def size(self, value: tuple[int, int]) -> None: + # Check that a matching size exists, + # or that there is a scale that would create a size that matches + for size in self.info["sizes"]: + simple_size = size[0] * size[2], size[1] * size[2] + scale = simple_size[0] // value[0] + if simple_size[1] / value[1] == scale: + self._size = value + return + msg = "This is not one of the allowed sizes of this image" + raise ValueError(msg) + + def load(self, scale: int | None = None) -> Image.core.PixelAccess | None: + if scale is not None: + width, height = self.size[:2] + self.size = width * scale, height * scale + self.best_size = width, height, scale + + px = Image.Image.load(self) + if self._im is not None and self.im.size == self.size: + # Already loaded + return px + 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 + px = im.load() + + self.im = im.im + self._mode = im.mode + self.size = im.size + + return px + + +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + """ + Saves the image as a series of PNG files, + that are then combined into a .icns file. + """ + if hasattr(fp, "flush"): + fp.flush() + + sizes = { + b"ic07": 128, + b"ic08": 256, + b"ic09": 512, + b"ic10": 1024, + b"ic11": 32, + b"ic12": 64, + b"ic13": 256, + b"ic14": 512, + } + provided_images = {im.width: im for im in im.encoderinfo.get("append_images", [])} + size_streams = {} + for size in set(sizes.values()): + image = ( + provided_images[size] + if size in provided_images + else im.resize((size, size)) + ) + + temp = io.BytesIO() + image.save(temp, "png") + size_streams[size] = temp.getvalue() + + entries = [] + for type, size in sizes.items(): + stream = size_streams[size] + entries.append((type, HEADERSIZE + len(stream), stream)) + + # Header + fp.write(MAGIC) + file_length = HEADERSIZE # Header + file_length += HEADERSIZE + 8 * len(entries) # TOC + file_length += sum(entry[1] for entry in entries) + fp.write(struct.pack(">i", file_length)) + + # TOC + fp.write(b"TOC ") + fp.write(struct.pack(">i", HEADERSIZE + len(entries) * HEADERSIZE)) + for entry in entries: + fp.write(entry[0]) + fp.write(struct.pack(">i", entry[1])) + + # Data + for entry in entries: + fp.write(entry[0]) + fp.write(struct.pack(">i", entry[1])) + fp.write(entry[2]) + + if hasattr(fp, "flush"): + fp.flush() + + +def _accept(prefix: bytes) -> bool: + return prefix.startswith(MAGIC) + + +Image.register_open(IcnsImageFile.format, IcnsImageFile, _accept) +Image.register_extension(IcnsImageFile.format, ".icns") + +Image.register_save(IcnsImageFile.format, _save) +Image.register_mime(IcnsImageFile.format, "image/icns") + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Syntax: python3 IcnsImagePlugin.py [file]") + sys.exit() + + with open(sys.argv[1], "rb") as fp: + imf = IcnsImageFile(fp) + for size in imf.info["sizes"]: + width, height, scale = imf.size = size + imf.save(f"out-{width}-{height}-{scale}.png") + with Image.open(sys.argv[1]) as im: + im.save("out.png") + if sys.platform == "windows": + os.startfile("out.png") diff --git a/voxvera/vendor/PIL/IcoImagePlugin.py b/voxvera/vendor/PIL/IcoImagePlugin.py new file mode 100644 index 0000000..8dd57ff --- /dev/null +++ b/voxvera/vendor/PIL/IcoImagePlugin.py @@ -0,0 +1,396 @@ +# +# 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/archive/p/casadebender/wikis/Win32IconImagePlugin.wiki +# +# Copyright 2008 Bryan Davis +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Icon format references: +# * https://en.wikipedia.org/wiki/ICO_(file_format) +# * https://msdn.microsoft.com/en-us/library/ms997538.aspx +from __future__ import annotations + +import warnings +from io import BytesIO +from math import ceil, log +from typing import IO, NamedTuple + +from . import BmpImagePlugin, Image, ImageFile, PngImagePlugin +from ._binary import i16le as i16 +from ._binary import i32le as i32 +from ._binary import o8 +from ._binary import o16le as o16 +from ._binary import o32le as o32 + +# +# -------------------------------------------------------------------- + +_MAGIC = b"\0\0\1\0" + + +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + fp.write(_MAGIC) # (2+2) + bmp = im.encoderinfo.get("bitmap_format") == "bmp" + sizes = im.encoderinfo.get( + "sizes", + [(16, 16), (24, 24), (32, 32), (48, 48), (64, 64), (128, 128), (256, 256)], + ) + frames = [] + provided_ims = [im] + im.encoderinfo.get("append_images", []) + width, height = im.size + for size in sorted(set(sizes)): + if size[0] > width or size[1] > height or size[0] > 256 or size[1] > 256: + continue + + for provided_im in provided_ims: + if provided_im.size != size: + continue + frames.append(provided_im) + if bmp: + bits = BmpImagePlugin.SAVE[provided_im.mode][1] + bits_used = [bits] + for other_im in provided_ims: + if other_im.size != size: + continue + bits = BmpImagePlugin.SAVE[other_im.mode][1] + if bits not in bits_used: + # Another image has been supplied for this size + # with a different bit depth + frames.append(other_im) + bits_used.append(bits) + break + else: + # TODO: invent a more convenient method for proportional scalings + frame = provided_im.copy() + frame.thumbnail(size, Image.Resampling.LANCZOS, reducing_gap=None) + frames.append(frame) + fp.write(o16(len(frames))) # idCount(2) + offset = fp.tell() + len(frames) * 16 + for frame in frames: + width, height = frame.size + # 0 means 256 + fp.write(o8(width if width < 256 else 0)) # bWidth(1) + fp.write(o8(height if height < 256 else 0)) # bHeight(1) + + bits, colors = BmpImagePlugin.SAVE[frame.mode][1:] if bmp else (32, 0) + fp.write(o8(colors)) # bColorCount(1) + fp.write(b"\0") # bReserved(1) + fp.write(b"\0\0") # wPlanes(2) + fp.write(o16(bits)) # wBitCount(2) + + image_io = BytesIO() + if bmp: + frame.save(image_io, "dib") + + if bits != 32: + and_mask = Image.new("1", size) + ImageFile._save( + and_mask, + image_io, + [ImageFile._Tile("raw", (0, 0) + size, 0, ("1", 0, -1))], + ) + else: + frame.save(image_io, "png") + image_io.seek(0) + image_bytes = image_io.read() + if bmp: + image_bytes = image_bytes[:8] + o32(height * 2) + image_bytes[12:] + bytes_len = len(image_bytes) + fp.write(o32(bytes_len)) # dwBytesInRes(4) + fp.write(o32(offset)) # dwImageOffset(4) + current = fp.tell() + fp.seek(offset) + fp.write(image_bytes) + offset = offset + bytes_len + fp.seek(current) + + +def _accept(prefix: bytes) -> bool: + return prefix.startswith(_MAGIC) + + +class IconHeader(NamedTuple): + width: int + height: int + nb_color: int + reserved: int + planes: int + bpp: int + size: int + offset: int + dim: tuple[int, int] + square: int + color_depth: int + + +class IcoFile: + def __init__(self, buf: IO[bytes]) -> None: + """ + Parse image from file-like object containing ico file data + """ + + # check magic + s = buf.read(6) + if not _accept(s): + msg = "not an ICO file" + raise SyntaxError(msg) + + 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) + + # See Wikipedia + width = s[0] or 256 + height = s[1] or 256 + + # No. of colors in image (0 if >=8bpp) + nb_color = s[2] + bpp = i16(s, 6) + icon_header = IconHeader( + width=width, + height=height, + nb_color=nb_color, + reserved=s[3], + planes=i16(s, 4), + bpp=i16(s, 6), + size=i32(s, 8), + offset=i32(s, 12), + dim=(width, height), + square=width * height, + # See Wikipedia notes about color depth. + # We need this just to differ images with equal sizes + color_depth=bpp or (nb_color != 0 and ceil(log(nb_color, 2))) or 256, + ) + + 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.square, reverse=True) + + def sizes(self) -> set[tuple[int, int]]: + """ + Get a set of all available icon sizes and color depths. + """ + return {(h.width, h.height) for h in self.entry} + + def getentryindex(self, size: tuple[int, int], bpp: int | bool = False) -> int: + for i, h in enumerate(self.entry): + if size == h.dim and (bpp is False or bpp == h.color_depth): + return i + return 0 + + def getimage(self, size: tuple[int, int], bpp: int | bool = False) -> Image.Image: + """ + Get an image from the icon + """ + return self.frame(self.getentryindex(size, bpp)) + + def frame(self, idx: int) -> Image.Image: + """ + 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) + + im: Image.Image + if data[:8] == PngImagePlugin._MAGIC: + # png frame + im = PngImagePlugin.PngImageFile(self.buf) + Image._decompression_bomb_check(im.size) + else: + # XOR + AND mask bmp frame + im = BmpImagePlugin.DibImageFile(self.buf) + Image._decompression_bomb_check(im.size) + + # 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] = ImageFile._Tile(d, (0, 0) + im.size, o, a) + + # figure out where AND mask image starts + if header.bpp == 32: + # 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 + try: + mask = Image.frombuffer( + "L", # 8bpp + im.size, # (w, h) + alpha_bytes, # source chars + "raw", # raw decoder + ("L", 0, -1), # 8bpp inverted, unpadded, reversed + ) + except ValueError: + if ImageFile.LOAD_TRUNCATED_IMAGES: + mask = None + else: + raise + 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 + + total_bytes = int((w * im.size[1]) / 8) + and_mask_offset = header.offset + header.size - total_bytes + + self.buf.seek(and_mask_offset) + mask_data = self.buf.read(total_bytes) + + # convert raw data to image + try: + mask = Image.frombuffer( + "1", # 1 bpp + im.size, # (w, h) + mask_data, # source chars + "raw", # raw decoder + ("1;I", int(w / 8), -1), # 1bpp inverted, padded, reversed + ) + except ValueError: + if ImageFile.LOAD_TRUNCATED_IMAGES: + mask = None + else: + raise + + # now we have two images, im is XOR image and mask is AND image + + # apply mask image as alpha channel + if mask: + 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. + + When saving, PNG compression is used. Support for this was only added in + Windows Vista. If you are unable to view the icon in Windows, convert the + image to "RGBA" mode before saving. + + This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis + . + https://code.google.com/archive/p/casadebender/wikis/Win32IconImagePlugin.wiki + """ + + format = "ICO" + format_description = "Windows Icon" + + def _open(self) -> None: + assert self.fp is not None + self.ico = IcoFile(self.fp) + self.info["sizes"] = self.ico.sizes() + self.size = self.ico.entry[0].dim + self.load() + + @property + def size(self) -> tuple[int, int]: + return self._size + + @size.setter + def size(self, value: tuple[int, int]) -> None: + if value not in self.info["sizes"]: + msg = "This is not one of the allowed sizes of this image" + raise ValueError(msg) + self._size = value + + def load(self) -> Image.core.PixelAccess | None: + if self._im is not None and self.im.size == self.size: + # Already loaded + return Image.Image.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 + if im.palette: + self.palette = im.palette + if im.size != self.size: + warnings.warn("Image was not the expected size") + + index = self.ico.getentryindex(self.size) + sizes = list(self.info["sizes"]) + sizes[index] = im.size + self.info["sizes"] = set(sizes) + + self.size = im.size + return Image.Image.load(self) + + def load_seek(self, pos: int) -> None: + # Flag the ImageFile.Parser so that it + # just does all the decode at the end. + pass + + +# +# -------------------------------------------------------------------- + + +Image.register_open(IcoImageFile.format, IcoImageFile, _accept) +Image.register_save(IcoImageFile.format, _save) +Image.register_extension(IcoImageFile.format, ".ico") + +Image.register_mime(IcoImageFile.format, "image/x-icon") diff --git a/voxvera/vendor/PIL/ImImagePlugin.py b/voxvera/vendor/PIL/ImImagePlugin.py new file mode 100644 index 0000000..ef54f16 --- /dev/null +++ b/voxvera/vendor/PIL/ImImagePlugin.py @@ -0,0 +1,390 @@ +# +# 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. +# +from __future__ import annotations + +import os +import re +from typing import IO, Any + +from . import Image, ImageFile, ImagePalette +from ._util import DeferredError + +# -------------------------------------------------------------------- +# 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"), + "PA image": ("LA", "PA;L"), + "RGBA image": ("RGBA", "RGBA;L"), + "RGBX image": ("RGB", "RGBX;L"), + "CMYK image": ("CMYK", "CMYK;L"), + "YCC image": ("YCbCr", "YCbCr;L"), +} + +# ifunc95 extensions +for i in ["8", "8S", "16", "16S", "32", "32F"]: + OPEN[f"L {i} image"] = ("F", f"F;{i}") + OPEN[f"L*{i} image"] = ("F", f"F;{i}") +for i in ["16", "16L", "16B"]: + OPEN[f"L {i} image"] = (f"I;{i}", f"I;{i}") + OPEN[f"L*{i} image"] = (f"I;{i}", f"I;{i}") +for i in ["32S"]: + OPEN[f"L {i} image"] = ("I", f"I;{i}") + OPEN[f"L*{i} image"] = ("I", f"I;{i}") +for j in range(2, 33): + OPEN[f"L*{j} image"] = ("F", f"F;{j}") + + +# -------------------------------------------------------------------- +# Read IM directory + +split = re.compile(rb"^([A-Za-z][^:]*):[ \t]*(.*)[ \t]*$") + + +def number(s: Any) -> float: + 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" + _close_exclusive_fp_after_loading = False + + def _open(self) -> None: + # Quick rejection: if there's not an LF among the first + # 100 bytes, this is (probably) not a text header. + + assert self.fp is not None + if b"\n" not in self.fp.read(100): + msg = "not an IM file" + raise SyntaxError(msg) + 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: + msg = "not an IM file" + raise SyntaxError(msg) + + if s.endswith(b"\r\n"): + s = s[:-2] + elif s.endswith(b"\n"): + s = s[:-1] + + try: + m = split.match(s) + except re.error as e: + msg = "not an IM file" + raise SyntaxError(msg) from e + + 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: + msg = f"Syntax error in IM header: {s.decode('ascii', 'replace')}" + raise SyntaxError(msg) + + if not n: + msg = "Not an IM file" + raise SyntaxError(msg) + + # Basic attributes + self._size = self.info[SIZE] + self._mode = self.info[MODE] + + # Skip forward to start of image data + while s and not s.startswith(b"\x1a"): + s = self.fp.read(1) + if not s: + msg = "File truncated" + raise SyntaxError(msg) + + 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 palette[i] != i: + linear = 0 + else: + greyscale = 0 + if self.mode in ["L", "LA", "P", "PA"]: + if greyscale: + if not linear: + self.lut = list(palette[:256]) + else: + if self.mode in ["L", "P"]: + self._mode = self.rawmode = "P" + elif self.mode in ["LA", "PA"]: + self._mode = "PA" + self.rawmode = "PA;L" + self.palette = ImagePalette.raw("RGB;L", palette) + elif self.mode == "RGB": + if not greyscale or not linear: + self.lut = list(palette) + + self.frame = 0 + + self.__offset = offs = self.fp.tell() + + self._fp = self.fp # FIXME: hack + + if self.rawmode.startswith("F;"): + # ifunc95 formats + try: + # use bit decoder (if necessary) + bits = int(self.rawmode[2:]) + if bits not in [8, 16, 32]: + self.tile = [ + ImageFile._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 = [ + ImageFile._Tile("raw", (0, 0) + self.size, offs, ("G", 0, -1)), + ImageFile._Tile("raw", (0, 0) + self.size, offs + size, ("R", 0, -1)), + ImageFile._Tile( + "raw", (0, 0) + self.size, offs + 2 * size, ("B", 0, -1) + ), + ] + else: + # LabEye/IFUNC files + self.tile = [ + ImageFile._Tile("raw", (0, 0) + self.size, offs, (self.rawmode, 0, -1)) + ] + + @property + def n_frames(self) -> int: + return self.info[FRAMES] + + @property + def is_animated(self) -> bool: + return self.info[FRAMES] > 1 + + def seek(self, frame: int) -> None: + if not self._seek_check(frame): + return + if isinstance(self._fp, DeferredError): + raise self._fp.ex + + 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 = [ + ImageFile._Tile("raw", (0, 0) + self.size, offs, (self.rawmode, 0, -1)) + ] + + def tell(self) -> int: + 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: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + try: + image_type, rawmode = SAVE[im.mode] + except KeyError as e: + msg = f"Cannot save {im.mode} images as IM" + raise ValueError(msg) from e + + frames = im.encoderinfo.get("frames", 1) + + fp.write(f"Image type: {image_type} image\r\n".encode("ascii")) + if filename: + # Each line must be 100 characters or less, + # or: SyntaxError("not an IM file") + # 8 characters are used for "Name: " and "\r\n" + # Keep just the filename, ditch the potentially overlong path + if isinstance(filename, bytes): + filename = filename.decode("ascii") + name, ext = os.path.splitext(os.path.basename(filename)) + name = "".join([name[: 92 - len(ext)], ext]) + + fp.write(f"Name: {name}\r\n".encode("ascii")) + fp.write(f"Image size (x*y): {im.size[0]}*{im.size[1]}\r\n".encode("ascii")) + fp.write(f"File size (no of images): {frames}\r\n".encode("ascii")) + if im.mode in ["P", "PA"]: + fp.write(b"Lut: 1\r\n") + fp.write(b"\000" * (511 - fp.tell()) + b"\032") + if im.mode in ["P", "PA"]: + im_palette = im.im.getpalette("RGB", "RGB;L") + colors = len(im_palette) // 3 + palette = b"" + for i in range(3): + palette += im_palette[colors * i : colors * (i + 1)] + palette += b"\x00" * (256 - colors) + fp.write(palette) # 768 bytes + ImageFile._save( + im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 0, (rawmode, 0, -1))] + ) + + +# +# -------------------------------------------------------------------- +# Registry + + +Image.register_open(ImImageFile.format, ImImageFile) +Image.register_save(ImImageFile.format, _save) + +Image.register_extension(ImImageFile.format, ".im") diff --git a/voxvera/vendor/PIL/Image.py b/voxvera/vendor/PIL/Image.py new file mode 100644 index 0000000..57ebea6 --- /dev/null +++ b/voxvera/vendor/PIL/Image.py @@ -0,0 +1,4246 @@ +# +# 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 annotations + +import abc +import atexit +import builtins +import io +import logging +import math +import os +import re +import struct +import sys +import tempfile +import warnings +from collections.abc import MutableMapping +from enum import IntEnum +from typing import IO, Protocol, cast + +# VERSION was removed in Pillow 6.0.0. +# PILLOW_VERSION was removed in Pillow 9.0.0. +# Use __version__ instead. +from . import ( + ExifTags, + ImageMode, + TiffTags, + UnidentifiedImageError, + __version__, + _plugins, +) +from ._binary import i32le, o32be, o32le +from ._deprecate import deprecate +from ._util import DeferredError, is_path + +ElementTree: ModuleType | None +try: + from defusedxml import ElementTree +except ImportError: + ElementTree = None + +TYPE_CHECKING = False +if TYPE_CHECKING: + from collections.abc import Callable, Iterator, Sequence + from types import ModuleType + from typing import Any, Literal + +logger = logging.getLogger(__name__) + + +class DecompressionBombWarning(RuntimeWarning): + pass + + +class DecompressionBombError(Exception): + pass + + +WARN_POSSIBLE_FORMATS: bool = False + +# Limit to around a quarter gigabyte for a 24-bit (3 bpp) image +MAX_IMAGE_PIXELS: int | None = int(1024 * 1024 * 1024 // 4 // 3) + + +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 . import _imaging as core + + if __version__ != getattr(core, "PILLOW_VERSION", None): + msg = ( + "The _imaging extension was built for another version of Pillow or PIL:\n" + f"Core version: {getattr(core, 'PILLOW_VERSION', None)}\n" + f"Pillow version: {__version__}" + ) + raise ImportError(msg) + +except ImportError as v: + # 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) + # Fail here anyway. Don't let people run with a mostly broken Pillow. + # see docs/porting.rst + raise + + +# +# Constants + + +# transpose +class Transpose(IntEnum): + FLIP_LEFT_RIGHT = 0 + FLIP_TOP_BOTTOM = 1 + ROTATE_90 = 2 + ROTATE_180 = 3 + ROTATE_270 = 4 + TRANSPOSE = 5 + TRANSVERSE = 6 + + +# transforms (also defined in Imaging.h) +class Transform(IntEnum): + AFFINE = 0 + EXTENT = 1 + PERSPECTIVE = 2 + QUAD = 3 + MESH = 4 + + +# resampling filters (also defined in Imaging.h) +class Resampling(IntEnum): + NEAREST = 0 + BOX = 4 + BILINEAR = 2 + HAMMING = 5 + BICUBIC = 3 + LANCZOS = 1 + + +_filters_support = { + Resampling.BOX: 0.5, + Resampling.BILINEAR: 1.0, + Resampling.HAMMING: 1.0, + Resampling.BICUBIC: 2.0, + Resampling.LANCZOS: 3.0, +} + + +# dithers +class Dither(IntEnum): + NONE = 0 + ORDERED = 1 # Not yet implemented + RASTERIZE = 2 # Not yet implemented + FLOYDSTEINBERG = 3 # default + + +# palettes/quantizers +class Palette(IntEnum): + WEB = 0 + ADAPTIVE = 1 + + +class Quantize(IntEnum): + MEDIANCUT = 0 + MAXCOVERAGE = 1 + FASTOCTREE = 2 + LIBIMAGEQUANT = 3 + + +module = sys.modules[__name__] +for enum in (Transpose, Transform, Resampling, Dither, Palette, Quantize): + for item in enum: + setattr(module, item.name, item.value) + + +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 + +TYPE_CHECKING = False +if TYPE_CHECKING: + import mmap + from xml.etree.ElementTree import Element + + from IPython.lib.pretty import PrettyPrinter + + from . import ImageFile, ImageFilter, ImagePalette, ImageQt, TiffImagePlugin + from ._typing import CapsuleType, NumpyArray, StrOrBytesPath +ID: list[str] = [] +OPEN: dict[ + str, + tuple[ + Callable[[IO[bytes], str | bytes], ImageFile.ImageFile], + Callable[[bytes], bool | str] | None, + ], +] = {} +MIME: dict[str, str] = {} +SAVE: dict[str, Callable[[Image, IO[bytes], str | bytes], None]] = {} +SAVE_ALL: dict[str, Callable[[Image, IO[bytes], str | bytes], None]] = {} +EXTENSION: dict[str, str] = {} +DECODERS: dict[str, type[ImageFile.PyDecoder]] = {} +ENCODERS: dict[str, type[ImageFile.PyEncoder]] = {} + +# -------------------------------------------------------------------- +# Modes + +_ENDIAN = "<" if sys.byteorder == "little" else ">" + + +def _conv_type_shape(im: Image) -> tuple[tuple[int, ...], str]: + m = ImageMode.getmode(im.mode) + shape: tuple[int, ...] = (im.height, im.width) + extra = len(m.bands) + if extra != 1: + shape += (extra,) + return shape, m.typestr + + +MODES = [ + "1", + "CMYK", + "F", + "HSV", + "I", + "I;16", + "I;16B", + "I;16L", + "I;16N", + "L", + "LA", + "La", + "LAB", + "P", + "PA", + "RGB", + "RGBA", + "RGBa", + "RGBX", + "YCbCr", +] + +# raw modes that may be memory mapped. NOTE: if you change this, you +# may have to modify the stride calculation in map.c too! +_MAPMODES = ("L", "P", "RGBX", "RGBA", "CMYK", "I;16", "I;16L", "I;16B") + + +def getmodebase(mode: str) -> str: + """ + Gets the "base" mode for given mode. This function returns "L" for + images that contain grayscale data, and "RGB" for images that + contain color data. + + :param mode: Input mode. + :returns: "L" or "RGB". + :exception KeyError: If the input mode was not a standard mode. + """ + return ImageMode.getmode(mode).basemode + + +def getmodetype(mode: str) -> str: + """ + Gets the storage type mode. Given a mode, this function returns a + single-layer mode suitable for storing individual bands. + + :param mode: Input mode. + :returns: "L", "I", or "F". + :exception KeyError: If the input mode was not a standard mode. + """ + return ImageMode.getmode(mode).basetype + + +def getmodebandnames(mode: str) -> tuple[str, ...]: + """ + Gets a list of individual band names. Given a mode, this function returns + a tuple containing the names of individual bands (use + :py:method:`~PIL.Image.getmodetype` to get the mode used to store each + individual band. + + :param mode: Input mode. + :returns: A tuple containing band names. The length of the tuple + gives the number of bands in an image of the given mode. + :exception KeyError: If the input mode was not a standard mode. + """ + return ImageMode.getmode(mode).bands + + +def getmodebands(mode: str) -> int: + """ + Gets the number of individual bands for this mode. + + :param mode: Input mode. + :returns: The number of bands in this mode. + :exception KeyError: If the input mode was not a standard mode. + """ + return len(ImageMode.getmode(mode).bands) + + +# -------------------------------------------------------------------- +# Helpers + +_initialized = 0 + + +def preinit() -> None: + """ + Explicitly loads BMP, GIF, JPEG, PPM and PPM file format drivers. + + It is called when opening or saving images. + """ + + global _initialized + if _initialized >= 1: + return + + try: + from . import BmpImagePlugin + + assert BmpImagePlugin + except ImportError: + pass + try: + from . import GifImagePlugin + + assert GifImagePlugin + except ImportError: + pass + try: + from . import JpegImagePlugin + + assert JpegImagePlugin + except ImportError: + pass + try: + from . import PpmImagePlugin + + assert PpmImagePlugin + except ImportError: + pass + try: + from . import PngImagePlugin + + assert PngImagePlugin + except ImportError: + pass + + _initialized = 1 + + +def init() -> bool: + """ + Explicitly initializes the Python Imaging Library. This function + loads all available file format drivers. + + It is called when opening or saving images if :py:meth:`~preinit()` is + insufficient, and by :py:meth:`~PIL.features.pilinfo`. + """ + + global _initialized + if _initialized >= 2: + return False + + parent_name = __name__.rpartition(".")[0] + for plugin in _plugins: + try: + logger.debug("Importing %s", plugin) + __import__(f"{parent_name}.{plugin}", globals(), locals(), []) + except ImportError as e: + logger.debug("Image: failed to import %s: %s", plugin, e) + + if OPEN or SAVE: + _initialized = 2 + return True + return False + + +# -------------------------------------------------------------------- +# Codec factories (used by tobytes/frombytes and ImageFile.load) + + +def _getdecoder( + mode: str, decoder_name: str, args: Any, extra: tuple[Any, ...] = () +) -> core.ImagingDecoder | ImageFile.PyDecoder: + # tweak arguments + if args is None: + args = () + elif not isinstance(args, tuple): + args = (args,) + + try: + decoder = DECODERS[decoder_name] + except KeyError: + pass + else: + return decoder(mode, *args + extra) + + try: + # get decoder + decoder = getattr(core, f"{decoder_name}_decoder") + except AttributeError as e: + msg = f"decoder {decoder_name} not available" + raise OSError(msg) from e + return decoder(mode, *args + extra) + + +def _getencoder( + mode: str, encoder_name: str, args: Any, extra: tuple[Any, ...] = () +) -> core.ImagingEncoder | ImageFile.PyEncoder: + # tweak arguments + if args is None: + args = () + elif not isinstance(args, tuple): + args = (args,) + + try: + encoder = ENCODERS[encoder_name] + except KeyError: + pass + else: + return encoder(mode, *args + extra) + + try: + # get encoder + encoder = getattr(core, f"{encoder_name}_encoder") + except AttributeError as e: + msg = f"encoder {encoder_name} not available" + raise OSError(msg) from e + return encoder(mode, *args + extra) + + +# -------------------------------------------------------------------- +# Simple expression analyzer + + +class ImagePointTransform: + """ + Used with :py:meth:`~PIL.Image.Image.point` for single band images with more than + 8 bits, this represents an affine transformation, where the value is multiplied by + ``scale`` and ``offset`` is added. + """ + + def __init__(self, scale: float, offset: float) -> None: + self.scale = scale + self.offset = offset + + def __neg__(self) -> ImagePointTransform: + return ImagePointTransform(-self.scale, -self.offset) + + def __add__(self, other: ImagePointTransform | float) -> ImagePointTransform: + if isinstance(other, ImagePointTransform): + return ImagePointTransform( + self.scale + other.scale, self.offset + other.offset + ) + return ImagePointTransform(self.scale, self.offset + other) + + __radd__ = __add__ + + def __sub__(self, other: ImagePointTransform | float) -> ImagePointTransform: + return self + -other + + def __rsub__(self, other: ImagePointTransform | float) -> ImagePointTransform: + return other + -self + + def __mul__(self, other: ImagePointTransform | float) -> ImagePointTransform: + if isinstance(other, ImagePointTransform): + return NotImplemented + return ImagePointTransform(self.scale * other, self.offset * other) + + __rmul__ = __mul__ + + def __truediv__(self, other: ImagePointTransform | float) -> ImagePointTransform: + if isinstance(other, ImagePointTransform): + return NotImplemented + return ImagePointTransform(self.scale / other, self.offset / other) + + +def _getscaleoffset( + expr: Callable[[ImagePointTransform], ImagePointTransform | float], +) -> tuple[float, float]: + a = expr(ImagePointTransform(1, 0)) + return (a.scale, a.offset) if isinstance(a, ImagePointTransform) else (0, a) + + +# -------------------------------------------------------------------- +# Implementation wrapper + + +class SupportsGetData(Protocol): + def getdata( + self, + ) -> tuple[Transform, Sequence[int]]: ... + + +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: str | None = None + format_description: str | None = None + _close_exclusive_fp_after_loading = True + + def __init__(self) -> None: + # FIXME: take "new" parameters / other image? + self._im: core.ImagingCore | DeferredError | None = None + self._mode = "" + self._size = (0, 0) + self.palette: ImagePalette.ImagePalette | None = None + self.info: dict[str | tuple[int, int], Any] = {} + self.readonly = 0 + self._exif: Exif | None = None + + @property + def im(self) -> core.ImagingCore: + if isinstance(self._im, DeferredError): + raise self._im.ex + assert self._im is not None + return self._im + + @im.setter + def im(self, im: core.ImagingCore) -> None: + self._im = im + + @property + def width(self) -> int: + return self.size[0] + + @property + def height(self) -> int: + return self.size[1] + + @property + def size(self) -> tuple[int, int]: + return self._size + + @property + def mode(self) -> str: + return self._mode + + @property + def readonly(self) -> int: + return (self._im and self._im.readonly) or self._readonly + + @readonly.setter + def readonly(self, readonly: int) -> None: + self._readonly = readonly + + def _new(self, im: core.ImagingCore) -> Image: + new = Image() + new.im = im + new._mode = im.mode + new._size = im.size + if im.mode in ("P", "PA"): + if self.palette: + new.palette = self.palette.copy() + else: + from . import ImagePalette + + new.palette = ImagePalette.ImagePalette() + new.info = self.info.copy() + return new + + # Context manager support + def __enter__(self) -> Image: + return self + + def __exit__(self, *args: object) -> None: + pass + + def close(self) -> None: + """ + This operation will destroy the image core and release its memory. + The image data will be unusable afterward. + + This function is required to close images that have multiple frames or + have not had their file read and closed by the + :py:meth:`~PIL.Image.Image.load` method. See :ref:`file-handling` for + more information. + """ + if getattr(self, "map", None): + if sys.platform == "win32" and hasattr(sys, "pypy_version_info"): + self.map.close() + self.map: mmap.mmap | None = None + + # 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 = DeferredError(ValueError("Operation on closed image")) + + def _copy(self) -> None: + self.load() + self.im = self.im.copy() + self.readonly = 0 + + def _ensure_mutable(self) -> None: + if self.readonly: + self._copy() + else: + self.load() + + def _dump( + self, file: str | None = None, format: str | None = None, **options: Any + ) -> str: + suffix = "" + if format: + suffix = f".{format}" + + if not file: + f, filename = tempfile.mkstemp(suffix) + os.close(f) + else: + filename = file + if not filename.endswith(suffix): + filename = filename + suffix + + self.load() + + if not format or format == "PPM": + self.im.save_ppm(filename) + else: + self.save(filename, format, **options) + + return filename + + def __eq__(self, other: object) -> bool: + if self.__class__ is not other.__class__: + return False + assert isinstance(other, Image) + return ( + self.mode == other.mode + and self.size == other.size + and self.info == other.info + and self.getpalette() == other.getpalette() + and self.tobytes() == other.tobytes() + ) + + def __repr__(self) -> str: + return ( + f"<{self.__class__.__module__}.{self.__class__.__name__} " + f"image mode={self.mode} size={self.size[0]}x{self.size[1]} " + f"at 0x{id(self):X}>" + ) + + def _repr_pretty_(self, p: PrettyPrinter, cycle: bool) -> None: + """IPython plain text display support""" + + # Same as __repr__ but without unpredictable id(self), + # to keep Jupyter notebook `text/plain` output stable. + p.text( + f"<{self.__class__.__module__}.{self.__class__.__name__} " + f"image mode={self.mode} size={self.size[0]}x{self.size[1]}>" + ) + + def _repr_image(self, image_format: str, **kwargs: Any) -> bytes | None: + """Helper function for iPython display hook. + + :param image_format: Image format. + :returns: image as bytes, saved into the given format. + """ + b = io.BytesIO() + try: + self.save(b, image_format, **kwargs) + except Exception: + return None + return b.getvalue() + + def _repr_png_(self) -> bytes | None: + """iPython display hook support for PNG format. + + :returns: PNG version of the image as bytes + """ + return self._repr_image("PNG", compress_level=1) + + def _repr_jpeg_(self) -> bytes | None: + """iPython display hook support for JPEG format. + + :returns: JPEG version of the image as bytes + """ + return self._repr_image("JPEG") + + @property + def __array_interface__(self) -> dict[str, str | bytes | int | tuple[int, ...]]: + # numpy array interface support + new: dict[str, str | bytes | int | tuple[int, ...]] = {"version": 3} + if self.mode == "1": + # Binary images need to be extended from bits to bytes + # See: https://github.com/python-pillow/Pillow/issues/350 + new["data"] = self.tobytes("raw", "L") + else: + new["data"] = self.tobytes() + new["shape"], new["typestr"] = _conv_type_shape(self) + return new + + def __arrow_c_schema__(self) -> object: + self.load() + return self.im.__arrow_c_schema__() + + def __arrow_c_array__( + self, requested_schema: object | None = None + ) -> tuple[object, object]: + self.load() + return (self.im.__arrow_c_schema__(), self.im.__arrow_c_array__()) + + def __getstate__(self) -> list[Any]: + im_data = self.tobytes() # load image first + return [self.info, self.mode, self.size, self.getpalette(), im_data] + + def __setstate__(self, state: list[Any]) -> None: + Image.__init__(self) + info, mode, size, palette, data = state[:5] + self.info = info + self._mode = mode + self._size = size + self.im = core.new(mode, size) + if mode in ("L", "LA", "P", "PA") and palette: + self.putpalette(palette) + self.frombytes(data) + + def tobytes(self, encoder_name: str = "raw", *args: Any) -> bytes: + """ + Return image as a bytes object. + + .. warning:: + + This method returns raw image data derived from Pillow's internal + storage. For compressed image data (e.g. PNG, JPEG) use + :meth:`~.save`, with a BytesIO parameter for in-memory data. + + :param encoder_name: What encoder to use. + + The default is to use the standard "raw" encoder. + To see how this packs pixel data into the returned + bytes, see :file:`libImaging/Pack.c`. + + A list of C encoders can be seen under codecs + section of the function array in + :file:`_imaging.c`. Python encoders are registered + within the relevant plugins. + :param args: Extra arguments to the encoder. + :returns: A :py:class:`bytes` object. + """ + + encoder_args: Any = args + if len(encoder_args) == 1 and isinstance(encoder_args[0], tuple): + # may pass tuple instead of argument list + encoder_args = encoder_args[0] + + if encoder_name == "raw" and encoder_args == (): + encoder_args = self.mode + + self.load() + + if self.width == 0 or self.height == 0: + return b"" + + # unpack data + e = _getencoder(self.mode, encoder_name, encoder_args) + e.setimage(self.im) + + from . import ImageFile + + bufsize = max(ImageFile.MAXBLOCK, self.size[0] * 4) # see RawEncode.c + + output = [] + while True: + bytes_consumed, errcode, data = e.encode(bufsize) + output.append(data) + if errcode: + break + if errcode < 0: + msg = f"encoder error {errcode} in tobytes" + raise RuntimeError(msg) + + return b"".join(output) + + def tobitmap(self, name: str = "image") -> bytes: + """ + 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": + msg = "not a bitmap" + raise ValueError(msg) + data = self.tobytes("xbm") + return b"".join( + [ + f"#define {name}_width {self.size[0]}\n".encode("ascii"), + f"#define {name}_height {self.size[1]}\n".encode("ascii"), + f"static char {name}_bits[] = {{\n".encode("ascii"), + data, + b"};", + ] + ) + + def frombytes( + self, + data: bytes | bytearray | SupportsArrayInterface, + decoder_name: str = "raw", + *args: Any, + ) -> None: + """ + 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. + """ + + if self.width == 0 or self.height == 0: + return + + decoder_args: Any = args + if len(decoder_args) == 1 and isinstance(decoder_args[0], tuple): + # may pass tuple instead of argument list + decoder_args = decoder_args[0] + + # default format + if decoder_name == "raw" and decoder_args == (): + decoder_args = self.mode + + # unpack data + d = _getdecoder(self.mode, decoder_name, decoder_args) + d.setimage(self.im) + s = d.decode(data) + + if s[0] >= 0: + msg = "not enough image data" + raise ValueError(msg) + if s[1] != 0: + msg = "cannot decode image data" + raise ValueError(msg) + + def load(self) -> core.PixelAccess | None: + """ + 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. + + If the file associated with the image was opened by Pillow, then this + method will close it. The exception to this is if the image has + multiple frames, in which case the file will be left open for seek + operations. See :ref:`file-handling` for more information. + + :returns: An image access object. + :rtype: :py:class:`.PixelAccess` + """ + if self._im is not None and self.palette and self.palette.dirty: + # realize palette + mode, arr = self.palette.getdata() + self.im.putpalette(self.palette.mode, mode, arr) + self.palette.dirty = 0 + self.palette.rawmode = None + if "transparency" in self.info and mode in ("LA", "PA"): + if isinstance(self.info["transparency"], int): + self.im.putpalettealpha(self.info["transparency"], 0) + else: + self.im.putpalettealphas(self.info["transparency"]) + self.palette.mode = "RGBA" + else: + self.palette.palette = self.im.getpalette( + self.palette.mode, self.palette.mode + ) + + if self._im is not None: + return self.im.pixel_access(self.readonly) + return None + + def verify(self) -> None: + """ + 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: str | None = None, + matrix: tuple[float, ...] | None = None, + dither: Dither | None = None, + palette: Palette = Palette.WEB, + colors: int = 256, + ) -> Image: + """ + 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. + + This supports all possible conversions between "L", "RGB" and "CMYK". The + ``matrix`` argument only supports "L" and "RGB". + + When translating a color image to grayscale (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 grayscale ("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 values larger than 127 are set to 255 (white), + all other values to 0 (black). To use other thresholds, use the + :py:meth:`~PIL.Image.Image.point` method. + + When converting from "RGBA" to "P" without a ``matrix`` argument, + this passes the operation to :py:meth:`~PIL.Image.Image.quantize`, + and ``dither`` and ``palette`` are ignored. + + When converting from "PA", if an "RGBA" palette is present, the alpha + channel from the image will be used instead of the values from the palette. + + :param mode: The requested mode. See: :ref:`concept-modes`. + :param matrix: An optional conversion matrix. If given, this + should be 4- or 12-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 :data:`Dither.NONE` or :data:`Dither.FLOYDSTEINBERG` + (default). Note that this is not used when ``matrix`` is supplied. + :param palette: Palette to use when converting from mode "RGB" + to "P". Available palettes are :data:`Palette.WEB` or + :data:`Palette.ADAPTIVE`. + :param colors: Number of colors to use for the :data:`Palette.ADAPTIVE` + palette. Defaults to 256. + :rtype: :py:class:`~PIL.Image.Image` + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + self.load() + + has_transparency = "transparency" in self.info + if not mode and self.mode == "P": + # determine default mode + if self.palette: + mode = self.palette.mode + else: + mode = "RGB" + if mode == "RGB" and has_transparency: + mode = "RGBA" + if not mode or (mode == self.mode and not matrix): + return self.copy() + + if matrix: + # matrix conversion + if mode not in ("L", "RGB"): + msg = "illegal conversion" + raise ValueError(msg) + im = self.im.convert_matrix(mode, matrix) + new_im = self._new(im) + if has_transparency and self.im.bands == 3: + transparency = new_im.info["transparency"] + + def convert_transparency( + m: tuple[float, ...], v: tuple[int, int, int] + ) -> int: + value = m[0] * v[0] + m[1] * v[1] + m[2] * v[2] + m[3] * 0.5 + return max(0, min(255, int(value))) + + if mode == "L": + transparency = convert_transparency(matrix, transparency) + elif len(mode) == 3: + transparency = tuple( + convert_transparency(matrix[i * 4 : i * 4 + 4], transparency) + for i in range(len(transparency)) + ) + new_im.info["transparency"] = transparency + return new_im + + if self.mode == "RGBA": + if mode == "P": + return self.quantize(colors) + elif mode == "PA": + r, g, b, a = self.split() + rgb = merge("RGB", (r, g, b)) + p = rgb.quantize(colors) + return merge("PA", (p, a)) + + trns = None + delete_trns = False + # transparency handling + if has_transparency: + if (self.mode in ("1", "L", "I", "I;16") and mode in ("LA", "RGBA")) or ( + self.mode == "RGB" and mode in ("La", "LA", "RGBa", "RGBA") + ): + # Use transparent conversion to promote from transparent + # color to an alpha channel. + new_im = self._new( + self.im.convert_transparent(mode, self.info["transparency"]) + ) + del new_im.info["transparency"] + return new_im + 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 = new(self.mode, (1, 1)) + if self.mode == "P": + assert self.palette is not None + trns_im.putpalette(self.palette, self.palette.mode) + if isinstance(t, tuple): + err = "Couldn't allocate a palette color for transparency" + assert trns_im.palette is not None + try: + t = trns_im.palette.getcolor(t, self) + except ValueError as e: + if str(e) == "cannot allocate more than 256 colors": + # If all 256 colors are in use, + # then there is no need for transparency + t = None + else: + raise ValueError(err) from e + if t is None: + trns = None + else: + 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 in ("LA", "PA", "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: + msg = "Transparency for P mode should be bytes or int" + raise ValueError(msg) + + if mode == "P" and palette == Palette.ADAPTIVE: + im = self.im.quantize(colors) + new_im = self._new(im) + from . import ImagePalette + + new_im.palette = ImagePalette.ImagePalette( + "RGB", new_im.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_im.info["transparency"] + if trns is not None: + try: + new_im.info["transparency"] = new_im.palette.getcolor( + cast(tuple[int, ...], trns), # trns was converted to RGB + new_im, + ) + except Exception: + # if we can't make a transparent color, don't leave the old + # transparency hanging around to mess us up. + del new_im.info["transparency"] + warnings.warn("Couldn't allocate palette entry for transparency") + return new_im + + if "LAB" in (self.mode, mode): + im = self + if mode == "LAB": + if im.mode not in ("RGB", "RGBA", "RGBX"): + im = im.convert("RGBA") + other_mode = im.mode + else: + other_mode = mode + if other_mode in ("RGB", "RGBA", "RGBX"): + from . import ImageCms + + srgb = ImageCms.createProfile("sRGB") + lab = ImageCms.createProfile("LAB") + profiles = [lab, srgb] if im.mode == "LAB" else [srgb, lab] + transform = ImageCms.buildTransform( + profiles[0], profiles[1], im.mode, mode + ) + return transform.apply(im) + + # colorspace conversion + if dither is None: + dither = Dither.FLOYDSTEINBERG + + try: + im = self.im.convert(mode, dither) + except ValueError: + try: + # normalize source image and try again + modebase = getmodebase(self.mode) + if modebase == self.mode: + raise + im = self.im.convert(modebase) + im = im.convert(mode, dither) + except KeyError as e: + msg = "illegal conversion" + raise ValueError(msg) from e + + new_im = self._new(im) + if mode in ("P", "PA") and palette != Palette.ADAPTIVE: + from . import ImagePalette + + new_im.palette = ImagePalette.ImagePalette("RGB", im.getpalette("RGB")) + 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" and new_im.palette: + try: + new_im.info["transparency"] = new_im.palette.getcolor( + cast(tuple[int, ...], trns), new_im # trns was converted to RGB + ) + except ValueError as e: + del new_im.info["transparency"] + if str(e) != "cannot allocate more than 256 colors": + # If all 256 colors are in use, + # then there is no need for transparency + warnings.warn( + "Couldn't allocate palette entry for transparency" + ) + else: + new_im.info["transparency"] = trns + return new_im + + def quantize( + self, + colors: int = 256, + method: int | None = None, + kmeans: int = 0, + palette: Image | None = None, + dither: Dither = Dither.FLOYDSTEINBERG, + ) -> Image: + """ + Convert the image to 'P' mode with the specified number + of colors. + + :param colors: The desired number of colors, <= 256 + :param method: :data:`Quantize.MEDIANCUT` (median cut), + :data:`Quantize.MAXCOVERAGE` (maximum coverage), + :data:`Quantize.FASTOCTREE` (fast octree), + :data:`Quantize.LIBIMAGEQUANT` (libimagequant; check support + using :py:func:`PIL.features.check_feature` with + ``feature="libimagequant"``). + + By default, :data:`Quantize.MEDIANCUT` will be used. + + The exception to this is RGBA images. :data:`Quantize.MEDIANCUT` + and :data:`Quantize.MAXCOVERAGE` do not support RGBA images, so + :data:`Quantize.FASTOCTREE` is used by default instead. + :param kmeans: Integer greater than or equal to zero. + :param palette: Quantize to the palette of given + :py:class:`PIL.Image.Image`. + :param dither: Dithering method, used when converting from + mode "RGB" to "P" or from "RGB" or "L" to "1". + Available methods are :data:`Dither.NONE` or :data:`Dither.FLOYDSTEINBERG` + (default). + :returns: A new image + """ + + self.load() + + if method is None: + # defaults: + method = Quantize.MEDIANCUT + if self.mode == "RGBA": + method = Quantize.FASTOCTREE + + if self.mode == "RGBA" and method not in ( + Quantize.FASTOCTREE, + Quantize.LIBIMAGEQUANT, + ): + # Caller specified an invalid mode. + msg = ( + "Fast Octree (method == 2) and libimagequant (method == 3) " + "are the only valid methods for quantizing RGBA images" + ) + raise ValueError(msg) + + if palette: + # use palette from reference image + palette.load() + if palette.mode != "P": + msg = "bad mode for palette image" + raise ValueError(msg) + if self.mode not in {"RGB", "L"}: + msg = "only RGB or L mode images can be quantized to a palette" + raise ValueError(msg) + im = self.im.convert("P", dither, palette.im) + new_im = self._new(im) + assert palette.palette is not None + new_im.palette = palette.palette.copy() + return new_im + + if kmeans < 0: + msg = "kmeans must not be negative" + raise ValueError(msg) + + im = self._new(self.im.quantize(colors, method, kmeans)) + + from . import ImagePalette + + mode = im.im.getpalettemode() + palette_data = im.im.getpalette(mode, mode)[: colors * len(mode)] + im.palette = ImagePalette.ImagePalette(mode, palette_data) + + return im + + def copy(self) -> Image: + """ + 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() + return self._new(self.im.copy()) + + __copy__ = copy + + def crop(self, box: tuple[float, float, float, float] | None = None) -> Image: + """ + Returns a rectangular region from this image. The box is a + 4-tuple defining the left, upper, right, and lower pixel + coordinate. See :ref:`coordinate-system`. + + Note: Prior to Pillow 3.4.0, this was a lazy operation. + + :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. + """ + + if box is None: + return self.copy() + + if box[2] < box[0]: + msg = "Coordinate 'right' is less than 'left'" + raise ValueError(msg) + elif box[3] < box[1]: + msg = "Coordinate 'lower' is less than 'upper'" + raise ValueError(msg) + + self.load() + return self._new(self._crop(self.im, box)) + + def _crop( + self, im: core.ImagingCore, box: tuple[float, float, float, float] + ) -> core.ImagingCore: + """ + Returns a rectangular region from the core image object im. + + This is equivalent to calling im.crop((x0, y0, x1, y1)), but + includes additional sanity checks. + + :param im: a core image object + :param box: The crop rectangle, as a (left, upper, right, lower)-tuple. + :returns: A core image object. + """ + + x0, y0, x1, y1 = map(int, map(round, box)) + + absolute_values = (abs(x1 - x0), abs(y1 - y0)) + + _decompression_bomb_check(absolute_values) + + return im.crop((x0, y0, x1, y1)) + + def draft( + self, mode: str | None, size: tuple[int, int] | None + ) -> tuple[str, tuple[int, int, float, float]] | None: + """ + 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 grayscale while loading it. + + If any changes are made, returns a tuple with the chosen ``mode`` and + ``box`` with coordinates of the original image within the altered one. + + 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. + + Note: This method is not implemented for most images. It is + currently implemented only for JPEG and MPO images. + + :param mode: The requested mode. + :param size: The requested size in pixels, as a 2-tuple: + (width, height). + """ + pass + + def filter(self, filter: ImageFilter.Filter | type[ImageFilter.Filter]) -> Image: + """ + 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.""" + + from . import ImageFilter + + self.load() + + if callable(filter): + filter = filter() + if not hasattr(filter, "filter"): + msg = "filter argument should be ImageFilter.Filter instance or class" + raise TypeError(msg) + + multiband = isinstance(filter, ImageFilter.MultibandFilter) + if self.im.bands == 1 or multiband: + return self._new(filter.filter(self.im)) + + ims = [ + self._new(filter.filter(self.im.getband(c))) for c in range(self.im.bands) + ] + return merge(self.mode, ims) + + def getbands(self) -> tuple[str, ...]: + """ + 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, *, alpha_only: bool = True) -> tuple[int, int, int, int] | None: + """ + Calculates the bounding box of the non-zero regions in the + image. + + :param alpha_only: Optional flag, defaulting to ``True``. + If ``True`` and the image has an alpha channel, trim transparent pixels. + Otherwise, trim pixels when all channels are zero. + Keyword-only argument. + :returns: The bounding box is returned as a 4-tuple defining the + left, upper, right, and lower pixel coordinate. See + :ref:`coordinate-system`. If the image is completely empty, this + method returns None. + + """ + + self.load() + return self.im.getbbox(alpha_only) + + def getcolors( + self, maxcolors: int = 256 + ) -> list[tuple[int, tuple[int, ...]]] | list[tuple[int, float]] | None: + """ + Returns a list of colors used in this image. + + The colors will be in the image's mode. For example, an RGB image will + return a tuple of (red, green, blue) color values, and a P image will + return the index of the color in the palette. + + :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: list[tuple[int, float]] = [(h[i], i) for i in range(256) if h[i]] + if len(out) > maxcolors: + return None + return out + return self.im.getcolors(maxcolors) + + def getdata(self, band: int | None = None) -> core.ImagingCore: + """ + 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. + """ + deprecate("Image.Image.getdata", 14, "get_flattened_data") + + self.load() + if band is not None: + return self.im.getband(band) + return self.im # could be abused + + def get_flattened_data( + self, band: int | None = None + ) -> tuple[tuple[int, ...], ...] | tuple[float, ...]: + """ + Returns the contents of this image as a tuple 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. + + :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 tuple containing pixel values. + """ + self.load() + if band is not None: + return tuple(self.im.getband(band)) + return tuple(self.im) + + def getextrema(self) -> tuple[float, float] | tuple[tuple[int, int], ...]: + """ + Gets 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: + return tuple(self.im.getband(i).getextrema() for i in range(self.im.bands)) + return self.im.getextrema() + + def getxmp(self) -> dict[str, Any]: + """ + Returns a dictionary containing the XMP tags. + Requires defusedxml to be installed. + + :returns: XMP tags in a dictionary. + """ + + def get_name(tag: str) -> str: + return re.sub("^{[^}]+}", "", tag) + + def get_value(element: Element) -> str | dict[str, Any] | None: + value: dict[str, Any] = {get_name(k): v for k, v in element.attrib.items()} + children = list(element) + if children: + for child in children: + name = get_name(child.tag) + child_value = get_value(child) + if name in value: + if not isinstance(value[name], list): + value[name] = [value[name]] + value[name].append(child_value) + else: + value[name] = child_value + elif value: + if element.text: + value["text"] = element.text + else: + return element.text + return value + + if ElementTree is None: + warnings.warn("XMP data cannot be read without defusedxml dependency") + return {} + if "xmp" not in self.info: + return {} + root = ElementTree.fromstring(self.info["xmp"].rstrip(b"\x00 ")) + return {get_name(root.tag): get_value(root)} + + def getexif(self) -> Exif: + """ + Gets EXIF data from the image. + + :returns: an :py:class:`~PIL.Image.Exif` object. + """ + if self._exif is None: + self._exif = Exif() + elif self._exif._loaded: + return self._exif + self._exif._loaded = True + + exif_info = self.info.get("exif") + if exif_info is None: + if "Raw profile type exif" in self.info: + exif_info = bytes.fromhex( + "".join(self.info["Raw profile type exif"].split("\n")[3:]) + ) + elif hasattr(self, "tag_v2"): + from . import TiffImagePlugin + + assert isinstance(self, TiffImagePlugin.TiffImageFile) + self._exif.bigtiff = self.tag_v2._bigtiff + self._exif.endian = self.tag_v2._endian + + assert self.fp is not None + self._exif.load_from_fp(self.fp, self.tag_v2._offset) + if exif_info is not None: + self._exif.load(exif_info) + + # XMP tags + if ExifTags.Base.Orientation not in self._exif: + xmp_tags = self.info.get("XML:com.adobe.xmp") + pattern: str | bytes = r'tiff:Orientation(="|>)([0-9])' + if not xmp_tags and (xmp_tags := self.info.get("xmp")): + pattern = rb'tiff:Orientation(="|>)([0-9])' + if xmp_tags: + match = re.search(pattern, xmp_tags) + if match: + self._exif[ExifTags.Base.Orientation] = int(match[2]) + + return self._exif + + def _reload_exif(self) -> None: + if self._exif is None or not self._exif._loaded: + return + self._exif._loaded = False + self.getexif() + + def get_child_images(self) -> list[ImageFile.ImageFile]: + from . import ImageFile + + deprecate("Image.Image.get_child_images", 13) + return ImageFile.ImageFile.get_child_images(self) # type: ignore[arg-type] + + def getim(self) -> CapsuleType: + """ + Returns a capsule that points to the internal image memory. + + :returns: A capsule object. + """ + + self.load() + return self.im.ptr + + def getpalette(self, rawmode: str | None = "RGB") -> list[int] | None: + """ + Returns the image palette as a list. + + :param rawmode: The mode in which to return the palette. ``None`` will + return the palette in its current mode. + + .. versionadded:: 9.1.0 + + :returns: A list of color values [r, g, b, ...], or None if the + image has no palette. + """ + + self.load() + try: + mode = self.im.getpalettemode() + except ValueError: + return None # no palette + if rawmode is None: + rawmode = mode + return list(self.im.getpalette(mode, rawmode)) + + @property + def has_transparency_data(self) -> bool: + """ + Determine if an image has transparency data, whether in the form of an + alpha channel, a palette with an alpha channel, or a "transparency" key + in the info dictionary. + + Note the image might still appear solid, if all of the values shown + within are opaque. + + :returns: A boolean. + """ + if ( + self.mode in ("LA", "La", "PA", "RGBA", "RGBa") + or "transparency" in self.info + ): + return True + if self.mode == "P": + assert self.palette is not None + return self.palette.mode.endswith("A") + return False + + def apply_transparency(self) -> None: + """ + If a P mode image has a "transparency" key in the info dictionary, + remove the key and instead apply the transparency to the palette. + Otherwise, the image is unchanged. + """ + if self.mode != "P" or "transparency" not in self.info: + return + + from . import ImagePalette + + palette = self.getpalette("RGBA") + assert palette is not None + transparency = self.info["transparency"] + if isinstance(transparency, bytes): + for i, alpha in enumerate(transparency): + palette[i * 4 + 3] = alpha + else: + palette[transparency * 4 + 3] = 0 + self.palette = ImagePalette.ImagePalette("RGBA", bytes(palette)) + self.palette.dirty = 1 + + del self.info["transparency"] + + def getpixel( + self, xy: tuple[int, int] | list[int] + ) -> float | tuple[int, ...] | None: + """ + Returns the pixel value at a given position. + + :param xy: The coordinate, given as (x, y). See + :ref:`coordinate-system`. + :returns: The pixel value. If the image is a multi-layer image, + this method returns a tuple. + """ + + self.load() + return self.im.getpixel(tuple(xy)) + + def getprojection(self) -> tuple[list[int], list[int]]: + """ + 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 list(x), list(y) + + def histogram( + self, mask: Image | None = None, extrema: tuple[float, float] | None = None + ) -> list[int]: + """ + 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. Counts are grouped into 256 bins for each band, even if + the image has more than 8 bits per band. 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 grayscale ("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 grayscale image ("L"). + + :param mask: An optional mask. + :param extrema: An optional tuple of manually-specified extrema. + :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"): + return self.im.histogram( + extrema if extrema is not None else self.getextrema() + ) + return self.im.histogram() + + def entropy( + self, mask: Image | None = None, extrema: tuple[float, float] | None = None + ) -> float: + """ + Calculates and returns the entropy for the image. + + A bilevel image (mode "1") is treated as a grayscale ("L") + image by this method. + + If a mask is provided, the method employs the 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 grayscale image ("L"). + + :param mask: An optional mask. + :param extrema: An optional tuple of manually-specified extrema. + :returns: A float value representing the image entropy + """ + self.load() + if mask: + mask.load() + return self.im.entropy((0, 0), mask.im) + if self.mode in ("I", "F"): + return self.im.entropy( + extrema if extrema is not None else self.getextrema() + ) + return self.im.entropy() + + def paste( + self, + im: Image | str | float | tuple[float, ...], + box: Image | tuple[int, int, int, int] | tuple[int, int] | None = None, + mask: Image | None = None, + ) -> 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)). See :ref:`coordinate-system`. 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. See + :ref:`colors` for more information. + + If a mask is given, this method updates only the regions + indicated by the mask. You can use either "1", "L", "LA", "RGBA" + or "RGBa" images (if present, 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 will mix the two images together, including their alpha + channels if they have them. + + See :py:meth:`~PIL.Image.Image.alpha_composite` if you want to + combine images with respect to their alpha channels. + + :param im: Source image or pixel value (integer, float 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 isinstance(box, Image): + if mask is not None: + msg = "If using second argument as mask, third argument must be None" + raise ValueError(msg) + # abbreviated paste(im, mask) syntax + mask = box + box = None + + if box is None: + box = (0, 0) + + if len(box) == 2: + # upper left corner given; get size from image or mask + if isinstance(im, Image): + size = im.size + elif isinstance(mask, Image): + size = mask.size + else: + # FIXME: use self.size here? + msg = "cannot determine region size; use 4-item box" + raise ValueError(msg) + box += (box[0] + size[0], box[1] + size[1]) + + source: core.ImagingCore | str | float | tuple[float, ...] + if isinstance(im, str): + from . import ImageColor + + source = ImageColor.getcolor(im, self.mode) + elif isinstance(im, Image): + im.load() + if self.mode != im.mode: + if self.mode != "RGB" or im.mode not in ("LA", "RGBA", "RGBa"): + # should use an adapter for this! + im = im.convert(self.mode) + source = im.im + else: + source = im + + self._ensure_mutable() + + if mask: + mask.load() + self.im.paste(source, box, mask.im) + else: + self.im.paste(source, box) + + def alpha_composite( + self, im: Image, dest: Sequence[int] = (0, 0), source: Sequence[int] = (0, 0) + ) -> None: + """'In-place' analog of Image.alpha_composite. Composites an image + onto this image. + + :param im: image to composite over this one + :param dest: Optional 2 tuple (left, top) specifying the upper + left corner in this (destination) image. + :param source: Optional 2 (left, top) tuple for the upper left + corner in the overlay source image, or 4 tuple (left, top, right, + bottom) for the bounds of the source rectangle + + Performance Note: Not currently implemented in-place in the core layer. + """ + + if not isinstance(source, (list, tuple)): + msg = "Source must be a list or tuple" + raise ValueError(msg) + if not isinstance(dest, (list, tuple)): + msg = "Destination must be a list or tuple" + raise ValueError(msg) + + if len(source) == 4: + overlay_crop_box = tuple(source) + elif len(source) == 2: + overlay_crop_box = tuple(source) + im.size + else: + msg = "Source must be a sequence of length 2 or 4" + raise ValueError(msg) + + if not len(dest) == 2: + msg = "Destination must be a sequence of length 2" + raise ValueError(msg) + if min(source) < 0: + msg = "Source must be non-negative" + raise ValueError(msg) + + # over image, crop if it's not the whole image. + if overlay_crop_box == (0, 0) + im.size: + overlay = im + else: + overlay = im.crop(overlay_crop_box) + + # target for the paste + box = tuple(dest) + (dest[0] + overlay.width, dest[1] + overlay.height) + + # destination image. don't copy if we're using the whole image. + if box == (0, 0) + self.size: + background = self + else: + background = self.crop(box) + + result = alpha_composite(background, overlay) + self.paste(result, box) + + def point( + self, + lut: ( + Sequence[float] + | NumpyArray + | Callable[[int], float] + | Callable[[ImagePointTransform], ImagePointTransform | float] + | ImagePointHandler + ), + mode: str | None = None, + ) -> Image: + """ + Maps this image through a lookup table or function. + + :param lut: A lookup table, containing 256 (or 65536 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. + + It may also be an :py:class:`~PIL.Image.ImagePointHandler` + object:: + + class Example(Image.ImagePointHandler): + def point(self, im: Image) -> Image: + # Return result + :param mode: Output mode (default is same as input). 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) # type: ignore[arg-type] + return self._new(self.im.point_transform(scale, offset)) + # for other modes, convert the function to a table + flatLut = [lut(i) for i in range(256)] * self.im.bands # type: ignore[arg-type] + else: + flatLut = lut + + if self.mode == "F": + # FIXME: _imaging returns a confusing error message for this case + msg = "point operation not supported for this mode" + raise ValueError(msg) + + if mode != "F": + flatLut = [round(i) for i in flatLut] + return self._new(self.im.point(flatLut, mode)) + + def putalpha(self, alpha: Image | int) -> None: + """ + 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. + """ + + self._ensure_mutable() + + if self.mode not in ("LA", "PA", "RGBA"): + # attempt to promote self to a matching alpha mode + try: + mode = getmodebase(self.mode) + "A" + try: + self.im.setmode(mode) + except (AttributeError, ValueError) as e: + # do things the hard way + im = self.im.convert(mode) + if im.mode not in ("LA", "PA", "RGBA"): + msg = "alpha channel could not be added" + raise ValueError(msg) from e # sanity check + self.im = im + self._mode = self.im.mode + except KeyError as e: + msg = "illegal image mode" + raise ValueError(msg) from e + + if self.mode in ("LA", "PA"): + band = 1 + else: + band = 3 + + if isinstance(alpha, Image): + # alpha layer + if alpha.mode not in ("1", "L"): + msg = "illegal image mode" + raise ValueError(msg) + 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: Sequence[float] | Sequence[Sequence[int]] | core.ImagingCore | NumpyArray, + scale: float = 1.0, + offset: float = 0.0, + ) -> None: + """ + Copies pixel data from a flattened sequence object into the image. The + values should start at the upper left corner (0, 0), continue to the + end of the line, followed directly by the first value of the second + line, and so on. Data will be read 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 flattened sequence object. See :ref:`colors` for more + information about values. + :param scale: An optional scale value. The default is 1.0. + :param offset: An optional offset value. The default is 0.0. + """ + + self._ensure_mutable() + + self.im.putdata(data, scale, offset) + + def putpalette( + self, + data: ImagePalette.ImagePalette | bytes | Sequence[int], + rawmode: str = "RGB", + ) -> None: + """ + Attaches a palette to this image. The image must be a "P", "PA", "L" + or "LA" image. + + The palette sequence must contain at most 256 colors, made up of one + integer value for each channel in the raw mode. + For example, if the raw mode is "RGB", then it can contain at most 768 + values, made up of red, green and blue values for the corresponding pixel + index in the 256 colors. + If the raw mode is "RGBA", then it can contain at most 1024 values, + containing red, green, blue and alpha values. + + Alternatively, an 8-bit string may be used instead of an integer sequence. + + :param data: A palette sequence (either a list or a string). + :param rawmode: The raw mode of the palette. Either "RGB", "RGBA", or a mode + that can be transformed to "RGB" or "RGBA" (e.g. "R", "BGR;15", "RGBA;L"). + """ + from . import ImagePalette + + if self.mode not in ("L", "LA", "P", "PA"): + msg = "illegal image mode" + raise ValueError(msg) + if isinstance(data, ImagePalette.ImagePalette): + if data.rawmode is not None: + palette = ImagePalette.raw(data.rawmode, data.palette) + else: + palette = ImagePalette.ImagePalette(palette=data.palette) + palette.dirty = 1 + else: + if not isinstance(data, bytes): + data = bytes(data) + palette = ImagePalette.raw(rawmode, data) + self._mode = "PA" if "A" in self.mode else "P" + self.palette = palette + self.palette.mode = "RGBA" if "A" in rawmode else "RGB" + self.load() # install new palette + + def putpixel( + self, xy: tuple[int, int], value: float | tuple[int, ...] | list[int] + ) -> None: + """ + 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. In addition to this, RGB and RGBA tuples are + accepted for P and PA images. See :ref:`colors` for more information. + + 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). See + :ref:`coordinate-system`. + :param value: The pixel value. + """ + + self._ensure_mutable() + + if ( + self.mode in ("P", "PA") + and isinstance(value, (list, tuple)) + and len(value) in [3, 4] + ): + # RGB or RGBA value for a P or PA image + if self.mode == "PA": + alpha = value[3] if len(value) == 4 else 255 + value = value[:3] + assert self.palette is not None + palette_index = self.palette.getcolor(tuple(value), self) + value = (palette_index, alpha) if self.mode == "PA" else palette_index + return self.im.putpixel(xy, value) + + def remap_palette( + self, dest_map: list[int], source_palette: bytes | bytearray | None = None + ) -> Image: + """ + Rewrites the image to reorder the palette. + + :param dest_map: A list of indexes into the original palette. + e.g. ``[1,0]`` would swap a two item palette, and ``list(range(256))`` + is the identity transform. + :param source_palette: Bytes or None. + :returns: An :py:class:`~PIL.Image.Image` object. + + """ + from . import ImagePalette + + if self.mode not in ("L", "P"): + msg = "illegal image mode" + raise ValueError(msg) + + bands = 3 + palette_mode = "RGB" + if source_palette is None: + if self.mode == "P": + self.load() + palette_mode = self.im.getpalettemode() + if palette_mode == "RGBA": + bands = 4 + source_palette = self.im.getpalette(palette_mode, palette_mode) + else: # L-mode + source_palette = bytearray(i // 3 for i in range(768)) + elif len(source_palette) > 768: + bands = 4 + palette_mode = "RGBA" + + palette_bytes = b"" + new_positions = [0] * 256 + + # pick only the used colors from the palette + for i, oldPosition in enumerate(dest_map): + palette_bytes += source_palette[ + oldPosition * bands : oldPosition * bands + bands + ] + new_positions[oldPosition] = i + + # replace the palette color id of all pixel with the new id + + # Palette images are [0..255], mapped through a 1 or 3 + # byte/color map. We need to remap the whole image + # from palette 1 to palette 2. New_positions is + # an array of indexes into palette 1. Palette 2 is + # palette 1 with any holes removed. + + # We're going to leverage the convert mechanism to use the + # C code to remap the image from palette 1 to palette 2, + # by forcing the source image into 'L' mode and adding a + # mapping 'L' mode palette, then converting back to 'L' + # sans palette thus converting the image bytes, then + # assigning the optimized RGB palette. + + # perf reference, 9500x4000 gif, w/~135 colors + # 14 sec prepatch, 1 sec postpatch with optimization forced. + + mapping_palette = bytearray(new_positions) + + m_im = self.copy() + m_im._mode = "P" + + m_im.palette = ImagePalette.ImagePalette( + palette_mode, palette=mapping_palette * bands + ) + # possibly set palette dirty, then + # m_im.putpalette(mapping_palette, 'L') # converts to 'P' + # or just force it. + # UNDONE -- this is part of the general issue with palettes + m_im.im.putpalette(palette_mode, palette_mode + ";L", m_im.palette.tobytes()) + + m_im = m_im.convert("L") + + m_im.putpalette(palette_bytes, palette_mode) + m_im.palette = ImagePalette.ImagePalette(palette_mode, palette=palette_bytes) + + if "transparency" in self.info: + try: + m_im.info["transparency"] = dest_map.index(self.info["transparency"]) + except ValueError: + if "transparency" in m_im.info: + del m_im.info["transparency"] + + return m_im + + def _get_safe_box( + self, + size: tuple[int, int], + resample: Resampling, + box: tuple[float, float, float, float], + ) -> tuple[int, int, int, int]: + """Expands the box so it includes adjacent pixels + that may be used by resampling with the given resampling filter. + """ + filter_support = _filters_support[resample] - 0.5 + scale_x = (box[2] - box[0]) / size[0] + scale_y = (box[3] - box[1]) / size[1] + support_x = filter_support * scale_x + support_y = filter_support * scale_y + + return ( + max(0, int(box[0] - support_x)), + max(0, int(box[1] - support_y)), + min(self.size[0], math.ceil(box[2] + support_x)), + min(self.size[1], math.ceil(box[3] + support_y)), + ) + + def resize( + self, + size: tuple[int, int] | list[int] | NumpyArray, + resample: int | None = None, + box: tuple[float, float, float, float] | None = None, + reducing_gap: float | None = None, + ) -> Image: + """ + Returns a resized copy of this image. + + :param size: The requested size in pixels, as a tuple or array: + (width, height). + :param resample: An optional resampling filter. This can be + one of :py:data:`Resampling.NEAREST`, :py:data:`Resampling.BOX`, + :py:data:`Resampling.BILINEAR`, :py:data:`Resampling.HAMMING`, + :py:data:`Resampling.BICUBIC` or :py:data:`Resampling.LANCZOS`. + If the image has mode "1" or "P", it is always set to + :py:data:`Resampling.NEAREST`. Otherwise, the default filter is + :py:data:`Resampling.BICUBIC`. See: :ref:`concept-filters`. + :param box: An optional 4-tuple of floats providing + the source image region to be scaled. + The values must be within (0, 0, width, height) rectangle. + If omitted or None, the entire source is used. + :param reducing_gap: Apply optimization by resizing the image + in two steps. First, reducing the image by integer times + using :py:meth:`~PIL.Image.Image.reduce`. + Second, resizing using regular resampling. The last step + changes size no less than by ``reducing_gap`` times. + ``reducing_gap`` may be None (no first step is performed) + or should be greater than 1.0. The bigger ``reducing_gap``, + the closer the result to the fair resampling. + The smaller ``reducing_gap``, the faster resizing. + With ``reducing_gap`` greater or equal to 3.0, the result is + indistinguishable from fair resampling in most cases. + The default value is None (no optimization). + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + if resample is None: + resample = Resampling.BICUBIC + elif resample not in ( + Resampling.NEAREST, + Resampling.BILINEAR, + Resampling.BICUBIC, + Resampling.LANCZOS, + Resampling.BOX, + Resampling.HAMMING, + ): + msg = f"Unknown resampling filter ({resample})." + + filters = [ + f"{filter[1]} ({filter[0]})" + for filter in ( + (Resampling.NEAREST, "Image.Resampling.NEAREST"), + (Resampling.LANCZOS, "Image.Resampling.LANCZOS"), + (Resampling.BILINEAR, "Image.Resampling.BILINEAR"), + (Resampling.BICUBIC, "Image.Resampling.BICUBIC"), + (Resampling.BOX, "Image.Resampling.BOX"), + (Resampling.HAMMING, "Image.Resampling.HAMMING"), + ) + ] + msg += f" Use {', '.join(filters[:-1])} or {filters[-1]}" + raise ValueError(msg) + + if reducing_gap is not None and reducing_gap < 1.0: + msg = "reducing_gap must be 1.0 or greater" + raise ValueError(msg) + + if box is None: + box = (0, 0) + self.size + + size = tuple(size) + if self.size == size and box == (0, 0) + self.size: + return self.copy() + + if self.mode in ("1", "P"): + resample = Resampling.NEAREST + + if self.mode in ["LA", "RGBA"] and resample != Resampling.NEAREST: + im = self.convert({"LA": "La", "RGBA": "RGBa"}[self.mode]) + im = im.resize(size, resample, box) + return im.convert(self.mode) + + self.load() + + if reducing_gap is not None and resample != Resampling.NEAREST: + factor_x = int((box[2] - box[0]) / size[0] / reducing_gap) or 1 + factor_y = int((box[3] - box[1]) / size[1] / reducing_gap) or 1 + if factor_x > 1 or factor_y > 1: + reduce_box = self._get_safe_box(size, cast(Resampling, resample), box) + factor = (factor_x, factor_y) + self = ( + self.reduce(factor, box=reduce_box) + if callable(self.reduce) + else Image.reduce(self, factor, box=reduce_box) + ) + box = ( + (box[0] - reduce_box[0]) / factor_x, + (box[1] - reduce_box[1]) / factor_y, + (box[2] - reduce_box[0]) / factor_x, + (box[3] - reduce_box[1]) / factor_y, + ) + + return self._new(self.im.resize(size, resample, box)) + + def reduce( + self, + factor: int | tuple[int, int], + box: tuple[int, int, int, int] | None = None, + ) -> Image: + """ + Returns a copy of the image reduced ``factor`` times. + If the size of the image is not dividable by ``factor``, + the resulting size will be rounded up. + + :param factor: A greater than 0 integer or tuple of two integers + for width and height separately. + :param box: An optional 4-tuple of ints providing + the source image region to be reduced. + The values must be within ``(0, 0, width, height)`` rectangle. + If omitted or ``None``, the entire source is used. + """ + if not isinstance(factor, (list, tuple)): + factor = (factor, factor) + + if box is None: + box = (0, 0) + self.size + + if factor == (1, 1) and box == (0, 0) + self.size: + return self.copy() + + if self.mode in ["LA", "RGBA"]: + im = self.convert({"LA": "La", "RGBA": "RGBa"}[self.mode]) + im = im.reduce(factor, box) + return im.convert(self.mode) + + self.load() + + return self._new(self.im.reduce(factor, box)) + + def rotate( + self, + angle: float, + resample: Resampling = Resampling.NEAREST, + expand: int | bool = False, + center: tuple[float, float] | None = None, + translate: tuple[int, int] | None = None, + fillcolor: float | tuple[float, ...] | str | None = None, + ) -> Image: + """ + 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:data:`Resampling.NEAREST` (use nearest neighbour), + :py:data:`Resampling.BILINEAR` (linear interpolation in a 2x2 + environment), or :py:data:`Resampling.BICUBIC` (cubic spline + interpolation in a 4x4 environment). If omitted, or if the image has + mode "1" or "P", it is set to :py:data:`Resampling.NEAREST`. + See :ref:`concept-filters`. + :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. Note that the expand flag assumes rotation around + the center and no translation. + :param center: Optional center of rotation (a 2-tuple). Origin is + the upper left corner. Default is the center of the image. + :param translate: An optional post-rotate translation (a 2-tuple). + :param fillcolor: An optional color for area outside the rotated image. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + angle = angle % 360.0 + + # Fast paths regardless of filter, as long as we're not + # translating or changing the center. + if not (center or translate): + if angle == 0: + return self.copy() + if angle == 180: + return self.transpose(Transpose.ROTATE_180) + if angle in (90, 270) and (expand or self.width == self.height): + return self.transpose( + Transpose.ROTATE_90 if angle == 90 else Transpose.ROTATE_270 + ) + + # Calculate the affine matrix. Note that this is the reverse + # transformation (from destination image to source) because we + # want to interpolate the (discrete) destination pixel from + # the local area around the (floating) source pixel. + + # The matrix we actually want (note that it operates from the right): + # (1, 0, tx) (1, 0, cx) ( cos a, sin a, 0) (1, 0, -cx) + # (0, 1, ty) * (0, 1, cy) * (-sin a, cos a, 0) * (0, 1, -cy) + # (0, 0, 1) (0, 0, 1) ( 0, 0, 1) (0, 0, 1) + + # The reverse matrix is thus: + # (1, 0, cx) ( cos -a, sin -a, 0) (1, 0, -cx) (1, 0, -tx) + # (0, 1, cy) * (-sin -a, cos -a, 0) * (0, 1, -cy) * (0, 1, -ty) + # (0, 0, 1) ( 0, 0, 1) (0, 0, 1) (0, 0, 1) + + # In any case, the final translation may be updated at the end to + # compensate for the expand flag. + + w, h = self.size + + if translate is None: + post_trans = (0, 0) + else: + post_trans = translate + if center is None: + center = (w / 2, h / 2) + + angle = -math.radians(angle) + matrix = [ + round(math.cos(angle), 15), + round(math.sin(angle), 15), + 0.0, + round(-math.sin(angle), 15), + round(math.cos(angle), 15), + 0.0, + ] + + def transform(x: float, y: float, matrix: list[float]) -> tuple[float, float]: + (a, b, c, d, e, f) = matrix + return a * x + b * y + c, d * x + e * y + f + + matrix[2], matrix[5] = transform( + -center[0] - post_trans[0], -center[1] - post_trans[1], matrix + ) + matrix[2] += center[0] + matrix[5] += center[1] + + if expand: + # calculate output size + xx = [] + yy = [] + for x, y in ((0, 0), (w, 0), (w, h), (0, h)): + transformed_x, transformed_y = transform(x, y, matrix) + xx.append(transformed_x) + yy.append(transformed_y) + nw = math.ceil(max(xx)) - math.floor(min(xx)) + nh = math.ceil(max(yy)) - math.floor(min(yy)) + + # We multiply a translation matrix from the right. Because of its + # special form, this is the same as taking the image of the + # translation vector as new translation vector. + matrix[2], matrix[5] = transform(-(nw - w) / 2.0, -(nh - h) / 2.0, matrix) + w, h = nw, nh + + return self.transform( + (w, h), Transform.AFFINE, matrix, resample, fillcolor=fillcolor + ) + + def save( + self, fp: StrOrBytesPath | IO[bytes], format: str | None = None, **params: Any + ) -> None: + """ + 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: A filename (string), os.PathLike object 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 params: Extra parameters to the image writer. These can also be + set on the image itself through ``encoderinfo``. This is useful when + saving multiple images:: + + # Saving XMP data to a single image + from PIL import Image + red = Image.new("RGB", (1, 1), "#f00") + red.save("out.mpo", xmp=b"test") + + # Saving XMP data to the second frame of an image + from PIL import Image + black = Image.new("RGB", (1, 1)) + red = Image.new("RGB", (1, 1), "#f00") + red.encoderinfo = {"xmp": b"test"} + black.save("out.mpo", save_all=True, append_images=[red]) + :returns: None + :exception ValueError: If the output format could not be determined + from the file name. Use the format option to solve this. + :exception OSError: If the file could not be written. The file + may have been created, and may contain partial data. + """ + + filename: str | bytes = "" + open_fp = False + if is_path(fp): + filename = os.fspath(fp) + open_fp = True + elif fp == sys.stdout: + try: + fp = sys.stdout.buffer + except AttributeError: + pass + if not filename and hasattr(fp, "name") and is_path(fp.name): + # only set the name for metadata purposes + filename = os.fspath(fp.name) + + preinit() + + filename_ext = os.path.splitext(filename)[1].lower() + ext = filename_ext.decode() if isinstance(filename_ext, bytes) else filename_ext + + if not format: + if ext not in EXTENSION: + init() + try: + format = EXTENSION[ext] + except KeyError as e: + msg = f"unknown file extension: {ext}" + raise ValueError(msg) from e + + from . import ImageFile + + # may mutate self! + if isinstance(self, ImageFile.ImageFile) and os.path.abspath( + filename + ) == os.path.abspath(self.filename): + self._ensure_mutable() + else: + self.load() + + save_all = params.pop("save_all", None) + self._default_encoderinfo = params + encoderinfo = getattr(self, "encoderinfo", {}) + self._attach_default_encoderinfo(self) + self.encoderconfig: tuple[Any, ...] = () + + if format.upper() not in SAVE: + init() + if save_all or ( + save_all is None + and params.get("append_images") + and format.upper() in SAVE_ALL + ): + save_handler = SAVE_ALL[format.upper()] + else: + save_handler = SAVE[format.upper()] + + created = False + if open_fp: + created = not os.path.exists(filename) + if params.get("append", False): + # Open also for reading ("+"), because TIFF save_all + # writer needs to go back and edit the written data. + fp = builtins.open(filename, "r+b") + else: + fp = builtins.open(filename, "w+b") + else: + fp = cast(IO[bytes], fp) + + try: + save_handler(self, fp, filename) + except Exception: + if open_fp: + fp.close() + if created: + try: + os.remove(filename) + except PermissionError: + pass + raise + finally: + self.encoderinfo = encoderinfo + if open_fp: + fp.close() + + def _attach_default_encoderinfo(self, im: Image) -> dict[str, Any]: + encoderinfo = getattr(self, "encoderinfo", {}) + self.encoderinfo = {**im._default_encoderinfo, **encoderinfo} + return encoderinfo + + def seek(self, frame: int) -> None: + """ + 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. + + See :py:meth:`~PIL.Image.Image.tell`. + + If defined, :attr:`~PIL.Image.Image.n_frames` refers to the + number of available frames. + + :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: + msg = "no more images in file" + raise EOFError(msg) + + def show(self, title: str | None = None) -> None: + """ + Displays this image. This method is mainly intended for debugging purposes. + + This method calls :py:func:`PIL.ImageShow.show` internally. You can use + :py:func:`PIL.ImageShow.register` to override its default behaviour. + + The image is first saved to a temporary file. By default, it will be in + PNG format. + + On Unix, the image is then opened using the **xdg-open**, **display**, + **gm**, **eog** or **xv** utility, depending on which one can be found. + + On macOS, the image is opened with the native Preview application. + + On Windows, the image is opened with the standard PNG display utility. + + :param title: Optional title to use for the image window, where possible. + """ + + from . import ImageShow + + ImageShow.show(self, title) + + def split(self) -> tuple[Image, ...]: + """ + 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). + + If you need only one band, :py:meth:`~PIL.Image.Image.getchannel` + method can be more convenient and faster. + + :returns: A tuple containing bands. + """ + + self.load() + if self.im.bands == 1: + return (self.copy(),) + return tuple(map(self._new, self.im.split())) + + def getchannel(self, channel: int | str) -> Image: + """ + Returns an image containing a single channel of the source image. + + :param channel: What channel to return. Could be index + (0 for "R" channel of "RGB") or channel name + ("A" for alpha channel of "RGBA"). + :returns: An image in "L" mode. + + .. versionadded:: 4.3.0 + """ + self.load() + + if isinstance(channel, str): + try: + channel = self.getbands().index(channel) + except ValueError as e: + msg = f'The image has no channel "{channel}"' + raise ValueError(msg) from e + + return self._new(self.im.getband(channel)) + + def tell(self) -> int: + """ + Returns the current frame number. See :py:meth:`~PIL.Image.Image.seek`. + + If defined, :attr:`~PIL.Image.Image.n_frames` refers to the + number of available frames. + + :returns: Frame number, starting with 0. + """ + return 0 + + def thumbnail( + self, + size: tuple[float, float], + resample: Resampling = Resampling.BICUBIC, + reducing_gap: float | None = 2.0, + ) -> None: + """ + 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: The requested size in pixels, as a 2-tuple: + (width, height). + :param resample: Optional resampling filter. This can be one + of :py:data:`Resampling.NEAREST`, :py:data:`Resampling.BOX`, + :py:data:`Resampling.BILINEAR`, :py:data:`Resampling.HAMMING`, + :py:data:`Resampling.BICUBIC` or :py:data:`Resampling.LANCZOS`. + If omitted, it defaults to :py:data:`Resampling.BICUBIC`. + (was :py:data:`Resampling.NEAREST` prior to version 2.5.0). + See: :ref:`concept-filters`. + :param reducing_gap: Apply optimization by resizing the image + in two steps. First, reducing the image by integer times + using :py:meth:`~PIL.Image.Image.reduce` or + :py:meth:`~PIL.Image.Image.draft` for JPEG images. + Second, resizing using regular resampling. The last step + changes size no less than by ``reducing_gap`` times. + ``reducing_gap`` may be None (no first step is performed) + or should be greater than 1.0. The bigger ``reducing_gap``, + the closer the result to the fair resampling. + The smaller ``reducing_gap``, the faster resizing. + With ``reducing_gap`` greater or equal to 3.0, the result is + indistinguishable from fair resampling in most cases. + The default value is 2.0 (very close to fair resampling + while still being faster in many cases). + :returns: None + """ + + provided_size = tuple(map(math.floor, size)) + + def preserve_aspect_ratio() -> tuple[int, int] | None: + def round_aspect(number: float, key: Callable[[int], float]) -> int: + return max(min(math.floor(number), math.ceil(number), key=key), 1) + + x, y = provided_size + if x >= self.width and y >= self.height: + return None + + aspect = self.width / self.height + if x / y >= aspect: + x = round_aspect(y * aspect, key=lambda n: abs(aspect - n / y)) + else: + y = round_aspect( + x / aspect, key=lambda n: 0 if n == 0 else abs(aspect - x / n) + ) + return x, y + + preserved_size = preserve_aspect_ratio() + if preserved_size is None: + return + final_size = preserved_size + + box = None + if reducing_gap is not None: + res = self.draft( + None, (int(size[0] * reducing_gap), int(size[1] * reducing_gap)) + ) + if res is not None: + box = res[1] + + if self.size != final_size: + im = self.resize(final_size, resample, box=box, reducing_gap=reducing_gap) + + self.im = im.im + self._size = final_size + self._mode = self.im.mode + + self.readonly = 0 + + # FIXME: the different transform methods need further explanation + # instead of bloating the method docs, add a separate chapter. + def transform( + self, + size: tuple[int, int], + method: Transform | ImageTransformHandler | SupportsGetData, + data: Sequence[Any] | None = None, + resample: int = Resampling.NEAREST, + fill: int = 1, + fillcolor: float | tuple[float, ...] | str | None = None, + ) -> Image: + """ + 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 in pixels, as a 2-tuple: + (width, height). + :param method: The transformation method. This is one of + :py:data:`Transform.EXTENT` (cut out a rectangular subregion), + :py:data:`Transform.AFFINE` (affine transform), + :py:data:`Transform.PERSPECTIVE` (perspective transform), + :py:data:`Transform.QUAD` (map a quadrilateral to a rectangle), or + :py:data:`Transform.MESH` (map a number of source quadrilaterals + in one operation). + + It may also be an :py:class:`~PIL.Image.ImageTransformHandler` + object:: + + class Example(Image.ImageTransformHandler): + def transform(self, size, data, resample, fill=1): + # Return result + + Implementations of :py:class:`~PIL.Image.ImageTransformHandler` + for some of the :py:class:`Transform` methods are provided + in :py:mod:`~PIL.ImageTransform`. + + It may also be an object with a ``method.getdata`` method + that returns a tuple supplying new ``method`` and ``data`` values:: + + class Example: + def getdata(self): + method = Image.Transform.EXTENT + data = (0, 0, 100, 100) + return method, data + :param data: Extra data to the transformation method. + :param resample: Optional resampling filter. It can be one of + :py:data:`Resampling.NEAREST` (use nearest neighbour), + :py:data:`Resampling.BILINEAR` (linear interpolation in a 2x2 + environment), or :py:data:`Resampling.BICUBIC` (cubic spline + interpolation in a 4x4 environment). If omitted, or if the image + has mode "1" or "P", it is set to :py:data:`Resampling.NEAREST`. + See: :ref:`concept-filters`. + :param fill: If ``method`` is an + :py:class:`~PIL.Image.ImageTransformHandler` object, this is one of + the arguments passed to it. Otherwise, it is unused. + :param fillcolor: Optional fill color for the area outside the + transform in the output image. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + if self.mode in ("LA", "RGBA") and resample != Resampling.NEAREST: + return ( + self.convert({"LA": "La", "RGBA": "RGBa"}[self.mode]) + .transform(size, method, data, resample, fill, fillcolor) + .convert(self.mode) + ) + + 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: + msg = "missing method data" + raise ValueError(msg) + + im = new(self.mode, size, fillcolor) + if self.mode == "P" and self.palette: + im.palette = self.palette.copy() + im.info = self.info.copy() + if method == Transform.MESH: + # list of quads + for box, quad in data: + im.__transformer( + box, self, Transform.QUAD, quad, resample, fillcolor is None + ) + else: + im.__transformer( + (0, 0) + size, self, method, data, resample, fillcolor is None + ) + + return im + + def __transformer( + self, + box: tuple[int, int, int, int], + image: Image, + method: Transform, + data: Sequence[float], + resample: int = Resampling.NEAREST, + fill: bool = True, + ) -> None: + w = box[2] - box[0] + h = box[3] - box[1] + + if method == Transform.AFFINE: + data = data[:6] + + elif method == Transform.EXTENT: + # convert extent to an affine transform + x0, y0, x1, y1 = data + xs = (x1 - x0) / w + ys = (y1 - y0) / h + method = Transform.AFFINE + data = (xs, 0, x0, 0, ys, y0) + + elif method == Transform.PERSPECTIVE: + data = data[:8] + + elif method == Transform.QUAD: + # quadrilateral warp. data specifies the four corners + # given as NW, SW, SE, and NE. + nw = data[: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: + msg = "unknown transformation method" + raise ValueError(msg) + + if resample not in ( + Resampling.NEAREST, + Resampling.BILINEAR, + Resampling.BICUBIC, + ): + if resample in (Resampling.BOX, Resampling.HAMMING, Resampling.LANCZOS): + unusable: dict[int, str] = { + Resampling.BOX: "Image.Resampling.BOX", + Resampling.HAMMING: "Image.Resampling.HAMMING", + Resampling.LANCZOS: "Image.Resampling.LANCZOS", + } + msg = unusable[resample] + f" ({resample}) cannot be used." + else: + msg = f"Unknown resampling filter ({resample})." + + filters = [ + f"{filter[1]} ({filter[0]})" + for filter in ( + (Resampling.NEAREST, "Image.Resampling.NEAREST"), + (Resampling.BILINEAR, "Image.Resampling.BILINEAR"), + (Resampling.BICUBIC, "Image.Resampling.BICUBIC"), + ) + ] + msg += f" Use {', '.join(filters[:-1])} or {filters[-1]}" + raise ValueError(msg) + + image.load() + + self.load() + + if image.mode in ("1", "P"): + resample = Resampling.NEAREST + + self.im.transform(box, image.im, method, data, resample, fill) + + def transpose(self, method: Transpose) -> Image: + """ + Transpose image (flip or rotate in 90 degree steps) + + :param method: One of :py:data:`Transpose.FLIP_LEFT_RIGHT`, + :py:data:`Transpose.FLIP_TOP_BOTTOM`, :py:data:`Transpose.ROTATE_90`, + :py:data:`Transpose.ROTATE_180`, :py:data:`Transpose.ROTATE_270`, + :py:data:`Transpose.TRANSPOSE` or :py:data:`Transpose.TRANSVERSE`. + :returns: Returns a flipped or rotated copy of this image. + """ + + self.load() + return self._new(self.im.transpose(method)) + + def effect_spread(self, distance: int) -> Image: + """ + Randomly spread pixels in an image. + + :param distance: Distance to spread pixels. + """ + self.load() + return self._new(self.im.effect_spread(distance)) + + def toqimage(self) -> ImageQt.ImageQt: + """Returns a QImage copy of this image""" + from . import ImageQt + + if not ImageQt.qt_is_installed: + msg = "Qt bindings are not installed" + raise ImportError(msg) + return ImageQt.toqimage(self) + + def toqpixmap(self) -> ImageQt.QPixmap: + """Returns a QPixmap copy of this image""" + from . import ImageQt + + if not ImageQt.qt_is_installed: + msg = "Qt bindings are not installed" + raise ImportError(msg) + return ImageQt.toqpixmap(self) + + +# -------------------------------------------------------------------- +# Abstract handlers. + + +class ImagePointHandler(abc.ABC): + """ + Used as a mixin by point transforms + (for use with :py:meth:`~PIL.Image.Image.point`) + """ + + @abc.abstractmethod + def point(self, im: Image) -> Image: + pass + + +class ImageTransformHandler(abc.ABC): + """ + Used as a mixin by geometry transforms + (for use with :py:meth:`~PIL.Image.Image.transform`) + """ + + @abc.abstractmethod + def transform( + self, + size: tuple[int, int], + image: Image, + **options: Any, + ) -> Image: + pass + + +# -------------------------------------------------------------------- +# Factories + + +def _check_size(size: Any) -> None: + """ + Common check to enforce type and sanity check on size tuples + + :param size: Should be a 2 tuple of (width, height) + :returns: None, or raises a ValueError + """ + + if not isinstance(size, (list, tuple)): + msg = "Size must be a list or tuple" + raise ValueError(msg) + if len(size) != 2: + msg = "Size must be a sequence of length 2" + raise ValueError(msg) + if size[0] < 0 or size[1] < 0: + msg = "Width and height must be >= 0" + raise ValueError(msg) + + +def new( + mode: str, + size: tuple[int, int] | list[int], + color: float | tuple[float, ...] | str | None = 0, +) -> Image: + """ + 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 or HSV images, you can also use color strings as supported + by the ImageColor module. See :ref:`colors` for more information. If the + color is None, the image is not initialised. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + _check_size(size) + + if color is None: + # don't initialize + return Image()._new(core.new(mode, size)) + + if isinstance(color, str): + # css3-style specifier + + from . import ImageColor + + color = ImageColor.getcolor(color, mode) + + im = Image() + if ( + mode == "P" + and isinstance(color, (list, tuple)) + and all(isinstance(i, int) for i in color) + ): + color_ints: tuple[int, ...] = cast(tuple[int, ...], tuple(color)) + if len(color_ints) == 3 or len(color_ints) == 4: + # RGB or RGBA value for a P image + from . import ImagePalette + + im.palette = ImagePalette.ImagePalette() + color = im.palette.getcolor(color_ints) + return im._new(core.fill(mode, size, color)) + + +def frombytes( + mode: str, + size: tuple[int, int], + data: bytes | bytearray | SupportsArrayInterface, + decoder_name: str = "raw", + *args: Any, +) -> Image: + """ + 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 + :ref:`Writing Your Own File Codec `. + + 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. + """ + + _check_size(size) + + im = new(mode, size) + if im.width != 0 and im.height != 0: + decoder_args: Any = args + if len(decoder_args) == 1 and isinstance(decoder_args[0], tuple): + # may pass tuple instead of argument list + decoder_args = decoder_args[0] + + if decoder_name == "raw" and decoder_args == (): + decoder_args = mode + + im.frombytes(data, decoder_name, decoder_args) + return im + + +def frombuffer( + mode: str, + size: tuple[int, int], + data: bytes | SupportsArrayInterface, + decoder_name: str = "raw", + *args: Any, +) -> Image: + """ + 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 + :py:class:`~io.BytesIO` object, and use :py:func:`~PIL.Image.open` to load it. + + The default parameters used for the "raw" decoder differs from that used for + :py:func:`~PIL.Image.frombytes`. 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 + """ + + _check_size(size) + + # 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 == (): + args = mode, 0, 1 + if args[0] in _MAPMODES: + im = new(mode, (0, 0)) + im = im._new(core.map_buffer(data, size, decoder_name, 0, args)) + if mode == "P": + from . import ImagePalette + + im.palette = ImagePalette.ImagePalette("RGB", im.im.getpalette("RGB")) + im.readonly = 1 + return im + + return frombytes(mode, size, data, decoder_name, args) + + +class SupportsArrayInterface(Protocol): + """ + An object that has an ``__array_interface__`` dictionary. + """ + + @property + def __array_interface__(self) -> dict[str, Any]: + raise NotImplementedError() + + +class SupportsArrowArrayInterface(Protocol): + """ + An object that has an ``__arrow_c_array__`` method corresponding to the arrow c + data interface. + """ + + def __arrow_c_array__( + self, requested_schema: "PyCapsule" = None # type: ignore[name-defined] # noqa: F821, UP037 + ) -> tuple["PyCapsule", "PyCapsule"]: # type: ignore[name-defined] # noqa: F821, UP037 + raise NotImplementedError() + + +def fromarray(obj: SupportsArrayInterface, mode: str | None = None) -> Image: + """ + Creates an image memory from an object exporting the array interface + (using the buffer protocol):: + + from PIL import Image + import numpy as np + a = np.zeros((5, 5)) + im = Image.fromarray(a) + + If ``obj`` is not contiguous, then the ``tobytes`` method is called + and :py:func:`~PIL.Image.frombuffer` is used. + + In the case of NumPy, be aware that Pillow modes do not always correspond + to NumPy dtypes. Pillow modes only offer 1-bit pixels, 8-bit pixels, + 32-bit signed integer pixels, and 32-bit floating point pixels. + + Pillow images can also be converted to arrays:: + + from PIL import Image + import numpy as np + im = Image.open("hopper.jpg") + a = np.asarray(im) + + When converting Pillow images to arrays however, only pixel values are + transferred. This means that P and PA mode images will lose their palette. + + :param obj: Object with array interface + :param mode: Optional mode to use when reading ``obj``. Since pixel values do not + contain information about palettes or color spaces, this can be used to place + grayscale L mode data within a P mode image, or read RGB data as YCbCr for + example. + + See: :ref:`concept-modes` for general information about modes. + :returns: An image object. + + .. versionadded:: 1.1.6 + """ + arr = obj.__array_interface__ + shape = arr["shape"] + ndim = len(shape) + strides = arr.get("strides", None) + try: + typekey = (1, 1) + shape[2:], arr["typestr"] + except KeyError as e: + if mode is not None: + typekey = None + color_modes: list[str] = [] + else: + msg = "Cannot handle this data type" + raise TypeError(msg) from e + if typekey is not None: + try: + typemode, rawmode, color_modes = _fromarray_typemap[typekey] + except KeyError as e: + typekey_shape, typestr = typekey + msg = f"Cannot handle this data type: {typekey_shape}, {typestr}" + raise TypeError(msg) from e + if mode is not None: + if mode != typemode and mode not in color_modes: + deprecate("'mode' parameter for changing data types", 13) + rawmode = mode + else: + mode = typemode + if mode in ["1", "L", "I", "P", "F"]: + ndmax = 2 + elif mode == "RGB": + ndmax = 3 + else: + ndmax = 4 + if ndim > ndmax: + msg = f"Too many dimensions: {ndim} > {ndmax}." + raise ValueError(msg) + + size = 1 if ndim == 1 else shape[1], shape[0] + if strides is not None: + if hasattr(obj, "tobytes"): + obj = obj.tobytes() + elif hasattr(obj, "tostring"): + obj = obj.tostring() + else: + msg = "'strides' requires either tobytes() or tostring()" + raise ValueError(msg) + + return frombuffer(mode, size, obj, "raw", rawmode, 0, 1) + + +def fromarrow( + obj: SupportsArrowArrayInterface, mode: str, size: tuple[int, int] +) -> Image: + """Creates an image with zero-copy shared memory from an object exporting + the arrow_c_array interface protocol:: + + from PIL import Image + import pyarrow as pa + arr = pa.array([0]*(5*5*4), type=pa.uint8()) + im = Image.fromarrow(arr, 'RGBA', (5, 5)) + + If the data representation of the ``obj`` is not compatible with + Pillow internal storage, a ValueError is raised. + + Pillow images can also be converted to Arrow objects:: + + from PIL import Image + import pyarrow as pa + im = Image.open('hopper.jpg') + arr = pa.array(im) + + As with array support, when converting Pillow images to arrays, + only pixel values are transferred. This means that P and PA mode + images will lose their palette. + + :param obj: Object with an arrow_c_array interface + :param mode: Image mode. + :param size: Image size. This must match the storage of the arrow object. + :returns: An Image object + + Note that according to the Arrow spec, both the producer and the + consumer should consider the exported array to be immutable, as + unsynchronized updates will potentially cause inconsistent data. + + See: :ref:`arrow-support` for more detailed information + + .. versionadded:: 11.2.1 + + """ + if not hasattr(obj, "__arrow_c_array__"): + msg = "arrow_c_array interface not found" + raise ValueError(msg) + + (schema_capsule, array_capsule) = obj.__arrow_c_array__() + _im = core.new_arrow(mode, size, schema_capsule, array_capsule) + if _im: + return Image()._new(_im) + + msg = "new_arrow returned None without an exception" + raise ValueError(msg) + + +def fromqimage(im: ImageQt.QImage) -> ImageFile.ImageFile: + """Creates an image instance from a QImage image""" + from . import ImageQt + + if not ImageQt.qt_is_installed: + msg = "Qt bindings are not installed" + raise ImportError(msg) + return ImageQt.fromqimage(im) + + +def fromqpixmap(im: ImageQt.QPixmap) -> ImageFile.ImageFile: + """Creates an image instance from a QPixmap image""" + from . import ImageQt + + if not ImageQt.qt_is_installed: + msg = "Qt bindings are not installed" + raise ImportError(msg) + return ImageQt.fromqpixmap(im) + + +_fromarray_typemap = { + # (shape, typestr) => mode, rawmode, color modes + # first two members of shape are set to one + ((1, 1), "|b1"): ("1", "1;8", []), + ((1, 1), "|u1"): ("L", "L", ["P"]), + ((1, 1), "|i1"): ("I", "I;8", []), + ((1, 1), "u2"): ("I", "I;16B", []), + ((1, 1), "i2"): ("I", "I;16BS", []), + ((1, 1), "u4"): ("I", "I;32B", []), + ((1, 1), "i4"): ("I", "I;32BS", []), + ((1, 1), "f4"): ("F", "F;32BF", []), + ((1, 1), "f8"): ("F", "F;64BF", []), + ((1, 1, 2), "|u1"): ("LA", "LA", ["La", "PA"]), + ((1, 1, 3), "|u1"): ("RGB", "RGB", ["YCbCr", "LAB", "HSV"]), + ((1, 1, 4), "|u1"): ("RGBA", "RGBA", ["RGBa", "RGBX", "CMYK"]), + # shortcuts: + ((1, 1), f"{_ENDIAN}i4"): ("I", "I", []), + ((1, 1), f"{_ENDIAN}f4"): ("F", "F", []), +} + + +def _decompression_bomb_check(size: tuple[int, int]) -> None: + if MAX_IMAGE_PIXELS is None: + return + + pixels = max(1, size[0]) * max(1, size[1]) + + if pixels > 2 * MAX_IMAGE_PIXELS: + msg = ( + f"Image size ({pixels} pixels) exceeds limit of {2 * MAX_IMAGE_PIXELS} " + "pixels, could be decompression bomb DOS attack." + ) + raise DecompressionBombError(msg) + + if pixels > MAX_IMAGE_PIXELS: + warnings.warn( + f"Image size ({pixels} pixels) exceeds limit of {MAX_IMAGE_PIXELS} pixels, " + "could be decompression bomb DOS attack.", + DecompressionBombWarning, + ) + + +def open( + fp: StrOrBytesPath | IO[bytes], + mode: Literal["r"] = "r", + formats: list[str] | tuple[str, ...] | None = None, +) -> ImageFile.ImageFile: + """ + 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`. See :ref:`file-handling`. + + :param fp: A filename (string), os.PathLike object or a file object. + The file object must implement ``file.read``, + ``file.seek``, and ``file.tell`` methods, + and be opened in binary mode. The file object will also seek to zero + before reading. + :param mode: The mode. If given, this argument must be "r". + :param formats: A list or tuple of formats to attempt to load the file in. + This can be used to restrict the set of formats checked. + Pass ``None`` to try all supported formats. You can print the set of + available formats by running ``python3 -m PIL`` or using + the :py:func:`PIL.features.pilinfo` function. + :returns: An :py:class:`~PIL.Image.Image` object. + :exception FileNotFoundError: If the file cannot be found. + :exception PIL.UnidentifiedImageError: If the image cannot be opened and + identified. + :exception ValueError: If the ``mode`` is not "r", or if a ``StringIO`` + instance is used for ``fp``. + :exception TypeError: If ``formats`` is not ``None``, a list or a tuple. + """ + + if mode != "r": + msg = f"bad mode {repr(mode)}" # type: ignore[unreachable] + raise ValueError(msg) + elif isinstance(fp, io.StringIO): + msg = ( # type: ignore[unreachable] + "StringIO cannot be used to open an image. " + "Binary data must be used instead." + ) + raise ValueError(msg) + + if formats is None: + formats = ID + elif not isinstance(formats, (list, tuple)): + msg = "formats must be a list or tuple" # type: ignore[unreachable] + raise TypeError(msg) + + exclusive_fp = False + filename: str | bytes = "" + if is_path(fp): + filename = os.fspath(fp) + fp = builtins.open(filename, "rb") + exclusive_fp = True + else: + fp = cast(IO[bytes], fp) + + try: + fp.seek(0) + except (AttributeError, io.UnsupportedOperation): + fp = io.BytesIO(fp.read()) + exclusive_fp = True + + prefix = fp.read(16) + + preinit() + + warning_messages: list[str] = [] + + def _open_core( + fp: IO[bytes], + filename: str | bytes, + prefix: bytes, + formats: list[str] | tuple[str, ...], + ) -> ImageFile.ImageFile | None: + for i in formats: + i = i.upper() + if i not in OPEN: + init() + try: + factory, accept = OPEN[i] + result = not accept or accept(prefix) + if isinstance(result, str): + warning_messages.append(result) + elif result: + fp.seek(0) + im = factory(fp, filename) + _decompression_bomb_check(im.size) + return im + except (SyntaxError, IndexError, TypeError, struct.error) as e: + if WARN_POSSIBLE_FORMATS: + warning_messages.append(i + " opening failed. " + str(e)) + except BaseException: + if exclusive_fp: + fp.close() + raise + return None + + im = _open_core(fp, filename, prefix, formats) + + if im is None and formats is ID: + checked_formats = ID.copy() + if init(): + im = _open_core( + fp, + filename, + prefix, + tuple(format for format in formats if format not in checked_formats), + ) + + if im: + im._exclusive_fp = exclusive_fp + return im + + if exclusive_fp: + fp.close() + for message in warning_messages: + warnings.warn(message) + msg = "cannot identify image file %r" % (filename if filename else fp) + raise UnidentifiedImageError(msg) + + +# +# Image processing. + + +def alpha_composite(im1: Image, im2: Image) -> Image: + """ + Alpha composite im2 over im1. + + :param im1: The first image. Must have mode RGBA or LA. + :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: Image, im2: Image, alpha: float) -> Image: + """ + 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: Image, image2: Image, mask: Image) -> Image: + """ + 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: Image, *args: Callable[[int], float]) -> Image: + """ + 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: str, bands: Sequence[Image]) -> Image: + """ + 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: + msg = "wrong number of bands" + raise ValueError(msg) + for band in bands[1:]: + if band.mode != getmodetype(mode): + msg = "mode mismatch" + raise ValueError(msg) + if band.size != bands[0].size: + msg = "size mismatch" + raise ValueError(msg) + for band in bands: + band.load() + return bands[0]._new(core.merge(mode, *[b.im for b in bands])) + + +# -------------------------------------------------------------------- +# Plugin registry + + +def register_open( + id: str, + factory: ( + Callable[[IO[bytes], str | bytes], ImageFile.ImageFile] + | type[ImageFile.ImageFile] + ), + accept: Callable[[bytes], bool | str] | None = None, +) -> 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() + if id not in ID: + ID.append(id) + OPEN[id] = factory, accept + + +def register_mime(id: str, mimetype: str) -> None: + """ + Registers an image MIME type by populating ``Image.MIME``. This function + should not be used in application code. + + ``Image.MIME`` provides a mapping from image format identifiers to mime + formats, but :py:meth:`~PIL.ImageFile.ImageFile.get_format_mimetype` can + provide a different result for specific images. + + :param id: An image format identifier. + :param mimetype: The image MIME type for this format. + """ + MIME[id.upper()] = mimetype + + +def register_save( + id: str, driver: Callable[[Image, IO[bytes], str | bytes], None] +) -> None: + """ + 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_save_all( + id: str, driver: Callable[[Image, IO[bytes], str | bytes], None] +) -> None: + """ + Registers an image function to save all the frames + of a multiframe format. 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_ALL[id.upper()] = driver + + +def register_extension(id: str, extension: str) -> None: + """ + 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() + + +def register_extensions(id: str, extensions: list[str]) -> None: + """ + Registers image extensions. This function should not be + used in application code. + + :param id: An image format identifier. + :param extensions: A list of extensions used for this format. + """ + for extension in extensions: + register_extension(id, extension) + + +def registered_extensions() -> dict[str, str]: + """ + Returns a dictionary containing all file extensions belonging + to registered plugins + """ + init() + return EXTENSION + + +def register_decoder(name: str, decoder: type[ImageFile.PyDecoder]) -> None: + """ + Registers an image decoder. This function should not be + used in application code. + + :param name: The name of the decoder + :param decoder: An ImageFile.PyDecoder object + + .. versionadded:: 4.1.0 + """ + DECODERS[name] = decoder + + +def register_encoder(name: str, encoder: type[ImageFile.PyEncoder]) -> None: + """ + Registers an image encoder. This function should not be + used in application code. + + :param name: The name of the encoder + :param encoder: An ImageFile.PyEncoder object + + .. versionadded:: 4.1.0 + """ + ENCODERS[name] = encoder + + +# -------------------------------------------------------------------- +# Simple display support. + + +def _show(image: Image, **options: Any) -> None: + from . import ImageShow + + deprecate("Image._show", 13, "ImageShow.show") + ImageShow.show(image, **options) + + +# -------------------------------------------------------------------- +# Effects + + +def effect_mandelbrot( + size: tuple[int, int], extent: tuple[float, float, float, float], quality: int +) -> Image: + """ + 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, y1). + :param quality: Quality. + """ + return Image()._new(core.effect_mandelbrot(size, extent, quality)) + + +def effect_noise(size: tuple[int, int], sigma: float) -> Image: + """ + 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)) + + +def linear_gradient(mode: str) -> Image: + """ + Generate 256x256 linear gradient from black to white, top to bottom. + + :param mode: Input mode. + """ + return Image()._new(core.linear_gradient(mode)) + + +def radial_gradient(mode: str) -> Image: + """ + Generate 256x256 radial gradient from black to white, centre to edge. + + :param mode: Input mode. + """ + return Image()._new(core.radial_gradient(mode)) + + +# -------------------------------------------------------------------- +# Resources + + +def _apply_env_variables(env: dict[str, str] | None = None) -> None: + env_dict = env if env is not None else os.environ + + for var_name, setter in [ + ("PILLOW_ALIGNMENT", core.set_alignment), + ("PILLOW_BLOCK_SIZE", core.set_block_size), + ("PILLOW_BLOCKS_MAX", core.set_blocks_max), + ]: + if var_name not in env_dict: + continue + + var = env_dict[var_name].lower() + + units = 1 + for postfix, mul in [("k", 1024), ("m", 1024 * 1024)]: + if var.endswith(postfix): + units = mul + var = var[: -len(postfix)] + + try: + var_int = int(var) * units + except ValueError: + warnings.warn(f"{var_name} is not int") + continue + + try: + setter(var_int) + except ValueError as e: + warnings.warn(f"{var_name}: {e}") + + +_apply_env_variables() +atexit.register(core.clear_cache) + + +if TYPE_CHECKING: + _ExifBase = MutableMapping[int, Any] +else: + _ExifBase = MutableMapping + + +class Exif(_ExifBase): + """ + This class provides read and write access to EXIF image data:: + + from PIL import Image + im = Image.open("exif.png") + exif = im.getexif() # Returns an instance of this class + + Information can be read and written, iterated over or deleted:: + + print(exif[274]) # 1 + exif[274] = 2 + for k, v in exif.items(): + print("Tag", k, "Value", v) # Tag 274 Value 2 + del exif[274] + + To access information beyond IFD0, :py:meth:`~PIL.Image.Exif.get_ifd` + returns a dictionary:: + + from PIL import ExifTags + im = Image.open("exif_gps.jpg") + exif = im.getexif() + gps_ifd = exif.get_ifd(ExifTags.IFD.GPSInfo) + print(gps_ifd) + + Other IFDs include ``ExifTags.IFD.Exif``, ``ExifTags.IFD.MakerNote``, + ``ExifTags.IFD.Interop`` and ``ExifTags.IFD.IFD1``. + + :py:mod:`~PIL.ExifTags` also has enum classes to provide names for data:: + + print(exif[ExifTags.Base.Software]) # PIL + print(gps_ifd[ExifTags.GPS.GPSDateStamp]) # 1999:99:99 99:99:99 + """ + + endian: str | None = None + bigtiff = False + _loaded = False + + def __init__(self) -> None: + self._data: dict[int, Any] = {} + self._hidden_data: dict[int, Any] = {} + self._ifds: dict[int, dict[int, Any]] = {} + self._info: TiffImagePlugin.ImageFileDirectory_v2 | None = None + self._loaded_exif: bytes | None = None + + def _fixup(self, value: Any) -> Any: + try: + if len(value) == 1 and isinstance(value, tuple): + return value[0] + except Exception: + pass + return value + + def _fixup_dict(self, src_dict: dict[int, Any]) -> dict[int, Any]: + # Helper function + # returns a dict with any single item tuples/lists as individual values + return {k: self._fixup(v) for k, v in src_dict.items()} + + def _get_ifd_dict( + self, offset: int, group: int | None = None + ) -> dict[int, Any] | None: + try: + # an offset pointer to the location of the nested embedded IFD. + # It should be a long, but may be corrupted. + self.fp.seek(offset) + except (KeyError, TypeError): + return None + else: + from . import TiffImagePlugin + + info = TiffImagePlugin.ImageFileDirectory_v2(self.head, group=group) + info.load(self.fp) + return self._fixup_dict(dict(info)) + + def _get_head(self) -> bytes: + version = b"\x2b" if self.bigtiff else b"\x2a" + if self.endian == "<": + head = b"II" + version + b"\x00" + o32le(8) + else: + head = b"MM\x00" + version + o32be(8) + if self.bigtiff: + head += o32le(8) if self.endian == "<" else o32be(8) + head += b"\x00\x00\x00\x00" + return head + + def load(self, data: bytes) -> None: + # Extract EXIF information. This 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 (!). + if data == self._loaded_exif: + return + self._loaded_exif = data + self._data.clear() + self._hidden_data.clear() + self._ifds.clear() + while data and data.startswith(b"Exif\x00\x00"): + data = data[6:] + if not data: + self._info = None + return + + self.fp: IO[bytes] = io.BytesIO(data) + self.head = self.fp.read(8) + # process dictionary + from . import TiffImagePlugin + + self._info = TiffImagePlugin.ImageFileDirectory_v2(self.head) + self.endian = self._info._endian + self.fp.seek(self._info.next) + self._info.load(self.fp) + + def load_from_fp(self, fp: IO[bytes], offset: int | None = None) -> None: + self._loaded_exif = None + self._data.clear() + self._hidden_data.clear() + self._ifds.clear() + + # process dictionary + from . import TiffImagePlugin + + self.fp = fp + if offset is not None: + self.head = self._get_head() + else: + self.head = self.fp.read(8) + self._info = TiffImagePlugin.ImageFileDirectory_v2(self.head) + if self.endian is None: + self.endian = self._info._endian + if offset is None: + offset = self._info.next + self.fp.tell() + self.fp.seek(offset) + self._info.load(self.fp) + + def _get_merged_dict(self) -> dict[int, Any]: + merged_dict = dict(self) + + # get EXIF extension + if ExifTags.IFD.Exif in self: + ifd = self._get_ifd_dict(self[ExifTags.IFD.Exif], ExifTags.IFD.Exif) + if ifd: + merged_dict.update(ifd) + + # GPS + if ExifTags.IFD.GPSInfo in self: + merged_dict[ExifTags.IFD.GPSInfo] = self._get_ifd_dict( + self[ExifTags.IFD.GPSInfo], ExifTags.IFD.GPSInfo + ) + + return merged_dict + + def tobytes(self, offset: int = 8) -> bytes: + from . import TiffImagePlugin + + head = self._get_head() + ifd = TiffImagePlugin.ImageFileDirectory_v2(ifh=head) + for tag, ifd_dict in self._ifds.items(): + if tag not in self: + ifd[tag] = ifd_dict + for tag, value in self.items(): + if tag in [ + ExifTags.IFD.Exif, + ExifTags.IFD.GPSInfo, + ] and not isinstance(value, dict): + value = self.get_ifd(tag) + if ( + tag == ExifTags.IFD.Exif + and ExifTags.IFD.Interop in value + and not isinstance(value[ExifTags.IFD.Interop], dict) + ): + value = value.copy() + value[ExifTags.IFD.Interop] = self.get_ifd(ExifTags.IFD.Interop) + ifd[tag] = value + return b"Exif\x00\x00" + head + ifd.tobytes(offset) + + def get_ifd(self, tag: int) -> dict[int, Any]: + if tag not in self._ifds: + if tag == ExifTags.IFD.IFD1: + if self._info is not None and self._info.next != 0: + ifd = self._get_ifd_dict(self._info.next) + if ifd is not None: + self._ifds[tag] = ifd + elif tag in [ExifTags.IFD.Exif, ExifTags.IFD.GPSInfo]: + offset = self._hidden_data.get(tag, self.get(tag)) + if offset is not None: + ifd = self._get_ifd_dict(offset, tag) + if ifd is not None: + self._ifds[tag] = ifd + elif tag in [ExifTags.IFD.Interop, ExifTags.IFD.MakerNote]: + if ExifTags.IFD.Exif not in self._ifds: + self.get_ifd(ExifTags.IFD.Exif) + tag_data = self._ifds[ExifTags.IFD.Exif][tag] + if tag == ExifTags.IFD.MakerNote: + from .TiffImagePlugin import ImageFileDirectory_v2 + + if tag_data.startswith(b"FUJIFILM"): + ifd_offset = i32le(tag_data, 8) + ifd_data = tag_data[ifd_offset:] + + makernote = {} + for i in range(struct.unpack(" 4: + (offset,) = struct.unpack("H", tag_data[:2])[0]): + ifd_tag, typ, count, data = struct.unpack( + ">HHL4s", tag_data[i * 12 + 2 : (i + 1) * 12 + 2] + ) + if ifd_tag == 0x1101: + # CameraInfo + (offset,) = struct.unpack(">L", data) + self.fp.seek(offset) + + camerainfo: dict[str, int | bytes] = { + "ModelID": self.fp.read(4) + } + + self.fp.read(4) + # Seconds since 2000 + camerainfo["TimeStamp"] = i32le(self.fp.read(12)) + + self.fp.read(4) + camerainfo["InternalSerialNumber"] = self.fp.read(4) + + self.fp.read(12) + parallax = self.fp.read(4) + handler = ImageFileDirectory_v2._load_dispatch[ + TiffTags.FLOAT + ][1] + camerainfo["Parallax"] = handler( + ImageFileDirectory_v2(), parallax, False + )[0] + + self.fp.read(4) + camerainfo["Category"] = self.fp.read(2) + + makernote = {0x1101: camerainfo} + self._ifds[tag] = makernote + else: + # Interop + ifd = self._get_ifd_dict(tag_data, tag) + if ifd is not None: + self._ifds[tag] = ifd + ifd = self._ifds.setdefault(tag, {}) + if tag == ExifTags.IFD.Exif and self._hidden_data: + ifd = { + k: v + for (k, v) in ifd.items() + if k not in (ExifTags.IFD.Interop, ExifTags.IFD.MakerNote) + } + return ifd + + def hide_offsets(self) -> None: + for tag in (ExifTags.IFD.Exif, ExifTags.IFD.GPSInfo): + if tag in self: + self._hidden_data[tag] = self[tag] + del self[tag] + + def __str__(self) -> str: + if self._info is not None: + # Load all keys into self._data + for tag in self._info: + self[tag] + + return str(self._data) + + def __len__(self) -> int: + keys = set(self._data) + if self._info is not None: + keys.update(self._info) + return len(keys) + + def __getitem__(self, tag: int) -> Any: + if self._info is not None and tag not in self._data and tag in self._info: + self._data[tag] = self._fixup(self._info[tag]) + del self._info[tag] + return self._data[tag] + + def __contains__(self, tag: object) -> bool: + return tag in self._data or (self._info is not None and tag in self._info) + + def __setitem__(self, tag: int, value: Any) -> None: + if self._info is not None and tag in self._info: + del self._info[tag] + self._data[tag] = value + + def __delitem__(self, tag: int) -> None: + if self._info is not None and tag in self._info: + del self._info[tag] + else: + del self._data[tag] + if tag in self._ifds: + del self._ifds[tag] + + def __iter__(self) -> Iterator[int]: + keys = set(self._data) + if self._info is not None: + keys.update(self._info) + return iter(keys) diff --git a/voxvera/vendor/PIL/ImageChops.py b/voxvera/vendor/PIL/ImageChops.py new file mode 100644 index 0000000..29a5c99 --- /dev/null +++ b/voxvera/vendor/PIL/ImageChops.py @@ -0,0 +1,311 @@ +# +# 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 __future__ import annotations + +from . import Image + + +def constant(image: Image.Image, value: int) -> Image.Image: + """Fill a channel with a given gray level. + + :rtype: :py:class:`~PIL.Image.Image` + """ + + return Image.new("L", image.size, value) + + +def duplicate(image: Image.Image) -> Image.Image: + """Copy a channel. Alias for :py:meth:`PIL.Image.Image.copy`. + + :rtype: :py:class:`~PIL.Image.Image` + """ + + return image.copy() + + +def invert(image: Image.Image) -> Image.Image: + """ + Invert an image (channel). :: + + out = MAX - image + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image.load() + return image._new(image.im.chop_invert()) + + +def lighter(image1: Image.Image, image2: Image.Image) -> Image.Image: + """ + Compares the two images, pixel by pixel, and returns a new image containing + the lighter values. :: + + 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: Image.Image, image2: Image.Image) -> Image.Image: + """ + Compares the two images, pixel by pixel, and returns a new image containing + the darker values. :: + + 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: Image.Image, image2: Image.Image) -> Image.Image: + """ + Returns the absolute value of the pixel-by-pixel difference between the two + images. :: + + 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: Image.Image, image2: Image.Image) -> Image.Image: + """ + 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. :: + + 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: Image.Image, image2: Image.Image) -> Image.Image: + """ + Superimposes two inverted images on top of each other. :: + + 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 soft_light(image1: Image.Image, image2: Image.Image) -> Image.Image: + """ + Superimposes two images on top of each other using the Soft Light algorithm + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_soft_light(image2.im)) + + +def hard_light(image1: Image.Image, image2: Image.Image) -> Image.Image: + """ + Superimposes two images on top of each other using the Hard Light algorithm + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_hard_light(image2.im)) + + +def overlay(image1: Image.Image, image2: Image.Image) -> Image.Image: + """ + Superimposes two images on top of each other using the Overlay algorithm + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_overlay(image2.im)) + + +def add( + image1: Image.Image, image2: Image.Image, scale: float = 1.0, offset: float = 0 +) -> Image.Image: + """ + Adds two images, dividing the result by scale and adding the + offset. If omitted, scale defaults to 1.0, and offset to 0.0. :: + + 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: Image.Image, image2: Image.Image, scale: float = 1.0, offset: float = 0 +) -> Image.Image: + """ + Subtracts two images, dividing the result by scale and adding the offset. + If omitted, scale defaults to 1.0, and offset to 0.0. :: + + 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: Image.Image, image2: Image.Image) -> Image.Image: + """Add two images, without clipping the result. :: + + 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: Image.Image, image2: Image.Image) -> Image.Image: + """Subtract two images, without clipping the result. :: + + 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: Image.Image, image2: Image.Image) -> Image.Image: + """Logical AND between two images. + + Both of the images must have mode "1". If you would like to perform a + logical AND on an image with a mode other than "1", try + :py:meth:`~PIL.ImageChops.multiply` instead, using a black-and-white mask + as the second image. :: + + 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: Image.Image, image2: Image.Image) -> Image.Image: + """Logical OR between two images. + + Both of the images must have mode "1". :: + + 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: Image.Image, image2: Image.Image) -> Image.Image: + """Logical XOR between two images. + + Both of the images must have mode "1". :: + + 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: Image.Image, image2: Image.Image, alpha: float) -> Image.Image: + """Blend images using constant transparency weight. Alias for + :py:func:`PIL.Image.blend`. + + :rtype: :py:class:`~PIL.Image.Image` + """ + + return Image.blend(image1, image2, alpha) + + +def composite( + image1: Image.Image, image2: Image.Image, mask: Image.Image +) -> Image.Image: + """Create composite using transparency mask. Alias for + :py:func:`PIL.Image.composite`. + + :rtype: :py:class:`~PIL.Image.Image` + """ + + return Image.composite(image1, image2, mask) + + +def offset(image: Image.Image, xoffset: int, yoffset: int | None = None) -> Image.Image: + """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 image: Input image. + :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/voxvera/vendor/PIL/ImageCms.py b/voxvera/vendor/PIL/ImageCms.py new file mode 100644 index 0000000..513e28a --- /dev/null +++ b/voxvera/vendor/PIL/ImageCms.py @@ -0,0 +1,1076 @@ +# The Python Imaging Library. +# $Id$ + +# Optional color management support, based on Kevin Cazabon's PyCMS +# library. + +# Originally released under LGPL. Graciously donated to PIL in +# March 2009, for distribution under the standard PIL license + +# 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 annotations + +import operator +import sys +from enum import IntEnum, IntFlag +from functools import reduce +from typing import Any, Literal, SupportsFloat, SupportsInt, Union + +from . import Image +from ._deprecate import deprecate +from ._typing import SupportsRead + +try: + from . import _imagingcms as core + + _CmsProfileCompatible = Union[ + str, SupportsRead[bytes], core.CmsProfile, "ImageCmsProfile" + ] +except ImportError as ex: + # Allow error import for doc purposes, but error out when accessing + # anything in core. + from ._util import DeferredError + + core = DeferredError.new(ex) + +_DESCRIPTION = """ +pyCMS + + a Python / PIL interface to the littleCMS ICC Color Management System + Copyright (C) 2002-2003 Kevin Cazabon + kevin@cazabon.com + https://www.cazabon.com + + pyCMS home page: https://www.cazabon.com/pyCMS + littleCMS home page: https://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 around 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" + + +# --------------------------------------------------------------------. + + +# +# intent/direction values + + +class Intent(IntEnum): + PERCEPTUAL = 0 + RELATIVE_COLORIMETRIC = 1 + SATURATION = 2 + ABSOLUTE_COLORIMETRIC = 3 + + +class Direction(IntEnum): + INPUT = 0 + OUTPUT = 1 + PROOF = 2 + + +# +# flags + + +class Flags(IntFlag): + """Flags and documentation are taken from ``lcms2.h``.""" + + NONE = 0 + NOCACHE = 0x0040 + """Inhibit 1-pixel cache""" + NOOPTIMIZE = 0x0100 + """Inhibit optimizations""" + NULLTRANSFORM = 0x0200 + """Don't transform anyway""" + GAMUTCHECK = 0x1000 + """Out of Gamut alarm""" + SOFTPROOFING = 0x4000 + """Do softproofing""" + BLACKPOINTCOMPENSATION = 0x2000 + NOWHITEONWHITEFIXUP = 0x0004 + """Don't fix scum dot""" + HIGHRESPRECALC = 0x0400 + """Use more memory to give better accuracy""" + LOWRESPRECALC = 0x0800 + """Use less memory to minimize resources""" + # this should be 8BITS_DEVICELINK, but that is not a valid name in Python: + USE_8BITS_DEVICELINK = 0x0008 + """Create 8 bits devicelinks""" + GUESSDEVICECLASS = 0x0020 + """Guess device class (for ``transform2devicelink``)""" + KEEP_SEQUENCE = 0x0080 + """Keep profile sequence for devicelink creation""" + FORCE_CLUT = 0x0002 + """Force CLUT optimization""" + CLUT_POST_LINEARIZATION = 0x0001 + """create postlinearization tables if possible""" + CLUT_PRE_LINEARIZATION = 0x0010 + """create prelinearization tables if possible""" + NONEGATIVES = 0x8000 + """Prevent negative numbers in floating point transforms""" + COPY_ALPHA = 0x04000000 + """Alpha channels are copied on ``cmsDoTransform()``""" + NODEFAULTRESOURCEDEF = 0x01000000 + + _GRIDPOINTS_1 = 1 << 16 + _GRIDPOINTS_2 = 2 << 16 + _GRIDPOINTS_4 = 4 << 16 + _GRIDPOINTS_8 = 8 << 16 + _GRIDPOINTS_16 = 16 << 16 + _GRIDPOINTS_32 = 32 << 16 + _GRIDPOINTS_64 = 64 << 16 + _GRIDPOINTS_128 = 128 << 16 + + @staticmethod + def GRIDPOINTS(n: int) -> Flags: + """ + Fine-tune control over number of gridpoints + + :param n: :py:class:`int` in range ``0 <= n <= 255`` + """ + return Flags.NONE | ((n & 0xFF) << 16) + + +_MAX_FLAG = reduce(operator.or_, 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 accuracy + "LOWRESPRECALC": 2048, # Use less memory to minimize resources + "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 +} + + +# --------------------------------------------------------------------. +# Experimental PIL-level API +# --------------------------------------------------------------------. + +## +# Profile. + + +class ImageCmsProfile: + def __init__(self, profile: str | SupportsRead[bytes] | core.CmsProfile) -> None: + """ + :param profile: Either a string representing a filename, + a file like object containing a profile or a + low-level profile object + + """ + self.filename: str | None = None + + if isinstance(profile, str): + if sys.platform == "win32": + profile_bytes_path = profile.encode() + try: + profile_bytes_path.decode("ascii") + except UnicodeDecodeError: + with open(profile, "rb") as f: + self.profile = core.profile_frombytes(f.read()) + return + self.filename = profile + self.profile = core.profile_open(profile) + elif hasattr(profile, "read"): + self.profile = core.profile_frombytes(profile.read()) + elif isinstance(profile, core.CmsProfile): + self.profile = profile + else: + msg = "Invalid type for Profile" # type: ignore[unreachable] + raise TypeError(msg) + + def __getattr__(self, name: str) -> Any: + if name in ("product_name", "product_info"): + deprecate(f"ImageCms.ImageCmsProfile.{name}", 13) + return None + msg = f"'{self.__class__.__name__}' object has no attribute '{name}'" + raise AttributeError(msg) + + def tobytes(self) -> bytes: + """ + 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 + :py:func:`~PIL.Image.Image.point` method. + + Will return the output profile in the ``output.info['icc_profile']``. + """ + + def __init__( + self, + input: ImageCmsProfile, + output: ImageCmsProfile, + input_mode: str, + output_mode: str, + intent: Intent = Intent.PERCEPTUAL, + proof: ImageCmsProfile | None = None, + proof_intent: Intent = Intent.ABSOLUTE_COLORIMETRIC, + flags: Flags = Flags.NONE, + ): + 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: Image.Image) -> Image.Image: + return self.apply(im) + + def apply(self, im: Image.Image, imOut: Image.Image | None = None) -> Image.Image: + if imOut is None: + imOut = Image.new(self.output_mode, im.size, None) + self.transform.apply(im.getim(), imOut.getim()) + imOut.info["icc_profile"] = self.output_profile.tobytes() + return imOut + + def apply_in_place(self, im: Image.Image) -> Image.Image: + if im.mode != self.output_mode: + msg = "mode mismatch" + raise ValueError(msg) # wrong output mode + self.transform.apply(im.getim(), im.getim()) + im.info["icc_profile"] = self.output_profile.tobytes() + return im + + +def get_display_profile(handle: SupportsInt | None = None) -> ImageCmsProfile | None: + """ + (experimental) Fetches the profile for the current display device. + + :returns: ``None`` if the profile is not known. + """ + + if sys.platform != "win32": + return None + + from . import ImageWin # type: ignore[unused-ignore, unreachable] + + if isinstance(handle, ImageWin.HDC): + profile = core.get_display_profile_win32(int(handle), 1) + else: + profile = core.get_display_profile_win32(int(handle or 0)) + if profile is None: + return None + 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: Image.Image, + inputProfile: _CmsProfileCompatible, + outputProfile: _CmsProfileCompatible, + renderingIntent: Intent = Intent.PERCEPTUAL, + outputMode: str | None = None, + inPlace: bool = False, + flags: Flags = Flags.NONE, +) -> Image.Image | None: + """ + (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 + :exc:`PyCMSError` will be raised. If ``inPlace`` is ``True`` and + ``outputMode != im.mode``, a :exc:`PyCMSError` will be raised. + If an error occurs during application of the profiles, + a :exc:`PyCMSError` will be raised. + If ``outputMode`` is not a mode supported by the ``outputProfile`` (or by pyCMS), + a :exc:`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 :py:class:`~PIL.Image.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 + + ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT) + ImageCms.Intent.RELATIVE_COLORIMETRIC = 1 + ImageCms.Intent.SATURATION = 2 + ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3 + + 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. If ``True``, the original image is modified in-place, + and ``None`` is returned. If ``False`` (default), a new + :py:class:`~PIL.Image.Image` object is returned with the transform applied. + :param flags: Integer (0-...) specifying additional flags + :returns: Either None or a new :py:class:`~PIL.Image.Image` object, depending on + the value of ``inPlace`` + :exception PyCMSError: + """ + + if outputMode is None: + outputMode = im.mode + + if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3): + msg = "renderingIntent must be an integer between 0 and 3" + raise PyCMSError(msg) + + if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): + msg = f"flags must be an integer between 0 and {_MAX_FLAG}" + raise PyCMSError(msg) + + 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 (OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + return imOut + + +def getOpenProfile( + profileFilename: str | SupportsRead[bytes] | core.CmsProfile, +) -> ImageCmsProfile: + """ + (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 valid filename for an ICC profile, + a :exc:`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 (OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def buildTransform( + inputProfile: _CmsProfileCompatible, + outputProfile: _CmsProfileCompatible, + inMode: str, + outMode: str, + renderingIntent: Intent = Intent.PERCEPTUAL, + flags: Flags = Flags.NONE, +) -> ImageCmsTransform: + """ + (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 + :exc:`PyCMSError` will be raised. If an error occurs during creation + of the transform, a :exc:`PyCMSError` will be raised. + + If ``inMode`` or ``outMode`` are not a mode supported by the ``outputProfile`` + (or by pyCMS), a :exc:`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 + + ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT) + ImageCms.Intent.RELATIVE_COLORIMETRIC = 1 + ImageCms.Intent.SATURATION = 2 + ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3 + + 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): + msg = "renderingIntent must be an integer between 0 and 3" + raise PyCMSError(msg) + + if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): + msg = f"flags must be an integer between 0 and {_MAX_FLAG}" + raise PyCMSError(msg) + + 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 (OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def buildProofTransform( + inputProfile: _CmsProfileCompatible, + outputProfile: _CmsProfileCompatible, + proofProfile: _CmsProfileCompatible, + inMode: str, + outMode: str, + renderingIntent: Intent = Intent.PERCEPTUAL, + proofRenderingIntent: Intent = Intent.ABSOLUTE_COLORIMETRIC, + flags: Flags = Flags.SOFTPROOFING, +) -> ImageCmsTransform: + """ + (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 :exc:`PyCMSError` will be raised. + + If an error occurs during creation of the transform, + a :exc:`PyCMSError` will be raised. + + If ``inMode`` or ``outMode`` are not a mode supported by the ``outputProfile`` + (or by pyCMS), a :exc:`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 + + ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT) + ImageCms.Intent.RELATIVE_COLORIMETRIC = 1 + ImageCms.Intent.SATURATION = 2 + ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3 + + 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 + + ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT) + ImageCms.Intent.RELATIVE_COLORIMETRIC = 1 + ImageCms.Intent.SATURATION = 2 + ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3 + + 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): + msg = "renderingIntent must be an integer between 0 and 3" + raise PyCMSError(msg) + + if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): + msg = f"flags must be an integer between 0 and {_MAX_FLAG}" + raise PyCMSError(msg) + + 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 (OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +buildTransformFromOpenProfiles = buildTransform +buildProofTransformFromOpenProfiles = buildProofTransform + + +def applyTransform( + im: Image.Image, transform: ImageCmsTransform, inPlace: bool = False +) -> Image.Image | None: + """ + (pyCMS) Applies a transform to a given image. + + If ``im.mode != transform.input_mode``, a :exc:`PyCMSError` is raised. + + If ``inPlace`` is ``True`` and ``transform.input_mode != transform.output_mode``, a + :exc:`PyCMSError` is raised. + + If ``im.mode``, ``transform.input_mode`` or ``transform.output_mode`` is not + supported by pyCMSdll or the profiles you used for the transform, a + :exc:`PyCMSError` is raised. + + If an error occurs while the transform is being applied, + a :exc:`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 calculation 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.input_mode`` and ``transform.output_mode`` 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 :py:class:`~PIL.Image.Image` + object of the same dimensions in mode ``transform.output_mode``. + + :param im: An :py:class:`~PIL.Image.Image` object, and ``im.mode`` must be the same + as the ``input_mode`` supported by the transform. + :param transform: A valid CmsTransform class object + :param inPlace: Bool. If ``True``, ``im`` is modified in place and ``None`` is + returned, if ``False``, a new :py:class:`~PIL.Image.Image` object with the + transform applied is returned (and ``im`` is not changed). The default is + ``False``. + :returns: Either ``None``, or a new :py:class:`~PIL.Image.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) from v + + return imOut + + +def createProfile( + colorSpace: Literal["LAB", "XYZ", "sRGB"], colorTemp: SupportsFloat = 0 +) -> core.CmsProfile: + """ + (pyCMS) Creates a profile. + + If colorSpace not in ``["LAB", "XYZ", "sRGB"]``, + a :exc:`PyCMSError` is raised. + + If using LAB and ``colorTemp`` is not a positive integer, + a :exc:`PyCMSError` is raised. + + If an error occurs while creating the profile, + a :exc:`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 number 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"]: + msg = ( + f"Color space not supported for on-the-fly profile creation ({colorSpace})" + ) + raise PyCMSError(msg) + + if colorSpace == "LAB": + try: + colorTemp = float(colorTemp) + except (TypeError, ValueError) as e: + msg = f'Color temperature must be numeric, "{colorTemp}" not valid' + raise PyCMSError(msg) from e + + try: + return core.createProfile(colorSpace, colorTemp) + except (TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def getProfileName(profile: _CmsProfileCompatible) -> str: + """ + + (pyCMS) Gets the internal product name for the given profile. + + If ``profile`` isn't a valid CmsProfile object or filename to a profile, + a :exc:`PyCMSError` is raised If an error occurs while trying + to obtain the name tag, a :exc:`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.model + manufacturer = profile.profile.manufacturer + + if not (model or manufacturer): + return (profile.profile.profile_description or "") + "\n" + if not manufacturer or (model and len(model) > 30): + return f"{model}\n" + return f"{model} - {manufacturer}\n" + + except (AttributeError, OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def getProfileInfo(profile: _CmsProfileCompatible) -> str: + """ + (pyCMS) Gets the internal product information for the given profile. + + If ``profile`` isn't a valid CmsProfile object or filename to a profile, + a :exc:`PyCMSError` is raised. + + If an error occurs while trying to obtain the info tag, + a :exc:`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.profile_description + cpright = profile.profile.copyright + elements = [element for element in (description, cpright) if element] + return "\r\n\r\n".join(elements) + "\r\n\r\n" + + except (AttributeError, OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def getProfileCopyright(profile: _CmsProfileCompatible) -> str: + """ + (pyCMS) Gets the copyright for the given profile. + + If ``profile`` isn't a valid CmsProfile object or filename to a profile, a + :exc:`PyCMSError` is raised. + + If an error occurs while trying to obtain the copyright tag, + a :exc:`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.copyright or "") + "\n" + except (AttributeError, OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def getProfileManufacturer(profile: _CmsProfileCompatible) -> str: + """ + (pyCMS) Gets the manufacturer for the given profile. + + If ``profile`` isn't a valid CmsProfile object or filename to a profile, a + :exc:`PyCMSError` is raised. + + If an error occurs while trying to obtain the manufacturer tag, a + :exc:`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.manufacturer or "") + "\n" + except (AttributeError, OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def getProfileModel(profile: _CmsProfileCompatible) -> str: + """ + (pyCMS) Gets the model for the given profile. + + If ``profile`` isn't a valid CmsProfile object or filename to a profile, a + :exc:`PyCMSError` is raised. + + If an error occurs while trying to obtain the model tag, + a :exc:`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.model or "") + "\n" + except (AttributeError, OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def getProfileDescription(profile: _CmsProfileCompatible) -> str: + """ + (pyCMS) Gets the description for the given profile. + + If ``profile`` isn't a valid CmsProfile object or filename to a profile, a + :exc:`PyCMSError` is raised. + + If an error occurs while trying to obtain the description tag, + a :exc:`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.profile_description or "") + "\n" + except (AttributeError, OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def getDefaultIntent(profile: _CmsProfileCompatible) -> int: + """ + (pyCMS) Gets the default intent name for the given profile. + + If ``profile`` isn't a valid CmsProfile object or filename to a profile, a + :exc:`PyCMSError` is raised. + + If an error occurs while trying to obtain the default intent, a + :exc:`PyCMSError` is raised. + + Use this function to determine the default (and usually best optimized) + 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. + + ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT) + ImageCms.Intent.RELATIVE_COLORIMETRIC = 1 + ImageCms.Intent.SATURATION = 2 + ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3 + + 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, OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v + + +def isIntentSupported( + profile: _CmsProfileCompatible, intent: Intent, direction: Direction +) -> Literal[-1, 1]: + """ + (pyCMS) Checks if a given intent is supported. + + Use this function to verify that you can use your desired + ``intent`` 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 :exc:`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 + + ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT) + ImageCms.Intent.RELATIVE_COLORIMETRIC = 1 + ImageCms.Intent.SATURATION = 2 + ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3 + + see the pyCMS documentation for details on rendering intents and what + they do. + :param direction: Integer specifying 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, OSError, TypeError, ValueError) as v: + raise PyCMSError(v) from v diff --git a/voxvera/vendor/PIL/ImageColor.py b/voxvera/vendor/PIL/ImageColor.py new file mode 100644 index 0000000..9a15a8e --- /dev/null +++ b/voxvera/vendor/PIL/ImageColor.py @@ -0,0 +1,320 @@ +# +# 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 __future__ import annotations + +import re +from functools import lru_cache + +from . import Image + + +@lru_cache +def getrgb(color: str) -> tuple[int, int, int] | tuple[int, int, int, int]: + """ + Convert a color string to an RGB or RGBA 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])`` + """ + if len(color) > 100: + msg = "color specifier is too long" + raise ValueError(msg) + color = color.lower() + + rgb = colormap.get(color, None) + if rgb: + if isinstance(rgb, tuple): + return rgb + rgb_tuple = getrgb(rgb) + assert len(rgb_tuple) == 3 + colormap[color] = rgb_tuple + return rgb_tuple + + # check for known string formats + if re.match("#[a-f0-9]{3}$", color): + return int(color[1] * 2, 16), int(color[2] * 2, 16), int(color[3] * 2, 16) + + if re.match("#[a-f0-9]{4}$", color): + return ( + int(color[1] * 2, 16), + int(color[2] * 2, 16), + int(color[3] * 2, 16), + int(color[4] * 2, 16), + ) + + if re.match("#[a-f0-9]{6}$", color): + return int(color[1:3], 16), int(color[3:5], 16), int(color[5:7], 16) + + if re.match("#[a-f0-9]{8}$", color): + return ( + int(color[1:3], 16), + int(color[3:5], 16), + int(color[5:7], 16), + int(color[7:9], 16), + ) + + m = re.match(r"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(r"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( + r"hsl\(\s*(\d+\.?\d*)\s*,\s*(\d+\.?\d*)%\s*,\s*(\d+\.?\d*)%\s*\)$", color + ) + if m: + from colorsys import hls_to_rgb + + rgb_floats = hls_to_rgb( + float(m.group(1)) / 360.0, + float(m.group(3)) / 100.0, + float(m.group(2)) / 100.0, + ) + return ( + int(rgb_floats[0] * 255 + 0.5), + int(rgb_floats[1] * 255 + 0.5), + int(rgb_floats[2] * 255 + 0.5), + ) + + m = re.match( + r"hs[bv]\(\s*(\d+\.?\d*)\s*,\s*(\d+\.?\d*)%\s*,\s*(\d+\.?\d*)%\s*\)$", color + ) + if m: + from colorsys import hsv_to_rgb + + rgb_floats = hsv_to_rgb( + float(m.group(1)) / 360.0, + float(m.group(2)) / 100.0, + float(m.group(3)) / 100.0, + ) + return ( + int(rgb_floats[0] * 255 + 0.5), + int(rgb_floats[1] * 255 + 0.5), + int(rgb_floats[2] * 255 + 0.5), + ) + + m = re.match(r"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)) + msg = f"unknown color specifier: {repr(color)}" + raise ValueError(msg) + + +@lru_cache +def getcolor(color: str, mode: str) -> int | tuple[int, ...]: + """ + Same as :py:func:`~PIL.ImageColor.getrgb` for most modes. However, if + ``mode`` is HSV, converts the RGB value to a HSV value, or if ``mode`` is + not color or a palette image, converts the RGB value to a grayscale value. + If the string cannot be parsed, this function raises a :py:exc:`ValueError` + exception. + + .. versionadded:: 1.1.4 + + :param color: A color string + :param mode: Convert result to this mode + :return: ``graylevel, (graylevel, alpha) or (red, green, blue[, alpha])`` + """ + # same as getrgb, but converts the result to the given mode + rgb, alpha = getrgb(color), 255 + if len(rgb) == 4: + alpha = rgb[3] + rgb = rgb[:3] + + if mode == "HSV": + from colorsys import rgb_to_hsv + + r, g, b = rgb + h, s, v = rgb_to_hsv(r / 255, g / 255, b / 255) + return int(h * 255), int(s * 255), int(v * 255) + elif Image.getmodebase(mode) == "L": + r, g, b = rgb + # ITU-R Recommendation 601-2 for nonlinear RGB + # scaled to 24 bits to match the convert's implementation. + graylevel = (r * 19595 + g * 38470 + b * 7471 + 0x8000) >> 16 + if mode[-1] == "A": + return graylevel, alpha + return graylevel + elif mode[-1] == "A": + return rgb + (alpha,) + return rgb + + +colormap: dict[str, str | tuple[int, int, int]] = { + # X11 colour table from https://drafts.csswg.org/css-color-4/, 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", + "rebeccapurple": "#663399", + "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/voxvera/vendor/PIL/ImageDraw.py b/voxvera/vendor/PIL/ImageDraw.py new file mode 100644 index 0000000..8bcf2d8 --- /dev/null +++ b/voxvera/vendor/PIL/ImageDraw.py @@ -0,0 +1,1036 @@ +# +# 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. +# +from __future__ import annotations + +import math +import struct +from collections.abc import Sequence +from typing import cast + +from . import Image, ImageColor, ImageText + +TYPE_CHECKING = False +if TYPE_CHECKING: + from collections.abc import Callable + from types import ModuleType + from typing import Any, AnyStr + + from . import ImageDraw2, ImageFont + from ._typing import Coords, _Ink + +# experimental access to the outline API +Outline: Callable[[], Image.core._Outline] = Image.core.outline + +""" +A simple 2D drawing interface for PIL images. +

+Application code should use the Draw factory, instead of +directly. +""" + + +class ImageDraw: + font: ( + ImageFont.ImageFont | ImageFont.FreeTypeFont | ImageFont.TransposedFont | None + ) = None + + def __init__(self, im: Image.Image, mode: str | None = None) -> None: + """ + 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. + """ + im._ensure_mutable() + blend = 0 + if mode is None: + mode = im.mode + if mode != im.mode: + if mode == "RGBA" and im.mode == "RGB": + blend = 1 + else: + msg = "mode mismatch" + raise ValueError(msg) + if mode == "P": + self.palette = im.palette + else: + self.palette = None + self._image = im + 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) + else: + self.ink = self.draw.draw_ink(-1) + 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 = False + + def getfont( + self, + ) -> ImageFont.ImageFont | ImageFont.FreeTypeFont | ImageFont.TransposedFont: + """ + Get the current default font. + + To set the default font for this ImageDraw instance:: + + from PIL import ImageDraw, ImageFont + draw.font = ImageFont.truetype("Tests/fonts/FreeMono.ttf") + + To set the default font for all future ImageDraw instances:: + + from PIL import ImageDraw, ImageFont + ImageDraw.ImageDraw.font = ImageFont.truetype("Tests/fonts/FreeMono.ttf") + + If the current default font is ``None``, + it is initialized with ``ImageFont.load_default()``. + + :returns: An image font.""" + if not self.font: + # FIXME: should add a font repository + from . import ImageFont + + self.font = ImageFont.load_default() + return self.font + + def _getfont( + self, font_size: float | None + ) -> ImageFont.ImageFont | ImageFont.FreeTypeFont | ImageFont.TransposedFont: + if font_size is not None: + from . import ImageFont + + return ImageFont.load_default(font_size) + else: + return self.getfont() + + def _getink( + self, ink: _Ink | None, fill: _Ink | None = None + ) -> tuple[int | None, int | None]: + result_ink = None + result_fill = None + if ink is None and fill is None: + if self.fill: + result_fill = self.ink + else: + result_ink = self.ink + else: + if ink is not None: + if isinstance(ink, str): + ink = ImageColor.getcolor(ink, self.mode) + if self.palette and isinstance(ink, tuple): + ink = self.palette.getcolor(ink, self._image) + result_ink = self.draw.draw_ink(ink) + if fill is not None: + if isinstance(fill, str): + fill = ImageColor.getcolor(fill, self.mode) + if self.palette and isinstance(fill, tuple): + fill = self.palette.getcolor(fill, self._image) + result_fill = self.draw.draw_ink(fill) + return result_ink, result_fill + + def arc( + self, + xy: Coords, + start: float, + end: float, + fill: _Ink | None = None, + width: int = 1, + ) -> None: + """Draw an arc.""" + ink, fill = self._getink(fill) + if ink is not None: + self.draw.draw_arc(xy, start, end, ink, width) + + def bitmap( + self, xy: Sequence[int], bitmap: Image.Image, fill: _Ink | None = None + ) -> None: + """Draw a bitmap.""" + 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) + + def chord( + self, + xy: Coords, + start: float, + end: float, + fill: _Ink | None = None, + outline: _Ink | None = None, + width: int = 1, + ) -> None: + """Draw a chord.""" + ink, fill_ink = self._getink(outline, fill) + if fill_ink is not None: + self.draw.draw_chord(xy, start, end, fill_ink, 1) + if ink is not None and ink != fill_ink and width != 0: + self.draw.draw_chord(xy, start, end, ink, 0, width) + + def ellipse( + self, + xy: Coords, + fill: _Ink | None = None, + outline: _Ink | None = None, + width: int = 1, + ) -> None: + """Draw an ellipse.""" + ink, fill_ink = self._getink(outline, fill) + if fill_ink is not None: + self.draw.draw_ellipse(xy, fill_ink, 1) + if ink is not None and ink != fill_ink and width != 0: + self.draw.draw_ellipse(xy, ink, 0, width) + + def circle( + self, + xy: Sequence[float], + radius: float, + fill: _Ink | None = None, + outline: _Ink | None = None, + width: int = 1, + ) -> None: + """Draw a circle given center coordinates and a radius.""" + ellipse_xy = (xy[0] - radius, xy[1] - radius, xy[0] + radius, xy[1] + radius) + self.ellipse(ellipse_xy, fill, outline, width) + + def line( + self, + xy: Coords, + fill: _Ink | None = None, + width: int = 0, + joint: str | None = None, + ) -> None: + """Draw a line, or a connected sequence of line segments.""" + ink = self._getink(fill)[0] + if ink is not None: + self.draw.draw_lines(xy, ink, width) + if joint == "curve" and width > 4: + points: Sequence[Sequence[float]] + if isinstance(xy[0], (list, tuple)): + points = cast(Sequence[Sequence[float]], xy) + else: + points = [ + cast(Sequence[float], tuple(xy[i : i + 2])) + for i in range(0, len(xy), 2) + ] + for i in range(1, len(points) - 1): + point = points[i] + angles = [ + math.degrees(math.atan2(end[0] - start[0], start[1] - end[1])) + % 360 + for start, end in ( + (points[i - 1], point), + (point, points[i + 1]), + ) + ] + if angles[0] == angles[1]: + # This is a straight line, so no joint is required + continue + + def coord_at_angle( + coord: Sequence[float], angle: float + ) -> tuple[float, ...]: + x, y = coord + angle -= 90 + distance = width / 2 - 1 + return tuple( + p + (math.floor(p_d) if p_d > 0 else math.ceil(p_d)) + for p, p_d in ( + (x, distance * math.cos(math.radians(angle))), + (y, distance * math.sin(math.radians(angle))), + ) + ) + + flipped = ( + angles[1] > angles[0] and angles[1] - 180 > angles[0] + ) or (angles[1] < angles[0] and angles[1] + 180 > angles[0]) + coords = [ + (point[0] - width / 2 + 1, point[1] - width / 2 + 1), + (point[0] + width / 2 - 1, point[1] + width / 2 - 1), + ] + if flipped: + start, end = (angles[1] + 90, angles[0] + 90) + else: + start, end = (angles[0] - 90, angles[1] - 90) + self.pieslice(coords, start - 90, end - 90, fill) + + if width > 8: + # Cover potential gaps between the line and the joint + if flipped: + gap_coords = [ + coord_at_angle(point, angles[0] + 90), + point, + coord_at_angle(point, angles[1] + 90), + ] + else: + gap_coords = [ + coord_at_angle(point, angles[0] - 90), + point, + coord_at_angle(point, angles[1] - 90), + ] + self.line(gap_coords, fill, width=3) + + def shape( + self, + shape: Image.core._Outline, + fill: _Ink | None = None, + outline: _Ink | None = None, + ) -> None: + """(Experimental) Draw a shape.""" + shape.close() + ink, fill_ink = self._getink(outline, fill) + if fill_ink is not None: + self.draw.draw_outline(shape, fill_ink, 1) + if ink is not None and ink != fill_ink: + self.draw.draw_outline(shape, ink, 0) + + def pieslice( + self, + xy: Coords, + start: float, + end: float, + fill: _Ink | None = None, + outline: _Ink | None = None, + width: int = 1, + ) -> None: + """Draw a pieslice.""" + ink, fill_ink = self._getink(outline, fill) + if fill_ink is not None: + self.draw.draw_pieslice(xy, start, end, fill_ink, 1) + if ink is not None and ink != fill_ink and width != 0: + self.draw.draw_pieslice(xy, start, end, ink, 0, width) + + def point(self, xy: Coords, fill: _Ink | None = None) -> None: + """Draw one or more individual pixels.""" + ink, fill = self._getink(fill) + if ink is not None: + self.draw.draw_points(xy, ink) + + def polygon( + self, + xy: Coords, + fill: _Ink | None = None, + outline: _Ink | None = None, + width: int = 1, + ) -> None: + """Draw a polygon.""" + ink, fill_ink = self._getink(outline, fill) + if fill_ink is not None: + self.draw.draw_polygon(xy, fill_ink, 1) + if ink is not None and ink != fill_ink and width != 0: + if width == 1: + self.draw.draw_polygon(xy, ink, 0, width) + elif self.im is not None: + # To avoid expanding the polygon outwards, + # use the fill as a mask + mask = Image.new("1", self.im.size) + mask_ink = self._getink(1)[0] + draw = Draw(mask) + draw.draw.draw_polygon(xy, mask_ink, 1) + + self.draw.draw_polygon(xy, ink, 0, width * 2 - 1, mask.im) + + def regular_polygon( + self, + bounding_circle: Sequence[Sequence[float] | float], + n_sides: int, + rotation: float = 0, + fill: _Ink | None = None, + outline: _Ink | None = None, + width: int = 1, + ) -> None: + """Draw a regular polygon.""" + xy = _compute_regular_polygon_vertices(bounding_circle, n_sides, rotation) + self.polygon(xy, fill, outline, width) + + def rectangle( + self, + xy: Coords, + fill: _Ink | None = None, + outline: _Ink | None = None, + width: int = 1, + ) -> None: + """Draw a rectangle.""" + ink, fill_ink = self._getink(outline, fill) + if fill_ink is not None: + self.draw.draw_rectangle(xy, fill_ink, 1) + if ink is not None and ink != fill_ink and width != 0: + self.draw.draw_rectangle(xy, ink, 0, width) + + def rounded_rectangle( + self, + xy: Coords, + radius: float = 0, + fill: _Ink | None = None, + outline: _Ink | None = None, + width: int = 1, + *, + corners: tuple[bool, bool, bool, bool] | None = None, + ) -> None: + """Draw a rounded rectangle.""" + if isinstance(xy[0], (list, tuple)): + (x0, y0), (x1, y1) = cast(Sequence[Sequence[float]], xy) + else: + x0, y0, x1, y1 = cast(Sequence[float], xy) + if x1 < x0: + msg = "x1 must be greater than or equal to x0" + raise ValueError(msg) + if y1 < y0: + msg = "y1 must be greater than or equal to y0" + raise ValueError(msg) + if corners is None: + corners = (True, True, True, True) + + d = radius * 2 + + x0 = round(x0) + y0 = round(y0) + x1 = round(x1) + y1 = round(y1) + full_x, full_y = False, False + if all(corners): + full_x = d >= x1 - x0 - 1 + if full_x: + # The two left and two right corners are joined + d = x1 - x0 + full_y = d >= y1 - y0 - 1 + if full_y: + # The two top and two bottom corners are joined + d = y1 - y0 + if full_x and full_y: + # If all corners are joined, that is a circle + return self.ellipse(xy, fill, outline, width) + + if d == 0 or not any(corners): + # If the corners have no curve, + # or there are no corners, + # that is a rectangle + return self.rectangle(xy, fill, outline, width) + + r = int(d // 2) + ink, fill_ink = self._getink(outline, fill) + + def draw_corners(pieslice: bool) -> None: + parts: tuple[tuple[tuple[float, float, float, float], int, int], ...] + if full_x: + # Draw top and bottom halves + parts = ( + ((x0, y0, x0 + d, y0 + d), 180, 360), + ((x0, y1 - d, x0 + d, y1), 0, 180), + ) + elif full_y: + # Draw left and right halves + parts = ( + ((x0, y0, x0 + d, y0 + d), 90, 270), + ((x1 - d, y0, x1, y0 + d), 270, 90), + ) + else: + # Draw four separate corners + parts = tuple( + part + for i, part in enumerate( + ( + ((x0, y0, x0 + d, y0 + d), 180, 270), + ((x1 - d, y0, x1, y0 + d), 270, 360), + ((x1 - d, y1 - d, x1, y1), 0, 90), + ((x0, y1 - d, x0 + d, y1), 90, 180), + ) + ) + if corners[i] + ) + for part in parts: + if pieslice: + self.draw.draw_pieslice(*(part + (fill_ink, 1))) + else: + self.draw.draw_arc(*(part + (ink, width))) + + if fill_ink is not None: + draw_corners(True) + + if full_x: + self.draw.draw_rectangle((x0, y0 + r + 1, x1, y1 - r - 1), fill_ink, 1) + elif x1 - r - 1 > x0 + r + 1: + self.draw.draw_rectangle((x0 + r + 1, y0, x1 - r - 1, y1), fill_ink, 1) + if not full_x and not full_y: + left = [x0, y0, x0 + r, y1] + if corners[0]: + left[1] += r + 1 + if corners[3]: + left[3] -= r + 1 + self.draw.draw_rectangle(left, fill_ink, 1) + + right = [x1 - r, y0, x1, y1] + if corners[1]: + right[1] += r + 1 + if corners[2]: + right[3] -= r + 1 + self.draw.draw_rectangle(right, fill_ink, 1) + if ink is not None and ink != fill_ink and width != 0: + draw_corners(False) + + if not full_x: + top = [x0, y0, x1, y0 + width - 1] + if corners[0]: + top[0] += r + 1 + if corners[1]: + top[2] -= r + 1 + self.draw.draw_rectangle(top, ink, 1) + + bottom = [x0, y1 - width + 1, x1, y1] + if corners[3]: + bottom[0] += r + 1 + if corners[2]: + bottom[2] -= r + 1 + self.draw.draw_rectangle(bottom, ink, 1) + if not full_y: + left = [x0, y0, x0 + width - 1, y1] + if corners[0]: + left[1] += r + 1 + if corners[3]: + left[3] -= r + 1 + self.draw.draw_rectangle(left, ink, 1) + + right = [x1 - width + 1, y0, x1, y1] + if corners[1]: + right[1] += r + 1 + if corners[2]: + right[3] -= r + 1 + self.draw.draw_rectangle(right, ink, 1) + + def text( + self, + xy: tuple[float, float], + text: AnyStr | ImageText.Text, + fill: _Ink | None = None, + font: ( + ImageFont.ImageFont + | ImageFont.FreeTypeFont + | ImageFont.TransposedFont + | None + ) = None, + anchor: str | None = None, + spacing: float = 4, + align: str = "left", + direction: str | None = None, + features: list[str] | None = None, + language: str | None = None, + stroke_width: float = 0, + stroke_fill: _Ink | None = None, + embedded_color: bool = False, + *args: Any, + **kwargs: Any, + ) -> None: + """Draw text.""" + if isinstance(text, ImageText.Text): + image_text = text + else: + if font is None: + font = self._getfont(kwargs.get("font_size")) + image_text = ImageText.Text( + text, font, self.mode, spacing, direction, features, language + ) + if embedded_color: + image_text.embed_color() + if stroke_width: + image_text.stroke(stroke_width, stroke_fill) + + def getink(fill: _Ink | None) -> int: + ink, fill_ink = self._getink(fill) + if ink is None: + assert fill_ink is not None + return fill_ink + return ink + + ink = getink(fill) + if ink is None: + return + + stroke_ink = None + if image_text.stroke_width: + stroke_ink = ( + getink(image_text.stroke_fill) + if image_text.stroke_fill is not None + else ink + ) + + for xy, anchor, line in image_text._split(xy, anchor, align): + + def draw_text(ink: int, stroke_width: float = 0) -> None: + mode = self.fontmode + if stroke_width == 0 and embedded_color: + mode = "RGBA" + coord = [] + for i in range(2): + coord.append(int(xy[i])) + start = (math.modf(xy[0])[0], math.modf(xy[1])[0]) + try: + mask, offset = image_text.font.getmask2( # type: ignore[union-attr,misc] + line, + mode, + direction=direction, + features=features, + language=language, + stroke_width=stroke_width, + stroke_filled=True, + anchor=anchor, + ink=ink, + start=start, + *args, + **kwargs, + ) + coord = [coord[0] + offset[0], coord[1] + offset[1]] + except AttributeError: + try: + mask = image_text.font.getmask( # type: ignore[misc] + line, + mode, + direction, + features, + language, + stroke_width, + anchor, + ink, + start=start, + *args, + **kwargs, + ) + except TypeError: + mask = image_text.font.getmask(line) + if mode == "RGBA": + # image_text.font.getmask2(mode="RGBA") + # returns color in RGB bands and mask in A + # extract mask and set text alpha + color, mask = mask, mask.getband(3) + ink_alpha = struct.pack("i", ink)[3] + color.fillband(3, ink_alpha) + x, y = coord + if self.im is not None: + self.im.paste( + color, (x, y, x + mask.size[0], y + mask.size[1]), mask + ) + else: + self.draw.draw_bitmap(coord, mask, ink) + + if stroke_ink is not None: + # Draw stroked text + draw_text(stroke_ink, image_text.stroke_width) + + # Draw normal text + if ink != stroke_ink: + draw_text(ink) + else: + # Only draw normal text + draw_text(ink) + + def multiline_text( + self, + xy: tuple[float, float], + text: AnyStr, + fill: _Ink | None = None, + font: ( + ImageFont.ImageFont + | ImageFont.FreeTypeFont + | ImageFont.TransposedFont + | None + ) = None, + anchor: str | None = None, + spacing: float = 4, + align: str = "left", + direction: str | None = None, + features: list[str] | None = None, + language: str | None = None, + stroke_width: float = 0, + stroke_fill: _Ink | None = None, + embedded_color: bool = False, + *, + font_size: float | None = None, + ) -> None: + return self.text( + xy, + text, + fill, + font, + anchor, + spacing, + align, + direction, + features, + language, + stroke_width, + stroke_fill, + embedded_color, + font_size=font_size, + ) + + def textlength( + self, + text: AnyStr, + font: ( + ImageFont.ImageFont + | ImageFont.FreeTypeFont + | ImageFont.TransposedFont + | None + ) = None, + direction: str | None = None, + features: list[str] | None = None, + language: str | None = None, + embedded_color: bool = False, + *, + font_size: float | None = None, + ) -> float: + """Get the length of a given string, in pixels with 1/64 precision.""" + if font is None: + font = self._getfont(font_size) + image_text = ImageText.Text( + text, + font, + self.mode, + direction=direction, + features=features, + language=language, + ) + if embedded_color: + image_text.embed_color() + return image_text.get_length() + + def textbbox( + self, + xy: tuple[float, float], + text: AnyStr, + font: ( + ImageFont.ImageFont + | ImageFont.FreeTypeFont + | ImageFont.TransposedFont + | None + ) = None, + anchor: str | None = None, + spacing: float = 4, + align: str = "left", + direction: str | None = None, + features: list[str] | None = None, + language: str | None = None, + stroke_width: float = 0, + embedded_color: bool = False, + *, + font_size: float | None = None, + ) -> tuple[float, float, float, float]: + """Get the bounding box of a given string, in pixels.""" + if font is None: + font = self._getfont(font_size) + image_text = ImageText.Text( + text, font, self.mode, spacing, direction, features, language + ) + if embedded_color: + image_text.embed_color() + if stroke_width: + image_text.stroke(stroke_width) + return image_text.get_bbox(xy, anchor, align) + + def multiline_textbbox( + self, + xy: tuple[float, float], + text: AnyStr, + font: ( + ImageFont.ImageFont + | ImageFont.FreeTypeFont + | ImageFont.TransposedFont + | None + ) = None, + anchor: str | None = None, + spacing: float = 4, + align: str = "left", + direction: str | None = None, + features: list[str] | None = None, + language: str | None = None, + stroke_width: float = 0, + embedded_color: bool = False, + *, + font_size: float | None = None, + ) -> tuple[float, float, float, float]: + return self.textbbox( + xy, + text, + font, + anchor, + spacing, + align, + direction, + features, + language, + stroke_width, + embedded_color, + font_size=font_size, + ) + + +def Draw(im: Image.Image, mode: str | None = None) -> ImageDraw: + """ + 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. + """ + try: + return getattr(im, "getdraw")(mode) + except AttributeError: + return ImageDraw(im, mode) + + +def getdraw(im: Image.Image | None = None) -> tuple[ImageDraw2.Draw | None, ModuleType]: + """ + :param im: The image to draw in. + :returns: A (drawing context, drawing resource factory) tuple. + """ + from . import ImageDraw2 + + draw = ImageDraw2.Draw(im) if im is not None else None + return draw, ImageDraw2 + + +def floodfill( + image: Image.Image, + xy: tuple[int, int], + value: float | tuple[int, ...], + border: float | tuple[int, ...] | None = None, + thresh: float = 0, +) -> None: + """ + .. warning:: This method is experimental. + + Fills a bounded region with a given color. + + :param image: Target image. + :param xy: Seed position (a 2-item coordinate tuple). See + :ref:`coordinate-system`. + :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. + :param thresh: Optional threshold value which specifies a maximum + tolerable difference of a pixel value from the 'background' in + order for it to be replaced. Useful for filling regions of + non-homogeneous, but similar, colors. + """ + # based on an implementation by Eric S. Raymond + # amended by yo1995 @20180806 + pixel = image.load() + assert pixel is not None + x, y = xy + try: + background = pixel[x, y] + if _color_diff(value, background) <= thresh: + return # seed point already has fill color + pixel[x, y] = value + except (ValueError, IndexError): + return # seed point outside image + edge = {(x, y)} + # use a set to keep record of current and previous edge pixels + # to reduce memory consumption + full_edge = set() + while edge: + new_edge = set() + for x, y in edge: # 4 adjacent method + for s, t in ((x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)): + # If already processed, or if a coordinate is negative, skip + if (s, t) in full_edge or s < 0 or t < 0: + continue + try: + p = pixel[s, t] + except (ValueError, IndexError): + pass + else: + full_edge.add((s, t)) + if border is None: + fill = _color_diff(p, background) <= thresh + else: + fill = p not in (value, border) + if fill: + pixel[s, t] = value + new_edge.add((s, t)) + full_edge = edge # discard pixels processed + edge = new_edge + + +def _compute_regular_polygon_vertices( + bounding_circle: Sequence[Sequence[float] | float], n_sides: int, rotation: float +) -> list[tuple[float, float]]: + """ + Generate a list of vertices for a 2D regular polygon. + + :param bounding_circle: The bounding circle is a sequence defined + by a point and radius. The polygon is inscribed in this circle. + (e.g. ``bounding_circle=(x, y, r)`` or ``((x, y), r)``) + :param n_sides: Number of sides + (e.g. ``n_sides=3`` for a triangle, ``6`` for a hexagon) + :param rotation: Apply an arbitrary rotation to the polygon + (e.g. ``rotation=90``, applies a 90 degree rotation) + :return: List of regular polygon vertices + (e.g. ``[(25, 50), (50, 50), (50, 25), (25, 25)]``) + + How are the vertices computed? + 1. Compute the following variables + - theta: Angle between the apothem & the nearest polygon vertex + - side_length: Length of each polygon edge + - centroid: Center of bounding circle (1st, 2nd elements of bounding_circle) + - polygon_radius: Polygon radius (last element of bounding_circle) + - angles: Location of each polygon vertex in polar grid + (e.g. A square with 0 degree rotation => [225.0, 315.0, 45.0, 135.0]) + + 2. For each angle in angles, get the polygon vertex at that angle + The vertex is computed using the equation below. + X= xcos(φ) + ysin(φ) + Y= −xsin(φ) + ycos(φ) + + Note: + φ = angle in degrees + x = 0 + y = polygon_radius + + The formula above assumes rotation around the origin. + In our case, we are rotating around the centroid. + To account for this, we use the formula below + X = xcos(φ) + ysin(φ) + centroid_x + Y = −xsin(φ) + ycos(φ) + centroid_y + """ + # 1. Error Handling + # 1.1 Check `n_sides` has an appropriate value + if not isinstance(n_sides, int): + msg = "n_sides should be an int" # type: ignore[unreachable] + raise TypeError(msg) + if n_sides < 3: + msg = "n_sides should be an int > 2" + raise ValueError(msg) + + # 1.2 Check `bounding_circle` has an appropriate value + if not isinstance(bounding_circle, (list, tuple)): + msg = "bounding_circle should be a sequence" + raise TypeError(msg) + + if len(bounding_circle) == 3: + if not all(isinstance(i, (int, float)) for i in bounding_circle): + msg = "bounding_circle should only contain numeric data" + raise ValueError(msg) + + *centroid, polygon_radius = cast(list[float], list(bounding_circle)) + elif len(bounding_circle) == 2 and isinstance(bounding_circle[0], (list, tuple)): + if not all( + isinstance(i, (int, float)) for i in bounding_circle[0] + ) or not isinstance(bounding_circle[1], (int, float)): + msg = "bounding_circle should only contain numeric data" + raise ValueError(msg) + + if len(bounding_circle[0]) != 2: + msg = "bounding_circle centre should contain 2D coordinates (e.g. (x, y))" + raise ValueError(msg) + + centroid = cast(list[float], list(bounding_circle[0])) + polygon_radius = cast(float, bounding_circle[1]) + else: + msg = ( + "bounding_circle should contain 2D coordinates " + "and a radius (e.g. (x, y, r) or ((x, y), r) )" + ) + raise ValueError(msg) + + if polygon_radius <= 0: + msg = "bounding_circle radius should be > 0" + raise ValueError(msg) + + # 1.3 Check `rotation` has an appropriate value + if not isinstance(rotation, (int, float)): + msg = "rotation should be an int or float" # type: ignore[unreachable] + raise ValueError(msg) + + # 2. Define Helper Functions + def _apply_rotation(point: list[float], degrees: float) -> tuple[float, float]: + return ( + round( + point[0] * math.cos(math.radians(360 - degrees)) + - point[1] * math.sin(math.radians(360 - degrees)) + + centroid[0], + 2, + ), + round( + point[1] * math.cos(math.radians(360 - degrees)) + + point[0] * math.sin(math.radians(360 - degrees)) + + centroid[1], + 2, + ), + ) + + def _compute_polygon_vertex(angle: float) -> tuple[float, float]: + start_point = [polygon_radius, 0] + return _apply_rotation(start_point, angle) + + def _get_angles(n_sides: int, rotation: float) -> list[float]: + angles = [] + degrees = 360 / n_sides + # Start with the bottom left polygon vertex + current_angle = (270 - 0.5 * degrees) + rotation + for _ in range(n_sides): + angles.append(current_angle) + current_angle += degrees + if current_angle > 360: + current_angle -= 360 + return angles + + # 3. Variable Declarations + angles = _get_angles(n_sides, rotation) + + # 4. Compute Vertices + return [_compute_polygon_vertex(angle) for angle in angles] + + +def _color_diff( + color1: float | tuple[int, ...], color2: float | tuple[int, ...] +) -> float: + """ + Uses 1-norm distance to calculate difference between two values. + """ + first = color1 if isinstance(color1, tuple) else (color1,) + second = color2 if isinstance(color2, tuple) else (color2,) + + return sum(abs(first[i] - second[i]) for i in range(len(second))) diff --git a/voxvera/vendor/PIL/ImageDraw2.py b/voxvera/vendor/PIL/ImageDraw2.py new file mode 100644 index 0000000..3d68658 --- /dev/null +++ b/voxvera/vendor/PIL/ImageDraw2.py @@ -0,0 +1,243 @@ +# +# 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. +# + + +""" +(Experimental) WCK-style drawing interface operations + +.. seealso:: :py:mod:`PIL.ImageDraw` +""" +from __future__ import annotations + +from typing import Any, AnyStr, BinaryIO + +from . import Image, ImageColor, ImageDraw, ImageFont, ImagePath +from ._typing import Coords, StrOrBytesPath + + +class Pen: + """Stores an outline color and width.""" + + def __init__(self, color: str, width: int = 1, opacity: int = 255) -> None: + self.color = ImageColor.getrgb(color) + self.width = width + + +class Brush: + """Stores a fill color""" + + def __init__(self, color: str, opacity: int = 255) -> None: + self.color = ImageColor.getrgb(color) + + +class Font: + """Stores a TrueType font and color""" + + def __init__( + self, color: str, file: StrOrBytesPath | BinaryIO, size: float = 12 + ) -> None: + # FIXME: add support for bitmap fonts + self.color = ImageColor.getrgb(color) + self.font = ImageFont.truetype(file, size) + + +class Draw: + """ + (Experimental) WCK-style drawing interface + """ + + def __init__( + self, + image: Image.Image | str, + size: tuple[int, int] | list[int] | None = None, + color: float | tuple[float, ...] | str | None = None, + ) -> None: + if isinstance(image, str): + if size is None: + msg = "If image argument is mode string, size must be a list or tuple" + raise ValueError(msg) + image = Image.new(image, size, color) + self.draw = ImageDraw.Draw(image) + self.image = image + self.transform: tuple[float, float, float, float, float, float] | None = None + + def flush(self) -> Image.Image: + return self.image + + def render( + self, + op: str, + xy: Coords, + pen: Pen | Brush | None, + brush: Brush | Pen | None = None, + **kwargs: Any, + ) -> 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: + path = ImagePath.Path(xy) + path.transform(self.transform) + xy = path + # render the item + if op in ("arc", "line"): + kwargs.setdefault("fill", outline) + else: + kwargs.setdefault("fill", fill) + kwargs.setdefault("outline", outline) + if op == "line": + kwargs.setdefault("width", width) + getattr(self.draw, op)(xy, **kwargs) + + def settransform(self, offset: tuple[float, float]) -> None: + """Sets a transformation offset.""" + (xoffset, yoffset) = offset + self.transform = (1, 0, xoffset, 0, 1, yoffset) + + def arc( + self, + xy: Coords, + pen: Pen | Brush | None, + start: float, + end: float, + *options: Any, + ) -> None: + """ + Draws an arc (a portion of a circle outline) between the start and end + angles, inside the given bounding box. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.arc` + """ + self.render("arc", xy, pen, *options, start=start, end=end) + + def chord( + self, + xy: Coords, + pen: Pen | Brush | None, + start: float, + end: float, + *options: Any, + ) -> None: + """ + Same as :py:meth:`~PIL.ImageDraw2.Draw.arc`, but connects the end points + with a straight line. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.chord` + """ + self.render("chord", xy, pen, *options, start=start, end=end) + + def ellipse(self, xy: Coords, pen: Pen | Brush | None, *options: Any) -> None: + """ + Draws an ellipse inside the given bounding box. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.ellipse` + """ + self.render("ellipse", xy, pen, *options) + + def line(self, xy: Coords, pen: Pen | Brush | None, *options: Any) -> None: + """ + Draws a line between the coordinates in the ``xy`` list. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.line` + """ + self.render("line", xy, pen, *options) + + def pieslice( + self, + xy: Coords, + pen: Pen | Brush | None, + start: float, + end: float, + *options: Any, + ) -> None: + """ + Same as arc, but also draws straight lines between the end points and the + center of the bounding box. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.pieslice` + """ + self.render("pieslice", xy, pen, *options, start=start, end=end) + + def polygon(self, xy: Coords, pen: Pen | Brush | None, *options: Any) -> None: + """ + Draws a polygon. + + The polygon outline consists of straight lines between the given + coordinates, plus a straight line between the last and the first + coordinate. + + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.polygon` + """ + self.render("polygon", xy, pen, *options) + + def rectangle(self, xy: Coords, pen: Pen | Brush | None, *options: Any) -> None: + """ + Draws a rectangle. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.rectangle` + """ + self.render("rectangle", xy, pen, *options) + + def text(self, xy: tuple[float, float], text: AnyStr, font: Font) -> None: + """ + Draws the string at the given position. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.text` + """ + if self.transform: + path = ImagePath.Path(xy) + path.transform(self.transform) + xy = path + self.draw.text(xy, text, font=font.font, fill=font.color) + + def textbbox( + self, xy: tuple[float, float], text: AnyStr, font: Font + ) -> tuple[float, float, float, float]: + """ + Returns bounding box (in pixels) of given text. + + :return: ``(left, top, right, bottom)`` bounding box + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.textbbox` + """ + if self.transform: + path = ImagePath.Path(xy) + path.transform(self.transform) + xy = path + return self.draw.textbbox(xy, text, font=font.font) + + def textlength(self, text: AnyStr, font: Font) -> float: + """ + Returns length (in pixels) of given text. + This is the amount by which following text should be offset. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.textlength` + """ + return self.draw.textlength(text, font=font.font) diff --git a/voxvera/vendor/PIL/ImageEnhance.py b/voxvera/vendor/PIL/ImageEnhance.py new file mode 100644 index 0000000..0e7e6dd --- /dev/null +++ b/voxvera/vendor/PIL/ImageEnhance.py @@ -0,0 +1,113 @@ +# +# 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 __future__ import annotations + +from . import Image, ImageFilter, ImageStat + + +class _Enhance: + image: Image.Image + degenerate: Image.Image + + def enhance(self, factor: float) -> Image.Image: + """ + 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: Image.Image) -> None: + self.image = image + self.intermediate_mode = "L" + if "A" in image.getbands(): + self.intermediate_mode = "LA" + + if self.intermediate_mode != image.mode: + image = image.convert(self.intermediate_mode).convert(image.mode) + self.degenerate = image + + +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 gray image. A factor of 1.0 gives the original image. + """ + + def __init__(self, image: Image.Image) -> None: + self.image = image + if image.mode != "L": + image = image.convert("L") + mean = int(ImageStat.Stat(image).mean[0] + 0.5) + self.degenerate = Image.new("L", image.size, mean) + if self.degenerate.mode != self.image.mode: + self.degenerate = self.degenerate.convert(self.image.mode) + + if "A" in self.image.getbands(): + self.degenerate.putalpha(self.image.getchannel("A")) + + +class Brightness(_Enhance): + """Adjust image brightness. + + This class can be used to control the brightness 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: Image.Image) -> None: + self.image = image + self.degenerate = Image.new(image.mode, image.size, 0) + + if "A" in image.getbands(): + self.degenerate.putalpha(image.getchannel("A")) + + +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: Image.Image) -> None: + self.image = image + self.degenerate = image.filter(ImageFilter.SMOOTH) + + if "A" in image.getbands(): + self.degenerate.putalpha(image.getchannel("A")) diff --git a/voxvera/vendor/PIL/ImageFile.py b/voxvera/vendor/PIL/ImageFile.py new file mode 100644 index 0000000..3390dfa --- /dev/null +++ b/voxvera/vendor/PIL/ImageFile.py @@ -0,0 +1,938 @@ +# +# 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 __future__ import annotations + +import abc +import io +import itertools +import logging +import os +import struct +from typing import IO, Any, NamedTuple, cast + +from . import ExifTags, Image +from ._util import DeferredError, is_path + +TYPE_CHECKING = False +if TYPE_CHECKING: + from ._typing import StrOrBytesPath + +logger = logging.getLogger(__name__) + +MAXBLOCK = 65536 +""" +By default, Pillow processes image data in blocks. This helps to prevent excessive use +of resources. Codecs may disable this behaviour with ``_pulls_fd`` or ``_pushes_fd``. + +When reading an image, this is the number of bytes to read at once. + +When writing an image, this is the number of bytes to write at once. +If the image width times 4 is greater, then that will be used instead. +Plugins may also set a greater number. + +User code may set this to another number. +""" + +SAFEBLOCK = 1024 * 1024 + +LOAD_TRUNCATED_IMAGES = False +"""Whether or not to load truncated image files. User code may change this.""" + +ERRORS = { + -1: "image buffer overrun error", + -2: "decoding error", + -3: "unknown error", + -8: "bad configuration", + -9: "out of memory error", +} +""" +Dict of known error codes returned from :meth:`.PyDecoder.decode`, +:meth:`.PyEncoder.encode` :meth:`.PyEncoder.encode_to_pyfd` and +:meth:`.PyEncoder.encode_to_file`. +""" + + +# +# -------------------------------------------------------------------- +# Helpers + + +def _get_oserror(error: int, *, encoder: bool) -> OSError: + try: + msg = Image.core.getcodecstatus(error) + except AttributeError: + msg = ERRORS.get(error) + if not msg: + msg = f"{'encoder' if encoder else 'decoder'} error {error}" + msg += f" when {'writing' if encoder else 'reading'} image file" + return OSError(msg) + + +def _tilesort(t: _Tile) -> int: + # sort on offset + return t[2] + + +class _Tile(NamedTuple): + codec_name: str + extents: tuple[int, int, int, int] | None + offset: int = 0 + args: tuple[Any, ...] | str | None = None + + +# +# -------------------------------------------------------------------- +# ImageFile base class + + +class ImageFile(Image.Image): + """Base class for image file format handlers.""" + + def __init__( + self, fp: StrOrBytesPath | IO[bytes], filename: str | bytes | None = None + ) -> None: + super().__init__() + + self._min_frame = 0 + + self.custom_mimetype: str | None = None + + self.tile: list[_Tile] = [] + """ A list of tile descriptors """ + + self.readonly = 1 # until we know better + + self.decoderconfig: tuple[Any, ...] = () + self.decodermaxblock = MAXBLOCK + + self.fp: IO[bytes] | None + self._fp: IO[bytes] | DeferredError + if is_path(fp): + # filename + self.fp = open(fp, "rb") + self.filename = os.fspath(fp) + self._exclusive_fp = True + else: + # stream + self.fp = cast(IO[bytes], fp) + self.filename = filename if filename is not None else "" + # can be overridden + self._exclusive_fp = False + + try: + try: + self._open() + except ( + IndexError, # end of data + TypeError, # end of data (ord) + KeyError, # unsupported mode + EOFError, # got header but not the first frame + struct.error, + ) as v: + raise SyntaxError(v) from v + + if not self.mode or self.size[0] <= 0 or self.size[1] <= 0: + msg = "not identified by this driver" + raise SyntaxError(msg) + except BaseException: + # close the file only if we have opened it this constructor + if self._exclusive_fp: + self.fp.close() + raise + + def _open(self) -> None: + pass + + # Context manager support + def __enter__(self) -> ImageFile: + return self + + def _close_fp(self) -> None: + if getattr(self, "_fp", False) and not isinstance(self._fp, DeferredError): + if self._fp != self.fp: + self._fp.close() + self._fp = DeferredError(ValueError("Operation on closed image")) + if self.fp: + self.fp.close() + + def __exit__(self, *args: object) -> None: + if getattr(self, "_exclusive_fp", False): + self._close_fp() + self.fp = None + + def close(self) -> None: + """ + 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 required to close images that have multiple frames or + have not had their file read and closed by the + :py:meth:`~PIL.Image.Image.load` method. See :ref:`file-handling` for + more information. + """ + try: + self._close_fp() + self.fp = None + except Exception as msg: + logger.debug("Error closing: %s", msg) + + super().close() + + def get_child_images(self) -> list[ImageFile]: + child_images = [] + exif = self.getexif() + ifds = [] + if ExifTags.Base.SubIFDs in exif: + subifd_offsets = exif[ExifTags.Base.SubIFDs] + if subifd_offsets: + if not isinstance(subifd_offsets, tuple): + subifd_offsets = (subifd_offsets,) + for subifd_offset in subifd_offsets: + ifds.append((exif._get_ifd_dict(subifd_offset), subifd_offset)) + ifd1 = exif.get_ifd(ExifTags.IFD.IFD1) + if ifd1 and ifd1.get(ExifTags.Base.JpegIFOffset): + assert exif._info is not None + ifds.append((ifd1, exif._info.next)) + + offset = None + for ifd, ifd_offset in ifds: + assert self.fp is not None + current_offset = self.fp.tell() + if offset is None: + offset = current_offset + + fp = self.fp + if ifd is not None: + thumbnail_offset = ifd.get(ExifTags.Base.JpegIFOffset) + if thumbnail_offset is not None: + thumbnail_offset += getattr(self, "_exif_offset", 0) + self.fp.seek(thumbnail_offset) + + length = ifd.get(ExifTags.Base.JpegIFByteCount) + assert isinstance(length, int) + data = self.fp.read(length) + fp = io.BytesIO(data) + + with Image.open(fp) as im: + from . import TiffImagePlugin + + if thumbnail_offset is None and isinstance( + im, TiffImagePlugin.TiffImageFile + ): + im._frame_pos = [ifd_offset] + im._seek(0) + im.load() + child_images.append(im) + + if offset is not None: + assert self.fp is not None + self.fp.seek(offset) + return child_images + + def get_format_mimetype(self) -> str | None: + if self.custom_mimetype: + return self.custom_mimetype + if self.format is not None: + return Image.MIME.get(self.format.upper()) + return None + + def __getstate__(self) -> list[Any]: + return super().__getstate__() + [self.filename] + + def __setstate__(self, state: list[Any]) -> None: + self.tile = [] + if len(state) > 5: + self.filename = state[5] + super().__setstate__(state) + + def verify(self) -> None: + """Check file integrity""" + + # raise exception if something's wrong. must be called + # directly after open, and closes file when finished. + if self._exclusive_fp and self.fp: + self.fp.close() + self.fp = None + + def load(self) -> Image.core.PixelAccess | None: + """Load image data based on tile list""" + + if not self.tile and self._im is None: + msg = "cannot load this image" + raise OSError(msg) + + pixel = Image.Image.load(self) + if not self.tile: + return pixel + + self.map: mmap.mmap | None = None + use_mmap = self.filename and len(self.tile) == 1 + + assert self.fp is not None + readonly = 0 + + # look for read/seek overrides + if hasattr(self, "load_read"): + read = self.load_read + # don't use mmap if there are custom read/seek functions + use_mmap = False + else: + read = self.fp.read + + if hasattr(self, "load_seek"): + seek = self.load_seek + use_mmap = False + else: + seek = self.fp.seek + + if use_mmap: + # try memory mapping + decoder_name, extents, offset, args = self.tile[0] + if isinstance(args, str): + args = (args, 0, 1) + if ( + decoder_name == "raw" + and isinstance(args, tuple) + and len(args) >= 3 + and args[0] == self.mode + and args[0] in Image._MAPMODES + ): + if offset < 0: + msg = "Tile offset cannot be negative" + raise ValueError(msg) + try: + # use mmap, if possible + import mmap + + with open(self.filename) as fp: + self.map = mmap.mmap(fp.fileno(), 0, access=mmap.ACCESS_READ) + if offset + self.size[1] * args[1] > self.map.size(): + msg = "buffer is not large enough" + raise OSError(msg) + self.im = Image.core.map_buffer( + self.map, self.size, decoder_name, offset, args + ) + readonly = 1 + # After trashing self.im, + # we might need to reload the palette data. + if self.palette: + self.palette.dirty = 1 + except (AttributeError, OSError, ImportError): + self.map = None + + self.load_prepare() + err_code = -3 # initialize to unknown error + if not self.map: + # sort tiles in file order + self.tile.sort(key=_tilesort) + + # FIXME: This is a hack to handle TIFF's JpegTables tag. + prefix = getattr(self, "tile_prefix", b"") + + # Remove consecutive duplicates that only differ by their offset + self.tile = [ + list(tiles)[-1] + for _, tiles in itertools.groupby( + self.tile, lambda tile: (tile[0], tile[1], tile[3]) + ) + ] + for i, (decoder_name, extents, offset, args) in enumerate(self.tile): + seek(offset) + decoder = Image._getdecoder( + self.mode, decoder_name, args, self.decoderconfig + ) + try: + decoder.setimage(self.im, extents) + if decoder.pulls_fd: + decoder.setfd(self.fp) + err_code = decoder.decode(b"")[1] + else: + b = prefix + while True: + read_bytes = self.decodermaxblock + if i + 1 < len(self.tile): + next_offset = self.tile[i + 1].offset + if next_offset > offset: + read_bytes = next_offset - offset + try: + s = read(read_bytes) + except (IndexError, struct.error) as e: + # truncated png/gif + if LOAD_TRUNCATED_IMAGES: + break + else: + msg = "image file is truncated" + raise OSError(msg) from e + + if not s: # truncated jpeg + if LOAD_TRUNCATED_IMAGES: + break + else: + msg = ( + "image file is truncated " + f"({len(b)} bytes not processed)" + ) + raise OSError(msg) + + b = b + s + n, err_code = decoder.decode(b) + if n < 0: + break + b = b[n:] + finally: + # Need to cleanup here to prevent leaks + decoder.cleanup() + + self.tile = [] + self.readonly = readonly + + self.load_end() + + if self._exclusive_fp and self._close_exclusive_fp_after_loading: + self.fp.close() + self.fp = None + + if not self.map and not LOAD_TRUNCATED_IMAGES and err_code < 0: + # still raised if decoder fails to return anything + raise _get_oserror(err_code, encoder=False) + + return Image.Image.load(self) + + def load_prepare(self) -> None: + # create image memory if necessary + if self._im is None: + self.im = Image.core.new(self.mode, self.size) + # create palette (optional) + if self.mode == "P": + Image.Image.load(self) + + def load_end(self) -> None: + # may be overridden + pass + + # may be defined for contained formats + # def load_seek(self, pos: int) -> None: + # pass + + # may be defined for blocked formats (e.g. PNG) + # def load_read(self, read_bytes: int) -> bytes: + # pass + + def _seek_check(self, frame: int) -> bool: + if ( + frame < self._min_frame + # Only check upper limit on frames if additional seek operations + # are not required to do so + or ( + not (hasattr(self, "_n_frames") and self._n_frames is None) + and frame >= getattr(self, "n_frames") + self._min_frame + ) + ): + msg = "attempt to seek outside sequence" + raise EOFError(msg) + + return self.tell() != frame + + +class StubHandler(abc.ABC): + def open(self, im: StubImageFile) -> None: + pass + + @abc.abstractmethod + def load(self, im: StubImageFile) -> Image.Image: + pass + + +class StubImageFile(ImageFile, metaclass=abc.ABCMeta): + """ + 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. + """ + + @abc.abstractmethod + def _open(self) -> None: + pass + + def load(self) -> Image.core.PixelAccess | None: + loader = self._load() + if loader is None: + msg = f"cannot find loader for this {self.format} file" + raise OSError(msg) + image = loader.load(self) + assert image is not None + # become the other object (!) + self.__class__ = image.__class__ # type: ignore[assignment] + self.__dict__ = image.__dict__ + return image.load() + + @abc.abstractmethod + def _load(self) -> StubHandler | None: + """(Hook) Find actual image loader.""" + pass + + +class Parser: + """ + Incremental image parser. This class implements the standard + feed/close consumer interface. + """ + + incremental = None + image: Image.Image | None = None + data: bytes | None = None + decoder: Image.core.ImagingDecoder | PyDecoder | None = None + offset = 0 + finished = 0 + + def reset(self) -> None: + """ + (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: bytes) -> None: + """ + (Consumer) Feed data to the parser. + + :param data: A string buffer. + :exception OSError: 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 _get_oserror(e, encoder=False) + 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: + with io.BytesIO(self.data) as fp: + im = Image.open(fp) + except OSError: + 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 __enter__(self) -> Parser: + return self + + def __exit__(self, *args: object) -> None: + self.close() + + def close(self) -> Image.Image: + """ + (Consumer) Close the stream. + + :returns: An image object. + :exception OSError: 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: + msg = "image was incomplete" + raise OSError(msg) + if not self.image: + msg = "cannot parse this image" + raise OSError(msg) + if self.data: + # incremental parsing not possible; reopen the file + # not that we have all data + with io.BytesIO(self.data) as fp: + try: + self.image = Image.open(fp) + finally: + self.image.load() + return self.image + + +# -------------------------------------------------------------------- + + +def _save(im: Image.Image, fp: IO[bytes], tile: list[_Tile], bufsize: int = 0) -> None: + """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() + _encode_tile(im, fp, tile, bufsize, fh) + except (AttributeError, io.UnsupportedOperation) as exc: + _encode_tile(im, fp, tile, bufsize, None, exc) + if hasattr(fp, "flush"): + fp.flush() + + +def _encode_tile( + im: Image.Image, + fp: IO[bytes], + tile: list[_Tile], + bufsize: int, + fh: int | None, + exc: BaseException | None = None, +) -> None: + for encoder_name, extents, offset, args in tile: + if offset > 0: + fp.seek(offset) + encoder = Image._getencoder(im.mode, encoder_name, args, im.encoderconfig) + try: + encoder.setimage(im.im, extents) + if encoder.pushes_fd: + encoder.setfd(fp) + errcode = encoder.encode_to_pyfd()[1] + else: + if exc: + # compress to Python file-compatible object + while True: + errcode, data = encoder.encode(bufsize)[1:] + fp.write(data) + if errcode: + break + else: + # slight speedup: compress to real file object + assert fh is not None + errcode = encoder.encode_to_file(fh, bufsize) + if errcode < 0: + raise _get_oserror(errcode, encoder=True) from exc + finally: + encoder.cleanup() + + +def _safe_read(fp: IO[bytes], size: int) -> bytes: + """ + 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 size bytes of data. + + Raises an OSError if the file is truncated and the read cannot be completed + + """ + if size <= 0: + return b"" + if size <= SAFEBLOCK: + data = fp.read(size) + if len(data) < size: + msg = "Truncated File Read" + raise OSError(msg) + return data + blocks: list[bytes] = [] + remaining_size = size + while remaining_size > 0: + block = fp.read(min(remaining_size, SAFEBLOCK)) + if not block: + break + blocks.append(block) + remaining_size -= len(block) + if sum(len(block) for block in blocks) < size: + msg = "Truncated File Read" + raise OSError(msg) + return b"".join(blocks) + + +class PyCodecState: + def __init__(self) -> None: + self.xsize = 0 + self.ysize = 0 + self.xoff = 0 + self.yoff = 0 + + def extents(self) -> tuple[int, int, int, int]: + return self.xoff, self.yoff, self.xoff + self.xsize, self.yoff + self.ysize + + +class PyCodec: + fd: IO[bytes] | None + + def __init__(self, mode: str, *args: Any) -> None: + self.im: Image.core.ImagingCore | None = None + self.state = PyCodecState() + self.fd = None + self.mode = mode + self.init(args) + + def init(self, args: tuple[Any, ...]) -> None: + """ + Override to perform codec specific initialization + + :param args: Tuple of arg items from the tile entry + :returns: None + """ + self.args = args + + def cleanup(self) -> None: + """ + Override to perform codec specific cleanup + + :returns: None + """ + pass + + def setfd(self, fd: IO[bytes]) -> None: + """ + Called from ImageFile to set the Python file-like object + + :param fd: A Python file-like object + :returns: None + """ + self.fd = fd + + def setimage( + self, + im: Image.core.ImagingCore, + extents: tuple[int, int, int, int] | None = None, + ) -> None: + """ + Called from ImageFile to set the core output image for the codec + + :param im: A core image object + :param extents: a 4 tuple of (x0, y0, x1, y1) defining the rectangle + for this tile + :returns: None + """ + + # following c code + self.im = im + + if extents: + (x0, y0, x1, y1) = extents + else: + (x0, y0, x1, y1) = (0, 0, 0, 0) + + if x0 == 0 and x1 == 0: + self.state.xsize, self.state.ysize = self.im.size + else: + self.state.xoff = x0 + self.state.yoff = y0 + self.state.xsize = x1 - x0 + self.state.ysize = y1 - y0 + + if self.state.xsize <= 0 or self.state.ysize <= 0: + msg = "Size cannot be negative" + raise ValueError(msg) + + if ( + self.state.xsize + self.state.xoff > self.im.size[0] + or self.state.ysize + self.state.yoff > self.im.size[1] + ): + msg = "Tile cannot extend outside image" + raise ValueError(msg) + + +class PyDecoder(PyCodec): + """ + Python implementation of a format decoder. Override this class and + add the decoding logic in the :meth:`decode` method. + + See :ref:`Writing Your Own File Codec in Python` + """ + + _pulls_fd = False + + @property + def pulls_fd(self) -> bool: + return self._pulls_fd + + def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: + """ + Override to perform the decoding process. + + :param buffer: A bytes object with the data to be decoded. + :returns: A tuple of ``(bytes consumed, errcode)``. + If finished with decoding return -1 for the bytes consumed. + Err codes are from :data:`.ImageFile.ERRORS`. + """ + msg = "unavailable in base decoder" + raise NotImplementedError(msg) + + def set_as_raw( + self, data: bytes, rawmode: str | None = None, extra: tuple[Any, ...] = () + ) -> None: + """ + Convenience method to set the internal image from a stream of raw data + + :param data: Bytes to be set + :param rawmode: The rawmode to be used for the decoder. + If not specified, it will default to the mode of the image + :param extra: Extra arguments for the decoder. + :returns: None + """ + + if not rawmode: + rawmode = self.mode + d = Image._getdecoder(self.mode, "raw", rawmode, extra) + assert self.im is not None + d.setimage(self.im, self.state.extents()) + s = d.decode(data) + + if s[0] >= 0: + msg = "not enough image data" + raise ValueError(msg) + if s[1] != 0: + msg = "cannot decode image data" + raise ValueError(msg) + + +class PyEncoder(PyCodec): + """ + Python implementation of a format encoder. Override this class and + add the decoding logic in the :meth:`encode` method. + + See :ref:`Writing Your Own File Codec in Python` + """ + + _pushes_fd = False + + @property + def pushes_fd(self) -> bool: + return self._pushes_fd + + def encode(self, bufsize: int) -> tuple[int, int, bytes]: + """ + Override to perform the encoding process. + + :param bufsize: Buffer size. + :returns: A tuple of ``(bytes encoded, errcode, bytes)``. + If finished with encoding return 1 for the error code. + Err codes are from :data:`.ImageFile.ERRORS`. + """ + msg = "unavailable in base encoder" + raise NotImplementedError(msg) + + def encode_to_pyfd(self) -> tuple[int, int]: + """ + If ``pushes_fd`` is ``True``, then this method will be used, + and ``encode()`` will only be called once. + + :returns: A tuple of ``(bytes consumed, errcode)``. + Err codes are from :data:`.ImageFile.ERRORS`. + """ + if not self.pushes_fd: + return 0, -8 # bad configuration + bytes_consumed, errcode, data = self.encode(0) + if data: + assert self.fd is not None + self.fd.write(data) + return bytes_consumed, errcode + + def encode_to_file(self, fh: int, bufsize: int) -> int: + """ + :param fh: File handle. + :param bufsize: Buffer size. + + :returns: If finished successfully, return 0. + Otherwise, return an error code. Err codes are from + :data:`.ImageFile.ERRORS`. + """ + errcode = 0 + while errcode == 0: + status, errcode, buf = self.encode(bufsize) + if status > 0: + os.write(fh, buf[status:]) + return errcode diff --git a/voxvera/vendor/PIL/ImageFilter.py b/voxvera/vendor/PIL/ImageFilter.py new file mode 100644 index 0000000..9326eee --- /dev/null +++ b/voxvera/vendor/PIL/ImageFilter.py @@ -0,0 +1,607 @@ +# +# 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 __future__ import annotations + +import abc +import functools +from collections.abc import Sequence +from typing import cast + +TYPE_CHECKING = False +if TYPE_CHECKING: + from collections.abc import Callable + from types import ModuleType + from typing import Any + + from . import _imaging + from ._typing import NumpyArray + + +class Filter(abc.ABC): + @abc.abstractmethod + def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: + pass + + +class MultibandFilter(Filter): + pass + + +class BuiltinFilter(MultibandFilter): + filterargs: tuple[Any, ...] + + def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: + if image.mode == "P": + msg = "cannot filter palette images" + raise ValueError(msg) + return image.filter(*self.filterargs) + + +class Kernel(BuiltinFilter): + """ + Create a convolution kernel. This only supports 3x3 and 5x5 integer and floating + point kernels. + + Kernels can only be applied to "L" and "RGB" images. + + :param size: Kernel size, given as (width, height). This must be (3,3) or (5,5). + :param kernel: A sequence containing kernel weights. The kernel will be flipped + vertically before being applied to the image. + :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. + """ + + name = "Kernel" + + def __init__( + self, + size: tuple[int, int], + kernel: Sequence[float], + scale: float | None = None, + offset: float = 0, + ) -> None: + if scale is None: + # default scale is sum of kernel + scale = functools.reduce(lambda a, b: a + b, kernel) + if size[0] * size[1] != len(kernel): + msg = "not enough coefficients in kernel" + raise ValueError(msg) + self.filterargs = size, scale, offset, kernel + + +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: int, rank: int) -> None: + self.size = size + self.rank = rank + + def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: + if image.mode == "P": + msg = "cannot filter palette images" + raise ValueError(msg) + 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: int = 3) -> None: + 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: int = 3) -> None: + 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: int = 3) -> None: + 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: int = 3) -> None: + self.size = size + + def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: + return image.modefilter(self.size) + + +class GaussianBlur(MultibandFilter): + """Blurs the image with a sequence of extended box filters, which + approximates a Gaussian kernel. For details on accuracy see + + + :param radius: Standard deviation of the Gaussian kernel. Either a sequence of two + numbers for x and y, or a single number for both. + """ + + name = "GaussianBlur" + + def __init__(self, radius: float | Sequence[float] = 2) -> None: + self.radius = radius + + def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: + xy = self.radius + if isinstance(xy, (int, float)): + xy = (xy, xy) + if xy == (0, 0): + return image.copy() + return image.gaussian_blur(xy) + + +class BoxBlur(MultibandFilter): + """Blurs 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 radius: Size of the box in a direction. Either a sequence of two numbers for + x and y, or a single number for both. + + Radius 0 does not blur, returns an identical image. + Radius 1 takes 1 pixel in each direction, i.e. 9 pixels in total. + """ + + name = "BoxBlur" + + def __init__(self, radius: float | Sequence[float]) -> None: + xy = radius if isinstance(radius, (tuple, list)) else (radius, radius) + if xy[0] < 0 or xy[1] < 0: + msg = "radius must be >= 0" + raise ValueError(msg) + self.radius = radius + + def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: + xy = self.radius + if isinstance(xy, (int, float)): + xy = (xy, xy) + if xy == (0, 0): + return image.copy() + return image.box_blur(xy) + + +class UnsharpMask(MultibandFilter): + """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: float = 2, percent: int = 150, threshold: int = 3 + ) -> None: + self.radius = radius + self.percent = percent + self.threshold = threshold + + def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: + return image.unsharp_mask(self.radius, self.percent, self.threshold) + + +class BLUR(BuiltinFilter): + name = "Blur" + # fmt: off + 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, + ) + # fmt: on + + +class CONTOUR(BuiltinFilter): + name = "Contour" + # fmt: off + filterargs = (3, 3), 1, 255, ( + -1, -1, -1, + -1, 8, -1, + -1, -1, -1, + ) + # fmt: on + + +class DETAIL(BuiltinFilter): + name = "Detail" + # fmt: off + filterargs = (3, 3), 6, 0, ( + 0, -1, 0, + -1, 10, -1, + 0, -1, 0, + ) + # fmt: on + + +class EDGE_ENHANCE(BuiltinFilter): + name = "Edge-enhance" + # fmt: off + filterargs = (3, 3), 2, 0, ( + -1, -1, -1, + -1, 10, -1, + -1, -1, -1, + ) + # fmt: on + + +class EDGE_ENHANCE_MORE(BuiltinFilter): + name = "Edge-enhance More" + # fmt: off + filterargs = (3, 3), 1, 0, ( + -1, -1, -1, + -1, 9, -1, + -1, -1, -1, + ) + # fmt: on + + +class EMBOSS(BuiltinFilter): + name = "Emboss" + # fmt: off + filterargs = (3, 3), 1, 128, ( + -1, 0, 0, + 0, 1, 0, + 0, 0, 0, + ) + # fmt: on + + +class FIND_EDGES(BuiltinFilter): + name = "Find Edges" + # fmt: off + filterargs = (3, 3), 1, 0, ( + -1, -1, -1, + -1, 8, -1, + -1, -1, -1, + ) + # fmt: on + + +class SHARPEN(BuiltinFilter): + name = "Sharpen" + # fmt: off + filterargs = (3, 3), 16, 0, ( + -2, -2, -2, + -2, 32, -2, + -2, -2, -2, + ) + # fmt: on + + +class SMOOTH(BuiltinFilter): + name = "Smooth" + # fmt: off + filterargs = (3, 3), 13, 0, ( + 1, 1, 1, + 1, 5, 1, + 1, 1, 1, + ) + # fmt: on + + +class SMOOTH_MORE(BuiltinFilter): + name = "Smooth More" + # fmt: off + 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, + ) + # fmt: on + + +class Color3DLUT(MultibandFilter): + """Three-dimensional color lookup table. + + Transforms 3-channel pixels using the values of the channels as coordinates + in the 3D lookup table and interpolating the nearest elements. + + This method allows you to apply almost any color transformation + in constant time by using pre-calculated decimated tables. + + .. versionadded:: 5.2.0 + + :param size: Size of the table. One int or tuple of (int, int, int). + Minimal size in any dimension is 2, maximum is 65. + :param table: Flat lookup table. A list of ``channels * size**3`` + float elements or a list of ``size**3`` channels-sized + tuples with floats. Channels are changed first, + then first dimension, then second, then third. + Value 0.0 corresponds lowest value of output, 1.0 highest. + :param channels: Number of channels in the table. Could be 3 or 4. + Default is 3. + :param target_mode: A mode for the result image. Should have not less + than ``channels`` channels. Default is ``None``, + which means that mode wouldn't be changed. + """ + + name = "Color 3D LUT" + + def __init__( + self, + size: int | tuple[int, int, int], + table: Sequence[float] | Sequence[Sequence[int]] | NumpyArray, + channels: int = 3, + target_mode: str | None = None, + **kwargs: bool, + ) -> None: + if channels not in (3, 4): + msg = "Only 3 or 4 output channels are supported" + raise ValueError(msg) + self.size = size = self._check_size(size) + self.channels = channels + self.mode = target_mode + + # Hidden flag `_copy_table=False` could be used to avoid extra copying + # of the table if the table is specially made for the constructor. + copy_table = kwargs.get("_copy_table", True) + items = size[0] * size[1] * size[2] + wrong_size = False + + numpy: ModuleType | None = None + if hasattr(table, "shape"): + try: + import numpy + except ImportError: + pass + + if numpy and isinstance(table, numpy.ndarray): + numpy_table: NumpyArray = table + if copy_table: + numpy_table = numpy_table.copy() + + if numpy_table.shape in [ + (items * channels,), + (items, channels), + (size[2], size[1], size[0], channels), + ]: + table = numpy_table.reshape(items * channels) + else: + wrong_size = True + + else: + if copy_table: + table = list(table) + + # Convert to a flat list + if table and isinstance(table[0], (list, tuple)): + raw_table = cast(Sequence[Sequence[int]], table) + flat_table: list[int] = [] + for pixel in raw_table: + if len(pixel) != channels: + msg = ( + "The elements of the table should " + f"have a length of {channels}." + ) + raise ValueError(msg) + flat_table.extend(pixel) + table = flat_table + + if wrong_size or len(table) != items * channels: + msg = ( + "The table should have either channels * size**3 float items " + "or size**3 items of channels-sized tuples with floats. " + f"Table should be: {channels}x{size[0]}x{size[1]}x{size[2]}. " + f"Actual length: {len(table)}" + ) + raise ValueError(msg) + self.table = table + + @staticmethod + def _check_size(size: Any) -> tuple[int, int, int]: + try: + _, _, _ = size + except ValueError as e: + msg = "Size should be either an integer or a tuple of three integers." + raise ValueError(msg) from e + except TypeError: + size = (size, size, size) + size = tuple(int(x) for x in size) + for size_1d in size: + if not 2 <= size_1d <= 65: + msg = "Size should be in [2, 65] range." + raise ValueError(msg) + return size + + @classmethod + def generate( + cls, + size: int | tuple[int, int, int], + callback: Callable[[float, float, float], tuple[float, ...]], + channels: int = 3, + target_mode: str | None = None, + ) -> Color3DLUT: + """Generates new LUT using provided callback. + + :param size: Size of the table. Passed to the constructor. + :param callback: Function with three parameters which correspond + three color channels. Will be called ``size**3`` + times with values from 0.0 to 1.0 and should return + a tuple with ``channels`` elements. + :param channels: The number of channels which should return callback. + :param target_mode: Passed to the constructor of the resulting + lookup table. + """ + size_1d, size_2d, size_3d = cls._check_size(size) + if channels not in (3, 4): + msg = "Only 3 or 4 output channels are supported" + raise ValueError(msg) + + table: list[float] = [0] * (size_1d * size_2d * size_3d * channels) + idx_out = 0 + for b in range(size_3d): + for g in range(size_2d): + for r in range(size_1d): + table[idx_out : idx_out + channels] = callback( + r / (size_1d - 1), g / (size_2d - 1), b / (size_3d - 1) + ) + idx_out += channels + + return cls( + (size_1d, size_2d, size_3d), + table, + channels=channels, + target_mode=target_mode, + _copy_table=False, + ) + + def transform( + self, + callback: Callable[..., tuple[float, ...]], + with_normals: bool = False, + channels: int | None = None, + target_mode: str | None = None, + ) -> Color3DLUT: + """Transforms the table values using provided callback and returns + a new LUT with altered values. + + :param callback: A function which takes old lookup table values + and returns a new set of values. The number + of arguments which function should take is + ``self.channels`` or ``3 + self.channels`` + if ``with_normals`` flag is set. + Should return a tuple of ``self.channels`` or + ``channels`` elements if it is set. + :param with_normals: If true, ``callback`` will be called with + coordinates in the color cube as the first + three arguments. Otherwise, ``callback`` + will be called only with actual color values. + :param channels: The number of channels in the resulting lookup table. + :param target_mode: Passed to the constructor of the resulting + lookup table. + """ + if channels not in (None, 3, 4): + msg = "Only 3 or 4 output channels are supported" + raise ValueError(msg) + ch_in = self.channels + ch_out = channels or ch_in + size_1d, size_2d, size_3d = self.size + + table: list[float] = [0] * (size_1d * size_2d * size_3d * ch_out) + idx_in = 0 + idx_out = 0 + for b in range(size_3d): + for g in range(size_2d): + for r in range(size_1d): + values = self.table[idx_in : idx_in + ch_in] + if with_normals: + values = callback( + r / (size_1d - 1), + g / (size_2d - 1), + b / (size_3d - 1), + *values, + ) + else: + values = callback(*values) + table[idx_out : idx_out + ch_out] = values + idx_in += ch_in + idx_out += ch_out + + return type(self)( + self.size, + table, + channels=ch_out, + target_mode=target_mode or self.mode, + _copy_table=False, + ) + + def __repr__(self) -> str: + r = [ + f"{self.__class__.__name__} from {self.table.__class__.__name__}", + "size={:d}x{:d}x{:d}".format(*self.size), + f"channels={self.channels:d}", + ] + if self.mode: + r.append(f"target_mode={self.mode}") + return "<{}>".format(" ".join(r)) + + def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: + from . import Image + + return image.color_lut_3d( + self.mode or image.mode, + Image.Resampling.BILINEAR, + self.channels, + self.size, + self.table, + ) diff --git a/voxvera/vendor/PIL/ImageFont.py b/voxvera/vendor/PIL/ImageFont.py new file mode 100644 index 0000000..d11f7bf --- /dev/null +++ b/voxvera/vendor/PIL/ImageFont.py @@ -0,0 +1,1320 @@ +# +# 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 annotations + +import base64 +import os +import sys +import warnings +from enum import IntEnum +from io import BytesIO +from types import ModuleType +from typing import IO, Any, BinaryIO, TypedDict, cast + +from . import Image +from ._typing import StrOrBytesPath +from ._util import DeferredError, is_path + +TYPE_CHECKING = False +if TYPE_CHECKING: + from . import ImageFile + from ._imaging import ImagingFont + from ._imagingft import Font + + +class Axis(TypedDict): + minimum: int | None + default: int | None + maximum: int | None + name: bytes | None + + +class Layout(IntEnum): + BASIC = 0 + RAQM = 1 + + +MAX_STRING_LENGTH = 1_000_000 + + +core: ModuleType | DeferredError +try: + from . import _imagingft as core +except ImportError as ex: + core = DeferredError.new(ex) + + +def _string_length_check(text: str | bytes | bytearray) -> None: + if MAX_STRING_LENGTH is not None and len(text) > MAX_STRING_LENGTH: + msg = "too many characters in string" + raise ValueError(msg) + + +# 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""" + + font: ImagingFont + + def _load_pilfont(self, filename: str) -> None: + with open(filename, "rb") as fp: + image: ImageFile.ImageFile | None = None + root = os.path.splitext(filename)[0] + + for ext in (".png", ".gif", ".pbm"): + if image: + image.close() + try: + fullname = root + ext + image = Image.open(fullname) + except Exception: + pass + else: + if image and image.mode in ("1", "L"): + break + else: + if image: + image.close() + + msg = f"cannot find glyph data file {root}.{{gif|pbm|png}}" + raise OSError(msg) + + self.file = fullname + + self._load_pilfont_data(fp, image) + image.close() + + def _load_pilfont_data(self, file: IO[bytes], image: Image.Image) -> None: + # check image + if image.mode not in ("1", "L"): + image.close() + + msg = "invalid font image mode" + raise TypeError(msg) + + # read PILfont header + if file.read(8) != b"PILfont\n": + image.close() + + msg = "Not a PILfont file" + raise SyntaxError(msg) + file.readline() + 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) + + image.load() + + self.font = Image.core.font(image.im, data) + + def getmask( + self, text: str | bytes, mode: str = "", *args: Any, **kwargs: Any + ) -> Image.core.ImagingCore: + """ + Create a bitmap for the text. + + If the font uses antialiasing, the bitmap should have mode ``L`` and use a + maximum value of 255. Otherwise, it should have mode ``1``. + + :param text: Text to render. + :param mode: Used by some graphics drivers to indicate what mode the + driver prefers; if empty, the renderer may return either + mode. Note that the mode is always a string, to simplify + C-level implementations. + + .. versionadded:: 1.1.5 + + :return: An internal PIL storage memory instance as defined by the + :py:mod:`PIL.Image.core` interface module. + """ + _string_length_check(text) + Image._decompression_bomb_check(self.font.getsize(text)) + return self.font.getmask(text, mode) + + def getbbox( + self, text: str | bytes | bytearray, *args: Any, **kwargs: Any + ) -> tuple[int, int, int, int]: + """ + Returns bounding box (in pixels) of given text. + + .. versionadded:: 9.2.0 + + :param text: Text to render. + + :return: ``(left, top, right, bottom)`` bounding box + """ + _string_length_check(text) + width, height = self.font.getsize(text) + return 0, 0, width, height + + def getlength( + self, text: str | bytes | bytearray, *args: Any, **kwargs: Any + ) -> int: + """ + Returns length (in pixels) of given text. + This is the amount by which following text should be offset. + + .. versionadded:: 9.2.0 + """ + _string_length_check(text) + width, height = self.font.getsize(text) + return width + + +## +# Wrapper for FreeType fonts. Application code should use the +# truetype factory function to create font objects. + + +class FreeTypeFont: + """FreeType font wrapper (requires _imagingft service)""" + + font: Font + font_bytes: bytes + + def __init__( + self, + font: StrOrBytesPath | BinaryIO, + size: float = 10, + index: int = 0, + encoding: str = "", + layout_engine: Layout | None = None, + ) -> None: + # FIXME: use service provider instead + + if isinstance(core, DeferredError): + raise core.ex + + if size <= 0: + msg = f"font size must be greater than 0, not {size}" + raise ValueError(msg) + + self.path = font + self.size = size + self.index = index + self.encoding = encoding + + if layout_engine not in (Layout.BASIC, Layout.RAQM): + layout_engine = Layout.BASIC + if core.HAVE_RAQM: + layout_engine = Layout.RAQM + elif layout_engine == Layout.RAQM and not core.HAVE_RAQM: + warnings.warn( + "Raqm layout was requested, but Raqm is not available. " + "Falling back to basic layout." + ) + layout_engine = Layout.BASIC + + self.layout_engine = layout_engine + + def load_from_bytes(f: IO[bytes]) -> None: + self.font_bytes = f.read() + self.font = core.getfont( + "", size, index, encoding, self.font_bytes, layout_engine + ) + + if is_path(font): + font = os.fspath(font) + if sys.platform == "win32": + font_bytes_path = font if isinstance(font, bytes) else font.encode() + try: + font_bytes_path.decode("ascii") + except UnicodeDecodeError: + # FreeType cannot load fonts with non-ASCII characters on Windows + # So load it into memory first + with open(font, "rb") as f: + load_from_bytes(f) + return + self.font = core.getfont( + font, size, index, encoding, layout_engine=layout_engine + ) + else: + load_from_bytes(cast(IO[bytes], font)) + + def __getstate__(self) -> list[Any]: + return [self.path, self.size, self.index, self.encoding, self.layout_engine] + + def __setstate__(self, state: list[Any]) -> None: + path, size, index, encoding, layout_engine = state + FreeTypeFont.__init__(self, path, size, index, encoding, layout_engine) + + def getname(self) -> tuple[str | None, str | None]: + """ + :return: A tuple of the font family (e.g. Helvetica) and the font style + (e.g. Bold) + """ + return self.font.family, self.font.style + + def getmetrics(self) -> tuple[int, int]: + """ + :return: A tuple of the font ascent (the distance from the baseline to + the highest outline point) and descent (the distance from the + baseline to the lowest outline point, a negative value) + """ + return self.font.ascent, self.font.descent + + def getlength( + self, + text: str | bytes, + mode: str = "", + direction: str | None = None, + features: list[str] | None = None, + language: str | None = None, + ) -> float: + """ + Returns length (in pixels with 1/64 precision) of given text when rendered + in font with provided direction, features, and language. + + This is the amount by which following text should be offset. + Text bounding box may extend past the length in some fonts, + e.g. when using italics or accents. + + The result is returned as a float; it is a whole number if using basic layout. + + Note that the sum of two lengths may not equal the length of a concatenated + string due to kerning. If you need to adjust for kerning, include the following + character and subtract its length. + + For example, instead of :: + + hello = font.getlength("Hello") + world = font.getlength("World") + hello_world = hello + world # not adjusted for kerning + assert hello_world == font.getlength("HelloWorld") # may fail + + use :: + + hello = font.getlength("HelloW") - font.getlength("W") # adjusted for kerning + world = font.getlength("World") + hello_world = hello + world # adjusted for kerning + assert hello_world == font.getlength("HelloWorld") # True + + or disable kerning with (requires libraqm) :: + + hello = draw.textlength("Hello", font, features=["-kern"]) + world = draw.textlength("World", font, features=["-kern"]) + hello_world = hello + world # kerning is disabled, no need to adjust + assert hello_world == draw.textlength("HelloWorld", font, features=["-kern"]) + + .. versionadded:: 8.0.0 + + :param text: Text to measure. + :param mode: Used by some graphics drivers to indicate what mode the + driver prefers; if empty, the renderer may return either + mode. Note that the mode is always a string, to simplify + C-level implementations. + + :param direction: Direction of the text. It can be 'rtl' (right to + left), 'ltr' (left to right) or 'ttb' (top to bottom). + Requires libraqm. + + :param features: A list of OpenType font features to be used during text + layout. This is usually used to turn on optional + font features that are not enabled by default, + for example 'dlig' or 'ss01', but can be also + used to turn off default font features for + example '-liga' to disable ligatures or '-kern' + to disable kerning. To get all supported + features, see + https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist + Requires libraqm. + + :param language: Language of the text. Different languages may use + different glyph shapes or ligatures. This parameter tells + the font which language the text is in, and to apply the + correct substitutions as appropriate, if available. + It should be a `BCP 47 language code + `_ + Requires libraqm. + + :return: Either width for horizontal text, or height for vertical text. + """ + _string_length_check(text) + return self.font.getlength(text, mode, direction, features, language) / 64 + + def getbbox( + self, + text: str | bytes, + mode: str = "", + direction: str | None = None, + features: list[str] | None = None, + language: str | None = None, + stroke_width: float = 0, + anchor: str | None = None, + ) -> tuple[float, float, float, float]: + """ + Returns bounding box (in pixels) of given text relative to given anchor + when rendered in font with provided direction, features, and language. + + Use :py:meth:`getlength()` to get the offset of following text with + 1/64 pixel precision. The bounding box includes extra margins for + some fonts, e.g. italics or accents. + + .. versionadded:: 8.0.0 + + :param text: Text to render. + :param mode: Used by some graphics drivers to indicate what mode the + driver prefers; if empty, the renderer may return either + mode. Note that the mode is always a string, to simplify + C-level implementations. + + :param direction: Direction of the text. It can be 'rtl' (right to + left), 'ltr' (left to right) or 'ttb' (top to bottom). + Requires libraqm. + + :param features: A list of OpenType font features to be used during text + layout. This is usually used to turn on optional + font features that are not enabled by default, + for example 'dlig' or 'ss01', but can be also + used to turn off default font features for + example '-liga' to disable ligatures or '-kern' + to disable kerning. To get all supported + features, see + https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist + Requires libraqm. + + :param language: Language of the text. Different languages may use + different glyph shapes or ligatures. This parameter tells + the font which language the text is in, and to apply the + correct substitutions as appropriate, if available. + It should be a `BCP 47 language code + `_ + Requires libraqm. + + :param stroke_width: The width of the text stroke. + + :param anchor: The text anchor alignment. Determines the relative location of + the anchor to the text. The default alignment is top left, + specifically ``la`` for horizontal text and ``lt`` for + vertical text. See :ref:`text-anchors` for details. + + :return: ``(left, top, right, bottom)`` bounding box + """ + _string_length_check(text) + size, offset = self.font.getsize( + text, mode, direction, features, language, anchor + ) + left, top = offset[0] - stroke_width, offset[1] - stroke_width + width, height = size[0] + 2 * stroke_width, size[1] + 2 * stroke_width + return left, top, left + width, top + height + + def getmask( + self, + text: str | bytes, + mode: str = "", + direction: str | None = None, + features: list[str] | None = None, + language: str | None = None, + stroke_width: float = 0, + anchor: str | None = None, + ink: int = 0, + start: tuple[float, float] | None = None, + ) -> Image.core.ImagingCore: + """ + Create a bitmap for the text. + + If the font uses antialiasing, the bitmap should have mode ``L`` and use a + maximum value of 255. If the font has embedded color data, the bitmap + should have mode ``RGBA``. Otherwise, it should have mode ``1``. + + :param text: Text to render. + :param mode: Used by some graphics drivers to indicate what mode the + driver prefers; if empty, the renderer may return either + mode. Note that the mode is always a string, to simplify + C-level implementations. + + .. versionadded:: 1.1.5 + + :param direction: Direction of the text. It can be 'rtl' (right to + left), 'ltr' (left to right) or 'ttb' (top to bottom). + Requires libraqm. + + .. versionadded:: 4.2.0 + + :param features: A list of OpenType font features to be used during text + layout. This is usually used to turn on optional + font features that are not enabled by default, + for example 'dlig' or 'ss01', but can be also + used to turn off default font features for + example '-liga' to disable ligatures or '-kern' + to disable kerning. To get all supported + features, see + https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist + Requires libraqm. + + .. versionadded:: 4.2.0 + + :param language: Language of the text. Different languages may use + different glyph shapes or ligatures. This parameter tells + the font which language the text is in, and to apply the + correct substitutions as appropriate, if available. + It should be a `BCP 47 language code + `_ + Requires libraqm. + + .. versionadded:: 6.0.0 + + :param stroke_width: The width of the text stroke. + + .. versionadded:: 6.2.0 + + :param anchor: The text anchor alignment. Determines the relative location of + the anchor to the text. The default alignment is top left, + specifically ``la`` for horizontal text and ``lt`` for + vertical text. See :ref:`text-anchors` for details. + + .. versionadded:: 8.0.0 + + :param ink: Foreground ink for rendering in RGBA mode. + + .. versionadded:: 8.0.0 + + :param start: Tuple of horizontal and vertical offset, as text may render + differently when starting at fractional coordinates. + + .. versionadded:: 9.4.0 + + :return: An internal PIL storage memory instance as defined by the + :py:mod:`PIL.Image.core` interface module. + """ + return self.getmask2( + text, + mode, + direction=direction, + features=features, + language=language, + stroke_width=stroke_width, + anchor=anchor, + ink=ink, + start=start, + )[0] + + def getmask2( + self, + text: str | bytes, + mode: str = "", + direction: str | None = None, + features: list[str] | None = None, + language: str | None = None, + stroke_width: float = 0, + anchor: str | None = None, + ink: int = 0, + start: tuple[float, float] | None = None, + *args: Any, + **kwargs: Any, + ) -> tuple[Image.core.ImagingCore, tuple[int, int]]: + """ + Create a bitmap for the text. + + If the font uses antialiasing, the bitmap should have mode ``L`` and use a + maximum value of 255. If the font has embedded color data, the bitmap + should have mode ``RGBA``. Otherwise, it should have mode ``1``. + + :param text: Text to render. + :param mode: Used by some graphics drivers to indicate what mode the + driver prefers; if empty, the renderer may return either + mode. Note that the mode is always a string, to simplify + C-level implementations. + + .. versionadded:: 1.1.5 + + :param direction: Direction of the text. It can be 'rtl' (right to + left), 'ltr' (left to right) or 'ttb' (top to bottom). + Requires libraqm. + + .. versionadded:: 4.2.0 + + :param features: A list of OpenType font features to be used during text + layout. This is usually used to turn on optional + font features that are not enabled by default, + for example 'dlig' or 'ss01', but can be also + used to turn off default font features for + example '-liga' to disable ligatures or '-kern' + to disable kerning. To get all supported + features, see + https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist + Requires libraqm. + + .. versionadded:: 4.2.0 + + :param language: Language of the text. Different languages may use + different glyph shapes or ligatures. This parameter tells + the font which language the text is in, and to apply the + correct substitutions as appropriate, if available. + It should be a `BCP 47 language code + `_ + Requires libraqm. + + .. versionadded:: 6.0.0 + + :param stroke_width: The width of the text stroke. + + .. versionadded:: 6.2.0 + + :param anchor: The text anchor alignment. Determines the relative location of + the anchor to the text. The default alignment is top left, + specifically ``la`` for horizontal text and ``lt`` for + vertical text. See :ref:`text-anchors` for details. + + .. versionadded:: 8.0.0 + + :param ink: Foreground ink for rendering in RGBA mode. + + .. versionadded:: 8.0.0 + + :param start: Tuple of horizontal and vertical offset, as text may render + differently when starting at fractional coordinates. + + .. versionadded:: 9.4.0 + + :return: A tuple of an internal PIL storage memory instance as defined by the + :py:mod:`PIL.Image.core` interface module, and the text offset, the + gap between the starting coordinate and the first marking + """ + _string_length_check(text) + if start is None: + start = (0, 0) + + def fill(width: int, height: int) -> Image.core.ImagingCore: + size = (width, height) + Image._decompression_bomb_check(size) + return Image.core.fill("RGBA" if mode == "RGBA" else "L", size) + + return self.font.render( + text, + fill, + mode, + direction, + features, + language, + stroke_width, + kwargs.get("stroke_filled", False), + anchor, + ink, + start, + ) + + def font_variant( + self, + font: StrOrBytesPath | BinaryIO | None = None, + size: float | None = None, + index: int | None = None, + encoding: str | None = None, + layout_engine: Layout | None = None, + ) -> FreeTypeFont: + """ + Create a copy of this FreeTypeFont object, + using any specified arguments to override the settings. + + Parameters are identical to the parameters used to initialize this + object. + + :return: A FreeTypeFont object. + """ + if font is None: + try: + font = BytesIO(self.font_bytes) + except AttributeError: + font = self.path + return FreeTypeFont( + font=font, + size=self.size if size is None else size, + index=self.index if index is None else index, + encoding=self.encoding if encoding is None else encoding, + layout_engine=layout_engine or self.layout_engine, + ) + + def get_variation_names(self) -> list[bytes]: + """ + :returns: A list of the named styles in a variation font. + :exception OSError: If the font is not a variation font. + """ + names = [] + for name in self.font.getvarnames(): + name = name.replace(b"\x00", b"") + if name not in names: + names.append(name) + return names + + def set_variation_by_name(self, name: str | bytes) -> None: + """ + :param name: The name of the style. + :exception OSError: If the font is not a variation font. + """ + names = self.get_variation_names() + if not isinstance(name, bytes): + name = name.encode() + index = names.index(name) + 1 + + if index == getattr(self, "_last_variation_index", None): + # When the same name is set twice in a row, + # there is an 'unknown freetype error' + # https://savannah.nongnu.org/bugs/?56186 + return + self._last_variation_index = index + + self.font.setvarname(index) + + def get_variation_axes(self) -> list[Axis]: + """ + :returns: A list of the axes in a variation font. + :exception OSError: If the font is not a variation font. + """ + axes = self.font.getvaraxes() + for axis in axes: + if axis["name"]: + axis["name"] = axis["name"].replace(b"\x00", b"") + return axes + + def set_variation_by_axes(self, axes: list[float]) -> None: + """ + :param axes: A list of values for each axis. + :exception OSError: If the font is not a variation font. + """ + self.font.setvaraxes(axes) + + +class TransposedFont: + """Wrapper for writing rotated or mirrored text""" + + def __init__( + self, font: ImageFont | FreeTypeFont, orientation: Image.Transpose | None = None + ): + """ + 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.Transpose.FLIP_LEFT_RIGHT, Image.Transpose.FLIP_TOP_BOTTOM, + Image.Transpose.ROTATE_90, Image.Transpose.ROTATE_180, or + Image.Transpose.ROTATE_270. + """ + self.font = font + self.orientation = orientation # any 'transpose' argument, or None + + def getmask( + self, text: str | bytes, mode: str = "", *args: Any, **kwargs: Any + ) -> Image.core.ImagingCore: + im = self.font.getmask(text, mode, *args, **kwargs) + if self.orientation is not None: + return im.transpose(self.orientation) + return im + + def getbbox( + self, text: str | bytes, *args: Any, **kwargs: Any + ) -> tuple[int, int, float, float]: + # TransposedFont doesn't support getmask2, move top-left point to (0, 0) + # this has no effect on ImageFont and simulates anchor="lt" for FreeTypeFont + left, top, right, bottom = self.font.getbbox(text, *args, **kwargs) + width = right - left + height = bottom - top + if self.orientation in (Image.Transpose.ROTATE_90, Image.Transpose.ROTATE_270): + return 0, 0, height, width + return 0, 0, width, height + + def getlength(self, text: str | bytes, *args: Any, **kwargs: Any) -> float: + if self.orientation in (Image.Transpose.ROTATE_90, Image.Transpose.ROTATE_270): + msg = "text length is undefined for text rotated by 90 or 270 degrees" + raise ValueError(msg) + return self.font.getlength(text, *args, **kwargs) + + +def load(filename: str) -> ImageFont: + """ + Load a font file. This function loads a font object from the given + bitmap font file, and returns the corresponding font object. For loading TrueType + or OpenType fonts instead, see :py:func:`~PIL.ImageFont.truetype`. + + :param filename: Name of font file. + :return: A font object. + :exception OSError: If the file could not be read. + """ + f = ImageFont() + f._load_pilfont(filename) + return f + + +def truetype( + font: StrOrBytesPath | BinaryIO, + size: float = 10, + index: int = 0, + encoding: str = "", + layout_engine: Layout | None = None, +) -> FreeTypeFont: + """ + Load a TrueType or OpenType font from a file or file-like object, + and create a font object. This function loads a font object from the given + file or file-like object, and creates a font object for a font of the given + size. For loading bitmap fonts instead, see :py:func:`~PIL.ImageFont.load` + and :py:func:`~PIL.ImageFont.load_path`. + + Pillow uses FreeType to open font files. On Windows, be aware that FreeType + will keep the file open as long as the FreeTypeFont object exists. Windows + limits the number of files that can be open in C at once to 512, so if many + fonts are opened simultaneously and that limit is approached, an + ``OSError`` may be thrown, reporting that FreeType "cannot open resource". + A workaround would be to copy the file(s) into memory, and open that instead. + + This function requires the _imagingft service. + + :param font: A filename or file-like object containing a TrueType font. + If the file is not found in this filename, the loader may also + search in other directories, such as: + + * The :file:`fonts/` directory on Windows, + * :file:`/Library/Fonts/`, :file:`/System/Library/Fonts/` + and :file:`~/Library/Fonts/` on macOS. + * :file:`~/.local/share/fonts`, :file:`/usr/local/share/fonts`, + and :file:`/usr/share/fonts` on Linux; or those specified by + the ``XDG_DATA_HOME`` and ``XDG_DATA_DIRS`` environment variables + for user-installed and system-wide fonts, respectively. + + :param size: The requested size, in pixels. + :param index: Which font face to load (default is first available face). + :param encoding: Which font encoding to use (default is Unicode). Possible + encodings include (see the FreeType documentation for more + information): + + * "unic" (Unicode) + * "symb" (Microsoft Symbol) + * "ADOB" (Adobe Standard) + * "ADBE" (Adobe Expert) + * "ADBC" (Adobe Custom) + * "armn" (Apple Roman) + * "sjis" (Shift JIS) + * "gb " (PRC) + * "big5" + * "wans" (Extended Wansung) + * "joha" (Johab) + * "lat1" (Latin-1) + + This specifies the character set to use. It does not alter the + encoding of any text provided in subsequent operations. + :param layout_engine: Which layout engine to use, if available: + :attr:`.ImageFont.Layout.BASIC` or :attr:`.ImageFont.Layout.RAQM`. + If it is available, Raqm layout will be used by default. + Otherwise, basic layout will be used. + + Raqm layout is recommended for all non-English text. If Raqm layout + is not required, basic layout will have better performance. + + You can check support for Raqm layout using + :py:func:`PIL.features.check_feature` with ``feature="raqm"``. + + .. versionadded:: 4.2.0 + :return: A font object. + :exception OSError: If the file could not be read. + :exception ValueError: If the font size is not greater than zero. + """ + + def freetype(font: StrOrBytesPath | BinaryIO) -> FreeTypeFont: + return FreeTypeFont(font, size, index, encoding, layout_engine) + + try: + return freetype(font) + except OSError: + if not is_path(font): + raise + ttf_filename = os.path.basename(font) + + dirs = [] + 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: + dirs.append(os.path.join(windir, "fonts")) + elif sys.platform in ("linux", "linux2"): + data_home = os.environ.get("XDG_DATA_HOME") + if not data_home: + # The freedesktop spec defines the following default directory for + # when XDG_DATA_HOME is unset or empty. This user-level directory + # takes precedence over system-level directories. + data_home = os.path.expanduser("~/.local/share") + xdg_dirs = [data_home] + + data_dirs = os.environ.get("XDG_DATA_DIRS") + if not data_dirs: + # Similarly, defaults are defined for the system-level directories + data_dirs = "/usr/local/share:/usr/share" + xdg_dirs += data_dirs.split(":") + + dirs += [os.path.join(xdg_dir, "fonts") for xdg_dir in xdg_dirs] + elif sys.platform == "darwin": + dirs += [ + "/Library/Fonts", + "/System/Library/Fonts", + os.path.expanduser("~/Library/Fonts"), + ] + + ext = os.path.splitext(ttf_filename)[1] + first_font_with_a_different_extension = None + for directory in dirs: + for walkroot, walkdir, walkfilenames in os.walk(directory): + for walkfilename in walkfilenames: + if ext and walkfilename == ttf_filename: + return freetype(os.path.join(walkroot, walkfilename)) + elif not ext and os.path.splitext(walkfilename)[0] == ttf_filename: + fontpath = os.path.join(walkroot, walkfilename) + if os.path.splitext(fontpath)[1] == ".ttf": + return freetype(fontpath) + if not ext and first_font_with_a_different_extension is None: + first_font_with_a_different_extension = fontpath + if first_font_with_a_different_extension: + return freetype(first_font_with_a_different_extension) + raise + + +def load_path(filename: str | bytes) -> ImageFont: + """ + 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 OSError: If the file could not be read. + """ + if not isinstance(filename, str): + filename = filename.decode("utf-8") + for directory in sys.path: + try: + return load(os.path.join(directory, filename)) + except OSError: + pass + msg = f'cannot find font file "{filename}" in sys.path' + if os.path.exists(filename): + msg += f', did you mean ImageFont.load("{filename}") instead?' + + raise OSError(msg) + + +def load_default_imagefont() -> ImageFont: + f = ImageFont() + f._load_pilfont_data( + # courB08 + BytesIO( + base64.b64decode( + 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.b64decode( + 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 + + +def load_default(size: float | None = None) -> FreeTypeFont | ImageFont: + """If FreeType support is available, load a version of Aileron Regular, + https://dotcolon.net/fonts/aileron, with a more limited character set. + + Otherwise, load a "better than nothing" font. + + .. versionadded:: 1.1.4 + + :param size: The font size of Aileron Regular. + + .. versionadded:: 10.1.0 + + :return: A font object. + """ + if isinstance(core, ModuleType) or size is not None: + return truetype( + BytesIO( + base64.b64decode( + b""" +AAEAAAAPAIAAAwBwRkZUTYwDlUAAADFoAAAAHEdERUYAqADnAAAo8AAAACRHUE9ThhmITwAAKfgAA +AduR1NVQnHxefoAACkUAAAA4k9TLzJovoHLAAABeAAAAGBjbWFw5lFQMQAAA6gAAAGqZ2FzcP//AA +MAACjoAAAACGdseWYmRXoPAAAGQAAAHfhoZWFkE18ayQAAAPwAAAA2aGhlYQboArEAAAE0AAAAJGh +tdHjjERZ8AAAB2AAAAdBsb2NhuOexrgAABVQAAADqbWF4cAC7AEYAAAFYAAAAIG5hbWUr+h5lAAAk +OAAAA6Jwb3N0D3oPTQAAJ9wAAAEKAAEAAAABGhxJDqIhXw889QALA+gAAAAA0Bqf2QAAAADhCh2h/ +2r/LgOxAyAAAAAIAAIAAAAAAAAAAQAAA8r/GgAAA7j/av9qA7EAAQAAAAAAAAAAAAAAAAAAAHQAAQ +AAAHQAQwAFAAAAAAACAAAAAQABAAAAQAAAAAAAAAADAfoBkAAFAAgCigJYAAAASwKKAlgAAAFeADI +BPgAAAAAFAAAAAAAAAAAAAAcAAAAAAAAAAAAAAABVS1dOAEAAIPsCAwL/GgDIA8oA5iAAAJMAAAAA +AhICsgAAACAAAwH0AAAAAAAAAU0AAADYAAAA8gA5AVMAVgJEAEYCRAA1AuQAKQKOAEAAsAArATsAZ +AE7AB4CMABVAkQAUADc/+EBEgAgANwAJQEv//sCRAApAkQAggJEADwCRAAtAkQAIQJEADkCRAArAk +QAMgJEACwCRAAxANwAJQDc/+ECRABnAkQAUAJEAEQB8wAjA1QANgJ/AB0CcwBkArsALwLFAGQCSwB +kAjcAZALGAC8C2gBkAQgAZAIgADcCYQBkAj8AZANiAGQCzgBkAuEALwJWAGQC3QAvAmsAZAJJADQC +ZAAiAqoAXgJuACADuAAaAnEAGQJFABMCTwAuATMAYgEv//sBJwAiAkQAUAH0ADIBLAApAhMAJAJjA +EoCEQAeAmcAHgIlAB4BIgAVAmcAHgJRAEoA7gA+AOn/8wIKAEoA9wBGA1cASgJRAEoCSgAeAmMASg +JnAB4BSgBKAcsAGAE5ABQCUABCAgIAAQMRAAEB4v/6AgEAAQHOABQBLwBAAPoAYAEvACECRABNA0Y +AJAItAHgBKgAcAkQAUAEsAHQAygAgAi0AOQD3ADYA9wAWAaEANgGhABYCbAAlAYMAeAGDADkA6/9q +AhsAFAIKABUB/QAVAAAAAwAAAAMAAAAcAAEAAAAAAKQAAwABAAAAHAAEAIgAAAAeABAAAwAOAH4Aq +QCrALEAtAC3ALsgGSAdICYgOiBEISL7Av//AAAAIACpAKsAsAC0ALcAuyAYIBwgJiA5IEQhIvsB// +//4/+5/7j/tP+y/7D/reBR4E/gR+A14CzfTwVxAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAEGAAABAAAAAAAAAAECAAAAAgAAAAAAAAAAAAAAAAAAAAEAAAMEBQYHCAkKCwwNDg8QERIT +FBUWFxgZGhscHR4fICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj9AQUJDREVGR0hJSktMT +U5PUFFSU1RVVldYWVpbXF1eX2BhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAA +AAAAAAYnFmAAAAAABlAAAAAAAAAAAAAAAAAAAAAAAAAAAAY2htAAAAAAAAAABrbGlqAAAAAHAAbm9 +ycwBnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmACYAJgAmAD4AUgCCAMoBCgFO +AVwBcgGIAaYBvAHKAdYB6AH2AgwCIAJKAogCpgLWAw4DIgNkA5wDugPUA+gD/AQQBEYEogS8BPoFJ +gVSBWoFgAWwBcoF1gX6BhQGJAZMBmgGiga0BuIHGgdUB2YHkAeiB8AH3AfyCAoIHAgqCDoITghcCG +oIogjSCPoJKglYCXwJwgnqCgIKKApACl4Klgq8CtwLDAs8C1YLjAuyC9oL7gwMDCYMSAxgDKAMrAz +qDQoNTA1mDYQNoA2uDcAN2g3oDfYODA4iDkoOXA5sDnoOnA7EDvwAAAAFAAAAAAH0ArwAAwAGAAkA +DAAPAAAxESERAxMhExcRASELARETAfT6qv6syKr+jgFUqsiqArz9RAGLAP/+1P8B/v3VAP8BLP4CA +P8AAgA5//IAuQKyAAMACwAANyMDMwIyFhQGIiY0oE4MZk84JCQ4JLQB/v3AJDgkJDgAAgBWAeUBPA +LfAAMABwAAEyMnMxcjJzOmRgpagkYKWgHl+vr6AAAAAAIARgAAAf4CsgAbAB8AAAEHMxUjByM3Iwc +jNyM1MzcjNTM3MwczNzMHMxUrAQczAZgdZXEvOi9bLzovWmYdZXEvOi9bLzovWp9bHlsBn4w429vb +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 +EQQ/BCQvEjQCFQZWFSEWQv40AcxCDiUblhP9uSEAAAAAAAAWAQ4AAQAAAAAAAAATACgAAQAAAAAAA +QAHAEwAAQAAAAAAAgAHAGQAAQAAAAAAAwAaAKIAAQAAAAAABAAHAM0AAQAAAAAABQA8AU8AAQAAAA +AABgAPAawAAQAAAAAACAALAdQAAQAAAAAACQALAfgAAQAAAAAACwAXAjQAAQAAAAAADAAXAnwAAwA +BBAkAAAAmAAAAAwABBAkAAQAOADwAAwABBAkAAgAOAFQAAwABBAkAAwA0AGwAAwABBAkABAAOAL0A +AwABBAkABQB4ANUAAwABBAkABgAeAYwAAwABBAkACAAWAbwAAwABBAkACQAWAeAAAwABBAkACwAuA +gQAAwABBAkADAAuAkwATgBvACAAUgBpAGcAaAB0AHMAIABSAGUAcwBlAHIAdgBlAGQALgAATm8gUm +lnaHRzIFJlc2VydmVkLgAAQQBpAGwAZQByAG8AbgAAQWlsZXJvbgAAUgBlAGcAdQBsAGEAcgAAUmV +ndWxhcgAAMQAuADEAMAAyADsAVQBLAFcATgA7AEEAaQBsAGUAcgBvAG4ALQBSAGUAZwB1AGwAYQBy +AAAxLjEwMjtVS1dOO0FpbGVyb24tUmVndWxhcgAAQQBpAGwAZQByAG8AbgAAQWlsZXJvbgAAVgBlA +HIAcwBpAG8AbgAgADEALgAxADAAMgA7AFAAUwAgADAAMAAxAC4AMQAwADIAOwBoAG8AdABjAG8Abg +B2ACAAMQAuADAALgA3ADAAOwBtAGEAawBlAG8AdABmAC4AbABpAGIAMgAuADUALgA1ADgAMwAyADk +AAFZlcnNpb24gMS4xMDI7UFMgMDAxLjEwMjtob3Rjb252IDEuMC43MDttYWtlb3RmLmxpYjIuNS41 +ODMyOQAAQQBpAGwAZQByAG8AbgAtAFIAZQBnAHUAbABhAHIAAEFpbGVyb24tUmVndWxhcgAAUwBvA +HIAYQAgAFMAYQBnAGEAbgBvAABTb3JhIFNhZ2FubwAAUwBvAHIAYQAgAFMAYQBnAGEAbgBvAABTb3 +JhIFNhZ2FubwAAaAB0AHQAcAA6AC8ALwB3AHcAdwAuAGQAbwB0AGMAbwBsAG8AbgAuAG4AZQB0AAB +odHRwOi8vd3d3LmRvdGNvbG9uLm5ldAAAaAB0AHQAcAA6AC8ALwB3AHcAdwAuAGQAbwB0AGMAbwBs +AG8AbgAuAG4AZQB0AABodHRwOi8vd3d3LmRvdGNvbG9uLm5ldAAAAAACAAAAAAAA/4MAMgAAAAAAA +AAAAAAAAAAAAAAAAAAAAHQAAAABAAIAAwAEAAUABgAHAAgACQAKAAsADAANAA4ADwAQABEAEgATAB +QAFQAWABcAGAAZABoAGwAcAB0AHgAfACAAIQAiACMAJAAlACYAJwAoACkAKgArACwALQAuAC8AMAA +xADIAMwA0ADUANgA3ADgAOQA6ADsAPAA9AD4APwBAAEEAQgBDAEQARQBGAEcASABJAEoASwBMAE0A +TgBPAFAAUQBSAFMAVABVAFYAVwBYAFkAWgBbAFwAXQBeAF8AYABhAIsAqQCDAJMAjQDDAKoAtgC3A +LQAtQCrAL4AvwC8AIwAwADBAAAAAAAB//8AAgABAAAADAAAABwAAAACAAIAAwBxAAEAcgBzAAIABA +AAAAIAAAABAAAACgBMAGYAAkRGTFQADmxhdG4AGgAEAAAAAP//AAEAAAAWAANDQVQgAB5NT0wgABZ +ST00gABYAAP//AAEAAAAA//8AAgAAAAEAAmxpZ2EADmxvY2wAFAAAAAEAAQAAAAEAAAACAAYAEAAG +AAAAAgASADQABAAAAAEATAADAAAAAgAQABYAAQAcAAAAAQABAE8AAQABAGcAAQABAE8AAwAAAAIAE +AAWAAEAHAAAAAEAAQAvAAEAAQBnAAEAAQAvAAEAGgABAAgAAgAGAAwAcwACAE8AcgACAEwAAQABAE +kAAAABAAAACgBGAGAAAkRGTFQADmxhdG4AHAAEAAAAAP//AAIAAAABABYAA0NBVCAAFk1PTCAAFlJ +PTSAAFgAA//8AAgAAAAEAAmNwc3AADmtlcm4AFAAAAAEAAAAAAAEAAQACAAYADgABAAAAAQASAAIA +AAACAB4ANgABAAoABQAFAAoAAgABACQAPQAAAAEAEgAEAAAAAQAMAAEAOP/nAAEAAQAkAAIGigAEA +AAFJAXKABoAGQAA//gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAD/sv+4/+z/7v/MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAD/xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/9T/6AAAAAD/8QAA +ABD/vQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/7gAAAAAAAAAAAAAAAAAA//MAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABIAAAAAAAAAAP/5AAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/gAAD/4AAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//L/9AAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAA/+gAAAAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/zAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/mAAAAAAAAAAAAAAAAAAD +/4gAA//AAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/+AAAAAAAAP/OAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/zv/qAAAAAP/0AAAACAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/ZAAD/egAA/1kAAAAA/5D/rgAAAAAAAAAAAA +AAAAAAAAAAAAAAAAD/9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAD/8AAA/7b/8P+wAAD/8P/E/98AAAAA/8P/+P/0//oAAAAAAAAAAAAA//gA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+AAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/w//C/9MAAP/SAAD/9wAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAD/yAAA/+kAAAAA//QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9wAAAAD//QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAP/cAAAAAAAAAAAAAAAA/7YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAP/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/6AAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAkAFAAEAAAAAQACwAAABcA +BgAAAAAAAAAIAA4AAAAAAAsAEgAAAAAAAAATABkAAwANAAAAAQAJAAAAAAAAAAAAAAAAAAAAGAAAA +AAABwAAAAAAAAAAAAAAFQAFAAAAAAAYABgAAAAUAAAACgAAAAwAAgAPABEAFgAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAEAEQBdAAYAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAcAAAAAAAAABwAAAAAACAAAAAAAAAAAAAcAAAAHAAAAEwAJ +ABUADgAPAAAACwAQAAAAAAAAAAAAAAAAAAUAGAACAAIAAgAAAAIAGAAXAAAAGAAAABYAFgACABYAA +gAWAAAAEQADAAoAFAAMAA0ABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASAAAAEgAGAAEAHgAkAC +YAJwApACoALQAuAC8AMgAzADcAOAA5ADoAPAA9AEUASABOAE8AUgBTAFUAVwBZAFoAWwBcAF0AcwA +AAAAAAQAAAADa3tfFAAAAANAan9kAAAAA4QodoQ== +""" + ) + ), + 10 if size is None else size, + layout_engine=Layout.BASIC, + ) + return load_default_imagefont() diff --git a/voxvera/vendor/PIL/ImageGrab.py b/voxvera/vendor/PIL/ImageGrab.py new file mode 100644 index 0000000..4228078 --- /dev/null +++ b/voxvera/vendor/PIL/ImageGrab.py @@ -0,0 +1,224 @@ +# +# The Python Imaging Library +# $Id$ +# +# screen grabber +# +# 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 __future__ import annotations + +import io +import os +import shutil +import subprocess +import sys +import tempfile + +from . import Image + +TYPE_CHECKING = False +if TYPE_CHECKING: + from . import ImageWin + + +def grab( + bbox: tuple[int, int, int, int] | None = None, + include_layered_windows: bool = False, + all_screens: bool = False, + xdisplay: str | None = None, + window: int | ImageWin.HWND | None = None, +) -> Image.Image: + im: Image.Image + if xdisplay is None: + if sys.platform == "darwin": + fh, filepath = tempfile.mkstemp(".png") + os.close(fh) + args = ["screencapture"] + if window: + args += ["-l", str(window)] + elif bbox: + left, top, right, bottom = bbox + args += ["-R", f"{left},{top},{right-left},{bottom-top}"] + subprocess.call(args + ["-x", filepath]) + im = Image.open(filepath) + im.load() + os.unlink(filepath) + if bbox: + if window: + # Determine if the window was in Retina mode or not + # by capturing it without the shadow, + # and checking how different the width is + fh, filepath = tempfile.mkstemp(".png") + os.close(fh) + subprocess.call( + ["screencapture", "-l", str(window), "-o", "-x", filepath] + ) + with Image.open(filepath) as im_no_shadow: + retina = im.width - im_no_shadow.width > 100 + os.unlink(filepath) + + # Since screencapture's -R does not work with -l, + # crop the image manually + if retina: + left, top, right, bottom = bbox + im_cropped = im.resize( + (right - left, bottom - top), + box=tuple(coord * 2 for coord in bbox), + ) + else: + im_cropped = im.crop(bbox) + im.close() + return im_cropped + else: + im_resized = im.resize((right - left, bottom - top)) + im.close() + return im_resized + return im + elif sys.platform == "win32": + if window is not None: + all_screens = -1 + offset, size, data = Image.core.grabscreen_win32( + include_layered_windows, + all_screens, + int(window) if window is not None else 0, + ) + im = Image.frombytes( + "RGB", + size, + data, + # RGB, 32-bit line padding, origin lower left corner + "raw", + "BGR", + (size[0] * 3 + 3) & -4, + -1, + ) + if bbox: + x0, y0 = offset + left, top, right, bottom = bbox + im = im.crop((left - x0, top - y0, right - x0, bottom - y0)) + return im + # Cast to Optional[str] needed for Windows and macOS. + display_name: str | None = xdisplay + try: + if not Image.core.HAVE_XCB: + msg = "Pillow was built without XCB support" + raise OSError(msg) + size, data = Image.core.grabscreen_x11(display_name) + except OSError: + if display_name is None and sys.platform not in ("darwin", "win32"): + if shutil.which("gnome-screenshot"): + args = ["gnome-screenshot", "-f"] + elif shutil.which("grim"): + args = ["grim"] + elif shutil.which("spectacle"): + args = ["spectacle", "-n", "-b", "-f", "-o"] + else: + raise + fh, filepath = tempfile.mkstemp(".png") + os.close(fh) + subprocess.call(args + [filepath]) + im = Image.open(filepath) + im.load() + os.unlink(filepath) + if bbox: + im_cropped = im.crop(bbox) + im.close() + return im_cropped + return im + else: + raise + else: + im = Image.frombytes("RGB", size, data, "raw", "BGRX", size[0] * 4, 1) + if bbox: + im = im.crop(bbox) + return im + + +def grabclipboard() -> Image.Image | list[str] | None: + if sys.platform == "darwin": + p = subprocess.run( + ["osascript", "-e", "get the clipboard as «class PNGf»"], + capture_output=True, + ) + if p.returncode != 0: + return None + + import binascii + + data = io.BytesIO(binascii.unhexlify(p.stdout[11:-3])) + return Image.open(data) + elif sys.platform == "win32": + fmt, data = Image.core.grabclipboard_win32() + if fmt == "file": # CF_HDROP + import struct + + o = struct.unpack_from("I", data)[0] + if data[16] == 0: + files = data[o:].decode("mbcs").split("\0") + else: + files = data[o:].decode("utf-16le").split("\0") + return files[: files.index("")] + if isinstance(data, bytes): + data = io.BytesIO(data) + if fmt == "png": + from . import PngImagePlugin + + return PngImagePlugin.PngImageFile(data) + elif fmt == "DIB": + from . import BmpImagePlugin + + return BmpImagePlugin.DibImageFile(data) + return None + else: + if os.getenv("WAYLAND_DISPLAY"): + session_type = "wayland" + elif os.getenv("DISPLAY"): + session_type = "x11" + else: # Session type check failed + session_type = None + + if shutil.which("wl-paste") and session_type in ("wayland", None): + args = ["wl-paste", "-t", "image"] + elif shutil.which("xclip") and session_type in ("x11", None): + args = ["xclip", "-selection", "clipboard", "-t", "image/png", "-o"] + else: + msg = "wl-paste or xclip is required for ImageGrab.grabclipboard() on Linux" + raise NotImplementedError(msg) + + p = subprocess.run(args, capture_output=True) + if p.returncode != 0: + err = p.stderr + for silent_error in [ + # wl-paste, when the clipboard is empty + b"Nothing is copied", + # Ubuntu/Debian wl-paste, when the clipboard is empty + b"No selection", + # Ubuntu/Debian wl-paste, when an image isn't available + b"No suitable type of content copied", + # wl-paste or Ubuntu/Debian xclip, when an image isn't available + b" not available", + # xclip, when an image isn't available + b"cannot convert ", + # xclip, when the clipboard isn't initialized + b"xclip: Error: There is no owner for the ", + ]: + if silent_error in err: + return None + msg = f"{args[0]} error" + if err: + msg += f": {err.strip().decode()}" + raise ChildProcessError(msg) + + data = io.BytesIO(p.stdout) + im = Image.open(data) + im.load() + return im diff --git a/voxvera/vendor/PIL/ImageMath.py b/voxvera/vendor/PIL/ImageMath.py new file mode 100644 index 0000000..dfdc50c --- /dev/null +++ b/voxvera/vendor/PIL/ImageMath.py @@ -0,0 +1,314 @@ +# +# 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 __future__ import annotations + +import builtins + +from . import Image, _imagingmath + +TYPE_CHECKING = False +if TYPE_CHECKING: + from collections.abc import Callable + from types import CodeType + from typing import Any + + +class _Operand: + """Wraps an image operand, providing standard operators""" + + def __init__(self, im: Image.Image): + self.im = im + + def __fixup(self, im1: _Operand | float) -> Image.Image: + # 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: + msg = f"unsupported mode: {im1.im.mode}" + raise ValueError(msg) + else: + # argument was a constant + if isinstance(im1, (int, float)) 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: str, + im1: _Operand | float, + im2: _Operand | float | None = None, + mode: str | None = None, + ) -> _Operand: + im_1 = self.__fixup(im1) + if im2 is None: + # unary operation + out = Image.new(mode or im_1.mode, im_1.size, None) + try: + op = getattr(_imagingmath, f"{op}_{im_1.mode}") + except AttributeError as e: + msg = f"bad operand type for '{op}'" + raise TypeError(msg) from e + _imagingmath.unop(op, out.getim(), im_1.getim()) + else: + # binary operation + im_2 = self.__fixup(im2) + if im_1.mode != im_2.mode: + # convert both arguments to floating point + if im_1.mode != "F": + im_1 = im_1.convert("F") + if im_2.mode != "F": + im_2 = im_2.convert("F") + if im_1.size != im_2.size: + # crop both arguments to a common size + size = ( + min(im_1.size[0], im_2.size[0]), + min(im_1.size[1], im_2.size[1]), + ) + if im_1.size != size: + im_1 = im_1.crop((0, 0) + size) + if im_2.size != size: + im_2 = im_2.crop((0, 0) + size) + out = Image.new(mode or im_1.mode, im_1.size, None) + try: + op = getattr(_imagingmath, f"{op}_{im_1.mode}") + except AttributeError as e: + msg = f"bad operand type for '{op}'" + raise TypeError(msg) from e + _imagingmath.binop(op, out.getim(), im_1.getim(), im_2.getim()) + return _Operand(out) + + # unary operators + def __bool__(self) -> bool: + # an image is "true" if it contains at least one non-zero pixel + return self.im.getbbox() is not None + + def __abs__(self) -> _Operand: + return self.apply("abs", self) + + def __pos__(self) -> _Operand: + return self + + def __neg__(self) -> _Operand: + return self.apply("neg", self) + + # binary operators + def __add__(self, other: _Operand | float) -> _Operand: + return self.apply("add", self, other) + + def __radd__(self, other: _Operand | float) -> _Operand: + return self.apply("add", other, self) + + def __sub__(self, other: _Operand | float) -> _Operand: + return self.apply("sub", self, other) + + def __rsub__(self, other: _Operand | float) -> _Operand: + return self.apply("sub", other, self) + + def __mul__(self, other: _Operand | float) -> _Operand: + return self.apply("mul", self, other) + + def __rmul__(self, other: _Operand | float) -> _Operand: + return self.apply("mul", other, self) + + def __truediv__(self, other: _Operand | float) -> _Operand: + return self.apply("div", self, other) + + def __rtruediv__(self, other: _Operand | float) -> _Operand: + return self.apply("div", other, self) + + def __mod__(self, other: _Operand | float) -> _Operand: + return self.apply("mod", self, other) + + def __rmod__(self, other: _Operand | float) -> _Operand: + return self.apply("mod", other, self) + + def __pow__(self, other: _Operand | float) -> _Operand: + return self.apply("pow", self, other) + + def __rpow__(self, other: _Operand | float) -> _Operand: + return self.apply("pow", other, self) + + # bitwise + def __invert__(self) -> _Operand: + return self.apply("invert", self) + + def __and__(self, other: _Operand | float) -> _Operand: + return self.apply("and", self, other) + + def __rand__(self, other: _Operand | float) -> _Operand: + return self.apply("and", other, self) + + def __or__(self, other: _Operand | float) -> _Operand: + return self.apply("or", self, other) + + def __ror__(self, other: _Operand | float) -> _Operand: + return self.apply("or", other, self) + + def __xor__(self, other: _Operand | float) -> _Operand: + return self.apply("xor", self, other) + + def __rxor__(self, other: _Operand | float) -> _Operand: + return self.apply("xor", other, self) + + def __lshift__(self, other: _Operand | float) -> _Operand: + return self.apply("lshift", self, other) + + def __rshift__(self, other: _Operand | float) -> _Operand: + return self.apply("rshift", self, other) + + # logical + def __eq__(self, other: _Operand | float) -> _Operand: # type: ignore[override] + return self.apply("eq", self, other) + + def __ne__(self, other: _Operand | float) -> _Operand: # type: ignore[override] + return self.apply("ne", self, other) + + def __lt__(self, other: _Operand | float) -> _Operand: + return self.apply("lt", self, other) + + def __le__(self, other: _Operand | float) -> _Operand: + return self.apply("le", self, other) + + def __gt__(self, other: _Operand | float) -> _Operand: + return self.apply("gt", self, other) + + def __ge__(self, other: _Operand | float) -> _Operand: + return self.apply("ge", self, other) + + +# conversions +def imagemath_int(self: _Operand) -> _Operand: + return _Operand(self.im.convert("I")) + + +def imagemath_float(self: _Operand) -> _Operand: + return _Operand(self.im.convert("F")) + + +# logical +def imagemath_equal(self: _Operand, other: _Operand | float | None) -> _Operand: + return self.apply("eq", self, other, mode="I") + + +def imagemath_notequal(self: _Operand, other: _Operand | float | None) -> _Operand: + return self.apply("ne", self, other, mode="I") + + +def imagemath_min(self: _Operand, other: _Operand | float | None) -> _Operand: + return self.apply("min", self, other) + + +def imagemath_max(self: _Operand, other: _Operand | float | None) -> _Operand: + return self.apply("max", self, other) + + +def imagemath_convert(self: _Operand, mode: str) -> _Operand: + return _Operand(self.im.convert(mode)) + + +ops = { + "int": imagemath_int, + "float": imagemath_float, + "equal": imagemath_equal, + "notequal": imagemath_notequal, + "min": imagemath_min, + "max": imagemath_max, + "convert": imagemath_convert, +} + + +def lambda_eval(expression: Callable[[dict[str, Any]], Any], **kw: Any) -> Any: + """ + Returns the result of an image function. + + :py:mod:`~PIL.ImageMath` only supports single-layer images. To process multi-band + images, use the :py:meth:`~PIL.Image.Image.split` method or + :py:func:`~PIL.Image.merge` function. + + :param expression: A function that receives a dictionary. + :param **kw: Values to add to the function's dictionary. + :return: The expression result. This is usually an image object, but can + also be an integer, a floating point value, or a pixel tuple, + depending on the expression. + """ + + args: dict[str, Any] = ops.copy() + args.update(kw) + for k, v in args.items(): + if isinstance(v, Image.Image): + args[k] = _Operand(v) + + out = expression(args) + try: + return out.im + except AttributeError: + return out + + +def unsafe_eval(expression: str, **kw: Any) -> Any: + """ + Evaluates an image expression. This uses Python's ``eval()`` function to process + the expression string, and carries the security risks of doing so. It is not + recommended to process expressions without considering this. + :py:meth:`~lambda_eval` is a more secure alternative. + + :py:mod:`~PIL.ImageMath` only supports single-layer images. To process multi-band + images, use the :py:meth:`~PIL.Image.Image.split` method or + :py:func:`~PIL.Image.merge` function. + + :param expression: A string containing a Python-style expression. + :param **kw: Values to add to the evaluation context. + :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: dict[str, Any] = ops.copy() + for k in kw: + if "__" in k or hasattr(builtins, k): + msg = f"'{k}' not allowed" + raise ValueError(msg) + + args.update(kw) + for k, v in args.items(): + if isinstance(v, Image.Image): + args[k] = _Operand(v) + + compiled_code = compile(expression, "", "eval") + + def scan(code: CodeType) -> None: + for const in code.co_consts: + if type(const) is type(compiled_code): + scan(const) + + for name in code.co_names: + if name not in args and name != "abs": + msg = f"'{name}' not allowed" + raise ValueError(msg) + + scan(compiled_code) + out = builtins.eval(expression, {"__builtins": {"abs": abs}}, args) + try: + return out.im + except AttributeError: + return out diff --git a/voxvera/vendor/PIL/ImageMode.py b/voxvera/vendor/PIL/ImageMode.py new file mode 100644 index 0000000..b7c6c86 --- /dev/null +++ b/voxvera/vendor/PIL/ImageMode.py @@ -0,0 +1,85 @@ +# +# 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. +# +from __future__ import annotations + +import sys +from functools import lru_cache +from typing import NamedTuple + + +class ModeDescriptor(NamedTuple): + """Wrapper for mode strings.""" + + mode: str + bands: tuple[str, ...] + basemode: str + basetype: str + typestr: str + + def __str__(self) -> str: + return self.mode + + +@lru_cache +def getmode(mode: str) -> ModeDescriptor: + """Gets a mode descriptor for the given mode.""" + endian = "<" if sys.byteorder == "little" else ">" + + modes = { + # core modes + # Bits need to be extended to bytes + "1": ("L", "L", ("1",), "|b1"), + "L": ("L", "L", ("L",), "|u1"), + "I": ("L", "I", ("I",), f"{endian}i4"), + "F": ("L", "F", ("F",), f"{endian}f4"), + "P": ("P", "L", ("P",), "|u1"), + "RGB": ("RGB", "L", ("R", "G", "B"), "|u1"), + "RGBX": ("RGB", "L", ("R", "G", "B", "X"), "|u1"), + "RGBA": ("RGB", "L", ("R", "G", "B", "A"), "|u1"), + "CMYK": ("RGB", "L", ("C", "M", "Y", "K"), "|u1"), + "YCbCr": ("RGB", "L", ("Y", "Cb", "Cr"), "|u1"), + # UNDONE - unsigned |u1i1i1 + "LAB": ("RGB", "L", ("L", "A", "B"), "|u1"), + "HSV": ("RGB", "L", ("H", "S", "V"), "|u1"), + # extra experimental modes + "RGBa": ("RGB", "L", ("R", "G", "B", "a"), "|u1"), + "LA": ("L", "L", ("L", "A"), "|u1"), + "La": ("L", "L", ("L", "a"), "|u1"), + "PA": ("RGB", "L", ("P", "A"), "|u1"), + } + if mode in modes: + base_mode, base_type, bands, type_str = modes[mode] + return ModeDescriptor(mode, bands, base_mode, base_type, type_str) + + mapping_modes = { + # I;16 == I;16L, and I;32 == I;32L + "I;16": "u2", + "I;16BS": ">i2", + "I;16N": f"{endian}u2", + "I;16NS": f"{endian}i2", + "I;32": "u4", + "I;32L": "i4", + "I;32LS": " +from __future__ import annotations + +import re + +from . import Image, _imagingmorph + +LUT_SIZE = 1 << 9 + +# fmt: off +ROTATION_MATRIX = [ + 6, 3, 0, + 7, 4, 1, + 8, 5, 2, +] +MIRROR_MATRIX = [ + 2, 1, 0, + 5, 4, 3, + 8, 7, 6, +] +# fmt: on + + +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: list[str] | None = None, op_name: str | None = None + ) -> None: + """ + :param patterns: A list of input patterns, or None. + :param op_name: The name of a known pattern. One of "corner", "dilation4", + "dilation8", "erosion4", "erosion8" or "edge". + :exception Exception: If the op_name is not recognized. + """ + self.lut: bytearray | None = 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: + msg = f"Unknown pattern {op_name}!" + raise Exception(msg) + + self.patterns = known_patterns[op_name] + elif patterns is not None: + self.patterns = patterns + else: + self.patterns = [] + + def add_patterns(self, patterns: list[str]) -> None: + """ + Append to list of patterns. + + :param patterns: Additional patterns. + """ + self.patterns += patterns + + def build_default_lut(self) -> bytearray: + """ + Set the current LUT, and return it. + + This is the default LUT that patterns will be applied against when building. + """ + symbols = [0, 1] + m = 1 << 4 # pos of current pixel + self.lut = bytearray(symbols[(i & m) > 0] for i in range(LUT_SIZE)) + return self.lut + + def get_lut(self) -> bytearray | None: + """ + Returns the current LUT + """ + return self.lut + + def _string_permute(self, pattern: str, permutation: list[int]) -> str: + """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: str, options: str, basic_result: int + ) -> list[tuple[str, int]]: + """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], ROTATION_MATRIX), res) + ) + # mirror + if "M" in options: + n = len(patterns) + for pattern, res in patterns[:n]: + patterns.append((self._string_permute(pattern, MIRROR_MATRIX), res)) + + # negate + if "N" in options: + n = len(patterns) + for pattern, res in patterns[:n]: + # Swap 0 and 1 + pattern = pattern.replace("0", "Z").replace("1", "0").replace("Z", "1") + res = 1 - int(res) + patterns.append((pattern, res)) + + return patterns + + def build_lut(self) -> bytearray: + """Compile all patterns into a morphology LUT, and return it. + + This is the data to be passed into MorphOp.""" + self.build_default_lut() + assert self.lut is not None + 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: + msg = 'Syntax error in pattern "' + p + '"' + raise Exception(msg) + 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) + + # Compile the patterns into regular expressions for speed + compiled_patterns = [] + for pattern in patterns: + p = pattern[0].replace(".", "X").replace("X", "[01]") + compiled_patterns.append((re.compile(p), pattern[1])) + + # Step through table and find patterns that match. + # Note that all the patterns are searched. The last one found takes priority + for i in range(LUT_SIZE): + # Build the bit pattern + bitpattern = bin(i)[2:] + bitpattern = ("0" * (9 - len(bitpattern)) + bitpattern)[::-1] + + for pattern, r in compiled_patterns: + if pattern.match(bitpattern): + self.lut[i] = [0, 1][r] + + return self.lut + + +class MorphOp: + """A class for binary morphological operators""" + + def __init__( + self, + lut: bytearray | None = None, + op_name: str | None = None, + patterns: list[str] | None = None, + ) -> None: + """Create a binary morphological operator. + + If the LUT is not provided, then it is built using LutBuilder from the op_name + or the patterns. + + :param lut: The LUT data. + :param patterns: A list of input patterns, or None. + :param op_name: The name of a known pattern. One of "corner", "dilation4", + "dilation8", "erosion4", "erosion8", "edge". + :exception Exception: If the op_name is not recognized. + """ + if patterns is None and op_name is None: + self.lut = lut + else: + self.lut = LutBuilder(patterns, op_name).build_lut() + + def apply(self, image: Image.Image) -> tuple[int, Image.Image]: + """Run a single morphological operation on an image. + + Returns a tuple of the number of changed pixels and the + morphed image. + + :param image: A 1-mode or L-mode image. + :exception Exception: If the current operator is None. + :exception ValueError: If the image is not 1 or L mode.""" + if self.lut is None: + msg = "No operator loaded" + raise Exception(msg) + + if image.mode not in ("1", "L"): + msg = "Image mode must be 1 or L" + raise ValueError(msg) + outimage = Image.new(image.mode, image.size) + count = _imagingmorph.apply(bytes(self.lut), image.getim(), outimage.getim()) + return count, outimage + + def match(self, image: Image.Image) -> list[tuple[int, int]]: + """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. See + :ref:`coordinate-system`. + + :param image: A 1-mode or L-mode image. + :exception Exception: If the current operator is None. + :exception ValueError: If the image is not 1 or L mode.""" + if self.lut is None: + msg = "No operator loaded" + raise Exception(msg) + + if image.mode not in ("1", "L"): + msg = "Image mode must be 1 or L" + raise ValueError(msg) + return _imagingmorph.match(bytes(self.lut), image.getim()) + + def get_on_pixels(self, image: Image.Image) -> list[tuple[int, int]]: + """Get a list of all turned on pixels in a 1 or L mode image. + + Returns a list of tuples of (x,y) coordinates of all non-empty pixels. See + :ref:`coordinate-system`. + + :param image: A 1-mode or L-mode image. + :exception ValueError: If the image is not 1 or L mode.""" + + if image.mode not in ("1", "L"): + msg = "Image mode must be 1 or L" + raise ValueError(msg) + return _imagingmorph.get_on_pixels(image.getim()) + + def load_lut(self, filename: str) -> None: + """ + Load an operator from an mrl file + + :param filename: The file to read from. + :exception Exception: If the length of the file data is not 512. + """ + with open(filename, "rb") as f: + self.lut = bytearray(f.read()) + + if len(self.lut) != LUT_SIZE: + self.lut = None + msg = "Wrong size operator file!" + raise Exception(msg) + + def save_lut(self, filename: str) -> None: + """ + Save an operator to an mrl file. + + :param filename: The destination file. + :exception Exception: If the current operator is None. + """ + if self.lut is None: + msg = "No operator loaded" + raise Exception(msg) + with open(filename, "wb") as f: + f.write(self.lut) + + def set_lut(self, lut: bytearray | None) -> None: + """ + Set the LUT from an external source + + :param lut: A new LUT. + """ + self.lut = lut diff --git a/voxvera/vendor/PIL/ImageOps.py b/voxvera/vendor/PIL/ImageOps.py new file mode 100644 index 0000000..42b10bd --- /dev/null +++ b/voxvera/vendor/PIL/ImageOps.py @@ -0,0 +1,746 @@ +# +# 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 __future__ import annotations + +import functools +import operator +import re +from collections.abc import Sequence +from typing import Literal, Protocol, cast, overload + +from . import ExifTags, Image, ImagePalette + +# +# helpers + + +def _border(border: int | tuple[int, ...]) -> tuple[int, int, int, int]: + 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: str | int | tuple[int, ...], mode: str) -> int | tuple[int, ...]: + if isinstance(color, str): + from . import ImageColor + + color = ImageColor.getcolor(color, mode) + return color + + +def _lut(image: Image.Image, lut: list[int]) -> Image.Image: + if image.mode == "P": + # FIXME: apply to lookup table, not image data + msg = "mode P support coming soon" + raise NotImplementedError(msg) + elif image.mode in ("L", "RGB"): + if image.mode == "RGB" and len(lut) == 256: + lut = lut + lut + lut + return image.point(lut) + else: + msg = f"not supported for mode {image.mode}" + raise OSError(msg) + + +# +# actions + + +def autocontrast( + image: Image.Image, + cutoff: float | tuple[float, float] = 0, + ignore: int | Sequence[int] | None = None, + mask: Image.Image | None = None, + preserve_tone: bool = False, +) -> Image.Image: + """ + Maximize (normalize) image contrast. This function calculates a + histogram of the input image (or mask region), 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: The percent to cut off from the histogram on the low and + high ends. Either a tuple of (low, high), or a single + number for both. + :param ignore: The background pixel value (use None for no background). + :param mask: Histogram used in contrast operation is computed using pixels + within the mask. If no mask is given the entire image is used + for histogram computation. + :param preserve_tone: Preserve image tone in Photoshop-like style autocontrast. + + .. versionadded:: 8.2.0 + + :return: An image. + """ + if preserve_tone: + histogram = image.convert("L").histogram(mask) + else: + histogram = image.histogram(mask) + + lut = [] + for layer in range(0, len(histogram), 256): + h = histogram[layer : layer + 256] + if ignore is not None: + # get rid of outliers + if isinstance(ignore, int): + h[ignore] = 0 + else: + for ix in ignore: + h[ix] = 0 + if cutoff: + # cut off pixels from both ends of the histogram + if not isinstance(cutoff, tuple): + cutoff = (cutoff, cutoff) + # get number of pixels + n = 0 + for ix in range(256): + n = n + h[ix] + # remove cutoff% pixels from the low end + cut = int(n * cutoff[0] // 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 high end + cut = int(n * cutoff[1] // 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: Image.Image, + black: str | tuple[int, ...], + white: str | tuple[int, ...], + mid: str | int | tuple[int, ...] | None = None, + blackpoint: int = 0, + whitepoint: int = 255, + midpoint: int = 127, +) -> Image.Image: + """ + Colorize grayscale image. + This function calculates a color wedge which maps all black pixels in + the source image to the first color and all white pixels to the + second color. If ``mid`` is specified, it uses three-color mapping. + The ``black`` and ``white`` arguments should be RGB tuples or color names; + optionally you can use three-color mapping by also specifying ``mid``. + Mapping positions for any of the colors can be specified + (e.g. ``blackpoint``), where these parameters are the integer + value corresponding to where the corresponding color should be mapped. + These parameters must have logical order, such that + ``blackpoint <= midpoint <= whitepoint`` (if ``mid`` is specified). + + :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. + :param mid: The color to use for midtone input pixels. + :param blackpoint: an int value [0, 255] for the black mapping. + :param whitepoint: an int value [0, 255] for the white mapping. + :param midpoint: an int value [0, 255] for the midtone mapping. + :return: An image. + """ + + # Initial asserts + assert image.mode == "L" + if mid is None: + assert 0 <= blackpoint <= whitepoint <= 255 + else: + assert 0 <= blackpoint <= midpoint <= whitepoint <= 255 + + # Define colors from arguments + rgb_black = cast(Sequence[int], _color(black, "RGB")) + rgb_white = cast(Sequence[int], _color(white, "RGB")) + rgb_mid = cast(Sequence[int], _color(mid, "RGB")) if mid is not None else None + + # Empty lists for the mapping + red = [] + green = [] + blue = [] + + # Create the low-end values + for i in range(blackpoint): + red.append(rgb_black[0]) + green.append(rgb_black[1]) + blue.append(rgb_black[2]) + + # Create the mapping (2-color) + if rgb_mid is None: + range_map = range(whitepoint - blackpoint) + + for i in range_map: + red.append( + rgb_black[0] + i * (rgb_white[0] - rgb_black[0]) // len(range_map) + ) + green.append( + rgb_black[1] + i * (rgb_white[1] - rgb_black[1]) // len(range_map) + ) + blue.append( + rgb_black[2] + i * (rgb_white[2] - rgb_black[2]) // len(range_map) + ) + + # Create the mapping (3-color) + else: + range_map1 = range(midpoint - blackpoint) + range_map2 = range(whitepoint - midpoint) + + for i in range_map1: + red.append( + rgb_black[0] + i * (rgb_mid[0] - rgb_black[0]) // len(range_map1) + ) + green.append( + rgb_black[1] + i * (rgb_mid[1] - rgb_black[1]) // len(range_map1) + ) + blue.append( + rgb_black[2] + i * (rgb_mid[2] - rgb_black[2]) // len(range_map1) + ) + for i in range_map2: + red.append(rgb_mid[0] + i * (rgb_white[0] - rgb_mid[0]) // len(range_map2)) + green.append( + rgb_mid[1] + i * (rgb_white[1] - rgb_mid[1]) // len(range_map2) + ) + blue.append(rgb_mid[2] + i * (rgb_white[2] - rgb_mid[2]) // len(range_map2)) + + # Create the high-end values + for i in range(256 - whitepoint): + red.append(rgb_white[0]) + green.append(rgb_white[1]) + blue.append(rgb_white[2]) + + # Return converted image + image = image.convert("RGB") + return _lut(image, red + green + blue) + + +def contain( + image: Image.Image, size: tuple[int, int], method: int = Image.Resampling.BICUBIC +) -> Image.Image: + """ + Returns a resized version of the image, set to the maximum width and height + within the requested size, while maintaining the original aspect ratio. + + :param image: The image to resize. + :param size: The requested output size in pixels, given as a + (width, height) tuple. + :param method: Resampling method to use. Default is + :py:attr:`~PIL.Image.Resampling.BICUBIC`. + See :ref:`concept-filters`. + :return: An image. + """ + + im_ratio = image.width / image.height + dest_ratio = size[0] / size[1] + + if im_ratio != dest_ratio: + if im_ratio > dest_ratio: + new_height = round(image.height / image.width * size[0]) + if new_height != size[1]: + size = (size[0], new_height) + else: + new_width = round(image.width / image.height * size[1]) + if new_width != size[0]: + size = (new_width, size[1]) + return image.resize(size, resample=method) + + +def cover( + image: Image.Image, size: tuple[int, int], method: int = Image.Resampling.BICUBIC +) -> Image.Image: + """ + Returns a resized version of the image, so that the requested size is + covered, while maintaining the original aspect ratio. + + :param image: The image to resize. + :param size: The requested output size in pixels, given as a + (width, height) tuple. + :param method: Resampling method to use. Default is + :py:attr:`~PIL.Image.Resampling.BICUBIC`. + See :ref:`concept-filters`. + :return: An image. + """ + + im_ratio = image.width / image.height + dest_ratio = size[0] / size[1] + + if im_ratio != dest_ratio: + if im_ratio < dest_ratio: + new_height = round(image.height / image.width * size[0]) + if new_height != size[1]: + size = (size[0], new_height) + else: + new_width = round(image.width / image.height * size[1]) + if new_width != size[0]: + size = (new_width, size[1]) + return image.resize(size, resample=method) + + +def pad( + image: Image.Image, + size: tuple[int, int], + method: int = Image.Resampling.BICUBIC, + color: str | int | tuple[int, ...] | None = None, + centering: tuple[float, float] = (0.5, 0.5), +) -> Image.Image: + """ + Returns a resized and padded version of the image, expanded to fill the + requested aspect ratio and size. + + :param image: The image to resize and crop. + :param size: The requested output size in pixels, given as a + (width, height) tuple. + :param method: Resampling method to use. Default is + :py:attr:`~PIL.Image.Resampling.BICUBIC`. + See :ref:`concept-filters`. + :param color: The background color of the padded image. + :param centering: Control the position of the original image within the + padded version. + + (0.5, 0.5) will keep the image centered + (0, 0) will keep the image aligned to the top left + (1, 1) will keep the image aligned to the bottom + right + :return: An image. + """ + + resized = contain(image, size, method) + if resized.size == size: + out = resized + else: + out = Image.new(image.mode, size, color) + if resized.palette: + palette = resized.getpalette() + if palette is not None: + out.putpalette(palette) + if resized.width != size[0]: + x = round((size[0] - resized.width) * max(0, min(centering[0], 1))) + out.paste(resized, (x, 0)) + else: + y = round((size[1] - resized.height) * max(0, min(centering[1], 1))) + out.paste(resized, (0, y)) + return out + + +def crop(image: Image.Image, border: int = 0) -> Image.Image: + """ + 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 scale( + image: Image.Image, factor: float, resample: int = Image.Resampling.BICUBIC +) -> Image.Image: + """ + Returns a rescaled image by a specific factor given in parameter. + A factor greater than 1 expands the image, between 0 and 1 contracts the + image. + + :param image: The image to rescale. + :param factor: The expansion factor, as a float. + :param resample: Resampling method to use. Default is + :py:attr:`~PIL.Image.Resampling.BICUBIC`. + See :ref:`concept-filters`. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + if factor == 1: + return image.copy() + elif factor <= 0: + msg = "the factor must be greater than 0" + raise ValueError(msg) + else: + size = (round(factor * image.width), round(factor * image.height)) + return image.resize(size, resample) + + +class SupportsGetMesh(Protocol): + """ + An object that supports the ``getmesh`` method, taking an image as an + argument, and returning a list of tuples. Each tuple contains two tuples, + the source box as a tuple of 4 integers, and a tuple of 8 integers for the + final quadrilateral, in order of top left, bottom left, bottom right, top + right. + """ + + def getmesh( + self, image: Image.Image + ) -> list[ + tuple[tuple[int, int, int, int], tuple[int, int, int, int, int, int, int, int]] + ]: ... + + +def deform( + image: Image.Image, + deformer: SupportsGetMesh, + resample: int = Image.Resampling.BILINEAR, +) -> Image.Image: + """ + 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: An optional resampling filter. Same values possible as + in the PIL.Image.transform function. + :return: An image. + """ + return image.transform( + image.size, Image.Transform.MESH, deformer.getmesh(image), resample + ) + + +def equalize(image: Image.Image, mask: Image.Image | None = None) -> Image.Image: + """ + 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 = (functools.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: Image.Image, + border: int | tuple[int, ...] = 0, + fill: str | int | tuple[int, ...] = 0, +) -> Image.Image: + """ + 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. + """ + left, top, right, bottom = _border(border) + width = left + image.size[0] + right + height = top + image.size[1] + bottom + color = _color(fill, image.mode) + if image.palette: + mode = image.palette.mode + palette = ImagePalette.ImagePalette(mode, image.getpalette(mode)) + if isinstance(color, tuple) and (len(color) == 3 or len(color) == 4): + color = palette.getcolor(color) + else: + palette = None + out = Image.new(image.mode, (width, height), color) + if palette: + out.putpalette(palette.palette, mode) + out.paste(image, (left, top)) + return out + + +def fit( + image: Image.Image, + size: tuple[int, int], + method: int = Image.Resampling.BICUBIC, + bleed: float = 0.0, + centering: tuple[float, float] = (0.5, 0.5), +) -> Image.Image: + """ + Returns a resized and cropped version of the image, cropped to the + requested aspect ratio and size. + + This function was contributed by Kevin Cazabon. + + :param image: The image to resize and crop. + :param size: The requested output size in pixels, given as a + (width, height) tuple. + :param method: Resampling method to use. Default is + :py:attr:`~PIL.Image.Resampling.BICUBIC`. + See :ref:`concept-filters`. + :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). + Cannot be greater than or equal to 0.5. + :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 + # https://www.cazabon.com + + centering_x, centering_y = centering + + if not 0.0 <= centering_x <= 1.0: + centering_x = 0.5 + if not 0.0 <= centering_y <= 1.0: + centering_y = 0.5 + + if not 0.0 <= bleed < 0.5: + 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 + bleed_pixels = (bleed * image.size[0], bleed * image.size[1]) + + live_size = ( + image.size[0] - bleed_pixels[0] * 2, + image.size[1] - bleed_pixels[1] * 2, + ) + + # calculate the aspect ratio of the live_size + live_size_ratio = live_size[0] / live_size[1] + + # calculate the aspect ratio of the output image + output_ratio = size[0] / size[1] + + # figure out if the sides or top/bottom will be cropped off + if live_size_ratio == output_ratio: + # live_size is already the needed ratio + crop_width = live_size[0] + crop_height = live_size[1] + elif live_size_ratio >= output_ratio: + # live_size is wider than what's needed, crop the sides + crop_width = output_ratio * live_size[1] + crop_height = live_size[1] + else: + # live_size is taller than what's needed, crop the top and bottom + crop_width = live_size[0] + crop_height = live_size[0] / output_ratio + + # make the crop + crop_left = bleed_pixels[0] + (live_size[0] - crop_width) * centering_x + crop_top = bleed_pixels[1] + (live_size[1] - crop_height) * centering_y + + crop = (crop_left, crop_top, crop_left + crop_width, crop_top + crop_height) + + # resize the image and return it + return image.resize(size, method, box=crop) + + +def flip(image: Image.Image) -> Image.Image: + """ + Flip the image vertically (top to bottom). + + :param image: The image to flip. + :return: An image. + """ + return image.transpose(Image.Transpose.FLIP_TOP_BOTTOM) + + +def grayscale(image: Image.Image) -> Image.Image: + """ + Convert the image to grayscale. + + :param image: The image to convert. + :return: An image. + """ + return image.convert("L") + + +def invert(image: Image.Image) -> Image.Image: + """ + Invert (negate) the image. + + :param image: The image to invert. + :return: An image. + """ + lut = list(range(255, -1, -1)) + return image.point(lut) if image.mode == "1" else _lut(image, lut) + + +def mirror(image: Image.Image) -> Image.Image: + """ + Flip image horizontally (left to right). + + :param image: The image to mirror. + :return: An image. + """ + return image.transpose(Image.Transpose.FLIP_LEFT_RIGHT) + + +def posterize(image: Image.Image, bits: int) -> Image.Image: + """ + 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. + """ + mask = ~(2 ** (8 - bits) - 1) + lut = [i & mask for i in range(256)] + return _lut(image, lut) + + +def solarize(image: Image.Image, threshold: int = 128) -> Image.Image: + """ + Invert all pixel values above a threshold. + + :param image: The image to solarize. + :param threshold: All pixels above this grayscale 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) + + +@overload +def exif_transpose(image: Image.Image, *, in_place: Literal[True]) -> None: ... + + +@overload +def exif_transpose( + image: Image.Image, *, in_place: Literal[False] = False +) -> Image.Image: ... + + +def exif_transpose(image: Image.Image, *, in_place: bool = False) -> Image.Image | None: + """ + If an image has an EXIF Orientation tag, other than 1, transpose the image + accordingly, and remove the orientation data. + + :param image: The image to transpose. + :param in_place: Boolean. Keyword-only argument. + If ``True``, the original image is modified in-place, and ``None`` is returned. + If ``False`` (default), a new :py:class:`~PIL.Image.Image` object is returned + with the transposition applied. If there is no transposition, a copy of the + image will be returned. + """ + image.load() + image_exif = image.getexif() + orientation = image_exif.get(ExifTags.Base.Orientation, 1) + method = { + 2: Image.Transpose.FLIP_LEFT_RIGHT, + 3: Image.Transpose.ROTATE_180, + 4: Image.Transpose.FLIP_TOP_BOTTOM, + 5: Image.Transpose.TRANSPOSE, + 6: Image.Transpose.ROTATE_270, + 7: Image.Transpose.TRANSVERSE, + 8: Image.Transpose.ROTATE_90, + }.get(orientation) + if method is not None: + if in_place: + image.im = image.im.transpose(method) + image._size = image.im.size + else: + transposed_image = image.transpose(method) + exif_image = image if in_place else transposed_image + + exif = exif_image.getexif() + if ExifTags.Base.Orientation in exif: + del exif[ExifTags.Base.Orientation] + if "exif" in exif_image.info: + exif_image.info["exif"] = exif.tobytes() + elif "Raw profile type exif" in exif_image.info: + exif_image.info["Raw profile type exif"] = exif.tobytes().hex() + for key in ("XML:com.adobe.xmp", "xmp"): + if key in exif_image.info: + for pattern in ( + r'tiff:Orientation="([0-9])"', + r"([0-9])", + ): + value = exif_image.info[key] + if isinstance(value, str): + value = re.sub(pattern, "", value) + elif isinstance(value, tuple): + value = tuple( + re.sub(pattern.encode(), b"", v) for v in value + ) + else: + value = re.sub(pattern.encode(), b"", value) + exif_image.info[key] = value + if not in_place: + return transposed_image + elif not in_place: + return image.copy() + return None diff --git a/voxvera/vendor/PIL/ImagePalette.py b/voxvera/vendor/PIL/ImagePalette.py new file mode 100644 index 0000000..eae7aea --- /dev/null +++ b/voxvera/vendor/PIL/ImagePalette.py @@ -0,0 +1,287 @@ +# +# 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. +# +from __future__ import annotations + +import array +from collections.abc import Sequence +from typing import IO + +from . import GimpGradientFile, GimpPaletteFile, ImageColor, PaletteFile + +TYPE_CHECKING = False +if TYPE_CHECKING: + from . import Image + + +class ImagePalette: + """ + Color palette for palette mapped images + + :param mode: The mode to use for the palette. See: + :ref:`concept-modes`. Defaults to "RGB" + :param palette: An optional palette. If given, it must be a bytearray, + an array or a list of ints between 0-255. The list must consist of + all channels for one color followed by the next color (e.g. RGBRGBRGB). + Defaults to an empty palette. + """ + + def __init__( + self, + mode: str = "RGB", + palette: Sequence[int] | bytes | bytearray | None = None, + ) -> None: + self.mode = mode + self.rawmode: str | None = None # if set, palette contains raw data + self.palette = palette or bytearray() + self.dirty: int | None = None + + @property + def palette(self) -> Sequence[int] | bytes | bytearray: + return self._palette + + @palette.setter + def palette(self, palette: Sequence[int] | bytes | bytearray) -> None: + self._colors: dict[tuple[int, ...], int] | None = None + self._palette = palette + + @property + def colors(self) -> dict[tuple[int, ...], int]: + if self._colors is None: + mode_len = len(self.mode) + self._colors = {} + for i in range(0, len(self.palette), mode_len): + color = tuple(self.palette[i : i + mode_len]) + if color in self._colors: + continue + self._colors[color] = i // mode_len + return self._colors + + @colors.setter + def colors(self, colors: dict[tuple[int, ...], int]) -> None: + self._colors = colors + + def copy(self) -> ImagePalette: + new = ImagePalette() + + new.mode = self.mode + new.rawmode = self.rawmode + if self.palette is not None: + new.palette = self.palette[:] + new.dirty = self.dirty + + return new + + def getdata(self) -> tuple[str, Sequence[int] | bytes | bytearray]: + """ + 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, self.tobytes() + + def tobytes(self) -> bytes: + """Convert palette to bytes. + + .. warning:: This method is experimental. + """ + if self.rawmode: + msg = "palette contains raw palette data" + raise ValueError(msg) + if isinstance(self.palette, bytes): + return self.palette + arr = array.array("B", self.palette) + return arr.tobytes() + + # Declare tostring as an alias for tobytes + tostring = tobytes + + def _new_color_index( + self, image: Image.Image | None = None, e: Exception | None = None + ) -> int: + if not isinstance(self.palette, bytearray): + self._palette = bytearray(self.palette) + index = len(self.palette) // len(self.mode) + special_colors: tuple[int | tuple[int, ...] | None, ...] = () + if image: + special_colors = ( + image.info.get("background"), + image.info.get("transparency"), + ) + while index in special_colors: + index += 1 + if index >= 256: + if image: + # Search for an unused index + for i, count in reversed(list(enumerate(image.histogram()))): + if count == 0 and i not in special_colors: + index = i + break + if index >= 256: + msg = "cannot allocate more than 256 colors" + raise ValueError(msg) from e + return index + + def getcolor( + self, + color: tuple[int, ...], + image: Image.Image | None = None, + ) -> int: + """Given an rgb tuple, allocate palette entry. + + .. warning:: This method is experimental. + """ + if self.rawmode: + msg = "palette contains raw palette data" + raise ValueError(msg) + if isinstance(color, tuple): + if self.mode == "RGB": + if len(color) == 4: + if color[3] != 255: + msg = "cannot add non-opaque RGBA color to RGB palette" + raise ValueError(msg) + color = color[:3] + elif self.mode == "RGBA": + if len(color) == 3: + color += (255,) + try: + return self.colors[color] + except KeyError as e: + # allocate new color slot + index = self._new_color_index(image, e) + assert isinstance(self._palette, bytearray) + self.colors[color] = index + mode_len = len(self.mode) + if index * mode_len < len(self.palette): + self._palette = ( + self._palette[: index * mode_len] + + bytes(color) + + self._palette[index * mode_len + mode_len :] + ) + else: + self._palette += bytes(color) + self.dirty = 1 + return index + else: + msg = f"unknown color specifier: {repr(color)}" # type: ignore[unreachable] + raise ValueError(msg) + + def save(self, fp: str | IO[str]) -> None: + """Save palette to text file. + + .. warning:: This method is experimental. + """ + if self.rawmode: + msg = "palette contains raw palette data" + raise ValueError(msg) + if isinstance(fp, str): + fp = open(fp, "w") + fp.write("# Palette\n") + fp.write(f"# Mode: {self.mode}\n") + for i in range(256): + fp.write(f"{i}") + for j in range(i * len(self.mode), (i + 1) * len(self.mode)): + try: + fp.write(f" {self.palette[j]}") + except IndexError: + fp.write(" 0") + fp.write("\n") + fp.close() + + +# -------------------------------------------------------------------- +# Internal + + +def raw(rawmode: str, data: Sequence[int] | bytes | bytearray) -> ImagePalette: + palette = ImagePalette() + palette.rawmode = rawmode + palette.palette = data + palette.dirty = 1 + return palette + + +# -------------------------------------------------------------------- +# Factories + + +def make_linear_lut(black: int, white: float) -> list[int]: + if black == 0: + return [int(white * i // 255) for i in range(256)] + + msg = "unavailable when black is non-zero" + raise NotImplementedError(msg) # FIXME + + +def make_gamma_lut(exp: float) -> list[int]: + return [int(((i / 255.0) ** exp) * 255.0 + 0.5) for i in range(256)] + + +def negative(mode: str = "RGB") -> ImagePalette: + palette = list(range(256 * len(mode))) + palette.reverse() + return ImagePalette(mode, [i // len(mode) for i in palette]) + + +def random(mode: str = "RGB") -> ImagePalette: + from random import randint + + palette = [randint(0, 255) for _ in range(256 * len(mode))] + return ImagePalette(mode, palette) + + +def sepia(white: str = "#fff0c0") -> ImagePalette: + bands = [make_linear_lut(0, band) for band in ImageColor.getrgb(white)] + return ImagePalette("RGB", [bands[i % 3][i // 3] for i in range(256 * 3)]) + + +def wedge(mode: str = "RGB") -> ImagePalette: + palette = list(range(256 * len(mode))) + return ImagePalette(mode, [i // len(mode) for i in palette]) + + +def load(filename: str) -> tuple[bytes, str]: + # FIXME: supports GIMP gradients only + + with open(filename, "rb") as fp: + paletteHandlers: list[ + type[ + GimpPaletteFile.GimpPaletteFile + | GimpGradientFile.GimpGradientFile + | PaletteFile.PaletteFile + ] + ] = [ + GimpPaletteFile.GimpPaletteFile, + GimpGradientFile.GimpGradientFile, + PaletteFile.PaletteFile, + ] + for paletteHandler in paletteHandlers: + try: + fp.seek(0) + lut = paletteHandler(fp).getpalette() + if lut: + break + except (SyntaxError, ValueError): + pass + else: + msg = "cannot load palette" + raise OSError(msg) + + return lut # data, rawmode diff --git a/voxvera/vendor/PIL/ImagePath.py b/voxvera/vendor/PIL/ImagePath.py new file mode 100644 index 0000000..77e8a60 --- /dev/null +++ b/voxvera/vendor/PIL/ImagePath.py @@ -0,0 +1,20 @@ +# +# 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 __future__ import annotations + +from . import Image + +Path = Image.core.path diff --git a/voxvera/vendor/PIL/ImageQt.py b/voxvera/vendor/PIL/ImageQt.py new file mode 100644 index 0000000..af4d074 --- /dev/null +++ b/voxvera/vendor/PIL/ImageQt.py @@ -0,0 +1,219 @@ +# +# 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 __future__ import annotations + +import sys +from io import BytesIO + +from . import Image +from ._util import is_path + +TYPE_CHECKING = False +if TYPE_CHECKING: + from collections.abc import Callable + from typing import Any + + from . import ImageFile + + QBuffer: type + +qt_version: str | None +qt_versions = [ + ["6", "PyQt6"], + ["side6", "PySide6"], +] + +# If a version has already been imported, attempt it first +qt_versions.sort(key=lambda version: version[1] in sys.modules, reverse=True) +for version, qt_module in qt_versions: + try: + qRgba: Callable[[int, int, int, int], int] + if qt_module == "PyQt6": + from PyQt6.QtCore import QBuffer, QByteArray, QIODevice + from PyQt6.QtGui import QImage, QPixmap, qRgba + elif qt_module == "PySide6": + from PySide6.QtCore import ( # type: ignore[assignment] + QBuffer, + QByteArray, + QIODevice, + ) + from PySide6.QtGui import QImage, QPixmap, qRgba # type: ignore[assignment] + except (ImportError, RuntimeError): + continue + qt_is_installed = True + qt_version = version + break +else: + qt_is_installed = False + qt_version = None + + +def rgb(r: int, g: int, b: int, a: int = 255) -> int: + """(Internal) Turns an RGB color into a Qt compatible color integer.""" + # 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 + + +def fromqimage(im: QImage | QPixmap) -> ImageFile.ImageFile: + """ + :param im: QImage or PIL ImageQt object + """ + buffer = QBuffer() + qt_openmode: object + if qt_version == "6": + try: + qt_openmode = getattr(QIODevice, "OpenModeFlag") + except AttributeError: + qt_openmode = getattr(QIODevice, "OpenMode") + else: + qt_openmode = QIODevice + buffer.open(getattr(qt_openmode, "ReadWrite")) + # preserve alpha channel with png + # otherwise ppm is more friendly with Image.open + if im.hasAlphaChannel(): + im.save(buffer, "png") + else: + im.save(buffer, "ppm") + + b = BytesIO() + b.write(buffer.data()) + buffer.close() + b.seek(0) + + return Image.open(b) + + +def fromqpixmap(im: QPixmap) -> ImageFile.ImageFile: + return fromqimage(im) + + +def align8to32(bytes: bytes, width: int, mode: str) -> bytes: + """ + converts each scanline of data from 8 bit to 32 bit aligned + """ + + bits_per_pixel = {"1": 1, "L": 8, "P": 8, "I;16": 16}[mode] + + # calculate bytes per line and the extra padding if needed + bits_per_line = bits_per_pixel * width + full_bytes_per_line, remaining_bits_per_line = divmod(bits_per_line, 8) + bytes_per_line = full_bytes_per_line + (1 if remaining_bits_per_line else 0) + + extra_padding = -bytes_per_line % 4 + + # already 32 bit aligned by luck + if not extra_padding: + return bytes + + new_data = [ + bytes[i * bytes_per_line : (i + 1) * bytes_per_line] + b"\x00" * extra_padding + for i in range(len(bytes) // bytes_per_line) + ] + + return b"".join(new_data) + + +def _toqclass_helper(im: Image.Image | str | QByteArray) -> dict[str, Any]: + data = None + colortable = None + exclusive_fp = False + + # handle filename, if given instead of image name + if hasattr(im, "toUtf8"): + # FIXME - is this really the best way to do this? + im = str(im.toUtf8(), "utf-8") + if is_path(im): + im = Image.open(im) + exclusive_fp = True + assert isinstance(im, Image.Image) + + qt_format = getattr(QImage, "Format") if qt_version == "6" else QImage + if im.mode == "1": + format = getattr(qt_format, "Format_Mono") + elif im.mode == "L": + format = getattr(qt_format, "Format_Indexed8") + colortable = [rgb(i, i, i) for i in range(256)] + elif im.mode == "P": + format = getattr(qt_format, "Format_Indexed8") + palette = im.getpalette() + assert palette is not None + colortable = [rgb(*palette[i : i + 3]) for i in range(0, len(palette), 3)] + elif im.mode == "RGB": + # Populate the 4th channel with 255 + im = im.convert("RGBA") + + data = im.tobytes("raw", "BGRA") + format = getattr(qt_format, "Format_RGB32") + elif im.mode == "RGBA": + data = im.tobytes("raw", "BGRA") + format = getattr(qt_format, "Format_ARGB32") + elif im.mode == "I;16": + im = im.point(lambda i: i * 256) + + format = getattr(qt_format, "Format_Grayscale16") + else: + if exclusive_fp: + im.close() + msg = f"unsupported image mode {repr(im.mode)}" + raise ValueError(msg) + + size = im.size + __data = data or align8to32(im.tobytes(), size[0], im.mode) + if exclusive_fp: + im.close() + return {"data": __data, "size": size, "format": format, "colortable": colortable} + + +if qt_is_installed: + + class ImageQt(QImage): + def __init__(self, im: Image.Image | str | QByteArray) -> None: + """ + An PIL image wrapper for Qt. This is a subclass of PyQt's QImage + class. + + :param im: A PIL Image object, or a file name (given either as + Python string or a PyQt string object). + """ + im_data = _toqclass_helper(im) + # must keep a reference, or Qt will crash! + # All QImage constructors that take data operate on an existing + # buffer, so this buffer has to hang on for the life of the image. + # Fixes https://github.com/python-pillow/Pillow/issues/1370 + self.__data = im_data["data"] + super().__init__( + self.__data, + im_data["size"][0], + im_data["size"][1], + im_data["format"], + ) + if im_data["colortable"]: + self.setColorTable(im_data["colortable"]) + + +def toqimage(im: Image.Image | str | QByteArray) -> ImageQt: + return ImageQt(im) + + +def toqpixmap(im: Image.Image | str | QByteArray) -> QPixmap: + qimage = toqimage(im) + pixmap = getattr(QPixmap, "fromImage")(qimage) + if qt_version == "6": + pixmap.detach() + return pixmap diff --git a/voxvera/vendor/PIL/ImageSequence.py b/voxvera/vendor/PIL/ImageSequence.py new file mode 100644 index 0000000..361be48 --- /dev/null +++ b/voxvera/vendor/PIL/ImageSequence.py @@ -0,0 +1,88 @@ +# +# 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. +# + +## +from __future__ import annotations + +from . import Image + +TYPE_CHECKING = False +if TYPE_CHECKING: + from collections.abc import Callable + + +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: Image.Image) -> None: + if not hasattr(im, "seek"): + msg = "im must have seek method" + raise AttributeError(msg) + self.im = im + self.position = getattr(self.im, "_min_frame", 0) + + def __getitem__(self, ix: int) -> Image.Image: + try: + self.im.seek(ix) + return self.im + except EOFError as e: + msg = "end of sequence" + raise IndexError(msg) from e + + def __iter__(self) -> Iterator: + return self + + def __next__(self) -> Image.Image: + try: + self.im.seek(self.position) + self.position += 1 + return self.im + except EOFError as e: + msg = "end of sequence" + raise StopIteration(msg) from e + + +def all_frames( + im: Image.Image | list[Image.Image], + func: Callable[[Image.Image], Image.Image] | None = None, +) -> list[Image.Image]: + """ + Applies a given function to all frames in an image or a list of images. + The frames are returned as a list of separate images. + + :param im: An image, or a list of images. + :param func: The function to apply to all of the image frames. + :returns: A list of images. + """ + if not isinstance(im, list): + im = [im] + + ims = [] + for imSequence in im: + current = imSequence.tell() + + ims += [im_frame.copy() for im_frame in Iterator(imSequence)] + + imSequence.seek(current) + return [func(im) for im in ims] if func else ims diff --git a/voxvera/vendor/PIL/ImageShow.py b/voxvera/vendor/PIL/ImageShow.py new file mode 100644 index 0000000..7705608 --- /dev/null +++ b/voxvera/vendor/PIL/ImageShow.py @@ -0,0 +1,362 @@ +# +# 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 annotations + +import abc +import os +import shutil +import subprocess +import sys +from shlex import quote +from typing import Any + +from . import Image + +_viewers = [] + + +def register(viewer: type[Viewer] | Viewer, order: int = 1) -> None: + """ + The :py:func:`register` function is used to register additional viewers:: + + from PIL import ImageShow + ImageShow.register(MyViewer()) # MyViewer will be used as a last resort + ImageShow.register(MySecondViewer(), 0) # MySecondViewer will be prioritised + ImageShow.register(ImageShow.XVViewer(), 0) # XVViewer will be prioritised + + :param viewer: The viewer to be registered. + :param order: + Zero or a negative integer to prepend this viewer to the list, + a positive integer to append it. + """ + if isinstance(viewer, type) and issubclass(viewer, Viewer): + viewer = viewer() + if order > 0: + _viewers.append(viewer) + else: + _viewers.insert(0, viewer) + + +def show(image: Image.Image, title: str | None = None, **options: Any) -> bool: + r""" + Display a given image. + + :param image: An image object. + :param title: Optional title. Not all viewers can display the title. + :param \**options: Additional viewer options. + :returns: ``True`` if a suitable viewer was found, ``False`` otherwise. + """ + for viewer in _viewers: + if viewer.show(image, title=title, **options): + return True + return False + + +class Viewer: + """Base class for viewers.""" + + # main api + + def show(self, image: Image.Image, **options: Any) -> int: + """ + The main function for displaying an image. + Converts the given image to the target format and displays it. + """ + + if not ( + image.mode in ("1", "RGBA") + or (self.format == "PNG" and image.mode in ("I;16", "LA")) + ): + base = Image.getmodebase(image.mode) + if image.mode != base: + image = image.convert(base) + + return self.show_image(image, **options) + + # hook methods + + format: str | None = None + """The format to convert the image into.""" + options: dict[str, Any] = {} + """Additional options used to convert the image.""" + + def get_format(self, image: Image.Image) -> str | None: + """Return format name, or ``None`` to save as PGM/PPM.""" + return self.format + + def get_command(self, file: str, **options: Any) -> str: + """ + Returns the command used to display the file. + Not implemented in the base class. + """ + msg = "unavailable in base viewer" + raise NotImplementedError(msg) + + def save_image(self, image: Image.Image) -> str: + """Save to temporary file and return filename.""" + return image._dump(format=self.get_format(image), **self.options) + + def show_image(self, image: Image.Image, **options: Any) -> int: + """Display the given image.""" + return self.show_file(self.save_image(image), **options) + + def show_file(self, path: str, **options: Any) -> int: + """ + Display given file. + """ + if not os.path.exists(path): + raise FileNotFoundError + os.system(self.get_command(path, **options)) # nosec + return 1 + + +# -------------------------------------------------------------------- + + +class WindowsViewer(Viewer): + """The default viewer on Windows is the default system application for PNG files.""" + + format = "PNG" + options = {"compress_level": 1, "save_all": True} + + def get_command(self, file: str, **options: Any) -> str: + return ( + f'start "Pillow" /WAIT "{file}" ' + "&& ping -n 4 127.0.0.1 >NUL " + f'&& del /f "{file}"' + ) + + def show_file(self, path: str, **options: Any) -> int: + """ + Display given file. + """ + if not os.path.exists(path): + raise FileNotFoundError + subprocess.Popen( + self.get_command(path, **options), + shell=True, + creationflags=getattr(subprocess, "CREATE_NO_WINDOW"), + ) # nosec + return 1 + + +if sys.platform == "win32": + register(WindowsViewer) + + +class MacViewer(Viewer): + """The default viewer on macOS using ``Preview.app``.""" + + format = "PNG" + options = {"compress_level": 1, "save_all": True} + + def get_command(self, file: str, **options: Any) -> str: + # on darwin open returns immediately resulting in the temp + # file removal while app is opening + command = "open -a Preview.app" + command = f"({command} {quote(file)}; sleep 20; rm -f {quote(file)})&" + return command + + def show_file(self, path: str, **options: Any) -> int: + """ + Display given file. + """ + if not os.path.exists(path): + raise FileNotFoundError + subprocess.call(["open", "-a", "Preview.app", path]) + + pyinstaller = getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS") + executable = (not pyinstaller and sys.executable) or shutil.which("python3") + if executable: + subprocess.Popen( + [ + executable, + "-c", + "import os, sys, time; time.sleep(20); os.remove(sys.argv[1])", + path, + ] + ) + return 1 + + +if sys.platform == "darwin": + register(MacViewer) + + +class UnixViewer(abc.ABC, Viewer): + format = "PNG" + options = {"compress_level": 1, "save_all": True} + + @abc.abstractmethod + def get_command_ex(self, file: str, **options: Any) -> tuple[str, str]: + pass + + def get_command(self, file: str, **options: Any) -> str: + command = self.get_command_ex(file, **options)[0] + return f"{command} {quote(file)}" + + +class XDGViewer(UnixViewer): + """ + The freedesktop.org ``xdg-open`` command. + """ + + def get_command_ex(self, file: str, **options: Any) -> tuple[str, str]: + command = executable = "xdg-open" + return command, executable + + def show_file(self, path: str, **options: Any) -> int: + """ + Display given file. + """ + if not os.path.exists(path): + raise FileNotFoundError + subprocess.Popen(["xdg-open", path]) + return 1 + + +class DisplayViewer(UnixViewer): + """ + The ImageMagick ``display`` command. + This viewer supports the ``title`` parameter. + """ + + def get_command_ex( + self, file: str, title: str | None = None, **options: Any + ) -> tuple[str, str]: + command = executable = "display" + if title: + command += f" -title {quote(title)}" + return command, executable + + def show_file(self, path: str, **options: Any) -> int: + """ + Display given file. + """ + if not os.path.exists(path): + raise FileNotFoundError + args = ["display"] + title = options.get("title") + if title: + args += ["-title", title] + args.append(path) + + subprocess.Popen(args) + return 1 + + +class GmDisplayViewer(UnixViewer): + """The GraphicsMagick ``gm display`` command.""" + + def get_command_ex(self, file: str, **options: Any) -> tuple[str, str]: + executable = "gm" + command = "gm display" + return command, executable + + def show_file(self, path: str, **options: Any) -> int: + """ + Display given file. + """ + if not os.path.exists(path): + raise FileNotFoundError + subprocess.Popen(["gm", "display", path]) + return 1 + + +class EogViewer(UnixViewer): + """The GNOME Image Viewer ``eog`` command.""" + + def get_command_ex(self, file: str, **options: Any) -> tuple[str, str]: + executable = "eog" + command = "eog -n" + return command, executable + + def show_file(self, path: str, **options: Any) -> int: + """ + Display given file. + """ + if not os.path.exists(path): + raise FileNotFoundError + subprocess.Popen(["eog", "-n", path]) + return 1 + + +class XVViewer(UnixViewer): + """ + The X Viewer ``xv`` command. + This viewer supports the ``title`` parameter. + """ + + def get_command_ex( + self, file: str, title: str | None = None, **options: Any + ) -> tuple[str, str]: + # note: xv is pretty outdated. most modern systems have + # imagemagick's display command instead. + command = executable = "xv" + if title: + command += f" -name {quote(title)}" + return command, executable + + def show_file(self, path: str, **options: Any) -> int: + """ + Display given file. + """ + if not os.path.exists(path): + raise FileNotFoundError + args = ["xv"] + title = options.get("title") + if title: + args += ["-name", title] + args.append(path) + + subprocess.Popen(args) + return 1 + + +if sys.platform not in ("win32", "darwin"): # unixoids + if shutil.which("xdg-open"): + register(XDGViewer) + if shutil.which("display"): + register(DisplayViewer) + if shutil.which("gm"): + register(GmDisplayViewer) + if shutil.which("eog"): + register(EogViewer) + if shutil.which("xv"): + register(XVViewer) + + +class IPythonViewer(Viewer): + """The viewer for IPython frontends.""" + + def show_image(self, image: Image.Image, **options: Any) -> int: + ipython_display(image) + return 1 + + +try: + from IPython.display import display as ipython_display +except ImportError: + pass +else: + register(IPythonViewer) + + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Syntax: python3 ImageShow.py imagefile [title]") + sys.exit() + + with Image.open(sys.argv[1]) as im: + print(show(im, *sys.argv[2:])) diff --git a/voxvera/vendor/PIL/ImageStat.py b/voxvera/vendor/PIL/ImageStat.py new file mode 100644 index 0000000..3a1044b --- /dev/null +++ b/voxvera/vendor/PIL/ImageStat.py @@ -0,0 +1,167 @@ +# +# 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. +# +from __future__ import annotations + +import math +from functools import cached_property + +from . import Image + + +class Stat: + def __init__( + self, image_or_list: Image.Image | list[int], mask: Image.Image | None = None + ) -> None: + """ + Calculate statistics for the given image. If a mask is included, + only the regions covered by that mask are included in the + statistics. You can also pass in a previously calculated histogram. + + :param image: A PIL image, or a precalculated histogram. + + .. note:: + + For a PIL image, calculations rely on the + :py:meth:`~PIL.Image.Image.histogram` method. The pixel counts are + grouped into 256 bins, even if the image has more than 8 bits per + channel. So ``I`` and ``F`` mode images have a maximum ``mean``, + ``median`` and ``rms`` of 255, and cannot have an ``extrema`` maximum + of more than 255. + + :param mask: An optional mask. + """ + if isinstance(image_or_list, Image.Image): + self.h = image_or_list.histogram(mask) + elif isinstance(image_or_list, list): + self.h = image_or_list + else: + msg = "first argument must be image or list" # type: ignore[unreachable] + raise TypeError(msg) + self.bands = list(range(len(self.h) // 256)) + + @cached_property + def extrema(self) -> list[tuple[int, int]]: + """ + Min/max values for each band in the image. + + .. note:: + This relies on the :py:meth:`~PIL.Image.Image.histogram` method, and + simply returns the low and high bins used. This is correct for + images with 8 bits per channel, but fails for other modes such as + ``I`` or ``F``. Instead, use :py:meth:`~PIL.Image.Image.getextrema` to + return per-band extrema for the image. This is more correct and + efficient because, for non-8-bit modes, the histogram method uses + :py:meth:`~PIL.Image.Image.getextrema` to determine the bins used. + """ + + def minmax(histogram: list[int]) -> tuple[int, int]: + res_min, res_max = 255, 0 + for i in range(256): + if histogram[i]: + res_min = i + break + for i in range(255, -1, -1): + if histogram[i]: + res_max = i + break + return res_min, res_max + + return [minmax(self.h[i:]) for i in range(0, len(self.h), 256)] + + @cached_property + def count(self) -> list[int]: + """Total number of pixels for each band in the image.""" + return [sum(self.h[i : i + 256]) for i in range(0, len(self.h), 256)] + + @cached_property + def sum(self) -> list[float]: + """Sum of all pixels for each band in the image.""" + + v = [] + for i in range(0, len(self.h), 256): + layer_sum = 0.0 + for j in range(256): + layer_sum += j * self.h[i + j] + v.append(layer_sum) + return v + + @cached_property + def sum2(self) -> list[float]: + """Squared sum of all pixels for each band in the image.""" + + 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 + + @cached_property + def mean(self) -> list[float]: + """Average (arithmetic mean) pixel level for each band in the image.""" + return [self.sum[i] / self.count[i] if self.count[i] else 0 for i in self.bands] + + @cached_property + def median(self) -> list[int]: + """Median pixel level for each band in the image.""" + + v = [] + for i in self.bands: + s = 0 + half = self.count[i] // 2 + b = i * 256 + for j in range(256): + s = s + self.h[b + j] + if s > half: + break + v.append(j) + return v + + @cached_property + def rms(self) -> list[float]: + """RMS (root-mean-square) for each band in the image.""" + return [ + math.sqrt(self.sum2[i] / self.count[i]) if self.count[i] else 0 + for i in self.bands + ] + + @cached_property + def var(self) -> list[float]: + """Variance for each band in the image.""" + return [ + ( + (self.sum2[i] - (self.sum[i] ** 2.0) / self.count[i]) / self.count[i] + if self.count[i] + else 0 + ) + for i in self.bands + ] + + @cached_property + def stddev(self) -> list[float]: + """Standard deviation for each band in the image.""" + return [math.sqrt(self.var[i]) for i in self.bands] + + +Global = Stat # compatibility diff --git a/voxvera/vendor/PIL/ImageText.py b/voxvera/vendor/PIL/ImageText.py new file mode 100644 index 0000000..e6ccd82 --- /dev/null +++ b/voxvera/vendor/PIL/ImageText.py @@ -0,0 +1,320 @@ +from __future__ import annotations + +from . import ImageFont +from ._typing import _Ink + + +class Text: + def __init__( + self, + text: str | bytes, + font: ( + ImageFont.ImageFont + | ImageFont.FreeTypeFont + | ImageFont.TransposedFont + | None + ) = None, + mode: str = "RGB", + spacing: float = 4, + direction: str | None = None, + features: list[str] | None = None, + language: str | None = None, + ) -> None: + """ + :param text: String to be drawn. + :param font: Either an :py:class:`~PIL.ImageFont.ImageFont` instance, + :py:class:`~PIL.ImageFont.FreeTypeFont` instance, + :py:class:`~PIL.ImageFont.TransposedFont` instance or ``None``. If + ``None``, the default font from :py:meth:`.ImageFont.load_default` + will be used. + :param mode: The image mode this will be used with. + :param spacing: The number of pixels between lines. + :param direction: Direction of the text. It can be ``"rtl"`` (right to left), + ``"ltr"`` (left to right) or ``"ttb"`` (top to bottom). + Requires libraqm. + :param features: A list of OpenType font features to be used during text + layout. This is usually used to turn on optional font features + that are not enabled by default, for example ``"dlig"`` or + ``"ss01"``, but can be also used to turn off default font + features, for example ``"-liga"`` to disable ligatures or + ``"-kern"`` to disable kerning. To get all supported + features, see `OpenType docs`_. + Requires libraqm. + :param language: Language of the text. Different languages may use + different glyph shapes or ligatures. This parameter tells + the font which language the text is in, and to apply the + correct substitutions as appropriate, if available. + It should be a `BCP 47 language code`_. + Requires libraqm. + """ + self.text = text + self.font = font or ImageFont.load_default() + + self.mode = mode + self.spacing = spacing + self.direction = direction + self.features = features + self.language = language + + self.embedded_color = False + + self.stroke_width: float = 0 + self.stroke_fill: _Ink | None = None + + def embed_color(self) -> None: + """ + Use embedded color glyphs (COLR, CBDT, SBIX). + """ + if self.mode not in ("RGB", "RGBA"): + msg = "Embedded color supported only in RGB and RGBA modes" + raise ValueError(msg) + self.embedded_color = True + + def stroke(self, width: float = 0, fill: _Ink | None = None) -> None: + """ + :param width: The width of the text stroke. + :param fill: Color to use for the text stroke when drawing. If not given, will + default to the ``fill`` parameter from + :py:meth:`.ImageDraw.ImageDraw.text`. + """ + self.stroke_width = width + self.stroke_fill = fill + + def _get_fontmode(self) -> str: + if self.mode in ("1", "P", "I", "F"): + return "1" + elif self.embedded_color: + return "RGBA" + else: + return "L" + + def get_length(self) -> float: + """ + Returns length (in pixels with 1/64 precision) of text. + + This is the amount by which following text should be offset. + Text bounding box may extend past the length in some fonts, + e.g. when using italics or accents. + + The result is returned as a float; it is a whole number if using basic layout. + + Note that the sum of two lengths may not equal the length of a concatenated + string due to kerning. If you need to adjust for kerning, include the following + character and subtract its length. + + For example, instead of:: + + hello = ImageText.Text("Hello", font).get_length() + world = ImageText.Text("World", font).get_length() + helloworld = ImageText.Text("HelloWorld", font).get_length() + assert hello + world == helloworld + + use:: + + hello = ( + ImageText.Text("HelloW", font).get_length() - + ImageText.Text("W", font).get_length() + ) # adjusted for kerning + world = ImageText.Text("World", font).get_length() + helloworld = ImageText.Text("HelloWorld", font).get_length() + assert hello + world == helloworld + + or disable kerning with (requires libraqm):: + + hello = ImageText.Text("Hello", font, features=["-kern"]).get_length() + world = ImageText.Text("World", font, features=["-kern"]).get_length() + helloworld = ImageText.Text( + "HelloWorld", font, features=["-kern"] + ).get_length() + assert hello + world == helloworld + + :return: Either width for horizontal text, or height for vertical text. + """ + if isinstance(self.text, str): + multiline = "\n" in self.text + else: + multiline = b"\n" in self.text + if multiline: + msg = "can't measure length of multiline text" + raise ValueError(msg) + return self.font.getlength( + self.text, + self._get_fontmode(), + self.direction, + self.features, + self.language, + ) + + def _split( + self, xy: tuple[float, float], anchor: str | None, align: str + ) -> list[tuple[tuple[float, float], str, str | bytes]]: + if anchor is None: + anchor = "lt" if self.direction == "ttb" else "la" + elif len(anchor) != 2: + msg = "anchor must be a 2 character string" + raise ValueError(msg) + + lines = ( + self.text.split("\n") + if isinstance(self.text, str) + else self.text.split(b"\n") + ) + if len(lines) == 1: + return [(xy, anchor, self.text)] + + if anchor[1] in "tb" and self.direction != "ttb": + msg = "anchor not supported for multiline text" + raise ValueError(msg) + + fontmode = self._get_fontmode() + line_spacing = ( + self.font.getbbox( + "A", + fontmode, + None, + self.features, + self.language, + self.stroke_width, + )[3] + + self.stroke_width + + self.spacing + ) + + top = xy[1] + parts = [] + if self.direction == "ttb": + left = xy[0] + for line in lines: + parts.append(((left, top), anchor, line)) + left += line_spacing + else: + widths = [] + max_width: float = 0 + for line in lines: + line_width = self.font.getlength( + line, fontmode, self.direction, self.features, self.language + ) + widths.append(line_width) + max_width = max(max_width, line_width) + + if anchor[1] == "m": + top -= (len(lines) - 1) * line_spacing / 2.0 + elif anchor[1] == "d": + top -= (len(lines) - 1) * line_spacing + + idx = -1 + for line in lines: + left = xy[0] + idx += 1 + width_difference = max_width - widths[idx] + + # align by align parameter + if align in ("left", "justify"): + pass + elif align == "center": + left += width_difference / 2.0 + elif align == "right": + left += width_difference + else: + msg = 'align must be "left", "center", "right" or "justify"' + raise ValueError(msg) + + if ( + align == "justify" + and width_difference != 0 + and idx != len(lines) - 1 + ): + words = ( + line.split(" ") if isinstance(line, str) else line.split(b" ") + ) + if len(words) > 1: + # align left by anchor + if anchor[0] == "m": + left -= max_width / 2.0 + elif anchor[0] == "r": + left -= max_width + + word_widths = [ + self.font.getlength( + word, + fontmode, + self.direction, + self.features, + self.language, + ) + for word in words + ] + word_anchor = "l" + anchor[1] + width_difference = max_width - sum(word_widths) + i = 0 + for word in words: + parts.append(((left, top), word_anchor, word)) + left += word_widths[i] + width_difference / (len(words) - 1) + i += 1 + top += line_spacing + continue + + # align left by anchor + if anchor[0] == "m": + left -= width_difference / 2.0 + elif anchor[0] == "r": + left -= width_difference + parts.append(((left, top), anchor, line)) + top += line_spacing + + return parts + + def get_bbox( + self, + xy: tuple[float, float] = (0, 0), + anchor: str | None = None, + align: str = "left", + ) -> tuple[float, float, float, float]: + """ + Returns bounding box (in pixels) of text. + + Use :py:meth:`get_length` to get the offset of following text with 1/64 pixel + precision. The bounding box includes extra margins for some fonts, e.g. italics + or accents. + + :param xy: The anchor coordinates of the text. + :param anchor: The text anchor alignment. Determines the relative location of + the anchor to the text. The default alignment is top left, + specifically ``la`` for horizontal text and ``lt`` for + vertical text. See :ref:`text-anchors` for details. + :param align: For multiline text, ``"left"``, ``"center"``, ``"right"`` or + ``"justify"`` determines the relative alignment of lines. Use the + ``anchor`` parameter to specify the alignment to ``xy``. + + :return: ``(left, top, right, bottom)`` bounding box + """ + bbox: tuple[float, float, float, float] | None = None + fontmode = self._get_fontmode() + for xy, anchor, line in self._split(xy, anchor, align): + bbox_line = self.font.getbbox( + line, + fontmode, + self.direction, + self.features, + self.language, + self.stroke_width, + anchor, + ) + bbox_line = ( + bbox_line[0] + xy[0], + bbox_line[1] + xy[1], + bbox_line[2] + xy[0], + bbox_line[3] + xy[1], + ) + if bbox is None: + bbox = bbox_line + else: + bbox = ( + min(bbox[0], bbox_line[0]), + min(bbox[1], bbox_line[1]), + max(bbox[2], bbox_line[2]), + max(bbox[3], bbox_line[3]), + ) + + assert bbox is not None + return bbox diff --git a/voxvera/vendor/PIL/ImageTk.py b/voxvera/vendor/PIL/ImageTk.py new file mode 100644 index 0000000..3a4cb81 --- /dev/null +++ b/voxvera/vendor/PIL/ImageTk.py @@ -0,0 +1,266 @@ +# +# 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. +# +from __future__ import annotations + +import tkinter +from io import BytesIO +from typing import Any + +from . import Image, ImageFile + +TYPE_CHECKING = False +if TYPE_CHECKING: + from ._typing import CapsuleType + +# -------------------------------------------------------------------- +# Check for Tkinter interface hooks + + +def _get_image_from_kw(kw: dict[str, Any]) -> ImageFile.ImageFile | None: + source = None + if "file" in kw: + source = kw.pop("file") + elif "data" in kw: + source = BytesIO(kw.pop("data")) + if not source: + return None + return Image.open(source) + + +def _pyimagingtkcall( + command: str, photo: PhotoImage | tkinter.PhotoImage, ptr: CapsuleType +) -> None: + tk = photo.tk + try: + tk.call(command, photo, repr(ptr)) + except tkinter.TclError: + # activate Tkinter hook + # may raise an error if it cannot attach to Tkinter + from . import _imagingtk + + _imagingtk.tkinit(tk.interpaddr()) + tk.call(command, photo, repr(ptr)) + + +# -------------------------------------------------------------------- +# 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: Image.Image | str | None = None, + size: tuple[int, int] | None = None, + **kw: Any, + ) -> None: + # Tk compatibility: file or data + if image is None: + image = _get_image_from_kw(kw) + + if image is None: + msg = "Image is required" + raise ValueError(msg) + elif isinstance(image, str): + mode = image + image = None + + if size is None: + msg = "If first argument is mode, size is required" + raise ValueError(msg) + else: + # got an image instead of a mode + mode = image.mode + if mode == "P": + # palette mapped data + image.apply_transparency() + image.load() + mode = image.palette.mode if image.palette else "RGB" + size = image.size + kw["width"], kw["height"] = size + + 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) -> None: + try: + name = self.__photo.name + except AttributeError: + return + self.__photo.name = None + try: + self.__photo.tk.call("image", "delete", name) + except Exception: + pass # ignore internal errors + + def __str__(self) -> str: + """ + 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) -> int: + """ + Get the width of the image. + + :return: The width, in pixels. + """ + return self.__size[0] + + def height(self) -> int: + """ + Get the height of the image. + + :return: The height, in pixels. + """ + return self.__size[1] + + def paste(self, im: Image.Image) -> 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. + """ + # convert to blittable + ptr = im.getim() + image = im.im + if not image.isblock() or im.mode != self.__mode: + block = Image.core.new_block(self.__mode, im.size) + image.convert2(block, image) # convert directly between buffers + ptr = block.ptr + + _pyimagingtkcall("PyImagingPhoto", self.__photo, ptr) + + +# -------------------------------------------------------------------- +# 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: Image.Image | None = None, **kw: Any) -> None: + # Tk compatibility: file or data + if image is None: + image = _get_image_from_kw(kw) + + if image is None: + msg = "Image is required" + raise ValueError(msg) + self.__mode = image.mode + self.__size = image.size + + self.__photo = tkinter.BitmapImage(data=image.tobitmap(), **kw) + + def __del__(self) -> None: + try: + name = self.__photo.name + except AttributeError: + return + self.__photo.name = None + try: + self.__photo.tk.call("image", "delete", name) + except Exception: + pass # ignore internal errors + + def width(self) -> int: + """ + Get the width of the image. + + :return: The width, in pixels. + """ + return self.__size[0] + + def height(self) -> int: + """ + Get the height of the image. + + :return: The height, in pixels. + """ + return self.__size[1] + + def __str__(self) -> str: + """ + 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: PhotoImage) -> Image.Image: + """Copies the contents of a PhotoImage to a PIL image memory.""" + im = Image.new("RGBA", (photo.width(), photo.height())) + + _pyimagingtkcall("PyImagingPhotoGet", photo, im.getim()) + + return im diff --git a/voxvera/vendor/PIL/ImageTransform.py b/voxvera/vendor/PIL/ImageTransform.py new file mode 100644 index 0000000..fb144ff --- /dev/null +++ b/voxvera/vendor/PIL/ImageTransform.py @@ -0,0 +1,136 @@ +# +# 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 __future__ import annotations + +from collections.abc import Sequence +from typing import Any + +from . import Image + + +class Transform(Image.ImageTransformHandler): + """Base class for other transforms defined in :py:mod:`~PIL.ImageTransform`.""" + + method: Image.Transform + + def __init__(self, data: Sequence[Any]) -> None: + self.data = data + + def getdata(self) -> tuple[Image.Transform, Sequence[int]]: + return self.method, self.data + + def transform( + self, + size: tuple[int, int], + image: Image.Image, + **options: Any, + ) -> Image.Image: + """Perform the transform. Called from :py:meth:`.Image.transform`.""" + # can be overridden + method, data = self.getdata() + return image.transform(size, method, data, **options) + + +class AffineTransform(Transform): + """ + Define an affine image transform. + + This function takes a 6-tuple (a, b, c, d, e, f) which contain the first + two rows from the inverse of 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. + + See :py:meth:`.Image.transform` + + :param matrix: A 6-tuple (a, b, c, d, e, f) containing the first two rows + from the inverse of an affine transform matrix. + """ + + method = Image.Transform.AFFINE + + +class PerspectiveTransform(Transform): + """ + Define a perspective image transform. + + This function takes an 8-tuple (a, b, c, d, e, f, g, h). For each pixel + (x, y) in the output image, the new value is taken from a position + ((a x + b y + c) / (g x + h y + 1), (d x + e y + f) / (g x + h y + 1)) in + the input image, rounded to nearest pixel. + + This function can be used to scale, translate, rotate, and shear the + original image. + + See :py:meth:`.Image.transform` + + :param matrix: An 8-tuple (a, b, c, d, e, f, g, h). + """ + + method = Image.Transform.PERSPECTIVE + + +class ExtentTransform(Transform): + """ + 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. + + See :py:meth:`.Image.transform` + + :param bbox: A 4-tuple (x0, y0, x1, y1) which specifies two points in the + input image's coordinate system. See :ref:`coordinate-system`. + """ + + method = Image.Transform.EXTENT + + +class QuadTransform(Transform): + """ + Define a quad image transform. + + Maps a quadrilateral (a region defined by four corners) from the image to a + rectangle of the given size. + + See :py:meth:`.Image.transform` + + :param xy: An 8-tuple (x0, y0, x1, y1, x2, y2, x3, y3) which contain the + upper left, lower left, lower right, and upper right corner of the + source quadrilateral. + """ + + method = Image.Transform.QUAD + + +class MeshTransform(Transform): + """ + Define a mesh image transform. A mesh transform consists of one or more + individual quad transforms. + + See :py:meth:`.Image.transform` + + :param data: A list of (bbox, quad) tuples. + """ + + method = Image.Transform.MESH diff --git a/voxvera/vendor/PIL/ImageWin.py b/voxvera/vendor/PIL/ImageWin.py new file mode 100644 index 0000000..98c28f2 --- /dev/null +++ b/voxvera/vendor/PIL/ImageWin.py @@ -0,0 +1,247 @@ +# +# 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. +# +from __future__ import annotations + +from . 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: int) -> None: + self.dc = dc + + def __int__(self) -> int: + 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: int) -> None: + self.wnd = wnd + + def __int__(self) -> int: + 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 graylevels + are allocated. For an "RGB" image, a 6x6x6 colour cube is used, together + with 20 graylevels. + + 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: Image.Image | str, size: tuple[int, int] | None = None + ) -> None: + if isinstance(image, str): + mode = image + image = "" + if size is None: + msg = "If first argument is mode, size is required" + raise ValueError(msg) + else: + mode = image.mode + size = image.size + 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: + assert not isinstance(image, str) + self.paste(image) + + def expose(self, handle: int | HDC | HWND) -> None: + """ + 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 + ``CDC.GetHandleAttrib()`` to get a suitable handle. + """ + handle_int = int(handle) + if isinstance(handle, HWND): + dc = self.image.getdc(handle_int) + try: + self.image.expose(dc) + finally: + self.image.releasedc(handle_int, dc) + else: + self.image.expose(handle_int) + + def draw( + self, + handle: int | HDC | HWND, + dst: tuple[int, int, int, int], + src: tuple[int, int, int, int] | None = None, + ) -> 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 src is None: + src = (0, 0) + self.size + handle_int = int(handle) + if isinstance(handle, HWND): + dc = self.image.getdc(handle_int) + try: + self.image.draw(dc, dst, src) + finally: + self.image.releasedc(handle_int, dc) + else: + self.image.draw(handle_int, dst, src) + + def query_palette(self, handle: int | HDC | HWND) -> int: + """ + 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: The number of entries that were changed (if one or more entries, + this indicates that the image should be redrawn). + """ + handle_int = int(handle) + if isinstance(handle, HWND): + handle = self.image.getdc(handle_int) + try: + result = self.image.query_palette(handle) + finally: + self.image.releasedc(handle, handle) + else: + result = self.image.query_palette(handle_int) + return result + + def paste( + self, im: Image.Image, box: tuple[int, int, int, int] | None = None + ) -> 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. See :ref:`coordinate-system`. 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: bytes) -> None: + """ + Load display memory contents from byte data. + + :param buffer: A buffer containing display data (usually + data returned from :py:func:`~PIL.ImageWin.Dib.tobytes`) + """ + self.image.frombytes(buffer) + + def tobytes(self) -> bytes: + """ + Copy display memory contents to bytes object. + + :return: A bytes object containing display data. + """ + return self.image.tobytes() + + +class Window: + """Create a Window with the given title size.""" + + def __init__( + self, title: str = "PIL", width: int | None = None, height: int | None = None + ) -> None: + self.hwnd = Image.core.createwindow( + title, self.__dispatcher, width or 0, height or 0 + ) + + def __dispatcher(self, action: str, *args: int) -> None: + getattr(self, f"ui_handle_{action}")(*args) + + def ui_handle_clear(self, dc: int, x0: int, y0: int, x1: int, y1: int) -> None: + pass + + def ui_handle_damage(self, x0: int, y0: int, x1: int, y1: int) -> None: + pass + + def ui_handle_destroy(self) -> None: + pass + + def ui_handle_repair(self, dc: int, x0: int, y0: int, x1: int, y1: int) -> None: + pass + + def ui_handle_resize(self, width: int, height: int) -> None: + pass + + def mainloop(self) -> None: + Image.core.eventloop() + + +class ImageWindow(Window): + """Create an image window which displays the given image.""" + + def __init__(self, image: Image.Image | Dib, title: str = "PIL") -> None: + if not isinstance(image, Dib): + image = Dib(image) + self.image = image + width, height = image.size + super().__init__(title, width=width, height=height) + + def ui_handle_repair(self, dc: int, x0: int, y0: int, x1: int, y1: int) -> None: + self.image.draw(dc, (x0, y0, x1, y1)) diff --git a/voxvera/vendor/PIL/ImtImagePlugin.py b/voxvera/vendor/PIL/ImtImagePlugin.py new file mode 100644 index 0000000..c4eccee --- /dev/null +++ b/voxvera/vendor/PIL/ImtImagePlugin.py @@ -0,0 +1,103 @@ +# +# 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. +# +from __future__ import annotations + +import re + +from . import Image, ImageFile + +# +# -------------------------------------------------------------------- + +field = re.compile(rb"([a-z]*) ([^ \r\n]*)") + + +## +# Image plugin for IM Tools images. + + +class ImtImageFile(ImageFile.ImageFile): + format = "IMT" + format_description = "IM Tools" + + def _open(self) -> None: + # Quick rejection: if there's not a LF among the first + # 100 bytes, this is (probably) not a text header. + + assert self.fp is not None + + buffer = self.fp.read(100) + if b"\n" not in buffer: + msg = "not an IM file" + raise SyntaxError(msg) + + xsize = ysize = 0 + + while True: + if buffer: + s = buffer[:1] + buffer = buffer[1:] + else: + s = self.fp.read(1) + if not s: + break + + if s == b"\x0c": + # image data begins + self.tile = [ + ImageFile._Tile( + "raw", + (0, 0) + self.size, + self.fp.tell() - len(buffer), + self.mode, + ) + ] + + break + + else: + # read key/value pair + if b"\n" not in buffer: + buffer += self.fp.read(100) + lines = buffer.split(b"\n") + s += lines.pop(0) + buffer = b"\n".join(lines) + if len(s) == 1 or len(s) > 100: + break + if s[0] == ord(b"*"): + continue # comment + + m = field.match(s) + if not m: + break + k, v = m.group(1, 2) + if k == b"width": + xsize = int(v) + self._size = xsize, ysize + elif k == b"height": + ysize = int(v) + self._size = xsize, ysize + elif k == b"pixel" and v == b"n8": + self._mode = "L" + + +# +# -------------------------------------------------------------------- + +Image.register_open(ImtImageFile.format, ImtImageFile) + +# +# no extension registered (".im" is simply too common) diff --git a/voxvera/vendor/PIL/IptcImagePlugin.py b/voxvera/vendor/PIL/IptcImagePlugin.py new file mode 100644 index 0000000..6fc824e --- /dev/null +++ b/voxvera/vendor/PIL/IptcImagePlugin.py @@ -0,0 +1,233 @@ +# +# 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 annotations + +from io import BytesIO +from typing import cast + +from . import Image, ImageFile +from ._binary import i16be as i16 +from ._binary import i32be as i32 + +COMPRESSION = {1: "raw", 5: "jpeg"} + + +# +# Helpers + + +def _i(c: bytes) -> int: + return i32((b"\0\0\0\0" + c)[-4:]) + + +## +# 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: tuple[int, int]) -> int: + return _i(self.info[key]) + + def field(self) -> tuple[tuple[int, int] | None, int]: + # + # get a IPTC field header + assert self.fp is not None + s = self.fp.read(5) + if not s.strip(b"\x00"): + return None, 0 + + tag = s[1], s[2] + + # syntax + if s[0] != 0x1C or tag[0] not in [1, 2, 3, 4, 5, 6, 7, 8, 9, 240]: + msg = "invalid IPTC/NAA file" + raise SyntaxError(msg) + + # field size + size = s[3] + if size > 132: + msg = "illegal field length in IPTC/NAA file" + raise OSError(msg) + 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) -> None: + # load descriptive fields + assert self.fp is not None + 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 self.info: + if isinstance(self.info[tag], list): + self.info[tag].append(tagdata) + else: + self.info[tag] = [self.info[tag], tagdata] + else: + self.info[tag] = tagdata + + # mode + layers = self.info[(3, 60)][0] + component = self.info[(3, 60)][1] + if layers == 1 and not component: + self._mode = "L" + band = None + else: + if layers == 3 and component: + self._mode = "RGB" + elif layers == 4 and component: + self._mode = "CMYK" + if (3, 65) in self.info: + band = self.info[(3, 65)][0] - 1 + else: + band = 0 + + # size + self._size = self.getint((3, 20)), self.getint((3, 30)) + + # compression + try: + compression = COMPRESSION[self.getint((3, 120))] + except KeyError as e: + msg = "Unknown IPTC image compression" + raise OSError(msg) from e + + # tile + if tag == (8, 10): + self.tile = [ + ImageFile._Tile("iptc", (0, 0) + self.size, offset, (compression, band)) + ] + + def load(self) -> Image.core.PixelAccess | None: + if self.tile: + args = self.tile[0].args + assert isinstance(args, tuple) + compression, band = args + + assert self.fp is not None + self.fp.seek(self.tile[0].offset) + + # Copy image data to temporary file + o = BytesIO() + if compression == "raw": + # To simplify access to the extracted file, + # prepend a PPM header + o.write(b"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) + + with Image.open(o) as _im: + if band is not None: + bands = [Image.new("L", _im.size)] * Image.getmodebands(self.mode) + bands[band] = _im + im = Image.merge(self.mode, bands) + else: + im = _im + im.load() + self.im = im.im + self.tile = [] + return ImageFile.ImageFile.load(self) + + +Image.register_open(IptcImageFile.format, IptcImageFile) + +Image.register_extension(IptcImageFile.format, ".iim") + + +def getiptcinfo( + im: ImageFile.ImageFile, +) -> dict[tuple[int, int], bytes | list[bytes]] | None: + """ + Get IPTC information from TIFF, JPEG, or IPTC file. + + :param im: An image containing IPTC data. + :returns: A dictionary containing IPTC information, or None if + no IPTC information block was found. + """ + from . import JpegImagePlugin, TiffImagePlugin + + data = None + + info: dict[tuple[int, int], bytes | list[bytes]] = {} + if isinstance(im, IptcImageFile): + # return info dictionary right away + for k, v in im.info.items(): + if isinstance(k, tuple): + info[k] = v + return info + + elif isinstance(im, JpegImagePlugin.JpegImageFile): + # extract the IPTC/NAA resource + photoshop = im.info.get("photoshop") + if photoshop: + data = photoshop.get(0x0404) + + 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_v2._tagdata[TiffImagePlugin.IPTC_NAA_CHUNK] + except KeyError: + pass + + if data is None: + return None # no properties + + # create an IptcImagePlugin object without initializing it + class FakeImage: + pass + + fake_im = FakeImage() + fake_im.__class__ = IptcImageFile # type: ignore[assignment] + iptc_im = cast(IptcImageFile, fake_im) + + # parse the IPTC information chunk + iptc_im.info = {} + iptc_im.fp = BytesIO(data) + + try: + iptc_im._open() + except (IndexError, KeyError): + pass # expected failure + + for k, v in iptc_im.info.items(): + if isinstance(k, tuple): + info[k] = v + return info diff --git a/voxvera/vendor/PIL/Jpeg2KImagePlugin.py b/voxvera/vendor/PIL/Jpeg2KImagePlugin.py new file mode 100644 index 0000000..d6ec38d --- /dev/null +++ b/voxvera/vendor/PIL/Jpeg2KImagePlugin.py @@ -0,0 +1,448 @@ +# +# The Python Imaging Library +# $Id$ +# +# JPEG2000 file handling +# +# History: +# 2014-03-12 ajh Created +# 2021-06-30 rogermb Extract dpi information from the 'resc' header box +# +# Copyright (c) 2014 Coriolis Systems Limited +# Copyright (c) 2014 Alastair Houghton +# +# See the README file for information on usage and redistribution. +# +from __future__ import annotations + +import io +import os +import struct +from typing import cast + +from . import Image, ImageFile, ImagePalette, _binary + +TYPE_CHECKING = False +if TYPE_CHECKING: + from collections.abc import Callable + from typing import IO + + +class BoxReader: + """ + A small helper class to read fields stored in JPEG2000 header boxes + and to easily step into and read sub-boxes. + """ + + def __init__(self, fp: IO[bytes], length: int = -1) -> None: + self.fp = fp + self.has_length = length >= 0 + self.length = length + self.remaining_in_box = -1 + + def _can_read(self, num_bytes: int) -> bool: + if self.has_length and self.fp.tell() + num_bytes > self.length: + # Outside box: ensure we don't read past the known file length + return False + if self.remaining_in_box >= 0: + # Inside box contents: ensure read does not go past box boundaries + return num_bytes <= self.remaining_in_box + else: + return True # No length known, just read + + def _read_bytes(self, num_bytes: int) -> bytes: + if not self._can_read(num_bytes): + msg = "Not enough data in header" + raise SyntaxError(msg) + + data = self.fp.read(num_bytes) + if len(data) < num_bytes: + msg = f"Expected to read {num_bytes} bytes but only got {len(data)}." + raise OSError(msg) + + if self.remaining_in_box > 0: + self.remaining_in_box -= num_bytes + return data + + def read_fields(self, field_format: str) -> tuple[int | bytes, ...]: + size = struct.calcsize(field_format) + data = self._read_bytes(size) + return struct.unpack(field_format, data) + + def read_boxes(self) -> BoxReader: + size = self.remaining_in_box + data = self._read_bytes(size) + return BoxReader(io.BytesIO(data), size) + + def has_next_box(self) -> bool: + if self.has_length: + return self.fp.tell() + self.remaining_in_box < self.length + else: + return True + + def next_box_type(self) -> bytes: + # Skip the rest of the box if it has not been read + if self.remaining_in_box > 0: + self.fp.seek(self.remaining_in_box, os.SEEK_CUR) + self.remaining_in_box = -1 + + # Read the length and type of the next box + lbox, tbox = cast(tuple[int, bytes], self.read_fields(">I4s")) + if lbox == 1: + lbox = cast(int, self.read_fields(">Q")[0]) + hlen = 16 + else: + hlen = 8 + + if lbox < hlen or not self._can_read(lbox - hlen): + msg = "Invalid header length" + raise SyntaxError(msg) + + self.remaining_in_box = lbox - hlen + return tbox + + +def _parse_codestream(fp: IO[bytes]) -> tuple[tuple[int, int], str]: + """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 = _binary.i16be(hdr) + siz = hdr + fp.read(lsiz - 2) + lsiz, rsiz, xsiz, ysiz, xosiz, yosiz, _, _, _, _, csiz = struct.unpack_from( + ">HHIIIIIIIIH", siz + ) + + size = (xsiz - xosiz, ysiz - yosiz) + if csiz == 1: + ssiz = struct.unpack_from(">B", siz, 38) + if (ssiz[0] & 0x7F) + 1 > 8: + mode = "I;16" + else: + mode = "L" + elif csiz == 2: + mode = "LA" + elif csiz == 3: + mode = "RGB" + elif csiz == 4: + mode = "RGBA" + else: + msg = "unable to determine J2K image mode" + raise SyntaxError(msg) + + return size, mode + + +def _res_to_dpi(num: int, denom: int, exp: int) -> float | None: + """Convert JPEG2000's (numerator, denominator, exponent-base-10) resolution, + calculated as (num / denom) * 10^exp and stored in dots per meter, + to floating-point dots per inch.""" + if denom == 0: + return None + return (254 * num * (10**exp)) / (10000 * denom) + + +def _parse_jp2_header( + fp: IO[bytes], +) -> tuple[ + tuple[int, int], + str, + str | None, + tuple[float, float] | None, + ImagePalette.ImagePalette | None, +]: + """Parse the JP2 header box to extract size, component count, + color space information, and optionally DPI information, + returning a (size, mode, mimetype, dpi) tuple.""" + + # Find the JP2 header box + reader = BoxReader(fp) + header = None + mimetype = None + while reader.has_next_box(): + tbox = reader.next_box_type() + + if tbox == b"jp2h": + header = reader.read_boxes() + break + elif tbox == b"ftyp": + if reader.read_fields(">4s")[0] == b"jpx ": + mimetype = "image/jpx" + assert header is not None + + size = None + mode = None + bpc = None + nc = None + dpi = None # 2-tuple of DPI info, or None + palette = None + + while header.has_next_box(): + tbox = header.next_box_type() + + if tbox == b"ihdr": + height, width, nc, bpc = header.read_fields(">IIHB") + assert isinstance(height, int) + assert isinstance(width, int) + assert isinstance(bpc, int) + size = (width, height) + 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" + elif tbox == b"colr" and nc == 4: + meth, _, _, enumcs = header.read_fields(">BBBI") + if meth == 1 and enumcs == 12: + mode = "CMYK" + elif tbox == b"pclr" and mode in ("L", "LA"): + ne, npc = header.read_fields(">HB") + assert isinstance(ne, int) + assert isinstance(npc, int) + max_bitdepth = 0 + for bitdepth in header.read_fields(">" + ("B" * npc)): + assert isinstance(bitdepth, int) + if bitdepth > max_bitdepth: + max_bitdepth = bitdepth + if max_bitdepth <= 8: + palette = ImagePalette.ImagePalette("RGBA" if npc == 4 else "RGB") + for i in range(ne): + color: list[int] = [] + for value in header.read_fields(">" + ("B" * npc)): + assert isinstance(value, int) + color.append(value) + palette.getcolor(tuple(color)) + mode = "P" if mode == "L" else "PA" + elif tbox == b"res ": + res = header.read_boxes() + while res.has_next_box(): + tres = res.next_box_type() + if tres == b"resc": + vrcn, vrcd, hrcn, hrcd, vrce, hrce = res.read_fields(">HHHHBB") + assert isinstance(vrcn, int) + assert isinstance(vrcd, int) + assert isinstance(hrcn, int) + assert isinstance(hrcd, int) + assert isinstance(vrce, int) + assert isinstance(hrce, int) + hres = _res_to_dpi(hrcn, hrcd, hrce) + vres = _res_to_dpi(vrcn, vrcd, vrce) + if hres is not None and vres is not None: + dpi = (hres, vres) + break + + if size is None or mode is None: + msg = "Malformed JP2 header" + raise SyntaxError(msg) + + return size, mode, mimetype, dpi, palette + + +## +# Image plugin for JPEG2000 images. + + +class Jpeg2KImageFile(ImageFile.ImageFile): + format = "JPEG2000" + format_description = "JPEG 2000 (ISO 15444)" + + def _open(self) -> None: + assert self.fp is not None + sig = self.fp.read(4) + if sig == b"\xff\x4f\xff\x51": + self.codec = "j2k" + self._size, self._mode = _parse_codestream(self.fp) + self._parse_comment() + else: + sig = sig + self.fp.read(8) + + if sig == b"\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a": + self.codec = "jp2" + header = _parse_jp2_header(self.fp) + self._size, self._mode, self.custom_mimetype, dpi, self.palette = header + if dpi is not None: + self.info["dpi"] = dpi + if self.fp.read(12).endswith(b"jp2c\xff\x4f\xff\x51"): + hdr = self.fp.read(2) + length = _binary.i16be(hdr) + self.fp.seek(length - 2, os.SEEK_CUR) + self._parse_comment() + else: + msg = "not a JPEG 2000 file" + raise SyntaxError(msg) + + self._reduce = 0 + self.layers = 0 + + fd = -1 + length = -1 + + try: + fd = self.fp.fileno() + length = os.fstat(fd).st_size + except Exception: + fd = -1 + try: + pos = self.fp.tell() + self.fp.seek(0, io.SEEK_END) + length = self.fp.tell() + self.fp.seek(pos) + except Exception: + length = -1 + + self.tile = [ + ImageFile._Tile( + "jpeg2k", + (0, 0) + self.size, + 0, + (self.codec, self._reduce, self.layers, fd, length), + ) + ] + + def _parse_comment(self) -> None: + assert self.fp is not None + while True: + marker = self.fp.read(2) + if not marker: + break + typ = marker[1] + if typ in (0x90, 0xD9): + # Start of tile or end of codestream + break + hdr = self.fp.read(2) + length = _binary.i16be(hdr) + if typ == 0x64: + # Comment + self.info["comment"] = self.fp.read(length - 2)[2:] + break + else: + self.fp.seek(length - 2, os.SEEK_CUR) + + @property # type: ignore[override] + def reduce( + self, + ) -> ( + Callable[[int | tuple[int, int], tuple[int, int, int, int] | None], Image.Image] + | int + ): + # https://github.com/python-pillow/Pillow/issues/4343 found that the + # new Image 'reduce' method was shadowed by this plugin's 'reduce' + # property. This attempts to allow for both scenarios + return self._reduce or super().reduce + + @reduce.setter + def reduce(self, value: int) -> None: + self._reduce = value + + def load(self) -> Image.core.PixelAccess | None: + if self.tile and self._reduce: + power = 1 << self._reduce + adjust = power >> 1 + self._size = ( + int((self.size[0] + adjust) / power), + int((self.size[1] + adjust) / power), + ) + + # Update the reduce and layers settings + t = self.tile[0] + assert isinstance(t[3], tuple) + t3 = (t[3][0], self._reduce, self.layers, t[3][3], t[3][4]) + self.tile = [ImageFile._Tile(t[0], (0, 0) + self.size, t[2], t3)] + + return ImageFile.ImageFile.load(self) + + +def _accept(prefix: bytes) -> bool: + return prefix.startswith( + (b"\xff\x4f\xff\x51", b"\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a") + ) + + +# ------------------------------------------------------------ +# Save support + + +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + # Get the keyword arguments + info = im.encoderinfo + + if isinstance(filename, str): + filename = filename.encode() + if filename.endswith(b".j2k") or info.get("no_jp2", False): + kind = "j2k" + else: + kind = "jp2" + + 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) + if quality_layers is not None and not ( + isinstance(quality_layers, (list, tuple)) + and all( + isinstance(quality_layer, (int, float)) for quality_layer in quality_layers + ) + ): + msg = "quality_layers must be a sequence of numbers" + raise ValueError(msg) + + 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") + mct = info.get("mct", 0) + signed = info.get("signed", False) + comment = info.get("comment") + if isinstance(comment, str): + comment = comment.encode() + plt = info.get("plt", False) + + fd = -1 + if hasattr(fp, "fileno"): + try: + fd = fp.fileno() + except Exception: + fd = -1 + + im.encoderconfig = ( + offset, + tile_offset, + tile_size, + quality_mode, + quality_layers, + num_resolutions, + cblk_size, + precinct_size, + irreversible, + progression, + cinema_mode, + mct, + signed, + fd, + comment, + plt, + ) + + ImageFile._save(im, fp, [ImageFile._Tile("jpeg2k", (0, 0) + im.size, 0, kind)]) + + +# ------------------------------------------------------------ +# Registry stuff + + +Image.register_open(Jpeg2KImageFile.format, Jpeg2KImageFile, _accept) +Image.register_save(Jpeg2KImageFile.format, _save) + +Image.register_extensions( + Jpeg2KImageFile.format, [".jp2", ".j2k", ".jpc", ".jpf", ".jpx", ".j2c"] +) + +Image.register_mime(Jpeg2KImageFile.format, "image/jp2") diff --git a/voxvera/vendor/PIL/JpegImagePlugin.py b/voxvera/vendor/PIL/JpegImagePlugin.py new file mode 100644 index 0000000..894c154 --- /dev/null +++ b/voxvera/vendor/PIL/JpegImagePlugin.py @@ -0,0 +1,895 @@ +# +# The Python Imaging Library. +# $Id$ +# +# JPEG (JFIF) file handling +# +# See "Digital Compression and Coding of Continuous-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. +# +from __future__ import annotations + +import array +import io +import math +import os +import struct +import subprocess +import sys +import tempfile +import warnings + +from . import Image, ImageFile +from ._binary import i16be as i16 +from ._binary import i32be as i32 +from ._binary import o8 +from ._binary import o16be as o16 +from .JpegPresets import presets + +TYPE_CHECKING = False +if TYPE_CHECKING: + from typing import IO, Any + + from .MpoImagePlugin import MpoImageFile + +# +# Parser + + +def Skip(self: JpegImageFile, marker: int) -> None: + assert self.fp is not None + n = i16(self.fp.read(2)) - 2 + ImageFile._safe_read(self.fp, n) + + +def APP(self: JpegImageFile, marker: int) -> None: + # + # Application marker. Store these in the APP dictionary. + # Also look for well-known application markers. + + assert self.fp is not None + n = i16(self.fp.read(2)) - 2 + s = ImageFile._safe_read(self.fp, n) + + app = f"APP{marker & 15}" + + self.app[app] = s # compatibility + self.applist.append((app, s)) + + if marker == 0xFFE0 and s.startswith(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 = s[7] + jfif_density = i16(s, 8), i16(s, 10) + except Exception: + pass + else: + if jfif_unit == 1: + self.info["dpi"] = jfif_density + elif jfif_unit == 2: # cm + # 1 dpcm = 2.54 dpi + self.info["dpi"] = tuple(d * 2.54 for d in jfif_density) + self.info["jfif_unit"] = jfif_unit + self.info["jfif_density"] = jfif_density + elif marker == 0xFFE1 and s.startswith(b"Exif\0\0"): + # extract EXIF information + if "exif" in self.info: + self.info["exif"] += s[6:] + else: + self.info["exif"] = s + self._exif_offset = self.fp.tell() - n + 6 + elif marker == 0xFFE1 and s.startswith(b"http://ns.adobe.com/xap/1.0/\x00"): + self.info["xmp"] = s.split(b"\x00", 1)[1] + elif marker == 0xFFE2 and s.startswith(b"FPXR\0"): + # extract FlashPix information (incomplete) + self.info["flashpix"] = s # FIXME: value will change + elif marker == 0xFFE2 and s.startswith(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 == 0xFFED and s.startswith(b"Photoshop 3.0\x00"): + # parse the image resource block + offset = 14 + photoshop = self.info.setdefault("photoshop", {}) + while s[offset : offset + 4] == b"8BIM": + try: + offset += 4 + # resource code + code = i16(s, offset) + offset += 2 + # resource name (usually empty) + name_len = s[offset] + # name = s[offset+1:offset+1+name_len] + offset += 1 + name_len + offset += offset & 1 # align + # resource data block + size = i32(s, offset) + offset += 4 + data = s[offset : offset + size] + if code == 0x03ED: # ResolutionInfo + photoshop[code] = { + "XResolution": i32(data, 0) / 65536, + "DisplayedUnitsX": i16(data, 4), + "YResolution": i32(data, 8) / 65536, + "DisplayedUnitsY": i16(data, 12), + } + else: + photoshop[code] = data + offset += size + offset += offset & 1 # align + except struct.error: + break # insufficient data + + elif marker == 0xFFEE and s.startswith(b"Adobe"): + self.info["adobe"] = i16(s, 5) + # extract Adobe custom properties + try: + adobe_transform = s[11] + except IndexError: + pass + else: + self.info["adobe_transform"] = adobe_transform + elif marker == 0xFFE2 and s.startswith(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: JpegImageFile, marker: int) -> None: + # + # Comment marker. Store these in the APP dictionary. + assert self.fp is not None + n = i16(self.fp.read(2)) - 2 + s = ImageFile._safe_read(self.fp, n) + + self.info["comment"] = s + self.app["COM"] = s # compatibility + self.applist.append(("COM", s)) + + +def SOF(self: JpegImageFile, marker: int) -> None: + # + # 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. + + assert self.fp is not None + n = i16(self.fp.read(2)) - 2 + s = ImageFile._safe_read(self.fp, n) + self._size = i16(s, 3), i16(s, 1) + if self._im is not None and self.size != self.im.size: + self._im = None + + self.bits = s[0] + if self.bits != 8: + msg = f"cannot handle {self.bits}-bit layers" + raise SyntaxError(msg) + + self.layers = s[5] + if self.layers == 1: + self._mode = "L" + elif self.layers == 3: + self._mode = "RGB" + elif self.layers == 4: + self._mode = "CMYK" + else: + msg = f"cannot handle {self.layers}-layer images" + raise SyntaxError(msg) + + 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 self.icclist[0][13] == len(self.icclist): + profile = [p[14:] for p in self.icclist] + icc_profile = b"".join(profile) + else: + icc_profile = None # wrong number of fragments + self.info["icc_profile"] = icc_profile + self.icclist = [] + + for i in range(6, len(s), 3): + t = s[i : i + 3] + # 4-tuples: id, vsamp, hsamp, qtable + self.layer.append((t[0], t[1] // 16, t[1] & 15, t[2])) + + +def DQT(self: JpegImageFile, marker: int) -> None: + # + # Define quantization table. Note that there might be more + # than one table in each marker. + + # FIXME: The quantization tables can be used to estimate the + # compression quality. + + assert self.fp is not None + n = i16(self.fp.read(2)) - 2 + s = ImageFile._safe_read(self.fp, n) + while len(s): + v = s[0] + precision = 1 if (v // 16 == 0) else 2 # in bytes + qt_length = 1 + precision * 64 + if len(s) < qt_length: + msg = "bad quantization table marker" + raise SyntaxError(msg) + data = array.array("B" if precision == 1 else "H", s[1:qt_length]) + if sys.byteorder == "little" and precision > 1: + data.byteswap() # the values are always big-endian + self.quantization[v & 15] = [data[i] for i in zigzag_index] + s = s[qt_length:] + + +# +# 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: bytes) -> bool: + # Magic number was taken from https://en.wikipedia.org/wiki/JPEG + return prefix.startswith(b"\xff\xd8\xff") + + +## +# Image plugin for JPEG and JFIF images. + + +class JpegImageFile(ImageFile.ImageFile): + format = "JPEG" + format_description = "JPEG (ISO 10918)" + + def _open(self) -> None: + assert self.fp is not None + s = self.fp.read(3) + + if not _accept(s): + msg = "not a JPEG file" + raise SyntaxError(msg) + s = b"\xff" + + # Create attributes + self.bits = self.layers = 0 + self._exif_offset = 0 + + # JPEG specifics (internal) + self.layer: list[tuple[int, int, int, int]] = [] + self._huffman_dc: dict[Any, Any] = {} + self._huffman_ac: dict[Any, Any] = {} + self.quantization: dict[int, list[int]] = {} + self.app: dict[str, bytes] = {} # compatibility + self.applist: list[tuple[str, bytes]] = [] + self.icclist: list[bytes] = [] + + while True: + i = s[0] + if i == 0xFF: + s = s + self.fp.read(1) + i = i16(s) + else: + # Skip non-0xFF junk + s = self.fp.read(1) + continue + + if i in MARKER: + name, description, handler = MARKER[i] + 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 = [ + ImageFile._Tile("jpeg", (0, 0) + self.size, 0, (rawmode, "")) + ] + # self.__offset = self.fp.tell() + break + s = self.fp.read(1) + elif i in {0, 0xFFFF}: + # padded marker or junk; move on + s = b"\xff" + elif i == 0xFF00: # Skip extraneous data (escaped 0xFF) + s = self.fp.read(1) + else: + msg = "no marker found" + raise SyntaxError(msg) + + self._read_dpi_from_exif() + + def __getstate__(self) -> list[Any]: + return super().__getstate__() + [self.layers, self.layer] + + def __setstate__(self, state: list[Any]) -> None: + self.layers, self.layer = state[6:] + super().__setstate__(state) + + def load_read(self, read_bytes: int) -> bytes: + """ + internal: read more image data + For premature EOF and LOAD_TRUNCATED_IMAGES adds EOI marker + so libjpeg can finish decoding + """ + assert self.fp is not None + s = self.fp.read(read_bytes) + + if not s and ImageFile.LOAD_TRUNCATED_IMAGES and not hasattr(self, "_ended"): + # Premature EOF. + # Pretend file is finished adding EOI marker + self._ended = True + return b"\xff\xd9" + + return s + + def draft( + self, mode: str | None, size: tuple[int, int] | None + ) -> tuple[str, tuple[int, int, float, float]] | None: + if len(self.tile) != 1: + return None + + # Protect from second call + if self.decoderconfig: + return None + + d, e, o, a = self.tile[0] + scale = 1 + original_size = self.size + + assert isinstance(a, tuple) + if a[0] == "RGB" and mode in ["L", "YCbCr"]: + self._mode = mode + a = mode, "" + + if size: + scale = min(self.size[0] // size[0], self.size[1] // size[1]) + for s in [8, 4, 2, 1]: + if scale >= s: + break + assert e is not None + 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 = [ImageFile._Tile(d, e, o, a)] + self.decoderconfig = (scale, 0) + + box = (0, 0, original_size[0] / scale, original_size[1] / scale) + return self.mode, box + + def load_djpeg(self) -> None: + # ALTERNATIVE: handle JPEGs via the IJG command line utilities + + f, path = tempfile.mkstemp() + os.close(f) + if os.path.exists(self.filename): + subprocess.check_call(["djpeg", "-outfile", path, self.filename]) + else: + try: + os.unlink(path) + except OSError: + pass + + msg = "Invalid Filename" + raise ValueError(msg) + + try: + with Image.open(path) as _im: + _im.load() + self.im = _im.im + finally: + try: + os.unlink(path) + except OSError: + pass + + self._mode = self.im.mode + self._size = self.im.size + + self.tile = [] + + def _getexif(self) -> dict[int, Any] | None: + return _getexif(self) + + def _read_dpi_from_exif(self) -> None: + # If DPI isn't in JPEG header, fetch from EXIF + if "dpi" in self.info or "exif" not in self.info: + return + try: + exif = self.getexif() + resolution_unit = exif[0x0128] + x_resolution = exif[0x011A] + try: + dpi = float(x_resolution[0]) / x_resolution[1] + except TypeError: + dpi = x_resolution + if math.isnan(dpi): + msg = "DPI is not a number" + raise ValueError(msg) + if resolution_unit == 3: # cm + # 1 dpcm = 2.54 dpi + dpi *= 2.54 + self.info["dpi"] = dpi, dpi + except ( + struct.error, # truncated EXIF + KeyError, # dpi not included + SyntaxError, # invalid/unreadable EXIF + TypeError, # dpi is an invalid float + ValueError, # dpi is an invalid float + ZeroDivisionError, # invalid dpi rational value + ): + self.info["dpi"] = 72, 72 + + def _getmp(self) -> dict[int, Any] | None: + return _getmp(self) + + +def _getexif(self: JpegImageFile) -> dict[int, Any] | None: + if "exif" not in self.info: + return None + return self.getexif()._get_merged_dict() + + +def _getmp(self: JpegImageFile) -> dict[int, Any] | None: + # 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 plugin. + + # 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_contents = io.BytesIO(data) + head = file_contents.read(8) + endianness = ">" if head.startswith(b"\x4d\x4d\x00\x2a") else "<" + # process dictionary + from . import TiffImagePlugin + + try: + info = TiffImagePlugin.ImageFileDirectory_v2(head) + file_contents.seek(info.next) + info.load(file_contents) + mp = dict(info) + except Exception as e: + msg = "malformed MP Index (unreadable directory)" + raise SyntaxError(msg) from e + # it's an error not to have a number of images + try: + quant = mp[0xB001] + except KeyError as e: + msg = "malformed MP Index (no number of images)" + raise SyntaxError(msg) from e + # get MP entries + mpentries = [] + try: + rawmpentries = mp[0xB002] + for entrynum in range(quant): + unpackedentry = struct.unpack_from( + f"{endianness}LLLHH", rawmpentries, entrynum * 16 + ) + 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: + msg = "unsupported picture format in MPO" + raise SyntaxError(msg) + 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 as e: + msg = "malformed MP Index (bad MP Entry)" + raise SyntaxError(msg) from e + # 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", + "RGBX": "RGB", + "CMYK": "CMYK;I", # assume adobe conventions + "YCbCr": "YCbCr", +} + +# fmt: off +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, +} +# fmt: on + + +def get_sampling(im: Image.Image) -> int: + # There's no subsampling when images have only 1 layer + # (grayscale images) or when they are CMYK (4 layers), + # so set subsampling to the 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 isinstance(im, JpegImageFile) 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: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + if im.width == 0 or im.height == 0: + msg = "cannot write empty image as JPEG" + raise ValueError(msg) + + try: + rawmode = RAWMODE[im.mode] + except KeyError as e: + msg = f"cannot write mode {im.mode} as JPEG" + raise OSError(msg) from e + + info = im.encoderinfo + + dpi = [round(x) for x in info.get("dpi", (0, 0))] + + quality = info.get("quality", -1) + subsampling = info.get("subsampling", -1) + qtables = info.get("qtables") + + if quality == "keep": + quality = -1 + subsampling = "keep" + qtables = "keep" + elif quality in presets: + preset = presets[quality] + quality = -1 + subsampling = preset.get("subsampling", -1) + qtables = preset.get("quantization") + elif not isinstance(quality, int): + msg = "Invalid quality setting" + raise ValueError(msg) + else: + if subsampling in presets: + subsampling = presets[subsampling].get("subsampling", -1) + if isinstance(qtables, str) 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:2:0": + subsampling = 2 + elif subsampling == "4:1:1": + # For compatibility. Before Pillow 4.3, 4:1:1 actually meant 4:2:0. + # Set 4:2:0 if someone is still using that value. + subsampling = 2 + elif subsampling == "keep": + if im.format != "JPEG": + msg = "Cannot use 'keep' when original image is not a JPEG" + raise ValueError(msg) + subsampling = get_sampling(im) + + def validate_qtables( + qtables: ( + str | tuple[list[int], ...] | list[list[int]] | dict[int, list[int]] | None + ), + ) -> list[list[int]] | None: + if qtables is None: + return qtables + if isinstance(qtables, str): + try: + lines = [ + int(num) + for line in qtables.splitlines() + for num in line.split("#", 1)[0].split() + ] + except ValueError as e: + msg = "Invalid quantization table" + raise ValueError(msg) from e + 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 = [ + qtables[key] for key in range(len(qtables)) if key in qtables + ] + elif isinstance(qtables, tuple): + qtables = list(qtables) + if not (0 < len(qtables) < 5): + msg = "None or too many quantization tables" + raise ValueError(msg) + for idx, table in enumerate(qtables): + try: + if len(table) != 64: + msg = "Invalid quantization table" + raise TypeError(msg) + table_array = array.array("H", table) + except TypeError as e: + msg = "Invalid quantization table" + raise ValueError(msg) from e + else: + qtables[idx] = list(table_array) + return qtables + + if qtables == "keep": + if im.format != "JPEG": + msg = "Cannot use 'keep' when original image is not a JPEG" + raise ValueError(msg) + qtables = getattr(im, "quantization", None) + qtables = validate_qtables(qtables) + + extra = info.get("extra", b"") + + MAX_BYTES_IN_MARKER = 65533 + if xmp := info.get("xmp"): + overhead_len = 29 # b"http://ns.adobe.com/xap/1.0/\x00" + max_data_bytes_in_marker = MAX_BYTES_IN_MARKER - overhead_len + if len(xmp) > max_data_bytes_in_marker: + msg = "XMP data is too long" + raise ValueError(msg) + size = o16(2 + overhead_len + len(xmp)) + extra += b"\xff\xe1" + size + b"http://ns.adobe.com/xap/1.0/\x00" + xmp + + if icc_profile := info.get("icc_profile"): + overhead_len = 14 # b"ICC_PROFILE\0" + o8(i) + o8(len(markers)) + max_data_bytes_in_marker = MAX_BYTES_IN_MARKER - 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 = o16(2 + overhead_len + len(marker)) + extra += ( + b"\xff\xe2" + + size + + b"ICC_PROFILE\0" + + o8(i) + + o8(len(markers)) + + marker + ) + i += 1 + + comment = info.get("comment", im.info.get("comment")) + + # "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 = info.get("progressive", False) or info.get("progression", False) + + optimize = info.get("optimize", False) + + exif = info.get("exif", b"") + if isinstance(exif, Image.Exif): + exif = exif.tobytes() + if len(exif) > MAX_BYTES_IN_MARKER: + msg = "EXIF data is too long" + raise ValueError(msg) + + # get keyword arguments + im.encoderconfig = ( + quality, + progressive, + info.get("smooth", 0), + optimize, + info.get("keep_rgb", False), + info.get("streamtype", 0), + dpi, + subsampling, + info.get("restart_marker_blocks", 0), + info.get("restart_marker_rows", 0), + qtables, + comment, + extra, + exif, + ) + + # 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 pixel size is + # channels*size, this is a value that's been used in a django patch. + # https://github.com/matthewwithanm/django-imagekit/issues/50 + if optimize or progressive: + # CMYK can be bigger + if im.mode == "CMYK": + bufsize = 4 * im.size[0] * im.size[1] + # keep sets quality to -1, but the actual value may be high. + elif quality >= 95 or quality == -1: + bufsize = 2 * im.size[0] * im.size[1] + else: + bufsize = im.size[0] * im.size[1] + if exif: + bufsize += len(exif) + 5 + if extra: + bufsize += len(extra) + 1 + else: + # The EXIF info needs to be written as one block, + APP1, + one spare byte. + # Ensure that our buffer is big enough. Same with the icc_profile block. + bufsize = max(len(exif) + 5, len(extra) + 1) + + ImageFile._save( + im, fp, [ImageFile._Tile("jpeg", (0, 0) + im.size, 0, rawmode)], bufsize + ) + + +## +# Factory for making JPEG and MPO instances +def jpeg_factory( + fp: IO[bytes], filename: str | bytes | None = None +) -> JpegImageFile | MpoImageFile: + im = JpegImageFile(fp, filename) + try: + mpheader = im._getmp() + if mpheader is not None and mpheader[45057] > 1: + for segment, content in im.applist: + if segment == "APP1" and b' hdrgm:Version="' in content: + # Ultra HDR images are not yet supported + return im + # It's actually an MPO + from .MpoImagePlugin import MpoImageFile + + # Don't reload everything, just convert it. + im = MpoImageFile.adopt(im, mpheader) + except (TypeError, IndexError): + # It is really a JPEG + pass + except SyntaxError: + warnings.warn( + "Image appears to be a malformed MPO file, it will be " + "interpreted as a base JPEG file" + ) + return im + + +# --------------------------------------------------------------------- +# Registry stuff + +Image.register_open(JpegImageFile.format, jpeg_factory, _accept) +Image.register_save(JpegImageFile.format, _save) + +Image.register_extensions(JpegImageFile.format, [".jfif", ".jpe", ".jpg", ".jpeg"]) + +Image.register_mime(JpegImageFile.format, "image/jpeg") diff --git a/voxvera/vendor/PIL/JpegPresets.py b/voxvera/vendor/PIL/JpegPresets.py new file mode 100644 index 0000000..d0e64a3 --- /dev/null +++ b/voxvera/vendor/PIL/JpegPresets.py @@ -0,0 +1,242 @@ +""" +JPEG quality settings equivalent to the Photoshop settings. +Can be used when saving JPEG files. + +The following presets are available by default: +``web_low``, ``web_medium``, ``web_high``, ``web_very_high``, ``web_maximum``, +``low``, ``medium``, ``high``, ``maximum``. +More presets can be added to the :py:data:`presets` dict if needed. + +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.: https://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:2:0. + +You can get the subsampling of a JPEG with the +:func:`.JpegImagePlugin.get_sampling` function. + +In JPEG compressed data a JPEG marker is used instead of an EXIF tag. +(ref.: https://exiv2.org/tags.html) + + +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.: https://en.wikipedia.org/wiki/Quantization_matrix#Quantization_matrices, +https://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 lists. You can pass this dict +directly as the qtables argument when saving a JPEG. + +The quantization table format in presets is a list with sublists. These formats +are interchangeable. + +Libjpeg ref.: +https://web.archive.org/web/20120328125543/http://www.jpegcameras.com/libjpeg/libjpeg-3.html + +""" + +from __future__ import annotations + +# fmt: off +presets = { + 'web_low': {'subsampling': 2, # "4:2:0" + '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:2:0" + '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:2:0" + '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:2:0" + '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] + ]}, +} +# fmt: on diff --git a/voxvera/vendor/PIL/McIdasImagePlugin.py b/voxvera/vendor/PIL/McIdasImagePlugin.py new file mode 100644 index 0000000..9a47933 --- /dev/null +++ b/voxvera/vendor/PIL/McIdasImagePlugin.py @@ -0,0 +1,78 @@ +# +# 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. +# +from __future__ import annotations + +import struct + +from . import Image, ImageFile + + +def _accept(prefix: bytes) -> bool: + return prefix.startswith(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) -> None: + # parse area file directory + assert self.fp is not None + + s = self.fp.read(256) + if not _accept(s) or len(s) != 256: + msg = "not an McIdas area file" + raise SyntaxError(msg) + + self.area_descriptor_raw = s + self.area_descriptor = w = [0, *struct.unpack("!64i", s)] + + # get mode + if w[11] == 1: + mode = rawmode = "L" + elif w[11] == 2: + mode = rawmode = "I;16B" + elif w[11] == 4: + # FIXME: add memory map support + mode = "I" + rawmode = "I;32B" + else: + msg = "unsupported McIdas format" + raise SyntaxError(msg) + + 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 = [ + ImageFile._Tile("raw", (0, 0) + self.size, offset, (rawmode, stride, 1)) + ] + + +# -------------------------------------------------------------------- +# registry + +Image.register_open(McIdasImageFile.format, McIdasImageFile, _accept) + +# no default extension diff --git a/voxvera/vendor/PIL/MicImagePlugin.py b/voxvera/vendor/PIL/MicImagePlugin.py new file mode 100644 index 0000000..99a07ba --- /dev/null +++ b/voxvera/vendor/PIL/MicImagePlugin.py @@ -0,0 +1,103 @@ +# +# 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. +# +from __future__ import annotations + +import olefile + +from . import Image, TiffImagePlugin + +# +# -------------------------------------------------------------------- + + +def _accept(prefix: bytes) -> bool: + return prefix.startswith(olefile.MAGIC) + + +## +# Image plugin for Microsoft's Image Composer file format. + + +class MicImageFile(TiffImagePlugin.TiffImageFile): + format = "MIC" + format_description = "Microsoft Image Composer" + _close_exclusive_fp_after_loading = False + + def _open(self) -> None: + # read the OLE directory and see if this is a likely + # to be a Microsoft Image Composer file + + try: + self.ole = olefile.OleFileIO(self.fp) + except OSError as e: + msg = "not an MIC file; invalid OLE file" + raise SyntaxError(msg) from e + + # find ACI subfiles with Image members (maybe not the + # best way to identify MIC files, but what the... ;-) + + self.images = [ + path + for path in self.ole.listdir() + if path[1:] and path[0].endswith(".ACI") and path[1] == "Image" + ] + + # if we didn't find any images, this is probably not + # an MIC file. + if not self.images: + msg = "not an MIC file; no image entries" + raise SyntaxError(msg) + + self.frame = -1 + self._n_frames = len(self.images) + self.is_animated = self._n_frames > 1 + + assert self.fp is not None + self.__fp = self.fp + self.seek(0) + + def seek(self, frame: int) -> None: + if not self._seek_check(frame): + return + filename = self.images[frame] + self.fp = self.ole.openstream(filename) + + TiffImagePlugin.TiffImageFile._open(self) + + self.frame = frame + + def tell(self) -> int: + return self.frame + + def close(self) -> None: + self.__fp.close() + self.ole.close() + super().close() + + def __exit__(self, *args: object) -> None: + self.__fp.close() + self.ole.close() + super().__exit__() + + +# +# -------------------------------------------------------------------- + +Image.register_open(MicImageFile.format, MicImageFile, _accept) + +Image.register_extension(MicImageFile.format, ".mic") diff --git a/voxvera/vendor/PIL/MpegImagePlugin.py b/voxvera/vendor/PIL/MpegImagePlugin.py new file mode 100644 index 0000000..47ebe9d --- /dev/null +++ b/voxvera/vendor/PIL/MpegImagePlugin.py @@ -0,0 +1,84 @@ +# +# 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. +# +from __future__ import annotations + +from . import Image, ImageFile +from ._binary import i8 +from ._typing import SupportsRead + +# +# Bitstream parser + + +class BitStream: + def __init__(self, fp: SupportsRead[bytes]) -> None: + self.fp = fp + self.bits = 0 + self.bitbuffer = 0 + + def next(self) -> int: + return i8(self.fp.read(1)) + + def peek(self, bits: int) -> int: + while self.bits < bits: + self.bitbuffer = (self.bitbuffer << 8) + self.next() + self.bits += 8 + return self.bitbuffer >> (self.bits - bits) & (1 << bits) - 1 + + def skip(self, bits: int) -> None: + 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: int) -> int: + v = self.peek(bits) + self.bits = self.bits - bits + return v + + +def _accept(prefix: bytes) -> bool: + return prefix.startswith(b"\x00\x00\x01\xb3") + + +## +# 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) -> None: + assert self.fp is not None + + s = BitStream(self.fp) + if s.read(32) != 0x1B3: + msg = "not an MPEG file" + raise SyntaxError(msg) + + self._mode = "RGB" + self._size = s.read(12), s.read(12) + + +# -------------------------------------------------------------------- +# Registry stuff + +Image.register_open(MpegImageFile.format, MpegImageFile, _accept) + +Image.register_extensions(MpegImageFile.format, [".mpg", ".mpeg"]) + +Image.register_mime(MpegImageFile.format, "video/mpeg") diff --git a/voxvera/vendor/PIL/MpoImagePlugin.py b/voxvera/vendor/PIL/MpoImagePlugin.py new file mode 100644 index 0000000..9360061 --- /dev/null +++ b/voxvera/vendor/PIL/MpoImagePlugin.py @@ -0,0 +1,204 @@ +# +# 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. +# +from __future__ import annotations + +import os +import struct +from typing import IO, Any, cast + +from . import ( + Image, + ImageFile, + ImageSequence, + JpegImagePlugin, + TiffImagePlugin, +) +from ._binary import o32le +from ._util import DeferredError + + +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + JpegImagePlugin._save(im, fp, filename) + + +def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + append_images = im.encoderinfo.get("append_images", []) + if not append_images and not getattr(im, "is_animated", False): + _save(im, fp, filename) + return + + mpf_offset = 28 + offsets: list[int] = [] + im_sequences = [im, *append_images] + total = sum(getattr(seq, "n_frames", 1) for seq in im_sequences) + for im_sequence in im_sequences: + for im_frame in ImageSequence.Iterator(im_sequence): + if not offsets: + # APP2 marker + ifd_length = 66 + 16 * total + im_frame.encoderinfo["extra"] = ( + b"\xff\xe2" + + struct.pack(">H", 6 + ifd_length) + + b"MPF\0" + + b" " * ifd_length + ) + exif = im_frame.encoderinfo.get("exif") + if isinstance(exif, Image.Exif): + exif = exif.tobytes() + im_frame.encoderinfo["exif"] = exif + if exif: + mpf_offset += 4 + len(exif) + + JpegImagePlugin._save(im_frame, fp, filename) + offsets.append(fp.tell()) + else: + encoderinfo = im_frame._attach_default_encoderinfo(im) + im_frame.save(fp, "JPEG") + im_frame.encoderinfo = encoderinfo + offsets.append(fp.tell() - offsets[-1]) + + ifd = TiffImagePlugin.ImageFileDirectory_v2() + ifd[0xB000] = b"0100" + ifd[0xB001] = len(offsets) + + mpentries = b"" + data_offset = 0 + for i, size in enumerate(offsets): + if i == 0: + mptype = 0x030000 # Baseline MP Primary Image + else: + mptype = 0x000000 # Undefined + mpentries += struct.pack(" None: + assert self.fp is not None + self.fp.seek(0) # prep the fp in order to pass the JPEG test + JpegImagePlugin.JpegImageFile._open(self) + self._after_jpeg_open() + + def _after_jpeg_open(self, mpheader: dict[int, Any] | None = None) -> None: + self.mpinfo = mpheader if mpheader is not None else self._getmp() + if self.mpinfo is None: + msg = "Image appears to be a malformed MPO file" + raise ValueError(msg) + self.n_frames = 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.n_frames == len(self.__mpoffsets) + del self.info["mpoffset"] # no longer needed + self.is_animated = self.n_frames > 1 + assert self.fp is not None + 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: int) -> None: + if isinstance(self._fp, DeferredError): + raise self._fp.ex + self._fp.seek(pos) + + def seek(self, frame: int) -> None: + if not self._seek_check(frame): + return + if isinstance(self._fp, DeferredError): + raise self._fp.ex + self.fp = self._fp + self.offset = self.__mpoffsets[frame] + + original_exif = self.info.get("exif") + if "exif" in self.info: + del self.info["exif"] + + self.fp.seek(self.offset + 2) # skip SOI marker + if not self.fp.read(2): + msg = "No data found for frame" + raise ValueError(msg) + self.fp.seek(self.offset) + JpegImagePlugin.JpegImageFile._open(self) + if self.info.get("exif") != original_exif: + self._reload_exif() + + self.tile = [ + ImageFile._Tile("jpeg", (0, 0) + self.size, self.offset, self.tile[0][-1]) + ] + self.__frame = frame + + def tell(self) -> int: + return self.__frame + + @staticmethod + def adopt( + jpeg_instance: JpegImagePlugin.JpegImageFile, + mpheader: dict[int, Any] | None = None, + ) -> MpoImageFile: + """ + Transform the instance of JpegImageFile into + an instance of MpoImageFile. + After the call, the JpegImageFile is extended + to be an MpoImageFile. + + This is essentially useful when opening a JPEG + file that reveals itself as an MPO, to avoid + double call to _open. + """ + jpeg_instance.__class__ = MpoImageFile + mpo_instance = cast(MpoImageFile, jpeg_instance) + mpo_instance._after_jpeg_open(mpheader) + return mpo_instance + + +# --------------------------------------------------------------------- +# 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(MpoImageFile.format, +# JpegImagePlugin.jpeg_factory, _accept) +Image.register_save(MpoImageFile.format, _save) +Image.register_save_all(MpoImageFile.format, _save_all) + +Image.register_extension(MpoImageFile.format, ".mpo") + +Image.register_mime(MpoImageFile.format, "image/mpo") diff --git a/voxvera/vendor/PIL/MspImagePlugin.py b/voxvera/vendor/PIL/MspImagePlugin.py new file mode 100644 index 0000000..277087a --- /dev/null +++ b/voxvera/vendor/PIL/MspImagePlugin.py @@ -0,0 +1,200 @@ +# +# The Python Imaging Library. +# +# 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 +# 17-02-21 es Fixed RLE interpretation +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1995-97. +# Copyright (c) Eric Soroos 2017. +# +# See the README file for information on usage and redistribution. +# +# More info on this format: https://archive.org/details/gg243631 +# Page 313: +# Figure 205. Windows Paint Version 1: "DanM" Format +# Figure 206. Windows Paint Version 2: "LinS" Format. Used in Windows V2.03 +# +# See also: https://www.fileformat.info/format/mspaint/egff.htm +from __future__ import annotations + +import io +import struct +from typing import IO + +from . import Image, ImageFile +from ._binary import i16le as i16 +from ._binary import o16le as o16 + +# +# read MSP files + + +def _accept(prefix: bytes) -> bool: + return prefix.startswith((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) -> None: + # Header + assert self.fp is not None + + s = self.fp.read(32) + if not _accept(s): + msg = "not an MSP file" + raise SyntaxError(msg) + + # Header checksum + checksum = 0 + for i in range(0, 32, 2): + checksum = checksum ^ i16(s, i) + if checksum != 0: + msg = "bad MSP checksum" + raise SyntaxError(msg) + + self._mode = "1" + self._size = i16(s, 4), i16(s, 6) + + if s.startswith(b"DanM"): + self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 32, "1")] + else: + self.tile = [ImageFile._Tile("MSP", (0, 0) + self.size, 32)] + + +class MspDecoder(ImageFile.PyDecoder): + # The algo for the MSP decoder is from + # https://www.fileformat.info/format/mspaint/egff.htm + # cc-by-attribution -- That page references is taken from the + # Encyclopedia of Graphics File Formats and is licensed by + # O'Reilly under the Creative Common/Attribution license + # + # For RLE encoded files, the 32byte header is followed by a scan + # line map, encoded as one 16bit word of encoded byte length per + # line. + # + # NOTE: the encoded length of the line can be 0. This was not + # handled in the previous version of this encoder, and there's no + # mention of how to handle it in the documentation. From the few + # examples I've seen, I've assumed that it is a fill of the + # background color, in this case, white. + # + # + # Pseudocode of the decoder: + # Read a BYTE value as the RunType + # If the RunType value is zero + # Read next byte as the RunCount + # Read the next byte as the RunValue + # Write the RunValue byte RunCount times + # If the RunType value is non-zero + # Use this value as the RunCount + # Read and write the next RunCount bytes literally + # + # e.g.: + # 0x00 03 ff 05 00 01 02 03 04 + # would yield the bytes: + # 0xff ff ff 00 01 02 03 04 + # + # which are then interpreted as a bit packed mode '1' image + + _pulls_fd = True + + def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: + assert self.fd is not None + + img = io.BytesIO() + blank_line = bytearray((0xFF,) * ((self.state.xsize + 7) // 8)) + try: + self.fd.seek(32) + rowmap = struct.unpack_from( + f"<{self.state.ysize}H", self.fd.read(self.state.ysize * 2) + ) + except struct.error as e: + msg = "Truncated MSP file in row map" + raise OSError(msg) from e + + for x, rowlen in enumerate(rowmap): + try: + if rowlen == 0: + img.write(blank_line) + continue + row = self.fd.read(rowlen) + if len(row) != rowlen: + msg = f"Truncated MSP file, expected {rowlen} bytes on row {x}" + raise OSError(msg) + idx = 0 + while idx < rowlen: + runtype = row[idx] + idx += 1 + if runtype == 0: + (runcount, runval) = struct.unpack_from("Bc", row, idx) + img.write(runval * runcount) + idx += 2 + else: + runcount = runtype + img.write(row[idx : idx + runcount]) + idx += runcount + + except struct.error as e: + msg = f"Corrupted MSP file in row {x}" + raise OSError(msg) from e + + self.set_as_raw(img.getvalue(), "1") + + return -1, 0 + + +Image.register_decoder("MSP", MspDecoder) + + +# +# write MSP files (uncompressed only) + + +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + if im.mode != "1": + msg = f"cannot write mode {im.mode} as MSP" + raise OSError(msg) + + # 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 + + checksum = 0 + for h in header: + checksum = checksum ^ h + header[12] = checksum # FIXME: is this the right field? + + # header + for h in header: + fp.write(o16(h)) + + # image body + ImageFile._save(im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 32, "1")]) + + +# +# registry + +Image.register_open(MspImageFile.format, MspImageFile, _accept) +Image.register_save(MspImageFile.format, _save) + +Image.register_extension(MspImageFile.format, ".msp") diff --git a/voxvera/vendor/PIL/PSDraw.py b/voxvera/vendor/PIL/PSDraw.py new file mode 100644 index 0000000..7fd4c5c --- /dev/null +++ b/voxvera/vendor/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 annotations + +import sys +from typing import IO + +from . import EpsImagePlugin + +TYPE_CHECKING = False + + +## +# Simple PostScript graphics interface. + + +class PSDraw: + """ + Sets up printing to the given file. If ``fp`` is omitted, + ``sys.stdout.buffer`` is assumed. + """ + + def __init__(self, fp: IO[bytes] | None = None) -> None: + if not fp: + fp = sys.stdout.buffer + self.fp = fp + + def begin_document(self, id: str | None = None) -> None: + """Set up printing of a document. (Write PostScript DSC header.)""" + # FIXME: incomplete + self.fp.write( + b"%!PS-Adobe-3.0\n" + b"save\n" + b"/showpage { } def\n" + b"%%EndComments\n" + b"%%BeginDocument\n" + ) + # self.fp.write(ERROR_PS) # debugging! + self.fp.write(EDROFF_PS) + self.fp.write(VDI_PS) + self.fp.write(b"%%EndProlog\n") + self.isofont: dict[bytes, int] = {} + + def end_document(self) -> None: + """Ends printing. (Write PostScript DSC footer.)""" + self.fp.write(b"%%EndDocument\nrestore showpage\n%%End\n") + if hasattr(self.fp, "flush"): + self.fp.flush() + + def setfont(self, font: str, size: int) -> None: + """ + Selects which font to use. + + :param font: A PostScript font name + :param size: Size in points. + """ + font_bytes = bytes(font, "UTF-8") + if font_bytes not in self.isofont: + # reencode font + self.fp.write( + b"/PSDraw-%s ISOLatin1Encoding /%s E\n" % (font_bytes, font_bytes) + ) + self.isofont[font_bytes] = 1 + # rough + self.fp.write(b"/F0 %d /PSDraw-%s F\n" % (size, font_bytes)) + + def line(self, xy0: tuple[int, int], xy1: tuple[int, int]) -> None: + """ + 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). + """ + self.fp.write(b"%d %d %d %d Vl\n" % (*xy0, *xy1)) + + def rectangle(self, box: tuple[int, int, int, int]) -> None: + """ + Draws a rectangle. + + :param box: A tuple of four integers, specifying left, bottom, width and + height. + """ + self.fp.write(b"%d %d M 0 %d %d Vr\n" % box) + + def text(self, xy: tuple[int, int], text: str) -> None: + """ + Draws text at the given position. You must use + :py:meth:`~PIL.PSDraw.PSDraw.setfont` before calling this method. + """ + text_bytes = bytes(text, "UTF-8") + text_bytes = b"\\(".join(text_bytes.split(b"(")) + text_bytes = b"\\)".join(text_bytes.split(b")")) + self.fp.write(b"%d %d M (%s) S\n" % (xy + (text_bytes,))) + + if TYPE_CHECKING: + from . import Image + + def image( + self, box: tuple[int, int, int, int], im: Image.Image, dpi: int | None = None + ) -> 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 # grayscale + # image size (on paper) + x = im.size[0] * 72 / dpi + y = 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(b"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(b"%f %f scale\n" % (sx, sy)) + EpsImagePlugin._save(im, self.fp, "", 0) + self.fp.write(b"\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 = b"""\ +/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 = b"""\ +/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 0 exch rlineto + exch neg 0 rlineto + 0 exch neg rlineto + setgray fill } 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 = b"""\ +/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/voxvera/vendor/PIL/PaletteFile.py b/voxvera/vendor/PIL/PaletteFile.py new file mode 100644 index 0000000..2a26e5d --- /dev/null +++ b/voxvera/vendor/PIL/PaletteFile.py @@ -0,0 +1,54 @@ +# +# 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 __future__ import annotations + +from typing import IO + +from ._binary import o8 + + +class PaletteFile: + """File handler for Teragon-style palette files.""" + + rawmode = "RGB" + + def __init__(self, fp: IO[bytes]) -> None: + palette = [o8(i) * 3 for i in range(256)] + + while True: + s = fp.readline() + + if not s: + break + if s.startswith(b"#"): + continue + if len(s) > 100: + msg = "bad palette file" + raise SyntaxError(msg) + + 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: + palette[i] = o8(r) + o8(g) + o8(b) + + self.palette = b"".join(palette) + + def getpalette(self) -> tuple[bytes, str]: + return self.palette, self.rawmode diff --git a/voxvera/vendor/PIL/PalmImagePlugin.py b/voxvera/vendor/PIL/PalmImagePlugin.py new file mode 100644 index 0000000..15f7129 --- /dev/null +++ b/voxvera/vendor/PIL/PalmImagePlugin.py @@ -0,0 +1,217 @@ +# +# The Python Imaging Library. +# $Id$ +# + +## +# Image plugin for Palm pixmap images (output only). +## +from __future__ import annotations + +from typing import IO + +from . import Image, ImageFile +from ._binary import o8 +from ._binary import o16be as o16b + +# fmt: off +_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)) +# fmt: on + + +# so build a prototype image to be used for palette resampling +def build_prototype_image() -> Image.Image: + image = Image.new("L", (1, len(_Palm8BitColormapValues))) + image.putdata(list(range(len(_Palm8BitColormapValues)))) + palettedata: tuple[int, ...] = () + for colormapValue in _Palm8BitColormapValues: + palettedata += colormapValue + palettedata += (0, 0, 0) * (256 - len(_Palm8BitColormapValues)) + 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} + + +# +# -------------------------------------------------------------------- + +## +# (Internal) Image save plugin for the Palm format. + + +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + if im.mode == "P": + rawmode = "P" + bpp = 8 + version = 1 + + elif im.mode == "L": + if im.encoderinfo.get("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 grayscale from white (0) to black (1) + bpp = im.encoderinfo["bpp"] + maxval = (1 << bpp) - 1 + shift = 8 - bpp + im = im.point(lambda x: maxval - (x >> shift)) + elif im.info.get("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"] + maxval = (1 << bpp) - 1 + im = im.point(lambda x: maxval - (x & maxval)) + else: + msg = f"cannot write mode {im.mode} as Palm" + raise OSError(msg) + + # we ignore the palette here + im._mode = "P" + rawmode = f"P;{bpp}" + version = 1 + + elif im.mode == "1": + # monochrome -- write it inverted, as is the Palm standard + rawmode = "1;I" + bpp = 1 + version = 0 + + else: + msg = f"cannot write mode {im.mode} as Palm" + raise OSError(msg) + + # + # 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": + flags |= _FLAGS["custom-colormap"] + colormap = im.im.getpalette() + colors = len(colormap) // 3 + colormapsize = 4 * colors + 2 + 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: + fp.write(o16b(colors)) + for i in range(colors): + fp.write(o8(i)) + fp.write(colormap[3 * i : 3 * i + 3]) + + # now convert data to raw form + ImageFile._save( + im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 0, (rawmode, rowbytes, 1))] + ) + + if hasattr(fp, "flush"): + fp.flush() + + +# +# -------------------------------------------------------------------- + +Image.register_save("Palm", _save) + +Image.register_extension("Palm", ".palm") + +Image.register_mime("Palm", "image/palm") diff --git a/voxvera/vendor/PIL/PcdImagePlugin.py b/voxvera/vendor/PIL/PcdImagePlugin.py new file mode 100644 index 0000000..296f377 --- /dev/null +++ b/voxvera/vendor/PIL/PcdImagePlugin.py @@ -0,0 +1,68 @@ +# +# 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. +# +from __future__ import annotations + +from . import Image, ImageFile + +## +# 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) -> None: + # rough + assert self.fp is not None + + self.fp.seek(2048) + s = self.fp.read(1539) + + if not s.startswith(b"PCD_"): + msg = "not a PCD file" + raise SyntaxError(msg) + + orientation = s[1538] & 3 + self.tile_post_rotate = None + if orientation == 1: + self.tile_post_rotate = 90 + elif orientation == 3: + self.tile_post_rotate = 270 + + self._mode = "RGB" + self._size = (512, 768) if orientation in (1, 3) else (768, 512) + self.tile = [ImageFile._Tile("pcd", (0, 0, 768, 512), 96 * 2048)] + + def load_prepare(self) -> None: + if self._im is None and self.tile_post_rotate: + self.im = Image.core.new(self.mode, (768, 512)) + ImageFile.ImageFile.load_prepare(self) + + def load_end(self) -> None: + if self.tile_post_rotate: + # Handle rotated PCDs + self.im = self.rotate(self.tile_post_rotate, expand=True).im + + +# +# registry + +Image.register_open(PcdImageFile.format, PcdImageFile) + +Image.register_extension(PcdImageFile.format, ".pcd") diff --git a/voxvera/vendor/PIL/PcfFontFile.py b/voxvera/vendor/PIL/PcfFontFile.py new file mode 100644 index 0000000..a00e9b9 --- /dev/null +++ b/voxvera/vendor/PIL/PcfFontFile.py @@ -0,0 +1,258 @@ +# +# 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 __future__ import annotations + +import io + +from . import FontFile, Image +from ._binary import i8 +from ._binary import i16be as b16 +from ._binary import i16le as l16 +from ._binary import i32be as b32 +from ._binary import i32le as l32 + +TYPE_CHECKING = False +if TYPE_CHECKING: + from collections.abc import Callable + from typing import BinaryIO + +# -------------------------------------------------------------------- +# 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: list[Callable[[int], int]] = [ + lambda bits: ((bits + 7) >> 3), + lambda bits: ((bits + 15) >> 3) & ~1, + lambda bits: ((bits + 31) >> 3) & ~3, + lambda bits: ((bits + 63) >> 3) & ~7, +] + + +def sz(s: bytes, o: int) -> bytes: + return s[o : s.index(b"\0", o)] + + +class PcfFontFile(FontFile.FontFile): + """Font file plugin for the X11 PCF format.""" + + name = "name" + + def __init__(self, fp: BinaryIO, charset_encoding: str = "iso8859-1"): + self.charset_encoding = charset_encoding + + magic = l32(fp.read(4)) + if magic != PCF_MAGIC: + msg = "not a PCF file" + raise SyntaxError(msg) + + super().__init__() + + 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, ix in enumerate(encoding): + if ix is not None: + ( + xsize, + ysize, + left, + right, + width, + ascent, + descent, + attributes, + ) = metrics[ix] + self.glyph[ch] = ( + (width, 0), + (left, descent - ysize, xsize + left, descent), + (0, 0, xsize, ysize), + bitmaps[ix], + ) + + def _getformat( + self, tag: int + ) -> tuple[BinaryIO, int, Callable[[bytes], int], Callable[[bytes], int]]: + 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) -> dict[bytes, bytes | int]: + # + # font properties + + properties = {} + + fp, format, i16, i32 = self._getformat(PCF_PROPERTIES) + + nprops = i32(fp.read(4)) + + # read property description + p = [(i32(fp.read(4)), i8(fp.read(1)), i32(fp.read(4))) for _ in range(nprops)] + + if nprops & 3: + fp.seek(4 - (nprops & 3), io.SEEK_CUR) # pad + + data = fp.read(i32(fp.read(4))) + + for k, s, v in p: + property_value: bytes | int = sz(data, v) if s else v + properties[sz(data, k)] = property_value + + return properties + + def _load_metrics(self) -> list[tuple[int, int, int, int, int, int, int, int]]: + # + # font metrics + + metrics: list[tuple[int, int, int, int, int, int, int, int]] = [] + + 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: list[tuple[int, int, int, int, int, int, int, int]] + ) -> list[Image.Image]: + # + # bitmap data + + fp, format, i16, i32 = self._getformat(PCF_BITMAPS) + + nbitmaps = i32(fp.read(4)) + + if nbitmaps != len(metrics): + msg = "Wrong number of bitmaps" + raise OSError(msg) + + offsets = [i32(fp.read(4)) for _ in range(nbitmaps)] + + bitmap_sizes = [i32(fp.read(4)) for _ in range(4)] + + # byteorder = format & 4 # non-zero => MSB + bitorder = format & 8 # non-zero => MSB + padindex = format & 3 + + bitmapsize = bitmap_sizes[padindex] + offsets.append(bitmapsize) + + data = fp.read(bitmapsize) + + pad = BYTES_PER_ROW[padindex] + mode = "1;R" + if bitorder: + mode = "1" + + bitmaps = [] + for i in range(nbitmaps): + xsize, ysize = metrics[i][:2] + b, e = offsets[i : i + 2] + bitmaps.append( + Image.frombytes("1", (xsize, ysize), data[b:e], "raw", mode, pad(xsize)) + ) + + return bitmaps + + def _load_encoding(self) -> list[int | None]: + fp, format, i16, i32 = self._getformat(PCF_BDF_ENCODINGS) + + first_col, last_col = i16(fp.read(2)), i16(fp.read(2)) + first_row, last_row = i16(fp.read(2)), i16(fp.read(2)) + + i16(fp.read(2)) # default + + nencoding = (last_col - first_col + 1) * (last_row - first_row + 1) + + # map character code to bitmap index + encoding: list[int | None] = [None] * min(256, nencoding) + + encoding_offsets = [i16(fp.read(2)) for _ in range(nencoding)] + + for i in range(first_col, len(encoding)): + try: + encoding_offset = encoding_offsets[ + ord(bytearray([i]).decode(self.charset_encoding)) + ] + if encoding_offset != 0xFFFF: + encoding[i] = encoding_offset + except UnicodeDecodeError: + # character is not supported in selected encoding + pass + + return encoding diff --git a/voxvera/vendor/PIL/PcxImagePlugin.py b/voxvera/vendor/PIL/PcxImagePlugin.py new file mode 100644 index 0000000..6b16d53 --- /dev/null +++ b/voxvera/vendor/PIL/PcxImagePlugin.py @@ -0,0 +1,228 @@ +# +# 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. +# +from __future__ import annotations + +import io +import logging +from typing import IO + +from . import Image, ImageFile, ImagePalette +from ._binary import i16le as i16 +from ._binary import o8 +from ._binary import o16le as o16 + +logger = logging.getLogger(__name__) + + +def _accept(prefix: bytes) -> bool: + return len(prefix) >= 2 and prefix[0] == 10 and prefix[1] in [0, 2, 3, 5] + + +## +# Image plugin for Paintbrush images. + + +class PcxImageFile(ImageFile.ImageFile): + format = "PCX" + format_description = "Paintbrush" + + def _open(self) -> None: + # header + assert self.fp is not None + + s = self.fp.read(68) + if not _accept(s): + msg = "not a PCX file" + raise SyntaxError(msg) + + # 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]: + msg = "bad PCX image size" + raise SyntaxError(msg) + logger.debug("BBox: %s %s %s %s", *bbox) + + offset = self.fp.tell() + 60 + + # format + version = s[1] + bits = s[3] + planes = s[65] + provided_stride = i16(s, 66) + logger.debug( + "PCX version %s, bits %s, planes %s, stride %s", + version, + bits, + planes, + provided_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 = f"P;{planes}L" + 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, io.SEEK_END) + s = self.fp.read(769) + if len(s) == 769 and s[0] == 12: + # check if the palette is linear grayscale + 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:]) + + elif version == 5 and bits == 8 and planes == 3: + mode = "RGB" + rawmode = "RGB;L" + + else: + msg = "unknown PCX mode" + raise OSError(msg) + + self._mode = mode + self._size = bbox[2] - bbox[0], bbox[3] - bbox[1] + + # Don't trust the passed in stride. + # Calculate the approximate position for ourselves. + # CVE-2020-35653 + stride = (self._size[0] * bits + 7) // 8 + + # While the specification states that this must be even, + # not all images follow this + if provided_stride != stride: + stride += stride % 2 + + bbox = (0, 0) + self.size + logger.debug("size: %sx%s", *self.size) + + self.tile = [ImageFile._Tile("pcx", bbox, offset, (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"), +} + + +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + try: + version, bits, planes, rawmode = SAVE[im.mode] + except KeyError as e: + msg = f"Cannot save {im.mode} images as PCX" + raise ValueError(msg) from e + + # 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. + + logger.debug( + "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, [ImageFile._Tile("pcx", (0, 0) + im.size, 0, (rawmode, bits * planes))] + ) + + if im.mode == "P": + # colour palette + fp.write(o8(12)) + palette = im.im.getpalette("RGB", "RGB") + palette += b"\x00" * (768 - len(palette)) + fp.write(palette) # 768 bytes + elif im.mode == "L": + # grayscale palette + fp.write(o8(12)) + for i in range(256): + fp.write(o8(i) * 3) + + +# -------------------------------------------------------------------- +# registry + + +Image.register_open(PcxImageFile.format, PcxImageFile, _accept) +Image.register_save(PcxImageFile.format, _save) + +Image.register_extension(PcxImageFile.format, ".pcx") + +Image.register_mime(PcxImageFile.format, "image/x-pcx") diff --git a/voxvera/vendor/PIL/PdfImagePlugin.py b/voxvera/vendor/PIL/PdfImagePlugin.py new file mode 100644 index 0000000..5594c7e --- /dev/null +++ b/voxvera/vendor/PIL/PdfImagePlugin.py @@ -0,0 +1,311 @@ +# +# 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). +## +from __future__ import annotations + +import io +import math +import os +import time +from typing import IO, Any + +from . import Image, ImageFile, ImageSequence, PdfParser, features + +# +# -------------------------------------------------------------------- + +# object ids: +# 1. catalogue +# 2. pages +# 3. image +# 4. page +# 5. page contents + + +def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + _save(im, fp, filename, save_all=True) + + +## +# (Internal) Image save plugin for the PDF format. + + +def _write_image( + im: Image.Image, + filename: str | bytes, + existing_pdf: PdfParser.PdfParser, + image_refs: list[PdfParser.IndirectReference], +) -> tuple[PdfParser.IndirectReference, str]: + # FIXME: Should replace ASCIIHexDecode with RunLengthDecode + # (packbits) or LZWDecode (tiff/lzw compression). Note that + # PDF 1.2 also supports Flatedecode (zip compression). + + params = None + decode = None + + # + # Get image characteristics + + width, height = im.size + + dict_obj: dict[str, Any] = {"BitsPerComponent": 8} + if im.mode == "1": + if features.check("libtiff"): + decode_filter = "CCITTFaxDecode" + dict_obj["BitsPerComponent"] = 1 + params = PdfParser.PdfArray( + [ + PdfParser.PdfDict( + { + "K": -1, + "BlackIs1": True, + "Columns": width, + "Rows": height, + } + ) + ] + ) + else: + decode_filter = "DCTDecode" + dict_obj["ColorSpace"] = PdfParser.PdfName("DeviceGray") + procset = "ImageB" # grayscale + elif im.mode == "L": + decode_filter = "DCTDecode" + # params = f"<< /Predictor 15 /Columns {width-2} >>" + dict_obj["ColorSpace"] = PdfParser.PdfName("DeviceGray") + procset = "ImageB" # grayscale + elif im.mode == "LA": + decode_filter = "JPXDecode" + # params = f"<< /Predictor 15 /Columns {width-2} >>" + procset = "ImageB" # grayscale + dict_obj["SMaskInData"] = 1 + elif im.mode == "P": + decode_filter = "ASCIIHexDecode" + palette = im.getpalette() + assert palette is not None + dict_obj["ColorSpace"] = [ + PdfParser.PdfName("Indexed"), + PdfParser.PdfName("DeviceRGB"), + len(palette) // 3 - 1, + PdfParser.PdfBinary(palette), + ] + procset = "ImageI" # indexed color + + if "transparency" in im.info: + smask = im.convert("LA").getchannel("A") + smask.encoderinfo = {} + + image_ref = _write_image(smask, filename, existing_pdf, image_refs)[0] + dict_obj["SMask"] = image_ref + elif im.mode == "RGB": + decode_filter = "DCTDecode" + dict_obj["ColorSpace"] = PdfParser.PdfName("DeviceRGB") + procset = "ImageC" # color images + elif im.mode == "RGBA": + decode_filter = "JPXDecode" + procset = "ImageC" # color images + dict_obj["SMaskInData"] = 1 + elif im.mode == "CMYK": + decode_filter = "DCTDecode" + dict_obj["ColorSpace"] = PdfParser.PdfName("DeviceCMYK") + procset = "ImageC" # color images + decode = [1, 0, 1, 0, 1, 0, 1, 0] + else: + msg = f"cannot save mode {im.mode}" + raise ValueError(msg) + + # + # image + + op = io.BytesIO() + + if decode_filter == "ASCIIHexDecode": + ImageFile._save(im, op, [ImageFile._Tile("hex", (0, 0) + im.size, 0, im.mode)]) + elif decode_filter == "CCITTFaxDecode": + im.save( + op, + "TIFF", + compression="group4", + # use a single strip + strip_size=math.ceil(width / 8) * height, + ) + elif decode_filter == "DCTDecode": + Image.SAVE["JPEG"](im, op, filename) + elif decode_filter == "JPXDecode": + del dict_obj["BitsPerComponent"] + Image.SAVE["JPEG2000"](im, op, filename) + else: + msg = f"unsupported PDF filter ({decode_filter})" + raise ValueError(msg) + + stream = op.getvalue() + filter: PdfParser.PdfArray | PdfParser.PdfName + if decode_filter == "CCITTFaxDecode": + stream = stream[8:] + filter = PdfParser.PdfArray([PdfParser.PdfName(decode_filter)]) + else: + filter = PdfParser.PdfName(decode_filter) + + image_ref = image_refs.pop(0) + existing_pdf.write_obj( + image_ref, + stream=stream, + Type=PdfParser.PdfName("XObject"), + Subtype=PdfParser.PdfName("Image"), + Width=width, # * 72.0 / x_resolution, + Height=height, # * 72.0 / y_resolution, + Filter=filter, + Decode=decode, + DecodeParms=params, + **dict_obj, + ) + + return image_ref, procset + + +def _save( + im: Image.Image, fp: IO[bytes], filename: str | bytes, save_all: bool = False +) -> None: + is_appending = im.encoderinfo.get("append", False) + filename_str = filename.decode() if isinstance(filename, bytes) else filename + if is_appending: + existing_pdf = PdfParser.PdfParser(f=fp, filename=filename_str, mode="r+b") + else: + existing_pdf = PdfParser.PdfParser(f=fp, filename=filename_str, mode="w+b") + + dpi = im.encoderinfo.get("dpi") + if dpi: + x_resolution = dpi[0] + y_resolution = dpi[1] + else: + x_resolution = y_resolution = im.encoderinfo.get("resolution", 72.0) + + info = { + "title": ( + None if is_appending else os.path.splitext(os.path.basename(filename))[0] + ), + "author": None, + "subject": None, + "keywords": None, + "creator": None, + "producer": None, + "creationDate": None if is_appending else time.gmtime(), + "modDate": None if is_appending else time.gmtime(), + } + for k, default in info.items(): + v = im.encoderinfo.get(k) if k in im.encoderinfo else default + if v: + existing_pdf.info[k[0].upper() + k[1:]] = v + + # + # make sure image data is available + im.load() + + existing_pdf.start_writing() + existing_pdf.write_header() + existing_pdf.write_comment("created by Pillow PDF driver") + + # + # pages + ims = [im] + if save_all: + append_images = im.encoderinfo.get("append_images", []) + for append_im in append_images: + append_im.encoderinfo = im.encoderinfo.copy() + ims.append(append_im) + number_of_pages = 0 + image_refs = [] + page_refs = [] + contents_refs = [] + for im in ims: + im_number_of_pages = 1 + if save_all: + im_number_of_pages = getattr(im, "n_frames", 1) + number_of_pages += im_number_of_pages + for i in range(im_number_of_pages): + image_refs.append(existing_pdf.next_object_id(0)) + if im.mode == "P" and "transparency" in im.info: + image_refs.append(existing_pdf.next_object_id(0)) + + page_refs.append(existing_pdf.next_object_id(0)) + contents_refs.append(existing_pdf.next_object_id(0)) + existing_pdf.pages.append(page_refs[-1]) + + # + # catalog and list of pages + existing_pdf.write_catalog() + + page_number = 0 + for im_sequence in ims: + im_pages: ImageSequence.Iterator | list[Image.Image] = ( + ImageSequence.Iterator(im_sequence) if save_all else [im_sequence] + ) + for im in im_pages: + image_ref, procset = _write_image(im, filename, existing_pdf, image_refs) + + # + # page + + existing_pdf.write_page( + page_refs[page_number], + Resources=PdfParser.PdfDict( + ProcSet=[PdfParser.PdfName("PDF"), PdfParser.PdfName(procset)], + XObject=PdfParser.PdfDict(image=image_ref), + ), + MediaBox=[ + 0, + 0, + im.width * 72.0 / x_resolution, + im.height * 72.0 / y_resolution, + ], + Contents=contents_refs[page_number], + ) + + # + # page contents + + page_contents = b"q %f 0 0 %f 0 0 cm /image Do Q\n" % ( + im.width * 72.0 / x_resolution, + im.height * 72.0 / y_resolution, + ) + + existing_pdf.write_obj(contents_refs[page_number], stream=page_contents) + + page_number += 1 + + # + # trailer + existing_pdf.write_xref_and_trailer() + if hasattr(fp, "flush"): + fp.flush() + existing_pdf.close() + + +# +# -------------------------------------------------------------------- + + +Image.register_save("PDF", _save) +Image.register_save_all("PDF", _save_all) + +Image.register_extension("PDF", ".pdf") + +Image.register_mime("PDF", "application/pdf") diff --git a/voxvera/vendor/PIL/PdfParser.py b/voxvera/vendor/PIL/PdfParser.py new file mode 100644 index 0000000..2c90314 --- /dev/null +++ b/voxvera/vendor/PIL/PdfParser.py @@ -0,0 +1,1075 @@ +from __future__ import annotations + +import calendar +import codecs +import collections +import mmap +import os +import re +import time +import zlib +from typing import Any, NamedTuple + +TYPE_CHECKING = False +if TYPE_CHECKING: + from typing import IO + + _DictBase = collections.UserDict[str | bytes, Any] +else: + _DictBase = collections.UserDict + + +# see 7.9.2.2 Text String Type on page 86 and D.3 PDFDocEncoding Character Set +# on page 656 +def encode_text(s: str) -> bytes: + return codecs.BOM_UTF16_BE + s.encode("utf_16_be") + + +PDFDocEncoding = { + 0x16: "\u0017", + 0x18: "\u02d8", + 0x19: "\u02c7", + 0x1A: "\u02c6", + 0x1B: "\u02d9", + 0x1C: "\u02dd", + 0x1D: "\u02db", + 0x1E: "\u02da", + 0x1F: "\u02dc", + 0x80: "\u2022", + 0x81: "\u2020", + 0x82: "\u2021", + 0x83: "\u2026", + 0x84: "\u2014", + 0x85: "\u2013", + 0x86: "\u0192", + 0x87: "\u2044", + 0x88: "\u2039", + 0x89: "\u203a", + 0x8A: "\u2212", + 0x8B: "\u2030", + 0x8C: "\u201e", + 0x8D: "\u201c", + 0x8E: "\u201d", + 0x8F: "\u2018", + 0x90: "\u2019", + 0x91: "\u201a", + 0x92: "\u2122", + 0x93: "\ufb01", + 0x94: "\ufb02", + 0x95: "\u0141", + 0x96: "\u0152", + 0x97: "\u0160", + 0x98: "\u0178", + 0x99: "\u017d", + 0x9A: "\u0131", + 0x9B: "\u0142", + 0x9C: "\u0153", + 0x9D: "\u0161", + 0x9E: "\u017e", + 0xA0: "\u20ac", +} + + +def decode_text(b: bytes) -> str: + if b[: len(codecs.BOM_UTF16_BE)] == codecs.BOM_UTF16_BE: + return b[len(codecs.BOM_UTF16_BE) :].decode("utf_16_be") + else: + return "".join(PDFDocEncoding.get(byte, chr(byte)) for byte in b) + + +class PdfFormatError(RuntimeError): + """An error that probably indicates a syntactic or semantic error in the + PDF file structure""" + + pass + + +def check_format_condition(condition: bool, error_message: str) -> None: + if not condition: + raise PdfFormatError(error_message) + + +class IndirectReferenceTuple(NamedTuple): + object_id: int + generation: int + + +class IndirectReference(IndirectReferenceTuple): + def __str__(self) -> str: + return f"{self.object_id} {self.generation} R" + + def __bytes__(self) -> bytes: + return self.__str__().encode("us-ascii") + + def __eq__(self, other: object) -> bool: + if self.__class__ is not other.__class__: + return False + assert isinstance(other, IndirectReference) + return other.object_id == self.object_id and other.generation == self.generation + + def __ne__(self, other: object) -> bool: + return not (self == other) + + def __hash__(self) -> int: + return hash((self.object_id, self.generation)) + + +class IndirectObjectDef(IndirectReference): + def __str__(self) -> str: + return f"{self.object_id} {self.generation} obj" + + +class XrefTable: + def __init__(self) -> None: + self.existing_entries: dict[int, tuple[int, int]] = ( + {} + ) # object ID => (offset, generation) + self.new_entries: dict[int, tuple[int, int]] = ( + {} + ) # object ID => (offset, generation) + self.deleted_entries = {0: 65536} # object ID => generation + self.reading_finished = False + + def __setitem__(self, key: int, value: tuple[int, int]) -> None: + if self.reading_finished: + self.new_entries[key] = value + else: + self.existing_entries[key] = value + if key in self.deleted_entries: + del self.deleted_entries[key] + + def __getitem__(self, key: int) -> tuple[int, int]: + try: + return self.new_entries[key] + except KeyError: + return self.existing_entries[key] + + def __delitem__(self, key: int) -> None: + if key in self.new_entries: + generation = self.new_entries[key][1] + 1 + del self.new_entries[key] + self.deleted_entries[key] = generation + elif key in self.existing_entries: + generation = self.existing_entries[key][1] + 1 + self.deleted_entries[key] = generation + elif key in self.deleted_entries: + generation = self.deleted_entries[key] + else: + msg = f"object ID {key} cannot be deleted because it doesn't exist" + raise IndexError(msg) + + def __contains__(self, key: int) -> bool: + return key in self.existing_entries or key in self.new_entries + + def __len__(self) -> int: + return len( + set(self.existing_entries.keys()) + | set(self.new_entries.keys()) + | set(self.deleted_entries.keys()) + ) + + def keys(self) -> set[int]: + return ( + set(self.existing_entries.keys()) - set(self.deleted_entries.keys()) + ) | set(self.new_entries.keys()) + + def write(self, f: IO[bytes]) -> int: + keys = sorted(set(self.new_entries.keys()) | set(self.deleted_entries.keys())) + deleted_keys = sorted(set(self.deleted_entries.keys())) + startxref = f.tell() + f.write(b"xref\n") + while keys: + # find a contiguous sequence of object IDs + prev: int | None = None + for index, key in enumerate(keys): + if prev is None or prev + 1 == key: + prev = key + else: + contiguous_keys = keys[:index] + keys = keys[index:] + break + else: + contiguous_keys = keys + keys = [] + f.write(b"%d %d\n" % (contiguous_keys[0], len(contiguous_keys))) + for object_id in contiguous_keys: + if object_id in self.new_entries: + f.write(b"%010d %05d n \n" % self.new_entries[object_id]) + else: + this_deleted_object_id = deleted_keys.pop(0) + check_format_condition( + object_id == this_deleted_object_id, + f"expected the next deleted object ID to be {object_id}, " + f"instead found {this_deleted_object_id}", + ) + try: + next_in_linked_list = deleted_keys[0] + except IndexError: + next_in_linked_list = 0 + f.write( + b"%010d %05d f \n" + % (next_in_linked_list, self.deleted_entries[object_id]) + ) + return startxref + + +class PdfName: + name: bytes + + def __init__(self, name: PdfName | bytes | str) -> None: + if isinstance(name, PdfName): + self.name = name.name + elif isinstance(name, bytes): + self.name = name + else: + self.name = name.encode("us-ascii") + + def name_as_str(self) -> str: + return self.name.decode("us-ascii") + + def __eq__(self, other: object) -> bool: + return ( + isinstance(other, PdfName) and other.name == self.name + ) or other == self.name + + def __hash__(self) -> int: + return hash(self.name) + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({repr(self.name)})" + + @classmethod + def from_pdf_stream(cls, data: bytes) -> PdfName: + return cls(PdfParser.interpret_name(data)) + + allowed_chars = set(range(33, 127)) - {ord(c) for c in "#%/()<>[]{}"} + + def __bytes__(self) -> bytes: + result = bytearray(b"/") + for b in self.name: + if b in self.allowed_chars: + result.append(b) + else: + result.extend(b"#%02X" % b) + return bytes(result) + + +class PdfArray(list[Any]): + def __bytes__(self) -> bytes: + return b"[ " + b" ".join(pdf_repr(x) for x in self) + b" ]" + + +class PdfDict(_DictBase): + def __setattr__(self, key: str, value: Any) -> None: + if key == "data": + collections.UserDict.__setattr__(self, key, value) + else: + self[key.encode("us-ascii")] = value + + def __getattr__(self, key: str) -> str | time.struct_time: + try: + value = self[key.encode("us-ascii")] + except KeyError as e: + raise AttributeError(key) from e + if isinstance(value, bytes): + value = decode_text(value) + if key.endswith("Date"): + if value.startswith("D:"): + value = value[2:] + + relationship = "Z" + if len(value) > 17: + relationship = value[14] + offset = int(value[15:17]) * 60 + if len(value) > 20: + offset += int(value[18:20]) + + format = "%Y%m%d%H%M%S"[: len(value) - 2] + value = time.strptime(value[: len(format) + 2], format) + if relationship in ["+", "-"]: + offset *= 60 + if relationship == "+": + offset *= -1 + value = time.gmtime(calendar.timegm(value) + offset) + return value + + def __bytes__(self) -> bytes: + out = bytearray(b"<<") + for key, value in self.items(): + if value is None: + continue + value = pdf_repr(value) + out.extend(b"\n") + out.extend(bytes(PdfName(key))) + out.extend(b" ") + out.extend(value) + out.extend(b"\n>>") + return bytes(out) + + +class PdfBinary: + def __init__(self, data: list[int] | bytes) -> None: + self.data = data + + def __bytes__(self) -> bytes: + return b"<%s>" % b"".join(b"%02X" % b for b in self.data) + + +class PdfStream: + def __init__(self, dictionary: PdfDict, buf: bytes) -> None: + self.dictionary = dictionary + self.buf = buf + + def decode(self) -> bytes: + try: + filter = self.dictionary[b"Filter"] + except KeyError: + return self.buf + if filter == b"FlateDecode": + try: + expected_length = self.dictionary[b"DL"] + except KeyError: + expected_length = self.dictionary[b"Length"] + return zlib.decompress(self.buf, bufsize=int(expected_length)) + else: + msg = f"stream filter {repr(filter)} unknown/unsupported" + raise NotImplementedError(msg) + + +def pdf_repr(x: Any) -> bytes: + if x is True: + return b"true" + elif x is False: + return b"false" + elif x is None: + return b"null" + elif isinstance(x, (PdfName, PdfDict, PdfArray, PdfBinary)): + return bytes(x) + elif isinstance(x, (int, float)): + return str(x).encode("us-ascii") + elif isinstance(x, time.struct_time): + return b"(D:" + time.strftime("%Y%m%d%H%M%SZ", x).encode("us-ascii") + b")" + elif isinstance(x, dict): + return bytes(PdfDict(x)) + elif isinstance(x, list): + return bytes(PdfArray(x)) + elif isinstance(x, str): + return pdf_repr(encode_text(x)) + elif isinstance(x, bytes): + # XXX escape more chars? handle binary garbage + x = x.replace(b"\\", b"\\\\") + x = x.replace(b"(", b"\\(") + x = x.replace(b")", b"\\)") + return b"(" + x + b")" + else: + return bytes(x) + + +class PdfParser: + """Based on + https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/PDF32000_2008.pdf + Supports PDF up to 1.4 + """ + + def __init__( + self, + filename: str | None = None, + f: IO[bytes] | None = None, + buf: bytes | bytearray | None = None, + start_offset: int = 0, + mode: str = "rb", + ) -> None: + if buf and f: + msg = "specify buf or f or filename, but not both buf and f" + raise RuntimeError(msg) + self.filename = filename + self.buf: bytes | bytearray | mmap.mmap | None = buf + self.f = f + self.start_offset = start_offset + self.should_close_buf = False + self.should_close_file = False + if filename is not None and f is None: + self.f = f = open(filename, mode) + self.should_close_file = True + if f is not None: + self.buf = self.get_buf_from_file(f) + self.should_close_buf = True + if not filename and hasattr(f, "name"): + self.filename = f.name + self.cached_objects: dict[IndirectReference, Any] = {} + self.root_ref: IndirectReference | None + self.info_ref: IndirectReference | None + self.pages_ref: IndirectReference | None + self.last_xref_section_offset: int | None + if self.buf: + self.read_pdf_info() + else: + self.file_size_total = self.file_size_this = 0 + self.root = PdfDict() + self.root_ref = None + self.info = PdfDict() + self.info_ref = None + self.page_tree_root = PdfDict() + self.pages: list[IndirectReference] = [] + self.orig_pages: list[IndirectReference] = [] + self.pages_ref = None + self.last_xref_section_offset = None + self.trailer_dict: dict[bytes, Any] = {} + self.xref_table = XrefTable() + self.xref_table.reading_finished = True + if f: + self.seek_end() + + def __enter__(self) -> PdfParser: + return self + + def __exit__(self, *args: object) -> None: + self.close() + + def start_writing(self) -> None: + self.close_buf() + self.seek_end() + + def close_buf(self) -> None: + if isinstance(self.buf, mmap.mmap): + self.buf.close() + self.buf = None + + def close(self) -> None: + if self.should_close_buf: + self.close_buf() + if self.f is not None and self.should_close_file: + self.f.close() + self.f = None + + def seek_end(self) -> None: + assert self.f is not None + self.f.seek(0, os.SEEK_END) + + def write_header(self) -> None: + assert self.f is not None + self.f.write(b"%PDF-1.4\n") + + def write_comment(self, s: str) -> None: + assert self.f is not None + self.f.write(f"% {s}\n".encode()) + + def write_catalog(self) -> IndirectReference: + assert self.f is not None + self.del_root() + self.root_ref = self.next_object_id(self.f.tell()) + self.pages_ref = self.next_object_id(0) + self.rewrite_pages() + self.write_obj(self.root_ref, Type=PdfName(b"Catalog"), Pages=self.pages_ref) + self.write_obj( + self.pages_ref, + Type=PdfName(b"Pages"), + Count=len(self.pages), + Kids=self.pages, + ) + return self.root_ref + + def rewrite_pages(self) -> None: + pages_tree_nodes_to_delete = [] + for i, page_ref in enumerate(self.orig_pages): + page_info = self.cached_objects[page_ref] + del self.xref_table[page_ref.object_id] + pages_tree_nodes_to_delete.append(page_info[PdfName(b"Parent")]) + if page_ref not in self.pages: + # the page has been deleted + continue + # make dict keys into strings for passing to write_page + stringified_page_info = {} + for key, value in page_info.items(): + # key should be a PdfName + stringified_page_info[key.name_as_str()] = value + stringified_page_info["Parent"] = self.pages_ref + new_page_ref = self.write_page(None, **stringified_page_info) + for j, cur_page_ref in enumerate(self.pages): + if cur_page_ref == page_ref: + # replace the page reference with the new one + self.pages[j] = new_page_ref + # delete redundant Pages tree nodes from xref table + for pages_tree_node_ref in pages_tree_nodes_to_delete: + while pages_tree_node_ref: + pages_tree_node = self.cached_objects[pages_tree_node_ref] + if pages_tree_node_ref.object_id in self.xref_table: + del self.xref_table[pages_tree_node_ref.object_id] + pages_tree_node_ref = pages_tree_node.get(b"Parent", None) + self.orig_pages = [] + + def write_xref_and_trailer( + self, new_root_ref: IndirectReference | None = None + ) -> None: + assert self.f is not None + if new_root_ref: + self.del_root() + self.root_ref = new_root_ref + if self.info: + self.info_ref = self.write_obj(None, self.info) + start_xref = self.xref_table.write(self.f) + num_entries = len(self.xref_table) + trailer_dict: dict[str | bytes, Any] = { + b"Root": self.root_ref, + b"Size": num_entries, + } + if self.last_xref_section_offset is not None: + trailer_dict[b"Prev"] = self.last_xref_section_offset + if self.info: + trailer_dict[b"Info"] = self.info_ref + self.last_xref_section_offset = start_xref + self.f.write( + b"trailer\n" + + bytes(PdfDict(trailer_dict)) + + b"\nstartxref\n%d\n%%%%EOF" % start_xref + ) + + def write_page( + self, ref: int | IndirectReference | None, *objs: Any, **dict_obj: Any + ) -> IndirectReference: + obj_ref = self.pages[ref] if isinstance(ref, int) else ref + if "Type" not in dict_obj: + dict_obj["Type"] = PdfName(b"Page") + if "Parent" not in dict_obj: + dict_obj["Parent"] = self.pages_ref + return self.write_obj(obj_ref, *objs, **dict_obj) + + def write_obj( + self, ref: IndirectReference | None, *objs: Any, **dict_obj: Any + ) -> IndirectReference: + assert self.f is not None + f = self.f + if ref is None: + ref = self.next_object_id(f.tell()) + else: + self.xref_table[ref.object_id] = (f.tell(), ref.generation) + f.write(bytes(IndirectObjectDef(*ref))) + stream = dict_obj.pop("stream", None) + if stream is not None: + dict_obj["Length"] = len(stream) + if dict_obj: + f.write(pdf_repr(dict_obj)) + for obj in objs: + f.write(pdf_repr(obj)) + if stream is not None: + f.write(b"stream\n") + f.write(stream) + f.write(b"\nendstream\n") + f.write(b"endobj\n") + return ref + + def del_root(self) -> None: + if self.root_ref is None: + return + del self.xref_table[self.root_ref.object_id] + del self.xref_table[self.root[b"Pages"].object_id] + + @staticmethod + def get_buf_from_file(f: IO[bytes]) -> bytes | mmap.mmap: + if hasattr(f, "getbuffer"): + return f.getbuffer() + elif hasattr(f, "getvalue"): + return f.getvalue() + else: + try: + return mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) + except ValueError: # cannot mmap an empty file + return b"" + + def read_pdf_info(self) -> None: + assert self.buf is not None + self.file_size_total = len(self.buf) + self.file_size_this = self.file_size_total - self.start_offset + self.read_trailer() + check_format_condition( + self.trailer_dict.get(b"Root") is not None, "Root is missing" + ) + self.root_ref = self.trailer_dict[b"Root"] + assert self.root_ref is not None + self.info_ref = self.trailer_dict.get(b"Info", None) + self.root = PdfDict(self.read_indirect(self.root_ref)) + if self.info_ref is None: + self.info = PdfDict() + else: + self.info = PdfDict(self.read_indirect(self.info_ref)) + check_format_condition(b"Type" in self.root, "/Type missing in Root") + check_format_condition( + self.root[b"Type"] == b"Catalog", "/Type in Root is not /Catalog" + ) + check_format_condition( + self.root.get(b"Pages") is not None, "/Pages missing in Root" + ) + check_format_condition( + isinstance(self.root[b"Pages"], IndirectReference), + "/Pages in Root is not an indirect reference", + ) + self.pages_ref = self.root[b"Pages"] + assert self.pages_ref is not None + self.page_tree_root = self.read_indirect(self.pages_ref) + self.pages = self.linearize_page_tree(self.page_tree_root) + # save the original list of page references + # in case the user modifies, adds or deletes some pages + # and we need to rewrite the pages and their list + self.orig_pages = self.pages[:] + + def next_object_id(self, offset: int | None = None) -> IndirectReference: + try: + # TODO: support reuse of deleted objects + reference = IndirectReference(max(self.xref_table.keys()) + 1, 0) + except ValueError: + reference = IndirectReference(1, 0) + if offset is not None: + self.xref_table[reference.object_id] = (offset, 0) + return reference + + delimiter = rb"[][()<>{}/%]" + delimiter_or_ws = rb"[][()<>{}/%\000\011\012\014\015\040]" + whitespace = rb"[\000\011\012\014\015\040]" + whitespace_or_hex = rb"[\000\011\012\014\015\0400-9a-fA-F]" + whitespace_optional = whitespace + b"*" + whitespace_mandatory = whitespace + b"+" + # No "\012" aka "\n" or "\015" aka "\r": + whitespace_optional_no_nl = rb"[\000\011\014\040]*" + newline_only = rb"[\r\n]+" + newline = whitespace_optional_no_nl + newline_only + whitespace_optional_no_nl + re_trailer_end = re.compile( + whitespace_mandatory + + rb"trailer" + + whitespace_optional + + rb"<<(.*>>)" + + newline + + rb"startxref" + + newline + + rb"([0-9]+)" + + newline + + rb"%%EOF" + + whitespace_optional + + rb"$", + re.DOTALL, + ) + re_trailer_prev = re.compile( + whitespace_optional + + rb"trailer" + + whitespace_optional + + rb"<<(.*?>>)" + + newline + + rb"startxref" + + newline + + rb"([0-9]+)" + + newline + + rb"%%EOF" + + whitespace_optional, + re.DOTALL, + ) + + def read_trailer(self) -> None: + assert self.buf is not None + search_start_offset = len(self.buf) - 16384 + if search_start_offset < self.start_offset: + search_start_offset = self.start_offset + m = self.re_trailer_end.search(self.buf, search_start_offset) + check_format_condition(m is not None, "trailer end not found") + # make sure we found the LAST trailer + last_match = m + while m: + last_match = m + m = self.re_trailer_end.search(self.buf, m.start() + 16) + if not m: + m = last_match + assert m is not None + trailer_data = m.group(1) + self.last_xref_section_offset = int(m.group(2)) + self.trailer_dict = self.interpret_trailer(trailer_data) + self.xref_table = XrefTable() + self.read_xref_table(xref_section_offset=self.last_xref_section_offset) + if b"Prev" in self.trailer_dict: + self.read_prev_trailer(self.trailer_dict[b"Prev"]) + + def read_prev_trailer(self, xref_section_offset: int) -> None: + assert self.buf is not None + trailer_offset = self.read_xref_table(xref_section_offset=xref_section_offset) + m = self.re_trailer_prev.search( + self.buf[trailer_offset : trailer_offset + 16384] + ) + check_format_condition(m is not None, "previous trailer not found") + assert m is not None + trailer_data = m.group(1) + check_format_condition( + int(m.group(2)) == xref_section_offset, + "xref section offset in previous trailer doesn't match what was expected", + ) + trailer_dict = self.interpret_trailer(trailer_data) + if b"Prev" in trailer_dict: + self.read_prev_trailer(trailer_dict[b"Prev"]) + + re_whitespace_optional = re.compile(whitespace_optional) + re_name = re.compile( + whitespace_optional + + rb"/([!-$&'*-.0-;=?-Z\\^-z|~]+)(?=" + + delimiter_or_ws + + rb")" + ) + re_dict_start = re.compile(whitespace_optional + rb"<<") + re_dict_end = re.compile(whitespace_optional + rb">>" + whitespace_optional) + + @classmethod + def interpret_trailer(cls, trailer_data: bytes) -> dict[bytes, Any]: + trailer = {} + offset = 0 + while True: + m = cls.re_name.match(trailer_data, offset) + if not m: + m = cls.re_dict_end.match(trailer_data, offset) + check_format_condition( + m is not None and m.end() == len(trailer_data), + "name not found in trailer, remaining data: " + + repr(trailer_data[offset:]), + ) + break + key = cls.interpret_name(m.group(1)) + assert isinstance(key, bytes) + value, value_offset = cls.get_value(trailer_data, m.end()) + trailer[key] = value + if value_offset is None: + break + offset = value_offset + check_format_condition( + b"Size" in trailer and isinstance(trailer[b"Size"], int), + "/Size not in trailer or not an integer", + ) + check_format_condition( + b"Root" in trailer and isinstance(trailer[b"Root"], IndirectReference), + "/Root not in trailer or not an indirect reference", + ) + return trailer + + re_hashes_in_name = re.compile(rb"([^#]*)(#([0-9a-fA-F]{2}))?") + + @classmethod + def interpret_name(cls, raw: bytes, as_text: bool = False) -> str | bytes: + name = b"" + for m in cls.re_hashes_in_name.finditer(raw): + if m.group(3): + name += m.group(1) + bytearray.fromhex(m.group(3).decode("us-ascii")) + else: + name += m.group(1) + if as_text: + return name.decode("utf-8") + else: + return bytes(name) + + re_null = re.compile(whitespace_optional + rb"null(?=" + delimiter_or_ws + rb")") + re_true = re.compile(whitespace_optional + rb"true(?=" + delimiter_or_ws + rb")") + re_false = re.compile(whitespace_optional + rb"false(?=" + delimiter_or_ws + rb")") + re_int = re.compile( + whitespace_optional + rb"([-+]?[0-9]+)(?=" + delimiter_or_ws + rb")" + ) + re_real = re.compile( + whitespace_optional + + rb"([-+]?([0-9]+\.[0-9]*|[0-9]*\.[0-9]+))(?=" + + delimiter_or_ws + + rb")" + ) + re_array_start = re.compile(whitespace_optional + rb"\[") + re_array_end = re.compile(whitespace_optional + rb"]") + re_string_hex = re.compile( + whitespace_optional + rb"<(" + whitespace_or_hex + rb"*)>" + ) + re_string_lit = re.compile(whitespace_optional + rb"\(") + re_indirect_reference = re.compile( + whitespace_optional + + rb"([-+]?[0-9]+)" + + whitespace_mandatory + + rb"([-+]?[0-9]+)" + + whitespace_mandatory + + rb"R(?=" + + delimiter_or_ws + + rb")" + ) + re_indirect_def_start = re.compile( + whitespace_optional + + rb"([-+]?[0-9]+)" + + whitespace_mandatory + + rb"([-+]?[0-9]+)" + + whitespace_mandatory + + rb"obj(?=" + + delimiter_or_ws + + rb")" + ) + re_indirect_def_end = re.compile( + whitespace_optional + rb"endobj(?=" + delimiter_or_ws + rb")" + ) + re_comment = re.compile( + rb"(" + whitespace_optional + rb"%[^\r\n]*" + newline + rb")*" + ) + re_stream_start = re.compile(whitespace_optional + rb"stream\r?\n") + re_stream_end = re.compile( + whitespace_optional + rb"endstream(?=" + delimiter_or_ws + rb")" + ) + + @classmethod + def get_value( + cls, + data: bytes | bytearray | mmap.mmap, + offset: int, + expect_indirect: IndirectReference | None = None, + max_nesting: int = -1, + ) -> tuple[Any, int | None]: + if max_nesting == 0: + return None, None + m = cls.re_comment.match(data, offset) + if m: + offset = m.end() + m = cls.re_indirect_def_start.match(data, offset) + if m: + check_format_condition( + int(m.group(1)) > 0, + "indirect object definition: object ID must be greater than 0", + ) + check_format_condition( + int(m.group(2)) >= 0, + "indirect object definition: generation must be non-negative", + ) + check_format_condition( + expect_indirect is None + or expect_indirect + == IndirectReference(int(m.group(1)), int(m.group(2))), + "indirect object definition different than expected", + ) + object, object_offset = cls.get_value( + data, m.end(), max_nesting=max_nesting - 1 + ) + if object_offset is None: + return object, None + m = cls.re_indirect_def_end.match(data, object_offset) + check_format_condition( + m is not None, "indirect object definition end not found" + ) + assert m is not None + return object, m.end() + check_format_condition( + not expect_indirect, "indirect object definition not found" + ) + m = cls.re_indirect_reference.match(data, offset) + if m: + check_format_condition( + int(m.group(1)) > 0, + "indirect object reference: object ID must be greater than 0", + ) + check_format_condition( + int(m.group(2)) >= 0, + "indirect object reference: generation must be non-negative", + ) + return IndirectReference(int(m.group(1)), int(m.group(2))), m.end() + m = cls.re_dict_start.match(data, offset) + if m: + offset = m.end() + result: dict[Any, Any] = {} + m = cls.re_dict_end.match(data, offset) + current_offset: int | None = offset + while not m: + assert current_offset is not None + key, current_offset = cls.get_value( + data, current_offset, max_nesting=max_nesting - 1 + ) + if current_offset is None: + return result, None + value, current_offset = cls.get_value( + data, current_offset, max_nesting=max_nesting - 1 + ) + result[key] = value + if current_offset is None: + return result, None + m = cls.re_dict_end.match(data, current_offset) + current_offset = m.end() + m = cls.re_stream_start.match(data, current_offset) + if m: + stream_len = result.get(b"Length") + if stream_len is None or not isinstance(stream_len, int): + msg = f"bad or missing Length in stream dict ({stream_len})" + raise PdfFormatError(msg) + stream_data = data[m.end() : m.end() + stream_len] + m = cls.re_stream_end.match(data, m.end() + stream_len) + check_format_condition(m is not None, "stream end not found") + assert m is not None + current_offset = m.end() + return PdfStream(PdfDict(result), stream_data), current_offset + return PdfDict(result), current_offset + m = cls.re_array_start.match(data, offset) + if m: + offset = m.end() + results = [] + m = cls.re_array_end.match(data, offset) + current_offset = offset + while not m: + assert current_offset is not None + value, current_offset = cls.get_value( + data, current_offset, max_nesting=max_nesting - 1 + ) + results.append(value) + if current_offset is None: + return results, None + m = cls.re_array_end.match(data, current_offset) + return results, m.end() + m = cls.re_null.match(data, offset) + if m: + return None, m.end() + m = cls.re_true.match(data, offset) + if m: + return True, m.end() + m = cls.re_false.match(data, offset) + if m: + return False, m.end() + m = cls.re_name.match(data, offset) + if m: + return PdfName(cls.interpret_name(m.group(1))), m.end() + m = cls.re_int.match(data, offset) + if m: + return int(m.group(1)), m.end() + m = cls.re_real.match(data, offset) + if m: + # XXX Decimal instead of float??? + return float(m.group(1)), m.end() + m = cls.re_string_hex.match(data, offset) + if m: + # filter out whitespace + hex_string = bytearray( + b for b in m.group(1) if b in b"0123456789abcdefABCDEF" + ) + if len(hex_string) % 2 == 1: + # append a 0 if the length is not even - yes, at the end + hex_string.append(ord(b"0")) + return bytearray.fromhex(hex_string.decode("us-ascii")), m.end() + m = cls.re_string_lit.match(data, offset) + if m: + return cls.get_literal_string(data, m.end()) + # return None, offset # fallback (only for debugging) + msg = f"unrecognized object: {repr(data[offset : offset + 32])}" + raise PdfFormatError(msg) + + re_lit_str_token = re.compile( + rb"(\\[nrtbf()\\])|(\\[0-9]{1,3})|(\\(\r\n|\r|\n))|(\r\n|\r|\n)|(\()|(\))" + ) + escaped_chars = { + b"n": b"\n", + b"r": b"\r", + b"t": b"\t", + b"b": b"\b", + b"f": b"\f", + b"(": b"(", + b")": b")", + b"\\": b"\\", + ord(b"n"): b"\n", + ord(b"r"): b"\r", + ord(b"t"): b"\t", + ord(b"b"): b"\b", + ord(b"f"): b"\f", + ord(b"("): b"(", + ord(b")"): b")", + ord(b"\\"): b"\\", + } + + @classmethod + def get_literal_string( + cls, data: bytes | bytearray | mmap.mmap, offset: int + ) -> tuple[bytes, int]: + nesting_depth = 0 + result = bytearray() + for m in cls.re_lit_str_token.finditer(data, offset): + result.extend(data[offset : m.start()]) + if m.group(1): + result.extend(cls.escaped_chars[m.group(1)[1]]) + elif m.group(2): + result.append(int(m.group(2)[1:], 8)) + elif m.group(3): + pass + elif m.group(5): + result.extend(b"\n") + elif m.group(6): + result.extend(b"(") + nesting_depth += 1 + elif m.group(7): + if nesting_depth == 0: + return bytes(result), m.end() + result.extend(b")") + nesting_depth -= 1 + offset = m.end() + msg = "unfinished literal string" + raise PdfFormatError(msg) + + re_xref_section_start = re.compile(whitespace_optional + rb"xref" + newline) + re_xref_subsection_start = re.compile( + whitespace_optional + + rb"([0-9]+)" + + whitespace_mandatory + + rb"([0-9]+)" + + whitespace_optional + + newline_only + ) + re_xref_entry = re.compile(rb"([0-9]{10}) ([0-9]{5}) ([fn])( \r| \n|\r\n)") + + def read_xref_table(self, xref_section_offset: int) -> int: + assert self.buf is not None + subsection_found = False + m = self.re_xref_section_start.match( + self.buf, xref_section_offset + self.start_offset + ) + check_format_condition(m is not None, "xref section start not found") + assert m is not None + offset = m.end() + while True: + m = self.re_xref_subsection_start.match(self.buf, offset) + if not m: + check_format_condition( + subsection_found, "xref subsection start not found" + ) + break + subsection_found = True + offset = m.end() + first_object = int(m.group(1)) + num_objects = int(m.group(2)) + for i in range(first_object, first_object + num_objects): + m = self.re_xref_entry.match(self.buf, offset) + check_format_condition(m is not None, "xref entry not found") + assert m is not None + offset = m.end() + is_free = m.group(3) == b"f" + if not is_free: + generation = int(m.group(2)) + new_entry = (int(m.group(1)), generation) + if i not in self.xref_table: + self.xref_table[i] = new_entry + return offset + + def read_indirect(self, ref: IndirectReference, max_nesting: int = -1) -> Any: + offset, generation = self.xref_table[ref[0]] + check_format_condition( + generation == ref[1], + f"expected to find generation {ref[1]} for object ID {ref[0]} in xref " + f"table, instead found generation {generation} at offset {offset}", + ) + assert self.buf is not None + value = self.get_value( + self.buf, + offset + self.start_offset, + expect_indirect=IndirectReference(*ref), + max_nesting=max_nesting, + )[0] + self.cached_objects[ref] = value + return value + + def linearize_page_tree( + self, node: PdfDict | None = None + ) -> list[IndirectReference]: + page_node = node if node is not None else self.page_tree_root + check_format_condition( + page_node[b"Type"] == b"Pages", "/Type of page tree node is not /Pages" + ) + pages = [] + for kid in page_node[b"Kids"]: + kid_object = self.read_indirect(kid) + if kid_object[b"Type"] == b"Page": + pages.append(kid) + else: + pages.extend(self.linearize_page_tree(node=kid_object)) + return pages diff --git a/voxvera/vendor/PIL/PixarImagePlugin.py b/voxvera/vendor/PIL/PixarImagePlugin.py new file mode 100644 index 0000000..d2b6d0a --- /dev/null +++ b/voxvera/vendor/PIL/PixarImagePlugin.py @@ -0,0 +1,72 @@ +# +# 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. +# +from __future__ import annotations + +from . import Image, ImageFile +from ._binary import i16le as i16 + +# +# helpers + + +def _accept(prefix: bytes) -> bool: + return prefix.startswith(b"\200\350\000\000") + + +## +# Image plugin for PIXAR raster images. + + +class PixarImageFile(ImageFile.ImageFile): + format = "PIXAR" + format_description = "PIXAR raster image" + + def _open(self) -> None: + # assuming a 4-byte magic label + assert self.fp is not None + + s = self.fp.read(4) + if not _accept(s): + msg = "not a PIXAR file" + raise SyntaxError(msg) + + # read rest of header + s = s + self.fp.read(508) + + self._size = i16(s, 418), i16(s, 416) + + # get channel/depth descriptions + mode = i16(s, 424), i16(s, 426) + + if mode == (14, 2): + self._mode = "RGB" + # FIXME: to be continued... + + # create tile descriptor (assuming "dumped") + self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 1024, self.mode)] + + +# +# -------------------------------------------------------------------- + +Image.register_open(PixarImageFile.format, PixarImageFile, _accept) + +Image.register_extension(PixarImageFile.format, ".pxr") diff --git a/voxvera/vendor/PIL/PngImagePlugin.py b/voxvera/vendor/PIL/PngImagePlugin.py new file mode 100644 index 0000000..9826a4c --- /dev/null +++ b/voxvera/vendor/PIL/PngImagePlugin.py @@ -0,0 +1,1564 @@ +# +# 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 annotations + +import itertools +import logging +import re +import struct +import warnings +import zlib +from enum import IntEnum +from fractions import Fraction +from typing import IO, NamedTuple, cast + +from . import Image, ImageChops, ImageFile, ImagePalette, ImageSequence +from ._binary import i16be as i16 +from ._binary import i32be as i32 +from ._binary import o8 +from ._binary import o16be as o16 +from ._binary import o32be as o32 +from ._deprecate import deprecate +from ._util import DeferredError + +TYPE_CHECKING = False +if TYPE_CHECKING: + from collections.abc import Callable + from typing import Any, NoReturn + + from . import _imaging + +logger = logging.getLogger(__name__) + +is_cid = re.compile(rb"\w\w\w\w").match + + +_MAGIC = b"\211PNG\r\n\032\n" + + +_MODES = { + # supported bits/color combinations, and corresponding modes/rawmodes + # Grayscale + (1, 0): ("1", "1"), + (2, 0): ("L", "L;2"), + (4, 0): ("L", "L;4"), + (8, 0): ("L", "L"), + (16, 0): ("I;16", "I;16B"), + # Truecolour + (8, 2): ("RGB", "RGB"), + (16, 2): ("RGB", "RGB;16B"), + # Indexed-colour + (1, 3): ("P", "P;1"), + (2, 3): ("P", "P;2"), + (4, 3): ("P", "P;4"), + (8, 3): ("P", "P"), + # Grayscale with alpha + (8, 4): ("LA", "LA"), + (16, 4): ("RGBA", "LA;16B"), # LA;16B->LA not yet available + # Truecolour with alpha + (8, 6): ("RGBA", "RGBA"), + (16, 6): ("RGBA", "RGBA;16B"), +} + + +_simple_palette = re.compile(b"^\xff*\x00\xff*$") + +MAX_TEXT_CHUNK = ImageFile.SAFEBLOCK +""" +Maximum decompressed size for a iTXt or zTXt chunk. +Eliminates decompression bombs where compressed chunks can expand 1000x. +See :ref:`Text in PNG File Format`. +""" +MAX_TEXT_MEMORY = 64 * MAX_TEXT_CHUNK +""" +Set the maximum total text chunk size. +See :ref:`Text in PNG File Format`. +""" + + +# APNG frame disposal modes +class Disposal(IntEnum): + OP_NONE = 0 + """ + No disposal is done on this frame before rendering the next frame. + See :ref:`Saving APNG sequences`. + """ + OP_BACKGROUND = 1 + """ + This frame’s modified region is cleared to fully transparent black before rendering + the next frame. + See :ref:`Saving APNG sequences`. + """ + OP_PREVIOUS = 2 + """ + This frame’s modified region is reverted to the previous frame’s contents before + rendering the next frame. + See :ref:`Saving APNG sequences`. + """ + + +# APNG frame blend modes +class Blend(IntEnum): + OP_SOURCE = 0 + """ + All color components of this frame, including alpha, overwrite the previous output + image contents. + See :ref:`Saving APNG sequences`. + """ + OP_OVER = 1 + """ + This frame should be alpha composited with the previous output image contents. + See :ref:`Saving APNG sequences`. + """ + + +def _safe_zlib_decompress(s: bytes) -> bytes: + dobj = zlib.decompressobj() + plaintext = dobj.decompress(s, MAX_TEXT_CHUNK) + if dobj.unconsumed_tail: + msg = "Decompressed data too large for PngImagePlugin.MAX_TEXT_CHUNK" + raise ValueError(msg) + return plaintext + + +def _crc32(data: bytes, seed: int = 0) -> int: + return zlib.crc32(data, seed) & 0xFFFFFFFF + + +# -------------------------------------------------------------------- +# Support classes. Suitable for PNG and related formats like MNG etc. + + +class ChunkStream: + def __init__(self, fp: IO[bytes]) -> None: + self.fp: IO[bytes] | None = fp + self.queue: list[tuple[bytes, int, int]] | None = [] + + def read(self) -> tuple[bytes, int, int]: + """Fetch a new chunk. Returns header information.""" + cid = None + + assert self.fp is not None + if self.queue: + cid, pos, length = self.queue.pop() + 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): + if not ImageFile.LOAD_TRUNCATED_IMAGES: + msg = f"broken PNG file (chunk {repr(cid)})" + raise SyntaxError(msg) + + return cid, pos, length + + def __enter__(self) -> ChunkStream: + return self + + def __exit__(self, *args: object) -> None: + self.close() + + def close(self) -> None: + self.queue = self.fp = None + + def push(self, cid: bytes, pos: int, length: int) -> None: + assert self.queue is not None + self.queue.append((cid, pos, length)) + + def call(self, cid: bytes, pos: int, length: int) -> bytes: + """Call the appropriate chunk handler""" + + logger.debug("STREAM %r %s %s", cid, pos, length) + return getattr(self, f"chunk_{cid.decode('ascii')}")(pos, length) + + def crc(self, cid: bytes, data: bytes) -> None: + """Read and verify checksum""" + + # Skip CRC checks for ancillary chunks if allowed to load truncated + # images + # 5th byte of first char is 1 [specs, section 5.4] + if ImageFile.LOAD_TRUNCATED_IMAGES and (cid[0] >> 5 & 1): + self.crc_skip(cid, data) + return + + assert self.fp is not None + try: + crc1 = _crc32(data, _crc32(cid)) + crc2 = i32(self.fp.read(4)) + if crc1 != crc2: + msg = f"broken PNG file (bad header checksum in {repr(cid)})" + raise SyntaxError(msg) + except struct.error as e: + msg = f"broken PNG file (incomplete checksum in {repr(cid)})" + raise SyntaxError(msg) from e + + def crc_skip(self, cid: bytes, data: bytes) -> None: + """Read checksum""" + + assert self.fp is not None + self.fp.read(4) + + def verify(self, endchunk: bytes = b"IEND") -> list[bytes]: + # Simple approach; just calculate checksum for all remaining + # blocks. Must be called directly after open. + + cids = [] + + assert self.fp is not None + while True: + try: + cid, pos, length = self.read() + except struct.error as e: + msg = "truncated PNG file" + raise OSError(msg) from e + + 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 + + """ + + lang: str | bytes | None + tkey: str | bytes | None + + @staticmethod + def __new__( + cls, text: str, lang: str | None = None, tkey: str | None = None + ) -> iTXt: + """ + :param cls: the class to use when creating the instance + :param text: 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) -> None: + self.chunks: list[tuple[bytes, bytes, bool]] = [] + + def add(self, cid: bytes, data: bytes, after_idat: bool = False) -> None: + """Appends an arbitrary chunk. Use with caution. + + :param cid: a byte string, 4 bytes long. + :param data: a byte string of the encoded data + :param after_idat: for use with private chunks. Whether the chunk + should be written after IDAT + + """ + + self.chunks.append((cid, data, after_idat)) + + def add_itxt( + self, + key: str | bytes, + value: str | bytes, + lang: str | bytes = "", + tkey: str | bytes = "", + zip: bool = False, + ) -> None: + """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: str | bytes, value: str | bytes | iTXt, zip: bool = False + ) -> None: + """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 if value.lang is not None else b"", + value.tkey if value.tkey is not None else b"", + zip=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=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 _RewindState(NamedTuple): + info: dict[str | tuple[int, int], Any] + tile: list[ImageFile._Tile] + seq_num: int | None + + +class PngStream(ChunkStream): + def __init__(self, fp: IO[bytes]) -> None: + super().__init__(fp) + + # local copies of Image attributes + self.im_info: dict[str | tuple[int, int], Any] = {} + self.im_text: dict[str, str | iTXt] = {} + self.im_size = (0, 0) + self.im_mode = "" + self.im_tile: list[ImageFile._Tile] = [] + self.im_palette: tuple[str, bytes] | None = None + self.im_custom_mimetype: str | None = None + self.im_n_frames: int | None = None + self._seq_num: int | None = None + self.rewind_state = _RewindState({}, [], None) + + self.text_memory = 0 + + def check_text_memory(self, chunklen: int) -> None: + self.text_memory += chunklen + if self.text_memory > MAX_TEXT_MEMORY: + msg = ( + "Too much memory used in text chunks: " + f"{self.text_memory}>MAX_TEXT_MEMORY" + ) + raise ValueError(msg) + + def save_rewind(self) -> None: + self.rewind_state = _RewindState( + self.im_info.copy(), + self.im_tile, + self._seq_num, + ) + + def rewind(self) -> None: + self.im_info = self.rewind_state.info.copy() + self.im_tile = self.rewind_state.tile + self._seq_num = self.rewind_state.seq_num + + def chunk_iCCP(self, pos: int, length: int) -> bytes: + # ICC profile + assert self.fp is not None + 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") + logger.debug("iCCP profile name %r", s[:i]) + comp_method = s[i + 1] + logger.debug("Compression method %s", comp_method) + if comp_method != 0: + msg = f"Unknown compression method {comp_method} in iCCP chunk" + raise SyntaxError(msg) + try: + icc_profile = _safe_zlib_decompress(s[i + 2 :]) + except ValueError: + if ImageFile.LOAD_TRUNCATED_IMAGES: + icc_profile = None + else: + raise + except zlib.error: + icc_profile = None # FIXME + self.im_info["icc_profile"] = icc_profile + return s + + def chunk_IHDR(self, pos: int, length: int) -> bytes: + # image header + assert self.fp is not None + s = ImageFile._safe_read(self.fp, length) + if length < 13: + if ImageFile.LOAD_TRUNCATED_IMAGES: + return s + msg = "Truncated IHDR chunk" + raise ValueError(msg) + self.im_size = i32(s, 0), i32(s, 4) + try: + self.im_mode, self.im_rawmode = _MODES[(s[8], s[9])] + except Exception: + pass + if s[12]: + self.im_info["interlace"] = 1 + if s[11]: + msg = "unknown filter category" + raise SyntaxError(msg) + return s + + def chunk_IDAT(self, pos: int, length: int) -> NoReturn: + # image data + if "bbox" in self.im_info: + tile = [ImageFile._Tile("zip", self.im_info["bbox"], pos, self.im_rawmode)] + else: + if self.im_n_frames is not None: + self.im_info["default_image"] = True + tile = [ImageFile._Tile("zip", (0, 0) + self.im_size, pos, self.im_rawmode)] + self.im_tile = tile + self.im_idat = length + msg = "image data found" + raise EOFError(msg) + + def chunk_IEND(self, pos: int, length: int) -> NoReturn: + msg = "end of PNG image" + raise EOFError(msg) + + def chunk_PLTE(self, pos: int, length: int) -> bytes: + # palette + assert self.fp is not None + s = ImageFile._safe_read(self.fp, length) + if self.im_mode == "P": + self.im_palette = "RGB", s + return s + + def chunk_tRNS(self, pos: int, length: int) -> bytes: + # transparency + assert self.fp is not None + s = ImageFile._safe_read(self.fp, length) + if self.im_mode == "P": + if _simple_palette.match(s): + # tRNS contains only one full-transparent entry, + # other entries are full opaque + i = s.find(b"\0") + if i >= 0: + self.im_info["transparency"] = i + else: + # otherwise, we have a byte string with one alpha value + # for each palette entry + self.im_info["transparency"] = s + elif self.im_mode == "1": + self.im_info["transparency"] = 255 if i16(s) else 0 + elif self.im_mode in ("L", "I;16"): + 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: int, length: int) -> bytes: + # gamma setting + assert self.fp is not None + s = ImageFile._safe_read(self.fp, length) + self.im_info["gamma"] = i32(s) / 100000.0 + return s + + def chunk_cHRM(self, pos: int, length: int) -> bytes: + # chromaticity, 8 unsigned ints, actual value is scaled by 100,000 + # WP x,y, Red x,y, Green x,y Blue x,y + + assert self.fp is not None + s = ImageFile._safe_read(self.fp, length) + raw_vals = struct.unpack(f">{len(s) // 4}I", s) + self.im_info["chromaticity"] = tuple(elt / 100000.0 for elt in raw_vals) + return s + + def chunk_sRGB(self, pos: int, length: int) -> bytes: + # srgb rendering intent, 1 byte + # 0 perceptual + # 1 relative colorimetric + # 2 saturation + # 3 absolute colorimetric + + assert self.fp is not None + s = ImageFile._safe_read(self.fp, length) + if length < 1: + if ImageFile.LOAD_TRUNCATED_IMAGES: + return s + msg = "Truncated sRGB chunk" + raise ValueError(msg) + self.im_info["srgb"] = s[0] + return s + + def chunk_pHYs(self, pos: int, length: int) -> bytes: + # pixels per unit + assert self.fp is not None + s = ImageFile._safe_read(self.fp, length) + if length < 9: + if ImageFile.LOAD_TRUNCATED_IMAGES: + return s + msg = "Truncated pHYs chunk" + raise ValueError(msg) + px, py = i32(s, 0), i32(s, 4) + unit = s[8] + if unit == 1: # meter + dpi = px * 0.0254, py * 0.0254 + self.im_info["dpi"] = dpi + elif unit == 0: + self.im_info["aspect"] = px, py + return s + + def chunk_tEXt(self, pos: int, length: int) -> bytes: + # text + assert self.fp is not None + 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: + k_str = k.decode("latin-1", "strict") + v_str = v.decode("latin-1", "replace") + + self.im_info[k_str] = v if k == b"exif" else v_str + self.im_text[k_str] = v_str + self.check_text_memory(len(v_str)) + + return s + + def chunk_zTXt(self, pos: int, length: int) -> bytes: + # compressed text + assert self.fp is not None + 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 = v[0] + else: + comp_method = 0 + if comp_method != 0: + msg = f"Unknown compression method {comp_method} in zTXt chunk" + raise SyntaxError(msg) + try: + v = _safe_zlib_decompress(v[1:]) + except ValueError: + if ImageFile.LOAD_TRUNCATED_IMAGES: + v = b"" + else: + raise + except zlib.error: + v = b"" + + if k: + k_str = k.decode("latin-1", "strict") + v_str = v.decode("latin-1", "replace") + + self.im_info[k_str] = self.im_text[k_str] = v_str + self.check_text_memory(len(v_str)) + + return s + + def chunk_iTXt(self, pos: int, length: int) -> bytes: + # international text + assert self.fp is not None + 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 = r[0], 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 ValueError: + if ImageFile.LOAD_TRUNCATED_IMAGES: + return s + else: + raise + except zlib.error: + return s + else: + return s + if k == b"XML:com.adobe.xmp": + self.im_info["xmp"] = v + try: + k_str = k.decode("latin-1", "strict") + lang_str = lang.decode("utf-8", "strict") + tk_str = tk.decode("utf-8", "strict") + v_str = v.decode("utf-8", "strict") + except UnicodeError: + return s + + self.im_info[k_str] = self.im_text[k_str] = iTXt(v_str, lang_str, tk_str) + self.check_text_memory(len(v_str)) + + return s + + def chunk_eXIf(self, pos: int, length: int) -> bytes: + assert self.fp is not None + s = ImageFile._safe_read(self.fp, length) + self.im_info["exif"] = b"Exif\x00\x00" + s + return s + + # APNG chunks + def chunk_acTL(self, pos: int, length: int) -> bytes: + assert self.fp is not None + s = ImageFile._safe_read(self.fp, length) + if length < 8: + if ImageFile.LOAD_TRUNCATED_IMAGES: + return s + msg = "APNG contains truncated acTL chunk" + raise ValueError(msg) + if self.im_n_frames is not None: + self.im_n_frames = None + warnings.warn("Invalid APNG, will use default PNG image if possible") + return s + n_frames = i32(s) + if n_frames == 0 or n_frames > 0x80000000: + warnings.warn("Invalid APNG, will use default PNG image if possible") + return s + self.im_n_frames = n_frames + self.im_info["loop"] = i32(s, 4) + self.im_custom_mimetype = "image/apng" + return s + + def chunk_fcTL(self, pos: int, length: int) -> bytes: + assert self.fp is not None + s = ImageFile._safe_read(self.fp, length) + if length < 26: + if ImageFile.LOAD_TRUNCATED_IMAGES: + return s + msg = "APNG contains truncated fcTL chunk" + raise ValueError(msg) + seq = i32(s) + if (self._seq_num is None and seq != 0) or ( + self._seq_num is not None and self._seq_num != seq - 1 + ): + msg = "APNG contains frame sequence errors" + raise SyntaxError(msg) + self._seq_num = seq + width, height = i32(s, 4), i32(s, 8) + px, py = i32(s, 12), i32(s, 16) + im_w, im_h = self.im_size + if px + width > im_w or py + height > im_h: + msg = "APNG contains invalid frames" + raise SyntaxError(msg) + self.im_info["bbox"] = (px, py, px + width, py + height) + delay_num, delay_den = i16(s, 20), i16(s, 22) + if delay_den == 0: + delay_den = 100 + self.im_info["duration"] = float(delay_num) / float(delay_den) * 1000 + self.im_info["disposal"] = s[24] + self.im_info["blend"] = s[25] + return s + + def chunk_fdAT(self, pos: int, length: int) -> bytes: + assert self.fp is not None + if length < 4: + if ImageFile.LOAD_TRUNCATED_IMAGES: + s = ImageFile._safe_read(self.fp, length) + return s + msg = "APNG contains truncated fDAT chunk" + raise ValueError(msg) + s = ImageFile._safe_read(self.fp, 4) + seq = i32(s) + if self._seq_num != seq - 1: + msg = "APNG contains frame sequence errors" + raise SyntaxError(msg) + self._seq_num = seq + return self.chunk_IDAT(pos + 4, length - 4) + + +# -------------------------------------------------------------------- +# PNG reader + + +def _accept(prefix: bytes) -> bool: + return prefix.startswith(_MAGIC) + + +## +# Image plugin for PNG images. + + +class PngImageFile(ImageFile.ImageFile): + format = "PNG" + format_description = "Portable network graphics" + + def _open(self) -> None: + assert self.fp is not None + if not _accept(self.fp.read(8)): + msg = "not a PNG file" + raise SyntaxError(msg) + self._fp = self.fp + self.__frame = 0 + + # + # Parse headers up to the first IDAT or fDAT chunk + + self.private_chunks: list[tuple[bytes, bytes] | tuple[bytes, bytes, bool]] = [] + self.png: PngStream | None = 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: + logger.debug("%r %s %s (unknown)", cid, pos, length) + s = ImageFile._safe_read(self.fp, length) + if cid[1:2].islower(): + self.private_chunks.append((cid, s)) + + 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: dict[str, str | iTXt] | None = None + self.tile = self.png.im_tile + self.custom_mimetype = self.png.im_custom_mimetype + self.n_frames = self.png.im_n_frames or 1 + self.default_image = self.info.get("default_image", False) + + if self.png.im_palette: + rawmode, data = self.png.im_palette + self.palette = ImagePalette.raw(rawmode, data) + + if cid == b"fdAT": + self.__prepare_idat = length - 4 + else: + self.__prepare_idat = length # used by load_prepare() + + if self.png.im_n_frames is not None: + self._close_exclusive_fp_after_loading = False + self.png.save_rewind() + self.__rewind_idat = self.__prepare_idat + self.__rewind = self._fp.tell() + if self.default_image: + # IDAT chunk contains default image and not first animation frame + self.n_frames += 1 + self._seek(0) + self.is_animated = self.n_frames > 1 + + @property + def text(self) -> dict[str, str | iTXt]: + # experimental + if self._text is None: + # iTxt, tEXt and zTXt chunks may appear at the end of the file + # So load the file to ensure that they are read + if self.is_animated: + frame = self.__frame + # for APNG, seek to the final frame before loading + self.seek(self.n_frames - 1) + self.load() + if self.is_animated: + self.seek(frame) + assert self._text is not None + return self._text + + def verify(self) -> None: + """Verify PNG file""" + + if self.fp is None: + msg = "verify must be called directly after open" + raise RuntimeError(msg) + + # back up to beginning of IDAT block + self.fp.seek(self.tile[0][2] - 8) + + assert self.png is not None + self.png.verify() + self.png.close() + + super().verify() + + def seek(self, frame: int) -> None: + if not self._seek_check(frame): + return + if frame < self.__frame: + self._seek(0, True) + + last_frame = self.__frame + for f in range(self.__frame + 1, frame + 1): + try: + self._seek(f) + except EOFError as e: + self.seek(last_frame) + msg = "no more images in APNG file" + raise EOFError(msg) from e + + def _seek(self, frame: int, rewind: bool = False) -> None: + assert self.png is not None + if isinstance(self._fp, DeferredError): + raise self._fp.ex + + self.dispose: _imaging.ImagingCore | None + dispose_extent = None + if frame == 0: + if rewind: + self._fp.seek(self.__rewind) + self.png.rewind() + self.__prepare_idat = self.__rewind_idat + self._im = None + self.info = self.png.im_info + self.tile = self.png.im_tile + self.fp = self._fp + self._prev_im = None + self.dispose = None + self.default_image = self.info.get("default_image", False) + self.dispose_op = self.info.get("disposal") + self.blend_op = self.info.get("blend") + dispose_extent = self.info.get("bbox") + self.__frame = 0 + else: + if frame != self.__frame + 1: + msg = f"cannot seek to frame {frame}" + raise ValueError(msg) + + # ensure previous frame was loaded + self.load() + + if self.dispose: + self.im.paste(self.dispose, self.dispose_extent) + self._prev_im = self.im.copy() + + self.fp = self._fp + + # advance to the next frame + if self.__prepare_idat: + ImageFile._safe_read(self.fp, self.__prepare_idat) + self.__prepare_idat = 0 + frame_start = False + while True: + self.fp.read(4) # CRC + + try: + cid, pos, length = self.png.read() + except (struct.error, SyntaxError): + break + + if cid == b"IEND": + msg = "No more images in APNG file" + raise EOFError(msg) + if cid == b"fcTL": + if frame_start: + # there must be at least one fdAT chunk between fcTL chunks + msg = "APNG missing frame data" + raise SyntaxError(msg) + frame_start = True + + try: + self.png.call(cid, pos, length) + except UnicodeDecodeError: + break + except EOFError: + if cid == b"fdAT": + length -= 4 + if frame_start: + self.__prepare_idat = length + break + ImageFile._safe_read(self.fp, length) + except AttributeError: + logger.debug("%r %s %s (unknown)", cid, pos, length) + ImageFile._safe_read(self.fp, length) + + self.__frame = frame + self.tile = self.png.im_tile + self.dispose_op = self.info.get("disposal") + self.blend_op = self.info.get("blend") + dispose_extent = self.info.get("bbox") + + if not self.tile: + msg = "image not found in APNG frame" + raise EOFError(msg) + if dispose_extent: + self.dispose_extent: tuple[float, float, float, float] = dispose_extent + + # setup frame disposal (actual disposal done when needed in the next _seek()) + if self._prev_im is None and self.dispose_op == Disposal.OP_PREVIOUS: + self.dispose_op = Disposal.OP_BACKGROUND + + self.dispose = None + if self.dispose_op == Disposal.OP_PREVIOUS: + if self._prev_im: + self.dispose = self._prev_im.copy() + self.dispose = self._crop(self.dispose, self.dispose_extent) + elif self.dispose_op == Disposal.OP_BACKGROUND: + self.dispose = Image.core.fill(self.mode, self.size) + self.dispose = self._crop(self.dispose, self.dispose_extent) + + def tell(self) -> int: + return self.__frame + + def load_prepare(self) -> None: + """internal: prepare to read PNG file""" + + if self.info.get("interlace"): + self.decoderconfig = self.decoderconfig + (1,) + + self.__idat = self.__prepare_idat # used by load_read() + ImageFile.ImageFile.load_prepare(self) + + def load_read(self, read_bytes: int) -> bytes: + """internal: read more image data""" + + assert self.png is not None + assert self.fp is not None + 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", b"fdAT"]: + self.png.push(cid, pos, length) + return b"" + + if cid == b"fdAT": + try: + self.png.call(cid, pos, length) + except EOFError: + pass + self.__idat = length - 4 # sequence_num has already been read + else: + 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) -> None: + """internal: finished reading image data""" + assert self.png is not None + assert self.fp is not None + if self.__idat != 0: + self.fp.read(self.__idat) + while True: + self.fp.read(4) # CRC + + try: + cid, pos, length = self.png.read() + except (struct.error, SyntaxError): + break + + if cid == b"IEND": + break + elif cid == b"fcTL" and self.is_animated: + # start of the next frame, stop reading + self.__prepare_idat = 0 + self.png.push(cid, pos, length) + break + + try: + self.png.call(cid, pos, length) + except UnicodeDecodeError: + break + except EOFError: + if cid == b"fdAT": + length -= 4 + try: + ImageFile._safe_read(self.fp, length) + except OSError as e: + if ImageFile.LOAD_TRUNCATED_IMAGES: + break + else: + raise e + except AttributeError: + logger.debug("%r %s %s (unknown)", cid, pos, length) + s = ImageFile._safe_read(self.fp, length) + if cid[1:2].islower(): + self.private_chunks.append((cid, s, True)) + self._text = self.png.im_text + if not self.is_animated: + self.png.close() + self.png = None + else: + if self._prev_im and self.blend_op == Blend.OP_OVER: + updated = self._crop(self.im, self.dispose_extent) + if self.im.mode == "RGB" and "transparency" in self.info: + mask = updated.convert_transparent( + "RGBA", self.info["transparency"] + ) + else: + if self.im.mode == "P" and "transparency" in self.info: + t = self.info["transparency"] + if isinstance(t, bytes): + updated.putpalettealphas(t) + elif isinstance(t, int): + updated.putpalettealpha(t) + mask = updated.convert("RGBA") + self._prev_im.paste(updated, self.dispose_extent, mask) + self.im = self._prev_im + + def _getexif(self) -> dict[int, Any] | None: + if "exif" not in self.info: + self.load() + if "exif" not in self.info and "Raw profile type exif" not in self.info: + return None + return self.getexif()._get_merged_dict() + + def getexif(self) -> Image.Exif: + if "exif" not in self.info: + self.load() + + return super().getexif() + + +# -------------------------------------------------------------------- +# PNG writer + +_OUTMODES = { + # supported PIL modes, and corresponding rawmode, bit depth and color type + "1": ("1", b"\x01", b"\x00"), + "L;1": ("L;1", b"\x01", b"\x00"), + "L;2": ("L;2", b"\x02", b"\x00"), + "L;4": ("L;4", b"\x04", b"\x00"), + "L": ("L", b"\x08", b"\x00"), + "LA": ("LA", b"\x08", b"\x04"), + "I": ("I;16B", b"\x10", b"\x00"), + "I;16": ("I;16B", b"\x10", b"\x00"), + "I;16B": ("I;16B", b"\x10", b"\x00"), + "P;1": ("P;1", b"\x01", b"\x03"), + "P;2": ("P;2", b"\x02", b"\x03"), + "P;4": ("P;4", b"\x04", b"\x03"), + "P": ("P", b"\x08", b"\x03"), + "RGB": ("RGB", b"\x08", b"\x02"), + "RGBA": ("RGBA", b"\x08", b"\x06"), +} + + +def putchunk(fp: IO[bytes], cid: bytes, *data: bytes) -> None: + """Write a PNG chunk (including CRC field)""" + + byte_data = b"".join(data) + + fp.write(o32(len(byte_data)) + cid) + fp.write(byte_data) + crc = _crc32(byte_data, _crc32(cid)) + fp.write(o32(crc)) + + +class _idat: + # wrap output from the encoder in IDAT chunks + + def __init__(self, fp: IO[bytes], chunk: Callable[..., None]) -> None: + self.fp = fp + self.chunk = chunk + + def write(self, data: bytes) -> None: + self.chunk(self.fp, b"IDAT", data) + + +class _fdat: + # wrap encoder output in fdAT chunks + + def __init__(self, fp: IO[bytes], chunk: Callable[..., None], seq_num: int) -> None: + self.fp = fp + self.chunk = chunk + self.seq_num = seq_num + + def write(self, data: bytes) -> None: + self.chunk(self.fp, b"fdAT", o32(self.seq_num), data) + self.seq_num += 1 + + +def _apply_encoderinfo(im: Image.Image, encoderinfo: dict[str, Any]) -> None: + im.encoderconfig = ( + encoderinfo.get("optimize", False), + encoderinfo.get("compress_level", -1), + encoderinfo.get("compress_type", -1), + encoderinfo.get("dictionary", b""), + ) + + +class _Frame(NamedTuple): + im: Image.Image + bbox: tuple[int, int, int, int] | None + encoderinfo: dict[str, Any] + + +def _write_multiple_frames( + im: Image.Image, + fp: IO[bytes], + chunk: Callable[..., None], + mode: str, + rawmode: str, + default_image: Image.Image | None, + append_images: list[Image.Image], +) -> Image.Image | None: + duration = im.encoderinfo.get("duration") + loop = im.encoderinfo.get("loop", im.info.get("loop", 0)) + disposal = im.encoderinfo.get("disposal", im.info.get("disposal", Disposal.OP_NONE)) + blend = im.encoderinfo.get("blend", im.info.get("blend", Blend.OP_SOURCE)) + + if default_image: + chain = itertools.chain(append_images) + else: + chain = itertools.chain([im], append_images) + + im_frames: list[_Frame] = [] + frame_count = 0 + for im_seq in chain: + for im_frame in ImageSequence.Iterator(im_seq): + if im_frame.mode == mode: + im_frame = im_frame.copy() + else: + im_frame = im_frame.convert(mode) + encoderinfo = im.encoderinfo.copy() + if isinstance(duration, (list, tuple)): + encoderinfo["duration"] = duration[frame_count] + elif duration is None and "duration" in im_frame.info: + encoderinfo["duration"] = im_frame.info["duration"] + if isinstance(disposal, (list, tuple)): + encoderinfo["disposal"] = disposal[frame_count] + if isinstance(blend, (list, tuple)): + encoderinfo["blend"] = blend[frame_count] + frame_count += 1 + + if im_frames: + previous = im_frames[-1] + prev_disposal = previous.encoderinfo.get("disposal") + prev_blend = previous.encoderinfo.get("blend") + if prev_disposal == Disposal.OP_PREVIOUS and len(im_frames) < 2: + prev_disposal = Disposal.OP_BACKGROUND + + if prev_disposal == Disposal.OP_BACKGROUND: + base_im = previous.im.copy() + dispose = Image.core.fill("RGBA", im.size, (0, 0, 0, 0)) + bbox = previous.bbox + if bbox: + dispose = dispose.crop(bbox) + else: + bbox = (0, 0) + im.size + base_im.paste(dispose, bbox) + elif prev_disposal == Disposal.OP_PREVIOUS: + base_im = im_frames[-2].im + else: + base_im = previous.im + delta = ImageChops.subtract_modulo( + im_frame.convert("RGBA"), base_im.convert("RGBA") + ) + bbox = delta.getbbox(alpha_only=False) + if ( + not bbox + and prev_disposal == encoderinfo.get("disposal") + and prev_blend == encoderinfo.get("blend") + and "duration" in encoderinfo + ): + previous.encoderinfo["duration"] += encoderinfo["duration"] + continue + else: + bbox = None + im_frames.append(_Frame(im_frame, bbox, encoderinfo)) + + if len(im_frames) == 1 and not default_image: + return im_frames[0].im + + # animation control + chunk( + fp, + b"acTL", + o32(len(im_frames)), # 0: num_frames + o32(loop), # 4: num_plays + ) + + # default image IDAT (if it exists) + if default_image: + default_im = im if im.mode == mode else im.convert(mode) + _apply_encoderinfo(default_im, im.encoderinfo) + ImageFile._save( + default_im, + cast(IO[bytes], _idat(fp, chunk)), + [ImageFile._Tile("zip", (0, 0) + im.size, 0, rawmode)], + ) + + seq_num = 0 + for frame, frame_data in enumerate(im_frames): + im_frame = frame_data.im + if not frame_data.bbox: + bbox = (0, 0) + im_frame.size + else: + bbox = frame_data.bbox + im_frame = im_frame.crop(bbox) + size = im_frame.size + encoderinfo = frame_data.encoderinfo + frame_duration = encoderinfo.get("duration", 0) + delay = Fraction(frame_duration / 1000).limit_denominator(65535) + if delay.numerator > 65535: + msg = "cannot write duration" + raise ValueError(msg) + frame_disposal = encoderinfo.get("disposal", disposal) + frame_blend = encoderinfo.get("blend", blend) + # frame control + chunk( + fp, + b"fcTL", + o32(seq_num), # sequence_number + o32(size[0]), # width + o32(size[1]), # height + o32(bbox[0]), # x_offset + o32(bbox[1]), # y_offset + o16(delay.numerator), # delay_numerator + o16(delay.denominator), # delay_denominator + o8(frame_disposal), # dispose_op + o8(frame_blend), # blend_op + ) + seq_num += 1 + # frame data + _apply_encoderinfo(im_frame, im.encoderinfo) + if frame == 0 and not default_image: + # first frame must be in IDAT chunks for backwards compatibility + ImageFile._save( + im_frame, + cast(IO[bytes], _idat(fp, chunk)), + [ImageFile._Tile("zip", (0, 0) + im_frame.size, 0, rawmode)], + ) + else: + fdat_chunks = _fdat(fp, chunk, seq_num) + ImageFile._save( + im_frame, + cast(IO[bytes], fdat_chunks), + [ImageFile._Tile("zip", (0, 0) + im_frame.size, 0, rawmode)], + ) + seq_num = fdat_chunks.seq_num + return None + + +def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + _save(im, fp, filename, save_all=True) + + +def _save( + im: Image.Image, + fp: IO[bytes], + filename: str | bytes, + chunk: Callable[..., None] = putchunk, + save_all: bool = False, +) -> None: + # save an image to disk (called by the save method) + + if save_all: + default_image = im.encoderinfo.get( + "default_image", im.info.get("default_image") + ) + modes = set() + sizes = set() + append_images = im.encoderinfo.get("append_images", []) + for im_seq in itertools.chain([im], append_images): + for im_frame in ImageSequence.Iterator(im_seq): + modes.add(im_frame.mode) + sizes.add(im_frame.size) + for mode in ("RGBA", "RGB", "P"): + if mode in modes: + break + else: + mode = modes.pop() + size = tuple(max(frame_size[i] for frame_size in sizes) for i in range(2)) + else: + size = im.size + mode = im.mode + + outmode = mode + if mode == "P": + # + # attempt to minimize storage requirements for palette images + if "bits" in im.encoderinfo: + # number of bits specified by user + colors = min(1 << im.encoderinfo["bits"], 256) + else: + # check palette contents + if im.palette: + colors = max(min(len(im.palette.getdata()[1]) // 3, 256), 1) + else: + colors = 256 + + if colors <= 16: + if colors <= 2: + bits = 1 + elif colors <= 4: + bits = 2 + else: + bits = 4 + outmode += f";{bits}" + + # get the corresponding PNG mode + try: + rawmode, bit_depth, color_type = _OUTMODES[outmode] + except KeyError as e: + msg = f"cannot write mode {mode} as PNG" + raise OSError(msg) from e + if outmode == "I": + deprecate("Saving I mode images as PNG", 13, stacklevel=4) + + # + # write minimal PNG file + + fp.write(_MAGIC) + + chunk( + fp, + b"IHDR", + o32(size[0]), # 0: size + o32(size[1]), + bit_depth, + color_type, + b"\0", # 10: compression + b"\0", # 11: filter category + b"\0", # 12: interlace flag + ) + + chunks = [b"cHRM", b"cICP", b"gAMA", b"sBIT", b"sRGB", b"tIME"] + + icc = im.encoderinfo.get("icc_profile", im.info.get("icc_profile")) + if icc: + # 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(icc) + chunk(fp, b"iCCP", data) + + # You must either have sRGB or iCCP. + # Disallow sRGB chunks when an iCCP-chunk has been emitted. + chunks.remove(b"sRGB") + + info = im.encoderinfo.get("pnginfo") + if info: + chunks_multiple_allowed = [b"sPLT", b"iTXt", b"tEXt", b"zTXt"] + for info_chunk in info.chunks: + cid, data = info_chunk[:2] + if cid in chunks: + chunks.remove(cid) + chunk(fp, cid, data) + elif cid in chunks_multiple_allowed: + chunk(fp, cid, data) + elif cid[1:2].islower(): + # Private chunk + after_idat = len(info_chunk) == 3 and info_chunk[2] + if not after_idat: + chunk(fp, cid, data) + + if im.mode == "P": + palette_byte_number = colors * 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 = colors + 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 in ("1", "L", "I", "I;16"): + 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. + msg = "cannot use transparency for this mode" + raise OSError(msg) + else: + if im.mode == "P" and im.im.getpalettemode() == "RGBA": + alpha = im.im.getpalette("RGBA", "A") + alpha_bytes = colors + chunk(fp, b"tRNS", alpha[:alpha_bytes]) + + 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", + ) + + if info: + chunks = [b"bKGD", b"hIST"] + for info_chunk in info.chunks: + cid, data = info_chunk[:2] + if cid in chunks: + chunks.remove(cid) + chunk(fp, cid, data) + + exif = im.encoderinfo.get("exif") + if exif: + if isinstance(exif, Image.Exif): + exif = exif.tobytes(8) + if exif.startswith(b"Exif\x00\x00"): + exif = exif[6:] + chunk(fp, b"eXIf", exif) + + single_im: Image.Image | None = im + if save_all: + single_im = _write_multiple_frames( + im, fp, chunk, mode, rawmode, default_image, append_images + ) + if single_im: + _apply_encoderinfo(single_im, im.encoderinfo) + ImageFile._save( + single_im, + cast(IO[bytes], _idat(fp, chunk)), + [ImageFile._Tile("zip", (0, 0) + single_im.size, 0, rawmode)], + ) + + if info: + for info_chunk in info.chunks: + cid, data = info_chunk[:2] + if cid[1:2].islower(): + # Private chunk + after_idat = len(info_chunk) == 3 and info_chunk[2] + if after_idat: + chunk(fp, cid, data) + + chunk(fp, b"IEND", b"") + + if hasattr(fp, "flush"): + fp.flush() + + +# -------------------------------------------------------------------- +# PNG chunk converter + + +def getchunks(im: Image.Image, **params: Any) -> list[tuple[bytes, bytes, bytes]]: + """Return a list of PNG chunks representing this image.""" + from io import BytesIO + + chunks = [] + + def append(fp: IO[bytes], cid: bytes, *data: bytes) -> None: + byte_data = b"".join(data) + crc = o32(_crc32(byte_data, _crc32(cid))) + chunks.append((cid, byte_data, crc)) + + fp = BytesIO() + + try: + im.encoderinfo = params + _save(im, fp, "", append) + finally: + del im.encoderinfo + + return chunks + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(PngImageFile.format, PngImageFile, _accept) +Image.register_save(PngImageFile.format, _save) +Image.register_save_all(PngImageFile.format, _save_all) + +Image.register_extensions(PngImageFile.format, [".png", ".apng"]) + +Image.register_mime(PngImageFile.format, "image/png") diff --git a/voxvera/vendor/PIL/PpmImagePlugin.py b/voxvera/vendor/PIL/PpmImagePlugin.py new file mode 100644 index 0000000..307bc97 --- /dev/null +++ b/voxvera/vendor/PIL/PpmImagePlugin.py @@ -0,0 +1,375 @@ +# +# 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. +# +from __future__ import annotations + +import math +from typing import IO + +from . import Image, ImageFile +from ._binary import i16be as i16 +from ._binary import o8 +from ._binary import o32le as o32 + +# +# -------------------------------------------------------------------- + +b_whitespace = b"\x20\x09\x0a\x0b\x0c\x0d" + +MODES = { + # standard + b"P1": "1", + b"P2": "L", + b"P3": "RGB", + b"P4": "1", + b"P5": "L", + b"P6": "RGB", + # extensions + b"P0CMYK": "CMYK", + b"Pf": "F", + # PIL extensions (for test purposes only) + b"PyP": "P", + b"PyRGBA": "RGBA", + b"PyCMYK": "CMYK", +} + + +def _accept(prefix: bytes) -> bool: + return len(prefix) >= 2 and prefix.startswith(b"P") and prefix[1] in b"0123456fy" + + +## +# Image plugin for PBM, PGM, and PPM images. + + +class PpmImageFile(ImageFile.ImageFile): + format = "PPM" + format_description = "Pbmplus image" + + def _read_magic(self) -> bytes: + assert self.fp is not None + + magic = b"" + # read until whitespace or longest available magic number + for _ in range(6): + c = self.fp.read(1) + if not c or c in b_whitespace: + break + magic += c + return magic + + def _read_token(self) -> bytes: + assert self.fp is not None + + token = b"" + while len(token) <= 10: # read until next whitespace or limit of 10 characters + c = self.fp.read(1) + if not c: + break + elif c in b_whitespace: # token ended + if not token: + # skip whitespace at start + continue + break + elif c == b"#": + # ignores rest of the line; stops at CR, LF or EOF + while self.fp.read(1) not in b"\r\n": + pass + continue + token += c + if not token: + # Token was not even 1 byte + msg = "Reached EOF while reading header" + raise ValueError(msg) + elif len(token) > 10: + msg_too_long = b"Token too long in file header: %s" % token + raise ValueError(msg_too_long) + return token + + def _open(self) -> None: + assert self.fp is not None + + magic_number = self._read_magic() + try: + mode = MODES[magic_number] + except KeyError: + msg = "not a PPM file" + raise SyntaxError(msg) + self._mode = mode + + if magic_number in (b"P1", b"P4"): + self.custom_mimetype = "image/x-portable-bitmap" + elif magic_number in (b"P2", b"P5"): + self.custom_mimetype = "image/x-portable-graymap" + elif magic_number in (b"P3", b"P6"): + self.custom_mimetype = "image/x-portable-pixmap" + + self._size = int(self._read_token()), int(self._read_token()) + + decoder_name = "raw" + if magic_number in (b"P1", b"P2", b"P3"): + decoder_name = "ppm_plain" + + args: str | tuple[str | int, ...] + if mode == "1": + args = "1;I" + elif mode == "F": + scale = float(self._read_token()) + if scale == 0.0 or not math.isfinite(scale): + msg = "scale must be finite and non-zero" + raise ValueError(msg) + self.info["scale"] = abs(scale) + + rawmode = "F;32F" if scale < 0 else "F;32BF" + args = (rawmode, 0, -1) + else: + maxval = int(self._read_token()) + if not 0 < maxval < 65536: + msg = "maxval must be greater than 0 and less than 65536" + raise ValueError(msg) + if maxval > 255 and mode == "L": + self._mode = "I" + + rawmode = mode + if decoder_name != "ppm_plain": + # If maxval matches a bit depth, use the raw decoder directly + if maxval == 65535 and mode == "L": + rawmode = "I;16B" + elif maxval != 255: + decoder_name = "ppm" + + args = rawmode if decoder_name == "raw" else (rawmode, maxval) + self.tile = [ + ImageFile._Tile(decoder_name, (0, 0) + self.size, self.fp.tell(), args) + ] + + +# +# -------------------------------------------------------------------- + + +class PpmPlainDecoder(ImageFile.PyDecoder): + _pulls_fd = True + _comment_spans: bool + + def _read_block(self) -> bytes: + assert self.fd is not None + + return self.fd.read(ImageFile.SAFEBLOCK) + + def _find_comment_end(self, block: bytes, start: int = 0) -> int: + a = block.find(b"\n", start) + b = block.find(b"\r", start) + return min(a, b) if a * b > 0 else max(a, b) # lowest nonnegative index (or -1) + + def _ignore_comments(self, block: bytes) -> bytes: + if self._comment_spans: + # Finish current comment + while block: + comment_end = self._find_comment_end(block) + if comment_end != -1: + # Comment ends in this block + # Delete tail of comment + block = block[comment_end + 1 :] + break + else: + # Comment spans whole block + # So read the next block, looking for the end + block = self._read_block() + + # Search for any further comments + self._comment_spans = False + while True: + comment_start = block.find(b"#") + if comment_start == -1: + # No comment found + break + comment_end = self._find_comment_end(block, comment_start) + if comment_end != -1: + # Comment ends in this block + # Delete comment + block = block[:comment_start] + block[comment_end + 1 :] + else: + # Comment continues to next block(s) + block = block[:comment_start] + self._comment_spans = True + break + return block + + def _decode_bitonal(self) -> bytearray: + """ + This is a separate method because in the plain PBM format, all data tokens are + exactly one byte, so the inter-token whitespace is optional. + """ + data = bytearray() + total_bytes = self.state.xsize * self.state.ysize + + while len(data) != total_bytes: + block = self._read_block() # read next block + if not block: + # eof + break + + block = self._ignore_comments(block) + + tokens = b"".join(block.split()) + for token in tokens: + if token not in (48, 49): + msg = b"Invalid token for this mode: %s" % bytes([token]) + raise ValueError(msg) + data = (data + tokens)[:total_bytes] + invert = bytes.maketrans(b"01", b"\xff\x00") + return data.translate(invert) + + def _decode_blocks(self, maxval: int) -> bytearray: + data = bytearray() + max_len = 10 + out_byte_count = 4 if self.mode == "I" else 1 + out_max = 65535 if self.mode == "I" else 255 + bands = Image.getmodebands(self.mode) + total_bytes = self.state.xsize * self.state.ysize * bands * out_byte_count + + half_token = b"" + while len(data) != total_bytes: + block = self._read_block() # read next block + if not block: + if half_token: + block = bytearray(b" ") # flush half_token + else: + # eof + break + + block = self._ignore_comments(block) + + if half_token: + block = half_token + block # stitch half_token to new block + half_token = b"" + + tokens = block.split() + + if block and not block[-1:].isspace(): # block might split token + half_token = tokens.pop() # save half token for later + if len(half_token) > max_len: # prevent buildup of half_token + msg = ( + b"Token too long found in data: %s" % half_token[: max_len + 1] + ) + raise ValueError(msg) + + for token in tokens: + if len(token) > max_len: + msg = b"Token too long found in data: %s" % token[: max_len + 1] + raise ValueError(msg) + value = int(token) + if value < 0: + msg_str = f"Channel value is negative: {value}" + raise ValueError(msg_str) + if value > maxval: + msg_str = f"Channel value too large for this mode: {value}" + raise ValueError(msg_str) + value = round(value / maxval * out_max) + data += o32(value) if self.mode == "I" else o8(value) + if len(data) == total_bytes: # finished! + break + return data + + def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: + self._comment_spans = False + if self.mode == "1": + data = self._decode_bitonal() + rawmode = "1;8" + else: + maxval = self.args[-1] + data = self._decode_blocks(maxval) + rawmode = "I;32" if self.mode == "I" else self.mode + self.set_as_raw(bytes(data), rawmode) + return -1, 0 + + +class PpmDecoder(ImageFile.PyDecoder): + _pulls_fd = True + + def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: + assert self.fd is not None + + data = bytearray() + maxval = self.args[-1] + in_byte_count = 1 if maxval < 256 else 2 + out_byte_count = 4 if self.mode == "I" else 1 + out_max = 65535 if self.mode == "I" else 255 + bands = Image.getmodebands(self.mode) + dest_length = self.state.xsize * self.state.ysize * bands * out_byte_count + while len(data) < dest_length: + pixels = self.fd.read(in_byte_count * bands) + if len(pixels) < in_byte_count * bands: + # eof + break + for b in range(bands): + value = ( + pixels[b] if in_byte_count == 1 else i16(pixels, b * in_byte_count) + ) + value = min(out_max, round(value / maxval * out_max)) + data += o32(value) if self.mode == "I" else o8(value) + rawmode = "I;32" if self.mode == "I" else self.mode + self.set_as_raw(bytes(data), rawmode) + return -1, 0 + + +# +# -------------------------------------------------------------------- + + +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + if im.mode == "1": + rawmode, head = "1;I", b"P4" + elif im.mode == "L": + rawmode, head = "L", b"P5" + elif im.mode in ("I", "I;16"): + rawmode, head = "I;16B", b"P5" + elif im.mode in ("RGB", "RGBA"): + rawmode, head = "RGB", b"P6" + elif im.mode == "F": + rawmode, head = "F;32F", b"Pf" + else: + msg = f"cannot write mode {im.mode} as PPM" + raise OSError(msg) + fp.write(head + b"\n%d %d\n" % im.size) + if head == b"P6": + fp.write(b"255\n") + elif head == b"P5": + if rawmode == "L": + fp.write(b"255\n") + else: + fp.write(b"65535\n") + elif head == b"Pf": + fp.write(b"-1.0\n") + row_order = -1 if im.mode == "F" else 1 + ImageFile._save( + im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 0, (rawmode, 0, row_order))] + ) + + +# +# -------------------------------------------------------------------- + + +Image.register_open(PpmImageFile.format, PpmImageFile, _accept) +Image.register_save(PpmImageFile.format, _save) + +Image.register_decoder("ppm", PpmDecoder) +Image.register_decoder("ppm_plain", PpmPlainDecoder) + +Image.register_extensions(PpmImageFile.format, [".pbm", ".pgm", ".ppm", ".pnm", ".pfm"]) + +Image.register_mime(PpmImageFile.format, "image/x-portable-anymap") diff --git a/voxvera/vendor/PIL/PsdImagePlugin.py b/voxvera/vendor/PIL/PsdImagePlugin.py new file mode 100644 index 0000000..69a8703 --- /dev/null +++ b/voxvera/vendor/PIL/PsdImagePlugin.py @@ -0,0 +1,334 @@ +# +# 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. +# +from __future__ import annotations + +import io +from functools import cached_property +from typing import IO + +from . import Image, ImageFile, ImagePalette +from ._binary import i8 +from ._binary import i16be as i16 +from ._binary import i32be as i32 +from ._binary import si16be as si16 +from ._binary import si32be as si32 +from ._util import DeferredError + +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), +} + + +# --------------------------------------------------------------------. +# read PSD images + + +def _accept(prefix: bytes) -> bool: + return prefix.startswith(b"8BPS") + + +## +# Image plugin for Photoshop images. + + +class PsdImageFile(ImageFile.ImageFile): + format = "PSD" + format_description = "Adobe Photoshop" + _close_exclusive_fp_after_loading = False + + def _open(self) -> None: + assert self.fp is not None + read = self.fp.read + + # + # header + + s = read(26) + if not _accept(s) or i16(s, 4) != 1: + msg = "not a PSD file" + raise SyntaxError(msg) + + 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: + msg = "not enough channels" + raise OSError(msg) + if mode == "RGB" and psd_channels == 4: + mode = "RGBA" + channels = 4 + + 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: + read(4) # signature + 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_position = None + + size = i32(read(4)) + if size: + end = self.fp.tell() + size + size = i32(read(4)) + if size: + self._layers_position = self.fp.tell() + self._layers_size = size + self.fp.seek(end) + self._n_frames: int | None = None + + # + # image descriptor + + self.tile = _maketile(self.fp, mode, (0, 0) + self.size, channels) + + # keep the file open + self._fp = self.fp + self.frame = 1 + self._min_frame = 1 + + @cached_property + def layers( + self, + ) -> list[tuple[str, str, tuple[int, int, int, int], list[ImageFile._Tile]]]: + layers = [] + if self._layers_position is not None: + if isinstance(self._fp, DeferredError): + raise self._fp.ex + self._fp.seek(self._layers_position) + _layer_data = io.BytesIO(ImageFile._safe_read(self._fp, self._layers_size)) + layers = _layerinfo(_layer_data, self._layers_size) + self._n_frames = len(layers) + return layers + + @property + def n_frames(self) -> int: + if self._n_frames is None: + self._n_frames = len(self.layers) + return self._n_frames + + @property + def is_animated(self) -> bool: + return len(self.layers) > 1 + + def seek(self, layer: int) -> None: + if not self._seek_check(layer): + return + if isinstance(self._fp, DeferredError): + raise self._fp.ex + + # seek to given layer (1..max) + _, mode, _, tile = self.layers[layer - 1] + self._mode = mode + self.tile = tile + self.frame = layer + self.fp = self._fp + + def tell(self) -> int: + # return layer number (0=image, 1..max=layers) + return self.frame + + +def _layerinfo( + fp: IO[bytes], ct_bytes: int +) -> list[tuple[str, str, tuple[int, int, int, int], list[ImageFile._Tile]]]: + # read layerinfo block + layers = [] + + def read(size: int) -> bytes: + return ImageFile._safe_read(fp, size) + + ct = si16(read(2)) + + # sanity check + if ct_bytes < (abs(ct) * 20): + msg = "Layer block too short for number of layers requested" + raise SyntaxError(msg) + + for _ in range(abs(ct)): + # bounding box + y0 = si32(read(4)) + x0 = si32(read(4)) + y1 = si32(read(4)) + x1 = si32(read(4)) + + # image info + bands = [] + ct_types = i16(read(2)) + if ct_types > 4: + fp.seek(ct_types * 6 + 12, io.SEEK_CUR) + size = i32(read(4)) + fp.seek(size, io.SEEK_CUR) + continue + + for _ in range(ct_types): + type = i16(read(2)) + + if type == 65535: + b = "A" + else: + b = "RGBA"[type] + + bands.append(b) + read(4) # size + + # figure out the image mode + bands.sort() + if bands == ["R"]: + mode = "L" + elif bands == ["B", "G", "R"]: + mode = "RGB" + elif bands == ["A", "B", "G", "R"]: + mode = "RGBA" + else: + mode = "" # unknown + + # skip over blend flags and extra information + read(12) # filler + name = "" + size = i32(read(4)) # length of the extra data field + if size: + data_end = fp.tell() + size + + length = i32(read(4)) + if length: + fp.seek(length - 16, io.SEEK_CUR) + + length = i32(read(4)) + if length: + fp.seek(length, io.SEEK_CUR) + + 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") + + fp.seek(data_end) + layers.append((name, mode, (x0, y0, x1, y1))) + + # get tiles + layerinfo = [] + for i, (name, mode, bbox) in enumerate(layers): + tile = [] + for m in mode: + t = _maketile(fp, m, bbox, 1) + if t: + tile.extend(t) + layerinfo.append((name, mode, bbox, tile)) + + return layerinfo + + +def _maketile( + file: IO[bytes], mode: str, bbox: tuple[int, int, int, int], channels: int +) -> list[ImageFile._Tile]: + tiles = [] + 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 + for channel in range(channels): + layer = mode[channel] + if mode == "CMYK": + layer += ";I" + tiles.append(ImageFile._Tile("raw", bbox, offset, layer)) + offset = offset + xsize * ysize + + elif compression == 1: + # + # packbits compression + i = 0 + bytecount = read(channels * ysize * 2) + offset = file.tell() + for channel in range(channels): + layer = mode[channel] + if mode == "CMYK": + layer += ";I" + tiles.append(ImageFile._Tile("packbits", bbox, offset, layer)) + for y in range(ysize): + offset = offset + i16(bytecount, i) + i += 2 + + file.seek(offset) + + if offset & 1: + read(1) # padding + + return tiles + + +# -------------------------------------------------------------------- +# registry + + +Image.register_open(PsdImageFile.format, PsdImageFile, _accept) + +Image.register_extension(PsdImageFile.format, ".psd") + +Image.register_mime(PsdImageFile.format, "image/vnd.adobe.photoshop") diff --git a/voxvera/vendor/PIL/QoiImagePlugin.py b/voxvera/vendor/PIL/QoiImagePlugin.py new file mode 100644 index 0000000..d0709b1 --- /dev/null +++ b/voxvera/vendor/PIL/QoiImagePlugin.py @@ -0,0 +1,235 @@ +# +# The Python Imaging Library. +# +# QOI support for PIL +# +# See the README file for information on usage and redistribution. +# +from __future__ import annotations + +import os +from typing import IO + +from . import Image, ImageFile +from ._binary import i32be as i32 +from ._binary import o8 +from ._binary import o32be as o32 + + +def _accept(prefix: bytes) -> bool: + return prefix.startswith(b"qoif") + + +class QoiImageFile(ImageFile.ImageFile): + format = "QOI" + format_description = "Quite OK Image" + + def _open(self) -> None: + assert self.fp is not None + if not _accept(self.fp.read(4)): + msg = "not a QOI file" + raise SyntaxError(msg) + + self._size = i32(self.fp.read(4)), i32(self.fp.read(4)) + + channels = self.fp.read(1)[0] + self._mode = "RGB" if channels == 3 else "RGBA" + + self.fp.seek(1, os.SEEK_CUR) # colorspace + self.tile = [ImageFile._Tile("qoi", (0, 0) + self._size, self.fp.tell())] + + +class QoiDecoder(ImageFile.PyDecoder): + _pulls_fd = True + _previous_pixel: bytes | bytearray | None = None + _previously_seen_pixels: dict[int, bytes | bytearray] = {} + + def _add_to_previous_pixels(self, value: bytes | bytearray) -> None: + self._previous_pixel = value + + r, g, b, a = value + hash_value = (r * 3 + g * 5 + b * 7 + a * 11) % 64 + self._previously_seen_pixels[hash_value] = value + + def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: + assert self.fd is not None + + self._previously_seen_pixels = {} + self._previous_pixel = bytearray((0, 0, 0, 255)) + + data = bytearray() + bands = Image.getmodebands(self.mode) + dest_length = self.state.xsize * self.state.ysize * bands + while len(data) < dest_length: + byte = self.fd.read(1)[0] + value: bytes | bytearray + if byte == 0b11111110 and self._previous_pixel: # QOI_OP_RGB + value = bytearray(self.fd.read(3)) + self._previous_pixel[3:] + elif byte == 0b11111111: # QOI_OP_RGBA + value = self.fd.read(4) + else: + op = byte >> 6 + if op == 0: # QOI_OP_INDEX + op_index = byte & 0b00111111 + value = self._previously_seen_pixels.get( + op_index, bytearray((0, 0, 0, 0)) + ) + elif op == 1 and self._previous_pixel: # QOI_OP_DIFF + value = bytearray( + ( + (self._previous_pixel[0] + ((byte & 0b00110000) >> 4) - 2) + % 256, + (self._previous_pixel[1] + ((byte & 0b00001100) >> 2) - 2) + % 256, + (self._previous_pixel[2] + (byte & 0b00000011) - 2) % 256, + self._previous_pixel[3], + ) + ) + elif op == 2 and self._previous_pixel: # QOI_OP_LUMA + second_byte = self.fd.read(1)[0] + diff_green = (byte & 0b00111111) - 32 + diff_red = ((second_byte & 0b11110000) >> 4) - 8 + diff_blue = (second_byte & 0b00001111) - 8 + + value = bytearray( + tuple( + (self._previous_pixel[i] + diff_green + diff) % 256 + for i, diff in enumerate((diff_red, 0, diff_blue)) + ) + ) + value += self._previous_pixel[3:] + elif op == 3 and self._previous_pixel: # QOI_OP_RUN + run_length = (byte & 0b00111111) + 1 + value = self._previous_pixel + if bands == 3: + value = value[:3] + data += value * run_length + continue + self._add_to_previous_pixels(value) + + if bands == 3: + value = value[:3] + data += value + self.set_as_raw(data) + return -1, 0 + + +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + if im.mode == "RGB": + channels = 3 + elif im.mode == "RGBA": + channels = 4 + else: + msg = "Unsupported QOI image mode" + raise ValueError(msg) + + colorspace = 0 if im.encoderinfo.get("colorspace") == "sRGB" else 1 + + fp.write(b"qoif") + fp.write(o32(im.size[0])) + fp.write(o32(im.size[1])) + fp.write(o8(channels)) + fp.write(o8(colorspace)) + + ImageFile._save(im, fp, [ImageFile._Tile("qoi", (0, 0) + im.size)]) + + +class QoiEncoder(ImageFile.PyEncoder): + _pushes_fd = True + _previous_pixel: tuple[int, int, int, int] | None = None + _previously_seen_pixels: dict[int, tuple[int, int, int, int]] = {} + _run = 0 + + def _write_run(self) -> bytes: + data = o8(0b11000000 | (self._run - 1)) # QOI_OP_RUN + self._run = 0 + return data + + def _delta(self, left: int, right: int) -> int: + result = (left - right) & 255 + if result >= 128: + result -= 256 + return result + + def encode(self, bufsize: int) -> tuple[int, int, bytes]: + assert self.im is not None + + self._previously_seen_pixels = {0: (0, 0, 0, 0)} + self._previous_pixel = (0, 0, 0, 255) + + data = bytearray() + w, h = self.im.size + bands = Image.getmodebands(self.mode) + + for y in range(h): + for x in range(w): + pixel = self.im.getpixel((x, y)) + if bands == 3: + pixel = (*pixel, 255) + + if pixel == self._previous_pixel: + self._run += 1 + if self._run == 62: + data += self._write_run() + else: + if self._run: + data += self._write_run() + + r, g, b, a = pixel + hash_value = (r * 3 + g * 5 + b * 7 + a * 11) % 64 + if self._previously_seen_pixels.get(hash_value) == pixel: + data += o8(hash_value) # QOI_OP_INDEX + elif self._previous_pixel: + self._previously_seen_pixels[hash_value] = pixel + + prev_r, prev_g, prev_b, prev_a = self._previous_pixel + if prev_a == a: + delta_r = self._delta(r, prev_r) + delta_g = self._delta(g, prev_g) + delta_b = self._delta(b, prev_b) + + if ( + -2 <= delta_r < 2 + and -2 <= delta_g < 2 + and -2 <= delta_b < 2 + ): + data += o8( + 0b01000000 + | (delta_r + 2) << 4 + | (delta_g + 2) << 2 + | (delta_b + 2) + ) # QOI_OP_DIFF + else: + delta_gr = self._delta(delta_r, delta_g) + delta_gb = self._delta(delta_b, delta_g) + if ( + -8 <= delta_gr < 8 + and -32 <= delta_g < 32 + and -8 <= delta_gb < 8 + ): + data += o8( + 0b10000000 | (delta_g + 32) + ) # QOI_OP_LUMA + data += o8((delta_gr + 8) << 4 | (delta_gb + 8)) + else: + data += o8(0b11111110) # QOI_OP_RGB + data += bytes(pixel[:3]) + else: + data += o8(0b11111111) # QOI_OP_RGBA + data += bytes(pixel) + + self._previous_pixel = pixel + + if self._run: + data += self._write_run() + data += bytes((0, 0, 0, 0, 0, 0, 0, 1)) # padding + + return len(data), 0, data + + +Image.register_open(QoiImageFile.format, QoiImageFile, _accept) +Image.register_decoder("qoi", QoiDecoder) +Image.register_extension(QoiImageFile.format, ".qoi") + +Image.register_save(QoiImageFile.format, _save) +Image.register_encoder("qoi", QoiEncoder) diff --git a/voxvera/vendor/PIL/SgiImagePlugin.py b/voxvera/vendor/PIL/SgiImagePlugin.py new file mode 100644 index 0000000..8530221 --- /dev/null +++ b/voxvera/vendor/PIL/SgiImagePlugin.py @@ -0,0 +1,231 @@ +# +# The Python Imaging Library. +# $Id$ +# +# SGI image file handling +# +# See "The SGI Image File Format (Draft version 0.97)", Paul Haeberli. +# +# +# +# History: +# 2017-22-07 mb Add RLE decompression +# 2016-16-10 mb Add save method without compression +# 1995-09-10 fl Created +# +# Copyright (c) 2016 by Mickael Bonfill. +# 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. +# +from __future__ import annotations + +import os +import struct +from typing import IO + +from . import Image, ImageFile +from ._binary import i16be as i16 +from ._binary import o8 + + +def _accept(prefix: bytes) -> bool: + return len(prefix) >= 2 and i16(prefix) == 474 + + +MODES = { + (1, 1, 1): "L", + (1, 2, 1): "L", + (2, 1, 1): "L;16B", + (2, 2, 1): "L;16B", + (1, 3, 3): "RGB", + (2, 3, 3): "RGB;16B", + (1, 3, 4): "RGBA", + (2, 3, 4): "RGBA;16B", +} + + +## +# Image plugin for SGI images. +class SgiImageFile(ImageFile.ImageFile): + format = "SGI" + format_description = "SGI Image File Format" + + def _open(self) -> None: + # HEAD + assert self.fp is not None + + headlen = 512 + s = self.fp.read(headlen) + + if not _accept(s): + msg = "Not an SGI image file" + raise ValueError(msg) + + # compression : verbatim or RLE + compression = s[2] + + # bpc : 1 or 2 bytes (8bits or 16bits) + bpc = s[3] + + # dimension : 1, 2 or 3 (depending on xsize, ysize and zsize) + dimension = i16(s, 4) + + # xsize : width + xsize = i16(s, 6) + + # ysize : height + ysize = i16(s, 8) + + # zsize : channels count + zsize = i16(s, 10) + + # determine mode from bits/zsize + try: + rawmode = MODES[(bpc, dimension, zsize)] + except KeyError: + msg = "Unsupported SGI image mode" + raise ValueError(msg) + + self._size = xsize, ysize + self._mode = rawmode.split(";")[0] + if self.mode == "RGB": + self.custom_mimetype = "image/rgb" + + # orientation -1 : scanlines begins at the bottom-left corner + orientation = -1 + + # decoder info + if compression == 0: + pagesize = xsize * ysize * bpc + if bpc == 2: + self.tile = [ + ImageFile._Tile( + "SGI16", + (0, 0) + self.size, + headlen, + (self.mode, 0, orientation), + ) + ] + else: + self.tile = [] + offset = headlen + for layer in self.mode: + self.tile.append( + ImageFile._Tile( + "raw", (0, 0) + self.size, offset, (layer, 0, orientation) + ) + ) + offset += pagesize + elif compression == 1: + self.tile = [ + ImageFile._Tile( + "sgi_rle", (0, 0) + self.size, headlen, (rawmode, orientation, bpc) + ) + ] + + +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + if im.mode not in {"RGB", "RGBA", "L"}: + msg = "Unsupported SGI image mode" + raise ValueError(msg) + + # Get the keyword arguments + info = im.encoderinfo + + # Byte-per-pixel precision, 1 = 8bits per pixel + bpc = info.get("bpc", 1) + + if bpc not in (1, 2): + msg = "Unsupported number of bytes per pixel" + raise ValueError(msg) + + # Flip the image, since the origin of SGI file is the bottom-left corner + orientation = -1 + # Define the file as SGI File Format + magic_number = 474 + # Run-Length Encoding Compression - Unsupported at this time + rle = 0 + + # X Dimension = width / Y Dimension = height + x, y = im.size + # Z Dimension: Number of channels + z = len(im.mode) + # Number of dimensions (x,y,z) + if im.mode == "L": + dimension = 1 if y == 1 else 2 + else: + dimension = 3 + + # Minimum Byte value + pinmin = 0 + # Maximum Byte value (255 = 8bits per pixel) + pinmax = 255 + # Image name (79 characters max, truncated below in write) + img_name = os.path.splitext(os.path.basename(filename))[0] + if isinstance(img_name, str): + img_name = img_name.encode("ascii", "ignore") + # Standard representation of pixel in the file + colormap = 0 + fp.write(struct.pack(">h", magic_number)) + fp.write(o8(rle)) + fp.write(o8(bpc)) + fp.write(struct.pack(">H", dimension)) + fp.write(struct.pack(">H", x)) + fp.write(struct.pack(">H", y)) + fp.write(struct.pack(">H", z)) + fp.write(struct.pack(">l", pinmin)) + fp.write(struct.pack(">l", pinmax)) + fp.write(struct.pack("4s", b"")) # dummy + fp.write(struct.pack("79s", img_name)) # truncates to 79 chars + fp.write(struct.pack("s", b"")) # force null byte after img_name + fp.write(struct.pack(">l", colormap)) + fp.write(struct.pack("404s", b"")) # dummy + + rawmode = "L" + if bpc == 2: + rawmode = "L;16B" + + for channel in im.split(): + fp.write(channel.tobytes("raw", rawmode, 0, orientation)) + + if hasattr(fp, "flush"): + fp.flush() + + +class SGI16Decoder(ImageFile.PyDecoder): + _pulls_fd = True + + def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: + assert self.fd is not None + assert self.im is not None + + rawmode, stride, orientation = self.args + pagesize = self.state.xsize * self.state.ysize + zsize = len(self.mode) + self.fd.seek(512) + + for band in range(zsize): + channel = Image.new("L", (self.state.xsize, self.state.ysize)) + channel.frombytes( + self.fd.read(2 * pagesize), "raw", "L;16B", stride, orientation + ) + self.im.putband(channel.im, band) + + return -1, 0 + + +# +# registry + + +Image.register_decoder("SGI16", SGI16Decoder) +Image.register_open(SgiImageFile.format, SgiImageFile, _accept) +Image.register_save(SgiImageFile.format, _save) +Image.register_mime(SgiImageFile.format, "image/sgi") + +Image.register_extensions(SgiImageFile.format, [".bw", ".rgb", ".rgba", ".sgi"]) + +# End of file diff --git a/voxvera/vendor/PIL/SpiderImagePlugin.py b/voxvera/vendor/PIL/SpiderImagePlugin.py new file mode 100644 index 0000000..8662922 --- /dev/null +++ b/voxvera/vendor/PIL/SpiderImagePlugin.py @@ -0,0 +1,332 @@ +# +# 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 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: +# https://spider.wadsworth.org/spider_doc/spider/docs/spider.html +# +# Details about the Spider image format: +# https://spider.wadsworth.org/spider_doc/spider/docs/image_doc.html +# +from __future__ import annotations + +import os +import struct +import sys +from typing import IO, Any, cast + +from . import Image, ImageFile +from ._util import DeferredError + +TYPE_CHECKING = False + + +def isInt(f: Any) -> int: + try: + i = int(f) + if f - i == 0: + return 1 + else: + return 0 + except (ValueError, OverflowError): + 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: tuple[float, ...]) -> int: + 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 + if labbyt != (labrec * lenbyt): + return 0 + # looks like a valid header + return labbyt + + +def isSpiderImage(filename: str) -> int: + with open(filename, "rb") as fp: + f = fp.read(92) # read 23 * 4 bytes + 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" + _close_exclusive_fp_after_loading = False + + def _open(self) -> None: + # check header + n = 27 * 4 # read 27 float values + assert self.fp is not None + 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: + msg = "not a valid Spider file" + raise SyntaxError(msg) + except struct.error as e: + msg = "not a valid Spider file" + raise SyntaxError(msg) from e + + h = (99,) + t # add 1 value : spider header index starts at 1 + iform = int(h[5]) + if iform != 1: + msg = "not a Spider 2D image" + raise SyntaxError(msg) + + 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: + msg = "inconsistent stack header values" + raise SyntaxError(msg) + + if self.bigendian: + self.rawmode = "F;32BF" + else: + self.rawmode = "F;32F" + self._mode = "F" + + self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, offset, self.rawmode)] + self._fp = self.fp # FIXME: hack + + @property + def n_frames(self) -> int: + return self._nimages + + @property + def is_animated(self) -> bool: + return self._nimages > 1 + + # 1st image index is zero (although SPIDER imgnumber starts at 1) + def tell(self) -> int: + if self.imgnumber < 1: + return 0 + else: + return self.imgnumber - 1 + + def seek(self, frame: int) -> None: + if self.istack == 0: + msg = "attempt to seek in a non-stack file" + raise EOFError(msg) + if not self._seek_check(frame): + return + if isinstance(self._fp, DeferredError): + raise self._fp.ex + 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: int = 255) -> Image.Image: + extrema = self.getextrema() + assert isinstance(extrema[0], float) + minimum, maximum = cast(tuple[float, float], extrema) + m: float = 1 + if maximum != minimum: + m = depth / (maximum - minimum) + b = -m * minimum + return self.point(lambda i: i * m + b).convert("L") + + if TYPE_CHECKING: + from . import ImageTk + + # returns a ImageTk.PhotoImage object, after rescaling to 0..255 + def tkPhotoImage(self) -> ImageTk.PhotoImage: + from . import ImageTk + + return ImageTk.PhotoImage(self.convert2byte(), palette=256) + + +# -------------------------------------------------------------------- +# Image series + + +# given a list of filenames, return a list of images +def loadImageSeries(filelist: list[str] | None = None) -> list[Image.Image] | None: + """create a list of :py:class:`~PIL.Image.Image` objects for use in a montage""" + if filelist is None or len(filelist) < 1: + return None + + byte_imgs = [] + for img in filelist: + if not os.path.exists(img): + print(f"unable to find {img}") + continue + try: + with Image.open(img) as im: + assert isinstance(im, SpiderImageFile) + byte_im = im.convert2byte() + except Exception: + if not isSpiderImage(img): + print(f"{img} is not a Spider image file") + continue + byte_im.info["filename"] = img + byte_imgs.append(byte_im) + return byte_imgs + + +# -------------------------------------------------------------------- +# For saving images in Spider format + + +def makeSpiderHeader(im: Image.Image) -> list[bytes]: + nsam, nrow = im.size + lenbyt = nsam * 4 # There are labrec records in the header + labrec = int(1024 / lenbyt) + if 1024 % lenbyt != 0: + labrec += 1 + labbyt = labrec * lenbyt + nvalues = int(labbyt / 4) + if nvalues < 23: + return [] + + hdr = [0.0] * nvalues + + # NB these are Fortran indices + hdr[1] = 1.0 # nslice (=1 for an image) + hdr[2] = float(nrow) # number of rows per slice + hdr[3] = float(nrow) # number of records in the image + 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 + return [struct.pack("f", v) for v in hdr] + + +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + if im.mode != "F": + im = im.convert("F") + + hdr = makeSpiderHeader(im) + if len(hdr) < 256: + msg = "Error creating Spider header" + raise OSError(msg) + + # write the SPIDER header + fp.writelines(hdr) + + rawmode = "F;32NF" # 32-bit native floating point + ImageFile._save(im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 0, rawmode)]) + + +def _save_spider(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + # get the filename extension and register it with Image + filename_ext = os.path.splitext(filename)[1] + ext = filename_ext.decode() if isinstance(filename_ext, bytes) else filename_ext + Image.register_extension(SpiderImageFile.format, ext) + _save(im, fp, filename) + + +# -------------------------------------------------------------------- + + +Image.register_open(SpiderImageFile.format, SpiderImageFile) +Image.register_save(SpiderImageFile.format, _save_spider) + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Syntax: python3 SpiderImagePlugin.py [infile] [outfile]") + sys.exit() + + filename = sys.argv[1] + if not isSpiderImage(filename): + print("input image must be in Spider format") + sys.exit() + + with Image.open(filename) as im: + print(f"image: {im}") + print(f"format: {im.format}") + print(f"size: {im.size}") + print(f"mode: {im.mode}") + print("max, min: ", end=" ") + print(im.getextrema()) + + if len(sys.argv) > 2: + outfile = sys.argv[2] + + # perform some image operation + transposed_im = im.transpose(Image.Transpose.FLIP_LEFT_RIGHT) + print( + f"saving a flipped version of {os.path.basename(filename)} " + f"as {outfile} " + ) + transposed_im.save(outfile, SpiderImageFile.format) diff --git a/voxvera/vendor/PIL/SunImagePlugin.py b/voxvera/vendor/PIL/SunImagePlugin.py new file mode 100644 index 0000000..8912379 --- /dev/null +++ b/voxvera/vendor/PIL/SunImagePlugin.py @@ -0,0 +1,145 @@ +# +# 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. +# +from __future__ import annotations + +from . import Image, ImageFile, ImagePalette +from ._binary import i32be as i32 + + +def _accept(prefix: bytes) -> bool: + return len(prefix) >= 4 and i32(prefix) == 0x59A66A95 + + +## +# Image plugin for Sun raster files. + + +class SunImageFile(ImageFile.ImageFile): + format = "SUN" + format_description = "Sun Raster File" + + def _open(self) -> None: + # The Sun Raster file header is 32 bytes in length + # and has the following format: + + # typedef struct _SunRaster + # { + # DWORD MagicNumber; /* Magic (identification) number */ + # DWORD Width; /* Width of image in pixels */ + # DWORD Height; /* Height of image in pixels */ + # DWORD Depth; /* Number of bits per pixel */ + # DWORD Length; /* Size of image data in bytes */ + # DWORD Type; /* Type of raster file */ + # DWORD ColorMapType; /* Type of color map */ + # DWORD ColorMapLength; /* Size of the color map in bytes */ + # } SUNRASTER; + + assert self.fp is not None + + # HEAD + s = self.fp.read(32) + if not _accept(s): + msg = "not an SUN raster file" + raise SyntaxError(msg) + + offset = 32 + + self._size = i32(s, 4), i32(s, 8) + + depth = i32(s, 12) + # data_length = i32(s, 16) # unreliable, ignore. + file_type = i32(s, 20) + palette_type = i32(s, 24) # 0: None, 1: RGB, 2: Raw/arbitrary + palette_length = i32(s, 28) + + if depth == 1: + self._mode, rawmode = "1", "1;I" + elif depth == 4: + self._mode, rawmode = "L", "L;4" + elif depth == 8: + self._mode = rawmode = "L" + elif depth == 24: + if file_type == 3: + self._mode, rawmode = "RGB", "RGB" + else: + self._mode, rawmode = "RGB", "BGR" + elif depth == 32: + if file_type == 3: + self._mode, rawmode = "RGB", "RGBX" + else: + self._mode, rawmode = "RGB", "BGRX" + else: + msg = "Unsupported Mode/Bit Depth" + raise SyntaxError(msg) + + if palette_length: + if palette_length > 1024: + msg = "Unsupported Color Palette Length" + raise SyntaxError(msg) + + if palette_type != 1: + msg = "Unsupported Palette Type" + raise SyntaxError(msg) + + offset = offset + palette_length + self.palette = ImagePalette.raw("RGB;L", self.fp.read(palette_length)) + if self.mode == "L": + self._mode = "P" + rawmode = rawmode.replace("L", "P") + + # 16 bit boundaries on stride + stride = ((self.size[0] * depth + 15) // 16) * 2 + + # file type: Type is the version (or flavor) of the bitmap + # file. The following values are typically found in the Type + # field: + # 0000h Old + # 0001h Standard + # 0002h Byte-encoded + # 0003h RGB format + # 0004h TIFF format + # 0005h IFF format + # FFFFh Experimental + + # Old and standard are the same, except for the length tag. + # byte-encoded is run-length-encoded + # RGB looks similar to standard, but RGB byte order + # TIFF and IFF mean that they were converted from T/IFF + # Experimental means that it's something else. + # (https://www.fileformat.info/format/sunraster/egff.htm) + + if file_type in (0, 1, 3, 4, 5): + self.tile = [ + ImageFile._Tile("raw", (0, 0) + self.size, offset, (rawmode, stride)) + ] + elif file_type == 2: + self.tile = [ + ImageFile._Tile("sun_rle", (0, 0) + self.size, offset, rawmode) + ] + else: + msg = "Unsupported Sun Raster file type" + raise SyntaxError(msg) + + +# +# registry + + +Image.register_open(SunImageFile.format, SunImageFile, _accept) + +Image.register_extension(SunImageFile.format, ".ras") diff --git a/voxvera/vendor/PIL/TarIO.py b/voxvera/vendor/PIL/TarIO.py new file mode 100644 index 0000000..86490a4 --- /dev/null +++ b/voxvera/vendor/PIL/TarIO.py @@ -0,0 +1,61 @@ +# +# 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 __future__ import annotations + +import io + +from . import ContainerIO + + +class TarIO(ContainerIO.ContainerIO[bytes]): + """A file object that provides read access to a given member of a TAR file.""" + + def __init__(self, tarfile: str, file: str) -> None: + """ + Create file object. + + :param tarfile: Name of TAR file. + :param file: Name of member file. + """ + self.fh = open(tarfile, "rb") + + while True: + s = self.fh.read(512) + if len(s) != 512: + self.fh.close() + + msg = "unexpected end of tar file" + raise OSError(msg) + + name = s[:100].decode("utf-8") + i = name.find("\0") + if i == 0: + self.fh.close() + + msg = "cannot find subfile" + raise OSError(msg) + if i > 0: + name = name[:i] + + size = int(s[124:135], 8) + + if file == name: + break + + self.fh.seek((size + 511) & (~511), io.SEEK_CUR) + + # Open region + super().__init__(self.fh, self.fh.tell(), size) diff --git a/voxvera/vendor/PIL/TgaImagePlugin.py b/voxvera/vendor/PIL/TgaImagePlugin.py new file mode 100644 index 0000000..90d5b5c --- /dev/null +++ b/voxvera/vendor/PIL/TgaImagePlugin.py @@ -0,0 +1,264 @@ +# +# 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. +# +from __future__ import annotations + +import warnings +from typing import IO + +from . import Image, ImageFile, ImagePalette +from ._binary import i16le as i16 +from ._binary import o8 +from ._binary import o16le as o16 + +# +# -------------------------------------------------------------------- +# Read RGA file + + +MODES = { + # map imagetype/depth to rawmode + (1, 8): "P", + (3, 1): "1", + (3, 8): "L", + (3, 16): "LA", + (2, 16): "BGRA;15Z", + (2, 24): "BGR", + (2, 32): "BGRA", +} + + +## +# Image plugin for Targa files. + + +class TgaImageFile(ImageFile.ImageFile): + format = "TGA" + format_description = "Targa" + + def _open(self) -> None: + # process header + assert self.fp is not None + + s = self.fp.read(18) + + id_len = s[0] + + colormaptype = s[1] + imagetype = s[2] + + depth = s[16] + + flags = 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) + ): + msg = "not a TGA file" + raise SyntaxError(msg) + + # image mode + if imagetype in (3, 11): + self._mode = "L" + if depth == 1: + self._mode = "1" # ??? + elif depth == 16: + self._mode = "LA" + elif imagetype in (1, 9): + self._mode = "P" if colormaptype else "L" + elif imagetype in (2, 10): + self._mode = "RGB" if depth == 24 else "RGBA" + else: + msg = "unknown TGA mode" + raise SyntaxError(msg) + + # orientation + orientation = flags & 0x30 + self._flip_horizontally = orientation in [0x10, 0x30] + if orientation in [0x20, 0x30]: + orientation = 1 + elif orientation in [0, 0x10]: + orientation = -1 + else: + msg = "unknown TGA orientation" + raise SyntaxError(msg) + + self.info["orientation"] = orientation + + if imagetype & 8: + self.info["compression"] = "tga_rle" + + if id_len: + self.info["id_section"] = self.fp.read(id_len) + + if colormaptype: + # read palette + start, size, mapdepth = i16(s, 3), i16(s, 5), s[7] + if mapdepth == 16: + self.palette = ImagePalette.raw( + "BGRA;15Z", bytes(2 * start) + self.fp.read(2 * size) + ) + self.palette.mode = "RGBA" + elif mapdepth == 24: + self.palette = ImagePalette.raw( + "BGR", bytes(3 * start) + self.fp.read(3 * size) + ) + elif mapdepth == 32: + self.palette = ImagePalette.raw( + "BGRA", bytes(4 * start) + self.fp.read(4 * size) + ) + else: + msg = "unknown TGA map depth" + raise SyntaxError(msg) + + # setup tile descriptor + try: + rawmode = MODES[(imagetype & 7, depth)] + if imagetype & 8: + # compressed + self.tile = [ + ImageFile._Tile( + "tga_rle", + (0, 0) + self.size, + self.fp.tell(), + (rawmode, orientation, depth), + ) + ] + else: + self.tile = [ + ImageFile._Tile( + "raw", + (0, 0) + self.size, + self.fp.tell(), + (rawmode, 0, orientation), + ) + ] + except KeyError: + pass # cannot decode + + def load_end(self) -> None: + if self._flip_horizontally: + self.im = self.im.transpose(Image.Transpose.FLIP_LEFT_RIGHT) + + +# +# -------------------------------------------------------------------- +# Write TGA file + + +SAVE = { + "1": ("1", 1, 0, 3), + "L": ("L", 8, 0, 3), + "LA": ("LA", 16, 0, 3), + "P": ("P", 8, 1, 1), + "RGB": ("BGR", 24, 0, 2), + "RGBA": ("BGRA", 32, 0, 2), +} + + +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + try: + rawmode, bits, colormaptype, imagetype = SAVE[im.mode] + except KeyError as e: + msg = f"cannot write mode {im.mode} as TGA" + raise OSError(msg) from e + + if "rle" in im.encoderinfo: + rle = im.encoderinfo["rle"] + else: + compression = im.encoderinfo.get("compression", im.info.get("compression")) + rle = compression == "tga_rle" + if rle: + imagetype += 8 + + id_section = im.encoderinfo.get("id_section", im.info.get("id_section", "")) + id_len = len(id_section) + if id_len > 255: + id_len = 255 + id_section = id_section[:255] + warnings.warn("id_section has been trimmed to 255 characters") + + if colormaptype: + palette = im.im.getpalette("RGB", "BGR") + colormaplength, colormapentry = len(palette) // 3, 24 + else: + colormaplength, colormapentry = 0, 0 + + if im.mode in ("LA", "RGBA"): + flags = 8 + else: + flags = 0 + + orientation = im.encoderinfo.get("orientation", im.info.get("orientation", -1)) + if orientation > 0: + flags = flags | 0x20 + + fp.write( + o8(id_len) + + o8(colormaptype) + + o8(imagetype) + + o16(0) # colormapfirst + + o16(colormaplength) + + o8(colormapentry) + + o16(0) + + o16(0) + + o16(im.size[0]) + + o16(im.size[1]) + + o8(bits) + + o8(flags) + ) + + if id_section: + fp.write(id_section) + + if colormaptype: + fp.write(palette) + + if rle: + ImageFile._save( + im, + fp, + [ImageFile._Tile("tga_rle", (0, 0) + im.size, 0, (rawmode, orientation))], + ) + else: + ImageFile._save( + im, + fp, + [ImageFile._Tile("raw", (0, 0) + im.size, 0, (rawmode, 0, orientation))], + ) + + # write targa version 2 footer + fp.write(b"\000" * 8 + b"TRUEVISION-XFILE." + b"\000") + + +# +# -------------------------------------------------------------------- +# Registry + + +Image.register_open(TgaImageFile.format, TgaImageFile) +Image.register_save(TgaImageFile.format, _save) + +Image.register_extensions(TgaImageFile.format, [".tga", ".icb", ".vda", ".vst"]) + +Image.register_mime(TgaImageFile.format, "image/x-tga") diff --git a/voxvera/vendor/PIL/TiffImagePlugin.py b/voxvera/vendor/PIL/TiffImagePlugin.py new file mode 100644 index 0000000..de2ce06 --- /dev/null +++ b/voxvera/vendor/PIL/TiffImagePlugin.py @@ -0,0 +1,2338 @@ +# +# 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 annotations + +import io +import itertools +import logging +import math +import os +import struct +import warnings +from collections.abc import Callable, MutableMapping +from fractions import Fraction +from numbers import Number, Rational +from typing import IO, Any, cast + +from . import ExifTags, Image, ImageFile, ImageOps, ImagePalette, TiffTags +from ._binary import i16be as i16 +from ._binary import i32be as i32 +from ._binary import o8 +from ._util import DeferredError, is_path +from .TiffTags import TYPES + +TYPE_CHECKING = False +if TYPE_CHECKING: + from collections.abc import Iterator + from typing import NoReturn + + from ._typing import Buffer, IntegralLike, StrOrBytesPath + +logger = logging.getLogger(__name__) + +# Set these to true to force use of libtiff for reading or writing. +READ_LIBTIFF = False +WRITE_LIBTIFF = False +STRIP_SIZE = 65536 + +II = b"II" # little-endian (Intel style) +MM = b"MM" # big-endian (Motorola style) + +# +# -------------------------------------------------------------------- +# Read TIFF files + +# a few tag names, just to make the code below a bit more readable +OSUBFILETYPE = 255 +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 +TRANSFERFUNCTION = 301 +SOFTWARE = 305 +DATE_TIME = 306 +ARTIST = 315 +PREDICTOR = 317 +COLORMAP = 320 +TILEWIDTH = 322 +TILELENGTH = 323 +TILEOFFSETS = 324 +TILEBYTECOUNTS = 325 +SUBIFD = 330 +EXTRASAMPLES = 338 +SAMPLEFORMAT = 339 +JPEGTABLES = 347 +YCBCRSUBSAMPLING = 530 +REFERENCEBLACKWHITE = 532 +COPYRIGHT = 33432 +IPTC_NAA_CHUNK = 33723 # newsphoto properties +PHOTOSHOP_CHUNK = 34377 # photoshop properties +ICCPROFILE = 34675 +EXIFIFD = 34665 +XMP = 700 +JPEGQUALITY = 65537 # pseudo-tag by libtiff + +# https://github.com/imagej/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", + 34925: "lzma", + 50000: "zstd", + 50001: "webp", +} + +COMPRESSION_INFO_REV = {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"), + (MM, 0, (1,), 1, (1,), ()): ("1", "1;I"), + (II, 0, (1,), 2, (1,), ()): ("1", "1;IR"), + (MM, 0, (1,), 2, (1,), ()): ("1", "1;IR"), + (II, 1, (1,), 1, (1,), ()): ("1", "1"), + (MM, 1, (1,), 1, (1,), ()): ("1", "1"), + (II, 1, (1,), 2, (1,), ()): ("1", "1;R"), + (MM, 1, (1,), 2, (1,), ()): ("1", "1;R"), + (II, 0, (1,), 1, (2,), ()): ("L", "L;2I"), + (MM, 0, (1,), 1, (2,), ()): ("L", "L;2I"), + (II, 0, (1,), 2, (2,), ()): ("L", "L;2IR"), + (MM, 0, (1,), 2, (2,), ()): ("L", "L;2IR"), + (II, 1, (1,), 1, (2,), ()): ("L", "L;2"), + (MM, 1, (1,), 1, (2,), ()): ("L", "L;2"), + (II, 1, (1,), 2, (2,), ()): ("L", "L;2R"), + (MM, 1, (1,), 2, (2,), ()): ("L", "L;2R"), + (II, 0, (1,), 1, (4,), ()): ("L", "L;4I"), + (MM, 0, (1,), 1, (4,), ()): ("L", "L;4I"), + (II, 0, (1,), 2, (4,), ()): ("L", "L;4IR"), + (MM, 0, (1,), 2, (4,), ()): ("L", "L;4IR"), + (II, 1, (1,), 1, (4,), ()): ("L", "L;4"), + (MM, 1, (1,), 1, (4,), ()): ("L", "L;4"), + (II, 1, (1,), 2, (4,), ()): ("L", "L;4R"), + (MM, 1, (1,), 2, (4,), ()): ("L", "L;4R"), + (II, 0, (1,), 1, (8,), ()): ("L", "L;I"), + (MM, 0, (1,), 1, (8,), ()): ("L", "L;I"), + (II, 0, (1,), 2, (8,), ()): ("L", "L;IR"), + (MM, 0, (1,), 2, (8,), ()): ("L", "L;IR"), + (II, 1, (1,), 1, (8,), ()): ("L", "L"), + (MM, 1, (1,), 1, (8,), ()): ("L", "L"), + (II, 1, (2,), 1, (8,), ()): ("L", "L"), + (MM, 1, (2,), 1, (8,), ()): ("L", "L"), + (II, 1, (1,), 2, (8,), ()): ("L", "L;R"), + (MM, 1, (1,), 2, (8,), ()): ("L", "L;R"), + (II, 1, (1,), 1, (12,), ()): ("I;16", "I;12"), + (II, 0, (1,), 1, (16,), ()): ("I;16", "I;16"), + (II, 1, (1,), 1, (16,), ()): ("I;16", "I;16"), + (MM, 1, (1,), 1, (16,), ()): ("I;16B", "I;16B"), + (II, 1, (1,), 2, (16,), ()): ("I;16", "I;16R"), + (II, 1, (2,), 1, (16,), ()): ("I", "I;16S"), + (MM, 1, (2,), 1, (16,), ()): ("I", "I;16BS"), + (II, 0, (3,), 1, (32,), ()): ("F", "F;32F"), + (MM, 0, (3,), 1, (32,), ()): ("F", "F;32BF"), + (II, 1, (1,), 1, (32,), ()): ("I", "I;32N"), + (II, 1, (2,), 1, (32,), ()): ("I", "I;32S"), + (MM, 1, (2,), 1, (32,), ()): ("I", "I;32BS"), + (II, 1, (3,), 1, (32,), ()): ("F", "F;32F"), + (MM, 1, (3,), 1, (32,), ()): ("F", "F;32BF"), + (II, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"), + (MM, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"), + (II, 2, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"), + (MM, 2, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"), + (II, 2, (1,), 2, (8, 8, 8), ()): ("RGB", "RGB;R"), + (MM, 2, (1,), 2, (8, 8, 8), ()): ("RGB", "RGB;R"), + (II, 2, (1,), 1, (8, 8, 8, 8), ()): ("RGBA", "RGBA"), # missing ExtraSamples + (MM, 2, (1,), 1, (8, 8, 8, 8), ()): ("RGBA", "RGBA"), # missing ExtraSamples + (II, 2, (1,), 1, (8, 8, 8, 8), (0,)): ("RGB", "RGBX"), + (MM, 2, (1,), 1, (8, 8, 8, 8), (0,)): ("RGB", "RGBX"), + (II, 2, (1,), 1, (8, 8, 8, 8, 8), (0, 0)): ("RGB", "RGBXX"), + (MM, 2, (1,), 1, (8, 8, 8, 8, 8), (0, 0)): ("RGB", "RGBXX"), + (II, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0, 0)): ("RGB", "RGBXXX"), + (MM, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0, 0)): ("RGB", "RGBXXX"), + (II, 2, (1,), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"), + (MM, 2, (1,), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"), + (II, 2, (1,), 1, (8, 8, 8, 8, 8), (1, 0)): ("RGBA", "RGBaX"), + (MM, 2, (1,), 1, (8, 8, 8, 8, 8), (1, 0)): ("RGBA", "RGBaX"), + (II, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (1, 0, 0)): ("RGBA", "RGBaXX"), + (MM, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (1, 0, 0)): ("RGBA", "RGBaXX"), + (II, 2, (1,), 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"), + (MM, 2, (1,), 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"), + (II, 2, (1,), 1, (8, 8, 8, 8, 8), (2, 0)): ("RGBA", "RGBAX"), + (MM, 2, (1,), 1, (8, 8, 8, 8, 8), (2, 0)): ("RGBA", "RGBAX"), + (II, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (2, 0, 0)): ("RGBA", "RGBAXX"), + (MM, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (2, 0, 0)): ("RGBA", "RGBAXX"), + (II, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10 + (MM, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10 + (II, 2, (1,), 1, (16, 16, 16), ()): ("RGB", "RGB;16L"), + (MM, 2, (1,), 1, (16, 16, 16), ()): ("RGB", "RGB;16B"), + (II, 2, (1,), 1, (16, 16, 16, 16), ()): ("RGBA", "RGBA;16L"), + (MM, 2, (1,), 1, (16, 16, 16, 16), ()): ("RGBA", "RGBA;16B"), + (II, 2, (1,), 1, (16, 16, 16, 16), (0,)): ("RGB", "RGBX;16L"), + (MM, 2, (1,), 1, (16, 16, 16, 16), (0,)): ("RGB", "RGBX;16B"), + (II, 2, (1,), 1, (16, 16, 16, 16), (1,)): ("RGBA", "RGBa;16L"), + (MM, 2, (1,), 1, (16, 16, 16, 16), (1,)): ("RGBA", "RGBa;16B"), + (II, 2, (1,), 1, (16, 16, 16, 16), (2,)): ("RGBA", "RGBA;16L"), + (MM, 2, (1,), 1, (16, 16, 16, 16), (2,)): ("RGBA", "RGBA;16B"), + (II, 3, (1,), 1, (1,), ()): ("P", "P;1"), + (MM, 3, (1,), 1, (1,), ()): ("P", "P;1"), + (II, 3, (1,), 2, (1,), ()): ("P", "P;1R"), + (MM, 3, (1,), 2, (1,), ()): ("P", "P;1R"), + (II, 3, (1,), 1, (2,), ()): ("P", "P;2"), + (MM, 3, (1,), 1, (2,), ()): ("P", "P;2"), + (II, 3, (1,), 2, (2,), ()): ("P", "P;2R"), + (MM, 3, (1,), 2, (2,), ()): ("P", "P;2R"), + (II, 3, (1,), 1, (4,), ()): ("P", "P;4"), + (MM, 3, (1,), 1, (4,), ()): ("P", "P;4"), + (II, 3, (1,), 2, (4,), ()): ("P", "P;4R"), + (MM, 3, (1,), 2, (4,), ()): ("P", "P;4R"), + (II, 3, (1,), 1, (8,), ()): ("P", "P"), + (MM, 3, (1,), 1, (8,), ()): ("P", "P"), + (II, 3, (1,), 1, (8, 8), (0,)): ("P", "PX"), + (MM, 3, (1,), 1, (8, 8), (0,)): ("P", "PX"), + (II, 3, (1,), 1, (8, 8), (2,)): ("PA", "PA"), + (MM, 3, (1,), 1, (8, 8), (2,)): ("PA", "PA"), + (II, 3, (1,), 2, (8,), ()): ("P", "P;R"), + (MM, 3, (1,), 2, (8,), ()): ("P", "P;R"), + (II, 5, (1,), 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"), + (MM, 5, (1,), 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"), + (II, 5, (1,), 1, (8, 8, 8, 8, 8), (0,)): ("CMYK", "CMYKX"), + (MM, 5, (1,), 1, (8, 8, 8, 8, 8), (0,)): ("CMYK", "CMYKX"), + (II, 5, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0)): ("CMYK", "CMYKXX"), + (MM, 5, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0)): ("CMYK", "CMYKXX"), + (II, 5, (1,), 1, (16, 16, 16, 16), ()): ("CMYK", "CMYK;16L"), + (MM, 5, (1,), 1, (16, 16, 16, 16), ()): ("CMYK", "CMYK;16B"), + (II, 6, (1,), 1, (8,), ()): ("L", "L"), + (MM, 6, (1,), 1, (8,), ()): ("L", "L"), + # JPEG compressed images handled by LibTiff and auto-converted to RGBX + # Minimal Baseline TIFF requires YCbCr images to have 3 SamplesPerPixel + (II, 6, (1,), 1, (8, 8, 8), ()): ("RGB", "RGBX"), + (MM, 6, (1,), 1, (8, 8, 8), ()): ("RGB", "RGBX"), + (II, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"), + (MM, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"), +} + +MAX_SAMPLESPERPIXEL = max(len(key_tp[4]) for key_tp in OPEN_INFO) + +PREFIXES = [ + b"MM\x00\x2a", # Valid TIFF header with big-endian byte order + b"II\x2a\x00", # Valid TIFF header with little-endian byte order + b"MM\x2a\x00", # Invalid TIFF header, assume big-endian + b"II\x00\x2a", # Invalid TIFF header, assume little-endian + b"MM\x00\x2b", # BigTIFF with big-endian byte order + b"II\x2b\x00", # BigTIFF with little-endian byte order +] + + +def _accept(prefix: bytes) -> bool: + return prefix.startswith(tuple(PREFIXES)) + + +def _limit_rational( + val: float | Fraction | IFDRational, max_val: int +) -> tuple[IntegralLike, IntegralLike]: + inv = abs(val) > 1 + n_d = IFDRational(1 / val if inv else val).limit_rational(max_val) + return n_d[::-1] if inv else n_d + + +def _limit_signed_rational( + val: IFDRational, max_val: int, min_val: int +) -> tuple[IntegralLike, IntegralLike]: + frac = Fraction(val) + n_d: tuple[IntegralLike, IntegralLike] = frac.numerator, frac.denominator + + if min(float(i) for i in n_d) < min_val: + n_d = _limit_rational(val, abs(min_val)) + + n_d_float = tuple(float(i) for i in n_d) + if max(n_d_float) > max_val: + n_d = _limit_rational(n_d_float[0] / n_d_float[1], max_val) + + return n_d + + +## +# Wrapper for TIFF IFDs. + +_load_dispatch = {} +_write_dispatch = {} + + +def _delegate(op: str) -> Any: + def delegate( + self: IFDRational, *args: tuple[float, ...] + ) -> bool | float | Fraction: + return getattr(self._val, op)(*args) + + return delegate + + +class IFDRational(Rational): + """Implements a rational class where 0/0 is a legal value to match + the in the wild use of exif rationals. + + e.g., DigitalZoomRatio - 0.00/0.00 indicates that no digital zoom was used + """ + + """ If the denominator is 0, store this as a float('nan'), otherwise store + as a fractions.Fraction(). Delegate as appropriate + + """ + + __slots__ = ("_numerator", "_denominator", "_val") + + def __init__( + self, value: float | Fraction | IFDRational, denominator: int = 1 + ) -> None: + """ + :param value: either an integer numerator, a + float/rational/other number, or an IFDRational + :param denominator: Optional integer denominator + """ + self._val: Fraction | float + if isinstance(value, IFDRational): + self._numerator = value.numerator + self._denominator = value.denominator + self._val = value._val + return + + if isinstance(value, Fraction): + self._numerator = value.numerator + self._denominator = value.denominator + else: + if TYPE_CHECKING: + self._numerator = cast(IntegralLike, value) + else: + self._numerator = value + self._denominator = denominator + + if denominator == 0: + self._val = float("nan") + elif denominator == 1: + self._val = Fraction(value) + elif int(value) == value: + self._val = Fraction(int(value), denominator) + else: + self._val = Fraction(value / denominator) + + @property + def numerator(self) -> IntegralLike: + return self._numerator + + @property + def denominator(self) -> int: + return self._denominator + + def limit_rational(self, max_denominator: int) -> tuple[IntegralLike, int]: + """ + + :param max_denominator: Integer, the maximum denominator value + :returns: Tuple of (numerator, denominator) + """ + + if self.denominator == 0: + return self.numerator, self.denominator + + assert isinstance(self._val, Fraction) + f = self._val.limit_denominator(max_denominator) + return f.numerator, f.denominator + + def __repr__(self) -> str: + return str(float(self._val)) + + def __hash__(self) -> int: # type: ignore[override] + return self._val.__hash__() + + def __eq__(self, other: object) -> bool: + val = self._val + if isinstance(other, IFDRational): + other = other._val + if isinstance(other, float): + val = float(val) + return val == other + + def __getstate__(self) -> list[float | Fraction | IntegralLike]: + return [self._val, self._numerator, self._denominator] + + def __setstate__(self, state: list[float | Fraction | IntegralLike]) -> None: + IFDRational.__init__(self, 0) + _val, _numerator, _denominator = state + assert isinstance(_val, (float, Fraction)) + self._val = _val + if TYPE_CHECKING: + self._numerator = cast(IntegralLike, _numerator) + else: + self._numerator = _numerator + assert isinstance(_denominator, int) + self._denominator = _denominator + + """ a = ['add','radd', 'sub', 'rsub', 'mul', 'rmul', + 'truediv', 'rtruediv', 'floordiv', 'rfloordiv', + 'mod','rmod', 'pow','rpow', 'pos', 'neg', + 'abs', 'trunc', 'lt', 'gt', 'le', 'ge', 'bool', + 'ceil', 'floor', 'round'] + print("\n".join("__%s__ = _delegate('__%s__')" % (s,s) for s in a)) + """ + + __add__ = _delegate("__add__") + __radd__ = _delegate("__radd__") + __sub__ = _delegate("__sub__") + __rsub__ = _delegate("__rsub__") + __mul__ = _delegate("__mul__") + __rmul__ = _delegate("__rmul__") + __truediv__ = _delegate("__truediv__") + __rtruediv__ = _delegate("__rtruediv__") + __floordiv__ = _delegate("__floordiv__") + __rfloordiv__ = _delegate("__rfloordiv__") + __mod__ = _delegate("__mod__") + __rmod__ = _delegate("__rmod__") + __pow__ = _delegate("__pow__") + __rpow__ = _delegate("__rpow__") + __pos__ = _delegate("__pos__") + __neg__ = _delegate("__neg__") + __abs__ = _delegate("__abs__") + __trunc__ = _delegate("__trunc__") + __lt__ = _delegate("__lt__") + __gt__ = _delegate("__gt__") + __le__ = _delegate("__le__") + __ge__ = _delegate("__ge__") + __bool__ = _delegate("__bool__") + __ceil__ = _delegate("__ceil__") + __floor__ = _delegate("__floor__") + __round__ = _delegate("__round__") + # Python >= 3.11 + if hasattr(Fraction, "__int__"): + __int__ = _delegate("__int__") + + +_LoaderFunc = Callable[["ImageFileDirectory_v2", bytes, bool], Any] + + +def _register_loader(idx: int, size: int) -> Callable[[_LoaderFunc], _LoaderFunc]: + def decorator(func: _LoaderFunc) -> _LoaderFunc: + from .TiffTags import TYPES + + if func.__name__.startswith("load_"): + TYPES[idx] = func.__name__[5:].replace("_", " ") + _load_dispatch[idx] = size, func # noqa: F821 + return func + + return decorator + + +def _register_writer(idx: int) -> Callable[[Callable[..., Any]], Callable[..., Any]]: + def decorator(func: Callable[..., Any]) -> Callable[..., Any]: + _write_dispatch[idx] = func # noqa: F821 + return func + + return decorator + + +def _register_basic(idx_fmt_name: tuple[int, str, str]) -> None: + from .TiffTags import TYPES + + idx, fmt, name = idx_fmt_name + TYPES[idx] = name + size = struct.calcsize(f"={fmt}") + + def basic_handler( + self: ImageFileDirectory_v2, data: bytes, legacy_api: bool = True + ) -> tuple[Any, ...]: + return self._unpack(f"{len(data) // size}{fmt}", data) + + _load_dispatch[idx] = size, basic_handler # noqa: F821 + _write_dispatch[idx] = lambda self, *values: ( # noqa: F821 + b"".join(self._pack(fmt, value) for value in values) + ) + + +if TYPE_CHECKING: + _IFDv2Base = MutableMapping[int, Any] +else: + _IFDv2Base = MutableMapping + + +class ImageFileDirectory_v2(_IFDv2Base): + """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:: + + ifd = ImageFileDirectory_v2() + ifd[key] = 'Some Data' + ifd.tagtype[key] = TiffTags.ASCII + print(ifd[key]) + 'Some Data' + + Individual values are returned as the strings or numbers, sequences are + returned as tuples of the values. + + The tiff metadata type of each item is stored in a dictionary of + tag types in + :attr:`~PIL.TiffImagePlugin.ImageFileDirectory_v2.tagtype`. The types + are read from a tiff file, guessed from the type added, or added + manually. + + Data Structures: + + * ``self.tagtype = {}`` + + * Key: numerical TIFF tag number + * Value: integer corresponding to the data type from + :py:data:`.TiffTags.TYPES` + + .. versionadded:: 3.0.0 + + 'Internal' data structures: + + * ``self._tags_v2 = {}`` + + * Key: numerical TIFF tag number + * Value: decoded data, as tuple for multiple values + + * ``self._tagdata = {}`` + + * Key: numerical TIFF tag number + * Value: undecoded byte string from file + + * ``self._tags_v1 = {}`` + + * Key: numerical TIFF tag number + * Value: decoded data in the v1 format + + Tags will be found in the private attributes ``self._tagdata``, and in + ``self._tags_v2`` once decoded. + + ``self.legacy_api`` is a value for internal use, and shouldn't be changed + from outside code. In cooperation with + :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1`, if ``legacy_api`` + is true, then decoded tags will be populated into both ``_tags_v1`` and + ``_tags_v2``. ``_tags_v2`` will be used if this IFD is used in the TIFF + save routine. Tags should be read from ``_tags_v1`` if + ``legacy_api == true``. + + """ + + _load_dispatch: dict[int, tuple[int, _LoaderFunc]] = {} + _write_dispatch: dict[int, Callable[..., Any]] = {} + + def __init__( + self, + ifh: bytes = b"II\x2a\x00\x00\x00\x00\x00", + prefix: bytes | None = None, + group: int | None = None, + ) -> None: + """Initialize an ImageFileDirectory. + + To construct an ImageFileDirectory from a real file, pass the 8-byte + magic header to the constructor. To only set the endianness, pass it + as the 'prefix' keyword argument. + + :param ifh: One of the accepted magic headers (cf. PREFIXES); also sets + endianness. + :param prefix: Override the endianness of the file. + """ + if not _accept(ifh): + msg = f"not a TIFF file (header {repr(ifh)} not valid)" + raise SyntaxError(msg) + self._prefix = prefix if prefix is not None else ifh[:2] + if self._prefix == MM: + self._endian = ">" + elif self._prefix == II: + self._endian = "<" + else: + msg = "not a TIFF IFD" + raise SyntaxError(msg) + self._bigtiff = ifh[2] == 43 + self.group = group + self.tagtype: dict[int, int] = {} + """ Dictionary of tag types """ + self.reset() + self.next = ( + self._unpack("Q", ifh[8:])[0] + if self._bigtiff + else self._unpack("L", ifh[4:])[0] + ) + self._legacy_api = False + + prefix = property(lambda self: self._prefix) + offset = property(lambda self: self._offset) + + @property + def legacy_api(self) -> bool: + return self._legacy_api + + @legacy_api.setter + def legacy_api(self, value: bool) -> NoReturn: + msg = "Not allowing setting of legacy api" + raise Exception(msg) + + def reset(self) -> None: + self._tags_v1: dict[int, Any] = {} # will remain empty if legacy_api is false + self._tags_v2: dict[int, Any] = {} # main tag storage + self._tagdata: dict[int, bytes] = {} + self.tagtype = {} # added 2008-06-05 by Florian Hoech + self._next = None + self._offset: int | None = None + + def __str__(self) -> str: + return str(dict(self)) + + def named(self) -> dict[str, Any]: + """ + :returns: dict of name|key: value + + Returns the complete tag dictionary, with named tags where possible. + """ + return { + TiffTags.lookup(code, self.group).name: value + for code, value in self.items() + } + + def __len__(self) -> int: + return len(set(self._tagdata) | set(self._tags_v2)) + + def __getitem__(self, tag: int) -> Any: + if tag not in self._tags_v2: # unpack on the fly + data = self._tagdata[tag] + typ = self.tagtype[tag] + size, handler = self._load_dispatch[typ] + self[tag] = handler(self, data, self.legacy_api) # check type + val = self._tags_v2[tag] + if self.legacy_api and not isinstance(val, (tuple, bytes)): + val = (val,) + return val + + def __contains__(self, tag: object) -> bool: + return tag in self._tags_v2 or tag in self._tagdata + + def __setitem__(self, tag: int, value: Any) -> None: + self._setitem(tag, value, self.legacy_api) + + def _setitem(self, tag: int, value: Any, legacy_api: bool) -> None: + basetypes = (Number, bytes, str) + + info = TiffTags.lookup(tag, self.group) + values = [value] if isinstance(value, basetypes) else value + + if tag not in self.tagtype: + if info.type: + self.tagtype[tag] = info.type + else: + self.tagtype[tag] = TiffTags.UNDEFINED + if all(isinstance(v, IFDRational) for v in values): + for v in values: + assert isinstance(v, IFDRational) + if v < 0: + self.tagtype[tag] = TiffTags.SIGNED_RATIONAL + break + else: + self.tagtype[tag] = TiffTags.RATIONAL + elif all(isinstance(v, int) for v in values): + short = True + signed_short = True + long = True + for v in values: + assert isinstance(v, int) + if short and not (0 <= v < 2**16): + short = False + if signed_short and not (-(2**15) < v < 2**15): + signed_short = False + if long and v < 0: + long = False + if short: + self.tagtype[tag] = TiffTags.SHORT + elif signed_short: + self.tagtype[tag] = TiffTags.SIGNED_SHORT + elif long: + self.tagtype[tag] = TiffTags.LONG + else: + self.tagtype[tag] = TiffTags.SIGNED_LONG + elif all(isinstance(v, float) for v in values): + self.tagtype[tag] = TiffTags.DOUBLE + elif all(isinstance(v, str) for v in values): + self.tagtype[tag] = TiffTags.ASCII + elif all(isinstance(v, bytes) for v in values): + self.tagtype[tag] = TiffTags.BYTE + + if self.tagtype[tag] == TiffTags.UNDEFINED: + values = [ + v.encode("ascii", "replace") if isinstance(v, str) else v + for v in values + ] + elif self.tagtype[tag] == TiffTags.RATIONAL: + values = [float(v) if isinstance(v, int) else v for v in values] + + is_ifd = self.tagtype[tag] == TiffTags.LONG and isinstance(values, dict) + if not is_ifd: + values = tuple( + info.cvt_enum(value) if isinstance(value, str) else value + for value in values + ) + + dest = self._tags_v1 if legacy_api else self._tags_v2 + + # Three branches: + # Spec'd length == 1, Actual length 1, store as element + # Spec'd length == 1, Actual > 1, Warn and truncate. Formerly barfed. + # No Spec, Actual length 1, Formerly (<4.2) returned a 1 element tuple. + # Don't mess with the legacy api, since it's frozen. + if not is_ifd and ( + (info.length == 1) + or self.tagtype[tag] == TiffTags.BYTE + or (info.length is None and len(values) == 1 and not legacy_api) + ): + # Don't mess with the legacy api, since it's frozen. + if legacy_api and self.tagtype[tag] in [ + TiffTags.RATIONAL, + TiffTags.SIGNED_RATIONAL, + ]: # rationals + values = (values,) + try: + (dest[tag],) = values + except ValueError: + # We've got a builtin tag with 1 expected entry + warnings.warn( + f"Metadata Warning, tag {tag} had too many entries: " + f"{len(values)}, expected 1" + ) + dest[tag] = values[0] + + else: + # Spec'd length > 1 or undefined + # Unspec'd, and length > 1 + dest[tag] = values + + def __delitem__(self, tag: int) -> None: + self._tags_v2.pop(tag, None) + self._tags_v1.pop(tag, None) + self._tagdata.pop(tag, None) + + def __iter__(self) -> Iterator[int]: + return iter(set(self._tagdata) | set(self._tags_v2)) + + def _unpack(self, fmt: str, data: bytes) -> tuple[Any, ...]: + return struct.unpack(self._endian + fmt, data) + + def _pack(self, fmt: str, *values: Any) -> bytes: + return struct.pack(self._endian + fmt, *values) + + list( + map( + _register_basic, + [ + (TiffTags.SHORT, "H", "short"), + (TiffTags.LONG, "L", "long"), + (TiffTags.SIGNED_BYTE, "b", "signed byte"), + (TiffTags.SIGNED_SHORT, "h", "signed short"), + (TiffTags.SIGNED_LONG, "l", "signed long"), + (TiffTags.FLOAT, "f", "float"), + (TiffTags.DOUBLE, "d", "double"), + (TiffTags.IFD, "L", "long"), + (TiffTags.LONG8, "Q", "long8"), + ], + ) + ) + + @_register_loader(1, 1) # Basic type, except for the legacy API. + def load_byte(self, data: bytes, legacy_api: bool = True) -> bytes: + return data + + @_register_writer(1) # Basic type, except for the legacy API. + def write_byte(self, data: bytes | int | IFDRational) -> bytes: + if isinstance(data, IFDRational): + data = int(data) + if isinstance(data, int): + data = bytes((data,)) + return data + + @_register_loader(2, 1) + def load_string(self, data: bytes, legacy_api: bool = True) -> str: + if data.endswith(b"\0"): + data = data[:-1] + return data.decode("latin-1", "replace") + + @_register_writer(2) + def write_string(self, value: str | bytes | int) -> bytes: + # remerge of https://github.com/python-pillow/Pillow/pull/1416 + if isinstance(value, int): + value = str(value) + if not isinstance(value, bytes): + value = value.encode("ascii", "replace") + return value + b"\0" + + @_register_loader(5, 8) + def load_rational( + self, data: bytes, legacy_api: bool = True + ) -> tuple[tuple[int, int] | IFDRational, ...]: + vals = self._unpack(f"{len(data) // 4}L", data) + + def combine(a: int, b: int) -> tuple[int, int] | IFDRational: + return (a, b) if legacy_api else IFDRational(a, b) + + return tuple(combine(num, denom) for num, denom in zip(vals[::2], vals[1::2])) + + @_register_writer(5) + def write_rational(self, *values: IFDRational) -> bytes: + return b"".join( + self._pack("2L", *_limit_rational(frac, 2**32 - 1)) for frac in values + ) + + @_register_loader(7, 1) + def load_undefined(self, data: bytes, legacy_api: bool = True) -> bytes: + return data + + @_register_writer(7) + def write_undefined(self, value: bytes | int | IFDRational) -> bytes: + if isinstance(value, IFDRational): + value = int(value) + if isinstance(value, int): + value = str(value).encode("ascii", "replace") + return value + + @_register_loader(10, 8) + def load_signed_rational( + self, data: bytes, legacy_api: bool = True + ) -> tuple[tuple[int, int] | IFDRational, ...]: + vals = self._unpack(f"{len(data) // 4}l", data) + + def combine(a: int, b: int) -> tuple[int, int] | IFDRational: + return (a, b) if legacy_api else IFDRational(a, b) + + return tuple(combine(num, denom) for num, denom in zip(vals[::2], vals[1::2])) + + @_register_writer(10) + def write_signed_rational(self, *values: IFDRational) -> bytes: + return b"".join( + self._pack("2l", *_limit_signed_rational(frac, 2**31 - 1, -(2**31))) + for frac in values + ) + + def _ensure_read(self, fp: IO[bytes], size: int) -> bytes: + ret = fp.read(size) + if len(ret) != size: + msg = ( + "Corrupt EXIF data. " + f"Expecting to read {size} bytes but only got {len(ret)}. " + ) + raise OSError(msg) + return ret + + def load(self, fp: IO[bytes]) -> None: + self.reset() + self._offset = fp.tell() + + try: + tag_count = ( + self._unpack("Q", self._ensure_read(fp, 8)) + if self._bigtiff + else self._unpack("H", self._ensure_read(fp, 2)) + )[0] + for i in range(tag_count): + tag, typ, count, data = ( + self._unpack("HHQ8s", self._ensure_read(fp, 20)) + if self._bigtiff + else self._unpack("HHL4s", self._ensure_read(fp, 12)) + ) + + tagname = TiffTags.lookup(tag, self.group).name + typname = TYPES.get(typ, "unknown") + msg = f"tag: {tagname} ({tag}) - type: {typname} ({typ})" + + try: + unit_size, handler = self._load_dispatch[typ] + except KeyError: + logger.debug("%s - unsupported type %s", msg, typ) + continue # ignore unsupported type + size = count * unit_size + if size > (8 if self._bigtiff else 4): + here = fp.tell() + (offset,) = self._unpack("Q" if self._bigtiff else "L", data) + msg += f" Tag Location: {here} - Data Location: {offset}" + fp.seek(offset) + data = ImageFile._safe_read(fp, size) + fp.seek(here) + else: + data = data[:size] + + if len(data) != size: + warnings.warn( + "Possibly corrupt EXIF data. " + f"Expecting to read {size} bytes but only got {len(data)}." + f" Skipping tag {tag}" + ) + logger.debug(msg) + continue + + if not data: + logger.debug(msg) + continue + + self._tagdata[tag] = data + self.tagtype[tag] = typ + + msg += " - value: " + msg += f"" if size > 32 else repr(data) + + logger.debug(msg) + + (self.next,) = ( + self._unpack("Q", self._ensure_read(fp, 8)) + if self._bigtiff + else self._unpack("L", self._ensure_read(fp, 4)) + ) + except OSError as msg: + warnings.warn(str(msg)) + return + + def _get_ifh(self) -> bytes: + ifh = self._prefix + self._pack("H", 43 if self._bigtiff else 42) + if self._bigtiff: + ifh += self._pack("HH", 8, 0) + ifh += self._pack("Q", 16) if self._bigtiff else self._pack("L", 8) + + return ifh + + def tobytes(self, offset: int = 0) -> bytes: + # FIXME What about tagdata? + result = self._pack("Q" if self._bigtiff else "H", len(self._tags_v2)) + + entries: list[tuple[int, int, int, bytes, bytes]] = [] + + fmt = "Q" if self._bigtiff else "L" + fmt_size = 8 if self._bigtiff else 4 + offset += ( + len(result) + len(self._tags_v2) * (20 if self._bigtiff else 12) + fmt_size + ) + stripoffsets = None + + # pass 1: convert tags to binary format + # always write tags in ascending order + for tag, value in sorted(self._tags_v2.items()): + if tag == STRIPOFFSETS: + stripoffsets = len(entries) + typ = self.tagtype[tag] + logger.debug("Tag %s, Type: %s, Value: %s", tag, typ, repr(value)) + is_ifd = typ == TiffTags.LONG and isinstance(value, dict) + if is_ifd: + ifd = ImageFileDirectory_v2(self._get_ifh(), group=tag) + values = self._tags_v2[tag] + for ifd_tag, ifd_value in values.items(): + ifd[ifd_tag] = ifd_value + data = ifd.tobytes(offset) + else: + values = value if isinstance(value, tuple) else (value,) + data = self._write_dispatch[typ](self, *values) + + tagname = TiffTags.lookup(tag, self.group).name + typname = "ifd" if is_ifd else TYPES.get(typ, "unknown") + msg = f"save: {tagname} ({tag}) - type: {typname} ({typ}) - value: " + msg += f"" if len(data) >= 16 else str(values) + logger.debug(msg) + + # count is sum of lengths for string and arbitrary data + if is_ifd: + count = 1 + elif typ in [TiffTags.BYTE, TiffTags.ASCII, TiffTags.UNDEFINED]: + count = len(data) + else: + count = len(values) + # figure out if data fits into the entry + if len(data) <= fmt_size: + entries.append((tag, typ, count, data.ljust(fmt_size, b"\0"), b"")) + else: + entries.append((tag, typ, count, self._pack(fmt, offset), data)) + offset += (len(data) + 1) // 2 * 2 # pad to word + + # update strip offset data to point beyond auxiliary data + if stripoffsets is not None: + tag, typ, count, value, data = entries[stripoffsets] + if data: + size, handler = self._load_dispatch[typ] + values = [val + offset for val in handler(self, data, self.legacy_api)] + data = self._write_dispatch[typ](self, *values) + else: + value = self._pack(fmt, self._unpack(fmt, value)[0] + offset) + entries[stripoffsets] = tag, typ, count, value, data + + # pass 2: write entries to file + for tag, typ, count, value, data in entries: + logger.debug("%s %s %s %s %s", tag, typ, count, repr(value), repr(data)) + result += self._pack( + "HHQ8s" if self._bigtiff else "HHL4s", tag, typ, count, value + ) + + # -- overwrite here for multi-page -- + result += self._pack(fmt, 0) # end of entries + + # pass 3: write auxiliary data to file + for tag, typ, count, value, data in entries: + result += data + if len(data) & 1: + result += b"\0" + + return result + + def save(self, fp: IO[bytes]) -> int: + if fp.tell() == 0: # skip TIFF header on subsequent pages + fp.write(self._get_ifh()) + + offset = fp.tell() + result = self.tobytes(offset) + fp.write(result) + return offset + len(result) + + +ImageFileDirectory_v2._load_dispatch = _load_dispatch +ImageFileDirectory_v2._write_dispatch = _write_dispatch +for idx, name in TYPES.items(): + name = name.replace(" ", "_") + setattr(ImageFileDirectory_v2, f"load_{name}", _load_dispatch[idx][1]) + setattr(ImageFileDirectory_v2, f"write_{name}", _write_dispatch[idx]) +del _load_dispatch, _write_dispatch, idx, name + + +# Legacy ImageFileDirectory support. +class ImageFileDirectory_v1(ImageFileDirectory_v2): + """This class represents the **legacy** interface to a TIFF tag directory. + + Exposes a dictionary interface of the tags in the directory:: + + ifd = ImageFileDirectory_v1() + ifd[key] = 'Some Data' + ifd.tagtype[key] = TiffTags.ASCII + print(ifd[key]) + ('Some Data',) + + Also contains a dictionary of tag types as read from the tiff image file, + :attr:`~PIL.TiffImagePlugin.ImageFileDirectory_v1.tagtype`. + + Values are returned as a tuple. + + .. deprecated:: 3.0.0 + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + self._legacy_api = True + + tags = property(lambda self: self._tags_v1) + tagdata = property(lambda self: self._tagdata) + + # defined in ImageFileDirectory_v2 + tagtype: dict[int, int] + """Dictionary of tag types""" + + @classmethod + def from_v2(cls, original: ImageFileDirectory_v2) -> ImageFileDirectory_v1: + """Returns an + :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1` + instance with the same data as is contained in the original + :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2` + instance. + + :returns: :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1` + + """ + + ifd = cls(prefix=original.prefix) + ifd._tagdata = original._tagdata + ifd.tagtype = original.tagtype + ifd.next = original.next # an indicator for multipage tiffs + return ifd + + def to_v2(self) -> ImageFileDirectory_v2: + """Returns an + :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2` + instance with the same data as is contained in the original + :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1` + instance. + + :returns: :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2` + + """ + + ifd = ImageFileDirectory_v2(prefix=self.prefix) + ifd._tagdata = dict(self._tagdata) + ifd.tagtype = dict(self.tagtype) + ifd._tags_v2 = dict(self._tags_v2) + return ifd + + def __contains__(self, tag: object) -> bool: + return tag in self._tags_v1 or tag in self._tagdata + + def __len__(self) -> int: + return len(set(self._tagdata) | set(self._tags_v1)) + + def __iter__(self) -> Iterator[int]: + return iter(set(self._tagdata) | set(self._tags_v1)) + + def __setitem__(self, tag: int, value: Any) -> None: + for legacy_api in (False, True): + self._setitem(tag, value, legacy_api) + + def __getitem__(self, tag: int) -> Any: + if tag not in self._tags_v1: # unpack on the fly + data = self._tagdata[tag] + typ = self.tagtype[tag] + size, handler = self._load_dispatch[typ] + for legacy in (False, True): + self._setitem(tag, handler(self, data, legacy), legacy) + val = self._tags_v1[tag] + if not isinstance(val, (tuple, bytes)): + val = (val,) + return val + + +# undone -- switch this pointer +ImageFileDirectory = ImageFileDirectory_v1 + + +## +# Image plugin for TIFF files. + + +class TiffImageFile(ImageFile.ImageFile): + format = "TIFF" + format_description = "Adobe TIFF" + _close_exclusive_fp_after_loading = False + + def __init__( + self, + fp: StrOrBytesPath | IO[bytes], + filename: str | bytes | None = None, + ) -> None: + self.tag_v2: ImageFileDirectory_v2 + """ Image file directory (tag dictionary) """ + + self.tag: ImageFileDirectory_v1 + """ Legacy tag entries """ + + super().__init__(fp, filename) + + def _open(self) -> None: + """Open the first image in a TIFF file""" + + # Header + assert self.fp is not None + ifh = self.fp.read(8) + if ifh[2] == 43: + ifh += self.fp.read(8) + + self.tag_v2 = ImageFileDirectory_v2(ifh) + + # setup frame pointers + self.__first = self.__next = self.tag_v2.next + self.__frame = -1 + self._fp = self.fp + self._frame_pos: list[int] = [] + self._n_frames: int | None = None + + logger.debug("*** TiffImageFile._open ***") + logger.debug("- __first: %s", self.__first) + logger.debug("- ifh: %s", repr(ifh)) # Use repr to avoid str(bytes) + + # and load the first frame + self._seek(0) + + @property + def n_frames(self) -> int: + current_n_frames = self._n_frames + if current_n_frames is None: + current = self.tell() + self._seek(len(self._frame_pos)) + while self._n_frames is None: + self._seek(self.tell() + 1) + self.seek(current) + assert self._n_frames is not None + return self._n_frames + + def seek(self, frame: int) -> None: + """Select a given frame as current image""" + if not self._seek_check(frame): + return + self._seek(frame) + if self._im is not None and ( + self.im.size != self._tile_size + or self.im.mode != self.mode + or self.readonly + ): + self._im = None + + def _seek(self, frame: int) -> None: + if isinstance(self._fp, DeferredError): + raise self._fp.ex + self.fp = self._fp + + while len(self._frame_pos) <= frame: + if not self.__next: + msg = "no more images in TIFF file" + raise EOFError(msg) + logger.debug( + "Seeking to frame %s, on frame %s, __next %s, location: %s", + frame, + self.__frame, + self.__next, + self.fp.tell(), + ) + if self.__next >= 2**63: + msg = "Unable to seek to frame" + raise ValueError(msg) + self.fp.seek(self.__next) + self._frame_pos.append(self.__next) + logger.debug("Loading tags, location: %s", self.fp.tell()) + self.tag_v2.load(self.fp) + if self.tag_v2.next in self._frame_pos: + # This IFD has already been processed + # Declare this to be the end of the image + self.__next = 0 + else: + self.__next = self.tag_v2.next + if self.__next == 0: + self._n_frames = frame + 1 + if len(self._frame_pos) == 1: + self.is_animated = self.__next != 0 + self.__frame += 1 + self.fp.seek(self._frame_pos[frame]) + self.tag_v2.load(self.fp) + if XMP in self.tag_v2: + xmp = self.tag_v2[XMP] + if isinstance(xmp, tuple) and len(xmp) == 1: + xmp = xmp[0] + self.info["xmp"] = xmp + elif "xmp" in self.info: + del self.info["xmp"] + self._reload_exif() + # fill the legacy tag/ifd entries + self.tag = self.ifd = ImageFileDirectory_v1.from_v2(self.tag_v2) + self.__frame = frame + self._setup() + + def tell(self) -> int: + """Return the current frame number""" + return self.__frame + + def get_photoshop_blocks(self) -> dict[int, dict[str, bytes]]: + """ + Returns a dictionary of Photoshop "Image Resource Blocks". + The keys are the image resource ID. For more information, see + https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_pgfId-1037727 + + :returns: Photoshop "Image Resource Blocks" in a dictionary. + """ + blocks = {} + val = self.tag_v2.get(ExifTags.Base.ImageResources) + if val: + while val.startswith(b"8BIM"): + id = i16(val[4:6]) + n = math.ceil((val[6] + 1) / 2) * 2 + size = i32(val[6 + n : 10 + n]) + data = val[10 + n : 10 + n + size] + blocks[id] = {"data": data} + + val = val[math.ceil((10 + n + size) / 2) * 2 :] + return blocks + + def load(self) -> Image.core.PixelAccess | None: + if self.tile and self.use_load_libtiff: + return self._load_libtiff() + return super().load() + + def load_prepare(self) -> None: + if self._im is None: + Image._decompression_bomb_check(self._tile_size) + self.im = Image.core.new(self.mode, self._tile_size) + ImageFile.ImageFile.load_prepare(self) + + def load_end(self) -> None: + # allow closing if we're on the first frame, there's no next + # This is the ImageFile.load path only, libtiff specific below. + if not self.is_animated: + self._close_exclusive_fp_after_loading = True + + # load IFD data from fp before it is closed + exif = self.getexif() + for key in TiffTags.TAGS_V2_GROUPS: + if key not in exif: + continue + exif.get_ifd(key) + + ImageOps.exif_transpose(self, in_place=True) + if ExifTags.Base.Orientation in self.tag_v2: + del self.tag_v2[ExifTags.Base.Orientation] + + def _load_libtiff(self) -> Image.core.PixelAccess | None: + """Overload method triggered when we detect a compressed tiff + Calls out to libtiff""" + + Image.Image.load(self) + + self.load_prepare() + + if not len(self.tile) == 1: + msg = "Not exactly one tile" + raise OSError(msg) + + # (self._compression, (extents tuple), + # 0, (rawmode, self._compression, fp)) + extents = self.tile[0][1] + args = self.tile[0][3] + + # To be nice on memory footprint, if there's a + # file descriptor, use that instead of reading + # into a string in python. + assert self.fp is not None + try: + fp = hasattr(self.fp, "fileno") and self.fp.fileno() + # flush the file descriptor, prevents error on pypy 2.4+ + # should also eliminate the need for fp.tell + # in _seek + if hasattr(self.fp, "flush"): + self.fp.flush() + except OSError: + # io.BytesIO have a fileno, but returns an OSError if + # it doesn't use a file descriptor. + fp = False + + if fp: + assert isinstance(args, tuple) + args_list = list(args) + args_list[2] = fp + args = tuple(args_list) + + decoder = Image._getdecoder(self.mode, "libtiff", args, self.decoderconfig) + try: + decoder.setimage(self.im, extents) + except ValueError as e: + msg = "Couldn't set the image" + raise OSError(msg) from e + + close_self_fp = self._exclusive_fp and not self.is_animated + 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 OSError if there's no underlying fp. Easier to + # deal with here by reordering. + logger.debug("have getvalue. just sending in a string from getvalue") + n, err = decoder.decode(self.fp.getvalue()) + elif fp: + # we've got a actual file on disk, pass in the fp. + logger.debug("have fileno, calling fileno version of the decoder.") + if not close_self_fp: + self.fp.seek(0) + # Save and restore the file position, because libtiff will move it + # outside of the Python runtime, and that will confuse + # io.BufferedReader and possible others. + # NOTE: This must use os.lseek(), and not fp.tell()/fp.seek(), + # because the buffer read head already may not equal the actual + # file position, and fp.seek() may just adjust it's internal + # pointer and not actually seek the OS file handle. + pos = os.lseek(fp, 0, os.SEEK_CUR) + # 4 bytes, otherwise the trace might error out + n, err = decoder.decode(b"fpfp") + os.lseek(fp, pos, os.SEEK_SET) + else: + # we have something else. + logger.debug("don't have fileno or getvalue. just reading") + self.fp.seek(0) + # UNDONE -- so much for that buffer size thing. + n, err = decoder.decode(self.fp.read()) + + self.tile = [] + self.readonly = 0 + + self.load_end() + + if close_self_fp: + self.fp.close() + self.fp = None # might be shared + + if err < 0: + msg = f"decoder error {err}" + raise OSError(msg) + + return Image.Image.load(self) + + def _setup(self) -> None: + """Setup this image object based on current tags""" + + if 0xBC01 in self.tag_v2: + msg = "Windows Media Photo files not yet supported" + raise OSError(msg) + + # extract relevant tags + self._compression = COMPRESSION_INFO[self.tag_v2.get(COMPRESSION, 1)] + self._planar_configuration = self.tag_v2.get(PLANAR_CONFIGURATION, 1) + + # photometric is a required tag, but not everyone is reading + # the specification + photo = self.tag_v2.get(PHOTOMETRIC_INTERPRETATION, 0) + + # old style jpeg compression images most certainly are YCbCr + if self._compression == "tiff_jpeg": + photo = 6 + + fillorder = self.tag_v2.get(FILLORDER, 1) + + logger.debug("*** Summary ***") + logger.debug("- compression: %s", self._compression) + logger.debug("- photometric_interpretation: %s", photo) + logger.debug("- planar_configuration: %s", self._planar_configuration) + logger.debug("- fill_order: %s", fillorder) + logger.debug("- YCbCr subsampling: %s", self.tag_v2.get(YCBCRSUBSAMPLING)) + + # size + try: + xsize = self.tag_v2[IMAGEWIDTH] + ysize = self.tag_v2[IMAGELENGTH] + except KeyError as e: + msg = "Missing dimensions" + raise TypeError(msg) from e + if not isinstance(xsize, int) or not isinstance(ysize, int): + msg = "Invalid dimensions" + raise ValueError(msg) + self._tile_size = xsize, ysize + orientation = self.tag_v2.get(ExifTags.Base.Orientation) + if orientation in (5, 6, 7, 8): + self._size = ysize, xsize + else: + self._size = xsize, ysize + + logger.debug("- size: %s", self.size) + + sample_format = self.tag_v2.get(SAMPLEFORMAT, (1,)) + if len(sample_format) > 1 and max(sample_format) == min(sample_format) == 1: + # SAMPLEFORMAT is properly per band, so an RGB image will + # be (1,1,1). But, we don't support per band pixel types, + # and anything more than one band is a uint8. So, just + # take the first element. Revisit this if adding support + # for more exotic images. + sample_format = (1,) + + bps_tuple = self.tag_v2.get(BITSPERSAMPLE, (1,)) + extra_tuple = self.tag_v2.get(EXTRASAMPLES, ()) + if photo in (2, 6, 8): # RGB, YCbCr, LAB + bps_count = 3 + elif photo == 5: # CMYK + bps_count = 4 + else: + bps_count = 1 + bps_count += len(extra_tuple) + bps_actual_count = len(bps_tuple) + samples_per_pixel = self.tag_v2.get( + SAMPLESPERPIXEL, + 3 if self._compression == "tiff_jpeg" and photo in (2, 6) else 1, + ) + + if samples_per_pixel > MAX_SAMPLESPERPIXEL: + # DOS check, samples_per_pixel can be a Long, and we extend the tuple below + logger.error( + "More samples per pixel than can be decoded: %s", samples_per_pixel + ) + msg = "Invalid value for samples per pixel" + raise SyntaxError(msg) + + if samples_per_pixel < bps_actual_count: + # If a file has more values in bps_tuple than expected, + # remove the excess. + bps_tuple = bps_tuple[:samples_per_pixel] + elif samples_per_pixel > bps_actual_count and bps_actual_count == 1: + # If a file has only one value in bps_tuple, when it should have more, + # presume it is the same number of bits for all of the samples. + bps_tuple = bps_tuple * samples_per_pixel + + if len(bps_tuple) != samples_per_pixel: + msg = "unknown data organization" + raise SyntaxError(msg) + + # mode: check photometric interpretation and bits per pixel + key = ( + self.tag_v2.prefix, + photo, + sample_format, + fillorder, + bps_tuple, + extra_tuple, + ) + logger.debug("format key: %s", key) + try: + self._mode, rawmode = OPEN_INFO[key] + except KeyError as e: + logger.debug("- unsupported format") + msg = "unknown pixel mode" + raise SyntaxError(msg) from e + + logger.debug("- raw mode: %s", rawmode) + logger.debug("- pil mode: %s", self.mode) + + self.info["compression"] = self._compression + + xres = self.tag_v2.get(X_RESOLUTION, 1) + yres = self.tag_v2.get(Y_RESOLUTION, 1) + + if xres and yres: + resunit = self.tag_v2.get(RESOLUTION_UNIT) + 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) + elif resunit is None: # used to default to 1, but now 2) + self.info["dpi"] = (xres, yres) + # For backward compatibility, + # we also preserve the old behavior + self.info["resolution"] = xres, yres + else: # No absolute unit of measurement + self.info["resolution"] = xres, yres + + # build tile descriptors + x = y = layer = 0 + self.tile = [] + self.use_load_libtiff = READ_LIBTIFF or self._compression != "raw" + if self.use_load_libtiff: + # 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 + # use the _load_libtiff function. + + # 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: + # Replace fillorder with fillorder=1 + key = key[:3] + (1,) + key[4:] + logger.debug("format key: %s", key) + # this should always work, since all the + # fillorder==2 modes have a corresponding + # fillorder=1 mode + self._mode, rawmode = OPEN_INFO[key] + # YCbCr images with new jpeg compression with pixels in one plane + # unpacked straight into RGB values + if ( + photo == 6 + and self._compression == "jpeg" + and self._planar_configuration == 1 + ): + rawmode = "RGB" + # 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. + elif rawmode == "I;16": + rawmode = "I;16N" + elif rawmode.endswith((";16B", ";16L")): + rawmode = rawmode[:-1] + "N" + + # 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, False, self.tag_v2.offset) + self.tile.append(ImageFile._Tile("libtiff", (0, 0, xsize, ysize), 0, a)) + + elif STRIPOFFSETS in self.tag_v2 or TILEOFFSETS in self.tag_v2: + # striped image + if STRIPOFFSETS in self.tag_v2: + offsets = self.tag_v2[STRIPOFFSETS] + h = self.tag_v2.get(ROWSPERSTRIP, ysize) + w = xsize + else: + # tiled image + offsets = self.tag_v2[TILEOFFSETS] + tilewidth = self.tag_v2.get(TILEWIDTH) + h = self.tag_v2.get(TILELENGTH) + if not isinstance(tilewidth, int) or not isinstance(h, int): + msg = "Invalid tile dimensions" + raise ValueError(msg) + w = tilewidth + + if w == xsize and h == ysize and self._planar_configuration != 2: + # Every tile covers the image. Only use the last offset + offsets = offsets[-1:] + + for offset in offsets: + if x + w > xsize: + stride = w * sum(bps_tuple) / 8 # bytes per line + else: + stride = 0 + + tile_rawmode = rawmode + if self._planar_configuration == 2: + # each band on it's own layer + tile_rawmode = rawmode[layer] + # adjust stride width accordingly + stride /= bps_count + + args = (tile_rawmode, int(stride), 1) + self.tile.append( + ImageFile._Tile( + self._compression, + (x, y, min(x + w, xsize), min(y + h, ysize)), + offset, + args, + ) + ) + x += w + if x >= xsize: + x, y = 0, y + h + if y >= ysize: + y = 0 + layer += 1 + else: + logger.debug("- unsupported data organization") + msg = "unknown data organization" + raise SyntaxError(msg) + + # Fix up info. + if ICCPROFILE in self.tag_v2: + self.info["icc_profile"] = self.tag_v2[ICCPROFILE] + + # fixup palette descriptor + + if self.mode in ["P", "PA"]: + palette = [o8(b // 256) for b in self.tag_v2[COLORMAP]] + self.palette = ImagePalette.raw("RGB;L", b"".join(palette)) + + +# +# -------------------------------------------------------------------- +# Write TIFF files + +# little endian is default except for image modes with +# explicit 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;16L": ("I;16L", II, 1, 1, (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;16B": ("I;16B", MM, 1, 1, (16,), None), +} + + +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + try: + rawmode, prefix, photo, format, bits, extra = SAVE_INFO[im.mode] + except KeyError as e: + msg = f"cannot write mode {im.mode} as TIFF" + raise OSError(msg) from e + + encoderinfo = im.encoderinfo + encoderconfig = im.encoderconfig + + ifd = ImageFileDirectory_v2(prefix=prefix) + if encoderinfo.get("big_tiff"): + ifd._bigtiff = True + + try: + compression = encoderinfo["compression"] + except KeyError: + compression = im.info.get("compression") + if isinstance(compression, int): + # compression value may be from BMP. Ignore it + compression = None + if compression is None: + compression = "raw" + elif compression == "tiff_jpeg": + # OJPEG is obsolete, so use new-style JPEG compression instead + compression = "jpeg" + elif compression == "tiff_deflate": + compression = "tiff_adobe_deflate" + + libtiff = WRITE_LIBTIFF or compression != "raw" + + # required for color libtiff images + ifd[PLANAR_CONFIGURATION] = 1 + + ifd[IMAGEWIDTH] = im.size[0] + ifd[IMAGELENGTH] = im.size[1] + + # write any arbitrary tags passed in as an ImageFileDirectory + if "tiffinfo" in encoderinfo: + info = encoderinfo["tiffinfo"] + elif "exif" in encoderinfo: + info = encoderinfo["exif"] + if isinstance(info, bytes): + exif = Image.Exif() + exif.load(info) + info = exif + else: + info = {} + logger.debug("Tiffinfo Keys: %s", list(info)) + if isinstance(info, ImageFileDirectory_v1): + info = info.to_v2() + for key in info: + if isinstance(info, Image.Exif) and key in TiffTags.TAGS_V2_GROUPS: + ifd[key] = info.get_ifd(key) + else: + ifd[key] = info.get(key) + try: + ifd.tagtype[key] = info.tagtype[key] + except Exception: + pass # might not be an IFD. Might not have populated type + + legacy_ifd = {} + if hasattr(im, "tag"): + legacy_ifd = im.tag.to_v2() + + supplied_tags = {**legacy_ifd, **getattr(im, "tag_v2", {})} + for tag in ( + # IFD offset that may not be correct in the saved image + EXIFIFD, + # Determined by the image format and should not be copied from legacy_ifd. + SAMPLEFORMAT, + ): + if tag in supplied_tags: + del supplied_tags[tag] + + # additions written by Greg Couch, gregc@cgl.ucsf.edu + # inspired by image-sig posting from Kevin Cazabon, kcazabon@home.com + if hasattr(im, "tag_v2"): + # 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_v2: + if key == IPTC_NAA_CHUNK and im.tag_v2.tagtype[key] not in ( + TiffTags.BYTE, + TiffTags.UNDEFINED, + ): + del supplied_tags[key] + else: + ifd[key] = im.tag_v2[key] + ifd.tagtype[key] = im.tag_v2.tagtype[key] + + # preserve ICC profile (should also work when saving other formats + # which support profiles as TIFF) -- 2008-06-06 Florian Hoech + icc = encoderinfo.get("icc_profile", im.info.get("icc_profile")) + if icc: + ifd[ICCPROFILE] = icc + + for key, name in [ + (IMAGEDESCRIPTION, "description"), + (X_RESOLUTION, "resolution"), + (Y_RESOLUTION, "resolution"), + (X_RESOLUTION, "x_resolution"), + (Y_RESOLUTION, "y_resolution"), + (RESOLUTION_UNIT, "resolution_unit"), + (SOFTWARE, "software"), + (DATE_TIME, "date_time"), + (ARTIST, "artist"), + (COPYRIGHT, "copyright"), + ]: + if name in encoderinfo: + ifd[key] = encoderinfo[name] + + dpi = encoderinfo.get("dpi") + if dpi: + ifd[RESOLUTION_UNIT] = 2 + ifd[X_RESOLUTION] = dpi[0] + ifd[Y_RESOLUTION] = 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 + + if PHOTOMETRIC_INTERPRETATION not in ifd: + ifd[PHOTOMETRIC_INTERPRETATION] = photo + elif im.mode in ("1", "L") and ifd[PHOTOMETRIC_INTERPRETATION] == 0: + if im.mode == "1": + inverted_im = im.copy() + px = inverted_im.load() + if px is not None: + for y in range(inverted_im.height): + for x in range(inverted_im.width): + px[x, y] = 0 if px[x, y] == 255 else 255 + im = inverted_im + else: + im = ImageOps.invert(im) + + if im.mode in ["P", "PA"]: + lut = im.im.getpalette("RGB", "RGB;L") + colormap = [] + colors = len(lut) // 3 + for i in range(3): + colormap += [v * 256 for v in lut[colors * i : colors * (i + 1)]] + colormap += [0] * (256 - colors) + ifd[COLORMAP] = colormap + # data orientation + w, h = ifd[IMAGEWIDTH], ifd[IMAGELENGTH] + stride = len(bits) * ((w * bits[0] + 7) // 8) + if ROWSPERSTRIP not in ifd: + # aim for given strip size (64 KB by default) when using libtiff writer + if libtiff: + im_strip_size = encoderinfo.get("strip_size", STRIP_SIZE) + rows_per_strip = 1 if stride == 0 else min(im_strip_size // stride, h) + # JPEG encoder expects multiple of 8 rows + if compression == "jpeg": + rows_per_strip = min(((rows_per_strip + 7) // 8) * 8, h) + else: + rows_per_strip = h + if rows_per_strip == 0: + rows_per_strip = 1 + ifd[ROWSPERSTRIP] = rows_per_strip + strip_byte_counts = 1 if stride == 0 else stride * ifd[ROWSPERSTRIP] + strips_per_image = (h + ifd[ROWSPERSTRIP] - 1) // ifd[ROWSPERSTRIP] + if strip_byte_counts >= 2**16: + ifd.tagtype[STRIPBYTECOUNTS] = TiffTags.LONG + ifd[STRIPBYTECOUNTS] = (strip_byte_counts,) * (strips_per_image - 1) + ( + stride * h - strip_byte_counts * (strips_per_image - 1), + ) + ifd[STRIPOFFSETS] = tuple( + range(0, strip_byte_counts * strips_per_image, strip_byte_counts) + ) # this is adjusted by IFD writer + # no compression by default: + ifd[COMPRESSION] = COMPRESSION_INFO_REV.get(compression, 1) + + if im.mode == "YCbCr": + for tag, default_value in { + YCBCRSUBSAMPLING: (1, 1), + REFERENCEBLACKWHITE: (0, 255, 128, 255, 128, 255), + }.items(): + ifd.setdefault(tag, default_value) + + blocklist = [TILEWIDTH, TILELENGTH, TILEOFFSETS, TILEBYTECOUNTS] + if libtiff: + if "quality" in encoderinfo: + quality = encoderinfo["quality"] + if not isinstance(quality, int) or quality < 0 or quality > 100: + msg = "Invalid quality setting" + raise ValueError(msg) + if compression != "jpeg": + msg = "quality setting only supported for 'jpeg' compression" + raise ValueError(msg) + ifd[JPEGQUALITY] = quality + + logger.debug("Saving using libtiff encoder") + logger.debug("Items: %s", sorted(ifd.items())) + _fp = 0 + if hasattr(fp, "fileno"): + try: + fp.seek(0) + _fp = fp.fileno() + except io.UnsupportedOperation: + pass + + # optional types for non core tags + types = {} + # STRIPOFFSETS and STRIPBYTECOUNTS are added by the library + # based on the data in the strip. + # OSUBFILETYPE is deprecated. + # The other tags expect arrays with a certain length (fixed or depending on + # BITSPERSAMPLE, etc), passing arrays with a different length will result in + # segfaults. Block these tags until we add extra validation. + # SUBIFD may also cause a segfault. + blocklist += [ + OSUBFILETYPE, + REFERENCEBLACKWHITE, + STRIPBYTECOUNTS, + STRIPOFFSETS, + TRANSFERFUNCTION, + SUBIFD, + ] + + # bits per sample is a single short in the tiff directory, not a list. + atts: dict[int, Any] = {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 tag, value in itertools.chain(ifd.items(), supplied_tags.items()): + # Libtiff can only process certain core items without adding + # them to the custom dictionary. + # Custom items are supported for int, float, unicode, string and byte + # values. Other types and tuples require a tagtype. + if tag not in TiffTags.LIBTIFF_CORE: + if tag in TiffTags.TAGS_V2_GROUPS: + types[tag] = TiffTags.LONG8 + elif tag in ifd.tagtype: + types[tag] = ifd.tagtype[tag] + elif isinstance(value, (int, float, str, bytes)) or ( + isinstance(value, tuple) + and all(isinstance(v, (int, float, IFDRational)) for v in value) + ): + type = TiffTags.lookup(tag).type + if type: + types[tag] = type + if tag not in atts and tag not in blocklist: + if isinstance(value, str): + atts[tag] = value.encode("ascii", "replace") + b"\0" + elif isinstance(value, IFDRational): + atts[tag] = float(value) + else: + atts[tag] = value + + if SAMPLEFORMAT in atts and len(atts[SAMPLEFORMAT]) == 1: + atts[SAMPLEFORMAT] = atts[SAMPLEFORMAT][0] + + logger.debug("Converted items: %s", sorted(atts.items())) + + # 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;16", "I;16B", "I;16L"): + rawmode = "I;16N" + + # Pass tags as sorted list so that the tags are set in a fixed order. + # This is required by libtiff for some tags. For example, the JPEGQUALITY + # pseudo tag requires that the COMPRESS tag was already set. + tags = list(atts.items()) + tags.sort() + a = (rawmode, compression, _fp, filename, tags, types) + encoder = Image._getencoder(im.mode, "libtiff", a, encoderconfig) + encoder.setimage(im.im, (0, 0) + im.size) + while True: + errcode, data = encoder.encode(ImageFile.MAXBLOCK)[1:] + if not _fp: + fp.write(data) + if errcode: + break + if errcode < 0: + msg = f"encoder error {errcode} when writing image file" + raise OSError(msg) + + else: + for tag in blocklist: + del ifd[tag] + offset = ifd.save(fp) + + ImageFile._save( + im, + fp, + [ImageFile._Tile("raw", (0, 0) + im.size, offset, (rawmode, stride, 1))], + ) + + # -- helper for multi-page save -- + if "_debug_multipage" in encoderinfo: + # just to access o32 and o16 (using correct byte order) + setattr(im, "_debug_multipage", ifd) + + +class AppendingTiffWriter(io.BytesIO): + fieldSizes = [ + 0, # None + 1, # byte + 1, # ascii + 2, # short + 4, # long + 8, # rational + 1, # sbyte + 1, # undefined + 2, # sshort + 4, # slong + 8, # srational + 4, # float + 8, # double + 4, # ifd + 2, # unicode + 4, # complex + 8, # long8 + ] + + Tags = { + 273, # StripOffsets + 288, # FreeOffsets + 324, # TileOffsets + 519, # JPEGQTables + 520, # JPEGDCTables + 521, # JPEGACTables + } + + def __init__(self, fn: StrOrBytesPath | IO[bytes], new: bool = False) -> None: + self.f: IO[bytes] + if is_path(fn): + self.name = fn + self.close_fp = True + try: + self.f = open(fn, "w+b" if new else "r+b") + except OSError: + self.f = open(fn, "w+b") + else: + self.f = cast(IO[bytes], fn) + self.close_fp = False + self.beginning = self.f.tell() + self.setup() + + def setup(self) -> None: + # Reset everything. + self.f.seek(self.beginning, os.SEEK_SET) + + self.whereToWriteNewIFDOffset: int | None = None + self.offsetOfNewPage = 0 + + self.IIMM = iimm = self.f.read(4) + self._bigtiff = b"\x2b" in iimm + if not iimm: + # empty file - first page + self.isFirst = True + return + + self.isFirst = False + if iimm not in PREFIXES: + msg = "Invalid TIFF file header" + raise RuntimeError(msg) + + self.setEndian("<" if iimm.startswith(II) else ">") + + if self._bigtiff: + self.f.seek(4, os.SEEK_CUR) + self.skipIFDs() + self.goToEnd() + + def finalize(self) -> None: + if self.isFirst: + return + + # fix offsets + self.f.seek(self.offsetOfNewPage) + + iimm = self.f.read(4) + if not iimm: + # Make it easy to finish a frame without committing to a new one. + return + + if iimm != self.IIMM: + msg = "IIMM of new page doesn't match IIMM of first page" + raise RuntimeError(msg) + + if self._bigtiff: + self.f.seek(4, os.SEEK_CUR) + ifd_offset = self._read(8 if self._bigtiff else 4) + ifd_offset += self.offsetOfNewPage + assert self.whereToWriteNewIFDOffset is not None + self.f.seek(self.whereToWriteNewIFDOffset) + self._write(ifd_offset, 8 if self._bigtiff else 4) + self.f.seek(ifd_offset) + self.fixIFD() + + def newFrame(self) -> None: + # Call this to finish a frame. + self.finalize() + self.setup() + + def __enter__(self) -> AppendingTiffWriter: + return self + + def __exit__(self, *args: object) -> None: + if self.close_fp: + self.close() + + def tell(self) -> int: + return self.f.tell() - self.offsetOfNewPage + + def seek(self, offset: int, whence: int = io.SEEK_SET) -> int: + """ + :param offset: Distance to seek. + :param whence: Whether the distance is relative to the start, + end or current position. + :returns: The resulting position, relative to the start. + """ + if whence == os.SEEK_SET: + offset += self.offsetOfNewPage + + self.f.seek(offset, whence) + return self.tell() + + def goToEnd(self) -> None: + self.f.seek(0, os.SEEK_END) + pos = self.f.tell() + + # pad to 16 byte boundary + pad_bytes = 16 - pos % 16 + if 0 < pad_bytes < 16: + self.f.write(bytes(pad_bytes)) + self.offsetOfNewPage = self.f.tell() + + def setEndian(self, endian: str) -> None: + self.endian = endian + self.longFmt = f"{self.endian}L" + self.shortFmt = f"{self.endian}H" + self.tagFormat = f"{self.endian}HH" + ("Q" if self._bigtiff else "L") + + def skipIFDs(self) -> None: + while True: + ifd_offset = self._read(8 if self._bigtiff else 4) + if ifd_offset == 0: + self.whereToWriteNewIFDOffset = self.f.tell() - ( + 8 if self._bigtiff else 4 + ) + break + + self.f.seek(ifd_offset) + num_tags = self._read(8 if self._bigtiff else 2) + self.f.seek(num_tags * (20 if self._bigtiff else 12), os.SEEK_CUR) + + def write(self, data: Buffer, /) -> int: + return self.f.write(data) + + def _fmt(self, field_size: int) -> str: + try: + return {2: "H", 4: "L", 8: "Q"}[field_size] + except KeyError: + msg = "offset is not supported" + raise RuntimeError(msg) + + def _read(self, field_size: int) -> int: + (value,) = struct.unpack( + self.endian + self._fmt(field_size), self.f.read(field_size) + ) + return value + + def readShort(self) -> int: + return self._read(2) + + def readLong(self) -> int: + return self._read(4) + + @staticmethod + def _verify_bytes_written(bytes_written: int | None, expected: int) -> None: + if bytes_written is not None and bytes_written != expected: + msg = f"wrote only {bytes_written} bytes but wanted {expected}" + raise RuntimeError(msg) + + def _rewriteLast( + self, value: int, field_size: int, new_field_size: int = 0 + ) -> None: + self.f.seek(-field_size, os.SEEK_CUR) + if not new_field_size: + new_field_size = field_size + bytes_written = self.f.write( + struct.pack(self.endian + self._fmt(new_field_size), value) + ) + self._verify_bytes_written(bytes_written, new_field_size) + + def rewriteLastShortToLong(self, value: int) -> None: + self._rewriteLast(value, 2, 4) + + def rewriteLastShort(self, value: int) -> None: + return self._rewriteLast(value, 2) + + def rewriteLastLong(self, value: int) -> None: + return self._rewriteLast(value, 4) + + def _write(self, value: int, field_size: int) -> None: + bytes_written = self.f.write( + struct.pack(self.endian + self._fmt(field_size), value) + ) + self._verify_bytes_written(bytes_written, field_size) + + def writeShort(self, value: int) -> None: + self._write(value, 2) + + def writeLong(self, value: int) -> None: + self._write(value, 4) + + def close(self) -> None: + self.finalize() + if self.close_fp: + self.f.close() + + def fixIFD(self) -> None: + num_tags = self._read(8 if self._bigtiff else 2) + + for i in range(num_tags): + tag, field_type, count = struct.unpack( + self.tagFormat, self.f.read(12 if self._bigtiff else 8) + ) + + field_size = self.fieldSizes[field_type] + total_size = field_size * count + fmt_size = 8 if self._bigtiff else 4 + is_local = total_size <= fmt_size + if not is_local: + offset = self._read(fmt_size) + self.offsetOfNewPage + self._rewriteLast(offset, fmt_size) + + if tag in self.Tags: + cur_pos = self.f.tell() + + logger.debug( + "fixIFD: %s (%d) - type: %s (%d) - type size: %d - count: %d", + TiffTags.lookup(tag).name, + tag, + TYPES.get(field_type, "unknown"), + field_type, + field_size, + count, + ) + + if is_local: + self._fixOffsets(count, field_size) + self.f.seek(cur_pos + fmt_size) + else: + self.f.seek(offset) + self._fixOffsets(count, field_size) + self.f.seek(cur_pos) + + elif is_local: + # skip the locally stored value that is not an offset + self.f.seek(fmt_size, os.SEEK_CUR) + + def _fixOffsets(self, count: int, field_size: int) -> None: + for i in range(count): + offset = self._read(field_size) + offset += self.offsetOfNewPage + + new_field_size = 0 + if self._bigtiff and field_size in (2, 4) and offset >= 2**32: + # offset is now too large - we must convert long to long8 + new_field_size = 8 + elif field_size == 2 and offset >= 2**16: + # offset is now too large - we must convert short to long + new_field_size = 4 + if new_field_size: + if count != 1: + msg = "not implemented" + raise RuntimeError(msg) # XXX TODO + + # simple case - the offset is just one and therefore it is + # local (not referenced with another offset) + self._rewriteLast(offset, field_size, new_field_size) + # Move back past the new offset, past 'count', and before 'field_type' + rewind = -new_field_size - 4 - 2 + self.f.seek(rewind, os.SEEK_CUR) + self.writeShort(new_field_size) # rewrite the type + self.f.seek(2 - rewind, os.SEEK_CUR) + else: + self._rewriteLast(offset, field_size) + + def fixOffsets( + self, count: int, isShort: bool = False, isLong: bool = False + ) -> None: + if isShort: + field_size = 2 + elif isLong: + field_size = 4 + else: + field_size = 0 + return self._fixOffsets(count, field_size) + + +def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + append_images = list(im.encoderinfo.get("append_images", [])) + if not hasattr(im, "n_frames") and not append_images: + return _save(im, fp, filename) + + cur_idx = im.tell() + try: + with AppendingTiffWriter(fp) as tf: + for ims in [im] + append_images: + encoderinfo = ims._attach_default_encoderinfo(im) + if not hasattr(ims, "encoderconfig"): + ims.encoderconfig = () + nfr = getattr(ims, "n_frames", 1) + + for idx in range(nfr): + ims.seek(idx) + ims.load() + _save(ims, tf, filename) + tf.newFrame() + ims.encoderinfo = encoderinfo + finally: + im.seek(cur_idx) + + +# +# -------------------------------------------------------------------- +# Register + +Image.register_open(TiffImageFile.format, TiffImageFile, _accept) +Image.register_save(TiffImageFile.format, _save) +Image.register_save_all(TiffImageFile.format, _save_all) + +Image.register_extensions(TiffImageFile.format, [".tif", ".tiff"]) + +Image.register_mime(TiffImageFile.format, "image/tiff") diff --git a/voxvera/vendor/PIL/TiffTags.py b/voxvera/vendor/PIL/TiffTags.py new file mode 100644 index 0000000..613a3b7 --- /dev/null +++ b/voxvera/vendor/PIL/TiffTags.py @@ -0,0 +1,566 @@ +# +# 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. +## +from __future__ import annotations + +from typing import NamedTuple + + +class _TagInfo(NamedTuple): + value: int | None + name: str + type: int | None + length: int | None + enum: dict[str, int] + + +class TagInfo(_TagInfo): + __slots__: list[str] = [] + + def __new__( + cls, + value: int | None = None, + name: str = "unknown", + type: int | None = None, + length: int | None = None, + enum: dict[str, int] | None = None, + ) -> TagInfo: + return super().__new__(cls, value, name, type, length, enum or {}) + + def cvt_enum(self, value: str) -> int | str: + # Using get will call hash(value), which can be expensive + # for some types (e.g. Fraction). Since self.enum is rarely + # used, it's usually better to test it first. + return self.enum.get(value, value) if self.enum else value + + +def lookup(tag: int, group: int | None = None) -> TagInfo: + """ + :param tag: Integer tag number + :param group: Which :py:data:`~PIL.TiffTags.TAGS_V2_GROUPS` to look in + + .. versionadded:: 8.3.0 + + :returns: Taginfo namedtuple, From the ``TAGS_V2`` info if possible, + otherwise just populating the value and name from ``TAGS``. + If the tag is not recognized, "unknown" is returned for the name + + """ + + if group is not None: + info = TAGS_V2_GROUPS[group].get(tag) if group in TAGS_V2_GROUPS else None + else: + info = TAGS_V2.get(tag) + return info or TagInfo(tag, TAGS.get(tag, "unknown")) + + +## +# Map tag numbers to tag info. +# +# id: (Name, Type, Length[, enum_values]) +# +# The length here differs from the length in the tiff spec. For +# numbers, the tiff spec is for the number of fields returned. We +# agree here. For string-like types, the tiff spec uses the length of +# field in bytes. In Pillow, we are using the number of expected +# fields, in general 1 for string-like types. + + +BYTE = 1 +ASCII = 2 +SHORT = 3 +LONG = 4 +RATIONAL = 5 +SIGNED_BYTE = 6 +UNDEFINED = 7 +SIGNED_SHORT = 8 +SIGNED_LONG = 9 +SIGNED_RATIONAL = 10 +FLOAT = 11 +DOUBLE = 12 +IFD = 13 +LONG8 = 16 + +_tags_v2: dict[int, tuple[str, int, int] | tuple[str, int, int, dict[str, int]]] = { + 254: ("NewSubfileType", LONG, 1), + 255: ("SubfileType", SHORT, 1), + 256: ("ImageWidth", LONG, 1), + 257: ("ImageLength", LONG, 1), + 258: ("BitsPerSample", SHORT, 0), + 259: ( + "Compression", + SHORT, + 1, + { + "Uncompressed": 1, + "CCITT 1d": 2, + "Group 3 Fax": 3, + "Group 4 Fax": 4, + "LZW": 5, + "JPEG": 6, + "PackBits": 32773, + }, + ), + 262: ( + "PhotometricInterpretation", + SHORT, + 1, + { + "WhiteIsZero": 0, + "BlackIsZero": 1, + "RGB": 2, + "RGB Palette": 3, + "Transparency Mask": 4, + "CMYK": 5, + "YCbCr": 6, + "CieLAB": 8, + "CFA": 32803, # TIFF/EP, Adobe DNG + "LinearRaw": 32892, # Adobe DNG + }, + ), + 263: ("Threshholding", SHORT, 1), + 264: ("CellWidth", SHORT, 1), + 265: ("CellLength", SHORT, 1), + 266: ("FillOrder", SHORT, 1), + 269: ("DocumentName", ASCII, 1), + 270: ("ImageDescription", ASCII, 1), + 271: ("Make", ASCII, 1), + 272: ("Model", ASCII, 1), + 273: ("StripOffsets", LONG, 0), + 274: ("Orientation", SHORT, 1), + 277: ("SamplesPerPixel", SHORT, 1), + 278: ("RowsPerStrip", LONG, 1), + 279: ("StripByteCounts", LONG, 0), + 280: ("MinSampleValue", SHORT, 0), + 281: ("MaxSampleValue", SHORT, 0), + 282: ("XResolution", RATIONAL, 1), + 283: ("YResolution", RATIONAL, 1), + 284: ("PlanarConfiguration", SHORT, 1, {"Contiguous": 1, "Separate": 2}), + 285: ("PageName", ASCII, 1), + 286: ("XPosition", RATIONAL, 1), + 287: ("YPosition", RATIONAL, 1), + 288: ("FreeOffsets", LONG, 1), + 289: ("FreeByteCounts", LONG, 1), + 290: ("GrayResponseUnit", SHORT, 1), + 291: ("GrayResponseCurve", SHORT, 0), + 292: ("T4Options", LONG, 1), + 293: ("T6Options", LONG, 1), + 296: ("ResolutionUnit", SHORT, 1, {"none": 1, "inch": 2, "cm": 3}), + 297: ("PageNumber", SHORT, 2), + 301: ("TransferFunction", SHORT, 0), + 305: ("Software", ASCII, 1), + 306: ("DateTime", ASCII, 1), + 315: ("Artist", ASCII, 1), + 316: ("HostComputer", ASCII, 1), + 317: ("Predictor", SHORT, 1, {"none": 1, "Horizontal Differencing": 2}), + 318: ("WhitePoint", RATIONAL, 2), + 319: ("PrimaryChromaticities", RATIONAL, 6), + 320: ("ColorMap", SHORT, 0), + 321: ("HalftoneHints", SHORT, 2), + 322: ("TileWidth", LONG, 1), + 323: ("TileLength", LONG, 1), + 324: ("TileOffsets", LONG, 0), + 325: ("TileByteCounts", LONG, 0), + 330: ("SubIFDs", LONG, 0), + 332: ("InkSet", SHORT, 1), + 333: ("InkNames", ASCII, 1), + 334: ("NumberOfInks", SHORT, 1), + 336: ("DotRange", SHORT, 0), + 337: ("TargetPrinter", ASCII, 1), + 338: ("ExtraSamples", SHORT, 0), + 339: ("SampleFormat", SHORT, 0), + 340: ("SMinSampleValue", DOUBLE, 0), + 341: ("SMaxSampleValue", DOUBLE, 0), + 342: ("TransferRange", SHORT, 6), + 347: ("JPEGTables", UNDEFINED, 1), + # obsolete JPEG tags + 512: ("JPEGProc", SHORT, 1), + 513: ("JPEGInterchangeFormat", LONG, 1), + 514: ("JPEGInterchangeFormatLength", LONG, 1), + 515: ("JPEGRestartInterval", SHORT, 1), + 517: ("JPEGLosslessPredictors", SHORT, 0), + 518: ("JPEGPointTransforms", SHORT, 0), + 519: ("JPEGQTables", LONG, 0), + 520: ("JPEGDCTables", LONG, 0), + 521: ("JPEGACTables", LONG, 0), + 529: ("YCbCrCoefficients", RATIONAL, 3), + 530: ("YCbCrSubSampling", SHORT, 2), + 531: ("YCbCrPositioning", SHORT, 1), + 532: ("ReferenceBlackWhite", RATIONAL, 6), + 700: ("XMP", BYTE, 0), + # Four private SGI tags + 32995: ("Matteing", SHORT, 1), + 32996: ("DataType", SHORT, 0), + 32997: ("ImageDepth", LONG, 1), + 32998: ("TileDepth", LONG, 1), + 33432: ("Copyright", ASCII, 1), + 33723: ("IptcNaaInfo", UNDEFINED, 1), + 34377: ("PhotoshopInfo", BYTE, 0), + # FIXME add more tags here + 34665: ("ExifIFD", LONG, 1), + 34675: ("ICCProfile", UNDEFINED, 1), + 34853: ("GPSInfoIFD", LONG, 1), + 36864: ("ExifVersion", UNDEFINED, 1), + 37724: ("ImageSourceData", UNDEFINED, 1), + 40965: ("InteroperabilityIFD", LONG, 1), + 41730: ("CFAPattern", UNDEFINED, 1), + # MPInfo + 45056: ("MPFVersion", UNDEFINED, 1), + 45057: ("NumberOfImages", LONG, 1), + 45058: ("MPEntry", UNDEFINED, 1), + 45059: ("ImageUIDList", UNDEFINED, 0), # UNDONE, check + 45060: ("TotalFrames", LONG, 1), + 45313: ("MPIndividualNum", LONG, 1), + 45569: ("PanOrientation", LONG, 1), + 45570: ("PanOverlap_H", RATIONAL, 1), + 45571: ("PanOverlap_V", RATIONAL, 1), + 45572: ("BaseViewpointNum", LONG, 1), + 45573: ("ConvergenceAngle", SIGNED_RATIONAL, 1), + 45574: ("BaselineLength", RATIONAL, 1), + 45575: ("VerticalDivergence", SIGNED_RATIONAL, 1), + 45576: ("AxisDistance_X", SIGNED_RATIONAL, 1), + 45577: ("AxisDistance_Y", SIGNED_RATIONAL, 1), + 45578: ("AxisDistance_Z", SIGNED_RATIONAL, 1), + 45579: ("YawAngle", SIGNED_RATIONAL, 1), + 45580: ("PitchAngle", SIGNED_RATIONAL, 1), + 45581: ("RollAngle", SIGNED_RATIONAL, 1), + 40960: ("FlashPixVersion", UNDEFINED, 1), + 50741: ("MakerNoteSafety", SHORT, 1, {"Unsafe": 0, "Safe": 1}), + 50780: ("BestQualityScale", RATIONAL, 1), + 50838: ("ImageJMetaDataByteCounts", LONG, 0), # Can be more than one + 50839: ("ImageJMetaData", UNDEFINED, 1), # see Issue #2006 +} +_tags_v2_groups = { + # ExifIFD + 34665: { + 36864: ("ExifVersion", UNDEFINED, 1), + 40960: ("FlashPixVersion", UNDEFINED, 1), + 40965: ("InteroperabilityIFD", LONG, 1), + 41730: ("CFAPattern", UNDEFINED, 1), + }, + # GPSInfoIFD + 34853: { + 0: ("GPSVersionID", BYTE, 4), + 1: ("GPSLatitudeRef", ASCII, 2), + 2: ("GPSLatitude", RATIONAL, 3), + 3: ("GPSLongitudeRef", ASCII, 2), + 4: ("GPSLongitude", RATIONAL, 3), + 5: ("GPSAltitudeRef", BYTE, 1), + 6: ("GPSAltitude", RATIONAL, 1), + 7: ("GPSTimeStamp", RATIONAL, 3), + 8: ("GPSSatellites", ASCII, 0), + 9: ("GPSStatus", ASCII, 2), + 10: ("GPSMeasureMode", ASCII, 2), + 11: ("GPSDOP", RATIONAL, 1), + 12: ("GPSSpeedRef", ASCII, 2), + 13: ("GPSSpeed", RATIONAL, 1), + 14: ("GPSTrackRef", ASCII, 2), + 15: ("GPSTrack", RATIONAL, 1), + 16: ("GPSImgDirectionRef", ASCII, 2), + 17: ("GPSImgDirection", RATIONAL, 1), + 18: ("GPSMapDatum", ASCII, 0), + 19: ("GPSDestLatitudeRef", ASCII, 2), + 20: ("GPSDestLatitude", RATIONAL, 3), + 21: ("GPSDestLongitudeRef", ASCII, 2), + 22: ("GPSDestLongitude", RATIONAL, 3), + 23: ("GPSDestBearingRef", ASCII, 2), + 24: ("GPSDestBearing", RATIONAL, 1), + 25: ("GPSDestDistanceRef", ASCII, 2), + 26: ("GPSDestDistance", RATIONAL, 1), + 27: ("GPSProcessingMethod", UNDEFINED, 0), + 28: ("GPSAreaInformation", UNDEFINED, 0), + 29: ("GPSDateStamp", ASCII, 11), + 30: ("GPSDifferential", SHORT, 1), + }, + # InteroperabilityIFD + 40965: {1: ("InteropIndex", ASCII, 1), 2: ("InteropVersion", UNDEFINED, 1)}, +} + +# Legacy Tags structure +# these tags aren't included above, but were in the previous versions +TAGS: dict[int | tuple[int, int], str] = { + 347: "JPEGTables", + 700: "XMP", + # Additional Exif Info + 32932: "Wang Annotation", + 33434: "ExposureTime", + 33437: "FNumber", + 33445: "MD FileTag", + 33446: "MD ScalePixel", + 33447: "MD ColorTable", + 33448: "MD LabName", + 33449: "MD SampleInfo", + 33450: "MD PrepDate", + 33451: "MD PrepTime", + 33452: "MD FileUnits", + 33550: "ModelPixelScaleTag", + 33723: "IptcNaaInfo", + 33918: "INGR Packet Data Tag", + 33919: "INGR Flag Registers", + 33920: "IrasB Transformation Matrix", + 33922: "ModelTiepointTag", + 34264: "ModelTransformationTag", + 34377: "PhotoshopInfo", + 34735: "GeoKeyDirectoryTag", + 34736: "GeoDoubleParamsTag", + 34737: "GeoAsciiParamsTag", + 34850: "ExposureProgram", + 34852: "SpectralSensitivity", + 34855: "ISOSpeedRatings", + 34856: "OECF", + 34864: "SensitivityType", + 34865: "StandardOutputSensitivity", + 34866: "RecommendedExposureIndex", + 34867: "ISOSpeed", + 34868: "ISOSpeedLatitudeyyy", + 34869: "ISOSpeedLatitudezzz", + 34908: "HylaFAX FaxRecvParams", + 34909: "HylaFAX FaxSubAddress", + 34910: "HylaFAX FaxRecvTime", + 36864: "ExifVersion", + 36867: "DateTimeOriginal", + 36868: "DateTimeDigitized", + 37121: "ComponentsConfiguration", + 37122: "CompressedBitsPerPixel", + 37724: "ImageSourceData", + 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", + 42112: "GDAL_METADATA", + 42113: "GDAL_NODATA", + 42240: "Gamma", + 50215: "Oce Scanjob Description", + 50216: "Oce Application Selector", + 50217: "Oce Identification Number", + 50218: "Oce ImageLogic Characteristics", + # 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", + 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", + 50778: "CalibrationIlluminant1", + 50779: "CalibrationIlluminant2", + 50784: "Alias Layer Metadata", +} + +TAGS_V2: dict[int, TagInfo] = {} +TAGS_V2_GROUPS: dict[int, dict[int, TagInfo]] = {} + + +def _populate() -> None: + for k, v in _tags_v2.items(): + # Populate legacy structure. + TAGS[k] = v[0] + if len(v) == 4: + for sk, sv in v[3].items(): + TAGS[(k, sv)] = sk + + TAGS_V2[k] = TagInfo(k, *v) + + for group, tags in _tags_v2_groups.items(): + TAGS_V2_GROUPS[group] = {k: TagInfo(k, *v) for k, v in tags.items()} + + +_populate() +## +# Map type numbers to type names -- defined in ImageFileDirectory. + +TYPES: dict[int, str] = {} + +# +# These tags are handled by default in libtiff, without +# adding to the custom dictionary. From tif_dir.c, searching for +# case TIFFTAG in the _TIFFVSetField function: +# Line: item. +# 148: case TIFFTAG_SUBFILETYPE: +# 151: case TIFFTAG_IMAGEWIDTH: +# 154: case TIFFTAG_IMAGELENGTH: +# 157: case TIFFTAG_BITSPERSAMPLE: +# 181: case TIFFTAG_COMPRESSION: +# 202: case TIFFTAG_PHOTOMETRIC: +# 205: case TIFFTAG_THRESHHOLDING: +# 208: case TIFFTAG_FILLORDER: +# 214: case TIFFTAG_ORIENTATION: +# 221: case TIFFTAG_SAMPLESPERPIXEL: +# 228: case TIFFTAG_ROWSPERSTRIP: +# 238: case TIFFTAG_MINSAMPLEVALUE: +# 241: case TIFFTAG_MAXSAMPLEVALUE: +# 244: case TIFFTAG_SMINSAMPLEVALUE: +# 247: case TIFFTAG_SMAXSAMPLEVALUE: +# 250: case TIFFTAG_XRESOLUTION: +# 256: case TIFFTAG_YRESOLUTION: +# 262: case TIFFTAG_PLANARCONFIG: +# 268: case TIFFTAG_XPOSITION: +# 271: case TIFFTAG_YPOSITION: +# 274: case TIFFTAG_RESOLUTIONUNIT: +# 280: case TIFFTAG_PAGENUMBER: +# 284: case TIFFTAG_HALFTONEHINTS: +# 288: case TIFFTAG_COLORMAP: +# 294: case TIFFTAG_EXTRASAMPLES: +# 298: case TIFFTAG_MATTEING: +# 305: case TIFFTAG_TILEWIDTH: +# 316: case TIFFTAG_TILELENGTH: +# 327: case TIFFTAG_TILEDEPTH: +# 333: case TIFFTAG_DATATYPE: +# 344: case TIFFTAG_SAMPLEFORMAT: +# 361: case TIFFTAG_IMAGEDEPTH: +# 364: case TIFFTAG_SUBIFD: +# 376: case TIFFTAG_YCBCRPOSITIONING: +# 379: case TIFFTAG_YCBCRSUBSAMPLING: +# 383: case TIFFTAG_TRANSFERFUNCTION: +# 389: case TIFFTAG_REFERENCEBLACKWHITE: +# 393: case TIFFTAG_INKNAMES: + +# Following pseudo-tags are also handled by default in libtiff: +# TIFFTAG_JPEGQUALITY 65537 + +# some of these are not in our TAGS_V2 dict and were included from tiff.h + +# This list also exists in encode.c +LIBTIFF_CORE = { + 255, + 256, + 257, + 258, + 259, + 262, + 263, + 266, + 274, + 277, + 278, + 280, + 281, + 340, + 341, + 282, + 283, + 284, + 286, + 287, + 296, + 297, + 321, + 320, + 338, + 32995, + 322, + 323, + 32998, + 32996, + 339, + 32997, + 330, + 531, + 530, + 301, + 532, + 333, + # as above + 269, # this has been in our tests forever, and works + 65537, +} + +LIBTIFF_CORE.remove(255) # We don't have support for subfiletypes +LIBTIFF_CORE.remove(322) # We don't have support for writing tiled images with libtiff +LIBTIFF_CORE.remove(323) # Tiled images + +# Note to advanced users: There may be combinations of these +# parameters and values that when added properly, will work and +# produce valid tiff images that may work in your application. +# It is safe to add and remove tags from this set from Pillow's point +# of view so long as you test against libtiff. diff --git a/voxvera/vendor/PIL/WalImageFile.py b/voxvera/vendor/PIL/WalImageFile.py new file mode 100644 index 0000000..fb3e1c0 --- /dev/null +++ b/voxvera/vendor/PIL/WalImageFile.py @@ -0,0 +1,128 @@ +# +# 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. +# + +""" +This reader is based on the specification available from: +https://www.flipcode.com/archives/Quake_2_BSP_File_Format.shtml +and has been tested with a few sample files found using google. + +.. note:: + This format cannot be automatically recognized, so the reader + is not registered for use with :py:func:`PIL.Image.open()`. + To open a WAL file, use the :py:func:`PIL.WalImageFile.open()` function instead. +""" +from __future__ import annotations + +from typing import IO + +from . import Image, ImageFile +from ._binary import i32le as i32 +from ._typing import StrOrBytesPath + + +class WalImageFile(ImageFile.ImageFile): + format = "WAL" + format_description = "Quake2 Texture" + + def _open(self) -> None: + self._mode = "P" + + # read header fields + assert self.fp is not None + header = self.fp.read(32 + 24 + 32 + 12) + self._size = i32(header, 32), i32(header, 36) + Image._decompression_bomb_check(self.size) + + # load pixel data + offset = i32(header, 40) + self.fp.seek(offset) + + # strings are null-terminated + self.info["name"] = header[:32].split(b"\0", 1)[0] + if next_name := header[56 : 56 + 32].split(b"\0", 1)[0]: + self.info["next_name"] = next_name + + def load(self) -> Image.core.PixelAccess | None: + if self._im is None: + assert self.fp is not None + self.im = Image.core.new(self.mode, self.size) + self.frombytes(self.fp.read(self.size[0] * self.size[1])) + self.putpalette(quake2palette) + return Image.Image.load(self) + + +def open(filename: StrOrBytesPath | IO[bytes]) -> WalImageFile: + """ + 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 :py:func:`PIL.Image.Image.putpalette()` method. + + :param filename: WAL file name, or an opened file handle. + :returns: An image instance. + """ + return WalImageFile(filename) + + +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" +) diff --git a/voxvera/vendor/PIL/WebPImagePlugin.py b/voxvera/vendor/PIL/WebPImagePlugin.py new file mode 100644 index 0000000..e20e40d --- /dev/null +++ b/voxvera/vendor/PIL/WebPImagePlugin.py @@ -0,0 +1,317 @@ +from __future__ import annotations + +from io import BytesIO + +from . import Image, ImageFile + +try: + from . import _webp + + SUPPORTED = True +except ImportError: + SUPPORTED = False + +TYPE_CHECKING = False +if TYPE_CHECKING: + from typing import IO, Any + +_VP8_MODES_BY_IDENTIFIER = { + b"VP8 ": "RGB", + b"VP8X": "RGBA", + b"VP8L": "RGBA", # lossless +} + + +def _accept(prefix: bytes) -> bool | str: + is_riff_file_format = prefix.startswith(b"RIFF") + is_webp_file = prefix[8:12] == b"WEBP" + is_valid_vp8_mode = prefix[12:16] in _VP8_MODES_BY_IDENTIFIER + + if is_riff_file_format and is_webp_file and is_valid_vp8_mode: + if not SUPPORTED: + return ( + "image file could not be identified because WEBP support not installed" + ) + return True + return False + + +class WebPImageFile(ImageFile.ImageFile): + format = "WEBP" + format_description = "WebP image" + __loaded = 0 + __logical_frame = 0 + + def _open(self) -> None: + # Use the newer AnimDecoder API to parse the (possibly) animated file, + # and access muxed chunks like ICC/EXIF/XMP. + assert self.fp is not None + self._decoder = _webp.WebPAnimDecoder(self.fp.read()) + + # Get info from decoder + self._size, self.info["loop"], bgcolor, self.n_frames, self.rawmode = ( + self._decoder.get_info() + ) + self.info["background"] = ( + (bgcolor >> 16) & 0xFF, # R + (bgcolor >> 8) & 0xFF, # G + bgcolor & 0xFF, # B + (bgcolor >> 24) & 0xFF, # A + ) + self.is_animated = self.n_frames > 1 + self._mode = "RGB" if self.rawmode == "RGBX" else self.rawmode + + # Attempt to read ICC / EXIF / XMP chunks from file + for key, chunk_name in { + "icc_profile": "ICCP", + "exif": "EXIF", + "xmp": "XMP ", + }.items(): + if value := self._decoder.get_chunk(chunk_name): + self.info[key] = value + + # Initialize seek state + self._reset(reset=False) + + def _getexif(self) -> dict[int, Any] | None: + if "exif" not in self.info: + return None + return self.getexif()._get_merged_dict() + + def seek(self, frame: int) -> None: + if not self._seek_check(frame): + return + + # Set logical frame to requested position + self.__logical_frame = frame + + def _reset(self, reset: bool = True) -> None: + if reset: + self._decoder.reset() + self.__physical_frame = 0 + self.__loaded = -1 + self.__timestamp = 0 + + def _get_next(self) -> tuple[bytes, int, int]: + # Get next frame + ret = self._decoder.get_next() + self.__physical_frame += 1 + + # Check if an error occurred + if ret is None: + self._reset() # Reset just to be safe + self.seek(0) + msg = "failed to decode next frame in WebP file" + raise EOFError(msg) + + # Compute duration + data, timestamp = ret + duration = timestamp - self.__timestamp + self.__timestamp = timestamp + + # libwebp gives frame end, adjust to start of frame + timestamp -= duration + return data, timestamp, duration + + def _seek(self, frame: int) -> None: + if self.__physical_frame == frame: + return # Nothing to do + if frame < self.__physical_frame: + self._reset() # Rewind to beginning + while self.__physical_frame < frame: + self._get_next() # Advance to the requested frame + + def load(self) -> Image.core.PixelAccess | None: + if self.__loaded != self.__logical_frame: + self._seek(self.__logical_frame) + + # We need to load the image data for this frame + data, self.info["timestamp"], self.info["duration"] = self._get_next() + self.__loaded = self.__logical_frame + + # Set tile + if self.fp and self._exclusive_fp: + self.fp.close() + self.fp = BytesIO(data) + self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 0, self.rawmode)] + + return super().load() + + def load_seek(self, pos: int) -> None: + pass + + def tell(self) -> int: + return self.__logical_frame + + +def _convert_frame(im: Image.Image) -> Image.Image: + # Make sure image mode is supported + if im.mode not in ("RGBX", "RGBA", "RGB"): + im = im.convert("RGBA" if im.has_transparency_data else "RGB") + return im + + +def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + encoderinfo = im.encoderinfo.copy() + append_images = list(encoderinfo.get("append_images", [])) + + # If total frame count is 1, then save using the legacy API, which + # will preserve non-alpha modes + total = 0 + for ims in [im] + append_images: + total += getattr(ims, "n_frames", 1) + if total == 1: + _save(im, fp, filename) + return + + background: int | tuple[int, ...] = (0, 0, 0, 0) + if "background" in encoderinfo: + background = encoderinfo["background"] + elif "background" in im.info: + background = im.info["background"] + if isinstance(background, int): + # GifImagePlugin stores a global color table index in + # info["background"]. So it must be converted to an RGBA value + palette = im.getpalette() + if palette: + r, g, b = palette[background * 3 : (background + 1) * 3] + background = (r, g, b, 255) + else: + background = (background, background, background, 255) + + duration = im.encoderinfo.get("duration", im.info.get("duration", 0)) + loop = im.encoderinfo.get("loop", 0) + minimize_size = im.encoderinfo.get("minimize_size", False) + kmin = im.encoderinfo.get("kmin", None) + kmax = im.encoderinfo.get("kmax", None) + allow_mixed = im.encoderinfo.get("allow_mixed", False) + verbose = False + lossless = im.encoderinfo.get("lossless", False) + quality = im.encoderinfo.get("quality", 80) + alpha_quality = im.encoderinfo.get("alpha_quality", 100) + method = im.encoderinfo.get("method", 0) + icc_profile = im.encoderinfo.get("icc_profile") or "" + exif = im.encoderinfo.get("exif", "") + if isinstance(exif, Image.Exif): + exif = exif.tobytes() + xmp = im.encoderinfo.get("xmp", "") + if allow_mixed: + lossless = False + + # Sensible keyframe defaults are from gif2webp.c script + if kmin is None: + kmin = 9 if lossless else 3 + if kmax is None: + kmax = 17 if lossless else 5 + + # Validate background color + if ( + not isinstance(background, (list, tuple)) + or len(background) != 4 + or not all(0 <= v < 256 for v in background) + ): + msg = f"Background color is not an RGBA tuple clamped to (0-255): {background}" + raise OSError(msg) + + # Convert to packed uint + bg_r, bg_g, bg_b, bg_a = background + background = (bg_a << 24) | (bg_r << 16) | (bg_g << 8) | (bg_b << 0) + + # Setup the WebP animation encoder + enc = _webp.WebPAnimEncoder( + im.size, + background, + loop, + minimize_size, + kmin, + kmax, + allow_mixed, + verbose, + ) + + # Add each frame + frame_idx = 0 + timestamp = 0 + cur_idx = im.tell() + try: + for ims in [im] + append_images: + # Get number of frames in this image + nfr = getattr(ims, "n_frames", 1) + + for idx in range(nfr): + ims.seek(idx) + + frame = _convert_frame(ims) + + # Append the frame to the animation encoder + enc.add( + frame.getim(), + round(timestamp), + lossless, + quality, + alpha_quality, + method, + ) + + # Update timestamp and frame index + if isinstance(duration, (list, tuple)): + timestamp += duration[frame_idx] + else: + timestamp += duration + frame_idx += 1 + + finally: + im.seek(cur_idx) + + # Force encoder to flush frames + enc.add(None, round(timestamp), lossless, quality, alpha_quality, 0) + + # Get the final output from the encoder + data = enc.assemble(icc_profile, exif, xmp) + if data is None: + msg = "cannot write file as WebP (encoder returned None)" + raise OSError(msg) + + fp.write(data) + + +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + lossless = im.encoderinfo.get("lossless", False) + quality = im.encoderinfo.get("quality", 80) + alpha_quality = im.encoderinfo.get("alpha_quality", 100) + icc_profile = im.encoderinfo.get("icc_profile") or "" + exif = im.encoderinfo.get("exif", b"") + if isinstance(exif, Image.Exif): + exif = exif.tobytes() + if exif.startswith(b"Exif\x00\x00"): + exif = exif[6:] + xmp = im.encoderinfo.get("xmp", "") + method = im.encoderinfo.get("method", 4) + exact = 1 if im.encoderinfo.get("exact") else 0 + + im = _convert_frame(im) + + data = _webp.WebPEncode( + im.getim(), + lossless, + float(quality), + float(alpha_quality), + icc_profile, + method, + exact, + exif, + xmp, + ) + if data is None: + msg = "cannot write file as WebP (encoder returned None)" + raise OSError(msg) + + fp.write(data) + + +Image.register_open(WebPImageFile.format, WebPImageFile, _accept) +if SUPPORTED: + Image.register_save(WebPImageFile.format, _save) + Image.register_save_all(WebPImageFile.format, _save_all) + Image.register_extension(WebPImageFile.format, ".webp") + Image.register_mime(WebPImageFile.format, "image/webp") diff --git a/voxvera/vendor/PIL/WmfImagePlugin.py b/voxvera/vendor/PIL/WmfImagePlugin.py new file mode 100644 index 0000000..3ae8624 --- /dev/null +++ b/voxvera/vendor/PIL/WmfImagePlugin.py @@ -0,0 +1,188 @@ +# +# 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. +# +# WMF/EMF reference documentation: +# https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-WMF/[MS-WMF].pdf +# http://wvware.sourceforge.net/caolan/index.html +# http://wvware.sourceforge.net/caolan/ora-wmf.html +from __future__ import annotations + +from typing import IO + +from . import Image, ImageFile +from ._binary import i16le as word +from ._binary import si16le as short +from ._binary import si32le as _long + +_handler = None + + +def register_handler(handler: ImageFile.StubHandler | None) -> None: + """ + Install application-specific WMF image handler. + + :param handler: Handler object. + """ + global _handler + _handler = handler + + +if hasattr(Image.core, "drawwmf"): + # install default handler (windows only) + + class WmfHandler(ImageFile.StubHandler): + def open(self, im: ImageFile.StubImageFile) -> None: + im._mode = "RGB" + self.bbox = im.info["wmf_bbox"] + + def load(self, im: ImageFile.StubImageFile) -> Image.Image: + assert im.fp is not None + 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()) + +# +# -------------------------------------------------------------------- +# Read WMF file + + +def _accept(prefix: bytes) -> bool: + return prefix.startswith((b"\xd7\xcd\xc6\x9a\x00\x00", b"\x01\x00\x00\x00")) + + +## +# Image plugin for Windows metafiles. + + +class WmfStubImageFile(ImageFile.StubImageFile): + format = "WMF" + format_description = "Windows Metafile" + + def _open(self) -> None: + # check placeable header + assert self.fp is not None + s = self.fp.read(44) + + if s.startswith(b"\xd7\xcd\xc6\x9a\x00\x00"): + # placeable windows metafile + + # get units per inch + inch = word(s, 14) + if inch == 0: + msg = "Invalid inch" + raise ValueError(msg) + self._inch: tuple[float, float] = inch, inch + + # 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 + self.info["dpi"] = 72 + size = ( + (x1 - x0) * self.info["dpi"] // inch, + (y1 - y0) * self.info["dpi"] // inch, + ) + + self.info["wmf_bbox"] = x0, y0, x1, y1 + + # sanity check (standard metafile header) + if s[22:26] != b"\x01\x00\t\x00": + msg = "Unsupported WMF file format" + raise SyntaxError(msg) + + elif s.startswith(b"\x01\x00\x00\x00") and s[40:44] == b" EMF": + # enhanced metafile + + # get bounding box + x0 = _long(s, 8) + y0 = _long(s, 12) + x1 = _long(s, 16) + y1 = _long(s, 20) + + # get frame (in 0.01 millimeter units) + frame = _long(s, 24), _long(s, 28), _long(s, 32), _long(s, 36) + + size = x1 - x0, y1 - y0 + + # calculate dots per inch from bbox and frame + xdpi = 2540.0 * (x1 - x0) / (frame[2] - frame[0]) + ydpi = 2540.0 * (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 + self._inch = xdpi, ydpi + + else: + msg = "Unsupported file format" + raise SyntaxError(msg) + + self._mode = "RGB" + self._size = size + + loader = self._load() + if loader: + loader.open(self) + + def _load(self) -> ImageFile.StubHandler | None: + return _handler + + def load( + self, dpi: float | tuple[float, float] | None = None + ) -> Image.core.PixelAccess | None: + if dpi is not None: + self.info["dpi"] = dpi + x0, y0, x1, y1 = self.info["wmf_bbox"] + if not isinstance(dpi, tuple): + dpi = dpi, dpi + self._size = ( + int((x1 - x0) * dpi[0] / self._inch[0]), + int((y1 - y0) * dpi[1] / self._inch[1]), + ) + return super().load() + + +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + if _handler is None or not hasattr(_handler, "save"): + msg = "WMF save handler not installed" + raise OSError(msg) + _handler.save(im, fp, filename) + + +# +# -------------------------------------------------------------------- +# Registry stuff + + +Image.register_open(WmfStubImageFile.format, WmfStubImageFile, _accept) +Image.register_save(WmfStubImageFile.format, _save) + +Image.register_extensions(WmfStubImageFile.format, [".wmf", ".emf"]) diff --git a/voxvera/vendor/PIL/XVThumbImagePlugin.py b/voxvera/vendor/PIL/XVThumbImagePlugin.py new file mode 100644 index 0000000..192c041 --- /dev/null +++ b/voxvera/vendor/PIL/XVThumbImagePlugin.py @@ -0,0 +1,83 @@ +# +# 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) +# +from __future__ import annotations + +from . import Image, ImageFile, ImagePalette +from ._binary import o8 + +_MAGIC = b"P7 332" + +# 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) + ) + + +def _accept(prefix: bytes) -> bool: + return prefix.startswith(_MAGIC) + + +## +# Image plugin for XV thumbnail images. + + +class XVThumbImageFile(ImageFile.ImageFile): + format = "XVThumb" + format_description = "XV thumbnail image" + + def _open(self) -> None: + # check magic + assert self.fp is not None + + if not _accept(self.fp.read(6)): + msg = "not an XV thumbnail file" + raise SyntaxError(msg) + + # Skip to beginning of next line + self.fp.readline() + + # skip info comments + while True: + s = self.fp.readline() + if not s: + msg = "Unexpected EOF reading XV thumbnail file" + raise SyntaxError(msg) + if s[0] != 35: # ie. when not a comment: '#' + break + + # parse header line (already read) + w, h = s.strip().split(maxsplit=2)[:2] + + self._mode = "P" + self._size = int(w), int(h) + + self.palette = ImagePalette.raw("RGB", PALETTE) + + self.tile = [ + ImageFile._Tile("raw", (0, 0) + self.size, self.fp.tell(), self.mode) + ] + + +# -------------------------------------------------------------------- + +Image.register_open(XVThumbImageFile.format, XVThumbImageFile, _accept) diff --git a/voxvera/vendor/PIL/XbmImagePlugin.py b/voxvera/vendor/PIL/XbmImagePlugin.py new file mode 100644 index 0000000..1e57aa1 --- /dev/null +++ b/voxvera/vendor/PIL/XbmImagePlugin.py @@ -0,0 +1,98 @@ +# +# 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. +# +from __future__ import annotations + +import re +from typing import IO + +from . import Image, ImageFile + +# XBM header +xbm_head = re.compile( + rb"\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")?" + rb"[\000-\377]*_bits\[]" +) + + +def _accept(prefix: bytes) -> bool: + return prefix.lstrip().startswith(b"#define") + + +## +# Image plugin for X11 bitmaps. + + +class XbmImageFile(ImageFile.ImageFile): + format = "XBM" + format_description = "X11 Bitmap" + + def _open(self) -> None: + assert self.fp is not None + + m = xbm_head.match(self.fp.read(512)) + + if not m: + msg = "not a XBM file" + raise SyntaxError(msg) + + 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 = [ImageFile._Tile("xbm", (0, 0) + self.size, m.end())] + + +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + if im.mode != "1": + msg = f"cannot write mode {im.mode} as XBM" + raise OSError(msg) + + fp.write(f"#define im_width {im.size[0]}\n".encode("ascii")) + fp.write(f"#define im_height {im.size[1]}\n".encode("ascii")) + + hotspot = im.encoderinfo.get("hotspot") + if hotspot: + fp.write(f"#define im_x_hot {hotspot[0]}\n".encode("ascii")) + fp.write(f"#define im_y_hot {hotspot[1]}\n".encode("ascii")) + + fp.write(b"static char im_bits[] = {\n") + + ImageFile._save(im, fp, [ImageFile._Tile("xbm", (0, 0) + im.size)]) + + fp.write(b"};\n") + + +Image.register_open(XbmImageFile.format, XbmImageFile, _accept) +Image.register_save(XbmImageFile.format, _save) + +Image.register_extension(XbmImageFile.format, ".xbm") + +Image.register_mime(XbmImageFile.format, "image/xbm") diff --git a/voxvera/vendor/PIL/XpmImagePlugin.py b/voxvera/vendor/PIL/XpmImagePlugin.py new file mode 100644 index 0000000..3be240f --- /dev/null +++ b/voxvera/vendor/PIL/XpmImagePlugin.py @@ -0,0 +1,157 @@ +# +# 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. +# +from __future__ import annotations + +import re + +from . import Image, ImageFile, ImagePalette +from ._binary import o8 + +# XPM header +xpm_head = re.compile(b'"([0-9]*) ([0-9]*) ([0-9]*) ([0-9]*)') + + +def _accept(prefix: bytes) -> bool: + return prefix.startswith(b"/* XPM */") + + +## +# Image plugin for X11 pixel maps. + + +class XpmImageFile(ImageFile.ImageFile): + format = "XPM" + format_description = "X11 Pixel Map" + + def _open(self) -> None: + assert self.fp is not None + if not _accept(self.fp.read(9)): + msg = "not an XPM file" + raise SyntaxError(msg) + + # skip forward to next string + while True: + line = self.fp.readline() + if not line: + msg = "broken XPM file" + raise SyntaxError(msg) + m = xpm_head.match(line) + if m: + break + + self._size = int(m.group(1)), int(m.group(2)) + + palette_length = int(m.group(3)) + bpp = int(m.group(4)) + + # + # load palette description + + palette = {} + + for _ in range(palette_length): + line = self.fp.readline().rstrip() + + c = line[1 : bpp + 1] + s = line[bpp + 1 : -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.startswith(b"#"): + rgb_int = int(rgb[1:], 16) + palette[c] = ( + o8((rgb_int >> 16) & 255) + + o8((rgb_int >> 8) & 255) + + o8(rgb_int & 255) + ) + else: + # unknown colour + msg = "cannot read this XPM file" + raise ValueError(msg) + break + + else: + # missing colour key + msg = "cannot read this XPM file" + raise ValueError(msg) + + args: tuple[int, dict[bytes, bytes] | tuple[bytes, ...]] + if palette_length > 256: + self._mode = "RGB" + args = (bpp, palette) + else: + self._mode = "P" + self.palette = ImagePalette.raw("RGB", b"".join(palette.values())) + args = (bpp, tuple(palette.keys())) + + self.tile = [ImageFile._Tile("xpm", (0, 0) + self.size, self.fp.tell(), args)] + + def load_read(self, read_bytes: int) -> bytes: + # + # load all image data in one chunk + + xsize, ysize = self.size + + assert self.fp is not None + s = [self.fp.readline()[1 : xsize + 1].ljust(xsize) for i in range(ysize)] + + return b"".join(s) + + +class XpmDecoder(ImageFile.PyDecoder): + _pulls_fd = True + + def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: + assert self.fd is not None + + data = bytearray() + bpp, palette = self.args + dest_length = self.state.xsize * self.state.ysize + if self.mode == "RGB": + dest_length *= 3 + pixel_header = False + while len(data) < dest_length: + line = self.fd.readline() + if not line: + break + if line.rstrip() == b"/* pixels */" and not pixel_header: + pixel_header = True + continue + line = b'"'.join(line.split(b'"')[1:-1]) + for i in range(0, len(line), bpp): + key = line[i : i + bpp] + if self.mode == "RGB": + data += palette[key] + else: + data += o8(palette.index(key)) + self.set_as_raw(bytes(data)) + return -1, 0 + + +# +# Registry + + +Image.register_open(XpmImageFile.format, XpmImageFile, _accept) +Image.register_decoder("xpm", XpmDecoder) + +Image.register_extension(XpmImageFile.format, ".xpm") + +Image.register_mime(XpmImageFile.format, "image/xpm") diff --git a/voxvera/vendor/PIL/__init__.py b/voxvera/vendor/PIL/__init__.py new file mode 100644 index 0000000..6e4c23f --- /dev/null +++ b/voxvera/vendor/PIL/__init__.py @@ -0,0 +1,87 @@ +"""Pillow (Fork of the Python Imaging Library) + +Pillow is the friendly PIL fork by Jeffrey A. Clark and contributors. + https://github.com/python-pillow/Pillow/ + +Pillow is forked from PIL 1.1.7. + +PIL is the Python Imaging Library by Fredrik Lundh and contributors. +Copyright (c) 1999 by Secret Labs AB. + +Use PIL.__version__ for this Pillow version. + +;-) +""" + +from __future__ import annotations + +from . import _version + +# VERSION was removed in Pillow 6.0.0. +# PILLOW_VERSION was removed in Pillow 9.0.0. +# Use __version__ instead. +__version__ = _version.__version__ +del _version + + +_plugins = [ + "AvifImagePlugin", + "BlpImagePlugin", + "BmpImagePlugin", + "BufrStubImagePlugin", + "CurImagePlugin", + "DcxImagePlugin", + "DdsImagePlugin", + "EpsImagePlugin", + "FitsImagePlugin", + "FliImagePlugin", + "FpxImagePlugin", + "FtexImagePlugin", + "GbrImagePlugin", + "GifImagePlugin", + "GribStubImagePlugin", + "Hdf5StubImagePlugin", + "IcnsImagePlugin", + "IcoImagePlugin", + "ImImagePlugin", + "ImtImagePlugin", + "IptcImagePlugin", + "JpegImagePlugin", + "Jpeg2KImagePlugin", + "McIdasImagePlugin", + "MicImagePlugin", + "MpegImagePlugin", + "MpoImagePlugin", + "MspImagePlugin", + "PalmImagePlugin", + "PcdImagePlugin", + "PcxImagePlugin", + "PdfImagePlugin", + "PixarImagePlugin", + "PngImagePlugin", + "PpmImagePlugin", + "PsdImagePlugin", + "QoiImagePlugin", + "SgiImagePlugin", + "SpiderImagePlugin", + "SunImagePlugin", + "TgaImagePlugin", + "TiffImagePlugin", + "WebPImagePlugin", + "WmfImagePlugin", + "XbmImagePlugin", + "XpmImagePlugin", + "XVThumbImagePlugin", +] + + +class UnidentifiedImageError(OSError): + """ + Raised in :py:meth:`PIL.Image.open` if an image cannot be opened and identified. + + If a PNG image raises this error, setting :data:`.ImageFile.LOAD_TRUNCATED_IMAGES` + to true may allow the image to be opened after all. The setting will ignore missing + data and checksum failures. + """ + + pass diff --git a/voxvera/vendor/PIL/__main__.py b/voxvera/vendor/PIL/__main__.py new file mode 100644 index 0000000..043156e --- /dev/null +++ b/voxvera/vendor/PIL/__main__.py @@ -0,0 +1,7 @@ +from __future__ import annotations + +import sys + +from .features import pilinfo + +pilinfo(supported_formats="--report" not in sys.argv) diff --git a/voxvera/vendor/PIL/_avif.cpython-312-x86_64-linux-gnu.so b/voxvera/vendor/PIL/_avif.cpython-312-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..c83a451ad82942efb4e779a23f164ad58d15ccff GIT binary patch literal 87889 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 Z=BM*>ZyOzb+iH_OsXdCsw0_Um{{iAILv{cF literal 0 HcmV?d00001 diff --git a/voxvera/vendor/PIL/_avif.pyi b/voxvera/vendor/PIL/_avif.pyi new file mode 100644 index 0000000..e27843e --- /dev/null +++ b/voxvera/vendor/PIL/_avif.pyi @@ -0,0 +1,3 @@ +from typing import Any + +def __getattr__(name: str) -> Any: ... diff --git a/voxvera/vendor/PIL/_binary.py b/voxvera/vendor/PIL/_binary.py new file mode 100644 index 0000000..4594ccc --- /dev/null +++ b/voxvera/vendor/PIL/_binary.py @@ -0,0 +1,112 @@ +# +# 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. +# + + +"""Binary input/output support routines.""" +from __future__ import annotations + +from struct import pack, unpack_from + + +def i8(c: bytes) -> int: + return c[0] + + +def o8(i: int) -> bytes: + return bytes((i & 255,)) + + +# Input, le = little endian, be = big endian +def i16le(c: bytes, o: int = 0) -> int: + """ + Converts a 2-bytes (16 bits) string to an unsigned integer. + + :param c: string containing bytes to convert + :param o: offset of bytes to convert in string + """ + return unpack_from(" int: + """ + Converts a 2-bytes (16 bits) string to a signed integer. + + :param c: string containing bytes to convert + :param o: offset of bytes to convert in string + """ + return unpack_from(" int: + """ + Converts a 2-bytes (16 bits) string to a signed integer, big endian. + + :param c: string containing bytes to convert + :param o: offset of bytes to convert in string + """ + return unpack_from(">h", c, o)[0] + + +def i32le(c: bytes, o: int = 0) -> int: + """ + Converts a 4-bytes (32 bits) string to an unsigned integer. + + :param c: string containing bytes to convert + :param o: offset of bytes to convert in string + """ + return unpack_from(" int: + """ + Converts a 4-bytes (32 bits) string to a signed integer. + + :param c: string containing bytes to convert + :param o: offset of bytes to convert in string + """ + return unpack_from(" int: + """ + Converts a 4-bytes (32 bits) string to a signed integer, big endian. + + :param c: string containing bytes to convert + :param o: offset of bytes to convert in string + """ + return unpack_from(">i", c, o)[0] + + +def i16be(c: bytes, o: int = 0) -> int: + return unpack_from(">H", c, o)[0] + + +def i32be(c: bytes, o: int = 0) -> int: + return unpack_from(">I", c, o)[0] + + +# Output, le = little endian, be = big endian +def o16le(i: int) -> bytes: + return pack(" bytes: + return pack(" bytes: + return pack(">H", i) + + +def o32be(i: int) -> bytes: + return pack(">I", i) diff --git a/voxvera/vendor/PIL/_deprecate.py b/voxvera/vendor/PIL/_deprecate.py new file mode 100644 index 0000000..711c62a --- /dev/null +++ b/voxvera/vendor/PIL/_deprecate.py @@ -0,0 +1,72 @@ +from __future__ import annotations + +import warnings + +from . import __version__ + + +def deprecate( + deprecated: str, + when: int | None, + replacement: str | None = None, + *, + action: str | None = None, + plural: bool = False, + stacklevel: int = 3, +) -> None: + """ + Deprecations helper. + + :param deprecated: Name of thing to be deprecated. + :param when: Pillow major version to be removed in. + :param replacement: Name of replacement. + :param action: Instead of "replacement", give a custom call to action + e.g. "Upgrade to new thing". + :param plural: if the deprecated thing is plural, needing "are" instead of "is". + + Usually of the form: + + "[deprecated] is deprecated and will be removed in Pillow [when] (yyyy-mm-dd). + Use [replacement] instead." + + You can leave out the replacement sentence: + + "[deprecated] is deprecated and will be removed in Pillow [when] (yyyy-mm-dd)" + + Or with another call to action: + + "[deprecated] is deprecated and will be removed in Pillow [when] (yyyy-mm-dd). + [action]." + """ + + is_ = "are" if plural else "is" + + if when is None: + removed = "a future version" + elif when <= int(__version__.split(".")[0]): + msg = f"{deprecated} {is_} deprecated and should be removed." + raise RuntimeError(msg) + elif when == 13: + removed = "Pillow 13 (2026-10-15)" + elif when == 14: + removed = "Pillow 14 (2027-10-15)" + else: + msg = f"Unknown removal version: {when}. Update {__name__}?" + raise ValueError(msg) + + if replacement and action: + msg = "Use only one of 'replacement' and 'action'" + raise ValueError(msg) + + if replacement: + action = f". Use {replacement} instead." + elif action: + action = f". {action.rstrip('.')}." + else: + action = "" + + warnings.warn( + f"{deprecated} {is_} deprecated and will be removed in {removed}{action}", + DeprecationWarning, + stacklevel=stacklevel, + ) diff --git a/voxvera/vendor/PIL/_imaging.cpython-312-x86_64-linux-gnu.so b/voxvera/vendor/PIL/_imaging.cpython-312-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..def877a3ca0048045b56eb1c44e3db0da2004a79 GIT binary patch literal 3357545 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

-P6co34 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�lX9Ug-82am2`T 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

}|3^7vKa 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 z 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

nT=uVU32N4Nai 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

IS9a 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 literal 0 HcmV?d00001 diff --git a/voxvera/vendor/PIL/_imaging.pyi b/voxvera/vendor/PIL/_imaging.pyi new file mode 100644 index 0000000..81028a5 --- /dev/null +++ b/voxvera/vendor/PIL/_imaging.pyi @@ -0,0 +1,31 @@ +from typing import Any + +class ImagingCore: + def __getitem__(self, index: int) -> float | tuple[int, ...] | None: ... + def __getattr__(self, name: str) -> Any: ... + +class ImagingFont: + def __getattr__(self, name: str) -> Any: ... + +class ImagingDraw: + def __getattr__(self, name: str) -> Any: ... + +class PixelAccess: + def __getitem__(self, xy: tuple[int, int]) -> float | tuple[int, ...]: ... + def __setitem__( + self, xy: tuple[int, int], color: float | tuple[int, ...] + ) -> None: ... + +class ImagingDecoder: + def __getattr__(self, name: str) -> Any: ... + +class ImagingEncoder: + def __getattr__(self, name: str) -> Any: ... + +class _Outline: + def close(self) -> None: ... + def __getattr__(self, name: str) -> Any: ... + +def font(image: ImagingCore, glyphdata: bytes) -> ImagingFont: ... +def outline() -> _Outline: ... +def __getattr__(name: str) -> Any: ... diff --git a/voxvera/vendor/PIL/_imagingcms.cpython-312-x86_64-linux-gnu.so b/voxvera/vendor/PIL/_imagingcms.cpython-312-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..2544b62097e2e2e0f62f2f1731222097ad0807e8 GIT binary patch literal 157841 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 literal 0 HcmV?d00001 diff --git a/voxvera/vendor/PIL/_imagingcms.pyi b/voxvera/vendor/PIL/_imagingcms.pyi new file mode 100644 index 0000000..4fc0d60 --- /dev/null +++ b/voxvera/vendor/PIL/_imagingcms.pyi @@ -0,0 +1,143 @@ +import datetime +import sys +from typing import Literal, SupportsFloat, TypeAlias, TypedDict + +from ._typing import CapsuleType + +littlecms_version: str | None + +_Tuple3f: TypeAlias = tuple[float, float, float] +_Tuple2x3f: TypeAlias = tuple[_Tuple3f, _Tuple3f] +_Tuple3x3f: TypeAlias = tuple[_Tuple3f, _Tuple3f, _Tuple3f] + +class _IccMeasurementCondition(TypedDict): + observer: int + backing: _Tuple3f + geo: str + flare: float + illuminant_type: str + +class _IccViewingCondition(TypedDict): + illuminant: _Tuple3f + surround: _Tuple3f + illuminant_type: str + +class CmsProfile: + @property + def rendering_intent(self) -> int: ... + @property + def creation_date(self) -> datetime.datetime | None: ... + @property + def copyright(self) -> str | None: ... + @property + def target(self) -> str | None: ... + @property + def manufacturer(self) -> str | None: ... + @property + def model(self) -> str | None: ... + @property + def profile_description(self) -> str | None: ... + @property + def screening_description(self) -> str | None: ... + @property + def viewing_condition(self) -> str | None: ... + @property + def version(self) -> float: ... + @property + def icc_version(self) -> int: ... + @property + def attributes(self) -> int: ... + @property + def header_flags(self) -> int: ... + @property + def header_manufacturer(self) -> str: ... + @property + def header_model(self) -> str: ... + @property + def device_class(self) -> str: ... + @property + def connection_space(self) -> str: ... + @property + def xcolor_space(self) -> str: ... + @property + def profile_id(self) -> bytes: ... + @property + def is_matrix_shaper(self) -> bool: ... + @property + def technology(self) -> str | None: ... + @property + def colorimetric_intent(self) -> str | None: ... + @property + def perceptual_rendering_intent_gamut(self) -> str | None: ... + @property + def saturation_rendering_intent_gamut(self) -> str | None: ... + @property + def red_colorant(self) -> _Tuple2x3f | None: ... + @property + def green_colorant(self) -> _Tuple2x3f | None: ... + @property + def blue_colorant(self) -> _Tuple2x3f | None: ... + @property + def red_primary(self) -> _Tuple2x3f | None: ... + @property + def green_primary(self) -> _Tuple2x3f | None: ... + @property + def blue_primary(self) -> _Tuple2x3f | None: ... + @property + def media_white_point_temperature(self) -> float | None: ... + @property + def media_white_point(self) -> _Tuple2x3f | None: ... + @property + def media_black_point(self) -> _Tuple2x3f | None: ... + @property + def luminance(self) -> _Tuple2x3f | None: ... + @property + def chromatic_adaptation(self) -> tuple[_Tuple3x3f, _Tuple3x3f] | None: ... + @property + def chromaticity(self) -> _Tuple3x3f | None: ... + @property + def colorant_table(self) -> list[str] | None: ... + @property + def colorant_table_out(self) -> list[str] | None: ... + @property + def intent_supported(self) -> dict[int, tuple[bool, bool, bool]] | None: ... + @property + def clut(self) -> dict[int, tuple[bool, bool, bool]] | None: ... + @property + def icc_measurement_condition(self) -> _IccMeasurementCondition | None: ... + @property + def icc_viewing_condition(self) -> _IccViewingCondition | None: ... + def is_intent_supported(self, intent: int, direction: int, /) -> int: ... + +class CmsTransform: + def apply(self, id_in: CapsuleType, id_out: CapsuleType) -> int: ... + +def profile_open(profile: str, /) -> CmsProfile: ... +def profile_frombytes(profile: bytes, /) -> CmsProfile: ... +def profile_tobytes(profile: CmsProfile, /) -> bytes: ... +def buildTransform( + input_profile: CmsProfile, + output_profile: CmsProfile, + in_mode: str, + out_mode: str, + rendering_intent: int = 0, + cms_flags: int = 0, + /, +) -> CmsTransform: ... +def buildProofTransform( + input_profile: CmsProfile, + output_profile: CmsProfile, + proof_profile: CmsProfile, + in_mode: str, + out_mode: str, + rendering_intent: int = 0, + proof_intent: int = 0, + cms_flags: int = 0, + /, +) -> CmsTransform: ... +def createProfile( + color_space: Literal["LAB", "XYZ", "sRGB"], color_temp: SupportsFloat = 0.0, / +) -> CmsProfile: ... + +if sys.platform == "win32": + def get_display_profile_win32(handle: int = 0, is_dc: int = 0, /) -> str | None: ... diff --git a/voxvera/vendor/PIL/_imagingft.cpython-312-x86_64-linux-gnu.so b/voxvera/vendor/PIL/_imagingft.cpython-312-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..f58fd53fe6359f20e72b06c1bdb80559c9dca44b GIT binary patch literal 322977 zcmeFa3v?7k_CG$82M!4Kpg~7vb<{xkK!h2If*PEO zrX5G43yQkCqWfaiUD;h1P!wmvBLP%G6bYab5Tu*o5kMe>M}D7MU7b`~X21LWo&P!i z^E=0INca8pty@*MZr!@|=+4uHf#GRZtHt<9x7=XqqAV5-OONJvzDm=Nq1k<(En9#XODa>aWpp<7(tbbz(J%Z@~e^GZ9KnJ>FhoA zbjFou&+5kVZsaA%65iXigfdP>-@&mC}$dAmac_M^6>nOD>2Xy zzuI)FE58)!=JwGUjjNGg$^WB2yxKpz>;q!hwD@oW^mV!j@OvNdlktCrMmQNincd0w z??vZ18UGT zo=pBJDfIS_6y+&Nq0c2L+GSP>IZvTpCo7MFa-OWdeN*W3>lAWcNk6&#f)wrkK??bw zq^R$Wz)x1rdibT2;f@sg380)O_^m1W*Q6Bu2UC>u?G$z%Pl1=G(EsBp_`NCG_lFeanVN$C(iHW5FNL1JM|nUB*Dd;5C|zf_W{ zttyp0pg~S%hoKbt7NyYhttslYEJge7Poba3Q}8cGz9%c^U1;Bv)%S}O^8cBloO4px z!|W90aiwVY`%~z1OA7gADfE103jFjG_MDTVz6~klA97$-^iI{n(j&{CZ!fe+54JS;YE_&C<9)rzcAKwV31f zH+1@Ov>*Nai_5QlU8g7N7t6V9T(DV6Ki7dXIKG3+nfr|n{D$MNaQ-TepT+U7n0_Qn zyuY5|JdTg${4HGmW$1_WQ)lG6UZ>yGFP2M@{B(=WQo-e;=Im!E>%TTjEoa=q@!lM_ z@cQ=V_yR89!{s|Uet`4Oe@_SavVdh0=eK{Y<5zM0Z{WD4S;w=voGKKI$~m9w-_To< z>$&x9ozcMW=lrD~>iA7ueiP@nzoX+9^Y&GFzPUVKqh8l@yo&2{6fe&=yk1rFbl`F> z=S|k{Y?i8*b=;8iHgDh7zvy^`%c=2qJdvD@QVii|I+~CZfobiw9dOO%zP*Ei1$`( zyP0`^P8&SNwR@`clS=5~HRSyqF`oRVtDfrrE^R8gG;g6gxK8a_>zeiQtC*prTQ28V z=$v0;g&@u122DT4zAUCe>}P(7er@CXwFX|naSNXh8Mr|^P54$#my>P6cW^w{gzw^b zz6syQagPZ<#PL!Se%4Ms-*OXv?Jga!FyRi)UunVvNu9sSg#U{3*O+h%mtSkbJsh87 z!Y|-*=9+L9$LE>wY>v-2;qq?1oQq8OrJO%*!XM>$g9*Q!CcJ{<9uxi`w+GpTS8%-8gg?&l5)+=!?X%Q`hj_l_Cj1eOSD5f8I9_SOpXPX# z34e~`H72~4wzj1uN3ICAei%j@pj>k=SoZ}59{A;e~ zMic%m$D2*~HjcNL@a0^-YQpO|-fF_X;CPz}|C-~Lv%A~>cO18w@E{(ebB-6A@UJ*tV!}6Y zywrq$$MJF#elG8)6()Q?$16?vRxYQ?glinHG2weSUTeY+aD0vlKf>|3Cj21ar?r~!9M0cn z!Y|{vg${b)$B2KfK8EAPCVV`{OHBCv94|HD6FFXP!XM*!g$aL}1yw-%j z$?-WR{2h+ZHR11Ze4YvakmK`B_@^9SWWtwmJZ{2QbG*TXH*vhtgm2_{vkBkI@fH)l zljEuh-^cM*6MmTEZ6^FU$1Qwc*RcO|KHssK@Ln9ZoA5I@?l9r~Iqo#!12~>-!Y}1` zt_jcMc)kfQ<9^Iz!q4aaRyN_Iczuga__bVqi3uOV@lq4+<#@RXFW`8E2_M1nN)ukp z@hTHOmg6-h{BDldn(#7?&oSW-aeS@`pUm;N2{-2T4JO=}*EgDQV_x5E!i{--iwQU8 z^{NRs=Jl;6+?dz5nQ&uXZ~0YszirIxZ6@59*V|3FF|T)+aARKYG~vd)KHG#F^ZHy9 zZp`cRO}H_y_n2^FUN4()V_siu!i{--i3vC6^`$1as{&^<6nDaN7@a3Gp(S+x6{#FzIIp=RP;dajNxVXDMzvBE(6K>)BRVKU_=dUs0 zxlifsHP?jyaf^=6GvV<{ZVx8>6VBgY!b_jf`CCl*MqVD(gty$U^II)<>docnoA7=dmreLUj+dD58#rEW!bfnt(u7aoc#R2vlH+qs_=_B$XTtx;@kJ*5 z9~^Hm;p!e;&&?+MbIz}t@J$?VGvT{9ZX4KL{}%3t9VXniSI;-wglBU+--MsV<;W)d z5{{Rc@EbW^Zo+Toc%=!ym*X`i{4tKtG2t(8e4Yva1IHJc@Q*m&V8XxPc(V!L!g1Av zALV$P2|spJT$;b9|l&Z{_$R6K?xa*H429@6YjO6MhB9RTJ*xc$*0y!*Sc7?)sOx zJv&VJ1DrqGgjaDq--N%!aoL2w&G8ZwKA+>|CVUCUD^2)nj@Ov*Z#h24gzw?_JQH5d z>$}K=TX%5%oA5pyZ#Lmhj;kj8dXBf5@LHa)?XvFrzk~BTO!y+spKZchIG%69ALVjn z6K-kM>s4aHvpHUF!izaxX~H91evJvQ;QVt;_*%INoN$w{rQm%e(9UD90Tp{47=1Teb=8%%roK3IsYOPzKP=vCj21Bn@#v>eE(WC;TLke&4lOj^4PBE zuKyB_mz=NTJy7OTS;_TZr2^htz{>?ZBH$GQezt&D3ivq!UM1j80k0A8iv+w@!1*f& z?CBffK%HWKe+-9zUxOxP-b=uX z1-y@dmk9W20$wWMl7N>BxLv?21iY_+R|>fJzD$*XKPT|l2)OZ10X?o2@GOCUj)3SxW&alnxJ|%a0&W-Z0Rrw2@QVf9Dd3j~c(#CN3wW-8=LmSd zfDaUKkAU;3J9{b%_@x4Wv49T}@Dc&POu$P8{Bi*=7w{_tyh6Zp1-w$g^8~y~z^@eW z8Uepbz-tBkY5|`k;7<$qTmc^};PV9h8Udd#;MWTHA_31A@VJ1x1-wDPuM_Y_0l!|r zn+5y^0dEoT8wFeyaF2ku3i#^+-X`E)0k>S(W>j+$P{Y0k;cyp@2ICe7Jx+1$>5p zXAAf<0-h`2Hwk#YfXf2z5pe!W8+$4XxL@Ee7Vr@QULxQl1-w+i0|H(y;6(ynA>cO) zc%^{fBH&d5UM%1>0)DH2*9!P;0zOB;jd$GW@mv8PBk<1?@Y@A^zJQMv@I?Y%BH(cW z=SyAeX@h{@DeyN6_&5P?7I3~K$DXzb_+0|OD&Thuc&mWlBj9ZUUMk=gzTQr;#=Qb= z6Y%>4+%Dh~1l%Fu_Y1gFz#kCsYymG5@LU0ZP{8vAyj;LN0{&|Omj(PG0WTKthXuSu zz>Ot)dR!{t69xWq0iPt`6#`x%;FSXYs(@Ds_@e?|Bj7Iyc&&g>5%4(zK2^Zy3V2Y! z=LxtNKhGEN#{~XG0zOT^;{yJ;fHw&E69V2S;Cv}rc>2rjFBAZG1 zyvrs>Ghbh0vB=f&U|L6`9A0Ky$`W@BUIn;gz~}fao$tXN(QM?(#3!`r% zS|Ymj1b_h}h^DE!*23svME51Sk z=!=M+&**oFrm3(tm(lMKO;cd4meFqzO;ca3iqWqSeF@POjDDWzY@$mU{S47LL>Dvq z38HCgt9ckbg=m_(YPpPlh-jL!YEDMqM>I`UH9Mp4B>HlqEsVa6=qreBJ;vIfXqu90 zEsP#UG)+adMn+#pG)+OZIHRv5nx>xGd`1r>nx>rETt;6=G)*bBT1KBk^fg3RG5U0( zX$q-TFuE7fGALh0#q!)09zbJ<8gjXb;gXjIJZvOLQZnKPGw@(Q!uqljs7X=QH|UqG>9q&1LjE zMAH;dt7Y^XMAOtyt77ykMBhYo1*4xQS|+-b(a#X=C%Tx?PY^wVXb+>O5KU7tEtk;` z5lvGt&B^Hdh%O@9&geUdzL{tXqi-Yn7NT3*S^E=BQ!K58(Zh(Qsg>5q=g&msDDqN^BvI?*(R(kd9;i)flUX{C%#Bbug6 zS}~*BKL<@yCC$U={Y29gNy}yQPNHdQq&XSAm1vq0X?8|$B$}o|nuXC#MAH;VYdyl+ zpXgGeTNqtOG);N5Mn->3G);B1IHUhb^aP^kGx}YkX=!e@%2Tqn{x9A)-Buos;b92*hq@!D3(VK z*E{7zM-Oco_$Yo7(&Kr)@mxN=FP)F+s!E;-!MAxEb> zbSjr9W%D@g62@Fb1@?{}GYavYT|EQ!g@#{%w~&>kY8F^P4p;|;XTweq_hljKv**Rf z-Q^v>6pf}=SdI?0f=7-8Txg)tE}I++x-!%UkUR<~3M4Nc1q5O9t08RDK`O*@1d>wZ zF9-`|r4GjOdpttX)Z_FtT4y%>E{(EL>eRoo9CrdA&2xFJ6S;*W)hErD&R9h=b0C>Ydz|d9 z{~;=Vz3PJRH>yFOP}Zw)8W(rXNyX6-EYv{z%3@hU0ZAb03@AI)$02e_FQSzl5UKu} zvZ&r2Je`8=K632IG}&D*J<|>jIoj{sWh7+qKcIe6*ylf?P-n5i)T=MS81+I0pVkZX z250*d*mS*0HG@%)PTf{tLX@&s9fcxm=YY`rBS9X5B7m6P_j`LO%lSZ9&-pfp)XLaX zA0c~HJMn+s$^WR%e-s7=b?RTsGd?S~aM$f+!<;4kE&E@5^$;t}#uFe`t zk-d1L*Yb~C#AgS(YPo}+8WvQ&f${EDCz43Lh$t3*M&-q+$0&!P(+jEf&=<&#d#m^9 zR(}Kfj~xcwTfG<6tB!|;!i&6GN1V&-jw;Z@dGH%ZmYnJM~QC9)E9$$sVY!o1z}J{L_JEP0!7 zmqmBD+UP1PK38@CzjA#_z~v$X#$>OG9{Hn7=HMbjL)q&2a4=paj_j&`N+WOvGJrBm zk<=+q*u5kjtm+XITZVI}kSC0GWyuqei98|ba>!OqcCS*_$jGBYR`y9n?Q;4fu2LCJ zq*8XHFN08KxU!VJQc)YF>n~gD0AO-T8M*f>-^dDh{S($nH}8|veXerZ-AIKm_elX2 zHH9jdTK(Z=4mFDk;U13~MNu{>Lgn@1Hp3ge+7(a^BTrYX+(}UH#PJ^9nC}#4VfqZ0 zJ<-;~hdlEkwZAuZlM`1DFt2;LPr5}_U)V=RB1N{r;NaP!nb9~YqJp*dMcSv(yU<)^ z@aGjMRGivZioAuVZhI(+WG!jxs2|06BYOPG zn!>zove9O&6=@nrt;pKdXjM{3F12PpwPv>V9e6tZxzL`u>MgK%-kx;ax?6j?I@>dc z^*W!c!aw0_YDlz-iss|0uVBpw-Lz1PW$7(SEy-H6UQwl@dL|eJV#eS^qoOTYOQJzx zBMx(GN=0>O%>Mpxy#uKn>P=9R9EKfPWwe(~c2nJO=aM3ypmN^mO=#9@^;XS}J(BM9 zrr%=&+S?a<*ooE+$DLkW(2RxdU4>F$JES_*#9qBADc)dnV|{$iR1k!qzZXAE8p&7R zZN=j|z@xVZ)=BW(r@%AljXp5BT5Rzy*TDHZ+dudRwwt^wVv?hexIA8EGYPET zEk$Ngzk;v*35AoxZE2zNqy>eRArqyD1uZmWiWKQX*GHttDd=ouUzs0$N=az^Tlf?! za$a2hH_TuABMteGFB`%y;lf`tgeRDA2>l(@D7`ci&q?%$be$ObLv*~$1~q3{o4rb% zH?i}4ueHvne6{u{l3A%tGBF0F$oX`=PpY1Tz|4oP<)LOmr**&$WO)2)8QfQg^sH=Sa?G7=yCZt8{34h=EK7=AySy4ot2dP(D=*CS#bNcWt{DS|pp&wfHY2 zMd7qVBm#wIt3qA<%NQ^H$}tuQ%Pe?C4C_$$?P2}D`VHp#P&8k(+!j#w`ry56>QzZp zjK{HlaEyrc{~Ig-J#DjkE&P#Rsb?|nzwr#kwpFi0ET?SDJ4Dgrp$@R_hP1 zuok%w1|KYlW-LVXg7^UrH9(WHmJB(4m_u4P+I6wJ*cMuc2f1te~P->a^MRtoAf7NQImEL6`h`B+vl?1#E54n(l*x;uyOD2!rOt|v%X$ZUqp6gZVO{IGajOsxA zf+1*Q^$MdgZR!e*DqYB02~(M_+Kn~Re~xJ_VilV@j*>?+?`LIVBZd_DHF+I8OrDPN zca9HIggnjlh!3=DaYHKjJ@V0rn~0Y|uvBjf8X)_ou#ZAQn&_1q_MNZxg%OfD?ZiMq z<7U1;`jkDIt@>4%Yj)yvk`#F9 z(XMjZ<2i6nT%zQQaiRKBi>}S@O4I zW^7mAW%?x>lp?QEcH}A0T0JT$Ya0F=b{MjvY0!2b-Img%NIlq#s<-)LpSVb*U-BPg zBF93cUs=XPUeqZPb{-qN2rDV-Gzj!ZGhD_j)K9%J@@pn-w?FnN7j~Qp>m>+VfL@`O zj|rPf!UmpHSZ@e}TdLj^xz$#UFxW6llCQo833*NEq2;pDDD+TcoEYsYCYzVxRBnBYIhDs@ zcZTQO#R^0X$`p2p6n11gHK%zEfbynbh1nO2_?zDY@XuR0qk3uL&1qz~#Wn&()?)SFv{AGGx zOwV^B8-0E}Q+WPsdY((qXBf{jZ>8t4!45nkFEa&&(CV0U#PPHc&`A6{bbg}aL_g2d zE9=$yL=OTTpq@z;`8^P-BlLlWmXtFfPCFOBWU$X9b{d<>IRuILgotRrwtrCxbbpzv z-3Jv24nw%r zhd!J@;=54-WjQ(OO)MoM4t*(bzz4qDTm2@exn93RLx9lHu z;bW|JJ3zip)HS|JksASDKakB|?`Qn^>I)Dpm{cu7o0-d4>nY7_C9!jz7G?X?L#0I3 ztH;68v`6uas^jP^TWlmMIkYy8s)Zq#Rjqme*bYec%zMKY%K_;Q1XHvI>48;{IA#?k z>;?-t2V`kb9D(3P~6VH?jWl2s{Z}enYp3ZAB5nZ5dJoQzs}u zSD@Y#)RrwM4&PR&a;Aiwnm--AuX|#r@`x~0?>nm7?)z?VS zRy<eW^ajS`THR_>=m z@%cbePCXv@#yiH3snTdH{EP`GUtrPr0Ad3<+GprzSnFIF>=k~>X_X?Ix%!->zQFYZ z-ueJNsrqdYMLk=k>N&WPqXih)EA9ShA)FBV%Kcvf+okGPaG_g_?1vFF&^k~yH4J*& zUUeNaTLc#{>)`eB$7A%LOxay0g)6A5))yiz$8KbUEQshxk6#SlnHl(b4(gL-R+U`!| zg_XMCIczM;YYHeUh;KTI6hYU2hCPP3DY$_;p2wCGRy5R0VIJffZecneJdMLy96n#? zzMsR#p#-)HN9LZ6B|*xf1jVH+jza`m`6J9@q=;XacLhthLtO?Qzp{dPA?Otq^4R>* z+tD3ao1@+Y>ZC+~vY`J=-fBcel`*x#@ns(KF+>{ukD zJh0xTo=L8rOFsb++QJG|hx+1Xy=3&)c#7QHrO2De1EOqL!xPOdE%esN=!}aU$+>9v zweK?porgU-YWH4{L9$+fA}K(1(riL77~WrFLlCg;NLQz`q7>3e7j$|IUeQ^uDijy~ zp5|GJt$hnD}0-1fM^#w?|=K6DDOor47^EaErCZ=Q@i95O`|DsoGme zf3vg%B)V}x`O&9jxzy=sC^V-hx*$Bv8$IOfkkzHM~fy|h(@LA zq0E+0I+9>*4Mg9fP8hY<)89YCQlFZJ2S&Ar-Zj!!)@>k zvylr{pQK0z3KVGyRiKyT50AR$vqLjzZyM+wc`JR&Hvy%;ORfHvti8VrQ65EhO`$8i zv9Y|5OhCw=b(0(~&XoTL}vLn9rODXF~2Hct83L%sT)JLlB+eQro|$W*ovCi#ilZS&AdEkp*$EEpc1EMMX}I6(DOfZrt>)ytIrRp9Q}z#i*|r^ zmZF(2=uDSkB0`Pz1yy&1yFq zvmR1}z8wJeY@NM@OS+fXZ!_4XnwvQ7lJtZA=vDpEDLmDBdelIV46pbAoT!YwSYsKW24+L! z6r$CYSRE$T#Fk81X_jMGpDJ{t+A?X9oPl4(cFfM;dg~k@JlOP~RI;l~x^ET!V>9`Y zgw%JK8kJRLL|at;Nf~iWcCQF6)qD_4wO-JS0Mk$Wt9u>_ zj>#xGiBq#7ZCS0p8-~?AeT#1;7}+{VF%maYVvPL`s18b_+8{`g$!p( zT)4?KZu04d%?I*}^?5*XfRd?~z^w1ShX#;L*3vUv*=2`b3&HfLmlQz=e z?UiP)pwQBpw_EMKi7NSHxUG+})FaJWo)-@vwMw(%V=&{OVnOF9It%f!=S6dmY&9Dj z#(r!Zw?f*TchG=T9Ye_y+q2-2(ku|bRuQGblHp@a`a3BCvoM%uU6sk8;+0@rjd!#Bh z##n4rc`#X91|4-mo$)F5DUa(-a}#9H3Kixynb-V@S|-EAG>tl9Bp|;>=TOlAb_RDg z9B!NpLAxQs1jSn;ms-QAz7DnMt+57dEQY5^4NWwHXP`0UGECz(sas)IP~B{7++aOI z4Rcf4gQk#X)zPRoD}Ebx4Ol~DQ$yrZLpX^&pBlohzD1jgXov@qmNi7ElsqxpxWVMa z0>#|}JDUh&8*9fB*{bjsYp~Cy=YY(HTMOFDYC(kSaxcA81s&QiPuQdf_@Q+qd9(Vv z^-Q<<-$c|18PwILlRO6rJDB_j{9xXu)FIHdMeiZ5&)u zEkQQqs@)3;^3^C>tW4S)$INqJ`~YLTl0Ax$cq+>URN>g*JMSkdU%iu3Pt@Jp&Gd+` z%Uzg!SZKJe_@r}vI5_fp>>om)`nE2~PLeKSl4wrPS839NH&CVj(1a$4X8w_QV}lP) z(35-zRj_l+c=WdZsQfj3n7;;*)+fo zAaxv*`hrU)GB(TQp=*7_MT;FwAxgxR&z?nG+ekrpX5-Ir+pKe0aQiL4B@Vlh8u$E5 z(JZA?!0Gr6{SZI@tLG%9g@K5j_56r}6O6c2)uTVq&rvQk>1Gr zrGdX?4ZL3}*&t;%NTXLtmuvf_L7NmEm!7*`Plq&j#<6V|N0Jv@yHI@6f^NKnTT3k# zNN7MpP0^J1*>g3gk%nV^ci}jspnI(XBdykbxdxSr4gO3gF`rh4Fvsq5$wgT9yHF1l z&o$!b(<$8f7-_oZFR1M&tg)C+kp@Owttit0sqYZi0cm`_ItcO(&>9!H9C}P`fF~I) zifYieh+XXguIk;y2h<0YxL{@QkhAJ+jZ$^L3=}vFUvH5jJ#mk02f0Y5bh#=$zpxjr z1N{TP(PS`-@vdx4coEP9ur=zW7B9?OiMepNAwyQiyGoEren7e3Ra~eX>*m8emS{l+ zMKb|}Z1(6?KBc$I8++swXS9G$-4w>GPJc`cAGQ5~n%j==?==heI9d^WwOZaw4o}VsD?qT0+vn~^yTFu{zlg%NjT{tb&1Y9)G)z``5PD-EoN;%1=nkY>DY zxLDuJe4NtwkVcO_5MLkys(?U<9~2KLe`LocVusOA@0mc7GZ*46^O-O&seHd!&amdKDT*eeH9s zph67w)oZZ=A;UXWAw_2yuv&yBT10IkMUJ510*NK< zw_=feNy^@bbVFyebX$-PV(Jbc-C?BrUb_8T zDSNf%>Pm-0z$?h{AR4UsDY4UD(}>~A(g6=U0!4bria`@2=YKnDe#3UWY1I5JbnGqh z>a(iP5b5N>wN6U%ar1p)+)T8jBTKu5x{o~- z*&-P2kW-}ULR{g-9euEZfZG*5*K}+0VziI60IP0&wmv|~ukP!q3A&~aNt2$NNa?V? z_W-Uwr3G2t56Op{3#+$De)Mo}toPN_)4f5mP;%yfpU z95)CbK5>zCL8TJx7x z?P*dqeLRZ1ryB*5Vl_xXV?Zg^;sdVfc}3t?< zWSFTmhW^D>CsYXandK@cRxHxf4t)pEtte-H?+~p+t zP^TsTpk4)&qcf2eI1@?B)ELI79u+v2DTRAsQLNKRWBAlgXFUn_%G@b!J{(r>ZGh3z zk#c<>IP+yR6n7PfVRA~kJO&&Y`J=h@%pd9QOh3P`JF*AUptA4o6P5I*fe(e-u)vk7$(TLSgCM^QQZy4Lr#4#B0?Oe!w80_GDKt5Q5aV6b{jn^VOoW+i08zzUdIA%kk1NJE-Mk0&Kw}J%7A!+= zlBES$Rl-4AZ7W#xd422-YoW9t&-*EvVgQGtCv5WOEceB3O)Hc>ICyhx$VEjH&`MYX zDpcx=tXpAwZ8ya}xuFW|g*fk%*kdcSo(Q1OB|haq&5hCmY!^bJ1U^XkqA>P?n|OW7 zn#5k%>KY2ci;Bh4U;_;O`0&^ZQWxW}Sbr&?tgpEdRXvU5c*7d(oy#Ce zvEHXx>labi@y4WcSWsKw6Ln$xG3NwAiERh)d+agq8AyLq*mt z0qc(xI91(Xk*eAG$MLRW1S72Ui56SWX8P5JU%sAkuVPKk3q*C*lUJNe^@079qRG`$ z)9@~){gEvLd&sbEeMdJeDxCU0Y?chF2@VefPG*02LBmcxpV{bcxgZorVg?`<--J?gE*@E{uqJk)VYkss{<2GFGt0Sv32(XAH_-n22?6mziB3P&VOt z?31%7z0#i8X|uN1TqP|yQ~IDWyt7JaOsF<1`<3uo>nEw71nuEYCq z+-kV$joo>I4zZ%2b?57w*QEEt!~1-0)d$vLu^}TxMRAF7@!fk4b6p zv%?!<&jITWX3qbF*H9WT?xGj&_TWF6GL^>HQ>9x~IYql1-G;^HKI*t+!_*dr4YwSD z;b7?|8;z@NBrEFlsgaI@WK`VuUrP+T8Pe}mgzm;}7v4XBw-bh&vRXVc-Y>!X2YAZP z_l#p{`>c7X%P0*~jXsJk&qJZCWG;_y9!2^Ddq3s8>#c14sT|2*gk+qtgl>l_6+N1H zGoHY7`xSRGJi;03!GIIJ;JNc;nd(Q=&QC5D3eWTJTL1dJ@A~}E9_R1SUU_mmf= zF4WDJBOC2fT_Z(B+=R9T9SxOt+=8XYkGdX4RuXPB-3%(|LN+#5q3KbBp|7LAYokA-4(_qgD=w=7 zN}^-H$G^i^QM*(qtG>>@%cgHN#D-RkMf&>8iD0B3HNFHHDVg_zW-GJvpcxeCqkC|{ z;=~qM4cyl-dW?7ID6x_mFqr88k-CIfl*}s(a5X?Ak@O^fBvIegr5yV$lX9yr8T#CMjcdDGLZvGS}0ELWB1xzu4d_P-68;UCO_VXYlG&mas0R z7fF$bh0?vwp6N2E>q6!P@UcZ-8{3~yf2ad*Ec3TCwkw%qn3T-l>(q^)G>X8=YSqQW z2fYwObZ9@uH@NzCYS-!$I`?MKA|F!>$0kmNo;t%1>jIALaswCn8)){kSe?G~hOw@J@_4X&BH zEtbxU7A>F)R{O z4sAB+)wp*iR`O1>cxN7C8N7-$uh5ryP03GdW3$)orE*vwdvWIRe7nUGd^R@w_YAsD zdhP^O;G-QV)YeNZr^wPD)=IC`Rkd5s-W2}fM0lx~ZGqANN@;;p68} z5spz2Xh*-R0XQ&cSuecK{asyE!QadvwuKSt0dppr?$E}ZDwwP=iZ!5BTV5M<( z+#5cIZ0lH4^=xEswi)|ai!o)eXuYW}x;|&_qKYEL+0v_ZsJ_H1VewXNdm5!+)teR1 zYtjzu@jP0XG6ug@dhS6KEU$?bOIy#ML;JtOnWnJOKKGu(+J{<)UU`Mg>F?ql^l`{4 zDCjFTo26>EO3yw7Ni&bLbdkH+J*7b7;yyOJMLj`gBt1(nk}t+|1=d_a&#SKmpk0Pv zq)WzTlz}~;ow%Suv$7(vDzomfq?RUgyQVx5jtvjY?{U5u7 zRJ|B%$<^KTcs@$S4NzC+^Wf<=0Kp%1NRii2+s@Ze;W(7la`YA_jtT@P%I?)g$}$*t zz@3m@N!S{KeZ+0yS~>i5t|ho9;9fe_>s1coXweuM+oldWS^)FJZeOOHUWoTl%lil1 zKTQ3VjMI71tLH>#%sHmg_dO!<(AUI>hf{tS$4bfHn1p2^4$h0~R|u7=$;o2Xnq&-% z26HopMTg|7y_l5X?K+y3JG1@XGtg|a;xpUd!a00+n4S$Y+t2UGZRdaRRqB zgif8g(_%$`Y`4Nsp~ymUbvvfA;kB6zlurterw+q1zdCnVbVlwmHHN4z5)W?B%JB#% zz%WFj&ur@I&*X$Zs|2XNvlxjnhZ<8)6B)J|iu1(zlmJ%`zshv2_f&Aw!LPaeTFS5G zbd~ygZ20fQ|6)GyU?gj!ou2gYDOTo3S^Lua_}JCM*8QmP6;`QwEP1c!ExFXfBjo5< zG<8$ZOK&mh8i)?Dl8}ql%NJ6G$2eJ2y1%04E6VN8723!jPD#koCvED{e^ZjXim@md z$B7?a@IqD*sc#_)T!`#8g+_AyM(-+NZTkW$1OcV$$4tPJ9`zr%8He~8_MrU<_skyD z#q=0En!mvno7BN20p$nmL^}e}36M*NYahp~gU;K53Sp6Ivs$jRhSs56lv(Ja`8mnVGGuBufxoN0$&Etso!_@$`d^Q_G3~Px5&wQxo_4}Uk4n}5 zA`0jFVR?1v#4St(;1G2<(_%FfX zV*W<@xd&x44my#BNIP^cBM_bJgmQ{-yttbFUWcw9DZ&=bVj0lUEp*KEnSgs5bo4Ug zP;rjqubqub{!5vjD_ilx$#e&1t{JXEI*KS6Acy^N1nQ0;TSR^{6+ZSE=W)uBBVdb%MokfAnA3&8pD) z?qd}aM^H>s@*3JB8%%u(C$?xZGYpmxP^Ri;F_Oh59$I=FmWH}RDe2NeL{^}?ITEIu;G=$a+XOxx49sqGFMKUgtX=T_)$178c_1bbA?9dl>`< zl)8YTCGYO?kG|N878gc8EH-wePbnZ)91Wd8?n0VH#Vr3A4|&!;J$)Sm8u^`qM|4c@yYHzUPOTG<0Or&ZL!XZ zLjyQ17s}J#hfAaSm(8rR;833o{l_!PX0}^0WT_xQ8}#T7ft-ZA_K57(rrM;1u#zT> z&+adz@U=+nr#BJwEHIP>9}OtF_~aPTe|8zt$qCTl zs_snKxar4kTrQmBH_-bMG(?B`F||Hk@(eEld$k`a) z>cnA11w&PMDULwnolthJy_Q2BTw4K$_G;50rmI?5En;Ue(Db`Q`5OJzAnvad-td-I zZ`F~O;Pv5_7QeDKuW3je$3I5IM)$%7^jbeUi$7B+TS`2|S-e?fElW95SW@kShq#R`KrB;v_y4F5)N?uWOc9PKT^0#=Tuu5z4? zay04XSmk$boO-2Rj!X4KC`Wr|IShHMJS)ZWd=)@YBDRNA{RqmUO(8wi2HZG3znlzF z8-vyWjTaWyF?u;>P|zKVCdua-O?NpGhQI7ktr$GCo?s$7WsKN3pA9;>t}B22d?i0p zi4P5hci~OGjv%_jVOq-Ds*d|RCir;!EYuI9^z1m|0r3^oW& zR$3jl`EUfGx19JfLv_q2)v~wr7Z;*qR@X!Sm@#$)D+AF22UcjPlU3)T?Lph=C8K+& zJD_&ksNWVOi%)L-h&Re+CaC{a^8R;(^}l+5cp3U%eITb^UW-;fO!jXpt6oX{?}Rt} zEh6s%xy&m~T0_6SreDkH*Y|YfTUJKFT(Qf4AKE2urPJRCvNEK;8NFb|vi1`-jM1KY z1x~OEw2+t3DQUdV9#i!4Z?ubefZnq~U`FHz8m>O2Oy^#NTG*)hqE7fL4O#`cH!JjMs2z0rR-ODe zv^#L<4WGV%L7Vw^2^RR zg2o-V7}Bp>8;n-a?U^P7;THJ&HpAaHcKQ1>`1?kD@(|-rgWs(R{+<%S-?#l2{(iYW z{>ZV(g8qf^XJuYT@>&!|udhC&>Eob2{+P!>y*wBPHwfkV**Iv(`}z2@ntG2g{=gFp z;}85LmQgU?#EtO>ld3Nmt&cxsJyNwW{#4VDgH_2KG(}l0luRLZH7)lgzoY#;ef8xU z)^E{iuaOg5Gx2hrog#?73O3T{JsfvKFpK#|9q=d>c;TQSv`-KF^m!(G&N|FOHgeno zhp;kqn!%#$FAyE;7~{p~WUOmP6;VNc!+7tp``s(0SC$r`@R9xGx0PnZcGzEqh*AHk zzje6+mVepI6_)HkteB5KCoCC$si2OX^;hbOayHUeliaIl{K+kYKWGfN*Gl0_(4FO2 zQHNdzS`wIIL|9jK<#7QCtLB!vj#b*ah@|c(5DPTJJq3`Rs}CVdC|BZ<(wW|(SoZ&d$~hI zuvrDQcdmB^lyB(cOL(1RW$0QhtFu2UOT$03Rvm2#-oS>J19^vr#68li6?hx(Jh-?6 zep*Y-`3lRnQ(e-69*6>eMM>JKj<$v{e6PqzZq)50ydpih2KMLPAcblD*B=|%rGF{1 z2i8hu*o`uv`*psLsVom~$2fYtB{-B7;Gj@|%TWNF#iatQlilm4UZfYGKc&IodAv20 zU?tc9d&Nz9vXSz~%p6OIUHX@*XCQAaj9=g@a&HJI8(^8*12mS`BD}>LP(-hzZUz0o zhDYHMTZopb|3+!AXK8hP>KuQg?)EL^O9RSl85p084=-Z5TMzTXSenB36EG3=(x;Qw z1!BHl9W+&@Al!#@KbQ*-{p3M<{0&a|_b!S}#z=a+2rEBq_xwrA5}bsxs6GEoBkgn# z`_K!b!5EHoQP_6@;WVLJO6`285TdKUfaTM%?GEopr~`^N1J44{aR>sjx(P?}6N(dv zjcX#lRh09U zf9+Af`zYCeFBbm=+?&1O4dm=~|A6VNjHnI)-&#mO!M9|{)&`&AQMYThu0qB|Cvsjt5pZuh(R)|yI2E_WKjoYpm5^*Z#duu!81q=U^tN_j|hKsvo6?T(_ zc$8#O9ErR1WPO)EAewD{aQ_8cb>Q4T8(M0ZEswNAB^ZgM2z|0RKygMBDPU#jdd&(% zXdY(2^)!AM_S>P`FVz@R%c7iBUE{}fWwgGJ@gw}bMO%c8E$-KrIl9G|?A0)u7ogWL z(_0z3QB!CijPYYA?2KI9!zQ-p(#P;V{DSxci+82ixB_m!RqEV>2v zdU`$7`4>~um%JR%6V_w=ZWzY*u#Z-46DkXT2SZ&&5W0Z^@5x&KS@K$o>A4Y)!RbLy zyqCMR2aNjRoNi}%3&LAb-iExDRqZs584(LuQQTG3*=eM8w@=NJ7W9oY1qbS>^iqDx zCKu~@d(r$NxvHx@1McHSZizZSV0ij-!5xVcLh`K%Ppc{VD&n%2&Wa#j~%hZXB7 zn2`ptolgWD)Cvn3?jxN zQiGx00RpG~yKGvx#rhj9US!JQA-yozUry<;ra^D7Z^!CN@**4v%B!T|eh;k%sF%Ho zd3hmDVn6Jp_2!${YA;?f9UdJtH=wkz{o>f5Gxin~H3PmX!9|D8bX*95PE^1M;2vTEq#k3W8A z=yS$ceI+GXLWo!SLSPTE@j_98S`FQiU@r0psfksDx z+#$zaU?uY_DpvnH>^NOW1yyixW@blUWQvtWn5j}Qgw7b{1fj5< zK6rqQSfxFAzK{#%6RbBU&@QC41*0=9$SkONYw*q?4#B~fVtv{X$XQdQ$f{qdY^84x zhWmqAS&tDWxt1*G+S$xD)GKhMH!Ci|wR3)k_p!+Lbj5dUpTMiE@G9%HFN|@TP6gYF zq7yL(M})ir=ZTnyOD2qQ7fxh}Ef-$k0tmyZz{hm;MYC#>*fyEuM$_}r) zO?t_Tf_P(D`E(%rd~4&{HhB)dlpgLRz3%-}zVf-hk;1e9;EQ>z$$IhwBV!r21+a?{ z^DT#;vDM%^Ao%D|VFaJI>(P@e4dD`5+7Wme;`b@_$$mn4@VWX9sb(kYB2TENPX^(` zqJ{Z0mfL4+54Xdu(9V%O0blOS@UT3R=L(5weil|8jPVCBDr;el_`9~tZIM}XW2Q_N}*Wd3h%kw4z|>V^L_D&U;-PKUHWZj<`T zY9KlSlRdg0=BU9fUI+RbHC4`p#e1vv;!w?g#BkJ=ncW_IMYo{cxRM2J#}$^qtX4nA zr2P}&URiDI7QE`F(u-;xaB--af32ns0!2!AVS2m1d}o`73^rgCB#-P~hxfhkfP`Ut z2^NhPu`nyU`c)=g$#_*8j>md`NBmJ=Fa$2z@|gp_)c$ae#+M%G?`O%h_kh1|0V9B$ zEVr%j*l`>QFLo1Jr0k{RR;ZDOy>Z}IzVq|*I*~uXq%p8K{WuHa4?kIqRFsRQz5osp zkLnAYbj5elz(_088Ghw%XHoPLZ}`dlbezMVOy8ix!La-3Te30a?>V#_{`6F0k504I z7uaYvT@VXg-JzVy#%yatRCdtU74W{r$lfTGUG0PFFg0LH=1`>#U0m(;Dhyc`nhd@l`@ghWS5k_@obHKVCU6nTNl9jYygDMO!@rIwuPxmROE}~40!q*qj=dvuJ z%teraIr5|iY|mmJq@hguW~5E6MNW9pt`aB5>al6VMrt)mzJu+TV)M_h+ym*DVef{! z-R;yzRyVTnMkRa}Eti}at6FSGHm2Eu=qbMFgSNt0U@!u2sJFt7Bd9)iDZW2G=_Uuh zJ5%TDxmI>hvy=Pu*p7tylu1t|+_FQ8jD%Ew^zIV2{CF`8=rd|U`ilV=U=SjJ5ix|? zAH~CAnr*b6fKxS-fYE#W=;ceLzNL1ynM3`039s=Ae9hdaT*UQ{1SJTD^7XF(eN$jV zYp`~xhFEF`jGRV0JTnWSlUjr$EB?f`OdNo)hg)FlKVl^skrsA`DW+lbLKR~(wNE}K zL`BMu0LpCFox8SyO{-KOw*6>k}zwT+pRlFnL)UW=yh6`_5MHrj#{B1>Vk5Pz&;EWRQz&8A)sGw?=3y@^Y`0GZ;wQ&bvBa7x6V zj-4s!Os#pDap8M};5v#*qaxXic&e73Osl9q^i)p<^kJ;G$o}Y94+crR_2&R?SF6Fu zZazo*k|Lb+8L4`;4{KR`L7!5~_y+s?5Ww&0OFv(rO!$O96gHmXxp8f@WqimV8-Z{Q z12+5MY!RIq!e?p20oQ2LYthr^|a60^9( za`FbHNRGukbh$!hxDDDJAFZ^ZCBq$=`Z&j?F8B&2{c$lU!i&iTg167yzK?7yu;Z=G z^q~JCCJ!Qr2<;Smtv!6!I5Mzl+d+mqdIV1e#*V?$d^Bfx2LFRUqyr0!+tDFbXj8EO zQC5q_DN@*blDXPJSh-)ht62LTESSet-th1Yt9AvR=;I&jWa`B*7ifAn>rUl%Z6p%t z&((3LIL0s-OFjEmbElk}GOUG)#Qx|WpEuail-fBz3B&wpQM3d4R}Wlim_PBnFw939 z58^4U^1cT%(}Gl{T6h|5DDJpOitV-f1H^~G{)PS2tQZr*Ehm!teh)?JYa#sNaiu05d_N_`6XQ+Xc4Rl610>wmQ1L68*J-b3(pR4O)gD(0|E z@I`=0|H3h`@GeZK(QM5aTIA?G#k9yu`$G*x(~|DzxPdPhLM)7@(;X`1Cr8IQmAK!E z@2YGHX#w{G<!ulp@M#Rz6Irgb~!~evkft- zB+k~MC^47_;wJ=vZw7EQ8IvdKgGI57S8?jRRFL~W-tu;j;%#s0sPyf-M3z$A621K= zrTC{PW;vL=`lDHWmC9|=$6J+3HTw8ZO69KT<8881+=iJ1j;4g-ML6Z*^jp7UXX>t? zs4o1Z1KKMrg&yHDna8ce&0;cdy!&oN*tNp1;PYUUmM#WVMVs&-wwC#%Ne9ZLinwL5 z1;IA%wpq#wrHVs1JSVlUD2n+q;UbWml#_u^y-b1x@Umy*BrxF9`T6M0S+w*z6gkFj zMWZ}v3*&|QXE$OnTHJwvQo$GfCPN6eyD7tDVrLpU$|n48V()*T))|!RZYKH1vVb&c z(_)%S%hq-xzG5$afTMj6k*gQOpsARTY$9?g-8Ip#mGmoKM&DD&MUK5u;>pW^P5uEl zkD#=;Jp#4csRD=Ko4&;VJ@~%@K-obs9WRqMt-uw3kcH^`PLxRx?ZLyz2T<@$J8-wT z9V`cNlRO4!@>YU&;;yWvOiDu5P?GK7VmY@W2|g^dxq;a5dBh|N{Wa2l{T+Suvf7U3hmfVD6HV>9#8Xz$m;N1m3RU4|z6n6ku?Ov+KpYr-A! zlU~EdZ5{p$WFtOvbu&7}3Is;@j`Tdt8?Zl%4Ub%77L2XMC+(KtE|_<42h7-NAv1q1mV*ms4yhr1Jl4a@la!R{D|NslfJQruvQoXN0iibXh4x z-0(oT_9(218qDUeFN8kPr_rLq*%Nw^o_)fX+?Un|g0a3r>$TCFh6wxB-P(tqUyrj4 z#gx^RWwlYv$yUkkFQ?eji}5vqZ>F4vZwoo`FtnYmo8WJa;6=cl8SqMvVIQG${{xF= z)=S{NGP=DnTUJ|7{>8!%+0x*&6B8BhSijQIIdb95E_>@B8^@Os8~0#4Ebk&6Xm*$m zpHQ#MJ4D-%@zAx&oBj`bZvq}wk@bz&?c3eyRXyI#svs?aq1n^LuA?&CgHq{^|ks{O`c9j`R;vaai2^ z{1n@5gw6t-uVWJ-3i}QA)wMqL3L4|}1a}W~mVh-}mDXctieG#LWo3^WR$hO@&nJiM zkzXM?P`I)NM#FymG^tA8+aHEATnt~r$@nF-p3YB@t6|tu3!$%c?psFNmtAr(+=z+= zEAhtv%9!@UR}W8?yR>1rTWdZ0s);&Ou zXUSjjLGV4knUI1fVDdda^#sg%k&hl1$`|?e)~Bi`V6Ca1dVxuW)uBlV|;t$4ElCy@sBPxzMH zjfPzIU(#067bVcSkLpgH`WH#VaUhMb{2!k3Cl`j{fqCJQg$W{Z>3fL4g}9%h)w&H9 z*ws1M^UQY9k9IF>3tR?JT1jXZdj|0dF?A74rWWk8MEJMbD9F`XuG_SOkii(MYi$#j zY^>P>v`lMU^RFtwfrNj$JjYs z`z}?+N^$6CWR4IAN=|@7M~g_#k(s{RKHibC#T2I4k8vwd$XfYj9KszR`-i2AqmT-i3qm zy;z0Mt)8d#d8>Zvf3F_adsJ2H{he~X|2wAie_HQ%QM@AWzwlW)Jug0WH*R2p{)?{_ zA$~X-L-IPOQ%sTmT)3hU-<+lYeFGF5gYA}U=Tner;o9`hL9=V)9v*utc3S1xYWG04 zkuEL0w&jKaKO_scR{I5P9C*(_N4OZ`|M6viKQH?&cXVpmm!tbKX8DQ_)H%W8{x8tF zKYJvGZJ4%;;@*QOX0qbJk}tovBR6Hc@B-SB8zjq}R8wvVXF23<5Ql@O=)d{lI9Js; z{V-lByb=_(>KDi|tojea%OBw#bOJRM_z_lJw< zu{`!xZlL&0-h`8zuBZF11+?tEa`LwM7D9(Xu9IUHA)wK6^$v!H{zc&FSUNX^f8Ye| zB}Bb9B9}gmqLn>7D~@R2;oo8{_$NXve#pi}PbAK)Z1B3?K1b(w3tU)yUIr`vCu`)I z&S-tb8CoRimpNK4lx<#=Ul$%)h$Sv(Wn7Lv9vh1Nb+M=W(0%L5@%9`&PS)wOzutf0 zD*MR`7@3!-Dv8895pOE-7r!l{?wf|MoP|d>xOb;x{hTEW`)du+g)0a9PGkQ^_f?Ye zRU!Z8_30hf`b2$L)t?rC)uZrO`SbeWSUd5*>G1XzR;R_ur~NHg&E~jh)u;V+6Pl!R z{gtpJEsmepogy;6ppL__e00-!>M-(B54x$tS06vS@tryURO>X}9bIzTcj;Ej*OH+7 zP?^@~9IVl&Wh^qX>WW36UuSySFnYy_EZ$h;W z^5grJ=irCfP3F?8%>_&T9ox{UaAp6)@a)E8`2Cr8z>j~_h%aKw@|E~`?UQFwgE$+p zb36b0nuM$WWRxTS5B-JxUD6ag+Y715kh+4NUY$eNFy0`Af`tDC1^sWa^k0ARf6&my>H-d)9g&ylLU-zdVs6Culg{e}>2q;Eq@IhLNc?Q< zdmW1h2fNVhXP{Sr9vrVFUq@m8-K5hXeHYmpPj&H|?U212QhxvZZh_w|@Vfm|9U@5h&~HORX1k+li`*>WZqyDp3|FFKMi75LG2}BpzrezrMPD zZo6uw#nie$Nkbs5qp!TAqB2m{RRrchQbVB3SKm-qQ9aEE*`;$E0`-c$KF~0^Auy*Q zSfQC*SyDZ%v1D4aEK%1`DJmQ4L_V}evYF~9@RVhYTj8x*wYv&D1 zD=+WZw=Pg!7N`>y6`~d$Evl%jteNdAtSXrX(}@wX-GLPiRVB5)s*3u0vS&FAD*GWC ztLqzUYisJr*rkf8wqj18(pObe77*naj_Q&sSgCqyO&N(Rhc1)VSSS-sMn5X517dQ8 zYOuUPpm)S3%1f#$D(8y&hPjmiQBpq@orEVSI6Y7?ZF+;4Gr6`lP$lLnpiE2kXO+}Z zyY-wenS(5boynY))O>2Gx`zF6YDsle;GvC*s@lo`4J0gC z4u5W}3s|i*l$4?wZnCDl96j;Pu4tH!F;zv@xTpp(ZPYN;)}bSH4ReFdNdx&RFx+#3 zw5gb@C73LAJ`7QFXW+e+G%i->rq)!|*3?%t1bE=7CsifYbA8aFp+3l_246a@RddYv z!M?JZsnSVKp<=-GzmSD1nkHiyE|oQn6kH35YL&YKo@y0;crUl>4dzRW)^U zG14?MG04gUqNU3z;XxyDTlIm!jEiV9DrzsJL9wz6Y2*MU4HVR^;V7@e#2^o6dUZf* zqsS#wrv~bqM^h1{+`1C5)%oD3)fEjD@b`IvG6XeAoLW;?*I0`_S%ez6d^!1f&>VGv zY1HdF^m{6qx4Dg)TIqMhSss|}vt|V5YeUJ@8NQ$>;%dg^s=<*@lvT{CAX6jA`{o7e zYJz>O3zVZt3e)8^jn!qovOsAgRs+={cRV*Qe@w7~Y@`m000s8I)JC!#<^?9@oCcn} z2x7rrl{Z#Tl`L4Gq+MvZ$K;O~i!gzREGMTGGzA5u3rN*+mk}*!Ih+v?G$W_i)Xb=d8_x>J zilqTWGsG34jN_}H3vK3bRJ6tf^=U|8Ngm#cY1K6dQwX2qsw<@J)MS%gl_nR8Ko%>l zPzwhI3|b1Nj2W9VdTjp45o0?*?*MwhaZ60OBymjPguIJ52Vp-l<|3+;Ohp%!*NpV8-Nn?(ifK@6T8WrW zxiY%IZ5qUIno5PazSMb*v?!6|NC(E&DNGC+e$`M>FlM|MmV>{6VpxeN$`PXn50sZ; z%9V(#29*w~!$ZuBUh*ynVYww zyrXJyOIlV@hn*L#Yt{`z09#2c?HzEd)8R5-2i)y+pgX1xxaGm|yu8mtaGG3OQa3qR zx%pm2mY0;3O_p1G-nljxH`ljr{U+1KEy&Q-QVmVevfz_TZLpooeAX=)sU%lbZlc}_ zUClQjv?r#`mhAM*DkZt3uC8RRuLL(WQ{_g;C-;aH)hn^C(eA!k0Pl}+ClcJ~Tl+J0 zljOsuyI$>ptviA0z%v;bx!^4y4*H>y z+)!6kgVuQQzJd+aVy`eA8$JwN+H_<(Ua++gR)6b#X_URFym{YGTi~>*xL(9VO}1!{ z_aV@Q! zj#Q7G7;lU(!Pl*^zOEaMOl3FtDImF^gcy`Pl!aMWTaL|eUSPh(N_*bk;0G+q;RANBsWqdk_RaYDF!JP zDGn(fDFG=F$wW#*(vtLkhoM+sfp0cm-PCjr`QoysCLa`@1K0y_JzxRgR=|mXuL9Nr zo&;P9=n6D7tp`j4q~DJ$0Ne#Q2k)HZ|=8Oat5x*cj8fT+zRM%fsUWtDgsQ{3O>Nz_-VC+fLk9$zwsr><@i~( zwSZdzw*Vg8*3@*A=zoE}c#xj3y{V}Zu;A&YruP8X1D*tY<~g*FZ}6Ubv8ib*V8Kh! z7jQb@e!zooH#POfx9iS*06zjuI0!z#Iew;q9=B4FQRC${u;7-8afb?(n z4g&h{W$|->JpkAL6a4^u6>vQuePR?4+Pu+1o8!@v!b4nZF~;pdPeyv?DvT9uBHW&Q zcfycJW43FN7-076)V+OMs_H??MM^JiYHCFUe27+z)c*onC1?epK{fdyzC}p6pjmnl z?RKQ13us$Gn+O`|L?7bYi8LLw6e8e5wEaj8E%*|Ee}c3Kw2pux$L(2So9f0eigd!! z*V|Ec4t^*Gx;?9GIqrmIdXC$-#GdO;pC0D+4RR--(m=Os2*xH2Y&}qCN&r4b^`*Qq zZr`1ECwl9%gVr0MHP z9%vWH%dzAM$Z4o)YT9EzPrtnL^<#OPG<|~YlJW{jUJ)h8`yTW53e4LJ?K#-J&2Agz ze#So5z002Oeidmy(m|x7_F@$39igo+*RnSWErXaXka-mQ?XPIinr$d`%p)7hKC=xM zX|`uCX~F_SPa)+mHtt}wvmBzsq4V^GO-*;f#?;Ol8mrYbR;%nI+-b{btd?M`&_tno zxt8xXHRhI|1bM{&t{H4$RBqK*C=o0`UxfuP0a0O)W#BLsj41yuR#Xf zhMEXc@^|mq_r{wA#JtS0%D!4(W0Pxu3+rNktdCU?Pkpsbce}_|KG1Im-LhYQ(0YKj z7PPsn3&%|KcZuD$Eo`?tA$Jd=);@}uw)?HwoQsxK%xysZ{eQq7kJ=hSz71a_-}c#R zf-Vd*7rR}%_ke}s%2w1_g>~D@ZML*A`mbz!-$nME3d*1s7S1)MkkEZ^(E9W*UX7nG>?h2Tl1KVyrB9ZLr)+}whW_)2|U&pxVyCBIkZ#xSX0wb(i5K^ zNa3`X1FaRc9U#9POn#{+K{T!H7@KJ-vwKegl`OLf>Sr$Mt;KK7Ux7JFI$yLV%QfDz zpPoyr(6HTD3-(}<-v`Hf1JnIY49Qz=*Kk8GLvy|JFIuuOA-1v~yj9>XS`xC%xyrHH zzDB==hK>AUAM_}|o>RtnrqO5gV!DFgj`(igNU{O5)Lpzjd(8BM?5e#s$61Y2*j zG#UiPmMYQwnn-)zSDKnyW1h9}g$w2Y)-TJ zOZFeM>7b24Ieo52Itto$(7Mq6>q;7%yx{(;dGEmcn<;uejm@BoY(LP@&rr$5qmpCj z3$ zkWG$)HWsvarqQ1Cd(ftUb{o;)udA`0!FmPbFT)aySc-1i#$h_0A30HU9)UuV9E)5m z4%;BlNAmEYHi|*p3EBoC{8k%RU(iNAc0<3>2HpRJzmIbSl7|oVjqZigKm+*w^ScFp zx4{3!7T87$3O=FpV&F)a8})$7doOom+g5g+(6Bu5@u$196h8YZJFs!x8 zS>IZQa~byL{xvdRCBu0P7c#tw;aY}wGu+JZj|~6Ba67~28SY~E9=Cg#;U^4V;qtu< z->~=@e$4O$!><{hW%vt2jr)_$xSjDZhLH?o874Dq%djKEZVY=c?8`8RVFAMt497CO znqeu!=?rTaHZr`P;Ub1NGQ5f5YKFHlyqn<$hMO4vk>TSEw=;Z>;foCSFx(~`%t4QgU&u~1$ za)z@QE@OBn!-p8|V7Q0j0fr|So@MA9rP_&O=x5lAVLrq049gkLVz`XqoeUphxP##y zh6fm)V0f0Hb2PWl(9f_J!+eJ08J07g#c&zJI~hL2a0kOZ3=c3o!SF0YCm$xnG4wO+ z#Sm|w%FlR)8YhY0w@G4wO+#W0`Yc!uQ+XE9vH z@J@ygG2Fp$55oftPcS^o(20LrFWYD6XV{BjKEv@0%NfpMxQyYQ3?E{+gW(>A2N<4U zc$T4aJh#u#&#)K6e1_v0mNT5ia2dlp89u~t2g5xK4=_Bz@GL{;mE1lH1%LXdf>lhP#c(l0tDXff9W|`dW_ZhQ z)Zf)!N!Y}&xyJum&c2fvN>xrruK%6SX0CtKx*zTpD;`B1*(|~{cX!JMc*~& z(&an3e9F8_mw&|akJ5oPK6V9y*Ara6YnmcmSpJPwpB|p!bD@4eTjlgZ%_Yhe^|!AV z%Ck5_c%EA4looy4|K{)=rf;jdb~iNHH=$l@LL!URmI}ZjE6CP@+*}w7~jX`;fx<*+{5@6 zj7KppUv5D@nsL|HD&u85jqw=9<3CZESjIar9>@3q#^V_u!FU4WC5-zRpT&3^##b@k zmhlG|Z^!tvjJId}4aQR$KgPJ9@qaVkf$`{1RmPfM-55{j@;t^f7$47gHsjYap2PSY z#(BFbmNU-VKe2&v-j0bU80YPg*v&X^SHuU5^Y%fUVcc5YT_;q-)^e20_!QQs8{;L6 z=Q3W(_&CO=GG4&rTg!NW%NH@ei1EdY-@^C}jNi}r62`YNzLfD77{8J6*BM{N_+iFx zV*D${moq+{^;^NX_L<7u%yar-1CjfY-juxE`N&gLyYfW{0GLLX57u^^3O2dmhop9PiOhhF<#6#qd_ou z(4fA)w810Bb@XNQ=$hFz!m_-LhynL!3@^pg|DfW@Wq2tbtBq&>i?%E|7QB=ktIUFz;<0p1`Y&|0 zbS-3^FGCk>vKu53e#(08>n+iXE{cD~P0F%(-(a0$2`C}I!Jmm~t>g=yfOieNh*c7l z{JYOu{F@FaKKgCUOMRw66T2*MX^tp7OMa=f5d9)9-4UDJz8VXv-r6sO^FVwy>e*Z& zpLBtUX>CC&)DkHEJM0d884^W~NXg-#Mcrl#S)KqM*9|~IYKfvbCnX_IJ58JB8jHNm z9&!rOP%<8v{s}6Eq{c%Iw~jy&r^|rdoUTr| zhB{qe64wbR6_((oK%^%_gRsPy=P7?XS`9N}-bFs75d8YsPI$$9N#b`@*g6jRbukk_ zx4G=s!s2=v1~fc8`9_e!C_g=-)a8N~7=MD9BBsV{jzvYo-2q5hB-ty?co_o}5s*l_ zv1SyIayN0kC^5;<`yE`h(pbY>9LOk6c~GY0IBe_3dK7}#x|5kTx*1*xnkqn zWFRxb3sEvf=r_ecorpFu&>)QRo`@l?{-jPD6Kq36$+E(@7oCh4W*kA@CG=rXF5I0= z@3@Ci-skS+x|!-+iHrO4Xwq63cjG-|ckie|sN*um5z;4$qQq-kEDguw@V}4;YWdZ!trf<5g94j5{yYb zP{$(`^$C3xuy9ZEHzb7^H>WoDOE&L#`?y5#2(l^BOE}d%;@*1O{7-?QD2V@?4a=FH03- zljgbA<#&Rw1H8&}n`5P{^dcP5b36B>0E6YZLn`7kdbS~?HEES*R6>mBZqGCfV7l>w z7sx%5t+&w%t$H>@-H4KGBPjyN#;9k36c~r#T%LPlcY~qGc%w6r``q86q}T|>*4Fbt z+-oSAVw}ay^=wIa3&?cievFyt!T9%q)Ed{2fQQ{ps58fShy*+mbrf1JGG2sXJ&#Ih z%Z+8-fIJrc7}(Yt>9~6S6#Xf{dgB0G)w3<;5TtD~lBwh`-fWm^t8tdP{gmwXHe*#s zAkRjV-#%kxb_KFCg8X)uQ4e?U?22jwi@j>>CEa$*Ztpklrjk8T!%=e3_>fBWMo$28 z)R+rT^ZY%U=Eq5+mMr>u^k+cM7*+5z&%Wp{ft-t^Z#{V4l%plI$UH;<&s&i%Vtia$ zWH0K`{^)5mP+DXhnc?l|24D$VE_nlcRkk zu_;>Q%I?4pNnf9?MJ{vzJ1om;waD+uVLp~VGDnMSO%vs4^ct9Zkrw%ncEFCyvgKN& zi^~2Pa}1iT)gmc)cush40_S>7c#5K8Ldg!{rH$nxDps~7!tq-VA}UTc4N29Z&woIO5cKN^inl1O{w2SSn!imPeHTZ4eKiuya6NjHX~Pf@KU z(%X0pT<1Mx`>3J{uU8i$Z-P8ZDz{Ud6ujZTwFpW7Dsc9AD#=~uYQxD$dw zj;0#XGB*~&QJKCFqmRLeN457Pg2U-b!PQH%xS3hRxNO@Uqd<$@1z)p0<)t=bZ$dk^ zr(=jN;wbjhbdS#lra=xx15bkO zQ>mGaMTTM$*t%ag_$^YVivDF*v2IhZfgQa^w&PS%BVdWf^~u0!L3Rc9P_n zN*tufY>OkB_=P>Z9oR0{+)E|f&BR6n@xXa(EV6qC@b=N%+a&J}i}$S{uN+L(-SKU} z+fR!;EP0Pxyl2nj74}9?GMJj7c_{ zuZm5|RlEZ6#+I#l#!KFbxOyovN2}6QWNkT`r$Ls^qf&~_+(R3-2eyG)?5(nNJ+9Jd z6g^ctG(&{FUkum=X|Xiq_Rg3Gwp=Z8kE}!QxJun9$Trtu`7e?M^R!5^p#5%YEMJQy z*RqcyWUv++j%nj`MbqRd#{f}q<+=|c*5D_UWAP&j;OIddWNHessvT*4Ig9K|5oT?L zEc$7++nBd6?8A*I7uI( z$I(Phbl9nXw&}W?z7u19i*XQ1?K%sw(A?RPX1SgI-J-2h*HclR?9)8`LNAZN1h1#i zob91$D~zW>@#ILvWwfVYKTskbV-0F~21z8rxWfY^S0X;+a$0BdxSl5O0NQScP1b;Q_!IqexDONtB#6P}?`bg}UZEoj3eGR3$R z`g?{;WV-Px;+bcJL~4zhwAPH2$Q+}VHVH*i+9G2-2^b|K@p9v4Y=u3eWyxCO1(?P& zR@PZ>jIo`%eU&WPW!y%hCrIQ~ zqd%#0wM6zCf5S+5CQ9U>@h93?Op?e^<8UV+lO=M}D5HJB6p5TMV&QzA5{aCPe3JT8 zDiJLBuOU`?%48p0TI63z)pA+p(IV&2ay(692^x0(h+m%R`dI2gk>}dbw=e+4AyRZk z1jS2XJkw>G2}LnQ0FTeJDD)=a z#x1lHT`a9AjQ7b-Hz<=Dd6;~jB@)3Ffsm!{1XRK1{|6ep8}&k99*^gy(5Fe#VB7?H zmd6~1cwvktjui9z{ zqIbn)k1mgvH)(##Pm8_+?yt9|{PZaLKM)?fUgnqyPiZv;?djF>X3}nJEm}D+JM|kC zt|y6BUt^T?n-#8^qSa6`-WtXeMXNh8aQa%t$*a@!9-&V_vfK&iB@VR{=nnVNOL_NH z0zQ3eBn9FGV=Mefzda-#ReVDK8KbA)p_6K!-ugYEUy!7ZFr~i1OHzf=l_YJHlG2RV zvw{3UZm`mYKF0^_KK*&{wRY)S9GU1#3V!WO=nqEHG8Owcwe?Um(PRIN;nx2c-J9|| zD8Ds23wapSKz*l+z9!~$eTSeFpA)!-u97H;6n4e z0iCYFBxy1%<4x5`ockScZPUCrdHN$#8?;D!m&+}uFrrcJy;&l5V+NY_u9S$&$j8d* zT_urlV?4F9S|T3fI$~QRkr-nkA-8z5(Po0N3Nz8WR+jjT`S4KhI*GJ5TEj2Aw@M_< zh;jqDO(K^W$Kkr(+a;22lw+v9cSxj%L0`i5-YF4;bP{lvn|hdSL}501*UORuBNXAx zd$%kpGP=TM-g_icY=jZAK_XL(60A(#jk3;kgZ?Y2_ujbIAg$JzMkRlc$RguGQs+L2 ztTjS0Sl&$%*<`fu2IPJzV4KmA?6z4V+l}u?>jxzAjA6$V_imBMPUA%~;)4>|Wn69t z@{mMcHpWt;f0W3p206WVt3>u0Hq!cGiR?FW$fA!(6P4I4)br9?iQoo;)m|ctPN@Y7F zhR{EP#_L&$h2zes1F+|$5G(}iNsZ?vhC9C#r0!0M`LxIfsoq~DmZn8MfgPcDmz0vO zMUFyX@cvDj7I%Ig1YYk;lCwaIyq7#@x5SFj0%0#p4EK7!`hdM6u_;<)6E+myJrbL) zMgB--dnHzjdp*Kll^E{zZXhptUADeRi(E~*?2~27(IW&_?;8?Zi~Cg?=Qky`UW;6e z<=Xq6thY&vJVa$5NNfwFkWYRjv8`AHX_|Qt>F>ie;%E&hhH2un1%29F2~Uc-CV|Lp zir^tJWh(!28|2GX{yxO5m%VByKeki{ALVd77V zcZ6PH@yEC1k9T=!txGWO$2^LUmBGj-^x>Jn;`DIT(edk5!tIrN^RQ?{oV19`Ja<{b zBD&}*&L2nP5j`R|qQGNZ4dX=gkw&18&=59ha4D9(w>2 zww;R133m5=U@|wuZH1YG(bUWz5ru3feLYS${pg6@ycKzec|WdB^FEAJh}nn@h|By0 z`A~Bk=waqd@Djt^myXi|^J$nb!aRbj+YE)|NYm&6OPJF!{i4jf;3v`MWQr%|BjhtU ztJPukWIYj!d81RZIo8KbcDB$kBJzwSsk&Y+`-rE z*uG_>JaL3hZ8}}##7MJ0VCu8BDE zeNw4CaW(0v+(hiP7vwrMgTGX zfNDvP#>uH+?1T>|Jr+j=E+eTJ$e-dGfOrfFBT0|P-3bKIq?9UM>6ie_>6(IT6xAor z=%1ij^wq#{ZVpA0Djka?*D_qaR9$h%_Vn(UAxWzpnfJI4pnztUlSBiKIe>dHk=U zUeZv_yc?Ve{(Epgo-|BhKL0rICk@xk2T_*h-`xw?2+ceMEZsi_3sX{&W>PnM`wx=- zqf~p@{&xr)t*`=-_F62OO?o$)nuw(*3Q6yWQ-@;jX@mTMaAgAe<(Q;{4pLvwrH23I z_%rg@)#DnK4GMK8TH1uLXo*q|JU8KLZwJ3jGBn3YS?w!ay+2x2aBz>vlH4bO8V*~C z;q@dB^>$2x+s+Zm!y|}MgwGYpBV-mXn}LB&E^;`)5<+}o8+yW9l4p6Z2V(dU%#!D* zYQ|q-i{v@7F_$qC{YaiGOFYJtn2pJEWeL3HZ4a>P9DO0ccVlO3Pn^6x>TOWWh42z# zet?;+nYUxI+sqAEVs!HfT zS}d8>^!pG*l9LpcNqn9ABt`yjzLc>EY)Z%bWs>W)vOd|H4SOsxiqf`fV^=t9xZW zWC+@Dy2j(G{{vpw>MwC$VP5F>L$_8hL|sShyAUc`y_UG0^4Ag1dr73f{y_)ikHk~Y z^bJ@RTYa7=!xf$Iw)#Q(t36CZ9&V8hCi^s4rd3-_m3#=TT6NVtRNek9RoS6==}0ma zgDpI*4rtyO%s4;YoV7ZrCCRzwUyFsk)rVTrVJPep_S-P{tqy69Q$X#XVs5mGu(j`s zYB>MHHT4eICZSaqo41NYy@pY1)y)>&28i$%BGR-&y9;wDI;5FHpsLM$#tmbcuY+ti z|Amm{Fu#E`r`ZAa4l%#Q`tCBvAh3m+u_z5QG1}OY1kjCevmW;Ln6bDIe)DJ4?O-l|hH2(rKSHJX7|fbsPJr36&2A{oF&n)?j54=jv`3pOq30O$ZjA6) z^N%q?j5ANcvBsN;Dfptk`3Phdn-8G%DdrZ?OUzNITWbD*(y8WC(4^K}g;=!6yc~0V zvH3m>c7quJWr^v8|1LGJkHmkrG(Uw7%gkFa0yml8VuCI=E79T#^Uv^~o6Yr*xzdaV z&nojP=(F0ag`R87YtYIq=HD>HYt1pxWSv=qUfgOf#;m!`9E0B7Ztg}~cbL0Dxzl_I zb?-8Ng;m#^v!V0d=3WfzJ*I(vZ7{z;e>a*hL(aYCZ0PU@^BCIQWFEoD-ft!$IBqs8 zpw9zl3gm1t`=Y^A(x%`u$bjsfp=J8)k z*h>ma@Xv-Lr0iCh&p(LDUQt+@zlg9s3QPCj3RgS+idhW|OOS3jQ}KEln=}%V0>+Y!dVMhr&!LHiab!dn`3*w>cI; zrhP1ok-G{G`H#aNQr=OR$KQcC-&I(Ge+;Cgyst2y ze@-m05A5;B;5BLfyD@Gl2Njm?-wY*FKC;KJLkqqA^xHNmhwSm6fiv4b9X^usu_`O@ zR}%IQd;FIuEAk&jzfz9c<0-@y`)`21r+jLUrv-Yte z?D4eC+2zlH*Qfk!kEihYs-JeFDZi@T9`qM>1lDAar-**iPftlxG)Mevu*(@lMQUQJ z&G#Lece;MWHT7P&OmWJg5HBg{f9VP^d>j%V4NC}R$A5%KJG%T~n29MzLgH^m>j_Ho z(UAD>$l}7@i@NfXEp<4gI9(JJE_)sGI$cXS=<-tk{5K+Kq-~=+OsAZ5NjKZ&ufR-9Ii;|d z{XIy^X;=K);C$6TQU~^>((H_X3}Iin;_re%&UJZ`=GPgQG`!Hd{GCSS8&~|*=!{G2 z;w4A>&J|Cy)}wWK5fLcmdxa)wU4|n7r<_%FeOebeYUf<>Pe5=Qo+>S+Ida<8?h)A0 z>Dq>C>Jl5|qw`}8QPU@pZli%uT&x3p?*eW!^q@zjdy#!it=?2Rg}t?zU*I_mS4PU}>SG4DAI zyCr(2R$pB*yHi_Ot7Ll3wVm#R5hc$ov?pu*fEg+&?cmU|))BNOYtfH7%34Ex_#vE; zxjCI)Lrl#Zh)gPY22th@aO4d93xQ7W!sjz7lZJ_0T~${i?DODgDSy=+WD5Ix=w?c^ zz2gDYbh-}X+PMZj!t-|&I9(K8oi1`n`}ZKH#S!SjDyJ3k1k+s{%Rm#gDWNq>pxdtld& zx4=_)JRdNknm{*g4k&JV5Lh+yAsEkq{hS6 zzLXCPZwca>pI(Pb`OrurVZ0W7WF%40cd3pXQkcj84TdJ=u#u$Z?Ibuw%Et;z7Iqs3 zIpv7q2!~AjP$!VjjkN24;HDebj-R62toa-6s24#pABT?%vkEawGv^?z+04&0(tJ3~ zZZ~Jc1P*f`syT(d7M)Bv=yoh7QS;DO7BwR>>RS>u8$K({uh22gq~9F12|XNL_T{zH zAvlD1IzJ9i*XfrweOI=L0oCa;aSi(!kt5Al>zxS1pdjX}OO`RrIF0`L>XWI!CEsE3 zH6&Af@fZ|)e2vKzdJ-V#SBTdu!RDLoAes6KxR`HAhk3|5U5juHy9zxk_WjZO5)k8l zABrE2E=67#PolTJN1|yBObc(QsTICQC32ZTQOx&PGzGDA$f28!j&*2K_1(8ui~0!? z%x-91n8jcgW-JJrc@ze+na9WtaQ})aVsA6T+aI&Y7=U=trpQgAaP$!hWRyg-aQt%< z(Pp$nY{p8ANt-be(ecOyHQS7HP-A*;%&fLox1*OH@g@$go&OH?e6w+iqtt47RA`^MW5BPw4TZ1-Ih?JsbDjzMqtLzJAb;rO=@qTN}Ec#Mak zX1jk$Bq6+^WSVIAW7JbHgU`S}n-J}OiXyL06Z-40P`jTUZ=jvjThRwkyEx5TjfVVL zU}%@1c?c1HSu0WVP&JqT4w$8#sjzTZh7_>Z!L!;WYmT0%kh%}k%hzt8<~=~NZic6} z%hgP(E&PvSp=p<=FuQ*>X_~Jvmwyd$;z)<8dHf&3B<*mda~K$T9$CQYYQWXL9u{pk zL~|?#5|#rCCA6R6rNc~vR*3dhD#97GK(w!xh|Ty6F{OQtMC``3E!4%MfFDeMDS+?P7RLyvci;vp!wTE4m_jAvp0)QNmD`5qi6b&@(+ zG%~RjNWDfP;RgP(yhxoa5s$Ha7?3G$dg>cvynmY&;$ z>Wez4R^#Z6;sq#{x`l}JAK(YtHb+b~Xkgzlk88^M->Uo(9RQ_qhZ zD7mSUdnu9e3_KwvI$V|w43fX%1|e@d?~Syn}5v`9o>4Qezkmg+(Un2=<`%&%Pt zD4pF$K)H?hDHBcKCyvfWPGls&4^!Mq;Il!2%&eggBy zdnEc8QRsp;`^TWoFA-%rCCg_&^mFGep8ZI<^ys38=|1^Q0IM;;i(`)Z?dE0r`1q&|C+=M*Y^^Gtei0xj^w-5ChL7kWOdGzL3nhm?kYkkdkm9wOtx9`NJES|orNO@+0{h@SPx`X03D z0V0vXMbBG&AhoyYH&~=-THm4xg%RlfJi`HkZ>CaW3$f8{sT>)R@fLze%8p1mxNlLJ zR8Wd&zR;mD{x1{SOSw-?)HCO^9>;1RGFrnXDdirWDu%<1N)|2nqWcWQwA2||Ya-JH zx?PF6m^lH0QnzaZK%ykeqVp63ElxNV1#{*Z#N^aJYb7Y4q(s;>+mAppw_Sn4HL`Gt zRjB4k=DP(bS|N)b2o{koqsQVRGJ0ShMZcq&M0$}Jq$kjO(jtRsT|c$S0Xh^clLL`? zBjQZzLMiubP%bUMBC`voOX?C?lmy*Xk7Se7lW4l2+Zg-QS&|9=6X`-Gk+})8t6MJ_ zQ?90RsTM_&XsKJ~1_SkH%KFy@>&u*!?{tj=Z|0*Esvd}=cZ=?_q=;V25K>YDawvN3 zhlyjNWEV3N;R*@uG~|I zcm{2>dgWtj$&8hiGa`|1ce`@*)l@~s1ImH>I9w<*1Tr&{CHKR`Am3xnJQ#|WR!P5o zk*G4pX1wc#w1rCAA&VSMIY=X+8HcI8VY*cDmmmkZzsQ&ug*Y-#lf6#t1)=gCTe*^C zjtB!oiqxV96$K34RPOuIg>TDu`g z&OD1~(>+!`&0HU(_N0fbJ-XW>9wm|-xL#{9L@^I5E8YOJ_DHhHMf6Q#$wq%f1{u9; zH!Xoo_GwU9_h+F`Ohok07DYv>%*ilmrzF|?gv+fi^1yaRAnERrjdi86t4V&ZPz1%E z<22&Q^ujHpZ-^(mj3W*r@>oSf)3oF6FF~md7L5faGM7O@^a@#PnN=iu)j~+>ekFG& z7V=)o)3!FZ{3#}rf2Nm&WG+L%%V%uNmT(Z)5G2+?;hI-uE{8rkXSRZ5!t9~Q`J8_Nga%Z*jUAa%k@DL?(ogr+;? z+Y^I8K%^G^X9>-&NKXP?g_M2_YG@9p+;rR4jm7t7B`){&wspnnGtwn4AKls3O}s*Y z|05it^e+)wrZbTSs3#_4H>CchLhG(bHvnCLlr>Gdm~y7AsD`fDuE%w(4U1^j>=vc3 zv@F&7iUGGxglv5JVzjB+EXd0)Zp1O$1XTb{@X=zNj+uS&z^NQXFM#J6q=9Q%@YzK+ z_-w89(437R(|>+&Ar0z2t67#aDAlr`ZKo~tYLc4^wp{QJL=po8Yem~hsTGJKo0>Zv zREvzF`JIk`Qgv!_2C7c8nzZ=zV2zedzG*f20Qm2hO{QO>Nok0_Y(7nFq87V2L+ABl zL+nG%JxJ0J7N1@I$CRKUOtc$^Bn@HlIE9sKsZ|-tB}0q`wTMavEV_hNYJ9c?)k-Jl z>gx<`)t}Xxi>i%Cxn!_EvjlrEhyA?#1XX+zyxWkZiWa{!$d3}mkFg4&Hb}(}K&88T zG2q?bRPptoic?6%9B1e*OT}MN^(Q2$qQz$yRu1t|HEFri8ah z^=M~os2+M$vH%%n!!YEnVM!G4^PXhnRv=k>86hEmKVL}F`9iWjY{B}&`K+lIuv+tq zr24Yd>}1t4&u7hxVXM<(m6yy2Eoxbo^OKOk`9iYK7m{VUIkDb+KI?F16?qS{a9VEj z{@k)9%T=iQTjy7wbU{n4Ckpu?*6+_}wTv#ZMzml`#F9Y4iMwyf6IseOLe}nk^t=Q3kr41oV;PCW4&iqMOZ9(jE z1iZUI*oibaf(6i?1*<8Y*YwRw2IEoHfc0BgF~J7fW?Ky$RSoG`j8Sm8xrb~l$Nwd~#f}-~+tv`i?V?ojTl-6yr$8U`^ z__6au&-O#~%S!aFVx?}Ypw_2?qF;9SNbAXva5a+B`enyuq%}R+Sx=%rW&t!?Mc*H= zq1ARO(MK;5ojcGn$WEp8hmi1oQ1nivbsRR#(MW@TX(_rO%Lmb09NISS3tEyNZR6IU z*ewo|G`5aI&mzbB1k@EI4LN{-{KV*cCoUCXr`aTLT*Q#0l>y&)!A9`pTn+h!T zs~xSVofpu?4y63IX$Dn0y3jVWT^QaeLMqJHK&)}do!o2?YLL<~9fUU1A-940x@Q&^ zj-xp>(;;7N{{S5CBK5BjTAf3_|7~JUyO9d}VuV1H-vxdUgnNld2ToeOBa&{2BCxO3 zkP6S>EJ$l`xapwi4iIiZ8dQZ9Am)12qn0^E(dIhBo*sntdI8vug8v^#Vo+;r3amsUDqo&4BhuoZn8EEV7lAPw0KguZ8SA0hjJoJHmg61E5vSPtFG(#9DkgI50l z(+nxGAIo7t%@PRoMmL&d@Xz#RZId~H7Zq7 zML&??G^AF=&|ofD@uzS(nzw;}9TFzMB36%X(8VBE176f(3jUQ2=ZKL$+Db?4Yt(Kc z*q%ZC9VA2Ut8Zpj>~d)c?{tLr%LgOvtB-)`5R%+`S$w)^&PmyF@8zdDv^|XX;5Hsf z4Cp;y6mDXVtG`?1MmuyZ1P@KTjz+_ zNpfibc0lHnNOAxyKD)4Tbo)Xd+Wh{J>d*ju1^zFP#DFB$qvZf(h466GxyC8fQPC2+ zRBSQ#9FyWe8%4oP(2ofd6=*P_}tR<)y6wY;`$Njg{(ecx&e-Tx=N83$yJY0gzD>#LkD7bpE6Bx%72xKthVvwXv=Id%4{8D@Pq+LStwQ+ z<8~01Bhiu?=ZK&aNq;PEvyu8=OCDZ<_>U2hu zrxg|-j%~mfJPNo6<+mfrP-F2pg_UzD;WE@jlUxclr@;Rik{GadhAQV!V_$b}0eYeo z`!T3kEh%=nBlK^UVlfy3HRZ5|1#Mdv_Twj=6ks!ppxEvi-`Wz)%;#ixt4q!xPru+_{~ z@H|A#m#7xd1V14-n)c%FD# zF8>+S-iNKRHpr3iC{?31G6z)#APuDw8Mdl{l_ANnRlu?|VdZ>if<9PiH$sS9rB%4P zhIua)u2Qd6wOTB&vzercB2sFkBdXO%?sID&UMWG6qiFHz!pfoR@#dIcfsqdao%jd$g-jk9hq!W?Dlmh7dYjvWy?`Ch7VR96?d{eY$1KG zVvo8WlCQ_VF>E2#3L)~`=R&EE9hE}mE(Sp(%g>%o8YhjC8b{5C%=y@kwvcJff09|C z*qSxneu2!pS>^zkq+BwO#myWsBC>of*m_>bW|>iB zWw&6Pcp;lLh9WPUorZcXMo~<8n=2No0TCL0Pvag4kO4skl38%KN^-fZBfPJmr=5S1 zjf|z$T;%GO z%vX4mbP@P297#(jH^%6xxH6?&3DLwE4Nch{v6~=Gu4=0xV+E27VHO|a3fHi6>l9;F zQXPshuYi9yk{ECwvsw2zC{$i^KNqBPeeJN_vmf=Fz2;j8I7vdX$;$%lAe!*mpy4@t z%%pMFMw=+o(YAI#U`CQF)^TnDyGYJq^Cv%8u|`;x)rxhFIWDzg9c@v@=K6_AHu*o` zwUhSHw;)fNs~Cei7D<}R;&Rx9=; znwfR7V4S>H?8EeLd*ERJ(=ljyWlWdTdMzUqPM=m%2Z3_@H^0<_a8^bY8XuYQ>J)Y?@N?T4#Oc^bhmb75XYr__y8hL(3 z=*}|BGAUKBZBgCwO&OJ(uCXv)-*?>@rpt91o6?~medIrp2h=uxGu4Vsm+Of#Hg&`) zERwnjyq-Slo4b_nwelW;SnW$E&jFM7BAC{j9Qv;le~y%nghJGvmfiw}fs zi?DWKVOuWDW9!TjQ!-utjx`*DZ)7LFpQ23k&gh5J$>%cLCxYkC2n5%op zw)(o|3n-%J>EY;?7}*^+SV)CW;BE{=`Fh(2D0u^^{~XvYUkNM5m=8nhKNoh(S9bdf z_(w#Wla5zR72h7X!^=qh=VTB~+V>~mpCc81m5N3Rbos9DbC(P87*gRBtT9@lF5j`8 z(i<@Xsc;bWc8D&2lJk2IP7slPc|sei%Vl9>A4CVF{uOw`VVEAVEe^;c6pw1EUYq9Gsv2D^@D4NO9@w4$n`|C!ZLl zN0&;DwF?Rb20$CFI#CBVs76Z9B{^es`BpLl!h$hn6?J&y*dlCNkbd7daE!AETNk9q zw+CUoMcB3=eaR>gu2kK_Db|8?diz`}wg|fxq%Wr`S6PHt7o@kPQJY|?e{_C2nL)eS zB78DGoxTLGO|<%Ra(+5}=@JC#&icA9=BLw3AA2cfMZd_TX?ORY>& zG9E{7A44jn-yYE#_2@^@;>RGoO~h;AZrT${!Ky2;HX)78riJqryVm_?w1CeQbbh_} zYOp^=Lo#2NZ(qIv*4L2wS5(gs+5$bAUb=T?;gx=*ygkcQU3!sIjNY(RVd$Iucqd{A z=&)b2*^@d#TYcUB1p{7SE_!j6g5ClOiE??TVm#N@V2Uf!!|?+6B(#fG$Jpi11EizH zu1JNA*qUi$?LK<_`$`bTAoXvks>MLsTdzUWe*|GQ(iMx)rN2Up!kaK3v?+E!m5OW} zePDYvlzxf2@LIBBz}|+;Ivgd1ld{ssE`0u>?@=8c~ zFL~jSPz;17&47?X2?;eMbV9cfihz`0D2k$B1v{)|EvVShwV*5Nt_`tw)?QGttzEH~ zwfw*5%)BJ*{yzWv`JDIW)HySArrf#B_nK2qo|*nS$wTJdhmn5>@P?jovSp`c`jZ6J zjYQq*dezSyaztuuEn0NZ>||esR2FBOv$Br`;e10&3PgxB@GVuyg!ihhTiA$PL*^W zL_KkM@O+!`VEgBp2mH*JQhsZBr(dPBByam%0WvL_8rp*56XI`1I ztNwg~FYf@hjW>{!IW(9ToRi91C@Og6NH+R_X|n2_u*Dhlc8JCTqoo}?3R$35i0bgj zLMLb&k+sU$^;T2%fk%-GLj(8F*y{+tzGr!y0{B~Y$r#~pd6K^wGNzX^^lsU9k~eap zvv50eWW}QnIg6%kRCD%J7}-t8KJVDqifoS!*1X)zd!?0)`iHnYy^0eZ``V)z3Y9%M zxMlk~PH$%fw``LCnQ2?LuDdYkRg|7=Ey?}x-={3a-?v120wXu>;8Vq9Cn~qq=hxez zQ17&h`U$7tRm^unaz}oJ6VmZA>g&s4#qh|xoKW3HkT+W(6&p{nIm`X(D^-0`+Ln98 z(~*l#wJcTI+5)LK__SuF?I|T&yoHXn346PgW!JJ7IQncJ6mQx@_{q0n3SvHPlw2(93NUS zo!BPH#3nOZMX{S^CE)#eQdlk|jdH$v6&E=6W-6weWy^w7Vn)sx)WnD9*c|0ykIIzH z2BJCOMl5N}iVhBuhenpG6wM$O@MMN5_9})t0eK|m3~NRsa=oLAhu2$r)bHKo;U#YL zlP6*xP_}MRj{JwCYdt)(*}5pUKd?6ZEt}adS+%9PwzywNo>3e4imc(j4CpCG>RWVw zHCE1SmF{M5Ncu9Mw{5H)MJogLl;g`l4>{%d9>Q0ET~U1%(31}O_ zE2^&oZbh|uyux63)kDzhp(-T>d{m(U{4?RTjG-t9tG^${hNSWyK{L*7wX>z zboCynW-1X?U7>wvRR=;W1MDjJdrO=L;cy}9D)2{W(S0vEHs;e@PMUU2j*fzXo)19@tW87q9SEHFiPX1!nXGBOu^p(qx5ues12zL$8uQ` z7^PuVX~SydI5zl!QF@kJW!0k-=xF02S=*Fhn5~oh)@c=+i+s9biId$A50_bV`n1G&EhNiL& z2F;$f*I2=o(^wk-W8UnH#Twfy+sxp`8MY~UE|B^tKbMOhay8APXw+8BR#pRC!MrJ%YbS+qMq(wMY*263hWWKw|Esk_ubUee~ z3cxO)c1aOe1v2L@PA#B>qJqWq2_-;ReleE`w?w#9q0K-RQ&UpPcG1_>N&Fv+zLBRn z|LppzqA#VFLuT%ESYh_jt#>X2IF39icbVmwLv^9GjqbPCadmL-?`+C);z<>C(|p>N z4^NWikC~&HXfE%ka@j9i|J@u%a`EUaa?017%T11Jr&5PVE`3zADVI+)m#wbY@+{ye zC&1uQ@s^{@o*WK0CpZ}m6}I( zX#8c^_)9V}#Q{6DWCU~%XfcF&LewE7(^|V1!kt3Y4VElROj}5HfSp>hEpaS_YQRn{ zvF3TgPAzdukaiL2WTIE`l~b2#q(s#Zm_~kgY@J5>o?y+%G&0x9>ZHIl zvV%A-cWjwPMxN~u8UKe~;1IbECk@X@j5p&>8SVEQgMer9mwDby-Sqj!=sY@8Z6X%WMrY}nx>@wS6Vp2U!EtnW|Ix(Nnl{u=Mi(nHb?*P!BC~bC|JCR^f_0MY z4bFJCBVJtOylJ~U<;}${z4>cPZ!T%+&8029xvZr(m$&q0YfEpgaNg)Ws#iHgr=6?p zYyGA&PF`i&)lomQ<)w{go6&cyPMx>NGgq4|d6w0d;~@K(1!R;+u;aphu^gsj?`xO zLtEhpZG}H=TWKSBEsJezv84#Ut!V41Z*rx*XAc=dmcEfU@q1GnkKyW)$&@E?XHSVKl$W=EJCP2AX**w?31 z9$S0uO!k-L<{dVbOL*gr&ZAT1V@4plfStN>0gg;CdPC1)v-PgvX-1#mvw0qFpEsQS z9t%fvgvnna=I9C;9l%%V5d}&rO}6 zo|`4FD`4PQH)KD>eT=$X@D7z`KFpJsR4vAxg}}&5X+JsADW11b?yUHRp+XMhZAQ_2 z6D>K$ZA0xu;SNAI-%?_JlY(m!-ID4kIR>~b_(x_zEdZw!d~_wQ3Z6ec$}gKp{UJArHbPF-Fu(buU{ z>WZE1J?tAX^A2bguJ3AVF$BzlLpqEN|>1;X)PLhnfQ zk{xwaSbPSn_GG7AAv1Fg$z$|>s0(Z8)>b^Q%8IvcJwS3|c>$hfN=4A08&8{Xgvsc%E=h`F{=+=Io0(Kkz-H-^cL| ze~%%^|Jh1@n3DXt)SCGs6XxD_=TQ{jthQC*?;T{B!wCcb70c|9V!q=1o5{j4@Q|;k zp1xckm?>{XgVIqRd!8vHKFEMdEYFsA5(ggka|Q8^{=F+$TR-8iL`w<{q8Dntqk9#> zio=p&OIh(0tAxQQaBrZCFHc1%>)wEM<>+q%uvWl&C&gM}S+jd*!#Oz+mscCjzMbk1 zoMI7g#`{UIbw*Fm{B*{(S?tw8D3-F@vaB=1q%7XyA26E+ezBr*L)(!e`b&3_JVHzT z%RnnQHG<&Ro$FBYC#oQ&`+A72&TFa8z`t#K%VQ8~<=?s-_YPkmp1##y%=ofeROVZU zsOAz`)9-1J%41uJEaWg-Ma83)I{I@&$>#g8Uui@2tOX1eQGrA}7x^&yX0#db$i)_L@?+ zudn%##63{%0BU6F%UDO*fto)+oC?@sq65XtlPKPM)Wo}h zeUc+38f;Q(<+McUOi_|2IYK8fssZ{WM=ugR0DY39j>KfZnyYB?A(d+K;Z75KJ!Nh! z3daI!?kW;j0BY_D5|07ia2cicG?`1KSa|4R?lAF8DzoYs#J)h`Gf`UJijZ1C?GJ^u zECWQ&V20KrBZs_}CbgLxWQUB7Q7J>^?*x?`Jar@C@|?+$M|P@8VI|iS6hPGokP}F( z1ZqXBW)hT-!QKKYWn|m|$Pw`N!}n*K*j8G2>}JT7GNh&WLH*pKA2HDQgDb*w&HLH?g@7)i=Q!s% z^K+>}XNJWW_wU+kykk#A7qE-zHBR#XT1*$V;4xv$c0xY}xv(-0rS__wTy}Eaw+LNDQvPbb@)3_&}EH%HIcvUaRr&1veKvfw? z<7w0&P$g#*lTN3kK-EN$qtBpFK-FO&M{Q)y08}jo`GdsIK&_aqSp#LlCVCRE>|_wB zqMGwa?1XYVP;)s*=9#zv)Z7SiEs665xeMf!vzT{*hKE5Oca^g2X%MO9n%79=p3NyD zpfgl}XNMm}0qm5L5)CyeWjifV>dD}%Gt^Be+z9BD@&bwH z0G(2PBJl%Y&9!qr7%!D-@}aGX9iKAS?i`*)0My(t5|x0OJDS8Iz*{c65-XxSr-9P4 z-?Y>_)>o9f3?u;WhSMSdcQggy(8HZnTckt+L2tS2X>7ybSemt_70Bh}bFO6E8f3z` zbWfnB2xJ|JMnLPejjMX&rBY30s^jf$8(h79iTvk)S1HTnlqe6CkR@Bn*(Zvij7jpb z!x*52pGTq|s1-4-fwRHR1PD7h{b}cNPmN+?>Jdzhy$|NZ$N5v&jt20n^WMgC;^0k0B^emQRa-=p!WCb-`PH9#(<(NuwDyoN>W8%-H za&N@Sid60?NxwU$}+E8BHHxnJkV|RzLY;njTZ~Hq&IoeGt z8k~PJcWkyAQGfJ%R@fW*NDHkxd&r0`MQyus; zHYMpd>G;-&?s?_uEP&Zc9cV`Aq^&awi(8=1Q#=ZZ~E=wt@A8)4hw&#SoG(Pu+{ z!qN5N1XtUSIKJ%gyl45AGcwDSML6EbZ&%oo%c}+}PCv#X@^-|0nF4hrWtNv)HLiC| zxdKw*nC||cZmA+y+}}cI-ho0rn}l9!A++g0p{5J-6`wdEd5NRkw&O*%%6ySAVo>*$@K zk8Gm5Wsz6$5670=<}3Sv_0T&l*r%r0KQuYxw+Q)bQta-ju&?OSg8gucozYagl`Yu& zQtU-d+V8Yr|CwUXY|{Rv1-k=F8|`vGf9dLJWGmO_WvMpwPKQV-Rjwgr2JvGq>?&Up z;nd$d=F^^>BLgJmUB4dzrQA~=;w0F6pWlc{i*qXeFvLNbi~?J1b_)Xb|JnhZ?nJ^L z_U6w->e})b2+NlS1Htg8N(@a?Vp_B_(^@cX@$yw(@Gl(@IJosNYy=`{+5DwNLK0Nr zh>Xku6FM9vO$RST)-OmL0X=vor3uDz&LSPTSP1iRD;Uq5E`%F}&|e6-dBTj`%TF{M zlw5)ZCSurue+mO$`gnv)dVU!>rT+oNPtO;%^ez0RrQgJFDE$`-7KVmUu(NqeY+QtB zut(k)Nna!BV(UpEmVR$%9i=D72c;N=A=w~EQV(oc|~A2GJxM!2?VBy z8U0Z+GX<5;9V62Vc38jq8IFi(=BL&|5$vcxBW3p~YCd?l*$6cpIhS9xE03&OyPEnR z<}+6H1T{D@Ww4)A>|FD@8hnS}EP2afi?XV_LLQ|=`ZCYf|hBo7g9`U|*DC-`>Q&vIYCD6#L00*B)-ceksL%Jf)rZ zvIRSlcR_P8at>*MWKdsPVmD{o;MkQ^Sk9#mkp?(HWodv4 z=b~$Xf3}dhDkU@i0GYhETF7W4Zg90=Z4k#2Kdt9V3>0K;b4@CNCWqPDo{>GdaupA` zv~44!k(rv*%gF88{$2QWZGINb_U)u+m`QEq9Yv-?zH|{Y*~>2#G%HW~aB4otQp1q9 z7JMh%Y*pP&l|Kj+RMMeL>>%m7e}ex9Xsa2`@r`C?XGksqU`bpMM}-zq+IqqzB7|^xk=v0el1H0E7s z{r&4O`#%W(0v(tU1Ft44Ao)yPUeL~KK|7(P*|{qeSs?r=!nf0uuTztq{QRSZ?xw!q zrXtum?@TMI)8L0J^ZhPKkqWfWC*8GRyRZW~Jq8MLAa!duQrOGz#_ygOD`}Z8{exSt z5m7r|h9jYfdOoDB4*mdy&2VpUopSxAk4X6hTa_^J$zsWVNO|G071lo~BT7Keo z(tZvN7?AljtswD)5ajZ9KqlExEJxCQe<8>u8;WO&C;mtw$Rr!e%@d|SO$ahGhI(j8 z12Qv)imm!eVam)HI@A(QgAj^T3Rz~L&{T7yQf}opvYVf%Omxvn@*^n1Vk2Eb@mjcw>gj zESB`9R+$VWnXAjZ@R_aJOL8&Ev(kH$^s=ORg`Z8Hp7F9|ohd>oJOp1P75BZY_uze!3L44zBi1mw*t;p^4qSU9)_h47bIXQC6otPUi;sT$dMA&P17`YI|X ze%%;kyg*MnO!zzhFhq0KNgWI^J+1)>MQ-IcSMFr-x07;KBj1=P)tR`W((~4?T{|oB zH|lE9^2M_f>Ek@__~VZ!f%&=hja7*U=`MU@t17Xi-18PLUob3j_-MAk8`lm`45#Mn z*DM&3_>@Fr!-CO?Ly%drpmB8K;!#kJUOhTdgu&H~B-V@<_4?|>o4CAK5`QqEHqKp8 zo#;Yd^^J?G6TK&sn7_O>Avf*TFR!mn+=NU+L;cu9C$d<+ibMviWows?OB_5DnYE1* z5`8A%&$9Zu#KB~-dg+3?L@v26u3uD_xVwfuh`EdF5}#6Y4J#UkCFG9q#r2KD61lMW z^w{vk14LQlf<+W(2&_e`MkaPsgN@4;)h2$SS{E*F7?&6^LMUs;CH^F_tbXBy#HFwr zm#?0jNSmfrHzn~T2In=dost+!7WI<*t(1N7;>9x)JMno{I(Tw0c4Zc+b+~Z((UTKFWExl3Pf6^AGJpB%DTyR9ix<~VOJoeEI4jpw zCFYUE>SZ|gI^|licHzjxA~7gI`=&I(QvCUrFkZZ>abn_A`a=Dh`kKW1)DO*Fn}|_| zt5;H)XJBXXf>q-ZgQeyh$@DUEU$lf6JCPP_Tn42rGDPOs#4D2f8fxsRT6|tb?lm~J zNJ8sG+?c<-eq3S=6}4;$mGBPHGOuyLxWuGk)R-h%5o7ZkmrhJ9!Seh@8m^e?t#53Y zl9-OqjZ3h+hrCv<#PSxRZtkLmRf*QL*U}ZMMvtmj=tcO`4m)(caS1s$oWC45+TqyZ`9ydhajc3TPjMSWxa^u*V+;If8ARf(ra9J_jHbwb`^+PI3C{~I2zTDp2%;x-H}T)w(G zaW-Lm?DB#3Ll;HByR?srdy93H~uOH)Bg(;wLEe$1WJ3 zxI*$;PDgtXJF6G19G|#UMu4?c?{SQEbC=i8O30hl8y7Z=NEFfpbo0@P!(@zF)G#^m z28sG5WKm0nA2pwfx?DniA?ose4`UR zY-JKBhlG%$GcdFk53G18lkUobOq7FjG`M(@6f5N%wzdO~6?l=_v+>k^ki zX;{@zmv{opi&ib3mY9T{MXQ$0N<1#T=~#Nx)f9QzLUKO{H>l^)i5qExMN1fRKch_< z>uMA4W3Zuq`GmxGsIQhz)Q_fYoR8&`X`O|uR@WsS#Z_(r9G1wJ0FV~^UbNOytK)HF z<k}svA(HTN<|7gL()f4!GUv2Q2kXX9^iAZwycPHn3v&zV za%ipP!)oDcO?)D{5??{=MB}AIeR}*vDz#O73A!2aImlG0^b)0p*myFlrPn-BxHJtjz2U76`@lR;jw(*CM zX%`RC0`0xPXq=0l;QvJmI1SgM9ZivZ{vcP7P^6w@C?XZaX9JOG^9k7}(ko&w!^r=M zwn-nFb9w-NekJVms+=o8g8A|wOZsqKTSW3RaW;Kajx0wq@(b}ceY7GmFCga9tNqil z)@C5hnn-^rc1Q-+{cX_FHf|jKcYxR!J7I;Fa7b9 zX)%}nguf5dffbVewcS`O`-Aw&6+&CP=t)1vWL*I*-fyh5-(YIT#QV~50`U?22IE%~ znUQ!l8nO5Z)JkFeI_Q=035+=#;tx@?e|kB`(U|F%7=IF$LJ`S>GmdwA3-SO+yTF~4 zq1E;B%2rR`A(Bzq=~b28%WoU0+ro^ST6Lgs`Bxr7Mg6U{hI9V?IUu*R7DgmrK2DNx zYis$6Tr9sj0diaGqd?ktfw`hIJ*@{=C{oOCE72Fp=+op7eF@A9j3w|hmZnWqscHO1 zMBPeget|;~&Nw%%Q6-M!SM6NfBqes#nC)(X!BONN?{tmIZ|67fpJ`k*${1;Khi4In zJt$cQp9_>%=X%MD`hfAdKsp9pZbidNFJp|wV#z1b%cwO4i;--TysQh@Sc?@Vi6Ady zoGDlhuY2;560q^6;BR2X$tj0|O*92k%(CR`;{PO@UuE(|!6sX*+RJzuKVCfcEw>eo8x;bSScsUztkvJ-|_|6TzZVwy30# zIhx;`q6k{8OxnGw_8`BJr$k0nyujylgv|0bl5v~$G;b`kHuo3U1;=@r<1$6iD_HAg zj@M+w3-qO-Gbg6K0;A340|=DNHM#P#;QX$b%RJ6%XCeT)ph1t<;3#u zq%UQzRStpjuNc?~X|hwB_cdbOGjmPui(tv|o#Et}Jc&^+DK}(iwzgO>8S4a=GQkX<5fh2 zmyqvBu2|MEFY#j^ok`~=+V*2nv}$!j;>7;UTn$TCB%W=H@zK+!csZS*coMMBueFoPW?rowc(VYFDv3(`E3&HG%EEY)~-UaMoi^Y;h3%1*0ZIZ`Q``M3JtS~uTc#m1E zdvc;+k6Wxb`4`$a`w5Gcc{u}#ob0De+9Bkgw~9(F%>LEn#&YpRbN|g`-zQCFJ^S5a z0UaQ-|1{a!jgyZekZl5r#ger&es;iOv|XNL$ZpeppR1F9 z&~dW&Sd1^K(|Xx2S}d0ACXz2%tW9zXRMcPh@T$^lTToCQ}RqYZT2U@q*U}-$p^*HPc3$iZG_J(c3$#y36C!ey3k?cQg zX zIs5H!avVN*wjSRJYduDi>6A75-Ei_TY_+kL_l1*vq{h9R5-G~=K$lHw`8<9PdWPYu z*kr#R$(8aY<+HBYZ$^^s)wE{xRzx$3X!p+E8_`ONCAUdTzs;xT$*_%cBBD+dS||2J zlG0_1lNa_x^1Vn>MzIyi@WP>ehfdh*}n2~Y_rKjPuXAlIX}UQ zB;`}e+57z*3HXepd`mg|8;fOm9ge^)JFR73<;O)Kej0LKB1TxuUAA^*;xMXs-s0t} z6WN6I%Ed>oOq@kQ3k&BiOf07j1}t1WH}MstGkaIZCOQ>!&bO%H=)_eroQcdD@>8uJ zXm+I(Q7F}Vx2w~VcjBv7Z6UV_P8h)%IgX4RMxqNc!zFPgf!udh{nErs9r%o#B)U_R zYy{SOImZ)d*$?<>QWZHbw+G1y9vml|P$bE3ZUZF>Mda~=P(+5;P(%VU=X(g*Bf_03PNG_Mkye}NnNi_M61kU{yi`kLo z!Ej6(yp0`M9IBgWU=lol2{ub{1uKM_q_;@|F zp7W#V@Xgqtl>WQWALl0^AGeN`way{}+Q`SPk24QMz5vNz0@pmG*B88eeccayzcLVHk0LYj z^>NA<6KBDhlmF{PI4`NrqWpSs^c5|_iqx5PY?P6FO8;X4Eg8k;`UjCu;Gg60`t~+*iK1hnxik8?WG>?7uDEvB z{B>+&S#_bR|*x1$R0>2@{^{ejytvCyHC-_kk|7# z^L|>o#FE!qUHQObk>m)vw)rrv^@XU!yqqg1gMO6eZ$__Ep_vzs<`L9MBeAp~98(KA zkD8+_7D;A}hPN;r6Tw(gYi&_D))q`YI4>0&ib&~ldNRG4#bN(NY!?k42C_7qEf%xn zO1`e1D8AQ`JphzDFDXM}Sy+2L&mn#Xi^J^-n0UIm6{&c}6o_%T9B<-350}^*%C8^4 z22;WKYtYl;JMlggzj~nT>Rd66O6Pl=qR=+oUxR%9|@`Kl-srjxM zgX!!A3IA(h$SF-!QgG|zs9rD*)r|oYXY3M)N^3>e(tb$DCQO%kIGBERz#Ix!-~b8P zOX+$U1~;PMY|g#;@$d^ZTK}_d`YY^SwtoN ztVdmXVn4fED?dn+;FQ-asKF@q-6LdO_mjyMpBlS?b>F z`2~OGiFC6D(uP-s#E4@5xQ9Wgz22kE5$P;((EdWSfnNP&YJ1WWXF-?-^uB?*GEZ3| zqf*d*M!U}n)<@>==|);(h0B8q(Zg&#hQo#Yiy6JzJw@|Z|RmPYes+CYi#D zEb+37+5Q0f-yr#PN$)86TyqIs1nB>#xYpGYpT5-d?g#pREz^8A>%=jaaR(Dn^x$wM z7K&&6-yp6{_w=O1fBEHnqYD@y1@A-l>YcZb!OHYXkCowNNdc@Uk=YKEbOkB7g7F+E z6|u5Iq4-yN-ZxO3p=xI+=*=O@Oz-sA$tgoWA^8}fhBB^ZgaArKObsmrn+o7r!Woh7 zJFufeq@Uk{twH+0&LCbuw}y8kr5qP-GiM!EdLAPSogGn)*4WEpn5Ln+<)9ThbKPEE zK(Qm*QPSntpaKlx{l9la+mA))UC@6CiP!($X-Owv%k?Xum%cIK@6nFWp&Go7TW}%v zKV~SFcSggad;`R-K>vvhfDm=T)#G~RQlNAj@lw&ans-n<9@Q1wS5Q9@&VJ$O%>HCF zL*~>WH$VmYUn~{+RJ66sPUk^5Lx}ojsi&g_G9i5rVLvdma3GI`;8+DurN0o3i`dSO5XT#*8Az zi=*bw7i=y=CzkM)>8`0}Uv8WIGeqA9O1Hta&A!4m``BA~*AZZw{TSQqS3tN(h}!Ip zw%LD&@Cz_>KQ^XF4mA6*w%Lc?M$7}Hm8EED!>*0Sq+$0!eHyS0e7q%gyPXjms9fQ^ z?q!|#dWl)uZhbVbH>5|AxgYSVhCXj4>YShmofegk4td>ou<`^r%HMcS|2=KRe>+54 zQND-iTJd{FZrO?hq-k;;gS{S^NREtVtn0=was#XrvA+^1xfA3^5?=trMX9h!DV3UK zUGU6+sHwof2r;0FOYY!mDKK&ulrBzjYD#=iG-JezFh;z?Pub`E+>Gosz^EUEVmLR- zxRb>wz{7GUfa;_Wf5E~ARx&x%_etvd?-LJ~tfIZ+3yUJ&a zg;q0{M6(R}l)Mis_IL6EO1=UaPhu2MDq>~-fpQnvEkJdo2OC^MmG9YixQo>#Fs3!g zb$eX7U}F6JNOnng$hW}14v=F&9;UXwXc1<5k-IFKb%r?64OZ4p3Ja7R3UVZg=|HK7 zl~qD{6zqPWdK}0up^hif<8Gn>7&99r{E{^uAT%)EI+|Uw4DvbfHvw{#-|gB#$ARJ# zhapG9^@in3Gxe3RdqA|SjNScoG%ksD6|Pr2x(3o)JROG^<%_n7s+&Q#JVmqpsw$2Y4q(#3)kPZ>gxJ?k(BHI8i|$XoogjxMO}YB z(#yL3lq@t(j_7o2_M~z-Inr8OFS?5n!JHb&lQWUp8lCwzMRew?Et)?D-sO?FoFEnH zEVnh%MrOGhJ@dXI(oSZ#8l4ocj3m}jCiU&Qh+f30Evg)iftw;aofW-13FMwgzMM?! zG4@i4o#hc@vU}n+!}s#YK{9;zW!P@8$$sj?ipW9g!&QS&XpCgao~Kv5a2yz$k829~ zze7w&=y{PsE!Zf@j)ys9#49S6I`AFx%yf`oBwJ3JidI&Fq(yQ>e_SI!5*Oi!o;7>p zu62Yqkqogj?jrCaJsocsX(bd2%)nY3(d$vJ_|vVh%yAtlP4Onl{PVD$6qzru(08@q zU1f>cSyz5vhCAqql7F1L2#YC=x}PBcQ$-wuO@qP@2~) zQd_fZ@q9iS9kwS-Uhy4OFn5P-B`wEJKP~a@a3?8oKiy1tBut+I(Ski1?xY1P&Y6nl z-Olavnl$eYcN9&pxQArB-C4e^iN8I?A1x!uIbm%X^A`NR%j#+4bHch5u@!$_SS#Ke zn>_>JQ^Usm)MV&X>!MeDrZ}=QWQ*`5!-KZ-&QKd|XWeSMCzLCUqEQkc_lB}%6Q}4) z)=lOgA?z(aqh7xjnagi?*!daoD7%Lh&8RYDc4Uzm3 zkaI#wx!u6TF8N} zkotPSL6$SFH4aK2tqy6I^u~2_gh?qvnIlZME$fY|g-~?uM9j|%>C&AB!@|l?)>v&k zx$(&y8_E=nW9+ZuNnyyCTT$$*V`5>bvqqj)Mq#K#DnpHQcHvWWYz0bPLkVeHj^Y<2 z$X-q}vXx{0t%ew#UQSDD^J)dXoYqGgphn-~_iCDMS9(4BNL=jXjeD>*JgdgMDfW-k zqw&Q&P%h)#&NQ83Ihoc;_U^PU5`Ctw>U5_=5a?k~XYNbWL4EJtych&v>3yUOkXPD# z<~W^Pt3o~-Qu$n&)q$Ny@s_ ztITrlcXE2O(Ca@%hTYL=diTyIbj|<uS2q?C&W2 z4Jh3s9BuiTX&OJd|761ku;&1?()84C5`-~A)N_E@X?p5+2ZURJ%Fmp`7dsa}lA;lW z^=Wz<_$e|U177*lElM`7CpWc8Z+ zq|2JGLQpd&r5!XvR<62Y9fZ)KK&h~F#dn}VlcI0 zy%d=(fF7lD`dg3AvL5{c;|E}jEM-4L87 zu+ja8P@1lg?ndz}pfXI@%0bS0+(@CNuH$JsfvniY^*X?tC{x(BF65C+i|nuDJR?fa zEEP(boTS}^&;>wQCy*N+<_#-A*&!gyc5~Valnn;?h{OlLq-u~H5^_wGl~SxLwx0|| zzAwdgM+l`tDeFxt{}JK@C>sd!7Kuj$84dE?qdcAsl+}Se@EETN0Vdaj407dKVzcnx zma07p#w?<&4j6L}$QmcbxsPNYi5yh&6y%%WUk{WDb>I{yB-_IBsVc%%K0H=_f}_j6 z(cp6(Vt0mAfSd=&3gQOG;_W+$8cG(zE8K5BhZve zO-ZJ6q}8|NHDVNEB~$qM0=-Xwk~tt<7#E6UY#1)f&OHRG9K0IKrJ|PLnLd%sZWOp= z8)7vmREtvGa#={8~mA{>)teZQHRM~>O{&s!^1cpZ$@SX-tRCBi;; zVu7YqJEve4MSP8}*ARXM82E#emFRi|b4_tD@Xr>Uw^N);BtpW>G;)YeDne+e=jr`< zj%B<#)V0|g%4j{9QtyKmdWL6T$+F}NkPAUJ0i_}~^%p4eVxY1R)jr}`1}>mXZq)Fe zBR&Kv1i1<1BA~1{$m!3s^8=I(1R3!Hc7aKwL6*4+9WWi_3+E7xBM#jZN}oLt*~PFv zK;aEhTmy0hdm590l2bvxBJqhJn?Z*Ei%tlXTmf=Fi93N(u~v37l<_Z;Jy3QJ$WtU9 z668^kVK32PfwF&rTtMPnLEZs*pTs+Yd=Ap%Wv&kcWj_mt#LK{xv_lBjgL&g{IUIR` z?Z#J_Qvh#@EGy^0I#yV1L0*RZqOxRxxf#}9gw+e=7sx*;OO~3uVBIRLAs|Jsa(M;t zrpTf*)`PVLHB&YgWWm2k>tkl6Sd9spxx;r$&mqa4QD0N)%Rik7T_{4)F( zfs#`}BCoTt4ODLi5wWr@B!)q$5b9QteL}s0#3@jY2lN>HlB_Q{0)-l35k4anI<&)~ ztbe?Lz5gK`d!6q%JeCMt!*3lgp&mG-1@of+W{zvYyz>BNUvJt4j`@Nz>e9As=VkVT z_=pBW?6jL4^HXICZ4jRcItk9>juRV{>I*U|o+NyU=s~qs`IL-+<-O&3zr87)Q8Xt)84RTa7+m1|bpaHJ&|SR& za*}k5vq$%0s5E}KwY)dIBbT%XhaM99ye0^w zDX`O+m@f=Mow9`5*?|SQ3SPDXr0?5Yg$Alm0FgT~%GU977nEy&DQALA1a1)dvq=2- zj^}+3Xx0AiG7EScV6jP1_{|Xfm8xz3E-N&kR5)6-Gr^_+gFEjM2b=n4i>j@A$a0Lg zOYF4?AJmV0wQ8?J^BQ1;RBfrSwMw6X^aQ}5B2~KNp_BoSvCtHR3!1AlxQ{FWtw};Xv-#lcpt+*pgw*|;v=9`WYxzm?{Py5P=3NLPY?C+Jcp#3 zT*eNDAJ^p7vb`hIP}$`q>d~GJRNnxyPpCJNcmT@ZfGKx@Or(M@s)Vx>7<=h44Y6|l`CY;B%eNTUH4FIPS+N$HBQVhr>R7j~?~fW*rg zNSy+d3PHBn>+JEPT>Hp%6=q@fl`svs+AbU|9Aq#E+inB5PQ3bm{b$`K#2^aHBvLG}xEE{S)bybMff z0IBTD+GH4ITmix-YS)*YNMh!{S>ga?XMsFK;vc}&zpBU=Cr!DI#0nHQpjdW2NbF-K zVW4^k$Tm1-caS&{N&`^#AV{xIxc37Xum|L&M{LEDCMG*=qyha-kz*x$1Mq`{v#JFm5~O}+IG4zl27MHQ~At6&)gV^Y923O z^0`zVKa%+VfILLDIgi24#{ZEAVe!3drNi3OlD>1N97Jwx!kOR}4GnpT*T95h%NE6> zmK|70;1)T$>}ZJp(7f_foME}4sr-D0LE$mu)^?Hm+#zyT*I1R4@4=N1c+A$;IC7Dr=ZtD9?USJH3hcPs5_9bz+hJFtsoQR! z1odi_nL1&iY_xwGJZLOJyr#V8S0v1CYhcb@R&#W~{+q2GM!9W;-N7!hJ67~5ciI8H zRt-Vzkzi*rb1sC_g(weZnn#138Ew2jA$$!C(H*GAEOF;&EINT2-Mf4usGHn+QFfEd zn$Fz?BQFG_$&o~uTvKiPIkyD?dTn(kiAjK7l)b1)DV17a9oK7nFO8rNH(iu{45fzw zy(oL7Q#>ps{=XMxqhH_#(0oyL3D`ma8*-`blATuhl$4DFF3{?=*_)BRMr<@)n@#_c zlOKSwS*|Ltkh1Oxw)&2;s;eX9!n2m`SLdSfQmGB8jJR%jdrCnoWh54>04>}9i2rZd z&Or7gpt)?HfPDzKvK6>UYRd9|%hv5H9wq>q%eE2hRDdTJHoCg$P|ICogb-_I)7P7M zqcbaHOY(YNQbQG-XXCH%cF`tg((J$4nuVlpYn>PDCh@li`ws#({>~3}9gF0)uepN- zu<^G!sMl>z-p^yrfQ`Q`mUziGTonc?H2yBI@mKmSaSaSzQOI2!V;GX;(KNFym?ODI z{lcVCcS?mXJMdQrb@Q`y7kI@{3+g>{Z7?CX*|zzPtJ%PS->2b`D~$1C9Wi%ZFr378 zN%nMhU68pQC}|5)^gWxLK&gn8b%%1$51c#$+A*GKGP6R=jPkR4(B-vbT!_qOKx5!} z5>EpfJg++?woP1Wh86(>1kV?vTXu}lkDQqS8a(ei@$o6~{|%nL$Q}$d2hRmy=L7C; zg&pGO0RdCGPl_gYE4+x<)1uteBl`Wsei*=^<=uFvra76EM;H0#dORZ4CjGbpvBiLv z_dyc(0;+VOQ=&(S5@mdvN^0)>(Vw|V02sXsGuuSufO3@zNpHpIk5Dh5xo{VQ{RQCA zdolKi_JO5~6}i&AhTL!L_ZIBYPCkT&g`_&jPMgyTq>pOC=;NRqpRZ z6wq9%E5I%SIA&gQhyHtZpwri5$#_@H@URz-ko5&WLGRFkbVgHU$G_rBteU*nNDi|# zjqy##zp7w68UMn6@Zv5qvE$#cU|SjgE`fLs(5qnxI>Uo)*A#L}^e1O-fE^S^1l#sT z@-_%tfgw8njkLs*=xf|G!r5((y)-Z-sFwz^jq$iIn3Xp4{(4iy)L^>U=ALD58&JN( zIb22mF8{G5vhq^0;}8x<&!R!N*4q}-yG3dG9wbSbt-Bd-be+;W*jtKeruLFYfK8uZ zZz})fF0ob*#UkZguQ@vuAVM)78-OsGcXuk zHO_Uec0uE{1~i(EA+Z2ZrSc}F&7#EkQjAh_he}H`-k%6-=v6!6)lxQ&1IHQ-y(5sX z1DZqc6|ffoHvvC+tEzfUHVz0wjlhFL#ybdTj=(FyE&&)u)=Xdx>1^=5DRFv8F#TgG z=_XkFk@^HExe%l-Y`n2R$<-kDlGq{0Z6NJ=Q)V78>pqZ~&fE;^8TXn&TnG1ffYTmX z&phCY9^ktuQt)6a9ks4S;wnHZ_H7bx09vt+I0Z5l1uFJbLZP{0^P|Sg1$5xvr#4&O zK35tmbOUz-LJNTA8vQreK7d09?n*@et2YZt>vB-K@%jSIb-4rVWKs990H0A|sa>I7Xg{j-O|7Pp0lig7Dg0D-p z=Wy{ppqD&UYWDcu; zAiv2fvpFYDzw4*JC?(wrt3Qu=^#V$^gIq#lvmiS`ek1X{AiF{8c}#mIFzW@7cbqvI zOpmfX5Fds6AW;5;OHrr?t!vSr! zn2W}0=Z-epsc=sO%G*1xHd|kZq?&D=subTYg*)Ai39`wmW5Vgay&Y8h=^1_}DT5so zHu`e0-;D{IETUt=#i=o&xO)XAkMs3uD^pbGOdjVSq{EDwTQ6SakYXG-D?$|ueM zJBTdu?al;*jX;ImM|ZTJDNn?`2mS^yThH8<`1-yn`RK!N$>%DUJJ+&Y>Zp_-jego2 zNaz{d&;oJ-^bGDK5_5slJ^2KgoWb>LXS}X}2L9vD5Kb20FK2LHOm*YbKasf$(B2Yl z&w2~c<@`%cN~zT0wmP)u+=hYXo>Pa?WWbJDPBAr2AJ~hu1Fc8)SfIHBy$AL-;QG%* z@$Ew{Gxq>Uv`Ei&FXpnr+vTHx!G&i{aL4`65D zwsr=VudnU}hHfL)zoR^IFHvth929hB{S4R^9qXJn-hMG!`>pPyd9dv2<#tI=fShwD*2m$yEAiw-az_opoiQtDqGQilfLp#Yaq7GYQCk8K;Jry0Z?aw ze+G2EIu{?;1HJUU3HJrM$YU2}J&e~Ku+e>gprg!RKSKC7FyM%VEMA;p2Kd&PeJT)n zaW;*$0@f`(89IQHbs#ytxF;VdITK_fiIWAn7~~BSF9D@upzK;Gy?b+h3QXAnvIlAb zCd=*s`3uzZfWgnW3X!2hYV0KZsuO>bj$xWoNa$gD7^AK{je#ctb>(XkUkW0w{# z38?#nNR$Jk#HCr{O3HT1;wW*cZdVJA|35NjkO`MOkH{QM9U&$QoriLNfF1(|%wfZS zo2EU6BP|6Q6-l0I*2_R>Z zI8~5MAn%fR38=mZq*CNBCb77f^E_b8^&sDG^YoxC#fNo)jwSa%z7hUaK*^&Z(Z0q@ z1FHW8vP;aoNMbaUp+MQ&AmM&Yynr6Vdo1F1#->I|rJ7FGo)7q4h{Tdo#HOG$2`C!| zavX^@z~u2Di=2TfYt&1kQZ@m`6-ZnTc;zV(d9d3XoXTf5G7=vr@;ix_JNIS1({mOf zWiGHCW7`$hH(RAOv}0Tl&_(GBn12|s9pl14M;RZFKh$_f1GZyaWIIM#e|ig` z9pepWn9uFxk=-?c$md76j`0;T{{}P~>joIF2GD4Hio_#=NHn%BVY>h*6$9Ea8o(9; z+A%gjJrtALFFF+QUyG95MO?T~uBK9FFPT7F6At2jHTnJRxfb19f zS`w!WWSs(xnFX@GsqTs;bjAnTl`Mt)H2iyklC>aP1{v>cKn@N1j;Dqi1*{> z!jY-$8>n9bgO77#S85PB5x;bHt#S-DqFw4BDK|&cAaZb}@wx%(%6JlE1(89d36Vi$ z71Tzcc@X(;i_L>biWB(XTvLO{rI`9FP+sHwY8pheFMi!rkEs;ls)y+u@NbYhl?muU zEZh%hU;Kc?dw}-E@121mPVEv@v@b@gc!CPxz6d8``(i4e1NtHpP(LQ1L#CHgVSA0) ze7QCEp0jGUo<85j&0f<%&dp0O-3zdfqgFGtQLkd2p1scO{ZnnUoK8h`jW~J7kHh zF&{N1cA1KLYnvkf%%fZh9yR)q(|9Zo1Jv1rojCEFgMp`vpIqRaeGQqH0Cjf1a9cVn z1a&q(+<2Kl`DACxIXgg>Ve1?%;K`dG&C|voC+21ORfA5o=$-ClG(ZM~VO`&(CGUSU z&lvwD(aM9h8jWS5*$HGD+_EAPuR!^i*!sw6N(Bx4vW4Lt7?AzFl~i2V)vCPxWpEpZ zNbXL6^E>h4E~n0lSG;)E_%o?V^&)SC@mc}uMNIm!c(Dn}X+Ze^XH&fx(jtQr45Yl6 z-O`JtErgo9D1W>~0m@%!fy{iX1%f=M1hgd%6VMO_A&(`+Ha?9=J&Qp(K#}46&sy2Ym4*|v@)ZQpyBvu zgJE{Sak)iOt#`0BPO zH$8{b%|srg7WX{w@Yc0RePxV{K`Ql?ZX+J~S6a{P*G-AqIqWxl;}q&sU&~{WvoFeR z4Bo&j?sBtV%WFN0%Fm=1ysE~%t**zM}9Ligpt zD&Ozg4x9d8NT>N%^BeN7igCK&uO=)a{)eQa{toEr{%*Q^EB{$8@Mie(Zlz5BU4FCt zD3-JRR{gpE&R^7n_fq+PE#lnLKa$^E|7Ly@sZSN9wfD)noiB9}dYB*HS>GZ0DT&CK zd^iUsza~wVk^k@;5{CQuQRsWpY2VIAsL+2=m>y+Km9bk^>of{;Fg@J!PdQA=IE!?^ z|B1Bk7tfJ0_C8$7xRBqFe;>^p_D`ZaMEvQrMASbUdb)od723*|8)7s3-_iPi#Jvf8 zT~~GQf3LRB@zrEYmTcM1q&RUBl2{%xIf?8ufPl43i| zgPnw#I?QEAfEFI54yC-77DEetl!pnFQeH`bwy)*Y(2>`^|M$1{Irp9`D+6i!`TLVx zowLs#)?V{kd+&21b));+K7v(uEj=u7 z|C6>&?h1_fLigW!y2zy|U+jLIC4_MAr{;z3bNpW9zRvH(?wi!P#NEMfvwMu+OWkkW zL#EDs3rH?=OBwNU_uu(l>e`ss6>by#TEnx5W+eyWBlX&nw)AfO)0+E#O(@KFO@Fa|;>kdbg6_8{GSt>5cAl{NCh#K+V;z zgYui*EBL*|H8A?E?oMECb+^%WjoS-O*19uX-{x)shTB~QHQU?>{jYPk@^rl$1gZ`0 zBm8c3FQ;Ua`$Hh#>}Jvb7Iy)^?d}oEJ6t8S*y>(I+fH``n76ra(yGh78+>-VN=Di4 zj??z#?o<5kaLv^3aTid&)AcaxUG83B*zKyRe}@}kzP)Y((Du2T!Q`FpLB_hvjWdh8 z-7?Bw;oiyhJ??wVH|w6_>Ah|jz4f~d^svXB1%?6luRI-ey_DqKD(Vcm`}p1KZlrvl z`yP-FyL-95&pk|U``zWV8gU1KWYkT7jWPEN%E#T6)SPgK>HmJWn;s^eg6h2cd*)kk zKcnW9o6UR=xXXd|pu2$ULvA5(9(KRW^%3`Lw0))fI5Yhf_gz{&;7(BfpzEUkL$1K@ zuevw!`zrSf>Kt`%pzSd?7f6n~Hn8!qTTcB~yN_`F8g~wOUhAr<|2nsi`mcAR%<&EG zIKOXnp9JPNxgYZTW;d6bZ*l)bKPTKtW_Qy4I=_#&Pf`A;dp&c1%>51}Z*@Q8_ie6* z-rnwR2f}x_hnd$q-EOYme(?|a<$nai)aKji5tcQdUXcmIcy_qxYu`#$$> z%HQwa&4?dxzsc_h-JenNA@}#t{lo53M*Ma6VIcoVD)=KntoXL8Pb1mV11Od6RIcU1 zE0ym?N+sAyHx+;8uH;Fm$ALb?=FT?V#dY~N*sAt(`dm* znEb4_EqaZ*x(`Cmdiw>suFObR)pRh8r-PN5>t+MU4_m2o-Rxl=r!sp1JGim(!`x&t zf5J?Ho9w18vjwIIR@=?O%>RW_!OeElocUIcn_KMWs?0c44Q{oY6`2PZIcT+;)tMSS zSz|Z1XO@8G;C8#&l6kATZnK;1nYTlXV4dCcW}Z=-4GTJf^WKcyvS3r=4sP~ko>rSJ z2K7WHt#R7zrjS|7IzFgHgt--B2|ccz!#j%EH9 z8V4_*yN{dKX4a{LzS?(D?L_7q0_U!#JWt+{IRTY|yB9sk&3iM-7YDB}a6XdxE6wMg z$_d(hD)W!J$yRl9^SR8GAvgCLY+uY21kRq!aXR=CpfFH4d(jirtNoF{xn|CsD!9Kg zvzZE&89kUQf(foVv4To(<7Up+0jn}Irpq@0RAuHcmsa<&xNZZbB5-{GptIjFM-SpJ zwE1sr%&mbfzZBPROogA}dfq+IHeFe#@=Eu`3#r1tx%#T(Pvfc|Nriu*s&}dCbqy-7 zboVy-stw|#%sdRPur-e zl6!cV+6%7&m+60B_%$B>7)S4i{GV!iIrC5dBQK1hG-`I0Z;e-}(nZ>G{g2H{=ohE5 zGNY+9eL2V7Gik}>z2ElL-(76AqLRU6_0!K>Dyx6ZM)vyf|KjFbv5|N{KdBdSe}|$^ z^Z$kKhhT0`oB{cJ64YtTX~QdUo4Ps*~Bse|1bXHEgs~H4dg$j==b>l!lxs* zOdx+b0dm!IgPcm&rTdQxbZYLs{ z72O8@Hi#-RTsW6DsxteEl>TIN4+o*~QdJFUH>bMVca!E}b#0>8)l{pYAQKOxg4$Yo z(Zw8V8Kf9?zU$|wuUyp5XFOo#z(8qGiS8SliZx%CwvzeD&hVqsY2UKx zQd%x&_=VSdNNvKmEoy~*7R^ept=3$DiDGrU_$q2$Nc9Ws0{`?vfJy*;QF5H>>e}j> zYEACq5Dv|=Gt#Xl+-f9TRV-%8rQ++#i+$be=_YkU@rm{0pWN7ziaIa$f%d)19vp1BHWw(;|K#=lCv(Gb9)&n{hT|l zF9Et$bHxJ}?6~05L8-n2?(9-&a;DugR1?0ot79B?JuaW)j%vs?Z?-D+lwll_?W`(J zPj20=}h$(PJRlTf~Ed$u6U^Tm+sC6O|1`nDJ#yd+&L; z(eV3{)d*LTJrTDgQRQd2za^qqQNBiul^J2wScQwRYU2U*KVAiuMX8AdosBN{OOjg` zjm`0P3xR(D3sTOE##++o0Mc0=$Bo z6Pu;EMK!~~B@I?Ly||2q^~nw++RTf!gD}*PP)Lm>Ii4Ta!P+HRq^$F%GG+kt3(Gi$ zf9+S zNi^Ng;e3?55nPbiE0`O51$F1szCNb?ymA+*A-0N*v7MYh-4ZXDZg`u@>?JI?u-F6+ z5pfqqHH=ebycZ`yR>1`c5V0$2bfJ8)GEW2#WG0h-$#etWTm}#Z{L*rtYe{q)4Rl$A zZnI}(l9IQwW$b@# z#KH!DBwNeM%9upU^nQz7;LenHUwk4etVrs<*i5X91jI%pPnH~vgjso$vhJ=gL#`LC z8_HF349 zw)so)btS_kUH+WTiFXV(CR9Ch!YA>jezVGB*MJ0{w-V&|&O-wkza)sH{$z1Glj!x< zXqz{pT_yNP2bRqk4%nU?SEzb1B+Qk)% zy{ul4Bu6B0kqz~+!9@u{y|^T(mpmr~fcN+f5GeKiSvX1QU(BiHH;q7Or6Zl697ru6 zpC~NP4v!UblVklO%LgV77xs;hExWE|$2oR$jk+a(uk7d@4V=d}Mf!o-IGL`o`>y*DssQ z4eskNEF0nTU5A!aF9I!Pbz?a93>Wg{CHcd78<^hO`MCV>*k1p9WPE`6#_jkxbFOfB zBDZRJe6@UFd~9g=MOL0RwdIrj_m5658=4&6GdwuFEWdAfbS4n^y7|zasUt_qz(-_< zJ=8;mmH|<1e9wKkfkMl^R0XO1vEhO7!CbM3B$Fr9K>x&Wc3^yTqF*(k{m{sGe<4{f zUl<(B4e=0DuZicmB*Kp`N$*huwphmhWByrGFjXp_|7>gUyuKV^Z zgYKi_q8UjldV=G*Cea6SheszyxDheN2lG=CCGrgw5S`Kf37*pVXn$cJ;xQ6&#l&G4 zDi1k*+x|Ut+aELGp2LM)zDPK_o;W;esNu2<#oP}L50p@@=i_60lTBeUYy%ua>Slo!8kW`1T}?@==q8lc)q>-`Gq0h z46Erl5!@u`CdwaPWDk!X^`M_9e^_kUtd^gOAKqbLHHY=hLG7A=)cF-*Ur^Z)T-6r@ zjX|x@(g9AmyE90KYl7gC-rxemOx(k_QtG0ip^@_1pz<|Aa92=ye-MPX-AO~*vkf49 zI!L>lI|`J#t!FatwFg0%d5UMV!V|m^o#m}Y_ZlIoQNu5F?|O*q1;OR+)4#&S`~aMG z_`cj%9VOwm2H$Xi|2GIn{di35*QmMXSmsh2aH)Ib z@Gm*Yk2N8U5q{gSqq!aB?s)k`{!abAq={69?x*x$J3oM$nZ2)q1Z#B3-#+nWz_;mV z@qT8$w>K-@p`Q~$r3P;bGku1{|7Bx`X|O_PkB8|Q0PT%2{@q^&(1&A;)A8eBrX@^= z=Y)F=@BWm#2m(*W@RY{?;6ql)s{FrF6+dn$(i!ybEv08M@@RX?2B;6+&zbrBFx~nD zeJ)UEC8qK7eUVI4HU$R1&b>zr9yByI1lNUgf~w1d>#h!}?hCG)2&%d~zny|-x=a*; z@GD0BpesmE^@i?Wr<#MnRi(p1_$CpY#)Co^9;J2bDR9*s>XGUtX}HidPd&L0LP%d`zQK$p?l`=ITJagj(>|#U+UE!ji>z$uk~n! z{V&K21y{JiGeJvhP;)G}cv-OED8(0wXEUcHE}3~;u90{lyWv?hAymFKeB4xGQ{UZp z2Mz9E@7mqe>I<5}#oQilU3>TLTU631&Dqeh_E&WsI-uieZ(Mgl&~WqGJ8!vjlhs^c zh^PqNwg^OK=&tK1miET8sQg(_3sQr5!iTHs4d%H6Ppk>*?vN}rQd}1-xHG8R9V}QY zsb~N(bApB2G_$%j40YIR0l<|Wjw$hrSGL5I@FXyD7^+W2o z_$Tdtk%HLnwGV;li-qW#HNl*>1T8JW6-Se}KK&way}?i&G`Y%F!}2XmrVj)!az}cF zLeLMs@6ff18-k`=g9c{os==L`@^!Fey=&u^R9P7`>=F@HtA~hFLih5tTOkhH=F#uA z^%_GCc^ZUt1rZt|5;Sx`g3(jKf^EUPEy8r&CXvFPF1RhI+kE>3_ni$ej++C`#7Z9f zm<;#NuY|=UC7Heu#bx@-9b$n*a^S_JHKvTeH=+ojbW1EKu&VB#S5Pu7G6Nr+)r*V{ zarr1vMCRuxxAy^F12)RNOd{oak$U%yROUqHanZHc+CHSVtuNHJBbc9gYj7ojf0l@+{2!N<(mQSPzT9yz{pPjqR|;fezjCczJNa@}PcsF#nd|f;B-sCN;dq z#4kYTWN`I&uKj&7`fCs}$ejgxcdr-$O8q_Wh8bjAwu>=iiW6fWq5ieK~ z%)gbPTGeEGZ-Xi=OIFDR7jy>mb8bgRh|H=>0KA?Nrh;a-^{8$z^`&0gL)U$dYI9)s zo6wo_VVdw>>ld$wR*kPJR;P#E4D4Q3;OdSRT3mCQ+ugn4PHS*=5|H)?CkCz%GT-&I zgl@vaeDe%_Z&2U2s_%{JdyV?u&sN%GEM&$f$ueL)x6JgF=lHPZN z(N$-e=aMrq<_v$D^RnQAKEvGo9*P>EI2NI`+1(FW-JA}_pJY_nyWZ6aA(Yp;b-j?r zUELd89=bH|id1Tbo$~=Rsn*5kdHB$OH4u+bU`Wu#&W1fVTIK-wGq8QT9li z3Ys;c@NE!NKc*tw4-XU- zy)P*nMiv!&se4CWtd$%;W7F&ou4t^@iO<# zSwuvhc~R|`&CI=XV2gQu!JKy3ae2g!2gH9;#y>NrHRmLJEU3&J3y4C3IT=~1%vy1N zP}3L8e-*cec>eA;zjuhMH82xyo_;g)LhsxDM7VlxzxwFR0m2E-ME}5_MGVQ|Rxtu|s&St_*dfutcm8^&*BI@e64PO;_PXDK0wjqHu z;+l5ve<2QyJ+H`1doJ^C94aNH>ABxDCwooHGlCJfWSWY4n-O)X`>XSzettqe7I+0) z;dbK8MJ_cS#q!KqzW9BoDKQ_c^gk13ri^nuiA@RlNAE;{qGz6~63^aRdgd9~o}psS z2-Mg^qF=b`8Wd>vW%xCkLeqoqjhab)Tf{U!kv_wpU z%!wDLCfw`NiY?*Dry|CBgZTV88B1f!;?2kZV&F`NCW6L(gE0vT!B6-fj`R{j)wtjI z74^>ZZ)Z*!+y3-?Iz9*6MjgLC!qDG6Z$p8FnrRYh8E|#zK5dTbd=eSl&^F^5o(QU4 zUe4o5Y`8>Am+{|H$pUY!N5hXdgsrHuD$P>^RCE$u$y2a9Hz{Al)gN_@y~a(opATww zQ{;_%5WYR?`k3jdrRw3%V3BI9wGrm2M9T9WrbrjZw_81L;_>3MU!A#Bf21m|4Ksfu z%6MPG%~jL~Wa@#04$e@6G&T7o$yT3S4W1k~tbbWKmgl4?5Eauek#_w}(llxJvj=Gg z293R*kK2Z3tWs{1fNJZrAugS}SbhI(${hSjgI|$mKi;|{ABDK|o@FMD!Q$PxC&%J> zT}757xYo_HnFt4!JX^ru`tJqvdV&id4(cBX=DH)~;y?)GY`|%+3?JbNv?wB2X{eJ+ ziP648hZODA8q9qKy(_>H1a}*QO39jwY(&cl zTW*YxkGb!pT6&uWy?g&de4lS#_=*UTt7f@ivHOX^hEJ02h=xkLx3gR&@z75}E&n*x zK~Mkw0K<{Bi09qtr4u2|14b-)`x_c;(p#yls^xR;kp4Q5n6(skP3WrSho;>JJ$91w z{w5WecY|b+?rM5{!tO*c-(cj~Tg|^TiMB9BL-1uN1M@Na8%g^dwaAcY3SZM;<&~%) z>r@Y(0WZg`D>GQ7+oor|3A=-asGO2ck~(`$)X`TOVvp$3NQFFRXn2kIWd=M1OWX&T zS+)D~po9`o{U*rl0}A_Qg`SKU@HK)$Q?hw|3;XLy);q$cJh_d zNne-SNrF?!2FXz^UZmn96UftEAj47=Lf6%y`}aH8Y$lZ@e=jM{2JMfI)yq_sCttDE0cp_NBr3HFKU|WMF z?ST@W_|sMQQ$^@A(U;H&j1Owgz)=<-ru+h@vq%Ir|Z{3VpKqX168$9P|aiBhYx!F4skQes` z*FCZIj(~(%Jpf%thbxY@1`8GhSKP*Aa79i%NLby-*ko5(EV|I$9f?PsyF7jLoG|#m z7;?UQ!UKGv+i4Ze>WA(cZo-FW27Q@<{_Rw{_Zfk%JH!d~cD|$b(z3>ArK8LO5PwGc zY`**GbeujfVBZBW2@}nMQHt1GG=Ti%10=?=DWQ_zB=)HCK1LwIVmQQJq*&w*8pBeu zc#&xo3QeJr0i=bG`WnB-z+ixch#}2ShDt2f_iF8{D!; zdz_$Lt_rF~**;{|edM8|+%;i>4aA01_RQ_o-9)C(-PTJ%y}QqY{DT(_aum^$ z``mw|P|ZZ*iDlM!X6D0O)#$yjtzG zTRa$3vaSA3{q8eUO&q%D+MxFCVBRgkqFX3l?z8j(%LDb{+?JqrxH)r{a;=1*RyB9E z{*-)&`Iwzo2brBg?d?J4Bb44Qy)7@gCW9ez1sQc$(_%UpHb16hi+4%7Op&KE-|lT~ zVR@_0?UHkKO-o0xDs@LQ{PXqy+UB1+}-)U&WFr4)vHZ=3vRF zQY%|3a)+d`GFY_vrjd*c;nfirsYb|(E4i*2pDXm>f$w!r(Aec64p+xqh}T#vdfXJ_ z7)Qevw}bStp#tc_`lj7QEeeJ2KLL6x!zBvmlz#P~T$=>ttN?1kX1Oo1cBf@baV57T zN~>;QdGl~fQ1v?fEd*5`^6a4X%vXyQ`kMHD0j){+kUQuK`E?R>b@s`oz0zr<;Y0UDCdn*%u^8^NuQatKk+FX&gB_~0Shd{kk_Kyjg4=4z<-J;k zd8z>`;Tlw@n$>2vM@C*YDf$-$$WJmPgk~YeujbIq+k~f{>o4t#ZGDLTWJ6QQTvXgK z4_~T59>yutagBEeiyq`|uR3fA8gC2gKg#WzMnxeS+SMp*N)MgOs1Z9~C@mQSBP9E8 zo&{wMJkMNT(z5JaGDQ;^A~iptuB_9FPP(z{-wov2G@lFRoCumv2SgCntab5c>DHk7 zEu#I2U@n)sdJAp+T4v2qz)9m;5?1X>TG#054})65EL|<5-c&SJyaM)^G)>U(Y_Qn& z?nqXpKmB^ZUrY9rie!deL(h>c1S5TNFl}DvU{v1qAJkzw%{}c6F4^j@GM5`LEU6fb3CZKC8#+NEM-l;KdAX9 zuWa0SE4*nwZbBI z#%qI`%{CL^vK}NV%JN^-a@rw zK9kI~=}-5)LzyArFWwC?O|P2HEGFp*WL5XM^lH3PLf+IE`_H1q%5$=~<>5`7l=i#n z9&SJqP?X0=?pL^fdk`Nm-dOh(`in{YGQDP*i~`8483KWyMv22QntpnABfu}g-E9EWp-8&GXzMA z*FP0Em-%i%_{ZEY0F2t8G4mU8%iO$E0?UlT-=v0&)=fVO>T*G2KB)WobOKa{ul4Mp z*y3%*vGh@U4li}yIX$t>k4&t^U3gApBcGwYHB($Rymt#%ou-cKwgwi+9}4OwC_Kmp zMo*=6p{sf#SazJ;suCCLTooHN=2hGrUQU+$MD z#N*?^Lbud=7gwoC=q@=Cw1(dfZg+#d!8NW~c0Jr3v<~9do#RP5xSj1)NTT+3T*}{T zLP>qa9W8gBR?acp!cWZ^K7UMMc(bh*!&x>HWvFFA+>cVIbuF>AsutTaj|S^>CAs|( z8P>JdnDW+m&Xpw2GV{!!)vdV|RWFtPq*pEOv!qhIT8!eI6Va`<8$5P2c$up@%fg=f zz0`@Y;xbm21NU&c)qJrR|9Z_XNz!jZja9*ZA~8rG~x`T)Xq z+q(j8G6hD^-9fu+RkP?($I;V%FsX@IN?~SB>8CZKDtV5Wt`|M!d4kwD!4s>PPqX{y z2Z%J?>!9i?j-IaxuDLZTyxhH(Lcpp&A?~~>oLk5gT0D=C2{oPZzaOkvAJ zS0gP=ptN###TD~H_xg&Xe(fZ5fApX%;6%l% zWJPcTIc08yuonG@vop_ltVKIp?u&%5BUtooa2qpQ-3r${Z2s{@3~;GQQW`UL5ds+B zvDY(X;rmvW<`FnoWsWu~AO=T7|dn>keLB-6R9_GVFmX2g-t`W8`Uam#xgqKcD4eIzd#>7 z*V>&Kp*R8g0yZe}?YD-fFtR`zyjo8Afr!S(v?tRWKEDZ2>`IY=6#BG>WX$eu*s`5~ zsAkoFF8gO=TQ2f!hID1LvCw@K#Q6c9DMw)0yjWO#;m(6#JDYLI9XUq$CH0b0ytwAE zUZRgv2Z7PDs3$`AG;h{R(0q}5wZr>vSR{$6sNq1?W1NI=sS}(vnCXaC;>Z+-lct{` ziVrZIckronF2s%v#ivpjYT7v-N=he4IAAn5GBlvmBb<;)1!)cyoH!Ysf*?R5G*5^1 z+I75Uh%27HH-1Wh%`VY}|3Dq`Gz)D*Ar9$WcSy0uXH1c3^$-bAI3SFGw2MxJ&#LfS z`U!2Vh6gv1jEN0M@Pj!_YNLG zK-yj2TM@vEm#-6F?tnpFh%fEDn;j+muQ<Elk{g~Hz zJ6{%^i7Vb0&&%<6`l&U}_9afOalEf|YRzt9V0BYEr^a1$&J4u>6dXc~2hZ>8pU`h27#+|8MYwQ{BV za$GGpr`i!iq}?KF&u?sSYceO`dTl0ucB`Y!8?{U}O8Fa9Lp?)1PeoVrxIURXgPe8*|`S0YH8IT?AoKfPpBb|pkS>kaPtd;l0EN~@5Ig- z3JSqmcQJkwW3T2I(=j~3we`X3p#R zLYF)3%hSQ^yMxWtA{P^cpEsQxRXNfTRne3&IcayPsC7p(S1;>r=5nV_*IjFyqxy&9 zk$+c9rfcYtBMFvVJ4=FRn}xVn9DDTS@BhZ;VZySzWWHdhXe^RL1}5NS7L?$5TJSJ( z=stD?Iy%gDnl(5qm185jaN_7m0e}nlX}9+x0w~r|93HbGTt|*Xo}eR!RaVG_2b2*d z$vx+FK@-V8H+7UkPLTN2h%V`+p0aH=Rzv9Os9`>Z79Zk`@{;>>$kRE!38Gofaf-%| zf|pN|AaZ`w4;$SV8WQjFvjh^m$M=w{vBEfmmom;qcX(3nvgA)i+z{G45?9img9@T; z--C4v459l9+?gh$rBP1;*+;prac3VSg51jUV-cFPv1BgyU79g@5n6uOTD}7nC3w>$ zTPS3`gYMq`5H+mugbLZ1auV~4d3Tot={2z6GO_aG53#g!C;Q7-m7GeGIhpI;^N@E# zew1SGmy(KATbOC5@t3B_sZO~ia6k~=m1ueX$iZw^`Ao8V(z`FQdsry#{)X)Y%Csb9 zjbnB0-JV`N{0s0=<=*#TZxG`jCi8UpuOQqH8BS}Ej^G#<)wu<%UfR89-t?4d#_?WO zuQ_>9zZsN0Y>1reP68y(-j6_bgwE;6y@y7I=h>#C5inRmo*1bkHo(u;MwWsk?BXO! z!mm6AJVzrDd#osGY4--9X`$(+<{cg22|FanGmTihsGioBwJ=@>Dv#Q}idpUhFrstc zCx^P+z4yUZU)mWg-=fwH2x;j4jF*PVcz%`54KSUON3x~_C&ufMil8(So*?`=5w4uz zDPII*FTsiBR(VpTg5$^9WF*0Xy^a*3?jq4iyU((zphoAU(kvV|H**B&7y!;@S&)(e z!E(~HhVr^(cO^^ZGK2^;DRhLnOz6hc)jZZvA95- z;a2jhpuU}&TP?R12fedUu;Est#P`8S+P%*-f?}nMu=&i-3=j$DHwAMw6}Rt{#TeD6 zxcORq8H9gg$+cb!riNmT|j}x4v#TRT6 zQ&;IQcUKa$tO>4c4~Bb#ep2zRK^qrO1TSw5?y?IItQa3o`d4uClIZt$#tLIHar1~F z=ev(RbV|?{7{5-qbl*TPf+ivx_hoh-zX(26Gp*t6D{`ap?@MIX-zFUevkF3()okyZ zNrl5=_-G{-ds~vlv;gjY4v%#w(b(M@Y>v^mnFPDA7yd*^suHSi=na;Mjd^sAVZU!i zL44XU^di~xe+fyjDf%Ji)UII8CUAHl!Kb$xpEd@|*)_rCtgV5SV6?n`u+*;@wc7o+ zib#35KYBohuT!dnZ=ZsYj}#g9Vt;-GC*(~!smk-@@&1AN^DmW2-)bxnF{w@j{>F4B{i(@ ze>o=Y{7+%hACxhv>A@fW3Wp`1pGj-n|1-TK{#sJxFL`+09Io}T#)CnceI}PkPnwT5 zCkPKgoZs>OGHZRZA@ew9Tj?fFxwqC32|>EQ$;rsP%b3NlSmTF(7%^3~Pu zu^KYGL|H5j5aw0i9#mZ$EM2ROD;!ycpp|`EAAHO-7xmcBDYvqEC|KGWtP~UU`o##- zcf_GBD$dX2S!P|lO776QFVW(ve3PSp#f(pAs&P8^n**JE0!&INn=-s4MR5Q zKQTZtejq8PjX*VSzf{P4lpJ8O6URH#zhkyU&NWsy_Q=CWq(BH$WL4#`JDA!(G``o< z6*D~43b}Wi>U89HhL*i*|BNuM*P$pQCzRg;%6p;a`?cmC)E2nhXP1?0gpc=wb8VNX zcGWEXaOsI~9tA`*MPMfKd&xmTg4qCS7fhPn;?v3Vp~B4MVU(}38Dk;K=LTT-_5TXc z?Cm5*F+eL)@7IB;L_HzwoPKQO)>7CopTIl8ffZ);*+M#Y!(w8xqah}y(aQp=*TRD_ zyQSS9u(*|P&mP-M#?pNv!$*US_DY3$?MH)+eJ2RXZS{e)hd&+c zh$eljyQ063*kA1(As=`Sg3ontX6GO6ehMWc?=c%U+ejPm!WmnSRYvyC7X%yXp)xE39lEy_%!Hj27M@55hxCzz<5Qjkb@^Vqd{M%2 zq6E)zFC0om%%{ zV=5r5a7~_xtL}jy_xtO)57Xq~An<3y+1lxqQ3dO8L3fl$;Bb{#oLPWxg>MURAQ!O{ z$5ViCK<;({HJx@>$dS4u!y7ZI+*PK6n`KiPjZ4#SV&Ef!hAm-LN7R+<%01-0DtEf? zRdF&0a(!@0M&&z-+6+cX*5-s!ISaQ{yN{td#LMR!mCOq>#fz~jpFkU?-P0b7$f`W; z`Nu-L55YgNtpDelmQUHpIxYdlJeh2K(@wH7Up9}ivIV?>uTSd)2BarN;0?p09L~P1 zMADPbPtr#qX|E?Kn@EZz)i`T}fyx%)`2^X{geh5(ke2^kQ&Og{r<;=RKOaqi@&{1s z{}+4mgXbgJbkibXLZ%H|Z(n{|*4&f4#(gPux<$hBee$m{r={IrO<7Js6zAEll33%@ z&G(GJ8^z>LN&kBP;MSyPp@)4lJX9#*8K^#3vUR4G;)z4)HvtrK9=VfVvXz}P9*iU! zH}PL95nT5#I1=z^rS=LLz`x)G8FEeYe__%rpH?+b7x2&UpxXU090!gP0*(u_LSAFV z6&xR-xBsZ$_(pc$rh(HGO(fx;oiYodRZjP1xGBbaEaA7v@2+V14?2%;wvnGh!a-qC z!o7T-igD}7bC-8G^ebq7mY`+(bV6sIn&Ih;lrC#3N`FaO<{mB%YJZA*ip_zs2AIKK z-kGqMpZaB;N!d%=)hcK5fHz{EYo=>}&p#i{fbwWU16&*a_00D6CBav8cJEBWSM(^q z@O&hlNdp`eQUTXzhQC(U+%y1NNnpY+C73Yn{_cQ;KVeP3t~yxLa67D$nQuOT!%Pcr zwvY9-6lKoYR+oA1-6SA~&LC^DQ@OYvw#TYTloeJcpsoOYC2=6UIB3 zGJfQ`y^< zl#gy?$qwBAK0D20T+2#5k9YKi=hux1U;mLA0&TAwy-Afn4zH2ZVQq~|B8}13RxD>j zx`RW=ZZ2sEVB3$y#lYsj9(gVM=##BbIs4*WYY0T3> zB->{qS6YH<%V3{V?5exa{UKE5&rw%Wc3vS7T=WIu9YOjACjAj{!e3=rPvclB)qljg z*L1ZsUASYllv(nU^b%Mm`!kbpt79+RH@;-yrfY=z15gN8>L|n_MuvbNMuWXbGX2N0 zIAyN4pQ`7d>7&2e%lm(!xg!J_;r1yGW|028sROdc_>J8 zijk8I$;C*HLy_{RIwaMfa8~C&D)V^U%p+d5Uk3hIycWjkpnnW3(K#WB_Iq!gtHU32 z6J$WmXVi+8wrsla)@ThZ@}g0{VrOhEE3*u%&G4kG;nV>ZgE>qur8)Z*LBmacqoR_A zhXDukq;{6Zi^ph;$H*5uw=v^2a9iskj00n&(bMjqZQa$MLc&U3YW{%c&6v@qOI1dg z7|unTHLh!_HIWqKll27-shlP~CyK{)#CQH{^ z@?K+NjV$V~8%Iug2ZXx3mIC%o3`SjegMB7gv=)T<9kaHwR^VXZbsUx28#m}+6-j+X z7JeSEx!Z8oeY_JD_XgD~f(*jrcLD#ZMKoGKk5>J)+2(k#5-_a7>iGG}uHc5&;F7pq zRPKi|4BHZ>U)g^Aao#xOSZvmM$uq19MBYWfvWDJ#;ulZQ&Wm~J<{^0^PwG&o4*8ib zt9}(lw#mA$c%?Dc8e!}#K}LI0uvQld3!uPRw_7%>^i9U6iF9dTZC|pGp-yomu zClOigT&NiCxON;Y)=n#p9nqW<%plBEuHTQy?%~h#VizHHQa`QwQC4!FZF9JdTNmEq zD+nDGq5N7lg8QBHi(~bDsMVKjPgu%gWqPBh9mC#guPULAy^w-;Z5C)^$(BD# zrH>suE^4==$%56mk3)2&{9(~ghU__Bw5Ih`FK;C`#=gait$(#MfY!LsgLdoPB(tTU+OfVoh+#+DTHSNl_w6O4}8vbY>%9?_l&y&;LD zAKCu+9d&2ovf0PQdA9}gZe}YML$CqJcp#F;p{!;Y-}~~ zw9~+w58fi8{cP?7RIPE}V^X$t%vbg&MFH5ra_^{46G7Sau>Dnnz@NX>AuVR^zQHM% zZALMgXurU|`}UZ#oV9*ZW1v_8#?_E`S8RR>V7cSJ|a+Jo!2 z28(XxB(2Iq_t*JeosGJU!_WU-Ew!OFgMxL6F-vPe)Ge4K0c`R!E)j+g(HB!)_oXzR3tc4Y6AW44;Q z(m5~!-g6(V?=ph9KTohhb@)~4{mzRa`{|j;9yx4~-KN6;RuZo~K-8~BY#`c~IDjN} zzQYd2LHx%#A5uwln0DVipZMgi!Qgw4n6!J>b5Q>jRXp{7SS0>ga~%E;U#5y*sQL+1 zq;c?&FFC>1xj)6iT;W;w6P$fa=zlwlVcJ@f=Kd;`HY@)WM{U#Y$4pd+olhX$*d5H@ zNR{7}Ig%5OjSMPQal)HQw)OJSOy^fPHb!wrnA!T3uL7+-ks`(UoSiu&H%u+V69xUz zn+4T+4(Y7gys@oW-PtG?5Fx)Q@@d&B?z*2j^F%>Nx{bLmA@*;gbT1wB*=|nr=M~-V zV9sv^VK+!vBc)?Ub#aGGiZ(0XmT0q5Z5r<2Z4+*#lt7ozyOp|F%M~_(#eRG7lG}rt z)lBOVskZuHj!<&mj8Kx(=R~#4LbG~$u^fw$Qf_ZBZ?8^HQT)suw$q>I&b7AM*8bna zanlwU5c&J5bCN^$yaV8)Fr5fC_wk@w??LvunFN^y};xBEJaIW#(bT`46j&llUndi@~_Iu$}YM;~V)3gui_VC+m!>&^5lQ62jq+y*fmE z1)ARvT7hr=W_4A?>k{ZxZn1ldf9FEn@1cG-0uIF;Y?ZK4~+5_EP=^tFjB>aU-4-Rh^dXm8>&K!6%?-gQOKG&If`!XM^xZ3;^ zfRukmQ!F~3o%tAdzc~p164&YxqfZ9I(igN5+7W zM{W~~h7YRm0FvW^&O*6Y6>hMVPaMgDq}CG6`Yyfev1oNs7P@g}6Bttuf9L zs9z%Az&E+Yns7p+H3$cSn#YHxp=s-RXp)cS+lurx4_mwC)7#C2-s$aj1vSfie;IuK z3^!#4d}_At%cyH|a`uIi6Vwif;ck!Yc5Z!m_iO$hlmK`NpQlIdzuO zZMo>X=0mWFw0!H?FTL!CqMAYkxuD3naeC4^ zFz&ocOFt(ghI!J+1J8a*$6C&_Qbe0)uu=00uokaxtY)7VC$?gX_t=&o{1~?IJ$G)Y zxN}C+RjsT6wvlF;Yt7V%*UqzEI=J2p3xxjTrxRk zOP{BQX=-^U^%|SymukHPq*9u^Q_2YBKEuS*-L|OQ$NoK}=;;UbR;Dy79X0NqJgrm; z6P#5;GD(tNofF#eKd35jRSZAP-V{@)KaAN5MY z^(r4WQAirJDz}OZ(h8yOao!1HyYgjm(r<)hnMEqcS>RiUqzH+LunW@+4oT}+(p>dB4IV9Zf~1S25sf&VIHBjowv>ub~h6|aa$zK>zK-i5N< zUT84wTgvu@UwcnO^sQgsYVe$IY?z!HC}cOacW&e*2fUJ@bfuR$jK^<2h(52&TM$yX zsJss4Y_u-D2zK#@wl9B6PQ^Uc?gV?0>7#!W1NbI&uq51_V% z^vHK&xHuXtrX?Hpo*;dYHyOE4r}-X%2l`3_`pO9CD`x_Fy+M@(`Vm{oih%Zy4QPK^ z2DG+>o(J^1o&)I15w-}f058h}{g(pzVp3Z?;jMnS$H$Bt^@@ya|DjyozAoIXPYgHn zHjLERp6m$U-X1EXHto!A&W+_JhX=B{-Z(j#-8P)h^P%J{e8FdqRoYXS%=M3E+s8Le zjSZx@9#m;3o$tu`cH45J z9~mCZ?)INUPGz?r&UTHDLP4S)P@qr;h>A~jH(UTflx4Sx-eyaaK|L{ovo{?NO zKYRqlY(Kp5&_H%4-|J@XfTSsm4erSCiRVHDpMAJmJ#6A_AX&o!H|y*h(VO+*>KF^c zURRES^_<=gtZ=mfi|3P$s9v=nq ze8yV!Hh`<_wryE@)@>4qP5qE<3(!II(TObd@Gn}SuMK>wnwcF;+1I^Oqa#^;eVYe& z3=azRu#321+Jx6_-qEI_^}Ky%TmM87OWphRpeHxN_uK6`pW9YRavELTJGQlTs&vET zFrU9JUBx|#r@8=S5Jct+{jeP%W!t;f@7TDBtG1r??d=i%=tc9>J6Q(yO{Rw6IqALj z4XOP7FyO?9CO?^*$blyM*s#(1I5;^B>*}lC`nG&(Y-&_!F|HMPG-q*FJKsM~6(+KS zIpaS8D#XTu)U$hYcFXwqeq%PV$ao|!)a#xqj0{5(>zi-D5APkzrxFg0*S3dj2FTHn2)y=!x}ZA16EjoHq&o$X!Om6B>d){Q&5)ZMPGP3=2(Zrq?TdfM;Y2xGl`*T$Zm*`0mcC5hYG zyF6=&PA0?Io#W%k^j^psOHNyU*Un9=+r|bpu7*Pi8qf*J?KYKVY&GgR80~@15p#(p z4(9sz8v+0%DQuo5*`EEw)++gE`*yGHd==w$7{lIqKz*)kh%h)nQIYBU;j z4+?jGK0AO`FW3u`veB37X(zpqLfhvijmHc7h6nbKsK_>0-#d47L%eKfd)LPG zZQHXwFW=R+!_;LgoQKLM--*<$@1Mvf=(S^G+lK7=Ep0negHtH5Nlh7G_KfFqsfoh_ z{R8`Q*$GOA4<#C{TGzL8V|HtM*9MfrNG^XE^*x%+$rgA)NdVT-4R>y9-wB+X;PMR- z>*WShxLIhhY+>A}=}kk{WK8JX(+>m@b=K`URAe<3#q`*`Zq;=wSE<&n_OZfss{nD` z`1pvaZ!=sxvA>72{euTg*B#F02OyxKZ)%Tmv94o#zG)btfMl|1rm3XBw;%Rq9e&8J zi@@@XmR&yrqGIbNJ<~IU8J2#H<}f^vmmtXGw&&YNvG@g~F43dvjpj!8V4H0sJ^QHQ znF95ao#>y;4h~PE^5IB>`aH?p*4B&lFM0_B<|Q2L&u7un`H^81`2JjOB5Sl%4?EF( z+3cRF;gQ1dn8qHMIGlnR$A*UY!l1)uk|bQQE*APX4d+G%1?$jA|K5BG;W9CSzbK3J z;JCr(MSd`MAZrqGXj}gwm3I#Bne4~i0TXSLd$Zg7C-b?TQxm8}uc4?CD@W0t_UZ>+2c&-Rv=e+;Mj+fNi64VR ztY^^A$hoNcM*_wXZjIrp3IgwYz_0s_1LU_u2vI!=uHbSo>~h z+iBi|sMk3@V8$pP(-Byp`p^)}oz3daajUbQ)Ui0-(bmyC<#T;{TvxD#GW5R;D*YL_MYzeB$CuWx%Ys&PP%6p_w4Qzj#B}F#TpEz4wdv# zk=KFJER8%GM8@0;T(!b5nrj4Arh)o#m}3n(k|QW--`2KSR@~g?9h=v+d2A?37{M_q zqz0xY^N8#8dc7r=NdCe7QUnF?jlr4LV}C-_Q-`+UNo$_`Ih6H5NNV}S&J~mp%wOds|w23V?(pu2*uFI_(9+tqt0$bq%G3c5evmQ@18K1 zvJ9|YU0b`l@95HOaWe~(i2ihfZUN3C*=;#&>p z@{!6hZ)6JY%4a8VfHrQM4tiu}q6owITPnm@h`b1v0jFYM1hh?Wne;X-Hk>pk%S)=f zbL@jG6DW>=F)kO~A}>Yo%BvlR7ZQ>Msm5iK4iLtOspK;Zph#iNu>p)%Ip3_5K7^4$ zi}Y4^4+?}js3Lil<#jj3bJ$>=XV)Fh-kF;mFR#>Ie%o1oyFE3T--9+8!4jAQh-otU zi8v`TMxfQaL0R_!rel^(7Bh;(Q~OaksnLjIhT#^Ix0oIGj$v+79*GfEnUVPmk)q#$ zKHGkH6KbYlak6ICldvsv*LHO8!hzy}1(7%~6890)`Wh(atvCb*<498ZbUTsq+`*Uv zd%QyhyNO5eLS>r{8->@6AF>!=Fkd)~ax0Ud&VGYV8b1aiwYh6o^ZJ!HHLtwBWmU_{ z=2a_J-LPWi$`#F5?Z^!_Z|N^Y50>5ZvgT!@g{iUJn!P?F!mY%7&7svdF1zvi=4E@E zmvvv)yliM}d>I}CA@Q>DNZcvh-o8G;k04BnA3v!FL6!s(qvebFj-hc+QRFX4(O5V5 zfFug9Mze$C11VB6N05xYarH!AVOw`scd8#&JcynqzQa43AbU~tIOATWjEb7WTg{a> zVm5K$`X?r^WN4Lr$i}b9*6jcAupTyvtXl z^t$%VH;5FwQK5*KgbraNxK2plWHAK=l5E7tO~7X$f{|$`r|$MPfL(9SowWN%ZftL1 zpGFb8b}5Dtvn9+Q-r3Y>4uOu1byn6Icq94F$7aXOd}=C>;>t70$W2vBE8@U}X`}xg zrUy#eUWtJSMW{OoF|J2%)3wId8D8Z82V%@W4pu* zk;`c*oSwtjxkH*d`bap41rGCD3M`VCAy%{y|b51SVDCSqzbmmP>=$3%#bHKv+445y9Bo@8@l=2;|Riwp&rn>4Ra zGfFC)L>RWaZAW|Cy3UP+1wJRTy>IizF8q|8yLNP`ov~FpCb2SkI6Lo4C-VxAl|g}{ z`t$j$csve_i+ZmuzoD4mur8zIM>71}yb(s+*tL1*mef%H=rG=mG8F1TeY%1SBL?{dTU{Ab`!Q7syy`%-l_F6)7 zojJzaC&%#+A~G5kgyMtP$?zPoQ~4r={Q`Fr&j-D7D*({saP0Qsfen$ePK}u} zI*#M2>SgRsdT_(|fLBxr$O%fNh|D)plUSyGs%&HL_KiDsGVLf)CUdiXUv6N(w?f!^ z)8#SEEtS46H@tVB1smp1XtD-O+%ww3^fo|vv_&Z)AH%IfAMf{BL?yjVe#8%{{60(% zo>P?LiA0D&#Sk-XHZrXaWPJH415sqD8mb zo}&Hv!{qv7f++}*52FxF+=g=T8Pc-UPF$9S=L=>5BZA;Rj}pYoVBm0UA{C4hlgpp; zlH5O47~dz@vHmc3_Tca!5(vCWN&y$H0er$$qf;)GE>PtzNSK6w(kH(UDiF`7(zfEy?eCyhMo4wJ)&vZIh=c!Jadz^}t& zH;GKzl*pv6AD^6@novLpa|%OXO6fCWm7eL!GX*+gk0>cpnz{tJMb$GaZJ2tNRd*MZzyuH3?F`VJ}*_Fe9k0LGd+*xb-1aP1(u1h z^%FE$JGLdFHx;8alCiTTwS5wmCjzCI#$B((rOkhHXW#ZM*-cs(K_TwmjCQxBJ250y zLbNk^rgagI7%H-O29wu331RQ)+qSNITGX+nXSdHy5c#TQWI(cq{W2UJX$G>ycUu)+ zL@;-aXJdBsSTLtz2p3Yy5hqF0^nQDhpkDS_8xRXd{lqS-V(J|-YkC0UWKyiYuZOdv zG&By?K7_EJg%Ixb%6>86#5ih5nKSBc#&{S&@c>2SZi?3yEJ3ayBWoEj^XOz_%HqXj zRBp@|2N^L?1Yx+?S_}2dO+e>;WgmDG<_dy@^Ue}kUuqzSkL{;aBKyWeg&Yb~5i6oF zMP{|Q9$OY<#SLSe5w7KkkNsMg)D&rLp&vquphc>B`LgL1g|p;tnpAB+++`LM2S$da zw7MHd3Q}YF?A`&(32%s;#PWcQ1eK|V0dR}h`?%w}Rgem+IK}#f*3iTj`4~_TVXKsy zrQ~XPR;!NiH(y+^b={&R?C#tUEg7v!4UF_3IbxPKk&_fJ#QMSL1>lxAiI5#F)_Qf( zHZY*IiD;3P*g}e$ipivx^3$w-`owY6$rek=Y=L#O?&BBF&Aj<(F+-m%mJ-4&jAHoI zhb(mDm*7Jv{+cPfWHuT!$LmOuL5=K(rDX+TY0;^;j2|h~uH0Un=mV_mdgC7lP?iYX zZhrWBA5l#Yxjm;O*ED0*;XRO^oo!tk+ICpj@1;G$t~B0y*}s88qz$435|H|elE?Lm z<0r?lKNO$8Mz?NVcdz>Kv8*I`J8ooTGo>F1J_h!eLa^8o0`vJ?Hm7yZ;#!Z-U9|ON zH+1h(u)u%`5%h^n7L1~J(&Kit=nWQ+XF_#Qsj(GBG<7lGHChs zXklVvnDC;g0f+-k@Gu`FQc6snjJ1^9S+{HF&UiZO6H%=MO^!|Kpv8cGk#`8GwXnmR zOT$vpjmhRGMtt}i<>_EO%K_rvuwuhpjZ_i4crQrFa@4t&{|wvctqAZKCeI1GlvXFl zpfxUq(%H7WMbv2E3FUDug96>4jsX=bw^)}E4fRG_2jGv`0&ha0$617Uv&hz*qBGzr z({%0Gl{c;vNQzQq_Xc56C8$P;%%df{$YngQ-837luDUFRY)oh}(>_Mhhy{mHTaPj4 z5lXX8hHmgHGUH39HNj5kCL^x;ySNv2N_(ibmAs)XHobai(W+CHp`XC5$Ke_rYuno ze~4G85_ub1KrXB!^9F``lQ(^4Yl=_gRz^2{@%SvtjSRBeqd;_MTb@u~to2Of2~!lj zOZeitk+w!(;?4{kO@$!8s;JC@G-O;ko>-hp&mA-)7b$S_vBkfg3Vow(T*VDjwu6V| z}6WfXKBk8X~iS-V|&0n zk0sK{mfcymareefabyxlMW@Ys=jXYz@tPma28li;(9u2~n4Fz2-u;}8L~#d}3QWn{ zB;=BEkClWeWkNP~u#m*En0f|o?NMA35hKD+Od@aZ+R;t6axAjMoNTRpIE9ahZ$x5# zcxxeI&U?)ZEO%#bx=-hr2kn&#v8G~r{s5$ znlNi*`1B!$j4yj=qE%cJO1y1vcx-=mMBC`1X%dHKyN4o|H!fUHK6#pMKwPz3O<5Rp zLSko`V!=GB+DAn|EUXCEek~ORq1Y*fDL){)C;K0BmuN#)NrAPkbnGGh`sfAS|U<$d7opHi?rHL))h8S~GTpo?}tq@V> zH7Y|7SF)mqI`Ml!qCI#%Y>qcYAdQOmZ86_xGP^eRn(d9$5iz6Br;)fWuBR4Vy7jhr z7URcKK0Xtv_(&rw)tN756l0fUceIxh^B}FN8nDP-R`Qe)C!iWXLJr%9AF_c-&EfZP zo@daE72-roNskD-p3(J#-m<5xNAt!VJGyto{JU#R!h!wnE?R@w9k6{>TR>E~^4ZnZ z&OWxHezv@NEa&Rg?=FC++32VIPyeKXhB!qCXDGw3iUt!qaU>?mEK2{NE$b<5T8fww zJ6~*5?U0YOd;k+qCdQ>@5fVyDk*Y;lWjVg~cpqgEmdK~~J3m*aNQF$YYZD(kN{6Uo z(nD4ns=J3hQljht+a2O0k2l;N=|vxXs$feH`P|4*a=I4SkCv9BOjSmZWF<*B7}5B! zBkSF^0LvQ8INN{Nl%uSm*VY8je&fvZF=MQDij#)%h@971(np94}0jf6}>0n^*GZ3iSvt}At&^zEa?iSuGdIo5JZJ)7@U z5*mdm-Z5Djiy~cB7Jesl*nk5}jLzD`;l*v?=rp`5#rkqd&3yr<^kkOYJjht?wcIFmYOY;r9JZaV z@40@xSef2?@GJ#|VXKr0!J8KPw3|q^#8$DS$GKsF=~q}2Sz-d%vP7}k#?GJLPBiX! zBqzCur~>C7`)UA9_tpH`KMpOSC1WDHbec4OJsUQqvN%5I|LT3?<6+cK5EG+IvgA^_wDC zgkfpEDCgvRh-{+yEle$IV#uBVQgpfP1*5%X@4TXqAm1`tVArdix`-2GJV_)vlm=ZT zv`#C#US-L-$FW>Aah@g!5mAA{^A+;?jh(gza{d#x4R^93a+_t*6?8=!8{TNc`;PpT zJZ=+OfrP)Ebg{gy&bbiT;o9%RvGeCLps0*BFhtw*qeTF4iQh|>GOxu9pK(bBH?j08 zai+{Qi=o<<7EM3azD0do)WC`9@7%GgYkeD*mqyw<>{DS;7OadT`quARZ>k4PvyvHS z`MWCmTSR$0rcj=bRK9{Bt)VIi8eqS$7RaW7xP5Y*#6rQ8R~d(G@9btvzIj<ZKM_%2JQ5R9ni>=@=Q&bU!`TO z?4}*<>)JQ8+jjVD&&HiS*{)sNvfDd%^<;Gn$@V1WOH1~}=wMiBrf41i`NmQv8Q*I7Ak_ArcxAa!1w_MYz5t8chDd*gFIS{;M8&QB(S0+vP=r{OfK;~{Z@U0eB$w*0}diOE3%&BEcb&wR%>tcjF{d@k;Y zp%ekgxXm!0Hu14aT;5d~MHA1W>1bxrB#DFxVh8=L!cFg<`_LROZOgiB&-xwh+i{mR zbhg>vQ1okdC!1FCMCqf63ftH(-`3ThC|SQ{qfUoxR8bUaVaU2R?m%%%;cf2T(U;xX zzO$2~BR!lV+Gu*FG~~9nEo~j;U3Rwj>`IKVxlQYEr52laY}~jtwKv*5Ynok715P@7 z6r%+#xyj1ZkQu<4D79V39-y{Pga0{!#pmiIO41(ZwjEm&jW=}b&>Xw)bBV(49ya6l zwWT~9AXg$@8nBCXnE#KvHvzM&suKOrtx7@|q9BNfieV5zQ4>*&1DeV}s!T%NnW|8g z0tLi5NI;aNl2%c)#SX>+MZIa;0b8_bRHWO9c52jz(n1R=t*EFxhc>OEwzl|R`|R~w z``o?vt%UZy*WdRKAGOzBXYDob@eEhqXA&Fc7CPP*Ke};Xg^a?Xd3$6`To$LAo8Zfz zmzVq!iA21-<1F{i-9y{Yakn|k& zKQlUV>YC2FnbzdGb>%Z%p~ zYh;~!C#XI$x^AS~pSUAy*N-^=*&`})nj%jb`7;=QyH%dQc($Co_baWhURFHsR)})1 z)fwcUZgCn6bo?pq1-F^HEC|{id8uyvR5#D~NBjCaU3e43b$?;GqI1eA?ggiHBWq?> zkGEq~y}7!zy4hXr-f(qqw#-O0$D1x*t%^(5j*dwmo{_v=Cjl)xela7)$ekNmG0Biv z#;$y9ED~97j<4#hTwM{bebcr z(YAE3kXo&=Rjb?b>~6J>`&^R#E<3M4xw<_%Htrtt^`!Dln>@|-oD1YR*q6Pae51H< zN3C*;>&$1n+bN;py2+Je@-Th5WY$x-%2jt0Tb@>2YmEC$bmyCncwnH#JMzYyESxK$ z4x&#TSs|~{tQP43w|;D7-1&!7Qt^?u$ls!OVRA=^b4sl@$GXz1XF9FEo;|*1m9tcK z*_-;3-Xyp+fjk&m-ncB68s&pZ3;@m5Ya~-8Z{=;gF^Rbx$py97j;@zs%PA*))g3}O zKZU;9=iZq!H@h>cYV_91dx7H;{LyOvT-A|9>13aG#+zd@(gv}uQR&is6zf;J)E`^b z9IJYDMQ2oACN7`WpSh^gUb?}WR+Zt(NlEb;c>#6YbFKEb=IZt1`L5+7q=!!x{{=WMy(UFD=FZ5NTe<8~R-Qg= z_4{ST$oh`kM7f*L=lF-`&MzNH3VheEUfo_jCUf4T%&q|~Clc}ydj;VHjLPdIyYhju@#+a-F<#|KDV+@4O*p~L3>L|47 z0(rJFkh#1W^W(AVaO&D|_sT?nY(__9d^=U8_Fr1PPP`VSvV1i_UVdFWvbIz07wenj zEa=?U%+0ehRxdrNqK z^lV)-mIGjUOGm69IPm#ZUb>uFD?ya=r;#zI&H3k^DKq*x7s%;)@Z?BW-URRSgG>Ue zT*{X(Q#ZBBeqnBc&`?9TBGZMM?Smod%CbK&4rAWb3YOj-66n zVXqwN$YNc(_soc-<*BZp3tz@|or>gUn17b-tU3MdpNOQVxkq}Xr}g=De2orpRh?Fi zm%GTZQ)NLT&z!l_33-2fthrKF!{*V-EEzn}9$z;uVQP)7^;uTFoG*TtR_X@X8Ea8MQvrl&P;T>1E8X_WAZ$bF?F`+y*Px$P#dTo$KUsI`2+@qnNT(W1*&wY>%{0 zTP=6*10zYWRU^VhmQ4Mbx2E$<8IIdkHCA-w)lXSsx?8LM-9>NnOU^Fu#<-cNYUS!R zlQIxX7FCC#Ah|8$`MU8ng&t)**R4L?d0n7ru9lsl5R|Jkzs1`mZ}DqghJtiGCysNI zMBuSDA@fV$48@LMrnA74Ly z%D6_b+z~`MBb$ zZ;<&dl`Eq}^yL&-aLe$OPG&OPfL%x#aYSl#comUOj|a^0n(U2=+R_tg93 ziTP16w0z#(_wDljS=flkdeEMn_BY1oaw>D`Tr}39RBV_WT>8p-s&9sp^P8FVuD{D6 zj!-he>RKVV#O>$S%Hj5N-N8qB+o(Dmj<-hERCi>ju9iMCmy#`cO*~4Ez9~{aUlHWH z{Y#eitb;3Ga5t<0f* z?hGVyY&AAAv$}ao)j~OnkxjkqX)B{rbswJO6ffD!8J3%q1AAF}t?$UeMR}0qrwIuJ zD-yR2aIo&jHTxtM5-*=na!>fhFOb_nK4jIXNEBwuQ?^dIu+Sxf;}}+LRr*O-mWSEp z`MwM^?qTnYC*yhEpFyEg`MgoNeX~Ai2VLc}Q%RRwy{@ySx;sHrgwODbS zj6f1Rc?Wz{j`aFA&;CSj&II&_lGW1-wtF%Gcq>Xr=}LI!S@KBG^DDyF%P1K=(}o6HZU`?WouUBw01=1hU&y1tB1~M z;u&c;aB>)z@x5=SBFedszDT-Bv+78&!f!nXb1Dy)lcV?ttPL*S7d4OouoSM7&tUNcW%xy0S8>X^j$xSFthzff-ULJ3c&B$2edTtOa z&)Um*!aCnY%L691GJaR+*Z6*1c-GnO$wI#haM|hBs@na}s+>2==6r^e5O?F)KlmLWvcz^@73$l(x$)U<6Zn=FFqsxFaj)&l{kqUi%3XTc8O-Uym2x&O z_aP>k7K1BfT_Q_^5n0+?Bnx);`8SE08WQgf2PT9(dMo#w*JU>7viKUEksBml?Q)EB z2Niu*EbBKyJKfrm)5_Vg3{RP&1I4c!Kdpbc-RIVo)g`E#sGWs&qrhkXK-PZ9N@n#i zp0fb1j_K{rx{lnVlI@6_;e)Sbzwb8=YNGU5nPFuc>UTxeE-1&2vdVH_9g9!H>@Y@R z+LFaMj*tj&%K&$s6F0FWo5W zjFm47`f0GdmFK?J7TA~H9<0>cXzkUr%=7s7RFiMJCE`8<>FZ-r?%LVLOxyK@T=x(NOAP@TMYmq_sb#iki z)Ygu6R*jq{+cZ6zEGMCU&&drrnZ(PE>c1HI8_M^MzcTvq<;CHc!VvXeV|Nvb(=TKPS%HRXF!JdOlbz7;$3#4K%iHwxNQ zW=?#((Uc_c=tgj&7{5S}H3tUmLERLqzY z(@J!5FK4CLQv_Rz@SluzLdrYJGU)l&M`DBBXU#;p7(J5pj>D!3I;{9XK2jvFMTc*k zVbs!@Whr~{b9tvq?8A!m{r;%YUIF7P3C$ zsdY1{@h+iO$TQ(C5#=VCywdHS*Ge=gtxd3FGs{}VIh*7?w9S(7<+H`%Yhr|aWkwVK zUZNA~6D7krmt6iG#Mo4EbH>XnWP6lln_`zHbS@)vVzQ~0=EjPo@yY~~Mte;{Gn!!1 z&gnXNDsOGlGVQAC6PkL0N!{QwAuG};lJ>YX*C!Jpu4S6#Dk#-kFQl?oJ}Y4@Pn5=S zTsQNC)G9Yzq>?6AuAE7IQhq`_<~or}nevg=nUrr`J|`VBsAoE-OaTd9neC}pMf`-! z!R2(8`qB*~E7FXUMrY;($RM81dnJ#QwLGGius9_*%8TNa$tXL@l>OTeM&(ePC2}7F ziJiALGsd`4G9eTta<-M|lv?aa4NftsJ~B@=!(3ag2V_5fR_as{EcXvCEZ;cd!$h%h zXXa8%#`1J55N>H^2xS@}q0C19%*;qy_P116iC$NRw48==^QKG?#dFVhllfRC7;)M|;oK>k7&aj+5~ORNI@Mv*@$!_Jlb&#DIx0BLQWg%I z&!fD?e$iO2ffFT#*O^&8^+KwuIMEGC8|0lGd3{}9{&2x_Kl_sKmyka`K??7Qj9o50 z5?UGfM=~S7u#k^m#0lng%S3I(nu(^%&Vi}v_~nwIT$CkpxJfWTei|~@LlZ9L4kE!y zf+aXdnf4pexeU0xHguMDJNH15>qq4eQ*4r#BVrAGn^Zx0sBQplngQ!F*;h_U2?vKw zv$|X-rRK;}y;%k_3%NAQY~RF~Gt(sSZ?D9LyHPsZX(k4xzq^ncFN=fJdoDhS?qy23 zA;d+b9G3?K#P!dVr#4SDI8&}9!sOV--LR5Zf?~t9zb1Ic#XrZKs_urP%p%3@&1FaF zzJx5F-oN05sjzx(!jP}-mJvMZ2@*rObtx}4o+ams;uW{yE>{-rm!KBQPfWRgCumtK zI)I1CuS&Up^NL^9Iz11^f1jkyo@&kOfGtC{PE!z$wW1X>(z^T(rTiNjD%=lNrT?x_ zM%NneI^drS)dgH2zx(C>)rvacxS_g$Gm082mh^yMW@L519fs-xeznr_E`@(x!2$0# zYLWs_=pDlOCj)&*EYCRq;6Eyv$b@##+tZ1 z-#cz`%LUf%<3Wp<$X$7~8aiTL>!J~n&A4dT+C46`OW8XCFINI`Q@DLtP5wy5bYQ%k z5$e2=;kFko7g)R5In*7l6?7I#7nJ`{EyEa*WX!x`FNhWx(LKsBW25{kV6nemCTWE1 z4T_>0<&!1TFpkLr<9`3aO1MVLwdJUmGW-IrSJdF)m&WU?vAiwf|5Y2elZ1CPbnh8$ zA(8>lQq*9{G=8=M?4bE*EKg}Aq0jrJkl3knK+VFSA zpaXb_%@QrZ!wl5{e6XSh>*zQyKHBp?K})xCQ}Co?{H0p45%9^1@;s?M=MhHef%q>% zPBbBHz*7v>0fe0sGT1-+s4DxLmUX~~8LIUO1?QWJtz`Du5IlQ@S<=M<)E6o&m^Mq2$xhTMmAOF0z0Abc z0kNi5Or>I{a3Dbus@Pk|aQLmtA2VfL2N>i`r*fu|6Kt6VJk!jp0|srTGujC1%c@6k z^UU+qms`!JGdmRwZk|cl4YidOrSy=PXlNfUQVtatFiIJ5N;R)ozWSJ+5h$=A;kH1y zEjaorO{+j(D{$`HRLSAu^6R$+0uR-OiW-C=oh|Rkzsw3p+DqGaD)aYfDM1Ac?5uYL zcJ)LNLF^r~T+4Uwl^Su&;Sv$I9V#{z*zo>f79eH~73atN+^;0dznI}Z!thP=j;{4$ zuEWKqS}*1+9*p_UFA(~9(JVQ}_c|q+pfe`T0K0<1D1;VNh z0Y;l8M%V^C*-#xo*v+*L8P{eTta%-^pA%3hczZwvr3}(^gZSM_?9vVR2F2aKIIln5 zxNxHq925~qE^+CmDoKC3ap}8*#F(xM)QXI?F_G2p@1X~3DY*c6;X(eAEHwdNrYN5a z)V_GI5gOYJ@NgAzf{ChMb)cvFn3ggGe^bGq2NYn}6q@?i2l$5bO!U-23T`k|J)?vT zAnsaQHF=?yE~>x?lgWGcESBD-8k9(@s4m0Bg|*(%TDioK()@3t!kq_#{()Y;zg)ijce8a1;Ax-ck6rj~TES)ooRTBdA;1K@KFHMLrq&ICDt zs36E&E!FaRS7zE zGu-x~y1CRXil6A^dyQ;@T=6xeUS)jWEh?i+2*GqtxD=yiLa$!^nGMiB@HL4{2y8|wv zXCfwcitFqjSAKJk^Kr{zYxfClpBd(B#f(l1Ojxy|k#d5F*)2mfR4}_Sv1N$n8PU}? z8zthL$#2uk@RUS@?We6}CP8V(43|kz;jVeWTz;%_ba?>0Tye=Rkqe_rkl7`2sZk|~ z@<1=Kf=I-~F`&Nq01rJ;OW6Pd-n__PlH3QpO;LUWNbR2c86j+QoIuljDS;4yl z>U%2UAY)p8k%BJ^D8OBcl5zjiZ9mou`jp666F=K|>{TlE6I!}}0dF%#6KUT2v+JAA9Z6KrxY5WI?7{zgL@lMoXtP@D0W;I>zS0 zjY^PNIdJJ;swDkdbBW<X!|`97@k(<>8}KiWg<}V> z%5m^B0cA6r-dt@){fbjv(qFB(F?FzvRG(Gw+lHzG;$!jce{G?HkEQ7`>gG``OFCS8 z>k@BCiKOx!Megr7zG>e13*R*Fs2<4IF0Hh9r&fqb$>voWn19x?L=HdEH~TjYQHhMh zQ6fFvz`yd)Z1^{q_r7QuI|;ZxWbF1WM?Ko}IaW)VRsr#%=(A%h6JNiccWf1>9_XA3vf|VoTQg4Gu{Gn=9a}R_-LW;})E!%c%W}}C)Kv#rc6Coz zuN`esDk%cOsCIM1rIj` zyQzZHDEMAeu$wB_MZw>iHp#f^HJK~lPPRPSm@1O47KNc-Ud_dGe+$UeQUxywC_p67 zE4CF6?;(839FrV#;Z{_5@*_QFotCmR2Yj4bC0YNDEmTf4Q|o|3VRU(i^^WgVkqr&O zg<2j`Tz>o3-^_c=uOCw$hCO!SpAJ9qm>yRzwt&?E8DdLSf(ic}=u>Lwe0^R{`u^>j zT0gK9s{_8@T;2f0<)X{3EiY7eYz=M!e1CA?^;@WXJ<#2v;2#6(UIp)G@v9Tk(*g>R zQAy4NKA_(IsFq?B>Hl^0rl@=U7COMwt%ug1k#ySnEsA>JuBctE4t}5c zyc7E=^9WT@5D=mv+Qq8qhRM)>uWJX=#m(+C|B43F;o`-Q!+l%>@CXgSP_eYfJj;T| z(J{^bRHNc>%(4t@4u^B;Mvk8;S_Nc%65CO|JlA-|L|ZF109NMQ9b2dn520gBsk#Ul zS4qC?PL?);++iclQ$x-JR@OVaw-DfAb!y4+Agvswr34hPG9mBYl9`ZqZ^=x^ySHQ} zwuNP_)>FGWiY;U%QJL9SQ&;{z>4@w zxAcp2^F>IFlQe;elFan~tn6-ioNp*7oJSf93fFzq#*wL;by-N_3KjYKaP!cBc`Rt; zgg!aZYKGA-Xf*?PLs} zHL>OoTabI3c2l7PB#%ovVoN5M$ee!QUakwU%fmtpkR7oM1%S*8!uICed~ov?g3AOc zjai~F3=0bBx$*<5$=?T!04w5eH1SO1qSK9u_!7y8zyw_)aT{8d$XK6!Bg9R*oVZcX zA6$O`zD;op+~Q3c3(_|2|{-GSI7>3>G7@wqXiR1-$jr=(}@-_)aPr$ zEh=78EL?od@^#0QJVBEXeLN%Uiv21lBXgPhV>MM}J#*a40v#E5@?6IGix`Kt5o6Gi z5x#9~gd4$#37UxSud#U;E+sNeOygq5udLtzYn2)J1KCY##VF2+*t!M&P6uQC` z!g^8YiY-*ISsDXY6u%-F5Yeco-R5sX_W^v7?W{JY-E?ExO*2DWt++??@AqNB2Z)|S zMSVBwP!1Is#%^*!@yk^kxZGF96F{w)?7Oa&WjeKBi;OGrgujQOrl0F(ozPxo6Wi&4 zmn-TJdD?$d@zBl1dBst0s!5+c;^^(Q-mg`W;S)VWnZp1VD(aBpf}(7&wKzh4tFbuZ zDA#bYUVW@JoCJKLq7IiM1sck7y98-IrZiKb`B@5X4k*A&6?MO2vvG7vyijM}i^B~3 z1_eK4s4n1V4AlX=(@+CC3i14k5jFw8YN&~O6};A_xMqKRd<|x!*0OuNwz$$-cIRrj z(OOQowZ&VlW%emkWS(g)8{4$SPHQ>9c$O0vTSV$tD)_~K0{o7m_LmGRV|{UPL}rya zq4^lrVD;GITERs6MH>l{4qYogtQY4X8a5*4;r!}HCl8Ljo>$pk18-Mca(jmhmnuQ# zE)th6uaflNMdA`?GKrYjNyFt@cer?u^1MMy88^NPyj0gV~7*>SzY+gY{6yEk=MBD=xXo$c5vSAaj$EOG~RH{f_1m zi>E|P>}Z)1l-qe)W)5%Wn`scAuEfqX;4>6=e_81Cy->r2bCuwrh(O+O;L;1LB>kpw z2|W`rv1uV7Ur??$Yw5HGzEg2YN^;?DQ>p(3t9uPdFa5kq(yuj_*yAN)25Zgs{!yB9 zPHW)hic8|lg;6EQq!E|aS4sM{=2Byjm{{vtaUYH&rSW3V-~cV1*c z%HKJq_QpU6_|`r`&nNk-A`jM5Hf(?w8L9(V<(S^QtSR$9Xv2r9d>1L;BTdaZ;BrGv z0#!g3Qx{l|)zCI0VbsoWb8Q;#%adsSrq#Wsp#W4i`F=9TcBk- zo1r>@?@*K%LhaB4tgQ#4zqP2Y)zmj_e6vLzA;G#4#MC``&p#CFe8sXBKeW5onkkaE)cce7N;IOoWT9yNMzNlJyJ^Q6@1zokx8=)Rtm z+P{Zv-91Z%6fH$jAw^53RPcLANd?c#6Via>s~1jfzK#YZODc;3@^`aT$gPqxjnCd- z(il!=Vt7|JhLf2XPG(|wcQ%HTxftF(n~ULOFB8MNW^*x|>}6tjS1yKk&t_tH*K8(+ zch9CVoXo{=vX_bBU9*`O-kpo#U9)k-bcid_`AHy-pPTs!V8bTJnMnodigF%AZzN!@ zCGf9p#%#^gvHWlw(_2qbke(^iIq|v9M)Vm#qEvQy`!j%F z4s*uuW~uyJpaUchr6zG760x!O{cWe}vt=no6@%YTP-o9Cwka zj-8IjIiZSut^8tt@87I>sHd z$evamO5}^9@uNhd5*aMWJ7>w^r`vc@2V}e`=}?;SzC@DRl`-Rv**JMi*`zL6QZ}bT zj+B&C$c~bd+#q@-B^8E-l9CF+EGgqqYA(j;zWQ7v^$#`l!u-ZrDnGG&=m0W&mvnOt z;2+j35yD$%seHszrw&*N$*r@QklZ?(3CWGQkldJ?V{gsPu{Y-C*jsImB{bDRpU9MU zQz0^?jZ`XOx^d3wbhs}#ROSlOtszLa&QkeC2ohi=NVm?;6{M4_i%ijLh=-g3#nBK{ zCKk8mVsUE{3u&C`9JeNs5IXwF9^!CovaFG|nJCr#Q z6di}BL}mhXM-rftG83XZ_9{eW>r9aDNP<*S#$hU-x3g*e`#Okl=HiCUXXtv4^A(4? zP;r#9Xn((=m~G`F#&S>ip}k>Ur!itks{Dc6&{Ynkh1RhqbF$~ub=vy>sqL-WZ&V#w z-RfG3DymmkWUN(LoI2}wsY$F%_mzX!$3LW88-rqXdO$I|tG3|iWeduyU|FP=5ut*E zw8;|RBwj@BEBDJ)R=kirz^~EuY8>c2)GY<}Kv!O7skh1ItNLaI-x5%Ol?CCQvzxUC zUt&GE4)`%c*}>TIFm|p|@SK1GtSlMtntg_*Fx^;AjGwQlm)aCr2YjWWCIFc#CHnv? z%gVc~_n)W9Btg7$mI@tWzGVN>WA95p$&4Ri=4GcOvhJEC6+fnFSR$$Xqax{3)JaUS z3h?LE_EdfuwWm(%N_#+dUZs30hiKp4*G+A2n&(CUP$asr09>QgB?~I+6p0Dc=^)Y` zkWo|GXBUEZ&9dihpl$uhRbA4D2W_l|-?NoEENe;G-&;op@qK1p} zxAmwn^@&5~n)rSZCcYbIIgFvtNcRAw$CS<_^}lT$qz+i=4L4eEI8J+m=#rWA(%^{P z+}beX?sVlX-c-NNs#|ty=|*SZU5ZO?6?5S#CCEIB!KJ^ilJwu(ku_iHIP&Hy(bLR{hmB(!aDj2`09HAs_m7RK0Nb_k-)QN43;bbY+`2}=PY2ZhR`5?+ zx|W4nvPetGZa`Ej$*LL{oKqzyWbZlR;EPP_I^Z`A)kOvxW>0dQmM3bP_gdQ)ZT}EZ zsP};212a(pcaQqB8pxBirSl8$sfvs4P{(ZM$v*Bruo-;cIF>7eO)QK0%?EqvA&2-< zG8^zEhkBGxV`{HetPlb&dQg?n!-M`J@8Kq|4fv`D`bM(#1^jD8IThMHYN#TFfUh>K z+JHMN>bHB;&{f(FQZdG-DmPGho~hggL`kP|!&JUD5CYy%v8~ag_T4}Tcy`5*Mvoen zOLhZZW=?1TVz$?Y+J}v>3y5DlVeEoXf0M@TZ!LaxK!PTN1WrI%K_UAplU)ZywonB@ z9vY4jQYRjG-1RizL(P44bilL2#lq!@n_XM{d`Qp?@R5e<0jXFO>4gwQUwMz%n@K7siLC3YSHK0|TIvu9j5R|zuDo^k2CDoOuL&!txm z5)*5k)YXP6{y-h+-lC-pf`C6W)HL9K^o@(u_C3sVl(v8e8fqGFsi8W6?={pk;O+-| zUar@6dejyij)ZBzqZH){sm(sr2mvoQR0r^0LrnuNdRSF1wWldojyM2M=_91}qCg0E z@58GcJ)HcFB0@BFn(NzuKQ>eg5dS+Dw0qQWkc>Tmc*he``$yBc1vvkRpmV!N?dfWX za0L8p#kh8l+8u!quVW^FO_`@09Fz+m~lxqNzpz;hC zAPFiD>Hv;wVhR-?+36)v!|6f@h{ru4wau2)8v)5#Pe|>}maYxJD-E>~@DqyiZK;vT zq75LK>JH=V^`zA>h%KY-seTkt;$7Sj~n;uRpqdwK?*DEI83Z-UKA{ zvJ?WWWVq#x^qp&r~j$%^tzrVZodNBz&GDHNqu20un1vNR4m_At15x zgwzO^5CRe_Pe_e$2_Yb{@`Tg~mkk@HHzw%Ch-!(}G7SSVZ_E4zST&$y1~6~WJ%tl5p+ZN}S|k86%L)}B zIW1IiE(t6{;$Wa?C<9T>vuv z$lwQ9Rqnn{NWp{#CNO7+1VAF-a<0*%#+)JA0baSM=_7WE=UBDf!LeWP2Nuit0a!IY zjKjgKDa>N@l(v8v;*@BbS;2+@18L5Nbm+ncbl+foe!!rGqNI@1YND4DmX13JbIwB` zeO2BW1!S<1Q39~)zcB6u|8--vq;0HIX3+5pp$hDVipz_bpTorr9WMSikmJG($AuY= zdej;7{bwB-@h>3@J^v`kr-ZbN4hszk<)DUpdO4l&Qa0uf7dW*~0SLwrklh!OBeMGY6* zw)Lo>me9pYxu!b%vhK(}5{Zht)En^2^(Yn}e91cNcV|0>#1>P=dzl@k3KiCto#SLpCdY>7hBR1=8M$;X!f@p0)r zLW;MA4)E3L_>vBn6J((SB;F+*6?`CcfF!0<54h5Ewr@+*bVJ1(C0H9iSA$9^Yn7O2 zxW>ivl{ohUh$)r)NZV-oBW*OQ=NTnpEVxgf1ttAPWz%opIs{ zrmd4TmYb|IO{Wo^PJ?$}7Jab{7w1@r!^Kh@E)KoJ#bK@1$JLG(m_2nw-5F3nQ}Dk6 z3gMe=kg6m5FgsPLAEV%sfT~J&aBb*!3HRxks2D;tWhRh?&CZC;tgMIkhG_mi*g~1% zR2w>16%WqKD9%O7tm60_M`y$h6?;K6ZvjM<8BVpKO&YWiyo};pq|7Rgr^Aexa2hW5 zf@uCe5K(3nciG}QB17g;YKTj7;N^--&Y-z4ssx!cXfCaTujlL&LywYc?yNbD-GF47*t17omW%Lt5l;advP=Fl$I5qxX9O&vyG0;3h_RWCF% zUYJU~@IQ7^$Wo%3^BnJ$OFt@Aqb3rgTr4Jf*u_bp$5JEEC0i=hemQ7=PG3X?**7y# zgkIN0I2<#(>tVGPhIDQ>V8==fnCC6iDj@1)wv|KGtWo2!c5i*ki0Hfm{Pmy(Aftke zp@2`XL<5+p2pJ%Eyqo~wA81oC6EK)L=P~0=>g>gOr^0asex%*KXyamLB1d+#-k^Dm zmJT9;mK4f(_gHQpl~C@si0!Xf{G;$&tIV)pv(}>uW_-V z#6!i_sB*G`LV^M9LE*m1*NS>il5KKMF=n~DEg__Qajx%4;aVTp=th3DiF;S>< zcXdjY6ORC*R%I4{wRPc*v6ey=YbdB7$V0W^2^B<{4}k;$0@N8QHZ26kL}?62IaJIh zIXP_1pppbxo$ld_8rLZ^+Id5Es2LwHzjg4)+cbW%hQ=dbu#qX*?R$0~Qa1l&?CKv? zHXk>3b--H;)lMCZW|X6ptxY+f}^iW=8<{(lJb=KIJkzX zH!Z)uX5QQwn?G#rE;Sz|M709neseIByqIX%h|UYWGbZx&uj$6?9@~J`0aC|YiaySYupB` zHr|tKy#{mr)u*5(b{)X2N-sMVz}G5jesSUU?aO*pf1zk^2elI|HM@X+sVGlK?R|j| z@H$0_pdbBUpB~lE2TH)(`&d%j9S8xJ4|y%UY-*<{R*VE(t0QaeiTCuKH?>gxMg5L4+$fLALEIYI#ZyrP`e?H;v%3xt4# zSe9@u52!9NXHEg)XU~$_ivl4a4)ui8{wfdxzSBIu#ia(-_XJA7k15Ktr1qIW2>7po zrHeS#S6H~F0N<=AG298K`cHup@U}`|+dXPO3xt5&J5*m%-N#a33XrT2N~h+1NrB^) z_%RmCIv|}`_NK&9zK~e{6BK>)tpW8e1wR;2^v^E_6g~0B0Y&ud7J(LV-WpJJmbK=p z7Tw}C0fpzU3#d;i`1OFo?T48&Tk3!T#bXuEl1rx5XDe!#mV&}h3~PeIDMMyR9dnxj zg}Dq9(hzeQ5Co-ytZ=`{?f+4ZY8~B%ieoKRE3XNv{rmW3Cg~ zfXs8zv`5%|dz?(k^#jlBO@2 zVq;w1V+|Qm?WN!<7Lp>43=@f-!vRxDQ^HA*99bM9hl}ImaEVRKJaR#Y%a|mp(@n5b zRuY&M2B9f>FKY*(0wlIN_2GamHa1PgW0N$-{4g{PSZv@FRv6M25Fh!K0m;k?LkIy` zUFdvIl@*3i0htdm zfCO8EK$S&@Py#Zts^(EPNJIA@Nl}?WNs3+CLPmLlIHaR*X4c!M(AUaQQT+}GD=DZ4Mgkr97M z?r#9DP?R5vsXZ$Y0=~#l9l$5AFm`||4K?6Q&$lRwRLeG6o@|n)0ny7R9yL^xSs4)3 zWKRN!YC;7Vv`mf)(J!ZA0}YYp)M@mnAyAG101@a3sRi+KoIyK9QYST0;i=}NDZmwq za>ANDYG@~Y4iN3+2m=t+gbFZd*$htckan<)hVIB|%W_Y3xt0=(H!1j`fQprIEo0HS z1FTYZR>}{jmy?dl`p`MA7>) zW%s{KLIV)tr5C8&7U-PL(LZ7MD);8B#gp>AtlmrS@FK%47?8 zxuVL}RKEL+YHMoWSF9)rxM+Em(8GK47neQC{L==+Pckm$~dFb6Fd3gSl)P z5GOf5v~w=owJqneb}x0=bjD@7w&h&b?xikk=UldT=h-;xICEA55YNaQ33!|Nr2#l$ zewhNS`lVrhnY8?v0>lr_ferHt_K93T?31MiAlV^Qb0r1#ooMzo0Wr!+Z}zBt$xLhl zqJ<}A9HiE#RTU)4`2ij~&3x1)ArL22u^2(+WqdD@&_V(UDGLN7kY$3( zvnz^UD8pZw7wdq*is@8_rS(h9u<4wP-zwjE=KMO~A%>cWTMm`uy2ru+ayJ11e?VEt z?}~5hQTZq1+X2Ls;bPM^OWSwY2q=wXmz5l2wS4`N${(hZCin-If{GdDrrbXMusze% zZvwu`Q1#a-_@RJ8_*Uz$O~6YHReytmLB)EiV&-sFbd(u!leUJj4Ca8ZHyI5;WRlRbFmpkg7erEeGh`UBKjiNG6GN#9jTKhsjW zBj6!wb~zF}NGm6pFB)j{8(VcY_C_Og`#Y8RO_MzFV+9W~Arm-!rJ1xgzTe2TvT77Fj6G=sgQas zB%cp8`BxN}QkC*HmGVh5uLJlkLpAnBiu)Y$Uu%==v|J<~VLsqb^6&7+AG5hQ>hp@e z%`Tr;y-b#b^0m5&`Qc1K=Bjt$2UO}MCwgznR*c_|KHoerg;P*SsM0jc z?%{4x{vS5}EttL5_z&E(d|zAtNJ}vU@cGtyBMgIf8_^DTWggsg_Wnfq{wDATTx1G2 zy=TJTA!`RwKGeIE#ePrlZSzzzR>{AEmFBT#RugbSQA5S%L;mn2&uYF#*x`VK`5BG< zWRH}0RW!QhiJoQGB-djp;`Ufb&X($ApOf@cmGpZp<~(5SHS zqp>#{dCQ98<0|nklf2>A3La@fHsJ7F$l zR^QU#%6JL*BSpz$-G`((oh&-t&=~mL&l)f3U8?Y1CZitn%PBZdwuElJM#U=2tCZE3 zjO8R?kkZ^6DQ>?0kaGE)mWzs)*@S#ianwzU{+?ZaN%b;;{_m7GALUyuKiZcvQ3F2f zk)T?Dn-%3JTWYTign(~1)C}NX_qC^1Na?7O#}YOP#XaswUma_fFp*Q0qhuRBjCFX)dIZAP}6`X+sK@m zBo=GL2v=sFbE&1vg>syAe};-l4Fsi2{VVm^zz=IFYk^0q&sUl6>wv^Sr~vV9| zOXYBL^)!CPO(k9Ipc45C^#gtpA%OU$q}xxObAK&!J-AlR^W`5Or~(rxT`O$(l38l` z^NNsCKh$R>7nxBlKBn1L#TE{X6MXtHh7apGBJF06hraS z6|?l9VDHl23nNh_;qMhWmph9ZmJ->aVrfr3YsIiZ9ys6_Up_(0;bPxE^{AYoNQv^< z3T_K1z}Fk9!Jsp1Bg`~o${>=PeW8MH45+>)Qjzzml!pR7SD|s}-4cv0b%dFE|amR_>!|ZaHZGP>__*r+o5 z13s%AHz={et|{OqXUIOUi#sFLhFDb8K$r%Xg(Ed zE)y+^2PLOsR|{3Fqo4+=h?`|1tE+;q4vGQ>MZ2k@-Bi&oik@v?z&0reH|-ZfxPaJ_HH$HMPx!4ftOa=YFVg;j&{EE<3gd>Ve04F8dbeaVfUU zaf7*z<-d7D=aw?GaRLji6#%1eFR-x=t`C7R-1ZO9vmT z06x|Oe5?%kpn-?nZ(sR;U0sN5))j`_83nw^HgobBUMj>}=wjsr73-C`&swf1$~hco zyh1};?$>u|<-Hc~2H@3(>U>;5Qb5+BvFRnZ#O@M-;FZe7jTdJqyxD}z#2j-UlQ{xhY2@W z^leCsVv{zxz?x0~zQRzQ*C}{qKmlHBs0QE-hH3)dq^Q!lRK9L>4Zu4M)dc*hHI-)+ zsoY~o+24{CAF**xPO_+k9YZ7biQk%I$hS;%2Zo=pv9tpSyX=9NJRz@4(C>c5c|!{q zAG2`rv7<+9Esjc7#7+g;{LvI>ep&@1Ewjs>FK^A!a)FY&^oX10#wHvws_oWkxt}cb zb}rnsaHsqSt{twOE7vx;Yv3mIn*2bcUTf~G?c4|SbM*2)#m)oE3&^E(M`}I@4#SBv zrPG{~_Mhrmy7cYqXKQ+(&e^i`08Y_4A1~=LcMGnc%^jO(ZWmlXi;|S0Xy`iu(#t#a zR>0%zE>jEejfR>5e6ym4ifxO5Yj)?L1;|Y)nSB9o3RHk6sc&R)3ixD2$ydP^1HaqI zT7cg+)J8z=P{=+RFu2)nR&YB)sA4AzDt2dP&+R_8NNjPPOhoV{jF}^MpD<^syF=R+ zZI85_RSPh%OAb%`o&Kwp-52bJ&NL2hn8WRsRJc`>+z=SVuA8#!lCyzbvfv!5`BUdu zzoFs@J#?iUDxTPjr8v4+jKh)Bq5OBX{FhqFXXnDj0M%kTx%*C&1)9{-_jV<{M_AN; zue|45ho}S6R@UJ`n1@*4%D2b0-GL@ZZlF-1x5)|;5N}8p;o~1H@k|AkCT<$dSeF9U z%T$F26%#ubPVJnxbKdNd1s4`a9ev==g=e}d1oUnh153I?ZqSyW*K)q&u=6mgcPRRP z#`pqZxpN`-pX&94`mUEExpcH<>_FWhz&{7MfO+)}xcsVFr#{YEubnB!tLX?KVE4ZW zy$&cZ#}2F&>(u}plNlV`ElV5?xeRW9)Rw3%sF=MU9MZNnqCVAna|ti&blvyfPw^lq~`M%irC%fHR4Wic>QSR8P*rbV14 zXd5OeElGsZ732LkY~*XsxMT`va$E`3Hx2I>X@lfMiE*VsUCLpc!OocT5oq;|GldO&~| z7opby<>lCewc;-I9FEBh1VgpoDbbLcl35T)VkTs+|Ivgp)BoH+Lm*K{B)m(7;g*b0 zKhUIYZE5v&Q@8~f*kuNS`z!sy)-E#;K=fE6$_@mF=;h;#tPV&^>2KF0bF^EElU{Gu zw%MV8i#Y0#%6PF!vUJdZ;g5{CKhASr(%*PGgy*Xy7XVql2o=jXCZ}viov)b|OA(8& zpGD4Nbq6t2oY(_KplsOzkzKZprI*M!tzF#@KFGgJkBx-EG zE8?%1jm77ZQnks5-Zguy@_&@csROnQwE?gqI=hw=Nk?d_qpWouuwke!AdK^ab$N=3 zhLF198u-n|aT@S#iW)Aq)_Q-d74*(|rxY@*J2x0pR)uXow+SDMyMz-mPDpU%Bh zx&PjzbapDA!%gjEg7g|mXVF%_vDOoSFx*G(TZ)!P^KvkaXCTy3Z(;JBeq2YkDsvbSd8{}?56&Ib-n zv(E2NmcW zoL+3aJTTYa)Sfo>yy~I8p)Be?xsA%pj_^pQe!EBQ4~i{Gsi3v&gaO~6+Lm-wFvYW^ zhBeX}5W~vWa~V~=mKxZ|7btP(z-lw&8V8t1Y-Z@&sDV_s0yduh9 zvyBRVkq!-rS4z5``s?qs^ej^sH8L)`W?R(_F`T-kiCa+432XMKp`VOyfau`~siBGx z0une+7^{-2*OP8GfqG=3Y>WWuTe4auV7OgSfLJ08u>fZa3h?dfYH0{a6!vpttea4m zKC}l8tA#waHoJ4l^^KU3(g@rCwR8T00 z3YodusqhsQijyi~!-!1S@-1!F2`w{A*4q<_86xkU?`x>eHvN{K$ zkE&z_aNyTT`6Zog$Sh$BW!WPgpDC0zkD%fOkD%h!j-Z$mS*-}l>HxBOmBnZ3(*{0$pXKg=tSpLH6K4#n1R?EINE;!4YeF)8 z4p+Y1Oi0cTZJ(>ITVSMZz{>Ak0d|$j?{l>hq$GhW_qi%zp^`gWfnRD;=L{VE3Kxn0 z@e)5cDJa0l*l^SV#F0Wpx5tr!qW68qd^#|2u_n$KR0%@b2zje8ZZVEcKunRgfY{-MPzzB`rkG$&um?qC6$VIXTaFScM<*v`oeJ@p;(LxNpQc=6KTp;0V$}fn1RO@I5KI5yp=KPsjLYgX?vXml=6I)j#Qxp#5+n^4N(Li=Ia}@l+z!;e|WIJByj-z zilN$o``Wf)0`Osmnxg}n8Y=iCW8DCJilTh~qP8j!jwyIfK%wb%hogK0@E;ZB*-`t? zKnRE(Zj1#EHVy98YRRL`l}$jj@_mZh=S=M;AYweB`KSVXO1Xb7NI6+eSZSsVSm~n< zJY-TpFgyfxld}1DV>jo(o-#FS0@Ef;zhO+9fZtKnKCU00Cyz6O_)(w*L?@%Ic%}kA zS)CIMZT|U89sf~fOdqKgrvqMQ0jUGNMN#E?hf2_?j!sxL;P@aF>oOC$fnPV}H*m#W z6h!`CEekgQ{?brwz&{$Q1sI~+Cb}pfqevwh6%e7{Qg+S|;K1+nl;7zUCMD7q!9GJ{o2l$YnE zLHR|K*#NvpQS#-u#lS(e0eKqYQ54$`@wHuAiamfIFw_*_hZW`0xYMI{qY-uhzw$uO zR+iI%H!I4wrMBcyBLrM-s1D$V&GMQBsy2HiCHpQ$M4kw%Z&Uj{28?zKwKDT0ny25W0&}PYJ2z3XX;qr zu70L}hz9`|S?KD3hbqcXnOX^cs-qKD4O~OVVqL-`H}IQG`Az2iB znmml~nZ#$kJygK8r7n`e1Mo6K)dAn4D8B*M3eshyNJVGY+o@QWAjl2;CS!iNJPMr< z=ig>wZUWw8s0QGl3^fTzl8MI{{z$Tn6TRSAs8bQCfnO))HYhJQvYf>?DSyL4gtO;VW z)*yn$E{6DrHUFZF5K3mLdY_&=@ORZ2YKjp zEv4H7USOye;5J416r=Vd4Xg}AuL{g8g67RWBFpH>0cKDj@(%;Lysq?T=D_?Stw+ z{3-Jy;7ip3GMfPIGE^N9ql60Z<*K9b0z|QaGt(DU4`L(z6j#GmP_Y*V_U4pbJ7w2S z*=2^(TU4_pW_yOnJN0%Y{jrvE^9*p2wXOpmswls?(Mph#EJmD+Sb`hD5fPdu%efG9 zys*e(Gy(WHLp1@PY^V-kh(<6yLQU1owCd1!JAaG-C7Pj`Mg01{?!QUfZtJ6nHN+7!%SYlED+69-Cu?2kUK51 zO~7{=ssk9j*-XuER`~+MKeU`lTMpFDaXIjQp4ZV@%4`pKlA)#mpQI>1+f!S6f6qg9 zsetPg^4i@dH#A0lc0USfNn8gjilT76n;8Q9pH+s~tT>2#DZV7EM>~n@{ z0Dkd6udy@){Ia5)t&JYF{mm&YK-}PlAK>7U#voUuE@-vBlyd&ON$8%e)~_xS?3ps{rcApq{gE;40{%i#c9aR?H-Q!qos2g2k*{w^4DJ5;OdadzDPuZ{ ztPB7zvsl&v-=Zjga*^m%M<=WrkS(cL7h>dwU~WkHZOHq_VF}V#t#dX3e{QG-;BO5z z2^dnQL3B}I;PMcmwF>eA4SreG4fQIfo_8>KB2G0zuXuTSmXQ&>%dLG3k=l&+-9f^fDHcPG5Q9Bf5wSka4giRh}6JuBIY;168m_r zEMyMw0-mL%EMx#rTI4UuECx8DC^tiNder_l5CVR@kC573fe`SghT7w7pQ_qM)u$Vm zX+V^7szs8j6 z0-~?ezR{!hWg~0=qOu494B98l3Yi1luZ|RBgVW;@%b3=KR29AX!wBit^tz_`A-R#~;*z%PrY#ds3+I@t_!Xu%*pSf#{DGq6 zyTV&~Khnw~lhg#nKB1en@i4?G+2%W4;Qi|$7F*_MDGlY}{8}-G(%-2njTB0GA_Rdb zEvPsGRSBVGBW;=Zn(fKosS?*|d6;~RY(M$o>Zcsz{%#g{_;3du*@NPfM)|S&Q@t->)rA!)_;G||D;EZmpYruFyCJkv5PUcTJda6m=9Ru zBwfBJ&P|9i%SMQ{C~GQs%=!g*HoPXL^;1OqV^I~bqWyWJR!AL8(|v|m1RT%4BGE; z1(j;wKzqC^iyJ`n^$R0vn@p)LAo_YjYOgcG1|TYn5Wt{)uA92OSn

`_+!4UHiPQ-1vs?8RtJL`D6MgS#QY8D9#G?f0N)nb>N8N| zbb!N(Z8Zhdcm%*>2>b=qo6dahYK$s%>v&Y^K0DE)5|ug|Nju;W{(d@z7sijWj0+}! z@qRmTaKu=Hr-)8`k`Ts@`$iJ?l5v}zI5lFt08D3+ao?!eOHt0HNaiSAjQFS&#@{LN zlkG*4x&*0_Xr?}EQh$?8ZPQm5%+wEC$?VlC-h;+l?ktpfaI4fRliE_^E&W<#4w6(| z&$nB}&-|BMZRs}vf2_%JlX_CNp3Awp+@xOAO6Ik#;&1!8%$lcKrLqOfBY9B!O?@z5 z%uLHs&3MYQcH91!kWqghl0JsYKLn}X6o>|O~&`_j)OXau?|T^r8XR9Vtn-n zI1qs`pa!xvjzrA4Kxcy>=uwEB*hjYmKl~4ZppWdljTH1UlKu>?+d+Na1E~KDm%TtA zff~r$_#{ARw9qJ*Y@^M{$eEcF0)lH zQ2i+Y(-4>fY9L$VxrjLhXaxw2C4gULE4q_{9H(F?EEDzJkn}XT{sUCM4?ul|t?EF1 z1_LyZw{aXoXCP`asBs#=>m6)$0tli{=%dv84Z4n&+IBER=Q`mj6n!R=dQ{n}1E|kM z0QH-2c?;+TkXD~>CX}TXOhMM9llTX-QhiJD6gn4FPxW=dlUoQ9HFoQ6tM;G<<`FeM z3iL2Y)ac-;=VJn@F|UWMzNnR|Qk|D))wvsA!dRc!UN3xmBfGc z_^g0iDc~zR6g{lyZL6gqota6J`87m63F2Q(599Rim+!GlF)RP&9{Zfh`}2G3nCv~a zVRuz@2VLQW*~g6ysA_ji8vfzAvKy{Qz5@@Y^*18vdCcUSLG@bzw(NsD9;ktA`#pph zc@TUCffKPJpN!Gg_&UJ#gKf1O)c6s=_MuPiT68mO@wz(F&xVt^VO0M0)QcO+2bV1OR8ZS~_UfCB*TJRH*u)HEHS z=}24}7vZw`2;2`qjVA+KdlYI3YC4C>m*TSSXj?4+HC_$y-LbYB3u?TL$%`>lL5=qU zJcz*M1djmReS)o4gJexyYIKM9WZj{=<1OEZo@Gs}`VH<`AkBDPmN5l`fHmLp`$UYh zz%)}Bx#oSCWo#zn5-YZujGVOhf$1KQOxo`d_?&=iU-L<}ngMEH9+|XP0sS5%llFn- z&@M({1(rX{MZeehY1Z8knc0 zoZ-L24Fn|R9C$VcBFL0;Wm#0tt(Yy=X_hw>(?a^G_j$JRKvK>Z_=ep`ZR;_|4RN<86NXIkEkQ8`y!iuQn{oSB!|stP3Kyl{oB zZUHqgPfI!HZm`v{ASvhkO{fdVl=E#xRL&YaQ&=}!VH_h`+EcRGR&kJ&^8f;u5wL$Q zyvA0)12r&@l+(m}t;iq#2V1oTLH=m4uIQlU<2fBG0nVB7_M`k6NSb?{t@?uc90j1c zuQS{h)@X3wYk5aS+-KcjtEr$?+>d0rZ|w{2`z-I0i2J&mY_$wzxOqo?yHiw?o%lx8 zde{nglACwb>u$zFH>l-~y66^LHG}kI$?vSQ;>iYYC9D^$Llr;EOWCMwkzC+-7 z0xD_q?Y3GAYG59joa_H&t9c-qG^JZ?<$y$Kz3#D95@gD|-ISN3>S$Sf-O9f;DsRJI zY;_4p%8PBa)z<_p?@s{Nf*P1d%A0ntt@Z;+d8uu-`r&?4-bH`4)wv*)_c7MC<^j5W zP5*`{H8crMpD(Rt|=| zx2?SKS$PjY9!SeW;1W6_M^4_z;ZCQB_LLNxuoru6<0@m*n1l}iL zJtNRS0gbHhl!t6}0H~4m{yPGXfsFopkBaKwJOt&P59d_8HF7aNY`d*Sfkgku?66e^ zB>Erq2pR%vV4jxAH}_HSfTW)XJ&Ct9AXCm4qoZ8mKfiYswiH7yE}ec8$9q^+Jj3R%i{0#K{SAu zI0ZEQX2kGa;&h|QcaYdsOPu~QP=P^G0@EcWbD1D=?R zLfRT2R^4_Q48fL+G84ppjWQ>oq#^jozRXEV5MSVzIr$P44>Rqxc~}j+j1_;-wyY`uakS2H{8?!t5f;~%!= zHR0g4u>Dw%*lj;!(Q_c`QCr?(44Hj2UKyGf!h;7fi+LYBH?F>iwh%Vs$9IvDR( z*~dkLE&RlQIO*89l3G%{!7-OFZU#C*~%rf+E%_#kkwk5Ae`SUol z(3hCJ<*2r0KKL+nxpwWLGrO392I zGqPG@K?6@Y6!|7wmh}RbcHKvltyUv@vLz#159Wg{pKN;IOtC!59DGzWBuueHZ-XCX zHKtg4sLIHhs@XW^rQP5d16=X6UC|5*|)lv;B%PPft~Tu)G@0Y=eu ztAh?>6iv6fNEkYrZgpdD@P};QA(oyAGA$0Via8_(-=b4G5DZy5i?4F58UdUmGb}wv zW=WWi(UqP^Gp)AoK?i!VV@Sq)45N;3p$Fzt0b`)g{ju5(l2v4kWyU-cI!OFtRD6sj z9Xbd`288Qa+aXI3Kh|ht3E~gXVF>>uA4BE?3Lj?aAvB1BrI*Xs~d}ho_Wg}J)FF^Sr*@$Y3aAMl75>=SEIjR9j>-4zV*{wSGVH2 zTDV3*eb-2x$MmDnYh>7?DWedUDKlyo6m(6Au4HvHcs5!VU+HL(8(WFoXbQfTT=Vg! zyb)Y7#O9}WD}<#f^L69*3?h>>egsya`GYdx&?=cfoZ(UYl4OB=cQT4!e=LyCPDbIQ zmW6RjX4Mzzs&8qAtVL4wG51Q?R2`FJk<81ne4{f8^K?leKP^!QFuc5gpP*o>aD4J* zn@p89YFr6r46&jOU~BZV2KHR;*;lCprA0%e-F;q!@eOpKv}}m9Y$5Q`pz%p6bcnR@ zdElL(2{IIhCiz`W-AlIW2%5M+S^N8x3={vU6VR$*(yFhJ_7BhmXlIzT?c$g5G!B}; z1z?!mSEjydt9?Kd7C~9VMOi1lZmWf$2}dzXl=d9pGmM(es6sBRKLCEms6|li@HC@( z?ZyT$(1fEHRmQ0E0MB96Y(`Zw>d%08fDZZ!HQpR*)!6FRu4wG2U@ao+J^Z$L6D+TR z8ko>jfSAVixU_u(4~d}0Du6)<3?S$YFc*Q@AiQ0mswO3KFR~Z`#;oW~6pQpbErZ=92+MOD{WW_(JwiZ;R1Zf7Dy;qM`zQhrg8K70Atrmz+VXXZuP|XQEiaCTmAS0ykY~%yVbKlf&h@bTiyJzte*Ql(BSG@!yJRnZOM~Ri_sIyH0FqbRZzJ#u0bgy8{KQrn zki6Qq*Jk9+_QQy{8YFMFhkphRki6Nx1c3_(_-6YD1YQG8;cNGtY46~AD|#Xqc7>)7 z0Mv9YkYkE;or`sqv#-C`w`H?tVa}O@L>JC72Z**wM!?iS~Bp z&^w>~e8~u;Vo&LGj21^bUc^5;&y*47TMBgEZ}GFnBG;IaH40JN*Y;PLz%^wU@2n*F->`Joq@sL>s^rE1tR!CQa!ApXg zt#ByrZo$hk*5_IwT@41WNZJA`?9c`1RT(48tdOn-gWa-&ex()C)nM?Zq@87jbTt^f zRmjzCjTO?>VDN4M7p!#_T)~Hh9&6(BtI4oP%2GaE4Ti;1lk9IW}wiBRT4TfcLE=t>?9wv>-}uW9w?~>3Q93YNuob_E$%LPG#Sa6-+$V7rHNT30R7VwG12XaIHZkH=#ZxdS<8U>2 zm~b3H4r+Z#3rDo`^)%r)lN_wb6%mItRu#Y02tjZ)c%87_Og47Jj-RlZYHjHTUbq^3 zPIzC6ct6YXN@wckMmt}z+M>C+xQg7i2(&a;6+eZ0cO0$;tAwpb#KzS|vps>zpq8jp z9Igh(3EQNIjq_EriCoQlO9${~tkg-uyAsztuFNT&n&^ns!B%RWq+ZEXuFj@~(azUD zOX^l!MQL0;bvc|Ns(5b^guvAxyS#W69ya4}HTZ$#*~>hrt>PlvlE=u8cD_=B#W$ys z5v~R~w2EgEz|~+KvcbNHYw`eeC>K|gH`@6+Q8=cOgR15ttIHAXvuITu?R*u{(au)^ zZ{k-8PZ{MfEdDYD09S)jot1dDio?~QfN1CI{ZiONQDIy>Q=C*SC92{%sAn9m20s+u zPa<9}V49aBQx)G;2{!C}%}0F9P2(!{ZzP*kT|!M@)eVlh@1uDou^cuj4p)PRf-`*p zR!3EONl$zgm_8me58p%L14e8^ah(20Z(C^vP2M;x=i+KG$fP(^wX4BkumJ69Fc>00 zyBZ9J3ec_wgZ%|)SA)SYxv6SbgTe6RUm#7p8Vp7V(5?o9kpi@!Y zSA)SA5v^Sf24e+iSA)R;0<^2aV4MK$YA`rZfOa()j2CIz)nG6|7GLdZFqkMQ+SMR@ zXtCMa)nG7LfOa()94tV)8Vsfg(5?o9snSyIYA~24DcaRwFkOIlH5eQsK)V_YW(d%( z27{Rbc6+_hIl*B9w5!3OS%7vm7|aszRY+HZ!E6Dz$D5t6)US)TPZ*2Ak&>3O!Vk-U zjuM2YWAB~rT@4CYA&?P@SME`JGEDeo>mJRC1T zyBZAUixRV^jBJ=28&W$=CrH9V2OyMA}G|v%M0smpao?okTiV z=5-2D6|?hI*9$)dx$~8M<9cT2tE{vt=Q2F`2P>RUkvWI127^=5O~{X%DXvKl{pkp_ zOuG~zk~wn{mBC8yN?>s{7@Q^}LAx3ZP8Z;ctHEHE0PSip_-&#Wvfz22t_FiMoMxaz zDmc@7l9J5MSB0u-SA)UX>E5ij%Au>l;2h@$u*F;!8*@3~VlF!(=CawiQ_8g&Q#0oB z$3skhkmS(SpjRLZXUr|bv!1L0bqMIDJ%QrD%O68Aw==FeX6I`$SaRra(D#y)5RSRi znY#zr7iV+JG9n_{waMX+wFDj~aW73@3>KAc#&Tz+zkv5eRy2n`1d~lwX;drbmgAZ|W^^P5Tf` zP74lV>J!bSZfb21&Zv$kH8O?{jwGYAMYB7A9x3H$AA-rF18)1w9y3Zd?L#nmoNqy`*<(ga0J6u7l1=*% zOfHncut0nWCKron?L#oRM8FD_V|KnCjDlkB3|w1|8NC@OI%ZU>&+L3<3EGEXa#Oqt zWoM5W?Msjo(uZJjvlIazf{9dejK{-4qhm(po-aNGlLrVB<^X*NCdWA}6Z^E$uh@ew zW0o27sE1U*E{wT*5iUF$LAF|+MIE`?I+pb@PpE|)^{i6a2(ZJ4V9fm(f#PWA>lh@( z+zGh0&+dF}!tK1AuOqngl?_$p(au+CcKK4s;?7qhw?glHWjLtNJ6{=2l}9^Yi3-bS z9gjTN`AU>7k9NKim6b<3Ux_Nq=VgSmJyGZKXy+>{Rb9>(Hr)Bjw7T+W=PS`Z<l{N5OEPbSNT`W94~&%x1}tGiL{Duz8}a| zJPZ5~sFUn@`+fw4pYTtZ2SJ@-&i_F)_-CM-L0z71Mp-{fT=`e{@(k2fEXTGLP~0RZ@~L8|&oJbLz9u9OwVQ)` z^qB8#RRijl0J!#hTU`q3))rv(54JiC)V(V}4Kvo(BXpj^w@;wj{s7AzM}2Jr3b z9km%QXKTj;tWG=XI8f~jfNx73^%STFcb@fFf=kbpxNzH9?Rf}%-_B7Ffoj(Qd{*hG zCqT7V1DxK`QOARNJr9s+#`&>@sK*Nczw6|v>7cqd09MXI?3)NY+r?3XLACz?m{{wm zw`;)i4L~#OJhk5<@TWRQT?4{juUmn~V}9)ib=2MoaA|Kx%?I_kAE5RTTn_5vs0L8) z4pK5i?Tml)!qg0 z@<>O$0IGce;M7r$8UyO}6u?eoUIO8@&jFl04jlrj<=Kv#4s_HSkg9th=+@)G^8o^r z#ye^(2!HzwIa9Zq({&4kG++zB$+%9+S-bL-+y-p#Sqe1K>ZA5qu%?Cejn=H#t=Pg? zry5taV$8}jOp8TYZ>9Dff-#7p+V<=84K#~l+K zbrq){XciM>(L*nCJJ(8gT~Dag4!N3HJt^_{ScMsWjP9 zU1qR4_rvyuVx_b^Jd1iW!y!FLa(5{eHeFtoR#DªIcYiT??rwI}_lF_h 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 *5Mmo;Jz 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⋙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%0Aob;zzaTvM8mU&`Yeh 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 H{5EToW$1XcG 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