UNPKG

pagedjs

Version:

Chunks up a document into paged media flows and applies print styles

1,958 lines (1,678 loc) 54.1 kB
import Handler from "../handler.js"; import csstree from "css-tree"; import pageSizes from "../../polisher/sizes.js"; import { rebuildAncestors } from "../../utils/dom.js"; import { CSSValueToString } from "../../utils/utils.js"; class AtPage extends Handler { constructor(chunker, polisher, caller) { super(chunker, polisher, caller); this.pages = {}; this.width = undefined; this.height = undefined; this.orientation = undefined; this.marginalia = {}; } pageModel(selector) { return { selector: selector, name: undefined, psuedo: undefined, nth: undefined, marginalia: {}, width: undefined, height: undefined, orientation: undefined, margin: { top: {}, right: {}, left: {}, bottom: {} }, padding: { top: {}, right: {}, left: {}, bottom: {} }, border: { top: {}, right: {}, left: {}, bottom: {} }, backgroundOrigin: undefined, block: {}, marks: undefined, notes: undefined, added: false }; } // Find and Remove @page rules onAtPage(node, item, list) { let page, marginalia; let selector = ""; let named, psuedo, nth; let needsMerge = false; if (node.prelude) { named = this.getTypeSelector(node); psuedo = this.getPsuedoSelector(node); nth = this.getNthSelector(node); selector = csstree.generate(node.prelude); } else { selector = "*"; } if (selector in this.pages) { // this.pages[selector] = Object.assign(this.pages[selector], page); // console.log("after", selector, this.pages[selector]); // this.pages[selector].added = false; page = this.pages[selector]; marginalia = this.replaceMarginalia(node); needsMerge = true; // Mark page for getting classes added again page.added = false; } else { page = this.pageModel(selector); marginalia = this.replaceMarginalia(node); this.pages[selector] = page; } page.name = named; page.psuedo = psuedo; page.nth = nth; if (needsMerge) { page.marginalia = Object.assign(page.marginalia, marginalia); } else { page.marginalia = marginalia; } let notes = this.replaceNotes(node); page.notes = notes; let declarations = this.replaceDeclarations(node); if (declarations.size) { page.size = declarations.size; page.width = declarations.size.width; page.height = declarations.size.height; page.orientation = declarations.size.orientation; page.format = declarations.size.format; } if (declarations.bleed && declarations.bleed[0] != "auto") { switch (declarations.bleed.length) { case 4: // top right bottom left page.bleed = { top: declarations.bleed[0], right: declarations.bleed[1], bottom: declarations.bleed[2], left: declarations.bleed[3] }; break; case 3: // top right bottom right page.bleed = { top: declarations.bleed[0], right: declarations.bleed[1], bottom: declarations.bleed[2], left: declarations.bleed[1] }; break; case 2: // top right top right page.bleed = { top: declarations.bleed[0], right: declarations.bleed[1], bottom: declarations.bleed[0], left: declarations.bleed[1] }; break; default: page.bleed = { top: declarations.bleed[0], right: declarations.bleed[0], bottom: declarations.bleed[0], left: declarations.bleed[0] }; } } if (declarations.marks) { if (!declarations.bleed || declarations.bleed && declarations.bleed[0] === "auto") { // Spec say 6pt, but needs more space for marks page.bleed = { top: { value: 6, unit: "mm" }, right: { value: 6, unit: "mm" }, bottom: { value: 6, unit: "mm" }, left: { value: 6, unit: "mm" } }; } page.marks = declarations.marks; } if (declarations.margin) { page.margin = declarations.margin; } if (declarations.padding) { page.padding = declarations.padding; } if (declarations.border) { page.border = declarations.border; } if (declarations.marks) { page.marks = declarations.marks; } if (needsMerge) { page.block.children.appendList(node.block.children); } else { page.block = node.block; } // Remove the rule list.remove(item); } /* Handled in breaks */ /* afterParsed(parsed) { for (let b in this.named) { // Find elements let elements = parsed.querySelectorAll(b); // Add break data for (var i = 0; i < elements.length; i++) { elements[i].setAttribute("data-page", this.named[b]); } } } */ afterTreeWalk(ast, sheet) { let dirtyPage = "*" in this.pages && this.pages["*"].added === false; this.addPageClasses(this.pages, ast, sheet); if (dirtyPage) { let width = this.pages["*"].width; let height = this.pages["*"].height; let format = this.pages["*"].format; let orientation = this.pages["*"].orientation; let bleed = this.pages["*"].bleed; let marks = this.pages["*"].marks; let bleedverso = undefined; let bleedrecto = undefined; if (":left" in this.pages) { bleedverso = this.pages[":left"].bleed; } if (":right" in this.pages) { bleedrecto = this.pages[":right"].bleed; } if ((width && height) && (this.width !== width || this.height !== height)) { this.width = width; this.height = height; this.format = format; this.orientation = orientation; this.addRootVars(ast, width, height, orientation, bleed, bleedrecto, bleedverso, marks); this.addRootPage(ast, this.pages["*"].size, bleed, bleedrecto, bleedverso); this.emit("size", { width, height, orientation, format, bleed }); this.emit("atpages", this.pages); } } } getTypeSelector(ast) { // Find page name let name; csstree.walk(ast, { visit: "TypeSelector", enter: (node, item, list) => { name = node.name; } }); return name; } getPsuedoSelector(ast) { // Find if it has :left & :right & :black & :first let name; csstree.walk(ast, { visit: "PseudoClassSelector", enter: (node, item, list) => { if (node.name !== "nth") { name = node.name; } } }); return name; } getNthSelector(ast) { // Find if it has :nth let nth; csstree.walk(ast, { visit: "PseudoClassSelector", enter: (node, item, list) => { if (node.name === "nth" && node.children) { let raw = node.children.first(); nth = raw.value; } } }); return nth; } replaceMarginalia(ast) { let parsed = {}; const MARGINS = [ "top-left-corner", "top-left", "top", "top-center", "top-right", "top-right-corner", "bottom-left-corner", "bottom-left", "bottom", "bottom-center", "bottom-right", "bottom-right-corner", "left-top", "left-middle", "left", "left-bottom", "top-right-corner", "right-top", "right-middle", "right", "right-bottom", "right-right-corner" ]; csstree.walk(ast.block, { visit: "Atrule", enter: (node, item, list) => { let name = node.name; if (MARGINS.includes(name)) { if (name === "top") { name = "top-center"; } if (name === "right") { name = "right-middle"; } if (name === "left") { name = "left-middle"; } if (name === "bottom") { name = "bottom-center"; } parsed[name] = node.block; list.remove(item); } } }); return parsed; } replaceNotes(ast) { let parsed = {}; csstree.walk(ast.block, { visit: "Atrule", enter: (node, item, list) => { let name = node.name; if (name === "footnote") { parsed[name] = node.block; list.remove(item); } } }); return parsed; } replaceDeclarations(ast) { let parsed = {}; csstree.walk(ast.block, { visit: "Declaration", enter: (declaration, dItem, dList) => { let prop = csstree.property(declaration.property).name; // let value = declaration.value; if (prop === "marks") { parsed.marks = []; csstree.walk(declaration, { visit: "Identifier", enter: (ident) => { parsed.marks.push(ident.name); } }); dList.remove(dItem); } else if (prop === "margin") { parsed.margin = this.getMargins(declaration); dList.remove(dItem); } else if (prop.indexOf("margin-") === 0) { let m = prop.substring("margin-".length); if (!parsed.margin) { parsed.margin = { top: {}, right: {}, left: {}, bottom: {} }; } parsed.margin[m] = declaration.value.children.first(); dList.remove(dItem); } else if (prop === "padding") { parsed.padding = this.getPaddings(declaration.value); dList.remove(dItem); } else if (prop.indexOf("padding-") === 0) { let p = prop.substring("padding-".length); if (!parsed.padding) { parsed.padding = { top: {}, right: {}, left: {}, bottom: {} }; } parsed.padding[p] = declaration.value.children.first(); dList.remove(dItem); } else if (prop === "border") { if (!parsed.border) { parsed.border = { top: {}, right: {}, left: {}, bottom: {} }; } parsed.border.top = csstree.generate(declaration.value); parsed.border.right = csstree.generate(declaration.value); parsed.border.left = csstree.generate(declaration.value); parsed.border.bottom = csstree.generate(declaration.value); dList.remove(dItem); } else if (prop.indexOf("border-") === 0) { if (!parsed.border) { parsed.border = { top: {}, right: {}, left: {}, bottom: {} }; } let p = prop.substring("border-".length); parsed.border[p] = csstree.generate(declaration.value); dList.remove(dItem); } else if (prop === "size") { parsed.size = this.getSize(declaration); dList.remove(dItem); } else if (prop === "bleed") { parsed.bleed = []; csstree.walk(declaration, { enter: (subNode) => { switch (subNode.type) { case "String": // bleed: "auto" if (subNode.value.indexOf("auto") > -1) { parsed.bleed.push("auto"); } break; case "Dimension": // bleed: 1in 2in, bleed: 20px ect. parsed.bleed.push({ value: subNode.value, unit: subNode.unit }); break; case "Number": parsed.bleed.push({ value: subNode.value, unit: "px" }); break; default: // ignore } } }); dList.remove(dItem); } } }); return parsed; } getSize(declaration) { let width, height, orientation, format; // Get size: Xmm Ymm csstree.walk(declaration, { visit: "Dimension", enter: (node, item, list) => { let { value, unit } = node; if (typeof width === "undefined") { width = { value, unit }; } else if (typeof height === "undefined") { height = { value, unit }; } } }); // Get size: "A4" csstree.walk(declaration, { visit: "String", enter: (node, item, list) => { let name = node.value.replace(/["|']/g, ""); let s = pageSizes[name]; if (s) { width = s.width; height = s.height; } } }); // Get Format or Landscape or Portrait csstree.walk(declaration, { visit: "Identifier", enter: (node, item, list) => { let name = node.name; if (name === "landscape" || name === "portrait") { orientation = node.name; } else if (name !== "auto") { let s = pageSizes[name]; if (s) { width = s.width; height = s.height; } format = name; } } }); return { width, height, orientation, format }; } getMargins(declaration) { let margins = []; let margin = { top: {}, right: {}, left: {}, bottom: {} }; csstree.walk(declaration, { enter: (node) => { switch (node.type) { case "Dimension": // margin: 1in 2in, margin: 20px, etc... margins.push(node); break; case "Number": // margin: 0 margins.push({value: node.value, unit: "px"}); break; default: // ignore } } }); if (margins.length === 1) { for (let m in margin) { margin[m] = margins[0]; } } else if (margins.length === 2) { margin.top = margins[0]; margin.right = margins[1]; margin.bottom = margins[0]; margin.left = margins[1]; } else if (margins.length === 3) { margin.top = margins[0]; margin.right = margins[1]; margin.bottom = margins[2]; margin.left = margins[1]; } else if (margins.length === 4) { margin.top = margins[0]; margin.right = margins[1]; margin.bottom = margins[2]; margin.left = margins[3]; } return margin; } getPaddings(declaration) { let paddings = []; let padding = { top: {}, right: {}, left: {}, bottom: {} }; csstree.walk(declaration, { enter: (node) => { switch (node.type) { case "Dimension": // padding: 1in 2in, padding: 20px, etc... paddings.push(node); break; case "Number": // padding: 0 paddings.push({value: node.value, unit: "px"}); break; default: // ignore } } }); if (paddings.length === 1) { for (let p in padding) { padding[p] = paddings[0]; } } else if (paddings.length === 2) { padding.top = paddings[0]; padding.right = paddings[1]; padding.bottom = paddings[0]; padding.left = paddings[1]; } else if (paddings.length === 3) { padding.top = paddings[0]; padding.right = paddings[1]; padding.bottom = paddings[2]; padding.left = paddings[1]; } else if (paddings.length === 4) { padding.top = paddings[0]; padding.right = paddings[1]; padding.bottom = paddings[2]; padding.left = paddings[3]; } return padding; } // get values for the border on the @page to pass them to the element with the .pagedjs_area class getBorders(declaration) { let border = { top: {}, right: {}, left: {}, bottom: {} }; if (declaration.prop == "border") { border.top = csstree.generate(declaration.value); border.right = csstree.generate(declaration.value); border.bottom = csstree.generate(declaration.value); border.left = csstree.generate(declaration.value); } else if (declaration.prop == "border-top") { border.top = csstree.generate(declaration.value); } else if (declaration.prop == "border-right") { border.right = csstree.generate(declaration.value); } else if (declaration.prop == "border-bottom") { border.bottom = csstree.generate(declaration.value); } else if (declaration.prop == "border-left") { border.left = csstree.generate(declaration.value); } return border; } addPageClasses(pages, ast, sheet) { // First add * page if ("*" in pages && pages["*"].added === false) { let p = this.createPage(pages["*"], ast.children, sheet); sheet.insertRule(p); pages["*"].added = true; } // Add :left & :right if (":left" in pages && pages[":left"].added === false) { let left = this.createPage(pages[":left"], ast.children, sheet); sheet.insertRule(left); pages[":left"].added = true; } if (":right" in pages && pages[":right"].added === false) { let right = this.createPage(pages[":right"], ast.children, sheet); sheet.insertRule(right); pages[":right"].added = true; } // Add :first & :blank if (":first" in pages && pages[":first"].added === false) { let first = this.createPage(pages[":first"], ast.children, sheet); sheet.insertRule(first); pages[":first"].added = true; } if (":blank" in pages && pages[":blank"].added === false) { let blank = this.createPage(pages[":blank"], ast.children, sheet); sheet.insertRule(blank); pages[":blank"].added = true; } // Add nth pages for (let pg in pages) { if (pages[pg].nth && pages[pg].added === false) { let nth = this.createPage(pages[pg], ast.children, sheet); sheet.insertRule(nth); pages[pg].added = true; } } // Add named pages for (let pg in pages) { if (pages[pg].name && pages[pg].added === false) { let named = this.createPage(pages[pg], ast.children, sheet); sheet.insertRule(named); pages[pg].added = true; } } } createPage(page, ruleList, sheet) { let selectors = this.selectorsForPage(page); let children = page.block.children.copy(); let block = { type: "Block", loc: 0, children: children }; let rule = this.createRule(selectors, block); this.addMarginVars(page.margin, children, children.first()); this.addPaddingVars(page.padding, children, children.first()); this.addBorderVars(page.border, children, children.first()); if (page.width) { this.addDimensions(page.width, page.height, page.orientation, children, children.first()); } if (page.marginalia) { this.addMarginaliaStyles(page, ruleList, rule, sheet); this.addMarginaliaContent(page, ruleList, rule, sheet); } if(page.notes) { this.addNotesStyles(page.notes, page, ruleList, rule, sheet); } return rule; } addMarginVars(margin, list, item) { // variables for margins for (let m in margin) { if (typeof margin[m].value !== "undefined") { let value = margin[m].value + (margin[m].unit || ""); let mVar = list.createItem({ type: "Declaration", property: "--pagedjs-margin-" + m, value: { type: "Raw", value: value } }); list.append(mVar, item); } } } addPaddingVars(padding, list, item) { // variables for padding for (let p in padding) { if (typeof padding[p].value !== "undefined") { let value = padding[p].value + (padding[p].unit || ""); let pVar = list.createItem({ type: "Declaration", property: "--pagedjs-padding-" + p, value: { type: "Raw", value: value } }); list.append(pVar, item); } } } addBorderVars(border, list, item) { // variables for borders for (const name of Object.keys(border)) { const value = border[name]; // value is an empty object when undefined if (typeof value === "string") { const borderItem = list.createItem({ type: "Declaration", property: "--pagedjs-border-" + name, value: { type: "Raw", value: value } }); list.append(borderItem, item); } } } addDimensions(width, height, orientation, list, item) { let widthString, heightString; widthString = CSSValueToString(width); heightString = CSSValueToString(height); if (orientation && orientation !== "portrait") { // reverse for orientation [widthString, heightString] = [heightString, widthString]; } // width variable let wVar = this.createVariable("--pagedjs-pagebox-width", widthString); list.appendData(wVar); // height variable let hVar = this.createVariable("--pagedjs-pagebox-height", heightString); list.appendData(hVar); // let w = this.createDimension("width", width); // let h = this.createDimension("height", height); // list.appendData(w); // list.appendData(h); } addMarginaliaStyles(page, list, item, sheet) { for (let loc in page.marginalia) { let block = csstree.clone(page.marginalia[loc]); let hasContent = false; if (block.children.isEmpty()) { continue; } csstree.walk(block, { visit: "Declaration", enter: (node, item, list) => { if (node.property === "content") { if (node.value.children && node.value.children.first().name === "none") { hasContent = false; } else { hasContent = true; } list.remove(item); } if (node.property === "vertical-align") { csstree.walk(node, { visit: "Identifier", enter: (identNode, identItem, identlist) => { let name = identNode.name; if (name === "top") { identNode.name = "flex-start"; } else if (name === "middle") { identNode.name = "center"; } else if (name === "bottom") { identNode.name = "flex-end"; } } }); node.property = "align-items"; } if (node.property === "width" && (loc === "top-left" || loc === "top-center" || loc === "top-right" || loc === "bottom-left" || loc === "bottom-center" || loc === "bottom-right")) { let c = csstree.clone(node); c.property = "max-width"; list.appendData(c); } if (node.property === "height" && (loc === "left-top" || loc === "left-middle" || loc === "left-bottom" || loc === "right-top" || loc === "right-middle" || loc === "right-bottom")) { let c = csstree.clone(node); c.property = "max-height"; list.appendData(c); } } }); let marginSelectors = this.selectorsForPageMargin(page, loc); let marginRule = this.createRule(marginSelectors, block); list.appendData(marginRule); let sel = csstree.generate({ type: "Selector", children: marginSelectors }); this.marginalia[sel] = { page: page, selector: sel, block: page.marginalia[loc], hasContent: hasContent }; } } addMarginaliaContent(page, list, item, sheet) { let displayNone; // Just content for (let loc in page.marginalia) { let content = csstree.clone(page.marginalia[loc]); csstree.walk(content, { visit: "Declaration", enter: (node, item, list) => { if (node.property !== "content") { list.remove(item); } if (node.value.children && node.value.children.first().name === "none") { displayNone = true; } } }); if (content.children.isEmpty()) { continue; } let displaySelectors = this.selectorsForPageMargin(page, loc); let displayDeclaration; displaySelectors.insertData({ type: "Combinator", name: ">" }); displaySelectors.insertData({ type: "ClassSelector", name: "pagedjs_margin-content" }); displaySelectors.insertData({ type: "Combinator", name: ">" }); displaySelectors.insertData({ type: "TypeSelector", name: "*" }); if (displayNone) { displayDeclaration = this.createDeclaration("display", "none"); } else { displayDeclaration = this.createDeclaration("display", "block"); } let displayRule = this.createRule(displaySelectors, [displayDeclaration]); sheet.insertRule(displayRule); // insert content rule let contentSelectors = this.selectorsForPageMargin(page, loc); contentSelectors.insertData({ type: "Combinator", name: ">" }); contentSelectors.insertData({ type: "ClassSelector", name: "pagedjs_margin-content" }); contentSelectors.insertData({ type: "PseudoElementSelector", name: "after", children: null }); let contentRule = this.createRule(contentSelectors, content); sheet.insertRule(contentRule); } } addRootVars(ast, width, height, orientation, bleed, bleedrecto, bleedverso, marks) { let rules = []; let selectors = new csstree.List(); selectors.insertData({ type: "PseudoClassSelector", name: "root", children: null }); let widthString, heightString; let widthStringRight, heightStringRight; let widthStringLeft, heightStringLeft; if (!bleed) { widthString = CSSValueToString(width); heightString = CSSValueToString(height); widthStringRight = CSSValueToString(width); heightStringRight = CSSValueToString(height); widthStringLeft = CSSValueToString(width); heightStringLeft = CSSValueToString(height); } else { widthString = `calc( ${CSSValueToString(width)} + ${CSSValueToString(bleed.left)} + ${CSSValueToString(bleed.right)} )`; heightString = `calc( ${CSSValueToString(height)} + ${CSSValueToString(bleed.top)} + ${CSSValueToString(bleed.bottom)} )`; widthStringRight = `calc( ${CSSValueToString(width)} + ${CSSValueToString(bleed.left)} + ${CSSValueToString(bleed.right)} )`; heightStringRight = `calc( ${CSSValueToString(height)} + ${CSSValueToString(bleed.top)} + ${CSSValueToString(bleed.bottom)} )`; widthStringLeft = `calc( ${CSSValueToString(width)} + ${CSSValueToString(bleed.left)} + ${CSSValueToString(bleed.right)} )`; heightStringLeft = `calc( ${CSSValueToString(height)} + ${CSSValueToString(bleed.top)} + ${CSSValueToString(bleed.bottom)} )`; let bleedTop = this.createVariable("--pagedjs-bleed-top", CSSValueToString(bleed.top)); let bleedRight = this.createVariable("--pagedjs-bleed-right", CSSValueToString(bleed.right)); let bleedBottom = this.createVariable("--pagedjs-bleed-bottom", CSSValueToString(bleed.bottom)); let bleedLeft = this.createVariable("--pagedjs-bleed-left", CSSValueToString(bleed.left)); let bleedTopRecto = this.createVariable("--pagedjs-bleed-right-top", CSSValueToString(bleed.top)); let bleedRightRecto = this.createVariable("--pagedjs-bleed-right-right", CSSValueToString(bleed.right)); let bleedBottomRecto = this.createVariable("--pagedjs-bleed-right-bottom", CSSValueToString(bleed.bottom)); let bleedLeftRecto = this.createVariable("--pagedjs-bleed-right-left", CSSValueToString(bleed.left)); let bleedTopVerso = this.createVariable("--pagedjs-bleed-left-top", CSSValueToString(bleed.top)); let bleedRightVerso = this.createVariable("--pagedjs-bleed-left-right", CSSValueToString(bleed.right)); let bleedBottomVerso = this.createVariable("--pagedjs-bleed-left-bottom", CSSValueToString(bleed.bottom)); let bleedLeftVerso = this.createVariable("--pagedjs-bleed-left-left", CSSValueToString(bleed.left)); if (bleedrecto) { bleedTopRecto = this.createVariable("--pagedjs-bleed-right-top", CSSValueToString(bleedrecto.top)); bleedRightRecto = this.createVariable("--pagedjs-bleed-right-right", CSSValueToString(bleedrecto.right)); bleedBottomRecto = this.createVariable("--pagedjs-bleed-right-bottom", CSSValueToString(bleedrecto.bottom)); bleedLeftRecto = this.createVariable("--pagedjs-bleed-right-left", CSSValueToString(bleedrecto.left)); widthStringRight = `calc( ${CSSValueToString(width)} + ${CSSValueToString(bleedrecto.left)} + ${CSSValueToString(bleedrecto.right)} )`; heightStringRight = `calc( ${CSSValueToString(height)} + ${CSSValueToString(bleedrecto.top)} + ${CSSValueToString(bleedrecto.bottom)} )`; } if (bleedverso) { bleedTopVerso = this.createVariable("--pagedjs-bleed-left-top", CSSValueToString(bleedverso.top)); bleedRightVerso = this.createVariable("--pagedjs-bleed-left-right", CSSValueToString(bleedverso.right)); bleedBottomVerso = this.createVariable("--pagedjs-bleed-left-bottom", CSSValueToString(bleedverso.bottom)); bleedLeftVerso = this.createVariable("--pagedjs-bleed-left-left", CSSValueToString(bleedverso.left)); widthStringLeft = `calc( ${CSSValueToString(width)} + ${CSSValueToString(bleedverso.left)} + ${CSSValueToString(bleedverso.right)} )`; heightStringLeft = `calc( ${CSSValueToString(height)} + ${CSSValueToString(bleedverso.top)} + ${CSSValueToString(bleedverso.bottom)} )`; } let pageWidthVar = this.createVariable("--pagedjs-width", CSSValueToString(width)); let pageHeightVar = this.createVariable("--pagedjs-height", CSSValueToString(height)); rules.push( bleedTop, bleedRight, bleedBottom, bleedLeft, bleedTopRecto, bleedRightRecto, bleedBottomRecto, bleedLeftRecto, bleedTopVerso, bleedRightVerso, bleedBottomVerso, bleedLeftVerso, pageWidthVar, pageHeightVar ); } if (marks) { marks.forEach((mark) => { let markDisplay = this.createVariable("--pagedjs-mark-" + mark + "-display", "block"); rules.push(markDisplay); }); } // orientation variable if (orientation) { let oVar = this.createVariable("--pagedjs-orientation", orientation); rules.push(oVar); if (orientation !== "portrait") { // reverse for orientation [widthString, heightString] = [heightString, widthString]; [widthStringRight, heightStringRight] = [heightStringRight, widthStringRight]; [widthStringLeft, heightStringLeft] = [heightStringLeft, widthStringLeft]; } } let wVar = this.createVariable("--pagedjs-width", widthString); let hVar = this.createVariable("--pagedjs-height", heightString); let wVarR = this.createVariable("--pagedjs-width-right", widthStringRight); let hVarR = this.createVariable("--pagedjs-height-right", heightStringRight); let wVarL = this.createVariable("--pagedjs-width-left", widthStringLeft); let hVarL = this.createVariable("--pagedjs-height-left", heightStringLeft); rules.push(wVar, hVar, wVarR, hVarR, wVarL, hVarL); let rule = this.createRule(selectors, rules); ast.children.appendData(rule); } addNotesStyles(notes, page, list, item, sheet) { for (const note in notes) { let selectors = this.selectorsForPage(page); selectors.insertData({ type: "Combinator", name: " " }); selectors.insertData({ type: "ClassSelector", name: "pagedjs_" + note + "_content" }); let notesRule = this.createRule(selectors, notes[note]); list.appendData(notesRule); } } /* @page { size: var(--pagedjs-width) var(--pagedjs-height); margin: 0; padding: 0; } */ addRootPage(ast, size, bleed, bleedrecto, bleedverso) { let { width, height, orientation, format } = size; let children = new csstree.List(); let childrenLeft = new csstree.List(); let childrenRight = new csstree.List(); let dimensions = new csstree.List(); let dimensionsLeft = new csstree.List(); let dimensionsRight = new csstree.List(); if (bleed) { let widthCalculations = new csstree.List(); let heightCalculations = new csstree.List(); // width widthCalculations.appendData({ type: "Dimension", unit: width.unit, value: width.value }); widthCalculations.appendData({ type: "WhiteSpace", value: " " }); widthCalculations.appendData({ type: "Operator", value: "+" }); widthCalculations.appendData({ type: "WhiteSpace", value: " " }); widthCalculations.appendData({ type: "Dimension", unit: bleed.left.unit, value: bleed.left.value }); widthCalculations.appendData({ type: "WhiteSpace", value: " " }); widthCalculations.appendData({ type: "Operator", value: "+" }); widthCalculations.appendData({ type: "WhiteSpace", value: " " }); widthCalculations.appendData({ type: "Dimension", unit: bleed.right.unit, value: bleed.right.value }); // height heightCalculations.appendData({ type: "Dimension", unit: height.unit, value: height.value }); heightCalculations.appendData({ type: "WhiteSpace", value: " " }); heightCalculations.appendData({ type: "Operator", value: "+" }); heightCalculations.appendData({ type: "WhiteSpace", value: " " }); heightCalculations.appendData({ type: "Dimension", unit: bleed.top.unit, value: bleed.top.value }); heightCalculations.appendData({ type: "WhiteSpace", value: " " }); heightCalculations.appendData({ type: "Operator", value: "+" }); heightCalculations.appendData({ type: "WhiteSpace", value: " " }); heightCalculations.appendData({ type: "Dimension", unit: bleed.bottom.unit, value: bleed.bottom.value }); dimensions.appendData({ type: "Function", name: "calc", children: widthCalculations }); dimensions.appendData({ type: "WhiteSpace", value: " " }); dimensions.appendData({ type: "Function", name: "calc", children: heightCalculations }); } else if (format) { dimensions.appendData({ type: "Identifier", name: format }); if (orientation) { dimensions.appendData({ type: "WhiteSpace", value: " " }); dimensions.appendData({ type: "Identifier", name: orientation }); } } else { dimensions.appendData({ type: "Dimension", unit: width.unit, value: width.value }); dimensions.appendData({ type: "WhiteSpace", value: " " }); dimensions.appendData({ type: "Dimension", unit: height.unit, value: height.value }); } children.appendData({ type: "Declaration", property: "size", loc: null, value: { type: "Value", children: dimensions } }); children.appendData({ type: "Declaration", property: "margin", loc: null, value: { type: "Value", children: [{ type: "Dimension", unit: "px", value: 0 }] } }); children.appendData({ type: "Declaration", property: "padding", loc: null, value: { type: "Value", children: [{ type: "Dimension", unit: "px", value: 0 }] } }); children.appendData({ type: "Declaration", property: "padding", loc: null, value: { type: "Value", children: [{ type: "Dimension", unit: "px", value: 0 }] } }); let rule = ast.children.createItem({ type: "Atrule", prelude: null, name: "page", block: { type: "Block", loc: null, children: children } }); ast.children.append(rule); if (bleedverso) { let widthCalculationsLeft = new csstree.List(); let heightCalculationsLeft = new csstree.List(); // width widthCalculationsLeft.appendData({ type: "Dimension", unit: width.unit, value: width.value }); widthCalculationsLeft.appendData({ type: "WhiteSpace", value: " " }); widthCalculationsLeft.appendData({ type: "Operator", value: "+" }); widthCalculationsLeft.appendData({ type: "WhiteSpace", value: " " }); widthCalculationsLeft.appendData({ type: "Dimension", unit: bleedverso.left.unit, value: bleedverso.left.value }); widthCalculationsLeft.appendData({ type: "WhiteSpace", value: " " }); widthCalculationsLeft.appendData({ type: "Operator", value: "+" }); widthCalculationsLeft.appendData({ type: "WhiteSpace", value: " " }); widthCalculationsLeft.appendData({ type: "Dimension", unit: bleedverso.right.unit, value: bleedverso.right.value }); // height heightCalculationsLeft.appendData({ type: "Dimension", unit: height.unit, value: height.value }); heightCalculationsLeft.appendData({ type: "WhiteSpace", value: " " }); heightCalculationsLeft.appendData({ type: "Operator", value: "+" }); heightCalculationsLeft.appendData({ type: "WhiteSpace", value: " " }); heightCalculationsLeft.appendData({ type: "Dimension", unit: bleedverso.top.unit, value: bleedverso.top.value }); heightCalculationsLeft.appendData({ type: "WhiteSpace", value: " " }); heightCalculationsLeft.appendData({ type: "Operator", value: "+" }); heightCalculationsLeft.appendData({ type: "WhiteSpace", value: " " }); heightCalculationsLeft.appendData({ type: "Dimension", unit: bleedverso.bottom.unit, value: bleedverso.bottom.value }); dimensionsLeft.appendData({ type: "Function", name: "calc", children: widthCalculationsLeft }); dimensionsLeft.appendData({ type: "WhiteSpace", value: " " }); dimensionsLeft.appendData({ type: "Function", name: "calc", children: heightCalculationsLeft }); childrenLeft.appendData({ type: "Declaration", property: "size", loc: null, value: { type: "Value", children: dimensionsLeft } }); let ruleLeft = ast.children.createItem({ type: "Atrule", prelude: null, name: "page :left", block: { type: "Block", loc: null, children: childrenLeft } }); ast.children.append(ruleLeft); } if (bleedrecto) { let widthCalculationsRight = new csstree.List(); let heightCalculationsRight = new csstree.List(); // width widthCalculationsRight.appendData({ type: "Dimension", unit: width.unit, value: width.value }); widthCalculationsRight.appendData({ type: "WhiteSpace", value: " " }); widthCalculationsRight.appendData({ type: "Operator", value: "+" }); widthCalculationsRight.appendData({ type: "WhiteSpace", value: " " }); widthCalculationsRight.appendData({ type: "Dimension", unit: bleedrecto.left.unit, value: bleedrecto.left.value }); widthCalculationsRight.appendData({ type: "WhiteSpace", value: " " }); widthCalculationsRight.appendData({ type: "Operator", value: "+" }); widthCalculationsRight.appendData({ type: "WhiteSpace", value: " " }); widthCalculationsRight.appendData({ type: "Dimension", unit: bleedrecto.right.unit, value: bleedrecto.right.value }); // height heightCalculationsRight.appendData({ type: "Dimension", unit: height.unit, value: height.value }); heightCalculationsRight.appendData({ type: "WhiteSpace", value: " " }); heightCalculationsRight.appendData({ type: "Operator", value: "+" }); heightCalculationsRight.appendData({ type: "WhiteSpace", value: " " }); heightCalculationsRight.appendData({ type: "Dimension", unit: bleedrecto.top.unit, value: bleedrecto.top.value }); heightCalculationsRight.appendData({ type: "WhiteSpace", value: " " }); heightCalculationsRight.appendData({ type: "Operator", value: "+" }); heightCalculationsRight.appendData({ type: "WhiteSpace", value: " " }); heightCalculationsRight.appendData({ type: "Dimension", unit: bleedrecto.bottom.unit, value: bleedrecto.bottom.value }); dimensionsRight.appendData({ type: "Function", name: "calc", children: widthCalculationsRight }); dimensionsRight.appendData({ type: "WhiteSpace", value: " " }); dimensionsRight.appendData({ type: "Function", name: "calc", children: heightCalculationsRight }); childrenRight.appendData({ type: "Declaration", property: "size", loc: null, value: { type: "Value", children: dimensionsRight } }); let ruleRight = ast.children.createItem({ type: "Atrule", prelude: null, name: "page :right", block: { type: "Block", loc: null, children: childrenRight } }); ast.children.append(ruleRight); } } getNth(nth) { let n = nth.indexOf("n"); let plus = nth.indexOf("+"); let splitN = nth.split("n"); let splitP = nth.split("+"); let a = null; let b = null; if (n > -1) { a = splitN[0]; if (plus > -1) { b = splitP[1]; } } else { b = nth; } return { type: "Nth", loc: null, selector: null, nth: { type: "AnPlusB", loc: null, a: a, b: b } }; } addPageAttributes(page, start, pages) { let namedPages = [start.dataset.page]; if (namedPages && namedPages.length) { for (const named of namedPages) { if (!named) { continue; } page.name = named; page.element.classList.add("pagedjs_named_page"); page.element.classList.add("pagedjs_" + named + "_page"); if (!start.dataset.splitFrom) { page.element.classList.add("pagedjs_" + named + "_first_page"); } } } } getStartElement(content, breakToken) { let node = breakToken && breakToken.node; if (!content && !breakToken) { return; } // No break if (!node) { return content.children[0]; } // Top level element if (node.nodeType === 1 && node.parentNode.nodeType === 11) { return node; } // Named page if (node.nodeType === 1 && node.dataset.page) { return node; } // Get top level Named parent let fragment = rebuildAncestors(node); let pages = fragment.querySelectorAll("[data-page]"); if (pages.length) { return pages[pages.length - 1]; } else { return fragment.children[0]; } } beforePageLayout(page, contents, breakToken, chunker) { let start = this.getStartElement(contents, breakToken); if (start) { this.addPageAttributes(page, start, chunker.pages); } // page.element.querySelector('.paged_area').style.color = red; } finalizePage(fragment, page, breakToken, chunker) { for (let m in this.marginalia) { let margin = this.marginalia[m]; let sels = m.split(" "); let content; if (page.element.matches(sels[0]) && margin.hasContent) { content = page.element.querySelector(sels[1]); content.classList.add("hasContent"); } } // check center ["top", "bottom"].forEach((loc) => { let marginGroup = page.element.querySelector(".pagedjs_margin-" + loc); let center = page.element.querySelector(".pagedjs_margin-" + loc + "-center"); let left = page.element.querySelector(".pagedjs_margin-" + loc + "-left"); let right = page.element.querySelector(".pagedjs_margin-" + loc + "-right"); let centerContent = center.classList.contains("hasContent"); let leftContent = left.classList.contains("hasContent"); let rightContent = right.classList.contains("hasContent"); let centerWidth, leftWidth, rightWidth; if (leftContent) { leftWidth = window.getComputedStyle(left)["max-width"]; } if (rightContent) { rightWidth = window.getComputedStyle(right)["max-width"]; } if (centerContent) { centerWidth = window.getComputedStyle(center)["max-width"]; if (centerWidth === "none" || centerWidth === "auto") { if (!leftContent && !rightContent) { marginGroup.style["grid-template-columns"] = "0 1fr 0"; } else if (leftContent) { if (!rightContent) { if (leftWidth !== "none" && leftWidth !== "auto") { marginGroup.style["grid-template-columns"] = leftWidth + " 1fr " + leftWidth; } else { marginGroup.style["grid-template-columns"] = "auto auto 1fr"; left.style["white-space"] = "nowrap"; center.style["white-space"] = "nowrap"; let leftOuterWidth = left.offsetWidth; let centerOuterWidth = center.offsetWidth; let outerwidths = leftOuterWidth + centerOuterWidth; let newcenterWidth = centerOuterWidth * 100 / outerwidths; marginGroup.style["grid-template-columns"] = "minmax(16.66%, 1fr) minmax(33%, " + newcenterWidth + "%) minmax(16.66%, 1fr)"; left.style["white-space"] = "normal"; center.style["white-space"] = "normal"; } } else { if (leftWidth !== "none" && leftWidth !== "auto") { if (rightWidth !== "none" && rightWidth !== "auto") { marginGroup.style["grid-template-columns"] = leftWidth + " 1fr " + rightWidth; } else { marginGroup.style["grid-template-columns"] = leftWidth + " 1fr " + leftWidth; } } else { if (rightWidth !== "none" && rightWidth !== "auto") { marginGroup.style["grid-template-columns"] = rightWidth + " 1fr " + rightWidth; } else { marginGroup.style["grid-template-columns"] = "auto auto 1fr"; left.style["white-space"] = "nowrap"; center.style["white-space"] = "nowrap"; right.style["white-space"] = "nowrap"; let leftOuterWidth = left.offsetWidth; let centerOuterWidth = center.offsetWidth; let rightOuterWidth = right.offsetWidth; let outerwidths = leftOuterWidth + centerOuterWidth + rightOuterWidth; let newcenterWidth = centerOuterWidth * 100 / outerwidths; if (newcenterWidth > 40) { marginGroup.style["grid-template-columns"] = "minmax(16.66%, 1fr) minmax(33%, " + newcenterWidth + "%) minmax(16.66%, 1fr)"; } else { marginGroup.style["grid-template-columns"] = "repeat(3, 1fr)"; } left.style["white-space"] = "normal"; center.style["white-space"] = "normal"; right.style["white-space"] = "normal"; } } } } else { if (rightWidth !== "none" && rightWidth !== "auto") { marginGroup.style["grid-template-columns"] = rightWidth + " 1fr " + rightWidth; } else { marginGroup.style["grid-template-columns"] = "auto auto 1fr"; right.style["white-space"] = "nowrap"; center.style["white-space"] = "nowrap"; let rightOuterWidth = right.offsetWidth; let centerOuterWidth = center.offsetWidth; let outerwidths = rightOuterWidth + centerOuterWidth; let newcenterWidth = centerOuterWidth * 100 / outerwidths; marginGroup.style["grid-template-columns"] = "minmax(16.66%, 1fr) minmax(33%, " + newcenterWidth + "%) minmax(16.66%, 1fr)"; right.style["white-space"] = "normal"; center.style["white-space"] = "normal"; } } } else if (centerWidth !== "none" && centerWidth !== "auto") { if (leftContent && leftWidth !== "none" && leftWidth !== "auto") { marginGroup.style["grid-template-columns"] = leftWidth + " " + centerWidth + " 1fr"; } else if (rightContent && rightWidth !== "none" && rightWidth !== "auto") { marginGroup.style["grid-template-columns"] = "1fr " + centerWidth + " " + rightWidth; } else { marginGroup.style["grid-template-columns"] = "1fr " + centerWidth + " 1fr"; } } } else { if (leftContent) { if (!rightContent) { marginGroup.style["grid-template-columns"] = "1fr 0 0"; } else { if (leftWidth !== "none" && leftWidth !== "auto") { if (rightWidth !== "none" && rightWidth !== "auto") { marginGroup.style["grid-template-columns"] = leftWidth + " 1fr " + rightWidth; } else { marginGroup.style["grid-template-columns"] = leftWidth + " 0 1fr"; } } else { if (rightWidth !== "none" && rightWidth !== "auto") { marginGroup.style["grid-template-columns"] = "1fr 0 " + rightWidth; } else { marginGroup.style["grid-template-columns"] = "auto 1fr auto"; left.style["white-space"] = "nowrap"; right.style["white-space"] = "nowrap"; let leftOuterWidth = left.offsetWidth; let rightOuterWidth = right.offsetWidth; let outerwidths = leftOuterWidth + rightOuterWidth; let newLeftWidth = leftOuterWidth * 100 / outerwidths; marginGroup.style["grid-template-columns"] = "minmax(16.66%, " + newLeftWidth + "%) 0 1fr"; left.style["white-space"] = "normal"; right.style["white-space"] = "normal"; } } } } else { if (rightWidth !== "none" && rightWidth !== "auto") { marginGroup.style["grid-template-columns"] = "1fr 0 " + rightWidth; } else { marginGroup.style["grid-template-columns"] = "0 0 1fr"; } } } }); // check middle ["left", "right"].forEach((loc) => { let middle = page.element.querySelector(".pagedjs_margin-" + loc + "-middle.hasContent"); let marginGroup = page.element.querySelector(".pagedjs_margin-" + loc); let top = page.element.querySelector(".pagedjs_margin-" + loc + "-top"); let bottom = page.element.querySelector(".pagedjs_margin-" + loc + "-bottom"); let topContent = top.classList.contains("hasContent"); let bottomContent = bottom.classList.contains("hasContent"); let middleHeight, topHeight, bottomHeight; if (topContent) { topHeight = window.getComputedStyle(top)["max-height"]; } if (bottomContent) { bottomHeight = window.getComputedStyle(bottom)["max-height"]; } if (middle) { middleHeight = window.getComputedStyle(middle)["max-height"]; if (middleHeight === "none" || middleHeight === "auto") { if (!topContent && !bottomContent) { marginGroup.style["grid-template-rows"] = "0 1fr 0"; } else if (topContent) { if (!bottomContent) { if (topHeight !== "none" && topHeight !== "auto") { marginGroup.style["grid-template-rows"] = topHeight + " calc(100% - " + topHeight + "*2) " + topHeight; } } else { if (topHeight !== "none" && topHeight !== "auto") { if (bottomHeight !== "none" && bottomHeight !== "auto") { marginGroup.style["grid-template-rows"] = topHeight + " calc(100% - " + topHeight + " - " + bottomHeight + ") " + bottomHeight; } else { marginGroup.style["grid-template-rows"] = topHeight + " calc(100% - " + topHeight + "*2) " + topHeight; } } else { if (bottomHeight !== "none" && bottomHeight !== "auto") { marginGroup.style["grid-template-rows"] = bottomHeight + " calc(100% - " + bottomHeight + "*2) " + bottomHeight; } } } } else { if (bottomHeight !== "none" && bottomHeight !== "auto") { marginGroup.style["grid-template-rows"] = bottomHeight + " calc(100% - " + bottomHeight + "*2) " + bottomHeight; } } } else { if (topContent && topHeight !== "none" && topHeight !== "auto") { marginGroup.style["grid-template-rows"] = topHeight + " " + middleHeight + " calc(100% - (" + topHeight + " + " + middleHeight + "))"; } else if (bottomContent && bottomHeight !== "none" && bottomHeight !== "auto") { marginGroup.style["grid-template-rows"] = "1fr " + middleHeight + " " + bottomHeight; } else { marginGroup.style["grid-template-rows"] = "calc((100% - " + middleHeight + ")/2) " + middleHeight + " calc((100% - " + middleHeight + ")/2)"; } } } else { if (topContent) { if (!bottomContent) { marginGroup.style["grid-template-rows"] = "1fr 0 0"; } else { if (topHeight !== "none" && topHeight !== "auto") { if (bottomHeight !== "none" && bottomHeight !== "auto") { marginGroup.style["grid-template-rows"] = topHeight + " 1fr " + bottomHeight; } else { marginGroup.style["grid-template-rows"] = topHeight + " 0 1fr"; } } else { if (bottomHeight !== "none" && bottomHei