html2pdf4doc 0.0.17__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: html2pdf4doc
|
|
3
|
+
Version: 0.0.17
|
|
4
|
+
Summary: Python client for HTML2PDF4Doc JavaScript library.
|
|
5
|
+
Project-URL: Changelog, https://github.com/mettta/html2pdf_python/releases/
|
|
6
|
+
Project-URL: Homepage, https://github.com/mettta/html2pdf_python/
|
|
7
|
+
Project-URL: Source, https://github.com/mettta/html2pdf_python/
|
|
8
|
+
Author-email: Stanislav Pankevich <s.pankevich@gmail.com>, Maryna Balioura <mettta@gmail.com>
|
|
9
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
17
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
18
|
+
Requires-Python: >=3.8
|
|
19
|
+
Requires-Dist: requests
|
|
20
|
+
Requires-Dist: selenium
|
|
21
|
+
Requires-Dist: webdriver-manager
|
|
22
|
+
Provides-Extra: development
|
|
23
|
+
Requires-Dist: invoke>=1.4.1; extra == 'development'
|
|
24
|
+
Requires-Dist: tox>=4.4.8; extra == 'development'
|
|
File without changes
|
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import atexit
|
|
3
|
+
import base64
|
|
4
|
+
import os.path
|
|
5
|
+
import platform
|
|
6
|
+
import re
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
import zipfile
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from time import sleep
|
|
13
|
+
from typing import Dict, List, Optional
|
|
14
|
+
|
|
15
|
+
import requests
|
|
16
|
+
from requests import Response
|
|
17
|
+
from selenium import webdriver
|
|
18
|
+
from selenium.webdriver.chrome.options import Options
|
|
19
|
+
from selenium.webdriver.chrome.service import Service
|
|
20
|
+
from webdriver_manager.core.os_manager import ChromeType, OperationSystemManager
|
|
21
|
+
|
|
22
|
+
__version__ = "0.0.17"
|
|
23
|
+
|
|
24
|
+
PATH_TO_HTML2PDF4DOC_JS = os.path.join(
|
|
25
|
+
os.path.dirname(os.path.join(__file__)),
|
|
26
|
+
"html2pdf4doc_js",
|
|
27
|
+
"html2pdf4doc.min.js",
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
DEFAULT_CACHE_DIR = os.path.join(Path.home(), ".html2pdf4doc", "chromedriver")
|
|
31
|
+
|
|
32
|
+
PATH_TO_CHROME_DRIVER_DEBUG_LOG = "/tmp/chromedriver.log"
|
|
33
|
+
|
|
34
|
+
# HTML2PDF4Doc.js prints unicode symbols to console. The following makes it work on
|
|
35
|
+
# Windows which otherwise complains:
|
|
36
|
+
# UnicodeEncodeError: 'charmap' codec can't encode characters in position 129-130: character maps to <undefined>
|
|
37
|
+
# How to make python 3 print() utf8
|
|
38
|
+
# https://stackoverflow.com/questions/3597480/how-to-make-python-3-print-utf8
|
|
39
|
+
sys.stdout = open(sys.stdout.fileno(), mode="w", encoding="utf8", closefd=False)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ChromeDriverManager:
|
|
43
|
+
def get_chrome_driver(self, path_to_cache_dir: str) -> str:
|
|
44
|
+
chrome_version: Optional[str] = self.get_chrome_version()
|
|
45
|
+
|
|
46
|
+
# If Web Driver Manager cannot detect Chrome, it returns None.
|
|
47
|
+
if chrome_version is None:
|
|
48
|
+
raise RuntimeError(
|
|
49
|
+
"html2pdf4doc: "
|
|
50
|
+
"Web Driver Manager could not detect an existing Chrome installation."
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
chrome_major_version = chrome_version.split(".")[0]
|
|
54
|
+
|
|
55
|
+
print( # noqa: T201
|
|
56
|
+
f"html2pdf4doc: Installed Chrome version: {chrome_version}"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
system_map = {
|
|
60
|
+
"Windows": "win32",
|
|
61
|
+
"Darwin": "mac-arm64"
|
|
62
|
+
if platform.machine() == "arm64"
|
|
63
|
+
else "mac-x64",
|
|
64
|
+
"Linux": "linux64",
|
|
65
|
+
}
|
|
66
|
+
os_type = system_map[platform.system()]
|
|
67
|
+
is_windows = platform.system() == "Windows"
|
|
68
|
+
|
|
69
|
+
print( # noqa: T201
|
|
70
|
+
f"html2pdf4doc: OS system: {platform.system()}, OS type: {os_type}."
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
path_to_cached_chrome_driver_dir = os.path.join(
|
|
74
|
+
path_to_cache_dir, chrome_major_version
|
|
75
|
+
)
|
|
76
|
+
path_to_cached_chrome_driver = os.path.join(
|
|
77
|
+
path_to_cached_chrome_driver_dir,
|
|
78
|
+
f"chromedriver-{os_type}",
|
|
79
|
+
"chromedriver",
|
|
80
|
+
)
|
|
81
|
+
if is_windows:
|
|
82
|
+
path_to_cached_chrome_driver += ".exe"
|
|
83
|
+
|
|
84
|
+
if os.path.isfile(path_to_cached_chrome_driver):
|
|
85
|
+
print( # noqa: T201
|
|
86
|
+
f"html2pdf4doc: ChromeDriver exists in the local cache: "
|
|
87
|
+
f"{path_to_cached_chrome_driver}"
|
|
88
|
+
)
|
|
89
|
+
return path_to_cached_chrome_driver
|
|
90
|
+
print( # noqa: T201
|
|
91
|
+
f"html2pdf4doc: ChromeDriver does not exist in the local cache: "
|
|
92
|
+
f"{path_to_cached_chrome_driver}"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
path_to_downloaded_chrome_driver = self._download_chromedriver(
|
|
96
|
+
chrome_major_version,
|
|
97
|
+
os_type,
|
|
98
|
+
path_to_cached_chrome_driver_dir,
|
|
99
|
+
path_to_cached_chrome_driver,
|
|
100
|
+
)
|
|
101
|
+
assert os.path.isfile(path_to_downloaded_chrome_driver)
|
|
102
|
+
os.chmod(path_to_downloaded_chrome_driver, 0o755)
|
|
103
|
+
|
|
104
|
+
return path_to_downloaded_chrome_driver
|
|
105
|
+
|
|
106
|
+
@staticmethod
|
|
107
|
+
def _download_chromedriver(
|
|
108
|
+
chrome_major_version: str,
|
|
109
|
+
os_type: str,
|
|
110
|
+
path_to_driver_cache_dir: str,
|
|
111
|
+
path_to_cached_chrome_driver: str,
|
|
112
|
+
) -> str:
|
|
113
|
+
url = "https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json"
|
|
114
|
+
response = ChromeDriverManager.send_http_get_request(url)
|
|
115
|
+
if response is None:
|
|
116
|
+
raise RuntimeError(
|
|
117
|
+
"Could not download known-good-versions-with-downloads.json"
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
response = response.json()
|
|
121
|
+
if response is None:
|
|
122
|
+
raise RuntimeError(
|
|
123
|
+
"Could not parse known-good-versions-with-downloads.json"
|
|
124
|
+
)
|
|
125
|
+
assert isinstance(response, dict)
|
|
126
|
+
|
|
127
|
+
matching_versions = [
|
|
128
|
+
item
|
|
129
|
+
for item in response["versions"]
|
|
130
|
+
if item["version"].startswith(chrome_major_version)
|
|
131
|
+
]
|
|
132
|
+
|
|
133
|
+
if not matching_versions:
|
|
134
|
+
raise RuntimeError(
|
|
135
|
+
f"No compatible ChromeDriver found for Chrome version {chrome_major_version}"
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
latest_version = matching_versions[-1]
|
|
139
|
+
|
|
140
|
+
driver_url: str
|
|
141
|
+
chrome_downloadable_versions = latest_version["downloads"][
|
|
142
|
+
"chromedriver"
|
|
143
|
+
]
|
|
144
|
+
for chrome_downloadable_version_ in chrome_downloadable_versions:
|
|
145
|
+
if chrome_downloadable_version_["platform"] == os_type:
|
|
146
|
+
driver_url = chrome_downloadable_version_["url"]
|
|
147
|
+
break
|
|
148
|
+
else:
|
|
149
|
+
raise RuntimeError(
|
|
150
|
+
f"Could not find a downloadable URL from downloadable versions: {chrome_downloadable_versions}"
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
print( # noqa: T201
|
|
154
|
+
f"html2pdf4doc: downloading ChromeDriver from: {driver_url}"
|
|
155
|
+
)
|
|
156
|
+
response = ChromeDriverManager.send_http_get_request(driver_url)
|
|
157
|
+
|
|
158
|
+
if response is None:
|
|
159
|
+
raise RuntimeError(
|
|
160
|
+
f"Could not download ChromeDriver from {driver_url}"
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
Path(path_to_driver_cache_dir).mkdir(parents=True, exist_ok=True)
|
|
164
|
+
zip_path = os.path.join(path_to_driver_cache_dir, "chromedriver.zip")
|
|
165
|
+
print( # noqa: T201
|
|
166
|
+
f"html2pdf4doc: saving downloaded ChromeDriver to path: {zip_path}"
|
|
167
|
+
)
|
|
168
|
+
with open(zip_path, "wb") as file:
|
|
169
|
+
file.write(response.content)
|
|
170
|
+
|
|
171
|
+
with zipfile.ZipFile(zip_path, "r") as zip_ref:
|
|
172
|
+
zip_ref.extractall(path_to_driver_cache_dir)
|
|
173
|
+
|
|
174
|
+
print( # noqa: T201
|
|
175
|
+
f"html2pdf4doc: ChromeDriver downloaded to: {path_to_cached_chrome_driver}"
|
|
176
|
+
)
|
|
177
|
+
return path_to_cached_chrome_driver
|
|
178
|
+
|
|
179
|
+
@staticmethod
|
|
180
|
+
def send_http_get_request(url: str) -> Response:
|
|
181
|
+
last_error: Optional[Exception] = None
|
|
182
|
+
for attempt in range(1, 4):
|
|
183
|
+
print( # noqa: T201
|
|
184
|
+
f"html2pdf4doc: sending GET request attempt {attempt}: {url}"
|
|
185
|
+
)
|
|
186
|
+
try:
|
|
187
|
+
return requests.get(url, timeout=(5, 5))
|
|
188
|
+
except requests.exceptions.ConnectTimeout as connect_timeout_:
|
|
189
|
+
last_error = connect_timeout_
|
|
190
|
+
except requests.exceptions.ReadTimeout as read_timeout_:
|
|
191
|
+
last_error = read_timeout_
|
|
192
|
+
except Exception as exception_:
|
|
193
|
+
raise AssertionError(
|
|
194
|
+
"html2pdf4doc: unknown exception", exception_
|
|
195
|
+
) from None
|
|
196
|
+
print( # noqa: T201
|
|
197
|
+
f"html2pdf4doc: "
|
|
198
|
+
f"failed to get response for URL: {url} with error: {last_error}"
|
|
199
|
+
)
|
|
200
|
+
raise RuntimeError(
|
|
201
|
+
f"GET request failed after 3 attempts: {url}"
|
|
202
|
+
) from last_error
|
|
203
|
+
|
|
204
|
+
@staticmethod
|
|
205
|
+
def get_chrome_version() -> Optional[str]:
|
|
206
|
+
# Special case: GitHub Actions macOS CI machines have both
|
|
207
|
+
# Google Chrome for Testing and normal Google Chrome installed, and
|
|
208
|
+
# sometimes their versions are of different major version families.
|
|
209
|
+
# The solution is to check if the Google Chrome for Testing is available,
|
|
210
|
+
# and use its version instead of the normal one.
|
|
211
|
+
if platform.system() == "Darwin":
|
|
212
|
+
chrome_path = "/Applications/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing"
|
|
213
|
+
try:
|
|
214
|
+
print( # noqa: T201
|
|
215
|
+
"html2pdf4doc: "
|
|
216
|
+
"checking if there is Google Chrome for Testing instead of "
|
|
217
|
+
"a normal Chrome available."
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
version_output = subprocess.run(
|
|
221
|
+
[chrome_path, "--version"],
|
|
222
|
+
capture_output=True,
|
|
223
|
+
text=True,
|
|
224
|
+
check=True,
|
|
225
|
+
)
|
|
226
|
+
chrome_version = version_output.stdout.strip()
|
|
227
|
+
match = re.search(r"\d+(\.\d+)+", chrome_version)
|
|
228
|
+
if not match:
|
|
229
|
+
raise RuntimeError(
|
|
230
|
+
"Cannot extract the version part using regex."
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
chrome_version = match.group(0)
|
|
234
|
+
|
|
235
|
+
print( # noqa: T201
|
|
236
|
+
f"html2pdf4doc: Google Chrome for Testing Version: {chrome_version}"
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
return chrome_version
|
|
240
|
+
except FileNotFoundError:
|
|
241
|
+
print("html2pdf4doc: Chrome for Testing not available.") # noqa: T201
|
|
242
|
+
except Exception as e:
|
|
243
|
+
print( # noqa: T201
|
|
244
|
+
f"html2pdf4doc: Error getting Google Chrome for Testing version: {e}"
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
os_manager = OperationSystemManager(os_type=None) # type: ignore[no-untyped-call]
|
|
248
|
+
version: str = os_manager.get_browser_version_from_os(ChromeType.GOOGLE) # type: ignore[no-untyped-call]
|
|
249
|
+
return version
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def get_inches_from_millimeters(mm: float) -> float:
|
|
253
|
+
return mm / 25.4
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def get_pdf_from_html(driver: webdriver.Chrome, url: str) -> bytes:
|
|
257
|
+
print(f"html2pdf4doc: opening URL with ChromeDriver: {url}") # noqa: T201
|
|
258
|
+
|
|
259
|
+
driver.get(url)
|
|
260
|
+
|
|
261
|
+
# https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-printToPDF
|
|
262
|
+
calculated_print_options = {
|
|
263
|
+
"landscape": False,
|
|
264
|
+
"displayHeaderFooter": False,
|
|
265
|
+
"printBackground": True,
|
|
266
|
+
# This is an experimental feature that generates a document outline
|
|
267
|
+
# (table of contents).
|
|
268
|
+
"generateDocumentOutline": True,
|
|
269
|
+
# Whether to prefer page size as defined by css. Defaults to
|
|
270
|
+
# false, in which case the content will be scaled to fit the paper size.
|
|
271
|
+
"preferCSSPageSize": True,
|
|
272
|
+
# Paper width in inches. Defaults to 8.5 inches.
|
|
273
|
+
"paperWidth": get_inches_from_millimeters(210),
|
|
274
|
+
# Paper height in inches. Defaults to 11 inches.
|
|
275
|
+
"paperHeight": get_inches_from_millimeters(297),
|
|
276
|
+
# WIP: Changing the margin settings has no effect.
|
|
277
|
+
# Top margin in inches. Defaults to 1cm (~0.4 inches).
|
|
278
|
+
"marginTop": get_inches_from_millimeters(12),
|
|
279
|
+
# Bottom margin in inches. Defaults to 1cm (~0.4 inches).
|
|
280
|
+
"marginBottom": get_inches_from_millimeters(12),
|
|
281
|
+
# Left margin in inches. Defaults to 1cm (~0.4 inches).
|
|
282
|
+
"marginLeft": get_inches_from_millimeters(21),
|
|
283
|
+
# Right margin in inches. Defaults to 1cm (~0.4 inches).
|
|
284
|
+
"marginRight": get_inches_from_millimeters(21),
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
class Done(Exception):
|
|
288
|
+
pass
|
|
289
|
+
|
|
290
|
+
datetime_start = datetime.today()
|
|
291
|
+
|
|
292
|
+
logs: List[Dict[str, str]] = []
|
|
293
|
+
try:
|
|
294
|
+
while True:
|
|
295
|
+
logs = driver.get_log("browser") # type: ignore[no-untyped-call]
|
|
296
|
+
for entry_ in logs:
|
|
297
|
+
if "[HTML2PDF4DOC] Total time:" in entry_["message"]:
|
|
298
|
+
print("success: HTML2PDF4Doc completed its job.") # noqa: T201
|
|
299
|
+
raise Done
|
|
300
|
+
if (datetime.today() - datetime_start).total_seconds() > 60:
|
|
301
|
+
raise TimeoutError
|
|
302
|
+
sleep(0.5)
|
|
303
|
+
except Done:
|
|
304
|
+
pass
|
|
305
|
+
except TimeoutError:
|
|
306
|
+
print( # noqa: T201
|
|
307
|
+
"error: html2pdf4doc: "
|
|
308
|
+
"could not receive a successful completion status from HTML2PDF4Doc."
|
|
309
|
+
)
|
|
310
|
+
sys.exit(1)
|
|
311
|
+
|
|
312
|
+
print("html2pdf4doc: JS logs from the print session:") # noqa: T201
|
|
313
|
+
print('"""') # noqa: T201
|
|
314
|
+
for entry in logs:
|
|
315
|
+
print(entry) # noqa: T201
|
|
316
|
+
print('"""') # noqa: T201
|
|
317
|
+
|
|
318
|
+
#
|
|
319
|
+
# Execute Print command with ChromeDriver.
|
|
320
|
+
#
|
|
321
|
+
print("html2pdf4doc: executing print command with ChromeDriver.") # noqa: T201
|
|
322
|
+
result = driver.execute_cdp_cmd("Page.printToPDF", calculated_print_options)
|
|
323
|
+
|
|
324
|
+
data = base64.b64decode(result["data"])
|
|
325
|
+
return data
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def create_webdriver(
|
|
329
|
+
chromedriver_argument: Optional[str],
|
|
330
|
+
path_to_cache_dir: str,
|
|
331
|
+
page_load_timeout: int,
|
|
332
|
+
debug: bool = False,
|
|
333
|
+
) -> webdriver.Chrome:
|
|
334
|
+
print("html2pdf4doc: creating ChromeDriver service.", flush=True) # noqa: T201
|
|
335
|
+
|
|
336
|
+
path_to_chrome_driver: str
|
|
337
|
+
if chromedriver_argument is None:
|
|
338
|
+
path_to_chrome_driver = ChromeDriverManager().get_chrome_driver(
|
|
339
|
+
path_to_cache_dir
|
|
340
|
+
)
|
|
341
|
+
else:
|
|
342
|
+
path_to_chrome_driver = chromedriver_argument
|
|
343
|
+
print( # noqa: T201
|
|
344
|
+
f"html2pdf4doc: ChromeDriver available at path: {path_to_chrome_driver}"
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
if debug:
|
|
348
|
+
service = Service(
|
|
349
|
+
path_to_chrome_driver, log_output=PATH_TO_CHROME_DRIVER_DEBUG_LOG
|
|
350
|
+
)
|
|
351
|
+
else:
|
|
352
|
+
service = Service(path_to_chrome_driver)
|
|
353
|
+
|
|
354
|
+
webdriver_options = Options()
|
|
355
|
+
webdriver_options.add_argument("start-maximized")
|
|
356
|
+
webdriver_options.add_argument("disable-infobars")
|
|
357
|
+
# Doesn't seem to be needed.
|
|
358
|
+
# webdriver_options.add_argument('--disable-gpu') # noqa: ERA001
|
|
359
|
+
webdriver_options.add_argument("--disable-extensions")
|
|
360
|
+
|
|
361
|
+
# Use --headless=new, as it seems to be more stable on Windows (available since Chrome 109).
|
|
362
|
+
# see https://www.selenium.dev/blog/2023/headless-is-going-away/
|
|
363
|
+
webdriver_options.add_argument("--headless=new")
|
|
364
|
+
|
|
365
|
+
# Docker disables some syscalls that are required for Chrome's sandbox to work.
|
|
366
|
+
# https://stackoverflow.com/questions/68855734/how-to-setup-chrome-sandbox-on-docker-container
|
|
367
|
+
# We prefer isolation of the container over isolation of tabs within Chrome,
|
|
368
|
+
# and thus disable the sandbox.
|
|
369
|
+
# See also:
|
|
370
|
+
# https://github.com/SeleniumHQ/selenium/issues/15327#issuecomment-2689287561
|
|
371
|
+
webdriver_options.add_argument("--no-sandbox")
|
|
372
|
+
|
|
373
|
+
# The Chrome option --disable-dev-shm-usage disables the use of /dev/shm
|
|
374
|
+
# (shared memory) for temporary storage in Chrome.
|
|
375
|
+
# By default, Chrome uses /dev/shm for storing temporary files to improve
|
|
376
|
+
# performance. However, in environments with limited shared memory (such as
|
|
377
|
+
# Docker containers), this can lead to crashes or issues due to insufficient
|
|
378
|
+
# space.
|
|
379
|
+
webdriver_options.add_argument("--disable-dev-shm-usage")
|
|
380
|
+
|
|
381
|
+
webdriver_options.add_experimental_option("useAutomationExtension", False)
|
|
382
|
+
webdriver_options.add_experimental_option(
|
|
383
|
+
"excludeSwitches", ["enable-automation"]
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
# Enable the capturing of everything in JS console.
|
|
387
|
+
webdriver_options.set_capability("goog:loggingPrefs", {"browser": "ALL"})
|
|
388
|
+
|
|
389
|
+
print("html2pdf4doc: creating ChromeDriver.", flush=True) # noqa: T201
|
|
390
|
+
|
|
391
|
+
driver = webdriver.Chrome(
|
|
392
|
+
options=webdriver_options,
|
|
393
|
+
service=service,
|
|
394
|
+
)
|
|
395
|
+
driver.set_page_load_timeout(page_load_timeout)
|
|
396
|
+
|
|
397
|
+
return driver
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def main() -> None:
|
|
401
|
+
if not os.path.isfile(PATH_TO_HTML2PDF4DOC_JS):
|
|
402
|
+
raise RuntimeError(
|
|
403
|
+
f"Corrupted html2pdf4doc package bundle. "
|
|
404
|
+
f"The html2pdf4doc JS file is missing at path: "
|
|
405
|
+
f"{PATH_TO_HTML2PDF4DOC_JS}."
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
parser = argparse.ArgumentParser(description="html2pdf4doc printer script.")
|
|
409
|
+
|
|
410
|
+
parser.add_argument(
|
|
411
|
+
"-v", "--version", action="version", version=__version__
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
command_subparsers = parser.add_subparsers(title="command", dest="command")
|
|
415
|
+
command_subparsers.required = True
|
|
416
|
+
|
|
417
|
+
print(f"html2pdf4doc: version {__version__}") # noqa: T201
|
|
418
|
+
|
|
419
|
+
#
|
|
420
|
+
# Get driver command.
|
|
421
|
+
#
|
|
422
|
+
command_parser_get_driver = command_subparsers.add_parser(
|
|
423
|
+
"get_driver",
|
|
424
|
+
help="Check if ChromeDriver already exists locally. If not, download it.",
|
|
425
|
+
description="",
|
|
426
|
+
)
|
|
427
|
+
command_parser_get_driver.add_argument(
|
|
428
|
+
"--cache-dir",
|
|
429
|
+
type=str,
|
|
430
|
+
help="Optional path to a cache directory whereto the ChromeDriver is downloaded.",
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
#
|
|
434
|
+
# Print command.
|
|
435
|
+
#
|
|
436
|
+
command_parser_print = command_subparsers.add_parser(
|
|
437
|
+
"print",
|
|
438
|
+
help="Main print command",
|
|
439
|
+
description="",
|
|
440
|
+
)
|
|
441
|
+
command_parser_print.add_argument(
|
|
442
|
+
"--chromedriver",
|
|
443
|
+
type=str,
|
|
444
|
+
help="Optional chromedriver path. Downloaded if not given.",
|
|
445
|
+
)
|
|
446
|
+
command_parser_print.add_argument(
|
|
447
|
+
"--cache-dir",
|
|
448
|
+
type=str,
|
|
449
|
+
help="Optional path to a cache directory whereto the ChromeDriver is downloaded.",
|
|
450
|
+
)
|
|
451
|
+
command_parser_print.add_argument(
|
|
452
|
+
"--page-load-timeout",
|
|
453
|
+
type=int,
|
|
454
|
+
default=2 * 60,
|
|
455
|
+
# 10 minutes should be enough to print even the largest documents.
|
|
456
|
+
choices=range(0, 10 * 60),
|
|
457
|
+
help=(
|
|
458
|
+
"How long shall html2pdf4doc Python driver wait while the "
|
|
459
|
+
"Chrome Driver is printing a given HTML page to PDF. "
|
|
460
|
+
"This is mainly driven by the time it takes for Chrome to open an "
|
|
461
|
+
"HTML file, load it, and let HTML2PDF4Doc.js finish its job."
|
|
462
|
+
),
|
|
463
|
+
)
|
|
464
|
+
command_parser_print.add_argument(
|
|
465
|
+
"--debug",
|
|
466
|
+
action="store_true",
|
|
467
|
+
help=(
|
|
468
|
+
f"Enables ChromeDriver logging to a file: "
|
|
469
|
+
f"{PATH_TO_CHROME_DRIVER_DEBUG_LOG}."
|
|
470
|
+
),
|
|
471
|
+
)
|
|
472
|
+
command_parser_print.add_argument(
|
|
473
|
+
"paths", nargs="+", help="Paths to input HTML file."
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
args = parser.parse_args()
|
|
477
|
+
|
|
478
|
+
path_to_cache_dir: str
|
|
479
|
+
if args.command == "get_driver":
|
|
480
|
+
path_to_cache_dir = (
|
|
481
|
+
args.cache_dir if args.cache_dir is not None else DEFAULT_CACHE_DIR
|
|
482
|
+
)
|
|
483
|
+
|
|
484
|
+
path_to_chrome = ChromeDriverManager().get_chrome_driver(
|
|
485
|
+
path_to_cache_dir
|
|
486
|
+
)
|
|
487
|
+
print(f"html2pdf4doc: ChromeDriver available at path: {path_to_chrome}") # noqa: T201
|
|
488
|
+
sys.exit(0)
|
|
489
|
+
|
|
490
|
+
elif args.command == "print":
|
|
491
|
+
paths: List[str] = args.paths
|
|
492
|
+
|
|
493
|
+
page_load_timeout: int = args.page_load_timeout
|
|
494
|
+
|
|
495
|
+
path_to_cache_dir = (
|
|
496
|
+
args.cache_dir if args.cache_dir is not None else DEFAULT_CACHE_DIR
|
|
497
|
+
)
|
|
498
|
+
driver: webdriver.Chrome = create_webdriver(
|
|
499
|
+
args.chromedriver,
|
|
500
|
+
path_to_cache_dir,
|
|
501
|
+
page_load_timeout,
|
|
502
|
+
debug=args.debug,
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
@atexit.register
|
|
506
|
+
def exit_handler() -> None:
|
|
507
|
+
print("html2pdf4doc: exit handler: quitting the ChromeDriver.") # noqa: T201
|
|
508
|
+
driver.quit()
|
|
509
|
+
|
|
510
|
+
assert len(paths) % 2 == 0, (
|
|
511
|
+
f"Expecting an even number of input/output path arguments: {paths}."
|
|
512
|
+
)
|
|
513
|
+
for current_pair_idx in range(0, len(paths), 2):
|
|
514
|
+
path_to_input_html = paths[current_pair_idx]
|
|
515
|
+
path_to_output_pdf = paths[current_pair_idx + 1]
|
|
516
|
+
|
|
517
|
+
assert os.path.isfile(path_to_input_html), path_to_input_html
|
|
518
|
+
|
|
519
|
+
path_to_output_pdf_dir = os.path.dirname(path_to_output_pdf)
|
|
520
|
+
Path(path_to_output_pdf_dir).mkdir(parents=True, exist_ok=True)
|
|
521
|
+
|
|
522
|
+
url = Path(os.path.abspath(path_to_input_html)).as_uri()
|
|
523
|
+
|
|
524
|
+
pdf_bytes = get_pdf_from_html(driver, url)
|
|
525
|
+
with open(path_to_output_pdf, "wb") as f:
|
|
526
|
+
f.write(pdf_bytes)
|
|
527
|
+
else:
|
|
528
|
+
print("html2pdf4doc: unknown command.") # noqa: T201
|
|
529
|
+
sys.exit(1)
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
if __name__ == "__main__":
|
|
533
|
+
main()
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! Version: 0.2.2 */
|
|
2
|
+
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:!0,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,t.debugMode&&console.info("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.contentFlow} {\n background:repeating-linear-gradient(\n -45deg,\n rgba(222, 222, 222, .1),\n rgba(222, 222, 222, .1) 10px,\n rgba(222, 222, 222, .2) 10px,\n rgba(222, 222, 222, .2) 20px\n );\n}\n\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)}))}findBetterPageStart(e,t,o,n){let i=e;const s=this.getTop(t,n);for(;;){const e=this.findFirstChildParent(i,o);if(e&&e!==i){i=e;continue}const t=this.findPreviousNonHangingsFromPage(i,s,n);if(!t||t===i)break;i=t}return 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<r?s:r,a=Math.trunc(t*l),h=Math.trunc(o*l);this._DOM.setStyles(e,{height:a+"px",width:h+"px"}),this._DOM.setAttribute(e,"height",`${a}px`),this._DOM.setAttribute(e,"width",`${h}px`)}create(e,t){let o;if(e){const t=e.charAt(0);if(t.match(/[#\[\.]/))o=this._DOM.createElement("div"),this._DOM.setAttribute(o,e);else{if(!t.match(/[a-zA-Z]/))return void console.assert(!1,"Expected valid html selector ot tag name, but received:",e);o=this._DOM.createElement(e)}}else o=this._DOM.createElement("div");return t&&this._DOM.setInnerHTML(o,t),o}createNeutral(){return this.create(this._selector.neutral)}createTextLine(){return this.create(this._selector.textLine)}createTextGroup(){return this.create(this._selector.textGroup)}createWithFlagNoBreak(e){const t=this.create(this._selector.flagNoBreak);return e&&this._DOM.setStyles(t,e),t}createPrintPageBreak(){return this.create(this._selector.printPageBreak)}createComplexTextBlock(){return this.create(this._selector.complexTextBlock)}createTestNodeFrom(e){const t=this._DOM.cloneNodeWrapper(e);return this._DOM.setAttribute(t,".test-node"),this._DOM.setStyles(t,{position:"absolute",background:"rgb(255 239 177)",width:this.getMaxWidth(e)+"px"}),t}createWord(e,t){const o=this.create(this._selector.word);return this._DOM.setInnerHTML(o,e),o.dataset.index=t,o}createSignpost(e,t=24){const o=this.create();return this._DOM.setStyles(o,{display:"flex",flexWrap:"nowrap",alignItems:"center",justifyContent:"center",textAlign:"center",fontSize:"8px",fontFamily:"sans-serif",letterSpacing:"1px",textTransform:"uppercase",height:t+"px"}),e&&this._DOM.setInnerHTML(o,e),o}createTable({wrapper:e,caption:t,colgroup:o,thead:n,tfoot:i,tbody:s}){const r=e||this.create("table"),l=this.create("TBODY");return t&&this._DOM.insertAtEnd(r,t),o&&this._DOM.insertAtEnd(r,o),n&&this._DOM.insertAtEnd(r,n),s&&this._DOM.insertAtEnd(l,...s),this._DOM.insertAtEnd(r,l),i&&this._DOM.insertAtEnd(r,i),r}markProcessed(e,t){this._markupDebugMode&&this._DOM.setAttribute(e,this._selector.processed,"🏷️ "+t)}setFlagNoBreak(e){this._DOM.setAttribute(e,this._selector.flagNoBreak)}setFlagNoHanging(e,t){this._DOM.setAttribute(e,this._selector.flagNoHanging,t)}markPageStartElement(e,t){this._DOM.setAttribute(e,this._selector.pageStartMarker,t)}unmarkPageStartElement(e){this._DOM.removeAttribute(e,this._selector.pageStartMarker)}markPartNodesWithClass(e){e.forEach((e=>{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,"<tr><td></td></tr>"),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="!<br />".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<this._minParagraphBreakableLines)return this._debug._&&console.log("groupedPartiallyLinedChildren.length < this._minParagraphBreakableLines",i.length,"<",this._minParagraphBreakableLines),this._end("NOT _splitComplexTextBlockIntoLines"),[];const s=i.slice(0,this._minParagraphLeftLines).flat(),r=i.slice(-this._minParagraphDanglingLines).flat();this._debug._&&console.log("groupedPartiallyLinedChildren",[...i],"\n","minLeftLines =",this._minParagraphLeftLines,"\n",s,"\n","minDanglingLines =",this._minParagraphDanglingLines,"\n",r),i.splice(0,this._minParagraphLeftLines,s),i.splice(-this._minParagraphDanglingLines,this._minParagraphDanglingLines,r);const l=i.map(((e,t)=>{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(e<this._referenceHeight)return this._debug._&&console.log(`contentFlow (${e}) fits on the page (${this._referenceHeight})`),void this._node.findAllForcedPageBreakInside(this._contentFlow).forEach((e=>this._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){t&&(e=this._node.findBetterPageStart(e,this.pages.at(-1)?.pageStart,this._contentFlow,this._root));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<e.length;s++)this._parseNode({previousElement:e[s-1]||t,currentElement:e[s],nextElement:e[s+1]||o,isCurrentFirst:0==s&&!e[s-1],parent:n,parentBottom:s===e.length-1?i:void 0})}_parseNode({isCurrentFirst:e,previousElement:t,currentElement:o,nextElement:n,parent:i,parentBottom:s}){const r=["%c_parseNode\n","color:white"];if(this._debug._parseNode&&console.group("%c_parseNode",c,""+(s?"★last★":"regular")),this._debug._parseNode&&console.log(...r,"3 nodes: ",{previousElement:t,currentElement:o,nextElement:n},"\n","\ncurrent: ",o,"\nparent: ",i,"\nisCurrentFirst: ",e),this._debug._parseNode&&console.log(...r,"parent:",i,"\n","parentBottom:",s,"\n","isCurrentFirst:",e,"\n","parent:",i,"\n"),!n)return this._node.markProcessed(o,"content-flow-end"),this._debug._parseNode&&console.log("%c END _parseNode (!nextElement)",_),void(this._debug._parseNode&&console.groupEnd());const l=s||this._node.getBottomWithMargin(o,this._root),a=this.pages.at(-1).pageBottom;if(this.pages.at(-1).pageStart===o&&(this._node.isNoBreak(o)||l<=a))return this._node.markProcessed(o,"node is already registered and fits in the page"),this._debug._parseNode&&console.log("%c END _parseNode (node is already registered and fits in the next page)",_),void(this._debug._parseNode&&console.groupEnd());if(this._node.isForcedPageBreak(o))return this._registerPageStart(o),this._node.markProcessed(o,"node is ForcedPageBreak"),this._debug._parseNode&&console.log("%c END _parseNode (isForcedPageBreak)",_),void(this._debug._parseNode&&console.groupEnd());this._debug._&&console.assert(this._DOM.getElementOffsetParent(o),"it is expected that the element has an offset parent",o);const h=this._node.getTop(n,this._root);if(this._debug._parseNode&&console.log(...r,"• newPageBottom",a,"\n","• nextElementTop",h),h<=a)this._debug._parseNode&&console.log("nextElementTop <= newPageBottom",h,"<=",a),this._node.markProcessed(o,"node fits"),this._node.findAllForcedPageBreakInside(o).forEach((e=>{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),l<this._referenceWidth&&this._debug._parseNode&&console.warn("%c IMAGE is too wide","color: red"),l<t)return this._node.markProcessed(o,"IMG that fits, and next starts on next"),this._registerPageStart(n),this._debug._parseNode&&console.log("Register next elements; 🖼️🖼️🖼️ IMG fits:",o),this._debug._parseNode&&console.log("%c END _parseNode 🖼️ IMG fits",_),void(this._debug._parseNode&&console.groupEnd());const d=t/l;return d>this._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(l<h+a*this._minPreBreakableLines)return this._debug._splitPreNode&&console.log("%c END _splitPreNode (small node)",_),[];const d=this._DOM.getChildNodes(e);if(this._debug._splitPreNode&&console.log("_children:",d.length),0==d.length)return this._debug._splitPreNode&&console.log("%c END _splitPreNode (not breakable)",_),[];if(d.length>1)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<this._minPreBreakableLines)return this._debug._splitPreNode&&console.log("%c END _splitPreNode few lines",_),[];const a=l.splice(0,this._minPreFirstBlockLines).join(""),g=l.splice(-this._minPreLastBlockLines).join("");l.unshift(a),l.push(g);const c=l.map((e=>{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;t<c.length;t++){const o=c[t];this._node.getBottom(o,e)>f&&(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;t<h.length;t++){const o=h[t];this._node.getBottom(o,e)>c&&(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;o<u.length;o++){this._debug._splitTableNode&&console.log(`%c 🟪 Check the Row # ${o}`,"color:blueviolet",[u[o]]);const n=u[o],i=this._node.getBottom(n,e)+d,r=this._node.getTop(n,e)+d,l=u[o+1];if((l?this._node.getTop(l,e)+d:i)>f){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<t;o++){const t=this._DOM.cloneNodeWrapper(l);this._node.setFlagNoBreak(t),this._DOM.setAttribute(t,`.splitted_row_${i}_part_${o}`),[...r].forEach(((n,i)=>{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<e.length;t++){const n=e[t];this._DOM.insertAtEnd(o,n),o=n}return this._debug._createSlicesBySplitFlag&&console.log(" createWrapperFromArray:",t),t},r=(e,t=null)=>{this._debug._createSlicesBySplitFlag&&console.group("processChildren"),this._debug._createSlicesBySplitFlag&&console.log("*start* children",e);for(let t=0;t<e.length;t++)l(e[t]);this._debug._createSlicesBySplitFlag&&console.log("- wrappers BEFORE pop:",[...n]);const o=n.pop();this._debug._createSlicesBySplitFlag&&console.log("- wrappers.pop()",o),this._debug._createSlicesBySplitFlag&&console.log("- parent",t),this._debug._createSlicesBySplitFlag&&console.log("- wrappers AFTER pop:",[...n]),i=n.at(-1),this._debug._createSlicesBySplitFlag&&console.log("🎯🎯 currentTargetInSlice",i),this._debug._createSlicesBySplitFlag&&console.log("🎯 wrappers.at(-1)",n.at(-1)),this._debug._createSlicesBySplitFlag&&console.log("*END* children",e),this._debug._createSlicesBySplitFlag&&console.log("%c END processChildren",_),this._debug._createSlicesBySplitFlag&&console.groupEnd()},l=e=>{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<o.length;t++){const g=o[t-1],_=o[t],u=o[t+1],m=u?this._node.getTop(u,e):void 0,f={id:t,element:o[t]},b={id:t+1,element:o[t+1]};t!==(l.length?l.at(-1).id:void 0)&&l.push(f);const M=0===r.length?i:null===r.at(-1)?s:s+this._node.getTop(r.at(-1),e);if(this._node.isForcedPageBreak(_)&&this._debug._getInternalBlockSplitters&&console.warn(_,"💟 is isForcedPageBreak"),m<=M)this._node.isNoHanging(_)&&(this._debug._getInternalBlockSplitters&&console.log("💟💟 currentElement _isNoHanging"),p(_,t));else{this._debug._getInternalBlockSplitters&&console.log("💟💟",_,`nextElementTop > 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).start<l.start?(e.at(-1).push(l),this._debug._splitGridNode&&console.log("Add to group:",l,[...e]),e):void(this._debug._&&console.assert(!0,"_splitGridNode: An unexpected case of splitting a grid.","\nOn the element:",t))}),[]);this._debug._splitGridNode&&console.groupEnd("make childrenGroups"),this._debug._splitGridNode&&console.log("%c childrenGroups","font-weight:bold",i);const s=i.length,r=this._DOM.getElementOffsetHeight(e);if(s<this._minBreakableGridRows&&r<o)return this._debug._splitGridNode&&console.log("%c END DONT _splitGridNode",_),this._debug._splitGridNode&&console.groupEnd(),[];const l=[...i.map((e=>e.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;e<c.length;e++)c[e]>u&&(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=parseInt(this._config.virtualPagesGap),t=parseInt(this._config.printHeight),o=parseInt(this._config.printTopMargin),n=parseInt(this._config.printBottomMargin),i=parseInt(this._config.headerMargin),s=parseInt(this._config.footerMargin),r=this._paper.headerHeight,l=this._paper.footerHeight,a=this._paper.bodyHeight,h=r?Math.ceil(i/2):0,d=l?Math.ceil(s/2):0,g=r-h,c=l-d,p=a+h+d,_=o+g,u=t+e;console.assert(t===p+g+o+c+n,"Paper size calculation params do not match"),function({targetElement:e,maskStep:t,maskWindow:o,maskFirstShift: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,maskStep:u,maskWindow:p,maskFirstShift:_})}_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<this._pages.length;e++){const t=this._paper.create({currentPage:e+1,totalPages:this._pages.length}),o=this._createVirtualPaperGap();this._insertIntoPaperFlow(t,o),this._insertIntoContentFlow(e,o)}}_insertIntoPaperFlow(e,t){this._insertPaper(this._paperFlow,e,t)}_insertIntoContentFlow(e,t){const o=this._pages[e].pageStart,n=this._createPageBreaker(e,t);this._DOM.insertBefore(o,n),t&&this._insertFooterSpacer(n,this._paper.footerHeight,t),this._insertHeaderSpacer(n,this._paper.headerHeight),this._updatePageStartElementAttrValue(o,e)}_createPageBreaker(e,t){const o=this._node.create(this._pageDivider);return this._DOM.setAttribute(o,"[page]",`${e+1}`),t&&this._paper.footerHeight&&this._DOM.setStyles(o,{marginTop:this._paper.footerHeight-1+"px"}),this._paper.headerHeight&&this._DOM.setStyles(o,{marginBottom:this._paper.headerHeight-1+"px"}),o}_updatePageStartElementAttrValue(e,t){this._hasFrontPage&&this._node.markPageStartElement(e,`${t+1}`)}_insertPaper(e,t,o){o?this._DOM.insertAtEnd(e,o,t):this._DOM.insertAtEnd(e,t)}_createVirtualPaperGap(){return this._node.create(this._virtualPaperGapSelector)}_createVirtualPaperTopMargin(){return this._paper.createVirtualTopMargin()}_createVirtualPaperBottomMargin(){return this._paper.createVirtualBottomMargin()}_insertFrontpageSpacer(e,t){const o=this._node.create();return this._DOM.setStyles(o,{paddingBottom:t+"px"}),this._DOM.setAttribute(o,".printFrontpageSpacer"),this._DOM.insertAtStart(e,o),o}_insertHeaderSpacer(e,t){const o=this._DOM.createDocumentFragment(),n=this._node.create(this._runningSafetySelector);this._DOM.insertAtEnd(o,this._createVirtualPaperTopMargin(),n),this._DOM.insertAtEnd(e,o)}_insertFooterSpacer(e,t,o){const n=this._DOM.createDocumentFragment(),i=this._createVirtualPaperGap(),s=this._node.create(this._runningSafetySelector);this._DOM.insertAtEnd(n,s,this._createVirtualPaperBottomMargin(),this._node.create(this._printPageBreakSelector),i),this._DOM.insertAtStart(e,n),this._balanceFooter(s,i,o)}_balanceFooter(e,t,o){const n=this._node.getTop(o,this._root)-this._node.getTop(t,this._root);this._DOM.setStyles(e,{marginBottom:n+"px"}),this._debug._&&console.assert(n>=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,node:n,layout:i}){this._config=e,this._selector=o,this._DOM=t,this._node=n,this._layout=i,this._root=i.root}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)],n=[...this._DOM.getAllElements(t)],i=o.map((e=>this._node.getTop(e))),s=n.map((e=>this._node.getTop(e,this._root))),r=i.reduce(((e,t,o)=>(t!==s[o]&&e.push(o+1),e)),[]);console.assert(!r.length,"Problems with preview generation on the following pages: ",r)}}const O="border:1px dashed #cccccc;background:#ffffff;color:#cccccc;";class S{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 D{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.2");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] Total 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 S(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 D(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,node:o,layout:r}).init(),this.debugMode&&console.timeEnd("⏱️ Validator time"),t.setAttribute(r.root,"[success]"),t.setAttribute(r.root,"[pages]",d.length),e.remove(),console.info("[HTML2PDF4DOC] Page count:",d.length),console.timeEnd("[HTML2PDF4DOC] Total time")}}(P),T="manual"===P.init;function k(){T&&E.render()}T&&console.info("HTML2PDF4DOC in manual initialization mode"),!T&&E.render(),HTML2PDF4DOC=t})();
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[tool.hatch.version]
|
|
6
|
+
path = "html2pdf4doc/html2pdf4doc.py"
|
|
7
|
+
|
|
8
|
+
[tool.hatch.build]
|
|
9
|
+
# Currently unused:
|
|
10
|
+
# We want html2pdf4doc.min.js to be gitignored, but we want it to make into the dist/
|
|
11
|
+
# folder, into both tar.gz and .whl when the Pip package is built.
|
|
12
|
+
# This option prevents Hatch from using .gitignore to exclude files.
|
|
13
|
+
# ignore-vcs = true
|
|
14
|
+
|
|
15
|
+
include = [
|
|
16
|
+
"html2pdf4doc/html2pdf4doc.py",
|
|
17
|
+
"html2pdf4doc/html2pdf4doc_js/html2pdf4doc.min.js",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
exclude = [
|
|
21
|
+
"/submodules",
|
|
22
|
+
"/tests",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project]
|
|
26
|
+
name = "html2pdf4doc"
|
|
27
|
+
dynamic = ["version"]
|
|
28
|
+
description = "Python client for HTML2PDF4Doc JavaScript library."
|
|
29
|
+
readme = "README.md"
|
|
30
|
+
# https://github.com/pypa/twine/issues/1216 license-files is broken as of 2025-02-03
|
|
31
|
+
# Using [] as a suggested workaround.
|
|
32
|
+
# license-files = [ "LICENSE" ]
|
|
33
|
+
license-files = []
|
|
34
|
+
requires-python = ">=3.8"
|
|
35
|
+
authors = [
|
|
36
|
+
{ name = "Stanislav Pankevich", email = "s.pankevich@gmail.com" },
|
|
37
|
+
{ name = "Maryna Balioura", email = "mettta@gmail.com" },
|
|
38
|
+
]
|
|
39
|
+
classifiers = [
|
|
40
|
+
"License :: OSI Approved :: Apache Software License",
|
|
41
|
+
"Operating System :: OS Independent",
|
|
42
|
+
"Programming Language :: Python :: 3",
|
|
43
|
+
"Programming Language :: Python :: 3.8",
|
|
44
|
+
"Programming Language :: Python :: 3.9",
|
|
45
|
+
"Programming Language :: Python :: 3.10",
|
|
46
|
+
"Programming Language :: Python :: 3.11",
|
|
47
|
+
"Programming Language :: Python :: Implementation :: CPython",
|
|
48
|
+
"Programming Language :: Python :: Implementation :: PyPy",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
dependencies = [
|
|
52
|
+
"selenium",
|
|
53
|
+
|
|
54
|
+
# Currently only used for detecting a local Chrome installation.
|
|
55
|
+
"webdriver-manager",
|
|
56
|
+
|
|
57
|
+
# requests is used for downloading the Chrome driver.
|
|
58
|
+
"requests",
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
[project.optional-dependencies]
|
|
62
|
+
development = [
|
|
63
|
+
# Development tasks
|
|
64
|
+
"invoke>=1.4.1",
|
|
65
|
+
"tox>=4.4.8",
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
[project.scripts]
|
|
69
|
+
html2pdf4doc = "html2pdf4doc.html2pdf4doc:main"
|
|
70
|
+
|
|
71
|
+
[project.urls]
|
|
72
|
+
Changelog = "https://github.com/mettta/html2pdf_python/releases/"
|
|
73
|
+
# Funding = "https://..."
|
|
74
|
+
Homepage = "https://github.com/mettta/html2pdf_python/"
|
|
75
|
+
Source = "https://github.com/mettta/html2pdf_python/"
|
|
76
|
+
|
|
77
|
+
[tool.pytest.ini_options]
|
|
78
|
+
addopts = "--import-mode=importlib"
|
|
79
|
+
pythonpath = [
|
|
80
|
+
"."
|
|
81
|
+
]
|