Open-source project
transpect/docx2tex avatar
transpect/docx2tex

docx2tex: converting Word documents to LaTeX through Hub XML

Converts Microsoft Word docx to LaTeX

676 stars65 forksXSLTBSD-2-Clause

At a glance

What is it?
docx2tex turns a Microsoft Word docx file into LaTeX by routing it through three stages, docx2hub, evolve-hub and xml2tex, and it is written mostly in XSLT with a XProc pipeline underneath. The interesting part is not the conversion but the five documented hooks, which let you map Word styles, inject your own transformation stages and handle fonts that are not Unicode compatible.
Who is it for?
Adopt docx2tex if you receive Word documents that have to become LaTeX and you need the output to be a real LaTeX file rather than a PDF, especially if your Word files use styles you want mapped to chapters and sections rather than a generic conversion. Do not adopt it for a quick one-off conversion of a simple document, because the toolchain is a Java and XProc stack with a three-stage pipeline and several configuration files, and the documented Java support stops at 1.15.
Can I use it commercially?
Yes. BSD-2-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 114 days ago.
What is it written in?
Mainly XSLT, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Three stages, and only the middle one is really configurable

The docx2tex pipeline consists of three macroscopic steps, and describing them in order explains both the design and the failure modes.

The first step is docx2hub, which transforms a docx file into a Hub XML representation, the Hub being the intermediate document model developed by le-tex. The README notes that this step is hardly configurable, and the repository contains a docx2hub directory, which is a submodule since the clone has to be recursive.

The second step is evolve-hub, described as a bag of XSLT modes that transform paragraphs with list markers and hanging indentation into proper nested lists, create a nested section hierarchy, and group images with their figure titles. Only some of those modes are used by docx2tex, and the orchestration happens in evolve-hub.xpl with the detail in evolve-hub-driver.xsl.

The third step is xml2tex, which turns the Hub XML into the LaTeX output. It is the stage whose configuration file, conf/conf.xml, is used by default and works with the styles defined in Microsoft Word's normal.dot.

The fact that the first stage is nearly opaque and the second and third are where the work happens is the shape of the whole tool. The structural work of getting a Word file into a sane document model is done once and generically, and everything that varies per publisher, per house style or per document set is expressed in the stages you can touch.

The primary language of the repository is XSLT, and the directory listing shows the components: a calabash directory for the XML Calabash runner, xpl/ for the XProc pipeline, xsl/ for the stylesheets, conf/ for the configuration, and separate directories for the mml2tex and fontmaps work.

The command line is short, and the option table is where the real choices are

Running the tool is a single command on Linux and macOS:

bash
./d2t [options ...] myfile.docx

The Windows equivalent is a batch script whose options are, in the README's words, somewhat limited compared to the Bash script:

bash
d2t.bat myfile.docx

The option table is where the decisions live. The -o option sets the path to a custom output directory, -c the path to a custom docx2tex configuration file, -f the path to a custom fontmaps directory, and -d turns on debug mode. Three options change how the document itself is treated. The -m option chooses the MathType source, with the values ole, wmf or ole+wmf, which tells you the tool is prepared for equations that arrived from MathType rather than from Word's own equation editor. The -t option chooses the table model, with the values tabularx, tabular or htmltabs. The -p option generates a PDF with pdflatex, which is the one option that pulls a LaTeX toolchain into the run.

The last two options are for extending the pipeline rather than configuring it. The -e option takes a custom XSLT stylesheet for evolve-hub overrides, and -x takes a custom XSLT stylesheet for postprocessing the evolve-hub results.

For anyone confined to Windows without Cygwin, WSL or MinGW, the README says d2t.bat does not have all the flags, and that you can invoke the Calabash runner yourself:

bash
calabash/calabash.sh -o result=myfile.tex -o hub=myfile.xml xpl/docx2tex.xpl docx=myfile.docx conf=conf/conf.xml

That invocation shows the pipeline's shape directly. It is a XProc pipeline, docx2tex.xpl, with the input document and the configuration file bound as named ports, and it writes two outputs, the LaTeX result and the intermediate Hub XML. The Hub XML output is what you inspect when something comes out wrong, and the debug option feeds a file in the same intermediate form.

Mapping Word styles to LaTeX commands, in CSV or in XML

The configuration is where most of the work in a real deployment happens, and there are two formats with different trade-offs.

The CSV-based configuration format permits a simple way to map from MS Word styles to LaTeX commands. For each MS Word style name you create a line with three semicolon separated values: the MS Word style name, the LaTeX start statement and the LaTeX end statement. The example in the README is a four-line file:

latex
Heading 1   ; \chapter{     ; }
Heading 2   ; \section{     ; }
Heading 3   ; \subsection{  ; }
Quote       ; \begin{quote} ; end{quote}

Two practical notes. The CSV can be edited with a simple text editor or with a spreadsheet application, which is the point of the format, because the people who know what a Word style should become are often not the people who will fight your XSLT. And the semicolon separated triple is easy to get wrong in a subtle way: the LaTeX start and end statements are matched per style, and a missing brace shows up as broken output rather than as a configuration error.

The xml2tex configuration format is recommended for a deeper level of configuration but requires basic knowledge of XML and XPath. The configuration in conf/conf.xml is used by default and works with the styles defined in Microsoft Word's normal.dot, so out of the box the tool already knows about the standard Word style set. If you want to configure docx2tex for other styles you edit that file or pass a custom one with the conf option.

The practical rule is that CSV is for style mapping and xml2tex is for everything else, including the character-level work. The README is specific about the division in the fontmap section: fontmaps map non-Unicode characters to Unicode, and if you want to map characters from Unicode to LaTeX you use the character map in the xml2tex configuration instead.

Five hooks, and the one that turns arbitrary paragraphs into headings

The README lists five major hooks for adding your own processing: CSV or xml2tex configuration, XSLT that is applied between evolve-hub and xml2tex, XSLT that modifies what happens in evolve-hub, and fontmaps.

The XSLT that runs between the stages is the one that sounds simplest. It works on the result of evolve-hub, and if debugging is enabled, on the file named [basename].debug/evolve-hub/70.docx2tex-postprocess.xml, which is a useful thing to know because it tells you exactly which intermediate document to look at when your transform does not fire. The location of the file, an absolute URI or a path relative to the directory where d2t and d2t.bat live, is provided to d2t via the -x option. This processing is applied before the xml2tex configuration, so your XSLT should transform Hub, in the DocBook namespace, to Hub.

The XSLT that runs during evolve-hub is the more powerful of the two, and the README explains how it differs. The custom-xsl stylesheet is passed as an option, whereas the evolve-hub one is passed to the pipeline on the input port custom-evolve-hub-driver, or using the -e option of d2t.

The example that ships with the project, custom-evolve-hub-driver-example.xsl, retains empty paragraphs that would otherwise be removed by default in one of the XSLT passes that comprise evolve-hub. It was created in response to a user request on the docx2hub issue tracker.

Then there is the technique for the case every academic publisher hits: if you want to create \chapter, \section and similar headings from arbitrary docx paragraphs, you add a template that sets the paragraph's @role attribute to Heading1, Heading2 and so on. For paragraphs that are not removed during evolve-hub, the same can be done in the -x stylesheet. The README strongly advises xsl:import of the default evolve-hub customization, following the example, and that advice is the difference between your customization layering on top of the pipeline and your customization replacing it.

The fifth hook is fontmaps, which handle fonts that are not Unicode compatible. You create a fontmap, store it in a directory, and pass the path of that directory to docx2tex with the -f option.

The toolchain asks for an old Java, and that is the practical snag

The requirements section is short and it contains the constraint that will affect you most: Java 1.7 up to 1.15, with more recent versions not yet tested. The README also warns that Java 11 has a bug with file URIs and should be avoided, and that Java 13 is safe again.

That is a peculiar set of constraints. The upper bound of 1.15 is the headline, and it means a current Java installation will not be the supported one. The Java 11 warning is the sort of detail that only appears because somebody hit it, and the advice to use 13 sidesteps it, but 13 is itself old enough that some distributions no longer package it.

The other requirement line is that it works on Windows, Linux and Mac OS X, which is unremarkable and worth stating anyway given how often cross-platform tools quietly are not.

The installation routes are correspondingly simple. The README says to download the latest docx2tex release, or get the source via Git, and it notes in the same sentence that you have to add the --recursive option in order to clone docx2hub with submodules:

bash
git clone https://github.com/transpect/docx2tex --recursive

The recursive clone is not optional, and the reason is the component layout. The top level contains directories for docx2hub, evolve-hub, xml2tex, mml2tex, fontmaps, cascade, mml-normalize, schema, xproc-util, xslt-util, htmlreports and xmlcatalog, and the sibling projects are pulled in as submodules through the .gitmodules file. The transpect framework is the umbrella, docx2tex is developed by le-tex on top of it, and the main author of both docx2tex and the underlying xml2tex is @mkraetke.

The licence is BSD-2-Clause, which is permissive and a good fit for a converter that institutional workflows tend to embed.

Debug output is the practical route into a wrong conversion

When a conversion comes out wrong, the useful question is which stage broke it, and the tool is built to let you answer that.

The -d option turns on debug mode, and the README mentions the intermediate file that the postprocess XSLT works on when debugging is enabled, named [basename].debug/evolve-hub/70.docx2tex-postprocess.xml. The naming shows the pipeline's internal structure directly, because the stages are numbered and the debug directory mirrors the pipeline's internal layout.

The Calabash invocation gives you a second handle. It writes two outputs, result=myfile.tex for the LaTeX and hub=myfile.xml for the intermediate Hub XML. Being able to look at the Hub XML is the difference between debugging a transformation and guessing at it, because the Hub representation is a normalised document model, so a list that was flattened by Word or an image that lost its caption shows up there in a form you can read.

The customisation points follow the same logic. The -e hook runs during evolve-hub, which is the stage that turns paragraphs with list markers and hanging indentation into proper nested lists, so that is where you intervene if the nesting is wrong. The -x hook runs after evolve-hub and before the xml2tex configuration, so it is where you intervene if the structure is right but the LaTeX is not. The README makes the boundary explicit when it says your XSLT should transform Hub to Hub, and that constraint is the reason the two hooks are not interchangeable.

The one thing the debugging path cannot do is tell you what a Word document meant. A document that uses direct formatting rather than named styles, or a heading applied by hand rather than by a style, will convert to something structurally different, and the fix is either a role attribute through the -e hook or a style map in the configuration.

Editorial conclusion

Adopt docx2tex if you receive Word documents that have to become LaTeX and you need the output to be a real LaTeX file rather than a PDF, especially if your Word files use styles you want mapped to chapters and sections rather than a generic conversion. Do not adopt it for a quick one-off conversion of a simple document, because the toolchain is a Java and XProc stack with a three-stage pipeline and several configuration files, and the documented Java support stops at 1.15. Before you commit, install a supported Java version, decide between the CSV style mapping and the xml2tex configuration, and check whether your documents contain MathType equations, since the -m option exists specifically to choose the MathType source format and the topics list names mathtype, mtef, omml and ooxml.

Frequently asked questions

What are the requirements for running docx2tex?

Java 1.7 up to 1.15, with more recent versions not yet tested, and the README advises avoiding Java 11 because of a file URI bug while noting Java 13 is safe again. It works on Windows, Linux and Mac OS X.

How do I convert a docx file to LaTeX with docx2tex?

Run ./d2t with options and the file name on Linux and macOS, or d2t.bat myfile.docx on Windows, whose options are more limited. You can also invoke the Calabash runner directly with the XProc pipeline, the docx input and the conf option, which writes both a LaTeX result and the intermediate Hub XML.

What are the three stages of the docx2tex pipeline?

docx2hub converts the docx into a Hub XML representation and is hardly configurable, evolve-hub applies XSLT modes that build nested lists, a section hierarchy and image groupings, and xml2tex turns the Hub XML into the LaTeX output using the configuration in conf/conf.xml by default.

How do I map Word styles to LaTeX commands in docx2tex?

With the CSV configuration format you write one line per Word style with three semicolon separated values, the style name, the LaTeX start statement and the LaTeX end statement. For deeper configuration the xml2tex format in conf/conf.xml is recommended, and it requires basic knowledge of XML and XPath.

How do I turn arbitrary docx paragraphs into LaTeX headings with docx2tex?

You add a template that sets the paragraph's @role attribute to Heading1, Heading2 and so on, in the custom evolve-hub stylesheet passed with the -e option. The README advises xsl:import of the default evolve-hub customization, and for paragraphs that survive evolve-hub the role can be set in the -x stylesheet instead.

Official sources

  1. Issues
  2. License: BSD-2-Clause
  3. README
  4. Releases
  5. transpect/docx2tex on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/transpect-docx2tex.svg)](https://hysenlabs.com/projects/transpect-docx2tex)

Was this useful?