UNPKG

nehan

Version:

Html layout engine for paged-media written in Typescript

103 lines 3.82 kB
import { Config, NehanDocument, LogicalNodeGenerator, LayoutOutlineEvaluator, ImageLoader, ImageLoaderContext, DomCallbackEffector, } from './public-api'; export class PagedNehanDocument extends NehanDocument { constructor(src, options = { styleSheets: [] }) { super(src, options); this.generator = options.generator || LogicalNodeGenerator.createRoot(this.body); this.evaluator = options.evaluator || this.generator.context.pageRoot.createLogicalNodeEvaluator(); this.effector = new DomCallbackEffector(this.generator.context.pageRoot); this.pages = []; this.timestamp = 0; } addPageNode(node) { const index = this.pages.length; const dom = (index === 0) ? this.evalNode(node, true) : undefined; const prevPage = this.pages[index - 1]; const progress = node.progress; const charCount = node.text.length; const acmCharCount = prevPage ? prevPage.acmCharCount + charCount : charCount; const pagedNode = { node, dom, index, progress, charCount, acmCharCount, }; this.pages.push(pagedNode); return pagedNode; } get pageCount() { return this.pages.length; } getAnchor(anchorName) { return this.generator.context.pageRoot.getAnchor(anchorName); } getAnchorPage(anchorName) { const anchor = this.getAnchor(anchorName); if (!anchor) { throw new Error(`anchor(${anchorName}) is not found!`); } return this.getPage(anchor.pageIndex); } evalNode(node, useEffector = true) { const dom = node.acceptEvaluator(this.evaluator); if (useEffector) { node.acceptEffector(this.effector); } return dom; } getPage(index) { const page = this.pages[index]; if (!page) { throw new Error(`page ${index} is not found!`); } if (page.dom) { return page; } page.dom = this.evalNode(page.node, true); return page; } createOutline(outlineEvaluator) { const evaluator = outlineEvaluator || new LayoutOutlineEvaluator(); return this.generator.context.flowRoot.createOutline(evaluator); } getSectionAt(pageIndex) { return this.generator.context.flowRoot.getSectionAt(pageIndex); } render(options = {}) { const images = this.querySelectorAll("img").concat(this.querySelectorAll("video")); const context = new ImageLoaderContext(images.length); new ImageLoader(images, context).load(options).then(_ => { this.timestamp = performance.now(); this.renderAsync(options); }); return this; } renderAsync(options) { requestAnimationFrame(() => { const result = this.generator.getNext(); if (!result) { const time = performance.now() - this.timestamp; const pageCount = this.pages.length; if (options.onComplete) { options.onComplete({ caller: this, time, pageCount }); } return; } const node = result.getBodyAsBlockNode(); if (node.children.some(child => child.extent > 0)) { const page = this.addPageNode(node); if (options.onPage) { options.onPage({ caller: this, page }); } } if (this.pages.length > Config.maxPageCount) { console.error("too many pages, abort."); return; } this.renderAsync(options); }); return this; } } //# sourceMappingURL=paged-nehan-document.js.map