Adding furigana to a Word document sounded like a small extension to EZFurigana.
Furigana are the small pronunciation guides that appear above Japanese kanji. I read Japanese myself, and I still run into kanji that I cannot read immediately. Adding furigana manually in Microsoft Word is fine for a few words. On a longer document, it gets tedious very quickly.
So I wanted EZFurigana to accept a Word document and return it with furigana automatically added.
My initial mental model was:
- Open the DOCX.
- Find the Japanese text.
- Generate the readings.
- Save the file.
That sounded fairly straightforward.
Then I looked inside a DOCX file.
It soon turned out to be quite the nightmare.
A DOCX is not a text file
A DOCX file is not what it seems. It's not one document, but rather a ZIP package containing XML files, styles, images, fonts, scattered into many pieces which are glued together.
For a typical document, most of the main body content is stored in:
document.xml
One option would have been to extract all the text, add furigana, and build a new Word document.
The people who invented the DOCX file, however, didn't want people's lives to be easy.
A Word document can contain tables, hyperlinks, images, and structures that are difficult to understand. Rebuilding the document would mean recreating all of them correctly.
Instead, EZFurigana modifies the existing Word document and leaves everything else alone. It changes only document.xml, the part containing the document's main content, while preserving the rest of the file as it is.
But life keeps getting worse.
document.xml does not contain text in the convenient form you might expect.
Suppose Word displays:
今日は東京駅に行きます。
That sentence may be split across several runs, which are separate elements containing pieces of text.
A formatting change can create a new run. So can hyperlinks, tables, images, and anything else Word lets you add. Word has been secretly slicing your sentences the whole time.
A single visible word can therefore span several elements.
That makes this kind of operation unsafe:
- find "東京駅"
- replace it with annotated "東京駅"
The characters may actually be chopped up into different parts of the file.
So EZFurigana keeps track of where each piece of text came from before changing anything. If the text crosses parts of the document that are too risky to rewrite, it simply leaves it alone.
This isn't perfect. But I would rather miss some furigana than fail to glue all the pieces back together.
Adding Word's actual furigana markup
Microsoft Word has native support for ruby, which is the general term for annotations such as furigana.
Internally, Word represents it with structures such as w:ruby, w:rubyBase, and w:rt.
Conceptually:
<w:ruby>
<w:rt>
<!-- とうきょう -->
</w:rt>
<w:rubyBase>
<!-- 東京 -->
</w:rubyBase>
</w:ruby>
w:rubyBase contains the original text and w:rt contains the reading.
If a run contains:
A東京B
and only 東京 needs furigana, EZFurigana effectively has to split it into:
A
東京 + ruby
B
while keeping the original formatting.
It also detects existing ruby instead of blindly adding another annotation.
Eventually I had a DOCX that Microsoft Word could open and display correctly.
Then I needed to preview it in the browser.
Rendering DOCX: The Nightmare Continues
EZFurigana lets users review generated readings and correct them, so I needed some way to display the modified document before download.
Creating a valid DOCX does not solve that.
Browsers do not understand Word documents natively. Something has to reconstruct the margins, fonts, spacing, and all the other pieces, then somehow apply the secret glue that makes it look like Word again.
- modified DOCX
- LibreOffice
- preview pages
This gets much closer to the original document layout.
But LibreOffice is not Microsoft Word, and this is where furigana caused problems again.
LibreOffice gets surprisingly close, but its handling of East Asian typography, especially ruby, is not quite where I want it to be. In particular, it adds extra spacing below ruby, so some documents look different from the same file opened in Microsoft Word.
I don't really blame the LibreOffice maintainers. They already have the job of gluing the pieces of a DOCX nightmare back together, and ruby is only one tiny part of that mess. I am happy to let them keep whatever sanity they have left.
What I Ended Up Shipping
The final flow looks roughly like this:
- upload DOCX
- map the Word document
- generate furigana
- rewrite the safe parts of the XML (keeping the glue)
- render a preview with LibreOffice
- download the modified DOCX
I am fine with that tradeoff if it means I can wake up from this nightmare. Plus, missing a few annotations is much better than destroying somebody's document.
I started this feature thinking DOCX support would be a straightforward addition.
Instead, I ended up dealing with a file split into dozens of pieces, text that refuses to stay in one place, formatting that must survive surgery, and a second office suite just to render the result.
All because I didn't want to add furigana manually.
Was it worth it? You can try the DOCX feature on EZFurigana and let me know. If you have a strange Word document that breaks something, I would like to hear about it. Those are usually the best way to find what I missed.
Top comments (2)
Your approach to handling the complexities of the DOCX format is quite insightful, especially with how you've navigated the challenges of maintaining document integrity while adding furigana. The decision to modify only the
document.xmlrather than rebuilding the entire document makes perfect sense to mitigate risks. I’ve encountered similar issues when working with document formats that have intricate structures; leveraging existing formatting while applying changes is often the safest route. If you need another set of hands for enhancing the preview functionality or further refining the parsing logic, I’m open to exploring a paid collaboration. What strategies have you considered for improving the user feedback loop for the generated readings?Thanks! For now, I let users report any incorrect readings. I’m still thinking about ways to make that process smoother.