@makhmudov.kay/az-editor 0.1.0-beta.5 → 0.1.0-beta.6
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.
- package/CHANGELOG.md +13 -0
- package/README.md +1 -1
- package/dist/az-editor.js +301 -233
- package/dist/library/hujjat/contract.d.ts +2 -0
- package/docs/integration.ru.md +37 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Изменения
|
|
2
2
|
|
|
3
|
+
## 0.1.0-beta.6
|
|
4
|
+
|
|
5
|
+
- Исправлена потеря `page-break-*` в Chromium. Поддерживаемые `break-*` приводятся к серверным `page-break-*` с учётом порядка деклараций; `page` соответствует `always`, `avoid-page` — `avoid`.
|
|
6
|
+
- Разрывы на абзацах, контейнерах и до/после таблиц сохраняются после правок и повторного открытия. Смежные правила одного разрыва не создают лишнюю страницу; явные пустые маркеры остаются разрывами.
|
|
7
|
+
- Сохраняются минимальные высоты строк таблиц, `cantSplit`, начальные повторяемые строки шапки и режим `table-layout:auto/fixed`. Ширина таблицы в абсолютных единицах не подгоняется молча под текстовую область.
|
|
8
|
+
- Потеря плавающего размещения таблицы теперь даёт `floating-table-position-lost` уровня `loss`. Строгий HTML-экспорт требует обработки этой диагностики; прямой DOCX-режим не изменён. Правило точной высоты строки диагностируется как `table-row-height-rule-lost`.
|
|
9
|
+
- В адресах диагностик появились необязательные `tableIndex` и `rowIndex` (с нуля, внутри XML-части).
|
|
10
|
+
|
|
11
|
+
Временный CSSOM/Vite-патч потребителя (`azEditorPaginationCompat`) для beta.4/beta.5 можно
|
|
12
|
+
убрать после установки beta.6 и проверки цикла редактирования. Новый формат метаданных,
|
|
13
|
+
изображения и расширение HTTP-контракта
|
|
14
|
+
в этот этап не входят; отсутствующие во входном HTML сведения не восстанавливаются.
|
|
15
|
+
|
|
3
16
|
## 0.1.0-beta.5
|
|
4
17
|
|
|
5
18
|
- `HujjatPage`, `HujjatLocaleContent`, `validateHujjatPage` и `AzHtmlEditor.page`: ориентация A4, шрифт, межстрочный и абзацный интервалы учитываются при открытии.
|
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ npm install @makhmudov.kay/az-editor@beta
|
|
|
17
17
|
Для воспроизводимой установки опубликованной версии:
|
|
18
18
|
|
|
19
19
|
```sh
|
|
20
|
-
npm install --save-exact @makhmudov.kay/az-editor@0.1.0-beta.
|
|
20
|
+
npm install --save-exact @makhmudov.kay/az-editor@0.1.0-beta.6
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
Требуются React и React DOM 19, Node.js `^24.15.0 || >=26.0.0` для установки/сборки,
|
package/dist/az-editor.js
CHANGED
|
@@ -360,7 +360,7 @@ function P(e) {
|
|
|
360
360
|
function F(e) {
|
|
361
361
|
return /courier|mono|consolas/i.test(e) ? "freemono" : /arial|helvetica|calibri|sans|verdana|tahoma|segoe|roboto/i.test(e) ? "freesans" : "freeserif";
|
|
362
362
|
}
|
|
363
|
-
var I = new Set("div p span br hr b strong i em u s sub sup h1 h2 h3 h4 h5 h6 ul ol li table thead tbody tfoot tr td th".split(" ")), L = new Set("script style iframe object embed svg math img picture video audio canvas link meta form input textarea select button template noscript base frame frameset".split(" ")), ee = new Set("div p h1 h2 h3 h4 h5 h6 ul ol li table thead tbody tfoot tr td th br hr".split(" ")), te = new Set(("color font-family font-size font-weight font-style text-decoration text-align text-transform text-indent letter-spacing line-height white-space vertical-align background background-color width height margin padding border border-color border-width border-style border-collapse table-layout page-break-before page-break-after page-break-inside " + [
|
|
363
|
+
var I = new Set("div p span br hr b strong i em u s sub sup h1 h2 h3 h4 h5 h6 ul ol li table thead tbody tfoot tr td th".split(" ")), L = new Set("script style iframe object embed svg math img picture video audio canvas link meta form input textarea select button template noscript base frame frameset".split(" ")), ee = new Set("div p h1 h2 h3 h4 h5 h6 ul ol li table thead tbody tfoot tr td th br hr".split(" ")), te = new Set(("color font-family font-size font-weight font-style text-decoration text-align text-transform text-indent letter-spacing line-height white-space vertical-align background background-color width height margin padding border border-color border-width border-style border-collapse table-layout page-break-before page-break-after page-break-inside break-before break-after break-inside " + [
|
|
364
364
|
"top",
|
|
365
365
|
"right",
|
|
366
366
|
"bottom",
|
|
@@ -391,6 +391,18 @@ function ne(e, t) {
|
|
|
391
391
|
let e = F(a);
|
|
392
392
|
a.replace(/['"]/g, "").toLowerCase() !== e && M(t, "font-substitution", `Шрифт ${a} заменён на ${e}.`), a = e;
|
|
393
393
|
}
|
|
394
|
+
if (/^(page-)?break-(before|after|inside)$/.test(i)) {
|
|
395
|
+
let e = i.startsWith("break-");
|
|
396
|
+
if (a = a.toLowerCase(), e && a === "page" && i !== "break-inside" && (a = "always"), e && a === "avoid-page" && (a = "avoid"), ![
|
|
397
|
+
"auto",
|
|
398
|
+
"avoid",
|
|
399
|
+
...i.endsWith("inside") ? [] : ["always"]
|
|
400
|
+
].includes(a)) {
|
|
401
|
+
M(t, "page-break-value-unsupported", `Значение ${a} для ${i} не поддерживается HTML-адаптером.`);
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
e && (i = `page-${i}`);
|
|
405
|
+
}
|
|
394
406
|
n.set(i, a);
|
|
395
407
|
}
|
|
396
408
|
}
|
|
@@ -480,11 +492,11 @@ function ae(e) {
|
|
|
480
492
|
warnings: r
|
|
481
493
|
};
|
|
482
494
|
}
|
|
483
|
-
function
|
|
495
|
+
function R(e) {
|
|
484
496
|
if (!O.includes(e)) throw new A(`Неподдерживаемая локаль: ${e}.`);
|
|
485
497
|
}
|
|
486
|
-
function
|
|
487
|
-
|
|
498
|
+
function oe(e, t) {
|
|
499
|
+
R(t);
|
|
488
500
|
let n = e[t];
|
|
489
501
|
if (!n || !Array.isArray(n.blocks) || !n.blocks.length) throw new A(`Нет HTML-шаблона для локали ${t}.`);
|
|
490
502
|
return n.blocks.map((e) => {
|
|
@@ -492,9 +504,9 @@ function se(e, t) {
|
|
|
492
504
|
return e.props.html;
|
|
493
505
|
}).join("");
|
|
494
506
|
}
|
|
495
|
-
function
|
|
496
|
-
|
|
497
|
-
for (let t of Object.keys(e))
|
|
507
|
+
function se(e, t, n) {
|
|
508
|
+
R(t);
|
|
509
|
+
for (let t of Object.keys(e)) R(t);
|
|
498
510
|
let r = ae(n), i = structuredClone(e);
|
|
499
511
|
return {
|
|
500
512
|
...r,
|
|
@@ -513,22 +525,22 @@ function ce(e, t, n) {
|
|
|
513
525
|
}
|
|
514
526
|
//#endregion
|
|
515
527
|
//#region src/library/hujjat/xml.ts
|
|
516
|
-
var
|
|
528
|
+
var z = "http://schemas.openxmlformats.org/wordprocessingml/2006/main", ce = (e) => e.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'"), B = (e, t) => Array.from(e?.children ?? []).filter((e) => e.namespaceURI === "http://schemas.openxmlformats.org/wordprocessingml/2006/main" && (!t || e.localName === t)), V = (e, t) => B(e, t)[0] ?? null, H = (e, t = "val") => e?.getAttributeNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", t) ?? "", U = (e, t) => H(V(e, t)), W = (e) => e !== null && ![
|
|
517
529
|
"0",
|
|
518
530
|
"false",
|
|
519
531
|
"off",
|
|
520
532
|
"none"
|
|
521
|
-
].includes(H(e)), G = (e) => `${Number(e) / 20}pt`,
|
|
522
|
-
function
|
|
533
|
+
].includes(H(e)), G = (e) => `${Number(e) / 20}pt`, K = (e) => Object.keys(e).length ? ` style="${ce(Object.entries(e).map(([e, t]) => `${e}:${t}`).join(";"))}"` : "";
|
|
534
|
+
function le(e) {
|
|
523
535
|
if (/<!DOCTYPE|<!ENTITY/i.test(e)) throw new A("XML с DTD/ENTITY не поддерживается.");
|
|
524
536
|
let t = new DOMParser().parseFromString(e, "application/xml");
|
|
525
537
|
if (t.querySelector("parsererror")) throw new A("Повреждённый XML в DOCX.");
|
|
526
538
|
if (t.getElementsByTagName("*").length > 5e4) throw new A("Слишком сложный XML в DOCX.");
|
|
527
539
|
return t;
|
|
528
540
|
}
|
|
529
|
-
async function
|
|
541
|
+
async function q(e, t) {
|
|
530
542
|
let n = e.file(t);
|
|
531
|
-
return n ?
|
|
543
|
+
return n ? le(await new Promise((e, t) => {
|
|
532
544
|
let r = n.internalStream("uint8array"), i = 0, a = [];
|
|
533
545
|
r.on("data", (e) => {
|
|
534
546
|
i += e.length, i > 8388608 ? (r.pause(), t(new A("XML-часть DOCX превышает 8 МиБ."))) : a.push(e);
|
|
@@ -539,7 +551,7 @@ async function K(e, t) {
|
|
|
539
551
|
}), r.resume();
|
|
540
552
|
})) : null;
|
|
541
553
|
}
|
|
542
|
-
function
|
|
554
|
+
function J(e) {
|
|
543
555
|
let t = {};
|
|
544
556
|
V(e, "vanish") && (t["--az-hidden"] = W(V(e, "vanish")) ? "1" : "0");
|
|
545
557
|
let n = V(e, "rFonts"), r = H(n, "ascii") || H(n, "hAnsi") || H(n, "cs");
|
|
@@ -562,7 +574,7 @@ function q(e) {
|
|
|
562
574
|
let o = H(V(e, "shd"), "fill");
|
|
563
575
|
return /^[\da-f]{6}$/i.test(o) ? t["background-color"] = `#${o}` : U(e, "highlight") && U(e, "highlight") !== "none" && (t["background-color"] = U(e, "highlight")), ["superscript", "subscript"].includes(U(e, "vertAlign")) && (t["vertical-align"] = U(e, "vertAlign") === "superscript" ? "super" : "sub"), W(V(e, "caps")) && (t["text-transform"] = "uppercase"), U(e, "spacing") && (t["letter-spacing"] = G(U(e, "spacing"))), U(e, "position") && (t["--az-position"] = U(e, "position")), t;
|
|
564
576
|
}
|
|
565
|
-
function
|
|
577
|
+
function ue(e) {
|
|
566
578
|
let t = {}, n = U(e, "jc");
|
|
567
579
|
n && (t["text-align"] = {
|
|
568
580
|
both: "justify",
|
|
@@ -602,7 +614,7 @@ function de(e) {
|
|
|
602
614
|
}
|
|
603
615
|
return t;
|
|
604
616
|
}
|
|
605
|
-
function
|
|
617
|
+
function de(e) {
|
|
606
618
|
if (!e) return;
|
|
607
619
|
if (["nil", "none"].includes(H(e))) return "none";
|
|
608
620
|
let t = {
|
|
@@ -614,7 +626,7 @@ function fe(e) {
|
|
|
614
626
|
}
|
|
615
627
|
//#endregion
|
|
616
628
|
//#region src/library/hujjat/page-layout.ts
|
|
617
|
-
function
|
|
629
|
+
function fe(e) {
|
|
618
630
|
let t = E().page, n = V(e, "pgSz"), r = V(e, "pgMar"), i = (e, t, n) => H(e, t) === "" ? n : Number(H(e, t)), a = i(n, "w", t.widthTwips), o = i(n, "h", t.heightTwips);
|
|
619
631
|
return E({ page: {
|
|
620
632
|
widthTwips: a,
|
|
@@ -623,10 +635,10 @@ function pe(e) {
|
|
|
623
635
|
margins: Object.fromEntries(Object.entries(t.margins).map(([e, t]) => [e, i(r, e, t)]))
|
|
624
636
|
} }).page;
|
|
625
637
|
}
|
|
626
|
-
function
|
|
627
|
-
let i = E(t), a = Array.from(e.getElementsByTagNameNS(
|
|
638
|
+
function pe(e, t, n, r) {
|
|
639
|
+
let i = E(t), a = Array.from(e.getElementsByTagNameNS(z, "sectPr")), o;
|
|
628
640
|
try {
|
|
629
|
-
o = a.map(
|
|
641
|
+
o = a.map(fe);
|
|
630
642
|
} catch (e) {
|
|
631
643
|
M(n, "unsupported-page-layout", e instanceof Error ? e.message : "Неподдерживаемые параметры страницы.", "loss", {
|
|
632
644
|
stage: "export",
|
|
@@ -659,7 +671,7 @@ function me(e, t, n, r) {
|
|
|
659
671
|
}
|
|
660
672
|
//#endregion
|
|
661
673
|
//#region src/library/hujjat/paragraph-format.ts
|
|
662
|
-
function
|
|
674
|
+
function me(e) {
|
|
663
675
|
let t, n = "auto";
|
|
664
676
|
for (let r of e) {
|
|
665
677
|
let e = V(r, "spacing");
|
|
@@ -672,16 +684,16 @@ function he(e) {
|
|
|
672
684
|
rule: n
|
|
673
685
|
} : void 0;
|
|
674
686
|
}
|
|
675
|
-
function
|
|
687
|
+
function he(e) {
|
|
676
688
|
return e.rule === "auto" ? String(e.value / 240) : `${e.value / 20}pt`;
|
|
677
689
|
}
|
|
678
|
-
function
|
|
690
|
+
function ge(e) {
|
|
679
691
|
return (parseFloat(e["font-size"]) || 12) + Math.abs(Number(e["--az-position"]) || 0) / 2;
|
|
680
692
|
}
|
|
681
693
|
//#endregion
|
|
682
694
|
//#region src/library/hujjat/docx-chrome.ts
|
|
683
|
-
async function
|
|
684
|
-
let a = Object.keys(e.files).filter((e) => /^word\/(header|footer)[^/]*\.xml$/.test(e)), o = Array.from(t.getElementsByTagNameNS(
|
|
695
|
+
async function _e(e, t, n, r, i) {
|
|
696
|
+
let a = Object.keys(e.files).filter((e) => /^word\/(header|footer)[^/]*\.xml$/.test(e)), o = Array.from(t.getElementsByTagNameNS(z, "sectPr")).flatMap((e) => B(e).filter((e) => ["headerReference", "footerReference"].includes(e.localName)));
|
|
685
697
|
if (n === void 0) {
|
|
686
698
|
(a.length || o.length) && M(r, "header-footer-removed", "Для сохранения колонтитулов передайте page (или locales с page) в exportHujjat.", "loss", { stage: "export" });
|
|
687
699
|
return;
|
|
@@ -689,7 +701,7 @@ async function ve(e, t, n, r, i) {
|
|
|
689
701
|
let s = n === null ? null : structuredClone(n);
|
|
690
702
|
if (!a.length && !o.length && !s?.header && !s?.footer) return s;
|
|
691
703
|
s ??= {};
|
|
692
|
-
let c = await
|
|
704
|
+
let c = await q(e, "word/_rels/document.xml.rels"), l = W(V((await q(e, "word/settings.xml"))?.documentElement, "evenAndOddHeaders")), u = Array.from(c?.documentElement.children ?? []), d = /* @__PURE__ */ new Set();
|
|
693
705
|
for (let n of ["header", "footer"]) {
|
|
694
706
|
let a = o.filter((e) => e.localName === `${n}Reference`);
|
|
695
707
|
if (l || a.some((e) => H(e, "type") !== "default") || a.length > 1 || Array.from(t.getElementsByTagNameNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "sectPr")).some((e) => W(V(e, "titlePg")))) {
|
|
@@ -713,7 +725,7 @@ async function ve(e, t, n, r, i) {
|
|
|
713
725
|
continue;
|
|
714
726
|
}
|
|
715
727
|
d.add(h);
|
|
716
|
-
let g = (await
|
|
728
|
+
let g = (await q(e, h))?.documentElement;
|
|
717
729
|
!g || g.namespaceURI !== "http://schemas.openxmlformats.org/wordprocessingml/2006/main" || g.localName !== (n === "header" ? "hdr" : "ftr") ? (s[n] = null, M(r, "header-footer-reference-unsupported", "Структура колонтитула повреждена.", "loss", {
|
|
718
730
|
stage: "export",
|
|
719
731
|
location: { part: h }
|
|
@@ -723,8 +735,8 @@ async function ve(e, t, n, r, i) {
|
|
|
723
735
|
}
|
|
724
736
|
//#endregion
|
|
725
737
|
//#region src/library/hujjat/server-page.ts
|
|
726
|
-
var
|
|
727
|
-
function
|
|
738
|
+
var ve = 102400;
|
|
739
|
+
function ye(e) {
|
|
728
740
|
if (e == null) return;
|
|
729
741
|
let t = (e) => {
|
|
730
742
|
throw new A(`Некорректное поле page.${e} в контракте Hujjat.`);
|
|
@@ -762,7 +774,7 @@ function J(e) {
|
|
|
762
774
|
}
|
|
763
775
|
}
|
|
764
776
|
function be(e, t) {
|
|
765
|
-
if (
|
|
777
|
+
if (ye(e), !e) return e;
|
|
766
778
|
let n = structuredClone(e);
|
|
767
779
|
for (let e of ["header", "footer"]) {
|
|
768
780
|
let r = n[e];
|
|
@@ -776,7 +788,7 @@ function be(e, t) {
|
|
|
776
788
|
}
|
|
777
789
|
})));
|
|
778
790
|
}
|
|
779
|
-
return
|
|
791
|
+
return ye(n), n;
|
|
780
792
|
}
|
|
781
793
|
function xe(e, t = {}) {
|
|
782
794
|
let n = E();
|
|
@@ -798,42 +810,42 @@ async function Se(e, t = {}, n) {
|
|
|
798
810
|
let i = Object.keys(r.files);
|
|
799
811
|
if (i.length > 2e3) throw new A("В DOCX более 2000 частей.");
|
|
800
812
|
if (i.some((e) => /vbaProject\.bin$/i.test(e))) throw new A("DOCX с макросами не поддерживается.");
|
|
801
|
-
let a = await
|
|
813
|
+
let a = await q(r, "[Content_Types].xml");
|
|
802
814
|
if (!Array.from(a?.getElementsByTagNameNS("*", "Override") ?? []).some((e) => e.getAttribute("PartName") === "/word/document.xml" && e.getAttribute("ContentType") === "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml")) throw new A("Файл не является обычным DOCX.");
|
|
803
|
-
let o = await
|
|
815
|
+
let o = await q(r, "word/document.xml"), s = await q(r, "word/styles.xml"), c = await q(r, "word/numbering.xml"), l = V(o?.documentElement, "body");
|
|
804
816
|
if (!l) throw new A("В DOCX нет поддерживаемой структуры документа.");
|
|
805
|
-
let u = [], d =
|
|
817
|
+
let u = [], d = pe(l, t, u, n === void 0 ? void 0 : xe(n).page), f, m = (e, t, n = "warning", r, i) => M(u, e, t, n, {
|
|
806
818
|
stage: "export",
|
|
807
819
|
location: f ? { ...f } : void 0,
|
|
808
820
|
before: r,
|
|
809
821
|
after: i
|
|
810
|
-
}), h = (e, t) => m(e, t, "loss"), g = new Map(Array.from(l.getElementsByTagNameNS(
|
|
822
|
+
}), h = (e, t) => m(e, t, "loss"), g = new Map(Array.from(l.getElementsByTagNameNS(z, "p")).map((e, t) => [e, t])), _ = new Map(Array.from(l.getElementsByTagNameNS(z, "tbl")).map((e, t) => [e, t])), v = "word/document.xml", y = 0;
|
|
811
823
|
i.some((e) => /^word\/comments/.test(e)) && h("review-removed", "Комментарии Word не сохраняются в HTML.");
|
|
812
|
-
let
|
|
824
|
+
let b = V(s?.documentElement, "docDefaults"), x = {
|
|
813
825
|
"margin-top": "0pt",
|
|
814
826
|
"margin-bottom": "8px",
|
|
815
|
-
...
|
|
816
|
-
},
|
|
827
|
+
...ue(V(V(b, "pPrDefault"), "pPr"))
|
|
828
|
+
}, S = {
|
|
817
829
|
"font-family": "freeserif",
|
|
818
830
|
"font-size": "12pt",
|
|
819
831
|
color: "#111111",
|
|
820
|
-
...
|
|
821
|
-
},
|
|
822
|
-
function
|
|
832
|
+
...J(V(V(b, "rPrDefault"), "rPr"))
|
|
833
|
+
}, C = new Map(B(s?.documentElement, "style").map((e) => [H(e, "styleId"), e]));
|
|
834
|
+
function w(e, t = /* @__PURE__ */ new Set()) {
|
|
823
835
|
if (!e || t.has(e) || t.size > 30) return [];
|
|
824
836
|
t.add(e);
|
|
825
|
-
let n =
|
|
826
|
-
return n ? [...
|
|
837
|
+
let n = C.get(e);
|
|
838
|
+
return n ? [...w(U(n, "basedOn"), t), n] : [];
|
|
827
839
|
}
|
|
828
|
-
let
|
|
829
|
-
if (++
|
|
840
|
+
let T = B(s?.documentElement, "style").find((e) => H(e, "type") === "paragraph" && H(e, "default") === "1"), E = new Map(B(c?.documentElement, "num").map((e) => [H(e, "numId"), e])), D = new Map(B(c?.documentElement, "abstractNum").map((e) => [H(e, "abstractNumId"), e])), O = /* @__PURE__ */ new Set(), k = !1, j = 0, N = (e) => {
|
|
841
|
+
if (++j > 5e4 || e > 80) throw new A("DOCX слишком сложный для HTML-адаптера.");
|
|
830
842
|
};
|
|
831
|
-
function
|
|
832
|
-
if (
|
|
843
|
+
function P(e, t, n) {
|
|
844
|
+
if (N(n), e.namespaceURI !== "http://schemas.openxmlformats.org/wordprocessingml/2006/main") return h("unsupported-content", "Нестандартный объект Word не сохраняется в HTML."), "";
|
|
833
845
|
switch (e.localName) {
|
|
834
|
-
case "t": return
|
|
835
|
-
let t = e === " " &&
|
|
836
|
-
return
|
|
846
|
+
case "t": return ce(Array.from(e.textContent ?? "").map((e) => {
|
|
847
|
+
let t = e === " " && k ? "\xA0" : e;
|
|
848
|
+
return k = e === " ", t;
|
|
837
849
|
}).join(""));
|
|
838
850
|
case "tab": return h("tab-normalized", "Положение текста после табуляции потеряно в HTML. Для точного расположения используйте исходный DOCX."), " ";
|
|
839
851
|
case "br": return H(e, "type") === "page" ? "<div style=\"page-break-before:always\"></div>" : "<br>";
|
|
@@ -843,13 +855,13 @@ async function Se(e, t = {}, n) {
|
|
|
843
855
|
case "r": {
|
|
844
856
|
f = {
|
|
845
857
|
...f,
|
|
846
|
-
runIndex:
|
|
858
|
+
runIndex: y++
|
|
847
859
|
};
|
|
848
|
-
let r = V(e, "rPr"), i = Object.assign({}, t, ...
|
|
860
|
+
let r = V(e, "rPr"), i = Object.assign({}, t, ...w(U(r, "rStyle")).map((e) => J(V(e, "rPr"))), J(r));
|
|
849
861
|
if (i["--az-hidden"] === "1") return h("hidden-text-removed", "Скрытый текст Word исключён из HTML."), "";
|
|
850
862
|
Number(i["--az-position"]) && m("baseline-shift-lost", "Вертикальное смещение фрагмента Word не представлено в HTML.", "loss", Number(i["--az-position"]) / 2, 0);
|
|
851
863
|
let a = Object.fromEntries(Object.entries(i).filter(([e]) => !e.startsWith("--az-")));
|
|
852
|
-
return B(e).filter((e) => e.localName !== "rPr").map((e) =>
|
|
864
|
+
return B(e).filter((e) => e.localName !== "rPr").map((e) => P(e, i, n + 1)).join("").split(/(<div style="page-break-before:always"><\/div>)/).map((e) => e.startsWith("<div ") ? e : e ? `<span${K(a)}>${e}</span>` : "").join("");
|
|
853
865
|
}
|
|
854
866
|
case "hyperlink":
|
|
855
867
|
M(u, "hyperlink-removed", "Ссылки сохраняются как текст без перехода.");
|
|
@@ -870,7 +882,7 @@ async function Se(e, t = {}, n) {
|
|
|
870
882
|
case "fldSimple":
|
|
871
883
|
h("field-flattened", "Поля Word сохраняют только имеющийся текстовый результат.");
|
|
872
884
|
break;
|
|
873
|
-
case "sdt": return h("content-control-flattened", "Элементы управления Word преобразуются в обычный текст."), V(e, "sdtContent") ?
|
|
885
|
+
case "sdt": return h("content-control-flattened", "Элементы управления Word преобразуются в обычный текст."), V(e, "sdtContent") ? P(V(e, "sdtContent"), t, n + 1) : "";
|
|
874
886
|
case "sdtContent":
|
|
875
887
|
case "smartTag":
|
|
876
888
|
case "customXml": break;
|
|
@@ -883,122 +895,146 @@ async function Se(e, t = {}, n) {
|
|
|
883
895
|
case "commentReference": return "";
|
|
884
896
|
default: h("unsupported-inline", `Элемент Word ${e.localName} не поддерживается HTML-адаптером.`);
|
|
885
897
|
}
|
|
886
|
-
return Array.from(e.children).map((e) =>
|
|
898
|
+
return Array.from(e.children).map((e) => P(e, t, n + 1)).join("");
|
|
887
899
|
}
|
|
888
|
-
function
|
|
900
|
+
function F(e, n) {
|
|
889
901
|
f = {
|
|
890
|
-
part:
|
|
902
|
+
part: v,
|
|
891
903
|
paragraphIndex: g.get(e)
|
|
892
|
-
},
|
|
893
|
-
let r = V(e, "pPr"), i =
|
|
894
|
-
if (
|
|
895
|
-
|
|
896
|
-
|
|
904
|
+
}, y = 0;
|
|
905
|
+
let r = V(e, "pPr"), i = w(U(r, "pStyle") || H(T, "styleId")), a = V(r, "spacing");
|
|
906
|
+
if (U(r, "pStyle") === "AzHujjatPageBoundary" && W(V(r, "pageBreakBefore")) && Array.from(r?.children ?? []).every((e) => e.namespaceURI === "http://schemas.openxmlformats.org/wordprocessingml/2006/main" && [
|
|
907
|
+
"pStyle",
|
|
908
|
+
"pageBreakBefore",
|
|
909
|
+
"spacing"
|
|
910
|
+
].includes(e.localName)) && H(a, "before") === "0" && H(a, "after") === "0" && H(a, "line") === "1" && H(a, "lineRule") === "exact" && Array.from(e.children).every((e) => e === r)) return { html: "<div style=\"page-break-before:always\"></div>" };
|
|
911
|
+
let o = [...i.map((e) => V(e, "pPr")), r], s = Object.assign({}, x, ...o.map(ue)), c = o.map((e) => V(e, "numPr")).filter(Boolean).at(-1), l = U(c, "numId"), d = Number(U(c, "ilvl") || 0), p = U(r, "pStyle") === "AzHujjatListContinuation";
|
|
912
|
+
if (l && l !== "0" || p) {
|
|
913
|
+
let e = E.get(l), t = ue(V(B(D.get(U(e, "abstractNumId")), "lvl").find((e) => H(e, "ilvl") === String(d)), "pPr"));
|
|
914
|
+
s["margin-left"] = G((parseFloat(s["margin-left"] ?? t["margin-left"] ?? "0") || 0) * 20 - 360), s["text-indent"] ??= t["text-indent"] ?? "0pt";
|
|
897
915
|
}
|
|
898
|
-
parseFloat(
|
|
899
|
-
let
|
|
900
|
-
|
|
901
|
-
let
|
|
902
|
-
let t = V(e, "rPr"), n = Object.assign({},
|
|
903
|
-
return n["--az-hidden"] === "1" ? 0 :
|
|
904
|
-
}),
|
|
905
|
-
|
|
906
|
-
let
|
|
907
|
-
|
|
916
|
+
parseFloat(s["text-indent"] ?? "0") < 0 && parseFloat(s["margin-left"] ?? "0") >= 0 && (s["padding-left"] = s["margin-left"] ?? "0pt", s["margin-left"] = "0pt");
|
|
917
|
+
let _ = me([V(V(b, "pPrDefault"), "pPr"), ...o]);
|
|
918
|
+
_ && (s["line-height"] = he(_));
|
|
919
|
+
let C = Object.assign({}, S, ...i.map((e) => J(V(e, "rPr")))), O = Array.from(e.getElementsByTagNameNS(z, "r")).map((e) => {
|
|
920
|
+
let t = V(e, "rPr"), n = Object.assign({}, C, ...w(U(t, "rStyle")).map((e) => J(V(e, "rPr"))), J(t));
|
|
921
|
+
return n["--az-hidden"] === "1" ? 0 : ge(n);
|
|
922
|
+
}), A = s["line-height"];
|
|
923
|
+
A && (/^\d*\.?\d+$/.test(A) ? Number(A) < 1 : A.endsWith("pt") && parseFloat(A) < Math.max(ge(C), ...O)) && (t.lineSpacingPolicy === "preserve" ? m("line-height-overlap-risk", "Авторский тесный интервал сохранён; строки могут перекрываться. Фактические метрики шрифта требуют проверки.", "warning", A, A) : (s["line-height"] = /^\d*\.?\d+$/.test(A) ? "1" : "normal", m("line-height-normalized", "Тесный межстрочный интервал нормализован по размерам фрагментов. Проверьте переносы на используемых шрифтах.", "warning", A, s["line-height"]))), _ && _.rule !== "auto" && m("line-rule-lost", "CSS line-height не сохраняет различие правил Word exact и atLeast.", "warning", _.rule, "CSS line-height"), o.some((e) => V(e, "tabs")) && h("tab-stops-removed", "Позиции, типы и заполнители табуляции не поддерживаются HTML-блоком.");
|
|
924
|
+
let j = [V(V(b, "pPrDefault"), "pPr"), ...o].map((e) => V(e, "widowControl")).filter(Boolean).at(-1);
|
|
925
|
+
j && M(u, "widow-control-lost", "Правило висячих строк Word не представлено в HTML-контракте.", "loss", {
|
|
908
926
|
stage: "export",
|
|
909
927
|
location: { ...f },
|
|
910
|
-
before: W(
|
|
928
|
+
before: W(j),
|
|
911
929
|
after: null
|
|
912
|
-
}),
|
|
913
|
-
let
|
|
914
|
-
if (
|
|
915
|
-
let e =
|
|
916
|
-
if (!t) return M(u, "numbering-unsupported", "Не найдена нумерация списка.", "loss"), { html:
|
|
930
|
+
}), k = !0;
|
|
931
|
+
let N = Array.from(e.children).filter((e) => e !== r).map((e) => P(e, C, n + 1)).join("").split("<div style=\"page-break-before:always\"></div>").map((e, t) => `${t ? "<div style=\"page-break-before:always\"></div>" : ""}<p${K(s)}>${e || "<br>"}</p>`).join("");
|
|
932
|
+
if (l && l !== "0") {
|
|
933
|
+
let e = E.get(l), t = B(D.get(U(e, "abstractNumId")), "lvl").find((e) => H(e, "ilvl") === String(d));
|
|
934
|
+
if (!t) return M(u, "numbering-unsupported", "Не найдена нумерация списка.", "loss"), { html: N };
|
|
917
935
|
let n = U(t, "numFmt");
|
|
918
|
-
return (!["decimal", "bullet"].includes(n) ||
|
|
919
|
-
html:
|
|
936
|
+
return (!["decimal", "bullet"].includes(n) || d || U(t, "start") && U(t, "start") !== "1" || V(e, "lvlOverride")) && M(u, "numbering-normalized", "Сложная нумерация/начальное число списка заменены обычным HTML-списком.", "loss"), {
|
|
937
|
+
html: N,
|
|
920
938
|
list: {
|
|
921
|
-
id:
|
|
922
|
-
level: Math.min(
|
|
939
|
+
id: l,
|
|
940
|
+
level: Math.min(d, 8),
|
|
923
941
|
tag: n === "bullet" ? "ul" : "ol"
|
|
924
942
|
}
|
|
925
943
|
};
|
|
926
944
|
}
|
|
927
945
|
return {
|
|
928
|
-
html:
|
|
946
|
+
html: N,
|
|
929
947
|
continuation: U(r, "pStyle") === "AzHujjatListContinuation"
|
|
930
948
|
};
|
|
931
949
|
}
|
|
932
|
-
function
|
|
933
|
-
|
|
934
|
-
let n = V(e, "tblPr")
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
950
|
+
function I(e, t) {
|
|
951
|
+
N(t);
|
|
952
|
+
let n = V(e, "tblPr"), r = U(n, "tblStyle"), i = {
|
|
953
|
+
part: v,
|
|
954
|
+
tableIndex: _.get(e)
|
|
955
|
+
};
|
|
956
|
+
f = i, V(n, "tblpPr") && h("floating-table-position-lost", "Плавающее размещение таблицы Word не поддерживается текущим HTML-контрактом; таблица остаётся в потоке документа."), r && M(u, "table-style-normalized", "Условные стили таблиц Word могут отличаться; проверьте границы и заливку.");
|
|
957
|
+
let a = V(n, "tblBorders"), o = B(V(e, "tblGrid"), "gridCol").map((e) => Number(H(e, "w"))), s = H(V(n, "tblLayout"), "type") === "fixed", c = B(e, "tr"), l = /* @__PURE__ */ new Map(), d = Math.max(1, ...c.map((e) => B(e, "tc").reduce((e, t) => e + Number(U(V(t, "tcPr"), "gridSpan") || 1), 0))), p = [], m = [], g = 0;
|
|
958
|
+
c.forEach((e, r) => {
|
|
959
|
+
let f = V(e, "trPr"), _ = {}, v = {
|
|
960
|
+
...i,
|
|
961
|
+
rowIndex: r
|
|
962
|
+
}, y = V(f, "trHeight");
|
|
963
|
+
y && H(y, "val") && H(y, "hRule") !== "auto" && (_.height = G(H(y, "val")), H(y, "hRule") === "exact" && M(u, "table-row-height-rule-lost", "Высота строки HTML задаёт минимум; правило Word exact станет atLeast.", "warning", {
|
|
964
|
+
stage: "export",
|
|
965
|
+
location: v,
|
|
966
|
+
before: "exact",
|
|
967
|
+
after: "atLeast"
|
|
968
|
+
})), V(f, "cantSplit") && (_["page-break-inside"] = W(V(f, "cantSplit")) ? "avoid" : "auto"), W(V(f, "tblHeader")) && (r === g ? g++ : M(u, "table-header-not-contiguous", "Повторяемая шапка вне начала таблицы не переносится в HTML.", "warning", {
|
|
969
|
+
stage: "export",
|
|
970
|
+
location: v
|
|
971
|
+
})), m.push(_);
|
|
972
|
+
let b = 0, x = [];
|
|
939
973
|
Number(U(V(e, "trPr"), "gridBefore") || 0) && h("table-grid-unsupported", "Таблица с пропущенными начальными ячейками требует проверки.");
|
|
940
|
-
for (let
|
|
941
|
-
let e = V(
|
|
942
|
-
if (V(e, "hMerge") && h("legacy-table-merge", "Старый формат горизонтального объединения hMerge требует преобразования в gridSpan."), !Number.isInteger(
|
|
943
|
-
if (
|
|
944
|
-
let e =
|
|
974
|
+
for (let i of B(e, "tc")) {
|
|
975
|
+
let e = V(i, "tcPr"), u = Number(U(e, "gridSpan") || 1), f = V(e, "vMerge");
|
|
976
|
+
if (V(e, "hMerge") && h("legacy-table-merge", "Старый формат горизонтального объединения hMerge требует преобразования в gridSpan."), !Number.isInteger(u) || u < 1 || u > 100) throw new A("Объединение таблицы превышает предел 100 ячеек.");
|
|
977
|
+
if (f && H(f) !== "restart" && l.has(b) && l.get(b).span === u) {
|
|
978
|
+
let e = l.get(b);
|
|
945
979
|
if (e.rowSpan++, e.rowSpan > 100) throw new A("Объединение таблицы превышает 100 строк.");
|
|
946
|
-
|
|
980
|
+
i.getElementsByTagNameNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "t").length && i.textContent?.trim() && h("table-merge-content", "Текст в продолжении объединённой ячейки требует ручной проверки."), b += u;
|
|
947
981
|
continue;
|
|
948
982
|
}
|
|
949
|
-
let
|
|
950
|
-
H(
|
|
951
|
-
let
|
|
952
|
-
(
|
|
953
|
-
let
|
|
954
|
-
/^[\da-f]{6}$/i.test(
|
|
983
|
+
let p = {}, m = V(e, "tcW");
|
|
984
|
+
H(m, "type") === "dxa" ? p.width = G(H(m, "w")) : H(m, "type") === "pct" && (p.width = `${Number(H(m, "w")) / 50}%`);
|
|
985
|
+
let g = o.slice(b, b + u);
|
|
986
|
+
(s || !p.width) && g.length === u && g.every((e) => Number.isFinite(e) && e > 0) && (p.width = G(g.reduce((e, t) => e + t, 0)));
|
|
987
|
+
let _ = H(V(e, "shd"), "fill");
|
|
988
|
+
/^[\da-f]{6}$/i.test(_) && (p["background-color"] = "#" + _), U(e, "vAlign") && (p["vertical-align"] = U(e, "vAlign") === "center" ? "middle" : U(e, "vAlign"));
|
|
955
989
|
for (let t of [
|
|
956
990
|
"top",
|
|
957
991
|
"right",
|
|
958
992
|
"bottom",
|
|
959
993
|
"left"
|
|
960
994
|
]) {
|
|
961
|
-
let i = V(V(e, "tcBorders"), t),
|
|
962
|
-
l && (
|
|
995
|
+
let i = V(V(e, "tcBorders"), t), o = t === "top" && r > 0 || t === "bottom" && r < c.length - 1 ? "insideH" : t === "left" && b > 0 || t === "right" && b + u < d ? "insideV" : t, s = V(a, o), l = de(i ?? s);
|
|
996
|
+
l && (p[`border-${t}`] = l);
|
|
963
997
|
let f = V(V(e, "tcMar"), t) ?? V(V(n, "tblCellMar"), t);
|
|
964
|
-
f && H(f, "w") && (
|
|
998
|
+
f && H(f, "w") && (p[`padding-${t}`] = G(H(f, "w")));
|
|
965
999
|
}
|
|
966
|
-
let
|
|
967
|
-
element:
|
|
1000
|
+
let v = {
|
|
1001
|
+
element: i,
|
|
968
1002
|
rowSpan: 1,
|
|
969
|
-
span:
|
|
970
|
-
html:
|
|
971
|
-
css:
|
|
1003
|
+
span: u,
|
|
1004
|
+
html: L(i, t + 1),
|
|
1005
|
+
css: p
|
|
972
1006
|
};
|
|
973
|
-
for (let e =
|
|
974
|
-
|
|
1007
|
+
for (let e = b; e < b + u; e++) l.delete(e);
|
|
1008
|
+
f && H(f) === "restart" && l.set(b, v), x.push(v), b += u;
|
|
975
1009
|
}
|
|
976
|
-
for (let e of
|
|
977
|
-
|
|
1010
|
+
for (let e of l.keys()) e >= b && l.delete(e);
|
|
1011
|
+
p.push(x);
|
|
978
1012
|
});
|
|
979
|
-
let
|
|
980
|
-
width: H(
|
|
1013
|
+
let y = V(n, "tblW"), b = {
|
|
1014
|
+
width: H(y, "type") === "dxa" ? G(H(y, "w")) : H(y, "type") === "pct" ? `${Number(H(y, "w")) / 50}%` : "100%",
|
|
981
1015
|
"border-collapse": "collapse"
|
|
982
|
-
},
|
|
983
|
-
|
|
1016
|
+
}, x = V(n, "tblInd");
|
|
1017
|
+
H(x, "type") === "dxa" && (b["margin-left"] = G(H(x, "w"))), b["table-layout"] = s ? "fixed" : "auto";
|
|
1018
|
+
let S = p.map((e, t) => `<tr${K(m[t])}>${e.map((e) => `<td${e.span > 1 ? ` colspan="${e.span}"` : ""}${e.rowSpan > 1 ? ` rowspan="${e.rowSpan}"` : ""}${K(e.css)}>${e.html || "<p><br></p>"}</td>`).join("")}</tr>`);
|
|
1019
|
+
return `<table${K(b)}>${g ? `<thead>${S.slice(0, g).join("")}</thead>` : ""}<tbody>${S.slice(g).join("")}</tbody></table>`;
|
|
984
1020
|
}
|
|
985
|
-
function
|
|
986
|
-
|
|
1021
|
+
function L(e, t) {
|
|
1022
|
+
N(t);
|
|
987
1023
|
let n = "", r, i = () => {
|
|
988
1024
|
r && (n += `</${r.tag}>`), r = void 0;
|
|
989
1025
|
};
|
|
990
1026
|
for (let a of Array.from(e.children)) if (a.namespaceURI !== "http://schemas.openxmlformats.org/wordprocessingml/2006/main") i(), h("unsupported-content", "Нестандартный блок Word не сохраняется в HTML.");
|
|
991
1027
|
else if (a.localName === "p") {
|
|
992
|
-
let e =
|
|
993
|
-
e.list ? ((r?.id !== e.list.id || r.tag !== e.list.tag) && (i(), r = e.list, r.tag === "ol" &&
|
|
994
|
-
} else a.localName === "tbl" ? (i(), n +=
|
|
1028
|
+
let e = F(a, t + 1);
|
|
1029
|
+
e.list ? ((r?.id !== e.list.id || r.tag !== e.list.tag) && (i(), r = e.list, r.tag === "ol" && O.has(r.id) && h("numbering-restart", "Продолжение нумерованного списка после другого блока начнётся с 1: Hujjat удаляет атрибут start."), O.add(r.id), n += `<${r.tag}>`), n += `<li>${e.html}</li>`) : e.continuation && r && n.endsWith("</li>") ? n = n.slice(0, -5) + e.html + "</li>" : (i(), n += e.html);
|
|
1030
|
+
} else a.localName === "tbl" ? (i(), n += I(a, t + 1)) : a.localName === "sectPr" || ([
|
|
995
1031
|
"sdt",
|
|
996
1032
|
"customXml",
|
|
997
1033
|
"ins",
|
|
998
1034
|
"del",
|
|
999
1035
|
"moveFrom",
|
|
1000
1036
|
"moveTo"
|
|
1001
|
-
].includes(a.localName) ? (i(), h("block-flattened", `Блок Word ${a.localName} преобразован без интерактивности/истории.`), ["del", "moveFrom"].includes(a.localName) || (n +=
|
|
1037
|
+
].includes(a.localName) ? (i(), h("block-flattened", `Блок Word ${a.localName} преобразован без интерактивности/истории.`), ["del", "moveFrom"].includes(a.localName) || (n += L(V(a, "sdtContent") ?? a, t + 1))) : [
|
|
1002
1038
|
"tblPr",
|
|
1003
1039
|
"tblGrid",
|
|
1004
1040
|
"tcPr",
|
|
@@ -1009,20 +1045,20 @@ async function Se(e, t = {}, n) {
|
|
|
1009
1045
|
].includes(a.localName) || (i(), h("unsupported-block", `Блок Word ${a.localName} не поддерживается HTML-адаптером.`)));
|
|
1010
1046
|
return i(), n;
|
|
1011
1047
|
}
|
|
1012
|
-
let
|
|
1013
|
-
return d && n !== void 0 && d.page.orientation !== (
|
|
1014
|
-
html:
|
|
1048
|
+
let ee = L(l, 0) || "<p><br></p>", te = await _e(r, l, n, u, (e, t) => (v = t, O.clear(), g = new Map(Array.from(e.getElementsByTagNameNS(z, "p")).map((e, t) => [e, t])), _ = new Map(Array.from(e.getElementsByTagNameNS(z, "tbl")).map((e, t) => [e, t])), L(e, 0) || "<p><br></p>"));
|
|
1049
|
+
return d && n !== void 0 && d.page.orientation !== (te?.orientation ?? "portrait") ? {
|
|
1050
|
+
html: ee,
|
|
1015
1051
|
warnings: u,
|
|
1016
1052
|
layout: d,
|
|
1017
1053
|
page: {
|
|
1018
|
-
...
|
|
1054
|
+
...te,
|
|
1019
1055
|
orientation: d.page.orientation
|
|
1020
1056
|
}
|
|
1021
1057
|
} : {
|
|
1022
|
-
html:
|
|
1058
|
+
html: ee,
|
|
1023
1059
|
warnings: u,
|
|
1024
1060
|
layout: d,
|
|
1025
|
-
page:
|
|
1061
|
+
page: te
|
|
1026
1062
|
};
|
|
1027
1063
|
}
|
|
1028
1064
|
//#endregion
|
|
@@ -1086,7 +1122,7 @@ function Y(e) {
|
|
|
1086
1122
|
let t = e.match(/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i);
|
|
1087
1123
|
return t ? t.slice(1).map((e) => Math.min(255, Number(e)).toString(16).padStart(2, "0")).join("") : Ee[e.toLowerCase()];
|
|
1088
1124
|
}
|
|
1089
|
-
function
|
|
1125
|
+
function De(e) {
|
|
1090
1126
|
if (e === "0") return 0;
|
|
1091
1127
|
let t = e?.match(/^(-?\d*\.?\d+)(pt|px|in|cm|mm|pc)$/i);
|
|
1092
1128
|
return t ? Number(t[1]) * {
|
|
@@ -1098,13 +1134,21 @@ function X(e) {
|
|
|
1098
1134
|
pc: 12
|
|
1099
1135
|
}[t[2].toLowerCase()] : void 0;
|
|
1100
1136
|
}
|
|
1101
|
-
var
|
|
1102
|
-
let t =
|
|
1137
|
+
var X = (e) => {
|
|
1138
|
+
let t = De(e);
|
|
1103
1139
|
return t === void 0 ? void 0 : Math.round(t * 20);
|
|
1104
|
-
},
|
|
1105
|
-
function
|
|
1140
|
+
}, Z = (e, t) => (X(e[`margin-${t}`]) ?? 0) + Math.max(0, X(e[`padding-${t}`]) ?? 0);
|
|
1141
|
+
function Q(e, t) {
|
|
1106
1142
|
let n = { ...t }, r = e.style;
|
|
1107
1143
|
for (let e of Array.from(r)) n[e] = r.getPropertyValue(e).trim();
|
|
1144
|
+
for (let e of [
|
|
1145
|
+
"before",
|
|
1146
|
+
"after",
|
|
1147
|
+
"inside"
|
|
1148
|
+
]) {
|
|
1149
|
+
let t = `page-break-${e}`, i = r.getPropertyValue(t);
|
|
1150
|
+
i && (n[t] = i);
|
|
1151
|
+
}
|
|
1108
1152
|
for (let e of ["margin", "padding"]) for (let t of [
|
|
1109
1153
|
"top",
|
|
1110
1154
|
"right",
|
|
@@ -1179,12 +1223,12 @@ async function ke(e, t, n = E(), r) {
|
|
|
1179
1223
|
};
|
|
1180
1224
|
r?.line_height !== void 0 && (h["line-height"] = String(r.line_height));
|
|
1181
1225
|
function g(e) {
|
|
1182
|
-
let t = `<w:rFonts w:ascii="${
|
|
1183
|
-
return n !== void 0 && (t += `<w:sz w:val="${Math.round(n * 2)}"/>`), e["font-weight"] && (t += `<w:b w:val="${e["font-weight"] === "bold" || Number(e["font-weight"]) >= 600 ? "1" : "0"}"/>`), e["font-style"] && (t += `<w:i w:val="${e["font-style"] === "italic" ? "1" : "0"}"/>`), e["text-decoration"] && (t += `<w:u w:val="${e["text-decoration"].includes("underline") ? "single" : "none"}"/><w:strike w:val="${e["text-decoration"].includes("line-through") ? "1" : "0"}"/>`), Y(e.color) && (t += `<w:color w:val="${Y(e.color)}"/>`), Y(e["background-color"]) && (t += `<w:shd w:fill="${Y(e["background-color"])}"/>`), ["super", "sub"].includes(e["vertical-align"]) && (t += `<w:vertAlign w:val="${e["vertical-align"] === "super" ? "superscript" : "subscript"}"/>`), e["text-transform"] === "uppercase" && (t += "<w:caps/>"),
|
|
1226
|
+
let t = `<w:rFonts w:ascii="${ce(e["font-family"] || "freeserif")}" w:hAnsi="${ce(e["font-family"] || "freeserif")}"/>`, n = De(e["font-size"]);
|
|
1227
|
+
return n !== void 0 && (t += `<w:sz w:val="${Math.round(n * 2)}"/>`), e["font-weight"] && (t += `<w:b w:val="${e["font-weight"] === "bold" || Number(e["font-weight"]) >= 600 ? "1" : "0"}"/>`), e["font-style"] && (t += `<w:i w:val="${e["font-style"] === "italic" ? "1" : "0"}"/>`), e["text-decoration"] && (t += `<w:u w:val="${e["text-decoration"].includes("underline") ? "single" : "none"}"/><w:strike w:val="${e["text-decoration"].includes("line-through") ? "1" : "0"}"/>`), Y(e.color) && (t += `<w:color w:val="${Y(e.color)}"/>`), Y(e["background-color"]) && (t += `<w:shd w:fill="${Y(e["background-color"])}"/>`), ["super", "sub"].includes(e["vertical-align"]) && (t += `<w:vertAlign w:val="${e["vertical-align"] === "super" ? "superscript" : "subscript"}"/>`), e["text-transform"] === "uppercase" && (t += "<w:caps/>"), X(e["letter-spacing"]) !== void 0 && (t += `<w:spacing w:val="${X(e["letter-spacing"])}"/>`), t;
|
|
1184
1228
|
}
|
|
1185
1229
|
function _(e, t) {
|
|
1186
1230
|
let n = "";
|
|
1187
|
-
for (let r of e) r.nodeType === 3 ? r.textContent && (n += `<w:r><w:rPr>${g(t)}</w:rPr><w:t xml:space="preserve">${
|
|
1231
|
+
for (let r of e) r.nodeType === 3 ? r.textContent && (n += `<w:r><w:rPr>${g(t)}</w:rPr><w:t xml:space="preserve">${ce(r.textContent)}</w:t></w:r>`) : r instanceof Element && (r.localName === "br" ? n += "<w:r><w:br/></w:r>" : n += _(r.childNodes, Q(r, t)));
|
|
1188
1232
|
return n;
|
|
1189
1233
|
}
|
|
1190
1234
|
function v(e, r, a, o = i) {
|
|
@@ -1195,7 +1239,7 @@ async function ke(e, t, n = E(), r) {
|
|
|
1195
1239
|
"center",
|
|
1196
1240
|
"justify"
|
|
1197
1241
|
].includes(c) && (s += `<w:jc w:val="${c === "justify" ? "both" : c}"/>`);
|
|
1198
|
-
let l = Math.max(o.before ?? 0,
|
|
1242
|
+
let l = Math.max(o.before ?? 0, X(r["margin-top"]) ?? 0), u = Math.max(o.after ?? 0, X(r["margin-bottom"]) ?? (o.cell ? 0 : 120)), d = "", f = r["line-height"];
|
|
1199
1243
|
if (f && /^\d*\.?\d+%?$/.test(f)) {
|
|
1200
1244
|
let e = parseFloat(f) / (f.endsWith("%") ? 100 : 1), r = n.lineSpacingPolicy === "preserve";
|
|
1201
1245
|
e < 1 && M(t, r ? "line-height-overlap-risk" : "line-height-normalized", r ? "Тесный интервал сохранён; проверьте наложение строк." : "Тесный интервал увеличен до одинарного; проверьте переносы.", "warning", {
|
|
@@ -1204,7 +1248,7 @@ async function ke(e, t, n = E(), r) {
|
|
|
1204
1248
|
before: e,
|
|
1205
1249
|
after: r ? e : 1
|
|
1206
1250
|
}), d = ` w:line="${Math.round(Math.max(+!r, e) * 240)}" w:lineRule="auto"`;
|
|
1207
|
-
} else
|
|
1251
|
+
} else De(f) !== void 0 && (d = ` w:line="${Math.max(0, X(f))}" w:lineRule="atLeast"`);
|
|
1208
1252
|
s += `<w:spacing w:before="${Math.max(0, l)}" w:after="${Math.max(0, u)}"${d}/>`;
|
|
1209
1253
|
let p = "";
|
|
1210
1254
|
for (let [e, t] of [
|
|
@@ -1212,7 +1256,7 @@ async function ke(e, t, n = E(), r) {
|
|
|
1212
1256
|
["margin-right", "right"],
|
|
1213
1257
|
["text-indent", "firstLine"]
|
|
1214
1258
|
]) {
|
|
1215
|
-
let n = t === "left" ? o.left : t === "right" ? o.right : void 0, i = t === "left" || t === "right" ? Math.max(0,
|
|
1259
|
+
let n = t === "left" ? o.left : t === "right" ? o.right : void 0, i = t === "left" || t === "right" ? Math.max(0, X(r[`padding-${t}`]) ?? 0) : 0, s = X(r[e]), c = s !== void 0 || i ? (s ?? 0) + i : void 0, l = a ? t === "left" ? 360 : t === "firstLine" ? a.pending ? -180 : 0 : void 0 : void 0, u = c === void 0 && !n && l === void 0 ? void 0 : (c ?? (t === "firstLine" ? l : 0) ?? 0) + (n ?? 0) + (t === "left" ? l ?? 0 : 0);
|
|
1216
1260
|
u !== void 0 && (p += t === "firstLine" && u < 0 ? ` w:hanging="${-u}"` : ` w:${t}="${u}"`);
|
|
1217
1261
|
}
|
|
1218
1262
|
p && (s += `<w:ind${p}/>`);
|
|
@@ -1233,7 +1277,7 @@ async function ke(e, t, n = E(), r) {
|
|
|
1233
1277
|
"avoid"
|
|
1234
1278
|
]
|
|
1235
1279
|
]) (r[e] === n || r[e] === "auto") && (s += `<w:${t} w:val="${r[e] === n ? "1" : "0"}"/>`);
|
|
1236
|
-
return a && (a.pending ? s += `<w:numPr><w:ilvl w:val="0"/><w:numId w:val="${a.id}"/></w:numPr>` : s += "<w:pStyle w:val=\"AzHujjatListContinuation\"/>", a.pending = !1), `<w:p><w:pPr>${s}</w:pPr>${_(e, r)}</w:p
|
|
1280
|
+
return a && (a.pending ? s += `<w:numPr><w:ilvl w:val="0"/><w:numId w:val="${a.id}"/></w:numPr>` : s += "<w:pStyle w:val=\"AzHujjatListContinuation\"/>", a.pending = !1), `<w:p><w:pPr>${s}</w:pPr>${_(e, r)}</w:p>`;
|
|
1237
1281
|
}
|
|
1238
1282
|
function y(e, t) {
|
|
1239
1283
|
let n = e.style, r = n.getPropertyValue(`border-${t}-style`) || n.borderStyle, i = n.getPropertyValue(`border-${t}-width`) || n.borderWidth, a = n.getPropertyValue(`border-${t}-color`) || n.borderColor;
|
|
@@ -1243,14 +1287,14 @@ async function ke(e, t, n = E(), r) {
|
|
|
1243
1287
|
dotted: "dotted",
|
|
1244
1288
|
double: "double",
|
|
1245
1289
|
none: "nil"
|
|
1246
|
-
}[r] ?? "single"}" w:sz="${Math.max(0, Math.round((
|
|
1290
|
+
}[r] ?? "single"}" w:sz="${Math.max(0, Math.round((De(i) ?? .75) * 8))}" w:color="${Y(a) ?? "000000"}"/>`;
|
|
1247
1291
|
}
|
|
1248
1292
|
function b(e, n, r) {
|
|
1249
|
-
let i = Array.from(e.querySelectorAll("tr")).filter((t) => t.closest("table") === e), a = /* @__PURE__ */ new Map(), o = 1, s = [], c = n.width?.endsWith("%") ? parseFloat(n.width) : void 0,
|
|
1250
|
-
(r.before || r.after ||
|
|
1251
|
-
let
|
|
1293
|
+
let i = Array.from(e.querySelectorAll("tr")).filter((t) => t.closest("table") === e), a = /* @__PURE__ */ new Map(), o = 1, s = [], c = n.width?.endsWith("%") ? parseFloat(n.width) : void 0, u = (r.left ?? 0) + (X(n["margin-left"]) ?? 0), d = (r.right ?? 0) + (X(n["margin-right"]) ?? 0), f = Math.max(1, r.width - u - d), p = Math.max(1, Math.round(X(n.width) ?? (c === void 0 ? f : f * c / 100)));
|
|
1294
|
+
(r.before || r.after || X(n["margin-top"]) || X(n["margin-bottom"])) && M(t, "table-margin-approximation", "Вертикальные отступы контейнера вокруг таблицы требуют проверки.");
|
|
1295
|
+
let m = (e) => {
|
|
1252
1296
|
if (e > 1e3) throw new A("Таблица содержит более 1000 столбцов с учётом объединённых ячеек.");
|
|
1253
|
-
},
|
|
1297
|
+
}, h = i.map((t) => {
|
|
1254
1298
|
let r = 0, i = [];
|
|
1255
1299
|
function c() {
|
|
1256
1300
|
for (; a.has(r);) {
|
|
@@ -1260,21 +1304,21 @@ async function ke(e, t, n = E(), r) {
|
|
|
1260
1304
|
span: e.span,
|
|
1261
1305
|
rowSpan: 0,
|
|
1262
1306
|
css: {}
|
|
1263
|
-
}), --e.remaining === 0 && a.delete(r), r += e.span,
|
|
1307
|
+
}), --e.remaining === 0 && a.delete(r), r += e.span, m(r);
|
|
1264
1308
|
}
|
|
1265
1309
|
}
|
|
1266
1310
|
for (let o of Array.from(t.children).filter((e) => e.localName === "td" || e.localName === "th")) {
|
|
1267
1311
|
c();
|
|
1268
1312
|
let t = Number(o.getAttribute("colspan") || 1), l = Number(o.getAttribute("rowspan") || 1);
|
|
1269
|
-
|
|
1313
|
+
m(r + t);
|
|
1270
1314
|
let u = [], d = o.parentElement;
|
|
1271
1315
|
for (; d && d !== e;) u.unshift(d), d = d.parentElement;
|
|
1272
|
-
let
|
|
1316
|
+
let f = (e) => ({
|
|
1273
1317
|
...Oe(e),
|
|
1274
1318
|
...e["background-color"] ? { "background-color": e["background-color"] } : {}
|
|
1275
|
-
}), h =
|
|
1319
|
+
}), h = Q(o, f(u.reduce((e, t) => Q(t, f(e)), n)));
|
|
1276
1320
|
o.localName === "th" && (h["font-weight"] = "bold");
|
|
1277
|
-
let g =
|
|
1321
|
+
let g = X(h.width) ?? (h.width?.endsWith("%") ? parseFloat(h.width) * p / 100 : void 0);
|
|
1278
1322
|
g !== void 0 && Number.isFinite(g) && g > 0 && s.push({
|
|
1279
1323
|
column: r,
|
|
1280
1324
|
span: t,
|
|
@@ -1302,44 +1346,55 @@ async function ke(e, t, n = E(), r) {
|
|
|
1302
1346
|
c();
|
|
1303
1347
|
}
|
|
1304
1348
|
return o = Math.max(o, r), i;
|
|
1305
|
-
}),
|
|
1306
|
-
let a =
|
|
1307
|
-
|
|
1308
|
-
let
|
|
1309
|
-
|
|
1310
|
-
"
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
"
|
|
1316
|
-
"
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1349
|
+
}), g = Ce(o, p, s, t), _ = "<w:p><w:pPr><w:spacing w:before=\"0\" w:after=\"0\"/></w:pPr></w:p>", v = !0, b = h.map((e, n) => {
|
|
1350
|
+
let r = i[n], a = Q(r, {}), o = X(a.height), s = o !== void 0 && o >= 0 ? `<w:trHeight w:val="${o}" w:hRule="atLeast"/>` : "";
|
|
1351
|
+
["avoid", "auto"].includes(a["page-break-inside"]) && (s += `<w:cantSplit w:val="${a["page-break-inside"] === "avoid" ? "1" : "0"}"/>`);
|
|
1352
|
+
let c = r.parentElement?.localName === "thead";
|
|
1353
|
+
return c && v ? s += "<w:tblHeader/>" : c && M(t, "table-header-not-contiguous", "Повторяемая шапка должна находиться в начале таблицы.", "warning", {
|
|
1354
|
+
stage: "import",
|
|
1355
|
+
location: { htmlPath: l.get(r) }
|
|
1356
|
+
}), c || (v = !1), `<w:tr>${s ? `<w:trPr>${s}</w:trPr>` : ""}${e.map(({ element: e, column: t, span: n, rowSpan: r, css: i }) => {
|
|
1357
|
+
let a = g.slice(t, t + n).reduce((e, t) => e + t, 0), o = `<w:tcW w:w="${a}" w:type="dxa"/>`;
|
|
1358
|
+
if (n > 1 && (o += `<w:gridSpan w:val="${n}"/>`), r > 1 && (o += "<w:vMerge w:val=\"restart\"/>"), !e) return `<w:tc><w:tcPr>${o}${r === 0 ? "<w:vMerge/>" : ""}</w:tcPr>${_}</w:tc>`;
|
|
1359
|
+
let s = Y(i["background-color"]);
|
|
1360
|
+
s && (o += `<w:shd w:fill="${s}"/>`), [
|
|
1361
|
+
"top",
|
|
1362
|
+
"middle",
|
|
1363
|
+
"bottom"
|
|
1364
|
+
].includes(i["vertical-align"]) && (o += `<w:vAlign w:val="${i["vertical-align"] === "middle" ? "center" : i["vertical-align"]}"/>`);
|
|
1365
|
+
let c = [
|
|
1366
|
+
"top",
|
|
1367
|
+
"left",
|
|
1368
|
+
"bottom",
|
|
1369
|
+
"right"
|
|
1370
|
+
], l = c.map((t) => y(e, t)).join("");
|
|
1371
|
+
l && (o += `<w:tcBorders>${l}</w:tcBorders>`);
|
|
1372
|
+
let u = c.map((e) => X(i[`padding-${e}`]) === void 0 ? "" : `<w:${e} w:w="${Math.max(0, X(i[`padding-${e}`]))}" w:type="dxa"/>`).join("");
|
|
1373
|
+
u && (o += `<w:tcMar>${u}</w:tcMar>`);
|
|
1374
|
+
let d = Math.max(1, a - Math.max(0, X(i["padding-left"]) ?? 0) - Math.max(0, X(i["padding-right"]) ?? 0)), f = C(S(e.childNodes, Oe(i), void 0, {
|
|
1375
|
+
width: d,
|
|
1376
|
+
cell: !0
|
|
1377
|
+
}));
|
|
1378
|
+
return (!f || f.endsWith("</w:tbl>")) && (f += _), `<w:tc><w:tcPr>${o}</w:tcPr>${f}</w:tc>`;
|
|
1379
|
+
}).join("") || `<w:tc>${_}</w:tc>`}</w:tr>`;
|
|
1380
|
+
}).join(""), x = g.map((e) => `<w:gridCol w:w="${e}"/>`).join(""), w = c !== void 0 && !u && !d ? `<w:tblW w:w="${Math.round(c * 50)}" w:type="pct"/>` : `<w:tblW w:w="${p}" w:type="dxa"/>`, T = [
|
|
1329
1381
|
"top",
|
|
1330
1382
|
"left",
|
|
1331
1383
|
"bottom",
|
|
1332
1384
|
"right"
|
|
1333
|
-
].map((t) => y(e, t)).join(""),
|
|
1334
|
-
return `<w:tbl><w:tblPr>${
|
|
1385
|
+
].map((t) => y(e, t)).join(""), E = n["table-layout"] === "auto" ? "autofit" : "fixed";
|
|
1386
|
+
return `<w:tbl><w:tblPr>${w}${u ? `<w:tblInd w:w="${u}" w:type="dxa"/>` : ""}${T ? `<w:tblBorders>${T}</w:tblBorders>` : ""}<w:tblLayout w:type="${E}"/></w:tblPr><w:tblGrid>${x}</w:tblGrid>${b}</w:tbl>`;
|
|
1387
|
+
}
|
|
1388
|
+
function x(e, t) {
|
|
1389
|
+
return e.length && (e[0].before ||= t["page-break-before"] === "always", e[e.length - 1].after ||= t["page-break-after"] === "always"), e;
|
|
1335
1390
|
}
|
|
1336
|
-
function
|
|
1337
|
-
let u =
|
|
1391
|
+
function S(e, n, a, c = i) {
|
|
1392
|
+
let u = [], d = [], f = 0, p = Array.from(e).filter((e) => e.nodeType !== 3 || e.textContent), m = (e, t = e) => ({
|
|
1338
1393
|
...c,
|
|
1339
1394
|
before: e === 0 ? c.before : void 0,
|
|
1340
1395
|
after: t === p.length - 1 ? c.after : void 0
|
|
1341
1396
|
}), h = (e) => {
|
|
1342
|
-
d.length && (
|
|
1397
|
+
d.length && u.push(...x([{ xml: v(d, n, a, m(f, e)) }], n)), d = [];
|
|
1343
1398
|
};
|
|
1344
1399
|
for (let [e, i] of p.entries()) {
|
|
1345
1400
|
if (!(i instanceof Element) || !Te.has(i.localName)) {
|
|
@@ -1347,53 +1402,66 @@ async function ke(e, t, n = E(), r) {
|
|
|
1347
1402
|
continue;
|
|
1348
1403
|
}
|
|
1349
1404
|
h(e - 1);
|
|
1350
|
-
let p =
|
|
1405
|
+
let p = Q(i, Oe(n)), g = {
|
|
1351
1406
|
...m(e),
|
|
1352
1407
|
htmlPath: l.get(i)
|
|
1353
1408
|
};
|
|
1354
|
-
if (i.localName === "p" && r?.paragraph_spacing !== void 0 && (p["margin-bottom"] ??= `${r.paragraph_spacing}pt`), r !== void 0 && i.localName === "h1" && (p["margin-bottom"] ??= "7.5pt"), i.localName === "table") u
|
|
1409
|
+
if (i.localName === "p" && r?.paragraph_spacing !== void 0 && (p["margin-bottom"] ??= `${r.paragraph_spacing}pt`), r !== void 0 && i.localName === "h1" && (p["margin-bottom"] ??= "7.5pt"), i.localName === "table") u.push(...x([{
|
|
1410
|
+
xml: b(i, p, g),
|
|
1411
|
+
table: !0
|
|
1412
|
+
}], p));
|
|
1355
1413
|
else if (i.localName === "ol" || i.localName === "ul") {
|
|
1356
1414
|
let e = o++;
|
|
1357
1415
|
s.push({
|
|
1358
1416
|
id: e,
|
|
1359
1417
|
tag: i.localName
|
|
1360
1418
|
}), a && M(t, "nested-list-normalized", "Вложенный список при открытии становится отдельным списком.", "loss");
|
|
1361
|
-
let n = Array.from(i.children).filter((e) => e.localName === "li");
|
|
1362
|
-
for (let [t,
|
|
1363
|
-
let
|
|
1419
|
+
let n = Array.from(i.children).filter((e) => e.localName === "li"), r = [];
|
|
1420
|
+
for (let [t, i] of n.entries()) {
|
|
1421
|
+
let a = {
|
|
1364
1422
|
id: e,
|
|
1365
1423
|
pending: !0
|
|
1366
|
-
},
|
|
1424
|
+
}, o = Q(i, Oe(p)), s = {
|
|
1367
1425
|
...g,
|
|
1368
|
-
before: Math.max(t === 0 ? g.before ?? 0 : 0,
|
|
1369
|
-
after: Math.max(t === n.length - 1 ? g.after ?? 0 : 0,
|
|
1370
|
-
left: (g.left ?? 0) +
|
|
1371
|
-
right: (g.right ?? 0) +
|
|
1372
|
-
},
|
|
1373
|
-
|
|
1426
|
+
before: Math.max(t === 0 ? g.before ?? 0 : 0, X(o["margin-top"]) ?? 0),
|
|
1427
|
+
after: Math.max(t === n.length - 1 ? g.after ?? 0 : 0, X(o["margin-bottom"]) ?? 0),
|
|
1428
|
+
left: (g.left ?? 0) + Z(p, "left") + Z(o, "left"),
|
|
1429
|
+
right: (g.right ?? 0) + Z(p, "right") + Z(o, "right")
|
|
1430
|
+
}, c = Object.fromEntries(Object.entries(o).filter(([e]) => !/^(margin|padding)(-|$)/.test(e))), l = S(i.childNodes, c, a, s);
|
|
1431
|
+
r.push(...x(l.length ? l : [{ xml: v([], c, a, s) }], o));
|
|
1374
1432
|
}
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
"margin-bottom": "0pt"
|
|
1378
|
-
})), u += x(i.childNodes, Oe(p), a, {
|
|
1433
|
+
u.push(...x(r, p));
|
|
1434
|
+
} else i.localName === "div" && Array.from(i.children).some((e) => Te.has(e.localName)) ? u.push(...x(S(i.childNodes, Oe(p), a, {
|
|
1379
1435
|
...g,
|
|
1380
|
-
left: (g.left ?? 0) +
|
|
1381
|
-
right: (g.right ?? 0) +
|
|
1382
|
-
before: Math.max(g.before ?? 0,
|
|
1383
|
-
after: Math.max(g.after ?? 0,
|
|
1384
|
-
}), p["page-break-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
})
|
|
1436
|
+
left: (g.left ?? 0) + Z(p, "left"),
|
|
1437
|
+
right: (g.right ?? 0) + Z(p, "right"),
|
|
1438
|
+
before: Math.max(g.before ?? 0, X(p["margin-top"]) ?? 0),
|
|
1439
|
+
after: Math.max(g.after ?? 0, X(p["margin-bottom"]) ?? 0)
|
|
1440
|
+
}), p)) : i.localName === "div" && !i.children.length && !i.textContent && p["page-break-before"] === "always" && Array.from(i.style).every((e) => ["page-break-before", "break-before"].includes(e)) ? u.push({
|
|
1441
|
+
xml: "",
|
|
1442
|
+
marker: !0
|
|
1443
|
+
}) : i.localName === "hr" ? (M(t, "rule-normalized", "Разделитель HTML преобразован в строку."), u.push(...x([{ xml: v([document.createTextNode("────────────────")], p, void 0, c) }], p))) : u.push(...x([{ xml: v(i.childNodes, p, a, g) }], p));
|
|
1388
1444
|
}
|
|
1389
1445
|
return h(p.length - 1), u;
|
|
1390
1446
|
}
|
|
1391
|
-
|
|
1447
|
+
function C(e) {
|
|
1448
|
+
let t = "<w:p><w:pPr><w:pStyle w:val=\"AzHujjatPageBoundary\"/><w:pageBreakBefore/><w:spacing w:before=\"0\" w:after=\"0\" w:line=\"1\" w:lineRule=\"exact\"/></w:pPr></w:p>", n = "", r = !1, i = 0;
|
|
1449
|
+
for (let a of e) {
|
|
1450
|
+
if (a.marker) {
|
|
1451
|
+
i++;
|
|
1452
|
+
continue;
|
|
1453
|
+
}
|
|
1454
|
+
let e = Math.max(i, r || a.before ? 1 : 0);
|
|
1455
|
+
e ? (n += t.repeat(Math.max(0, e - 1)), a.table ? n += t + a.xml : n += a.xml.replace(/<w:pPr>(.*?)<\/w:pPr>/s, (e, t) => `<w:pPr>${t.replace(/<w:pageBreakBefore\b[^>]*\/>/g, "")}<w:pageBreakBefore/></w:pPr>`)) : n += a.xml, r = !!a.after, i = 0;
|
|
1456
|
+
}
|
|
1457
|
+
return n + t.repeat(i);
|
|
1458
|
+
}
|
|
1459
|
+
let w = C(S(a.childNodes, h)) || "<w:p/>", D = c.map((e) => ({
|
|
1392
1460
|
...e,
|
|
1393
|
-
xml:
|
|
1461
|
+
xml: C(S(e.root.childNodes, h)) || "<w:p/>"
|
|
1394
1462
|
}));
|
|
1395
|
-
if (new TextEncoder().encode(
|
|
1396
|
-
let
|
|
1463
|
+
if (new TextEncoder().encode(w + D.map((e) => e.xml).join("")).byteLength > 8387584) throw new A("После преобразования HTML документ превышает допустимый размер XML (8 МиБ).");
|
|
1464
|
+
let O = new Set("font-family font-size font-weight font-style color background-color text-align text-decoration text-transform vertical-align letter-spacing line-height margin margin-top margin-bottom margin-left margin-right text-indent width border border-style border-width border-color border-top border-right border-bottom border-left border-collapse table-layout padding padding-top padding-right padding-bottom padding-left page-break-before page-break-after page-break-inside break-before break-after break-inside".split(" "));
|
|
1397
1465
|
for (let e of [a, ...c.map((e) => e.root)]) for (let n of e.querySelectorAll("[style]")) for (let e of Array.from(n.style)) {
|
|
1398
1466
|
let r = n.style.getPropertyValue(e), i = ["td", "th"].includes(n.localName);
|
|
1399
1467
|
e === "vertical-align" && !i && ![
|
|
@@ -1406,21 +1474,21 @@ async function ke(e, t, n = E(), r) {
|
|
|
1406
1474
|
before: r,
|
|
1407
1475
|
after: "baseline"
|
|
1408
1476
|
});
|
|
1409
|
-
let a = Te.has(n.localName) && !["table", "hr"].includes(n.localName);
|
|
1410
|
-
(
|
|
1477
|
+
let a = Te.has(n.localName) && !["table", "hr"].includes(n.localName), o = e.startsWith("padding") && !i && (!a || !["padding-left", "padding-right"].includes(e)) || e.startsWith("border") && !i && n.localName !== "table" || e === "font-size" && De(r) === void 0 || ["color", "background-color"].includes(e) && Y(r) === void 0, s = n.localName === "tr" && e === "height" && X(r) !== void 0 && X(r) >= 0;
|
|
1478
|
+
(o || !s && !O.has(e) && !/^border-(top|right|bottom|left)-(width|style|color)$/.test(e)) && M(t, "html-style-approximation", `Стиль ${e} может измениться при открытии HTML в DOCX-редакторе.`, "warning", {
|
|
1411
1479
|
stage: "import",
|
|
1412
1480
|
location: { htmlPath: l.get(n) },
|
|
1413
1481
|
before: r
|
|
1414
1482
|
});
|
|
1415
1483
|
}
|
|
1416
|
-
let
|
|
1417
|
-
|
|
1418
|
-
let
|
|
1419
|
-
for (let { kind: e, xml: t } of
|
|
1420
|
-
let
|
|
1421
|
-
|
|
1422
|
-
let
|
|
1423
|
-
return
|
|
1484
|
+
let k = new p();
|
|
1485
|
+
k.file("[Content_Types].xml", "<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\"><Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/><Default Extension=\"xml\" ContentType=\"application/xml\"/><Override PartName=\"/word/document.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml\"/><Override PartName=\"/word/styles.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml\"/><Override PartName=\"/word/numbering.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml\"/></Types>"), k.file("_rels/.rels", "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\"><Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument\" Target=\"word/document.xml\"/></Relationships>"), k.file("word/_rels/document.xml.rels", "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\"><Relationship Id=\"rIdStyles\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles\" Target=\"styles.xml\"/><Relationship Id=\"rIdNumbering\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering\" Target=\"numbering.xml\"/></Relationships>");
|
|
1486
|
+
let j = n.page, N = "http://schemas.openxmlformats.org/officeDocument/2006/relationships", P = D.map(({ kind: e }) => `<w:${e}Reference w:type="default" r:id="rId${e}"/>`).join("");
|
|
1487
|
+
for (let { kind: e, xml: t } of D) k.file(`word/${e}1.xml`, `<w:${e === "header" ? "hdr" : "ftr"} xmlns:w="${z}">${t}</w:${e === "header" ? "hdr" : "ftr"}>`);
|
|
1488
|
+
let F = await k.file("[Content_Types].xml").async("string");
|
|
1489
|
+
k.file("[Content_Types].xml", F.replace("</Types>", D.map(({ kind: e }) => `<Override PartName="/word/${e}1.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.${e}+xml"/>`).join("") + "</Types>"));
|
|
1490
|
+
let I = await k.file("word/_rels/document.xml.rels").async("string");
|
|
1491
|
+
return k.file("word/_rels/document.xml.rels", I.replace("</Relationships>", D.map(({ kind: e }) => `<Relationship Id="rId${e}" Type="${N}/${e}" Target="${e}1.xml"/>`).join("") + "</Relationships>")), k.file("word/document.xml", `<w:document xmlns:w="${z}" xmlns:r="${N}"><w:body>${w}<w:sectPr>${P}<w:pgSz w:w="${j.widthTwips}" w:h="${j.heightTwips}" w:orient="${j.orientation}"/><w:pgMar ${Object.entries(j.margins).map(([e, t]) => `w:${e}="${t}"`).join(" ")}/></w:sectPr></w:body></w:document>`), k.file("word/styles.xml", `<w:styles xmlns:w="${z}"><w:docDefaults><w:rPrDefault><w:rPr>${g(h)}</w:rPr></w:rPrDefault></w:docDefaults><w:style w:type="paragraph" w:default="1" w:styleId="Normal"><w:name w:val="Normal"/><w:pPr><w:spacing w:after="120"/></w:pPr></w:style><w:style w:type="paragraph" w:styleId="AzHujjatListContinuation"><w:name w:val="List continuation"/><w:basedOn w:val="Normal"/></w:style><w:style w:type="paragraph" w:styleId="AzHujjatPageBoundary"><w:name w:val="Page boundary"/><w:basedOn w:val="Normal"/></w:style></w:styles>`), k.file("word/numbering.xml", `<w:numbering xmlns:w="${z}">${s.map(({ id: e, tag: t }) => `<w:abstractNum w:abstractNumId="${e}"><w:multiLevelType w:val="singleLevel"/><w:lvl w:ilvl="0"><w:start w:val="1"/><w:numFmt w:val="${t === "ol" ? "decimal" : "bullet"}"/><w:lvlText w:val="${t === "ol" ? "%1." : "•"}"/><w:pPr><w:ind w:left="360" w:hanging="180"/></w:pPr></w:lvl></w:abstractNum><w:num w:numId="${e}"><w:abstractNumId w:val="${e}"/></w:num>`).join("")}</w:numbering>`), new Uint8Array(await k.generateAsync({
|
|
1424
1492
|
type: "arraybuffer",
|
|
1425
1493
|
compression: "DEFLATE"
|
|
1426
1494
|
}));
|
|
@@ -1429,12 +1497,12 @@ async function ke(e, t, n = E(), r) {
|
|
|
1429
1497
|
//#region src/library/hujjat/docx.ts
|
|
1430
1498
|
async function Ae(e, t) {
|
|
1431
1499
|
let n = t.page === void 0 ? t.locales?.[t.locale]?.page : t.page;
|
|
1432
|
-
|
|
1433
|
-
let r = await Se(e, t.layout, n), i =
|
|
1500
|
+
ye(n);
|
|
1501
|
+
let r = await Se(e, t.layout, n), i = se(t.locales ?? {}, t.locale, r.html);
|
|
1434
1502
|
return i.warnings.unshift(...r.warnings), i.layout = r.layout, i.page = be(r.page, i), i.page !== void 0 && (i.locales[t.locale].page = structuredClone(i.page)), N(i.warnings, t.allowLossy), i;
|
|
1435
1503
|
}
|
|
1436
1504
|
async function je(e, t, n = {}) {
|
|
1437
|
-
let r = ae(
|
|
1505
|
+
let r = ae(oe(e, t));
|
|
1438
1506
|
r.page = be(e[t]?.page, r);
|
|
1439
1507
|
let i = xe(r.page, n.layout);
|
|
1440
1508
|
D(i.page, xe(r.page).page) || M(r.warnings, "layout-not-carried-by-html", "Геометрия layout отличается от настроек page на сервере. Текущий PDF-контракт поддерживает только ориентацию A4 и фиксированные поля.", "warning", { stage: "import" });
|
|
@@ -1451,16 +1519,16 @@ async function Me(e) {
|
|
|
1451
1519
|
if (e.byteLength > 31457280) throw new A("DOCX превышает 30 МиБ.");
|
|
1452
1520
|
let t = await p.loadAsync(e);
|
|
1453
1521
|
if (Object.keys(t.files).length > 2e3) throw new A("В DOCX более 2000 частей.");
|
|
1454
|
-
let [n, r] = await Promise.all([
|
|
1522
|
+
let [n, r] = await Promise.all([q(t, "word/document.xml"), q(t, "word/styles.xml")]), i = V(n?.documentElement, "body");
|
|
1455
1523
|
if (!i) throw new A("Нет структуры документа Word.");
|
|
1456
|
-
let a = [], o = V(r?.documentElement, "docDefaults"), s = V(V(o, "pPrDefault"), "pPr"), c =
|
|
1524
|
+
let a = [], o = V(r?.documentElement, "docDefaults"), s = V(V(o, "pPrDefault"), "pPr"), c = J(V(V(o, "rPrDefault"), "rPr")), l = new Map(B(r?.documentElement, "style").map((e) => [H(e, "styleId"), e])), u = B(r?.documentElement, "style").find((e) => H(e, "type") === "paragraph" && H(e, "default") === "1");
|
|
1457
1525
|
function d(e, t = /* @__PURE__ */ new Set()) {
|
|
1458
1526
|
if (!e || t.has(e) || t.size >= 30) return [];
|
|
1459
1527
|
t.add(e);
|
|
1460
1528
|
let n = l.get(e);
|
|
1461
1529
|
return n ? [...d(U(n, "basedOn"), t), n] : [];
|
|
1462
1530
|
}
|
|
1463
|
-
let f = Array.from(i.getElementsByTagNameNS(
|
|
1531
|
+
let f = Array.from(i.getElementsByTagNameNS(z, "sectPr")), m = (e) => [V(e, "pgSz"), V(e, "pgMar")].map((e) => Array.from(e?.attributes ?? []).map((e) => [e.localName, e.value]).sort()).flat();
|
|
1464
1532
|
return f.forEach((e, t) => {
|
|
1465
1533
|
t && JSON.stringify(m(e)) !== JSON.stringify(m(f[0])) && M(a, "source-section-layout", "Раздел использует другую геометрию. Она сохраняется в DOCX; единый профиль HTML её не описывает.", "warning", {
|
|
1466
1534
|
stage: "import",
|
|
@@ -1471,18 +1539,18 @@ async function Me(e) {
|
|
|
1471
1539
|
before: JSON.stringify(m(e)),
|
|
1472
1540
|
after: JSON.stringify(m(e))
|
|
1473
1541
|
});
|
|
1474
|
-
}), Array.from(i.getElementsByTagNameNS(
|
|
1542
|
+
}), Array.from(i.getElementsByTagNameNS(z, "p")).forEach((e, t) => {
|
|
1475
1543
|
let n = {
|
|
1476
1544
|
part: "word/document.xml",
|
|
1477
1545
|
paragraphIndex: t
|
|
1478
|
-
}, r = V(e, "pPr"), i = d(U(r, "pStyle") || H(u, "styleId")), o =
|
|
1546
|
+
}, r = V(e, "pPr"), i = d(U(r, "pStyle") || H(u, "styleId")), o = me([
|
|
1479
1547
|
s,
|
|
1480
1548
|
...i.map((e) => V(e, "pPr")),
|
|
1481
1549
|
r
|
|
1482
|
-
]), l = Object.assign({}, c, ...i.map((e) =>
|
|
1483
|
-
Array.from(e.getElementsByTagNameNS(
|
|
1484
|
-
let r = V(e, "rPr"), i = Object.assign({}, l, ...d(U(r, "rStyle")).map((e) =>
|
|
1485
|
-
i["--az-hidden"] !== "1" && (f = Math.max(f,
|
|
1550
|
+
]), l = Object.assign({}, c, ...i.map((e) => J(V(e, "rPr")))), f = ge(l);
|
|
1551
|
+
Array.from(e.getElementsByTagNameNS(z, "r")).forEach((e, t) => {
|
|
1552
|
+
let r = V(e, "rPr"), i = Object.assign({}, l, ...d(U(r, "rStyle")).map((e) => J(V(e, "rPr"))), J(r));
|
|
1553
|
+
i["--az-hidden"] !== "1" && (f = Math.max(f, ge(i)), Number(i["--az-position"]) && M(a, "source-baseline-shift", "Исходный фрагмент смещён по вертикали; сохранён без нормализации.", "warning", {
|
|
1486
1554
|
stage: "import",
|
|
1487
1555
|
location: {
|
|
1488
1556
|
...n,
|
|
@@ -2200,7 +2268,7 @@ var Xe = {
|
|
|
2200
2268
|
function Ze(e) {
|
|
2201
2269
|
if (!e || !Object.keys(e).length) throw new A("Нет локалей шаблона.");
|
|
2202
2270
|
for (let [t, n] of Object.entries(e)) {
|
|
2203
|
-
if (
|
|
2271
|
+
if (R(t), ye(n?.page), !n || !Array.isArray(n.blocks) || !n.blocks.length) throw new A(`Нет блоков для ${t}.`);
|
|
2204
2272
|
for (let e of n.blocks) if (e.type === "html") {
|
|
2205
2273
|
if (typeof e.props?.html != "string" || !e.props.html.trim()) throw new A(`Пустой HTML для ${t}.`);
|
|
2206
2274
|
if (new TextEncoder().encode(e.props.html).length > 1048576) throw new A(`HTML для ${t} превышает 1 МБ.`);
|
|
@@ -2221,7 +2289,7 @@ async function $e(e) {
|
|
|
2221
2289
|
let t = AbortSignal.any([AbortSignal.timeout(95e3), ...e.signal ? [e.signal] : []]);
|
|
2222
2290
|
t.throwIfAborted(), Ze(e.locales);
|
|
2223
2291
|
let n = e.locale ?? "uz";
|
|
2224
|
-
|
|
2292
|
+
R(n);
|
|
2225
2293
|
let r = new Headers(e.headers);
|
|
2226
2294
|
r.set("Content-Type", "application/json"), r.set("Accept", "application/pdf, application/json");
|
|
2227
2295
|
let i = await (e.fetcher ?? fetch)(e.url, {
|
|
@@ -2249,4 +2317,4 @@ async function $e(e) {
|
|
|
2249
2317
|
};
|
|
2250
2318
|
}
|
|
2251
2319
|
//#endregion
|
|
2252
|
-
export { Ie as AzDocumentEditor, Re as AzHtmlEditor, Be as DOCX_MIME, O as HUJJAT_LOCALES,
|
|
2320
|
+
export { Ie as AzDocumentEditor, Re as AzHtmlEditor, Be as DOCX_MIME, O as HUJJAT_LOCALES, ve as HUJJAT_MAX_CHROME_BYTES, k as HUJJAT_MAX_HTML_BYTES, C as HUJJAT_PAGE, A as HujjatCompatibilityError, ze as MAX_DOCX_BYTES, Me as analyzeDocxFormatting, Qe as createHujjatTemplatePayload, qe as downloadDocx, Ye as downloadPdf, Ke as exportFilename, Ae as exportHujjatDocx, je as importHujjatTemplate, Xe as pdfRenderProfile, Ge as readDocxFile, oe as readHujjatHtml, $e as requestHujjatPreview, Je as requestPdf, E as resolveHtmlLayout, ae as sanitizeHujjatHtml, ye as validateHujjatPage, P as validateHujjatVariable, se as withHujjatLocale };
|
package/docs/integration.ru.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Интеграция Az Editor в React-приложение
|
|
2
2
|
|
|
3
|
-
Руководство описывает API `@makhmudov.kay/az-editor` версии `0.1.0-beta.
|
|
3
|
+
Руководство описывает API `@makhmudov.kay/az-editor` версии `0.1.0-beta.6`.
|
|
4
4
|
Поддержка серверного `page`, описанная ниже, добавлена в beta.5.
|
|
5
|
+
Совместимые исправления разрывов и строк таблиц добавлены в beta.6 и описаны ниже.
|
|
5
6
|
Примеры написаны на TypeScript. Для JavaScript удалите аннотации типов и `import type`.
|
|
6
7
|
|
|
7
8
|
## Содержание
|
|
@@ -69,7 +70,7 @@ npm install @makhmudov.kay/az-editor@beta
|
|
|
69
70
|
Для фиксации опубликованной версии:
|
|
70
71
|
|
|
71
72
|
```sh
|
|
72
|
-
npm install --save-exact @makhmudov.kay/az-editor@0.1.0-beta.
|
|
73
|
+
npm install --save-exact @makhmudov.kay/az-editor@0.1.0-beta.6
|
|
73
74
|
```
|
|
74
75
|
|
|
75
76
|
Тег `beta` может указывать на более новую версию. Сохраняйте lockfile и проверяйте
|
|
@@ -952,6 +953,40 @@ CSS ограничен шрифтом/цветом, базовым оформл
|
|
|
952
953
|
пропорции колонок, `colspan` и `rowspan` сохраняются при повторном открытии.
|
|
953
954
|
Противоречивые размеры нормализуются с предупреждением `table-width-normalized`.
|
|
954
955
|
|
|
956
|
+
**С beta.6:** абсолютная ширина таблицы сохраняется и при выходе
|
|
957
|
+
за текстовую область страницы; ширина в процентах рассчитывается от доступной области.
|
|
958
|
+
`<tr style="height:53.7pt">` задаёт минимальную высоту 1074 twips, а не точную высоту:
|
|
959
|
+
строка может расти с содержимым. Начальные строки `<thead>` становятся повторяемой шапкой;
|
|
960
|
+
`page-break-inside:avoid` / `auto` на `<tr>` сохраняют запрет / разрешение разрыва строки.
|
|
961
|
+
Режимы `table-layout:fixed` и `auto` проходят обратное преобразование.
|
|
962
|
+
|
|
963
|
+
```html
|
|
964
|
+
<table style="width:566.85pt;table-layout:fixed">
|
|
965
|
+
<thead>
|
|
966
|
+
<tr style="page-break-inside:avoid"><th>Наименование</th><th>Значение</th></tr>
|
|
967
|
+
</thead>
|
|
968
|
+
<tbody>
|
|
969
|
+
<tr style="height:53.7pt"><td>Примечание</td><td><p><br></p></td></tr>
|
|
970
|
+
</tbody>
|
|
971
|
+
</table>
|
|
972
|
+
```
|
|
973
|
+
|
|
974
|
+
`table-row-height-rule-lost` сообщает, что Word `exact` превращается в минимальную высоту.
|
|
975
|
+
`floating-table-position-lost` имеет уровень `loss`: содержание таблицы остаётся, но её
|
|
976
|
+
плавающее размещение не входит в текущий HTML-контракт. Адрес содержит `part`, `tableIndex`
|
|
977
|
+
и для строк `rowIndex`; индексы начинаются с нуля. Обработайте предупреждения через
|
|
978
|
+
существующий механизм диагностики перед разрешением экспорта с потерями.
|
|
979
|
+
|
|
980
|
+
**Разрывы с beta.6:** `break-before:page` и `break-after:page`
|
|
981
|
+
принимаются наряду с `page-break-*:always`. `avoid-page` преобразуется в `avoid`;
|
|
982
|
+
`auto` остаётся `auto`. При конфликте одного свойства действует последняя допустимая
|
|
983
|
+
декларация. В сохраняемом HTML используются только серверные имена `page-break-*`.
|
|
984
|
+
Разрыв до/после контейнера либо таблицы переносится на границу следующего блока.
|
|
985
|
+
Совпадающие правила на одной границе не создают второй разрыв. Явные пустые маркеры
|
|
986
|
+
`<div style="page-break-before:always"></div>` сохраняются, в том числе в конце документа.
|
|
987
|
+
Разрыв `page-break-after:always` на последнем содержательном блоке сам по себе не добавляет
|
|
988
|
+
пустую страницу. Автоматическая пагинация по-прежнему зависит от шрифтов и геометрии.
|
|
989
|
+
|
|
955
990
|
Без `page.paragraph_spacing` абзацы внутри ячеек не получают дополнительный отступ 6pt после текста;
|
|
956
991
|
явный `margin-bottom` сохраняется. Значение `line-height` в абсолютных единицах
|
|
957
992
|
задаёт минимальную, а не жёсткую высоту строки (`atLeast`), чтобы крупный текст
|