/tanniedi¶
Round-trips a LaTeX paper to Word for human copy-editing (pack) and applies the editor's tracked changes back into the original .tex files (unpack), preserving LaTeX formatting, equations, and cross-references.
revision-editingTanniedi — LaTeX ↔ Word Round-Trip for Language Editing¶
You are automating the round-trip of sending a LaTeX paper to language editor Di Kilpert for copy-editing. The skill has two modes:
pack: Convert a LaTeX project → Word manuscript + separate Word file with figures & editable tablesunpack: Apply edits from the returned Word file back into the original.texfile
This skill works from any LaTeX project directory, not just a specific one.
Prerequisites¶
Before doing anything else, verify prerequisites:
-
Pandoc: Run
Stop execution if pandoc is not found.pandoc --version. If missing, tell the user: -
Python 3: Run
python --version. Must be 3.x. -
python-docx: Run
python -c "import docx; print(docx.__version__)". If missing, tell the user to install it:pip install python-docx.
PACK workflow (/tanniedi pack)¶
Step 1: Discover project files¶
Find the main .tex file in the current working directory:
- If there is exactly one
.texfile → use it. - If there are multiple
.texfiles → look for one matching*main*.texor the one containing\begin{document}. If still ambiguous, ask the user which file to use.
From the main .tex file, extract:
- Bibliography file: Look for
{...}or\addbibresource{...}. Extract the filename (add.bibextension if not present). Verify the.bibfile exists. - Included sub-files: Recursively resolve
\input{...}and\include{...}directives. Add.texextension if not present. Read all sub-file contents for processing. - Graphics paths: Check for
\graphicspath{...}to know where images are stored. - Figure environments: Extract all
\begin{figure}...\end{figure}and\begin{figure*}...\end{figure*}blocks (including content). Track the\includegraphics{...}path and\caption{...}text within each. - Table environments: Extract all
\begin{table}...\end{table}and\begin{table*}...\end{table*}blocks (including content). Track\caption{...},\label{...}, and the\begin{tabular}...\end{tabular}content within each.
Step 2: Create output directory¶
Create _tanniedi/ in the project root. If it already exists, ask the user whether to overwrite or abort.
Step 3: Save originals for later unpack¶
- Copy the original main
.texfile →_tanniedi/_original.tex - If there are
\input{}/\include{}sub-files, also copy them into_tanniedi/_original_inputs/preserving relative paths.
Step 4: Pre-process the .tex¶
Create a stripped version of the .tex content (combining main file + resolved inputs into one logical document):
- Remove all
\begin{figure}...\end{figure}blocks (includingfigure*). Number figures sequentially (Figure 1, Figure 2, …) in the order they appear. Replace each with a LaTeX comment and a visible placeholder paragraph that pandoc will render into the Word document: - Remove all
\begin{table}...\end{table}blocks (includingtable*). Number tables sequentially (Table 1, Table 2, …) in the order they appear. Replace each with a LaTeX comment and a visible placeholder paragraph: - Keep everything else intact: preamble,
\section{},\cite{},\ref{},\footnote{}, math environments, comments, etc. - Save →
_tanniedi/_stripped.tex
Step 5: Convert to Word via pandoc¶
Run:
pandoc _tanniedi/_stripped.tex --from=latex --to=docx \
--bibliography=<bibfile>.bib --citeproc \
--output=_tanniedi/manuscript.docx
Key flags:
- --citeproc resolves citations to readable author-year text (e.g. "Acemoglu and Wolitzky (2011)")
- Pandoc handles \ref{} cross-references, math, footnotes, formatting natively
If the .bib file is not found or pandoc fails on bibliography, try without --bibliography and --citeproc flags and warn the user that citations will appear as raw keys.
If pandoc returns errors, show them to the user and attempt to fix common issues (e.g., missing packages, encoding problems).
Step 6: Generate original plain text for unpack comparison¶
Run:
pandoc _tanniedi/_stripped.tex --from=latex --to=plain --wrap=none \
--bibliography=<bibfile>.bib --citeproc \
--output=_tanniedi/_original_plain.txt
This plain text version serves as the baseline for diffing against the edited Word file during unpack. Use the same --bibliography and --citeproc flags as Step 5 so citation rendering matches.
Step 7: Build figures-and-tables Word document¶
Write a Python script (inline or as a temp file) using python-docx to create _tanniedi/figures_and_tables.docx:
For each figure extracted in Step 1:
1. Determine the image file path. Check for the file with extensions .png, .jpg, .jpeg, .pdf in order.
2. If the image is .pdf:
- Try converting to PNG using Python (e.g., pdf2image if available, or subprocess call to magick / ImageMagick).
- If conversion fails, insert a placeholder paragraph: "[Figure image: .png, .jpg, or .jpeg, insert it into the Word document using document.add_picture(path, width=Inches(5.5)).
4. Add the caption text below the image as a paragraph with bold "Figure N:" prefix.
For each table extracted in Step 1:
1. Parse the \begin{tabular}{...}...\end{tabular} content:
- Determine column count from the column spec (e.g., {lccr} → 4 columns).
- Split rows by \\.
- Split cells by &.
- Strip LaTeX formatting: convert \textbf{...} → bold run, \textit{...} / \emph{...} → italic run, remove \toprule, \midrule, \bottomrule, \hline, \cline{...}.
- Handle \multicolumn{N}{spec}{text} by merging cells where python-docx supports it, or by placing the text in the first cell with a note.
2. Create a native Word table using document.add_table(rows=..., cols=...).
3. Populate cells with the parsed content, applying bold/italic formatting to individual runs as needed.
4. Add the caption text above or below the table as a paragraph with bold "Table N:" prefix.
5. If parsing fails for a table, fall back to inserting the raw LaTeX source as a code-formatted paragraph.
If there are no figures and no tables, skip creating this file and inform the user.
Save → _tanniedi/figures_and_tables.docx
The Python script should handle errors gracefully — if one figure or table fails, continue with the rest and report issues at the end.
Step 8: Print summary¶
Display a clear summary to the user:
Pack complete!
Output files in _tanniedi/:
- manuscript.docx — Main text for Di to edit
- figures_and_tables.docx — Figures and tables (for reference/editing)
- _original.tex — Original .tex (preserved for unpack)
- _original_plain.txt — Plain text baseline (for unpack diffing)
- _stripped.tex — Stripped .tex (intermediate, can ignore)
Instructions for Di:
1. Edit manuscript.docx — track changes or edit freely
2. Save the edited file in _tanniedi/ (keep or rename as you wish)
3. Do NOT restructure sections or delete citation markers
4. Tables in figures_and_tables.docx are editable — edit if needed
5. When done, Johan will run /tanniedi unpack to apply changes back to LaTeX
UNPACK workflow (/tanniedi unpack)¶
Step 1: Locate files¶
In the current project directory, verify:
_tanniedi/directory exists_tanniedi/_original.texexists_tanniedi/_original_plain.txtexists
Find the edited .docx file:
- Look for any .docx file in _tanniedi/ that is NOT manuscript.docx (the original), NOT figures_and_tables.docx, and NOT a file starting with ~$ (Word temp file).
- If no such file is found, check if manuscript.docx has been modified more recently than _original_plain.txt (the user may have edited it in place).
- If still ambiguous or no edited file found, ask the user which .docx file contains the edits. Accept a path.
Step 2: Convert edited Word to plain text¶
Run:
pandoc "_tanniedi/<edited_file>.docx" --from=docx --to=plain --wrap=none \
--output=_tanniedi/_edited_plain.txt
Step 3: Compute diff¶
Write a Python script using difflib to produce a word-level diff between _tanniedi/_original_plain.txt and _tanniedi/_edited_plain.txt:
- Read both files.
- Normalize both texts:
- Replace en-dash (
–) and em-dash (—) with--and---for matching - Normalize whitespace (collapse multiple spaces/newlines)
- Strip leading/trailing whitespace from lines
- Split into sections based on headings (lines that were
\section{},\subsection{}etc. — they appear as plain text headings in the pandoc output). - For each section, compute a word-level diff using
difflib.SequenceMatcherordifflib.ndiff. - Save the diff report →
_tanniedi/_diff_report.txt
Step 4: Apply changes to the .tex file¶
This is the core algorithm. Read _tanniedi/_original.tex (and any sub-files from _tanniedi/_original_inputs/ if they exist).
The approach:
- Tokenize the original
.texinto a stream of tokens, each classified as either: - Prose token: A word that would appear in the pandoc plain-text output (i.e., visible text content)
- Command token: LaTeX markup that is invisible in plain text —
\cite{...},\citet{...},\citep{...},\ref{...},\label{...},\textit{...}(the command, not the content),\textbf{...}(the command, not the content),\emph{...},\footnote{...}, comments (%...), environment markers (\begin{...},\end{...}), preamble commands,\section{...}(the command itself), etc. -
Whitespace/structure token: Newlines, blank lines,
\\,& -
Extract the prose sequence from the original
.textokens — this should closely match_original_plain.txtafter normalization. -
Align the original prose sequence with the edited prose sequence (from
_edited_plain.txt) usingdifflib.SequenceMatcher: - Matching blocks → keep the original
.textokens (prose + surrounding commands) unchanged - Deleted words → remove the prose token but preserve adjacent command tokens (e.g., if a word before
\cite{...}is deleted, keep the cite) - Inserted words → insert new prose tokens at the correct position
-
Changed words → replace the prose token in-place, keeping surrounding LaTeX commands
-
Special character handling during patching:
- If the editor changed
--to–(en-dash), convert back to--in LaTeX - If the editor changed
---to—(em-dash), convert back to---in LaTeX - Preserve
~(non-breaking space) where it existed in the original - Preserve
\%,\&,\$escaping -
If the editor introduced new special characters, escape them for LaTeX
-
Preserve these LaTeX constructs absolutely (never modify or remove):
\cite{...},\citet{...},\citep{...}and their arguments\ref{...},\label{...},\pageref{...}\footnote{...}(the command wrapper; content inside may be edited if it appeared in plain text)\textit{...},\textbf{...},\emph{...}(the command; content may change)- All preamble content (everything before
\begin{document}) - All
% [FIGURE REMOVED: ...]and% [TABLE REMOVED: ...]placeholders and their accompanying\bigskip\noindent\textbf{...}\bigskipmarker lines \section{...},\subsection{...},\subsubsection{...}commands (content may change if heading text was edited)-
Math environments:
$...$,\[...\],\begin{equation}...\end{equation}, etc. -
Reconstruct the patched
.texby emitting all tokens in order (command tokens preserved, prose tokens updated where the diff indicates changes).
Step 5: Re-insert figures and tables¶
Read the % [FIGURE REMOVED: ...] and % [TABLE REMOVED: ...] comment placeholders in the patched output. Each placeholder consists of two lines: the comment line and the visible \bigskip\noindent\textbf{...}\bigskip marker. Replace both lines of each placeholder with the corresponding original \begin{figure}...\end{figure} or \begin{table}...\end{table} block from _tanniedi/_original.tex.
If figures_and_tables.docx was edited (tables modified), attempt to detect table changes:
- Convert figures_and_tables.docx tables back to LaTeX tabular format
- Compare with original tables
- If changes are detected, show a diff to the user and ask whether to apply
For simplicity in the first version: always re-insert the original figure/table blocks unchanged and note to the user that table edits from Word must be transferred manually.
Step 6: Write output¶
Save the patched .tex file as <original_name>_edited.tex in the project root (not inside _tanniedi/).
Step 7: Show diff and ask user¶
Display a summary of changes to the user:
- Show statistics: number of words changed, inserted, deleted.
- Show a section-by-section summary of edits (e.g., "Section 2: 14 words changed, 3 deleted, 5 inserted").
- For each section with changes, show representative examples of edits (first few changes per section).
- Read and display the full diff from
_tanniedi/_diff_report.txtif the user wants it.
Then ask: "Overwrite the original .tex file with the edited version?"
- If yes → copy <name>_edited.tex over the original .tex file (back up original to _tanniedi/_original.tex if not already there).
- If no → leave <name>_edited.tex alongside the original for manual review.
Edge cases and error handling¶
Multiple .tex files with \input{}/\include{}¶
- The pack step must resolve
\input{}/\include{}recursively to find all prose content. - Track which prose tokens belong to which source file.
- During unpack, write changes back to the correct source file.
- If sub-files exist, produce one
_editedversion per modified sub-file.
No bibliography file found¶
- Run pandoc without
--bibliographyand--citeproc. - Warn the user that citations will appear as raw LaTeX keys in the Word document.
No figures or tables¶
- Skip creating
figures_and_tables.docx. - Adjust the summary message accordingly.
PDF-only figures¶
- Attempt conversion via Python or ImageMagick.
- If conversion is not possible, insert a text placeholder in the Word document.
- Never fail the entire pack because of one unconvertible figure.
Complex tables¶
- Parse heuristically: handle
booktabscommands (\toprule,\midrule,\bottomrule),\multicolumn{}{}{},\textit{},\textbf{},\emph{},\cmidrule{}. - If parsing fails entirely for a table, insert the raw LaTeX as a code block in the Word document with a note.
Special characters in diff¶
--(en-dash) and---(em-dash): normalize during comparison, restore LaTeX form in output.~(non-breaking space): preserve original usage.\%,\&,\$: keep escaped in LaTeX output.- Curly quotes introduced by Word: convert back to straight quotes or LaTeX equivalents.
- Ligatures (fi, fl, etc.) that Word may introduce: normalize back to individual characters.
Pandoc conversion failures¶
- If pandoc fails, show the error message and suggest common fixes.
- For encoding issues, try adding
--from=latex+smartor specifying input encoding. - Never silently swallow pandoc errors.
Empty or minimal edits¶
- If the diff shows zero changes, inform the user and do not create an
_edited.texfile.
Important notes¶
- Never modify the original
.texfile during pack — only create files inside_tanniedi/. - Always preserve LaTeX commands during unpack — the editor edits prose, not markup.
- The
_tanniedi/directory is the working area — all intermediate files go there. - Idempotency: Running pack twice should produce the same result (after confirming overwrite of
_tanniedi/). - Platform: This runs on Windows. Use forward slashes in Python paths. Pandoc commands run in bash.