Images and TIFF pages
Image conversion uses macOS ImageIO and the bundled GIF, WebP, JPEG XL, and AVIF encoders. The app reads the file contents to identify the source. A wrong extension does not change the detected image format.
Single-frame images convert to formats supplied by ImageIO or a bundled encoder. AVIF remains available on macOS 14 through the bundled libaom fallback when ImageIO cannot write it. A format with no available writer is not offered. Asking for it anyway is refused by name. TIFF files with multiple pages convert directly to TIFF or PDF. These two routes keep every page in order. Each page keeps its own dimensions and orientation. The PDF page size uses the image resolution, with 72 DPI when the source has no resolution.
Animated GIF, WebP, and PNG inputs convert to animated GIF or animated WebP.
ICO and ICNS conversion reads the largest image from a multi-size icon. Icon output generates the standard size set with transparent padding around rectangular artwork.
Photoshop PSD conversion prepares ZIP, high-precision PackBits, and bitmap encodings for the native reader. It exports the document's saved merged image.
The same routes work for manual conversion and automatic extension changes. Automatic conversion keeps the original for Undo. Conversion refuses to overwrite an existing output. A failed conversion keeps the source and removes temporary output.
Settings and output checks
Image quality defaults to 85%. It applies to image-to-PDF output as well as supported lossy image formats. Lower PDF quality can reduce file size by changing the embedded JPEG data. At 100%, the native writer can choose lossless compression for some images and JPEG for others. This setting does not guarantee identical decoded PDF pixels. The independent PDF check measures both compressed data and displayed pixels. Lossless TIFF checks compare the decoded pixels exactly.
JPEG backgrounds offer Automatic, White, Black, and Custom. Automatic uses white where an image is transparent. Custom accepts six hex digits, with an optional #, and interprets the color as sRGB. Partial transparency is blended with the chosen background. PNG, TIFF, BMP, GIF, ICO, AVIF, WebP, and JPEG XL keep the transparency their encoders support. The background option does not remove their alpha channels. Grayscale images with alpha are converted to sRGB when a custom color needs a color JPEG.
When ImageIO supplies the AVIF writer, 100% requests its highest supported lossy setting. The exact native value 1.0 fails on the tested macOS version, so the app uses the next lower Float value. The native writer also compresses alpha. On macOS 14, the fallback writes color and alpha as separate AV1 planes. It uses the quality control for color and the highest encoder quality for alpha. Use a lossless format when exact pixels matter.
PNG compression ranges from 0 to 9 and defaults to 6. Level 0 stores the image stream without compression. Higher levels can make smaller files and take longer. All levels preserve the generated pixels, transparency, color depth, and metadata. The setting also applies to PNG output from SVG and PDF. It is separate from lossy image quality.
TIFF compression offers Automatic, None, LZW, Deflate, and JPEG. Automatic uses the native writer's choice. None, LZW, and Deflate preserve the generated samples exactly. JPEG has a separate quality control from 0% to 100%, with 85% as the default. It stores 8-bit samples and is lossy, including the alpha channel. Use LZW or Deflate when exact transparency matters. JPEG keeps RGB samples without chroma subsampling. Its page width is limited to 65,500 pixels.
JPEG TIFF uses a private uncompressed native file followed by the bundled encoder. This avoids compressing the temporary image before JPEG encoding. The private file is limited to 2 GiB. The helper has a 120-second CPU limit, a 256 MiB single-allocation limit, and a 512 MiB output limit. The allocation limit is not a total process-memory bound. Its sandbox denies network access and unrelated file reads.
TIFF output retains supported EXIF and GPS fields in their native directories, plus custom XMP, captions, resolution, and color profiles. The JPEG metadata step preserves the compressed strips and tables byte for byte. The native preparation step has known limits: ImageIO drops the camera interoperability directory and truncates fractional GPS time seconds on the tested macOS version. LibTIFF also rounds some EXIF fractions through 32-bit floating-point values. Metadata retention is not an exact copy of every source field. These limits remain open fidelity work.
The command accepts these settings through --image-options FILE.json. For example, {"pngCompressionLevel":9} requests PNG level 9. {"quality":0.5} requests 50% image quality, including image-to-PDF output. {"tiffCompression":"jpeg","tiffJPEGQuality":0.4} requests TIFF JPEG at 40%. {"alphaHandling":"custom","alphaCustomColor":"#336699"} selects a JPEG background. The other alpha values are preserve, white, and black. Missing fields retain their defaults. Invalid image quality and invalid custom JPEG colors fail before publication.
Metadata retention defaults to on. Turning it off removes descriptive metadata. Orientation and resolution remain because they affect appearance and physical size. Optional sRGB conversion uses an 8-bit color buffer. Progressive JPEG applies only to JPEG output. Full metadata and color fidelity across all native writers still need checks.
The adapter decodes and writes one page at a time. It checks input dimensions before decoding and releases each source page cache after writing. After encoding, it checks every raster page's format, dimensions, and readability. PDF output uses a PDF reader to check page count and physical size. ImageIO can inspect a PDF but cannot decode those pages with its raster-image API.
Inputs and outputs are limited to 512 MiB. There can be at most 10,000 TIFF pages. Each page is limited to 32 million pixels and 100,000 pixels per edge. The total is limited to 256 million pixels. PDF resolution must be between 1 and 12,000 DPI. The source file version must still match when conversion ends. Cancellation is checked between pages and before publication. Native encoding calls cannot be interrupted in the middle of a page.
JPEG input receives an additional decoder check before image conversion, image PDF output, or OCR. The check first verifies source size and dimensions. It then uses the JPEG library already bundled with the PDF tool. Corrupt-data warnings stop conversion. This decoder reads the complete compressed stream with reduced-size output and one output row at a time. Its JPEG allocations are limited to 256 MiB. This allocation limit is not a limit on total process RSS.
The bundled media decoder checks JPEG processes that the first decoder does not support. Strict mode rejects a missing end marker. The second decoder uses one requested codec thread, a 32-million-pixel limit, and a 256 MiB limit per allocation. Both helpers use the existing time and cancellation controls. Source version checks cover validation through final output publication. Archiving preserves the original file bytes and does not require a decodable image.
Remaining work
Video-to-animation output supports GIF and WebP. JPEG XL output supports still images. Bitmap tracing makes SVG or SVGZ paths from one still image. Icon Composer artwork exports project layers to raster images. Camera RAW uses the native reader, with checks for eight format families. Images to video connects still images and animations to 13 video containers. Complete camera coverage, animated JPEG XL, styled icon rendering, and the remaining image settings are still in development. WebP output supports lossy and lossless compression. TIFF-to-PDF and TIFF-to-TIFF support does not imply support for every multipage format. A TIFF conversion that uses PDF as an intermediate follows the later PDF route's page-selection limits, except that SVG tracing refuses multipage bitmap input.
Full damaged-image detection remains open for other image formats. JPEG checks cover missing end markers and detected corrupt data. They cannot prove that a valid encoded image contains the pixels its author intended. Some lossless JPEG variants and arithmetic-coded JPEGs with trailing data still need a rendering fallback.
Image OCR still accepts one source image. To recognize a multipage TIFF, convert it to PDF and use the scanned-page PDF OCR option. See text recognition and PDF pages.
Development checks
Run swift test --filter testPNGCompressionPreservesStreamAndRejectsDamage. It checks all ten levels, exact filtered bytes, chunk checksums, truncated and extra compressed data, chunk order, cancellation, saved defaults, automatic conversion, and exact Undo. The compression pass uses system zlib and 64 KiB buffers. It does not add a helper or codec dependency. Input and output files are limited to 512 MiB. The streamed raw data limit is 2 GiB to cover the PDF renderer's 256-megapixel limit. It does not allocate a buffer of that size.
Run python3 tools/check-png-compression.py with Pillow. It checks all levels through the command, including RGB, alpha, grayscale, palette, 16-bit pixels, metadata, and SVG/PDF routes. --command and --tools can select a packaged build. --benchmark-only measures three complete TIFF-to-PNG conversions per workload and setting. --report selects the output report. Private PNG inputs used by other codecs do not receive this extra compression pass.
Run swift test --filter testImagePDFAndMultipageTIFFWithAutomaticUndo. It checks mixed page sizes, orientation, resolution after metadata removal, sRGB conversion, overwrite refusal, extension changes, and exact Undo.
Run swift test --filter testDamagedJPEGIsRejectedBeforeImagePDFAndOCRConversion after building the PDF and media helpers. It checks all JPEG consumers, misleading extensions, failed automatic conversion, source preservation, and byte-preserving archive output. The OCR check also covers valid JPEG input.
Run python3 tools/check-jpeg.py with Pillow and a development installation of libjpeg-turbo's cjpeg encoder. Original fixtures cover baseline, progressive, arithmetic, arithmetic-progressive, extended 12-bit, lossless 12-bit, grayscale, and CMYK JPEG. The check exercises damaged input, trailing data, dimension limits, and symlink refusal. --command and --tools can point to the packaged command and Helpers directory. --benchmark records complete JPEG-to-PNG conversions. These fixture tools are not needed by app users.
On the measured packaged build, JPEG validation added 0.02–0.04 seconds for 1-megapixel JPEG-to-PNG conversion. It added 0.19 seconds for a 24-megapixel baseline JPEG and 0.50 seconds for a progressive JPEG of that size. Each figure compares three-run medians on the same original input bytes. Reported peak RSS changed by less than 0.2 MB. This value can include a child helper's peak. It is not the sum of simultaneous process memory. The before and after reports are research/jpeg-validation-before.json and research/jpeg-validation-performance.json.
Run python3 tools/check-image-pages.py with Python that has Pillow, pypdf, and pypdfium2. It creates an original eight-page TIFF with all orientation values. Independent readers check page order, physical dimensions, TIFF pixels, PDF appearance and quality, metadata, transparent PNG input, excessive dimensions, refusal to discard animation frames, source preservation, and cleanup. Add --command /absolute/path/to/Allomer.app/Contents/MacOS/allomer to check a packaged command. Add --benchmark to measure three complete conversions of one and twelve image-heavy TIFF pages. The report is stored in research/image-page-performance.json. --report selects another report path.
Run python3 tools/check-tiff.py with Pillow for all five compression modes. It checks 16-bit input, RGB, grayscale, CMYK, palettes, alpha, eight orientations, profiles, per-page metadata, metadata removal, source preservation, and sandbox denial. A separate camera fixture checks native EXIF/GPS directories, a nested interoperability directory in the copy tool, unchanged JPEG payloads, and rejection of a looping metadata link. --command and --tools select a packaged command and Helpers directory. --benchmark-only measures three complete conversions for each mode on one and twelve distinct noise and flat-color pages.
Run python3 tools/check-image-background.py with Pillow for JPEG background choices, partial transparency, grayscale and palette inputs, sRGB and Display P3 colors, unchanged alpha-capable output, invalid color rejection, and AVIF quality. The automatic conversion test in swift test checks the selected color and exact Undo. The background option uses Apple's native background-color setting. RGB input needs no additional preparation buffer. Grayscale input with alpha needs an RGB page buffer for a custom color. --benchmark-only measures three complete 24-megapixel RGBA-to-JPEG conversions.
Run swift test --filter AVIFTests after building the media tools. It checks the libaom fallback and its separate transparency plane. The packaged app check runs the same path on macOS 14, where ImageIO has no AVIF writer.
The background-control benchmark compares the prior automatic white fill with the explicit White setting at JPEG quality 100. On identical 24-megapixel RGBA noise inputs, median time changed from 0.93 to 0.96 seconds and reported process RSS from 212,877,312 to 212,828,160 bytes. All six runs produced identical 89,819,889-byte JPEG files. This shows no meaningful memory increase for this RGB path; it is not evidence of a speed improvement. The GUI is excluded. Reports are research/image-background-before.json and research/image-background-performance.json.
On an M2 Max with 32 GiB RAM, changing the private JPEG preparation from LZW to uncompressed TIFF reduced the twelve-page noise sample from 0.54 to 0.19 seconds. The flat-color sample fell from 0.23 to 0.11 seconds. Output sizes stayed at 25,466,650 and 562,660 bytes respectively. Reported process RSS remained about 112 MB. An uncompressed temporary file uses more disk space for compressible pictures. These are three-run medians on identical original input bytes. RSS can include a helper's peak; it is not the sum of simultaneous process memory. The GUI is excluded. Reports are research/tiff-compression-before.json and research/tiff-compression-performance.json.
The packaged image-PDF benchmark at quality 85 took a median 0.09 seconds and 47.5 MB of process RSS for one page. Twelve distinct pages took 0.30 seconds and 146.9 MB. The twelve-page input was 37.8 MB and the PDF output was 14.4 MB. Before the quality fix, the native writer used its own default and produced 13.1 MB in 0.29 seconds. The larger output follows the requested quality; this is not a speed or compression improvement. These figures cover the full command conversion. Native image conversion runs in the command process; the GUI is excluded. They do not establish a constant-memory bound for arbitrary TIFF files.
The PNG benchmark compares the previous native default with level 6 on the same inputs. A 1-megapixel flat-color image changed from 17,505 to 5,918 output bytes, with median time increasing from 0.06 to 0.07 seconds. A 24-megapixel random-noise image remained about 72.1 MB, while time increased from 1.92 to 3.29 seconds. Peak process RSS stayed about 29.4 MB and 190.3 MB respectively. Level 0 took 2.02 seconds on the noise image. Level 9 took 3.31 seconds and made the same size as level 6. Noise is hard to compress; higher effort does not always help. These are three-run measurements on an M2 Max with 32 GiB RAM. The reports are research/png-compression-before.json and research/png-compression-performance.json.