UNPKG

@vivliostyle/core

Version:

Vivliostyle Core library for HTML+CSS typesetting with EPUB/Web publications support

501 lines (500 loc) 24.7 kB
/** * Copyright 2013 Google, Inc. * Copyright 2015 Trim-marks Inc. * Copyright 2019 Vivliostyle Foundation * * Vivliostyle.js is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Vivliostyle.js is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with Vivliostyle.js. If not, see <http://www.gnu.org/licenses/>. * * @fileoverview Layout - Fills a column with styled content. * This file does not communicate with the styling system directly. * Instead it goes through the layout interface that gives it one view tree * node at a time. */ import * as LayoutRetryers from "./layout-retryers"; import * as BreakPosition from "./break-position"; import * as Css from "./css"; import * as GeometryUtil from "./geometry-util"; import * as PageFloats from "./page-floats"; import * as Matchers from "./matchers"; import * as PseudoElement from "./pseudo-element"; import * as Task from "./task"; import * as VtreeImpl from "./vtree"; import { FragmentLayoutConstraintType, Layout, RepetitiveElement, Selectors, Vtree } from "./types"; export declare const isInstanceOfAfterIfContinuesLayoutConstraint: typeof Selectors.isInstanceOfAfterIfContinuesLayoutConstraint; export declare const registerFragmentIndex: typeof Matchers.NthFragmentMatcher.registerFragmentIndex; export declare const clearFragmentIndices: typeof Matchers.NthFragmentMatcher.clearFragmentIndices; export declare class AfterIfContinues implements Selectors.AfterIfContinues { readonly sourceNode: Element; readonly styler: PseudoElement.PseudoelementStyler; constructor(sourceNode: Element, styler: PseudoElement.PseudoelementStyler); createElement(column: Layout.Column, parentNodeContext: Vtree.NodeContext): Task.Result<Element>; private createNodePositionForPseudoElement; private createShadowContext; } export declare class AfterIfContinuesLayoutConstraint implements Selectors.AfterIfContinuesLayoutConstraint { nodeContext: Vtree.NodeContext; afterIfContinues: Selectors.AfterIfContinues; pseudoElementHeight: number; flagmentLayoutConstraintType: FragmentLayoutConstraintType; constructor(nodeContext: Vtree.NodeContext, afterIfContinues: Selectors.AfterIfContinues, pseudoElementHeight: number); /** @override */ allowLayout(nodeContext: Vtree.NodeContext, overflownNodeContext: Vtree.NodeContext, column: Layout.Column): boolean; /** @override */ nextCandidate(nodeContext: Vtree.NodeContext): boolean; /** @override */ postLayout(allowed: boolean, positionAfter: Vtree.NodeContext, initialPosition: Vtree.NodeContext, column: Layout.Column): void; /** @override */ finishBreak(nodeContext: Vtree.NodeContext, column: Layout.Column): Task.Result<boolean>; getRepetitiveElements(): AfterIfContinuesElementsOffset; /** @override */ equalsTo(constraint: Layout.FragmentLayoutConstraint): boolean; /** @override */ getPriorityOfFinishBreak(): number; } export declare class AfterIfContinuesElementsOffset implements Selectors.AfterIfContinuesElementsOffset { nodeContext: any; pseudoElementHeight: any; constructor(nodeContext: any, pseudoElementHeight: any); /** @override */ calculateOffset(nodeContext: Vtree.NodeContext): number; /** @override */ calculateMinimumOffset(nodeContext: Vtree.NodeContext): number; affectTo(nodeContext: Vtree.NodeContext): boolean; } export declare function processAfterIfContinues(result: Task.Result<Vtree.NodeContext>, column: Layout.Column): Task.Result<Vtree.NodeContext>; export declare function processAfterIfContinuesOfAncestors(nodeContext: Vtree.NodeContext, column: Layout.Column): Task.Result<boolean>; export declare function calculatePseudoElementHeight(nodeContext: Vtree.NodeContext, column: Layout.Column, pseudoElement: Element): number; export declare const mediaTags: { img: boolean; svg: boolean; audio: boolean; video: boolean; }; /** * Represents a constraint on layout */ export declare type LayoutConstraint = Layout.LayoutConstraint; /** * Represents a constraint that allows layout if all the constraints it contains * allow layout. */ export declare class AllLayoutConstraint implements LayoutConstraint { readonly constraints: LayoutConstraint[]; constructor(constraints: LayoutConstraint[]); /** * @override */ allowLayout(nodeContext: Vtree.NodeContext): boolean; } /** * Represents constraints on laying out fragments */ export declare type FragmentLayoutConstraint = Layout.FragmentLayoutConstraint; export declare type BreakPositionAndNodeContext = Layout.BreakPositionAndNodeContext; /** * Potential breaking position inside CSS box (between lines). * @param checkPoints array of breaking points for breakable block */ export declare class BoxBreakPosition extends BreakPosition.AbstractBreakPosition implements Layout.BoxBreakPosition { readonly checkPoints: Vtree.NodeContext[]; readonly penalty: number; private alreadyEvaluated; breakNodeContext: Vtree.NodeContext; constructor(checkPoints: Vtree.NodeContext[], penalty: number); /** * @override */ findAcceptableBreak(column: Column, penalty: number): Vtree.NodeContext; /** * @override */ getMinBreakPenalty(): number; /** @override */ getNodeContext(): Vtree.NodeContext; } export declare function validateCheckPoints(checkPoints: Vtree.NodeContext[]): void; export declare function isSpecialInlineDisplay(display: string): boolean; export declare class Column extends VtreeImpl.Container implements Layout.Column { layoutContext: Vtree.LayoutContext; clientLayout: Vtree.ClientLayout; readonly layoutConstraint: LayoutConstraint; readonly pageFloatLayoutContext: PageFloats.PageFloatLayoutContext; last: Node; viewDocument: Document; flowRootFormattingContext: Vtree.FormattingContext; isFloat: boolean; isFootnote: boolean; startEdge: number; endEdge: number; beforeEdge: number; afterEdge: number; footnoteEdge: number; box: GeometryUtil.Rect; chunkPositions: Vtree.ChunkPosition[]; bands: GeometryUtil.Band[]; overflown: boolean; breakPositions: BreakPosition.BreakPosition[]; pageBreakType: string | null; forceNonfitting: boolean; leftFloatEdge: number; rightFloatEdge: number; bottommostFloatTop: number; stopAtOverflow: boolean; lastAfterPosition: Vtree.NodePosition | null; fragmentLayoutConstraints: FragmentLayoutConstraint[]; pseudoParent: Column; nodeContextOverflowingDueToRepetitiveElements: Vtree.NodeContext | null; blockDistanceToBlockEndFloats: number; computedBlockSize: number; constructor(element: Element, layoutContext: Vtree.LayoutContext, clientLayout: Vtree.ClientLayout, layoutConstraint: LayoutConstraint, pageFloatLayoutContext: PageFloats.PageFloatLayoutContext); getTopEdge(): number; getBottomEdge(): number; getLeftEdge(): number; getRightEdge(): number; isFloatNodeContext(nodeContext: Vtree.NodeContext): boolean; stopByOverflow(nodeContext: Vtree.NodeContext): boolean; isOverflown(edge: number): boolean; getExclusions(): GeometryUtil.Shape[]; openAllViews(position: Vtree.NodePosition): Task.Result<Vtree.NodeContext>; calculateOffsetInNodeForNodeContext(position: Vtree.NodePosition): number; /** * @param count first-XXX nesting identifier */ maybePeelOff(position: Vtree.NodeContext, count: number): Task.Result<Vtree.NodeContext>; /** * Builds the view until a CSS box edge is reached. * @param position start source position. * @param checkPoints array to append possible breaking points. * @return holding box edge position reached or null if the source is exhausted. */ buildViewToNextBlockEdge(position: Vtree.NodeContext, checkPoints: Vtree.NodeContext[]): Task.Result<Vtree.NodeContext>; nextInTree(position: Vtree.NodeContext, atUnforcedBreak?: boolean): Task.Result<Vtree.NodeContext>; /** * Builds the view for a single unbreakable element. * @param position start source position. * @return holding box edge position reached or null if the source is exhausted. */ buildDeepElementView(position: Vtree.NodeContext): Task.Result<Vtree.NodeContext>; /** * Create a single floating element (for exclusion areas). * @param ref container's child to insert float before (can be null). * @param side float side ("left" or "right"). * @param width float inline dimension. * @param height float box progression dimension. * @return newly created float element. */ createFloat(ref: Node, side: string, width: number, height: number): Element; /** * Remove all the exclusion floats. */ killFloats(): void; /** * Create exclusion floats for a column. */ createFloats(): void; /** * @param nodeContext position after the block * @param checkPoints array of possible breaking points. * @param index index of the breaking point * @param boxOffset box offset * @return edge position */ calculateEdge(nodeContext: Vtree.NodeContext, checkPoints: Vtree.NodeContext[], index: number, boxOffset: number): number; /** * Parse CSS computed length (in pixels) * @param val CSS length in "px" units or a number. * @return value in pixels or 0 if not parsable */ parseComputedLength(val: string | number): number; /** * Reads element's computed CSS margin. */ getComputedMargin(element: Element): GeometryUtil.Insets; /** * Reads element's computed padding + borders. */ getComputedPaddingBorder(element: Element): GeometryUtil.Insets; /** * Reads element's computed CSS insets(margins + border + padding or margins : * depends on box-sizing) */ getComputedInsets(element: Element): GeometryUtil.Insets; /** * Set element's computed CSS insets to Column Container */ setComputedInsets(element: Element, container: Column): void; /** * Set element's computed width and height to Column Container */ setComputedWidthAndHeight(element: Element, container: Column): void; /** * Layout a single unbreakable element. */ layoutUnbreakable(nodeContextIn: Vtree.NodeContext): Task.Result<Vtree.NodeContext>; /** * Layout a single float element. */ layoutFloat(nodeContext: Vtree.NodeContext): Task.Result<Vtree.NodeContext>; setupFloatArea(area: PageFloatArea, floatReference: PageFloats.FloatReference, floatSide: string, anchorEdge: number | null, strategy: PageFloats.PageFloatLayoutStrategy, condition: PageFloats.PageFloatPlacementCondition): boolean; createPageFloatArea(float: PageFloats.PageFloat | null, floatSide: string, anchorEdge: number | null, strategy: PageFloats.PageFloatLayoutStrategy, condition: PageFloats.PageFloatPlacementCondition): PageFloatArea | null; layoutSinglePageFloatFragment(continuations: PageFloats.PageFloatContinuation[], floatSide: string, clearSide: string | null, allowFragmented: boolean, strategy: PageFloats.PageFloatLayoutStrategy, anchorEdge: number | null, pageFloatFragment?: PageFloats.PageFloatFragment | null): Task.Result<SinglePageFloatLayoutResult>; layoutPageFloatInner(continuation: PageFloats.PageFloatContinuation, strategy: PageFloats.PageFloatLayoutStrategy, anchorEdge: number | null, pageFloatFragment?: PageFloats.PageFloatFragment): Task.Result<boolean>; /** * @returns Represents if the layout was succeeded or not */ private layoutStashedPageFloats; setFloatAnchorViewNode(nodeContext: Vtree.NodeContext): Vtree.NodeContext; resolveFloatReferenceFromColumnSpan(floatReference: PageFloats.FloatReference, columnSpan: Css.Val, nodeContext: Vtree.NodeContext): Task.Result<PageFloats.FloatReference>; layoutPageFloat(nodeContext: Vtree.NodeContext): Task.Result<Vtree.NodeContext>; createJustificationAdjustmentElement(insertionPoint: Node, doc: Document, parentNode: Node, vertical: boolean): HTMLElement; addAndAdjustJustificationElement(nodeContext: Vtree.NodeContext, insertAfter: boolean, node: Node, insertionPoint: Node, doc: Document, parentNode: Node): HTMLElement; compensateJustificationLineHeight(span: Element, br: Element, nodeContext: Vtree.NodeContext): void; /** * Fix justification of the last line of text broken across pages (if * needed). */ fixJustificationIfNeeded(nodeContext: Vtree.NodeContext, endOfColumn: boolean): void; processLineStyling(nodeContext: Vtree.NodeContext, resNodeContext: Vtree.NodeContext, checkPoints: Vtree.NodeContext[]): Task.Result<Vtree.NodeContext>; isLoneImage(checkPoints: Vtree.NodeContext[]): boolean; getTrailingMarginEdgeAdjustment(trailingEdgeContexts: Vtree.NodeContext[]): number; /** * Layout a single CSS box. */ layoutBreakableBlock(nodeContext: Vtree.NodeContext): Task.Result<Vtree.NodeContext>; postLayoutBlock(nodeContext: Vtree.NodeContext, checkPoints: Vtree.NodeContext[]): void; findEndOfLine(linePosition: number, checkPoints: Vtree.NodeContext[], isUpdateMaxReachedAfterEdge: boolean): { nodeContext: Vtree.NodeContext; index: number; checkPointIndex: number; }; findAcceptableBreakInside(checkPoints: Vtree.NodeContext[], edgePosition: number, force: boolean): Vtree.NodeContext; resolveTextNodeBreaker(nodeContext: Vtree.NodeContext): TextNodeBreaker; /** * Read ranges skipping special elments */ getRangeBoxes(start: Node, end: Node): Vtree.ClientRect[]; /** * Give block's initial and final nodes, find positions of the line bottoms. * This is, of course, somewhat hacky implementation. * @return position of line breaks */ findLinePositions(checkPoints: Vtree.NodeContext[]): number[]; calculateClonedPaddingBorder(nodeContext: Vtree.NodeContext): number; private getOffsetByRepetitiveElements; findBoxBreakPosition(bp: BoxBreakPosition, force: boolean): Vtree.NodeContext; getAfterEdgeOfBlockContainer(nodeContext: Vtree.NodeContext): number; findFirstOverflowingEdgeAndCheckPoint(checkPoints: Vtree.NodeContext[]): { edge: number; checkPoint: Vtree.NodeContext | null; }; findEdgeBreakPosition(bp: BreakPosition.EdgeBreakPosition): Vtree.NodeContext; /** * Finalize a line break. * @return holing true */ finishBreak(nodeContext: Vtree.NodeContext, forceRemoveSelf: boolean, endOfColumn: boolean): Task.Result<boolean>; findAcceptableBreakPosition(): BreakPositionAndNodeContext; doFinishBreak(nodeContext: Vtree.NodeContext, overflownNodeContext: Vtree.NodeContext, initialNodeContext: Vtree.NodeContext, initialComputedBlockSize: number): Task.Result<Vtree.NodeContext>; /** * Determines if a page break is acceptable at this position */ isBreakable(flowPosition: Vtree.NodeContext): boolean; /** * Determines if an indent value is zero */ zeroIndent(val: string | number): boolean; /** * @return true if overflows */ checkOverflowAndSaveEdge(nodeContext: Vtree.NodeContext, trailingEdgeContexts: Vtree.NodeContext[]): boolean; /** * Save a possible page break position on a CSS block edge. Check if it * overflows. * @return true if overflows */ checkOverflowAndSaveEdgeAndBreakPosition(nodeContext: Vtree.NodeContext, trailingEdgeContexts: Vtree.NodeContext[], saveEvenOverflown: boolean, breakAtTheEdge: string | null): boolean; applyClearance(nodeContext: Vtree.NodeContext): boolean; isBFC(formattingContext: Vtree.FormattingContext): boolean; /** * Skips positions until either the start of unbreakable block or inline * content. Also sets breakBefore on the result combining break-before and * break-after properties from all elements that meet at the edge. */ skipEdges(nodeContext: Vtree.NodeContext, leadingEdge: boolean, forcedBreakValue: string | null): Task.Result<Vtree.NodeContext>; /** * Skips non-renderable positions until it hits the end of the flow or some * renderable content. Returns the nodeContext that was passed in if some * content remains and null if all content could be skipped. */ skipTailEdges(nodeContext: Vtree.NodeContext): Task.Result<Vtree.NodeContext>; layoutFloatOrFootnote(nodeContext: Vtree.NodeContext): Task.Result<Vtree.NodeContext>; /** * Layout next portion of the source. */ layoutNext(nodeContext: Vtree.NodeContext, leadingEdge: boolean, forcedBreakValue?: string | null): Task.Result<Vtree.NodeContext>; clearOverflownViewNodes(nodeContext: Vtree.NodeContext, removeSelf: boolean): void; initGeom(): void; init(): void; /** * Save the potential breaking position at the edge. Should, in general, save * "after" position but only after skipping all of the "before" ones and * getting to the non-empty content (to get breakAtEdge right). */ saveEdgeBreakPosition(position: Vtree.NodeContext, breakAtEdge: string | null, overflows: boolean): void; /** * @param checkPoints array of breaking points for breakable block */ saveBoxBreakPosition(checkPoints: Vtree.NodeContext[]): void; updateMaxReachedAfterEdge(afterEdge: number): void; /** * @param chunkPosition starting position. * @return holding end position. */ layout(chunkPosition: Vtree.ChunkPosition, leadingEdge: boolean, breakAfter?: string | null): Task.Result<Vtree.ChunkPosition>; isFullWithPageFloats(): boolean; getMaxBlockSizeOfPageFloats(): number; doFinishBreakOfFragmentLayoutConstraints(nodeContext: Vtree.NodeContext): Task.Result<boolean>; /** * @param nodeContext starting position. * @return holding end position. */ doLayout(nodeContext: Vtree.NodeContext, leadingEdge: boolean, breakAfter?: string | null): Task.Result<{ nodeContext: Vtree.NodeContext; overflownNodeContext: Vtree.NodeContext; }>; /** * Re-layout already laid-out chunks. Return the position of the last flow if * there is an overflow. * TODO: deal with chunks that did not fit at all. * @return holding end position. */ redoLayout(): Task.Result<Vtree.ChunkPosition>; saveDistanceToBlockEndFloats(): void; collectElementsOffset(): RepetitiveElement.ElementsOffset[]; } /** * Represents a "pseudo"-column nested inside a real column. * This class is created to handle parallel fragmented flows (e.g. table columns * in a single table row). A pseudo-column behaves in the same way as the * original column, sharing its properties. Property changes on the * pseudo-column are not propagated to the original column. The LayoutContext of * the original column is also cloned and used by the pseudo-column, not to * propagate state changes of the LayoutContext caused by the pseudo-column. * @param column The original (parent) column * @param viewRoot Root element for the pseudo-column, i.e., the root of the * fragmented flow. * @param parentNodeContext A NodeContext generating this PseudoColumn */ export declare class PseudoColumn { startNodeContexts: Vtree.NodeContext[]; private column; constructor(column: Layout.Column, viewRoot: Element, parentNodeContext: Vtree.NodeContext); /** * @param chunkPosition starting position. * @return holding end position. */ layout(chunkPosition: Vtree.ChunkPosition, leadingEdge: boolean): Task.Result<Vtree.ChunkPosition>; findAcceptableBreakPosition(allowBreakAtStartPosition: boolean): Layout.BreakPositionAndNodeContext; /** * @return holing true */ finishBreak(nodeContext: Vtree.NodeContext, forceRemoveSelf: boolean, endOfColumn: boolean): Task.Result<boolean>; doFinishBreakOfFragmentLayoutConstraints(positionAfter: Vtree.NodeContext): void; isStartNodeContext(nodeContext: Vtree.NodeContext): boolean; isLastAfterNodeContext(nodeContext: Vtree.NodeContext): boolean; getColumnElement(): Element; getColumn(): Layout.Column; } export declare const firstCharPattern: RegExp; export declare type SinglePageFloatLayoutResult = Layout.SinglePageFloatLayoutResult; export declare function fixJustificationOnHyphen(nodeContext: Vtree.NodeContext, insertAfter: boolean, node: Node, insertionPoint: Node): void; /** * breaking point resolver for Text Node. */ export declare class TextNodeBreaker implements Layout.TextNodeBreaker { breakTextNode(textNode: Text, nodeContext: Vtree.NodeContext, low: number, checkPoints: Vtree.NodeContext[], checkpointIndex: number, force: boolean): Vtree.NodeContext; breakAfterSoftHyphen(textNode: Text, text: string, viewIndex: number, nodeContext: Vtree.NodeContext): number; breakAfterOtherCharacter(textNode: Text, text: string, viewIndex: number, nodeContext: Vtree.NodeContext): number; updateNodeContext(nodeContext: Vtree.NodeContext, viewIndex: number, textNode: Text): Vtree.NodeContext; static instance: TextNodeBreaker; } export declare function resolveHyphenateCharacter(nodeContext: Vtree.NodeContext): string; export declare class ColumnLayoutRetryer extends LayoutRetryers.AbstractLayoutRetryer { readonly leadingEdge: boolean; breakAfter: string | null; private initialPageBreakType; initialComputedBlockSize: number; private initialOverflown; context: { overflownNodeContext: Vtree.NodeContext; }; constructor(leadingEdge: boolean, breakAfter?: string | null); /** * @override */ resolveLayoutMode(nodeContext: Vtree.NodeContext): Layout.LayoutMode; /** * @override */ prepareLayout(nodeContext: Vtree.NodeContext, column: Layout.Column): void; /** * @override */ clearNodes(initialPosition: Vtree.NodeContext): void; /** * @override */ saveState(nodeContext: Vtree.NodeContext, column: Layout.Column): void; /** * @override */ restoreState(nodeContext: Vtree.NodeContext, column: Layout.Column): void; } export declare class DefaultLayoutMode implements Layout.LayoutMode { readonly leadingEdge: boolean; readonly breakAfter: string | null; readonly context: { overflownNodeContext: Vtree.NodeContext; }; constructor(leadingEdge: boolean, breakAfter: string | null, context: { overflownNodeContext: Vtree.NodeContext; }); /** * @override */ doLayout(nodeContext: Vtree.NodeContext, column: Layout.Column): Task.Result<Vtree.NodeContext>; /** * @override */ accept(nodeContext: Vtree.NodeContext, column: Layout.Column): boolean; /** * @override */ postLayout(positionAfter: Vtree.NodeContext, initialPosition: Vtree.NodeContext, column: Layout.Column, accepted: boolean): boolean; } export declare class PageFloatArea extends Column implements Layout.PageFloatArea { readonly floatSide: string; readonly parentContainer: Vtree.Container; private rootViewNodes; private floatMargins; adjustContentRelativeSize: boolean; constructor(floatSide: string, element: Element, layoutContext: Vtree.LayoutContext, clientLayout: Vtree.ClientLayout, layoutConstraint: LayoutConstraint, pageFloatLayoutContext: PageFloats.PageFloatLayoutContext, parentContainer: Vtree.Container); /** * @override */ openAllViews(position: Vtree.NodePosition): Task.Result<Vtree.NodeContext>; convertPercentageSizesToPx(target: Element): void; fixFloatSizeAndPosition(nodeContext: Vtree.NodeContext): void; getContentInlineSize(): number; }