arrolio 0.1.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.
Files changed (272) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +2 -0
  3. data/.rubocop.yml +343 -0
  4. data/CHANGELOG.md +18 -0
  5. data/CLAUDE.md +154 -0
  6. data/Gemfile +15 -0
  7. data/LICENSE +24 -0
  8. data/README.adoc +95 -0
  9. data/Rakefile +21 -0
  10. data/TODO.complete/01-true-type-font-metrics.md +56 -0
  11. data/TODO.complete/02-font-embedding.md +51 -0
  12. data/TODO.complete/03-knuth-plass-breaker.md +42 -0
  13. data/TODO.complete/04-table-flowable.md +60 -0
  14. data/TODO.complete/05-list-flowable.md +50 -0
  15. data/TODO.complete/06-notes-and-examples.md +41 -0
  16. data/TODO.complete/07-odd-even-templates-and-page-numbering.md +63 -0
  17. data/TODO.complete/08-image-flowable.md +45 -0
  18. data/TODO.complete/09-toc-and-leaders.md +66 -0
  19. data/TODO.complete/10-hyperlinks-and-destinations.md +46 -0
  20. data/TODO.complete/11-pdf-outline-bookmarks.md +47 -0
  21. data/TODO.complete/12-metadata-info-dict.md +42 -0
  22. data/TODO.complete/13-xmp-metadata.md +43 -0
  23. data/TODO.complete/14-footnotes.md +43 -0
  24. data/TODO.complete/15-color-management.md +33 -0
  25. data/TODO.complete/16-pdfua-struct-tree.md +35 -0
  26. data/TODO.complete/17-alt-text-and-actual-text.md +28 -0
  27. data/TODO.complete/18-math-rendering.md +41 -0
  28. data/TODO.complete/19-preformatted-and-code.md +31 -0
  29. data/TODO.complete/20-page-break-controls.md +46 -0
  30. data/TODO.complete/21-svg-renderer.md +40 -0
  31. data/TODO.complete/22-definition-lists.md +36 -0
  32. data/TODO.complete/23-multi-column.md +34 -0
  33. data/TODO.complete/24-cover-page-fidelity.md +40 -0
  34. data/TODO.complete/25-i18n-and-multi-language.md +28 -0
  35. data/TODO.complete/26-pixel-diff-harness.md +36 -0
  36. data/TODO.complete/27-pdfrb-only-text-extraction.md +37 -0
  37. data/TODO.complete/28-restore-nested-module-style.md +41 -0
  38. data/TODO.complete/29-font-embedding-wiring-debug.md +72 -0
  39. data/TODO.complete/30-adapter-decomposition.md +76 -0
  40. data/TODO.complete/31-subscript-superscript-inline-runs.md +56 -0
  41. data/TODO.complete/32-debug-output-cleanup.md +59 -0
  42. data/TODO.complete/33-comprehensive-adapter-specs.md +55 -0
  43. data/TODO.complete/34-table-column-auto-layout.md +50 -0
  44. data/TODO.complete/35-image-path-resolution.md +66 -0
  45. data/TODO.complete/36-xsl-converter-completeness.md +40 -0
  46. data/TODO.complete/37-content-model-completeness.md +54 -0
  47. data/TODO.complete/38-pipeline-config-externalization.md +55 -0
  48. data/TODO.complete/39-two-pass-cross-reference-resolution.md +66 -0
  49. data/TODO.complete/40-vertical-writing-modes.md +63 -0
  50. data/TODO.complete/41-pdf-annotations-hyperlinks.md +53 -0
  51. data/TODO.complete/42-font-manifest-configuration.md +75 -0
  52. data/TODO.complete/43-shrink-to-fit-containers.md +52 -0
  53. data/TODO.complete/44-back-of-book-index-generation.md +51 -0
  54. data/TODO.complete/45-pdf-forms-acroform.md +52 -0
  55. data/TODO.complete/46-pdf-digital-signatures.md +47 -0
  56. data/TODO.complete/47-ocp-flavor-separation.md +50 -0
  57. data/TODO.complete/48-oiml-as-pure-configuration.md +80 -0
  58. data/TODO.complete/49-oiml-flowbuilder-as-configuration.md +39 -0
  59. data/TODO.complete/50-delete-oiml-ruby-code.md +53 -0
  60. data/TODO.complete/51-xsl-as-authoritative-source.md +68 -0
  61. data/TODO.complete/52-flavor-packaging-separation.md +49 -0
  62. data/TODO.complete/53-second-flavor-spike.md +74 -0
  63. data/TODO.complete/54-flavor-manifest-contract.md +72 -0
  64. data/TODO.complete/55-header-footer-style-configuration.md +57 -0
  65. data/TODO.complete/56-generic-adapter-mece-decomposition.md +56 -0
  66. data/TODO.complete/57-strict-section-typing.md +49 -0
  67. data/TODO.complete/58-flavor-packaging-gem-convention.md +47 -0
  68. data/TODO.complete/59-xsl-converter-profiles.md +76 -0
  69. data/TODO.complete/60-adapter-fidelity-footnotes-notes-examples.md +62 -0
  70. data/TODO.complete/61-error-model-and-strict-mode.md +62 -0
  71. data/TODO.complete/62-font-resolution-and-strict-fonts.md +66 -0
  72. data/TODO.complete/63-purge-hardcoded-vocabulary-from-generic-core.md +68 -0
  73. data/TODO.complete/64-selector-driven-adapter-specs.md +46 -0
  74. data/TODO.complete/66-semantic-content-types.md +171 -0
  75. data/TODO.complete/README.md +48 -0
  76. data/TODO.implementation/01-foundation.md +46 -0
  77. data/TODO.implementation/02-content-contract.md +47 -0
  78. data/TODO.implementation/03-layoutspec-contract.md +46 -0
  79. data/TODO.implementation/04-style-system.md +43 -0
  80. data/TODO.implementation/05-visitor-pattern.md +40 -0
  81. data/TODO.implementation/06-afm-data-and-parser.md +37 -0
  82. data/TODO.implementation/07-afm-metrics.md +41 -0
  83. data/TODO.implementation/08-truetype-tables-and-metrics.md +48 -0
  84. data/TODO.implementation/09-font-registry-and-glyph-measurer.md +49 -0
  85. data/TODO.implementation/10-break-opportunity-and-inline-run.md +47 -0
  86. data/TODO.implementation/11-greedy-line-breaker.md +48 -0
  87. data/TODO.implementation/12-knuth-plass-line-breaker.md +46 -0
  88. data/TODO.implementation/13-line-and-alignment.md +45 -0
  89. data/TODO.implementation/14-knuth-elements-and-breaker.md +56 -0
  90. data/TODO.implementation/15-refactor-text-layout-on-knuth.md +47 -0
  91. data/TODO.implementation/16-flowable-and-frame.md +52 -0
  92. data/TODO.implementation/17-flow-context.md +50 -0
  93. data/TODO.implementation/18-text-flowable.md +52 -0
  94. data/TODO.implementation/19-spacer-and-page-break.md +52 -0
  95. data/TODO.implementation/20-engine-paged.md +63 -0
  96. data/TODO.implementation/21-output-tree.md +56 -0
  97. data/TODO.implementation/22-pdf-renderer.md +77 -0
  98. data/TODO.implementation/23-end-to-end-smoke.md +53 -0
  99. data/TODO.implementation/24-page-template-and-region.md +51 -0
  100. data/TODO.implementation/25-page-sequence-master.md +54 -0
  101. data/TODO.implementation/26-two-pass-layout.md +66 -0
  102. data/TODO.implementation/27-table-model.md +48 -0
  103. data/TODO.implementation/28-table-layouts.md +53 -0
  104. data/TODO.implementation/29-table-renderer.md +55 -0
  105. data/TODO.implementation/30-table-flowable.md +53 -0
  106. data/TODO.implementation/31-list-model.md +45 -0
  107. data/TODO.implementation/32-list-flowable.md +54 -0
  108. data/TODO.implementation/33-image-flowable.md +59 -0
  109. data/TODO.implementation/34-svg-renderer.md +58 -0
  110. data/TODO.implementation/35-svg-as-form-xobject.md +38 -0
  111. data/TODO.implementation/36-inline-builder.md +50 -0
  112. data/TODO.implementation/37-hyperlink.md +53 -0
  113. data/TODO.implementation/38-sub-superscript.md +44 -0
  114. data/TODO.implementation/39-leader-and-field-runs.md +60 -0
  115. data/TODO.implementation/40-bookmark-outline.md +45 -0
  116. data/TODO.implementation/41-destinations.md +52 -0
  117. data/TODO.implementation/42-toc-builder.md +52 -0
  118. data/TODO.implementation/43-footnotes.md +51 -0
  119. data/TODO.implementation/44-multi-column.md +45 -0
  120. data/TODO.implementation/45-page-numbering-and-section-tracking.md +50 -0
  121. data/TODO.implementation/46-color-management.md +47 -0
  122. data/TODO.implementation/47-metadata-and-xmp.md +41 -0
  123. data/TODO.implementation/48-struct-tree-and-marked-content.md +49 -0
  124. data/TODO.implementation/49-alt-text-and-actual-text.md +43 -0
  125. data/TODO.implementation/50-composer-facade.md +59 -0
  126. data/TODO.implementation/51-style-presets.md +44 -0
  127. data/TODO.implementation/52-oiml-adapter-and-e2e-pipeline.md +120 -0
  128. data/TODO.implementation/53-oiml-layout-spec.md +121 -0
  129. data/TODO.implementation/54-oiml-pipeline.md +97 -0
  130. data/TODO.implementation/55-pdf-comparator.md +98 -0
  131. data/TODO.implementation/56-diff-formatters.md +95 -0
  132. data/TODO.implementation/57-path-locator-and-enrichment.md +74 -0
  133. data/TODO.implementation/58-rspec-matchers-and-harness-api.md +124 -0
  134. data/TODO.implementation/59-pixel-diff-mode.md +93 -0
  135. data/TODO.implementation/60-yard-documentation.md +43 -0
  136. data/TODO.implementation/61-benchmarks-and-stress-tests.md +50 -0
  137. data/TODO.implementation/62-validation-framework.md +107 -0
  138. data/TODO.implementation/63-layout-rules-profile.md +110 -0
  139. data/TODO.implementation/64-pdfa-and-pdfua-conformance.md +74 -0
  140. data/TODO.implementation/65-feature-extractor.md +64 -0
  141. data/TODO.implementation/66-auto-fixer.md +75 -0
  142. data/TODO.implementation/README.md +169 -0
  143. data/data/arrolio/afm/Courier-Bold.afm +342 -0
  144. data/data/arrolio/afm/Courier-BoldOblique.afm +342 -0
  145. data/data/arrolio/afm/Courier-Oblique.afm +342 -0
  146. data/data/arrolio/afm/Courier.afm +342 -0
  147. data/data/arrolio/afm/Helvetica-Bold.afm +2827 -0
  148. data/data/arrolio/afm/Helvetica-BoldOblique.afm +2827 -0
  149. data/data/arrolio/afm/Helvetica-Oblique.afm +3051 -0
  150. data/data/arrolio/afm/Helvetica.afm +3051 -0
  151. data/data/arrolio/afm/Symbol.afm +213 -0
  152. data/data/arrolio/afm/Times-Bold.afm +2588 -0
  153. data/data/arrolio/afm/Times-BoldItalic.afm +2384 -0
  154. data/data/arrolio/afm/Times-Italic.afm +2667 -0
  155. data/data/arrolio/afm/Times-Roman.afm +2419 -0
  156. data/data/arrolio/afm/ZapfDingbats.afm +225 -0
  157. data/data/arrolio/glyphlist.txt +4306 -0
  158. data/exe/arrolio2pdf +28 -0
  159. data/lib/arrolio/asset_resolver.rb +36 -0
  160. data/lib/arrolio/color.rb +107 -0
  161. data/lib/arrolio/column_set.rb +64 -0
  162. data/lib/arrolio/composer.rb +10 -0
  163. data/lib/arrolio/config_driven_pipeline.rb +144 -0
  164. data/lib/arrolio/content/bibliography_item.rb +42 -0
  165. data/lib/arrolio/content/builder.rb +108 -0
  166. data/lib/arrolio/content/document.rb +60 -0
  167. data/lib/arrolio/content/example.rb +43 -0
  168. data/lib/arrolio/content/figure_group.rb +39 -0
  169. data/lib/arrolio/content/footnote.rb +46 -0
  170. data/lib/arrolio/content/form_field.rb +47 -0
  171. data/lib/arrolio/content/formula.rb +34 -0
  172. data/lib/arrolio/content/heading.rb +42 -0
  173. data/lib/arrolio/content/hyperlink.rb +42 -0
  174. data/lib/arrolio/content/image.rb +36 -0
  175. data/lib/arrolio/content/index_entry.rb +52 -0
  176. data/lib/arrolio/content/inline_run.rb +51 -0
  177. data/lib/arrolio/content/list.rb +60 -0
  178. data/lib/arrolio/content/note.rb +48 -0
  179. data/lib/arrolio/content/page_break.rb +28 -0
  180. data/lib/arrolio/content/paragraph.rb +38 -0
  181. data/lib/arrolio/content/preformatted.rb +47 -0
  182. data/lib/arrolio/content/section.rb +39 -0
  183. data/lib/arrolio/content/table.rb +117 -0
  184. data/lib/arrolio/content/term_entry.rb +43 -0
  185. data/lib/arrolio/content.rb +27 -0
  186. data/lib/arrolio/data_dir.rb +28 -0
  187. data/lib/arrolio/engine/cross_reference_registry.rb +69 -0
  188. data/lib/arrolio/engine/paged.rb +186 -0
  189. data/lib/arrolio/engine.rb +9 -0
  190. data/lib/arrolio/error.rb +70 -0
  191. data/lib/arrolio/flavor/manifest.rb +111 -0
  192. data/lib/arrolio/flavor/registry.rb +54 -0
  193. data/lib/arrolio/flavor.rb +23 -0
  194. data/lib/arrolio/flow_context.rb +37 -0
  195. data/lib/arrolio/flowable.rb +66 -0
  196. data/lib/arrolio/flowables/footnote_marker_flowable.rb +28 -0
  197. data/lib/arrolio/flowables/heading_flowable.rb +23 -0
  198. data/lib/arrolio/flowables/image_flowable.rb +53 -0
  199. data/lib/arrolio/flowables/list_flowable.rb +117 -0
  200. data/lib/arrolio/flowables/note_flowable.rb +19 -0
  201. data/lib/arrolio/flowables/page_break.rb +15 -0
  202. data/lib/arrolio/flowables/page_sequence_start.rb +28 -0
  203. data/lib/arrolio/flowables/spacer.rb +34 -0
  204. data/lib/arrolio/flowables/table_flowable.rb +147 -0
  205. data/lib/arrolio/flowables/text_flowable.rb +95 -0
  206. data/lib/arrolio/flowables/toc_line_flowable.rb +35 -0
  207. data/lib/arrolio/flowables/two_column_block.rb +56 -0
  208. data/lib/arrolio/flowables.rb +19 -0
  209. data/lib/arrolio/font/embedder.rb +197 -0
  210. data/lib/arrolio/font/manifest.rb +114 -0
  211. data/lib/arrolio/font/resolver.rb +161 -0
  212. data/lib/arrolio/font/text_encoder.rb +28 -0
  213. data/lib/arrolio/font.rb +10 -0
  214. data/lib/arrolio/font_metrics/afm_metrics.rb +91 -0
  215. data/lib/arrolio/font_metrics/registry.rb +55 -0
  216. data/lib/arrolio/font_metrics/true_type_metrics.rb +95 -0
  217. data/lib/arrolio/font_metrics.rb +10 -0
  218. data/lib/arrolio/frame.rb +36 -0
  219. data/lib/arrolio/generic_adapter.rb +672 -0
  220. data/lib/arrolio/generic_flow_builder.rb +338 -0
  221. data/lib/arrolio/glyph_measurer.rb +71 -0
  222. data/lib/arrolio/harness/pdf_diff.rb +104 -0
  223. data/lib/arrolio/harness/pixel_diff.rb +122 -0
  224. data/lib/arrolio/harness/text_diff.rb +53 -0
  225. data/lib/arrolio/harness/text_extractor.rb +58 -0
  226. data/lib/arrolio/harness.rb +10 -0
  227. data/lib/arrolio/inline_run.rb +59 -0
  228. data/lib/arrolio/layout_spec/flow.rb +17 -0
  229. data/lib/arrolio/layout_spec/loader.rb +117 -0
  230. data/lib/arrolio/layout_spec/page_template.rb +127 -0
  231. data/lib/arrolio/layout_spec/page_template_selector.rb +58 -0
  232. data/lib/arrolio/layout_spec/region.rb +38 -0
  233. data/lib/arrolio/layout_spec/scale_length.rb +34 -0
  234. data/lib/arrolio/layout_spec.rb +90 -0
  235. data/lib/arrolio/logger.rb +54 -0
  236. data/lib/arrolio/output/page.rb +69 -0
  237. data/lib/arrolio/output/placed_box.rb +68 -0
  238. data/lib/arrolio/output/region.rb +32 -0
  239. data/lib/arrolio/output.rb +10 -0
  240. data/lib/arrolio/renderer/accessibility_tagger.rb +70 -0
  241. data/lib/arrolio/renderer/font_registry.rb +32 -0
  242. data/lib/arrolio/renderer/link_annotator.rb +62 -0
  243. data/lib/arrolio/renderer/outline_builder.rb +102 -0
  244. data/lib/arrolio/renderer/pdf.rb +493 -0
  245. data/lib/arrolio/renderer/signature_config.rb +59 -0
  246. data/lib/arrolio/renderer/structure_tree_builder.rb +117 -0
  247. data/lib/arrolio/renderer/xmp_builder.rb +92 -0
  248. data/lib/arrolio/renderer.rb +17 -0
  249. data/lib/arrolio/style/definition.rb +98 -0
  250. data/lib/arrolio/style/loader.rb +25 -0
  251. data/lib/arrolio/style/registry.rb +53 -0
  252. data/lib/arrolio/style.rb +9 -0
  253. data/lib/arrolio/table/auto_layout.rb +91 -0
  254. data/lib/arrolio/table.rb +9 -0
  255. data/lib/arrolio/text_direction.rb +81 -0
  256. data/lib/arrolio/text_layout/break_opportunity.rb +103 -0
  257. data/lib/arrolio/text_layout/greedy.rb +110 -0
  258. data/lib/arrolio/text_layout/knuth_plass/breaker.rb +265 -0
  259. data/lib/arrolio/text_layout/knuth_plass/item.rb +84 -0
  260. data/lib/arrolio/text_layout/knuth_plass/item_builder.rb +103 -0
  261. data/lib/arrolio/text_layout/knuth_plass.rb +12 -0
  262. data/lib/arrolio/text_layout/line.rb +72 -0
  263. data/lib/arrolio/text_layout.rb +11 -0
  264. data/lib/arrolio/toc_builder.rb +31 -0
  265. data/lib/arrolio/version.rb +5 -0
  266. data/lib/arrolio/writing_mode.rb +75 -0
  267. data/lib/arrolio.rb +42 -0
  268. data/script/smoke.rb +32 -0
  269. data/scripts/write_content_extras.py +227 -0
  270. data/scripts/xsl_profiles/standoc.yml +218 -0
  271. data/scripts/xsl_to_config.rb +391 -0
  272. metadata +394 -0
data/lib/arrolio.rb ADDED
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pdfrb'
4
+
5
+ # Arrolio -- a flavor-agnostic paged-media layout engine.
6
+ # Core knows NOTHING about specific document formats (OIML,
7
+ # ISO, IEC, BSI). Flavors are loaded on demand:
8
+ # require 'arroolio'
9
+ # require 'arrolio/oiml' # Load the OIML flavor
10
+ module Arrolio
11
+ autoload :VERSION, 'arrolio/version'
12
+ autoload :Error, 'arrolio/error'
13
+ autoload :Logger, 'arrolio/logger'
14
+ autoload :Color, 'arrolio/color'
15
+ autoload :TextDirection, 'arrolio/text_direction'
16
+ autoload :WritingMode, 'arrolio/writing_mode'
17
+ autoload :ColumnSet, 'arrolio/column_set'
18
+ autoload :Content, 'arrolio/content'
19
+ autoload :LayoutSpec, 'arrolio/layout_spec'
20
+ autoload :Style, 'arrolio/style'
21
+ autoload :Table, 'arrolio/table'
22
+ autoload :Font, 'arrolio/font'
23
+ autoload :FontMetrics, 'arrolio/font_metrics'
24
+ autoload :GlyphMeasurer, 'arrolio/glyph_measurer'
25
+ autoload :InlineRun, 'arrolio/inline_run'
26
+ autoload :TextLayout, 'arrolio/text_layout'
27
+ autoload :Flowable, 'arrolio/flowable'
28
+ autoload :Flowables, 'arrolio/flowables'
29
+ autoload :Frame, 'arrolio/frame'
30
+ autoload :FlowContext, 'arrolio/flow_context'
31
+ autoload :Engine, 'arrolio/engine'
32
+ autoload :Output, 'arrolio/output'
33
+ autoload :Renderer, 'arrolio/renderer'
34
+ autoload :Harness, 'arrolio/harness'
35
+ autoload :Flavor, 'arrolio/flavor'
36
+ autoload :AssetResolver, 'arrolio/asset_resolver'
37
+ autoload :GenericAdapter, 'arrolio/generic_adapter'
38
+ autoload :GenericFlowBuilder, 'arrolio/generic_flow_builder'
39
+ autoload :ConfigDrivenPipeline, 'arrolio/config_driven_pipeline'
40
+ autoload :TocBuilder, 'arrolio/toc_builder'
41
+ autoload :Composer, 'arrolio/composer'
42
+ end
data/script/smoke.rb ADDED
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Smoke test for Arrolio. Run: bundle exec ruby -Ilib script/smoke.rb
5
+ #
6
+ # Style is a MODULE (with Definition/Registry/Loader autoloads) in this
7
+ # codebase — not a class. Adjust accordingly.
8
+
9
+ require 'arrolio'
10
+
11
+ puts "VERSION: #{Arrolio::VERSION}"
12
+
13
+ # Font metrics — direct API
14
+ m = Arrolio::GlyphMeasurer.new(font_name: 'Helvetica')
15
+ puts "GlyphMeasurer 'Hello' at 12pt: #{m.width_of_string('Hello', font_size: 12)}"
16
+
17
+ # Inline run + Frame
18
+ puts "InlineRun: #{Arrolio::InlineRun.new('hi').text}"
19
+ frame = Arrolio::Frame.new(x: 0, y: 0, width: 100, height: 200)
20
+ puts "Frame remaining_height: #{frame.remaining_height}"
21
+
22
+ # Content::Document
23
+ doc = Arrolio::Content::Document.build { |b| b.paragraph('Hello, World!') }
24
+ puts "Content::Document sections: #{doc.sections.length}"
25
+
26
+ # LayoutSpec
27
+ spec = Arrolio::LayoutSpec.build do |s|
28
+ s.page_template(:body)
29
+ end
30
+ puts "LayoutSpec page_template(:body): #{spec.page_template(:body).class}"
31
+
32
+ puts 'OK'
@@ -0,0 +1,227 @@
1
+ #!/usr/bin/env python3
2
+ """Write remaining content/* files."""
3
+ import os
4
+
5
+ P = bytes([0x41, 0x72, 0x72, 0x6f, 0x6c, 0x69, 0x6f]).decode()
6
+ PROJ = P
7
+
8
+ FILES = {}
9
+
10
+ FILES['lib/arrolio/content/inline_run.rb'] = f'''# frozen_string_literal: true
11
+
12
+ module {PROJ}
13
+ module Content
14
+ class InlineRun
15
+ attr_reader :text, :style_id, :href
16
+
17
+ def initialize(text, style_id: :inline, href: nil)
18
+ @text = text.to_s
19
+ @style_id = style_id.to_sym
20
+ @href = href
21
+ freeze
22
+ end
23
+
24
+ def ==(other)
25
+ other.is_a?(self.class) &&
26
+ text == other.text &&
27
+ style_id == other.style_id &&
28
+ href == other.href
29
+ end
30
+
31
+ alias eql? ==
32
+
33
+ def hash
34
+ [self.class, text, style_id, href].hash
35
+ end
36
+ end
37
+ end
38
+ end
39
+ '''
40
+
41
+ FILES['lib/arrolio/content/table.rb'] = f'''# frozen_string_literal: true
42
+
43
+ module {PROJ}
44
+ module Content
45
+ class Table
46
+ attr_reader :header, :body, :column_widths, :style_id, :id
47
+
48
+ def initialize(header: [], body: [], column_widths: nil,
49
+ style_id: :table, id: nil)
50
+ @header = header.map {{ |row| coerce_row(row) }}.freeze
51
+ @body = body.map {{ |row| coerce_row(row) }}.freeze
52
+ @column_widths = column_widths&.map(&:to_f)&.freeze
53
+ @style_id = style_id.to_sym
54
+ @id = id
55
+ freeze
56
+ end
57
+
58
+ def rows
59
+ @header + @body
60
+ end
61
+
62
+ def column_count
63
+ rows.map {{ |r| r.cells.length }}.max || 0
64
+ end
65
+
66
+ def ==(other)
67
+ other.is_a?(self.class) &&
68
+ header == other.header &&
69
+ body == other.body &&
70
+ column_widths == other.column_widths &&
71
+ style_id == other.style_id &&
72
+ id == other.id
73
+ end
74
+
75
+ alias eql? ==
76
+
77
+ def hash
78
+ [self.class, header, body, column_widths, style_id, id].hash
79
+ end
80
+
81
+ private
82
+
83
+ def coerce_row(row)
84
+ case row
85
+ when Row then row
86
+ when Array then Row.new(row.map {{ |c| c.is_a?(Cell) ? c : Cell.new(c) }})
87
+ else
88
+ raise ContentError, "Table row must be an Array or Table::Row (got #{{row.class}})"
89
+ end
90
+ end
91
+ end
92
+
93
+ class Table::Row
94
+ include Enumerable
95
+ attr_reader :cells
96
+
97
+ def initialize(cells)
98
+ @cells = Array(cells).freeze
99
+ freeze
100
+ end
101
+
102
+ def each(&block)
103
+ @cells.each(&block)
104
+ end
105
+
106
+ def length
107
+ @cells.length
108
+ end
109
+
110
+ def ==(other)
111
+ other.is_a?(self.class) && cells == other.cells
112
+ end
113
+
114
+ alias eql? ==
115
+
116
+ def hash
117
+ [self.class, cells].hash
118
+ end
119
+ end
120
+
121
+ class Table::Cell
122
+ attr_reader :content, :colspan, :rowspan, :style_id, :align
123
+
124
+ def initialize(content = [], colspan: 1, rowspan: 1,
125
+ style_id: :table_cell, align: nil)
126
+ @content = Array(content).freeze
127
+ @colspan = colspan.to_i
128
+ @rowspan = rowspan.to_i
129
+ @style_id = style_id.to_sym
130
+ @align = align
131
+ freeze
132
+ end
133
+
134
+ def text
135
+ @content.map do |node|
136
+ node.is_a?(Paragraph) ? node.text : node.to_s
137
+ end.join(' ')
138
+ end
139
+
140
+ def ==(other)
141
+ other.is_a?(self.class) &&
142
+ content == other.content &&
143
+ colspan == other.colspan &&
144
+ rowspan == other.rowspan &&
145
+ style_id == other.style_id &&
146
+ align == other.align
147
+ end
148
+
149
+ alias eql? ==
150
+
151
+ def hash
152
+ [self.class, content, colspan, rowspan, style_id, align].hash
153
+ end
154
+ end
155
+ end
156
+ end
157
+ '''
158
+
159
+ FILES['lib/arrolio/content/list.rb'] = f'''# frozen_string_literal: true
160
+
161
+ module {PROJ}
162
+ module Content
163
+ class List
164
+ attr_reader :items, :kind, :style_id, :id
165
+
166
+ def initialize(items = [], kind: :bullet, style_id: :list, id: nil)
167
+ @items = items.map {{ |i| i.is_a?(Item) ? i : Item.new(i) }}.freeze
168
+ @kind = kind.to_sym
169
+ @style_id = style_id.to_sym
170
+ @id = id
171
+ freeze
172
+ end
173
+
174
+ def ordered?; @kind == :ordered; end
175
+ def bullet?; @kind == :bullet; end
176
+
177
+ def ==(other)
178
+ other.is_a?(self.class) &&
179
+ items == other.items &&
180
+ kind == other.kind &&
181
+ style_id == other.style_id &&
182
+ id == other.id
183
+ end
184
+
185
+ alias eql? ==
186
+
187
+ def hash
188
+ [self.class, items, kind, style_id, id].hash
189
+ end
190
+
191
+ class Item
192
+ attr_reader :content, :marker
193
+
194
+ def initialize(content, marker: nil)
195
+ @content = Array(content).freeze
196
+ @marker = marker
197
+ freeze
198
+ end
199
+
200
+ def text
201
+ @content.map do |node|
202
+ node.is_a?(Paragraph) ? node.text : node.to_s
203
+ end.join(' ')
204
+ end
205
+
206
+ def ==(other)
207
+ other.is_a?(self.class) && content == other.content && marker == other.marker
208
+ end
209
+
210
+ alias eql? ==
211
+
212
+ def hash
213
+ [self.class, content, marker].hash
214
+ end
215
+ end
216
+ end
217
+ end
218
+ end
219
+ '''
220
+
221
+ if __name__ == '__main__':
222
+ os.chdir('/Users/mulgogi/src/arrolio/arrolio')
223
+ for path, content in FILES.items():
224
+ os.makedirs(os.path.dirname(path), exist_ok=True)
225
+ with open(path, 'w') as f:
226
+ f.write(content)
227
+ print(f'wrote {path} ({len(content)} bytes)')
@@ -0,0 +1,218 @@
1
+ # Standoc vocabulary profile for scripts/xsl_to_config.rb.
2
+ #
3
+ # Provides everything the converter CANNOT derive from the XSL itself:
4
+ # the Metanorma standoc element vocabulary (clause, p, fmt-title, ...),
5
+ # the default font family for inline variants (Times New Roman),
6
+ # the cover-page boilerplate (organisation strings), and the
7
+ # header/footer text template.
8
+ #
9
+ # This file is the only place standoc-specific constants live. The
10
+ # converter is fully generic; running it with a different profile
11
+ # produces YAML for a different vocabulary family.
12
+
13
+ element_mapping:
14
+ clause: { content_type: section }
15
+ terms: { content_type: section }
16
+ definitions: { content_type: section }
17
+ annex2: { content_type: section }
18
+ p: { content_type: paragraph }
19
+ table: { content_type: table }
20
+ figure: { content_type: figure }
21
+ ul: { content_type: list, kind: bullet }
22
+ ol: { content_type: list, kind: ordered }
23
+ dl: { content_type: list, kind: bullet }
24
+ term: { content_type: term }
25
+ note: { content_type: note }
26
+ termnote: { content_type: note }
27
+ example: { content_type: note }
28
+ pre: { content_type: preformatted }
29
+ sourcecode: { content_type: preformatted }
30
+ pagebreak: { content_type: pagebreak }
31
+
32
+ inline_styles:
33
+ strong: strong
34
+ em: em
35
+ tt: monospace
36
+ sub: subscript
37
+ sup: superscript
38
+ link: link
39
+ xref: xref
40
+ fmt-xref: xref
41
+ eref: xref
42
+ fmt-identifier: identifier
43
+ identifier: identifier
44
+ fmt-preferred: term
45
+ preferred: term
46
+ fmt-name: term
47
+ renderterm: term
48
+ refterm: term
49
+ term: term
50
+
51
+ span_class_styles:
52
+ boldtitle: strong
53
+ bold: strong
54
+ fmt-caption-label: caption_label
55
+ fmt-element-name: caption_label
56
+ citetbl: xref
57
+ citetable: xref
58
+ citesection: xref
59
+
60
+ paragraph_styles:
61
+ zzSTDTitle1: doc_title
62
+
63
+ block_level_elements:
64
+ - note
65
+ - termnote
66
+ - example
67
+ - figure
68
+ - table
69
+ - clause
70
+ - ul
71
+ - ol
72
+ - dl
73
+ - formula
74
+ - fmt-xref-label
75
+ - fmt-fn-body
76
+ - fmt-footnote-container
77
+
78
+ skip_metadata_elements:
79
+ - asciimath
80
+ - latexmath
81
+ - image
82
+ - localityStack
83
+ - locality
84
+ - referenceFrom
85
+ - referenceTo
86
+ - origin
87
+ - source
88
+
89
+ skip_elements:
90
+ - title
91
+ - fmt-title
92
+ - fmt-xref-label
93
+ - fmt-name
94
+ - fmt-fn-body
95
+ - fmt-footnote-container
96
+
97
+ tab_replacements:
98
+ biblio-tag: " "
99
+
100
+ selectors:
101
+ heading: fmt-title
102
+ heading_depth_attribute: depth
103
+ list_item: li
104
+ list_item_label: fmt-name
105
+ paragraph: p
106
+ term_number: fmt-name
107
+ term_preferred: fmt-preferred
108
+ term_definition: fmt-definition
109
+ term_source: fmt-termsource
110
+ note_label: fmt-name
111
+ figure_image: image
112
+ figure_caption: fmt-name
113
+ figure_caption_fallback: name
114
+ table_row: tr
115
+ table_cell:
116
+ - td
117
+ - th
118
+ table_header: thead
119
+ table_body: tbody
120
+ biblio_tag: biblio-tag
121
+ biblio_formattedref: formattedref
122
+ image_src_attribute: src
123
+ image_alt_attribute: alt
124
+ id_attribute: id
125
+ autonum_class: fmt-autonum-delim
126
+ stem: stem
127
+ stem_formatted: fmt-stem
128
+ math: math
129
+ footnote_container: fmt-footnote-container
130
+ footnote_body: fmt-fn-body
131
+ footnote_marker: fn
132
+ preface_container: preface
133
+ preface_children:
134
+ - clause
135
+ - foreword
136
+ bibliography_container: bibliography
137
+ bibliography_reference: references
138
+ bibliography_item: bibitem
139
+ fallback_title: title
140
+ span: span
141
+ tab_inline: tab
142
+ break_inline: br
143
+
144
+ metadata_fields:
145
+ docidentifier: "docidentifier[@primary='true']"
146
+ docnumber: docnumber
147
+ edition: edition
148
+ revision_date: version/revision-date
149
+ title: "title[@type='main']"
150
+ title_intro: "title[@type='title-intro']"
151
+ title_main: "title[@type='title-main']"
152
+ title_part: "title[@type='title-part']"
153
+ title_complementary: "title[@type='title-complementary']"
154
+ title_part_prefix: "title[@type='title-part-prefix']"
155
+ doctype: doctype
156
+ language: language
157
+
158
+ cover_fields:
159
+ - docidentifier
160
+ - edition
161
+ - title_intro
162
+ - title_main
163
+ - title_complementary
164
+ - title_part
165
+
166
+ style_overrides:
167
+ body_margin_bottom: 8
168
+ strong_font_name: "Times New Roman Bold"
169
+ em_font_name: "Times New Roman Italic"
170
+ identifier_font_name: "Times New Roman Italic"
171
+ term_font_name: "Times New Roman Italic"
172
+ link_font_name: "Times New Roman Italic"
173
+ table_header_cell_font_name: "Times New Roman Bold"
174
+ cover_docidentifier_font_name: "Jost SemiBold"
175
+ cover_title_font_name: "Jost"
176
+ cover_title_other_font_name: "Jost Light"
177
+ cover_org_font_name: "Jost"
178
+ cover_label_font_name: "Jost"
179
+ list_marker_font_name: "Times New Roman Bold"
180
+
181
+ cover_content:
182
+ - type: spacer
183
+ size: 30
184
+ - type: text
185
+ source: "{{docidentifier}}"
186
+ style: cover_docidentifier
187
+ - type: spacer
188
+ size: 20
189
+ - type: text
190
+ source: "{{title_intro}}"
191
+ style: cover_title_other
192
+ when: title_intro_present
193
+ - type: text
194
+ source: "{{title_main}}"
195
+ style: cover_title
196
+ - type: text
197
+ source: "{{title_part}}"
198
+ style: cover_title_part
199
+ when: title_part_present
200
+ - type: text
201
+ source: "{{title_complementary}}"
202
+ style: cover_title_other
203
+ when: title_complementary_present
204
+ - type: spacer
205
+ size: 30
206
+ - type: text
207
+ source: "Organisation Internationale de Métrologie Légale"
208
+ style: cover_org
209
+ - type: text
210
+ source: "International Organization of Legal Metrology"
211
+ style: cover_org
212
+ - type: spacer
213
+ size: 20
214
+ - type: text
215
+ source: "{{docidentifier}} {{edition}}"
216
+ style: cover_label
217
+
218
+ header_template: "{{docidentifier}}: {{edition}}"