texdocxdrafter 0.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. texdocxdrafter-0.1.2/LICENSE +21 -0
  2. texdocxdrafter-0.1.2/PKG-INFO +463 -0
  3. texdocxdrafter-0.1.2/README.md +437 -0
  4. texdocxdrafter-0.1.2/pyproject.toml +41 -0
  5. texdocxdrafter-0.1.2/setup.cfg +4 -0
  6. texdocxdrafter-0.1.2/src/texdocxdrafter/__init__.py +1 -0
  7. texdocxdrafter-0.1.2/src/texdocxdrafter/audit.py +113 -0
  8. texdocxdrafter-0.1.2/src/texdocxdrafter/auxreader.py +84 -0
  9. texdocxdrafter-0.1.2/src/texdocxdrafter/bbl.py +538 -0
  10. texdocxdrafter-0.1.2/src/texdocxdrafter/cli.py +227 -0
  11. texdocxdrafter-0.1.2/src/texdocxdrafter/defaults/macros.yaml +167 -0
  12. texdocxdrafter-0.1.2/src/texdocxdrafter/expand.py +408 -0
  13. texdocxdrafter-0.1.2/src/texdocxdrafter/fidelity.py +154 -0
  14. texdocxdrafter-0.1.2/src/texdocxdrafter/harvest.py +443 -0
  15. texdocxdrafter-0.1.2/src/texdocxdrafter/inventory.py +61 -0
  16. texdocxdrafter-0.1.2/src/texdocxdrafter/lexer.py +191 -0
  17. texdocxdrafter-0.1.2/src/texdocxdrafter/parser/__init__.py +63 -0
  18. texdocxdrafter-0.1.2/src/texdocxdrafter/parser/blocks.py +728 -0
  19. texdocxdrafter-0.1.2/src/texdocxdrafter/parser/core.py +332 -0
  20. texdocxdrafter-0.1.2/src/texdocxdrafter/parser/frontmatter.py +89 -0
  21. texdocxdrafter-0.1.2/src/texdocxdrafter/parser/inlines.py +213 -0
  22. texdocxdrafter-0.1.2/src/texdocxdrafter/parser/math.py +90 -0
  23. texdocxdrafter-0.1.2/src/texdocxdrafter/parser/model.py +298 -0
  24. texdocxdrafter-0.1.2/src/texdocxdrafter/parser/tables.py +279 -0
  25. texdocxdrafter-0.1.2/src/texdocxdrafter/pipeline.py +614 -0
  26. texdocxdrafter-0.1.2/src/texdocxdrafter/preflight.py +541 -0
  27. texdocxdrafter-0.1.2/src/texdocxdrafter/refdoc.py +411 -0
  28. texdocxdrafter-0.1.2/src/texdocxdrafter/resolve.py +135 -0
  29. texdocxdrafter-0.1.2/src/texdocxdrafter/sandbox.py +418 -0
  30. texdocxdrafter-0.1.2/src/texdocxdrafter/sidecompile.py +107 -0
  31. texdocxdrafter-0.1.2/src/texdocxdrafter/stylemap.py +136 -0
  32. texdocxdrafter-0.1.2/src/texdocxdrafter/tikz.py +93 -0
  33. texdocxdrafter-0.1.2/src/texdocxdrafter/validate.py +228 -0
  34. texdocxdrafter-0.1.2/src/texdocxdrafter/writer/__init__.py +1368 -0
  35. texdocxdrafter-0.1.2/src/texdocxdrafter/writer/images.py +108 -0
  36. texdocxdrafter-0.1.2/src/texdocxdrafter/writer/math.py +171 -0
  37. texdocxdrafter-0.1.2/src/texdocxdrafter/writer/numbering.py +117 -0
  38. texdocxdrafter-0.1.2/src/texdocxdrafter/writer/textmath.py +222 -0
  39. texdocxdrafter-0.1.2/src/texdocxdrafter/writer/xmlutil.py +132 -0
  40. texdocxdrafter-0.1.2/src/texdocxdrafter.egg-info/PKG-INFO +463 -0
  41. texdocxdrafter-0.1.2/src/texdocxdrafter.egg-info/SOURCES.txt +43 -0
  42. texdocxdrafter-0.1.2/src/texdocxdrafter.egg-info/dependency_links.txt +1 -0
  43. texdocxdrafter-0.1.2/src/texdocxdrafter.egg-info/entry_points.txt +2 -0
  44. texdocxdrafter-0.1.2/src/texdocxdrafter.egg-info/requires.txt +9 -0
  45. texdocxdrafter-0.1.2/src/texdocxdrafter.egg-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lin Ma
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,463 @@
1
+ Metadata-Version: 2.4
2
+ Name: texdocxdrafter
3
+ Version: 0.1.2
4
+ Summary: TeX-DOCX Drafter: drafts a clean, style-mapped Word document from a compiled LaTeX paper, for coauthors who edit in Word
5
+ Author: Lin Ma
6
+ License-Expression: MIT
7
+ Keywords: latex,docx,word,biblatex,conversion,academic-writing
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Science/Research
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Topic :: Text Processing :: Markup :: LaTeX
13
+ Classifier: Topic :: Office/Business :: Office Suites
14
+ Requires-Python: >=3.12
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: python-docx
18
+ Requires-Dist: pylatexenc
19
+ Requires-Dist: lxml
20
+ Requires-Dist: pyyaml
21
+ Requires-Dist: latex2mathml
22
+ Requires-Dist: mathml2omml
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest; extra == "dev"
25
+ Dynamic: license-file
26
+
27
+ # TeX-DOCX Drafter (`texdocxdrafter`)
28
+
29
+ Drafts a clean, style-mapped Word document (`.docx`) from a LaTeX paper, for coauthors who
30
+ edit in Word.
31
+
32
+ It is a drafter, not a converter: it does not replicate the PDF. The goal is an editable base
33
+ document with proper style, auto-numbering and cross-references.
34
+
35
+ ## Why I make this package
36
+
37
+ As a researcher writing in LaTeX, I frequently encounter coauthors wanting an editable Word file for
38
+ their editing and for the final submission.
39
+
40
+ So I need to hand them my draft, the whole paper or just a section, in a form they can continue to
41
+ edit: not just plain texts.
42
+ It does not matter whether the page looks exactly like my PDF.
43
+ Also, figures generated in LaTeX are converted to pictures and inserted into Word, so nobody has to redraw them elsewhere.
44
+
45
+ Many complex LaTeX features are not supported, because they cost only a little manual editing or a
46
+ macro in Microsoft Word, while supporting them would make this package complex.
47
+
48
+ The reference list is rendered as static text, because different scholars favour different tools:
49
+ Zotero, EndNote, Word's own citations, or static text.
50
+ Automatic referencing is part of the downstream editing.
51
+
52
+ If you want an easily editable file to start downstream editing from, here it is.
53
+ If you want to keep the exact layout, there are many other tools available. Please search for them
54
+ first.
55
+
56
+ I intend to keep the package small and simple. You are welcome to fork it and make it fancier.
57
+
58
+ Good luck.
59
+
60
+ What you get:
61
+
62
+ - Every structural element uses a named Word style (Heading 1, Body Text, Caption Table, Bibliography, ...).
63
+ - Headings, table and figure captions, and equations use Word's own auto-numbering.
64
+ - `\ref`, `\cref`, `\autoref`, `\eqref` and `\pageref` become live REF/PAGEREF fields.
65
+ - Citations and the reference list are rendered by the real biblatex or BibTeX (italics, punctuation, style)
66
+ and inserted as plain formatted text. There are no Zotero or Word citation fields.
67
+ - Review markup such as `\add{...}` and `\del{...}` becomes Word tracked changes (insertions and deletions).
68
+ - A two-column LaTeX layout becomes a two-column Word layout.
69
+ - Equations become native Word (OMML) equations.
70
+ - `tikzpicture` environments are compiled with your TeX engine and embedded as PNG images.
71
+ - With `--comments`, LaTeX `%` comments in the document body become Word comments on the paragraph they annotate.
72
+ - A report lists every construct that was dropped or approximated, with `file:line`.
73
+
74
+ Pure Python plus your existing TeX installation. No pandoc, no LibreOffice, no Java.
75
+
76
+ ## Requirements
77
+
78
+ - Python 3.12 or newer.
79
+ - The TeX distribution that compiled your paper. biblatex citations and TikZ pictures need two small side
80
+ compiles with the same engine (`pdflatex`/`xelatex`/`lualatex`) and `biber`; see "Side compiles".
81
+ - `pdftotext`, `pdftoppm` and `pdfinfo` (poppler-utils). `gs` (Ghostscript) is needed only for EPS figures.
82
+ - Linux: `bwrap` (bubblewrap) lets the tool run the side compiles itself, in a sandbox. Without it the tool
83
+ writes the side documents and you compile them.
84
+
85
+ ## Install
86
+
87
+ ```
88
+ pip install texdocxdrafter
89
+ ```
90
+
91
+ Or, to get the command without managing a virtual environment, with [pipx](https://pipx.pypa.io/):
92
+
93
+ ```
94
+ pipx install texdocxdrafter
95
+ ```
96
+
97
+ This installs the `texdocxdrafter` command (also runnable as `python -m texdocxdrafter.cli`).
98
+ The name is `tex` + `docx` + `drafter`; `pip install tex-docx-drafter` resolves to the same package.
99
+ To work on the code itself, see Development at the end.
100
+
101
+ ## Before you run it
102
+
103
+ `texdocxdrafter` works on a document you have already compiled. Next to `main.tex` there must be the `.aux` file
104
+ and the `.bbl` file (required for biblatex; for BibTeX the citations are read from it):
105
+
106
+ ```
107
+ pdflatex main.tex && biber main && pdflatex main.tex && pdflatex main.tex
108
+ ```
109
+
110
+ If they are missing, `texdocxdrafter` stops and tells you to compile first. For documents without citations
111
+ or when you only want the structure, use `--no-harvest`.
112
+
113
+ ## Quick start
114
+
115
+ ```
116
+ texdocxdrafter build main.tex
117
+ ```
118
+
119
+ This writes `main.docx` and `main.texdocxdrafter-report.md` next to `main.tex`. Open the report first: it lists
120
+ what needs manual attention.
121
+
122
+ If `main.docx` already exists and is not byte-for-byte a file this tool wrote (for example because a coauthor
123
+ has saved it), the build stops rather than overwrite it. Pass `-o other.docx` or `--overwrite`. The same
124
+ protection covers the report.
125
+
126
+ In Word, press `Ctrl+A` then `F9` to refresh all fields (`texdocxdrafter` also sets "update fields on open", so
127
+ Word may ask whether to update them). After that, inserting or moving a section renumbers headings,
128
+ captions, equations and every cross-reference.
129
+
130
+ ## Side compiles
131
+
132
+ Two things cannot be produced by parsing alone: biblatex citations, whose formatting lives in the style's TeX
133
+ macros, and `tikzpicture` environments, which are TeX programs. For them the tool builds two small side
134
+ documents from your own preamble, `harvest.tex` (every citation command and the bibliography on one huge page)
135
+ and `tikz.tex` (one page per picture), and needs their PDFs.
136
+
137
+ - **With bubblewrap (`bwrap`, Linux).** The tool compiles them itself, in a sandbox in which the engine sees
138
+ only the system and TeX directories, your document's directories (read-only) and a private work directory,
139
+ with no network. TeX's own cache (`TEXMFVAR`) is read-only too; the run gets a throwaway one that is deleted
140
+ with the work directory, so a document cannot leave Lua code or a format file behind for a later compile.
141
+ - **Without bubblewrap (macOS, Windows, Linux without it).** The tool never starts a TeX engine. It writes the
142
+ side documents into `main.texdocxdrafter/`, finishes the docx with citations as `[key]` and pictures as
143
+ visible text, and the report gives the exact command to compile them, of the form
144
+
145
+ ```
146
+ cd main.texdocxdrafter && TEXINPUTS="/path/to/paper:" BIBINPUTS="/path/to/paper:" BSTINPUTS="/path/to/paper:" xelatex harvest && biber harvest && xelatex harvest && xelatex harvest
147
+ ```
148
+
149
+ Run it, then rerun `texdocxdrafter build`: the PDFs are picked up. A side document is rewritten only when
150
+ its content changes, so this step is needed again only after you edit the preamble, a citation or a picture.
151
+ The side document is your project's preamble: compile it only if you trust the project, exactly as when you
152
+ compile the paper itself. A PDF counts only if it is newer than a side document this tool recorded writing,
153
+ so a `harvest.pdf` that arrived inside an archive is ignored until you compile.
154
+
155
+ BibTeX documents (`\bibliography{...}` with natbib or plain `thebibliography` styles) need no side compile:
156
+ the `.bst` style has already typeset the entries into `main.bbl`, natbib's author-year labels are in it too,
157
+ and citations and the reference list are read from that file.
158
+
159
+ ## Commands
160
+
161
+ | Command | Purpose |
162
+ |---|---|
163
+ | `texdocxdrafter build main.tex` | Convert to docx and write the report |
164
+ | `texdocxdrafter inventory main.tex` | Print a census of constructs and the Word style each maps to; create `stylemap.yaml` if none exists |
165
+ | `texdocxdrafter parse main.tex` | Dump the parsed document tree as JSON (`--facts` prints preamble facts only) |
166
+ | `texdocxdrafter harvest main.tex` | Print every citation and bibliography entry as the docx will show them (side compile or `.bbl`) |
167
+ | `texdocxdrafter check out.docx` | Style usage table and structural validation of a docx |
168
+
169
+ ### `build` options
170
+
171
+ | Option | Effect |
172
+ |---|---|
173
+ | `-o out.docx` | Output path (the report is written next to it) |
174
+ | `--style-map file.yaml` | Use this style map instead of `main.texdocxdrafter/stylemap.yaml` |
175
+ | `--reference-doc tpl.docx` | Use a journal or house template. Style names in the style map must exist there; missing ones are created and reported |
176
+ | `--link-citations` | Make each citation a hyperlink to its bibliography entry |
177
+ | `-f`, `--overwrite` | Replace an existing output file even if it was not written by `texdocxdrafter` |
178
+ | `--keep-work` | Keep the intermediate files (harvest files, `tree.json`, images) under `main.texdocxdrafter/work/` |
179
+ | `--no-harvest` | Skip citations (no side compile, no `.bbl` reading); they appear as `[key]` |
180
+ | `--no-tikz` | Skip the `tikzpicture` side compile; pictures stay as visible text |
181
+ | `--comments` | Turn LaTeX `%` comments into Word comments (off by default, since source comments are often private) |
182
+ | `--allow-dir DIR` | Also accept `\input`, `\include` and graphics from `DIR` (repeatable). By default only files under the directory of `main.tex` are read |
183
+ | `--timeout SECONDS` | Limit for each TeX or biber run of the side compiles (default 120, at most 3600) |
184
+ | `--validate-libreoffice` | Also try to open the result with headless LibreOffice, if installed. It runs in the same sandbox as the TeX engines with a private profile; a snap-packaged LibreOffice cannot start there and is reported as such |
185
+ | `--strict` | Write nothing when the word-count check or the content check finds a loss (by default both are reported, and only structural validation and the policy check are fatal) |
186
+
187
+ With `--no-harvest --no-tikz` the tool only parses; the same is true for BibTeX documents without TikZ.
188
+
189
+ ## Customising styles
190
+
191
+ Run `texdocxdrafter inventory main.tex` once. It writes `main.texdocxdrafter/stylemap.yaml` and never overwrites an
192
+ existing one. Edit it to rename Word styles, then run `build` again. Sections:
193
+
194
+ - `paragraph`: Word paragraph style per construct (`body`, `first_paragraph`, `quote`, `caption_table`,
195
+ `table_text`, `bibliography`, ...). `heading` is the base name, giving Heading 1 to Heading 9.
196
+ - `character`: character styles (`emph`, `strong`, `code`, `link`, `citation`, ...).
197
+ - `options`:
198
+ - `heading_offset`: `auto` (article: `\section` is Heading 1; report/book: `\chapter` is Heading 1) or an integer.
199
+ - `caption_position`: `above` or `below` for tables and figures.
200
+ - `paragraph_as_heading`: `false` makes `\paragraph{X}` a run-in bold sentence; `true` makes it a heading.
201
+ - `strip_bold_in_headings`: unwrap `\textbf` inside headings (default true).
202
+ - `reference_doc`: path of a template docx.
203
+ - `table_style`: name of the table style.
204
+ - `columns`: `auto` (two columns when the LaTeX layout has them), `1` or `2`.
205
+ - `revision_macros`: `auto`, a mapping such as `{add: ins, del: del}`, or `false` to expand the macros as text.
206
+ - `revision_author`: author name on tracked changes (default `LaTeX`).
207
+ - `revision_environments`: environments whose content is a tracked change, default
208
+ `{addblock: ins, delblock: del}`. A map you give replaces the default.
209
+ - `inline_math_as_text`: `false` keeps every inline formula as a Word equation.
210
+
211
+ To change fonts, spacing or colours, edit the styles in Word once, or pass your journal template with
212
+ `--reference-doc`.
213
+
214
+ ## Tracked changes from review markup
215
+
216
+ Revision markup in the LaTeX source becomes Word tracked changes, so a coauthor can accept or reject
217
+ each change in Word. Define the macros and environments in your preamble as you like; only their
218
+ effect in LaTeX matters, not their names:
219
+
220
+ ```latex
221
+ \usepackage[dvipsnames]{xcolor}
222
+ \usepackage{ulem}
223
+ \newcommand{\add}[1]{\uline{#1}} % underlined in the PDF -> tracked insertion
224
+ \newcommand{\del}[1]{\sout{#1}} % struck out in the PDF -> tracked deletion
225
+ \newenvironment{addblock}{\color{NavyBlue}}{} % colour only in the PDF -> tracked insertion
226
+ \newenvironment{delblock}{\color{red}}{} % colour only in the PDF -> tracked deletion
227
+ ```
228
+
229
+ Inline changes:
230
+
231
+ ```latex
232
+ The sample holds \del{five} \add{four} industries.
233
+ ```
234
+
235
+ Block changes, for passages that `\add{...}` cannot hold (headings, labels, lists, floats, footnotes):
236
+
237
+ ```latex
238
+ \begin{delblock}
239
+ This paragraph is removed in the revision.
240
+
241
+ \subsection{A heading that goes too}
242
+ Its text, with a footnote.\footnote{Removed with it.}
243
+ \end{delblock}
244
+
245
+ \begin{addblock}
246
+ This paragraph is new in the revision.
247
+ \begin{itemize}
248
+ \item so is this list
249
+ \end{itemize}
250
+ \end{addblock}
251
+ ```
252
+
253
+ In the PDF the environments only colour the text, so the LaTeX document compiles as usual. In the docx
254
+ every paragraph, heading, list item, table row, caption and footnote inside `delblock` is a tracked
255
+ deletion and inside `addblock` a tracked insertion. Accepting all changes yields the revised text;
256
+ rejecting all yields the original.
257
+
258
+ How the tool decides:
259
+
260
+ - An author macro whose definition strikes out its argument (`\sout`, `\xout`) is a deletion; one that
261
+ underlines it (`\uline`, `\uuline`, `\uwave`) is an insertion. Other macro names work the same way.
262
+ - The environment names `addblock` and `delblock` are the defaults of `options.revision_environments`
263
+ in the style map; set your own map, for example `{newtext: ins, oldtext: del}`, to use other names.
264
+ - Text deleted inside an insertion nests as Word does. A lone space between `\del{a} \add{b}` is dropped,
265
+ so accepting or rejecting all changes leaves single spacing.
266
+ - The author shown on the changes is `options.revision_author` (default `LaTeX`).
267
+ - Set `options.revision_macros: false` to turn the detection off and keep the macros as plain text.
268
+
269
+ ## What is supported
270
+
271
+ - Sectioning, paragraphs, run-in `\paragraph`, appendices (flagged), quotes, verbatim, footnotes.
272
+ - Emphasis and bold, monospace, links (`\url`, `\href`), special characters and accents.
273
+ - `itemize`, `enumerate`, `description`, nested; enumitem `label=` with `\arabic*`, `\alph*`, `\roman*`;
274
+ `\item[label]` in `itemize`/`enumerate` replaces the bullet or number with the label.
275
+ - Tables: `tabular`, `tabularx`, `tabulary`, `longtable`, `threeparttable` notes, booktabs and `\hline`
276
+ rules, `\multicolumn`, `\multirow`. Rows above the first mid rule become repeating header rows.
277
+ - Figures: `\includegraphics` (PDF, EPS, PNG, JPEG), `subfigure`/`subcaption` panels, `\graphicspath`.
278
+ - TikZ: every `tikzpicture` is drawn in one side compile (the document's own preamble plus the `preview`
279
+ package, one page per picture, labels taken from the `.aux`; see "Side compiles") and rasterised at 300 dpi. A picture inside a
280
+ float keeps the float's font size and single spacing. If the compile fails, the source stays as visible text.
281
+ - Comments (`--comments`): a comment at the end of a text line, or on its own line within a paragraph, goes on that
282
+ paragraph; a comment line between blocks goes on the next block. Consecutive comment lines become one
283
+ Word comment (author "LaTeX"). Comments in the preamble, inside a `tikzpicture`, and empty or decorative
284
+ ones (`%%%%`) are skipped.
285
+ - Math: inline, `equation`, `align`, `gather`, `multline`, `eqnarray`, labels and `\nonumber`.
286
+ - Simple inline math (`$\alpha = 0.63$`, `$R^2$`, `$p_{ik} \in [0,1]$`) is written as plain characters: Unicode
287
+ Greek and symbols, sub/superscripts as raised or lowered text, LaTeX spacing around relations and operators.
288
+ Latin letters and lowercase Greek use the italic character style "Math Variable". Formulas with fractions,
289
+ big operators, accents or font switches stay native Word equations. Set `options.inline_math_as_text: false`
290
+ in the style map to keep every formula as an equation.
291
+ Numbered equations use a borderless layout table with a `SEQ` number.
292
+ - Author macros: `\newcommand`, `\renewcommand`, `\def`, `\newenvironment`, `\DeclareMathOperator`,
293
+ `\newif` conditionals and `\iffalse`.
294
+ - Citations: the biblatex cite family (including `\cites` forms and natbib-style `\citet`/`\citep` with
295
+ `natbib=true`), `\printbibliography`, `refsection`. BibTeX with natbib or plain `thebibliography` styles:
296
+ citations and entries are read from the document's own `.bbl` with no compile (natbib author-year and
297
+ numeric forms, starred and capitalised variants, pre- and post-notes, the bracket and separator options,
298
+ `sort&compress`, `\bibpunct`, `\setcitestyle`).
299
+ - Review markup: `\add{...}`, `\del{...}`, `addblock` and `delblock` become tracked changes; see
300
+ "Tracked changes from review markup" above.
301
+ - Two columns (`twocolumn` option, `\twocolumn` in the preamble or in a local `.cls`/`.sty` such as
302
+ `acl.sty`): the body is a two-column Word section; the title block and `table*`/`figure*` sit in
303
+ one-column sections joined by continuous section breaks. Tables and pictures inside a column are sized
304
+ to the column. Word lays this out correctly. LibreOffice 26.2 opens the file but can fail to export it
305
+ to PDF when footnotes fall in a two-column section that ends at a break; use `columns: 1` if you need
306
+ LibreOffice.
307
+ - Front matter: `\title`, `\author` (with `\and`, `\thanks`), `\date`, `\maketitle`, `abstract`, `\keywords`.
308
+
309
+ ## What is not
310
+
311
+ - Page layout, fonts, spacing, float placement, line breaks.
312
+ - Live citation fields, editable TikZ drawings (pictures arrive as images), beamer, three or more columns.
313
+ - Colour that is not inside a recognised review macro (`{\color{X} ...}` around a section is dropped).
314
+ - Arbitrary TeX. Anything outside the list above is kept as visible red text (style "Drafter Todo") and
315
+ listed in the report with its source line, so the manual fix is bounded.
316
+
317
+ ## What the tool does to your files
318
+
319
+ `texdocxdrafter` reads your sources and writes a docx. It does not compile your paper. biblatex citations and
320
+ TikZ pictures need two side compiles of your own preamble (see "Side compiles"), and the tool runs them itself
321
+ only inside bubblewrap: a sandbox in which the engine sees the system and TeX directories and the caches
322
+ (read-only; of `/etc` only the font, TeX, loader and Ghostscript configuration, and no `/proc`), your document's
323
+ directories (read-only) and a private work directory holding a throwaway `TEXMFVAR`, with no network, no
324
+ capabilities, shell escape disabled, `openout_any=p`, a filtered environment and limits on time, CPU and output
325
+ size (set through `prlimit` where util-linux provides it). Without
326
+ bubblewrap no TeX engine is started; you compile the side documents yourself, exactly as you compile the paper.
327
+ The report's run summary says which of the two happened. Image converters (poppler, Ghostscript) run under the
328
+ same limits. LuaTeX additionally loads a small guard that refuses file writes, links and directory changes
329
+ outside the work directory, running programs, loading native code and recovering the unguarded functions
330
+ through the `debug` library; the sandbox, not the guard, is the boundary. Whatever a compile or a converter
331
+ leaves in the work directory is treated as untrusted: the tool
332
+ reads only regular files there, never through a symbolic link, and creates its own files afresh.
333
+
334
+ - Files are written only to the output `.docx`, its report, and `main.texdocxdrafter/` (the style map, the hash
335
+ registry, the side documents and, with `--keep-work`, a `work/` directory that is replaced on the next run
336
+ only if this tool created it). `main.texdocxdrafter/` must be a real directory next to `main.tex`, and the
337
+ side documents are replaced only if the registry says this tool wrote them; nothing is ever written through
338
+ a symbolic link.
339
+ Nothing else next to your sources is created, changed or deleted. The docx is written to a temporary name
340
+ and renamed into place, so an interrupted run never leaves a half-written file.
341
+ - Nothing is published before it is checked. The docx is validated on a private copy in the work directory:
342
+ it must be a readable package whose XML is well formed, whose styles, relationships, bookmarks and numbering
343
+ resolve, and whose children sit in schema order; and the whole package is checked against the
344
+ active-content policy below (every relationship, content type, part and field, wherever it sits). An error
345
+ there ends the run with a message and the existing output, report and registry are untouched. The
346
+ word-count check and the content check are reported in the same way but are fatal only with `--strict`.
347
+ Every input (sources, `.aux`, `.bbl`, `.bib`, graphics) is hashed when it is read and checked again right
348
+ before publication: if one changed meanwhile, nothing is written and the message names it.
349
+ - The output and the report are never source files or symbolic links. The tool records a hash of every file
350
+ it writes in `main.texdocxdrafter/outputs.json`; an existing file whose hash does not match, that is, any
351
+ file it did not write or that has been changed since, is not overwritten without `--overwrite`. Files are
352
+ written through an exclusively created temporary file and renamed into place, and the target is checked
353
+ again at that moment: a file that appeared or changed while the conversion ran is left alone. The report,
354
+ the docx and the registry are committed as one step, in that order; if one of the three fails (disk full,
355
+ permissions), the ones already written are put back to their previous content, and if that fails as well the
356
+ run ends with a message naming the files that now disagree and asking for `--overwrite` on the next run. A
357
+ new file gets the usual permissions (0666 minus your umask) and a replaced file keeps its mode. On drives
358
+ without hard links (FAT, some network shares) the name is reserved by exclusive creation and the finished
359
+ temporary file renamed onto it, so a disk-full error never leaves a truncated output. The output may not be
360
+ the reference document. What a sequence of renames
361
+ cannot promise: a crash between two of the three leaves them inconsistent (the next run will say so), the
362
+ moment between the last identity check of the target and its rename remains open to a concurrent save by
363
+ another program, and the directory is `fsync`ed after the rename only where the platform allows it, so
364
+ durability across a power loss is best effort. A file at the
365
+ registry's path that is not a registry this tool wrote, or that is one of your sources, stops the run.
366
+ - Source files must be UTF-8. Bytes that are not are replaced by U+FFFD and the report names the file and the
367
+ offset of the first one; the files themselves are never modified. A `%` inside `\verb`, `\url`, `\href` or
368
+ `\path` is text, not a comment.
369
+ - A reference document is used for its styles, numbering, page setup, headers and footers only. Its comments
370
+ and people list, footnotes and endnotes, glossary, custom XML, custom and extended properties, thumbnail,
371
+ every core property, attached-template path and document variables are removed first, so nothing of the
372
+ template's own text or author travels into your draft. Macro-enabled templates (`.docm`, `.dotm`) are
373
+ refused, and a VBA project inside a `.docx` is dropped. In its headers and footers only fields computed from
374
+ the document itself survive (`PAGE`, `NUMPAGES`, dates, `STYLEREF`, `REF`, `SEQ` and the like); fields that
375
+ make Word fetch other files or URLs (`INCLUDETEXT`, `INCLUDEPICTURE`, `LINK`, `DDE`, `MERGEFIELD`, …) or
376
+ show your file path (`FILENAME`), alternate-format chunks, sub-documents, embedded objects and controls, and
377
+ externally linked pictures are removed, as are mail-merge settings and their data sources, because the docx
378
+ asks Word to update all fields on opening so that its own cross-references show the right numbers. The page
379
+ background and its fill, web-layout frames, the view setting and editing or write protection are removed too.
380
+ Hyperlinks in its headers and footers follow the same rule as the tool's own links: `http`, `https` and
381
+ `mailto` targets stay links, anything else (network shares, `file:`, `javascript:`, relative paths) becomes
382
+ plain text.
383
+ - The citation harvest marks its output with a nonce that the `.bib` files never see (random per run; kept in
384
+ `main.texdocxdrafter/harvest.salt` when you compile the side document yourself), so text in a bibliography
385
+ entry cannot pose as a marker and forge or truncate citations. Characters that XML cannot carry are dropped
386
+ from all text before it is written.
387
+ - Build files (`.aux`, `.bbl`, `.log`) that are symbolic links to files outside the project are refused, and
388
+ the TikZ side compile receives only the `\newlabel` lines of the `.aux`, never the raw file.
389
+ - Only `http`, `https` and `mailto` targets become Word hyperlinks; `\url` and `\href` with other schemes
390
+ (`file:`, `javascript:`, network paths) stay plain text.
391
+ - `\input`, `\include` and graphics are read only from the directory of `main.tex`, symlinks resolved, unless
392
+ you add directories with `--allow-dir`. A shared preamble in a parent directory therefore needs
393
+ `--allow-dir ..`.
394
+ - Source comments stay out of the docx unless you pass `--comments`.
395
+ - The tool's own process runs under a memory cap (4 GiB of address space) and a CPU-time cap (30 minutes),
396
+ also when `texdocxdrafter.pipeline.build` is called from Python; the report notes when a limit could not
397
+ be applied. Style names from the style map must be printable text and are escaped when written into XML.
398
+ Author macro expansion has a budget (20,000 expansions, 2 million characters in total, 200,000 per
399
+ expansion); a runaway definition such as `\newcommand{\dbl}{\dbl\dbl}` stops there with an error in the
400
+ report and the remaining calls kept as text. `\input` expansion stops after 500 files or 50 million
401
+ characters, a table column spec is capped at 200 columns, and font declarations nest at most 50 deep. A
402
+ document that nests groups thousands of levels deep is refused with a message rather than a crash.
403
+
404
+ ## Reading the report
405
+
406
+ `main.texdocxdrafter-report.md` contains the run summary, style usage, construct census, and sections for
407
+ unsupported constructs, dropped presentation commands, direct formatting, math that stayed as LaTeX text,
408
+ missing figures, harvest problems (for example cited keys missing from the `.bib`), numbering
409
+ differences between LaTeX and Word, a word-count check that warns when more than 2 % of the source
410
+ words are missing from the docx, and a content check that compares the parsed document with the finished
411
+ file: every heading in order and with its text, and the number of tables, embedded pictures, equations,
412
+ footnotes and bibliography entries. The content check does not compare the order of body paragraphs, the
413
+ contents of table cells, citations (the harvest line gives their count), metadata or formatting.
414
+
415
+ ## Known limits
416
+
417
+ - Appendix headings keep numeric numbering in Word (flagged in the report). Where LaTeX's figure, table or
418
+ equation number (from the `.aux`) is a plain integer that differs from Word's running count, after
419
+ `\setcounter` or `subequations` for instance, the SEQ field resets Word's sequence to LaTeX's number. Not
420
+ when the `.aux` is stale (older than a source, or lacking a label of the document): then Word counts on
421
+ its own and the report asks for a recompile.
422
+ `\caption*` gives an unnumbered caption that does not advance the sequence.
423
+ - Footnote citations (`\footcite`, and `\autocite` in verbose styles) become Word footnotes holding the
424
+ citation text as LaTeX set it. A citation inside a `\footnote` is typeset in biblatex's footnote context in
425
+ the side document too, so the repeat, ibid. and idem trackers of verbose and -ibid styles give the same short
426
+ forms as the PDF. A footnote citation inside a footnote is written in parentheses, as biblatex writes it.
427
+ A period the source puts right after a citation that ends in one ("ibid." followed by ".") is dropped, as
428
+ biblatex drops it.
429
+ - Bibliography labels (`[1]`, `[Knu84]`, `12.`, a shorthand) are read from the list labels biblatex typesets,
430
+ whatever their shape; a bibliography that is not a LaTeX list falls back to a text pattern for them.
431
+ - `\numberwithin` (per-section equation numbers) is not reproduced; Word numbers continuously.
432
+ - A multi-line numbered `align` loses its alignment points (each numbered line is its own row);
433
+ unnumbered `align*` keeps them.
434
+ - Math conversion uses `latex2mathml` and `mathml2omml`; unusual formulas fall back to LaTeX text, and a
435
+ macro the converter does not know stays in the formula as its literal name (reported). `\intertext` becomes
436
+ a body paragraph between two equation blocks.
437
+ - Word has no footnotes inside footnotes: an inner footnote is written in parentheses inside the outer one
438
+ (reported). A `longtable` footer repeated on every page (`\endfoot`) is dropped (reported); the closing rows
439
+ (`\endlastfoot`) follow the body. `\multirow{-n}` is merged upwards only into empty cells of the same
440
+ width; otherwise its text stays in its own cell (reported), so nothing above it is ever overwritten.
441
+ - In a BibTeX document the `.bbl` is read directly: `\url`, `\href` and `\doi` become links, natbib's year
442
+ compression (`2020a,b`) is reproduced, and a macro the reader does not know keeps only its argument text
443
+ (reported). In a biblatex harvest `\LaTeX` and `\TeX` are written as plain words.
444
+ - A float with several `\caption`s gives one captioned table or figure per caption; tabulars inside a figure
445
+ (or pictures inside a table) follow the main block.
446
+ - Verify the result in Word at least once per document: open it, update fields, and insert a section
447
+ to confirm renumbering.
448
+
449
+ ## License
450
+
451
+ MIT. See `LICENSE`.
452
+
453
+ ## Development
454
+
455
+ ```
456
+ python3 -m venv .venv
457
+ .venv/bin/pip install -e '.[dev]'
458
+ .venv/bin/python -m pytest -q
459
+ ```
460
+
461
+ The unit tests in `tests/unit` are self-contained. End-to-end checks on real documents are run outside
462
+ this repository, since the test corpora are not shipped.
463
+