Ruby library for working with Metanorma document XML.
Installation
gem install -document
Usage
require 'metanorma/document'
doc = IO.read('spec/fixtures/rice-amd-en.final.xml')
standard = Metanorma::IsoDocument::Root.from_xml(doc)
puts standard.to_xml(pretty: true)
HTML Generation
The Metanorma::Html::Generator produces a complete, self-contained HTML document from a presentation XML document model. It handles body content rendering, table of contents, CSS theming, JavaScript interactivity, and responsive layout.
Quick Start
require "metanorma/document"
require "metanorma/html/generator"
Parse presentation XML into a document model
xml = File.read("spec/fixtures/iso/is/document-en.presentation.xml") doc = Metanorma::IsoDocument::Root.from_xml(xml)
Generate a complete HTML document
html = Metanorma::Html::Generator.generate(doc) File.write("output.html", html)
Renderer Selection
The Generator automatically selects the correct renderer based on the document model class:
Metanorma::Document::Root
|
|
Metanorma::StandardDocument::Root
|
|
Metanorma::IsoDocument::Root
|
|
For publisher-based dispatch within the same model (e.g., ICC documents published through ISO), use taste registration:
ICC documents are IsoDocument::Root but use IccRenderer
Metanorma::Html::Generator.register_taste( Metanorma::IsoDocument::Root, "ICC", Metanorma::Html::IccRenderer )
The lookup order is: taste match (publisher) → model class (most specific last) → BaseRenderer.
Renderer Architecture
The renderer hierarchy follows the open/closed principle:
BaseRenderer # Core HTML rendering, document assembly, CSS/JS pipeline
└── StandardRenderer # Terms, definitions, annexes, bibliography
├── IsoRenderer # ISO cover page, copyright, title formatting
│ ├── IccRenderer # ICC publisher styling
│ └── PdfaRenderer # PDF Association publisher styling
├── IecRenderer # IEC-specific formatting
├── IeeeRenderer # IEEE-specific formatting
├── IetfRenderer # IETF-specific formatting
├── IhoRenderer # IHO-specific formatting
├── ItuRenderer # ITU-specific formatting
├── OgcRenderer # OGC-specific formatting
├── OimlRenderer # OIML-specific formatting
├── BipmRenderer # BIPM-specific formatting
├── CcRenderer # CC-specific formatting
└── RiboseRenderer # Ribose-specific formatting
Drop Pattern
Block elements (notes, examples, sourcecode, formulas, figures, admonitions) use the Drop pattern:
A Drop captures rendered content via RendererContext, then passes
data to a Liquid template. The renderer never emits HTML directly.
def render_note(note, **_opts) drop = Drops::NoteDrop.from_model(note, renderer: renderer_context) @output << render_liquid("_note.html.liquid", { "block" ⇒ drop }) end
Each Drop class inherits from BlockElementDrop and implements .from_model(model, renderer:) which:
-
Captures child content via
renderer.capture_output { … } -
Returns a new Drop instance with pre-rendered HTML strings
-
The Liquid template reads Drop attributes and outputs final HTML
RendererContext is a facade that exposes only the rendering methods Drops need, maintaining encapsulation.
Class Name Ownership
The HTML renderer owns its class names entirely. No XML-originated class names appear in the HTML output.
-
Block-level classes (
note-block,formula,figure, etc.) are assigned by the renderer based on what it’s rendering -
Inline span classes use
SPAN_ROLE_CLASSESto map XML span roles to HTML-specific names (e.g.,boldtitle→title-text,citesec→xref-section) -
The XML’s
class_attris read as input only to determine semantic role, never emitted directly
Presentation XML
The HTML renderer expects presentation XML (not source XML). Presentation XML contains fmt- display elements (fmt-title, fmt-xref, fmt-link, fmt-concept, fmt-definition, fmt-preferred, etc.) alongside semantic elements. The renderer prioritizes fmt- elements for rendering, falling back to semantic elements when display elements are absent.
Theming
Each renderer has a Theme object controlling colors, typography, and layout. Override theme properties in subclasses:
class MyRenderer < Metanorma::Html::StandardRenderer
def theme
@theme ||= begin
t = Theme.new
t.primary = "#1a5276"
t.accent = "#2e86c1"
t.font_body = '"Charter", serif'
t
end
end
end
Theme properties are emitted as CSS custom properties (--mn-primary, --font-body, etc.) for runtime customization.
Liquid Templates
HTML structure is defined in .liquid templates under lib/metanorma/html/templates/:
document.html.liquid
|
Full document shell ( |
_header.html.liquid
|
Sticky header with publisher logos |
_footer.html.liquid
|
Footer with copyright |
_cover.html.liquid
|
Cover page layout |
_iso_cover.html.liquid
|
ISO-specific cover page |
_footnotes.html.liquid
|
Footnotes section |
_doc_title.html.liquid
|
Document title rendering |
_note.html.liquid
|
Note block |
_example.html.liquid
|
Example block |
_sourcecode.html.liquid
|
Source code block |
_formula.html.liquid
|
Formula block |
_figure.html.liquid
|
Figure block |
_admonition.html.liquid
|
Admonition block |
Templates use Liquid::LocalFileSystem for partials (prefixed with _). The render_liquid method handles template caching via TEMPLATE_CACHE.
Asset Pipeline
The AssetPipeline compiles CSS and JavaScript from modular source files:
| CSS |
|
| JS |
|
All assets are compiled into inline <style> and <script> blocks for self-contained output.
CLI Usage
From the command line:
Generate HTML from presentation XML
bundle exec ruby -e ' require "metanorma/document" require "metanorma/html/generator" doc = Metanorma::IsoDocument::Root.from_xml(ARGF.read) puts Metanorma::Html::Generator.generate(doc) ' < document.presentation.xml > output.html
API Reference
Generator.generate(document, **options)
|
Returns a complete HTML string. Auto-selects the renderer. |
Generator.register(model_class, renderer_class)
|
Register a model-to-renderer mapping. |
Generator.register_taste(model_class, publisher_abbrev, renderer_class)
|
Register a publisher-based override. |
Generator.renderer_for(document)
|
Returns the renderer class that would be used. |
Renderer instance methods:
generate_full_document(document)
|
Full HTML document (body + assembly). |
to_html
|
Returns the rendered body content after |
theme
|
Returns the |
render_liquid(template_name, assigns)
|
Renders a Liquid template with caching. |
Document Flavors
The gem provides a hierarchy of document flavors:
-
BasicDocument - Basic document model
-
StandardDocument - Standard document model (extends BasicDocument)
-
IsoDocument - ISO standard document model (extends StandardDocument)
-
IecDocument - IEC standard document model
-
IeeeDocument - IEEE standard document model
-
IetfDocument - IETF standard document model
-
IhoDocument - IHO standard document model
-
OimlDocument - OIML standard document model
-
BipmDocument - BIPM standard document model
-
ItuDocument - ITU standard document model
-
OgcDocument - OGC standard document model
-
CcDocument - CC standard document model
-
RiboseDocument - Ribose standard document model
Supported XML Formats
This library targets the modern Metanorma XML format which uses <metanorma> as the root element.
Legacy XML formats that use flavor-specific root elements (e.g. <iso-standard>, <m3d-standard>, <csa-standard>, <un-standard>) are not supported.
Documentation
Detailed architecture documentation is in the docs/ directory:
-
HTML Renderer Architecture — renderer pipeline, drop pattern, class name ownership, template system