@nextgensoftwares/folio-reader
Standalone, read-only book reader. Depends on @nextgensoftwares/folio-model, @nextgensoftwares/folio-layout and @nextgensoftwares/folio-render-canvas only: no @nextgensoftwares/folio-editor, no ProseMirror, no @nextgensoftwares/folio-react (a test fails the build if one sneaks in, see Bundle size). Guide: Standalone reader.
| Entry | What |
|---|---|
@nextgensoftwares/folio-reader | the reader (framework-free DOM), sources, numbering, anchors, search, plugins |
@nextgensoftwares/folio-reader/react | FolioReaderView, useReaderState, ReaderToolbar (navigation |
@nextgensoftwares/folio-reader/worker | workerLayout (main thread), serveLayout (in the worker), mathKey, MathRequest, ToWorker, FromWorker |
@nextgensoftwares/folio-reader/fonts | readerFonts, chapterFamilies (wraps createFontLoader from @nextgensoftwares/folio-fonts) |
@nextgensoftwares/folio-reader/export-pdf | exportReaderPdf, ReaderPdfOptions, ReaderExportRequest, ReaderExportScope; exportPagesPdf(layouts, { pdf, plugins?, variables? }), PagesPdfOptions (DOM-free: the server-side watermarked download, see the reader guide) |
Reader
createReader(container, options): FolioReader
Mounts a reader into container (give it a height). FolioReader implements ReaderHandle. ReaderOptions:
| Option | Meaning |
|---|---|
source: ReaderSource | chapters and their content (required) |
layout?: ChapterLayoutFn | lays out chapters that arrive as documents: localLayout(...) or workerLayout(...) |
plugins?: ReaderPlugin[] | watermark, protection, highlights, host plugins |
variables? | for page layers (user, email, nid…) |
paint?: 'dom' | 'canvas' | real text (default) or canvas pixels over an invisible text layer; canvas?: CanvasPaintOptions |
interaction?: ReaderInteraction | 'none' (default: no selection) or 'select'; same values as <FolioView readerInteraction> |
numbering?: NumberingMode | 'global' (default) or 'local' |
display?: ReaderDisplay, firstPageAlone? | 'single' or 'spread' |
view?: ReaderView | 'scroll' (default) or 'paged': one page or spread at a time, turned with side buttons, ←/→ (mirrored in RTL), PageUp/PageDown/Space at the page edges, Home/End, the wheel, swipes and click zones; slides in, no motion under prefers-reduced-motion. Fit-page zoom by default |
turnZones? | paged view: clicking the outer fifths turns pages (default true) |
pageHash? | #page=… deep links: any go-to-page input (12, iv, 3:12) is read on open and on hashchange; the hash follows the current page as chapter:page |
zoom?: ReaderZoom | a number, 'fit-width' (scroll default) or 'fit-page' (paged default) |
theme?: ReaderTheme | 'light', 'sepia', 'dark' |
direction? | 'ltr'/'rtl' (default from the first chapter's dir) |
metrics?, renderMath?, bodySize? | font metrics the layout used; LaTeX → safe HTML (KaTeX with trust: false) |
keepChapters?, countInBackground?, tocLevels? | memory (laid-out chapters kept, default 4), exact page counts in idle time (default true), TOC depth |
initial?: ReaderTarget | where to open (a saved ReaderLocation, a deep link) |
pageAccess? | ({ chapterId, page, chapter }) => boolean: locked pages are placeholders, never painted |
resolveMedia? | attrs → url (sync or async; e.g. signed URLs by mediaId); only safe URLs are used |
onLocationChange?, onPageView?, onChapterEnd?, onLinkClick? | events (ReaderLocation, ReaderPageView) |
label?, strings?: Partial<ReaderStrings>, gap? | accessible name, i18n, spacing |
ReaderHandle
getState(): ReaderState (status, chapters: ReaderChapterInfo[], current: ReaderPageInfo, totalPages, exact, zoom, display, view, theme, numbering, interaction, direction, fullscreen) · subscribe(fn) · goTo(target: ReaderTarget) · goToInput(text) · next() · prev() · nextChapter() · prevChapter() · setZoom · zoomBy · setDisplay · setView(ReaderView) · setTheme · setNumbering · setInteraction · setVariables · toggleFullscreen() · location() · selection(): ReaderRange | null · selectionText() · setDecorations(key, ReaderDecoration[]) · onDecorationClick(fn) · ensureChapter(i) · chapterIndex(id) · loadedChapters() · currentPage() · renderPage(chapter, page, target) · repaint() · element() · variables() · plugins · options · destroy().
ReaderTarget: { chapterId, page?, offset?, point? }, { chapterId, blockId }, { global } (printed arabic number), { sequence } (0-based position in the book).
Sources
ReaderSource:listChapters(),loadChapter(id)→ChapterContent(doc,layout,blockIds), optionalsearch(query)→ReaderSearchHit[], optionalloadPages(id, from, to)→ChapterWindow(windowed chapters: only pages near the viewport are ever fetched).ReaderChapterMeta:id,title,kind?: ChapterKind(chapter,front-matter,inserted),pageCount?,estimatedPages?,pageSize?,headings?: ReaderHeading[],dir?,lang?.memorySource(chapters: MemoryChapter[], MemorySourceOptions),splitIntoChapters(doc, SplitOptions).insertedPart(id, title, InsertedPage[], size): unnumbered cover/image/title/blank pages;insertedTextPainter,INSERTED_TEXT.toPlainLayout(layout)(serialize a layout: lazy fragments materialized),readLayout(json)(validate an untrusted one).localLayout(options: ChapterLayoutOptions, hooks?: LocalLayoutHooks): main-threadChapterLayoutFn;ChapterLayoutContext.
Numbering, text and anchors (DOM-free)
PageNumbering(NumberingChapter,PagePosition,PageRef):label,position,sequence,at,globalArabic,globalRoman.resolvePageInput(text, numbering, mode, chapter)(12,iv,3:12, Arabic-Indic digits),parseRoman.docTextRuns,layoutTextRuns,docHeadings,blockIdOf,isTextblock,inlineText,nodeText,ATOM,TextRun.TextAnchor(positions +quote/prefix/suffix),anchorFromRange,reanchor→Reanchored | null,chapterText(ChapterText),offsetOf,pointAt,comparePoints.ReaderPoint,ReaderRange.normalizeSearchText,searchRuns(SearchOptions,TextMatch),snippet.
Painting
READER_CSS, injectReaderStyles (constructed stylesheets: strict-CSP safe) · THEME_ADAPTERS, PAGE_BACKGROUND, ReaderTheme, ReaderColorAdapter · paintPageContent(PagePaintInput) · safeSrc, isFollowableHref, browserMetrics · DomPainter, DomPaintContext, ReaderPaintEnv, ReaderColor, CanvasPaintOptions.
Plugins
ReaderPlugin is FolioPlugin's reader subset: pageLayers, painters (DOM painters), renderers, exporters.canvas / exporters.pdf, colors, ui.items (ReaderUIItem; shown only with reader: true or a predicate, like a read-only FolioView), css, attach(reader). composeReaderPlugins → ComposedReaderPlugins; isReaderItem(item, ctx: ReaderUIContext).
watermarkPlugin(ReaderWatermarkOptions): tiled text (NID:\n, opacity 0.07, −40°), DOM / canvas / PDF; a guard repaints a removed watermark and reports it.density(sparsedefault,normal,dense;WATERMARK_DENSITY) orgapX/gapYset the spacing.fillTemplate,watermarkTiles,WATERMARK_TYPE.protectPlugin(ProtectOptions): copyallow/block/{ maxChars },print: PrintPolicy(allow,current-page: only the page on screen, watermarked,block), context menu, drag, Ctrl+S.printCurrentPage(reader, open). Deterrents only.highlights(HighlightsOptions)→HighlightsController(plugin,selectionAnchor,add,remove,update,list,setVisibleLayers,flash);ReaderHighlight,HighlightStore.createFind(reader)→FindController(search,next,prev,go,clear,state(): FindState,subscribe);FindResult.
Bundle size
Measured by pnpm --filter @nextgensoftwares/folio-reader size (esbuild, minified, every dependency inlined but React); budgets enforced by src/__tests__/deps.test.ts:
| Entry | min | gzip |
|---|---|---|
| core (createReader + plugins; precomputed layouts) | 75 KB | 27 KB |
core + localLayout (layout engine, bidi, line breaking) | 192 KB | 74 KB |
| react shell | 82 KB | 28 KB |
| worker client | 1 KB | 0.5 KB |