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.
- checksums.yaml +7 -0
- data/.rspec +2 -0
- data/.rubocop.yml +343 -0
- data/CHANGELOG.md +18 -0
- data/CLAUDE.md +154 -0
- data/Gemfile +15 -0
- data/LICENSE +24 -0
- data/README.adoc +95 -0
- data/Rakefile +21 -0
- data/TODO.complete/01-true-type-font-metrics.md +56 -0
- data/TODO.complete/02-font-embedding.md +51 -0
- data/TODO.complete/03-knuth-plass-breaker.md +42 -0
- data/TODO.complete/04-table-flowable.md +60 -0
- data/TODO.complete/05-list-flowable.md +50 -0
- data/TODO.complete/06-notes-and-examples.md +41 -0
- data/TODO.complete/07-odd-even-templates-and-page-numbering.md +63 -0
- data/TODO.complete/08-image-flowable.md +45 -0
- data/TODO.complete/09-toc-and-leaders.md +66 -0
- data/TODO.complete/10-hyperlinks-and-destinations.md +46 -0
- data/TODO.complete/11-pdf-outline-bookmarks.md +47 -0
- data/TODO.complete/12-metadata-info-dict.md +42 -0
- data/TODO.complete/13-xmp-metadata.md +43 -0
- data/TODO.complete/14-footnotes.md +43 -0
- data/TODO.complete/15-color-management.md +33 -0
- data/TODO.complete/16-pdfua-struct-tree.md +35 -0
- data/TODO.complete/17-alt-text-and-actual-text.md +28 -0
- data/TODO.complete/18-math-rendering.md +41 -0
- data/TODO.complete/19-preformatted-and-code.md +31 -0
- data/TODO.complete/20-page-break-controls.md +46 -0
- data/TODO.complete/21-svg-renderer.md +40 -0
- data/TODO.complete/22-definition-lists.md +36 -0
- data/TODO.complete/23-multi-column.md +34 -0
- data/TODO.complete/24-cover-page-fidelity.md +40 -0
- data/TODO.complete/25-i18n-and-multi-language.md +28 -0
- data/TODO.complete/26-pixel-diff-harness.md +36 -0
- data/TODO.complete/27-pdfrb-only-text-extraction.md +37 -0
- data/TODO.complete/28-restore-nested-module-style.md +41 -0
- data/TODO.complete/29-font-embedding-wiring-debug.md +72 -0
- data/TODO.complete/30-adapter-decomposition.md +76 -0
- data/TODO.complete/31-subscript-superscript-inline-runs.md +56 -0
- data/TODO.complete/32-debug-output-cleanup.md +59 -0
- data/TODO.complete/33-comprehensive-adapter-specs.md +55 -0
- data/TODO.complete/34-table-column-auto-layout.md +50 -0
- data/TODO.complete/35-image-path-resolution.md +66 -0
- data/TODO.complete/36-xsl-converter-completeness.md +40 -0
- data/TODO.complete/37-content-model-completeness.md +54 -0
- data/TODO.complete/38-pipeline-config-externalization.md +55 -0
- data/TODO.complete/39-two-pass-cross-reference-resolution.md +66 -0
- data/TODO.complete/40-vertical-writing-modes.md +63 -0
- data/TODO.complete/41-pdf-annotations-hyperlinks.md +53 -0
- data/TODO.complete/42-font-manifest-configuration.md +75 -0
- data/TODO.complete/43-shrink-to-fit-containers.md +52 -0
- data/TODO.complete/44-back-of-book-index-generation.md +51 -0
- data/TODO.complete/45-pdf-forms-acroform.md +52 -0
- data/TODO.complete/46-pdf-digital-signatures.md +47 -0
- data/TODO.complete/47-ocp-flavor-separation.md +50 -0
- data/TODO.complete/48-oiml-as-pure-configuration.md +80 -0
- data/TODO.complete/49-oiml-flowbuilder-as-configuration.md +39 -0
- data/TODO.complete/50-delete-oiml-ruby-code.md +53 -0
- data/TODO.complete/51-xsl-as-authoritative-source.md +68 -0
- data/TODO.complete/52-flavor-packaging-separation.md +49 -0
- data/TODO.complete/53-second-flavor-spike.md +74 -0
- data/TODO.complete/54-flavor-manifest-contract.md +72 -0
- data/TODO.complete/55-header-footer-style-configuration.md +57 -0
- data/TODO.complete/56-generic-adapter-mece-decomposition.md +56 -0
- data/TODO.complete/57-strict-section-typing.md +49 -0
- data/TODO.complete/58-flavor-packaging-gem-convention.md +47 -0
- data/TODO.complete/59-xsl-converter-profiles.md +76 -0
- data/TODO.complete/60-adapter-fidelity-footnotes-notes-examples.md +62 -0
- data/TODO.complete/61-error-model-and-strict-mode.md +62 -0
- data/TODO.complete/62-font-resolution-and-strict-fonts.md +66 -0
- data/TODO.complete/63-purge-hardcoded-vocabulary-from-generic-core.md +68 -0
- data/TODO.complete/64-selector-driven-adapter-specs.md +46 -0
- data/TODO.complete/66-semantic-content-types.md +171 -0
- data/TODO.complete/README.md +48 -0
- data/TODO.implementation/01-foundation.md +46 -0
- data/TODO.implementation/02-content-contract.md +47 -0
- data/TODO.implementation/03-layoutspec-contract.md +46 -0
- data/TODO.implementation/04-style-system.md +43 -0
- data/TODO.implementation/05-visitor-pattern.md +40 -0
- data/TODO.implementation/06-afm-data-and-parser.md +37 -0
- data/TODO.implementation/07-afm-metrics.md +41 -0
- data/TODO.implementation/08-truetype-tables-and-metrics.md +48 -0
- data/TODO.implementation/09-font-registry-and-glyph-measurer.md +49 -0
- data/TODO.implementation/10-break-opportunity-and-inline-run.md +47 -0
- data/TODO.implementation/11-greedy-line-breaker.md +48 -0
- data/TODO.implementation/12-knuth-plass-line-breaker.md +46 -0
- data/TODO.implementation/13-line-and-alignment.md +45 -0
- data/TODO.implementation/14-knuth-elements-and-breaker.md +56 -0
- data/TODO.implementation/15-refactor-text-layout-on-knuth.md +47 -0
- data/TODO.implementation/16-flowable-and-frame.md +52 -0
- data/TODO.implementation/17-flow-context.md +50 -0
- data/TODO.implementation/18-text-flowable.md +52 -0
- data/TODO.implementation/19-spacer-and-page-break.md +52 -0
- data/TODO.implementation/20-engine-paged.md +63 -0
- data/TODO.implementation/21-output-tree.md +56 -0
- data/TODO.implementation/22-pdf-renderer.md +77 -0
- data/TODO.implementation/23-end-to-end-smoke.md +53 -0
- data/TODO.implementation/24-page-template-and-region.md +51 -0
- data/TODO.implementation/25-page-sequence-master.md +54 -0
- data/TODO.implementation/26-two-pass-layout.md +66 -0
- data/TODO.implementation/27-table-model.md +48 -0
- data/TODO.implementation/28-table-layouts.md +53 -0
- data/TODO.implementation/29-table-renderer.md +55 -0
- data/TODO.implementation/30-table-flowable.md +53 -0
- data/TODO.implementation/31-list-model.md +45 -0
- data/TODO.implementation/32-list-flowable.md +54 -0
- data/TODO.implementation/33-image-flowable.md +59 -0
- data/TODO.implementation/34-svg-renderer.md +58 -0
- data/TODO.implementation/35-svg-as-form-xobject.md +38 -0
- data/TODO.implementation/36-inline-builder.md +50 -0
- data/TODO.implementation/37-hyperlink.md +53 -0
- data/TODO.implementation/38-sub-superscript.md +44 -0
- data/TODO.implementation/39-leader-and-field-runs.md +60 -0
- data/TODO.implementation/40-bookmark-outline.md +45 -0
- data/TODO.implementation/41-destinations.md +52 -0
- data/TODO.implementation/42-toc-builder.md +52 -0
- data/TODO.implementation/43-footnotes.md +51 -0
- data/TODO.implementation/44-multi-column.md +45 -0
- data/TODO.implementation/45-page-numbering-and-section-tracking.md +50 -0
- data/TODO.implementation/46-color-management.md +47 -0
- data/TODO.implementation/47-metadata-and-xmp.md +41 -0
- data/TODO.implementation/48-struct-tree-and-marked-content.md +49 -0
- data/TODO.implementation/49-alt-text-and-actual-text.md +43 -0
- data/TODO.implementation/50-composer-facade.md +59 -0
- data/TODO.implementation/51-style-presets.md +44 -0
- data/TODO.implementation/52-oiml-adapter-and-e2e-pipeline.md +120 -0
- data/TODO.implementation/53-oiml-layout-spec.md +121 -0
- data/TODO.implementation/54-oiml-pipeline.md +97 -0
- data/TODO.implementation/55-pdf-comparator.md +98 -0
- data/TODO.implementation/56-diff-formatters.md +95 -0
- data/TODO.implementation/57-path-locator-and-enrichment.md +74 -0
- data/TODO.implementation/58-rspec-matchers-and-harness-api.md +124 -0
- data/TODO.implementation/59-pixel-diff-mode.md +93 -0
- data/TODO.implementation/60-yard-documentation.md +43 -0
- data/TODO.implementation/61-benchmarks-and-stress-tests.md +50 -0
- data/TODO.implementation/62-validation-framework.md +107 -0
- data/TODO.implementation/63-layout-rules-profile.md +110 -0
- data/TODO.implementation/64-pdfa-and-pdfua-conformance.md +74 -0
- data/TODO.implementation/65-feature-extractor.md +64 -0
- data/TODO.implementation/66-auto-fixer.md +75 -0
- data/TODO.implementation/README.md +169 -0
- data/data/arrolio/afm/Courier-Bold.afm +342 -0
- data/data/arrolio/afm/Courier-BoldOblique.afm +342 -0
- data/data/arrolio/afm/Courier-Oblique.afm +342 -0
- data/data/arrolio/afm/Courier.afm +342 -0
- data/data/arrolio/afm/Helvetica-Bold.afm +2827 -0
- data/data/arrolio/afm/Helvetica-BoldOblique.afm +2827 -0
- data/data/arrolio/afm/Helvetica-Oblique.afm +3051 -0
- data/data/arrolio/afm/Helvetica.afm +3051 -0
- data/data/arrolio/afm/Symbol.afm +213 -0
- data/data/arrolio/afm/Times-Bold.afm +2588 -0
- data/data/arrolio/afm/Times-BoldItalic.afm +2384 -0
- data/data/arrolio/afm/Times-Italic.afm +2667 -0
- data/data/arrolio/afm/Times-Roman.afm +2419 -0
- data/data/arrolio/afm/ZapfDingbats.afm +225 -0
- data/data/arrolio/glyphlist.txt +4306 -0
- data/exe/arrolio2pdf +28 -0
- data/lib/arrolio/asset_resolver.rb +36 -0
- data/lib/arrolio/color.rb +107 -0
- data/lib/arrolio/column_set.rb +64 -0
- data/lib/arrolio/composer.rb +10 -0
- data/lib/arrolio/config_driven_pipeline.rb +144 -0
- data/lib/arrolio/content/bibliography_item.rb +42 -0
- data/lib/arrolio/content/builder.rb +108 -0
- data/lib/arrolio/content/document.rb +60 -0
- data/lib/arrolio/content/example.rb +43 -0
- data/lib/arrolio/content/figure_group.rb +39 -0
- data/lib/arrolio/content/footnote.rb +46 -0
- data/lib/arrolio/content/form_field.rb +47 -0
- data/lib/arrolio/content/formula.rb +34 -0
- data/lib/arrolio/content/heading.rb +42 -0
- data/lib/arrolio/content/hyperlink.rb +42 -0
- data/lib/arrolio/content/image.rb +36 -0
- data/lib/arrolio/content/index_entry.rb +52 -0
- data/lib/arrolio/content/inline_run.rb +51 -0
- data/lib/arrolio/content/list.rb +60 -0
- data/lib/arrolio/content/note.rb +48 -0
- data/lib/arrolio/content/page_break.rb +28 -0
- data/lib/arrolio/content/paragraph.rb +38 -0
- data/lib/arrolio/content/preformatted.rb +47 -0
- data/lib/arrolio/content/section.rb +39 -0
- data/lib/arrolio/content/table.rb +117 -0
- data/lib/arrolio/content/term_entry.rb +43 -0
- data/lib/arrolio/content.rb +27 -0
- data/lib/arrolio/data_dir.rb +28 -0
- data/lib/arrolio/engine/cross_reference_registry.rb +69 -0
- data/lib/arrolio/engine/paged.rb +186 -0
- data/lib/arrolio/engine.rb +9 -0
- data/lib/arrolio/error.rb +70 -0
- data/lib/arrolio/flavor/manifest.rb +111 -0
- data/lib/arrolio/flavor/registry.rb +54 -0
- data/lib/arrolio/flavor.rb +23 -0
- data/lib/arrolio/flow_context.rb +37 -0
- data/lib/arrolio/flowable.rb +66 -0
- data/lib/arrolio/flowables/footnote_marker_flowable.rb +28 -0
- data/lib/arrolio/flowables/heading_flowable.rb +23 -0
- data/lib/arrolio/flowables/image_flowable.rb +53 -0
- data/lib/arrolio/flowables/list_flowable.rb +117 -0
- data/lib/arrolio/flowables/note_flowable.rb +19 -0
- data/lib/arrolio/flowables/page_break.rb +15 -0
- data/lib/arrolio/flowables/page_sequence_start.rb +28 -0
- data/lib/arrolio/flowables/spacer.rb +34 -0
- data/lib/arrolio/flowables/table_flowable.rb +147 -0
- data/lib/arrolio/flowables/text_flowable.rb +95 -0
- data/lib/arrolio/flowables/toc_line_flowable.rb +35 -0
- data/lib/arrolio/flowables/two_column_block.rb +56 -0
- data/lib/arrolio/flowables.rb +19 -0
- data/lib/arrolio/font/embedder.rb +197 -0
- data/lib/arrolio/font/manifest.rb +114 -0
- data/lib/arrolio/font/resolver.rb +161 -0
- data/lib/arrolio/font/text_encoder.rb +28 -0
- data/lib/arrolio/font.rb +10 -0
- data/lib/arrolio/font_metrics/afm_metrics.rb +91 -0
- data/lib/arrolio/font_metrics/registry.rb +55 -0
- data/lib/arrolio/font_metrics/true_type_metrics.rb +95 -0
- data/lib/arrolio/font_metrics.rb +10 -0
- data/lib/arrolio/frame.rb +36 -0
- data/lib/arrolio/generic_adapter.rb +672 -0
- data/lib/arrolio/generic_flow_builder.rb +338 -0
- data/lib/arrolio/glyph_measurer.rb +71 -0
- data/lib/arrolio/harness/pdf_diff.rb +104 -0
- data/lib/arrolio/harness/pixel_diff.rb +122 -0
- data/lib/arrolio/harness/text_diff.rb +53 -0
- data/lib/arrolio/harness/text_extractor.rb +58 -0
- data/lib/arrolio/harness.rb +10 -0
- data/lib/arrolio/inline_run.rb +59 -0
- data/lib/arrolio/layout_spec/flow.rb +17 -0
- data/lib/arrolio/layout_spec/loader.rb +117 -0
- data/lib/arrolio/layout_spec/page_template.rb +127 -0
- data/lib/arrolio/layout_spec/page_template_selector.rb +58 -0
- data/lib/arrolio/layout_spec/region.rb +38 -0
- data/lib/arrolio/layout_spec/scale_length.rb +34 -0
- data/lib/arrolio/layout_spec.rb +90 -0
- data/lib/arrolio/logger.rb +54 -0
- data/lib/arrolio/output/page.rb +69 -0
- data/lib/arrolio/output/placed_box.rb +68 -0
- data/lib/arrolio/output/region.rb +32 -0
- data/lib/arrolio/output.rb +10 -0
- data/lib/arrolio/renderer/accessibility_tagger.rb +70 -0
- data/lib/arrolio/renderer/font_registry.rb +32 -0
- data/lib/arrolio/renderer/link_annotator.rb +62 -0
- data/lib/arrolio/renderer/outline_builder.rb +102 -0
- data/lib/arrolio/renderer/pdf.rb +493 -0
- data/lib/arrolio/renderer/signature_config.rb +59 -0
- data/lib/arrolio/renderer/structure_tree_builder.rb +117 -0
- data/lib/arrolio/renderer/xmp_builder.rb +92 -0
- data/lib/arrolio/renderer.rb +17 -0
- data/lib/arrolio/style/definition.rb +98 -0
- data/lib/arrolio/style/loader.rb +25 -0
- data/lib/arrolio/style/registry.rb +53 -0
- data/lib/arrolio/style.rb +9 -0
- data/lib/arrolio/table/auto_layout.rb +91 -0
- data/lib/arrolio/table.rb +9 -0
- data/lib/arrolio/text_direction.rb +81 -0
- data/lib/arrolio/text_layout/break_opportunity.rb +103 -0
- data/lib/arrolio/text_layout/greedy.rb +110 -0
- data/lib/arrolio/text_layout/knuth_plass/breaker.rb +265 -0
- data/lib/arrolio/text_layout/knuth_plass/item.rb +84 -0
- data/lib/arrolio/text_layout/knuth_plass/item_builder.rb +103 -0
- data/lib/arrolio/text_layout/knuth_plass.rb +12 -0
- data/lib/arrolio/text_layout/line.rb +72 -0
- data/lib/arrolio/text_layout.rb +11 -0
- data/lib/arrolio/toc_builder.rb +31 -0
- data/lib/arrolio/version.rb +5 -0
- data/lib/arrolio/writing_mode.rb +75 -0
- data/lib/arrolio.rb +42 -0
- data/script/smoke.rb +32 -0
- data/scripts/write_content_extras.py +227 -0
- data/scripts/xsl_profiles/standoc.yml +218 -0
- data/scripts/xsl_to_config.rb +391 -0
- metadata +394 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: eb7855962c5ce79ad8835c3047502c8383bfd9aab11c3ecf885b94def434a05b
|
|
4
|
+
data.tar.gz: abb650933f49b7b58608446cbf05c319246289451ee89d29ad214bec091d1ce9
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 4ba64c22b8c3097f797e54c42905ba0a9b5afd92c16cdfaba9525ff3fc16a8829cace77d766daa020f6ef707e8c510a7c94acf9fd8cad326f03050e90b1aedeb
|
|
7
|
+
data.tar.gz: 67698e186ad720c1d46a14b75fe97f75c9b7e9db19364f3252983de16d43d055bda2c7a29f147136f2be48978d991a1ee132a952d143c6990f7e288499f4483e
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
plugins:
|
|
2
|
+
- rubocop-performance
|
|
3
|
+
- rubocop-rake
|
|
4
|
+
- rubocop-rspec
|
|
5
|
+
|
|
6
|
+
AllCops:
|
|
7
|
+
NewCops: enable
|
|
8
|
+
TargetRubyVersion: 3.0
|
|
9
|
+
SuggestExtensions: false
|
|
10
|
+
|
|
11
|
+
Style/StringLiterals:
|
|
12
|
+
EnforcedStyle: single_quotes
|
|
13
|
+
|
|
14
|
+
Style/SymbolArray:
|
|
15
|
+
EnforcedStyle: brackets
|
|
16
|
+
|
|
17
|
+
Style/WordArray:
|
|
18
|
+
EnforcedStyle: brackets
|
|
19
|
+
|
|
20
|
+
Layout/LineLength:
|
|
21
|
+
Max: 120
|
|
22
|
+
|
|
23
|
+
Metrics/AbcSize:
|
|
24
|
+
Max: 80
|
|
25
|
+
|
|
26
|
+
Metrics/MethodLength:
|
|
27
|
+
Max: 60
|
|
28
|
+
|
|
29
|
+
Metrics/ClassLength:
|
|
30
|
+
Max: 600
|
|
31
|
+
|
|
32
|
+
Metrics/ModuleLength:
|
|
33
|
+
Max: 500
|
|
34
|
+
|
|
35
|
+
Metrics/CyclomaticComplexity:
|
|
36
|
+
Max: 30
|
|
37
|
+
|
|
38
|
+
Metrics/PerceivedComplexity:
|
|
39
|
+
Max: 30
|
|
40
|
+
|
|
41
|
+
Metrics/BlockLength:
|
|
42
|
+
Max: 150
|
|
43
|
+
|
|
44
|
+
Metrics/ParameterLists:
|
|
45
|
+
Max: 15
|
|
46
|
+
|
|
47
|
+
RSpec/ExampleLength:
|
|
48
|
+
Max: 15
|
|
49
|
+
|
|
50
|
+
RSpec/MultipleExpectations:
|
|
51
|
+
Max: 8
|
|
52
|
+
|
|
53
|
+
RSpec/DescribeClass:
|
|
54
|
+
Enabled: false
|
|
55
|
+
|
|
56
|
+
RSpec/LeakyLocalVariable:
|
|
57
|
+
Enabled: false
|
|
58
|
+
|
|
59
|
+
RSpec/ExampleLength:
|
|
60
|
+
Max: 30
|
|
61
|
+
|
|
62
|
+
RSpec/InstanceVariable:
|
|
63
|
+
Enabled: false
|
|
64
|
+
|
|
65
|
+
RSpec/HookArgument:
|
|
66
|
+
Enabled: false
|
|
67
|
+
|
|
68
|
+
RSpec/ContextWording:
|
|
69
|
+
Enabled: false
|
|
70
|
+
|
|
71
|
+
RSpec/BeEq:
|
|
72
|
+
Enabled: false
|
|
73
|
+
|
|
74
|
+
RSpec/StubbedMock:
|
|
75
|
+
Enabled: false
|
|
76
|
+
|
|
77
|
+
RSpec/AnyInstance:
|
|
78
|
+
Enabled: false
|
|
79
|
+
|
|
80
|
+
RSpec/SpecFilePathFormat:
|
|
81
|
+
Enabled: false
|
|
82
|
+
|
|
83
|
+
RSpec/MultipleDescribes:
|
|
84
|
+
Enabled: false
|
|
85
|
+
|
|
86
|
+
RSpec/NestedGroups:
|
|
87
|
+
Max: 5
|
|
88
|
+
|
|
89
|
+
RSpec/MultipleMemoizedHelpers:
|
|
90
|
+
Enabled: false
|
|
91
|
+
|
|
92
|
+
Style/HashSyntax:
|
|
93
|
+
EnforcedShorthandSyntax: either
|
|
94
|
+
|
|
95
|
+
Style/MapToHash:
|
|
96
|
+
Enabled: false
|
|
97
|
+
|
|
98
|
+
Style/HashTransformKeys:
|
|
99
|
+
Enabled: false
|
|
100
|
+
|
|
101
|
+
Style/RedundantCondition:
|
|
102
|
+
Enabled: false
|
|
103
|
+
|
|
104
|
+
Style/ReturnNilInPredicateMethodDefinition:
|
|
105
|
+
Enabled: false
|
|
106
|
+
|
|
107
|
+
Style/SafeNavigationChain:
|
|
108
|
+
Enabled: false
|
|
109
|
+
|
|
110
|
+
Style/GuardClause:
|
|
111
|
+
Enabled: false
|
|
112
|
+
|
|
113
|
+
Style/MutableConstant:
|
|
114
|
+
Enabled: false
|
|
115
|
+
|
|
116
|
+
Style/ParallelAssignment:
|
|
117
|
+
Enabled: false
|
|
118
|
+
|
|
119
|
+
Style/SoleNestedConditional:
|
|
120
|
+
Enabled: false
|
|
121
|
+
|
|
122
|
+
Style/IfUnlessModifier:
|
|
123
|
+
Enabled: false
|
|
124
|
+
|
|
125
|
+
Style/Next:
|
|
126
|
+
Enabled: false
|
|
127
|
+
|
|
128
|
+
Style/EmptyElse:
|
|
129
|
+
Enabled: false
|
|
130
|
+
|
|
131
|
+
Style/MultipleComparison:
|
|
132
|
+
Enabled: false
|
|
133
|
+
|
|
134
|
+
Style/RedundantReturn:
|
|
135
|
+
Enabled: false
|
|
136
|
+
|
|
137
|
+
Style/NestedTernaryOperator:
|
|
138
|
+
Enabled: false
|
|
139
|
+
|
|
140
|
+
Style/RedundantParentheses:
|
|
141
|
+
Enabled: false
|
|
142
|
+
|
|
143
|
+
Style/RedundantSelf:
|
|
144
|
+
Enabled: false
|
|
145
|
+
|
|
146
|
+
Naming/MethodParameterName:
|
|
147
|
+
Enabled: false
|
|
148
|
+
|
|
149
|
+
Style/IdenticalConditionalBranches:
|
|
150
|
+
Enabled: false
|
|
151
|
+
|
|
152
|
+
Style/RedundantCondition:
|
|
153
|
+
Enabled: false
|
|
154
|
+
|
|
155
|
+
Layout/IndentationWidth:
|
|
156
|
+
Enabled: false
|
|
157
|
+
|
|
158
|
+
Lint/BinaryOperatorWithIdenticalOperands:
|
|
159
|
+
Enabled: false
|
|
160
|
+
|
|
161
|
+
Lint/DuplicateBranch:
|
|
162
|
+
Enabled: false
|
|
163
|
+
|
|
164
|
+
Lint/DuplicateCaseCondition:
|
|
165
|
+
Enabled: false
|
|
166
|
+
|
|
167
|
+
Performance/CollectionLiteralInLoop:
|
|
168
|
+
Enabled: false
|
|
169
|
+
|
|
170
|
+
Style/ClassAndModuleChildren:
|
|
171
|
+
EnforcedStyle: nested
|
|
172
|
+
Enabled: false
|
|
173
|
+
|
|
174
|
+
Layout/RescueEnsureAlignment:
|
|
175
|
+
Enabled: false
|
|
176
|
+
|
|
177
|
+
Style/SymbolArray:
|
|
178
|
+
Enabled: false
|
|
179
|
+
|
|
180
|
+
Style/ArrayFirstLast:
|
|
181
|
+
Enabled: false
|
|
182
|
+
|
|
183
|
+
RSpec/RepeatedExample:
|
|
184
|
+
Enabled: false
|
|
185
|
+
|
|
186
|
+
RSpec/RepeatedDescription:
|
|
187
|
+
Enabled: false
|
|
188
|
+
|
|
189
|
+
Style/ArgumentsForwarding:
|
|
190
|
+
Enabled: false
|
|
191
|
+
|
|
192
|
+
Style/HashEachMethods:
|
|
193
|
+
Enabled: false
|
|
194
|
+
|
|
195
|
+
Style/SymbolProc:
|
|
196
|
+
Enabled: false
|
|
197
|
+
|
|
198
|
+
Style/PerlBackrefs:
|
|
199
|
+
Enabled: false
|
|
200
|
+
|
|
201
|
+
Style/GlobalStdStream:
|
|
202
|
+
Enabled: false
|
|
203
|
+
|
|
204
|
+
Style/StringConcatenation:
|
|
205
|
+
Enabled: false
|
|
206
|
+
|
|
207
|
+
Style/RescueModifier:
|
|
208
|
+
Enabled: false
|
|
209
|
+
|
|
210
|
+
Layout/MultilineMethodCallIndentation:
|
|
211
|
+
Enabled: false
|
|
212
|
+
|
|
213
|
+
Layout/MultilineOperationIndentation:
|
|
214
|
+
Enabled: false
|
|
215
|
+
|
|
216
|
+
Layout/ElseAlignment:
|
|
217
|
+
Enabled: false
|
|
218
|
+
|
|
219
|
+
Layout/FirstHashElementIndentation:
|
|
220
|
+
Enabled: false
|
|
221
|
+
|
|
222
|
+
Layout/FirstArrayElementIndentation:
|
|
223
|
+
Enabled: false
|
|
224
|
+
|
|
225
|
+
Layout/FirstArgumentIndentation:
|
|
226
|
+
Enabled: false
|
|
227
|
+
|
|
228
|
+
Layout/EndAlignment:
|
|
229
|
+
Enabled: false
|
|
230
|
+
|
|
231
|
+
Layout/BeginEndAlignment:
|
|
232
|
+
Enabled: false
|
|
233
|
+
|
|
234
|
+
Layout/CaseIndentation:
|
|
235
|
+
Enabled: false
|
|
236
|
+
|
|
237
|
+
Layout/ClosingParenthesisIndentation:
|
|
238
|
+
Enabled: false
|
|
239
|
+
|
|
240
|
+
Layout/SpaceAroundOperators:
|
|
241
|
+
Enabled: false
|
|
242
|
+
|
|
243
|
+
Layout/ExtraSpacing:
|
|
244
|
+
Enabled: false
|
|
245
|
+
|
|
246
|
+
Layout/SpaceBeforeBlockBraces:
|
|
247
|
+
Enabled: false
|
|
248
|
+
|
|
249
|
+
Layout/SpaceInsideBlockBraces:
|
|
250
|
+
Enabled: false
|
|
251
|
+
|
|
252
|
+
Layout/SpaceInsideHashLiteralBraces:
|
|
253
|
+
Enabled: false
|
|
254
|
+
|
|
255
|
+
Layout/EmptyLineAfterGuardClause:
|
|
256
|
+
Enabled: false
|
|
257
|
+
|
|
258
|
+
Layout/EmptyLinesAroundAttributeAccessor:
|
|
259
|
+
Enabled: false
|
|
260
|
+
|
|
261
|
+
Layout/EmptyLinesAroundClassBody:
|
|
262
|
+
Enabled: false
|
|
263
|
+
|
|
264
|
+
Layout/EmptyLinesAroundModuleBody:
|
|
265
|
+
Enabled: false
|
|
266
|
+
|
|
267
|
+
Layout/EmptyLinesAroundMethodBody:
|
|
268
|
+
Enabled: false
|
|
269
|
+
|
|
270
|
+
Layout/LeadingEmptyLines:
|
|
271
|
+
Enabled: false
|
|
272
|
+
|
|
273
|
+
Layout/EmptyLines:
|
|
274
|
+
Enabled: false
|
|
275
|
+
|
|
276
|
+
Layout/SpaceAroundKeyword:
|
|
277
|
+
Enabled: false
|
|
278
|
+
|
|
279
|
+
Layout/SpaceAfterColon:
|
|
280
|
+
Enabled: false
|
|
281
|
+
|
|
282
|
+
Layout/SpaceAfterComma:
|
|
283
|
+
Enabled: false
|
|
284
|
+
|
|
285
|
+
Lint/SuppressedException:
|
|
286
|
+
Enabled: false
|
|
287
|
+
|
|
288
|
+
Lint/UselessAccessModifier:
|
|
289
|
+
Enabled: false
|
|
290
|
+
|
|
291
|
+
Lint/ConstantDefinitionInBlock:
|
|
292
|
+
Enabled: false
|
|
293
|
+
|
|
294
|
+
Lint/EmptyBlock:
|
|
295
|
+
Enabled: false
|
|
296
|
+
|
|
297
|
+
Lint/ShadowingOuterLocalVariable:
|
|
298
|
+
Enabled: false
|
|
299
|
+
|
|
300
|
+
Lint/NonLocalExitFromIterator:
|
|
301
|
+
Enabled: false
|
|
302
|
+
|
|
303
|
+
Naming/VariableNumber:
|
|
304
|
+
Enabled: false
|
|
305
|
+
|
|
306
|
+
Naming/AccessorMethodName:
|
|
307
|
+
Enabled: false
|
|
308
|
+
|
|
309
|
+
Naming/PredicateName:
|
|
310
|
+
Enabled: false
|
|
311
|
+
|
|
312
|
+
Naming/MemoizedInstanceVariableName:
|
|
313
|
+
Enabled: false
|
|
314
|
+
|
|
315
|
+
Performance/MapMethodChain:
|
|
316
|
+
Enabled: false
|
|
317
|
+
|
|
318
|
+
Performance/FlatMap:
|
|
319
|
+
Enabled: false
|
|
320
|
+
|
|
321
|
+
Performance/UnfreezeString:
|
|
322
|
+
Enabled: false
|
|
323
|
+
|
|
324
|
+
Performance/StringInclude:
|
|
325
|
+
Enabled: false
|
|
326
|
+
|
|
327
|
+
Style/Documentation:
|
|
328
|
+
Enabled: false
|
|
329
|
+
|
|
330
|
+
Style/DocumentationMethod:
|
|
331
|
+
Enabled: false
|
|
332
|
+
|
|
333
|
+
Style/CommentedKeyword:
|
|
334
|
+
Enabled: false
|
|
335
|
+
|
|
336
|
+
Style/AsciiComments:
|
|
337
|
+
Enabled: false
|
|
338
|
+
|
|
339
|
+
Style/FormatStringToken:
|
|
340
|
+
Enabled: false
|
|
341
|
+
|
|
342
|
+
Lint/MissingSuper:
|
|
343
|
+
Enabled: false
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the arrolio gem will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
### Added — 2026-08-01 (initial scaffold)
|
|
8
|
+
|
|
9
|
+
* Project skeleton: gemspec, Gemfile, Rakefile, README.adoc, LICENSE,
|
|
10
|
+
`.rspec`, `.gitignore`.
|
|
11
|
+
* `Arrolio::VERSION` (`0.1.0.alpha1`).
|
|
12
|
+
* `Arrolio::Error` base + typed subclasses (`LayoutError`,
|
|
13
|
+
`RenderError`, `ContentError`, `LayoutSpecError`).
|
|
14
|
+
* Top-level autoload tree declaring the module structure: `Content`,
|
|
15
|
+
`LayoutSpec`, `Engine`, `Output`, `Renderer`, `Style`,
|
|
16
|
+
`FontMetrics`, `TextLayout`, `Flowables`, `Table`, `List`, `SVG`,
|
|
17
|
+
`FieldRun`, `Composer`. Subclass implementations land in follow-up
|
|
18
|
+
commits as code is ported from `Pdfrb::Layout::*`.
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## What Arrolio is
|
|
6
|
+
|
|
7
|
+
Arrolio is the middle layer of a three-library stack for paged-media PDF generation in pure Ruby:
|
|
8
|
+
|
|
9
|
+
- **`pdfrb`** (sibling gem, `~/src/claricle/pdfrb`) — pure-Ruby PDF byte format (streams, fonts, xref, encryption). No layout knowledge.
|
|
10
|
+
- **`arrolio`** (this gem) — FOP-like paged layout: page templates, threaded flows, line/page breaking, tables, lists, SVG, page-model selection, running headers/footers. **Flavor-agnostic**: the gem knows nothing about any specific document format.
|
|
11
|
+
- **`loom`** (separate gem, not yet built) — multi-target layout compiler (DSL → Arrolio IR, CSS, XSL-FO, IDML).
|
|
12
|
+
|
|
13
|
+
Arrolio deliberately does NOT own: PDF byte format (Pdfrb), DSL authoring (Loom), multi-target compilation (Loom), the semantic content model (`metanorma-document` and friends — Arrolio defines a *contract*, not a parser), or **any flavor-specific artifact** (no OIML/ISO/IEC/BSI code, data, XSL, or YAML inside the gem).
|
|
14
|
+
|
|
15
|
+
## The flavor-encapsulation invariant (non-negotiable)
|
|
16
|
+
|
|
17
|
+
**Flavors live OUTSIDE the gem.** A flavor is a directory containing XSL-derived YAML configuration (and the upstream XSL itself). The gem ships zero flavor artifacts:
|
|
18
|
+
|
|
19
|
+
- `lib/` contains no flavor-named files (`oiml`, `iso`, `iec`, `bsi`, …).
|
|
20
|
+
- `lib/arrolio.rb` has no flavor autoloads and no flavor-specific Ruby anywhere.
|
|
21
|
+
- `lib/arrolio/generic_adapter.rb` has zero hardcoded XML element names outside a clearly-labelled `DEFAULT_SELECTORS` constant (Metanorma standoc convention).
|
|
22
|
+
- `lib/arrolio/renderer/pdf.rb` reads header/footer and cover-logo values from `layout_spec.yml`, not hardcoded constants (the only literals are engine-safe fallback defaults).
|
|
23
|
+
- `lib/arrolio/content/document.rb` constructor validates that `sections`, `preface`, `bibliography` are `Content::Section[]` — non-Section items raise `ContentError` at the construction site.
|
|
24
|
+
- `flavors/` (sibling of `lib/`) is excluded from the gemspec's `spec.files`.
|
|
25
|
+
- `spec/fixtures/flavors/sample/` is a synthetic, flavor-neutral fixture used by the gem's own tests.
|
|
26
|
+
- `flavors/PACKAGING.md` documents the flavor gem convention for external authors.
|
|
27
|
+
|
|
28
|
+
A flavor is rendered by pointing `Arroolio::ConfigDrivenPipeline` at the flavor directory:
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
Arroolio::ConfigDrivenPipeline.render(xml, io: io, flavor_dir: '/path/to/flavor')
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Or via CLI: `exe/arrolio2pdf input.xml output.pdf /path/to/flavor`.
|
|
35
|
+
|
|
36
|
+
The user's principle: **"OIML FLAVOR SHOULD ONLY BE CONFIGURATION — like how the XSL is PURE CONFIGURATION!! NO CODE!"** A flavor is XSL + 3 generated YAML files. Zero Ruby.
|
|
37
|
+
|
|
38
|
+
## Two-pass model — the central architectural invariant
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
Content tree + LayoutSpec
|
|
42
|
+
↓ (layout pass)
|
|
43
|
+
Engine::Paged
|
|
44
|
+
↓
|
|
45
|
+
Output::Page[] ← medium-neutral contract between Engine and Renderer
|
|
46
|
+
↓ (render pass)
|
|
47
|
+
Renderer::Pdf
|
|
48
|
+
↓
|
|
49
|
+
PDF bytes (via Pdfrb)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Output::Page[] is the only contract between Engine and Renderer.** The renderer must never make layout decisions — every coordinate, style, and content reference comes from the Output tree. Future renderers (PostScript, PPML, in-memory test canvas) walk the same tree.
|
|
53
|
+
|
|
54
|
+
Each layer is independently testable. Keep layer boundaries airtight: Content knows nothing about pages or PDF; Engine knows nothing about PDF bytes; Renderer knows nothing about layout algorithms.
|
|
55
|
+
|
|
56
|
+
## Project state (as of 2026-08-03)
|
|
57
|
+
|
|
58
|
+
Generic config-driven pipeline works end-to-end: the OIML r060/1 fixture renders to a 25-page A4 PDF (~194 KB) via `Arroolio::ConfigDrivenPipeline` pointed at `flavors/oiml/`. 232 specs pass.
|
|
59
|
+
|
|
60
|
+
What's in the gem (core, flavor-agnostic):
|
|
61
|
+
- `lib/arrolio/{content,style,layout_spec,font,font_metrics,glyph_measurer,inline_run,text_layout,flowable,flowables,frame,flow_context,engine,output,renderer,harness,flavor}/`
|
|
62
|
+
- `lib/arrolio/generic_adapter.rb` — reads `adapter_rules.yml`, parses any flavor's XML into `Content::Document`.
|
|
63
|
+
- `lib/arrolio/generic_flow_builder.rb` — reads `flow_rules.yml`, builds flowables for any flavor.
|
|
64
|
+
- `lib/arrolio/config_driven_pipeline.rb` — orchestrates adapter + flow builder + engine + renderer + ToC.
|
|
65
|
+
- `lib/arrolio/asset_resolver.rb`, `lib/arrolio/toc_builder.rb` — generic helpers.
|
|
66
|
+
- `exe/arrolio2pdf` — generic CLI taking a flavor directory argument.
|
|
67
|
+
- `scripts/xsl_to_config.rb` — generates all three flavor YAML files from an XSL stylesheet.
|
|
68
|
+
- `data/arrolio/` — AFM metrics + glyphlist for the 14 PDF standard fonts (core data, not flavor data).
|
|
69
|
+
|
|
70
|
+
What's outside the gem (flavor artifacts):
|
|
71
|
+
- `flavors/oiml/{layout_spec,adapter_rules,flow_rules}.yml` — generated by `xsl_to_config.rb` from the authoritative XSL at `~/src/mn/metanorma-taste/data/oiml/oiml.xsl`.
|
|
72
|
+
- `spec/fixtures/flavors/sample/` — synthetic flavor-neutral fixture for the gem's tests.
|
|
73
|
+
|
|
74
|
+
What's deferred (intentionally — see `TODO.complete/`):
|
|
75
|
+
- TODO 53: second flavor spike (proves the generic design works beyond OIML).
|
|
76
|
+
- TODO 54: `flavors/<name>/manifest.yml` contract.
|
|
77
|
+
- TODO 55: header/footer style in `layout_spec.yml` (currently hardcoded in renderer).
|
|
78
|
+
- TODO 56: split `GenericAdapter` into MECE per-element converters.
|
|
79
|
+
- TODO 57: strict `Content::Document#sections` typing (only `Section[]`).
|
|
80
|
+
- TODO 58: flavor packaging convention (`arrolio-<flavor>` gems).
|
|
81
|
+
- TODO 59: XSL converter profiles (metanorma, dita, docbook).
|
|
82
|
+
- TODO 60: footnote / note-hanging-indent / example-body-margin fidelity.
|
|
83
|
+
- TODO 61: typed error model + strict mode.
|
|
84
|
+
- TODO 62: font resolution via fontist (currently absolute paths in YAML).
|
|
85
|
+
- Plus the older TODO.implementation items (Knuth-Plass, PageSequenceMaster, table layout, etc.).
|
|
86
|
+
|
|
87
|
+
The TODO files in `TODO.complete/` and `TODO.implementation/` are the roadmap. `TODO.complete/README.md` covers the parity roadmap; `TODO.implementation/README.md` covers the foundation phase plan.
|
|
88
|
+
|
|
89
|
+
**Naming:** the code uses `Arroolio` (one r); the gemspec/gem name is also `arrolio`. Some TODO files use `Arroolio` (two r's) — the code is authoritative.
|
|
90
|
+
|
|
91
|
+
## Commands
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
bundle exec rake # rspec + rubocop (the default task)
|
|
95
|
+
bundle exec rspec # full suite
|
|
96
|
+
bundle exec rspec spec/arrolio/style_spec.rb # one file
|
|
97
|
+
bundle exec rspec spec/arrolio/style_spec.rb:42 # one example by line
|
|
98
|
+
bundle exec rspec -t ~e2e # skip slow end-to-end specs
|
|
99
|
+
bundle exec rubocop
|
|
100
|
+
bundle exec rubocop -A # auto-correct safe offences
|
|
101
|
+
|
|
102
|
+
# Render any flavor via the generic CLI:
|
|
103
|
+
bundle exec ruby exe/arrolio2pdf input.xml out.pdf flavors/oiml
|
|
104
|
+
|
|
105
|
+
# Regenerate a flavor's YAML from its XSL (development time, not runtime):
|
|
106
|
+
bundle exec rake flavor:generate XSL=path/to/flavor.xsl OUT=flavors/name
|
|
107
|
+
# Or directly:
|
|
108
|
+
bundle exec ruby scripts/xsl_to_config.rb path/to/flavor.xsl flavors/name
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The OIML reference PDF for r060/1 is at `~/src/mn/mn-samples-oiml/_site/documents/r060/1/document.pdf` (mn2pdf v2.55, 28 pages). The OIML fixture XML is at `~/src/mn/mn-samples-oiml/_site/documents/r060/1/document.presentation.xml`.
|
|
112
|
+
|
|
113
|
+
`pdfrb` is pinned to a local path (`/Users/mulgogi/src/claricle/pdfrb`) in the Gemfile until it ships to RubyGems.
|
|
114
|
+
|
|
115
|
+
## Architectural conventions (non-negotiable)
|
|
116
|
+
|
|
117
|
+
- **`autoload` only — never `require_relative`, never `require` for internal code.** Autoloads live in the immediate parent namespace's file (create that file if it doesn't exist yet). Example: classes under `Arroolio::Content::*` are autoloaded from `lib/arrolio/content.rb`, which itself is autoloaded from `lib/arrolio.rb`.
|
|
118
|
+
- **Nested module/class style, NOT compact.** Always write `module Arroolio; class Foo; end; end`, never `class Arroolio::Foo`. Compact style breaks Ruby's lexical constant lookup — sibling constants resolved by short name (e.g. `FontMetrics::Registry` inside `class Arroolio::GlyphMeasurer`) won't autoload.
|
|
119
|
+
- **`# frozen_string_literal: true` at the top of every Ruby file.**
|
|
120
|
+
- **Immutable value objects.** Models that cross a layer boundary freeze in their constructor and define `==`, `eql?`, `hash` by value. Builders accumulate into Arrays; the immutable node is constructed in one shot.
|
|
121
|
+
- **Dispatch by `is_a?` and `role` Symbols, not by `respond_to?`.** Each content/layout node carries a `style_id` Symbol used for style registry lookup.
|
|
122
|
+
- **No `send` to private methods, no `instance_variable_set`/`instance_variable_get`, no `respond_to?` type checks.** Redesign the boundary instead of bypassing encapsulation.
|
|
123
|
+
- **No `double()` in specs.** Use real model instances, or `Struct.new(...)` stand-ins for plain data.
|
|
124
|
+
- **No hand-rolled `to_h`/`from_h`/`to_json`/`serialize` on model classes.** (Style::Definition#to_h is allowed: Definition is a value object whose Hash form is internal config shape, not a persisted wire contract.)
|
|
125
|
+
- **Validation in constructors.** Bad input raises typed errors from `Arroolio::Error` (`ContentError`, `LayoutSpecError`, `LayoutError`, `RenderError`) carrying structured metadata, not generic `RuntimeError`.
|
|
126
|
+
- **No flavor artifacts in the gem.** No OIML/ISO/IEC/BSI Ruby, YAML, or XSL under `lib/` or `data/`. Flavors live in `flavors/` (excluded from the gemspec).
|
|
127
|
+
|
|
128
|
+
## Design principles for new code
|
|
129
|
+
|
|
130
|
+
When extending any layer, apply OCP/DRY/MECE strictly:
|
|
131
|
+
|
|
132
|
+
- **Open/Closed:** adding a new flavor = adding a `flavors/<name>/` directory with XSL + 3 generated YAML files. Zero core changes. Adding a new flowable, content node, renderer, or visitor = *registering* a new class — not editing a switch statement.
|
|
133
|
+
- **MECE per layer:** Content describes *what*, LayoutSpec describes *how*, Engine produces *where*, Output freezes the result, Renderer emits bytes. Flavor describes *what the source format looks like* (adapter_rules.yml) and *how to lay it out* (flow_rules.yml + layout_spec.yml). If a class reaches across these boundaries, the responsibility is in the wrong place.
|
|
134
|
+
- **Greedy line breaking** is the only breaker today. When Knuth-Plass lands (TODO 12), it plugs into `TextLayout::*` without touching flowables or the engine.
|
|
135
|
+
- **Specs follow layer boundaries.** Each layer has its own spec directory; the `:e2e` tag is reserved for full-pipeline specs. The gem's own specs use the synthetic `spec/fixtures/flavors/sample/` flavor — they never depend on OIML.
|
|
136
|
+
|
|
137
|
+
## Renderer/Pdfrb coupling notes
|
|
138
|
+
|
|
139
|
+
- `Renderer::Pdf#render_page` deliberately points each pdfrb page's `Resources` Hash at the catalog's. Pdfrb's `Fonts#add` attaches fonts to the *catalog* Resources, but each Page shadows with its own empty Hash — without this workaround, fonts are invisible to readers.
|
|
140
|
+
- `Renderer::FontRegistry` memoises font family name → Pdfrb resource Symbol per render pass. Falls back to Helvetica for unknown families so the render never crashes mid-document. TODO 62 makes this strict.
|
|
141
|
+
- TODO 55 moves the header/footer visual style (Arial 9pt + 0.5pt border, currently hardcoded in the renderer) into `layout_spec.yml` so each flavor controls its own header/footer look.
|
|
142
|
+
|
|
143
|
+
## Key files to read first
|
|
144
|
+
|
|
145
|
+
- `lib/arrolio.rb` — the autoload tree; this is the map of every module.
|
|
146
|
+
- `lib/arrolio/error.rb` — the typed error hierarchy; raise these, never `RuntimeError`.
|
|
147
|
+
- `lib/arrolio/config_driven_pipeline.rb` — the end-to-end orchestration: adapter → flow builder → engine → ToC → renderer.
|
|
148
|
+
- `lib/arrolio/generic_adapter.rb` — XML → `Content::Document`, driven by `adapter_rules.yml`.
|
|
149
|
+
- `lib/arrolio/generic_flow_builder.rb` — `Content::Document` → flowables, driven by `flow_rules.yml`.
|
|
150
|
+
- `scripts/xsl_to_config.rb` — generates all three flavor YAML files from an XSL stylesheet.
|
|
151
|
+
- `spec/fixtures/flavors/sample/` — the synthetic flavor-neutral fixture used by the gem's specs.
|
|
152
|
+
- `flavors/oiml/` — the real OIML flavor (generated YAML + upstream XSL); lives outside the gem package.
|
|
153
|
+
- `TODO.complete/README.md` — parity roadmap and critical path.
|
|
154
|
+
- `TODO.implementation/README.md` — foundation phase plan.
|
data/Gemfile
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
source 'https://rubygems.org'
|
|
4
|
+
|
|
5
|
+
gemspec
|
|
6
|
+
|
|
7
|
+
gem 'pry', '~> 0.14'
|
|
8
|
+
gem 'rake', '~> 13.0'
|
|
9
|
+
gem 'rspec', '~> 3.13'
|
|
10
|
+
gem 'rubocop', '~> 1.75'
|
|
11
|
+
gem 'rubocop-performance', '~> 1.23'
|
|
12
|
+
gem 'rubocop-rake', '~> 0.7'
|
|
13
|
+
gem 'rubocop-rspec', '~> 3.5'
|
|
14
|
+
|
|
15
|
+
gem 'rexml'
|
data/LICENSE
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
BSD 2-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Ribose Inc.
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
16
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
17
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
18
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
19
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
20
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
21
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
22
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
23
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
24
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.adoc
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
= Arrolio
|
|
2
|
+
|
|
3
|
+
FOP-like paged layout engine for Ruby. Renders to PDF via the sibling
|
|
4
|
+
https://github.com/claricle/pdfrb[pdfrb] gem.
|
|
5
|
+
|
|
6
|
+
== What this is
|
|
7
|
+
|
|
8
|
+
Arrolio is the middle layer of a three-library stack:
|
|
9
|
+
|
|
10
|
+
[cols="1,4,3"]
|
|
11
|
+
|===
|
|
12
|
+
| Library | Owns | Depends on
|
|
13
|
+
|
|
14
|
+
| `pdfrb`
|
|
15
|
+
| Pure-Ruby PDF library (bytes ↔ model; streams, fonts, xref, encryption)
|
|
16
|
+
| —
|
|
17
|
+
|
|
18
|
+
| `arrolio` (this gem)
|
|
19
|
+
| FOP-like paged layout: page templates, threaded flows, Knuth-Plass
|
|
20
|
+
line/page breaking, tables, lists, SVG, page-model selection,
|
|
21
|
+
running headers/footers
|
|
22
|
+
| `pdfrb`
|
|
23
|
+
|
|
24
|
+
| `loom` (separate gem, not yet built)
|
|
25
|
+
| Multi-target layout compiler. Compiles a higher-level DSL to
|
|
26
|
+
Arrolio's IR, plus CSS, XSL-FO, and IDML.
|
|
27
|
+
| `arrolio` (for the PDF target only)
|
|
28
|
+
|===
|
|
29
|
+
|
|
30
|
+
Arrolio's job: take a content tree plus a layout spec, produce a
|
|
31
|
+
sequence of laid-out pages, render those to PDF (or, in future, to
|
|
32
|
+
other paged formats).
|
|
33
|
+
|
|
34
|
+
== Two-direction contract
|
|
35
|
+
|
|
36
|
+
* *Content + LayoutSpec → Arrolio::Engine::Paged → Output::Page[]*
|
|
37
|
+
(the layout pass).
|
|
38
|
+
* *Output::Page[] → Arrolio::Renderer::Pdf → bytes*
|
|
39
|
+
(the render pass; uses Pdfrb internally).
|
|
40
|
+
|
|
41
|
+
== Quick start
|
|
42
|
+
|
|
43
|
+
[source,ruby]
|
|
44
|
+
----
|
|
45
|
+
require "arrolio"
|
|
46
|
+
|
|
47
|
+
content = Arrolio::Content::Document.new
|
|
48
|
+
content.add_section("Introduction", level: 1) do |section|
|
|
49
|
+
section.add_paragraph("Hello, World!")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
layout = Arrolio::LayoutSpec.new
|
|
53
|
+
layout.add_page_template(:body, page_size: :A4, margins: 25)
|
|
54
|
+
layout.add_style(:body, font: "Times-Roman", size: 11)
|
|
55
|
+
|
|
56
|
+
engine = Arrolio::Engine::Paged.new(layout)
|
|
57
|
+
pages = engine.layout(content)
|
|
58
|
+
renderer = Arrolio::Renderer::Pdf.new
|
|
59
|
+
|
|
60
|
+
File.binwrite("out.pdf", renderer.render(pages))
|
|
61
|
+
----
|
|
62
|
+
|
|
63
|
+
Higher-level convenience API:
|
|
64
|
+
|
|
65
|
+
[source,ruby]
|
|
66
|
+
----
|
|
67
|
+
Arrolio.compose("out.pdf") do |doc|
|
|
68
|
+
doc.page_size = :A4
|
|
69
|
+
doc.margins = 25
|
|
70
|
+
doc.heading "Introduction"
|
|
71
|
+
doc.text "Hello, World!"
|
|
72
|
+
end
|
|
73
|
+
----
|
|
74
|
+
|
|
75
|
+
== Where Arrolio's responsibilities end
|
|
76
|
+
|
|
77
|
+
Arrolio deliberately does *not* own:
|
|
78
|
+
|
|
79
|
+
* *PDF byte format* — that's Pdfrb's job.
|
|
80
|
+
* *DSL authoring* (YAML, FO XML, etc.) — that's Loom's job.
|
|
81
|
+
* *Multi-target compilation* (CSS / XSL-FO / IDML) — Loom.
|
|
82
|
+
* *The semantic content model* — that's
|
|
83
|
+
https://github.com/metanorma/metanorma-document[metanorma-document]
|
|
84
|
+
(or any conforming source). Arrolio defines a *contract*, not an
|
|
85
|
+
implementation.
|
|
86
|
+
|
|
87
|
+
== Status
|
|
88
|
+
|
|
89
|
+
Pre-1.0. The skeleton lands first; modules port incrementally from
|
|
90
|
+
`Pdfrb::Layout::*` (which will eventually be removed from Pdfrb once
|
|
91
|
+
Arrolio is stable).
|
|
92
|
+
|
|
93
|
+
== License
|
|
94
|
+
|
|
95
|
+
BSD-2-Clause. See link:LICENSE[].
|
data/Rakefile
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'bundler/gem_tasks'
|
|
4
|
+
require 'rspec/core/rake_task'
|
|
5
|
+
require 'rubocop/rake_task'
|
|
6
|
+
|
|
7
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
8
|
+
RuboCop::RakeTask.new
|
|
9
|
+
|
|
10
|
+
task default: [:spec, :rubocop]
|
|
11
|
+
|
|
12
|
+
namespace :flavor do
|
|
13
|
+
GENERATOR = File.expand_path('scripts/xsl_to_config.rb', __dir__)
|
|
14
|
+
|
|
15
|
+
desc 'Regenerate flavor config from an XSL stylesheet (XSL=, OUT=)'
|
|
16
|
+
task :generate do
|
|
17
|
+
xsl = ENV.fetch('XSL') { raise 'Set XSL= to the stylesheet path' }
|
|
18
|
+
out = ENV.fetch('OUT') { raise 'Set OUT= to the flavor directory' }
|
|
19
|
+
sh "bundle exec ruby #{GENERATOR} #{File.expand_path(xsl)} #{File.expand_path(out)}"
|
|
20
|
+
end
|
|
21
|
+
end
|