@fiduswriter/document 0.1.0-alpha.3 → 0.1.0-alpha.5
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 +22 -1
- package/dist/bibliography/common.d.ts +10 -0
- package/dist/bibliography/common.d.ts.map +1 -0
- package/dist/bibliography/common.js +86 -0
- package/dist/bibliography/common.js.map +1 -0
- package/dist/bibliography/csl_bib.d.ts +3 -0
- package/dist/bibliography/csl_bib.d.ts.map +1 -0
- package/dist/bibliography/csl_bib.js +131 -0
- package/dist/bibliography/csl_bib.js.map +1 -0
- package/dist/citations/citeproc_sys.d.ts +12 -0
- package/dist/citations/citeproc_sys.d.ts.map +1 -0
- package/dist/citations/citeproc_sys.js +48 -0
- package/dist/citations/citeproc_sys.js.map +1 -0
- package/dist/citations/format.d.ts +40 -0
- package/dist/citations/format.d.ts.map +1 -0
- package/dist/citations/format.js +186 -0
- package/dist/citations/format.js.map +1 -0
- package/dist/editor/e2ee/encryptor.d.ts +123 -0
- package/dist/editor/e2ee/encryptor.d.ts.map +1 -0
- package/dist/editor/e2ee/encryptor.js +201 -0
- package/dist/editor/e2ee/encryptor.js.map +1 -0
- package/dist/exporter/docx/citations.d.ts +31 -0
- package/dist/exporter/docx/citations.d.ts.map +1 -0
- package/dist/exporter/docx/citations.js +171 -0
- package/dist/exporter/docx/citations.js.map +1 -0
- package/dist/exporter/docx/comments.d.ts +22 -0
- package/dist/exporter/docx/comments.d.ts.map +1 -0
- package/dist/exporter/docx/comments.js +170 -0
- package/dist/exporter/docx/comments.js.map +1 -0
- package/dist/exporter/docx/footnotes.d.ts +47 -0
- package/dist/exporter/docx/footnotes.d.ts.map +1 -0
- package/dist/exporter/docx/footnotes.js +216 -0
- package/dist/exporter/docx/footnotes.js.map +1 -0
- package/dist/exporter/docx/images.d.ts +25 -0
- package/dist/exporter/docx/images.d.ts.map +1 -0
- package/dist/exporter/docx/images.js +101 -0
- package/dist/exporter/docx/images.js.map +1 -0
- package/dist/exporter/docx/index.d.ts +16 -0
- package/dist/exporter/docx/index.d.ts.map +1 -0
- package/dist/exporter/docx/index.js +115 -0
- package/dist/exporter/docx/index.js.map +1 -0
- package/dist/exporter/docx/lists.d.ts +38 -0
- package/dist/exporter/docx/lists.d.ts.map +1 -0
- package/dist/exporter/docx/lists.js +276 -0
- package/dist/exporter/docx/lists.js.map +1 -0
- package/dist/exporter/docx/math.d.ts +13 -0
- package/dist/exporter/docx/math.d.ts.map +1 -0
- package/dist/exporter/docx/math.js +59 -0
- package/dist/exporter/docx/math.js.map +1 -0
- package/dist/exporter/docx/metadata.d.ts +17 -0
- package/dist/exporter/docx/metadata.d.ts.map +1 -0
- package/dist/exporter/docx/metadata.js +272 -0
- package/dist/exporter/docx/metadata.js.map +1 -0
- package/dist/exporter/docx/rels.d.ts +27 -0
- package/dist/exporter/docx/rels.d.ts.map +1 -0
- package/dist/exporter/docx/rels.js +183 -0
- package/dist/exporter/docx/rels.js.map +1 -0
- package/dist/exporter/docx/render.d.ts +22 -0
- package/dist/exporter/docx/render.d.ts.map +1 -0
- package/dist/exporter/docx/render.js +780 -0
- package/dist/exporter/docx/render.js.map +1 -0
- package/dist/exporter/docx/richtext.d.ts +25 -0
- package/dist/exporter/docx/richtext.d.ts.map +1 -0
- package/dist/exporter/docx/richtext.js +1070 -0
- package/dist/exporter/docx/richtext.js.map +1 -0
- package/dist/exporter/docx/tables.d.ts +16 -0
- package/dist/exporter/docx/tables.d.ts.map +1 -0
- package/dist/exporter/docx/tables.js +115 -0
- package/dist/exporter/docx/tables.js.map +1 -0
- package/dist/exporter/docx/tools.d.ts +4 -0
- package/dist/exporter/docx/tools.d.ts.map +1 -0
- package/dist/exporter/docx/tools.js +48 -0
- package/dist/exporter/docx/tools.js.map +1 -0
- package/dist/exporter/epub/index.d.ts +19 -0
- package/dist/exporter/epub/index.d.ts.map +1 -0
- package/dist/exporter/epub/index.js +109 -0
- package/dist/exporter/epub/index.js.map +1 -0
- package/dist/exporter/epub/templates.d.ts +63 -0
- package/dist/exporter/epub/templates.d.ts.map +1 -0
- package/dist/exporter/epub/templates.js +95 -0
- package/dist/exporter/epub/templates.js.map +1 -0
- package/dist/exporter/epub/tools.d.ts +13 -0
- package/dist/exporter/epub/tools.d.ts.map +1 -0
- package/{src → dist}/exporter/epub/tools.js +32 -46
- package/dist/exporter/epub/tools.js.map +1 -0
- package/dist/exporter/html/citations.d.ts +29 -0
- package/dist/exporter/html/citations.d.ts.map +1 -0
- package/dist/exporter/html/citations.js +110 -0
- package/dist/exporter/html/citations.js.map +1 -0
- package/dist/exporter/html/convert.d.ts +50 -0
- package/dist/exporter/html/convert.d.ts.map +1 -0
- package/dist/exporter/html/convert.js +750 -0
- package/dist/exporter/html/convert.js.map +1 -0
- package/dist/exporter/html/index.d.ts +64 -0
- package/dist/exporter/html/index.d.ts.map +1 -0
- package/dist/exporter/html/index.js +154 -0
- package/dist/exporter/html/index.js.map +1 -0
- package/dist/exporter/html/templates.d.ts +24 -0
- package/dist/exporter/html/templates.d.ts.map +1 -0
- package/dist/exporter/html/templates.js +21 -0
- package/dist/exporter/html/templates.js.map +1 -0
- package/dist/exporter/html/tools.d.ts +2 -0
- package/dist/exporter/html/tools.d.ts.map +1 -0
- package/dist/exporter/html/tools.js +49 -0
- package/dist/exporter/html/tools.js.map +1 -0
- package/dist/exporter/jats/bibliography.d.ts +3 -0
- package/dist/exporter/jats/bibliography.d.ts.map +1 -0
- package/dist/exporter/jats/bibliography.js +158 -0
- package/dist/exporter/jats/bibliography.js.map +1 -0
- package/dist/exporter/jats/citations.d.ts +16 -0
- package/dist/exporter/jats/citations.d.ts.map +1 -0
- package/dist/exporter/jats/citations.js +105 -0
- package/dist/exporter/jats/citations.js.map +1 -0
- package/dist/exporter/jats/convert.d.ts +41 -0
- package/dist/exporter/jats/convert.d.ts.map +1 -0
- package/dist/exporter/jats/convert.js +816 -0
- package/dist/exporter/jats/convert.js.map +1 -0
- package/dist/exporter/jats/index.d.ts +25 -0
- package/dist/exporter/jats/index.d.ts.map +1 -0
- package/dist/exporter/jats/index.js +82 -0
- package/dist/exporter/jats/index.js.map +1 -0
- package/dist/exporter/jats/templates.d.ts +18 -0
- package/dist/exporter/jats/templates.d.ts.map +1 -0
- package/dist/exporter/jats/templates.js +25 -0
- package/dist/exporter/jats/templates.js.map +1 -0
- package/dist/exporter/jats/text.d.ts +4 -0
- package/dist/exporter/jats/text.d.ts.map +1 -0
- package/dist/exporter/jats/text.js +72 -0
- package/dist/exporter/jats/text.js.map +1 -0
- package/dist/exporter/latex/convert.d.ts +24 -0
- package/dist/exporter/latex/convert.d.ts.map +1 -0
- package/dist/exporter/latex/convert.js +881 -0
- package/dist/exporter/latex/convert.js.map +1 -0
- package/dist/exporter/latex/escape_latex.d.ts +2 -0
- package/dist/exporter/latex/escape_latex.d.ts.map +1 -0
- package/dist/exporter/latex/escape_latex.js +20 -0
- package/dist/exporter/latex/escape_latex.js.map +1 -0
- package/dist/exporter/latex/index.d.ts +24 -0
- package/dist/exporter/latex/index.d.ts.map +1 -0
- package/dist/exporter/latex/index.js +67 -0
- package/dist/exporter/latex/index.js.map +1 -0
- package/dist/exporter/latex/readme.d.ts +2 -0
- package/dist/exporter/latex/readme.d.ts.map +1 -0
- package/dist/exporter/latex/readme.js +23 -0
- package/dist/exporter/latex/readme.js.map +1 -0
- package/dist/exporter/native/copy.d.ts +30 -0
- package/dist/exporter/native/copy.d.ts.map +1 -0
- package/dist/exporter/native/copy.js +151 -0
- package/dist/exporter/native/copy.js.map +1 -0
- package/dist/exporter/native/file.d.ts +12 -0
- package/dist/exporter/native/file.d.ts.map +1 -0
- package/dist/exporter/native/file.js +38 -0
- package/dist/exporter/native/file.js.map +1 -0
- package/dist/exporter/native/index.d.ts +5 -0
- package/dist/exporter/native/index.d.ts.map +1 -0
- package/dist/exporter/native/index.js +5 -0
- package/dist/exporter/native/index.js.map +1 -0
- package/dist/exporter/native/revision.d.ts +18 -0
- package/dist/exporter/native/revision.d.ts.map +1 -0
- package/dist/exporter/native/revision.js +39 -0
- package/dist/exporter/native/revision.js.map +1 -0
- package/dist/exporter/native/shrink.d.ts +36 -0
- package/dist/exporter/native/shrink.d.ts.map +1 -0
- package/dist/exporter/native/shrink.js +124 -0
- package/dist/exporter/native/shrink.js.map +1 -0
- package/dist/exporter/native/zip.d.ts +25 -0
- package/dist/exporter/native/zip.d.ts.map +1 -0
- package/dist/exporter/native/zip.js +56 -0
- package/dist/exporter/native/zip.js.map +1 -0
- package/dist/exporter/odt/citations.d.ts +21 -0
- package/dist/exporter/odt/citations.d.ts.map +1 -0
- package/dist/exporter/odt/citations.js +104 -0
- package/dist/exporter/odt/citations.js.map +1 -0
- package/dist/exporter/odt/footnotes.d.ts +31 -0
- package/dist/exporter/odt/footnotes.d.ts.map +1 -0
- package/dist/exporter/odt/footnotes.js +129 -0
- package/dist/exporter/odt/footnotes.js.map +1 -0
- package/dist/exporter/odt/images.d.ts +25 -0
- package/dist/exporter/odt/images.d.ts.map +1 -0
- package/dist/exporter/odt/images.js +106 -0
- package/dist/exporter/odt/images.js.map +1 -0
- package/dist/exporter/odt/index.d.ts +17 -0
- package/dist/exporter/odt/index.d.ts.map +1 -0
- package/dist/exporter/odt/index.js +108 -0
- package/dist/exporter/odt/index.js.map +1 -0
- package/dist/exporter/odt/math.d.ts +15 -0
- package/dist/exporter/odt/math.d.ts.map +1 -0
- package/dist/exporter/odt/math.js +71 -0
- package/dist/exporter/odt/math.js.map +1 -0
- package/dist/exporter/odt/metadata.d.ts +18 -0
- package/dist/exporter/odt/metadata.d.ts.map +1 -0
- package/dist/exporter/odt/metadata.js +224 -0
- package/dist/exporter/odt/metadata.js.map +1 -0
- package/dist/exporter/odt/render.d.ts +26 -0
- package/dist/exporter/odt/render.d.ts.map +1 -0
- package/dist/exporter/odt/render.js +651 -0
- package/dist/exporter/odt/render.js.map +1 -0
- package/dist/exporter/odt/richtext.d.ts +22 -0
- package/dist/exporter/odt/richtext.d.ts.map +1 -0
- package/dist/exporter/odt/richtext.js +743 -0
- package/dist/exporter/odt/richtext.js.map +1 -0
- package/dist/exporter/odt/styles.d.ts +41 -0
- package/dist/exporter/odt/styles.d.ts.map +1 -0
- package/dist/exporter/odt/styles.js +370 -0
- package/dist/exporter/odt/styles.js.map +1 -0
- package/dist/exporter/odt/track.d.ts +18 -0
- package/dist/exporter/odt/track.d.ts.map +1 -0
- package/dist/exporter/odt/track.js +66 -0
- package/dist/exporter/odt/track.js.map +1 -0
- package/dist/exporter/pandoc/citations.d.ts +23 -0
- package/dist/exporter/pandoc/citations.d.ts.map +1 -0
- package/dist/exporter/pandoc/citations.js +99 -0
- package/dist/exporter/pandoc/citations.js.map +1 -0
- package/dist/exporter/pandoc/convert.d.ts +19 -0
- package/dist/exporter/pandoc/convert.d.ts.map +1 -0
- package/dist/exporter/pandoc/convert.js +886 -0
- package/dist/exporter/pandoc/convert.js.map +1 -0
- package/dist/exporter/pandoc/index.d.ts +25 -0
- package/dist/exporter/pandoc/index.d.ts.map +1 -0
- package/dist/exporter/pandoc/index.js +79 -0
- package/dist/exporter/pandoc/index.js.map +1 -0
- package/dist/exporter/pandoc/readme.d.ts +2 -0
- package/dist/exporter/pandoc/readme.d.ts.map +1 -0
- package/dist/exporter/pandoc/readme.js +8 -0
- package/dist/exporter/pandoc/readme.js.map +1 -0
- package/dist/exporter/pandoc/tools.d.ts +22 -0
- package/dist/exporter/pandoc/tools.d.ts.map +1 -0
- package/dist/exporter/pandoc/tools.js +52 -0
- package/dist/exporter/pandoc/tools.js.map +1 -0
- package/dist/exporter/print/index.d.ts +20 -0
- package/dist/exporter/print/index.d.ts.map +1 -0
- package/dist/exporter/print/index.js +142 -0
- package/dist/exporter/print/index.js.map +1 -0
- package/dist/exporter/tools/doc_content.d.ts +6 -0
- package/dist/exporter/tools/doc_content.d.ts.map +1 -0
- package/dist/exporter/tools/doc_content.js +130 -0
- package/dist/exporter/tools/doc_content.js.map +1 -0
- package/dist/exporter/tools/file.d.ts +2 -0
- package/dist/exporter/tools/file.d.ts.map +1 -0
- package/dist/exporter/tools/file.js +10 -0
- package/dist/exporter/tools/file.js.map +1 -0
- package/dist/exporter/tools/json.d.ts +7 -0
- package/dist/exporter/tools/json.d.ts.map +1 -0
- package/dist/exporter/tools/json.js +82 -0
- package/dist/exporter/tools/json.js.map +1 -0
- package/dist/exporter/tools/svg.d.ts +8 -0
- package/dist/exporter/tools/svg.d.ts.map +1 -0
- package/dist/exporter/tools/svg.js +26 -0
- package/dist/exporter/tools/svg.js.map +1 -0
- package/dist/exporter/tools/xml.d.ts +42 -0
- package/dist/exporter/tools/xml.d.ts.map +1 -0
- package/dist/exporter/tools/xml.js +467 -0
- package/dist/exporter/tools/xml.js.map +1 -0
- package/dist/exporter/tools/xml_zip.d.ts +21 -0
- package/dist/exporter/tools/xml_zip.d.ts.map +1 -0
- package/dist/exporter/tools/xml_zip.js +92 -0
- package/dist/exporter/tools/xml_zip.js.map +1 -0
- package/dist/exporter/tools/zip.d.ts +35 -0
- package/dist/exporter/tools/zip.d.ts.map +1 -0
- package/dist/exporter/tools/zip.js +78 -0
- package/dist/exporter/tools/zip.js.map +1 -0
- package/dist/exporter/tools/zotero_csl.d.ts +23 -0
- package/dist/exporter/tools/zotero_csl.d.ts.map +1 -0
- package/dist/exporter/tools/zotero_csl.js +78 -0
- package/dist/exporter/tools/zotero_csl.js.map +1 -0
- package/dist/importer/citations.d.ts +28 -0
- package/dist/importer/citations.d.ts.map +1 -0
- package/dist/importer/citations.js +68 -0
- package/dist/importer/citations.js.map +1 -0
- package/dist/importer/docx/citations.d.ts +43 -0
- package/dist/importer/docx/citations.d.ts.map +1 -0
- package/dist/importer/docx/citations.js +81 -0
- package/dist/importer/docx/citations.js.map +1 -0
- package/dist/importer/docx/convert.d.ts +150 -0
- package/dist/importer/docx/convert.d.ts.map +1 -0
- package/dist/importer/docx/convert.js +1243 -0
- package/dist/importer/docx/convert.js.map +1 -0
- package/dist/importer/docx/helpers.d.ts +2 -0
- package/dist/importer/docx/helpers.d.ts.map +1 -0
- package/dist/importer/docx/helpers.js +10 -0
- package/dist/importer/docx/helpers.js.map +1 -0
- package/dist/importer/docx/index.d.ts +27 -0
- package/dist/importer/docx/index.d.ts.map +1 -0
- package/dist/importer/docx/index.js +77 -0
- package/dist/importer/docx/index.js.map +1 -0
- package/dist/importer/docx/omml2mathml.d.ts +6 -0
- package/dist/importer/docx/omml2mathml.d.ts.map +1 -0
- package/dist/importer/docx/omml2mathml.js +1146 -0
- package/dist/importer/docx/omml2mathml.js.map +1 -0
- package/dist/importer/docx/parse.d.ts +96 -0
- package/dist/importer/docx/parse.d.ts.map +1 -0
- package/dist/importer/docx/parse.js +681 -0
- package/dist/importer/docx/parse.js.map +1 -0
- package/dist/importer/native/extract_template.d.ts +6 -0
- package/dist/importer/native/extract_template.d.ts.map +1 -0
- package/dist/importer/native/extract_template.js +80 -0
- package/dist/importer/native/extract_template.js.map +1 -0
- package/dist/importer/native/file.d.ts +37 -0
- package/dist/importer/native/file.d.ts.map +1 -0
- package/dist/importer/native/file.js +162 -0
- package/dist/importer/native/file.js.map +1 -0
- package/dist/importer/native/get_images.d.ts +21 -0
- package/dist/importer/native/get_images.d.ts.map +1 -0
- package/dist/importer/native/get_images.js +58 -0
- package/dist/importer/native/get_images.js.map +1 -0
- package/dist/importer/native/importer.d.ts +41 -0
- package/dist/importer/native/importer.d.ts.map +1 -0
- package/dist/importer/native/importer.js +170 -0
- package/dist/importer/native/importer.js.map +1 -0
- package/dist/importer/native/index.d.ts +7 -0
- package/dist/importer/native/index.d.ts.map +1 -0
- package/dist/importer/native/index.js +7 -0
- package/dist/importer/native/index.js.map +1 -0
- package/dist/importer/native/update.d.ts +7 -0
- package/dist/importer/native/update.d.ts.map +1 -0
- package/dist/importer/native/update.js +27 -0
- package/dist/importer/native/update.js.map +1 -0
- package/dist/importer/native/update_template.d.ts +12 -0
- package/dist/importer/native/update_template.d.ts.map +1 -0
- package/dist/importer/native/update_template.js +19 -0
- package/dist/importer/native/update_template.js.map +1 -0
- package/dist/importer/odt/citations.d.ts +27 -0
- package/dist/importer/odt/citations.d.ts.map +1 -0
- package/dist/importer/odt/citations.js +57 -0
- package/dist/importer/odt/citations.js.map +1 -0
- package/dist/importer/odt/convert.d.ts +146 -0
- package/dist/importer/odt/convert.d.ts.map +1 -0
- package/dist/importer/odt/convert.js +1530 -0
- package/dist/importer/odt/convert.js.map +1 -0
- package/dist/importer/odt/index.d.ts +30 -0
- package/dist/importer/odt/index.d.ts.map +1 -0
- package/dist/importer/odt/index.js +124 -0
- package/dist/importer/odt/index.js.map +1 -0
- package/dist/importer/pandoc/convert.d.ts +32 -0
- package/dist/importer/pandoc/convert.d.ts.map +1 -0
- package/dist/importer/pandoc/convert.js +790 -0
- package/dist/importer/pandoc/convert.js.map +1 -0
- package/dist/importer/pandoc/helpers.d.ts +5 -0
- package/dist/importer/pandoc/helpers.d.ts.map +1 -0
- package/dist/importer/pandoc/helpers.js +73 -0
- package/dist/importer/pandoc/helpers.js.map +1 -0
- package/dist/importer/pandoc/index.d.ts +31 -0
- package/dist/importer/pandoc/index.d.ts.map +1 -0
- package/dist/importer/pandoc/index.js +114 -0
- package/dist/importer/pandoc/index.js.map +1 -0
- package/dist/importer/registry.d.ts +21 -0
- package/dist/importer/registry.d.ts.map +1 -0
- package/dist/importer/registry.js +41 -0
- package/dist/importer/registry.js.map +1 -0
- package/dist/importer/zip_analyzer.d.ts +31 -0
- package/dist/importer/zip_analyzer.d.ts.map +1 -0
- package/dist/importer/zip_analyzer.js +89 -0
- package/dist/importer/zip_analyzer.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/mathlive/opf_includes.d.ts +2 -0
- package/dist/mathlive/opf_includes.d.ts.map +1 -0
- package/dist/mathlive/opf_includes.js +25 -0
- package/dist/mathlive/opf_includes.js.map +1 -0
- package/dist/schema/common/annotate.d.ts +47 -0
- package/dist/schema/common/annotate.d.ts.map +1 -0
- package/dist/schema/common/annotate.js +75 -0
- package/dist/schema/common/annotate.js.map +1 -0
- package/dist/schema/common/base.d.ts +91 -0
- package/dist/schema/common/base.d.ts.map +1 -0
- package/dist/schema/common/base.js +109 -0
- package/dist/schema/common/base.js.map +1 -0
- package/dist/schema/common/citation.d.ts +30 -0
- package/dist/schema/common/citation.d.ts.map +1 -0
- package/dist/schema/common/citation.js +61 -0
- package/dist/schema/common/citation.js.map +1 -0
- package/dist/schema/common/equation.d.ts +17 -0
- package/dist/schema/common/equation.d.ts.map +1 -0
- package/dist/schema/common/equation.js +32 -0
- package/dist/schema/common/equation.js.map +1 -0
- package/dist/schema/common/figure.d.ts +87 -0
- package/dist/schema/common/figure.d.ts.map +1 -0
- package/dist/schema/common/figure.js +178 -0
- package/dist/schema/common/figure.js.map +1 -0
- package/dist/schema/common/heading.d.ts +9 -0
- package/dist/schema/common/heading.d.ts.map +1 -0
- package/dist/schema/common/heading.js +41 -0
- package/dist/schema/common/heading.js.map +1 -0
- package/dist/schema/common/index.d.ts +11 -0
- package/dist/schema/common/index.d.ts.map +1 -0
- package/dist/schema/common/index.js +11 -0
- package/dist/schema/common/index.js.map +1 -0
- package/dist/schema/common/list.d.ts +63 -0
- package/dist/schema/common/list.d.ts.map +1 -0
- package/dist/schema/common/list.js +92 -0
- package/dist/schema/common/list.js.map +1 -0
- package/dist/schema/common/reference.d.ts +70 -0
- package/dist/schema/common/reference.d.ts.map +1 -0
- package/dist/schema/common/reference.js +97 -0
- package/dist/schema/common/reference.js.map +1 -0
- package/dist/schema/common/table.d.ts +76 -0
- package/dist/schema/common/table.d.ts.map +1 -0
- package/dist/schema/common/table.js +86 -0
- package/dist/schema/common/table.js.map +1 -0
- package/dist/schema/common/track.d.ts +117 -0
- package/dist/schema/common/track.d.ts.map +1 -0
- package/{src → dist}/schema/common/track.js +44 -49
- package/dist/schema/common/track.js.map +1 -0
- package/dist/schema/const.d.ts +3 -0
- package/dist/schema/const.d.ts.map +1 -0
- package/{src → dist}/schema/const.js +3 -3
- package/dist/schema/const.js.map +1 -0
- package/dist/schema/convert.d.ts +3 -0
- package/dist/schema/convert.d.ts.map +1 -0
- package/dist/schema/convert.js +1215 -0
- package/dist/schema/convert.js.map +1 -0
- package/dist/schema/document/content.d.ts +110 -0
- package/dist/schema/document/content.d.ts.map +1 -0
- package/dist/schema/document/content.js +177 -0
- package/dist/schema/document/content.js.map +1 -0
- package/dist/schema/document/index.d.ts +795 -0
- package/dist/schema/document/index.d.ts.map +1 -0
- package/dist/schema/document/index.js +68 -0
- package/dist/schema/document/index.js.map +1 -0
- package/dist/schema/document/structure.d.ts +164 -0
- package/dist/schema/document/structure.d.ts.map +1 -0
- package/dist/schema/document/structure.js +445 -0
- package/dist/schema/document/structure.js.map +1 -0
- package/dist/schema/export.d.ts +7 -0
- package/dist/schema/export.d.ts.map +1 -0
- package/dist/schema/export.js +17 -0
- package/dist/schema/export.js.map +1 -0
- package/dist/schema/footnotes.d.ts +454 -0
- package/dist/schema/footnotes.d.ts.map +1 -0
- package/dist/schema/footnotes.js +90 -0
- package/dist/schema/footnotes.js.map +1 -0
- package/dist/schema/footnotes_convert.d.ts +5 -0
- package/dist/schema/footnotes_convert.d.ts.map +1 -0
- package/dist/schema/footnotes_convert.js +30 -0
- package/dist/schema/footnotes_convert.js.map +1 -0
- package/dist/schema/i18n.d.ts +55 -0
- package/dist/schema/i18n.d.ts.map +1 -0
- package/{src → dist}/schema/i18n.js +14 -23
- package/dist/schema/i18n.js.map +1 -0
- package/dist/schema/index.d.ts +6 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +6 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/mini_json.d.ts +4 -0
- package/dist/schema/mini_json.d.ts.map +1 -0
- package/{src → dist}/schema/mini_json.js +19 -28
- package/dist/schema/mini_json.js.map +1 -0
- package/dist/schema/text.d.ts +3 -0
- package/dist/schema/text.d.ts.map +1 -0
- package/dist/schema/text.js +23 -0
- package/dist/schema/text.js.map +1 -0
- package/dist/types.d.ts +281 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/package.json +51 -16
- package/scripts/export-schema.js +1 -1
- package/src/bibliography/{common.js → common.ts} +15 -12
- package/src/bibliography/{csl_bib.js → csl_bib.ts} +16 -15
- package/src/citations/{citeproc_sys.js → citeproc_sys.ts} +26 -6
- package/src/citations/{format.js → format.ts} +80 -24
- package/src/editor/e2ee/{encryptor.js → encryptor.ts} +76 -44
- package/src/exporter/docx/{citations.js → citations.ts} +51 -23
- package/src/exporter/docx/{comments.js → comments.ts} +69 -26
- package/src/exporter/docx/{footnotes.js → footnotes.ts} +81 -42
- package/src/exporter/docx/images.ts +128 -0
- package/src/exporter/docx/{index.js → index.ts} +48 -25
- package/src/exporter/docx/{lists.js → lists.ts} +89 -46
- package/src/exporter/docx/{math.js → math.ts} +29 -9
- package/src/exporter/docx/{metadata.js → metadata.ts} +78 -42
- package/src/exporter/docx/{rels.js → rels.ts} +71 -50
- package/src/exporter/docx/{render.js → render.ts} +124 -87
- package/src/exporter/docx/{richtext.js → richtext.ts} +107 -84
- package/src/exporter/docx/{tables.js → tables.ts} +33 -23
- package/src/exporter/docx/{tools.js → tools.ts} +4 -3
- package/src/exporter/epub/{index.js → index.ts} +44 -15
- package/src/exporter/epub/{templates.js → templates.ts} +71 -7
- package/src/exporter/epub/tools.ts +102 -0
- package/src/exporter/html/{citations.js → citations.ts} +79 -31
- package/src/exporter/html/{convert.js → convert.ts} +118 -71
- package/src/exporter/html/{index.js → index.ts} +63 -29
- package/src/exporter/html/{templates.js → templates.ts} +22 -2
- package/src/exporter/html/{tools.js → tools.ts} +9 -6
- package/src/exporter/jats/bibliography.ts +199 -0
- package/src/exporter/jats/{citations.js → citations.ts} +56 -20
- package/src/exporter/jats/{convert.js → convert.ts} +72 -53
- package/src/exporter/jats/index.ts +132 -0
- package/src/exporter/jats/{templates.js → templates.ts} +19 -3
- package/src/exporter/jats/{text.js → text.ts} +8 -6
- package/src/exporter/latex/{convert.js → convert.ts} +160 -125
- package/src/exporter/latex/{escape_latex.js → escape_latex.ts} +1 -1
- package/src/exporter/latex/{index.js → index.ts} +27 -12
- package/src/exporter/latex/{readme.js → readme.ts} +2 -2
- package/src/exporter/native/copy.ts +231 -0
- package/src/exporter/native/file.ts +65 -0
- package/src/exporter/native/index.ts +4 -0
- package/src/exporter/native/revision.ts +76 -0
- package/src/exporter/native/{shrink.js → shrink.ts} +64 -29
- package/src/exporter/native/zip.ts +85 -0
- package/src/exporter/odt/{citations.js → citations.ts} +35 -15
- package/src/exporter/odt/{footnotes.js → footnotes.ts} +53 -30
- package/src/exporter/odt/images.ts +135 -0
- package/src/exporter/odt/{index.js → index.ts} +49 -27
- package/src/exporter/odt/{math.js → math.ts} +38 -9
- package/src/exporter/odt/{metadata.js → metadata.ts} +54 -29
- package/src/exporter/odt/{render.js → render.ts} +89 -60
- package/src/exporter/odt/{richtext.js → richtext.ts} +102 -72
- package/src/exporter/odt/{styles.js → styles.ts} +98 -66
- package/src/exporter/odt/{track.js → track.ts} +22 -6
- package/src/exporter/pandoc/{citations.js → citations.ts} +31 -15
- package/src/exporter/pandoc/{convert.js → convert.ts} +98 -79
- package/src/exporter/pandoc/{index.js → index.ts} +36 -13
- package/src/exporter/pandoc/{readme.js → readme.ts} +0 -1
- package/src/exporter/pandoc/{tools.js → tools.ts} +28 -5
- package/src/exporter/print/{index.js → index.ts} +38 -19
- package/src/exporter/tools/{doc_content.js → doc_content.ts} +34 -24
- package/src/exporter/tools/{file.js → file.ts} +1 -1
- package/src/exporter/tools/{json.js → json.ts} +30 -19
- package/src/exporter/tools/{svg.js → svg.ts} +9 -3
- package/src/exporter/tools/{xml.js → xml.ts} +187 -138
- package/src/exporter/tools/{xml_zip.js → xml_zip.ts} +19 -11
- package/src/exporter/tools/{zip.js → zip.ts} +35 -11
- package/src/exporter/tools/{zotero_csl.js → zotero_csl.ts} +35 -18
- package/src/global.d.ts +13 -0
- package/src/importer/citations.ts +116 -0
- package/src/importer/docx/{citations.js → citations.ts} +22 -37
- package/src/importer/docx/{convert.js → convert.ts} +176 -141
- package/src/importer/docx/{helpers.js → helpers.ts} +1 -1
- package/src/importer/docx/index.ts +124 -0
- package/src/importer/docx/{omml2mathml.js → omml2mathml.ts} +154 -176
- package/src/importer/docx/{parse.js → parse.ts} +103 -81
- package/src/importer/native/extract_template.ts +83 -0
- package/src/importer/native/file.ts +282 -0
- package/src/importer/native/get_images.ts +82 -0
- package/src/importer/native/importer.ts +244 -0
- package/src/importer/native/index.ts +6 -0
- package/src/importer/native/{update.js → update.ts} +13 -5
- package/src/importer/native/update_template.ts +32 -0
- package/src/importer/odt/{citations.js → citations.ts} +17 -29
- package/src/importer/odt/{convert.js → convert.ts} +261 -237
- package/src/importer/odt/index.ts +202 -0
- package/src/importer/pandoc/{convert.js → convert.ts} +116 -94
- package/src/importer/pandoc/{helpers.js → helpers.ts} +24 -12
- package/src/importer/pandoc/index.ts +171 -0
- package/src/importer/registry.ts +69 -0
- package/src/importer/{zip_analyzer.js → zip_analyzer.ts} +44 -18
- package/src/mathlive/{opf_includes.js → opf_includes.ts} +1 -1
- package/src/modules.d.ts +40 -0
- package/src/schema/common/{annotate.js → annotate.ts} +10 -8
- package/src/schema/common/{base.js → base.ts} +12 -10
- package/src/schema/common/{citation.js → citation.ts} +17 -8
- package/src/schema/common/{equation.js → equation.ts} +6 -4
- package/src/schema/common/{figure.js → figure.ts} +43 -23
- package/src/schema/common/{heading.js → heading.ts} +7 -5
- package/src/schema/common/{list.js → list.ts} +15 -13
- package/src/schema/common/{reference.js → reference.ts} +14 -12
- package/src/schema/common/{table.js → table.ts} +16 -14
- package/src/schema/common/track.ts +201 -0
- package/src/schema/const.ts +58 -0
- package/src/schema/{convert.js → convert.ts} +6 -2
- package/src/schema/document/{content.js → content.ts} +22 -20
- package/src/schema/document/{structure.js → structure.ts} +15 -9
- package/src/schema/{export.js → export.ts} +4 -2
- package/src/schema/{footnotes.js → footnotes.ts} +11 -8
- package/src/schema/{footnotes_convert.js → footnotes_convert.ts} +11 -8
- package/src/schema/i18n.ts +601 -0
- package/src/schema/mini_json.ts +60 -0
- package/src/schema/{text.js → text.ts} +6 -2
- package/src/types.ts +314 -0
- package/jest.config.js +0 -24
- package/src/exporter/docx/images.js +0 -101
- package/src/exporter/jats/bibliography.js +0 -183
- package/src/exporter/jats/index.js +0 -92
- package/src/exporter/odt/images.js +0 -115
- package/src/importer/citations.js +0 -129
- package/src/importer/native/get_images.js +0 -76
- /package/src/{index.js → index.ts} +0 -0
- /package/src/schema/common/{index.js → index.ts} +0 -0
- /package/src/schema/document/{index.js → index.ts} +0 -0
- /package/src/schema/{index.js → index.ts} +0 -0
|
@@ -0,0 +1,1530 @@
|
|
|
1
|
+
import { MathMLToLaTeX } from "mathml-to-latex";
|
|
2
|
+
import { xmlDOM } from "../../exporter/tools/xml.js";
|
|
3
|
+
import { randomCommentId, randomFigureId, randomHeadingId, randomListId, randomTableId } from "../../schema/common/index.js";
|
|
4
|
+
import { parseTracks } from "../../schema/common/track.js";
|
|
5
|
+
import { isOdtBibliographyReferenceMark, isOdtBibliographySection, isOdtCitationMark, parseOdtBibliographyMark, parseOdtReferenceMark } from "./citations.js";
|
|
6
|
+
import { gettext } from "fwtoolkit";
|
|
7
|
+
function attr(node, name) {
|
|
8
|
+
if (node && typeof node === "object" && "getAttribute" in node) {
|
|
9
|
+
return String(node.getAttribute(name) || "");
|
|
10
|
+
}
|
|
11
|
+
return "";
|
|
12
|
+
}
|
|
13
|
+
export class OdtConvert {
|
|
14
|
+
importId;
|
|
15
|
+
template;
|
|
16
|
+
bibliography;
|
|
17
|
+
bibDB;
|
|
18
|
+
images;
|
|
19
|
+
styles;
|
|
20
|
+
contentDoc;
|
|
21
|
+
stylesDoc;
|
|
22
|
+
metaDoc;
|
|
23
|
+
manifestDoc;
|
|
24
|
+
tracks;
|
|
25
|
+
comments;
|
|
26
|
+
currentCommentIds;
|
|
27
|
+
currentTracks;
|
|
28
|
+
referenceableObjects;
|
|
29
|
+
constructor(contentXml, stylesXml, metaXml, manifestXml, importId, template, bibliography, bibDb) {
|
|
30
|
+
this.importId = importId;
|
|
31
|
+
this.template = template;
|
|
32
|
+
this.bibliography = bibliography;
|
|
33
|
+
this.bibDB = bibDb;
|
|
34
|
+
this.images = {};
|
|
35
|
+
this.styles = {};
|
|
36
|
+
this.contentDoc = contentXml ? xmlDOM(contentXml) : null;
|
|
37
|
+
this.stylesDoc = stylesXml ? xmlDOM(stylesXml) : null;
|
|
38
|
+
this.metaDoc = metaXml ? xmlDOM(metaXml) : null;
|
|
39
|
+
this.manifestDoc = manifestXml ? xmlDOM(manifestXml) : null;
|
|
40
|
+
this.tracks = {};
|
|
41
|
+
this.comments = {};
|
|
42
|
+
this.currentCommentIds = [];
|
|
43
|
+
this.currentTracks = [];
|
|
44
|
+
this.referenceableObjects = {}; // All objects that can be referenced
|
|
45
|
+
}
|
|
46
|
+
init() {
|
|
47
|
+
this.parseTrackedChanges();
|
|
48
|
+
this.parseStyles();
|
|
49
|
+
this.parseComments();
|
|
50
|
+
this.collectReferenceableObjects(this.contentDoc);
|
|
51
|
+
const content = this.convert();
|
|
52
|
+
return {
|
|
53
|
+
content,
|
|
54
|
+
settings: {
|
|
55
|
+
import_id: this.importId,
|
|
56
|
+
tracked: Object.keys(this.tracks).length > 0,
|
|
57
|
+
language: this.detectLanguage()
|
|
58
|
+
},
|
|
59
|
+
comments: this.comments
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
parseTrackedChanges() {
|
|
63
|
+
if (!this.contentDoc) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const trackedChangesEl = this.contentDoc.query("text:tracked-changes");
|
|
67
|
+
if (!trackedChangesEl) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
// Tracked deletions are stored in two different ways in FW and ODT.
|
|
71
|
+
// FW: The deleted content stays in place where it was before the deletion,
|
|
72
|
+
// and is marked with a tracked change mark. Megre only occurs after change
|
|
73
|
+
// has been accepted.
|
|
74
|
+
// ODT: The deleted content is removed from the content flow and is replaced by a marker.
|
|
75
|
+
// The removed content is stored in a special section of the document.
|
|
76
|
+
// This method takes all the deleted content and puts it back into the place where
|
|
77
|
+
// it was previously. That way the structure is more similar to the output FW document
|
|
78
|
+
// and is more easily converted.
|
|
79
|
+
const deletions = {};
|
|
80
|
+
const changedRegions = trackedChangesEl.queryAll("text:changed-region");
|
|
81
|
+
changedRegions.forEach((region) => {
|
|
82
|
+
const id = attr(region, "text:id");
|
|
83
|
+
const insertion = region.query("text:insertion");
|
|
84
|
+
const deletion = region.query("text:deletion");
|
|
85
|
+
if (!insertion && !deletion) {
|
|
86
|
+
// Neither insertion or deletion. Must be type unknown to us
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const changeInfo = region.query("office:change-info");
|
|
90
|
+
if (changeInfo) {
|
|
91
|
+
const track = {
|
|
92
|
+
type: insertion ? "insertion" : "deletion",
|
|
93
|
+
user: 1,
|
|
94
|
+
username: changeInfo.query("dc:creator")?.textContent || "",
|
|
95
|
+
date: Math.floor(new Date(changeInfo.query("dc:date")?.textContent || "").getTime() / 60000)
|
|
96
|
+
};
|
|
97
|
+
if (insertion) {
|
|
98
|
+
track.approved = false;
|
|
99
|
+
}
|
|
100
|
+
this.tracks[id] = track;
|
|
101
|
+
if (deletion) {
|
|
102
|
+
// Store deletion content for later use
|
|
103
|
+
deletions[id] = deletion.children.filter((child) => child.tagName !== "office:change-info");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
// Then find and replace all deletion change markers
|
|
108
|
+
const changeMarkers = this.contentDoc.queryAll("text:change");
|
|
109
|
+
changeMarkers.forEach((marker) => {
|
|
110
|
+
const changeId = attr(marker, "text:change-id");
|
|
111
|
+
const deletion = deletions[changeId];
|
|
112
|
+
if (deletion) {
|
|
113
|
+
if (deletion.length > 0) {
|
|
114
|
+
// Create change-start and change-end elements
|
|
115
|
+
const markerIndex = marker.parentElement.children.indexOf(marker);
|
|
116
|
+
marker.parentElement.insertXMLAt(`<text:change-start text:change-id="${changeId}"/>`, markerIndex);
|
|
117
|
+
marker.parentElement.insertXMLAt(`<text:change-end text:change-id="${changeId}"/>`, markerIndex + 2);
|
|
118
|
+
if (deletion.length === 1) {
|
|
119
|
+
// Single block - just insert the content
|
|
120
|
+
deletion[0].children.forEach((content) => {
|
|
121
|
+
marker.parentElement.insertBefore(content, marker);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
// Multiple blocks - need to split the paragraph/headline
|
|
126
|
+
const parentElement = marker.parentElement;
|
|
127
|
+
parentElement.splitAtChildElement(marker, deletion[0].children
|
|
128
|
+
?.map((node) => node.toString())
|
|
129
|
+
.join("") || "", // First block content to be added to current node
|
|
130
|
+
deletion
|
|
131
|
+
.slice(1, -1)
|
|
132
|
+
.map((node) => node.toString())
|
|
133
|
+
.join(""), // Middle blocks
|
|
134
|
+
deletion[deletion.length - 1].toString() // Last block
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// Remove the original change marker
|
|
139
|
+
marker.parentElement.removeChild(marker);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
parseStyles() {
|
|
144
|
+
if (!this.stylesDoc) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const styleNodes = this.stylesDoc.queryAll("style:style");
|
|
148
|
+
styleNodes.forEach((node) => {
|
|
149
|
+
const styleName = attr(node, "style:name");
|
|
150
|
+
this.styles[styleName] = this.parseStyle(node);
|
|
151
|
+
});
|
|
152
|
+
const contentStyleNodes = this.contentDoc.queryAll("style:style");
|
|
153
|
+
contentStyleNodes.forEach((node) => {
|
|
154
|
+
const styleName = attr(node, "style:name");
|
|
155
|
+
this.styles[styleName] = this.parseStyle(node);
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
parseStyle(styleNode) {
|
|
159
|
+
const properties = {
|
|
160
|
+
// Basic style information
|
|
161
|
+
parentStyleName: attr(styleNode, "style:parent-style-name"),
|
|
162
|
+
isSection: attr(styleNode, "style:family") === "section" ||
|
|
163
|
+
Boolean(styleNode.query("style:section-properties")),
|
|
164
|
+
title: attr(styleNode, "style:display-name"),
|
|
165
|
+
// Family and name info
|
|
166
|
+
family: attr(styleNode, "style:family"),
|
|
167
|
+
name: attr(styleNode, "style:name"),
|
|
168
|
+
// Heading related
|
|
169
|
+
isHeading: attr(styleNode, "style:family") === "paragraph" &&
|
|
170
|
+
(attr(styleNode, "style:name")
|
|
171
|
+
.toLowerCase()
|
|
172
|
+
.includes("heading") ||
|
|
173
|
+
attr(styleNode, "style:parent-style-name")
|
|
174
|
+
?.toLowerCase()
|
|
175
|
+
.includes("heading")),
|
|
176
|
+
outlineLevel: attr(styleNode, "text:outline-level"),
|
|
177
|
+
// Text properties
|
|
178
|
+
textProperties: {},
|
|
179
|
+
// Paragraph properties
|
|
180
|
+
paragraphProperties: {},
|
|
181
|
+
// Section properties
|
|
182
|
+
sectionProperties: {}
|
|
183
|
+
};
|
|
184
|
+
// Parse text properties
|
|
185
|
+
const textProperties = styleNode.query("style:text-properties");
|
|
186
|
+
if (textProperties) {
|
|
187
|
+
properties.textProperties = {
|
|
188
|
+
bold: attr(textProperties, "fo:font-weight") === "bold",
|
|
189
|
+
italic: attr(textProperties, "fo:font-style") === "italic",
|
|
190
|
+
fontSize: this.convertLength(attr(textProperties, "fo:font-size")),
|
|
191
|
+
fontFamily: attr(textProperties, "fo:font-family"),
|
|
192
|
+
color: attr(textProperties, "fo:color"),
|
|
193
|
+
backgroundColor: attr(textProperties, "fo:background-color"),
|
|
194
|
+
textDecoration: attr(textProperties, "style:text-underline-style") ||
|
|
195
|
+
attr(textProperties, "style:text-line-through-style"),
|
|
196
|
+
textPosition: attr(textProperties, "style:text-position")
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
// Parse paragraph properties
|
|
200
|
+
const paragraphProperties = styleNode.query("style:paragraph-properties");
|
|
201
|
+
if (paragraphProperties) {
|
|
202
|
+
properties.paragraphProperties = {
|
|
203
|
+
marginTop: this.convertLength(attr(paragraphProperties, "fo:margin-top")),
|
|
204
|
+
marginBottom: this.convertLength(attr(paragraphProperties, "fo:margin-bottom")),
|
|
205
|
+
marginLeft: this.convertLength(attr(paragraphProperties, "fo:margin-left")),
|
|
206
|
+
marginRight: this.convertLength(attr(paragraphProperties, "fo:margin-right")),
|
|
207
|
+
textAlign: attr(paragraphProperties, "fo:text-align"),
|
|
208
|
+
lineHeight: attr(paragraphProperties, "fo:line-height"),
|
|
209
|
+
backgroundColor: attr(paragraphProperties, "fo:background-color"),
|
|
210
|
+
padding: this.convertLength(attr(paragraphProperties, "fo:padding")),
|
|
211
|
+
borderStyle: attr(paragraphProperties, "fo:border-style")
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
// Parse section properties
|
|
215
|
+
const sectionProperties = styleNode.query("style:section-properties");
|
|
216
|
+
if (sectionProperties) {
|
|
217
|
+
properties.sectionProperties = {
|
|
218
|
+
columnCount: attr(sectionProperties, "fo:column-count"),
|
|
219
|
+
columnGap: this.convertLength(attr(sectionProperties, "fo:column-gap")),
|
|
220
|
+
backgroundColor: attr(sectionProperties, "fo:background-color"),
|
|
221
|
+
margins: {
|
|
222
|
+
top: this.convertLength(attr(sectionProperties, "fo:margin-top")),
|
|
223
|
+
bottom: this.convertLength(attr(sectionProperties, "fo:margin-bottom")),
|
|
224
|
+
left: this.convertLength(attr(sectionProperties, "fo:margin-left")),
|
|
225
|
+
right: this.convertLength(attr(sectionProperties, "fo:margin-right"))
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
// Additional table-specific properties
|
|
230
|
+
if (attr(styleNode, "style:family") === "table") {
|
|
231
|
+
properties.tableProperties = {
|
|
232
|
+
align: attr(styleNode, "table:align"),
|
|
233
|
+
width: this.convertLength(attr(styleNode, "style:width")),
|
|
234
|
+
relWidth: attr(styleNode, "style:rel-width")
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
return properties;
|
|
238
|
+
}
|
|
239
|
+
convertObject(node, attrs) {
|
|
240
|
+
const mathEl = node.query("math");
|
|
241
|
+
if (mathEl) {
|
|
242
|
+
attrs = Object.assign({
|
|
243
|
+
equation: MathMLToLaTeX.convert(mathEl.innerXML)
|
|
244
|
+
}, attrs);
|
|
245
|
+
return {
|
|
246
|
+
type: "equation",
|
|
247
|
+
attrs
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
parseComments() {
|
|
253
|
+
if (!this.contentDoc) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
const annotations = this.contentDoc.queryAll("office:annotation");
|
|
257
|
+
annotations.forEach((annotation) => {
|
|
258
|
+
const username = annotation.query("dc:creator")?.textContent || "";
|
|
259
|
+
const date = new Date(annotation.query("dc:date")?.textContent || "").getTime();
|
|
260
|
+
const id = (attr(annotation, "office:name") || "")
|
|
261
|
+
.replace(/\D/g, "")
|
|
262
|
+
.slice(0, 9);
|
|
263
|
+
if (id) {
|
|
264
|
+
// main comment
|
|
265
|
+
this.comments[id] = {
|
|
266
|
+
user: 0,
|
|
267
|
+
username,
|
|
268
|
+
date,
|
|
269
|
+
comment: annotation
|
|
270
|
+
.queryAll("text:p")
|
|
271
|
+
.map((par) => this.convertBlockNode(par))
|
|
272
|
+
.filter((par) => par)
|
|
273
|
+
.flat(),
|
|
274
|
+
answers: [],
|
|
275
|
+
resolved: attr(annotation, "loext:resolved") === "true"
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
const parentId = (attr(annotation, "loext:parent-name") || "")
|
|
280
|
+
.replace(/\D/g, "")
|
|
281
|
+
.slice(0, 9);
|
|
282
|
+
if (parentId && this.comments[parentId]) {
|
|
283
|
+
this.comments[parentId].answers.push({
|
|
284
|
+
id: randomCommentId(),
|
|
285
|
+
user: 0,
|
|
286
|
+
username,
|
|
287
|
+
date,
|
|
288
|
+
// drop the frist paragraph. It only contains "Reply to...."
|
|
289
|
+
answer: annotation
|
|
290
|
+
.queryAll("text:p")
|
|
291
|
+
.slice(1)
|
|
292
|
+
.map((par) => this.convertBlockNode(par))
|
|
293
|
+
.filter((par) => par)
|
|
294
|
+
.flat()
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
collectReferenceableObjects(node) {
|
|
301
|
+
// Handle heading bookmarks
|
|
302
|
+
const bookmarkStarts = node.queryAll("text:bookmark-start");
|
|
303
|
+
bookmarkStarts.forEach((mark) => {
|
|
304
|
+
const refName = attr(mark, "text:name");
|
|
305
|
+
if (!refName) {
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
// Find the closest heading
|
|
309
|
+
let targetParent = mark.parentElement;
|
|
310
|
+
while (targetParent) {
|
|
311
|
+
if (targetParent.tagName === "text:h") {
|
|
312
|
+
const id = randomHeadingId();
|
|
313
|
+
this.referenceableObjects[refName] = {
|
|
314
|
+
type: "heading",
|
|
315
|
+
id,
|
|
316
|
+
node: targetParent
|
|
317
|
+
};
|
|
318
|
+
break;
|
|
319
|
+
}
|
|
320
|
+
targetParent = targetParent.parentElement;
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
// Handle figure sequences
|
|
324
|
+
const sequences = node.queryAll("text:sequence");
|
|
325
|
+
sequences.forEach((sequence) => {
|
|
326
|
+
const refName = attr(sequence, "text:ref-name");
|
|
327
|
+
if (!refName) {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
// Find the figure container
|
|
331
|
+
let targetParent = sequence.parentElement;
|
|
332
|
+
while (targetParent) {
|
|
333
|
+
if (targetParent.tagName === "draw:frame") {
|
|
334
|
+
const id = randomFigureId();
|
|
335
|
+
this.referenceableObjects[refName] = {
|
|
336
|
+
type: "figure",
|
|
337
|
+
id,
|
|
338
|
+
node: targetParent
|
|
339
|
+
};
|
|
340
|
+
break;
|
|
341
|
+
}
|
|
342
|
+
targetParent = targetParent.parentElement;
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
convert() {
|
|
347
|
+
const templateParts = this.template.content.content.slice();
|
|
348
|
+
templateParts.shift();
|
|
349
|
+
const document = {
|
|
350
|
+
type: "doc",
|
|
351
|
+
attrs: {
|
|
352
|
+
import_id: this.importId
|
|
353
|
+
},
|
|
354
|
+
content: []
|
|
355
|
+
};
|
|
356
|
+
// Add title (required first element)
|
|
357
|
+
const title = this.extractTitle();
|
|
358
|
+
if (title.content.length) {
|
|
359
|
+
document.content.push({
|
|
360
|
+
type: "title",
|
|
361
|
+
content: title.content
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
// If no title found, use default title
|
|
366
|
+
document.content.push({
|
|
367
|
+
type: "title",
|
|
368
|
+
content: [
|
|
369
|
+
{
|
|
370
|
+
type: "text",
|
|
371
|
+
text: gettext("Untitled")
|
|
372
|
+
}
|
|
373
|
+
]
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
title.containerNodes.forEach((node) => {
|
|
377
|
+
node.parentElement.removeChild(node);
|
|
378
|
+
});
|
|
379
|
+
document.attrs.title =
|
|
380
|
+
title.content.map((node) => node.textContent).join("") ||
|
|
381
|
+
gettext("Untitled");
|
|
382
|
+
// Get all content sections from the ODT
|
|
383
|
+
const body = this.contentDoc.query("office:text");
|
|
384
|
+
if (!body) {
|
|
385
|
+
return document;
|
|
386
|
+
}
|
|
387
|
+
// Look for metadata sections first (author, abstract, etc.)
|
|
388
|
+
const metadataContent = this.extractMetadata();
|
|
389
|
+
metadataContent.forEach(({ type, attrs, content }) => {
|
|
390
|
+
const templatePart = templateParts.find((part) => part.attrs.metadata === type);
|
|
391
|
+
if (templatePart) {
|
|
392
|
+
document.content.push({
|
|
393
|
+
type: templatePart.type,
|
|
394
|
+
attrs: {
|
|
395
|
+
...templatePart.attrs,
|
|
396
|
+
...attrs
|
|
397
|
+
},
|
|
398
|
+
content: content.content
|
|
399
|
+
});
|
|
400
|
+
// Remove paragraphs from content so they are not added to body
|
|
401
|
+
content.containerNodes.forEach((node) => {
|
|
402
|
+
node.parentElement.removeChild(node);
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
// Group remaining content by sections based on style names/titles
|
|
407
|
+
const sections = this.groupContentIntoSections(body);
|
|
408
|
+
// Map ODT sections to template parts
|
|
409
|
+
sections.forEach((section) => {
|
|
410
|
+
// Find matching template part
|
|
411
|
+
const templatePart = this.findMatchingTemplatePart(section.title, templateParts);
|
|
412
|
+
if (templatePart) {
|
|
413
|
+
// If template part found, use its configuration
|
|
414
|
+
document.content.push({
|
|
415
|
+
type: "richtext_part",
|
|
416
|
+
attrs: {
|
|
417
|
+
title: templatePart.attrs.title,
|
|
418
|
+
id: templatePart.attrs.id,
|
|
419
|
+
metadata: templatePart.attrs.metadata || undefined,
|
|
420
|
+
marks: templatePart.attrs.marks || [
|
|
421
|
+
"strong",
|
|
422
|
+
"em",
|
|
423
|
+
"link"
|
|
424
|
+
]
|
|
425
|
+
},
|
|
426
|
+
content: section.content
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
// Add remaining content to body section
|
|
431
|
+
const unassignedContent = sections
|
|
432
|
+
.filter((section) => !this.findMatchingTemplatePart(section.title, templateParts))
|
|
433
|
+
.flatMap((section) => section.content);
|
|
434
|
+
if (unassignedContent.length) {
|
|
435
|
+
// Find default body template part
|
|
436
|
+
const bodyTemplatePart = templateParts.find((part) => !part.attrs.metadata && part.type === "richtext_part");
|
|
437
|
+
document.content.push({
|
|
438
|
+
type: "richtext_part",
|
|
439
|
+
attrs: {
|
|
440
|
+
title: bodyTemplatePart?.attrs?.title || "Body",
|
|
441
|
+
id: bodyTemplatePart?.attrs?.id || "body",
|
|
442
|
+
marks: ["strong", "em", "link"]
|
|
443
|
+
},
|
|
444
|
+
content: unassignedContent
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
return document;
|
|
448
|
+
}
|
|
449
|
+
extractMetadata() {
|
|
450
|
+
const metadata = [];
|
|
451
|
+
// Try structured contributor data from meta.xml first
|
|
452
|
+
const contributorsByRole = this.extractContributorsFromMeta();
|
|
453
|
+
if (Object.keys(contributorsByRole).length) {
|
|
454
|
+
Object.entries(contributorsByRole).forEach(([role, contributors]) => {
|
|
455
|
+
metadata.push({
|
|
456
|
+
type: role,
|
|
457
|
+
content: { content: contributors, containerNodes: [] }
|
|
458
|
+
});
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
else {
|
|
462
|
+
// Fall back to legacy author extraction
|
|
463
|
+
const authors = this.extractAuthors();
|
|
464
|
+
if (authors.content.length) {
|
|
465
|
+
metadata.push({
|
|
466
|
+
type: "authors",
|
|
467
|
+
content: authors
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
// Extract abstract if present
|
|
472
|
+
const abstract = this.extractAbstract();
|
|
473
|
+
if (abstract.content.length) {
|
|
474
|
+
metadata.push({
|
|
475
|
+
type: "abstract",
|
|
476
|
+
content: abstract
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
// Extract keywords if present
|
|
480
|
+
const keywords = this.extractKeywords();
|
|
481
|
+
if (keywords.content.length) {
|
|
482
|
+
metadata.push({
|
|
483
|
+
type: "keywords",
|
|
484
|
+
content: keywords
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
return metadata;
|
|
488
|
+
}
|
|
489
|
+
extractContributorsFromMeta() {
|
|
490
|
+
if (!this.metaDoc) {
|
|
491
|
+
return {};
|
|
492
|
+
}
|
|
493
|
+
const userDefined = this.metaDoc.queryAll("meta:user-defined");
|
|
494
|
+
const contributors = [];
|
|
495
|
+
userDefined.forEach((prop) => {
|
|
496
|
+
const name = attr(prop, "meta:name");
|
|
497
|
+
if (!name || !name.startsWith("fidus_contributor_")) {
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
const match = name.match(/^fidus_contributor_(\d+)_(\w+)$/);
|
|
501
|
+
if (!match) {
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
const num = parseInt(match[1]);
|
|
505
|
+
const field = match[2];
|
|
506
|
+
const value = prop.textContent || "";
|
|
507
|
+
if (!contributors[num - 1]) {
|
|
508
|
+
contributors[num - 1] = {
|
|
509
|
+
type: "contributor",
|
|
510
|
+
attrs: {
|
|
511
|
+
firstname: "",
|
|
512
|
+
lastname: "",
|
|
513
|
+
email: "",
|
|
514
|
+
institution: "",
|
|
515
|
+
id_type: "",
|
|
516
|
+
id_value: "",
|
|
517
|
+
role: ""
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
if (field === "role") {
|
|
522
|
+
contributors[num - 1].attrs.role = value;
|
|
523
|
+
}
|
|
524
|
+
else if ([
|
|
525
|
+
"firstname",
|
|
526
|
+
"lastname",
|
|
527
|
+
"email",
|
|
528
|
+
"institution",
|
|
529
|
+
"id_type",
|
|
530
|
+
"id_value"
|
|
531
|
+
].includes(field)) {
|
|
532
|
+
contributors[num - 1].attrs[field] = value;
|
|
533
|
+
}
|
|
534
|
+
});
|
|
535
|
+
const byRole = {};
|
|
536
|
+
contributors.forEach((contributor) => {
|
|
537
|
+
if (!contributor) {
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
const role = contributor.attrs.role || "authors";
|
|
541
|
+
if (!byRole[role]) {
|
|
542
|
+
byRole[role] = [];
|
|
543
|
+
}
|
|
544
|
+
byRole[role].push(contributor);
|
|
545
|
+
});
|
|
546
|
+
return byRole;
|
|
547
|
+
}
|
|
548
|
+
extractAuthors() {
|
|
549
|
+
const authors = [];
|
|
550
|
+
// Try to find author information in metadata
|
|
551
|
+
const metaAuthors = this.contentDoc.queryAll("meta:user-defined", {
|
|
552
|
+
"meta:name": "author"
|
|
553
|
+
});
|
|
554
|
+
metaAuthors.forEach((authorMeta) => {
|
|
555
|
+
const authorText = authorMeta.textContent;
|
|
556
|
+
const [firstname = "", lastname = ""] = authorText.split(" ", 2);
|
|
557
|
+
authors.push({
|
|
558
|
+
type: "contributor",
|
|
559
|
+
attrs: {
|
|
560
|
+
firstname,
|
|
561
|
+
lastname,
|
|
562
|
+
email: "",
|
|
563
|
+
institution: ""
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
});
|
|
567
|
+
if (authors.length) {
|
|
568
|
+
return {
|
|
569
|
+
content: authors,
|
|
570
|
+
containerNodes: metaAuthors
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
// Also check for creator in document metadata
|
|
574
|
+
const creator = this.contentDoc.query("meta:creator");
|
|
575
|
+
if (creator) {
|
|
576
|
+
const [firstname = "", lastname = ""] = creator.textContent.split(" ", 2);
|
|
577
|
+
return {
|
|
578
|
+
content: [
|
|
579
|
+
{
|
|
580
|
+
type: "contributor",
|
|
581
|
+
attrs: {
|
|
582
|
+
firstname,
|
|
583
|
+
lastname,
|
|
584
|
+
email: "",
|
|
585
|
+
institution: ""
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
],
|
|
589
|
+
containerNodes: []
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
return { content: [], containerNodes: [] };
|
|
593
|
+
}
|
|
594
|
+
extractAbstract() {
|
|
595
|
+
// Look for section titled "Abstract" or with abstract style
|
|
596
|
+
const abstractSection = this.contentDoc.query("text:section", {
|
|
597
|
+
"text:style-name": "Abstract"
|
|
598
|
+
}) ||
|
|
599
|
+
this.contentDoc.query("text:h", {
|
|
600
|
+
"text:outline-level": "1"
|
|
601
|
+
}); // Then check content for "Abstract"
|
|
602
|
+
if (abstractSection &&
|
|
603
|
+
(attr(abstractSection, "text:style-name") === "Abstract" ||
|
|
604
|
+
abstractSection.textContent.includes("Abstract"))) {
|
|
605
|
+
return {
|
|
606
|
+
content: this.convertContainer(abstractSection),
|
|
607
|
+
containerNodes: [abstractSection]
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
return {
|
|
611
|
+
content: [],
|
|
612
|
+
containerNodes: []
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
extractKeywords() {
|
|
616
|
+
// Look for keywords section or metadata
|
|
617
|
+
const keywordsSection = this.contentDoc.query("text:p", { "text:style-name": "Keywords" }) ||
|
|
618
|
+
this.contentDoc.query("meta:user-defined", {
|
|
619
|
+
"meta:name": "keywords"
|
|
620
|
+
});
|
|
621
|
+
if (keywordsSection) {
|
|
622
|
+
return {
|
|
623
|
+
content: this.convertContainer(keywordsSection),
|
|
624
|
+
containerNodes: [keywordsSection]
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
return { content: [], containerNodes: [] };
|
|
628
|
+
}
|
|
629
|
+
findMatchingTemplatePart(sectionTitle, templateParts) {
|
|
630
|
+
if (!sectionTitle) {
|
|
631
|
+
return null;
|
|
632
|
+
}
|
|
633
|
+
// Try exact match first
|
|
634
|
+
let matchingPart = templateParts.find((part) => part.type === "richtext_part" &&
|
|
635
|
+
!part.attrs.metadata &&
|
|
636
|
+
part.attrs.title.toLowerCase() === sectionTitle.toLowerCase());
|
|
637
|
+
if (!matchingPart) {
|
|
638
|
+
// Try fuzzy matching if exact match fails
|
|
639
|
+
matchingPart = templateParts.find((part) => part.type === "richtext_part" &&
|
|
640
|
+
!part.attrs.metadata &&
|
|
641
|
+
this.isSimilarTitle(part.attrs.title, sectionTitle));
|
|
642
|
+
}
|
|
643
|
+
return matchingPart;
|
|
644
|
+
}
|
|
645
|
+
isSimilarTitle(title1, title2) {
|
|
646
|
+
// Remove special characters and extra spaces
|
|
647
|
+
const normalize = (str) => str
|
|
648
|
+
.toLowerCase()
|
|
649
|
+
.replace(/[^a-z0-9]/g, "")
|
|
650
|
+
.trim();
|
|
651
|
+
const normalized1 = normalize(title1);
|
|
652
|
+
const normalized2 = normalize(title2);
|
|
653
|
+
// Check if one string contains the other
|
|
654
|
+
return (normalized1.includes(normalized2) ||
|
|
655
|
+
normalized2.includes(normalized1));
|
|
656
|
+
}
|
|
657
|
+
extractTitle() {
|
|
658
|
+
// First try to find paragraph with Title style
|
|
659
|
+
const titleParagraph = this.contentDoc.query("text:p", {
|
|
660
|
+
"text:style-name": "Title"
|
|
661
|
+
});
|
|
662
|
+
if (titleParagraph) {
|
|
663
|
+
return {
|
|
664
|
+
content: this.convertBlockNode(titleParagraph)?.content || [],
|
|
665
|
+
containerNodes: [titleParagraph]
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
// Fall back to first heading
|
|
669
|
+
const titleHeading = this.contentDoc.query("text:h", {
|
|
670
|
+
"text:outline-level": "1"
|
|
671
|
+
});
|
|
672
|
+
if (titleHeading) {
|
|
673
|
+
return {
|
|
674
|
+
content: this.convertBlockNode(titleHeading)?.content || [],
|
|
675
|
+
containerNodes: [titleHeading]
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
// Check for other common title style names
|
|
679
|
+
const commonTitleStyles = [
|
|
680
|
+
"title",
|
|
681
|
+
"doctitle",
|
|
682
|
+
"document-title",
|
|
683
|
+
"heading-title"
|
|
684
|
+
];
|
|
685
|
+
for (const styleName of commonTitleStyles) {
|
|
686
|
+
const titleElement = this.contentDoc.query("text:p", {
|
|
687
|
+
"text:style-name": styleName
|
|
688
|
+
});
|
|
689
|
+
if (titleElement) {
|
|
690
|
+
return {
|
|
691
|
+
content: this.convertBlockNode(titleElement)?.content || [],
|
|
692
|
+
containerNodes: [titleElement]
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
// Check style properties for title-like formatting
|
|
697
|
+
const firstParagraph = this.contentDoc.query("text:p");
|
|
698
|
+
if (firstParagraph) {
|
|
699
|
+
const styleName = attr(firstParagraph, "text:style-name");
|
|
700
|
+
const style = this.styles[styleName];
|
|
701
|
+
if (style && this.isTitleStyle(style)) {
|
|
702
|
+
// Remove this node from the document so it's not processed again
|
|
703
|
+
return {
|
|
704
|
+
content: this.convertBlockNode(firstParagraph)?.content || [],
|
|
705
|
+
containerNodes: [firstParagraph]
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
return {
|
|
710
|
+
content: [],
|
|
711
|
+
containerNodes: []
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
isTitleStyle(style) {
|
|
715
|
+
// Check if style or its parent has characteristics of a title style
|
|
716
|
+
if (!style) {
|
|
717
|
+
return false;
|
|
718
|
+
}
|
|
719
|
+
// Check style name
|
|
720
|
+
if (style.title?.toLowerCase().includes("title")) {
|
|
721
|
+
return true;
|
|
722
|
+
}
|
|
723
|
+
// Check text properties for title-like formatting
|
|
724
|
+
const textProps = style.textProperties;
|
|
725
|
+
if (textProps) {
|
|
726
|
+
// Title usually has larger font size and/or bold weight
|
|
727
|
+
if (textProps.fontSize > 14 || textProps.bold) {
|
|
728
|
+
return true;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
// Check paragraph properties
|
|
732
|
+
const paraProps = style.paragraphProperties;
|
|
733
|
+
if (paraProps) {
|
|
734
|
+
// Titles are often centered and have larger margins
|
|
735
|
+
if (paraProps.textAlign === "center" ||
|
|
736
|
+
(paraProps.marginTop > 0.5 && paraProps.marginBottom > 0.5)) {
|
|
737
|
+
return true;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
// Check parent style if exists
|
|
741
|
+
if (style.parentStyleName) {
|
|
742
|
+
const parentStyle = this.styles[style.parentStyleName];
|
|
743
|
+
return this.isTitleStyle(parentStyle);
|
|
744
|
+
}
|
|
745
|
+
return false;
|
|
746
|
+
}
|
|
747
|
+
getSectionTitle(node, styleName) {
|
|
748
|
+
if (!node || !styleName) {
|
|
749
|
+
return null;
|
|
750
|
+
}
|
|
751
|
+
// For headings, use the text content as section title
|
|
752
|
+
if (node.tagName === "text:h") {
|
|
753
|
+
// Get the heading level
|
|
754
|
+
const level = parseInt(attr(node, "text:outline-level")) || 1;
|
|
755
|
+
// Only use level 1 and 2 headings as section titles
|
|
756
|
+
if (level <= 2) {
|
|
757
|
+
return node.textContent.trim();
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
// Check if the style indicates a section title
|
|
761
|
+
const style = this.styles[styleName];
|
|
762
|
+
if (style) {
|
|
763
|
+
// Check for explicit section title style
|
|
764
|
+
if (style.title ||
|
|
765
|
+
styleName.toLowerCase().includes("section") ||
|
|
766
|
+
styleName.toLowerCase().includes("title")) {
|
|
767
|
+
// If it's a styled paragraph, use its content as title
|
|
768
|
+
if (node.tagName === "text:p") {
|
|
769
|
+
return node.textContent.trim();
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
// Check if it's a custom section style
|
|
773
|
+
const parentStyle = style.parentStyleName
|
|
774
|
+
? this.styles[style.parentStyleName]
|
|
775
|
+
: null;
|
|
776
|
+
if (parentStyle?.isSection) {
|
|
777
|
+
return node.textContent.trim();
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
// For text:section elements, check for section-name attribute
|
|
781
|
+
if (node.tagName === "text:section") {
|
|
782
|
+
const sectionName = attr(node, "text:name");
|
|
783
|
+
if (sectionName) {
|
|
784
|
+
return this.formatSectionName(sectionName);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
return null;
|
|
788
|
+
}
|
|
789
|
+
formatSectionName(name) {
|
|
790
|
+
// Remove common suffixes
|
|
791
|
+
name = name.replace(/_?(section|part|chapter)$/i, "");
|
|
792
|
+
// Split by underscores or hyphens
|
|
793
|
+
const words = name.split(/[_-]/);
|
|
794
|
+
// Capitalize first letter of each word and join
|
|
795
|
+
return words
|
|
796
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
797
|
+
.join(" ")
|
|
798
|
+
.trim();
|
|
799
|
+
}
|
|
800
|
+
groupContentIntoSections(body) {
|
|
801
|
+
const sections = [];
|
|
802
|
+
let currentSection = {
|
|
803
|
+
title: null,
|
|
804
|
+
content: []
|
|
805
|
+
};
|
|
806
|
+
body.children.forEach((node) => {
|
|
807
|
+
const styleName = attr(node, "text:style-name");
|
|
808
|
+
const title = this.getSectionTitle(node, styleName);
|
|
809
|
+
if (title && this.isHeadingStyle(styleName)) {
|
|
810
|
+
// Start new section
|
|
811
|
+
if (currentSection.content.length) {
|
|
812
|
+
sections.push(currentSection);
|
|
813
|
+
}
|
|
814
|
+
currentSection = {
|
|
815
|
+
title: title,
|
|
816
|
+
content: []
|
|
817
|
+
};
|
|
818
|
+
}
|
|
819
|
+
const converted = [this.convertBlockNode(node)]
|
|
820
|
+
.filter((node) => node)
|
|
821
|
+
.flat();
|
|
822
|
+
converted.forEach((node) => currentSection.content.push(node));
|
|
823
|
+
});
|
|
824
|
+
// Add final section
|
|
825
|
+
if (currentSection.content.length) {
|
|
826
|
+
sections.push(currentSection);
|
|
827
|
+
}
|
|
828
|
+
return sections;
|
|
829
|
+
}
|
|
830
|
+
isCodeBlockStyle(styleName, style) {
|
|
831
|
+
if (!styleName) {
|
|
832
|
+
return false;
|
|
833
|
+
}
|
|
834
|
+
// Check if style name contains preformatted or code indicators
|
|
835
|
+
const lowerStyleName = styleName.toLowerCase();
|
|
836
|
+
if (lowerStyleName.includes("preformatted") ||
|
|
837
|
+
lowerStyleName.includes("code") ||
|
|
838
|
+
styleName === "Preformatted_20_Text") {
|
|
839
|
+
return true;
|
|
840
|
+
}
|
|
841
|
+
// Check if parent style is a code block style
|
|
842
|
+
if (style?.parentStyleName) {
|
|
843
|
+
const parentStyle = this.styles[style.parentStyleName];
|
|
844
|
+
return this.isCodeBlockStyle(style.parentStyleName, parentStyle);
|
|
845
|
+
}
|
|
846
|
+
// Check text properties for monospace fonts
|
|
847
|
+
if (style?.textProperties?.fontFamily) {
|
|
848
|
+
const fontFamily = style.textProperties.fontFamily.toLowerCase();
|
|
849
|
+
const monospacePatterns = [
|
|
850
|
+
"courier",
|
|
851
|
+
"consolas",
|
|
852
|
+
"monaco",
|
|
853
|
+
"menlo",
|
|
854
|
+
"lucida console",
|
|
855
|
+
"liberation mono",
|
|
856
|
+
"dejavu sans mono",
|
|
857
|
+
"bitstream vera sans mono",
|
|
858
|
+
"source code pro",
|
|
859
|
+
"fira code"
|
|
860
|
+
];
|
|
861
|
+
return monospacePatterns.some((pattern) => fontFamily.includes(pattern));
|
|
862
|
+
}
|
|
863
|
+
return false;
|
|
864
|
+
}
|
|
865
|
+
isHeadingStyle(styleName) {
|
|
866
|
+
if (!styleName) {
|
|
867
|
+
return false;
|
|
868
|
+
}
|
|
869
|
+
const style = this.styles[styleName];
|
|
870
|
+
if (!style) {
|
|
871
|
+
return false;
|
|
872
|
+
}
|
|
873
|
+
// Check multiple indicators that this might be a heading style
|
|
874
|
+
return (
|
|
875
|
+
// Direct heading indicators
|
|
876
|
+
style.isHeading ||
|
|
877
|
+
styleName.toLowerCase().includes("heading") ||
|
|
878
|
+
styleName.toLowerCase().includes("title") ||
|
|
879
|
+
// Check outline level property
|
|
880
|
+
Boolean(style.outlineLevel) ||
|
|
881
|
+
// Check if it's derived from a heading style
|
|
882
|
+
(style.parentStyleName &&
|
|
883
|
+
this.isHeadingStyle(style.parentStyleName)) ||
|
|
884
|
+
// Check specific formatting that's typical for headings
|
|
885
|
+
(style.paragraphProperties &&
|
|
886
|
+
// Larger margins than normal paragraphs
|
|
887
|
+
(style.paragraphProperties.marginTop > 0.3 ||
|
|
888
|
+
style.paragraphProperties.marginBottom > 0.3 ||
|
|
889
|
+
// Different alignment
|
|
890
|
+
style.paragraphProperties.textAlign === "center")) ||
|
|
891
|
+
// Check text properties typical for headings
|
|
892
|
+
(style.textProperties &&
|
|
893
|
+
// Larger font size
|
|
894
|
+
(style.textProperties.fontSize > 12 ||
|
|
895
|
+
// Bold text
|
|
896
|
+
style.textProperties.bold ||
|
|
897
|
+
// Different font family
|
|
898
|
+
style.textProperties.fontFamily)));
|
|
899
|
+
}
|
|
900
|
+
convertContainer(container) {
|
|
901
|
+
return container.children
|
|
902
|
+
.map((node) => this.convertBlockNode(node))
|
|
903
|
+
.filter((node) => node)
|
|
904
|
+
.flat();
|
|
905
|
+
}
|
|
906
|
+
convertBlockNode(node) {
|
|
907
|
+
const track = this.currentTracks.map((track) => ({
|
|
908
|
+
type: track.type,
|
|
909
|
+
user: track.attrs.user,
|
|
910
|
+
username: track.attrs.username,
|
|
911
|
+
date: track.attrs.date
|
|
912
|
+
}));
|
|
913
|
+
const attrs = track.length ? { track } : {};
|
|
914
|
+
switch (node.tagName) {
|
|
915
|
+
case "text:p":
|
|
916
|
+
if (node.children.length === 1 &&
|
|
917
|
+
node.children[0].tagName === "draw:frame") {
|
|
918
|
+
// Paragraph consists of only one figure/image.
|
|
919
|
+
return this.convertImage(node.children[0], attrs);
|
|
920
|
+
}
|
|
921
|
+
return this.convertParagraph(node, attrs);
|
|
922
|
+
case "text:h":
|
|
923
|
+
return this.convertHeading(node, attrs);
|
|
924
|
+
case "text:list":
|
|
925
|
+
return this.convertList(node, attrs);
|
|
926
|
+
case "draw:frame":
|
|
927
|
+
return this.convertImage(node, attrs);
|
|
928
|
+
case "draw:object":
|
|
929
|
+
return this.convertObject(node, attrs);
|
|
930
|
+
case "table:table":
|
|
931
|
+
return this.convertTable(node, attrs);
|
|
932
|
+
case "text:sequence-decls":
|
|
933
|
+
case "office:forms":
|
|
934
|
+
case "text:tracked-changes":
|
|
935
|
+
return null;
|
|
936
|
+
case "text:bibliography":
|
|
937
|
+
// LibreOffice native bibliography — rendered output only,
|
|
938
|
+
// skip entirely in favour of Fidus Writer's own system.
|
|
939
|
+
return null;
|
|
940
|
+
case "text:section": {
|
|
941
|
+
// Skip bibliography sections inserted by citation managers
|
|
942
|
+
// (Zotero: name contains "ZOTERO_BIBL"/"CSL_BIBLIOGRAPHY",
|
|
943
|
+
// JabRef: name is "JR_bib" / "JR_BIB").
|
|
944
|
+
const sectionName = attr(node, "text:name") || "";
|
|
945
|
+
if (isOdtBibliographySection(sectionName)) {
|
|
946
|
+
return null;
|
|
947
|
+
}
|
|
948
|
+
// Other named sections are not bibliographies — fall through
|
|
949
|
+
// to default handling (treat children as block content).
|
|
950
|
+
return this.convertContainer(node);
|
|
951
|
+
}
|
|
952
|
+
default:
|
|
953
|
+
console.warn(`Unsupported block node: ${node.tagName}`);
|
|
954
|
+
return null;
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
convertParagraph(node, attrs = {}) {
|
|
958
|
+
const styleName = attr(node, "text:style-name");
|
|
959
|
+
const style = this.styles[styleName];
|
|
960
|
+
// Check if this is a code block (preformatted text)
|
|
961
|
+
if (this.isCodeBlockStyle(styleName, style)) {
|
|
962
|
+
attrs = Object.assign({
|
|
963
|
+
track: [],
|
|
964
|
+
language: "",
|
|
965
|
+
category: "",
|
|
966
|
+
title: "",
|
|
967
|
+
id: ""
|
|
968
|
+
}, attrs);
|
|
969
|
+
return {
|
|
970
|
+
type: "code_block",
|
|
971
|
+
attrs,
|
|
972
|
+
content: this.convertNodeChildren(node)
|
|
973
|
+
};
|
|
974
|
+
}
|
|
975
|
+
// Check if this paragraph is title-like
|
|
976
|
+
if (this.isTitleStyle(style)) {
|
|
977
|
+
attrs = Object.assign({
|
|
978
|
+
id: randomHeadingId()
|
|
979
|
+
}, attrs);
|
|
980
|
+
return {
|
|
981
|
+
type: "heading1",
|
|
982
|
+
attrs,
|
|
983
|
+
content: this.convertNodeChildren(node)
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
if (this.isHeadingStyle(styleName)) {
|
|
987
|
+
return this.convertHeading(node, attrs);
|
|
988
|
+
}
|
|
989
|
+
return {
|
|
990
|
+
type: "paragraph",
|
|
991
|
+
attrs,
|
|
992
|
+
content: this.convertNodeChildren(node)
|
|
993
|
+
};
|
|
994
|
+
}
|
|
995
|
+
convertHeading(node, attrs = {}) {
|
|
996
|
+
const level = parseInt(attr(node, "text:outline-level") || "1") || 1;
|
|
997
|
+
// Check for bookmark
|
|
998
|
+
let id = null;
|
|
999
|
+
const bookmarkStart = node.query("text:bookmark-start");
|
|
1000
|
+
if (bookmarkStart) {
|
|
1001
|
+
const refName = attr(bookmarkStart, "text:name");
|
|
1002
|
+
if (refName && this.referenceableObjects[refName]) {
|
|
1003
|
+
id = this.referenceableObjects[refName].id;
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
attrs = Object.assign({
|
|
1007
|
+
id: id || randomHeadingId()
|
|
1008
|
+
}, attrs);
|
|
1009
|
+
return {
|
|
1010
|
+
type: `heading${level}`,
|
|
1011
|
+
attrs,
|
|
1012
|
+
content: this.convertNodeChildren(node)
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
convertNodeChildren(node, currentStyleMarks = []) {
|
|
1016
|
+
let insideCitationReferenceMark = false;
|
|
1017
|
+
let insideBibliographyReferenceMark = false;
|
|
1018
|
+
return node.children
|
|
1019
|
+
.map((child) => {
|
|
1020
|
+
if (insideBibliographyReferenceMark) {
|
|
1021
|
+
// Swallow all rendered bibliography content until the
|
|
1022
|
+
// closing mark — we have our own bibliography system.
|
|
1023
|
+
if (child.tagName === "text:reference-mark-end") {
|
|
1024
|
+
const name = attr(child, "text:name");
|
|
1025
|
+
if (name && isOdtBibliographyReferenceMark(name)) {
|
|
1026
|
+
insideBibliographyReferenceMark = false;
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
return null;
|
|
1030
|
+
}
|
|
1031
|
+
if (insideCitationReferenceMark) {
|
|
1032
|
+
if (child.tagName === "text:reference-mark-end") {
|
|
1033
|
+
// Process citation when we hit the end mark
|
|
1034
|
+
const name = attr(child, "text:name");
|
|
1035
|
+
if (name && isOdtCitationMark(name)) {
|
|
1036
|
+
insideCitationReferenceMark = false;
|
|
1037
|
+
return this.convertCitation(name, currentStyleMarks);
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
return null;
|
|
1041
|
+
}
|
|
1042
|
+
switch (child.tagName) {
|
|
1043
|
+
case "text:change-start": {
|
|
1044
|
+
const changeId = attr(child, "text:change-id");
|
|
1045
|
+
const track = this.tracks[changeId];
|
|
1046
|
+
if (track) {
|
|
1047
|
+
const trackMark = {
|
|
1048
|
+
type: track.type,
|
|
1049
|
+
attrs: {
|
|
1050
|
+
user: track.user,
|
|
1051
|
+
username: track.username,
|
|
1052
|
+
date: track.date
|
|
1053
|
+
}
|
|
1054
|
+
};
|
|
1055
|
+
if (track.type === "insertion") {
|
|
1056
|
+
trackMark.attrs.approved = track.approved;
|
|
1057
|
+
}
|
|
1058
|
+
this.currentTracks.push(trackMark);
|
|
1059
|
+
}
|
|
1060
|
+
return null;
|
|
1061
|
+
}
|
|
1062
|
+
case "text:change-end": {
|
|
1063
|
+
const changeId = attr(child, "text:change-id");
|
|
1064
|
+
const track = this.tracks[changeId];
|
|
1065
|
+
if (track) {
|
|
1066
|
+
this.currentTracks = this.currentTracks.filter((mark) => mark.type !== track.type);
|
|
1067
|
+
}
|
|
1068
|
+
return null;
|
|
1069
|
+
}
|
|
1070
|
+
case "#text":
|
|
1071
|
+
return this.convertText(String(child.textContent), currentStyleMarks);
|
|
1072
|
+
case "text:s": // space
|
|
1073
|
+
return this.convertText(" ", currentStyleMarks);
|
|
1074
|
+
case "text:span": {
|
|
1075
|
+
return this.convertSpan(child, currentStyleMarks);
|
|
1076
|
+
}
|
|
1077
|
+
case "text:a":
|
|
1078
|
+
return this.convertLink(child, currentStyleMarks);
|
|
1079
|
+
case "text:note":
|
|
1080
|
+
return this.convertFootnote(child, currentStyleMarks);
|
|
1081
|
+
case "office:annotation":
|
|
1082
|
+
return this.convertAnnotationStart(child);
|
|
1083
|
+
case "office:annotation-end":
|
|
1084
|
+
return this.convertAnnotationEnd(child);
|
|
1085
|
+
case "text:reference-mark-start": {
|
|
1086
|
+
const name = attr(child, "text:name");
|
|
1087
|
+
if (name && isOdtCitationMark(name)) {
|
|
1088
|
+
insideCitationReferenceMark = true;
|
|
1089
|
+
}
|
|
1090
|
+
else if (name &&
|
|
1091
|
+
isOdtBibliographyReferenceMark(name)) {
|
|
1092
|
+
insideBibliographyReferenceMark = true;
|
|
1093
|
+
}
|
|
1094
|
+
return null;
|
|
1095
|
+
}
|
|
1096
|
+
case "text:bibliography-mark":
|
|
1097
|
+
return this.convertBibliographyMark(child, currentStyleMarks);
|
|
1098
|
+
case "text:bookmark-ref":
|
|
1099
|
+
return this.convertHeadingReference(child);
|
|
1100
|
+
case "text:sequence-ref":
|
|
1101
|
+
return this.convertFigureReference(child);
|
|
1102
|
+
case "text:soft-page-break":
|
|
1103
|
+
return null;
|
|
1104
|
+
default:
|
|
1105
|
+
console.warn(`Unsupported inline node: ${child.tagName}`);
|
|
1106
|
+
}
|
|
1107
|
+
})
|
|
1108
|
+
.filter((node) => node)
|
|
1109
|
+
.flat();
|
|
1110
|
+
}
|
|
1111
|
+
getCurrentMarks(currentStyleMarks = []) {
|
|
1112
|
+
const commentMarks = [];
|
|
1113
|
+
// Add comment marks for any active comment IDs
|
|
1114
|
+
this.currentCommentIds.forEach((commentId) => {
|
|
1115
|
+
commentMarks.push({
|
|
1116
|
+
type: "comment",
|
|
1117
|
+
attrs: {
|
|
1118
|
+
id: commentId
|
|
1119
|
+
}
|
|
1120
|
+
});
|
|
1121
|
+
});
|
|
1122
|
+
return [...currentStyleMarks, ...this.currentTracks, ...commentMarks];
|
|
1123
|
+
}
|
|
1124
|
+
convertText(text, currentStyleMarks) {
|
|
1125
|
+
const textNode = {
|
|
1126
|
+
type: "text",
|
|
1127
|
+
text
|
|
1128
|
+
};
|
|
1129
|
+
const marks = this.getCurrentMarks(currentStyleMarks);
|
|
1130
|
+
if (marks.length) {
|
|
1131
|
+
textNode.marks = marks;
|
|
1132
|
+
}
|
|
1133
|
+
return textNode;
|
|
1134
|
+
}
|
|
1135
|
+
convertSpan(node, currentStyleMarks) {
|
|
1136
|
+
const styleName = attr(node, "text:style-name");
|
|
1137
|
+
const style = this.styles[styleName];
|
|
1138
|
+
if (style?.textProperties?.bold) {
|
|
1139
|
+
currentStyleMarks = [...currentStyleMarks, { type: "strong" }];
|
|
1140
|
+
}
|
|
1141
|
+
if (style?.textProperties?.italic) {
|
|
1142
|
+
currentStyleMarks = [...currentStyleMarks, { type: "em" }];
|
|
1143
|
+
}
|
|
1144
|
+
// Handle superscript and subscript
|
|
1145
|
+
if (style?.textProperties?.textPosition) {
|
|
1146
|
+
const position = style.textProperties.textPosition;
|
|
1147
|
+
if (position.includes("super")) {
|
|
1148
|
+
currentStyleMarks = [...currentStyleMarks, { type: "sup" }];
|
|
1149
|
+
}
|
|
1150
|
+
else if (position.includes("sub")) {
|
|
1151
|
+
currentStyleMarks = [...currentStyleMarks, { type: "sub" }];
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
// Handle inline code (monospace fonts)
|
|
1155
|
+
if (style?.textProperties?.fontFamily) {
|
|
1156
|
+
const fontFamily = style.textProperties.fontFamily.toLowerCase();
|
|
1157
|
+
const monospacePatterns = [
|
|
1158
|
+
"courier",
|
|
1159
|
+
"consolas",
|
|
1160
|
+
"monaco",
|
|
1161
|
+
"menlo",
|
|
1162
|
+
"lucida console",
|
|
1163
|
+
"liberation mono",
|
|
1164
|
+
"dejavu sans mono",
|
|
1165
|
+
"bitstream vera sans mono",
|
|
1166
|
+
"source code pro",
|
|
1167
|
+
"fira code",
|
|
1168
|
+
"ubuntu mono",
|
|
1169
|
+
"droid sans mono",
|
|
1170
|
+
"monospace"
|
|
1171
|
+
];
|
|
1172
|
+
const isMonospace = monospacePatterns.some((pattern) => fontFamily.includes(pattern));
|
|
1173
|
+
if (isMonospace) {
|
|
1174
|
+
currentStyleMarks = [...currentStyleMarks, { type: "code" }];
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
return this.convertNodeChildren(node, currentStyleMarks);
|
|
1178
|
+
}
|
|
1179
|
+
convertFootnote(node, currentStyleMarks) {
|
|
1180
|
+
const noteBody = node.query("text:note-body");
|
|
1181
|
+
if (!noteBody) {
|
|
1182
|
+
return null;
|
|
1183
|
+
}
|
|
1184
|
+
// Get the first paragraph in the footnote
|
|
1185
|
+
const firstParagraph = noteBody.query("text:p");
|
|
1186
|
+
if (!firstParagraph) {
|
|
1187
|
+
return null;
|
|
1188
|
+
}
|
|
1189
|
+
// Check if this is a citation-only footnote
|
|
1190
|
+
const referenceMarkStart = firstParagraph.query("text:reference-mark-start");
|
|
1191
|
+
const referenceMarkEnd = firstParagraph.query("text:reference-mark-end");
|
|
1192
|
+
const markName = attr(referenceMarkStart, "text:name");
|
|
1193
|
+
if (referenceMarkStart &&
|
|
1194
|
+
referenceMarkEnd &&
|
|
1195
|
+
markName &&
|
|
1196
|
+
isOdtCitationMark(markName) &&
|
|
1197
|
+
// Check that there's no content outside the reference marks
|
|
1198
|
+
firstParagraph.children.every((child) => child.tagName === "text:reference-mark-start" ||
|
|
1199
|
+
child.tagName === "text:reference-mark-end" ||
|
|
1200
|
+
(child.tagName === "text:span" &&
|
|
1201
|
+
child.previousSibling?.tagName ===
|
|
1202
|
+
"text:reference-mark-start" &&
|
|
1203
|
+
child.nextSibling?.tagName ===
|
|
1204
|
+
"text:reference-mark-end"))) {
|
|
1205
|
+
// If it's a citation-only footnote, convert it directly to a citation
|
|
1206
|
+
return this.convertCitation(markName, currentStyleMarks);
|
|
1207
|
+
}
|
|
1208
|
+
// Otherwise, convert as regular footnote
|
|
1209
|
+
return {
|
|
1210
|
+
type: "footnote",
|
|
1211
|
+
attrs: {
|
|
1212
|
+
footnote: this.convertContainer(noteBody)
|
|
1213
|
+
},
|
|
1214
|
+
marks: this.getCurrentMarks(currentStyleMarks)
|
|
1215
|
+
};
|
|
1216
|
+
}
|
|
1217
|
+
convertCitation(markName, currentStyleMarks) {
|
|
1218
|
+
const citationNode = parseOdtReferenceMark(markName, this.bibliography, this.bibDB);
|
|
1219
|
+
if (citationNode) {
|
|
1220
|
+
citationNode.marks = this.getCurrentMarks(currentStyleMarks);
|
|
1221
|
+
return citationNode;
|
|
1222
|
+
}
|
|
1223
|
+
return null;
|
|
1224
|
+
}
|
|
1225
|
+
convertBibliographyMark(bibMarkNode, currentStyleMarks) {
|
|
1226
|
+
const citationNode = parseOdtBibliographyMark(bibMarkNode, this.bibliography);
|
|
1227
|
+
if (citationNode) {
|
|
1228
|
+
citationNode.marks = this.getCurrentMarks(currentStyleMarks);
|
|
1229
|
+
return citationNode;
|
|
1230
|
+
}
|
|
1231
|
+
return null;
|
|
1232
|
+
}
|
|
1233
|
+
convertList(node, attrs) {
|
|
1234
|
+
const listStyle = attr(node, "text:style-name");
|
|
1235
|
+
const isOrdered = this.isOrderedList(listStyle);
|
|
1236
|
+
attrs = Object.assign({
|
|
1237
|
+
id: randomListId()
|
|
1238
|
+
}, attrs);
|
|
1239
|
+
if (isOrdered) {
|
|
1240
|
+
attrs.order = 1;
|
|
1241
|
+
}
|
|
1242
|
+
return {
|
|
1243
|
+
type: isOrdered ? "ordered_list" : "bullet_list",
|
|
1244
|
+
attrs,
|
|
1245
|
+
content: node.queryAll("text:list-item").map((item) => ({
|
|
1246
|
+
type: "list_item",
|
|
1247
|
+
content: this.convertContainer(item)
|
|
1248
|
+
}))
|
|
1249
|
+
};
|
|
1250
|
+
}
|
|
1251
|
+
convertAnnotationStart(node) {
|
|
1252
|
+
const commentId = (attr(node, "office:name") || "")
|
|
1253
|
+
.replace(/\D/g, "")
|
|
1254
|
+
.slice(0, 9);
|
|
1255
|
+
if (commentId && this.comments[commentId]) {
|
|
1256
|
+
this.currentCommentIds.push(commentId);
|
|
1257
|
+
}
|
|
1258
|
+
return null;
|
|
1259
|
+
}
|
|
1260
|
+
convertAnnotationEnd(node) {
|
|
1261
|
+
const commentId = (attr(node, "office:name") || "")
|
|
1262
|
+
.replace(/\D/g, "")
|
|
1263
|
+
.slice(0, 9);
|
|
1264
|
+
if (commentId) {
|
|
1265
|
+
const index = this.currentCommentIds.indexOf(commentId);
|
|
1266
|
+
if (index !== -1) {
|
|
1267
|
+
this.currentCommentIds.splice(index, 1);
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
return null;
|
|
1271
|
+
}
|
|
1272
|
+
convertHeadingReference(node) {
|
|
1273
|
+
const refName = attr(node, "text:ref-name");
|
|
1274
|
+
if (!refName || !this.referenceableObjects[refName]) {
|
|
1275
|
+
return null;
|
|
1276
|
+
}
|
|
1277
|
+
const targetObject = this.referenceableObjects[refName];
|
|
1278
|
+
if (targetObject.type !== "heading") {
|
|
1279
|
+
return null;
|
|
1280
|
+
}
|
|
1281
|
+
return {
|
|
1282
|
+
type: "cross_reference",
|
|
1283
|
+
attrs: {
|
|
1284
|
+
id: targetObject.id,
|
|
1285
|
+
title: targetObject.node.textContent
|
|
1286
|
+
}
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
1289
|
+
convertFigureReference(node) {
|
|
1290
|
+
const refName = attr(node, "text:ref-name");
|
|
1291
|
+
if (!refName || !this.referenceableObjects[refName]) {
|
|
1292
|
+
return null;
|
|
1293
|
+
}
|
|
1294
|
+
const targetObject = this.referenceableObjects[refName];
|
|
1295
|
+
if (targetObject.type !== "figure") {
|
|
1296
|
+
return null;
|
|
1297
|
+
}
|
|
1298
|
+
// Find the caption text within the figure
|
|
1299
|
+
const caption = targetObject.node.query("text:p")?.textContent || "";
|
|
1300
|
+
return {
|
|
1301
|
+
type: "cross_reference",
|
|
1302
|
+
attrs: {
|
|
1303
|
+
id: targetObject.id,
|
|
1304
|
+
title: caption
|
|
1305
|
+
}
|
|
1306
|
+
};
|
|
1307
|
+
}
|
|
1308
|
+
isOrderedList(styleName) {
|
|
1309
|
+
if (!this.stylesDoc) {
|
|
1310
|
+
return false;
|
|
1311
|
+
}
|
|
1312
|
+
const listStyle = this.stylesDoc.query("text:list-style", {
|
|
1313
|
+
"style:name": styleName
|
|
1314
|
+
});
|
|
1315
|
+
return listStyle?.query("text:list-level-style-number") !== null;
|
|
1316
|
+
}
|
|
1317
|
+
convertImage(node, attrs = {}) {
|
|
1318
|
+
const imageElement = node.query("draw:image");
|
|
1319
|
+
if (!imageElement) {
|
|
1320
|
+
return null;
|
|
1321
|
+
}
|
|
1322
|
+
const frame = node.closest("draw:frame");
|
|
1323
|
+
if (!frame) {
|
|
1324
|
+
return null;
|
|
1325
|
+
}
|
|
1326
|
+
const href = attr(imageElement, "xlink:href");
|
|
1327
|
+
if (!href || !href.startsWith("Pictures/")) {
|
|
1328
|
+
return null;
|
|
1329
|
+
}
|
|
1330
|
+
const imageId = Math.floor(Math.random() * 1000000);
|
|
1331
|
+
const width = this.convertLength(attr(node, "svg:width"));
|
|
1332
|
+
const height = this.convertLength(attr(node, "svg:height"));
|
|
1333
|
+
const title = href.split("/").pop();
|
|
1334
|
+
this.images[imageId] = {
|
|
1335
|
+
id: imageId,
|
|
1336
|
+
title,
|
|
1337
|
+
copyright: {
|
|
1338
|
+
holder: false,
|
|
1339
|
+
year: false,
|
|
1340
|
+
freeToRead: true,
|
|
1341
|
+
licenses: []
|
|
1342
|
+
},
|
|
1343
|
+
image: href,
|
|
1344
|
+
file_type: this.getImageFileType(title),
|
|
1345
|
+
file: null,
|
|
1346
|
+
width,
|
|
1347
|
+
height,
|
|
1348
|
+
checksum: 0
|
|
1349
|
+
};
|
|
1350
|
+
// Find sequence element for figure reference
|
|
1351
|
+
const sequence = frame.query("text:sequence");
|
|
1352
|
+
let figureId = null;
|
|
1353
|
+
if (sequence) {
|
|
1354
|
+
const refName = attr(sequence, "text:ref-name");
|
|
1355
|
+
if (refName && this.referenceableObjects[refName]) {
|
|
1356
|
+
figureId = this.referenceableObjects[refName].id;
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
const caption = node.query("text:p");
|
|
1360
|
+
const captionContent = caption ? this.convertNodeChildren(caption) : [];
|
|
1361
|
+
attrs = Object.assign({
|
|
1362
|
+
id: figureId || randomFigureId(),
|
|
1363
|
+
aligned: "center",
|
|
1364
|
+
width: Math.min(Math.round((width / 8.5) * 100), 100),
|
|
1365
|
+
caption: Boolean(captionContent.length)
|
|
1366
|
+
}, attrs);
|
|
1367
|
+
const figureCaption = { type: "figure_caption" };
|
|
1368
|
+
if (captionContent.length) {
|
|
1369
|
+
figureCaption.content = captionContent;
|
|
1370
|
+
}
|
|
1371
|
+
return {
|
|
1372
|
+
type: "figure",
|
|
1373
|
+
attrs,
|
|
1374
|
+
content: [
|
|
1375
|
+
{
|
|
1376
|
+
type: "image",
|
|
1377
|
+
attrs: {
|
|
1378
|
+
image: imageId
|
|
1379
|
+
}
|
|
1380
|
+
},
|
|
1381
|
+
figureCaption
|
|
1382
|
+
]
|
|
1383
|
+
};
|
|
1384
|
+
}
|
|
1385
|
+
getImageFileType(filename) {
|
|
1386
|
+
const ext = filename.split(".").pop().toLowerCase();
|
|
1387
|
+
switch (ext) {
|
|
1388
|
+
case "avif":
|
|
1389
|
+
case "avifs":
|
|
1390
|
+
return "image/avif";
|
|
1391
|
+
case "png":
|
|
1392
|
+
return "image/png";
|
|
1393
|
+
case "jpg":
|
|
1394
|
+
case "jpeg":
|
|
1395
|
+
return "image/jpeg";
|
|
1396
|
+
case "gif":
|
|
1397
|
+
return "image/gif";
|
|
1398
|
+
case "svg":
|
|
1399
|
+
return "image/svg+xml";
|
|
1400
|
+
case "webp":
|
|
1401
|
+
return "image/webp";
|
|
1402
|
+
default:
|
|
1403
|
+
return "image/png"; // Default fallback
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
convertLength(length) {
|
|
1407
|
+
if (!length) {
|
|
1408
|
+
return 0;
|
|
1409
|
+
}
|
|
1410
|
+
// Match number and unit
|
|
1411
|
+
const match = length.match(/^(-?\d*\.?\d+)(pt|cm|mm|in|pc|px|%)?$/);
|
|
1412
|
+
if (!match) {
|
|
1413
|
+
return 0;
|
|
1414
|
+
}
|
|
1415
|
+
const [_, value, unit = "pt"] = match;
|
|
1416
|
+
const numValue = parseFloat(value);
|
|
1417
|
+
// Convert to inches first (as base unit)
|
|
1418
|
+
switch (unit) {
|
|
1419
|
+
case "pt": // points
|
|
1420
|
+
return numValue / 72;
|
|
1421
|
+
case "pc": // picas (1 pica = 12 points)
|
|
1422
|
+
return (numValue * 12) / 72;
|
|
1423
|
+
case "cm": // centimeters
|
|
1424
|
+
return numValue / 2.54;
|
|
1425
|
+
case "mm": // millimeters
|
|
1426
|
+
return numValue / 25.4;
|
|
1427
|
+
case "in": // inches
|
|
1428
|
+
return numValue;
|
|
1429
|
+
case "px": // pixels (assuming 96 DPI)
|
|
1430
|
+
return numValue / 96;
|
|
1431
|
+
case "%": // percentage (return as is)
|
|
1432
|
+
return numValue;
|
|
1433
|
+
default:
|
|
1434
|
+
return 0;
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
convertTable(node, attrs) {
|
|
1438
|
+
const width = attr(node, "style:rel-width")?.replace("%", "") || "100";
|
|
1439
|
+
const styleName = attr(node, "table:style-name");
|
|
1440
|
+
const style = this.styles[styleName];
|
|
1441
|
+
const aligned = style?.tableProperties.align || "center";
|
|
1442
|
+
attrs = Object.assign({
|
|
1443
|
+
id: randomTableId(),
|
|
1444
|
+
track: parseTracks(attr(node, "text:change-id")),
|
|
1445
|
+
width,
|
|
1446
|
+
aligned,
|
|
1447
|
+
layout: "fixed",
|
|
1448
|
+
category: "none",
|
|
1449
|
+
caption: false
|
|
1450
|
+
}, attrs);
|
|
1451
|
+
return {
|
|
1452
|
+
type: "table",
|
|
1453
|
+
attrs,
|
|
1454
|
+
content: [
|
|
1455
|
+
{ type: "table_caption" },
|
|
1456
|
+
{
|
|
1457
|
+
type: "table_body",
|
|
1458
|
+
content: node
|
|
1459
|
+
.queryAll("table:table-row")
|
|
1460
|
+
.map((row) => this.convertTableRow(row))
|
|
1461
|
+
}
|
|
1462
|
+
]
|
|
1463
|
+
};
|
|
1464
|
+
}
|
|
1465
|
+
convertTableRow(row) {
|
|
1466
|
+
return {
|
|
1467
|
+
type: "table_row",
|
|
1468
|
+
content: row
|
|
1469
|
+
.queryAll(["table:table-cell", "table:covered-table-cell"])
|
|
1470
|
+
.map((cell) => this.convertTableCell(cell))
|
|
1471
|
+
};
|
|
1472
|
+
}
|
|
1473
|
+
convertTableCell(node) {
|
|
1474
|
+
if (node.tagName === "table:covered-table-cell") {
|
|
1475
|
+
return null;
|
|
1476
|
+
}
|
|
1477
|
+
return {
|
|
1478
|
+
type: "table_cell",
|
|
1479
|
+
attrs: {
|
|
1480
|
+
colspan: parseInt(attr(node, "table:number-columns-spanned")) || 1,
|
|
1481
|
+
rowspan: parseInt(attr(node, "table:number-rows-spanned")) ||
|
|
1482
|
+
1,
|
|
1483
|
+
track: parseTracks(attr(node, "text:change-id"))
|
|
1484
|
+
},
|
|
1485
|
+
content: this.convertContainer(node)
|
|
1486
|
+
};
|
|
1487
|
+
}
|
|
1488
|
+
convertLink(node, currentStyleMarks) {
|
|
1489
|
+
const href = attr(node, "xlink:href");
|
|
1490
|
+
currentStyleMarks = currentStyleMarks.concat([
|
|
1491
|
+
{ type: "link", attrs: { href } }
|
|
1492
|
+
]);
|
|
1493
|
+
return this.convertNodeChildren(node, currentStyleMarks);
|
|
1494
|
+
}
|
|
1495
|
+
detectLanguage() {
|
|
1496
|
+
// Try to detect document language in following order:
|
|
1497
|
+
// 1. From document content
|
|
1498
|
+
// 2. From document styles
|
|
1499
|
+
// 3. Default to "en-US"
|
|
1500
|
+
// Check content language
|
|
1501
|
+
if (this.contentDoc) {
|
|
1502
|
+
const langAttr = attr(this.contentDoc, "office:default-language") ||
|
|
1503
|
+
attr(this.contentDoc, "dc:language");
|
|
1504
|
+
if (langAttr) {
|
|
1505
|
+
return langAttr;
|
|
1506
|
+
}
|
|
1507
|
+
const firstParagraph = this.contentDoc.query("text:p");
|
|
1508
|
+
if (firstParagraph) {
|
|
1509
|
+
const paraLang = attr(firstParagraph, "xml:lang");
|
|
1510
|
+
if (paraLang) {
|
|
1511
|
+
return paraLang;
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
// Check styles language
|
|
1516
|
+
if (this.stylesDoc) {
|
|
1517
|
+
const defaultStyle = this.stylesDoc.query("style:default-style");
|
|
1518
|
+
if (defaultStyle) {
|
|
1519
|
+
const styleLang = attr(defaultStyle, "fo:language") ||
|
|
1520
|
+
attr(defaultStyle, "style:language-complex");
|
|
1521
|
+
if (styleLang) {
|
|
1522
|
+
return styleLang;
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
// Default to "en-US"
|
|
1527
|
+
return "en-US";
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
//# sourceMappingURL=convert.js.map
|