Methodology & Accuracy
Every tool on FileConverters is a thin interface over a set of small, pure, automatically tested functions. This page documents how the pipeline actually works — detection, conversion, verification — and, just as importantly, what it deliberately does not do, so you can judge exactly how far to trust each output.
This page is the summary. Each stage has a longer guide that runs the same engine on
illustrative inputs and shows every figure it returns.
How image formats are detected covers the signatures,
the ftyp brand rules, the extension check and animation detection.
The conversion pipeline follows one file from detection
through planning, decoding, encoding and verification.
Transparency and flattening shows which targets
cannot hold alpha and when the background prompt appears.
Quality parameters and compress-to-target explains the quality scales, the MozJPEG lane and the binary search.
HEIC and AVIF: decoding and licensing
restates the vendored-codec facts and why conversions run from HEIC and to AVIF, never the reverse.
What re-encoding does to metadata lists what
a canvas re-encode discards and what survives.
Resizing math works the fit, exact and percent arithmetic, the
rounding rule and the caps.
Image to PDF: page math works the points-per-inch arithmetic
and the A4 and Letter fit. All eight are listed at /guides/.
Files are identified by content, not name
The first thing any tool does with a file is read its leading bytes and match them against the
format signatures: \x89PNG, FF D8 FF for JPEG,
RIFF…WEBP, GIF87a/GIF89a, and BM for BMP. The
filename's extension is compared against that detection: a file named
photo.png whose bytes are JPEG gets a visible notice and is converted from what it actually
is. Animated WebP is recognized from the VP8X animation flag and ANIM chunks; animated GIF by counting
image descriptors — so the "first frame only" caveat appears exactly when it applies. HEIC is identified
by parsing the ISO-BMFF ftyp box — major and compatible brands (heic, heix, hevc, hevx, mif1,
msf1) read from the box structure, bounded by the declared box size, never by
substring-searching the file. AVIF — the same container, AV1-coded — is detected from its own
brands (avif, avis) by the same box parser, with explicitly HEVC-coded
brands taking precedence, and the avis image-sequence brand drives the same "first frame
only" caveat that animated GIF and WebP get.
The conversion pipeline
Decoding uses the browser's own codecs via createImageBitmap with
imageOrientation: 'from-image', which applies EXIF rotation so phone photos come
out upright. The pixels are drawn to a canvas — composited over a background color first
whenever the target format cannot represent transparency (JPEG; the page asks for the color
before converting) — and re-encoded by the browser's PNG, JPEG, or WebP encoder, or by one of
the two self-hosted encoders described below (MozJPEG on the Compress JPEG lane, libavif+aom for
AVIF output). Which conversions need flattening, which take a quality parameter, and what the
output filename becomes are decided by a pure planning module with its own tests, not ad-hoc in
the UI.
HEIC decoding — the one third-party decoder on the site
Every other tool here decodes with the browser's own codecs. HEIC is the exception, because no
browser ships a HEIC decoder: the two HEIC pages load
libheif — © struktur AG and contributors, licensed
LGPL-3.0 — compiled to WebAssembly from pinned
upstream source, together with its
libde265 HEVC decoder from the same upstream
family, also LGPL. The upstream codec source is compiled without source modifications, self-hosted
from this site's own origin, and loaded at runtime as a separately replaceable module in a dedicated
worker (a script plus a
.wasm file under /vendor/libheif/) — never compiled into the site's
own JavaScript — so anyone can inspect it, verify its checksums, or substitute their own build.
Source code is at
github.com/strukturag/libheif; exact
versions, commits, checksums, and the reproducible build recipe are in the project's provenance
record. The full license text ships alongside the module at
/vendor/libheif/LICENSE.txt. Decoding still happens
entirely in your browser: the module is fetched from this site, and your photo never leaves the
tab. Why a third-party decoder at all? Because "convert my iPhone photo" is the single most
common conversion request there is, and the alternative — uploading your camera roll to
someone's server — is exactly what this site exists to avoid.
Vendored encoders — MozJPEG and libavif + aom
Two tools go beyond the browser's built-in encoders, using the same self-hosting arrangement as libheif: open-source codecs compiled from pinned upstream source, served from this site's own origin as separately replaceable files, fetched lazily only when a file is actually dropped, and run in bounded workers entirely in your browser.
- Compress JPEG encodes with MozJPEG, Mozilla's JPEG encoder built on libjpeg-turbo — this site includes the libjpeg-turbo software, covered by the IJG, Modified (3-clause) BSD, and zlib licenses. The hardened browser binding is derived from Google's Squoosh/jSquash wrapper under Apache-2.0. License texts ship alongside the module at /vendor/mozjpeg/LICENSE.txt and /vendor/mozjpeg/LICENSE-codec.txt.
- JPG to AVIF and PNG to AVIF encode with libavif (© Joe Drago and contributors) driving the aom AV1 encoder (© Alliance for Open Media), both BSD-2-Clause, under the royalty-free Alliance for Open Media Patent License. The hardened browser binding is derived from Google's Squoosh/jSquash wrapper under Apache-2.0. License and patent-grant texts ship alongside the module at /vendor/avif/LICENSE.txt, /vendor/avif/LICENSE-libavif.txt, /vendor/avif/LICENSE-aom.txt, and /vendor/avif/PATENTS-aom.txt.
Every vendored file's exact size and SHA-256 is pinned by automated tests, and every codec build uses immutable upstream commits, so served bytes cannot drift silently. AVIF decoding (AVIF to JPG, AVIF to PNG) deliberately vendors nothing: current Chrome and Edge, Firefox 93+, and Safari 16.4+ decode AVIF natively, so those pages use the browser's own codec with a runtime feature probe and a plain-language notice when a browser cannot.
Why the site encodes AVIF but never HEIC
AVIF and HEIC are structurally siblings — the same HEIF container around a modern video codec — but they live under opposite legal regimes. HEIC's HEVC codec is covered by commercial patent pools, which is why browsers do not ship HEVC encoders and why this site's libheif build is decode-only: producing HEIC files would require licensing terms that a free, in-browser tool cannot honestly satisfy. AVIF's AV1 codec was developed by the Alliance for Open Media to be royalty-free under an explicit patent grant, which is why every current browser reads it and why this site is comfortable shipping an encoder for it. Practically: HEIC-class compression, or better, in a format the web actually reads — so conversions here go from HEIC and to AVIF, never the reverse.
What re-encoding does to metadata
Canvas re-encoding strips all embedded metadata: EXIF (including GPS location, camera model, and timestamps), color-profile tags, and text chunks. We present this as both a feature and a caveat, because it is both — publishing a converted photo cannot leak where it was taken, and equally, photographers lose copyright fields and ICC profiles, so originals should be kept. Orientation is applied during decoding, before metadata is discarded, so uprightness survives even though the orientation tag does not.
Every output is verified before download
A download button on this site means the output has already been checked: the produced bytes are sniffed with the same magic-byte detector applied to inputs (which catches a browser silently substituting PNG when it cannot encode WebP — reported as an honest error, never a mislabeled file), and the blob is re-decoded to confirm its pixel dimensions match the plan. File sizes shown are the output blob's true byte count. Where a result row shows a percent change, it is computed from those two measured byte counts for your file — the site publishes no blanket compression percentages anywhere, because no percentage is true for every image, and a result that comes out larger says "larger — keep your original" in the same breath.
The compress-to-target search
"Make this fit under N kilobytes" is solved by measurement, not prediction: the compressor encodes at maximum quality, then binary-searches the quality range — bracketing between the highest quality known to fit and the lowest known to overshoot — converging within eight encodes. The planner is a pure function over the list of attempts so far, and its tests pin the convergence bound and the failure mode: if even minimum quality overshoots the target, the tool reports the smallest achievable size instead of claiming success. PNG inputs are never "PNG-compressed" — a canvas cannot honestly do that — they are converted to a lossy format, labeled as such.
Resize arithmetic
Fit mode scales by min(boxWidth/w, boxHeight/h) preserving aspect ratio; exact mode stretches
(with an optional lock that derives height from width); percent mode scales uniformly. Dimensions
round to whole pixels with a 1-pixel floor so extreme aspect ratios cannot collapse to zero. Outputs
are capped at 16,384 pixels per side and 80 megapixels overall, with a warning above 50 megapixels.
Inputs are limited to 50 megapixels, 100 MB per file, and 40 files or 300 MB per selection. Enlargement
is permitted but flagged: interpolation adds no detail, and the tool says so rather than advertising
"enhancement".
The PDF writer
Image to PDF uses no library. A ~200-line writer assembles PDF 1.4 bytes directly: a catalog, a page tree, and per image a page object, a content stream, and the JPEG data embedded verbatim as a DCTDecode XObject — grayscale and CMYK JPEGs get the matching color space read from their own frame headers. Page geometry ("match image" at a chosen DPI, or A4/Letter with the image fit inside half-inch margins and centered) is computed by a pure function. Tests assert the header, object count, cross-reference offsets, EOF marker, and that embedded JPEG bytes round-trip byte-for-byte; the client re-checks the assembled structure before offering the download.
Verify the privacy claim yourself
"Your files never leave your browser" is enforced by architecture, and you do not have to take our word for it:
- Open your browser's DevTools (F12) → Network tab, then convert a file. You will see no request carrying your image — no POST, no fetch, nothing but the page's own static assets.
-
The site's
Content-Security-Policyheader setsconnect-src 'self': the browser itself refuses to let scripts on this page send data to any other origin. Check it in DevTools → Network → the document request → Headers. - The site is static files — there is no server application and no upload endpoint to receive files in the first place. Downloads are object URLs pointing at memory in your own tab.
Worked examples can't drift
Numbers in page copy — the resizer's "4032×3024 fit into 1200×1200 becomes 1200×900", the PDF page's point sizes — are computed at build time by the same engine code that processes your files. If the logic changed, the copy would change with it (or the build would fail), so the documentation cannot silently disagree with the tools.
Worked figures, computed at build time
The sections above state the rules. This section shows the engine applying them. Every figure
below was produced when this page was built, by calling the functions the tools call —
detectFormat, checkExtension, planConversion,
outputFilename, planResize, pageGeometry,
buildPdf and describeSizeChange — on inputs that are deliberately invented:
byte arrays constructed to begin the way a file begins, fictional filenames, made-up dimensions and
byte counts. The figures illustrate what the engine decides for those inputs. They are not measurements
of any real file and imply nothing about what a real file will do. If a rule changed, these figures
would change on the next build, or the build would fail.
Detection on constructed signatures
Each row is a short byte array that begins the way a file of that kind begins — the signature
and little else. The detector reads bytes, not names, and for the PNG, JPEG, RIFF and GIF rows
the verdict depends only on the leading bytes, so it is the same whether the array is a dozen
bytes or a photograph. Two signatures also carry a minimum length, found here by probing the
detector rather than copied from it. A BM header is accepted only from 14 bytes — the
same header one byte shorter is unrecognized — which is why the BM row is padded to
14 bytes. An ISO-BMFF ftyp box must be at least 12 bytes long
and declare a box size of 16 or more; a box declaring 15
is unrecognized, because the brand parser returns nothing for it. For the ISO-BMFF
rows the table also shows the brand list the ftyp parser read before deciding.
| Constructed input | Leading bytes | ftyp brands | Verdict |
|---|---|---|---|
| PNG signature, then an IHDR chunk |
89 50 4E 47 0D 0A 1A 0A
|
— | PNG |
| JPEG SOI marker, then an APP1 segment |
FF D8 FF E1
|
— | JPEG |
| RIFF header, WEBP form, one VP8 chunk |
52 49 46 46 10 00 00 00 57 45 42 50
|
— | WebP |
| GIF89a header and screen descriptor |
47 49 46 38 39 61
|
— | GIF |
| BM header padded to 14 bytes |
42 4D
|
— | BMP |
| ftyp box, major brand heic |
00 00 00 18 66 74 79 70 68 65 69 63
|
heic, mif1, heic | HEIC |
| ftyp box, major brand mif1, compatibles heic and hevc |
00 00 00 1C 66 74 79 70 6D 69 66 31
|
mif1, mif1, heic, hevc | HEIC |
| ftyp box, major brand avif |
00 00 00 18 66 74 79 70 61 76 69 66
|
avif, mif1, miaf | AVIF |
| ftyp box, major brand avis |
00 00 00 18 66 74 79 70 61 76 69 73
|
avis, avif, mif1 | AVIF |
| ftyp box, major brand avif, compatible heic |
00 00 00 18 66 74 79 70 61 76 69 66
|
avif, mif1, heic | HEIC |
| plain text |
68 65 6C 6C 6F 2C
|
— | unrecognized |
Three rows carry the rules worth noticing. A box whose major brand is the structural
mif1 is HEIC because an HEVC brand appears among its compatibles.
A box declaring both avif and heic is HEIC, because an
explicitly HEVC-coded brand takes precedence, as the detection section states. Plain text is unrecognized: the detector returns no format rather than a guess, and the tool reports the file as
unrecognized instead of converting it.
The extension check compares that verdict with the name. For a file named
holiday.png whose bytes are the JPEG row above, the bytes are
JPEG, the name claims PNG, and the mismatch flag is
yes — the tool shows the notice and converts from
JPEG. The same bytes named holiday.jpg give a mismatch flag of
no. Named holiday.xyz, the claimed format is
unrecognized and the flag is no: the check fires only
when both sides are known and disagree.
Animation is a separate question from format, answered by three separate checks on the same kind of constructed bytes:
-
isAnimatedWebPon a WebP with a VP8 chunk only: no -
isAnimatedWebPon a WebP with the VP8X animation flag and an ANIM chunk: yes -
isAnimatedGifon a GIF with one image descriptor: no -
isAnimatedGifon a GIF with three image descriptors: yes -
isAnimatedAvifon an ftyp box, major brand avif: no -
isAnimatedAvifon an ftyp box, major brand avis: yes
Three conversion plans
The planning module takes a detected source format, a target format and optional settings, and returns the decisions the client must carry out. It never sees pixels. These three plans were computed for invented filenames; the table shows every field a plan returns.
| Input | Source → target | Flatten | Background | Quality | First frame only | Output name |
|---|---|---|---|---|---|---|
logo.png
|
PNG → JPEG | yes |
#ffffff
|
0.92 | no |
logo.jpg
|
sticker.gif
|
GIF → PNG | no |
#ffffff
|
none (lossless) | yes |
sticker.png
|
photo.jpg
|
JPEG → AVIF | no |
#ffffff
|
0.5 | no |
photo.avif
|
PNG to JPEG is the flattening case. The target's capability entry says it cannot
carry alpha and PNG is among the formats that can, so the flatten flag is
yes and the background is
#ffffff unless the page is given another hex colour. The plan does not
know whether this particular PNG has any transparent pixels; on an opaque image the composite changes
nothing. The quality of 0.92 is on the canvas encoder's scale, taken
from the capability table because no override was passed.
GIF to PNG is the first-frame case: the flag is
yes because the GIF container can hold more than one frame,
and it is set before the file has been examined — whether this GIF actually has several frames is
isAnimatedGif's question, answered above. PNG is lossless, so the quality is none (lossless), and it carries alpha, so flattening is
no. JPEG to AVIF runs on the vendored encoder
lane: the plan's quality of 0.5 maps to
50 on the encoder's own scale, flattening is
no and the first-frame flag is
no. The two flags are independent: a WebP to JPEG
plan sets both — flatten yes, first frame only
yes.
Output names swap the extension for the target's canonical one. When the swap would leave the
name unchanged, a suffix keeps the download from shadowing the original:
photo.jpg re-encoded as JPEG is named photo-converted.jpg. The planner
refuses rather than guesses, in these words: a background given as a colour name — "Enter the background as a hex color like #ffffff."; a quality passed for a lossless target — "This output format is lossless and has no quality setting."; a quality outside the
accepted range — "Quality must be between 0.05 and 1."
Resize plans in each mode
The resize planner takes source dimensions and a request and returns target dimensions, the source rectangle to draw from, an enlargement flag and a list of warnings. Besides the fit, exact and percent modes described above, the module defines a fill mode that covers the box exactly and records a centred crop of the source; the Image Resizer page offers fit, exact and percent. Every row below is the planner's output for an invented source.
| Mode | Request | Source | Plan | Drawn from | Enlargement | Warnings |
|---|---|---|---|---|---|---|
| fit | 1200×1200 box | 4032×3024 | 1200×900 | whole source | no | none |
| fill | 1200×1200 box | 4032×3024 | 1200×1200 | 3024×3024 at (504, 0) | no | none |
| exact, lock on | width 1600 | 4032×3024 | 1600×1200 | whole source | no | none |
| exact, lock off | 1200×1200 | 4032×3024 | 1200×1200 | whole source | no | none |
| percent | 25% | 4032×3024 | 1008×756 | whole source | no | none |
| fit | 100×100 box | 10000×10 | 100×1 | whole source | no | none |
| fit | 1600×1600 box | 800×600 | 1600×1200 | whole source | yes | 1 |
| percent | 150% | 6000×4000 | 9000×6000 | whole source | yes | 2 |
Fit chose the smaller of the two ratios, so the 4032×3024 source landed at 1200×900 inside the box rather than being padded to fill it. Fill chose the larger ratio and cropped the overflow: 1200×1200 drawn from a 3024×3024 region of the source starting at (504, 0). The aspect lock ran first for the exact-mode row: a width of 1600 derives a height of 1200 from the source ratio. Without the lock, 1200×1200 is handed through as 1200×1200 and the plan records no distortion — its only flag is enlargement, which is no here. Percent is linear on each side: 25% leaves 762,048 of the original 12,192,768 pixels, 6.25% of them. The 1-pixel floor shows in the 10000×10 row, which becomes 100×1 instead of collapsing to zero height.
The enlargement row's warning reads, verbatim: "Enlarging beyond the original size cannot add detail — the result will be softer than the source." The 6000×4000 source at 150% is permitted — 9000×6000, 54,000,000 pixels — and arrives with 2 warnings:
- "Enlarging beyond the original size cannot add detail — the result will be softer than the source."
- "Very large output (over 50 megapixels) — encoding may be slow or fail on low-memory devices."
Three requests were refused before any pixel work, in the engine's words. The 4032×3024 source at 500% — "Output dimensions are limited to 16384 pixels per side (browser canvas limit)." The 6000×4000 source at 200%, within the side limit but over the pixel cap — "Outputs are limited to 80 megapixels to keep the browser responsive." A request of 1001% — "Scaling beyond 1000% is not supported."
Page geometry for A4 and Letter
The PDF writer works in points. Rather than type its units and defaults here, the build reads them back from the writer: an image declared at one pixel and one DPI measures 72 points across, so there are 72 points to the inch; a PDF built with no options declares a page 720 points wide for a 960-pixel image, which makes the default 96 DPI; and a very wide image fitted on A4 sits 36 points in from the left edge, which makes the default margin 36 points — 0.5 inch, the half inch the PDF section states. The geometry below uses those defaults plus one illustrative chosen DPI of 300.
| Image (px) | Page | Page size (pt) | Image drawn (pt) | Offset (pt) | Bound by |
|---|---|---|---|---|---|
| 4032×3024 | match image, 96 DPI | 3024×2268 | 3024×2268 | (0, 0) | — |
| 4032×3024 | match image, 300 DPI | 967.68×725.76 | 967.68×725.76 | (0, 0) | — |
| 4032×3024 | A4 | 595.28×841.89 | 523.28×392.46 | (36, 224.72) | width |
| 4032×3024 | Letter | 612×792 | 540×405 | (36, 193.5) | width |
| 1000×4000 | A4 | 595.28×841.89 | 192.47×769.89 | (201.4, 36) | height |
| 1000×4000 | Letter | 612×792 | 180×720 | (216, 36) | height |
In "match image" mode the page is the image: 4032×3024 pixels at 96 DPI is a 3024×2268-point page, and at 300 DPI a 967.68×725.76-point page; the embedded JPEG is the same bytes in both. On A4 the image is scaled by the smaller of the two available-to-image ratios and centred: 523.28×392.46 points at (36, 224.72), bound by its width. On Letter the same image is 540×405 points at (36, 193.5). The tall 1000×4000 image is bound by its height on both pages, so its vertical offset is exactly the margin and its horizontal offset is larger; the build checks that relationship before publishing the table.
The writer's structure is read back rather than described. A one-image PDF built from a fixture
begins %PDF-1.4, ends %%EOF and contains 5 objects — a
catalog, a page tree, and a page, a content stream and an image object for the image. Three images
produce 11 objects, a page tree with /Count 3 and a cross-reference
table of 12 entries. The image object's filter is
/DCTDecode; its colour space is /DeviceRGB for a three-component frame
header, /DeviceGray for one component and /DeviceCMYK for four. The build
confirmed that each fixture's bytes appear unchanged inside the output and that
startxref points at the table. The frame-header parser read the 1920×1080 fixture as
1920×1080 with 3 components; handed PNG bytes it refused:
"Not a JPEG file (missing FF D8 FF signature)." A DPI outside the writer's range is refused as "DPI must be between 24 and 600."
Size-change wording on invented byte counts
The result row's percent is produced by one function from two measured byte counts. The byte counts below are invented to exercise each rounding rule the function applies; they are arithmetic, not measurements, and they say nothing about how much any file will shrink.
| Before (bytes) | After (bytes) | Shown as |
|---|---|---|
| 2,400,000 | 1,800,000 | 25% smaller |
| 1,000,000 | 964,000 | 3.6% smaller |
| 1,000,000 | 905,000 | 9.5% smaller |
| 1,000,000 | 900,000 | 10% smaller |
| 850,000 | 918,000 | 8% larger — keep your original |
| 1,000,000 | 999,900 | less than 0.1% smaller |
| 640,000 | 640,000 | same size |
The rows show the rules. "3.6% smaller" and "9.5% smaller" keep one decimal place; from "10% smaller" upward the figure is a whole number. A change too small to round to a tenth of a percent is shown as "less than 0.1% smaller", identical counts as "same size", and growth is stated as plainly as shrinkage with the advice attached: "8% larger — keep your original". A before-count of zero returns no value (null): no percent exists, so none is shown.
Constants, read from the engine
The tables below are not typed. Each value is read from the engine module that enforces it when this page is built, so a changed constant changes this page.
Formats
| Format | MIME type | Output extension | Decoded | Produced | Note |
|---|---|---|---|---|---|
| PNG |
image/png
|
.png
|
yes | yes | — |
| JPEG |
image/jpeg
|
.jpg
|
yes | yes | — |
| WebP |
image/webp
|
.webp
|
yes | yes | — |
| GIF |
image/gif
|
.gif
|
yes | no | decoded, never encoded |
| BMP |
image/bmp
|
.bmp
|
yes | no | decoded, never encoded |
| HEIC |
image/heic
|
.heic
|
yes | no | decoded, never encoded |
| AVIF |
image/avif
|
.avif
|
yes | yes | decoded by the browser only |
The engine decodes PNG, JPEG, WebP, GIF, BMP, HEIC, AVIF and produces PNG, JPEG, WebP, AVIF. GIF, BMP, HEIC are decoded and never encoded — the target type admits only the 4 produced formats, and HEIC's absence from it is the licensing matter explained above. AVIF is decoded by the browser's own codec with no vendored fallback, as the encoder section states.
Target capabilities
| Target | Carries alpha | Lossy | Default quality (engine scale) | Encoder-scale equivalent |
|---|---|---|---|---|
| PNG | yes | no | none (lossless) | — |
| JPEG | no | yes | 0.92 | 92 |
| WebP | yes | yes | 0.85 | 85 |
| AVIF | yes | yes | 0.5 | 50 |
The engine's quality scale runs from zero to one; encoderQualityPercent maps it to the
whole numbers from 0 to 100 that the self-hosted encoders take. A target
whose quality is none is lossless and accepts no quality setting — the planner's refusal
for that case is quoted above. The two vendored lanes carry their own defaults. The Compress JPEG
lane encodes at 0.75 on the engine scale —
75 on the encoder's — with progressive scan order
on by default, and the compress-to-target search is
available on it. The AVIF lane encodes at
0.5 — 50 on the encoder's scale — at speed preset
8, and the compress-to-target search is
not available on it, so those pages offer the
quality control only.
Limits and page sizes
-
Output dimensions: at most 16,384 pixels per side (
MAX_DIMENSION). -
Output pixels: at most 80 megapixels (
MAX_OUTPUT_PIXELS), with a warning above 50 megapixels (MEGAPIXEL_WARN_THRESHOLD). - Percent scaling: refused above the bound the planner states as "Scaling beyond 1000% is not supported."
- A4: 595.28×841.89 points (8.27×11.69 inches).
- Letter: 612×792 points (8.50×11.00 inches).
- Points per inch 72; default DPI 96; default margin 36 points — each read back from the writer as described above.
The input limits are those stated under "Resize arithmetic" above; they bound a selection before any plan is made.
Tested against fixtures, fixed with tests
The engines are covered by an automated suite: byte-array fixtures for every format signature (including mismatched-extension and truncated cases), convergence and unreachable-target cases for the compression search, aspect-ratio edge cases for the resizer, and structural assertions for the PDF writer. If you believe an output is wrong, that is exactly the report we want — see the contact page. Confirmed issues are fixed in the engine and locked in with a new test so they cannot recur silently.
Last reviewed: 2026-10-01, against the engine modules named on this page. The worked figures and constants above are recomputed on every build.