metanorma-document 0.2.12 → 0.4.0
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/CLAUDE.md +3 -2
- data/README.adoc +1 -1
- data/Rakefile +30 -5
- data/frontend/dist/app.css +1 -0
- data/frontend/dist/app.iife.js +24 -0
- data/frontend/package-lock.json +7 -505
- data/frontend/tailwind.config.js +5 -0
- data/lib/metanorma/csa_document/root.rb +27 -0
- data/lib/metanorma/csa_document.rb +9 -0
- data/lib/metanorma/document/cli.rb +52 -0
- data/lib/metanorma/document/components/ancillary_blocks/sourcecode_body.rb +14 -0
- data/lib/metanorma/document/components/inline/comma_element.rb +3 -0
- data/lib/metanorma/document/components/inline/enum_comma_element.rb +3 -0
- data/lib/metanorma/document/components/inline/eref_element.rb +4 -1
- data/lib/metanorma/document/components/inline/fmt_termsource_element.rb +4 -2
- data/lib/metanorma/document/components/inline/math_element.rb +13 -7
- data/lib/metanorma/document/components/inline/semx_element.rb +7 -8
- data/lib/metanorma/document/components/inline/vocabulary.rb +4 -0
- data/lib/metanorma/document/components/multi_paragraph/admonition_block.rb +6 -1
- data/lib/metanorma/document/components/paragraphs/paragraph_block.rb +3 -0
- data/lib/metanorma/document/components/reference_elements/callout.rb +3 -2
- data/lib/metanorma/document/components/tables/table_cell.rb +3 -0
- data/lib/metanorma/document/elements/input.rb +44 -0
- data/lib/metanorma/document/elements.rb +1 -0
- data/lib/metanorma/document/relaton/address.rb +5 -0
- data/lib/metanorma/document/relaton/affiliation.rb +3 -0
- data/lib/metanorma/document/relaton/bib_item_size.rb +8 -9
- data/lib/metanorma/document/relaton/bib_item_type.rb +3 -0
- data/lib/metanorma/document/relaton/bibliographic_date.rb +5 -0
- data/lib/metanorma/document/relaton/contact_method.rb +5 -0
- data/lib/metanorma/document/relaton/contribution_info.rb +6 -2
- data/lib/metanorma/document/relaton/contributor.rb +4 -0
- data/lib/metanorma/document/relaton/contributor_role.rb +3 -0
- data/lib/metanorma/document/relaton/copyright_association.rb +3 -0
- data/lib/metanorma/document/relaton/copyright_owner.rb +3 -0
- data/lib/metanorma/document/relaton/date_time.rb +3 -0
- data/lib/metanorma/document/relaton/document_identifier.rb +4 -0
- data/lib/metanorma/document/relaton/document_relation.rb +8 -0
- data/lib/metanorma/document/relaton/document_status.rb +4 -0
- data/lib/metanorma/document/relaton/formatted_address.rb +4 -0
- data/lib/metanorma/document/relaton/full_name.rb +14 -3
- data/lib/metanorma/document/relaton/keyword_type.rb +3 -0
- data/lib/metanorma/document/relaton/locality_stack.rb +6 -9
- data/lib/metanorma/document/relaton/localized_name.rb +8 -5
- data/lib/metanorma/document/relaton/logo_element.rb +5 -0
- data/lib/metanorma/document/relaton/medium_type.rb +5 -16
- data/lib/metanorma/document/relaton/org_identifier.rb +2 -0
- data/lib/metanorma/document/relaton/org_subdivision.rb +2 -0
- data/lib/metanorma/document/relaton/org_subdivision_identifier.rb +2 -0
- data/lib/metanorma/document/relaton/organization.rb +3 -0
- data/lib/metanorma/document/relaton/person.rb +7 -1
- data/lib/metanorma/document/relaton/person_identifier.rb +3 -0
- data/lib/metanorma/document/relaton/place_type.rb +5 -0
- data/lib/metanorma/document/relaton/region_element.rb +3 -9
- data/lib/metanorma/document/relaton/relaton_collection.rb +2 -0
- data/lib/metanorma/document/relaton/series_type.rb +7 -0
- data/lib/metanorma/document/relaton/typed_note.rb +4 -0
- data/lib/metanorma/document/relaton/typed_title_string.rb +4 -0
- data/lib/metanorma/document/relaton/typed_uri.rb +3 -8
- data/lib/metanorma/document/relaton/validity_type.rb +8 -10
- data/lib/metanorma/document/relaton/variant_full_name.rb +3 -0
- data/lib/metanorma/document/relaton/variant_org_name.rb +3 -0
- data/lib/metanorma/document/relaton/version_info.rb +5 -0
- data/lib/metanorma/document/relaton/vocab_id_type.rb +6 -12
- data/lib/metanorma/document/relaton.rb +0 -14
- data/lib/metanorma/document/version.rb +1 -1
- data/lib/metanorma/document.rb +1 -0
- data/lib/metanorma/html/base_renderer.rb +133 -307
- data/lib/metanorma/html/component/base.rb +2 -9
- data/lib/metanorma/html/concerns/metadata_extraction.rb +61 -0
- data/lib/metanorma/html/concerns/presentation_validation.rb +61 -0
- data/lib/metanorma/html/concerns/svg_processing.rb +32 -0
- data/lib/metanorma/html/concerns/text_extraction.rb +93 -0
- data/lib/metanorma/html/concerns/toc_registry.rb +47 -0
- data/lib/metanorma/html/csa_renderer.rb +9 -0
- data/lib/metanorma/html/drops/figure_drop.rb +33 -10
- data/lib/metanorma/html/drops/sourcecode_drop.rb +3 -6
- data/lib/metanorma/html/flavor.rb +50 -0
- data/lib/metanorma/html/flavor_registry.rb +49 -0
- data/lib/metanorma/html/generator.rb +113 -29
- data/lib/metanorma/html/iso_renderer.rb +0 -142
- data/lib/metanorma/html/renderer_delegation.rb +21 -0
- data/lib/metanorma/html/renderers/block_renderer.rb +10 -0
- data/lib/metanorma/html/renderers/element_order_traversal.rb +48 -0
- data/lib/metanorma/html/renderers/inline_renderer.rb +49 -124
- data/lib/metanorma/html/renderers/pubid_renderer.rb +4 -26
- data/lib/metanorma/html/renderers.rb +2 -0
- data/lib/metanorma/html/standard_renderer.rb +198 -36
- data/lib/metanorma/html.rb +4 -51
- data/lib/metanorma/iso_document/sections/iso_preface.rb +27 -13
- data/lib/metanorma/mirror/default_registry.rb +21 -0
- data/lib/metanorma/mirror/handlers/inline/catalog.rb +52 -0
- data/lib/metanorma/mirror/handlers/inline/rich_html_renderer.rb +34 -35
- data/lib/metanorma/mirror/handlers/inline.rb +1 -0
- data/lib/metanorma/mirror/handlers/sourcecode.rb +1 -1
- data/lib/metanorma/mirror/model/guide.rb +3 -2
- data/lib/metanorma/mirror/output/formats/inline_format.rb +64 -3
- data/lib/metanorma/mirror/output/pipeline.rb +1 -0
- data/lib/metanorma/mirror/output.rb +0 -2
- data/lib/metanorma/mirror/rewriter.rb +3 -1
- data/lib/metanorma/mirror/transformer.rb +91 -3
- data/lib/metanorma/mirror.rb +0 -1
- data/lib/metanorma/registers/setup.rb +10 -0
- data/lib/metanorma/standard_document/annotation_container.rb +2 -0
- data/lib/metanorma/standard_document/elements/input.rb +4 -25
- data/lib/metanorma/standard_document/metadata/metanorma_extension.rb +12 -2
- data/lib/metanorma/standard_document/metadata/presentation_metadata.rb +107 -0
- data/lib/metanorma/standard_document/metadata/semantic_metadata.rb +18 -0
- data/lib/metanorma/standard_document/metadata/unitsml/amount_of_substance.rb +19 -0
- data/lib/metanorma/standard_document/metadata/unitsml/base_quantity.rb +34 -0
- data/lib/metanorma/standard_document/metadata/unitsml/dimension.rb +39 -0
- data/lib/metanorma/standard_document/metadata/unitsml/dimension_set.rb +19 -0
- data/lib/metanorma/standard_document/metadata/unitsml/electric_current.rb +19 -0
- data/lib/metanorma/standard_document/metadata/unitsml/enumerated_root_unit.rb +23 -0
- data/lib/metanorma/standard_document/metadata/unitsml/length.rb +19 -0
- data/lib/metanorma/standard_document/metadata/unitsml/luminous_intensity.rb +19 -0
- data/lib/metanorma/standard_document/metadata/unitsml/mass.rb +19 -0
- data/lib/metanorma/standard_document/metadata/unitsml/namespace.rb +17 -0
- data/lib/metanorma/standard_document/metadata/unitsml/plane_angle.rb +19 -0
- data/lib/metanorma/standard_document/metadata/unitsml/prefix.rb +30 -0
- data/lib/metanorma/standard_document/metadata/unitsml/prefix_name.rb +21 -0
- data/lib/metanorma/standard_document/metadata/unitsml/prefix_set.rb +19 -0
- data/lib/metanorma/standard_document/metadata/unitsml/prefix_symbol.rb +21 -0
- data/lib/metanorma/standard_document/metadata/unitsml/quantity.rb +28 -0
- data/lib/metanorma/standard_document/metadata/unitsml/quantity_name.rb +21 -0
- data/lib/metanorma/standard_document/metadata/unitsml/quantity_set.rb +19 -0
- data/lib/metanorma/standard_document/metadata/unitsml/root_units.rb +19 -0
- data/lib/metanorma/standard_document/metadata/unitsml/thermodynamic_temperature.rb +19 -0
- data/lib/metanorma/standard_document/metadata/unitsml/time.rb +19 -0
- data/lib/metanorma/standard_document/metadata/unitsml/unit.rb +32 -0
- data/lib/metanorma/standard_document/metadata/unitsml/unit_name.rb +21 -0
- data/lib/metanorma/standard_document/metadata/unitsml/unit_set.rb +19 -0
- data/lib/metanorma/standard_document/metadata/unitsml/unit_symbol.rb +36 -0
- data/lib/metanorma/standard_document/metadata/unitsml/unit_system.rb +23 -0
- data/lib/metanorma/standard_document/metadata/unitsml/unitsml_root.rb +28 -0
- data/lib/metanorma/standard_document/metadata/unitsml.rb +39 -0
- data/lib/metanorma/standard_document/metadata.rb +4 -0
- data/lib/metanorma/standard_document/sections/misc_container.rb +8 -6
- data/lib/metanorma/standard_document/sections/sections.rb +7 -0
- data/lib/metanorma/un_document/blocks/un_admonition_block.rb +86 -0
- data/lib/metanorma/un_document/blocks.rb +9 -0
- data/lib/metanorma/un_document/root.rb +8 -0
- data/lib/metanorma/un_document/sections/un_abstract_section.rb +48 -0
- data/lib/metanorma/un_document/sections/un_preface.rb +19 -2
- data/lib/metanorma/un_document/sections.rb +1 -0
- data/lib/metanorma/un_document/un_text_element.rb +116 -0
- data/lib/metanorma/un_document.rb +2 -0
- data/script/benchmark_render.rb +35 -0
- data/tasks/roundtrip_samples.rake +1 -1
- metadata +78 -50
- data/.rubocop_todo.yml +0 -327
- data/TODO.finalize/01-split-multi-class-files.md +0 -8
- data/TODO.finalize/02-remove-data-dist-iso-document-frontend.md +0 -6
- data/TODO.finalize/03-align-frontend-iife-and-gha-build.md +0 -11
- data/TODO.finalize/04-theme-per-flavor-config-driven.md +0 -9
- data/TODO.finalize/05-migrate-all-html-to-liquid.md +0 -16
- data/TODO.finalize/06-integrate-pubid-library.md +0 -16
- data/TODO.finalize/07-replace-relaton-models-with-relaton-bib.md +0 -39
- data/TODO.finalize/08-refactor-renderer-architecture.md +0 -25
- data/TODO.finalize/09-eliminate-code-smells.md +0 -14
- data/TODO.finalize/10-comprehensive-spec-coverage.md +0 -24
- data/TODO.finalize/11-config-driven-flavor-metadata.md +0 -20
- data/TODO.finalize/12-add-frozen-string-literal.md +0 -6
- data/TODO.finalize/13-extract-composition-from-god-class.md +0 -96
- data/TODO.finalize/14-config-driven-section-ordering.md +0 -31
- data/TODO.finalize/15-theme-directories-per-flavor.md +0 -46
- data/TODO.finalize/16-theme-lutaml-model-schema.md +0 -44
- data/docs/html-renderer.adoc +0 -261
- data/lib/data/dist/iso_document/frontend/assets/index-mwzbzmnK.js +0 -22
- data/lib/data/dist/iso_document/frontend/assets/index-pGvKTNid.css +0 -1
- data/lib/data/dist/iso_document/frontend/assets/iso-red.svg +0 -53
- data/lib/data/dist/iso_document/frontend/index.html +0 -13
- data/lib/metanorma/document/relaton/bib_item_size_type.rb +0 -21
- data/lib/metanorma/document/relaton/bibliographic_date_type.rb +0 -22
- data/lib/metanorma/document/relaton/document_relation_type.rb +0 -31
- data/lib/metanorma/document/relaton/iso4217_code.rb +0 -17
- data/lib/metanorma/document/relaton/iso8601_date.rb +0 -17
- data/lib/metanorma/document/relaton/personal_identifier_type.rb +0 -21
- data/lib/metanorma/document/relaton/region_type.rb +0 -20
- data/lib/metanorma/document/relaton/series_type_type.rb +0 -21
- data/lib/metanorma/document/relaton/specific_locality_type.rb +0 -25
- data/lib/metanorma/document/relaton/title_type.rb +0 -21
- data/lib/metanorma/mirror/metanorma_to_mirror.rb +0 -101
- data/lib/metanorma/mirror/output/html_renderer.rb +0 -95
- data/lib/metanorma/mirror/output/html_renderers/block_renderers.rb +0 -178
- data/lib/metanorma/mirror/output/html_renderers/inline_renderer.rb +0 -61
- data/lib/metanorma/mirror/output/html_renderers/list_renderers.rb +0 -96
- data/lib/metanorma/mirror/output/html_renderers/mark_renderers.rb +0 -64
- data/lib/metanorma/mirror/output/html_renderers/section_renderers.rb +0 -129
- data/lib/metanorma/mirror/output/html_renderers/structural_renderers.rb +0 -80
- data/lib/metanorma/mirror/output/html_renderers/table_renderers.rb +0 -87
- data/lib/metanorma/mirror/output/html_renderers.rb +0 -76
- data/lib/metanorma/standard_document/annotation.rb +0 -37
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Metanorma
|
|
4
|
+
module Html
|
|
5
|
+
module Concerns
|
|
6
|
+
# Presentation-XML validation mixed into BaseRenderer. HTML
|
|
7
|
+
# generation requires Presentation XML input — semantic XML does not
|
|
8
|
+
# contain the formatting data needed for HTML — so the document is
|
|
9
|
+
# checked for presentation markers before rendering begins.
|
|
10
|
+
module PresentationValidation
|
|
11
|
+
def validate_presentation_xml!
|
|
12
|
+
has_presentation = check_presentation_markers(@document)
|
|
13
|
+
return if has_presentation
|
|
14
|
+
|
|
15
|
+
raise ArgumentError,
|
|
16
|
+
"HTML generation requires Presentation XML input. " \
|
|
17
|
+
"Semantic XML does not contain formatting data needed for HTML. " \
|
|
18
|
+
"Use a '.presentation.xml' file instead."
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Name kept for API stability (public renderer API); the boolean
|
|
22
|
+
# return predates the Naming/PredicateMethod convention.
|
|
23
|
+
# rubocop:disable Naming/PredicateMethod
|
|
24
|
+
def check_presentation_markers(node)
|
|
25
|
+
return false unless node
|
|
26
|
+
return false if node.is_a?(String)
|
|
27
|
+
|
|
28
|
+
if node.is_a?(Lutaml::Model::Serializable)
|
|
29
|
+
node_attrs = node.class.attributes
|
|
30
|
+
if node_attrs.key?(:type) && node.type == "presentation"
|
|
31
|
+
return true
|
|
32
|
+
end
|
|
33
|
+
if node_attrs.key?(:fmt_title) && node.fmt_title
|
|
34
|
+
return true
|
|
35
|
+
end
|
|
36
|
+
if node_attrs.key?(:displayorder) && node.displayorder
|
|
37
|
+
return true
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
%i[preface sections annex bibliography].each do |attr|
|
|
41
|
+
next unless node_attrs.key?(attr)
|
|
42
|
+
|
|
43
|
+
val = node.public_send(attr)
|
|
44
|
+
next unless val
|
|
45
|
+
|
|
46
|
+
Array(val).each { |v| return true if check_presentation_markers(v) }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
node.each_mixed_content do |child|
|
|
50
|
+
next if child.is_a?(String)
|
|
51
|
+
return true if check_presentation_markers(child)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
false
|
|
56
|
+
end
|
|
57
|
+
# rubocop:enable Naming/PredicateMethod
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Metanorma
|
|
4
|
+
module Html
|
|
5
|
+
module Concerns
|
|
6
|
+
# SVG logo loading and normalization mixed into BaseRenderer.
|
|
7
|
+
# Logos ship as standalone SVG files; for inline embedding the xml
|
|
8
|
+
# prolog and leading comments are stripped, a CSS class is added,
|
|
9
|
+
# and width/height are normalized to the requested display height.
|
|
10
|
+
module SvgProcessing
|
|
11
|
+
def load_logo_svg(filename, height: 32)
|
|
12
|
+
path = theme.resolve_asset(filename) || File.join(BaseRenderer::LOGO_DIR, filename)
|
|
13
|
+
return nil unless File.exist?(path)
|
|
14
|
+
|
|
15
|
+
svg = File.read(path)
|
|
16
|
+
svg = svg.sub(/\A<\?xml[^?]*\?>\s*/, "")
|
|
17
|
+
svg = svg.sub(/\A\s*<!--.*?-->\s*/m, "")
|
|
18
|
+
svg = svg.sub(/<svg\s/, '<svg class="header-logo" ')
|
|
19
|
+
svg = if svg.match?(/<svg[^>]*\sheight="[^"]*"/)
|
|
20
|
+
svg.sub(/(<svg[^>]*?)(\sheight="[^"]*")/,
|
|
21
|
+
"\\1 height=\"#{height}\"")
|
|
22
|
+
else
|
|
23
|
+
svg.sub(/(<svg\b)/, "\\1 height=\"#{height}\"")
|
|
24
|
+
end
|
|
25
|
+
svg.sub(/(<svg[^>]*?)\swidth="[^"]*"/, '\1')
|
|
26
|
+
rescue StandardError
|
|
27
|
+
nil
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Metanorma
|
|
4
|
+
module Html
|
|
5
|
+
module Concerns
|
|
6
|
+
# Plain-text extraction from model nodes, mixed into BaseRenderer.
|
|
7
|
+
# Used wherever rendered output needs a text-only form (titles,
|
|
8
|
+
# labels, captions, ToC entries). Walks `element_order` and typed
|
|
9
|
+
# attributes directly on the model — never strips tags from
|
|
10
|
+
# rendered HTML.
|
|
11
|
+
module TextExtraction
|
|
12
|
+
def extract_plain_text(node)
|
|
13
|
+
return node.to_s if node.is_a?(String)
|
|
14
|
+
return extract_text_value(node).to_s unless node.is_a?(Lutaml::Model::Serializable)
|
|
15
|
+
|
|
16
|
+
parts = []
|
|
17
|
+
xml_mapping = node.class.mappings_for(:xml, node.lutaml_register)
|
|
18
|
+
|
|
19
|
+
if node.element_order.is_a?(Array) && xml_mapping
|
|
20
|
+
element_to_attr =
|
|
21
|
+
Renderers::ElementOrderTraversal.element_to_attr_map(xml_mapping)
|
|
22
|
+
|
|
23
|
+
indices = Hash.new(0)
|
|
24
|
+
node.element_order.each do |el|
|
|
25
|
+
next unless el.is_a?(Lutaml::Xml::Element)
|
|
26
|
+
|
|
27
|
+
if el.text?
|
|
28
|
+
parts << el.text_content.to_s
|
|
29
|
+
elsif el.name == "tab"
|
|
30
|
+
parts << " "
|
|
31
|
+
# rubocop:disable Lint/DuplicateBranch
|
|
32
|
+
elsif el.name == "br"
|
|
33
|
+
parts << " "
|
|
34
|
+
# rubocop:enable Lint/DuplicateBranch
|
|
35
|
+
elsif el.element?
|
|
36
|
+
attr_name = element_to_attr[el.name]
|
|
37
|
+
if attr_name
|
|
38
|
+
coll = node.public_send(attr_name)
|
|
39
|
+
obj = if coll.is_a?(Array)
|
|
40
|
+
idx = indices[attr_name]
|
|
41
|
+
indices[attr_name] += 1
|
|
42
|
+
coll[idx]
|
|
43
|
+
else
|
|
44
|
+
coll
|
|
45
|
+
end
|
|
46
|
+
text = extract_plain_text(obj)
|
|
47
|
+
parts << (text.empty? ? " " : text)
|
|
48
|
+
elsif el.name == "span"
|
|
49
|
+
parts << " "
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
if parts.join.strip.empty?
|
|
56
|
+
t = safe_attr(node, :text)
|
|
57
|
+
parts << (t.is_a?(Array) ? t.join : t.to_s) if t
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
parts.join.strip.gsub(" ", " ")
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def extract_text_value(val)
|
|
64
|
+
return nil if val.nil?
|
|
65
|
+
return val if val.is_a?(String)
|
|
66
|
+
|
|
67
|
+
if val.is_a?(Array)
|
|
68
|
+
val.map { |v| extract_text_value(v) }.join
|
|
69
|
+
elsif val.is_a?(Lutaml::Model::Serializable)
|
|
70
|
+
c = safe_attr(val, :content)
|
|
71
|
+
if c && !c.equal?(val)
|
|
72
|
+
extract_text_value(c)
|
|
73
|
+
else
|
|
74
|
+
t = safe_attr(val, :text)
|
|
75
|
+
if t
|
|
76
|
+
extract_text_value(t)
|
|
77
|
+
else
|
|
78
|
+
v = safe_attr(val, :value)
|
|
79
|
+
if v
|
|
80
|
+
extract_text_value(v)
|
|
81
|
+
else
|
|
82
|
+
val.to_s
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
else
|
|
87
|
+
val.to_s
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Metanorma
|
|
4
|
+
module Html
|
|
5
|
+
module Concerns
|
|
6
|
+
# Table-of-contents entry collection and ToC rendering, mixed into
|
|
7
|
+
# BaseRenderer. Sub-renderers register section/figure/table entries
|
|
8
|
+
# as they walk the document; assemble_document renders the collected
|
|
9
|
+
# entries through the _toc.html.liquid template.
|
|
10
|
+
module TocRegistry
|
|
11
|
+
def toc_entries
|
|
12
|
+
@toc_entries
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def build_toc_html(entries)
|
|
16
|
+
entry_drops = entries.map { |e| Drops::TocEntryDrop.new(e) }
|
|
17
|
+
figure_drops = @figure_entries.map { |f| Drops::FigureListEntryDrop.new(f) }
|
|
18
|
+
table_drops = @table_entries.map { |t| Drops::FigureListEntryDrop.new(t) }
|
|
19
|
+
has_special_lists = !@figure_entries.empty? || !@table_entries.empty?
|
|
20
|
+
|
|
21
|
+
render_liquid("_toc.html.liquid", {
|
|
22
|
+
"entries" => entry_drops,
|
|
23
|
+
"figures" => figure_drops,
|
|
24
|
+
"tables" => table_drops,
|
|
25
|
+
"has_special_lists" => has_special_lists,
|
|
26
|
+
})
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def register_toc_entry(id:, level:, text:)
|
|
30
|
+
@toc_entries << { id: id, level: level, text: text }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def register_figure_entry(id:, text:)
|
|
34
|
+
@figure_entries << { id: id, text: text }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def figure_entries
|
|
38
|
+
@figure_entries
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def register_table_entry(id:, text:)
|
|
42
|
+
@table_entries << { id: id, text: text }
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -47,24 +47,47 @@ module Metanorma
|
|
|
47
47
|
end
|
|
48
48
|
sub_figures_html = sub_figures_parts.join
|
|
49
49
|
|
|
50
|
-
key_parts = []
|
|
51
|
-
renderer.safe_attr(figure, :note)&.each do |n|
|
|
52
|
-
key_parts << (renderer.render_note(n) || "")
|
|
53
|
-
end
|
|
54
|
-
renderer.safe_attr(figure, :dl)&.then do |dl|
|
|
55
|
-
key_parts << (renderer.render_definition_list(dl) || "")
|
|
56
|
-
end
|
|
57
|
-
key_html = key_parts.join
|
|
58
|
-
|
|
59
50
|
new(
|
|
60
51
|
id: id,
|
|
61
52
|
image_html: image_html,
|
|
62
53
|
caption_html: caption_html,
|
|
63
|
-
key_html:
|
|
54
|
+
key_html: build_key_html(figure, renderer),
|
|
64
55
|
sub_figures_html: sub_figures_html,
|
|
65
56
|
css_class: "figure",
|
|
66
57
|
)
|
|
67
58
|
end
|
|
59
|
+
|
|
60
|
+
class << self
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
# Notes, plain definition lists, and figure.key (e.g.
|
|
64
|
+
# <key class="formula_dl">) — symbol/footnote definitions
|
|
65
|
+
# belonging to the figure.
|
|
66
|
+
def build_key_html(figure, renderer)
|
|
67
|
+
parts = []
|
|
68
|
+
renderer.safe_attr(figure, :note)&.each do |n|
|
|
69
|
+
parts << (renderer.render_note(n) || "")
|
|
70
|
+
end
|
|
71
|
+
renderer.safe_attr(figure, :dl)&.then do |dl|
|
|
72
|
+
parts << (renderer.render_definition_list(dl) || "")
|
|
73
|
+
end
|
|
74
|
+
append_figure_key(parts, renderer.safe_attr(figure, :key), renderer)
|
|
75
|
+
parts.join
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def append_figure_key(parts, key, renderer)
|
|
79
|
+
return unless key
|
|
80
|
+
|
|
81
|
+
name = renderer.safe_attr(key, :name)
|
|
82
|
+
parts << (renderer.render_inline_element(name) || "") if name
|
|
83
|
+
if renderer.safe_attr(key, :dl)
|
|
84
|
+
parts << (renderer.render_definition_list(key.dl) || "")
|
|
85
|
+
end
|
|
86
|
+
renderer.safe_attr(key, :p)&.each do |para|
|
|
87
|
+
parts << (renderer.render_paragraph(para) || "")
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
68
91
|
end
|
|
69
92
|
end
|
|
70
93
|
end
|
|
@@ -23,22 +23,19 @@ css_class: nil)
|
|
|
23
23
|
renderer.render_inline_element(sc.name)
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
code_text = if sc.body
|
|
27
|
-
|
|
26
|
+
code_text = if sc.body
|
|
27
|
+
sc.body.decoded_content
|
|
28
28
|
elsif sc.content
|
|
29
29
|
Array(sc.content).join
|
|
30
30
|
else
|
|
31
31
|
""
|
|
32
32
|
end
|
|
33
|
-
raw_text = code_text.gsub("<", "<").gsub(">", ">").gsub("&", "&").gsub(
|
|
34
|
-
""", "\""
|
|
35
|
-
)
|
|
36
33
|
|
|
37
34
|
new(
|
|
38
35
|
id: id,
|
|
39
36
|
lang: lang,
|
|
40
37
|
name_html: name_html,
|
|
41
|
-
code_html: renderer.escape_html(
|
|
38
|
+
code_html: renderer.escape_html(code_text),
|
|
42
39
|
css_class: "sourcecode",
|
|
43
40
|
)
|
|
44
41
|
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "pubid"
|
|
4
|
+
|
|
5
|
+
module Metanorma
|
|
6
|
+
module Html
|
|
7
|
+
# A flavor ties together the four concepts that identify a Metanorma
|
|
8
|
+
# document variant:
|
|
9
|
+
# - a symbolic name (used for theme loading, CSS class names)
|
|
10
|
+
# - a model class (the typed root class for the flavor)
|
|
11
|
+
# - a renderer class (the HTML renderer that handles the flavor)
|
|
12
|
+
# - an optional Pubid module (for parsing identifiers like ISO/IEC/etc.)
|
|
13
|
+
#
|
|
14
|
+
# Flavor centralizes flavor identity so BaseRenderer, PubidRenderer,
|
|
15
|
+
# and Generator all read from the same source of truth. Adding a new
|
|
16
|
+
# flavor is one Flavor entry in the registry, not three coordinated
|
|
17
|
+
# edits across three files.
|
|
18
|
+
class Flavor
|
|
19
|
+
attr_reader :name, :model_class, :renderer_class, :pubid_module
|
|
20
|
+
|
|
21
|
+
def initialize(name:, model_class:, renderer_class:, pubid_module: nil)
|
|
22
|
+
@name = name
|
|
23
|
+
@model_class = model_class
|
|
24
|
+
@renderer_class = renderer_class
|
|
25
|
+
@pubid_module = pubid_module
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Match by ancestry: a document whose class is the flavor's model
|
|
29
|
+
# class, or any descendant of it, belongs to this flavor. Walks
|
|
30
|
+
# parent classes via is_a? to support subclassing.
|
|
31
|
+
def matches?(document_class)
|
|
32
|
+
return false unless document_class.is_a?(Class)
|
|
33
|
+
|
|
34
|
+
# `<=` returns nil for unrelated classes; coerce to boolean.
|
|
35
|
+
!!(document_class <= model_class)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def pubid_module_const
|
|
39
|
+
return nil unless pubid_module
|
|
40
|
+
|
|
41
|
+
Object.const_get(pubid_module.to_s)
|
|
42
|
+
rescue NameError
|
|
43
|
+
raise ArgumentError,
|
|
44
|
+
"Flavor #{name.inspect}: pubid module #{pubid_module} could " \
|
|
45
|
+
"not be resolved — fix the registration in " \
|
|
46
|
+
"Html::Generator.build_flavor_registry"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Metanorma
|
|
4
|
+
module Html
|
|
5
|
+
# Registry of known flavors. Queried by:
|
|
6
|
+
# - Generator — to pick the renderer for a document
|
|
7
|
+
# - BaseRenderer — to resolve flavor_name for theme loading
|
|
8
|
+
# - PubidRenderer — to resolve the Pubid module
|
|
9
|
+
#
|
|
10
|
+
# Single source of truth for flavor identity. Adding a flavor is
|
|
11
|
+
# one Flavor entry via #register.
|
|
12
|
+
class FlavorRegistry
|
|
13
|
+
include Enumerable
|
|
14
|
+
|
|
15
|
+
def initialize
|
|
16
|
+
@flavors = []
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def register(flavor)
|
|
20
|
+
@flavors << flavor
|
|
21
|
+
self
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def each(&)
|
|
25
|
+
@flavors.each(&)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Returns the most-specific Flavor whose model_class matches the
|
|
29
|
+
# given document class (by ancestry). Most-specific = registered
|
|
30
|
+
# last. We scan in reverse so general flavors (registered first)
|
|
31
|
+
# only match if no more specific flavor does.
|
|
32
|
+
def find_for(document_class)
|
|
33
|
+
@flavors.reverse_each.find { |f| f.matches?(document_class) }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def name_for(document_class)
|
|
37
|
+
find_for(document_class)&.name
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def renderer_for(document_class)
|
|
41
|
+
find_for(document_class)&.renderer_class
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def pubid_module_for(document_class)
|
|
45
|
+
find_for(document_class)&.pubid_module_const
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -3,18 +3,22 @@
|
|
|
3
3
|
module Metanorma
|
|
4
4
|
module Html
|
|
5
5
|
class Generator
|
|
6
|
-
@renderers = []
|
|
7
6
|
@tastes = []
|
|
8
7
|
@setup = false
|
|
9
8
|
|
|
10
9
|
class << self
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
# Returns the full FlavorRegistry built from setup!. Read-only
|
|
11
|
+
# access for BaseRenderer, PubidRenderer, and any consumer that
|
|
12
|
+
# needs flavor identity.
|
|
13
|
+
def flavors
|
|
14
|
+
setup! unless @setup
|
|
15
|
+
@flavors
|
|
13
16
|
end
|
|
14
17
|
|
|
15
|
-
# Register a taste: same document model, different renderer based
|
|
16
|
-
# When the document's first author publisher
|
|
17
|
-
# the taste renderer takes precedence over
|
|
18
|
+
# Register a taste: same document model, different renderer based
|
|
19
|
+
# on publisher. When the document's first author publisher
|
|
20
|
+
# abbreviation matches, the taste renderer takes precedence over
|
|
21
|
+
# the model-based renderer.
|
|
18
22
|
def register_taste(model_class, publisher_abbrev, renderer_class)
|
|
19
23
|
@tastes << [model_class, publisher_abbrev, renderer_class]
|
|
20
24
|
end
|
|
@@ -27,15 +31,11 @@ module Metanorma
|
|
|
27
31
|
def renderer_for(document)
|
|
28
32
|
setup! unless @setup
|
|
29
33
|
|
|
30
|
-
# Check tastes first (publisher-based dispatch)
|
|
31
34
|
taste_renderer = find_taste(document)
|
|
32
35
|
return taste_renderer if taste_renderer
|
|
33
36
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return renderer_class if document.is_a?(model_class)
|
|
37
|
-
end
|
|
38
|
-
BaseRenderer
|
|
37
|
+
flavor = @flavors.find_for(document.class)
|
|
38
|
+
flavor&.renderer_class || BaseRenderer
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
private
|
|
@@ -79,6 +79,7 @@ module Metanorma
|
|
|
79
79
|
return if @setup
|
|
80
80
|
|
|
81
81
|
@setup = true
|
|
82
|
+
@flavors = build_flavor_registry
|
|
82
83
|
|
|
83
84
|
# Trigger autoloads by referencing constants
|
|
84
85
|
BaseRenderer
|
|
@@ -86,6 +87,7 @@ module Metanorma
|
|
|
86
87
|
IsoRenderer
|
|
87
88
|
BipmRenderer
|
|
88
89
|
CcRenderer
|
|
90
|
+
CsaRenderer
|
|
89
91
|
IccRenderer
|
|
90
92
|
IecRenderer
|
|
91
93
|
IeeeRenderer
|
|
@@ -97,27 +99,109 @@ module Metanorma
|
|
|
97
99
|
PdfaRenderer
|
|
98
100
|
RiboseRenderer
|
|
99
101
|
|
|
100
|
-
# Register renderers (most general first, most specific last)
|
|
101
|
-
register Metanorma::Document::Root, BaseRenderer
|
|
102
|
-
register Metanorma::StandardDocument::Root, StandardRenderer
|
|
103
|
-
register Metanorma::IsoDocument::Root, IsoRenderer
|
|
104
|
-
register Metanorma::BipmDocument::Root, BipmRenderer
|
|
105
|
-
register Metanorma::CcDocument::Root, CcRenderer
|
|
106
|
-
register Metanorma::IecDocument::Root, IecRenderer
|
|
107
|
-
register Metanorma::IeeeDocument::Root, IeeeRenderer
|
|
108
|
-
register Metanorma::IetfDocument::Root, IetfRenderer
|
|
109
|
-
register Metanorma::IhoDocument::Root, IhoRenderer
|
|
110
|
-
register Metanorma::ItuDocument::Root, ItuRenderer
|
|
111
|
-
register Metanorma::OgcDocument::Root, OgcRenderer
|
|
112
|
-
register Metanorma::OimlDocument::Root, OimlRenderer
|
|
113
|
-
register Metanorma::RiboseDocument::Root, RiboseRenderer
|
|
114
|
-
|
|
115
102
|
# Register tastes (publisher-based dispatch within same model)
|
|
116
|
-
register_taste Metanorma::IsoDocument::Root,
|
|
117
|
-
IccRenderer
|
|
103
|
+
register_taste Metanorma::IsoDocument::Root, "ICC", IccRenderer
|
|
118
104
|
register_taste Metanorma::RiboseDocument::Root, "PDF Association",
|
|
119
105
|
PdfaRenderer
|
|
120
106
|
end
|
|
107
|
+
|
|
108
|
+
# Single source of truth for flavor identity. Each Flavor ties
|
|
109
|
+
# together the model class, the renderer, the symbolic name, and
|
|
110
|
+
# (if applicable) the Pubid module. Adding a new flavor = one
|
|
111
|
+
# entry here.
|
|
112
|
+
def build_flavor_registry
|
|
113
|
+
FlavorRegistry.new.tap do |registry|
|
|
114
|
+
registry.register(Flavor.new(
|
|
115
|
+
name: nil,
|
|
116
|
+
model_class: Metanorma::Document::Root,
|
|
117
|
+
renderer_class: BaseRenderer,
|
|
118
|
+
))
|
|
119
|
+
registry.register(Flavor.new(
|
|
120
|
+
name: nil,
|
|
121
|
+
model_class: Metanorma::StandardDocument::Root,
|
|
122
|
+
renderer_class: StandardRenderer,
|
|
123
|
+
))
|
|
124
|
+
registry.register(Flavor.new(
|
|
125
|
+
name: :iso,
|
|
126
|
+
model_class: Metanorma::IsoDocument::Root,
|
|
127
|
+
renderer_class: IsoRenderer,
|
|
128
|
+
pubid_module: :"Pubid::Iso",
|
|
129
|
+
))
|
|
130
|
+
registry.register(Flavor.new(
|
|
131
|
+
name: :bipm,
|
|
132
|
+
model_class: Metanorma::BipmDocument::Root,
|
|
133
|
+
renderer_class: BipmRenderer,
|
|
134
|
+
))
|
|
135
|
+
registry.register(Flavor.new(
|
|
136
|
+
name: :cc,
|
|
137
|
+
model_class: Metanorma::CcDocument::Root,
|
|
138
|
+
renderer_class: CcRenderer,
|
|
139
|
+
))
|
|
140
|
+
registry.register(Flavor.new(
|
|
141
|
+
name: :csa,
|
|
142
|
+
model_class: Metanorma::CsaDocument::Root,
|
|
143
|
+
renderer_class: CsaRenderer,
|
|
144
|
+
pubid_module: :"Pubid::Csa",
|
|
145
|
+
))
|
|
146
|
+
registry.register(Flavor.new(
|
|
147
|
+
name: :iec,
|
|
148
|
+
model_class: Metanorma::IecDocument::Root,
|
|
149
|
+
renderer_class: IecRenderer,
|
|
150
|
+
pubid_module: :"Pubid::Iec",
|
|
151
|
+
))
|
|
152
|
+
registry.register(Flavor.new(
|
|
153
|
+
name: :ieee,
|
|
154
|
+
model_class: Metanorma::IeeeDocument::Root,
|
|
155
|
+
renderer_class: IeeeRenderer,
|
|
156
|
+
pubid_module: :"Pubid::Ieee",
|
|
157
|
+
))
|
|
158
|
+
registry.register(Flavor.new(
|
|
159
|
+
name: :ietf,
|
|
160
|
+
model_class: Metanorma::IetfDocument::Root,
|
|
161
|
+
renderer_class: IetfRenderer,
|
|
162
|
+
))
|
|
163
|
+
registry.register(Flavor.new(
|
|
164
|
+
name: :iho,
|
|
165
|
+
model_class: Metanorma::IhoDocument::Root,
|
|
166
|
+
renderer_class: IhoRenderer,
|
|
167
|
+
pubid_module: :"Pubid::Iho",
|
|
168
|
+
))
|
|
169
|
+
registry.register(Flavor.new(
|
|
170
|
+
name: :itu,
|
|
171
|
+
model_class: Metanorma::ItuDocument::Root,
|
|
172
|
+
renderer_class: ItuRenderer,
|
|
173
|
+
pubid_module: :"Pubid::Itu",
|
|
174
|
+
))
|
|
175
|
+
registry.register(Flavor.new(
|
|
176
|
+
name: :ogc,
|
|
177
|
+
model_class: Metanorma::OgcDocument::Root,
|
|
178
|
+
renderer_class: OgcRenderer,
|
|
179
|
+
))
|
|
180
|
+
registry.register(Flavor.new(
|
|
181
|
+
name: :oiml,
|
|
182
|
+
model_class: Metanorma::OimlDocument::Root,
|
|
183
|
+
renderer_class: OimlRenderer,
|
|
184
|
+
pubid_module: :"Pubid::Oiml",
|
|
185
|
+
))
|
|
186
|
+
registry.register(Flavor.new(
|
|
187
|
+
name: :pdfa,
|
|
188
|
+
model_class: Metanorma::RiboseDocument::Root,
|
|
189
|
+
renderer_class: PdfaRenderer,
|
|
190
|
+
))
|
|
191
|
+
registry.register(Flavor.new(
|
|
192
|
+
name: :ribose,
|
|
193
|
+
model_class: Metanorma::RiboseDocument::Root,
|
|
194
|
+
renderer_class: RiboseRenderer,
|
|
195
|
+
))
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def safe_attr(node, attr_name)
|
|
200
|
+
return nil unless node.is_a?(Lutaml::Model::Serializable)
|
|
201
|
+
return nil unless node.class.attributes.key?(attr_name)
|
|
202
|
+
|
|
203
|
+
node.public_send(attr_name)
|
|
204
|
+
end
|
|
121
205
|
end
|
|
122
206
|
end
|
|
123
207
|
end
|