Documentation page: RTF and DOCX Output. This page is also available as Markdown at rtf-and-docx-output.md.

RTF and DOCX Output

Beyond PDF, PD4ML's two-phase conversion model (see the Programmer's Manual's reading/writing the output section) extends to two further destination formats: RTF and OOXML/DOCX. The same parsed HTML document that produces a PDF can, without being re-parsed, also be serialized as an RTF file (opened by MS Word, WordPad, and most legacy word processors) or a modern DOCX file -- letting one source template feed a print-ready PDF and an editable, reader-facing office document from a single conversion pass. Because the two formats share almost the same conversion pipeline, supported HTML feature set, and reference application code, this page covers both together, calling out the handful of places they diverge.

MS Word is the reference target for both formats: it has by far the most complete and predictable support for the HTML/CSS feature set PD4ML maps onto RTF and DOCX markup, and is the environment PD4ML's own conversion logic is validated against. Other readers (WordPad, LibreOffice, older or third-party RTF/DOCX parsers) generally cope with the same output, but with more variable fidelity -- worth keeping in mind before committing to a reader other than Word for a given deployment.

Supported HTML features

Both formats cover the same practical subset of HTML/CSS: page margins, character- and paragraph-level text formatting, page backgrounds, ordered and unordered lists, tables -- including nested tables -- images, hyperlinks, repeating headers/footers, and forced page breaks. Nested-table support specifically matters more than it might first appear: neither RTF nor DOCX gives an inline-level element a native way to carry block-level formatting (a background color or border on a <span>, for instance) on its own, so PD4ML's converters fall back to wrapping such content in a single-cell table, the same workaround word processors themselves rely on. The practical effect is that a source document making heavy use of nested layout tables for styling purposes tends to convert with high fidelity.

HTML source converted side by side to RTF/DOCX output

Page scale and font sizes

The Word document gets the same scale as the PDF: the page's printable width divided by the HTML width. Text that is 16px (1em, 12pt) in the HTML is 12pt in the Word document with the default HTML width, and smaller with a wider one - setHtmlWidth(1600) on A4 gives about 5.5pt, as in the PDF.

  • To keep sizes given in physical units (pt, mm, cm, in) true to size while pixel-sized content still scales, use protectPhysicalUnitDimensions(true) (v40 engine). From PD4ML v4.2.0 it works for DOCX and RTF as for PDF: with an HTML width of 1600 px, font-size: 12pt stays 12pt and a 30mm table cell 30 mm.
  • From v4.2.0 the v40 engine writes font sizes rounded to the nearest half-point. Before, they were cut to a whole point, so most text came out 1pt smaller than in the PDF (12pt as 11pt).

Boxes and backgrounds

A block with a background color or a border is written as a single-cell table, so that its size, padding and background enclose all of its content, as in the PDF. From PD4ML v4.2.0 (v40 engine):

  • An empty box, like <div style="width: 50mm; height: 20mm; background: red"></div>, is written wherever it is. Before, a block without content was taken for a spacer and written only right after a block with text, so an empty box at the start of the document, or right after another empty block, was missing.
  • In RTF, a background with its own width or height gets a box of that size. Before, without padding, it became paragraph shading as wide as the text column and one line high.
  • RTF table cells without a border have no border in Word. Before, Word drew a thin line around them.
  • In DOCX, an empty spacer with a height of its own, like <div style="height: 40mm"></div>, is an empty paragraph of exactly that height, so the text after it is where it is in the PDF; the margins of the paragraphs around it are kept in full, as in a browser. Before, the spacer was dropped.

Watermarks

From PD4ML v4.2.0. Watermarks set with setWatermark() or <pd4ml:watermark> appear in RTF and DOCX output too, placed in the page header so the word processor repeats them on the pages they belong to:

v40 engine (default) v42 engine
What is written A text watermark as Word's own (Design → Watermark): a WordArt shape behind the text, with the watermark's text, font, color, opacity, rotation and position. It can be edited or removed in Word. A transparent picture of the watermark exactly as the PDF draws it - any HTML, images included.
Page scope Page 1 and the other pages: Word paginates the document itself, so page 1 gets a header of its own when its watermarks differ (keeping the running header), and the rest follow page 2. A scope such as 3-5 cannot be matched. Any scope: a new section, with its own header, starts where the watermarks change.
Not written Watermarks without text (an <img> alone). -
pd4ml.setWatermark("<b style='font-size: 60px; color: red'>CONFIDENTIAL</b>",
        80, 300,     // left, top, points
        0.3f,        // opacity
        45,          // rotation, degrees clockwise
        1,           // scale
        true, true,  // screen, print
        null);       // every page
pd4ml.readHTML(inputStream);
pd4ml.writeDOCX(outputStream);

Before PD4ML 4.2, watermarks were left out of both formats.

Accessibility

From PD4ML v4.2.0, with a license that includes PDF/UA (the UA edition). With the v42 engine, DOCX and RTF output carries the structure Word, its Accessibility Checker and screen readers use:

HTML In the Word document
<h1>-<h6> Headings ("heading 1"-"heading 6" styles): the navigation pane, a table of contents and screen readers find them. The styles set only the outline level, so the page looks the same.
<thead> rows, or <th> rows at the top of a table Table header rows
<html lang="de"> The document language (spelling, hyphenation, screen-reader voice)
<img alt="..."> Alternative text of the picture
<img alt=""> A decorative picture, skipped by screen readers (DOCX)
<title> The document title
PD4ML pd4ml = new PD4ML();
pd4ml.useHtmlRenderer(Constants.LayoutEngine.v42);
pd4ml.readHTML(new URL("file:report.html"));   // <html lang="de">, <h1>...<h3>, tables with <th>
pd4ml.writeDOCX(outputStream);

All of this is accessibility markup, so it is written only with the UA edition's license. With any other license the output is a plain Word document: no headings, header rows, language, alternative text or title (with the v40 engine too, which otherwise writes the title only). The rest - text, layout, links, images - is the same either way. See PD4ML editions and prices.

Triggering the conversion

As with PDF, a conversion can be started from the Java API, the JSP taglib, or the command line. All three ultimately call the same underlying converter; which one to reach for is purely a matter of where in an application the conversion needs to happen.

RTF

// read and parse HTML
pd4ml.readHTML(inputStream);
boolean convertImagesToWmf = true; // 'true' improves compatibility but increases resulting file size
pd4ml.writeRTF(outputStream, convertImagesToWmf);
<pd4tl:transform ... outputFormat="rtf"> ... </pd4tl:transform>
<pd4tl:transform ... outputFormat="rtfwmf"> ... </pd4tl:transform>
java -Xmx512m -Djava.awt.headless=true -jar ./pd4ml.jar <URL> 1200 -out doc.rtf -outformat rtf
java -Xmx512m -Djava.awt.headless=true -jar ./pd4ml.jar <URL> 1200 -out doc.rtf -outformat rtfwmf

The boolean argument to writeRTF(...) (equivalently, the choice between the plain rtf and rtfwmf output formats on the taglib/CLI) controls how embedded images are encoded. Plain RTF keeps each image in its original format; RTF_WMF re-encodes every image as WMF (Windows Metafile) instead, which is what lets older or more limited RTF readers -- WordPad chief among them -- display images at all, at the cost of a noticeably larger output file. Reach for the WMF variant only when a target reader specifically needs it.

DOCX

// read and parse HTML
pd4ml.readHTML(inputStream);
pd4ml.writeDOCX(outputStream);
<pd4tl:transform ... outputFormat="docx"> ... </pd4tl:transform>
java -Xmx512m -Djava.awt.headless=true -jar ./pd4ml.jar <URL> 1200 -out doc.docx -outformat docx

writeDOCX(...) has no WMF-equivalent counterpart -- OOXML's native image embedding is already broadly compatible across DOCX readers, so there's no legacy-compatibility trade-off to make here the way there is for RTF.

Fonts and typography in Word output

Word draws a Word document's text itself, with the fonts installed on the reader's computer. To make it draw the text with the document's fonts, let the DOCX carry them:

pd4ml.setParam("pd4ml.docx.embed.fonts", "true");   // fonts up to 10 MB each; "all": any size
pd4ml.writeDOCX(outputStream);

Fonts PD4ML maps to Arial, Times New Roman and Courier New are not embedded, nor a font whose license (its embedding permissions) forbids it; embedding is off by default. RTF has no font embedding that Word reads.

With the v42 engine, the CSS font features reach Word as its own OpenType settings - ligatures, number forms and spacing, a stylistic set, contextual alternates, kerning - and small capitals and superscripts as Word's own; vertical writing becomes Word's vertical text. See CSS Font Features and Vertical Writing Modes.

A complete converter application

The reference converter below reads a URL and writes an RTF file, configuring page size, orientation, and margins along the way; producing DOCX instead is a one-line change; substitute pd4ml.writeDOCX(fos) for the pd4ml.writeRTF(fos, contvertImagesToWmf) call, and adjust the output file's extension. Two API styles are shown, matching the two shown throughout the v3 to v4 Migration Guide:

package samples;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.InvalidParameterException;

import com.pd4ml.Dimensions.Units;
import com.pd4ml.PD4ML;
import com.pd4ml.PageMargins;
import com.pd4ml.PageSize;

public class GettingStarted2 {
    protected int topValue = 10;
    protected int leftValue = 20;
    protected int rightValue = 10;
    protected int bottomValue = 10;
    protected int userSpaceWidth = 1300;

    public static void main(String[] args) {
        try {
            GettingStarted2 jt = new GettingStarted2();
            jt.doConversion("https://pd4ml.com/i/rtf/demo.htm", "c:/invoice.rtf");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void doConversion( String url, String outputPath )
                throws InvalidParameterException, MalformedURLException, IOException {
        File output = new File(outputPath);
        java.io.FileOutputStream fos = new java.io.FileOutputStream(output);

        PD4ML pd4ml = new PD4ML();

        pd4ml.setHtmlWidth(userSpaceWidth); // set frame width of "virtual web browser"

        // choose target paper format and "rotate" it to landscape orientation
        pd4ml.setPageSize(PageSize.A4.rotate());

        // define page margins
        pd4ml.setPageMargins(new PageMargins(topValue, leftValue, bottomValue, rightValue, Units.MM));

        // read and parse HTML
        pd4ml.readHTML(new URL(url));
        boolean contvertImagesToWmf = false;
        pd4ml.writeRTF(fos, contvertImagesToWmf);  // actual document conversion from URL to RTF file
        fos.close();

        System.out.println( outputPath + "\ndone." );
    }
}

For DOCX, the legacy-API equivalent follows exactly the same pattern, substituting pd4ml.outputFormat(PD4Constants.DOCX) for PD4Constants.RTF_WMF (DOCX has no WMF variant to choose between, per the note above).

Demo: the same source rendered three ways

A single source HTML document run through the converters above produces the following reference outputs, useful for comparing fidelity across formats or as a starting point for experimenting with the API calls above:

Output Link
Source HTML demo.htm
RTF result pd4ml.rtf
RTF result, as plain text pd4ml.txt
DOCX result pd4ml.docx
Equivalent PDF, for comparison pd4ml.pdf

See also