metanorma-document 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +231 -53
- data/README.adoc +59 -18
- data/data/stylesheets/components/bibliography.css +2 -2
- data/data/stylesheets/components/inline.css +11 -12
- data/docs/html-renderer.adoc +261 -0
- data/lib/metanorma/document/version.rb +1 -1
- data/lib/metanorma/html/base_renderer.rb +210 -257
- data/lib/metanorma/html/bipm_renderer.rb +0 -1
- data/lib/metanorma/html/cc_renderer.rb +0 -1
- data/lib/metanorma/html/drops/admonition_drop.rb +26 -0
- data/lib/metanorma/html/drops/block_element_drop.rb +20 -0
- data/lib/metanorma/html/drops/example_drop.rb +35 -0
- data/lib/metanorma/html/drops/figure_drop.rb +53 -0
- data/lib/metanorma/html/drops/formula_drop.rb +44 -0
- data/lib/metanorma/html/drops/note_drop.rb +32 -0
- data/lib/metanorma/html/drops/sourcecode_drop.rb +37 -0
- data/lib/metanorma/html/drops.rb +7 -0
- data/lib/metanorma/html/iec_renderer.rb +0 -1
- data/lib/metanorma/html/ieee_renderer.rb +0 -1
- data/lib/metanorma/html/ietf_renderer.rb +0 -1
- data/lib/metanorma/html/iho_renderer.rb +0 -1
- data/lib/metanorma/html/iso_renderer.rb +77 -209
- data/lib/metanorma/html/itu_renderer.rb +0 -1
- data/lib/metanorma/html/ogc_renderer.rb +5 -6
- data/lib/metanorma/html/oiml_renderer.rb +0 -1
- data/lib/metanorma/html/pdfa_renderer.rb +0 -1
- data/lib/metanorma/html/ribose_renderer.rb +0 -1
- data/lib/metanorma/html/standard_renderer.rb +63 -82
- data/lib/metanorma/html/templates/_admonition.html.liquid +4 -0
- data/lib/metanorma/html/templates/_doc_title.html.liquid +1 -1
- data/lib/metanorma/html/templates/_example.html.liquid +3 -0
- data/lib/metanorma/html/templates/_figure.html.liquid +6 -0
- data/lib/metanorma/html/templates/_formula.html.liquid +6 -0
- data/lib/metanorma/html/templates/_iso_doc_title.html.liquid +2 -2
- data/lib/metanorma/html/templates/_note.html.liquid +3 -0
- data/lib/metanorma/html/templates/_sourcecode.html.liquid +4 -0
- data/lib/metanorma/html.rb +0 -1
- metadata +16 -3
- data/lib/metanorma/html/component_registry.rb +0 -37
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
= HTML Renderer Architecture
|
|
2
|
+
|
|
3
|
+
== Overview
|
|
4
|
+
|
|
5
|
+
The HTML renderer converts a Metanorma document model (parsed from presentation XML) into a complete, self-contained HTML document. It produces all body content, header, footer, table of contents, CSS theming, and JavaScript interactivity.
|
|
6
|
+
|
|
7
|
+
== Pipeline
|
|
8
|
+
|
|
9
|
+
....
|
|
10
|
+
Generator.generate(doc)
|
|
11
|
+
├── Auto-selects renderer by document class
|
|
12
|
+
├── Renderer.new
|
|
13
|
+
└── renderer.generate_full_document(doc)
|
|
14
|
+
├── Header (publisher logos, document title)
|
|
15
|
+
├── ToC sidebar
|
|
16
|
+
├── Body content (recursive render)
|
|
17
|
+
├── Footnotes
|
|
18
|
+
├── Footer (copyright)
|
|
19
|
+
└── Asset pipeline (inline CSS + JS)
|
|
20
|
+
....
|
|
21
|
+
|
|
22
|
+
== Renderer Hierarchy
|
|
23
|
+
|
|
24
|
+
[source,ruby]
|
|
25
|
+
BaseRenderer # Core HTML rendering, document assembly, CSS/JS pipeline
|
|
26
|
+
└── StandardRenderer # Terms, definitions, annexes, bibliography
|
|
27
|
+
├── IsoRenderer # ISO cover page, copyright, title formatting
|
|
28
|
+
│ ├── IccRenderer # ICC publisher styling
|
|
29
|
+
│ └── PdfaRenderer # PDF Association publisher styling
|
|
30
|
+
├── IecRenderer # IEC-specific formatting
|
|
31
|
+
├── IeeeRenderer # IEEE-specific formatting
|
|
32
|
+
├── IetfRenderer # IETF-specific formatting
|
|
33
|
+
├── IhoRenderer # IHO-specific formatting
|
|
34
|
+
├── ItuRenderer # ITU-specific formatting
|
|
35
|
+
├── OgcRenderer # OGC-specific formatting
|
|
36
|
+
├── OimlRenderer # OIML-specific formatting
|
|
37
|
+
├── BipmRenderer # BIPM-specific formatting
|
|
38
|
+
├── CcRenderer # CC-specific formatting
|
|
39
|
+
└── RiboseRenderer # Ribose-specific formatting
|
|
40
|
+
|
|
41
|
+
=== Renderer Selection
|
|
42
|
+
|
|
43
|
+
The `Generator` uses a two-tier lookup:
|
|
44
|
+
|
|
45
|
+
1. **Taste match**: If the document's publisher matches a registered taste, use that renderer.
|
|
46
|
+
2. **Model class**: Walk the ancestor chain from most specific to `BaseRenderer`.
|
|
47
|
+
|
|
48
|
+
[source,ruby]
|
|
49
|
+
# ICC documents are IsoDocument::Root but use IccRenderer
|
|
50
|
+
Metanorma::Html::Generator.register_taste(
|
|
51
|
+
Metanorma::IsoDocument::Root,
|
|
52
|
+
"ICC",
|
|
53
|
+
Metanorma::Html::IccRenderer
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
== Class Name Ownership
|
|
57
|
+
|
|
58
|
+
The HTML renderer owns its class names entirely. **No XML-originated class names appear in the HTML output.**
|
|
59
|
+
|
|
60
|
+
The XML document model's `class_attr` is read as **input only** to determine semantic role. The renderer maps XML roles to HTML-specific class names via `SPAN_ROLE_CLASSES`:
|
|
61
|
+
|
|
62
|
+
[source,ruby]
|
|
63
|
+
SPAN_ROLE_CLASSES = {
|
|
64
|
+
"boldtitle" => "title-text",
|
|
65
|
+
"citefig" => "xref-fig",
|
|
66
|
+
"citesec" => "xref-section",
|
|
67
|
+
"fmt-element-name" => "element-label",
|
|
68
|
+
"fmt-obligation" => "obligation-text",
|
|
69
|
+
# ...
|
|
70
|
+
}.freeze
|
|
71
|
+
|
|
72
|
+
Block-level classes (`note-block`, `formula`, `figure`, etc.) are assigned by the renderer based on what it's rendering — never carried over from XML.
|
|
73
|
+
|
|
74
|
+
Inline span classes go through `html_class_for_span`:
|
|
75
|
+
|
|
76
|
+
[source,ruby]
|
|
77
|
+
def html_class_for_span(xml_class)
|
|
78
|
+
SPAN_ROLE_CLASSES[xml_class] || "span-#{xml_class}"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
Raw XML content (e.g., boilerplate) passes through Nokogiri post-processing that remaps any remaining XML class names.
|
|
82
|
+
|
|
83
|
+
== Drop Pattern
|
|
84
|
+
|
|
85
|
+
Block elements (notes, examples, sourcecode, formulas, figures, admonitions) use the Drop pattern to separate data capture from template rendering.
|
|
86
|
+
|
|
87
|
+
=== How It Works
|
|
88
|
+
|
|
89
|
+
. A `Drop` class captures rendered content via `RendererContext`
|
|
90
|
+
. The Drop passes pre-rendered HTML strings to a Liquid template
|
|
91
|
+
. The template reads Drop attributes and outputs final HTML
|
|
92
|
+
|
|
93
|
+
[source,ruby]
|
|
94
|
+
# In the renderer:
|
|
95
|
+
def render_note(note, **_opts)
|
|
96
|
+
drop = Drops::NoteDrop.from_model(note, renderer: renderer_context)
|
|
97
|
+
@output << render_liquid("_note.html.liquid", { "block" => drop })
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# In the Drop:
|
|
101
|
+
class NoteDrop < BlockElementDrop
|
|
102
|
+
def self.from_model(note, renderer:)
|
|
103
|
+
content_html = renderer.capture_output do
|
|
104
|
+
note.content.each { |para| renderer.render_paragraph(para) }
|
|
105
|
+
end
|
|
106
|
+
new(id: renderer.safe_attr(note, :id),
|
|
107
|
+
label_html: renderer.escape_html(label),
|
|
108
|
+
content_html: content_html,
|
|
109
|
+
css_class: "note-block")
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
=== BlockElementDrop
|
|
114
|
+
|
|
115
|
+
All block drops inherit from `BlockElementDrop`, which provides common attributes:
|
|
116
|
+
|
|
117
|
+
`id` :: HTML element id
|
|
118
|
+
`type` :: Block type identifier
|
|
119
|
+
`label_html` :: Pre-rendered label (e.g., "NOTE", "EXAMPLE 1")
|
|
120
|
+
`content_html` :: Pre-rendered inner content
|
|
121
|
+
`css_class` :: HTML-specific class name
|
|
122
|
+
|
|
123
|
+
=== RendererContext
|
|
124
|
+
|
|
125
|
+
`RendererContext` is a facade inside `BaseRenderer` that exposes only the rendering methods Drops need. Drops call renderer methods through this facade — the renderer's private interface stays encapsulated.
|
|
126
|
+
|
|
127
|
+
[source,ruby]
|
|
128
|
+
class RendererContext
|
|
129
|
+
def safe_attr(obj, method_name)
|
|
130
|
+
def escape_html(text)
|
|
131
|
+
def extract_block_label(block, default)
|
|
132
|
+
def extract_plain_text(node)
|
|
133
|
+
def capture_output(&block)
|
|
134
|
+
def render_paragraph(p)
|
|
135
|
+
def render_mixed_inline(node)
|
|
136
|
+
def render_inline_element(el)
|
|
137
|
+
def render_unordered_list(ul)
|
|
138
|
+
def render_ordered_list(ol)
|
|
139
|
+
def render_definition_list(dl)
|
|
140
|
+
def render_table(table)
|
|
141
|
+
def render_figure(figure)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
== Liquid Templates
|
|
145
|
+
|
|
146
|
+
HTML structure is defined in `.liquid` templates under `lib/metanorma/html/templates/`.
|
|
147
|
+
|
|
148
|
+
[horizontal]
|
|
149
|
+
`document.html.liquid` :: Full document shell (`<html>`, `<head>`, `<body>`)
|
|
150
|
+
`_header.html.liquid` :: Sticky header with publisher logos
|
|
151
|
+
`_footer.html.liquid` :: Footer with copyright
|
|
152
|
+
`_cover.html.liquid` :: Generic cover page layout
|
|
153
|
+
`_iso_cover.html.liquid` :: ISO-specific cover page
|
|
154
|
+
`_iso_doc_title.html.liquid` :: ISO document title rendering
|
|
155
|
+
`_doc_title.html.liquid` :: Generic document title rendering
|
|
156
|
+
`_footnotes.html.liquid` :: Footnotes section
|
|
157
|
+
`_note.html.liquid` :: Note block
|
|
158
|
+
`_example.html.liquid` :: Example block
|
|
159
|
+
`_sourcecode.html.liquid` :: Source code block
|
|
160
|
+
`_formula.html.liquid` :: Formula block
|
|
161
|
+
`_figure.html.liquid` :: Figure block
|
|
162
|
+
`_admonition.html.liquid` :: Admonition block
|
|
163
|
+
|
|
164
|
+
Templates use `Liquid::LocalFileSystem` for partials (prefixed with `_`). The `render_liquid` method handles template caching.
|
|
165
|
+
|
|
166
|
+
=== Template Example
|
|
167
|
+
|
|
168
|
+
`_note.html.liquid`:
|
|
169
|
+
|
|
170
|
+
[source,liquid]
|
|
171
|
+
----
|
|
172
|
+
<div{% if block.id %} id="{{ block.id }}"{% endif %} class="{{ block.css_class }}">
|
|
173
|
+
{% if block.label_html %}
|
|
174
|
+
<p class="note-label"><strong>{{ block.label_html }}</strong></p>
|
|
175
|
+
{% endif %}
|
|
176
|
+
<div class="note-content">
|
|
177
|
+
{{ block.content_html }}
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
----
|
|
181
|
+
|
|
182
|
+
== Theming
|
|
183
|
+
|
|
184
|
+
Each renderer has a `Theme` object controlling colors, typography, and layout. Theme properties are emitted as CSS custom properties (`--mn-primary`, `--font-body`, etc.).
|
|
185
|
+
|
|
186
|
+
[source,ruby]
|
|
187
|
+
class MyRenderer < Metanorma::Html::StandardRenderer
|
|
188
|
+
def theme
|
|
189
|
+
@theme ||= begin
|
|
190
|
+
t = Theme.new
|
|
191
|
+
t.primary = "#1a5276"
|
|
192
|
+
t.accent = "#2e86c1"
|
|
193
|
+
t.font_body = '"Charter", serif'
|
|
194
|
+
t
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
=== Theme Properties
|
|
200
|
+
|
|
201
|
+
[horizontal]
|
|
202
|
+
`primary` :: Primary brand color
|
|
203
|
+
`accent` :: Accent/highlight color
|
|
204
|
+
`gradient` :: Header gradient
|
|
205
|
+
`font_body` :: Body text font stack
|
|
206
|
+
`font_sans` :: Sans-serif font stack
|
|
207
|
+
`font_mono` :: Monospace font stack
|
|
208
|
+
`font_url` :: Google Fonts URL
|
|
209
|
+
`note_border` :: Note block border color
|
|
210
|
+
`note_bg` :: Note block background
|
|
211
|
+
`example_border` :: Example block border color
|
|
212
|
+
`example_bg` :: Example block background
|
|
213
|
+
`admonition_border` :: Admonition border color
|
|
214
|
+
`admonition_bg` :: Admonition background
|
|
215
|
+
`extra_css` :: Additional CSS string appended after defaults
|
|
216
|
+
|
|
217
|
+
== Presentation XML
|
|
218
|
+
|
|
219
|
+
The HTML renderer expects **presentation XML** (not source XML). Presentation XML contains `fmt-*` display elements alongside semantic elements:
|
|
220
|
+
|
|
221
|
+
- `fmt-title` — formatted section/block titles
|
|
222
|
+
- `fmt-xref` — formatted cross-reference text
|
|
223
|
+
- `fmt-link` — formatted link text
|
|
224
|
+
- `fmt-concept` — formatted concept references
|
|
225
|
+
- `fmt-definition` — formatted term definitions
|
|
226
|
+
- `fmt-preferred` — formatted preferred term names
|
|
227
|
+
|
|
228
|
+
The renderer prioritizes `fmt-*` elements for rendering, falling back to semantic elements when display elements are absent.
|
|
229
|
+
|
|
230
|
+
== Asset Pipeline
|
|
231
|
+
|
|
232
|
+
The `AssetPipeline` compiles CSS and JavaScript from modular source files into inline `<style>` and `<script>` blocks for self-contained output.
|
|
233
|
+
|
|
234
|
+
=== CSS
|
|
235
|
+
|
|
236
|
+
[horizontal]
|
|
237
|
+
`data/stylesheets/base/` :: Reset, typography, layout, dark mode, print
|
|
238
|
+
`data/stylesheets/components/` :: Component stylesheets (inline, tables, notes, etc.)
|
|
239
|
+
|
|
240
|
+
=== JavaScript
|
|
241
|
+
|
|
242
|
+
[horizontal]
|
|
243
|
+
`data/javascripts/core/` :: Reader, theme, scroll, navigation, reveal
|
|
244
|
+
`data/javascripts/components/` :: ToC, search, lightbox, code copy, glossary, etc.
|
|
245
|
+
|
|
246
|
+
== Extending
|
|
247
|
+
|
|
248
|
+
To add a new flavor renderer:
|
|
249
|
+
|
|
250
|
+
. Subclass the appropriate base (usually `IsoRenderer` or `StandardRenderer`)
|
|
251
|
+
. Override `theme` with brand colors/fonts
|
|
252
|
+
. Override `publisher_logo_map` with logo filenames
|
|
253
|
+
. Register with the Generator:
|
|
254
|
+
|
|
255
|
+
[source,ruby]
|
|
256
|
+
Metanorma::Html::Generator.register(
|
|
257
|
+
Metanorma::MyDocument::Root,
|
|
258
|
+
Metanorma::Html::MyRenderer
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
To customize how a specific block renders, override the `render_*` method and either emit HTML directly or use the Drop pattern with a new template.
|