metanorma-document 0.2.0 → 0.2.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc7125fd4984bbedebb8d471fc29d0d34d5b56b7de1ec518570bc27f8e0671d5
4
- data.tar.gz: bc63e4f58ba6053d66df6a2671737c0ce051057ef4e0b14fc51154d050ca0c67
3
+ metadata.gz: a15c5f341f9fbc477322f57c757f26896b1ba2bd3e80a89815ba6942a44c5c6c
4
+ data.tar.gz: 95f0e2d121bc523f38600fe0d5e3e058f7c6b8212b4e5e8b4ab4654d06b10a83
5
5
  SHA512:
6
- metadata.gz: 45e13915f1b88579d06b94968c5a7f469fdbf77b6980cf3cb68d6747bb9fd1a87d111c8388d2f0f337ba5ccde71aa96c7f755ca6d0080f8ccafa154a15ed7075
7
- data.tar.gz: 1a6ab72e96afe880682ffb9646273e794dec60ce1a2e208d794054fe99ea38c6adf09672678a8234ebf805b800ab491bfa01332efcd663b65c403e8cd617712c
6
+ metadata.gz: 01eee74753ce15ebf8bd68a3523130d7231eee75c42c2bb3551b67b30fd79e022c8e12858a1aae6d6fc03baf6bbac511b5b5e62db0b054487ee5c1bb62650c2c
7
+ data.tar.gz: 499dc86b2c426e22f107443b8939eead6e2861ab985b9dd427c54a2572531f11ab9c09114b7aa44e9c2fd0dd16eb5dfd36366dbc4f8b2c7c2c713994d6e53aa2
data/README.adoc CHANGED
@@ -75,6 +75,34 @@ BaseRenderer # Core HTML rendering, document assembly, CSS/JS p
75
75
  └── RiboseRenderer # Ribose-specific formatting
76
76
  ....
77
77
 
78
+ ===== Drop Pattern
79
+
80
+ Block elements (notes, examples, sourcecode, formulas, figures, admonitions) use the Drop pattern:
81
+
82
+ [source,ruby]
83
+ # A Drop captures rendered content via RendererContext, then passes
84
+ # data to a Liquid template. The renderer never emits HTML directly.
85
+ def render_note(note, **_opts)
86
+ drop = Drops::NoteDrop.from_model(note, renderer: renderer_context)
87
+ @output << render_liquid("_note.html.liquid", { "block" => drop })
88
+ end
89
+
90
+ Each Drop class inherits from `BlockElementDrop` and implements `.from_model(model, renderer:)` which:
91
+
92
+ . Captures child content via `renderer.capture_output { ... }`
93
+ . Returns a new Drop instance with pre-rendered HTML strings
94
+ . The Liquid template reads Drop attributes and outputs final HTML
95
+
96
+ `RendererContext` is a facade that exposes only the rendering methods Drops need, maintaining encapsulation.
97
+
98
+ ===== Class Name Ownership
99
+
100
+ The HTML renderer owns its class names entirely. **No XML-originated class names appear in the HTML output.**
101
+
102
+ * Block-level classes (`note-block`, `formula`, `figure`, etc.) are assigned by the renderer based on what it's rendering
103
+ * Inline span classes use `SPAN_ROLE_CLASSES` to map XML span roles to HTML-specific names (e.g., `boldtitle` → `title-text`, `citesec` → `xref-section`)
104
+ * The XML's `class_attr` is read as **input only** to determine semantic role, never emitted directly
105
+
78
106
  ==== Presentation XML
79
107
 
80
108
  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.
@@ -107,8 +135,15 @@ HTML structure is defined in `.liquid` templates under `lib/metanorma/html/templ
107
135
  `_header.html.liquid` :: Sticky header with publisher logos
108
136
  `_footer.html.liquid` :: Footer with copyright
109
137
  `_cover.html.liquid` :: Cover page layout
138
+ `_iso_cover.html.liquid` :: ISO-specific cover page
110
139
  `_footnotes.html.liquid` :: Footnotes section
111
140
  `_doc_title.html.liquid` :: Document title rendering
141
+ `_note.html.liquid` :: Note block
142
+ `_example.html.liquid` :: Example block
143
+ `_sourcecode.html.liquid` :: Source code block
144
+ `_formula.html.liquid` :: Formula block
145
+ `_figure.html.liquid` :: Figure block
146
+ `_admonition.html.liquid` :: Admonition block
112
147
 
113
148
  Templates use `Liquid::LocalFileSystem` for partials (prefixed with `_`). The `render_liquid` method handles template caching via `TEMPLATE_CACHE`.
114
149
 
@@ -151,26 +186,32 @@ Renderer instance methods:
151
186
  `theme` :: Returns the `Theme` instance (override in subclasses).
152
187
  `render_liquid(template_name, assigns)` :: Renders a Liquid template with caching.
153
188
 
154
- == Document Flavors
189
+ == Document Flavors
190
+
191
+ The gem provides a hierarchy of document flavors:
192
+
193
+ * BasicDocument - Basic document model
194
+ * StandardDocument - Standard document model (extends BasicDocument)
195
+ * IsoDocument - ISO standard document model (extends StandardDocument)
196
+ * IecDocument - IEC standard document model
197
+ * IeeeDocument - IEEE standard document model
198
+ * IetfDocument - IETF standard document model
199
+ * IhoDocument - IHO standard document model
200
+ * OimlDocument - OIML standard document model
201
+ * BipmDocument - BIPM standard document model
202
+ * ItuDocument - ITU standard document model
203
+ * OgcDocument - OGC standard document model
204
+ * CcDocument - CC standard document model
205
+ * RiboseDocument - Ribose standard document model
206
+
207
+ == Supported XML Formats
155
208
 
156
- The gem provides a hierarchy of document flavors:
209
+ This library targets the modern Metanorma XML format which uses `<metanorma>` as the root element.
157
210
 
158
- * BasicDocument - Basic document model
159
- * StandardDocument - Standard document model (extends BasicDocument)
160
- * IsoDocument - ISO standard document model (extends StandardDocument)
161
- * IecDocument - IEC standard document model
162
- * IeeeDocument - IEEE standard document model
163
- * IetfDocument - IETF standard document model
164
- * IhoDocument - IHO standard document model
165
- * OimlDocument - OIML standard document model
166
- * BipmDocument - BIPM standard document model
167
- * ItuDocument - ITU standard document model
168
- * OgcDocument - OGC standard document model
169
- * CcDocument - CC standard document model
170
- * RiboseDocument - Ribose standard document model
211
+ Legacy XML formats that use flavor-specific root elements (e.g. `<iso-standard>`, `<m3d-standard>`, `<csa-standard>`, `<un-standard>`) are *not supported*.
171
212
 
172
- == Supported XML Formats
213
+ == Documentation
173
214
 
174
- This library targets the modern Metanorma XML format which uses `<metanorma>` as the root element.
215
+ Detailed architecture documentation is in the `docs/` directory:
175
216
 
176
- Legacy XML formats that use flavor-specific root elements (e.g. `<iso-standard>`, `<m3d-standard>`, `<csa-standard>`, `<un-standard>`) are *not supported*.
217
+ * xref:docs/html-renderer.adoc[HTML Renderer Architecture] renderer pipeline, drop pattern, class name ownership, template system
@@ -12,7 +12,7 @@
12
12
  border-bottom: 1px solid var(--color-border);
13
13
  }
14
14
  .norm-ref-entry:last-child { border-bottom-color: transparent; }
15
- .std-doc-number { font-weight: 500; color: var(--mn-primary); font-family: var(--font-sans); }
16
- .std-year { font-weight: 500; }
15
+ .ref-doc-number { font-weight: 500; color: var(--mn-primary); font-family: var(--font-sans); }
16
+ .ref-year { font-weight: 500; }
17
17
  .biblio-link { color: inherit; text-decoration: none; }
18
18
  .biblio-link:hover { color: var(--mn-primary); text-decoration: underline; }
@@ -1,8 +1,8 @@
1
1
  /* === Inline Elements === */
2
2
  .fn-marker { vertical-align: super; font-size: 0.78em; color: var(--mn-primary); font-family: var(--font-sans); }
3
3
  .small-caps { font-variant: small-caps; letter-spacing: 0.03em; }
4
- .obligation { font-style: italic; color: var(--mn-accent); }
5
- .bold-title { font-weight: 700; }
4
+ .obligation-text { font-style: italic; color: var(--mn-accent); }
5
+ .title-text { font-weight: 700; }
6
6
 
7
7
  /* Math container — hover dropdown for source formats */
8
8
  .math-container { position: relative; display: inline; }
@@ -30,28 +30,27 @@
30
30
  border-radius: 2px; transition: background 0.15s, color 0.15s;
31
31
  }
32
32
  .stem-copy-btn:hover { background: var(--mn-primary-light); color: var(--mn-primary); }
33
- .nonboldtitle { font-weight: 400; }
33
+ .subtitle-text { font-weight: 400; }
34
34
 
35
- .citesec, .citefig, .citetbl, .citeapp {
35
+ .xref-section, .xref-fig, .xref-table, .xref-app {
36
36
  color: var(--mn-primary);
37
37
  text-decoration: underline;
38
38
  text-decoration-color: rgba(40,56,138,0.2);
39
39
  cursor: pointer;
40
40
  }
41
- .citesec:hover, .citefig:hover, .citetbl:hover, .citeapp:hover {
41
+ .xref-section:hover, .xref-fig:hover, .xref-table:hover, .xref-app:hover {
42
42
  color: var(--mn-accent);
43
43
  }
44
44
 
45
- .doc-subtitle { font-weight: 400; }
46
- .doc-part-number { font-weight: 500; }
47
- .doc-part-title { font-style: italic; }
48
- .doc-publisher, .doc-publisher-name { font-weight: 500; }
49
- .std-doc-number { font-weight: 500; }
50
- .std-year { font-weight: 500; }
45
+ .ref-part-number { font-weight: 500; }
46
+ .ref-title { font-style: italic; }
47
+ .ref-publisher, .ref-publisher-name { font-weight: 500; }
48
+ .ref-doc-number { font-weight: 500; }
49
+ .ref-year { font-weight: 500; }
51
50
  .xref-label { font-weight: 600; }
52
51
  a.xref { color: var(--mn-primary); text-decoration: none; border-bottom: 1px solid var(--mn-primary-light); }
53
52
  a.xref:hover { border-bottom-color: var(--mn-primary); }
54
- .doc-content a.xref .element-name { font-style: italic; }
53
+ .doc-content a.xref .element-label { font-style: italic; }
55
54
 
56
55
  .kbd-hint {
57
56
  display: inline-block;
@@ -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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Metanorma
4
4
  module Document
5
- VERSION = "0.2.0"
5
+ VERSION = "0.2.1"
6
6
  Version = VERSION
7
7
  end
8
8
  end