From 259a5399bf436d5f2251aad234d4e36b1e2ade50 Mon Sep 17 00:00:00 2001 From: Stanislav Pankevich Date: Sun, 2 Mar 2025 09:43:45 +0100 Subject: [PATCH] Update HTML2PDF.js to 0.2.1, add a testable and observable Dockerfile --- .github/workflows/docker.yml | 31 +++++++++ .gitignore | 3 +- Dockerfile | 55 ++++++++++++++++ html2print/html2pdf_js/html2pdf.min.js | 2 + html2print/html2print.py | 33 ++++++++-- tasks.py | 64 ++++++++++++++++++- tests/integration/01_hello_world/index.html | 4 +- tests/integration/02_two_pages/index.html | 4 +- .../03_cache_dir_argument/index.html | 4 +- .../integration/04_two_documents/index1.html | 4 +- .../integration/04_two_documents/index2.html | 4 +- .../05_page_load_timeout/index1.html | 4 +- tests/integration/main.css | 53 +++++++++++++++ 13 files changed, 246 insertions(+), 19 deletions(-) create mode 100644 .github/workflows/docker.yml create mode 100644 Dockerfile create mode 100644 html2print/html2pdf_js/html2pdf.min.js create mode 100644 tests/integration/main.css diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..b9acc53 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,31 @@ +name: HTML2Print Docker CI + +on: + pull_request: + branches: [ "**" ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Upgrade pip + run: | + python -m pip install --upgrade pip + + - name: Install Python packages + run: | + pip install -r requirements.development.txt + + - name: Build Docker image with PR branch + run: | + invoke build-docker \ + --image pr-${{ github.event.pull_request.number }} \ + --source=${{ github.event.pull_request.head.sha }} + + - name: Run container and test StrictDoc installation + run: | + invoke test-docker \ + --image pr-${{ github.event.pull_request.number }} diff --git a/.gitignore b/.gitignore index 9a07f38..ff8b010 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ # HTML2PDF JS file. -html2print/html2pdf_js/ +# html2print/html2pdf_js/ .idea/ **/.wdm/ @@ -7,4 +7,5 @@ build/ dist/ tests/integration/.lit_test_times.txt tests/integration/**/Output/ +output/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..446a290 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,55 @@ +FROM ubuntu:24.04 + +# Install dependencies +RUN apt-get update && apt-get install -y \ + curl \ + git \ + python3 \ + python3-pip \ + python3-venv \ + sudo \ + vim \ + wget \ + && rm -rf /var/lib/apt/lists/* + +# Download and install Google Chrome +RUN wget -q -O google-chrome.deb https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb \ + && apt-get update \ + && apt-get install -y ./google-chrome.deb \ + && rm google-chrome.deb + +# Create a new non-root user and group. +# NOTE: It is important that a non-root user is used because otherwise the +# Chrome Driver fails with: "User data directory is already in use" +# https://github.com/SeleniumHQ/selenium/issues/15327#issuecomment-2688613182 +RUN groupadd -r html2print && useradd -r -m -g html2print html2print + +# Grant the new user sudo privileges. +RUN echo "html2print ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/html2print + +# Create a virtual environment in the user's home directory. +RUN python3 -m venv /opt/venv + +# Ensure the virtual environment is used by modifying the PATH. +ENV PATH="/opt/venv/bin:$PATH" + +# Install StrictDoc. Set default StrictDoc installation from PyPI but allow +# overriding it with an environment variable. +ARG HTML2PRINT_SOURCE="pypi" +ENV HTML2PRINT_SOURCE=${HTML2PRINT_SOURCE} + +RUN if [ "$HTML2PRINT_SOURCE" = "pypi" ]; then \ + pip install --no-cache-dir --upgrade pip && \ + pip install --no-cache-dir html2print; \ + else \ + pip install --no-cache-dir --upgrade pip && \ + pip install --no-cache-dir git+https://github.com/mettta/html2pdf_python.git@${HTML2PRINT_SOURCE}; \ + fi; \ + chmod -R 777 /opt/venv; + +USER html2print + +# Set the working directory to the user's home directory. +WORKDIR /data + +ENTRYPOINT ["/bin/bash"] diff --git a/html2print/html2pdf_js/html2pdf.min.js b/html2print/html2pdf_js/html2pdf.min.js new file mode 100644 index 0000000..79eb172 --- /dev/null +++ b/html2print/html2pdf_js/html2pdf.min.js @@ -0,0 +1,2 @@ +/*! Version: 0.2.1 */ +var HTML2PDF4DOC;(()=>{"use strict";var e={d:(t,o)=>{for(var n in o)e.o(o,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:o[n]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};e.r(t),e.d(t,{init:()=>k});const o={init:"[html2pdf]",pageDivider:"html2pdf-page",pageStartMarker:"[html2pdf-page-start]",contentFlowStart:"html2pdf-content-flow-start",contentFlowEnd:"html2pdf-content-flow-end",style:"[html2pdf-style]",footerTemplate:"[html2pdf-footer]",headerTemplate:"[html2pdf-header]",frontpageTemplate:"[html2pdf-frontpage]",frontpageContent:"html2pdf-frontpage",headerContent:"html2pdf-header",footerContent:"html2pdf-footer",pageNumberRoot:"[html2pdf-page-number]",pageNumberCurrent:"[html2pdf-page-number-current]",pageNumberTotal:"[html2pdf-page-number-total]",root:"html2pdf-root",paperFlow:"html2pdf-paper-flow",contentFlow:"html2pdf-content-flow",virtualPaper:"html2pdf-virtual-paper",virtualPaperTopMargin:"html2pdf-virtual-paper-margin-top",virtualPaperBottomMargin:"html2pdf-virtual-paper-margin-bottom",virtualPaperGap:"html2pdf-virtual-paper-gap",paperBody:"html2pdf-paper-body",paperHeader:"html2pdf-paper-header",paperFooter:"html2pdf-paper-footer",runningSafety:"html2pdf-print-running",printPageBreak:"html2pdf-print-page-break",printIgnore:"[html2pdf-print-ignore]",printHide:"[html2pdf-print-hide]",neutral:"html2pdf-neutral",word:"html2pdf-word",textNode:"html2pdf-text-node",textLine:"html2pdf-text-line",textGroup:"html2pdf-text-group",complexTextBlock:"html2pdf-complex-text-block",printForcedPageBreak:"html2pdf-print-forced-page-break",splitted:"[html2pdf-splitted]",processed:"[html2pdf-processed]",flagNoBreak:"[html2pdf-flag-no-break]",flagNoHanging:"[html2pdf-flag-no-hanging]",topCutPart:".html2pdf-top-cut",bottomCutPart:".html2pdf-bottom-cut",tocPageNumber:"html2pdf-toc-page-number"};function n(e){let t={debugMode:!1,preloader:!1,preloaderTarget:"",preloaderBackground:"",mask:!1,noHangingSelectors:"",forcedPageBreakSelectors:"",pageBreakBeforeSelectors:"",pageBreakAfterSelectors:"",noBreakSelectors:"",tocPageNumberSelector:"html2pdf-toc-page-number",printLeftMargin:"21mm",printRightMargin:"21mm",printTopMargin:"12mm",printBottomMargin:"12mm",printFontSize:"12pt",printWidth:"210mm",printHeight:"297mm",headerMargin:"16px",footerMargin:"16px",virtualPagesGap:"16px"};const n={printWidth:"210mm",printHeight:"297mm"},i={printWidth:"148.5mm",printHeight:"210mm"};switch(e.printPaperSize){case"A5":case"a5":t={...t,...i};break;default:t={...t,...n}}t={...t,initialRoot:o.init,tocPageNumberSelector:o.tocPageNumber,...e},console.info("HTML2PDF4DOC config:",t);const s={printLeftMargin:t.printLeftMargin,printRightMargin:t.printRightMargin,printTopMargin:t.printTopMargin,printBottomMargin:t.printBottomMargin,printFontSize:t.printFontSize,printWidth:t.printWidth,printHeight:t.printHeight,headerMargin:t.headerMargin,footerMargin:t.footerMargin,virtualPagesGap:t.virtualPagesGap},r=document.createElement("div");return r.style="\n position:absolute;\n z-index:1000;\n left: 200%;\n ",document.body.append(r),Object.entries(s).forEach((([e,t])=>{r.style.width=t,s[e]=`${Math.trunc(r.getBoundingClientRect().width)}px`})),r.remove(),t={...t,...s},t.noHangingSelectors=t.noHangingSelectors+" H1 H2 H3 H4 H5 H6",t.forcedPageBreakSelectors=t.forcedPageBreakSelectors+" "+o.printForcedPageBreak,console.info("HTML2PDF4DOC config with converted units:",t),t}const i={DOM:{_:!1},layout:{_:!0},pages:{_:!0,_parseNode:!1,_parseNodes:!1,_registerPageStart:!1,_getProcessedChildren:!1,_splitPreNode:!1,_splitTableNode:!1,_splitTableLikeNode:!1,_splitTableRow:!1,_splitGridNode:!1,_createSlicesBySplitFlag:!1,_getInternalBlockSplitters:!1},paragraph:{_:!1},node:{_:!1},paper:{_:!1},preview:{_:!1},toc:{_:!1}};class s{constructor({DOM:e,config:t}){this.document=e,this.body=e.body,this._debugMode=t.debugMode,this._debug=t.debugMode?{...t.debugConfig.DOM}:{}}createElement(e){return this.document.createElement(e)}createDocumentFragment(){return this.document.createDocumentFragment()}cloneNode(e){return e?.cloneNode(!0)}cloneNodeWrapper(e){return e?.cloneNode(!1)}insertBefore(e,...t){e.before(...t)}insertAfter(e,...t){e.after(...t)}insertAtEnd(e,...t){e.append(...t)}insertAtStart(e,...t){e.prepend(...t)}insertInsteadOf(e,...t){e.before(...t),e.remove()}moveContent(e,t){for(;e.firstChild;)t.append(e.firstChild);console.assert(""===this.getInnerHTML(e))}removeNode(e){e.remove()}getElement(e,t=this.document){return t.querySelector(e)}getAllElements(e,t=this.document){return t.querySelectorAll(e)}getElementById(e,t=this.document){return t.getElementById(e)}getRightNeighbor(e){return e.nextElementSibling}getLeftNeighbor(e){return e.previousElementSibling}getParentNode(e){return e.parentElement}getNodeValue(e){return e.nodeValue}getLastElementChild(e){return e.lastElementChild}getFirstElementChild(e){return e.firstElementChild}getChildNodes(e){return e.childNodes}getChildren(e){return e.children}getElementOffsetParent(e){return e.offsetParent}getComputedStyle(e){return window.getComputedStyle(e)}getElementBCR(e){return e.getBoundingClientRect()}getElementOffsetLeft(e){return e?.offsetLeft}getElementOffsetHeight(e){return e?.offsetHeight}getElementOffsetWidth(e){return e?.offsetWidth}getElementOffsetTop(e){return e?.offsetTop}getElementOffsetBottom(e){return e.offsetTop+e.offsetHeight||void 0}getElementTagName(e){return e.tagName}getDataId(e){return e.dataset.id}getAttribute(e,t){if(!e||!t)return void(this._debug._&&console.warn("getAttribute() must have 2 params"));const o=t.charAt(0);if("."!==o&&"#"!==o||this._debug._&&console.log(`you're really sure ${t} is attribute selector?`),"["===o){this._debug._&&console.assert("]"===t.at(-1),`the ${t} selector is not OK.`);const o=t.substring(1,t.length-1);return e.getAttribute(o)}e.getAttribute(t)}setAttribute(e,t,o){if(!e||!t)return void(this._debug._&&console.warn("setAttribute() must have 2 params"));const n=t.charAt(0);if("."!==n)if("#"!==n)if("["!==n)this._debug._&&console.log(`you're really sure ${t} is a selector?`);else{this._debug._&&console.assert("]"===t.at(-1),`the ${t} selector is not OK.`);const n=t.substring(1,t.length-1);e.setAttribute(n,o||"")}else{const o=t.substring(1);e.id=o}else{const o=t.substring(1);e.classList.add(o)}}setStyles(e,t){Object.entries(t).forEach((([t,o])=>e.style[t]=o))}addClasses(e,...t){e.classList.add(...t)}removeAttribute(e,t){if(!e||!t)return void(this._debug._&&console.warn("removeAttribute() must have 2 params"));const o=t.charAt(0);if(console.assert(o.match(/[a-zA-Z#\[\.]/),`removeAttribute() expects a valid selector, but received ${t}`),"."!==o)if("#"!==o)if("["!==o)e.removeAttribute(attr);else{this._debug._&&console.assert("]"===t.at(-1),`the ${t} selector is not OK.`);const o=t.substring(1,t.length-1);e.removeAttribute(o)}else{const o=t.substring(1);e.removeAttribute(o)}else{const o=t.substring(1);e.classList.remove(o)}}removeAllAttributes(e){for(;e.attributes.length>0;)e.removeAttribute(e.attributes[0].name)}removeClasses(e,...t){e.classList.remove(...t)}removeAllClasses(e){e.classList=""}removeAllStyles(e){e.style=""}getInnerHTML(e){if("string"==typeof e){const t=this.document.querySelector(e);return t?t.innerHTML:void 0}return e.innerHTML}setInnerHTML(e,t){if("string"==typeof e){const o=this.document.querySelector(e);o&&(o.innerHTML=t)}e.innerHTML=t}isDocumentBody(e){return"BODY"===e.tagName}isTextNode(e){return e.nodeType===Node.TEXT_NODE}isElementNode(e){return e.nodeType===Node.ELEMENT_NODE}hasClass(e,t){return e.classList.contains(t)}hasID(e,t){return e.id===t}hasAttribute(e,t){return e.hasAttribute(t)}}class r{constructor(e){this.config=e,this.charWidth="10px"}create(){return this._baseStyle()+this._testStyle()}_baseStyle(){return`\n\n@page {\n size: A4;\n /* 2 values: width then height */\n size: ${this.config.printWidth} ${this.config.printHeight};\n\n margin-left: ${this.config.printLeftMargin};\n margin-right: ${this.config.printRightMargin};\n margin-top: ${this.config.printTopMargin};\n margin-bottom: 0; /* hack */\n}\n\n${o.root} {\n /* reset user styles */\n display: block;\n\n /* for proper printable flow positioning */\n position: relative;\n\n /* to compensate for possible BG in the parent node */\n z-index: 1;\n\n /* set print styles: affects previews */\n margin: 0 auto;\n width: calc(${this.config.printWidth} - ${this.config.printLeftMargin} - ${this.config.printRightMargin});\n font-size: ${this.config.printFontSize};\n\n /* protection against unpredictability of margins */\n padding-top: .1px;\n padding-bottom: calc(2 * ${this.config.virtualPagesGap});\n}\n\n${o.contentFlowStart},\n${o.contentFlowEnd},\n${o.pageDivider} {\n display: block;\n}\n\n${o.virtualPaper} {\n display: grid;\n grid-template-columns: 1fr;\n grid-template-rows: minmax(min-content, max-content) minmax(min-content, max-content) 1fr minmax(min-content, max-content) minmax(min-content, max-content);\n place-items: stretch stretch;\n place-content: stretch stretch;\n width: calc(${this.config.printWidth} - ${this.config.printLeftMargin} - ${this.config.printRightMargin});\n height: ${this.config.printHeight};\n font-size: ${this.config.printFontSize};\n}\n\n${o.virtualPaper}::before {\n position: absolute;\n content: '';\n width: ${this.config.printWidth};\n height: ${this.config.printHeight};\n left: -${this.config.printLeftMargin};\n background-color: #fff;\n box-shadow: rgba(0, 0, 0, 0.1) 2px 2px 12px 0px;\n z-index: -1;\n}\n\n${o.paperFooter},\n${o.paperHeader} {\n display: block;\n position: relative;\n}\n\n${o.headerContent},\n${o.footerContent} {\n display: block;\n font-size: small;\n}\n\n${o.headerContent} p,\n${o.footerContent} p {\n margin: 0;\n}\n\n${o.headerContent} {\n padding-bottom: ${this.config.headerMargin};\n /* padding-top: 1px; */\n /* Page numbers: */\n padding-top: 10px;\n}\n\n${o.footerContent} {\n padding-top: ${this.config.footerMargin};\n /* padding-bottom: 1px; */\n /* Page numbers: */\n min-height: 32px;\n}\n\n${o.tocPageNumber} {\n min-width: 3ch;\n display: flex;\n justify-content: flex-end;\n align-items: baseline;\n}\n\n${o.pageNumberRoot} {\n display: flex;\n column-gap: 2px;\n position: absolute;\n /* left: 100%; */\n right: 0;\n text-align: right;\n line-height: 1;\n}\n\n${o.headerContent} ${o.pageNumberRoot} {\n top: 0;\n}\n\n${o.footerContent} ${o.pageNumberRoot} {\n bottom: 0;\n}\n\n${o.paperFlow} {\n display: block;\n position: absolute;\n width: 100%;\n z-index: -1;\n /* affect only screen */\n padding-bottom: 100px;\n}\n\n${o.contentFlow} {\n display: block;\n}\n\n${o.runningSafety} {\n display: block;\n /* ? should be checked and updated,\n but in the meantime, bring back the common solution:\n firefox ignores 0.1px size, so it's necessary to make a full-size pixel\n and take it into account in the calculations:\n padding-top: 1px;\n */\n padding-top: .1px;\n}\n\n${o.virtualPaperTopMargin} {\n display: block;\n height: ${this.config.printTopMargin};\n}\n\n${o.virtualPaperBottomMargin} {\n display: block;\n height: ${this.config.printBottomMargin};\n}\n\n${o.virtualPaperGap} {\n display: block;\n padding-top: ${this.config.virtualPagesGap};\n}\n\n${o.paperBody} {\n display: block;\n}\n\n${o.frontpageContent} {\n display: block;\n transform-origin: top center;\n padding: .1px;\n height: 100%;\n}\n\n.null {\n display: inline;\n padding: 0;\n margin: 0;\n font: 0;\n color: transparent;\n line-height: 0;\n border: none;\n outline: none;\n background: none;\n background-color: transparent;\n}\n\n${o.word},\n${o.textNode},\n${o.textLine},\n${o.textGroup},\n${o.neutral},\n${o.neutral} span {\n display: inline;\n padding: 0;\n margin: 0;\n font: inherit;\n color: inherit;\n line-height: inherit;\n background: none;\n background-color: transparent;\n}\n\n${o.textGroup} {\n display: block;\n}\n\n/*${o.splitted} ${o.textGroup} {\n display: inline;\n}*/\n\n${o.complexTextBlock} > ${o.textLine} {\n /* Firefox and inconsistent values of offset top for inline element */\n display: inline-block;\n}\n\n${o.textGroup} ${o.textLine} {\n display: inline;\n}\n\n${o.complexTextBlock} {\n display: block;\n}\n\n${o.complexTextBlock} ${o.complexTextBlock} {\n display: inline;\n}\n\n${o.printPageBreak} {\n display: block;\n}\n\n${o.printForcedPageBreak} {\n display: block;\n visibility: hidden;\n height: 0;\n overflow: hidden;\n}\n\n@media print {\n ${o.root} {\n /* to prevent a blank last page */\n padding: 0;\n }\n\n ${o.paperFlow} {\n padding-bottom: 0;\n }\n\n ${o.contentFlow} {\n -webkit-mask-image: none !important;\n mask-image: none !important;\n }\n\n ${o.printIgnore} {\n display: contents;\n }\n\n ${o.printHide},\n ${o.virtualPaper}::before,\n ${o.virtualPaperTopMargin},\n ${o.virtualPaperBottomMargin},\n ${o.virtualPaperGap} {\n display: none;\n }\n\n ${o.virtualPaper} {\n break-inside: avoid;\n height: auto;\n }\n\n ${o.paperBody} {\n break-inside: avoid;\n }\n\n ${o.printPageBreak} {\n break-after: page;\n padding: .1px;\n }\n\n ${o.printForcedPageBreak} {\n /* JUST MANUAL! */\n /* break-after: page; */\n }\n\n ${o.flagNoBreak} {\n /*\n TODO: temporary commented!\n When splitting blocks, printPageBreak falls INTO this element,\n and in Firefox it causes a blank page.\n FIX the split of complex blocks and check in Firefox.\n */\n /* break-inside: avoid-page; */\n }\n}\n\n/* arrangement */\n${o.topCutPart} {\n margin-top: 0 !important;\n border-top: none !important;\n}\n${o.bottomCutPart} {\n margin-bottom: 0 !important;\n border-bottom: none !important;\n}\n `}_testStyle(){return this.config.debugMode?`\n/* FOR TEST */\n${o.virtualPaperGap} {\n background: #ff000020;\n}\n\n${o.paperFooter},\n${o.paperHeader} {\n background: #fa96ff20;\n}\n${o.paperBody} {\n background: #ffee0020;\n}\n${o.runningSafety} {\n background: #f200ff;\n}\n${o.frontpageContent} {\n background: #00fcff20;\n}\n\n${o.neutral} {\n background: #00ffee10;\n}\n\n${o.textNode} {\n background: #00ff0010;\n}\n\n${o.textGroup},\n${o.textLine} {\n background: #0000ff08;\n}\n\n `:""}}class l{constructor({config:e,DOM:t,node:o,selector:n}){this.success=!1,this.root,this.paperFlow,this.contentFlow,this.frontpageTemplate,this.headerTemplate,this.footerTemplate,this._initialRoot,this._contentRoot,this._config=e,this._debug=e.debugMode?{...e.debugConfig.layout}:{},this._DOM=t,this._selector=n,this._node=o,this._customInitialRootSelector=e.initialRoot,this._defaultInitialRootSelector=n.init}create(){if(this._getTemplates(),this._insertStyle(),this._DOM.getElement(`style${this._selector.style}`)){if(this._createLayout(),this._DOM.getParentNode(this.root)!==this._initialRoot||this._DOM.getElementOffsetParent(this.paperFlow)!==this.root||this._DOM.getElementOffsetParent(this.contentFlow)!==this.root)return console.assert(this._DOM.getParentNode(this.root)===this._initialRoot,"Failed to insert the layout root into the DOM."),console.assert(this._DOM.getElementOffsetParent(this.paperFlow)===this.root,"Failed to insert the paperFlow element into the DOM."),void console.assert(this._DOM.getElementOffsetParent(this.contentFlow)===this.root,"Failed to insert the contentFlow element into the DOM.");this.success=!0}else console.error("Failed to add print styles into the DOM.")}_getTemplates(){console.assert(this._selector.frontpageTemplate,"frontpageTemplate selector is missing"),console.assert(this._selector.headerTemplate,"headerTemplate selector is missing"),console.assert(this._selector.footerTemplate,"footerTemplate selector is missing"),this.frontpageTemplate=this._DOM.getInnerHTML(this._selector.frontpageTemplate),this.headerTemplate=this._DOM.getInnerHTML(this._selector.headerTemplate),this.footerTemplate=this._DOM.getInnerHTML(this._selector.footerTemplate)}_insertStyle(){const e=this._DOM.getElement("head"),t=this._DOM.body;if(!e&&!t)return void console.error("Check the structure of your document. We didn`t find HEAD and BODY tags. HTML2PDF4DOC expects valid HTML.");const o=this._node.create("style",new r(this._config).create());o?(this._DOM.setAttribute(o,this._selector.style,""),e?this._DOM.insertAtEnd(e,o):t?this._DOM.insertBefore(t,o):console.assert(!1,"We expected to find the HEAD and BODY tags.")):console.error("Failed to create print styles")}_createLayout(){this._getInitialRoot(),this._initialRoot?(this._debug._&&console.log("initial root:",this._initialRoot),this._createRoot(),this._createPaperFlow(),this._createContentFlow(),this._DOM.moveContent(this._initialRoot,this.contentFlow),this._DOM.insertAtEnd(this._initialRoot,this.root),this._DOM.insertAtEnd(this.root,this.paperFlow,this.contentFlow),this._insertContentFlowStartAndEnd(this.contentFlow),this._ignoreUnprintableEnvironment(this.root)):console.error("Failed to initialize the root element.")}_insertContentFlowStartAndEnd(e){const t=this._node.create(this._selector.contentFlowStart),o=this._node.create(this._selector.contentFlowEnd);return this._DOM.insertAtStart(e,t),this._DOM.insertAtEnd(e,o),{contentFlowStart:t,contentFlowEnd:o}}_getInitialRoot(){let e=this._customInitialRootSelector?this._DOM.getElement(this._customInitialRootSelector):this._DOM.getElement(this._defaultInitialRootSelector);if(!e){if(!this._DOM.body)return void console.error("We expected to find the BODY tag.");e=this._DOM.body,console.warn(`The printable area is currently unspecified and encompasses the entire contents of the BODY tag. To restrict the printed content to a specific area, include ${this._defaultInitialRootSelector} in the root element of the desired printing area.`)}return this._initialRoot=e,e}_createRoot(){const e=this._node.create(this._selector.root);return this.root=e,e}_createPaperFlow(){const e=this._node.create(this._selector.paperFlow);return this.paperFlow=e,e}_createContentFlow(){const e=this._node.create(this._selector.contentFlow);return this.contentFlow=e,e}_ignoreUnprintableEnvironment(e){if(e===this._DOM.body)return void console.assert(!1,"misshapen root");let t=this._DOM.getParentNode(e);this._DOM.setAttribute(t,this._selector.printIgnore),this._DOM.getChildNodes(t).forEach((t=>{if(t!==e&&this._DOM.isElementNode(t))this._DOM.setAttribute(t,this._selector.printHide);else{if(!this._node.isSignificantTextNode(t))return;this._DOM.setAttribute(this._node.wrapTextNode(t),this._selector.printHide)}})),this._DOM.isDocumentBody(t)||this._ignoreUnprintableEnvironment(t)}}class a{constructor({config:e,DOM:t,selector:o}){this._config=e,this._DOM=t,this._selector=o,this._debug=e.debugMode?{...e.debugConfig.node}:{},this._markupDebugMode=this._config.markupDebugMode}get(e,t=this._DOM){return console.assert(e),this._DOM.getElement(e,t)}getAll(e,t=this._DOM){return console.assert(e),"string"==typeof e&&(e=e.split(",").filter(Boolean)),console.assert(Array.isArray(e),"Selectors must be provided as an array or string (one selector or multiple selectors, separated by commas). Now the selectors are:",e),console.assert(e.length>0,"getAll(selectors), selectors:",e),1===e.length?[...this._DOM.getAllElements(e[0],t)]:[...e].flatMap((e=>[...this._DOM.getAllElements(e,t)]))}getTableEntries(e){const t=[...e.children].reduce(((e,t)=>{const o=t.tagName;return"TBODY"===o?{...e,rows:[...e.rows,...t.children]}:"CAPTION"===o?(this.setFlagNoBreak(t),{...e,caption:t}):"COLGROUP"===o?(this.setFlagNoBreak(t),{...e,colgroup:t}):"THEAD"===o?(this.setFlagNoBreak(t),{...e,thead:t}):"TFOOT"===o?(this.setFlagNoBreak(t),{...e,tfoot:t}):"TR"===o?{...e,rows:[...e.rows,...t]}:{...e,unexpected:[...e.unexpected,...t]}}),{caption:null,thead:null,tfoot:null,rows:[],unexpected:[]});return t.unexpected.length>0&&this._debug._&&console.warn(`something unexpected is found in the table ${e}`),t}getPreparedChildren(e){if(this.isComplexTextBlock(e))return[...this._DOM.getChildren(e)];{let t=[...this._DOM.getChildNodes(e)].reduce(((e,t)=>{if(this.isSTYLE(t))return e;if(this.isSignificantTextNode(t))return e.push(this.wrapTextNode(t)),e;if(!this._DOM.getElementOffsetParent(t)){const o=this.getPreparedChildren(t);return o.length>0&&e.push(...o),e}return this._DOM.isElementNode(t)?(e.push(t),e):void 0}),[]);return this.isVerticalFlowDisrupted(t)&&(t=this._processInlineChildren(t)),t}}_processInlineChildren(e){let t=null;const o=[];return e.forEach((e=>{this.isInline(this._DOM.getComputedStyle(e))?(t||(t=this.createComplexTextBlock(),this.wrapNode(e,t),o.push(t)),this._DOM.insertAtEnd(t,e)):(t=null,o.push(e))})),o}clearTemplates(e){this.getAll("template",e).forEach((e=>this._DOM.removeNode(e)))}isSelectorMatching(e,t){if(!e||!t)return void(this._debug._&&console.warn("isSelectorMatching() must have 2 params","\n element: ",e,"\n selector: ",t));const o=t.charAt(0);if("."===o){const o=t.substring(1);return this._DOM.hasClass(e,o)}if("#"===o){const o=t.substring(1);return this._DOM.hasID(e,o)}if("["===o){this._debug._&&console.assert("]"===t.at(-1),`the ${t} selector is not OK.`);const o=t.substring(1,t.length-1);return this._DOM.hasAttribute(e,o)}return this._DOM.getElementTagName(e)===t.toUpperCase()}isSignificantTextNode(e){return!!this._DOM.isTextNode(e)&&this._DOM.getNodeValue(e).trim().length>0}isSTYLE(e){return"STYLE"===this._DOM.getElementTagName(e)}isIMG(e){return"IMG"===this._DOM.getElementTagName(e)}isSVG(e){return"svg"===this._DOM.getElementTagName(e)}isOBJECT(e){return"OBJECT"===this._DOM.getElementTagName(e)}isLiNode(e){return"LI"===this._DOM.getElementTagName(e)}isNeutral(e){return this.isSelectorMatching(e,this._selector.neutral)}isWrappedTextNode(e){return this.isSelectorMatching(e,this._selector.textNode)}isWrappedTextLine(e){return this.isSelectorMatching(e,this._selector.textLine)}isWrappedTextGroup(e){return this.isSelectorMatching(e,this._selector.textGroup)}isPageStartElement(e){return this.isSelectorMatching(e,this._selector.pageStartMarker)}isContentFlowStart(e){return this.isSelectorMatching(e,this._selector.contentFlowStart)}isContentFlowEnd(e){return this.isSelectorMatching(e,this._selector.contentFlowEnd)}isComplexTextBlock(e){return this.isSelectorMatching(e,this._selector.complexTextBlock)}isNoBreak(e,t=this._DOM.getComputedStyle(e)){return this.isSelectorMatching(e,this._selector.flagNoBreak)||this.isWrappedTextLine(e)||this.isWrappedTextGroup(e)||this.isInlineBlock(t)||this.notSolved(e)}isNoHanging(e){return this.isSelectorMatching(e,this._selector.flagNoHanging)}isForcedPageBreak(e){return this.isSelectorMatching(e,this._selector.printForcedPageBreak)}isInline(e){const t=e.display;return"inline"===t||"inline-block"===t||"inline-table"===t||"inline-flex"===t||"inline-grid"===t}isInlineBlock(e){const t=e.display;return"inline-block"===t||"inline-table"===t||"inline-flex"===t||"inline-grid"===t}isGrid(e){return"grid"===e.display}isTableLikeNode(e,t=this._DOM.getComputedStyle(e)){return"TABLE"!==this._DOM.getElementTagName(e)&&["table"].includes(t.display)}isTableNode(e,t=this._DOM.getComputedStyle(e)){return"TABLE"===this._DOM.getElementTagName(e)||["table"].includes(t.display)}isPRE(e,t=this._DOM.getComputedStyle(e)){return["block"].includes(t.display)&&["pre","pre-wrap","pre-line","break-spaces","nowrap"].includes(t.whiteSpace)}isGridAutoFlowRow(e){const t=e.display,o=e.gridAutoFlow;return("grid"===t||"inline-grid"===t)&&"row"===o}isFullySPlitted(e){const t=this._DOM.getComputedStyle(e);return this.isPRE(e,t)||this.isTableNode(e,t)||this.isTableLikeNode(e,t)||this.isGridAutoFlowRow(t)}isSlough(e){return this._DOM.hasAttribute(e,"slough-node")}isFirstChildOfFirstChild(e,t){if(!e||!this._DOM.getParentNode(e))return!1;let o=e;for(;this._DOM.getParentNode(o)&&o!==t;){if(this._DOM.getFirstElementChild(this._DOM.getParentNode(o))!==o)return!1;o=this._DOM.getParentNode(o)}return o===t}isLastChildOfLastChild(e,t){if(!e||!this._DOM.getParentNode(e))return!1;let o=e;for(;this._DOM.getParentNode(o)&&o!==t;){if(this._DOM.getParentNode(o)===t){let e=this._DOM.getRightNeighbor(o);for(;!this._DOM.getElementOffsetHeight(e)&&!this._DOM.getElementOffsetWidth(e);)if(e=this._DOM.getRightNeighbor(e),this.isContentFlowEnd(e))return!0;return this.isContentFlowEnd(e)}if(this._DOM.getLastElementChild(this._DOM.getParentNode(o))!==o)return!1;o=this._DOM.getParentNode(o)}return o===t}isLineChanged(e,t){return this._DOM.getElementOffsetTop(t)-this._DOM.getElementOffsetBottom(e)>-2}isLineKept(e,t,o){const n=this._DOM.getElementOffsetBottom(e),i=this._DOM.getElementOffsetTop(t),s=n-i,r=s>=2;return o&&console.group("isLineKept?"),o&&console.log("\n",r,"\n","\n currentBottom",n,[e],"\n nextTop",i,[t],"\n delta",s),o&&console.groupEnd("isLineKept?"),r}findFirstChildParent(e,t){let o=this._DOM.getParentNode(e),n=null;for(;o&&o!==t;){if(e!==this._DOM.getFirstElementChild(o))return n;n=o,e=o,o=this._DOM.getParentNode(e)}return n}findLastChildParent(e,t){let o=this._DOM.getParentNode(e),n=null;for(;o&&o!==t;){if(e!==this._DOM.getLastElementChild(o))return n;n=o,e=o,o=this._DOM.getParentNode(e)}return n}isVerticalFlowDisrupted(e){return e.some(((e,t,o)=>{const n=e,i=o[t+1];if(!i)return!1;return this._DOM.getElementOffsetBottom(n)>this._DOM.getElementOffsetTop(i)}))}findAllForcedPageBreakInside(e){return this.getAll(this._selector.printForcedPageBreak,e)}findPreviousNonHangingsFromPage(e,t,o){let n=null,i=this._DOM.getLeftNeighbor(e);for(;i&&this.getTop(i,o)>t;){if(!this.isNoHanging(i))return n;if(this.isPageStartElement(i))return e;n=i,e=i,i=this._DOM.getLeftNeighbor(e)}return n}findSuitableNonHangingPageStart(e,t){let o=e,n=null;for(;;){const e=this._DOM.getLastElementChild(o);if(!e)break;if(this.isNoHanging(e)){n=e;break}o=e}if(n)for(o=n;o&&o!==e;){const t=o.parentElement;if(!t||t===e)break;if(this._DOM.getFirstElementChild(t)!==o)break;n=t,o=t}else n=e;return this.getTop(n)>t?n:null}insertForcedPageBreakBefore(e){const t=this.create(this._selector.printForcedPageBreak);return this._DOM.insertBefore(e,t),t}insertForcedPageBreakAfter(e){const t=this.create(this._selector.printForcedPageBreak);return this._DOM.insertAfter(e,t),t}replaceNodeContentsWith(e,...t){this._DOM.setInnerHTML(e,""),this._DOM.insertAtEnd(e,...t)}fitElementWithinBoundaries({element:e,height:t,width:o,vspace:n,hspace:i}){const s=n/t,r=i/o,l=s{this._DOM.setAttribute(e,this._selector.topCutPart),this._DOM.setAttribute(e,this._selector.bottomCutPart)})),this._DOM.removeAttribute(e.at(0),this._selector.topCutPart),this._DOM.removeAttribute(e.at(-1),this._selector.bottomCutPart)}wrapNode(e,t){this._DOM.insertBefore(e,t),this._DOM.insertAtEnd(t,e)}wrapTextNode(e){if(!this.isSignificantTextNode(e))return;const t=this.create(this._selector.textNode);return this._DOM.insertBefore(e,t),this._DOM.insertAtEnd(t,e),t}getTop(e,t=null,o=0){if(!e)return void(this._debug._&&console.warn("element must be provided, but was received:",e,"\nThe function returned:",void 0));if(null===t)return this._DOM.getElementOffsetTop(e);if(!t)return void(this._debug._&&console.warn("root must be provided, but was received:",t,"\nThe function returned:",void 0));const n=this._DOM.getElementOffsetParent(e);if(!n)return void(this._debug._&&console.warn("Element has no offset parent.","\n element:",[e],"\n offsetParent:",n,"\n The function returned:",void 0));const i=this._DOM.getElementOffsetTop(e);return n===t?i+o:this.getTop(n,t,o+i)}getBottom(e,t=null){if(e){if(null===t)return this._DOM.getElementOffsetBottom(e);if(t)return this.getTop(e,t)+this._DOM.getElementOffsetHeight(e);this._debug._&&console.warn("root must be provided, but was received:",t,"\nThe function returned:",void 0)}else this._debug._&&console.warn("element must be provided, but was received:",e,"\nThe function returned:",void 0)}getHeightWithMargin(e){const t=parseInt(this._DOM.getComputedStyle(e).marginTop),o=parseInt(this._DOM.getComputedStyle(e).marginBottom);return this._DOM.getElementOffsetHeight(e)+t+o}getBottomWithMargin(e,t){const o=this.create();e&&this._DOM.insertAfter(e,o);const n=e?this.getTop(o,t):void 0;return this._DOM.removeNode(o),n}getTopWithMargin(e,t){const o=parseInt(this._DOM.getComputedStyle(e).marginTop);return this.getTop(e,t)-o}getMaxWidth(e){const t=this.create();this._DOM.insertAtEnd(e,t);const o=this._DOM.getElementOffsetWidth(t);return this._DOM.removeNode(t),o}getEmptyNodeHeight(e,t=!0){const o=this.create();t&&this._DOM.setStyles(o,{padding:"0.1px"});const n=this._DOM.cloneNodeWrapper(e);"TABLE"===this._DOM.getElementTagName(e)&&this._DOM.setInnerHTML(n,""),this._DOM.insertAtEnd(o,n),this._DOM.insertBefore(e,o);const i=this._DOM.getElementOffsetHeight(o);return this._DOM.removeNode(o),i}getLineHeight(e){const t=this.createNeutral();this._DOM.setInnerHTML(t,"!"),this._DOM.setStyles(t,{display:"block"}),this._DOM.insertAtEnd(e,t);const o=this._DOM.getElementOffsetHeight(t);return this._DOM.removeNode(t),o}getTableRowHeight(e,t=0){const o=this._DOM.getElementOffsetTop(e),n=this._DOM.cloneNode(e),i="!
".repeat(t);[...n.children].forEach((e=>this._DOM.setInnerHTML(e,i))),this._DOM.insertBefore(e,n);const s=this._DOM.getElementOffsetTop(e);return this._DOM.removeNode(n),s-o}copyNodeWidth(e,t){this._DOM.setStyles(e,{width:`${this._DOM.getElementOffsetWidth(t)}px`,"min-width":`${this._DOM.getElementOffsetWidth(t)}px`})}lockTableWidths(e){this.copyNodeWidth(e,e),this.getAll("td",e).forEach((e=>this.copyNodeWidth(e,e)))}prepareSplittedNode(e){const t=e,o=this.splitByWordsGreedy(e),n=o.map((e=>{const t=this._DOM.createElement("span");return this._DOM.setInnerHTML(t,e+" "),t})),i=this.createTestNodeFrom(e);return this._DOM.insertAtEnd(i,...n),this._DOM.insertAtEnd(e,i),{splittedNode:t,nodeWords:o,nodeWordItems:n}}splitByLinesGreedy(e){return e.split(/(?<=\n)/)}splitByWordsGreedy(e){return(this._DOM.getNodeValue(e)||this._DOM.getInnerHTML(e)).split(/(?<=\s|-)/)}splitByWordsGreedyWithSpacesFilter(e){return(this._DOM.getNodeValue(e)||this._DOM.getInnerHTML(e)).trim().split(/(?<=\s|-)/).filter((e=>" "!=e))}notSolved(e){this._DOM.getElementTagName(e);return!1}}function h(e){return e?.length?e?.split(/\s+/).filter(Boolean):[]}class d{constructor({config:e,DOM:t,node:o,selector:n}){this._debug=e.debugMode?{...e.debugConfig.paragraph}:{},this._DOM=t,this._selector=n,this._node=o,this._minParagraphLeftLines=2,this._minParagraphDanglingLines=2,this._minParagraphBreakableLines=this._minParagraphLeftLines+this._minParagraphDanglingLines||2}init(){this._debug._&&console.log("🚨 init Paragraph")}split(e){return this._splitComplexTextBlockIntoLines(e)}_getLines(e){return Math.ceil(this._DOM.getElementOffsetHeight(e)/this._node.getLineHeight(e))}_splitComplexTextBlockIntoLines(e){if(this._debug._&&console.group("_splitComplexTextBlockIntoLines",[e]),this._node.isSelectorMatching(e,this._selector.splitted))return this._end(this._selector.splitted),this._DOM.getChildren(e);const t=this._node.getPreparedChildren(e),o=t.map((e=>{const t=this._node.getLineHeight(e),o=this._DOM.getElementOffsetHeight(e),n=this._DOM.getElementOffsetLeft(e),i=this._DOM.getElementOffsetTop(e);return{element:e,lines:Math.ceil(o/t),left:n,top:i,height:o,lineHeight:t,text:this._DOM.getInnerHTML(e)}}));this._debug._&&console.log("\n🚸 nodeChildren",[...t],"\n🚸 extendedChildrenArray",[...o]);const n=o.flatMap((e=>e.lines>1&&!this._node.isNoBreak(e.element)?this._breakItIntoLines(e.element):e.element));this._debug._&&console.log("\n🚸🚸🚸\n partiallyLinedChildren",[...n]);const i=n.reduce(((e,t,o,n)=>(e||(e=[]),"BR"===this._DOM.getElementTagName(t)?(e.at(-1).push(t),e.push([]),this._debug._&&console.log("br; push:",t),e):!e.length||this._node.isLineChanged(e.at(-1).at(-1),t)?(e.push([t]),this._debug._&&console.log("◼️ start new line:",t),e):0===e.at(-1).length||e.length&&this._node.isLineKept(e.at(-1).at(-1),t)?(this._debug._&&console.log("⬆ add to line:",t),e.at(-1).push(t),e):void(this._debug._&&console.assert(!0,"groupedPartiallyLinedChildren: An unexpected case of splitting a complex paragraph into lines.","\nOn the element:",t)))),[]);if(this._debug._&&console.log("🟑🟑🟑 groupedPartiallyLinedChildren \n",i.length,[...i]),i.length{let o;if(0==e.length)o=e[0],o.setAttribute("role","🚫"),console.assert(0==e.length,"The string cannot be empty (_splitComplexTextBlockIntoLines)");else if(1==e.length)o=e[0];else{o=this._node.createTextGroup(),this._DOM.insertBefore(e[0],o),this._DOM.insertAtEnd(o,...e)}return o.dataset.child=t,o}));return this._end("OK _splitComplexTextBlockIntoLines"),this._DOM.setAttribute(e,this._selector.splitted),l}_breakItIntoLines(e){if(this._debug._&&console.group("_breakItIntoLines",[e]),this._node.isNoBreak(e))return this._end("isNoBreak"),e;if(this._node.isWrappedTextNode(e)){const t=this._breakWrappedTextNodeIntoLines(e);return this._end("TextNode newLines"),t}return this._end("(recursive _breakItIntoLines)"),this._processNestedInlineElements(e)}_processNestedInlineElements(e){this._debug._&&console.group("_processNestedInlineElements",[e]);const t=this._getNestedInlineChildren(e).flatMap((e=>this._getLines(e)>1?this._breakItIntoLines(e):e)),o=this._findNewLineStarts(t),n=o.map(((n,i)=>{const s=t[n],r=t[o[i+1]];return this._cloneAndCleanOutsideRange(e,s,r)}));return this._DOM.insertInsteadOf(e,...n),this._end("Nested Inline parts"),n}_cloneAndCleanOutsideRange(e,t,o){t&&t.setAttribute("split","start"),o&&o.setAttribute("split","end");let n=e.cloneNode(!0);if(t){let t=n.querySelector('[split="start"]'),o=t.previousElementSibling;for(;o;){let e=o;o=o.previousElementSibling,e.remove()}let i=t.parentElement;for(;i&&i!==e;){let e=i.previousElementSibling;for(;e;){let t=e;e=e.previousElementSibling,t.remove()}i=i.parentElement}t.removeAttribute("split")}if(o){let t=n.querySelector('[split="end"]'),o=t.nextElementSibling;for(;o;){let e=o;o=o.nextElementSibling,e.remove()}let i=t.parentElement;for(;i&&i!==e;){let e=i.nextElementSibling;for(;e;){let t=e;e=e.nextElementSibling,t.remove()}i=i.parentElement}t.remove()}return t&&t.removeAttribute("split"),o&&o.removeAttribute("split"),n}_getNestedInlineChildren(e){return[...this._DOM.getChildNodes(e)].reduce(((e,t)=>{if(this._node.isSignificantTextNode(t))return e.push(this._node.wrapTextNode(t)),e;if(!this._DOM.getElementOffsetParent(t)){const o=this._node.getPreparedChildren(t);return o.length>0&&e.push(...o),e}if(this._DOM.isElementNode(t)){return this._getNestedInlineChildren(t).forEach((t=>e.push(t))),e}}),[])}_makeWordsFromTextNode(e){const t=this._node.splitByWordsGreedy(e);this._debug._&&console.log("wordArray",t);const o=t.map(((e,t)=>this._node.createWord(e+"",t)));return this._debug._&&console.log("wrappedWordArray",o),{wordArray:t,wrappedWordArray:o}}_breakWrappedTextNodeIntoLines(e){e.classList.add("πŸ” _breakItIntoLines");const{wordArray:t,wrappedWordArray:o}=this._makeWordsFromTextNode(e);this._DOM.setInnerHTML(e,""),this._DOM.insertAtEnd(e,...o);const n=this._findNewLineStarts(o),i=n.reduce(((o,i,s)=>{const r=this._node.createTextLine(),l=n[s],a=n[s+1],h=t.slice(l,a).join("")+"";return this._DOM.setInnerHTML(r,h),this._DOM.insertBefore(e,r),o.push(r),o}),[]);return e.remove(),i}_findNewLineStarts(e){return e.reduce(((t,o,n)=>(n>0&&e[n-1].offsetTop+e[n-1].offsetHeight<=o.offsetTop&&t.push(n),t)),[0])}_end(e){this._debug._&&console.log(`%c β–² ${e} `,"background:#eee;color:#888;padding: 0 1px 0 0;"),this._debug._&&console.groupEnd()}}const g="#66CC00",c=`color: ${g};font-weight:bold`,p=`border:1px solid ${g};background:#EEEEEE;color:${g};`,_="background:#999;color:#FFF;padding: 0 4px;";class u{constructor({config:e,DOM:t,node:o,selector:n,layout:i,referenceWidth:s,referenceHeight:r}){this._debug=e.debugMode?{...e.debugConfig.pages}:{},this._selector=n,this._node=o,this._noHangingSelectors=h(e.noHangingSelectors),this._pageBreakBeforeSelectors=h(e.pageBreakBeforeSelectors),this._pageBreakAfterSelectors=h(e.pageBreakAfterSelectors),this._forcedPageBreakSelectors=h(e.forcedPageBreakSelectors),this._noBreakSelectors=h(e.noBreakSelectors),this._garbageSelectors=h(e.garbageSelectors),this._DOM=t,this._paragraph=new d({config:e,DOM:t,node:o,selector:n}),this._paragraph.init(),this._root=i.root,this._contentFlow=i.contentFlow,this._referenceWidth=s,this._referenceHeight=r,this._minLeftLines=2,this._minDanglingLines=2,this._minBreakableLines=this._minLeftLines+this._minDanglingLines,this._minLeftRows=1,this._minDanglingRows=1,this._minBreakableRows=this._minLeftRows+this._minDanglingRows,this._minPreFirstBlockLines=3,this._minPreLastBlockLines=3,this._minPreBreakableLines=this._minPreFirstBlockLines+this._minPreLastBlockLines,this._minBreakableGridRows=4,this._imageReductionRatio=.8,this._signpostHeight=24,this._commonLineHeight=this._node.getLineHeight(this._root),this._minimumBreakableHeight=this._commonLineHeight*this._minBreakableLines,this._isFirefox="undefined"!=typeof InstallTrigger,this.pages=[]}calculate(){return this._removeGarbageElements(),this._prepareForcedPageBreakElements(),this._prepareNoBreakElements(),this._prepareNoHangingElements(),this._calculate(),this._debug._&&console.log("%c βœ” Pages.calculate()",p,this.pages),this.pages}_removeGarbageElements(){if(this._garbageSelectors.length){this._node.getAll(this._garbageSelectors,this._contentFlow).forEach((e=>{this._DOM.removeNode(e)}))}}_prepareNoHangingElements(){if(this._noHangingSelectors.length){this._node.getAll(this._noHangingSelectors,this._contentFlow).forEach((e=>{this._node.setFlagNoHanging(e);const t=this._node.findLastChildParent(e,this._contentFlow);t&&this._node.setFlagNoHanging(t,"parent")}))}}_prepareNoBreakElements(){if(this._noBreakSelectors.length){this._node.getAll(this._noBreakSelectors,this._contentFlow).forEach((e=>this._node.setFlagNoBreak(e)))}}_prepareForcedPageBreakElements(){const e=this._pageBreakBeforeSelectors.length?this._node.getAll(this._pageBreakBeforeSelectors,this._contentFlow):[],t=this._pageBreakAfterSelectors.length?this._node.getAll(this._pageBreakAfterSelectors,this._contentFlow):[],o=this._node.getAll(this._forcedPageBreakSelectors,this._contentFlow);if(e.length){const t=e[0],o=this._node.findFirstChildParent(t,this._contentFlow)||t,n=this._DOM.getLeftNeighbor(o);this._node.isContentFlowStart(n)&&(console.log(e[0]),e.shift())}if(t.length){const e=t.at(-1),o=this._node.findLastChildParent(e,this._contentFlow)||e,n=this._DOM.getRightNeighbor(o);this._node.isContentFlowEnd(n)&&t.pop()}e.length&&e.forEach((e=>{const t=this._node.findFirstChildParent(e,this._contentFlow);t&&(e=t),this._node.insertForcedPageBreakBefore(e)})),o&&o.forEach((e=>{if(!this._node.isForcedPageBreak(e)){const t=this._node.findFirstChildParent(e,this._contentFlow);t&&(e=t),this._node.insertForcedPageBreakBefore(e)}})),t.length&&t.forEach((e=>{const t=this._node.findLastChildParent(e,this._contentFlow);t&&(e=t),this._node.isForcedPageBreak(e.nextElementSibling)||this._node.insertForcedPageBreakAfter(e)}))}_calculate(){this._debug._&&console.groupCollapsed("β€’β€’ init data β€’β€’"),this._debug._&&console.log("this._referenceHeight",this._referenceHeight,"\n","this._noHangingSelectors",this._noHangingSelectors,"\n","this._pageBreakBeforeSelectors",this._pageBreakBeforeSelectors,"\n","this._pageBreakAfterSelectors",this._pageBreakAfterSelectors,"\n","this._forcedPageBreakSelectors",this._forcedPageBreakSelectors,"\n","this._noBreakSelectors",this._noBreakSelectors,"\n","isFirefox",this._isFirefox),this._debug._&&console.groupEnd("β€’β€’ init data β€’β€’"),this._registerPageStart(this._node.get(this._selector.contentFlowStart,this._contentFlow));const e=this._node.getBottomWithMargin(this._contentFlow,this._root);if(ethis._registerPageStart(e)));const t=this._node.getPreparedChildren(this._contentFlow);this._debug._&&console.groupCollapsed("%c🚸 children(contentFlow)",p),this._debug._&&console.log(t),this._debug._&&console.groupEnd("%c🚸 children(contentFlow)",p),this._parseNodes({array:t})}_registerPageStart(e,t=!1){if(t){e=this._node.findFirstChildParent(e,this._contentFlow)||e;e=this._node.findPreviousNonHangingsFromPage(e,this._node.getTop(this.pages.at(-1)?.pageStart,this._root),this._root)||e}const o=this._node.getTopWithMargin(e,this._root)+this._referenceHeight;this.pages.push({pageStart:e,pageBottom:o}),this._node.markPageStartElement(e,this.pages.length),this._debug._registerPageStart&&console.log(`%cπŸ“register page ${this.pages.length}`,"background:yellow;font-weight:bold","\n pageBottom:",o,"\n pageStart:",e)}_parseNodes({array:e,previous:t,next:o,parent:n,parentBottom:i}){this._debug._parseNodes&&console.log("πŸ”΅ _parseNodes","\narray:",[...e],"\ntracedParent:",n);for(let s=0;s{this._node.markProcessed(e,"node is ForcedPageBreak (inside a node that fits)"),this._registerPageStart(e)}));else{if(this._debug._parseNode&&console.log("nextElementTop > newPageBottom",h,">",a),this._node.isSVG(o)||this._node.isIMG(o)||this._node.isOBJECT(o)){const e=this._node.isSVG(o)?this._node.createSignpost(o):o;let t=i?a-this._node.getTop(e,this._root):a-this._node.getTop(i,this._root);t-=s?s-this._node.getBottom(e,this._root):0;let r=this._referenceHeight-(i?this._node.getTop(e,this._root)-this._node.getTop(i,this._root):0);const l=this._DOM.getElementOffsetHeight(e),h=this._DOM.getElementOffsetWidth(e);if(this._debug._parseNode&&console.log("πŸ–ΌοΈπŸ–ΌοΈπŸ–ΌοΈπŸ–ΌοΈπŸ–ΌοΈπŸ–ΌοΈ\n",`H-space: ${t}, image Height: ${l}, image Width: ${h}`,o,"\n parent",i,"parentBottom",s),lthis._imageReductionRatio?(this._debug._parseNode&&console.log("Register next elements; πŸ–ΌοΈπŸ–ΌοΈπŸ–ΌοΈ IMG RESIZE to availableImageNodeSpace:",t,o),this._node.markProcessed(o,`IMG with ratio ${d}, and next starts on next`),this._node.fitElementWithinBoundaries({element:o,height:l,width:h,vspace:t,hspace:this._referenceWidth}),this._registerPageStart(n),this._debug._parseNode&&console.log("%c END _parseNode πŸ–ΌοΈ IMG scaled",_),void(this._debug._parseNode&&console.groupEnd())):(this._node.markProcessed(o,"IMG starts on next"),this._registerPageStart(e,!0),this._debug._parseNode&&console.log("πŸ–ΌοΈ register Page Start",o),l>r&&(this._node.fitElementWithinBoundaries({element:o,height:l,width:h,vspace:r,hspace:this._referenceWidth}),this._node.markProcessed(o,"IMG starts on next and resized"),this._debug._parseNode&&console.log("πŸ–ΌοΈ ..and fit it to full page",o)),this._debug._parseNode&&console.log("%c END",_),void(this._debug._parseNode&&console.groupEnd()))}if(o.style.height){this._debug._parseNode&&console.log("πŸ₯ currentElement has HEIGHT",o.style.height);const e=this._node.getTop(o,this._root),t=a-e,i=h-e,s=t/i,r=this._referenceHeight/i;return this._debug._parseNode&&console.log("\nπŸ₯ currentElementTop",e,"\nπŸ₯ newPageBottom",a,"\nπŸ₯ availableSpace",t,"\nπŸ₯ currentElementContextualHeight",i,"\nπŸ₯ availableSpaceFactor",s,"\nπŸ₯ fullPageFactor",r),console.assert(s<1),s>.8?(this._debug._parseNode&&console.log("πŸ₯ availableSpaceFactor > 0.8: ",s),this._DOM.setStyles(o,{transform:`scale(${s})`}),this._registerPageStart(n),this._node.markProcessed(o,"processed as a image, has been scaled down within 20%, the next one starts a new page"),this._node.markProcessed(n,"the previous one was scaled down within 20%, and this one starts a new page."),this._debug._parseNode&&console.log("%c END _parseNode (has height & scale)",_),void(this._debug._parseNode&&console.groupEnd())):(r<1&&(this._debug._parseNode&&console.log("πŸ₯ fullPageFactor < 1: ",r),this._node.markProcessed(o,"processed as a image, has been scaled down, and starts new page"),this._DOM.setStyles(o,{transform:`scale(${r})`})),this._debug._parseNode&&console.log("πŸ₯ _registerPageStart",o),this._registerPageStart(o),this._node.markProcessed(o,"processed as a image, starts new page"),this._debug._parseNode&&console.log("%c END _parseNode (has height & put on next page)",_),void(this._debug._parseNode&&console.groupEnd()))}if(this._debug._parseNode&&console.log("split or not? \n","currentElementBottom",l),l<=a)return this._debug._parseNode&&console.log("currentElementBottom <= newPageBottom",l,"<=",a,"\n register nextElement as pageStart"),this._node.isNoHanging(o)?(this._debug._parseNode&&console.log("currentElement fits / last, and _isNoHanging => move it to the next page"),this._node.markProcessed(o,"it fits & last & _isNoHanging => move it to the next page"),this._registerPageStart(o,!0),this._debug._parseNode&&console.log("%c END _parseNode (isNoHanging)",_),void(this._debug._parseNode&&console.groupEnd())):(this._registerPageStart(n),this._node.markProcessed(o,"fits, its bottom falls exactly on the cut"),this._node.markProcessed(n,"starts new page, its top is exactly on the cut"),this._debug._parseNode&&console.log("%c END _parseNode (currentElement fits, register the next element)",_),void(this._debug._parseNode&&console.groupEnd()));this._debug._parseNode&&console.log("currentElementBottom > newPageBottom",l,">",a);const d=this._getProcessedChildren(o,a,this._referenceHeight);this._debug._parseNode&&console.log("try to break it and loop the children:",d);const g=d.length;this._debug._parseNode&&console.log(...r,"childrenNumber ",g),this._debug._parseNode&&console.log(...r,"currentElement ",o);const c=e&&i||o;if(g){const e=this._node.isFullySPlitted(o)||this._node.isSlough(o);this._parseNodes({array:d,previous:t,next:n,parent:e?void 0:c,parentBottom:e?void 0:l}),this._node.markProcessed(o,"getProcessedChildren and _parseNodes")}else if(this._node.isNoHanging(t)){const e=this._node.findSuitableNonHangingPageStart(t,this.pages.at(-2)?.pageBottom)||o;this._registerPageStart(e,!0),this._node.markProcessed(o,"doesn't fit, has no children, isNoHanging(previousElement)"),this._node.markProcessed(t,"isNoHanging - register it or parents")}else this._debug._parseNode&&console.log(...r,"_registerPageStart (from _parseNode): \n",o),this._registerPageStart(o,!0),this._node.markProcessed(o,"doesn't fit, has no children, register it or parents")}this._debug._parseNode&&console.log("%c END _parseNode",_),this._debug._parseNode&&console.groupEnd()}_getProcessedChildren(e,t,o){const n=["%c_getProcessedChildren\n","color:white"];let i=[];if(this._node.isNoBreak(e))return this._debug._getProcessedChildren&&console.info(...n,"🧑 isNoBreak",e),[];if(this._node.isComplexTextBlock(e))return this._debug._getProcessedChildren&&console.info(...n,"πŸ’š ComplexTextBlock",e),this._paragraph.split(e)||[];if(this._node.isWrappedTextNode(e))return this._debug._getProcessedChildren&&console.info(...n,"πŸ’š TextNode",e),this._paragraph.split(e)||[];const s=this._DOM.getComputedStyle(e);return this._node.isTableLikeNode(e,s)?(this._debug._getProcessedChildren&&console.info(...n,"πŸ’š TABLE like",e),i=this._splitTableLikeNode(e,t,o,s)||[]):this._node.isTableNode(e,s)?(this._debug._getProcessedChildren&&console.info(...n,"πŸ’š TABLE",e),i=this._splitTableNode(e,t,o,s)||[]):this._node.isPRE(e,s)?(this._debug._getProcessedChildren&&console.info(...n,"πŸ’š PRE",e),i=this._splitPreNode(e,t,o)||[]):this._node.isGridAutoFlowRow(this._DOM.getComputedStyle(e))?(this._debug._getProcessedChildren&&console.info(...n,"πŸ’œ GRID"),i=this._splitGridNode(e,t,o)||[]):(this._debug._getProcessedChildren&&console.info(...n,"πŸ’š some node",e),i=this._node.getPreparedChildren(e),this._debug._getProcessedChildren&&console.info(...n,"🚸 get element children ",i)),i}_splitPreNode(e,t,o,n){const i=n||this._DOM.getComputedStyle(e),s=["%c_splitPreNode\n","color:white"];this._debug._splitPreNode&&console.group("%c_splitPreNode","background:cyan"),this._debug._splitPreNode&&console.log(...s,"node",e);const r=this._node.getTop(e,this._root),l=this._DOM.getElementOffsetHeight(e),a=this._node.getLineHeight(e),h=this._node.getEmptyNodeHeight(e,!1);if(l1)return this._debug._splitPreNode&&console.log("%c END _splitPreNode TODO!",_),[];{if(this._DOM.isElementNode(d[0])){const e=d[0];return this._debug._splitPreNode&&console.warn("is Element Node",e),this._debug._splitPreNode&&console.log("%c END _splitPreNode ???????",_),[]}this._node.isWrappedTextNode(d[0])&&this._debug._splitPreNode&&console.warn(`is TEXT Node: ${d[0]}`);const n=d[0].wholeText,l=this._node.splitByLinesGreedy(n);if(l.length{const t=this._node.createWithFlagNoBreak();return this._DOM.setInnerHTML(t,e),t}));this._debug._splitPreNode&&console.log("linesFromNode",c),this._node.replaceNodeContentsWith(e,...c);const p=o-h;let u=0,m=[],f=t-r-h;const b=i.position;"relative"!=b&&this._DOM.setStyles(e,{position:"relative"});for(let t=0;tf&&(t&&m.push(t),t&&(u+=1),f=t?this._node.getTop(o,e)+p:p)}if(this._DOM.setStyles(e,{position:b}),!m.length)return this._debug._splitPreNode&&console.log("%c END _splitPreNode NO SPLIITERS",_),[];m.push(null),this._debug._splitPreNode&&console.log(...s,"splitters",m);const M=m.map(((t,o,n)=>{const i=this._DOM.cloneNodeWrapper(e);this._node.setFlagNoBreak(i);const s=n[o-1]||0,r=t||n[n.length];return this._DOM.insertAtEnd(i,...c.slice(s,r)),i}));return this._node.markPartNodesWithClass(M),this._debug._splitPreNode&&console.log(...s,"newPreElementsArray",M),this._node.replaceNodeContentsWith(e,...M),this._DOM.setStyles(e,{display:"contents"}),this._DOM.setAttribute(e,"[slough-node]",""),this._DOM.removeAllClasses(e),this._debug._splitPreNode&&console.log("%c END _splitPreNode",_),this._debug._splitPreNode&&console.groupEnd(),M}}_insertTableSplit({startId:e,endId:t,table:o,tableEntries:n}){const i=this._DOM.cloneNodeWrapper(o),s=n.rows.slice(e,t),r=this._node.createWithFlagNoBreak();return o.before(r),e&&this._DOM.insertAtEnd(r,this._node.createSignpost("(table continued)",this._signpostHeight)),this._DOM.insertAtEnd(r,this._node.createTable({wrapper:i,colgroup:this._DOM.cloneNode(n.colgroup),caption:this._DOM.cloneNode(n.caption),thead:this._DOM.cloneNode(n.thead),tbody:s}),this._node.createSignpost("(table continues on the next page)",this._signpostHeight)),r}_splitTableLikeNode(e,t,o,n){const i=n||this._DOM.getComputedStyle(e),s=this._node.getPreparedChildren(e),r=this._node.getTop(e,this._root),l=this._node.getEmptyNodeHeight(e),a=o-l;let h=s,d=0,g=[],c=t-r-l;const p=i.position;"relative"!=p&&this._DOM.setStyles(e,{position:"relative"});for(let t=0;tc&&(t&&g.push(t),t&&(d+=1),c=t?this._node.getTop(o,e)+a:a)}if(this._DOM.setStyles(e,{position:p}),!g.length)return this._debug._splitTableLikeNode&&console.log("splitters.length",g.length),[];g.push(null);const _=g.map(((t,o,n)=>{const i=this._DOM.cloneNodeWrapper(e);this._node.setFlagNoBreak(i),this._node.unmarkPageStartElement(i);const s=n[o-1]||0,r=t||n[n.length];return this._DOM.insertAtEnd(i,...h.slice(s,r)),i}));return this._node.markPartNodesWithClass(_),this._node.replaceNodeContentsWith(e,..._),this._DOM.removeAllClasses(e),this._DOM.removeAllStyles(e),this._DOM.setStyles(e,{display:"contents"}),this._DOM.setAttribute(e,"[slough-node]",""),_}_splitTableNode(e,t,o){const n=["%c_splitTableNode\n","color:white"];this._debug._splitTableNode&&console.time("_splitTableNode"),this._debug._splitTableNode&&console.group("%c_splitTableNode","background:cyan"),this._node.lockTableWidths(e);const i=this._node.getEmptyNodeHeight(e),s=this._node.getTableEntries(e);this._debug._splitTableNode&&console.log(...n,e,"\ntableEntries",s);const r=this._node.getTopWithMargin(e,this._root),l=this._DOM.getElementOffsetHeight(s.caption)||0,a=this._DOM.getElementOffsetHeight(s.thead)||0,h=this._DOM.getElementOffsetHeight(s.tfoot)||0,d=(l??0)*(this._isFirefox??0),g=t-r-i-this._signpostHeight,c=o-l-a-h-i-2*this._signpostHeight;this._debug._splitTableNode&&console.log(...n,"\n β€’ tableFirstPartBottom",g,"\n","\n pageBottom",t,"\n - tableTop",r,"\n - tableCaptionHeight",l,"\n - tableTheadHeight",a,"\n - tableWrapperHeight",i,"\n - this._signpostHeight",this._signpostHeight,"\n","\n fullPageHeight",o,"\n - tableCaptionHeight",l,"\n - tableTheadHeight",a,"\n - tableTfootHeight",h,"\n - 2 * this._signpostHeight",2*this._signpostHeight,"\n - tableWrapperHeight",i,"\n = tableFullPartContentHeight",c);const p=e=>[...e.rows,...e.tfoot?[e.tfoot]:[]];let u=p(s),m=[],f=g;this._debug._splitTableNode&&console.log(this._node.getTop(u[1],e)-this._node.getBottom(u[0],e),"(row[1].top - row[0].bottom)"),this._node.getTop(u[0],e)>f&&(f=c,this._debug._splitTableNode&&console.log("The Row 0 goes to the 2nd page"));for(let o=0;of){const i=o,l=n,a=this._DOM.getElementOffsetHeight(l),h=this._node.getTableRowHeight(l,this._minBreakableLines),g=this._node.getTableRowHeight(l),b=r,M=this._node.isNoBreak(l),O=a>=h&&!M;if(this._debug._splitTableNode&&console.log(`%c β€’ Row # ${o}: try to split`,"color:blueviolet"),O){this._debug._splitTableRow&&console.groupCollapsed(`Split The ROW.${i}`);const e=f-b-g,n=c-g,r=this._DOM.getChildren(l);let a;a=[...r].map(((o,s)=>{const r=this._node.getPreparedChildren(o);this._debug._splitTableRow&&console.groupCollapsed(`Split TD.${s} in ROW.${i}`);const l=this._getInternalBlockSplitters({rootNode:o,children:r,pageBottom:t,firstPartHeight:e,fullPageHeight:n});return this._debug._splitTableRow&&console.groupEnd(`Split TD.${s} in ROW.${i}`),l})),this._debug._splitTableRow&&console.log("🟣 \ntheRowContentSlicesByTD",a);const h=a.some((e=>(this._debug._splitTableRow&&console.log("🟣","\nobj.result.length",e.result.length,"\nobj.result[0]",e.result[0]),e.result.length&&null===e.result[0])));this._debug._splitTableRow&&console.log("🟣","\nshouldFirstPartBeSkipped",h),h&&(a=[...r].map((e=>{const o=this._node.getPreparedChildren(e);return this._getInternalBlockSplitters({rootNode:e,children:o,pageBottom:t,firstPartHeight:n,fullPageHeight:n})}))),this._debug._splitTableRow&&console.log("🟣","\n theRowContentSlicesByTD",a);const d=a.some((e=>e.result.length));if(this._debug._splitTableRow&&console.log("🟣 ifThereIsSplit",d),d){const e=a.map((e=>{if(e.result.length)return this._createSlicesBySplitFlag(e.trail);{const t=this._node.createWithFlagNoBreak();t.classList.add("🟣"),this._DOM.setStyles(t,{display:"contents"});const o=e.trail.map((e=>e.element));return this._DOM.insertAtEnd(t,...o),[t]}}));this._debug._splitTableRow&&console.log("🟣 theTdContentElements",e);const t=Math.max(...e.map((e=>e.length)));this._debug._splitTableRow&&console.log("🟣 theNewTrCount",t);const n=[];for(let o=0;o{const s=this._DOM.cloneNodeWrapper(n);e[i][o]&&this._DOM.insertAtEnd(s,e[i][o]),this._DOM.insertAtEnd(t,s)})),n.push(t)}this._debug._splitTableRow&&console.log("🟣","\n theNewRows",n),this._DOM.setAttribute(l,".🚫_must_be_removed"),this._debug._splitTableRow&&console.log("🟣 splittingRow",l),this._DOM.insertInsteadOf(l,...n),s.rows.splice(i,1,...n),u=p(s),o-=1}this._debug._splitTableRow&&console.log(`%c END πŸŸͺ Split The ROW.${i}`,_),this._debug._splitTableRow&&console.groupEnd("END OF 'if makesSenseToSplitTheRow'")}else this._debug._splitTableNode&&console.log(`%c β€’ Row # ${o}: small or noBreak`,"color:blueviolet"),o>=this._minLeftRows&&(m.push(o),this._debug._splitTableNode&&console.log(`%c β€’ Row # ${o}: REGISTER as start, index >= ${this._minLeftRows} (_minLeftRows) `,"color:blueviolet")),f=this._node.getTop(u[o],e)+d+c}else this._debug._splitTableNode&&console.log(`%c β€’ Row # ${o}: PASS ...`,"color:blueviolet")}if(this._debug._splitTableNode&&console.log(...n,"splitsIds",m),!m.length)return this._debug._splitTableNode&&console.log("%c END _splitTableNode !splitsIds.length",_),this._debug._splitTableNode&&console.groupEnd(),[];const b=u.length-1-this._minDanglingRows;m[m.length-1]>b&&(m[m.length-1]=b);const M=m.map(((t,o,n)=>this._insertTableSplit({startId:n[o-1]||0,endId:t,table:e,tableEntries:s})));this._debug._splitTableNode&&console.log(...n,"splits",M);const O=this._node.createWithFlagNoBreak();return e.before(O),this._DOM.insertAtEnd(O,this._node.createSignpost("(table continued)",this._signpostHeight),e),this._debug._splitTableNode&&console.timeEnd("_splitTableNode"),this._debug._splitTableNode&&console.log("%c END _splitTableNode",_),this._debug._splitTableNode&&console.groupEnd(),[...M,O]}_createSlicesBySplitFlag(e){this._debug._createSlicesBySplitFlag&&console.group("_createSlicesBySplitFlag");const t=this._node.createWithFlagNoBreak();this._DOM.setStyles(t,{display:"contents"}),t.classList.add("🧰");const o=[t];let n=[t],i=t;const s=e=>{if(0===e.length)return null;const t=e[0];let o=t;for(let t=1;t{this._debug._createSlicesBySplitFlag&&console.group("processChildren"),this._debug._createSlicesBySplitFlag&&console.log("*start* children",e);for(let t=0;t{const t=e.children?.length>0,l=e.split,a=e.element,h=e.id;if(this._debug._createSlicesBySplitFlag&&console.group(`processObj # ${h}`),this._debug._createSlicesBySplitFlag&&console.log("currentElement",a),a&&this._DOM.removeNode(a),l){this._debug._createSlicesBySplitFlag&&console.log("β€’β€’β€’ hasSplitFlag"),n=n.map((e=>{const t=this._DOM.cloneNodeWrapper(e);return t.classList.add("🚩"),t})),this._debug._createSlicesBySplitFlag&&console.log("β€’ hasSplitFlag: NEW wrappers.map:",[...n]);const e=s(n);o.push(e),this._debug._createSlicesBySplitFlag&&console.log("β€’ hasSplitFlag: slices.push(nextWrapper):",[...o]),i=n.at(-1),this._debug._createSlicesBySplitFlag&&console.log("β€’ hasSplitFlag: currentTargetInSlice:",i)}if(t){this._debug._createSlicesBySplitFlag&&console.log("β€’β€’β€’ hasChildren");const t=this._DOM.cloneNodeWrapper(a);n.push(t),this._debug._createSlicesBySplitFlag&&console.log("β€’ hasChildren: wrappers.push(cloneCurrentElementWrapper)",t,[...n]),this._debug._createSlicesBySplitFlag&&console.log("β€’ hasChildren: currentTargetInSlice (check):",i),i?(this._debug._createSlicesBySplitFlag&&console.log("β€’ hasChildren: currentTargetInSlice","TRUE, add to existing",t),this._DOM.insertAtEnd(i,t)):(this._debug._createSlicesBySplitFlag&&console.log("β€’ hasChildren: currentTargetInSlice","FALSE, init the first",t),t.classList.add("🏁first"),this._DOM.setStyles(t,{background:"yellow"}),o.push(t),this._debug._createSlicesBySplitFlag&&console.log("β€’ hasChildren: slices.push(cloneCurrentElementWrapper)",t,[...o])),i=n.at(-1),this._debug._createSlicesBySplitFlag&&console.log("β€’ hasChildren: currentTargetInSlice (=):",i),r(e.children,a)}else i=n.at(-1),this._debug._createSlicesBySplitFlag&&console.log("insert currentElement",a,"to target",i),this._DOM.insertAtEnd(i,a);this._debug._createSlicesBySplitFlag&&console.log(`%c END processObj # ${h}`,_),this._debug._createSlicesBySplitFlag&&console.groupEnd()};return this._debug._createSlicesBySplitFlag&&console.log("####### currentTargetInSlice (=):",i),r(e),this._debug._createSlicesBySplitFlag&&console.log("slices:",o),this._debug._createSlicesBySplitFlag&&o.forEach((e=>console.log("slice:",e))),this._debug._createSlicesBySplitFlag&&console.log("%c END _createSlicesBySplitFlag",_),this._debug._createSlicesBySplitFlag&&console.groupEnd(),o}_getInternalBlockSplitters({rootNode:e,rootComputedStyle:t,children:o,pageBottom:n,firstPartHeight:i,fullPageHeight:s,result:r=[],trail:l=[],indexTracker:a=[],stack:h=[]}){const d=t||this._DOM.getComputedStyle(e),g=d.position;"relative"!=g&&this._DOM.setStyles(e,{position:"relative"}),this._debug._getInternalBlockSplitters&&console.group("πŸ’Ÿ _getInternalBlockSplitters");const c=e=>{e>=0?a.push(e):a.pop()},p=(e,t)=>{this._debug._getInternalBlockSplitters&&console.assert(t>=0,"registerResult: ID mast be provided",e);let o,n=l[t];if(this._debug._getInternalBlockSplitters&&console.groupCollapsed("πŸ’œπŸ’œπŸ’œ registerResult(element, id)"),this._debug._getInternalBlockSplitters&&console.log("\n element",e,"\n id",t,"\n theElementObject (trail[id])",n,"\n theElementIndexInStack",o),0==t){const e=(e=>{let t,o=null;for(let n=e.length-1;n>=0;n--){if(0!==e[n].id)return{item:o,index:t};o=e[n],t=n}return{item:o,index:t}})(h);this._debug._getInternalBlockSplitters&&console.log("πŸ’œπŸ’œ id == 0","\nπŸ’œ [...stack]",[...h],"\nπŸ’œ topParentElementFromStack",e),e.item&&(n=e.item,o=e.index)}this._debug._getInternalBlockSplitters&&console.log("πŸ’œ","\n theElementObject",n,"\n theElementIndexInStack",o,"\n [...indexTracker]",[...a]),0===o?(r.push(null),this._debug._getInternalBlockSplitters&&console.log("result.push(null)","\n\nπŸ’œπŸ’œπŸ’œ")):(r.push(n.element),n&&(n.split=!0),this._debug._getInternalBlockSplitters&&console.log("\n theElementObject",n,"\n theElementObject.element",n.element,"\n result.push(theElementObject.element)","\n\nπŸ’œπŸ’œπŸ’œ ")),this._debug._getInternalBlockSplitters&&console.log("%c END _getInternalBlockSplitters registerResult",_),this._debug._getInternalBlockSplitters&&console.groupEnd()};this._debug._getInternalBlockSplitters&&console.log("πŸ’Ÿ result πŸ’Ÿ",r,"\n\n","\n rootNode:",e,"\n children:",o,"\n pageBottom:",n,"\n firstPartHeight:",i,"\n fullPageHeight:",s,"\n\n\n","πŸ’Ÿ stack",[...h]);for(let t=0;t floater \n ${m} > ${M} `),(this._node.isSVG(_)||this._node.isIMG(_))&&this._debug._getInternalBlockSplitters&&console.log("%cIMAGE πŸ’ŸπŸ’Ÿ","color:red;text-weight:bold");const o=this._node.getBottomWithMargin(_,e);if(this._debug._getInternalBlockSplitters&&console.log("πŸ’ŸπŸ’Ÿ current ???","\n currentElement",_,"\n currentElementBottom",o,"\n floater",M),o<=M)this._debug._getInternalBlockSplitters&&console.log("πŸ’ŸπŸ’ŸπŸ’Ÿ currentElementBottom <= floater"),u&&(this._debug._getInternalBlockSplitters&&console.log("πŸ’ŸπŸ’ŸπŸ’ŸπŸ’Ÿ register nextElement"),l.push(b),p(u,t+1));else{this._debug._getInternalBlockSplitters&&console.log("πŸ’ŸπŸ’ŸπŸ’Ÿ currentElementBottom > floater,\ntry to split",_);const o=this._getProcessedChildren(_,n,s);if(o.length)c(t),h.push(f),this._getInternalBlockSplitters({rootNode:e,rootComputedStyle:d,children:o,pageBottom:n,firstPartHeight:i,fullPageHeight:s,result:r,trail:l[t].children=[],indexTracker:a,stack:h}),h.pop(),this._debug._getInternalBlockSplitters&&console.log("πŸŸͺ back from _getInternalBlockSplitters;\n trail[i]",l[t]);else if(g&&this._node.isNoHanging(g)){console.warn("tst improveResult",g);let e=g;e=this._node.findFirstChildParent(e,this._contentFlow)||e;e=this._node.findPreviousNonHangingsFromPage(e,this.pages.at(-2)?.pageBottom,this._root)||e,this._debug._getInternalBlockSplitters&&console.log("previousElement _isNoHanging"),p(e,t-1)}else this._debug._getInternalBlockSplitters&&console.log(_,"currentElement has no children"),p(_,t)}}}return c(),this._DOM.setStyles(e,{position:g}),this._debug._getInternalBlockSplitters&&console.log("%c END _getInternalBlockSplitters",_),this._debug._getInternalBlockSplitters&&console.groupEnd(),{result:r,trail:l}}_splitGridNode(e,t,o){this._debug._splitGridNode&&console.group("%c_splitGridNode","background:#00FFFF");const n=this._node.getPreparedChildren(e);this._debug._splitGridNode&&console.log("πŸ’  children",n),this._debug._splitGridNode&&console.groupCollapsed("make childrenGroups");const i=n.reduce(((e,t,o,n)=>{const i=this._DOM.getComputedStyle(t),s=i.getPropertyValue("grid-column-start"),r=i.getPropertyValue("grid-column-end"),l={element:t,start:"auto"===s?"auto":parseInt(i.getPropertyValue("grid-column-start")),end:"auto"===r?"auto":parseInt(i.getPropertyValue("grid-column-end")),top:this._DOM.getElementOffsetTop(t)};return!e.length||e.at(-1).at(-1).start>=l.start||"auto"===e.at(-1).at(-1).start||"auto"===l.start?(e.at(-1)&&this._node.isNoHanging(e.at(-1).at(-1).element)?(e.at(-1).push(l),this._debug._splitGridNode&&console.log("Add to group (after no-hang.)",l)):(e.push([l]),this._debug._splitGridNode&&console.log("Start new group:",l)),this._debug._splitGridNode&&console.log("result:",[...e]),e):e.length&&e.at(-1).at(-1).starte.map((e=>e.top)).sort())).map((e=>e[0])),r];this._debug._splitGridNode&&console.log("gridPseudoRowsTopPoints",l);const a=this._node.getTop(e,this._root),h=this._node.getEmptyNodeHeight(e),d=t-a-h,g=o-h;this._debug._splitGridNode&&console.log("\n β€’ firstPartHeight",d,"\n β€’ fullPagePartHeight",g);const c=l;let p=[],u=d;for(let e=0;eu&&(e>this._minLeftRows&&p.push(e-1),u=c[e-1]+g);this._debug._splitGridNode&&console.log("splitsIds",p);const m=(t,o)=>{this._debug._splitGridNode&&console.log(`=> insertGridSplit(${t}, ${o})`);const n=i.slice(t,o).flat().map((e=>e.element));this._debug._splitGridNode&&console.log("partEntries",n);const s=this._DOM.cloneNodeWrapper(e);return this._node.copyNodeWidth(s,e),this._node.setFlagNoBreak(s),e.before(s),this._DOM.insertAtEnd(s,...n),s},f=[...p.map(((e,t,o)=>m(o[t-1]||0,e))),e];return this._debug._splitGridNode&&console.log("splits",f),f.forEach(((e,t)=>this._DOM.setAttribute(e,"[part]",`${t}`))),this._node.setFlagNoBreak(e),this._debug._splitGridNode&&console.log("%c END _splitGridNode",_),this._debug._splitGridNode&&console.groupEnd(),f}}class m{constructor({config:e,DOM:t,node:o,selector:n,layout:i}){this._DOM=t,this._selector=n,this._node=o,this._frontpageTemplate=i.frontpageTemplate,this._headerTemplate=i.headerTemplate,this._footerTemplate=i.footerTemplate,this._paperBodySelector=n?.paperBody||".paperBody",this._paperHeaderSelector=n?.paperHeader||".paperHeader",this._paperFooterSelector=n?.paperFooter||".paperFooter",this._headerContentSelector=n?.headerContent||".headerContent",this._footerContentSelector=n?.footerContent||".footerContent",this._frontpageContentSelector=n?.frontpageContent||".frontpageContent",this._virtualPaperSelector=n?.virtualPaper||".virtualPaper",this._virtualPaperTopMarginSelector=n?.virtualPaperTopMargin||".virtualPaperTopMargin",this._virtualPaperBottomMarginSelector=n?.virtualPaperBottomMargin||".virtualPaperBottomMargin",this._pageNumberRootSelector=n?.pageNumberRoot||void 0,this._pageNumberCurrentSelector=n?.pageNumberCurrent||void 0,this._pageNumberTotalSelector=n?.pageNumberTotal||void 0,this._paperHeight,this._frontpageFactor,this.headerHeight,this.footerHeight,this.bodyHeight,this.bodyWidth,this._calculatePaperParams()}create({currentPage:e,totalPages:t}){const o=this._createPaperBody(this.bodyHeight),n=this._createPaperHeader(this._headerTemplate),i=this._createPaperFooter(this._footerTemplate);return this._createPaper({header:n,body:o,footer:i,currentPage:e,totalPages:t})}createFrontpage({currentPage:e,totalPages:t}){const o=this._createFrontpageContent(this._frontpageTemplate,this._frontpageFactor),n=this._createPaperBody(this.bodyHeight,o),i=this._createPaperHeader(this._headerTemplate),s=this._createPaperFooter(this._footerTemplate);return this._createPaper({header:i,body:n,footer:s,currentPage:e,totalPages:t})}createVirtualTopMargin(){return this._node.create(this._virtualPaperTopMarginSelector)}createVirtualBottomMargin(){return this._node.create(this._virtualPaperBottomMarginSelector)}_createPaper({header:e,body:t,footer:o,currentPage:n,totalPages:i}){const s=this._node.create(this._virtualPaperSelector);return this._DOM.insertAtEnd(s,this.createVirtualTopMargin(),e,t,o,this.createVirtualBottomMargin()),n&&i&&(this._setPageNumber(e,n,i),this._setPageNumber(o,n,i)),s}_createFrontpageContent(e,t){const o=this._node.create(this._frontpageContentSelector);return e&&this._DOM.setInnerHTML(o,e),t&&this._DOM.setStyles(o,{transform:`scale(${t})`}),o}_createPaperBody(e,t){const o=this._node.create(this._paperBodySelector);return this._DOM.setStyles(o,{height:e+"px"}),t&&this._DOM.insertAtEnd(o,t),o}_createPaperHeader(e){const t=this._node.create(this._paperHeaderSelector);if(e){const o=this._node.create(this._headerContentSelector);this._DOM.setInnerHTML(o,e),this._DOM.insertAtEnd(t,o)}return t}_createPaperFooter(e){const t=this._node.create(this._paperFooterSelector);if(e){const o=this._node.create(this._footerContentSelector);this._DOM.setInnerHTML(o,e),this._DOM.insertAtEnd(t,o)}return t}_setPageNumber(e,t,o){const n=this._pageNumberRootSelector?this._DOM.getElement(this._pageNumberRootSelector,e):this._pageNumberRootSelector;if(n){const e=this._DOM.getElement(this._pageNumberCurrentSelector,n),i=this._DOM.getElement(this._pageNumberTotalSelector,n);this._DOM.setInnerHTML(e,t),this._DOM.setInnerHTML(i,o)}}_calculatePaperParams(){const e=this._createPaperBody(),t=this._createFrontpageContent(this._frontpageTemplate),o=this._createPaperHeader(this._headerTemplate),n=this._createPaperFooter(this._footerTemplate),i=this._createPaper({header:o,body:e,footer:n}),s=this._node.create("#workbench");this._DOM.setStyles(s,{position:"absolute",left:"-3000px"}),this._DOM.insertAtEnd(s,i),this._DOM.insertAtStart(this._DOM.body,s);const r=this._DOM.getElementBCR(i).height,l=this._DOM.getElementOffsetHeight(o)||0,a=this._DOM.getElementOffsetHeight(n)||0,h=this._DOM.getElementOffsetHeight(e),d=this._DOM.getElementOffsetWidth(e);this._DOM.insertAtStart(e,t);const g=this._DOM.getElementOffsetHeight(e),c=g>h?h/g:1;this._DOM.removeNode(s),l>.2*r&&console.warn("It seems that your custom header is too high"),a>.15*r&&console.warn("It seems that your custom footer is too high"),c<1&&console.warn("It seems that your frontpage content is too large. We made it smaller to fit on the page. Check out how it looks! It might make sense to fix this with styles or reduce the text amount."),this._paperHeight=r,this.headerHeight=l,this.footerHeight=a,this.bodyHeight=h,this.bodyWidth=d,this._frontpageFactor=c}}class f{constructor({config:e,DOM:t,selector:o,node:n,pages:i,layout:s,paper:r}){this._config=e,this._debug=e.debugMode?{...e.debugConfig.preview}:{},this._DOM=t,this._selector=o,this._node=n,this._virtualPaperGapSelector=o.virtualPaperGap,this._runningSafetySelector=o.runningSafety,this._printPageBreakSelector=o.printPageBreak,this._pageDivider=o.pageDivider,this._virtualPaper=o.virtualPaper,this._virtualPaperTopMargin=o.virtualPaperTopMargin,this._paperBody=o.paperBody,this._pages=i,this._root=s.root,this._contentFlow=s.contentFlow,this._paperFlow=s.paperFlow,this._paper=r,this._hasFrontPage=!!s.frontpageTemplate}create(){this._processFirstPage(),this._processOtherPages(),(!0===this._config.mask||"true"===this._config.mask)&&this._addMask()}_addMask(){const e=[...this._paperFlow.querySelectorAll(this._virtualPaper)],t=this._DOM.getElementOffsetTop(e.at(-1))/(e.length-1),o=this._DOM.getElementOffsetHeight(this._paperFlow.querySelector(this._virtualPaperTopMargin)),n=this._DOM.getElementOffsetHeight(this._paperFlow.querySelector(this._paperBody));!function({targetElement:e,maskHeight:t,maskWindow:o,maskTopPosition:n}){e.style=`\n -webkit-mask-image: linear-gradient(\n black 0,\n black ${o}px,\n transparent ${o}px,\n transparent ${t}px\n );\n mask-image: linear-gradient(\n black 0,\n black ${o}px,\n transparent ${o}px,\n transparent ${t}px\n );\n -webkit-mask-repeat: no-repeat;\n mask-repeat: no-repeat;\n -webkit-mask-size: 100% ${t}px;\n mask-size: 100% ${t}px;\n -webkit-mask-position: 100% ${n}px;\n mask-position: 100% ${n}px;\n -webkit-mask-repeat: repeat-y;\n mask-repeat: repeat-y;\n -webkit-mask-origin: border-box;\n mask-origin: border-box;\n `}({targetElement:this._contentFlow,maskHeight:t,maskWindow:n,maskTopPosition:o})}_processFirstPage(){let e;if(this._hasFrontPage){const t=this._insertFrontpageSpacer(this._contentFlow,this._paper.bodyHeight);this._pages.unshift({pageStart:t}),e=this._paper.createFrontpage({currentPage:1,totalPages:this._pages.length})}else e=this._paper.create({currentPage:1,totalPages:this._pages.length});this._insertIntoPaperFlow(e),this._insertIntoContentFlow(0)}_processOtherPages(){for(let e=1;e=0,`balancer is negative: ${n} < 0`,t)}}class b{constructor({config:e,DOM:t,selector:o,node:n,layout:i}){this._debugMode=e.debugMode,this._debug=e.debugMode?{...e.debugConfig.toc}:{},this._DOM=t,this._node=n,this._tocPageNumberSelector=e.tocPageNumberSelector,this._root=i.root,this._contentFlow=i.contentFlow,this._pageDividerSelector=o.pageDivider}render(){this._debugMode&&console.time("Processing TOC"),this._debug._&&console.log(`\nπŸ“‘ TOC: I am here!\n\ntocPageNumberSelector:\n β€’ ${this._tocPageNumberSelector}\n pageDividerSelector:\n β€’ ${this._pageDividerSelector}\n `);const e=this._node.getAll(this._tocPageNumberSelector,this._contentFlow);if(this._debug._&&console.log("πŸ“‘ tocPageNumberBoxes",e.length),!e.length)return void(this._debug._&&console.log("πŸ“‘ no valid toc"));const t=this._node.getAll(this._pageDividerSelector,this._contentFlow).reduce(((e,t,o)=>{const n=this._node.getTop(t,this._root)-1,i=this._DOM.getAttribute(t,"[page]");return e[n]=i,e}),{});this._debug._&&console.log("πŸ“‘ dataFromPagesMarkers",t);const o=e.reduce(((e,t)=>{const o=this._DOM.getDataId(t),n=this._DOM.getElementById(o),i=this._node.getTop(n,this._root);return e[i]={box:t,id:o,targetTop:i},e}),{});this._debug._&&console.log("πŸ“‘ dataFromTOC",o);const n={...t,...o};let i=0;this._debug._&&console.groupCollapsed("Processing obj");for(const e in n){const t=n[e];this._debug._&&console.log(`Processing ${e}: ${t}`),"string"==typeof t?i=t:(t.page=i,this._DOM.setInnerHTML(t.box,i))}this._debug._&&console.groupEnd("Processing obj"),this._debug._&&console.log("πŸ“‘ tocObject",n),this._debugMode&&console.timeEnd("Processing TOC")}}class M{constructor({config:e,DOM:t,selector:o}){this._config=e,this._selector=o,this._DOM=t}init(){this._config.debugMode&&console.log("πŸ™ i am Validator!");const e=`${this._selector.paperFlow} ${this._selector.virtualPaperGap}`,t=`${this._selector.contentFlow} ${this._selector.virtualPaperGap}`,o=[...this._DOM.getAllElements(e)].map((e=>e.offsetTop)),n=[...this._DOM.getAllElements(t)].map((e=>e.offsetTop)),i=o.reduce(((e,t,o)=>(t!==n[o]&&e.push(o+1),e)),[]);console.assert(!i.length,"Problems with preview generation on the following pages: ",i)}}const O="border:1px dashed #cccccc;background:#ffffff;color:#cccccc;";class D{constructor(e){this._debugMode=e.debugMode,this._preloader,this._preloaderTarget=document.querySelector(e.preloaderTarget)||document.body,this._preloaderBackground=e.preloaderBackground||"white"}create(){this._debugMode&&console.groupCollapsed("%c Preloader ",O),this._insertStyle(),this._preloader=document.createElement("div"),this._preloader.classList.add("lds-dual-ring"),this._preloaderTarget.append(this._preloader),this._debugMode&&console.groupEnd("%c Preloader ",O)}remove(){if(!this._preloader)return;let e=1;const t=setInterval((()=>{e<=.1&&(clearInterval(t),this._preloader.remove()),this._preloader.style.opacity=e,e-=.1*e}),50);this._debugMode&&console.log("%c Preloader removed ",O)}_insertStyle(){const e=document.querySelector("head"),t=document.createElement("style");t.append(document.createTextNode(this._css())),t.setAttribute("data-preloader-style",""),e.append(t)}_css(){return`\n /* PRELOADER */\n .lds-dual-ring {\n position: absolute;\n z-index: 99999;\n top: 0; left: 0; bottom: 0; right: 0;\n background: ${this._preloaderBackground};\n display: flex;\n justify-content: center;\n align-items: center;\n }\n /*\n .lds-dual-ring:after {\n content: " ";\n display: block;\n width: 64px;\n height: 64px;\n margin: 8px;\n border-radius: 50%;\n border: 6px solid #eee;\n border-color: #eee transparent #eee transparent;\n animation: lds-dual-ring 1.2s linear infinite;\n }\n @keyframes lds-dual-ring {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n }\n */\n `}}class S{constructor(e){this._debugMode=e.debugMode}run(){let e=[...document.querySelectorAll("object")];this._debugMode&&console.log(e);let t=[];return e.forEach((e=>{const o=new Promise((t=>{e.addEventListener("load",(o=>{this._debugMode&&console.log("⏰ EVENT: object load",e.clientHeight,e.clientWidth,e),t()}))}));t.push(o)})),Promise.all(t)}}const N="color:Gray;border:1px solid;";console.info("HTML2PDF4DOC version: 0.2.1");const P=document.currentScript.dataset,E=new class{constructor(e){this.params=e,this.debugMode=e.debugMode,this.preloader=e.preloader,this.selector=o,this.config}async render(){console.time("⏱️ HTML2PDF4DOC time"),this.debugMode&&console.log("🏁 document.readyState",document.readyState),document.addEventListener("readystatechange",(e=>{this.debugMode&&console.log("🏁 readystatechange",document.readyState)})),this.debugMode&&console.time("⏱️ await DOMContentLoaded time"),await new Promise((e=>{window.addEventListener("DOMContentLoaded",(t=>{this.debugMode&&console.log("⏰ EVENT: DOMContentLoaded"),e()}))})),this.debugMode&&console.timeEnd("⏱️ await DOMContentLoaded time"),this.debugMode&&console.time("⏱️ create Preloader time");const e=new D(this.params);"true"===this.preloader&&e.create(),this.debugMode&&console.timeEnd("⏱️ create Preloader time"),this.debugMode&&console.time("⏱️ Config time"),this.debugMode&&console.groupCollapsed("%c config ",N+"color:LightGray"),this.config={...n(this.params),debugConfig:i},this.debugMode&&console.groupEnd(),this.debugMode&&console.info("βš™οΈ Current config with debugConfig:",this.config),this.debugMode&&console.timeEnd("⏱️ Config time"),this.debugMode&&console.time("⏱️ DOM helpers init time");const t=new s({DOM:window.document,config:this.config});this.debugMode&&console.timeEnd("⏱️ DOM helpers init time"),this.debugMode&&console.time("⏱️ node helpers init time");const o=new a({config:this.config,DOM:t,selector:this.selector});this.debugMode&&console.timeEnd("⏱️ node helpers init time"),this.debugMode&&console.time("⏱️ await window load time"),await new Promise((e=>{window.addEventListener("load",(t=>{this.debugMode&&console.log("⏰ EVENT: window load"),e()}))})),this.debugMode&&console.timeEnd("⏱️ await window load time"),this.debugMode&&console.time("⏱️ Layout time"),this.debugMode&&console.groupCollapsed("%c Layout ",N);const r=new l({config:this.config,DOM:t,selector:this.selector,node:o});if(r.create(),this.debugMode&&console.groupEnd(),this.debugMode&&console.timeEnd("⏱️ Layout time"),!r.success)return void(this.debugMode&&console.error("Failed to create layout.\n\nWe have to interrupt the process of creating PDF preview."));this.debugMode&&console.info("%c calculate Paper params ",N),this.debugMode&&console.time("⏱️ Paper time");const h=new m({config:this.config,DOM:t,selector:this.selector,node:o,layout:r});if(this.debugMode&&console.timeEnd("⏱️ Paper time"),!h||!h.bodyHeight||!h.bodyWidth)return void(this.debugMode&&console.error("Failed to create paper calculations.\n\nWe have to interrupt the process of creating PDF preview."));this.debugMode&&console.time("⏱️ Preprocess time"),this.debugMode&&console.groupCollapsed("%c Preprocess ",N),await new S(this.config).run(),this.debugMode&&console.groupEnd(),this.debugMode&&console.timeEnd("⏱️ Preprocess time"),this.debugMode&&console.time("⏱️ Pages time"),this.debugMode&&console.groupCollapsed("%c Pages ",N);const d=new u({config:this.config,DOM:t,selector:this.selector,node:o,layout:r,referenceHeight:h.bodyHeight,referenceWidth:h.bodyWidth}).calculate();this.debugMode&&console.groupEnd(),this.debugMode&&console.timeEnd("⏱️ Pages time"),this.debugMode&&console.time("⏱️ Preview time"),this.debugMode&&console.groupCollapsed("%c Preview ",N),new f({config:this.config,DOM:t,selector:this.selector,node:o,layout:r,paper:h,pages:d}).create(),this.debugMode&&console.groupEnd(),this.debugMode&&console.timeEnd("⏱️ Preview time"),this.debugMode&&console.time("⏱️ Toc time"),new b({config:this.config,DOM:t,selector:this.selector,node:o,layout:r}).render(),this.debugMode&&console.timeEnd("⏱️ Toc time"),this.debugMode&&console.time("⏱️ Validator time"),new M({config:this.config,DOM:t,selector:this.selector,layout:r}).init(),this.debugMode&&console.timeEnd("⏱️ Validator time"),t.setAttribute(r.root,"[success]"),e.remove(),console.timeEnd("⏱️ HTML2PDF4DOC time")}}(P),T="manual"===P.init;function k(){T&&E.render()}T&&console.info("HTML2PDF4DOC in manual initialization mode"),!T&&E.render(),HTML2PDF4DOC=t})(); \ No newline at end of file diff --git a/html2print/html2print.py b/html2print/html2print.py index c1b006a..07fea16 100644 --- a/html2print/html2print.py +++ b/html2print/html2print.py @@ -20,7 +20,7 @@ from selenium.webdriver.chrome.service import Service from webdriver_manager.core.os_manager import ChromeType, OperationSystemManager -__version__ = "0.0.14" +__version__ = "0.0.15a2" PATH_TO_HTML2PDF_JS = os.path.join( os.path.dirname(os.path.join(__file__)), "html2pdf_js", "html2pdf.min.js" @@ -28,6 +28,8 @@ DEFAULT_CACHE_DIR = os.path.join(Path.home(), ".html2print", "chromedriver") +PATH_TO_CHROME_DRIVER_DEBUG_LOG = "/tmp/chromedriver.log" + # HTML2PDF.js prints unicode symbols to console. The following makes it work on # Windows which otherwise complains: # UnicodeEncodeError: 'charmap' codec can't encode characters in position 129-130: character maps to @@ -303,7 +305,10 @@ class Done(Exception): def create_webdriver( - chromedriver: Optional[str], path_to_cache_dir: str, page_load_timeout: int + chromedriver: Optional[str], + path_to_cache_dir: str, + page_load_timeout: int, + debug: bool = False, ) -> webdriver.Chrome: print("html2print: creating ChromeDriver service.", flush=True) # noqa: T201 if chromedriver is None: @@ -314,13 +319,20 @@ def create_webdriver( path_to_chrome = chromedriver print(f"html2print: ChromeDriver available at path: {path_to_chrome}") # noqa: T201 - service = Service(path_to_chrome) + if debug: + service = Service( + path_to_chrome, log_output=PATH_TO_CHROME_DRIVER_DEBUG_LOG + ) + else: + service = Service(path_to_chrome) webdriver_options = Options() webdriver_options.add_argument("start-maximized") webdriver_options.add_argument("disable-infobars") + # Doesn't seem to be needed. + # webdriver_options.add_argument('--disable-gpu') # noqa: ERA001 webdriver_options.add_argument("--disable-extensions") - webdriver_options.add_argument("--headless") + webdriver_options.add_argument("--headless=chrome") # FIXME: This is not nice but otherwise it does not work in Ubuntu 24-based Docker image. # https://github.com/SeleniumHQ/selenium/issues/15327#issuecomment-2689287561 webdriver_options.add_argument("--no-sandbox") @@ -415,6 +427,14 @@ def main(): "HTML file, load it, and let HTML2PDF.js finish its job." ), ) + command_parser_print.add_argument( + "--debug", + action="store_true", + help=( + f"Enables ChromeDriver logging to a file: " + f"{PATH_TO_CHROME_DRIVER_DEBUG_LOG}." + ), + ) command_parser_print.add_argument( "paths", nargs="+", help="Paths to input HTML file." ) @@ -442,7 +462,10 @@ def main(): args.cache_dir if args.cache_dir is not None else DEFAULT_CACHE_DIR ) driver = create_webdriver( - args.chromedriver, path_to_cache_dir, page_load_timeout + args.chromedriver, + path_to_cache_dir, + page_load_timeout, + debug=args.debug, ) @atexit.register diff --git a/tasks.py b/tasks.py index 4ade146..4aff1a3 100644 --- a/tasks.py +++ b/tasks.py @@ -30,6 +30,7 @@ def run_invoke( cmd, environment: Optional[dict] = None, warn: bool = False, + pty: bool = False, ) -> invoke.runners.Result: def one_line_command(string): return re.sub("\\s+", " ", string).strip() @@ -39,7 +40,7 @@ def one_line_command(string): env=environment, hide=False, warn=warn, - pty=False, + pty=pty, echo=True, ) @@ -267,3 +268,64 @@ def release(context, test_pypi=False, username=None, password=None): {user_password} """, ) + + +@task(aliases=["bd"]) +def build_docker( + context, + image: str = "html2print:latest", + no_cache: bool = False, + source="pypi", +): + no_cache_argument = "--no-cache" if no_cache else "" + run_invoke( + context, + f""" + docker build . + --build-arg HTML2PRINT_SOURCE={source} + -t {image} + {no_cache_argument} + """, + ) + + +@task(aliases=["rd"]) +def run_docker( + context, image: str = "html2print:latest", command: Optional[str] = None +): + command_argument = ( + f'/bin/bash -c "{command}"' if command is not None else "" + ) + entry_point_argument = '--entrypoint=""' if command_argument else "" + + run_invoke( + context, + f""" + docker run + --name html2print + --rm + -it + -v "$(pwd):/data" + {entry_point_argument} + {image} + {command_argument} + """, + pty=True, + ) + + +@task(aliases=["td"]) +def test_docker(context, image: str = "html2print:latest"): + run_invoke( + context, + """ + mkdir -p output/ && chmod 777 output/ + """, + ) + run_docker( + context, + image=image, + command=( + "cd tests/integration/01_hello_world && html2print print --debug index.html /data/output/index.pdf && cat /tmp/chromedriver.log" + ), + ) diff --git a/tests/integration/01_hello_world/index.html b/tests/integration/01_hello_world/index.html index b49f361..81a4dc3 100644 --- a/tests/integration/01_hello_world/index.html +++ b/tests/integration/01_hello_world/index.html @@ -4,8 +4,8 @@ Test page - - + + diff --git a/tests/integration/02_two_pages/index.html b/tests/integration/02_two_pages/index.html index a263dba..383775f 100644 --- a/tests/integration/02_two_pages/index.html +++ b/tests/integration/02_two_pages/index.html @@ -4,8 +4,8 @@ Test page - - + + diff --git a/tests/integration/03_cache_dir_argument/index.html b/tests/integration/03_cache_dir_argument/index.html index b49f361..81a4dc3 100644 --- a/tests/integration/03_cache_dir_argument/index.html +++ b/tests/integration/03_cache_dir_argument/index.html @@ -4,8 +4,8 @@ Test page - - + + diff --git a/tests/integration/04_two_documents/index1.html b/tests/integration/04_two_documents/index1.html index b49f361..81a4dc3 100644 --- a/tests/integration/04_two_documents/index1.html +++ b/tests/integration/04_two_documents/index1.html @@ -4,8 +4,8 @@ Test page - - + + diff --git a/tests/integration/04_two_documents/index2.html b/tests/integration/04_two_documents/index2.html index b49f361..81a4dc3 100644 --- a/tests/integration/04_two_documents/index2.html +++ b/tests/integration/04_two_documents/index2.html @@ -4,8 +4,8 @@ Test page - - + + diff --git a/tests/integration/05_page_load_timeout/index1.html b/tests/integration/05_page_load_timeout/index1.html index db0d82f..ac8cb15 100644 --- a/tests/integration/05_page_load_timeout/index1.html +++ b/tests/integration/05_page_load_timeout/index1.html @@ -4,8 +4,8 @@ Test page - - + + diff --git a/tests/integration/main.css b/tests/integration/main.css new file mode 100644 index 0000000..60734e9 --- /dev/null +++ b/tests/integration/main.css @@ -0,0 +1,53 @@ +body { + font-size: 16px; + line-height: 1.25; +} + +[filler] { + background:repeating-linear-gradient( + -45deg, + rgba(100, 100, 100, .1), + rgba(100, 100, 100, .1) 10px, + rgba(100, 100, 100, .15) 10px, + rgba(100, 100, 100, .15) 20px + ); +} + +[filler='blue'] { + background:repeating-linear-gradient( + -45deg, + rgba(0, 175, 255, .1), + rgba(0, 175, 255, .1) 10px, + rgba(0, 175, 255, .15) 10px, + rgba(0, 175, 255, .15) 20px + ); +} + +[filler='red'] { + background:repeating-linear-gradient( + -45deg, + rgba(255, 25, 0, .1), + rgba(255, 25, 0, .1) 10px, + rgba(255, 25, 0, .15) 10px, + rgba(255, 25, 0, .15) 20px + ); +} + +[filler='green'] { + background:repeating-linear-gradient( + -45deg, + rgba(0, 175, 0, .1), + rgba(0, 175, 0, .1) 10px, + rgba(0, 175, 0, .15) 10px, + rgba(0, 175, 0, .15) 20px + ); +} + +[floater] { + outline: rgba(255, 0, 200, 0.2) 1px solid; +} + +[floater='solid'] { + outline: rgba(255, 0, 55, 0.55) 1px solid; + overflow: auto; +}