Word renderer#

The WordRenderer turns a calculation into a Word document (.docx) whose equations are native Word equations, the kind Word’s own equation editor makes. Click into any value or symbol in Word to edit it. Fonts and sizes come from the document’s styles, so every equation matches the text around it.

Installing#

The renderer builds documents with python-docx, an optional dependency:

$ pip install "handcalcs[word]"

Usage#

from handcalcs import HandCalcs
from handcalcs.renderers import WordRenderer

source = '''
## Roots of a quadratic
from math import sqrt
a = 2; b = -7; c_0 = 3
x_1 = (-b + sqrt(b**2 - 4*a*c_0)) / (2*a)  # larger root
'''

HandCalcs(WordRenderer())(source, destination="quadratic.docx")

With destination=, the document is saved there and its Path is returned. Without it, the renderer returns a python-docx Document: call document.save(path) yourself, or keep adding to it with python-docx first. The renderer’s filepath= option saves every render it makes, wherever it is called from.

This works from a plain .py script or any Python program, with no Jupyter or export step. Pass a pathlib.Path to render a whole file: HandCalcs(WordRenderer())(Path("calc.py"), destination="calc.docx").

Layout#

handcalcs

Word

a run of calc lines

one equation, flush left, its rows aligned on the first =

a params line (b = 300; d = 500)

a borderless table, one equation per cell, so the grid keeps its columns

## Heading

a paragraph in the Heading 1 style (### → Heading 2, and so on)

comment lines

a text paragraph

an import

the import’s source in the HC Code style

if/for/function headers

a text paragraph with the condition as an inline equation: “Since (a > 1) → (2 > 1) is True:”

a nested block

indented 0.5” per level

an inline comment

normal text after the equation; on its own row when the line shows substitutions

a -m calc or a line continuation (\)

extra rows in the same aligned equation

Names follow the shared naming rules: subscripts, Greek letters, accents and the symbols option look the same as in the other renderers. A single letter is italic, as in print; a word (max, Rd) is upright and stays one run, so you can edit it as one word in Word.

Choosing a layout for Google Docs and LibreOffice#

Word aligns the rows of a calc on their = signs with an equation array. Google Docs and LibreOffice don’t show equation arrays properly: Google Docs runs the rows together on one line, and LibreOffice draws the alignment marks as stray symbols. For a document headed there, use the "lines" layout. Each row becomes an equation of its own (no = alignment), and rows that continue the one above are indented a little:

WordRenderer(filepath="calc.docx", layout="lines")

layout

Looks right in

Rows

"aligned" (default)

Word

one equation per run of calcs, aligned on =

"lines"

Word, Google Docs, LibreOffice

one equation per row, not aligned

A comment command changes the layout for the calcs that follow it:

# hc: layout = "lines"

Templates and reports#

A company template. template= starts each document from a .docx. Its styles, page setup, headers and footers and any content already in it (a title block, say) are kept, and the calculation is added after that content:

WordRenderer(template="company.docx", filepath="beam.docx")

Several calcs in one report. document= appends to a python-docx Document you already have, so one report can hold many calculations with your own text between them:

import docx
from handcalcs import HandCalcs
from handcalcs.renderers import WordRenderer

report = docx.Document("company.docx")
report.add_heading("Beam design", level=1)
calc = HandCalcs(renderer=WordRenderer(document=report))
calc(beam_source)
report.add_paragraph("The column carries the beam reaction.")
calc(column_source)
report.save("report.docx")

Give template= or document=, not both.

Styles#

The renderer uses the document’s Normal and Heading 1-Heading 9 styles, and adds two of its own when the document doesn’t define them:

Style

Kind

Used for

Default

HC Math

paragraph, based on Normal

paragraphs holding equations

6 pt before and after a group, none between its rows

HC Code

character

imports

Consolas

To restyle them, define styles with these names in your template. Equations take their paragraph’s font size, so a larger HC Math gives larger equations. If a template has no Heading N style, that heading becomes a bold paragraph and handcalcs warns you.

Options#

WordRenderer(
    filepath=None,      # also save every render here
    layout="aligned",   # or "lines"
    template=None,      # a .docx to start each document from
    document=None,      # a python-docx Document to append to
)

An unknown layout or a missing template raises an error when the renderer is created.

Comment commands#

command

effect in Word

-f CODE

formats calculated values, as in every renderer

-m

puts the symbolic, numeric and result columns on rows of their own

-b

inserts an empty paragraph

-i

leaves the line out

-h, ;

a params table

layout = "lines"

one equation per row for the calcs that follow

Units#

pint and forallpeople values show their number as math and their unit as text: 2.8125 kN·m. The number takes the format code. A pint unit is written in the registry’s format, so set ureg.formatter.default_format = "~P" for short unit symbols (mm² rather than millimeter ** 2).

In Jupyter#

%%render has no Word display, so the Word renderer isn’t a jupyter_renderer. To make a document from a notebook, put the calculation in a Python cell:

document = HandCalcs(renderer=WordRenderer(filepath="calc.docx"))(source)

To convert rendered Markdown instead, pandoc turns the Markdown renderer’s output into Word equations too:

$ pandoc calc.md -o calc.docx

pandoc centres every equation, splits upright words into single letters and flattens params grids into one row, so the Word renderer gives a closer match to handcalcs’ layout.

Notes#

  • A list or tuple longer than 10 items shows its first and last 3 items around “…”, as in every renderer (the max_items and edge_items settings). An equation row can’t break across lines, so use a line continuation (\) to break a long equation yourself.

  • HandCalcs(renderer=WordRenderer()).demo() renders the general handcalcs demo as a document.

Source#

The Word renderer’s handlers return OMML strings, Word’s math markup, the way the LaTeX renderer’s return LaTeX, so the base renderer’s line machinery works on them unchanged. Only complete() builds the document. See Example: a non-text renderer.