@aster-editor/word 0.4.2 → 0.5.0
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/README.md +204 -81
- package/dist/docx/compare/align.d.ts +24 -0
- package/dist/docx/compare/align.d.ts.map +1 -0
- package/dist/docx/compare/align.js +217 -0
- package/dist/docx/compare/align.js.map +1 -0
- package/dist/docx/compare/compare-blocks.d.ts +29 -0
- package/dist/docx/compare/compare-blocks.d.ts.map +1 -0
- package/dist/docx/compare/compare-blocks.js +234 -0
- package/dist/docx/compare/compare-blocks.js.map +1 -0
- package/dist/docx/compare/compare-context.d.ts +25 -0
- package/dist/docx/compare/compare-context.d.ts.map +1 -0
- package/dist/docx/compare/compare-context.js +31 -0
- package/dist/docx/compare/compare-context.js.map +1 -0
- package/dist/docx/compare/compare-docx.d.ts +4 -0
- package/dist/docx/compare/compare-docx.d.ts.map +1 -0
- package/dist/docx/compare/compare-docx.js +173 -0
- package/dist/docx/compare/compare-docx.js.map +1 -0
- package/dist/docx/compare/compare-tables.d.ts +13 -0
- package/dist/docx/compare/compare-tables.d.ts.map +1 -0
- package/dist/docx/compare/compare-tables.js +47 -0
- package/dist/docx/compare/compare-tables.js.map +1 -0
- package/dist/docx/compare/model-borders.d.ts +27 -0
- package/dist/docx/compare/model-borders.d.ts.map +1 -0
- package/dist/docx/compare/model-borders.js +41 -0
- package/dist/docx/compare/model-borders.js.map +1 -0
- package/dist/docx/compare/model-paragraph.d.ts +25 -0
- package/dist/docx/compare/model-paragraph.d.ts.map +1 -0
- package/dist/docx/compare/model-paragraph.js +309 -0
- package/dist/docx/compare/model-paragraph.js.map +1 -0
- package/dist/docx/compare/model-sections.d.ts +11 -0
- package/dist/docx/compare/model-sections.d.ts.map +1 -0
- package/dist/docx/compare/model-sections.js +97 -0
- package/dist/docx/compare/model-sections.js.map +1 -0
- package/dist/docx/compare/model.d.ts +113 -0
- package/dist/docx/compare/model.d.ts.map +1 -0
- package/dist/docx/compare/model.js +214 -0
- package/dist/docx/compare/model.js.map +1 -0
- package/dist/docx/compare/parts.d.ts +40 -0
- package/dist/docx/compare/parts.d.ts.map +1 -0
- package/dist/docx/compare/parts.js +113 -0
- package/dist/docx/compare/parts.js.map +1 -0
- package/dist/docx/compare/tally.d.ts +23 -0
- package/dist/docx/compare/tally.d.ts.map +1 -0
- package/dist/docx/compare/tally.js +94 -0
- package/dist/docx/compare/tally.js.map +1 -0
- package/dist/docx/compare/types.d.ts +55 -0
- package/dist/docx/compare/types.d.ts.map +1 -0
- package/dist/docx/compare/types.js +2 -0
- package/dist/docx/compare/types.js.map +1 -0
- package/dist/docx/compare/values.d.ts +13 -0
- package/dist/docx/compare/values.d.ts.map +1 -0
- package/dist/docx/compare/values.js +44 -0
- package/dist/docx/compare/values.js.map +1 -0
- package/dist/docx/import-docx.d.ts.map +1 -1
- package/dist/docx/import-docx.js +65 -6
- package/dist/docx/import-docx.js.map +1 -1
- package/dist/docx/postprocess/strip-empty-paragraphs.d.ts +3 -0
- package/dist/docx/postprocess/strip-empty-paragraphs.d.ts.map +1 -1
- package/dist/docx/postprocess/strip-empty-paragraphs.js +7 -1
- package/dist/docx/postprocess/strip-empty-paragraphs.js.map +1 -1
- package/dist/docx/postprocess/types.d.ts +1 -1
- package/dist/docx/postprocess/types.d.ts.map +1 -1
- package/dist/docx/preprocess/variables.d.ts.map +1 -1
- package/dist/docx/preprocess/variables.js +2 -2
- package/dist/docx/preprocess/variables.js.map +1 -1
- package/dist/docx/preprocess/xml-utils.d.ts +7 -0
- package/dist/docx/preprocess/xml-utils.d.ts.map +1 -1
- package/dist/docx/preprocess/xml-utils.js +9 -0
- package/dist/docx/preprocess/xml-utils.js.map +1 -1
- package/dist/docx/reader/headers.d.ts +17 -0
- package/dist/docx/reader/headers.d.ts.map +1 -0
- package/dist/docx/reader/headers.js +64 -0
- package/dist/docx/reader/headers.js.map +1 -0
- package/dist/docx/reader/html.d.ts +12 -0
- package/dist/docx/reader/html.d.ts.map +1 -0
- package/dist/docx/reader/html.js +27 -0
- package/dist/docx/reader/html.js.map +1 -0
- package/dist/docx/reader/images.d.ts +27 -0
- package/dist/docx/reader/images.d.ts.map +1 -0
- package/dist/docx/reader/images.js +288 -0
- package/dist/docx/reader/images.js.map +1 -0
- package/dist/docx/reader/index.d.ts +10 -0
- package/dist/docx/reader/index.d.ts.map +1 -0
- package/dist/docx/reader/index.js +177 -0
- package/dist/docx/reader/index.js.map +1 -0
- package/dist/docx/reader/lists.d.ts +44 -0
- package/dist/docx/reader/lists.d.ts.map +1 -0
- package/dist/docx/reader/lists.js +360 -0
- package/dist/docx/reader/lists.js.map +1 -0
- package/dist/docx/reader/lost.d.ts +30 -0
- package/dist/docx/reader/lost.d.ts.map +1 -0
- package/dist/docx/reader/lost.js +215 -0
- package/dist/docx/reader/lost.js.map +1 -0
- package/dist/docx/reader/notes.d.ts +4 -0
- package/dist/docx/reader/notes.d.ts.map +1 -0
- package/dist/docx/reader/notes.js +112 -0
- package/dist/docx/reader/notes.js.map +1 -0
- package/dist/docx/reader/package.d.ts +16 -0
- package/dist/docx/reader/package.d.ts.map +1 -0
- package/dist/docx/reader/package.js +138 -0
- package/dist/docx/reader/package.js.map +1 -0
- package/dist/docx/reader/paragraphs.d.ts +15 -0
- package/dist/docx/reader/paragraphs.d.ts.map +1 -0
- package/dist/docx/reader/paragraphs.js +162 -0
- package/dist/docx/reader/paragraphs.js.map +1 -0
- package/dist/docx/reader/properties.d.ts +90 -0
- package/dist/docx/reader/properties.d.ts.map +1 -0
- package/dist/docx/reader/properties.js +402 -0
- package/dist/docx/reader/properties.js.map +1 -0
- package/dist/docx/reader/runs.d.ts +49 -0
- package/dist/docx/reader/runs.d.ts.map +1 -0
- package/dist/docx/reader/runs.js +442 -0
- package/dist/docx/reader/runs.js.map +1 -0
- package/dist/docx/reader/sections.d.ts +36 -0
- package/dist/docx/reader/sections.d.ts.map +1 -0
- package/dist/docx/reader/sections.js +158 -0
- package/dist/docx/reader/sections.js.map +1 -0
- package/dist/docx/reader/styles.d.ts +24 -0
- package/dist/docx/reader/styles.d.ts.map +1 -0
- package/dist/docx/reader/styles.js +383 -0
- package/dist/docx/reader/styles.js.map +1 -0
- package/dist/docx/reader/tables.d.ts +6 -0
- package/dist/docx/reader/tables.d.ts.map +1 -0
- package/dist/docx/reader/tables.js +175 -0
- package/dist/docx/reader/tables.js.map +1 -0
- package/dist/docx/reader/test-support.d.ts +25 -0
- package/dist/docx/reader/test-support.d.ts.map +1 -0
- package/dist/docx/reader/test-support.js +135 -0
- package/dist/docx/reader/test-support.js.map +1 -0
- package/dist/docx/reader/testing-blocks.d.ts +50 -0
- package/dist/docx/reader/testing-blocks.d.ts.map +1 -0
- package/dist/docx/reader/testing-blocks.js +302 -0
- package/dist/docx/reader/testing-blocks.js.map +1 -0
- package/dist/docx/reader/types.d.ts +181 -0
- package/dist/docx/reader/types.d.ts.map +1 -0
- package/dist/docx/reader/types.js +2 -0
- package/dist/docx/reader/types.js.map +1 -0
- package/dist/docx/reader/units-blocks.d.ts +75 -0
- package/dist/docx/reader/units-blocks.d.ts.map +1 -0
- package/dist/docx/reader/units-blocks.js +254 -0
- package/dist/docx/reader/units-blocks.js.map +1 -0
- package/dist/docx/reader/units.d.ts +72 -0
- package/dist/docx/reader/units.d.ts.map +1 -0
- package/dist/docx/reader/units.js +177 -0
- package/dist/docx/reader/units.js.map +1 -0
- package/dist/docx/types.d.ts +47 -2
- package/dist/docx/types.d.ts.map +1 -1
- package/dist/docx/writer/content-types.d.ts +30 -0
- package/dist/docx/writer/content-types.d.ts.map +1 -0
- package/dist/docx/writer/content-types.js +81 -0
- package/dist/docx/writer/content-types.js.map +1 -0
- package/dist/docx/writer/core-properties.d.ts +16 -0
- package/dist/docx/writer/core-properties.d.ts.map +1 -0
- package/dist/docx/writer/core-properties.js +64 -0
- package/dist/docx/writer/core-properties.js.map +1 -0
- package/dist/docx/writer/footnotes-merge.d.ts +30 -0
- package/dist/docx/writer/footnotes-merge.d.ts.map +1 -0
- package/dist/docx/writer/footnotes-merge.js +71 -0
- package/dist/docx/writer/footnotes-merge.js.map +1 -0
- package/dist/docx/writer/graft.d.ts +41 -0
- package/dist/docx/writer/graft.d.ts.map +1 -0
- package/dist/docx/writer/graft.js +277 -0
- package/dist/docx/writer/graft.js.map +1 -0
- package/dist/docx/writer/header-footer-graft.d.ts +34 -0
- package/dist/docx/writer/header-footer-graft.d.ts.map +1 -0
- package/dist/docx/writer/header-footer-graft.js +155 -0
- package/dist/docx/writer/header-footer-graft.js.map +1 -0
- package/dist/docx/writer/header-footer-variables.d.ts +25 -0
- package/dist/docx/writer/header-footer-variables.d.ts.map +1 -0
- package/dist/docx/writer/header-footer-variables.js +101 -0
- package/dist/docx/writer/header-footer-variables.js.map +1 -0
- package/dist/docx/writer/media.d.ts +32 -0
- package/dist/docx/writer/media.d.ts.map +1 -0
- package/dist/docx/writer/media.js +88 -0
- package/dist/docx/writer/media.js.map +1 -0
- package/dist/docx/writer/numbering-merge.d.ts +37 -0
- package/dist/docx/writer/numbering-merge.d.ts.map +1 -0
- package/dist/docx/writer/numbering-merge.js +140 -0
- package/dist/docx/writer/numbering-merge.js.map +1 -0
- package/dist/docx/writer/relationships.d.ts +63 -0
- package/dist/docx/writer/relationships.d.ts.map +1 -0
- package/dist/docx/writer/relationships.js +127 -0
- package/dist/docx/writer/relationships.js.map +1 -0
- package/dist/docx/writer/settings-merge.d.ts +21 -0
- package/dist/docx/writer/settings-merge.d.ts.map +1 -0
- package/dist/docx/writer/settings-merge.js +165 -0
- package/dist/docx/writer/settings-merge.js.map +1 -0
- package/dist/docx/writer/style-reconcile.d.ts +29 -0
- package/dist/docx/writer/style-reconcile.d.ts.map +1 -0
- package/dist/docx/writer/style-reconcile.js +319 -0
- package/dist/docx/writer/style-reconcile.js.map +1 -0
- package/dist/docx/writer/style-strip.d.ts +33 -0
- package/dist/docx/writer/style-strip.d.ts.map +1 -0
- package/dist/docx/writer/style-strip.js +267 -0
- package/dist/docx/writer/style-strip.js.map +1 -0
- package/dist/docx/writer/styles-merge.d.ts +57 -0
- package/dist/docx/writer/styles-merge.d.ts.map +1 -0
- package/dist/docx/writer/styles-merge.js +184 -0
- package/dist/docx/writer/styles-merge.js.map +1 -0
- package/dist/docx/writer/template.d.ts +48 -0
- package/dist/docx/writer/template.d.ts.map +1 -0
- package/dist/docx/writer/template.js +280 -0
- package/dist/docx/writer/template.js.map +1 -0
- package/dist/docx/writer/testing.d.ts +52 -0
- package/dist/docx/writer/testing.d.ts.map +1 -0
- package/dist/docx/writer/testing.js +318 -0
- package/dist/docx/writer/testing.js.map +1 -0
- package/dist/docx/writer/xml-io.d.ts +90 -0
- package/dist/docx/writer/xml-io.d.ts.map +1 -0
- package/dist/docx/writer/xml-io.js +215 -0
- package/dist/docx/writer/xml-io.js.map +1 -0
- package/dist/export/blocks.d.ts.map +1 -1
- package/dist/export/blocks.js +149 -12
- package/dist/export/blocks.js.map +1 -1
- package/dist/export/context.d.ts +34 -2
- package/dist/export/context.d.ts.map +1 -1
- package/dist/export/context.js +28 -2
- package/dist/export/context.js.map +1 -1
- package/dist/export/css.d.ts +58 -0
- package/dist/export/css.d.ts.map +1 -1
- package/dist/export/css.js +194 -0
- package/dist/export/css.js.map +1 -1
- package/dist/export/dom.d.ts +2 -0
- package/dist/export/dom.d.ts.map +1 -1
- package/dist/export/dom.js +2 -0
- package/dist/export/dom.js.map +1 -1
- package/dist/export/export-docx.d.ts +4 -0
- package/dist/export/export-docx.d.ts.map +1 -1
- package/dist/export/export-docx.js +57 -4
- package/dist/export/export-docx.js.map +1 -1
- package/dist/export/header-footer.d.ts +15 -1
- package/dist/export/header-footer.d.ts.map +1 -1
- package/dist/export/header-footer.js +37 -0
- package/dist/export/header-footer.js.map +1 -1
- package/dist/export/html-to-docx.d.ts +20 -2
- package/dist/export/html-to-docx.d.ts.map +1 -1
- package/dist/export/html-to-docx.js +40 -14
- package/dist/export/html-to-docx.js.map +1 -1
- package/dist/export/inline.d.ts +25 -3
- package/dist/export/inline.d.ts.map +1 -1
- package/dist/export/inline.js +91 -34
- package/dist/export/inline.js.map +1 -1
- package/dist/export/styles.d.ts +51 -7
- package/dist/export/styles.d.ts.map +1 -1
- package/dist/export/styles.js +121 -16
- package/dist/export/styles.js.map +1 -1
- package/dist/export/tables.d.ts.map +1 -1
- package/dist/export/tables.js +140 -12
- package/dist/export/tables.js.map +1 -1
- package/dist/export/types.d.ts +44 -5
- package/dist/export/types.d.ts.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 📄 @aster-editor/word
|
|
2
2
|
|
|
3
|
-
> Import et export `.docx`, nettoyage du collage depuis Word et Google Docs pour Aster Editor. Produit du HTML
|
|
3
|
+
> Import et export `.docx`, nettoyage du collage depuis Word et Google Docs pour Aster Editor. Produit du HTML que le schéma ProseMirror accepte tel quel (stylé avec le lecteur OOXML d'Aster, sémantique avec mammoth), un fichier Word depuis ce même HTML, neuf ou greffé dans le `.docx` d'origine, et un rapport de fidélité qui compare les deux.
|
|
4
4
|
|
|
5
5
|
## 📦 Installation
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
npm install @aster-editor/word
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Peer dependency : `@aster-editor/core`.
|
|
11
|
+
Peer dependency : `@aster-editor/core`. Dépendances embarquées : `jszip` (lecture et écriture des paquets `.docx` : prétraitement des variables, lecteur stylé, greffe dans le gabarit, comparateur), `mammoth` (BSD-2, chargé en **import dynamique** uniquement par `importDocx` en mode `semantic`) et `docx` (MIT, chargé en import dynamique par `exportDocx`).
|
|
12
12
|
|
|
13
13
|
## 📥 `importDocx`
|
|
14
14
|
|
|
@@ -17,38 +17,71 @@ type DocxInput = File | ArrayBuffer | ArrayBufferView;
|
|
|
17
17
|
function importDocx(input: DocxInput, options?: DocxImportOptions): Promise<DocxImportResult>;
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
Convertit un `.docx` en HTML prêt pour `editor.setHTML` ou `editor.insertHTML`. L'entrée (`DocxInput`) est un `File`, un `ArrayBuffer` ou une vue typée (`Uint8Array`, `Buffer` Node) dont seule la fenêtre `byteOffset`/`byteLength` est lue. Ne lève jamais pour un document Word valide : ce qui n'est pas converti fidèlement remonte dans `warnings
|
|
20
|
+
Convertit un `.docx` en HTML prêt pour `editor.setHTML` ou `editor.insertHTML`. L'entrée (`DocxInput`) est un `File`, un `ArrayBuffer` ou une vue typée (`Uint8Array`, `Buffer` Node) dont seule la fenêtre `byteOffset`/`byteLength` est lue. Ne lève jamais pour un document Word valide : ce qui n'est pas converti fidèlement remonte dans `warnings` (et, en mode `styled`, dans `lost`). Rejette une erreur nommée `DocxImportError` (message préfixé `invalid-docx:`) si l'entrée n'est pas de l'un de ces types (`null`, chaîne, objet quelconque), n'est pas une archive zip, ne peut pas être lue (`arrayBuffer()` qui rejette), si mammoth ne peut pas la décoder (`semantic`) ou si la partie principale est absente ou illisible (`styled`).
|
|
21
|
+
|
|
22
|
+
Deux chemins de lecture, choisis par `fidelity` :
|
|
23
|
+
|
|
24
|
+
- **`'styled'`** : le lecteur OOXML d'Aster (`src/docx/reader/`) lit `document.xml`, `styles.xml`, `numbering.xml`, le thème, les en-têtes, pieds et notes avec `jszip` et le `DOMParser` XML, résout les styles (`docDefaults` → thème → `basedOn` → paragraphe → run) et produit le **HTML stylé** du contrat `api/layout.ts` du core : `style` inline normalisés, `data-docx-*`, tableaux et listes complets, plus `layout`, `template` et `lost`. Pas de mammoth sur ce chemin.
|
|
25
|
+
- **`'semantic'`** : mammoth, HTML sémantique sans mise en forme, comportement des versions 0.4.x, `layout` vide.
|
|
26
|
+
|
|
27
|
+
> ⚠️ **Défaut en 0.5.0 : `semantic`.** Sans option, `importDocx` lit encore avec mammoth, pour ne pas changer le HTML reçu par les intégrateurs existants sans qu'ils l'aient demandé. La bascule du défaut vers `styled` est prévue en 0.6 : passe `fidelity` explicitement dès maintenant. Le composant Angular, la démo et le playground vanilla lisent déjà en `styled`.
|
|
21
28
|
|
|
22
29
|
### Options (`DocxImportOptions`)
|
|
23
30
|
|
|
24
|
-
| Champ
|
|
25
|
-
|
|
|
26
|
-
| `
|
|
27
|
-
| `
|
|
28
|
-
| `
|
|
29
|
-
| `
|
|
30
|
-
| `
|
|
31
|
+
| Champ | Type | Défaut | Rôle |
|
|
32
|
+
| ----------------- | ------------------------ | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
33
|
+
| `fidelity` | `'styled' \| 'semantic'` | `'semantic'` | Chemin de lecture. `styled` : lecteur OOXML d'Aster, HTML stylé, `layout` et `lost` renseignés. `semantic` : mammoth, HTML sémantique, comportement 0.4.x. Défaut `styled` prévu en 0.6. |
|
|
34
|
+
| `upload` | `UploadHandler` | — | Hook `(file: File) => Promise<string>` appelé pour chaque image embarquée. Sans hook : data URL base64. Mêmes règles sur les deux chemins. |
|
|
35
|
+
| `footnotes` | `boolean` | `true` | Conserver les notes de bas de page et de fin en liste finale. `false` les supprime avec un avertissement. |
|
|
36
|
+
| `styleMap` | `readonly string[]` | `[]` | Règles mammoth additionnelles (`p[style-name='X'] => h2:fresh`), ajoutées **après** celles d'Aster, qui restent prioritaires. Lignes vides et `#` ignorées. **Mode `semantic` seulement** : le lecteur stylé lit les styles Word tels quels. |
|
|
37
|
+
| `emptyParagraphs` | `'keep' \| 'drop'` | `'keep'` | Sort des paragraphes vides (lignes laissées par Entrée). `keep` les rend en `<p></p>`, fidèle aux sauts de ligne de l'auteur ; `drop` les supprime comme avant 0.4.3, pour nettoyer un Word qui espace ses blocs avec des lignes vides. |
|
|
38
|
+
| `parse` | `HtmlParser` | `new DOMParser()` | Parseur HTML injectable (`{ parseFromString(html, 'text/html'): Document }`) pour SSR ou tests. Le XML du paquet passe par le `DOMParser` global (erreur explicite s'il manque). |
|
|
39
|
+
| `variables` | `DocxVariableOptions` | tout actif | `{ detect?, pattern? }` : sources de variables reconnues et motif alternatif à `{{nom}}`. Voir [Variables](#variables). Mêmes résultats sur les deux chemins. |
|
|
31
40
|
|
|
32
41
|
### Résultat (`DocxImportResult`)
|
|
33
42
|
|
|
34
|
-
| Champ | Type | Rôle
|
|
35
|
-
| ----------- | ------------------------------ |
|
|
36
|
-
| `html` | `string` | HTML sémantique
|
|
37
|
-
| `warnings` | `readonly DocxImportWarning[]` | `{ code, message }`, vide si tout est passé.
|
|
38
|
-
| `variables` | `readonly AsterVariable[]` | Variables trouvées (`name`, `label?`, `value?`), ordre d'apparition. Voir [Variables](#variables).
|
|
43
|
+
| Champ | Type | Rôle |
|
|
44
|
+
| ----------- | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
45
|
+
| `html` | `string` | `body.innerHTML` : HTML stylé (`styled`) ou sémantique (`semantic`). |
|
|
46
|
+
| `warnings` | `readonly DocxImportWarning[]` | `{ code, message }`, vide si tout est passé. |
|
|
47
|
+
| `variables` | `readonly AsterVariable[]` | Variables trouvées (`name`, `label?`, `value?`), ordre d'apparition. Voir [Variables](#variables). |
|
|
48
|
+
| `layout` | `DocumentLayout` | Mise en page lue dans la **dernière section** : `page` (format nommé ou libre, orientation, marges, distances d'en-tête et de pied), `columns`, `defaultFont`, `lang`, `headers` / `footers` (`default`, `first`, `even`, HTML stylé, hérités entre sections comme Word le fait), `titlePage`. Objet vide `{}` en mode `semantic`. |
|
|
49
|
+
| `template` | `ArrayBuffer` | Le `.docx` d'origine, **intact** (le tampon reçu, pas une copie, jamais la version prétraitée pour les variables), à repasser à `exportDocx({ template })`. Renseigné sur les deux chemins. |
|
|
50
|
+
| `lost` | `readonly DocxLostObject[]` | Objets non représentables rencontrés (`{ kind, where, text?, detail? }`, voir [Objets non représentables](#objets-non-représentables)). Vide en mode `semantic`. |
|
|
39
51
|
|
|
40
52
|
### Codes d'avertissement (`DocxImportWarningCode`)
|
|
41
53
|
|
|
42
|
-
| Code | Sens
|
|
43
|
-
| ----------------------------- |
|
|
44
|
-
| `mammoth` | Message mammoth remonté tel quel (`warning: Unrecognised paragraph style…`, image illisible).
|
|
45
|
-
| `image-upload-failed` | Le hook `upload` a rejeté ou résolu autre chose qu'une chaîne non vide ; l'image est repliée en base64.
|
|
46
|
-
| `
|
|
47
|
-
| `
|
|
48
|
-
| `
|
|
54
|
+
| Code | Sens |
|
|
55
|
+
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
56
|
+
| `mammoth` | Message mammoth remonté tel quel (`warning: Unrecognised paragraph style…`, image illisible). Mode `semantic`. |
|
|
57
|
+
| `image-upload-failed` | Le hook `upload` a rejeté ou résolu autre chose qu'une chaîne non vide ; l'image est repliée en base64. |
|
|
58
|
+
| `image-skipped` | Mode `styled` : image illisible ou introuvable dans le paquet (relation absente, octets vides, dimensions absurdes), ignorée. |
|
|
59
|
+
| `object-lost` | Mode `styled` : objet non représentable rencontré dans le corps (zone de texte, forme, équation, commentaire, révision, champ inconnu, objet incorporé). L'objet est aussi dans `result.lost`. Une fois par objet. |
|
|
60
|
+
| `reader` | Mode `styled` : valeur hors des bornes de Word ignorée (page hors 12,7-558,8 mm, marges hors page, plus de 45 colonnes…) ou partie annexe illisible. |
|
|
61
|
+
| `footnotes-dropped` | Des notes existaient et `footnotes: false` les a supprimées. |
|
|
62
|
+
| `unsupported-style` | Réservé aux post-traitements : élément non converti fidèlement. |
|
|
63
|
+
| `variables-preprocess-failed` | Le prétraitement des champs de fusion et contrôles de contenu a échoué (XML illisible, pas de `DOMParser` XML) ; le document est importé sans, les `{{nom}}` saisis restent détectés. |
|
|
64
|
+
|
|
65
|
+
### Ce que l'import stylé lit et ce que l'export réécrit
|
|
66
|
+
|
|
67
|
+
Le lecteur écrit sur chaque élément ce qui diffère des `docDefaults` du document, **polices et tailles sur les runs, jamais sur les blocs** (Tiptap ne lit pas `font-size` sur `<p>`), dans l'ordre de sérialisation figé par `packages/core/src/api/layout.ts`. L'export (neuf ou dans le gabarit) relit chaque propriété ; dans le gabarit, celles qui sont égales au style Word référencé par `data-docx-style` / `data-docx-run-style` ne sont pas réécrites en propriétés directes.
|
|
68
|
+
|
|
69
|
+
| Élément | Lu (OOXML → HTML) | Écrit (HTML → OOXML) | Pas lu, ou lu sans être écrit |
|
|
70
|
+
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
71
|
+
| Paragraphe | `w:pStyle` → `data-docx-style` et `h1..h6` pour `Heading 1..6` ; `w:jc` → `text-align` ; `w:spacing` → `line-height` (multiple ou `Npt` exact), `margin-top`, `margin-bottom` ; `w:ind` → `margin-left`, `margin-right`, `text-indent` (négatif = suspendu) ; `w:pBdr` → `border-*` et `data-border-between` ; `w:shd` → `background-color` ; `w:keepNext` → `data-keep-next` ; `w:pageBreakBefore` → `data-page-break-before` ; `w:br w:type="page"` → `<hr data-page-break>`. | `w:pStyle` (réutilisé dans le gabarit, ajouté s'il vient de l'export neuf), `w:jc`, `w:spacing` (`line`, `lineRule`, `before`, `after`), `w:ind`, `w:pBdr` (`between` compris), `w:shd`, `w:keepNext`, `w:pageBreakBefore`, `w:br w:type="page"`. | Interligne « au moins », espacement automatique, contrôle des veuves, `w:lang` du paragraphe, taquets de tabulation. |
|
|
72
|
+
| Run | `w:rStyle` → `data-docx-run-style` ; `w:rFonts` (thème compris) → `font-family` ; `w:sz` → `font-size` ; `w:b` / `w:i` → `<strong>` / `<em>` ; `w:u` → `<u style="text-decoration-style">` ; `w:strike` / `w:dstrike` → `<s>` ; `w:color` → `color` ; `w:highlight` / `w:shd` → `<mark>` ou `background-color` ; `w:vertAlign` → `<sup>` / `<sub>` ; `w:spacing` → `letter-spacing` ; `w:caps` / `w:smallCaps` → `text-transform` / `font-variant` ; `w:tab` → U+2003. | `w:rStyle`, `w:rFonts`, `w:sz`, `w:b`, `w:i`, `w:u` (style de trait), `w:strike` / `w:dstrike`, `w:color`, `w:highlight` (couleurs nommées) ou `w:shd`, `w:vertAlign`, `w:spacing`, `w:caps`, `w:smallCaps`. | `w:lang` du run (résolu, non écrit dans le HTML), `w:kern`, `w:w` (échelle), `w:position`, effets de texte, `w:vanish`. La tabulation ressort en texte U+2003, pas en `w:tab`. |
|
|
73
|
+
| Liste | `numbering.xml` (`w:abstractNum`, `w:num`, `w:lvlOverride`, `w:startOverride`) → `<ol type start data-docx-num-id>` / `<ul data-bullet data-docx-num-id>` imbriqués par niveau, puces Symbol / Wingdings ramenées à l'Unicode ; retrait du niveau → `data-docx-num-indent` sur le paragraphe. | Dans le gabarit : `w:numPr` avec le `w:numId` d'origine (compteur, puces, retraits inchangés), niveau retrouvé d'après le format et la puce ; sinon numérotation neuve ajoutée. `data-docx-num-indent` empêche de réécrire le retrait en `w:ind`. | `numStyleLink`, formats hors contrat (`decimalZero`, `ordinal`… relus comme `1`), texte de niveau composé (`%1.%2.`) réécrit par la numérotation du gabarit seulement. |
|
|
74
|
+
| Tableau | `w:tblW` → `width` (`%` ou `pt`) ; `w:tblStyle` → `data-docx-style` ; `w:tblBorders` extérieures → `border-*` sur `<table>` ; `insideH` / `insideV` et `w:tcBorders` → `border-*` sur les cellules ; `w:shd` → `background-color` ; `w:vAlign` → `vertical-align` ; `w:tcMar` → `padding` ; `w:tblGrid` → `colwidth` ; `w:gridSpan` / `w:vMerge` → `colspan` / `rowspan` (bornés à 63 et 1 000) ; tableaux imbriqués. | `w:tblW`, `w:tblStyle`, `w:tblBorders` (côtés extérieurs déclarés, `nil` sinon), `w:tblGrid` + `w:tcW`, `w:tcBorders` (un côté intérieur non déclaré est écrit `nil`), `w:shd`, `w:vAlign`, `w:tcMar`, `w:gridSpan`, `w:vMerge`. | `w:jc` du tableau (centrage perdu), styles conditionnels (`tblStylePr` : première ligne, bandes) approximés en propriétés directes, `w:tblLook`, `w:tblInd`, répétition de la ligne d'en-tête. |
|
|
75
|
+
| Image | `w:drawing` (`wp:inline`, `wp:anchor`), `w:pict` VML et `mc:AlternateContent` → `<img src width height data-align alt>` ; octets via le hook `upload` ou en data URL. | Image inline aux dimensions du HTML (plafonnées à la largeur utile du gabarit ou de la page), octets ajoutés à `word/media/` ou partie existante réutilisée. | Habillage et position d'une image ancrée (devient inline alignée), rognage, effets, légendes. |
|
|
76
|
+
| Note | `footnotes.xml` et `endnotes.xml` → `<sup data-footnote-ref="n">` + `<ol data-footnotes><li data-footnote="n">`, une seule séquence pour les deux sortes. | Notes de bas de page (`footnotes.xml`, ajoutées après celles du gabarit, séparateurs conservés). Les notes de fin ressortent en notes de bas de page. | Notes de fin en tant que telles, mise en forme de la marque d'appel. |
|
|
77
|
+
| Section | Dernier `w:sectPr` → `layout.page` (format nommé à ±1 mm sinon dimensions libres, orientation, marges, `header` / `footer`), `w:cols` → `columns`, `docDefaults` → `defaultFont`, `w:lang` → `lang` ; `w:headerReference` / `w:footerReference` de **toutes** les sections, héritées dans l'ordre → `headers` / `footers` (`default`, `first`, `even`) en HTML stylé, `w:titlePg` → `titlePage`. | Export neuf : tout (`w:pgSz`, `w:pgMar`, `w:cols`, `w:titlePg`, en-têtes et pieds des trois types, `w:evenAndOddHeaders`, `docDefaults`, `w:lang`). Dans le gabarit : rien de tout ça n'est réécrit (conservé à l'octet), sauf un en-tête ou pied de `layout` qui diffère du gabarit, régénéré. | **Sections intermédiaires** : une seule section ressort, la dernière. Types de section (`nextPage`, `continuous`), numérotation de page de section, bordures de page, filigranes, en-têtes liés au précédent. |
|
|
49
78
|
|
|
50
79
|
### Ce que l'import produit
|
|
51
80
|
|
|
81
|
+
Sur le chemin `styled`, en plus des `style` et `data-docx-*` du tableau ci-dessus, la structure est la même qu'en `semantic` (titres, listes imbriquées avec `<p>` dans chaque `li`, `thead` / `th`, notes, puces de variables), à deux différences près : les marques sont imbriquées dans un ordre figé (`<span><strong><em><s><u><sup>`) et les déclarations du `span` dans un ordre figé (`background-color, color, font-family, font-size, letter-spacing, …`), calés sur ce que `getHTML()` du core réémet, pour que `setHTML(getHTML())` rende exactement le HTML de l'import (idempotence stricte, vérifiée sur les fixtures 11-17). Les attendus de ces fixtures (`fixtures/docx/expected/11-17.html`) sont figés et régénérés par `ASTER_REGEN=1 npx vitest run src/docx/reader/regen-expected.spec.ts`.
|
|
82
|
+
|
|
83
|
+
Sur le chemin `semantic` (mammoth) :
|
|
84
|
+
|
|
52
85
|
- **Titres** : `Title`/`Titre` et `Heading 1..6`/`Titre 1..6` (noms localisés et identifiants internes `Heading1`, `Titre1`) → `h1..h6`.
|
|
53
86
|
- **Listes** : numérotation native Word (`w:numPr`) → `ul`/`ol` imbriqués ; le texte de chaque `li` est enveloppé dans un `<p>` (schéma Tiptap `listItem = paragraph block*`), les sous-listes orphelines sont rattachées au `li` précédent.
|
|
54
87
|
- **Marks** : gras, italique, `Strong`/`Fort` → `strong`, `Emphasis`/`Accentuation` → `em`, souligné → `u`, barré → `s`, surlignage → `mark`, style `Code` → `code`.
|
|
@@ -59,7 +92,11 @@ Convertit un `.docx` en HTML prêt pour `editor.setHTML` ou `editor.insertHTML`.
|
|
|
59
92
|
- **Notes** : liste `<ol data-footnotes>` en fin de document, appels `[1]` en texte simple, identifiants dans `data-footnote`. Note : le schéma Tiptap ne conserve pas `data-footnote` après `setHTML`.
|
|
60
93
|
- **Liens** : `<a href>` natifs mammoth, puis filtrés par la politique de liens du core à l'insertion.
|
|
61
94
|
- **Variables** : `<span data-aster-variable="nom" data-label="…">{{nom}}</span>`, voir ci-dessous.
|
|
62
|
-
- Paragraphes vides supprimés
|
|
95
|
+
- Paragraphes vides conservés en `<p></p>` (`emptyParagraphs: 'keep'`), supprimés avec `'drop'`.
|
|
96
|
+
|
|
97
|
+
### Objets non représentables
|
|
98
|
+
|
|
99
|
+
Mode `styled` seulement. Un objet que l'éditeur ne sait pas représenter est classé (`DocxLostKind` : `textbox`, `shape`, `smartart`, `chart`, `equation`, `comment`, `revision`, `field`, `embedded-object`, `unknown`) et localisé (`DocxLostWhere` : `body`, `header`, `footer`, `footnote`, `endnote`). **Hors du corps**, il reste tel quel dans le gabarit (l'export dans le gabarit ne réécrit pas ces parties). **Dans le corps**, il est remplacé par son texte quand il en a un (`text` : contenu d'une zone de texte, texte inséré d'une révision `w:ins`, résultat affiché d'un champ `DATE`), signalé dans `result.lost` avec un `detail` lisible (nom de la forme, instruction du champ, type d'objet incorporé) et par un avertissement `object-lost`, puis perdu à l'export. Un objet n'est signalé qu'une fois (révisions et commentaires reviennent souvent). Un dessin qui porte une image **et** une zone de texte rend l'image, puis le texte de la zone, signalée comme perdue. Les formes DrawingML de Word 2010+ sont enveloppées dans `mc:AlternateContent` : le lecteur lit le `mc:Choice`, sans quoi tout dessin moderne serait invisible (revue R25). Le texte supprimé d'une révision `w:del` n'est jamais lu.
|
|
63
100
|
|
|
64
101
|
### Variables
|
|
65
102
|
|
|
@@ -71,7 +108,7 @@ Trois sources sont reconnues et ramenées à la même puce `<span data-aster-var
|
|
|
71
108
|
| `mergeFields` | Champs de fusion `MERGEFIELD nom`, forme simple (`w:fldSimple`) ou complexe (`w:fldChar begin … end`, instruction répartie sur plusieurs runs). Un nom, entre guillemets ou non, est gardé tel quel s'il respecte la règle des noms (`"Prénom"` → `Prénom`) ; sinon il est normalisé : minuscules, accents retirés, espaces → `_` (`"Nom complet"` → `nom_complet`). | Le nom, la mise en forme du run et **le résultat affiché** comme valeur connue (`value`), sauf s'il est vide ou n'est que le placeholder `«nom»` / `{{nom}}` : un export `fields` avec `values` se réimporte donc avec ses valeurs. Les autres champs (`HYPERLINK`, `PAGE`, `TOC`, `IF`…) sont intacts. |
|
|
72
109
|
| `contentControls` | Contrôles de contenu `w:sdt` avec `w:tag` (nom) et `w:alias` (libellé) ; sans tag, l'alias normalisé sert de nom. Un contrôle de ligne (dans un paragraphe) devient un run ; un contrôle de bloc fait de paragraphes devient un paragraphe. Sans tag ni alias, ou pour une table des matières, un groupe, une case à cocher, ou un bloc qui contient autre chose que des paragraphes (table, ligne, cellule), le contrôle est laissé tel quel. Liste déroulante, zone de liste et date donnent la valeur affichée. | Le nom, le libellé (`data-label`, `label`) et **la valeur saisie** (`value`), sauf si le contrôle affiche encore son texte de remplissage (`w:showingPlcHdr`). Un tag présent plusieurs fois garde le libellé et la valeur de sa première apparition. Une variable imbriquée dans un contrôle de bloc remplacé n'est pas listée : elle n'est plus dans le document. |
|
|
73
110
|
|
|
74
|
-
Les champs et contrôles sont réécrits dans le XML du `.docx` **avant** mammoth, car mammoth perd les premiers et traverse les seconds. Les parties traitées sont localisées par les relations du paquet : le corps désigné par `_rels/.rels` (`word/document.xml` en général), puis ses en-têtes, pieds de page et notes d'après `word/_rels/document.xml.rels` ; sans ce fichier, repli sur les noms usuels (`header1.xml`, `footer1.xml`, `footnotes.xml`, `endnotes.xml`). Une partie sans champ ni contrôle n'est pas parsée. Le prétraitement ne lève jamais : en cas d'échec, le document est importé sans et un avertissement `variables-preprocess-failed` est émis. `result.variables` liste les variables du HTML dans l'ordre d'apparition, puis celles trouvées dans les en-têtes et pieds de page (que mammoth ne rend pas) ; donne-la au core avec `editor.variables.define(result.variables)`.
|
|
111
|
+
Les champs et contrôles sont réécrits dans le XML du `.docx` **avant** la lecture (mammoth ou lecteur stylé), car mammoth perd les premiers et traverse les seconds ; le lecteur stylé relit les runs `{{nom}}` ainsi produits avec la mise en forme du champ. `result.template` reste le tampon d'origine, pas la version réécrite. Les parties traitées sont localisées par les relations du paquet : le corps désigné par `_rels/.rels` (`word/document.xml` en général), puis ses en-têtes, pieds de page et notes d'après `word/_rels/document.xml.rels` ; sans ce fichier, repli sur les noms usuels (`header1.xml`, `footer1.xml`, `footnotes.xml`, `endnotes.xml`). Une partie sans champ ni contrôle n'est pas parsée. Le prétraitement ne lève jamais : en cas d'échec, le document est importé sans et un avertissement `variables-preprocess-failed` est émis. `result.variables` liste les variables du HTML dans l'ordre d'apparition, puis celles trouvées dans les en-têtes et pieds de page (que mammoth ne rend pas) ; donne-la au core avec `editor.variables.define(result.variables)`.
|
|
75
112
|
|
|
76
113
|
Les `{{nom}}` tapés dans un en-tête ou un pied de page sont eux aussi remontés dans `result.variables` (sans libellé ni valeur) quand `braces` est actif, avec `pattern` s'il est fourni. Un `{{nom}}` dont les accolades et le nom sont répartis sur plusieurs marques d'un même paragraphe (`<strong>{{</strong>nom}}`, après une correction dans Word) est reconnu : la puce est posée à la place du premier fragment, dans ses marques, et les fragments suivants sont retirés. Il n'est jamais recollé à travers un saut de ligne, une puce ou un bloc.
|
|
77
114
|
|
|
@@ -99,6 +136,7 @@ input.addEventListener('change', async () => {
|
|
|
99
136
|
if (!file) return;
|
|
100
137
|
try {
|
|
101
138
|
const { html, warnings } = await importDocx(file, {
|
|
139
|
+
fidelity: 'styled',
|
|
102
140
|
upload: async (image) => {
|
|
103
141
|
const body = new FormData();
|
|
104
142
|
body.append('file', image);
|
|
@@ -201,66 +239,78 @@ Le JSON passe par `jsonToHtml`, sérialiseur minimal limité au schéma d'Aster
|
|
|
201
239
|
|
|
202
240
|
### Options (`DocxExportOptions`)
|
|
203
241
|
|
|
204
|
-
| Champ | Type | Défaut | Rôle
|
|
205
|
-
| ------------------ | --------------------------------------------------------- | ----------------- |
|
|
206
|
-
| `parse` | `HtmlParser` | `new DOMParser()` | Parseur HTML injectable, jamais le `document` global (SSR, tests).
|
|
207
|
-
| `fetchImage` | `(src: string) => Promise<ArrayBuffer\|null>` | — | Récupère les octets d'une image `http(s)://`. Sans hook, seules les data URL sont embarquées, les autres sont ignorées avec avertissement.
|
|
208
|
-
| `title` | `string` | — | Titre inscrit dans les propriétés du document.
|
|
209
|
-
| `creator` | `string` | — | Auteur inscrit dans les propriétés du document.
|
|
210
|
-
| `pageSize` | `'A4' \| 'LETTER'` | `'A4'` | Format de page, marges de 2,54 cm. `page.size` prime.
|
|
211
|
-
| `page` | `DocxPageOptions` | — | `size`, `orientation` (`portrait`/`landscape`), `margins` en mm (`top`, `right`, `bottom`, `left`, 25,4 mm par défaut).
|
|
212
|
-
| `lang` | `string` | — | Langue du document (`w:lang`, ex. `fr-FR`) pour le correcteur de Word.
|
|
213
|
-
| `defaultFont` | `{ family?, sizePt? }` | — | Police et taille du texte courant (`docDefaults`).
|
|
214
|
-
| `variables` | `DocxExportVariableOptions` | `placeholders` | Rendu des variables : `mode` (`placeholders`/`resolve`/`fields`), `values`, `pattern`. Voir [Variables à l'export](#variables-à-lexport).
|
|
215
|
-
| `header` | `string \| DocxHeaderOptions` | — | En-tête : `text` brut ou `html` inline (gras, liens, variables), `align`.
|
|
216
|
-
| `footer` | `string \| DocxFooterOptions` | — | Pied de page : idem, plus `pageNumbers: true` (`PAGE`) ou `'x-of-y'` (`PAGE / NUMPAGES`).
|
|
217
|
-
| `toc` | `boolean \| { title?, levels? }` | — | Table des matières en tête du corps, 3 niveaux par défaut. Word propose de la mettre à jour à l'ouverture.
|
|
218
|
-
| `numberedHeadings` | `boolean \| { levels?, format? }` | — | Numérotation `1.`, `1.1.`… (`decimal`) ou `1`, `1.1` (`legal`) liée aux styles `Heading 1..n`, 3 niveaux par défaut.
|
|
219
|
-
| `headingStyles` | `{ h1?: { color?, sizePt?, bold?, italics?, font? }, … }` | — | Surcharges des styles de titres ; sans option, les titres restent ceux de Word.
|
|
220
|
-
| `
|
|
242
|
+
| Champ | Type | Défaut | Rôle |
|
|
243
|
+
| ------------------ | --------------------------------------------------------- | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
244
|
+
| `parse` | `HtmlParser` | `new DOMParser()` | Parseur HTML injectable, jamais le `document` global (SSR, tests). |
|
|
245
|
+
| `fetchImage` | `(src: string) => Promise<ArrayBuffer\|null>` | — | Récupère les octets d'une image `http(s)://`. Sans hook, seules les data URL sont embarquées, les autres sont ignorées avec avertissement. |
|
|
246
|
+
| `title` | `string` | — | Titre inscrit dans les propriétés du document. |
|
|
247
|
+
| `creator` | `string` | — | Auteur inscrit dans les propriétés du document. |
|
|
248
|
+
| `pageSize` | `'A4' \| 'LETTER'` | `'A4'` | Format de page, marges de 2,54 cm. `page.size` prime. |
|
|
249
|
+
| `page` | `DocxPageOptions` | — | `size`, `orientation` (`portrait`/`landscape`), `margins` en mm (`top`, `right`, `bottom`, `left`, 25,4 mm par défaut). |
|
|
250
|
+
| `lang` | `string` | — | Langue du document (`w:lang`, ex. `fr-FR`) pour le correcteur de Word. |
|
|
251
|
+
| `defaultFont` | `{ family?, sizePt? }` | — | Police et taille du texte courant (`docDefaults`). |
|
|
252
|
+
| `variables` | `DocxExportVariableOptions` | `placeholders` | Rendu des variables : `mode` (`placeholders`/`resolve`/`fields`), `values`, `pattern`. Voir [Variables à l'export](#variables-à-lexport). |
|
|
253
|
+
| `header` | `string \| DocxHeaderOptions` | — | En-tête : `text` brut ou `html` inline (gras, liens, variables), `align`. |
|
|
254
|
+
| `footer` | `string \| DocxFooterOptions` | — | Pied de page : idem, plus `pageNumbers: true` (`PAGE`) ou `'x-of-y'` (`PAGE / NUMPAGES`). |
|
|
255
|
+
| `toc` | `boolean \| { title?, levels? }` | — | Table des matières en tête du corps, 3 niveaux par défaut. Word propose de la mettre à jour à l'ouverture. |
|
|
256
|
+
| `numberedHeadings` | `boolean \| { levels?, format? }` | — | Numérotation `1.`, `1.1.`… (`decimal`) ou `1`, `1.1` (`legal`) liée aux styles `Heading 1..n`, 3 niveaux par défaut. |
|
|
257
|
+
| `headingStyles` | `{ h1?: { color?, sizePt?, bold?, italics?, font? }, … }` | — | Surcharges des styles de titres ; sans option, les titres restent ceux de Word. |
|
|
258
|
+
| `layout` | `DocumentLayout` | — | Mise en page de l'**export neuf** (format, marges, colonnes, police, langue, en-têtes et pieds `default` / `first` / `even`, `titlePage`), en général `DocxImportResult.layout`. Englobe `page`, `header`, `footer`, `defaultFont` et `lang`, qui restent acceptés et priment champ par champ. Avec un gabarit, seuls `headers` / `footers` servent. |
|
|
259
|
+
| `template` | `DocxInput` | — | **Export dans le gabarit** : le `.docx` donné (en général `DocxImportResult.template`) est conservé tel quel, seul le corps est régénéré. Voir [Export dans le gabarit](#export-dans-le-gabarit). |
|
|
260
|
+
| `onWarning` | `(warning: DocxExportWarning) => void` | — | Reçoit chaque avertissement au fil de la conversion. Une exception levée par le hook n'est pas rattrapée : `exportDocx` rejette. |
|
|
221
261
|
|
|
222
262
|
### Codes d'avertissement (`DocxExportWarningCode`)
|
|
223
263
|
|
|
224
|
-
| Code
|
|
225
|
-
|
|
|
226
|
-
| `image-skipped`
|
|
227
|
-
| `unsupported-element`
|
|
228
|
-
| `variable-unresolved`
|
|
229
|
-
| `footnote-missing`
|
|
230
|
-
| `footnote-not-allowed`
|
|
231
|
-
| `footnote-duplicate`
|
|
232
|
-
| `table-span-clamped`
|
|
233
|
-
| `page-margins-clamped`
|
|
264
|
+
| Code | Sens |
|
|
265
|
+
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
266
|
+
| `image-skipped` | Image ignorée : URL distante sans `fetchImage`, hook en échec ou `null`, data URL illisible, format non raster (SVG, WebP). |
|
|
267
|
+
| `unsupported-element` | `iframe`, `video`, `audio`, `canvas`, `svg`, `form`… sans équivalent Word, ignorés. |
|
|
268
|
+
| `variable-unresolved` | Mode `resolve` : variable sans valeur dans `values`, laissée en `{{nom}}` (corps, et en-têtes / pieds du gabarit). |
|
|
269
|
+
| `footnote-missing` | `<sup data-footnote-ref="n">` sans note `n` dans la liste `data-footnotes`, laissé en exposant. |
|
|
270
|
+
| `footnote-not-allowed` | Appel de note dans un en-tête, un pied de page ou une autre note : Word l'y refuse, laissé en exposant `[n]`. |
|
|
271
|
+
| `footnote-duplicate` | Seconde note déclarée sous une clé déjà prise (ignorée), ou second appel vers une note déjà référencée (exposant `[n]`). |
|
|
272
|
+
| `table-span-clamped` | `colspan` ou `<col span>` borné à 63 colonnes, `rowspan` à 65 534 lignes : plafonds de Word. |
|
|
273
|
+
| `page-margins-clamped` | Marge négative ou non finie (défaut 25,4 mm appliqué), ou somme des marges qui ne laisse pas 1 cm utile (marges réduites). |
|
|
274
|
+
| `template-invalid` | `options.template` n'est pas un `.docx` lisible (pas une archive zip, partie principale absente, ou partie principale de plus de 16 Mo, refusée avant tout parse) ; ou la greffe a levé. **Export neuf à la place**, jamais d'exception. |
|
|
275
|
+
| `template-numbering-fallback` | Une liste portait `data-docx-num-id` mais cette numérotation n'existe pas (ou plus) dans le `numbering.xml` du gabarit ; numérotation neuve ajoutée. |
|
|
276
|
+
| `template-style-missing` | Un bloc ou un run référence un style (`data-docx-style`, `data-docx-run-style`) absent du gabarit et de l'export neuf, ou d'un autre type que la référence ; seules ses propriétés directes sont écrites. Les styles intégrés de Word (`TOC1`, `Hyperlink`, `ListParagraph`…) ne sont jamais signalés : Word les matérialise à l'ouverture. |
|
|
277
|
+
| `template-section-missing` | Le gabarit n'a pas de `w:sectPr` final : `layout.headers` / `layout.footers` n'ont nulle part où s'accrocher et sont ignorés. |
|
|
234
278
|
|
|
235
279
|
`DocxWarning<Code>` est le type générique commun : `DocxImportWarning` et `DocxExportWarning` en sont deux spécialisations structurellement compatibles.
|
|
236
280
|
|
|
237
281
|
### Mapping HTML → Word
|
|
238
282
|
|
|
239
|
-
| HTML
|
|
240
|
-
|
|
|
241
|
-
| `h1`..`h6`
|
|
242
|
-
| `p`, `text-align`
|
|
243
|
-
| `strong`/`b`, `em`/`i`, `u`, `s`
|
|
244
|
-
| `sub`, `sup`
|
|
245
|
-
| `code`, `kbd`, `samp`
|
|
246
|
-
| `mark`, `background-color`
|
|
247
|
-
| `span style="color / font-family / font-size"`
|
|
248
|
-
| `a href`
|
|
249
|
-
| `ul`, `ol`, `ol type="a/A/i/I"`, `ol start`
|
|
250
|
-
| `blockquote`
|
|
251
|
-
| `pre`
|
|
252
|
-
| `hr`
|
|
253
|
-
| `br`
|
|
254
|
-
| `img`
|
|
255
|
-
| `table`, `thead`, `th`, `colspan`, `rowspan`
|
|
256
|
-
| `div`, `section`, `article`…
|
|
257
|
-
| `td colwidth`, `style="width"`, `col`
|
|
258
|
-
| `div data-page-break`, `hr data-page-break`
|
|
259
|
-
| `p style="page-break-before: always"` / `break-before: page` / `p data-page-break`
|
|
260
|
-
| `span data-aster-variable`, `{{nom}}`
|
|
261
|
-
| `sup data-footnote-ref="n"`, `sup>[n]` + `ol data-footnotes > li data-footnote`
|
|
262
|
-
|
|
263
|
-
|
|
283
|
+
| HTML | Word |
|
|
284
|
+
| ----------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
285
|
+
| `h1`..`h6` | Styles `Heading 1`..`Heading 6` |
|
|
286
|
+
| `p`, `text-align` | Paragraphe, alignement `center`/`right`/`justify` (`left` est le défaut) |
|
|
287
|
+
| `strong`/`b`, `em`/`i`, `u`, `s` | Gras, italique, souligné, barré (aussi lus depuis `font-weight`, `font-style`, `text-decoration`) |
|
|
288
|
+
| `sub`, `sup` | Indice, exposant |
|
|
289
|
+
| `code`, `kbd`, `samp` | Style de caractère `Code`, police Consolas |
|
|
290
|
+
| `mark`, `background-color` | Surlignage jaune |
|
|
291
|
+
| `span style="color / font-family / font-size"` | Couleur (`#hex`, `rgb()` en nombres ou `%`, `hsl()`, noms courants), police, taille (`px`, `pt`, `em`, `rem`, `%` et mots-clés `small`, `large`… relatifs à 11 pt) |
|
|
292
|
+
| `a href` | Lien externe (`http`, `https`, `mailto`, `tel`, `ftp` ; autres schémas → texte simple). Espaces, `\|` et non-ASCII sont percent-encodés dans la cible, les `%xx` existants conservés. |
|
|
293
|
+
| `ul`, `ol`, `ol type="a/A/i/I"`, `ol start` | Numérotation Word sur 9 niveaux : puces, décimal, lettres, romains. Chaque liste (racine ou imbriquée) a sa propre numérotation et repart à 1, ou au `start` de l'`<ol>`. Le premier bloc d'un `li` porte la puce. |
|
|
294
|
+
| `blockquote` | Style de paragraphe `Quote` (italique, indenté) |
|
|
295
|
+
| `pre` | Une ligne = un paragraphe de style `HTML Preformatted` (Consolas) |
|
|
296
|
+
| `hr` | Paragraphe vide à bordure basse |
|
|
297
|
+
| `br` | Saut de ligne. Les `<br>` terminaux d'un paragraphe qui a d'autre contenu sont ignorés, comme en HTML (`<p><br></p>` garde le sien). |
|
|
298
|
+
| `img` | Image inline PNG/JPEG/GIF/BMP, dimensions lues dans `width`/`height` ou l'en-tête du fichier, plafonnées à la largeur de page |
|
|
299
|
+
| `table`, `thead`, `th`, `colspan`, `rowspan` | Table 100 % avec bordures, lignes d'en-tête marquées (`thead` ou ligne entièrement en `th`) et en gras, fusions horizontales et verticales. `colspan`/`<col span>` bornés à 63 et `rowspan` à 65 534 (plafonds de Word, avertissement `table-span-clamped`) |
|
|
300
|
+
| `div`, `section`, `article`… | Traversés ; les inline nus deviennent des paragraphes |
|
|
301
|
+
| `td colwidth`, `style="width"`, `col` | Largeurs de colonnes en DXA (1 px = 15 twips) : grille `w:tblGrid` + `w:tcW` par cellule et disposition fixe quand toutes les colonnes sont connues (réduites à l'échelle si la table déborde de la page) ; sinon table à 100 % et largeur sur les seules cellules connues |
|
|
302
|
+
| `div data-page-break`, `hr data-page-break` | Saut de page (`w:br w:type="page"`) |
|
|
303
|
+
| `p style="page-break-before: always"` / `break-before: page` / `p data-page-break` | Paragraphe avec `w:pageBreakBefore` (titres compris) |
|
|
304
|
+
| `span data-aster-variable`, `{{nom}}` | Variable : texte, valeur ou champ `MERGEFIELD` selon `options.variables.mode`. Voir [Variables à l'export](#variables-à-lexport) |
|
|
305
|
+
| `sup data-footnote-ref="n"`, `sup>[n]` + `ol data-footnotes > li data-footnote` | Vraie note de bas de page Word (`footnotes.xml`, style `Footnote Text`), appel `w:footnoteReference`, liste retirée du corps |
|
|
306
|
+
| `p style="line-height / margin-* / text-indent / border-* / background-color"` | Session 3 : `w:spacing` (multiple ×240 ou exact en twips, avant, après), `w:ind` (gauche, droite, première ligne ou suspendu), `w:pBdr` (quatre côtés plus `data-border-between`), `w:shd` ; `data-keep-next` → `w:keepNext`, `data-page-break-before` → `w:pageBreakBefore` |
|
|
307
|
+
| `p data-docx-style`, `span data-docx-run-style`, `table data-docx-style` | Style Word d'origine : `w:pStyle` / `w:rStyle` / `w:tblStyle` quand le style existe dans le document final (gabarit, ou styles de l'export neuf) ; ignoré sinon en export neuf, `template-style-missing` dans le gabarit |
|
|
308
|
+
| `span style="letter-spacing / text-transform / font-variant"`, `u`/`s` `text-decoration-style` | `w:spacing` de run, `w:caps`, `w:smallCaps`, `w:u w:val="double\|dotted\|dash\|wave"`, `w:dstrike` |
|
|
309
|
+
| `ol/ul data-docx-num-id`, `ul data-bullet`, `p data-docx-num-indent` | Dans le gabarit : réutilisation du `w:num` d'origine ; partout : puce personnalisée, retrait de numérotation non réécrit en `w:ind` |
|
|
310
|
+
| `table style="width / border-*"`, `td style="border-* / background-color / vertical-align / padding"` | `w:tblW` (`pct` ou `dxa`), `w:tblBorders` (un côté non déclaré est `nil`), `w:tcBorders`, `w:shd`, `w:vAlign`, `w:tcMar`. Un tableau venu de l'import est en mode « explicite » : ce que le HTML déclare fait foi, rien n'est ajouté |
|
|
311
|
+
| `img height` | Hauteur écrite telle quelle ; sans `height`, ratio de l'image |
|
|
312
|
+
|
|
313
|
+
Les styles `Quote`, `HTML Preformatted` et `Code` portent les noms que la style map d'`importDocx` reconnaît : un aller-retour `exportDocx` → `importDocx` restitue titres, marques, lien, listes imbriquées, citation, bloc de code, image et table. En mode `semantic`, les alignements, couleurs et polices ne survivent pas à l'import (mammoth les jette volontairement) ; en mode `styled`, tout le contrat de mise en page survit à l'aller-retour export neuf → import (`export/layout.spec.ts`).
|
|
264
314
|
|
|
265
315
|
Le texte est assaini avant d'entrer dans le XML : les caractères de contrôle que XML 1.0 interdit (U+0000-U+0008, U+000B, U+000C, U+000E-U+001F) et les non-caractères U+FFFE/U+FFFF sont retirés des runs, des `alt`/`title` d'image, des `href` et des propriétés `title`/`creator`. Sans ça, Word refuse d'ouvrir le fichier. Tabulations, sauts de ligne, espaces insécables et Unicode étendu sont conservés tels quels ; seuls les blancs HTML (espace, tabulation, LF, CR, FF) sont réduits selon les règles du HTML.
|
|
266
316
|
|
|
@@ -289,6 +339,63 @@ await exportDocx(editor.getHTML(), {
|
|
|
289
339
|
- **Notes de bas de page** : le format produit par `importDocx` (`<sup>[1]</sup>` + `<ol data-footnotes><li data-footnote="footnote-1">`) redevient de vraies notes Word ; `<sup data-footnote-ref="1">` est aussi accepté, et un `<li>` sans `data-footnote` est désigné par sa position (ou la première clé libre si cette position est déjà prise par un `data-footnote` explicite). Word n'accepte qu'un appel par note et aucun appel dans une note : un second `<sup>` vers la même note, ou un appel depuis le texte d'une note, reste un exposant `[n]` avec avertissement `footnote-duplicate` / `footnote-not-allowed` ; une note déclarée deux fois sous la même clé garde la première. L'aller-retour `exportDocx` → `importDocx` restitue le même HTML.
|
|
290
340
|
- **Numérotation des titres** : `numberedHeadings: true` lie une numérotation à `Heading 1..3` (`w:pStyle` dans `numbering.xml`, `w:numPr` dans le style) : Word numérote les titres existants et ceux ajoutés ensuite, la table des matières reprend les numéros. `format: 'legal'` supprime le point final (`1`, `1.1`).
|
|
291
341
|
- **Mise en page** : `page: { orientation: 'landscape', margins: { top: 20, bottom: 20, left: 15, right: 15 } }`. Les images et les tables sont plafonnées à la largeur utile résultante. Une marge négative reprend le défaut, et deux marges opposées dont la somme ne laisse pas 1 cm de zone utile sont réduites à proportion ; les deux cas remontent `page-margins-clamped`.
|
|
342
|
+
- **`layout`** (session 3) : un `DocumentLayout` du core, en général celui de l'import, pilote l'export neuf d'un coup : `page` (format nommé ou libre en mm, orientation, marges, distances d'en-tête et de pied), `columns`, `defaultFont`, `lang`, `headers` / `footers` des trois types (`default`, `first`, `even` : `w:titlePg` et `w:evenAndOddHeaders` posés), `titlePage`. Les options historiques (`page`, `header`, `footer`, `defaultFont`, `lang`) priment champ par champ quand les deux sont données : `options.header` remplace `layout.headers.default`, `first` vient toujours du `layout`. Un aller-retour export neuf → import stylé rend le même `layout`.
|
|
343
|
+
|
|
344
|
+
### Export dans le gabarit
|
|
345
|
+
|
|
346
|
+
```ts
|
|
347
|
+
const imported = await importDocx(file, { fidelity: 'styled' });
|
|
348
|
+
editor.setHTML(imported.html);
|
|
349
|
+
// … édition …
|
|
350
|
+
const blob = await exportDocx(editor.getHTML(), {
|
|
351
|
+
template: imported.template,
|
|
352
|
+
layout: imported.layout,
|
|
353
|
+
variables: { mode: 'fields' },
|
|
354
|
+
});
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
Avec `template`, `exportDocx` ne construit pas un document neuf : il **ouvre le gabarit** et y **greffe** le corps produit par la chaîne d'export (`src/docx/writer/`). Tout ce qui n'est pas le corps ressort **identique à l'octet** : `styles.xml`, `numbering.xml`, `theme/*`, `fontTable.xml`, `settings.xml`, `docProps/*`, en-têtes, pieds, médias existants et le `w:sectPr` final (format, marges, colonnes, références d'en-têtes). Un gabarit relu puis réécrit sans toucher une partie ne la recompresse même pas (`xml-io.ts` ne marque modifiées que les parties écrites). Un aller-retour sans modification des fixtures 11 et 13 rend un `document.xml` lui aussi identique à l'octet ; 12, 14-17 ne diffèrent que par des équivalences (`w:jc left` omis, `w:tab`).
|
|
358
|
+
|
|
359
|
+
Ce que la greffe fait, module par module (`writer/`) :
|
|
360
|
+
|
|
361
|
+
| Module | Rôle |
|
|
362
|
+
| -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
363
|
+
| `template.ts` | Ouvre et valide le gabarit (zip, partie principale sous 16 Mo, `w:body` présent), lit ses styles, sa numérotation, sa largeur utile, et ses en-têtes / pieds quand `layout` en fournit. Invalide → `template-invalid`, export neuf. |
|
|
364
|
+
| `graft.ts` | Remplace les enfants de `w:body` par ceux du corps généré, conserve le `w:sectPr` final, l'orchestre tout le reste. Un corps régénéré n'a plus de sections intermédiaires : **seule la dernière section survit**, complétée des en-têtes et pieds que les précédentes lui transmettaient par héritage. |
|
|
365
|
+
| `styles-merge.ts` | Un style référencé (`w:pStyle`, `w:rStyle`, `w:tblStyle`) est réutilisé s'il existe dans le gabarit (par identifiant, ou par nom quand l'identifiant est localisé : `Hyperlink` → `Lienhypertexte`, `TableGrid` → `Grilledutableau`), ajouté depuis les styles de l'export neuf sinon (`Quote`, `Code`, `ListParagraph`), laissé tel quel pour un style intégré de Word, retiré avec `template-style-missing` s'il n'existe nulle part. Les styles du gabarit ne sont jamais modifiés. |
|
|
366
|
+
| `style-strip.ts` | Retire du corps greffé toute propriété directe de `w:pPr` / `w:rPr` égale à ce que le style résolu donne déjà : ne restent que les vraies surcharges, comme dans l'original. |
|
|
367
|
+
| `style-reconcile.ts` | L'inverse : le lecteur n'écrit dans le HTML que ce qui diffère des `docDefaults`, jamais ce que le style de paragraphe apporte ; toute propriété **absente** du run dont le style résolu diffère des `docDefaults` est réécrite en propriété directe (`w:b w:val="0"`, `w:u w:val="none"`, police des `docDefaults`…). Sans cette passe, 159 runs du document client passaient de Calibri à Times. |
|
|
368
|
+
| `numbering-merge.ts` | Les `w:numId` du corps généré (numérotés depuis 1 par la lib `docx`) sont remappés : une liste avec `data-docx-num-id` réutilise le `w:num` du gabarit, les autres sont ajoutées à `numbering.xml` sous des identifiants libres (partie créée si absente). Jamais de définition modifiée. |
|
|
369
|
+
| `relationships.ts`, `media.ts`, `content-types.ts` | Relations du corps remappées (réutilisation d'une relation de même type et même cible, sinon ajout), images copiées dans `word/media/` (octets déjà présents → partie réutilisée, nom suffixé en cas de collision), `[Content_Types].xml` complété. Rien n'est supprimé. |
|
|
370
|
+
| `footnotes-merge.ts` | Notes du corps ajoutées à `footnotes.xml` sous des identifiants libres, séparateurs du gabarit conservés, partie créée si absente ; les notes du gabarit que le corps n'appelle plus restent (Word les ignore). |
|
|
371
|
+
| `header-footer-variables.ts` | Mode `resolve` : dans les parties d'en-tête et de pied du gabarit, champs de fusion et contrôles de contenu deviennent du texte, puis chaque `{{nom}}` est remplacé par `values[nom]` (sans valeur : `variable-unresolved`). Modes `fields` et `placeholders` : rien n'est touché. Limite : un `{{nom}}` tapé à cheval sur deux runs n'est pas reconnu. |
|
|
372
|
+
| `header-footer-graft.ts` | Pour chaque type (`default`, `first`, `even`) dont le HTML de `layout.headers` / `footers` diffère de ce que le lecteur relit dans le gabarit, la partie générée est copiée sous un nom libre et le `w:sectPr` final pointe dessus ; l'ancienne partie reste dans le paquet. Identique ou absent → partie intacte à l'octet. Sans `w:sectPr` final → `template-section-missing`. |
|
|
373
|
+
| `settings-merge.ts` | `w:updateFields` avec `toc`, `w:evenAndOddHeaders` quand un en-tête ou pied `even` est greffé ; un réglage déjà actif laisse la partie intacte. |
|
|
374
|
+
| `core-properties.ts` | `dc:title` / `dc:creator` réécrits seulement si `title` / `creator` sont fournis et différents. |
|
|
375
|
+
|
|
376
|
+
Options **ignorées** avec un gabarit, parce qu'il les porte déjà : `page`, `pageSize`, `header`, `footer`, `defaultFont`, `lang`, `headingStyles`, et `layout.page`, `layout.columns`, `layout.titlePage`, `layout.defaultFont`, `layout.lang`. Options **appliquées** : `variables`, `toc`, `numberedHeadings` (numérotation posée en `w:numPr` direct sur chaque titre, les styles du gabarit n'étant pas modifiés), `title`, `creator`, `layout.headers` et `layout.footers`. La JSDoc de `template` dans `src/export/types.ts` est la référence. Quand un `.docx` vient d'un import, `exportDocx` ne pose **jamais** d'exception à cause du gabarit : tout repli passe par `onWarning`.
|
|
377
|
+
|
|
378
|
+
## 📏 `compareDocx` et le rapport de fidélité
|
|
379
|
+
|
|
380
|
+
```ts
|
|
381
|
+
function compareDocx(
|
|
382
|
+
original: DocxInput,
|
|
383
|
+
result: DocxInput,
|
|
384
|
+
options?: CompareDocxOptions,
|
|
385
|
+
): Promise<FidelityReport>;
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
Compare deux `.docx` **propriété par propriété**, pas à l'octet : les parties annexes du paquet sont comparées à l'octet (`identicalParts` / `changedParts`, tout sauf `word/document.xml`, `docProps/core.xml` et les `.rels` du corps par défaut, `binaryParts` pour choisir), les blocs des deux corps sont alignés (préfixe et suffixe communs, LCS borné, puis ancrage par clés uniques façon patience diff sur les longues séquences : un bloc déplacé ne décale pas tout le reste) et leurs propriétés **résolues** (styles, `docDefaults`, thème de chaque document) comparées, puis les sections avec leurs en-têtes et pieds. Chaque occurrence compte : conservée si les deux valeurs canoniques sont égales, perdue sinon, cinq exemples par propriété. Décisions figées : une valeur **ajoutée** par le résultat compte comme perdue (strict, pour que les régressions se voient) ; les défauts de Word (gauche, portrait, une colonne, espacement nul, interligne simple) valent « absent » ; les `w:rsid*` sont ignorés (`ignoreRevisionIds`, vrai par défaut). Le comparateur mesure **ce que Word rend**, pas la forme XML : bordures de cellule effectives (style de tableau → `w:tblBorders` selon la position dans la grille → `w:tcBorders`, `nil` efface), saut de page comme frontière de bloc, champs de fusion par nom et résultat (un `{{nom}}` en clair vaut un champ de même nom), largeurs à ±0,5 pt, images par nombre et dimensions (±1 px). Les styles conditionnels de tableau, l'habillage des images, les commentaires, signets et `w:lastRenderedPageBreak` ne sont pas comparés (`report.notes` le rappelle).
|
|
389
|
+
|
|
390
|
+
`FidelityReport` : `score` (moyenne pondérée des taux conservé / total par groupe ; poids `text` 3, `paragraph-style` 2, `paragraph-format` 2, `run-format` 2, `list` 1, `table` 2, `image` 1, `note` 1, `header-footer` 2, `section` 2, `package-part` 2 ; un groupe sans occurrence ne compte pas), `properties` (`{ name, group, total, kept, lost, examples: { where, expected, actual }[] }`), `identicalParts`, `changedParts` (`{ path, summary }`, `summary` vaut `ajoutée` pour une partie nouvelle), `lost` (objets non représentables signalés par l'import stylé des deux documents), `notes`. Un document comparé à lui-même vaut 1.
|
|
391
|
+
|
|
392
|
+
```bash
|
|
393
|
+
npm run fidelity -- fixtures/docx/14-tableaux-riches.docx
|
|
394
|
+
npm run fidelity -- fixtures/docx/client/mon-document.docx --keep sortie.docx --out rapport.md
|
|
395
|
+
npm run fidelity -- fixtures/docx/16-sections.docx --no-template --threshold 0.7
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
`scripts/fidelity-report.ts` importe le document en `styled`, le réexporte **sans modification dans son gabarit** (`exportDocx({ template, layout })`), compare et imprime un rapport Markdown (groupe, propriété, total, conservé, perdu, exemples, parties changées, objets perdus, score). Code de sortie 1 sous le seuil (0,98 par défaut). `--no-template` mesure l'export neuf avec `layout` seul, structurellement plus bas. Le script bundle les sources de `word` avec esbuild et fournit un DOM jsdom : aucun build préalable. Scores mesurés et ce qui manque encore, document par document, dans [`fixtures/docx/README.md`](../../fixtures/docx/README.md) : 1 pour 11, 13 et 15 ; 0,990 (12), 0,998 (14), 0,756 (16, sections multiples), 0,944 (17) ; document client 0,889 dans le gabarit contre 0,627 en export neuf. Cible ≥ 0,98 (`src/docx/compare/compare-fixtures.spec.ts`).
|
|
292
399
|
|
|
293
400
|
#### Limites connues de l'export
|
|
294
401
|
|
|
@@ -333,9 +440,25 @@ Un `exportDocx` sans `DOMParser` global ni `options.parse` lève aussi une erreu
|
|
|
333
440
|
|
|
334
441
|
## ⚠️ Limites connues
|
|
335
442
|
|
|
336
|
-
|
|
443
|
+
Mode `styled` et export dans le gabarit (session 3) :
|
|
444
|
+
|
|
445
|
+
- **Sections multiples réduites à la dernière** : `layout` ne décrit que la dernière section et le corps greffé n'a plus de `w:sectPr` intermédiaire. Un document à deux formats de page ou à marges changeantes ressort avec un seul (fixture 16 : 0,756 ; document client, trois sections : 0,889). Les en-têtes et pieds des sections précédentes sont fusionnés par héritage, pas perdus.
|
|
446
|
+
- **Styles conditionnels de tableau** (`tblStylePr` : première ligne, première colonne, bandes) : approximés en propriétés directes à l'import, jamais relus comme style, non comparés par `compareDocx`. Un tableau dont le style posait des traits gris peut ressortir avec des traits noirs ou des bordures `nil` oubliées (document client, `table.cellBorders` ×8).
|
|
447
|
+
- **Habillage des images** : une image ancrée (flottante, derrière le texte) devient inline, alignée par `data-align` ; sa position exacte, son rognage et ses effets sont perdus.
|
|
448
|
+
- **Pagination** : rien dans ce paquet ne calcule de pages. La vue page du core et sa pagination approximative sont un confort d'écran ; le `.docx` produit laisse Word paginer.
|
|
449
|
+
- **Tabulations** : `w:tab` devient un espace cadratin U+2003, réécrit en texte, pas en `w:tab` ; les taquets sont ignorés (fixture 12, une propriété perdue).
|
|
450
|
+
- **Espaces finaux et runs vides** : un run réduit à un espace peut être retiré avec son espace à l'export (document client, 21 paragraphes).
|
|
451
|
+
- **Alignement d'un tableau** (`w:jc` sur `w:tblPr`) : non lu, un tableau centré ressort aligné à gauche (fixture 14).
|
|
452
|
+
- **Objets non représentables** dans le corps : remplacés par leur texte et perdus (voir plus haut). Dans les en-têtes et pieds, intacts tant qu'un `layout` ne régénère pas la partie.
|
|
453
|
+
- **En-têtes et pieds régénérés** depuis `layout` passent par l'export neuf : ils perdent ce que l'export neuf ne sait pas écrire (tableaux complexes, images ancrées, champs autres que `PAGE` / `NUMPAGES` / fusion).
|
|
454
|
+
- **`{{nom}}` d'un en-tête du gabarit coupé sur deux runs** : non résolu en mode `resolve`.
|
|
455
|
+
- **Gabarit volumineux** : une partie principale de plus de 16 Mo est refusée (`template-invalid`, export neuf) plutôt que parsée.
|
|
456
|
+
|
|
457
|
+
Mode `semantic` (mammoth) et collage :
|
|
458
|
+
|
|
459
|
+
- **Pas de fidélité de mise en page** : colonnes, en-têtes et pieds de page, sauts de page, polices exactes, tailles et interlignes sont volontairement jetés par mammoth. L'objectif est de préserver l'intention (structure, emphase), pas le rendu. Les en-têtes et pieds ne sont lus que pour y collecter des variables (`result.variables`). Passe en `styled` pour tout conserver.
|
|
337
460
|
- **Images collées depuis Word sous Windows** : le presse-papier référence des fichiers locaux (`file:///C:/Users/…/clip_image001.png`). Le navigateur ne peut pas les lire, la politique de liens du core rejette `file:` et l'image est perdue. Passe par l'import `.docx` ou par un glisser-déposer du fichier image.
|
|
338
|
-
- **Formes, zones de texte, SmartArt, équations** : contenu VML/DrawingML non converti. Le texte des zones de texte peut survivre, pas la mise en page.
|
|
461
|
+
- **Formes, zones de texte, SmartArt, équations** : contenu VML/DrawingML non converti par mammoth, sans signalement. Le texte des zones de texte peut survivre, pas la mise en page.
|
|
339
462
|
- **Numérotation reprise** : une liste Word qui « continue » une liste précédente arrive avec `start`, mais la reprise exacte dépend de ce que Word a sérialisé.
|
|
340
463
|
- **Notes de bas de page** : conservées dans `html` via `data-footnotes` / `data-footnote`, mais le schéma Tiptap ne garde pas ces attributs après `setHTML` ; seule la liste finale et les appels `[1]` restent.
|
|
341
464
|
- **Styles custom** : un style de paragraphe inconnu remonte en avertissement `mammoth` et devient un `<p>`. Ajoute une règle dans `styleMap` pour le mapper.
|
|
@@ -344,7 +467,7 @@ Un `exportDocx` sans `DOMParser` global ni `options.parse` lève aussi une erreu
|
|
|
344
467
|
|
|
345
468
|
## ⚖️ Poids de mammoth et de docx, import dynamique
|
|
346
469
|
|
|
347
|
-
mammoth pèse environ **660 kB non minifié**, la lib `docx` environ **1,3 MB**. `importDocx` charge mammoth via `await import('mammoth')` au premier appel et `exportDocx` fait de même pour `docx
|
|
470
|
+
mammoth pèse environ **660 kB non minifié**, la lib `docx` environ **1,3 MB**. `importDocx` charge mammoth via `await import('mammoth')` au premier appel **en mode `semantic`** (le lecteur stylé ne s'en sert pas : `jszip` et le `DOMParser` suffisent) et `exportDocx` fait de même pour `docx`, gabarit ou pas : ni l'un ni l'autre n'entre dans le bundle initial si ton bundler suit le code-splitting (esbuild, Vite, Angular CLI le font). Pour aller plus loin, importe aussi le paquet à la demande :
|
|
348
471
|
|
|
349
472
|
```ts
|
|
350
473
|
async function loadDocx(file: File): Promise<string> {
|
|
@@ -353,8 +476,8 @@ async function loadDocx(file: File): Promise<string> {
|
|
|
353
476
|
}
|
|
354
477
|
```
|
|
355
478
|
|
|
356
|
-
`wordPasteTransformer`, `normalizeWordHtml`, `normalizeGoogleDocsHtml`, `jsonToHtml` et `
|
|
479
|
+
`wordPasteTransformer`, `normalizeWordHtml`, `normalizeGoogleDocsHtml`, `jsonToHtml`, `downloadDocx` et `compareDocx` n'ont **aucune dépendance** à mammoth ni à `docx` : les importer statiquement ne coûte que leur propre code (et `jszip` pour le comparateur). Sur `ng serve`, le tout premier `importDocx` ou `exportDocx` d'une session peut recharger la page : l'optimiseur de dépendances de Vite découvre mammoth ou `docx` à ce moment-là (voir le [guide d'intégration](../../docs/GUIDE_INTEGRATION.md)).
|
|
357
480
|
|
|
358
481
|
## 🧾 Exports
|
|
359
482
|
|
|
360
|
-
`importDocx`, `exportDocx`, `downloadDocx`, `jsonToHtml`, `DOCX_MIME_TYPE`, `normalizeWordHtml`, `normalizeGoogleDocsHtml`, `isWordHtml`, `isGoogleDocsHtml`, `wordPasteTransformer`, et les types `DocxInput`, `DocxImportOptions`, `DocxImportResult`, `DocxImportWarning`, `DocxImportWarningCode`, `DocxVariableOptions`, `VariableDetection`, `DocxExportInput`, `DocxExportOptions`, `DocxExportWarning`, `DocxExportWarningCode`, `DocxImageFetcher`, `DocxWarning`, `DocxPageSize`, `DocxPageOrientation`, `DocxPageMargins`, `DocxPageOptions`, `DocxDefaultFont`, `DocxExportVariableOptions`, `DocxVariableMode`, `DocxHeaderAlign`, `DocxHeaderOptions`, `DocxFooterOptions`, `DocxTocOptions`, `DocxNumberedHeadingsOptions`, `DocxHeadingStyle`, `DocxHeadingStyles`, `HtmlParser`, `WordPasteOptions`, `PasteVariableOptions`, `ListLevelFormat`.
|
|
483
|
+
`importDocx`, `exportDocx`, `downloadDocx`, `jsonToHtml`, `compareDocx`, `DOCX_MIME_TYPE`, `normalizeWordHtml`, `normalizeGoogleDocsHtml`, `isWordHtml`, `isGoogleDocsHtml`, `wordPasteTransformer`, et les types `DocxInput`, `DocxImportOptions`, `DocxImportResult`, `DocxImportWarning`, `DocxImportWarningCode`, `DocxLostObject`, `DocxLostKind`, `DocxLostWhere`, `DocxVariableOptions`, `VariableDetection`, `DocxExportInput`, `DocxExportOptions`, `DocxExportWarning`, `DocxExportWarningCode`, `DocxImageFetcher`, `DocxWarning`, `DocxPageSize`, `DocxPageOrientation`, `DocxPageMargins`, `DocxPageOptions`, `DocxDefaultFont`, `DocxExportVariableOptions`, `DocxVariableMode`, `DocxHeaderAlign`, `DocxHeaderOptions`, `DocxFooterOptions`, `DocxTocOptions`, `DocxNumberedHeadingsOptions`, `DocxHeadingStyle`, `DocxHeadingStyles`, `CompareDocx`, `CompareDocxOptions`, `FidelityReport`, `FidelityProperty`, `FidelityPropertyGroup`, `FidelityExample`, `HtmlParser`, `WordPasteOptions`, `PasteVariableOptions`, `ListLevelFormat`. `DocumentLayout` et les types de mise en page viennent de `@aster-editor/core`.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Alignement de deux séquences de blocs (ou de runs) pour le rapport de fidélité.
|
|
3
|
+
* Préfixe et suffixe communs retirés, puis LCS sur les clés du milieu, borné
|
|
4
|
+
* (`MAX_LCS_CELLS`) ; au-delà, appariement guidé par les clés **uniques** des deux côtés :
|
|
5
|
+
* plus longue sous-suite croissante de leurs positions (patience diff, R30-06), puis LCS ou
|
|
6
|
+
* nouvel ancrage dans chaque trou entre deux ancres, glouton monotone en dernier recours.
|
|
7
|
+
* Entre deux paires appariées, un trou de **même longueur** des deux côtés est apparié par
|
|
8
|
+
* position (blocs modifiés, R30-09) : deux séquences de même longueur dont tout colle
|
|
9
|
+
* restent alignées par position, à coût nul, et un bloc retiré plus un bloc ajouté coûtent
|
|
10
|
+
* plus qu'un bloc retiré.
|
|
11
|
+
*/
|
|
12
|
+
/** Paire alignée : `a` et `b` sont des index, `undefined` pour un élément sans vis-à-vis. */
|
|
13
|
+
export interface AlignedPair {
|
|
14
|
+
readonly a?: number;
|
|
15
|
+
readonly b?: number;
|
|
16
|
+
}
|
|
17
|
+
/** Au-delà de ce nombre de cellules de la table LCS, l'ancrage par clés uniques prend le relais. */
|
|
18
|
+
export declare const MAX_LCS_CELLS = 1000000;
|
|
19
|
+
/**
|
|
20
|
+
* Aligne deux séquences par leurs clés. Les paires sont rendues dans l'ordre, les
|
|
21
|
+
* éléments sans vis-à-vis intercalés à leur place (`a` seul = supprimé, `b` seul = inséré).
|
|
22
|
+
*/
|
|
23
|
+
export declare function alignSequences(a: readonly string[], b: readonly string[]): AlignedPair[];
|
|
24
|
+
//# sourceMappingURL=align.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"align.d.ts","sourceRoot":"","sources":["../../../src/docx/compare/align.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,6FAA6F;AAC7F,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,oGAAoG;AACpG,eAAO,MAAM,aAAa,UAAY,CAAC;AAiLvC;;;GAGG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,SAAS,MAAM,EAAE,EAAE,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,WAAW,EAAE,CAuBxF"}
|