Skip to content

Releases: yuroyami/KitePDF

KitePDF 0.13.0

Choose a tag to compare

@yuroyami yuroyami released this 09 Oct 22:05

KitePDF 0.13.0 is an enormous update with major features, so expect regressions. Please file any problems you encounter in the issue tracker.

Highlights in this version:

  • Support for video/audio playback inside EPUBs, via my very own KitePlayer KMP library.
  • Full Javascript runtime support inside PDFs and EPUBs, via QuickJS wrapped for KMP as KiteJS
  • Support for read-aloud (read-along).
  • Support for more image formats (AVIF, JPEG-XL, lossy WebP).
  • Major enhancements to scrolling, text shaping for a wide range of languages.
  • Improved PDF rendering extensively (quality, performance, UX).

Most of these features are made possible thanks to the public W3C resources used in the test suites to confirm EPUB and PDF engine work is up to standard.

Usage

// Every format: PDF, EPUB, CBZ, SVG, XPS and OpenXPS
implementation("io.github.yuroyami:kitepdf:0.13.0")

// Or one format only
implementation("io.github.yuroyami:kitepdf-pdf:0.13.0")
implementation("io.github.yuroyami:kitepdf-epub:0.13.0")
implementation("io.github.yuroyami:kitepdf-cbz:0.13.0")
implementation("io.github.yuroyami:kitepdf-svg:0.13.0")
implementation("io.github.yuroyami:kitepdf-xps:0.13.0")

// Optional (but you may probably need one of these depending on your use case)
implementation("io.github.yuroyami:kitepdf-compose-viewer:0.13.0")   // KiteDocView for Compose Multiplatform (renderer)
implementation("io.github.yuroyami:kitepdf-native-renderer:0.13.0")  // page-to-image on the platform canvas (headless+renderer)
implementation("io.github.yuroyami:kitepdf-skia-renderer:0.13.0")    // page-to-image on Skia multiplatform (headless+renderer)
implementation("io.github.yuroyami:kitepdf-javascript:0.13.0")       // runs the JavaScript inside PDFs and EPUBs (pulls KiteJS artifact)
implementation("io.github.yuroyami:kitepdf-net:0.13.0")              // allows KitePDF to remotely load documents from a URL
implementation("io.github.yuroyami:kitepdf-media:0.13.0")            // new in 0.13.0: plays EPUB audio and video in KiteDocView (pulls KitePlayer)

kitepdf-core arrives with every handler. Do not add it yourself. kitepdf-media needs Android minSdk 26, and on iOS the linker flags and privacy manifest entries that KitePlayer's install guide lists.

Changelog

🔴 Breaking changes (8)
Change What to do
#489: Every event method of PdfScriptHandler and EpubScriptHandler is now suspend Add suspend to each override, and call the methods from a coroutine
#598: KiteJsScriptEngine.asmReports and PdfScriptRunner.asmReports are gone Remove the calls
#506: EpubLayout has a new ROLL value Handle it in a when that has no else
#544: KiteXmlNode and KiteXmlToken have a new Comment subclass Handle it in a when that has no else
#428: The synchronous KitePageRasterizer.rasterize throws off the UI thread Call rasterizeOffMain from a worker
10539a3: KiteScriptEngine.defineFunction passes binary data as a ByteArray Convert binary data the same way in your own KiteScriptEngine
#209: KiteCanvas has a new rasterStep with a default body Override it in a by wrapper
#519: KiteDocView opens web and mail links after a prompt Pass externalLinks = null to turn it off

The last four still compile. They change what a call does, and a wrapper that overrides only the old KiteCanvas methods silently misses rasterStep.

Everything since 0.12.0 (196 changes)
AreaTypeChange
Colour (3)New Features#201: Applied overprint mode 1 to DeviceCMYK path fills and strokes, and to those in the CMYK output intent
#625: Applied overprint to CMYK text, and to Separation and DeviceN fills, strokes, text and images
Fixes#470: Read the samples of a CMYK JPEG in a PDF as ink, so a light image no longer turns black
EPUB (19)New Features#38: Loaded the images, fonts and backgrounds that a book names by an https URL through EpubSettings.resourceFetcher. kitepdf-net ships one on Ktor
#505: Scrolled a book whose rendition:flow is scrolled-continuous or scrolled-doc, and let the wheel move a page that overflows the view
🔴 #506: Read a rendition:layout roll book of EPUB 3.4 as one strip. EpubLayout has a new ROLL value
#510: Added EpubMetadata.titleRightToLeft and creatorsRightToLeft, the base direction of the title and of each creator
#517: Added EpubDocument.markupErrors(chapter), which lists where a chapter breaks the rules of XML
#614: Showed an image that the spine lists as a page of its own
Performance#389: Laid chapters out in steps with KiteDocument.prepareChapter(chapter, checkpoint), so JS and Wasm keep drawing while a chapter loads
#492: Waited once per process for a stalled remote resource, where every chapter waited two seconds for it
Fixes#502: Read the viewport tag of a fixed-layout page and its rendition overrides as EPUB Reading Systems 3.3 asks
#514: Loaded resources from data: URLs, over the new KiteDataUrl in kitepdf-core
#515: Collapsed the white space of package metadata values, as EPUB 3.3 asks
#516: Stopped loading a file of the book's zip that the manifest does not list
#518: Dropped a spine item whose fallback chain holds no content document, where its bytes were laid out as text
#526: Labelled a table of contents entry made of an image with the image's text alternative
#527: Ignored an epub:type on the head, so a link to it no longer counts as a footnote
#529: Kept a tag whole when a quoted attribute value holds a >, and read a < in a script as text
#563: Counted only the chapters a book keeps in TocEntry.spineIndex
#567: Returned from fetchRemoteResources only once remoteArrivals counts what it waited for
Docs#497: Published an implementation report against the W3C EPUB 3 test suite, with a docs page that lists its 206 tests
EPUB layout (37)New Features#207: Hyphenated EPUB text in 71 languages, every hyph-utf8 set that an Apache-2.0 library can carry, with their exception lists
#508: Supported the eight CSS text properties that EPUB lists with -epub- prefixes, from tate-chu-yoko to emphasis marks
Performance#452: Parsed deeply nested block elements in linear time, where it took quadratic time
Fixes#450: Limited how deep EPUB markup nests, so a deeply nested book cannot overflow the stack
#451: Limited how deep @media blocks and :not()
Read more

KitePDF 0.12.0

Choose a tag to compare

@yuroyami yuroyami released this 29 Sep 20:20

KitePDF 0.12.0 now checks the digital signatures of a signed PDF, and decodes big scans at the size they are drawn, so a 600 dpi page on a phone no longer needs hundreds of megabytes. EPUB books gain MathML, flex, grid and multi-column layout, every format gets a reading order for screen readers, and the release closes more than a hundred issues (127 issues in total). Two changes break the API (they are marked 🔴 below). The biggest thing about this update is that it brings major performance enhancements to the Compose renderer, and fixes a plethora of issues most of which caused crashes.

Usage

// Every format: PDF, EPUB, CBZ, SVG, XPS and OpenXPS
implementation("io.github.yuroyami:kitepdf:0.12.0")

// Or one format only
implementation("io.github.yuroyami:kitepdf-pdf:0.12.0")
implementation("io.github.yuroyami:kitepdf-epub:0.12.0")
implementation("io.github.yuroyami:kitepdf-cbz:0.12.0")
implementation("io.github.yuroyami:kitepdf-svg:0.12.0")
implementation("io.github.yuroyami:kitepdf-xps:0.12.0")

// Optional (but you may probably need one of these depending on your use case)
implementation("io.github.yuroyami:kitepdf-compose-viewer:0.12.0")   // KiteDocView for Compose Multiplatform (renderer)
implementation("io.github.yuroyami:kitepdf-native-renderer:0.12.0")  // page-to-image on the platform canvas (headless+renderer)
implementation("io.github.yuroyami:kitepdf-skia-renderer:0.12.0")    // page-to-image on Skia multiplatform (headless+renderer)
implementation("io.github.yuroyami:kitepdf-javascript:0.12.0")       // runs the JavaScript inside PDFs (pulls KiteJS artifact)
implementation("io.github.yuroyami:kitepdf-net:0.12.0")              // allows KitePDF to remotely load documents from a URL

kitepdf-core arrives with every handler. Do not add it yourself.

Changelog

🔴 Breaking changes

Change What to do
#449: KiteLinkAction.Uri is gone, and KiteLinkAction has the new kinds Epub and Plain Read link.uri, or add Epub and Plain to a when without else
#449: KiteDocView no longer takes onEpubReferenceTap Check link.kind in onLinkTap
2fe3273: kitepdf-core no longer passes its image engine, now KiteImageCodec, on to apps If you call the engine directly, add io.github.yuroyami:kiteimagecodec:0.2.0, import io.github.yuroyami.kiteimagecodec, and call KiteImageCodec.decode

Two changes still compile. onLinkTap now also receives links inside the document before the viewer follows them, so a callback that returns true for every link stops that navigation (#449). KiteSelectionMenu now sits next to the selection by default. Pass an alignment to pin it as before (#408).

Everything since 0.11.0

AreaTypeChange
Viewer (60)New Features#410: Flung the pan of a zoomed page, and turned the page past its edge
#411: Zoomed with Ctrl and the wheel, paged and zoomed with the keyboard, and selected text with a mouse drag
#433: Brought a linked place to the top at any zoom and layout, turned the page for named links, and ran script links
#444: Let the app see an internal EPUB link before the viewer follows it, to show a note in place (reported by @ksokolovskiy)
API Changes#30: Added pageOverlay, pageRectToViewport and displayRectToViewport, to place a composable over a rectangle on a page
#408: Placed the selection menu next to the selection, and added selectionBounds for hosts that place their own
#429: Moved a state's suspend calls onto the viewer's thread, so an app can navigate from any thread
#431: Let KitePageRasterizer.rasterize take a form state, to export a filled form
#432: Added hitTestDisplay, which gives a hit in display space for every format
🔴 #449: Gave onLinkTap every link in every format before the viewer acts, with uri, target, kind, pageIndex and rect on each link
acbe57d: Passed Compose runtime, foundation and ui on to apps, since KiteDocView's API takes and returns their types
Performance#370: Rendered two pages at once, the visible one first, with cancellation for every format
#372: Gave a Vectorized page its own layer, so an overlay change no longer redraws it
#373: Moved the layer on a zoom or pan frame, with no recomposition
#374: Recomposed the page widgets only when the reader crosses a page
#375: Drew the part on screen in tiles past the bitmap cap, so deep zoom stays sharp
#376: Rasterized a settled zoom only for the pages in view
#380: Built a page's text off the main thread before a long press selects it
#390: Rasterized a page once a resize holds, not on every frame
#391: Cached thumbnails, and kept one EPUB outline per state
#393: Put decoded pixels into a bitmap once on Skia targets
#394: Kept a themed page's raster when only the page background changes
#395: Freed the page cache when its budget is zero or the view is Vectorized
a0ebaa7: Rendered a page drawn in a host font once, not twice
e75ca6a: Let a soft mask's canvas share the image bitmaps of its page
e8a5c53: Converted each glyph outline to a path once per draw
Fixes#332: Laid out very tall and very wide pages in the Continuous layout
#338: Ignored zoom input that is not finite, and checked doubleTapZoom and offscreenPages
#342: Kept the reader on their page when a chapter lands above them in the Continuous layout
#343: Ran the Paged correction for a long chapter that lands, in an app as in tests
#344: Kept the jump to a bookmark going when the reader drags while a book opens
#345: Made the viewer state the only saved reading position
#346: Took the position of a new KiteDocViewState under the same KiteDocView
#348: Landed on the last page of a loading chapter when the reader pages back onto it
#350: Kept a selection drag going while a chapter lands
#352: Kept the position when Continuous switches between vertical and horizontal
#353: Letterboxed a chapter placeholder in a pager like a page
#392: Confirmed that a Vectorized page and a pool raster of one document draw safely at once
#397: Let the reader reach both ends of a zoomed Continuous strip
#398: Made swiping, panning and the opening zoom work with any minZoom
Read more

KitePDF 0.11.0

Choose a tag to compare

@yuroyami yuroyami released this 25 Sep 13:15

KitePDF 0.11.0 now adds XPS (and OpenXPS) format support. It can finally run the JavaScript inside PDF forms (thanks to KiteJS integration), and closes more than a hundred issues (156 issues in total). Six of them break-change the API (they are marked 🔴 below).
Note that Javascript integration is still experimental and only runs in PDF forms. Javascript in EPUBs is still not supported (planned for v0.12.0 probably).

Usage

// Every format: PDF, EPUB, CBZ, SVG, XPS and OpenXPS
implementation("io.github.yuroyami:kitepdf:0.11.0")

// Or one format only
implementation("io.github.yuroyami:kitepdf-pdf:0.11.0")
implementation("io.github.yuroyami:kitepdf-epub:0.11.0")
implementation("io.github.yuroyami:kitepdf-cbz:0.11.0")
implementation("io.github.yuroyami:kitepdf-svg:0.11.0")
implementation("io.github.yuroyami:kitepdf-xps:0.11.0")        // new in 0.11.0

// Optional (but you may probably need one of these depending on your use case)
implementation("io.github.yuroyami:kitepdf-compose-viewer:0.11.0")   // KiteDocView for Compose Multiplatform (renderer)
implementation("io.github.yuroyami:kitepdf-native-renderer:0.11.0")  // page-to-image on the platform canvas (headless+renderer)
implementation("io.github.yuroyami:kitepdf-skia-renderer:0.11.0")    // page-to-image on Skia multiplatform (headless+renderer)
implementation("io.github.yuroyami:kitepdf-javascript:0.11.0")       // runs the JavaScript inside PDFs (pulls KiteJS artifact)
implementation("io.github.yuroyami:kitepdf-net:0.11.0")              // allows KitePDF to remotely load documents from a URL

kitepdf-core arrives with every handler. Do not add it yourself.

Changelog

🔴 Breaking changes

Change What to do
#234: PdfScriptRunner.onAlert is now (PdfScriptAlert) -> Int, and formState and policy are new 2nd and 3rd parameters Update onAlert, and pass arguments by name
#196: KiteShading.FlatQuad is gone, and PatchMesh.quads is now patches plus colorTable Read PatchMesh.patches
#238: PdfAnnotation has a new additionalActions parameter before raw Pass raw by name
#205: KiteDocFormat has a new Xps value Handle Xps in a when that has no else
#113: KiteCanvas has a new drawImage overload with a blend mode Override both overloads in a by wrapper
#68: KiteCanvas has a new applySoftMask overload with a transfer function Override both overloads in a by wrapper

The last two still compile. A wrapper that overrides only the old overload silently misses those calls.

Everything since 0.10.0

AreaTypeChange
CBZ (1)New Features#206: Read a comic archive's ComicInfo.xml into its metadata and outline
Core (27)New Features#197: Bundled outlines for the 14 standard fonts, so every host renders them the same
#199: Added support for CFF2 fonts and TrueType collections
#200: Converted colours through lookup-table ICC profiles instead of a device space
#211: Added contextual substitution and Indic reordering
a7541c7: Converted DeviceCMYK through a PDF/X file's CMYK output intent, as MuPDF does
API Changes🔴 #113: Drew images with the blend mode of the graphics state, through a new drawImage overload
Performance#119: Lexed numbers and keywords without allocating a String or a parse buffer
#185: Transformed path and glyph points without allocating a pair for each point
Fixes🔴 #68: Applied a soft mask's /BC backdrop and /TR transfer function
#72: Kept the declared colour space and /Decode array of JPEG images
#126: Removed the pale seams between mesh and patch shading cells
#140: Placed CFF and Type 1 outlines by their own font matrix, not a fixed 1000 units per em
#159: Undid the /Matte preblend (premultiplied alpha) of soft-masked images
#160: Read a /Mask that points at a multi-bit image without the stencil flag as a soft mask
🔴 #196: Rasterized mesh shadings on their exact surface, with smooth colours and every tensor control point
#198: Selected the right glyphs in Unicode-keyed CJK documents
#279: Read every glyph of an embedded Type 1 font, including the first ones in its CharStrings
#280: Picked the glyphs of CID-keyed CFF fonts through the charset
#282: Drew fonts embedded as /FontFile3 /Subtype /OpenType with their own outlines
#298: Drew text in a non-embedded standard 14 font from the bundled outlines
#299: Converted DeviceCMYK colours the way MuPDF and PDFium do
#302: Confirmed that JPEG 2000 decoding matches OpenJPEG, and scored the small drawing difference as rounding
#303: Drew accented letters in embedded Type1C fonts
#304: Drew the right ZapfDingbats symbols for codes from 128 up
#305: Kept ZapfDingbats symbols in text extraction
#309: Drew text in a non-embedded CJK font that has no ToUnicode map
#311: Kept grey content neutral under an ICC grey profile whose white point is not D50
Docs (1)Fixes#307: Removed the stale AGPL note from the README, and listed what KitePDF bundles
EPUB (28)New Features#26: Rendered and named SVG chapters, and sized fixed-layout SVG pages
#317: Reordered Sinhala, Khmer, Myanmar and Tibetan, and split Thai sara am, as HarfBuzz does
API Changes#227: Exposed semantic EPUB link targets and fragment content for a host's footnote UI (reported by @ksokolovskiy)
Fixes#100: Kept images upright in vertical writing
#101: Painted the frame and background of framed figures
#103: Drew underlines and strikethroughs
#104: Used an embedded typeface that is listed after an unavailable family
#168: Painted inline background colours, so highlighted spans show
#170: Kept cover-fitted images in proportion in vertical writing
#172: Painted block images and text in document order (CSS 2.1, Appendix E)
#210: Resolved collapsed table borders by style and width
#253: Kept the alpha of inline backgrounds, so the text stays visible
#257: Resolved a system-ui font stack to sans-serif instea...
Read more

KitePDF 0.10.0

Choose a tag to compare

@yuroyami yuroyami released this 13 Sep 18:20

Some large EPUB books still made Android apps run out of memory after 0.9.0 (reported in #224). This release fixes that and about 90 smaller bugs where pages did not look right. Most of them were found by comparing KitePDF's pages with other readers. There is also a new, optional module that runs the JavaScript inside PDFs.

implementation("io.github.yuroyami:kitepdf:0.10.0")

Requirements

  • Kotlin 2.4.20+
  • JDK 21
  • Android SDK 37 / minSdk 21 (compose-viewer 24, native-renderer 29)
  • Compose Multiplatform 1.12.0 for kitepdf-compose-viewer
  • Pulls in KiteImage 0.1.0, Ktor 3.5.2 (kitepdf-net), Skiko 0.150.1 (renderers), KiteJS 0.1.0 (kitepdf-javascript)
  • Targets: Android, JVM, iOS, tvOS, watchOS, macOS, Linux, Windows, Android Native, JS, Wasm (kitepdf-javascript has no tvOS, watchOS, Android Native or wasmWasi build)

Fixed

  • Out of memory on large EPUBs (fixes #224, reported by @Tulis12). The book repeats the same font in every chapter, and KitePDF loaded a new copy each time. Now each font loads once per book. That book went from 340 MB of memory to 32 MB (#228).
  • Wrong colours in PDFs. Some colours came out black, grey or yellowish, and some artwork was hidden under white boxes (#76, #81, #82, #83, #84, #115, #147).
  • One broken part could ruin a whole page. A single bad piece of a PDF could stop the page from drawing, or shift and hide everything drawn after it. Now only the broken part is affected (#47, #48, #49, #50, #51, #52, #128, #149, #151, #152, #166).
  • Show/hide layers. Some PDFs let you turn parts of a page on and off. Hidden parts sometimes showed, visible parts sometimes disappeared, and one kind of broken file crashed the app (#53, #54, #55, #56, #58).
  • Comments, stamps and highlights. Unchecked checkboxes looked checked, hidden notes were shown, stamps were stretched, and highlights covered the text under them. Outlines now use their real thickness and dash style (#59, #60, #61, #62, #63, #64, #163, #165).
  • Text. Some text did not show at all, and some was covered by black boxes. Text whose font is not inside the file had the wrong spacing (#86, #87, #120, #139, #141, #142, #144, #145, #146).
  • Lines. Dashed and dotted lines drew solid or with the wrong dashes, and some line ends were wrong (#106, #107, #134, #135, #136, #137).
  • See-through areas. Transparent parts came out too dark where shapes overlap, blocky at soft edges, fully solid, or in the wrong place (#65, #66, #67, #73, #74, #75, #138, #162).
  • Gradients and repeating patterns. Gradients looked banded or spilled outside their area. Repeating patterns lost their transparency or filled the whole shape (#95, #148, #153, #154, #155, #156, #157, #158).
  • SVG images. Some drew upside down, stretched, or at three quarters of their real size. Dashed lines, outlines, spaces between words, see-through groups and cut-out shapes were also wrong in some files (#90, #91, #92, #96, #97, #98, #173, #174, #175, #176, #178, #182).
  • EPUB layout. Scene-break lines were missing, and fixed-layout books (like picture books) drew at 75% of their size. Images were too big, always centred, or missing when placed right after a word. Right-to-left books had list bullets and alignment on the wrong side, and a link around a picture could not be tapped (#99, #102, #111, #112, #167, #169, #171, #214).
  • Comics. WebP pages were blank and had the wrong size. WebP and TIFF pages now open at their real size. Lossy WebP pages still do not show (#129, #183).
  • Compose viewer. Creating a ReaderTheme inside a composable made the viewer redraw every page on each recomposition (#186).
  • Security alert about a JavaScript tool that only our tests use. That tool was never part of what you download (fixes #222, reported by @begininvoke).

Added

kitepdf-javascript, a new optional module. Some PDFs contain small scripts, for example one that shows a message when the file opens. This module runs them with KiteJS. For now a script can show a message and write to a log. Forms that fill in and calculate themselves come later (#236). A script that never ends is stopped, so it cannot freeze your app.

implementation("io.github.yuroyami:kitepdf-javascript:0.10.0")

PdfScriptRunner(doc, onAlert = { message -> showDialog(message) }).use { runner ->
    runner.runDocumentScripts()
}
  • EpubSettings(hyphenate = true) turns word hyphenation on for the whole book, and false turns it off, whatever the book's own styles say (fixes #24, requested by @ksokolovskiy).
  • page.renderTo(canvas, matrix, annotations = { false }) draws a PDF page without its comments, stamps and highlights, for printing or clean thumbnails (fixes #133).
  • PdfAnnotation has four new properties: isInvisible, isNoZoom, borderStyle and borderDash.

Some public classes gained constructor parameters, EpubSettings and PdfAnnotation among them. Your code compiles as it is, but a library built against 0.9.0 has to be rebuilt.

Full changelog: v0.9.0...v0.10.0

KitePDF 0.9.0

Choose a tag to compare

@yuroyami yuroyami released this 08 Sep 03:28

Reading an EPUB on Android ran the app out of memory within seconds and killed it. Reported, along with the flickering next to it.

implementation("io.github.yuroyami:kitepdf:0.9.0")

Requirements

  • Kotlin 2.4.10+ (older compilers cannot read these artifacts)
  • JDK 21
  • Android SDK 37 / minSdk 21 (compose-viewer 24, native-renderer 29)
  • Compose Multiplatform 1.12.0-beta02 for kitepdf-compose-viewer
  • Pulls in KiteImage 0.1.0, Ktor 3.5.2 (kitepdf-net), Skiko 0.148.2 (renderers)
  • Targets: Android, JVM, iOS, tvOS, watchOS, macOS, Linux, Windows, Android Native, JS, Wasm

Fixed

Fixed a long EPUB running the app out of memory (fixes #218, reported by @Tulis12 in #217).

The viewer laid every chapter out behind the reader and kept all of them, roughly 165 bytes per character of text. Android gives a normal app 192 MB, so a novel filled the heap before you finished the first page. Heap retained after laying a whole book out:

791k-character novel:        124 MB  ->  39 MB
698k-character illustrated:  204 MB  ->  54 MB

Chapters now drop least-recently-used to fit layoutCacheBytes, and lay out again in about a tenth of a second when you come back. Page counts, bookmarks and links keep working while a chapter is dropped, so navigation never waits on one. Glyphs for the same character also share one String now (#220).

Fixed visible pages redrawing on every chapter load (fixes #221).

The document built a new page object per lookup, and the viewer keys its bitmap cache on that object, so every chapter landing was a total cache miss. On a 40-chapter book the two pages on screen rasterized 30 times each, crossfading back in every time. A location now answers the same object for the life of the document.

Fixed an out-of-memory page killing the app (fixes #219).

The rasterizer's guard caught Exception, and OutOfMemoryError is an Error, so the one failure the guard existed for walked straight past it. It catches Throwable now: the page keeps its placeholder and the app survives.

Fixed blank Standard-14 text on Linux.

Skia was asked for Helvetica, Times New Roman and Courier New by name. Linux ships metric-compatible clones under other names, and Skia answers a family it cannot find with a font that paints nothing and reports no error. Each family now lists the stand-ins other systems do ship.

Added

EpubSettings.layoutCacheBytes, the budget above, 48 MB by default.

EpubDocument.open(bytes, EpubSettings(layoutCacheBytes = 24L * 1024 * 1024))

EpubSettings gained a parameter, so source stays compatible but anything compiled against 0.8.2 needs recompiling.

Full changelog: v0.8.2...v0.9.0

KitePDF 0.8.2

Choose a tag to compare

@yuroyami yuroyami released this 30 Aug 19:41

One fix, the second half of the 0.8.1 story. Words ran together in dense text. Found by auditing the related code, not from a report.

implementation("io.github.yuroyami:kitepdf:0.8.2")

Requirements

  • Kotlin 2.4.10+ (older compilers cannot read these artifacts)
  • JDK 21
  • Android SDK 37 / minSdk 21 (compose-viewer 24, native-renderer 29)
  • Compose Multiplatform 1.12.0-beta02 for kitepdf-compose-viewer
  • Pulls in KiteImage 0.1.0, Ktor 3.5.2 (kitepdf-net), Skiko 0.148.2 (renderers)
  • Targets: Android, JVM, iOS, tvOS, watchOS, macOS, Linux, Windows, Android Native, JS, Wasm

Fixed

Fixed words gluing together in dense chart text.

0.8.1 fixed the line-grouping tolerance that 0.8.0's corrected font size had rescaled. The sibling rule had the same problem: KitePDF inserts a space at a gap of a quarter of a font size, and that threshold rescaled too. On fonts that pad their em box it grew past real word gaps, so words glued:

0.7.0 / 0.8.2:  HIRL (60m)   RVR 150m   1 RVR 75m
0.8.0 / 0.8.1:  HIRL(60m)    RVR150m    1RVR 75m

The threshold is now a tenth of the font size, floored at 0.25pt. Calibrated against two references at once: a normal document keeps its exact mutool-matching spacing (any factor from 0.05 to 0.12 does), and the regressed chart returns to 0.7.0 spacing byte for byte. Line text and the search and selection path share the rule.

Full changelog: v0.8.1...v0.8.2

KitePDF 0.8.1

Choose a tag to compare

@yuroyami yuroyami released this 30 Aug 18:24

One fix. 0.8.0 merged separate rows of text into a single line on dense pages, so extracted text came out scrambled.

implementation("io.github.yuroyami:kitepdf:0.8.1")

Requirements

  • Kotlin 2.4.10+ (older compilers cannot read these artifacts)
  • JDK 21
  • Android SDK 37 / minSdk 21 (compose-viewer 24, native-renderer 29)
  • Compose Multiplatform 1.12.0-beta02 for kitepdf-compose-viewer
  • Pulls in KiteImage 0.1.0, Ktor 3.5.2 (kitepdf-net), Skiko 0.148.2 (renderers)
  • Targets: Android, JVM, iOS, tvOS, watchOS, macOS, Linux, Windows, Android Native, JS, Wasm

Fixed

Fixed separate rows merging into one line (fixes #23, reported by @bfovez).

0.8.0 made fontSize report the real size. KitePDF groups text into lines by allowing half a font size of vertical wobble, so that tolerance rescaled with it: from an accidental 0.5pt to half the em box, around 11pt on charts whose fonts pad the em. Rows 1 to 1.4pt apart merged, text interleaved in reading order:

0.7.0:  LFBP/PUF PAU/PYRENEES, FRANCE
0.8.0:  LApt ElevFBP/P616'UF PAU/PYRENEES, FRANCE
0.8.1:  LFBP/PUF PAU/PYRENEES, FRANCE

A line is a shared baseline. The tolerance is now slack around it: 5% of the font size, floored at 0.5pt. That restores 0.7.0's grouping line for line on the reported file, and matches how mutool splits the page. The corrected fontSize values stay.

Full changelog: v0.8.0...v0.8.1

KitePDF 0.8.0

Choose a tag to compare

@yuroyami yuroyami released this 29 Aug 17:26

Adds comic book (CBZ) and SVG support, and fixes text that came back with the wrong font size and wrong spacing. Encrypted PDF writing now needs a random source you supply, so read the breaking section before upgrading.

implementation("io.github.yuroyami:kitepdf:0.8.0")

Requirements

  • Kotlin 2.4.10+ (older compilers cannot read these artifacts)
  • JDK 21
  • Android SDK 37 / minSdk 21 (compose-viewer 24, native-renderer 29)
  • Compose Multiplatform 1.12.0-beta02 for kitepdf-compose-viewer
  • Pulls in KiteImage 0.1.0, Ktor 3.5.2 (kitepdf-net), Skiko 0.148.2 (renderers)
  • Targets: Android, JVM, iOS, tvOS, watchOS, macOS, Linux, Windows, Android Native, JS, Wasm

⚠️ Breaking

Encrypted writing now needs a random source from you. AES-256 was falling back to Kotlin's Random.Default for keys and IVs, which is not safe for encryption. It now takes a proper platform random source explicitly, and encrypted editing fails at startup without one.

PdfDocument.bytes now copies, which makes its documented immutability true. If you need the old zero-copy access:

@OptIn(KiteRawApi::class)
val raw = doc.rawBytes

ZipReader moved to kitepdf-core (io.github.yuroyami.kitepdf.core.zip). The old name compiles as a deprecated typealias for one release. KiteXml, KiteXmlNode and CssValues made the same trip out of kitepdf-epub and are public there now.

EpubSettings now validates when you construct it. Non-finite, negative and geometrically impossible settings throw, where they used to lay out nonsense.

Two new formats

Comic book archives (CBZ) and SVG, each its own artifact. KiteDoc.open detects them the way it already detected PDF and EPUB.

val comic = KiteDoc.open(cbzBytes)   // one page per image, page2 before page10
val art = KiteDoc.open(svgBytes)     // one page at the SVG's own viewport

kitepdf-cbz brings no EPUB engine with it. Page sizes come from image headers, so opening an archive does not decode it, and packaging noise (ComicInfo.xml, Thumbs.db, dotfiles) is skipped.

kitepdf-svg holds the renderer that was buried inside the EPUB handler. It gained <use>, <symbol>, <image>, <text>, gradients and clip-path, so a fixed-layout comic whose pages are an SVG wrapping one JPEG now draws.

Text extraction

Every piece of text reported a font size of 1.0pt, whatever its real size (fixes #22, reported by @bfovez). Some PDF producers set the font operator to 1 and carry the real size in the text matrix instead:

BT /F1 1 Tf 15 0 0 15 72 720 Tm (Hello) Tj ET

That is legal, and fontSize was handing back the raw operand.

span.fontSize   // 0.7.0: 1.0    0.8.0: 15.0

Not just cosmetic. KitePDF uses font size to decide what is one line and where a word break goes: half a font size of vertical wobble splits a line, a quarter of a font size of gap inserts a space. Stuck at 1.0pt, a 0.25pt gap invented word breaks and 0.5pt of wobble split one line into several. extractText judged its line breaks the same wrong way.

Letter-spaced and word-spaced text drew too tightly, then jumped. Tc and Tw only reached the advance positioning the next run, never moving the pen inside the current one. Fixed on every backend, both the stroke and clip passes, and structured-text bounds.

Measured against mutool on a line with 6 units of extra character spacing: 101px short before, 1px after.

EPUB

Russian hyphenation (PR #7 by @ksokolovskiy). A Russian book with automatic hyphenation was broken against the English pattern set, which does nothing for Russian. Eight languages now, chosen per chapter.

Indented first lines were clipped at the right edge (fixes #6, reported by @ksokolovskiy). Lines were measured against the full page width, then the indent was added at placement, so a packed first line stuck out by up to the indent width. Cyrillic was worse: measured at a flat half-em instead of the real widths already in core.

The Paged viewer jumped while chapters loaded (fixes #5, reported by @ksokolovskiy), and lost a saved position if opening was interrupted. It now opens at your bookmark's chapter, holds zoom and selection through landings, and clamps to the nearest page when a saved spot is gone.

Hardening

  • Remote downloads stream into a size-capped buffer: 128 MiB default, maxBytes on every URL API
  • Rasterizers reject nonsense scale values and cap allocation at 40 megapixels (KITE_DEFAULT_MAX_RASTER_PIXELS)
  • ZIP entries get 128 MiB decompressed and 100,000 record ceilings. Malformed offsets and truncated records fail closed

Known limits

  • Without an embedded font, Skia and Compose draw a run as one platform string, so per-character spacing inside it is approximated. AWT, Android and Canvas2D are exact
  • extractText follows the text matrix but not the page transform (cm), so its thresholds are off under a scaled page. structuredText is exact
  • WebP images inside a CBZ are detected but render blank

Full changelog: v0.7.0...v0.8.0

KitePDF 0.7.0

Choose a tag to compare

@yuroyami yuroyami released this 24 Aug 22:39

Redaction no longer leaves the removed content inside the file. Big EPUBs open instantly instead of paginating first. EPUB page numbering changed, so read the breaking section before upgrading.

implementation("io.github.yuroyami:kitepdf-pdf:0.7.0")

Requirements

  • Kotlin 2.4.10+ (older compilers cannot read these artifacts)
  • JDK 21
  • Android SDK 37 / minSdk 21 (compose-viewer 24, native-renderer 29)
  • Compose Multiplatform 1.12.0-beta02 for kitepdf-compose-viewer
  • Pulls in KiteImage 0.1.0, Ktor 3.5.2 (kitepdf-net), Skiko 0.148.2 (renderers)
  • Targets: Android, JVM, iOS, tvOS, watchOS, macOS, Linux, Windows, Android Native, JS, Wasm

⚠️ Breaking

Every chapter now starts on a fresh page. Page counts rise by roughly one per chapter boundary.

EpubDocument.outline no longer paginates the whole book just to build the table of contents. Navigate by target instead:

// before
state.goToPage(entry.pageIndex)

// after
entry.target?.let { state.goTo(it) }   // pageIndex fills in once laid out

KiteDocViewState.currentPage counts slots, not pages, while a reflowable book is still being laid out. For an exact position, use:

val exact = state.currentLocation   // KiteLocation(chapter, page)
val saved = doc.bookmarkOf(exact)   // survives a re-flow

A font declared inside one document's own <style> block now belongs to that document only, not to the whole book. Fonts declared in a shared stylesheet are still book-wide.

Redaction

Redacting two areas on one page only removed the second. The first area's content stayed in the file.

val editor = doc.edit()
editor.redactRegion(doc.pages[0], KiteRectangle(100.0, 600.0, 300.0, 650.0))
editor.redactRegion(doc.pages[0], KiteRectangle(100.0, 400.0, 300.0, 450.0))
val bytes = editor.saveRewritten()   // 0.6.3: region 1 survives. 0.7.0: both gone.

Also fixed in the same release:

  • A shape drawn in several spots (a form XObject) is now redacted per spot. Rewriting it once under-redacted the others and destroyed content that was never in a region
  • A redacted form field kept its value. It is now detached from the document's form
  • Vector art survived under the black box. Paths whose ink reaches a region are now removed, allowing for line width and corner joins. A drawn signature used to sit under the box with its coordinates intact
  • Removed objects could be recovered. They are now emptied, not just unlinked, so a leftover reference cannot restore them

saveRewritten() is still required. saveIncremental() refuses after a redaction.

EPUB

Footnote links landed at the top of the notes file instead of on the note (fixes #4, reported by @ksokolovskiy). The markup that broke it:

<a href="ch2.xhtml#id33">[2]</a>
...
<span id="id33"><div>2</div><p>the note</p></span>

An inline element wrapping block children collapsed into one paragraph, so the id anchored to the enclosing block. pageOf("ch2.xhtml#id33") now returns the note's page.

Big books open at your last reading position instead of laying out the whole book first (fixes #3, reported by @ksokolovskiy). A 9.9 MB book: 986 ms to first page before, 3 ms now.

Rendering

Rotation, cropping, page origins and custom page units (/Rotate, /CropBox, MediaBox origins, /UserUnit) now work on the AWT, Android and Apple rasterizers, not only Skia. A page rotated 90 degrees came out the wrong shape.

Images wrapped in an extra encoding layer now decode, for example:

/Filter [/ASCII85Decode /DCTDecode]

Known limits

  • The non-JVM renderers do not yet draw everything the JVM and Skia renderers do
  • Redaction does not handle sh shadings. A large flat fill whose edges lie outside every redacted region survives

Full changelog: v0.6.3...v0.7.0

KitePDF 0.6.3

Choose a tag to compare

@yuroyami yuroyami released this 19 Aug 02:16

Adds a switch to turn text selection off. Everything that changed since 0.6.0.

implementation("io.github.yuroyami:kitepdf:0.6.3")
implementation("io.github.yuroyami:kitepdf-compose-viewer:0.6.3")

Requirements

  • Kotlin 2.4.10+ (older compilers cannot read these artifacts)
  • JDK 21
  • Android SDK 37 / minSdk 21 (compose-viewer 24, native-renderer 29)
  • Compose Multiplatform 1.12.0-beta02 for kitepdf-compose-viewer
  • Pulls in KiteImage 0.1.0, Skiko 0.148.2 (renderers)
  • Targets: Android, JVM, iOS, tvOS, watchOS, macOS, Linux, Windows, Android Native, JS, Wasm

Compose renderer

  • Added a way to switch text selection off completely, with a new parameter on the composable:
PdfView(state, selectionEnabled = false)
  • Text selection handles can now be dragged. Before this, the two markers at the ends of a selection were only a drawing: you could see them, but pressing them did nothing
  • Links inside PDFs work again. They were invisible and impossible to tap

Core

Nothing changed.