NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
A pure Dart package wrapping the PDFium library for PDF rendering, text extraction, and annotation support.
Last release 1 months ago
24 Aug 2026
Too new to tell
only 2 release windows
Nearly every release is documented
notes for 1 of 1 stable releases
1 version withdrawn
withdrawn after publishing
3 months old
5 releases · first in 2026
One column per month.
The first stable release of betto_pdfium and its iOS companion plugin betto_pdfium_ios .
The first stable release of betto_pdfium and its iOS companion plugin
betto_pdfium_ios.
betto_pdfium is a pure Dart wrapper around
PDFium via dart:ffi. There is no
dependency on dart:ui or Flutter, so it works in CLI tools, server-side Dart,
and Flutter apps alike. Pre-built PDFium binaries
(bblanchon/pdfium-binaries
chromium/7906) are downloaded automatically by the native-assets build hook on
first use — no C++ toolchain required.
There are no functional changes since 0.1.0-dev.4; this release promotes the
dev channel to a stable version. The notes below summarise everything that
landed across 0.1.0-dev.1 → 0.1.0.
dependencies:
betto_pdfium: ^0.1.0Flutter iOS apps also need the companion plugin, which delivers the PDFium
xcframework via Swift Package Manager:
dependencies:
betto_pdfium: ^0.1.0
betto_pdfium_ios: ^0.1.0All eight targets are supported:
| Platform | Status |
|---|---|
| macOS arm64 | Supported |
| Linux x86_64 | Supported |
| Linux arm64 | Supported |
| Windows x86_64 | Supported |
| iOS arm64 | Supported (xcframework) |
| Android arm64 | Supported |
| Android x86_64 | Supported |
| Web (WASM) | Supported |
On desktop the binary is fetched automatically on the first dart test or
dart run. On iOS the xcframework arrives through SPM during flutter pub get.
Web requires make fetch_wasm_assets to stage the WASM/JS artifacts — see the
package README.
Everything hangs off PdfDocument:
PdfDocument.fromBytes() loads into a backgroundPdfExtractionExceptionPdfError.passwordRequired from PdfError.invalidDocument.close() releases the native handle and is safe to call more than once.getMetadata() (title, author, subject, keywords, creator,getDocumentInfo() (file version,pageCount.extractPlainText() streams PdfPageText per page withhasTextLayer and hasUnicodeErrors flags; isPlainTextExtractable() givesrenderPageToBytes() produces a raw BGRA pixel buffer, withgetPageSize() returns dimensions in points plus sizeForDpi().extractAnnotations() streams typed annotations: text,quadPoints), free text,extractImages() streams image objects with bounding boxes andrenderImage() fetches a single image'ssearch() streams matches with page index, character index, andtableOfContents returns the full nested bookmarkgetThumbnail() returns the embedded /Thumb stream whenpdfinfo CLI — dart run bin/pdfinfo.dart inspects metadata, documentStreams are fully cancellable — cancelling or calling close() stops processing
immediately with no native handle leaks.
PdfiumIsolate), lazily spawned and shared by every PdfDocument.dart:isolate is unavailable, so the same concurrency model ispostMessage protocol. Largedart:ui Image, keeping thedev.4) — when running from.dart_tool/lib/. The loader now walks up from thedev.4) — SDK constraint raised to ^3.13.0, verified608 tests passing at 96.2% line coverage.
packages/betto_pdfium/README.mdpackages/betto_pdfium/CHANGELOG.mdWhen dart test runs from a workspace member package, the native-assets pipeline stages the bundled PDFium library to the workspace root .dart_tool/lib
When dart test runs from a workspace member package, the native-assets
pipeline stages the bundled PDFium library to the workspace root
.dart_tool/lib/, not the package's own .dart_tool/lib/. The runtime loader
(_openLibrary) previously probed only the current directory's
.dart_tool/lib/, so a PDFium call issued from a spawned isolate — which cannot
see the test runner's LD_LIBRARY_PATH and so cannot use the bare-name
fallback — failed with cannot open shared object file: No such file or directory.
This surfaced under Dart 3.13 in the kmdb workspace's PDF vault indexing:
dart test (run from packages/kmdb_cli) stages libpdfium.so to the
workspace root .dart_tool/lib/, but _openLibrary looked in the package dir
and missed it. betto_pdfium's own CI never exercised this because its test
suite injects an explicit dylib path via nativeDylibPath().
dartToolLibCandidates(startDir, libName): walks up from the working.dart_tool/lib/<libName> nearest-first._openLibrary in place$cwd/.dart_tool/lib/ probe.coverage:ignore region.Version stays at 0.1.0-dev.4; bump to 0.1.0 (WI-9) once CI is green.
Co-authored-by: Claude Opus 4.8 noreply@anthropic.com
dart test runs from a
workspace member package, the native-assets pipeline stages the bundled
PDFium library to the workspace root .dart_tool/lib/, not the package's
own. The runtime loader previously probed only the current directory, so a
PDFium call issued from a spawned isolate (which cannot see the test runner's
LD_LIBRARY_PATH) failed to load the library with
cannot open shared object file. The loader now walks up from the working
directory, probing each ancestor's .dart_tool/lib/, covering both the
single-package and workspace layouts on Linux, macOS, and Windows. This
surfaced under Dart 3.13 in the kmdb workspace's PDF vault indexing.^3.13.0. Verified end-to-end
against the Dart 3.13 native-assets pipeline: the prebuilt-binary build hook,
library loading, and the full native API surface (text/annotation/image
extraction, rendering, and search) pass on macOS, Linux (x64 and arm64), and
Windows.lcov/genhtml invocations now
pass --ignore-errors empty, so the line-only tracefiles Dart emits no longer
fail the build under lcov 2.0 (shipped on current Ubuntu runners).pdfium_worker.js with the Dart 3.13 toolchain.Windows x86_64 — pre-built pdfium.dll (bblanchon/pdfium-binaries) is now downloaded automatically by the native-assets hook, matching the existing mac
pdfium.dll (bblanchon/pdfium-binaries) is now
downloaded automatically by the native-assets hook, matching the existing
macOS/Linux workflow. No native toolchain required.PdfDocument is now fully implemented on Flutter web and
dart2wasm via dart:js_interop, covering the complete API surface
(metadata, text/annotation/image extraction, rendering, search, table of
contents, thumbnails). Run make fetch_wasm_assets to place the PDFium WASM +
JS + Worker artifacts in your app's web/assets/pdfium/ directory; see the
package README for setup.dart:isolate is not supported on web, so
this uses a hand-rolled Worker + postMessage protocol mirroring the native
PdfiumIsolate architecture's shape. make fetch_wasm_assets now also copies
the checked-in pdfium_worker.js artifact alongside pdfium.wasm/pdfium.js
— no other consumer-facing change. See the README's "Web (WASM)" section,
including the "Adopting the Web Worker backend" migration guide, and
spec/02_pdfium_isolate.md's "Web Worker concurrency model" section.Version bump — no API changes. Minor fix to publication config
Version bump — no API changes. Minor fix to publication config
minor fixes for release
minor fixes for release
First developer preview. All core PDF operations are implemented and tested (96.1 % line coverage). API is subject to change before 1.0.
Pre-built PDFium binaries (bblanchon/pdfium-binaries chromium/7906) are shipped for:
The binary is downloaded automatically by the native-assets hook on the first
dart test or dart run — no manual setup needed on desktop. Windows and WASM
are not yet supported.
PdfDocument.fromBytes(Uint8List) — loads a PDF from raw bytes into a
background isolate so the calling isolate is never blocked.PdfExtractionException — thrown when loading fails; exception.error
distinguishes PdfError.passwordRequired from PdfError.invalidDocument.PdfDocument.close() — releases the native PDFium handle; safe to call more
than once. A Finalizer is registered as a fallback but explicit disposal is
preferred. After close(), all methods throw StateError.getMetadata() — returns PdfMetadata with title, author, subject,
keywords, creator, producer, creationDate, and modificationDate. All
fields are nullable; dates are PdfDate values with a parsed DateTime?.getDocumentInfo() — returns PdfDocumentInfo with fileVersion and the
16-byte permanentId / changingId file identifiers.pageCount — total page count.extractPlainText({int? pageIndex}) — streams PdfPageText per page. Each
result carries text, hasTextLayer, and hasUnicodeErrors.isPlainTextExtractable() — quick check that returns false when too many
pages lack a text layer (configurable via
PdfTextExtractorConfig.scannedPageRatio).close() stops further processing
immediately with no handle leaks.getPageSize(int pageIndex) — returns PdfPageSize with widthPt,
heightPt, aspectRatio, and sizeForDpi(dpi) for pixel conversion.renderPageToBytes(pageIndex, pixelWidth, pixelHeight) — renders a page to a
raw BGRA pixel buffer; returns
({Uint8List pixels, int pixelWidth, int pixelHeight}). Optional flags:
renderAnnotations, lcdText, backgroundColor (ARGB packed int).extractAnnotations({int? pageIndex}) — streams PdfPageAnnotations per
page; pages with no annotations yield an entry with an empty list so callers
can track page coverage without gaps.PdfTextAnnotation, PdfMarkupAnnotation
(highlight, underline, squiggly, strikeout), PdfFreeTextAnnotation,
PdfInkAnnotation, PdfShapeAnnotation, PdfPopupAnnotation,
PdfStampAnnotation, PdfUnknownAnnotation.PdfMarkupAnnotation includes quadPoints for precise text-span geometry.popup field.extractImages({int? pageIndex, bool includeBitmap}) — streams
PdfPageImages per page, each containing a list of PdfImage objects with
bounding box and PdfImageMetadata (dimensions, colour space, bits per pixel,
filter chain).includeBitmap: false (default) — metadata-only; no bitmap allocation.includeBitmap: true — populates PdfImage.bgra for every image on the page.renderImage(pageIndex, objectIndex) — fetches the BGRA bitmap for a single
image object on demand; returns null for mask-only images.search(String query, {Set<PdfSearchFlag> flags, int? pageIndex}) — streams
PdfSearchMatch values with pageIndex, charIndex, and rects in PDF
user-space (origin bottom-left).PdfSearchFlag.matchCase, PdfSearchFlag.matchWholeWord,
PdfSearchFlag.consecutive.tableOfContents — returns the complete bookmark tree as List<PdfTocEntry>.
Each entry has title, pageIndex, and children for nested entries.
Returns an empty list when the document has no bookmarks.getThumbnail(int pageIndex, {bool generateIfAbsent, int maxDimension}) —
returns a PdfThumbnail with bgra, width, height, and source
(embedded or rendered). When no embedded /Thumb stream is present, a
fallback render is produced at the requested maxDimension (longest edge)
unless generateIfAbsent: false.pdfinfo CLI tooldart run bin/pdfinfo.dart — a command-line tool for inspecting PDF metadata,
document info, page count, table of contents, and plain-text extractability.Your coding agent can read these notes before it upgrades. Set up the MCP server →