NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
pub.dev · #4547 most downloaded on pub.dev
Cross-platform PDF toolkit for Flutter. Merge, split, render, extract, search, sign, encrypt, convert, build from scratch. Rust engine, off the main thread.
Last release 11 days ago
27 Sep 2026
Ships unpredictably
gaps range from 1 weeks to 3.4 years
Some releases are documented
notes for 21 of 45 stable releases
2 versions withdrawn
withdrawn after publishing
4 years old
70 releases · first in 2022
Engine updated — web: re-run flutter pub run pdf_manipulator:setup --force web (native updates itself)
flutter pub run pdf_manipulator:setup --force web (native updates itself)keep: list without extract, such as keep: [], failing to compile the engine with cannot find search in crate — the search module now compiles in every keep-set (#256 reported by @DarkWingMcQuack)compiled without 'legacy-crypto' — the web engine now carries the same legacy encryption and ICC colour support as native (#257 reported by @crurui)dependencies:
pdf_manipulator:
git:
url: https://github.com/whuppi/pdf_manipulator.git
ref: v5.0.1dependencies:
pdf_manipulator: ^5.0.1One column per quarter.
Engine updated — web: re-run flutter pub run pdf_manipulator:setup --force web (native updates itself)
flutter pub run pdf_manipulator:setup --force web (native updates itself)keep: list without extract, such as keep: [], failing to compile the engine with cannot find search in crate — the search module now compiles in every keep-set (#256 reported by @DarkWingMcQuack)compiled without 'legacy-crypto' — the web engine now carries the same legacy encryption and ICC colour support as native (#257 reported by @crurui)dependencies:
pdf_manipulator:
git:
url: https://github.com/whuppi/pdf_manipulator.git
ref: v5.0.1-dev.0dependencies:
pdf_manipulator: ^5.0.1-dev.0Breaking: PdfEditor.optimizeImages(quality:, minSize:) is removed → use reduceImages(PdfImagePolicy(jpegQuality: quality, minPixels: minSize, convertC
PdfEditor.optimizeImages(quality:, minSize:) is removed → use reduceImages(PdfImagePolicy(jpegQuality: quality, minPixels: minSize, convertCmykToRgb: true)); the count it returned is report.changedcompress(imageQuality:) → compress(images:), a PdfImagePolicy defaulting to PdfImagePolicy.ebook (150 ppi, JPEG quality 75), so a plain compress now downsamples images drawn above 225 ppi; pass PdfImagePolicy(jpegQuality: q) for the old behaviourget prefix): getTitle() → title, getAuthor() → author, getSubject() → subject, getKeywords() → keywords, getProducer() → producer, getCreationDate() → creationDate, and on PdfDoc getSignatures() → signatures; the one read with an argument is a noun method, getPageMediaBox(page) → pageMediaBox(page)flattenAllAnnotations() is flattenAnnotations({int? page}), and flattenForms() gains the same optional page; without it both flatten every page as beforeapplyRedactions() now returns a PdfRedactionReport (regions, glyphs removed, images modified or removed, paths pruned) instead of void; awaiting it still applies the redactionsmergeFrom(other) gains pages: (a 0-based list; null merges every page) and embedFile(name, data) gains description:, mimeType: and relationship:; positional callers are unchangedflutter pub run pdf_manipulator:setup --force web (native updates itself)PdfEditor.pageImages(page) — every image XObject drawn on a page with its resource name, bounds and transform; the name is what resizeImage takesPdfEditor.repositionImage(page, name, x:, y:) and setImageBounds(page, name, bounds) — move, or move and resize, an image by the name pageImages listsPdfEditor.reduceImages(PdfImagePolicy) — downsamples images to the resolution they are drawn at and re-encodes them by kind (JPEG, predicted Flate, CCITT G4), soft masks included, presets screen/ebook/print/lossless, one PdfImageReport row per image with the reason when it is kept; images reached only through annotation appearances, patterns or inline BI … EI stay as storedcompress(images: PdfImagePolicy.screen) — the one-shot takes the same policyPdfImagePolicy.minSavings (default 10%: a lossy re-encode must earn its bytes or the image is kept), chromaSubsampling (auto = 4:4:4 from quality 90, full, half; print pins full) and recompressJpeg (re-encode stored JPEGs at jpegQuality even when not downsampled; off by default)PdfEditor.pageCropBox(page) (null when the page has no CropBox), setPageMediaBox, setPageCropBox and setPageRotation(page, degrees:) (absolute, where rotatePage is relative)PdfEditor.clearEraseRegions(page) to drop the regions queued by eraseRegions before savePdfEditor.sanitize(PdfSanitizeOptions) — strip metadata, JavaScript actions and embedded files in one pass; scrubMetadata() is its metadata-only formPdfDoc.formFields and formField(name) — every AcroForm field with its type, typed value, tooltip, bounds, max length, alignment and read-only/required flagsPdfEditor: removeFormField, setFormFieldReadOnly, setFormFieldRequired, setFormFieldTooltip, setFormFieldBounds, setFormFieldMaxLength, setFormFieldAlignment, setFormFieldBackgroundColor, setFormFieldBorderColor, setFormFieldBorderWidth, setFormFieldAppearance and setFormFieldFlags — each read back after save by formFieldsPdfDoc.exportFormData(sink, format:) — the filled values as FDF or XFDFPdfDoc.xfa (PdfXfaInfo with field count, page count and field types, or null without an XFA packet) and the one-shot convertXfaToAcroForm(source, output) that rewrites an XFA form as a plain AcroFormPdfDoc.attachments (name, size, description, MIME type, read from the name tree without decoding the files) and extractAttachment(name, sink)pageImages listing Form XObjects — only /Subtype /Image resources are listed, so every name is a valid resizeImage targetw:tblGrid widths, scaled to the printable width (#243 reported by @kampmapa1-design)WASM init failed: Out of memory during rapid create-and-dispose churn — a worker slot is freed when the browser confirms the worker is gone, not at terminate()pageMediaBox returning the box's far corner as its width and height whenever the MediaBox origin is not (0, 0)eraseRegions and addRedaction acting on the wrong rectangle — the width and height were passed where the far corner was expected, so every region past the origin was off by its own sizeaddStamp losing the stamp on save when its rectangle sits high on the page — the same size-for-corner mix-up placed the label above the page edge; the builder's image was drawn at the wrong size for the same reasonmergeFrom dropping the merged document's form fields — the pages arrived but no /Fields entry pointed at them, so they could not be listed or filled; the AcroForm is carried across nowtext#232Fplain — the solidus was escaped twice.doc fixes)dependencies:
pdf_manipulator:
git:
url: https://github.com/whuppi/pdf_manipulator.git
ref: v5.0.0dependencies:
pdf_manipulator: ^5.0.0Breaking: PdfEditor.optimizeImages(quality:, minSize:) is removed → use reduceImages(PdfImagePolicy(jpegQuality: quality, minPixels: minSize, convertC
PdfEditor.optimizeImages(quality:, minSize:) is removed → use reduceImages(PdfImagePolicy(jpegQuality: quality, minPixels: minSize, convertCmykToRgb: true)); the count it returned is report.changedcompress(imageQuality:) → compress(images:), a PdfImagePolicy defaulting to PdfImagePolicy.ebook (150 ppi, JPEG quality 75), so a plain compress now downsamples images drawn above 225 ppi; pass PdfImagePolicy(jpegQuality: q) for the old behaviourget prefix): getTitle() → title, getAuthor() → author, getSubject() → subject, getKeywords() → keywords, getProducer() → producer, getCreationDate() → creationDate, and on PdfDoc getSignatures() → signatures; the one read with an argument is a noun method, getPageMediaBox(page) → pageMediaBox(page)flattenAllAnnotations() is flattenAnnotations({int? page}), and flattenForms() gains the same optional page; without it both flatten every page as beforeapplyRedactions() now returns a PdfRedactionReport (regions, glyphs removed, images modified or removed, paths pruned) instead of void; awaiting it still applies the redactionsmergeFrom(other) gains pages: (a 0-based list; null merges every page) and embedFile(name, data) gains description:, mimeType: and relationship:; positional callers are unchangedflutter pub run pdf_manipulator:setup --force web (native updates itself)PdfEditor.pageImages(page) — every image XObject drawn on a page with its resource name, bounds and transform; the name is what resizeImage takesPdfEditor.repositionImage(page, name, x:, y:) and setImageBounds(page, name, bounds) — move, or move and resize, an image by the name pageImages listsPdfEditor.reduceImages(PdfImagePolicy) — downsamples images to the resolution they are drawn at and re-encodes them by kind (JPEG, predicted Flate, CCITT G4), soft masks included, presets screen/ebook/print/lossless, one PdfImageReport row per image with the reason when it is kept; images reached only through annotation appearances, patterns or inline BI … EI stay as storedcompress(images: PdfImagePolicy.screen) — the one-shot takes the same policyPdfImagePolicy.minSavings (default 10%: a lossy re-encode must earn its bytes or the image is kept), chromaSubsampling (auto = 4:4:4 from quality 90, full, half; print pins full) and recompressJpeg (re-encode stored JPEGs at jpegQuality even when not downsampled; off by default)PdfEditor.pageCropBox(page) (null when the page has no CropBox), setPageMediaBox, setPageCropBox and setPageRotation(page, degrees:) (absolute, where rotatePage is relative)PdfEditor.clearEraseRegions(page) to drop the regions queued by eraseRegions before savePdfEditor.sanitize(PdfSanitizeOptions) — strip metadata, JavaScript actions and embedded files in one pass; scrubMetadata() is its metadata-only formPdfDoc.formFields and formField(name) — every AcroForm field with its type, typed value, tooltip, bounds, max length, alignment and read-only/required flagsPdfEditor: removeFormField, setFormFieldReadOnly, setFormFieldRequired, setFormFieldTooltip, setFormFieldBounds, setFormFieldMaxLength, setFormFieldAlignment, setFormFieldBackgroundColor, setFormFieldBorderColor, setFormFieldBorderWidth, setFormFieldAppearance and setFormFieldFlags — each read back after save by formFieldsPdfDoc.exportFormData(sink, format:) — the filled values as FDF or XFDFPdfDoc.xfa (PdfXfaInfo with field count, page count and field types, or null without an XFA packet) and the one-shot convertXfaToAcroForm(source, output) that rewrites an XFA form as a plain AcroFormPdfDoc.attachments (name, size, description, MIME type, read from the name tree without decoding the files) and extractAttachment(name, sink)pageImages listing Form XObjects — only /Subtype /Image resources are listed, so every name is a valid resizeImage targetdependencies:
pdf_manipulator:
git:
url: https://github.com/whuppi/pdf_manipulator.git
ref: v5.0.0-dev.0dependencies:
pdf_manipulator: ^5.0.0-dev.0Engine updated — web: re-run flutter pub run pdf_manipulator:setup --force web (native updates itself)
flutter pub run pdf_manipulator:setup --force web (native updates itself)w:tblGrid, so no edit to w:tblW, w:gridCol or w:tcW could change the layout. Declared widths now drive the columns, scaled down proportionally when they exceed the printable width; content sampling remains only for tables that declare no widths, and can no longer size a table past the page edge (#243 reported by @kampmapa1-design)WASM init failed: Out of memory during rapid create-and-dispose churn. The page-wide worker budget freed a slot the moment worker.terminate() was called, but the browser releases a worker's WASM memory later, on its own schedule, so a burst of disposes let new workers boot into memory that dead ones still held. Every worker now holds a liveness lock for its lifetime in all three I/O modes (OPFS reclaim already used it), and a slot returns only when the browser confirms the worker is gone — the next boot past the cap waits its turn, as the native lane budget already did/Perms fix rides on top) and office_oxide v0.1.3 → v0.1.11 (write-path correctness fixes, legacy .doc tables and lists)dependencies:
pdf_manipulator:
git:
url: https://github.com/whuppi/pdf_manipulator.git
ref: v4.2.1-dev.0dependencies:
pdf_manipulator: ^4.2.1-dev.0Engine updated — web: re-run flutter pub run pdf_manipulator:setup --force web (native updates itself)
flutter pub run pdf_manipulator:setup --force web (native updates itself)setCheckboxFieldValue(fieldName, checked) for a checkbox whose on-state name you do not know. A checkbox is on when its value names one of the states in its /AP /N dictionary, and that name is a convention rather than a rule — /Yes is the common one, /On and /1 are not rare. Passing true picks whichever name that widget offers. setFormFieldValue still takes the name directly when you do know it.setFormFieldValue passed every value through as text, so a checkbox kept the unchecked state it was parsed with and flattenForms baked that empty appearance into the page. /V and /AS were written as text strings too, which name no appearance state, so the value was also lost when the file was opened again. A button's value is now read as a state name, resolved against the states that widget actually offers, and written as a name; flattening draws the state that was set, using the widget's own artwork where the document supplies it (#215 reported by @DarkWingMcQuack)/AS was written to the parent field, but a radio group's appearance states live on its kid widgets, so nothing changed on the page. The two button flags were also swapped when reading a widget — ISO 32000-1 puts Radio at bit position 16 and Pushbutton at 17 — so a spec-conformant radio group was read as a push button, which has no state to track. Setting a group's value now turns on the kid that offers that state and clears its siblingsdependencies:
pdf_manipulator:
git:
url: https://github.com/whuppi/pdf_manipulator.git
ref: v4.2.0dependencies:
pdf_manipulator: ^4.2.0flutter pub run pdf_manipulator:setup --force web (native updates itself)setCheckboxFieldValue(fieldName, checked) — checks a checkbox without knowing its on-state name (/Yes, /On, /1 …); setFormFieldValue still takes the name directlyEngine updated — web: re-run flutter pub run pdf_manipulator:setup --force web (native updates itself)
flutter pub run pdf_manipulator:setup --force web (native updates itself)setCheckboxFieldValue(fieldName, checked) for a checkbox whose on-state name you do not know. A checkbox is on when its value names one of the states in its /AP /N dictionary, and that name is a convention rather than a rule — /Yes is the common one, /On and /1 are not rare. Passing true picks whichever name that widget offers. setFormFieldValue still takes the name directly when you do know it.setFormFieldValue passed every value through as text, so a checkbox kept the unchecked state it was parsed with and flattenForms baked that empty appearance into the page. /V and /AS were written as text strings too, which name no appearance state, so the value was also lost when the file was opened again. A button's value is now read as a state name, resolved against the states that widget actually offers, and written as a name; flattening draws the state that was set, using the widget's own artwork where the document supplies it (#215 reported by @DarkWingMcQuack)/AS was written to the parent field, but a radio group's appearance states live on its kid widgets, so nothing changed on the page. The two button flags were also swapped when reading a widget — ISO 32000-1 puts Radio at bit position 16 and Pushbutton at 17 — so a spec-conformant radio group was read as a push button, which has no state to track. Setting a group's value now turns on the kid that offers that state and clears its siblingsdependencies:
pdf_manipulator:
git:
url: https://github.com/whuppi/pdf_manipulator.git
ref: v4.2.0-dev.0dependencies:
pdf_manipulator: ^4.2.0-dev.0Added scan-dirs : extra directories for keep: auto to scan, on top of your app's lib/ and bin/ . Code living somewhere else — a tools/ folder, a sibli
scan-dirs: extra directories for keep: auto to scan, on top of your app's lib/ and bin/. Code living somewhere else — a tools/ folder, a sibling package in a monorepo — was invisible to the scan, so a capability you really call could be trimmed away. Now you point at it: scan-dirs: [tools, packages/shared/lib], paths relative to your app. It only applies where there is a scan to widen (keep: auto, and not detector: record-use), and a directory that isn't there keeps the full binary rather than quietly scanning less than you asked for.keep: auto now actually trims the native engine, so a build that had been silently shipping every capability gets a smaller one. If anything is missing, state it yourself with keep: [render, …].setup ignoring your config when run from a subfolder of your project. It read the config and scanned for source relative to wherever you were standing, so from anywhere but the app root it found no pubspec, silently read that as "nothing configured", and shipped the full web engine. Both lanes now locate your app the same way — from a path inside its own .dart_tool/, which neither the working directory nor where you invoke the command can move.keep: auto keeping every capability on native no matter what your app used. The build hook read its own working directory as your app's source, but hooks are started inside the package they belong to — so the scan matched pdf_manipulator's own implementations and concluded you used all of them, every time, while reporting success. It now derives your app from the hook's output path. Web was never affected: you run setup from your own project, so its scan was always looking in the right place.sign() call to an app that had already been built and the engine kept compiling without signature support. The source the scan reads is now a hook dependency, so the decision re-evaluates when your app changes.dependencies:
pdf_manipulator:
git:
url: https://github.com/whuppi/pdf_manipulator.git
ref: v4.1.0dependencies:
pdf_manipulator: ^4.1.0scan-dirs — extra directories for keep: auto to scan beyond lib/ and bin/ (scan-dirs: [tools, packages/shared/lib], relative to your app); a directory that is not there keeps the full binary rather than scanning lesskeep: auto now actually trims the native engine; if something is missing, state it with keep: [render, …]setup ignoring your config when run from a subfolder of your project — both lanes now locate your app from its own .dart_tool/, not from the working directorykeep: auto keeping every capability on native — the build hook scanned its own package instead of your app; it now derives your app from the hook's output path (web was never affected)sign() call recompiles the engine with signaturesAdded scan-dirs : extra directories for keep: auto to scan, on top of your app's lib/ and bin/ . Code living somewhere else — a tools/ folder, a sibli
scan-dirs: extra directories for keep: auto to scan, on top of your app's lib/ and bin/. Code living somewhere else — a tools/ folder, a sibling package in a monorepo — was invisible to the scan, so a capability you really call could be trimmed away. Now you point at it: scan-dirs: [tools, packages/shared/lib], paths relative to your app. It only applies where there is a scan to widen (keep: auto, and not detector: record-use), and a directory that isn't there keeps the full binary rather than quietly scanning less than you asked for.keep: auto now actually trims the native engine, so a build that had been silently shipping every capability gets a smaller one. If anything is missing, state it yourself with keep: [render, …].setup ignoring your config when run from a subfolder of your project. It read the config and scanned for source relative to wherever you were standing, so from anywhere but the app root it found no pubspec, silently read that as "nothing configured", and shipped the full web engine. Both lanes now locate your app the same way — from a path inside its own .dart_tool/, which neither the working directory nor where you invoke the command can move.keep: auto keeping every capability on native no matter what your app used. The build hook read its own working directory as your app's source, but hooks are started inside the package they belong to — so the scan matched pdf_manipulator's own implementations and concluded you used all of them, every time, while reporting success. It now derives your app from the hook's output path. Web was never affected: you run setup from your own project, so its scan was always looking in the right place.sign() call to an app that had already been built and the engine kept compiling without signature support. The source the scan reads is now a hook dependency, so the decision re-evaluates when your app changes.dependencies:
pdf_manipulator:
git:
url: https://github.com/whuppi/pdf_manipulator.git
ref: v4.1.0-dev.0dependencies:
pdf_manipulator: ^4.1.0-dev.0Breaking: the engine is configured with three flat keys under hooks: user_defines: pdf_manipulator: — keep (which capabilities compile: auto to scan y
hooks: user_defines: pdf_manipulator: — keep (which capabilities compile: auto to scan your source, [render, …] for an exact set, or omit for everything), detector (how auto detects, only valid with keep: auto), and build (how they compile). Migrating from 3.x: trim: auto → keep: auto, trim: {keep: [render, …]} → keep: [render, …], trim-detector: X → detector: X, and the web-only setup --trim flag is gone — put your choice in pubspec and re-run flutter pub run pdf_manipulator:setup (web now reads the same block native does). Invalid configs fail the build loudly, never silently: an unknown key, a bad value, or a detector set without keep: auto all stop with a clear message (PR #185)build: in that same block — pick HOW the engine compiles: speed (default, prebuilt), size (smaller binary, opt-level z, a little slower), or debug (keeps symbols so a native engine crash points to a file and line). size and debug compile from source and need Rust; works for web and native (PR #185)failed to load manifest ... feature edition2024 is required cargo error when the engine compiles from source (iOS device builds, git dependencies, or any download-miss) on an older Rust toolchain — the build now checks the required Rust version first and stops with a clear message naming the exact version and the rustup command to install it (#183 reported by @mrhazelh, PR #185)dependencies:
pdf_manipulator:
git:
url: https://github.com/whuppi/pdf_manipulator.git
ref: v4.0.0dependencies:
pdf_manipulator: ^4.0.0hooks: user_defines: pdf_manipulator: — keep (auto, [render, …], or omit for everything), detector (only with keep: auto) and build. Migrate: trim: auto → keep: auto, trim: {keep: […]} → keep: […], trim-detector: X → detector: X, drop setup --trim and re-run flutter pub run pdf_manipulator:setup; an invalid config fails the build with a clear messagebuild: in the same block — speed (default, prebuilt), size (smaller, opt-level z) or debug (symbols for native crash traces); size and debug compile from source and need Rustfeature edition2024 is required cargo error on older Rust toolchains — the build checks the required Rust version first and names the rustup command to run (#183 reported by @mrhazelh)Breaking: the engine is configured with three flat keys under hooks: user_defines: pdf_manipulator: — keep (which capabilities compile: auto to scan y
hooks: user_defines: pdf_manipulator: — keep (which capabilities compile: auto to scan your source, [render, …] for an exact set, or omit for everything), detector (how auto detects, only valid with keep: auto), and build (how they compile). Migrating from 3.x: trim: auto → keep: auto, trim: {keep: [render, …]} → keep: [render, …], trim-detector: X → detector: X, and the web-only setup --trim flag is gone — put your choice in pubspec and re-run flutter pub run pdf_manipulator:setup (web now reads the same block native does). Invalid configs fail the build loudly, never silently: an unknown key, a bad value, or a detector set without keep: auto all stop with a clear message (PR #185)build: in that same block — pick HOW the engine compiles: speed (default, prebuilt), size (smaller binary, opt-level z, a little slower), or debug (keeps symbols so a native engine crash points to a file and line). size and debug compile from source and need Rust; works for web and native (PR #185)failed to load manifest ... feature edition2024 is required cargo error when the engine compiles from source (iOS device builds, git dependencies, or any download-miss) on an older Rust toolchain — the build now checks the required Rust version first and stops with a clear message naming the exact version and the rustup command to install it (#183 reported by @mrhazelh, PR #185)dependencies:
pdf_manipulator:
git:
url: https://github.com/whuppi/pdf_manipulator.git
ref: v4.0.0-dev.0dependencies:
pdf_manipulator: ^4.0.0-dev.0Breaking: flattening CJK or emoji form values no longer uses a bundled font (it added 4.4 MB to every install). Register one once: await pdf.registerF
await pdf.registerFallbackFont(PdfFallbackFontKind.cjk, fontBytes) (.emoji for emoji). Without one the value is still saved correctly — only the baked-in look falls back to the field's own font.trim-detector value analyzer is now scan → change it or delete the line — it is the default (PR #172)flutter pub run pdf_manipulator:setup --force web (native updates itself)trim: auto under hooks: user_defines: pdf_manipulator: in your app pubspec, or choose yourself with trim: {keep: [render, signatures]}; on web also run flutter pub run pdf_manipulator:setup --trim. Needs Rust — the engine compiles once on your machine and is cached. Wrong or missing pieces fail with a clear message, never a broken app.Pdf.registerFallbackFont(PdfFallbackFontKind, Uint8List).Cargo.lock — wasm-bindgen-cli, binaryen, and jq are no longer needed, Rust is the only requirement on every platform, and the exact-version rejection is gone with the tools (#177 reported by @DarkWingMcQuack, PR #178)analyzer version onto your app, which blocked current freezed / json_serializable and friends. The analyzer dependency is gone: trim: auto uses a dependency-free source scan. It can only keep slightly more than you use, never less — state trim: {keep: [...]} yourself for the exact minimum (#171 reported by @DarkWingMcQuack, PR #172)Cargo.lock), which broke trim and every compile-from-source path with "No such file or directory" (#171 reported by @DarkWingMcQuack, PR #172)wasm-pack — it is not used. The build now points at the tool it actually misses, with the exact install command (PR #172)trim: auto counting member names inside comments as usage — a doc line saying "render them" kept the render capability. The scan now ignores comments, and prints where each kept member was matched, e.g. render (lib/preview.dart:12) (#175 reported by @DarkWingMcQuack, PR #178)dependencies:
pdf_manipulator:
git:
url: https://github.com/whuppi/pdf_manipulator.git
ref: v3.0.0dependencies:
pdf_manipulator: ^3.0.0await pdf.registerFallbackFont(PdfFallbackFontKind.cjk, fontBytes) (.emoji for emoji); without one the value is still saved, only the baked-in look falls back to the field's fonttrim-detector: analyzer is now scan → change it or delete the line, it is the defaultflutter pub run pdf_manipulator:setup --force web (native updates itself)trim: auto under hooks: user_defines: pdf_manipulator:, or trim: {keep: [render, signatures]}; on web also run flutter pub run pdf_manipulator:setup --trim; needs Rust, compiled once and cached (#167)Pdf.registerFallbackFont(PdfFallbackFontKind, Uint8List)wasm-bindgen-cli, binaryen and jq are no longer needed, Rust is the only requirement (#177 reported by @DarkWingMcQuack)analyzer version onto your app — the dependency is gone; trim: auto uses a dependency-free scan that can only keep slightly more than you use, never less (#171 reported by @DarkWingMcQuack)Cargo.lock, which broke trim and every compile-from-source path (#171 reported by @DarkWingMcQuack)wasm-pack — it now names the tool it actually misses, with the install commandtrim: auto counting member names inside comments as usage — the scan ignores comments and prints where each kept member matched, e.g. render (lib/preview.dart:12) (#175 reported by @DarkWingMcQuack)Engine updated — web: re-run flutter pub run pdf_manipulator:setup --force web (native updates itself)
flutter pub run pdf_manipulator:setup --force web (native updates itself)Cargo.lock — wasm-bindgen-cli, binaryen, and jq are no longer needed, Rust is the only requirement on every platform, and the exact-version rejection is gone with the tools (#177 reported by @DarkWingMcQuack, PR #178)trim: auto counting member names inside comments as usage — a doc line saying "render them" kept the render capability. The scan now ignores comments, and prints where each kept member was matched, e.g. render (lib/preview.dart:12) (#175 reported by @DarkWingMcQuack, PR #178)dependencies:
pdf_manipulator:
git:
url: https://github.com/whuppi/pdf_manipulator.git
ref: v3.0.0-dev.1dependencies:
pdf_manipulator: ^3.0.0-dev.1Breaking: flattening CJK or emoji form values no longer uses a bundled font (it added 4.4 MB to every install). Register one once: await pdf.registerF
await pdf.registerFallbackFont(PdfFallbackFontKind.cjk, fontBytes) (.emoji for emoji). Without one the value is still saved correctly — only the baked-in look falls back to the field's own font.trim-detector value analyzer is now scan → change it or delete the line — it is the default (PR #172)flutter pub run pdf_manipulator:setup --force web (native updates itself)trim: auto under hooks: user_defines: pdf_manipulator: in your app pubspec, or choose yourself with trim: {keep: [render, signatures]}; on web also run flutter pub run pdf_manipulator:setup --trim. Needs Rust — the engine compiles once on your machine and is cached. Wrong or missing pieces fail with a clear message, never a broken app.Pdf.registerFallbackFont(PdfFallbackFontKind, Uint8List).analyzer version onto your app, which blocked current freezed / json_serializable and friends. The analyzer dependency is gone: trim: auto uses a dependency-free source scan. It can only keep slightly more than you use, never less — state trim: {keep: [...]} yourself for the exact minimum (#171 reported by @DarkWingMcQuack, PR #172)Cargo.lock), which broke trim and every compile-from-source path with "No such file or directory" (#171 reported by @DarkWingMcQuack, PR #172)wasm-pack — it is not used. The build now points at the tool it actually misses, with the exact install command (PR #172)dependencies:
pdf_manipulator:
git:
url: https://github.com/whuppi/pdf_manipulator.git
ref: v3.0.0-dev.0dependencies:
pdf_manipulator: ^3.0.0-dev.0Warning Retracted on pub.dev : the published archive was missing a build file (the engine's Cargo.lock ), breaking trim and compile-from-source instal
Warning
Retracted on pub.dev: the published archive was missing a build file (the engine's Cargo.lock), breaking trim and compile-from-source installs. Use 3.0.0, which consolidates everything here. Git-tag consumers (ref: v2.2.0) are unaffected — this tag and its binaries are complete.
await pdf.registerFallbackFont(PdfFallbackFontKind.cjk, fontBytes) (.emoji for emoji). Without one the value is still saved correctly — only the baked-in look falls back to the field's own font.flutter pub run pdf_manipulator:setup --force web (native updates itself)trim: auto under hooks: user_defines: pdf_manipulator: in your app pubspec, or choose yourself with trim: {keep: [render, signatures]}; on web also run flutter pub run pdf_manipulator:setup --trim. Needs Rust — the engine compiles once on your machine and is cached. Wrong or missing pieces fail with a clear message, never a broken app.Pdf.registerFallbackFont(PdfFallbackFontKind, Uint8List).dependencies:
pdf_manipulator:
git:
url: https://github.com/whuppi/pdf_manipulator.git
ref: v2.2.0dependencies:
pdf_manipulator: ^2.2.0Retracted on pub.dev: the published archive was missing a build file (the engine's
Cargo.lock), breakingtrimand compile-from-source installs. Superseded by 3.0.0, which consolidates everything here.
await pdf.registerFallbackFont(PdfFallbackFontKind.cjk, fontBytes) (.emoji for emoji); without one the value is still saved, only the baked-in look falls back to the field's fontflutter pub run pdf_manipulator:setup --force web (native updates itself)trim: auto under hooks: user_defines: pdf_manipulator:, or trim: {keep: [render, signatures]}; on web also run flutter pub run pdf_manipulator:setup --trim; needs Rust, compiled once and cached (#167)Pdf.registerFallbackFont(PdfFallbackFontKind, Uint8List)Warning Retracted on pub.dev : the published archive was missing a build file (the engine's Cargo.lock ), breaking trim and compile-from-source instal
Warning
Retracted on pub.dev: the published archive was missing a build file (the engine's Cargo.lock), breaking trim and compile-from-source installs. Use 3.0.0, which consolidates everything here. Git-tag consumers (ref: v2.2.0) are unaffected — this tag and its binaries are complete.
await pdf.registerFallbackFont(PdfFallbackFontKind.cjk, fontBytes) (.emoji for emoji). Without one the value is still saved correctly — only the baked-in look falls back to the field's own font.flutter pub run pdf_manipulator:setup --force web (native updates itself)trim: auto under hooks: user_defines: pdf_manipulator: in your app pubspec, or choose yourself with trim: {keep: [render, signatures]}; on web also run flutter pub run pdf_manipulator:setup --trim. Needs Rust — the engine compiles once on your machine and is cached. Wrong or missing pieces fail with a clear message, never a broken app.Pdf.registerFallbackFont(PdfFallbackFontKind, Uint8List).dependencies:
pdf_manipulator:
git:
url: https://github.com/whuppi/pdf_manipulator.git
ref: v2.2.0-dev.0dependencies:
pdf_manipulator: ^2.2.0-dev.0Engine updated — web: re-run flutter pub run pdf_manipulator:setup --force web (native updates itself)
flutter pub run pdf_manipulator:setup --force web (native updates itself)flattenForms dropping a field's value when the fill and the flatten happen in separate editor sessions — a reopened editor carries no in-session modified-field map, so the flattener now regenerates the appearance from the persisted /V (saved alongside /NeedAppearances) instead of baking the stale placeholder (#161 reported by @DarkWingMcQuack, PR #162)addImageStamp erasing a page's existing form widgets when the page references its annotations through an indirect /Annots reference rather than a direct array (#161 reported by @DarkWingMcQuack, PR #162)/V when the AcroForm sets /NeedAppearances, matching the flattener (PR #162)dependencies:
pdf_manipulator:
git:
url: https://github.com/whuppi/pdf_manipulator.git
ref: v2.1.4dependencies:
pdf_manipulator: ^2.1.4flutter pub run pdf_manipulator:setup --force web (native updates itself)flattenForms dropping a field's value when fill and flatten happen in separate editor sessions — the appearance is regenerated from the persisted /V (#161 reported by @DarkWingMcQuack)addImageStamp erasing a page's form widgets when /Annots is an indirect reference (#161 reported by @DarkWingMcQuack)/V under /NeedAppearances, matching the flattenerNothing published for this version
Engine updated — web: re-run flutter pub run pdf_manipulator:setup --force web (native updates itself)
flutter pub run pdf_manipulator:setup --force web (native updates itself)setFormFieldValue reporting field-not-found on forms whose field names are raw UTF-8 (LibreOffice-class producers) — one spec-tolerant text-string decoder covers UTF-16, UTF-8 and PDFDocEncoding (#155)flattenForms baking mojibake for values outside ASCII (ß → ß) — values decode per ISO 32000-1 §7.9.2.2 and appearance text is written in WinAnsi (#155)getTitle and friends) mangling non-ASCII on read, and writes now encode per spec (#155)Nothing published for this version
Engine updated — web: re-run flutter pub run pdf_manipulator:setup --force web (native updates itself)
flutter pub run pdf_manipulator:setup --force web (native updates itself)dart2wasm) compile failure — a switch over Object? that dart2js accepted was non-exhaustive under dart2wasm (#145 reported by @DarkWingMcQuack)pana can analyzeNothing published for this version
Fixed the README banner not rendering on pub.dev — the element is flattened to a plain image in the published package
<picture> element is flattened to a plain image in the published packageNothing published for this version
Engine updated — web: re-run flutter pub run pdf_manipulator:setup --force web (native updates itself)
flutter pub run pdf_manipulator:setup --force web (native updates itself)PdfEditor.setProducer() / getProducer() and setCreationDate() / getCreationDate() (raw PDF date strings, e.g. D:20240101120000Z), plus PdfDoc.producer, PdfDoc.creator, and PdfDoc.creationDate read on openaddImageStamp rendering a transparent-background PNG as a solid black box — the alpha channel ships as a grayscale /SMask (#103 reported by @DarkWingMcQuack)RenderedPage.data doc — render() returns PNG-encoded bytes (decode to read pixels), not raw RGBANothing published for this version
Nothing published for this version
Docs-only — no code or API changes.
Docs-only — no code or API changes.
Nothing published for this version
The concurrency rewrite. Every operation now runs fully isolated on its own *lane* — a dedicated Rust thread (native) or Web Worker (web).
The concurrency rewrite. Every operation now runs fully isolated on its own lane — a dedicated Rust thread (native) or Web Worker (web).
webCoordinatorUrl + webWorkerUrl → one webLaneWorkerUrllane_worker.js (was coordinator.js + worker.js)flutter pub run pdf_manipulator:setup --force web (native updates itself)PdfTask<T> — a Future plus cancel(); cancelling kills just that jobpdf.dispose() is instant — no joins, no timeouts, no leaks; in-flight ops resolve with PdfCancelledPdfConfig.maxLanes — concurrent lanes per instance (default: half the cores, min 2)Nothing published for this version
Fixed release build routing for consumer builds (by @Binary-Parse)
.cmd extension for Android cross-compilation (by @Binary-Parse)Nothing published for this version
Fixed README version not stamped on pub.dev
Nothing published for this version
Web setup now verifies all assets against release hashes — detects stale files automatically
setup supports --force to re-download everything, --native to pre-fetch the native binaryNothing published for this version
- Fixed changelog on pub.dev
Nothing published for this version
Fixed changelog on pub.dev missing commit history between versions
Nothing published for this version
Added public API doc comments across all exported classes and methods
flutter pub run pdf_manipulator:setup (avoids triggering native build hooks with dart run)Nothing published for this version
Complete ground-up rewrite — new Rust engine, new instance API, cross-platform (previously Android only). The package docs carry the migration guide a
Complete ground-up rewrite — new Rust engine, new instance API, cross-platform (previously Android only). The package docs carry the migration guide and the full capability list.
Pdf() with dispose(); batch editing via pdf.edit(source), create from scratch via pdf.build()DataSource in, DataSink out — no file paths, no dart:io, same code on every targetPdfError sealed class — no more PlatformExceptionNothing published for this version
The last release of Android-only version before the cross-platform rewrite
<!-- release: no-tag -->
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →