How Capital Publisher Works
9. Fonts in Service Explorer Diagrams
Status: tested 2026-09-28 on Capital 2512.2608. Every statement below came from a real
publish, the Service Explorer server that ships inside the package, and measurements in Edge,
unless it is marked not tested. The tool is tools/embed_diagram_font.py in the private
repository capital-publisher-config; the full record and evidence images are in its
handoffs/2026-09-28-iewd-font-embedding.md.
The problem
A Service Explorer diagram names its font by family only. Capital writes a CSS class inside each
diagram SVG, for example .I,.J{font-family:Lucida Sans Unicode;...}, with no fallback list and
no @font-face. The viewer runtime ships no font files. So a PC without that font installed
renders the diagram in some other face, and right- or centre-justified text no longer lines up.
Nothing in Publisher's settings embeds a font for Service Explorer. The only documented font
embedding is PDF-only (fontFilePath in pdf\pdfConfiguration.xml, vendor page
ee-publisher/pdf-packager).
Why the viewer's custom.css cannot fix it
Custom Runtime\styles\custom.css is loaded by the viewer page, but each diagram is loaded in an
<object> element as its own SVG document. Page CSS does not cross into it, so custom.css
cannot change diagram text. The viewer has no supported hook for custom script either:
index.html loads a fixed list of scripts, and the Custom Runtime\scripts folder is empty and referenced
by nothing.
What works: ship the font inside the package
The Service Explorer server serves every file inside a package's contents.zip, under
/zipped/data/<package>/<entry>, and a relative URL inside a diagram resolves to another entry of
the same zip. (The viewer's own introduction page loads its images this way.) So a font stored in
contents.zip and declared with @font-face in each diagram reaches every browser, with nothing
installed on the viewing PC.
embed_diagram_font.py does exactly that, after publishing:
python tools/embed_diagram_font.py <package folder> --family "Monospac821 BT" --font <licensed .ttf>
It converts the font to WOFF2, stores it once per package at fonts/<family>.woff2, adds the
@font-face to every diagram SVG whose CSS uses that family, backs up the original zip, and
reads the result back before reporting. Exit code 0 means done, 1 means no diagram uses that
family (nothing written; it lists the families it found), 2 means it could not run.
Measured result (font uninstalled from the test PC, text widths of "iiiiiiiiii" and "MMMMMMMMMM" in the diagram's own text class):
| State | i | M | Font used |
|---|---|---|---|
| Monospace 821 installed, no fix | 8739 | 8739 | installed font |
| Font uninstalled, no fix | 4033 | 12906 | fallback |
| Font uninstalled, script applied | 8739 | 8739 | embedded font |
The embedded font renders with exactly the widths of the installed one.
Rules for using it
- Run it after every publish. A re-publish writes a new
contents.zipand undoes it. A Capital update does not, because no Siemens runtime file is changed. - Use the family name stored in the font file, not the marketing name. Monospace 821 BT's
family name is
Monospac821 BT(no "e"). Capital's font list shows that name and a publish writesfont-family:Monospac821 BT. - Install the font on the machine that publishes (the client for interactive publishes, the CIS machine for background ones). Capital lays text out with that machine's fonts; embedding fixes the viewer, not the layout.
- The font file is licensed: web use may need a web-font licence. Keep it out of git.
- The script needs Python with
fonttoolsandbrotlifor TTF or OTF input. - The font file is served without a Content-Type header. Edge loaded it anyway; Firefox and Safari are not tested.
Doing it in the Publish click: the Embed Diagram Font plugin
Publisher accepts generator plugins (Java jars in <Capital install>\plugins, API reference in
doc\plugin\api, interfaces IXResourceGenerator and IXDiagramDataGenerator). Each installed
generator appears as a checkbox under Includes in Generate Documentation. Tests with a probe
plugin showed that, in a desktop publish, generators run after Capital writes the diagram SVGs
as loose files and before it zips them into contents.zip, and that Capital zips whatever is
in that folder, including files a plugin adds.
The Embed Diagram Font plugin (plugins/embed-diagram-font in capital-publisher-config,
shipped as a zip with its own INSTALL guide) uses that: tick it under Includes and every diagram
using the configured family gets the @font-face and the font file, with no step after the
publish. It supports separate bold and italic faces. Tested end to end on a desktop publish: the
real viewer rendered the embedded face.
Three things the tests established, which the install guide repeats:
- Background (CIS) publishes cannot use it. The package is built elsewhere and copied out
after the plugin has run, so there is nothing for it to change. Run
embed_diagram_font.pyafter the background task instead. - Publisher does not show plugin errors. Desktop and background publishes both report no
errors even when the plugin fails. The plugin writes
embed-diagram-font-report.txtinto every package folder (OK:orNOT EMBEDDED:with the reason). - Verify before deploying with
embed_diagram_font.py <package> --family "..." --check. It follows each diagram's font URLs, so it checks packages made by the plugin or the script.
Where the diagram font is set
Publisher takes fonts from the style set used for the publish (for example
Default_Documentation), edited in Publisher under Edit All Styling Data. Style sets live in
the Capital database, not in publisher\styleset.xml. Each object type has its own font entries,
for example the wire label is Wire > Decorations > Horizontal > Composite Texts >
WireCSA + " - " + WireMaterial + " - ". In Default_Documentation all 44 font entries are
Lucida Sans Unicode. A style set cannot be copied from that dialog.
Alignment: what the font fixes and what it does not
Capital places every text by its left x, calculated from the font on the publishing machine.
Capital 2512.2608 also writes textLength on every text, which forces the browser to draw the run
at Capital's width. Measured on a right-justified label (target right edge 14152):
| Diagram | Right edge | Off target |
|---|---|---|
| As published, font missing | 14152 | 0 (fallback glyphs stretched to fit) |
textLength removed, font missing |
7996 | -6156 |
| As published, font embedded | 14152 | 0, real glyphs |
textLength removed, font embedded |
12144 | -2008 (spaces collapsed, see below) |
So with textLength present, alignment already holds and embedding makes the glyphs right.
Without it (older builds, version not established), the font removes most of the shift but not
all of it, for two reasons:
- Capital's text widths are 4 to 11 percent narrower than the font's real width at the size Capital writes, with the correct font installed, Lucida as well as Monospace 821.
- Browsers collapse repeated and trailing spaces in these SVGs (Chromium ignores
xml:space="preserve"). A label such as0.0 - -loses two characters' width.
To check which case a site is in, look for textLength in one published diagram SVG.
Related findings from the same tests
- A headless PackagingTask (REST) with "Application Files" produces an incomplete package: no
launcher, no
WEB-INF, noPackets\client-info.json, noCustom Runtimefiles. Generate complete packages from the desktop client. - The package server's HTTPS certificate is always
lib\keystorein the package, copied from<CAPITAL_HOME>\publisher\server-config\keystore. ThekeystorePathsetting inserver-config.xmlis ignored; the passwords in it are used (JettyOBF:form). To serve your own certificate, replace that keystore file and set both passwords in all four explorer XMLs, which share it.
Source: Tested 2026-09-28 on Capital 2512.2608 (desktop and headless publishes, the package's own Service Explorer server, Edge); tool and evidence in repository capital-publisher-config · retrieved 2026-09-28