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
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'nokogiri'
|
|
5
|
+
require 'yaml'
|
|
6
|
+
|
|
7
|
+
module XslConfigGenerator
|
|
8
|
+
XSL_NS = 'http://www.w3.org/1999/XSL/Transform'
|
|
9
|
+
FO_NS = 'http://www.w3.org/1999/XSL/Format'
|
|
10
|
+
DEFAULT_PROFILE = File.expand_path('xsl_profiles/standoc.yml', __dir__).freeze
|
|
11
|
+
|
|
12
|
+
def self.generate(xsl_path:, output_dir:, profile: DEFAULT_PROFILE)
|
|
13
|
+
Builder.new(xsl_path, output_dir, profile).generate
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class Builder
|
|
17
|
+
def initialize(xsl_path, output_dir, profile_path)
|
|
18
|
+
@xsl_path = File.expand_path(xsl_path)
|
|
19
|
+
@output_dir = File.expand_path(output_dir)
|
|
20
|
+
@profile_path = profile_path
|
|
21
|
+
@stylesheet = File.basename(@xsl_path)
|
|
22
|
+
@profile = YAML.safe_load_file(profile_path, aliases: true) || {}
|
|
23
|
+
@document = Nokogiri::XML(File.read(@xsl_path)) { |config| config.nonet }
|
|
24
|
+
@attribute_sets = extract_attribute_sets
|
|
25
|
+
@variables = extract_variables
|
|
26
|
+
@templates = extract_templates
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def generate
|
|
30
|
+
File.write(File.join(@output_dir, 'layout_spec.yml'), yaml_file(layout_spec))
|
|
31
|
+
File.write(File.join(@output_dir, 'adapter_rules.yml'), yaml_file(adapter_rules))
|
|
32
|
+
File.write(File.join(@output_dir, 'flow_rules.yml'), yaml_file(flow_rules))
|
|
33
|
+
profile_name = File.basename(@profile_path)
|
|
34
|
+
warn "generated XSL configuration from #{@stylesheet} (profile: #{profile_name}) in #{@output_dir}"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def stylesheet_name
|
|
40
|
+
@stylesheet
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def layout_spec
|
|
44
|
+
{
|
|
45
|
+
'generated_from' => stylesheet_name,
|
|
46
|
+
'profile' => File.basename(@profile_path),
|
|
47
|
+
'stylesheet' => stylesheet_name,
|
|
48
|
+
'xsl_variables' => @variables,
|
|
49
|
+
'xsl_attribute_sets' => @attribute_sets,
|
|
50
|
+
'default_page_template' => 'body',
|
|
51
|
+
'page_templates' => {
|
|
52
|
+
'body' => {
|
|
53
|
+
'page_size' => 'A4',
|
|
54
|
+
'margins' => {
|
|
55
|
+
'top' => length(@variables['marginTop'], '26.5mm'),
|
|
56
|
+
'bottom' => length(@variables['marginBottom'], '26.5mm'),
|
|
57
|
+
'left' => length(@variables['marginLeftRight1'], '25.5mm'),
|
|
58
|
+
'right' => length(@variables['marginLeftRight1'], '25.5mm')
|
|
59
|
+
},
|
|
60
|
+
'region_extents' => { 'before' => '18mm', 'after' => '18mm' }
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
'styles' => styles,
|
|
64
|
+
'flows' => {
|
|
65
|
+
'main' => { 'region' => 'body', 'default_style_id' => 'body' }
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def styles
|
|
71
|
+
text_styles.merge(inline_styles_arg)
|
|
72
|
+
.merge(list_styles)
|
|
73
|
+
.merge(table_styles)
|
|
74
|
+
.merge(structure_styles)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def text_styles
|
|
78
|
+
o = @profile['style_overrides'] || {}
|
|
79
|
+
{
|
|
80
|
+
'body' => style_from('root-style', 'clause_style').merge('margin_bottom' => o['body_margin_bottom'] || 8),
|
|
81
|
+
**heading_styles,
|
|
82
|
+
'note' => style_from('note-style').merge('parent' => 'body'),
|
|
83
|
+
'termnote' => style_from('termnote-style').merge('parent' => 'body'),
|
|
84
|
+
'note_label' => style_from('note-name-style').merge(
|
|
85
|
+
'parent' => 'body', 'font_name' => o['em_font_name']
|
|
86
|
+
),
|
|
87
|
+
'example' => style_from('example-style').merge('parent' => 'body'),
|
|
88
|
+
'caption_label' => { 'parent' => 'body' },
|
|
89
|
+
'example_label' => { 'parent' => 'note_label' }
|
|
90
|
+
}
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def inline_styles_arg
|
|
94
|
+
o = @profile['style_overrides'] || {}
|
|
95
|
+
{
|
|
96
|
+
'inline' => { 'parent' => 'body' },
|
|
97
|
+
'strong' => { 'parent' => 'body', 'font_name' => o['strong_font_name'] },
|
|
98
|
+
'em' => { 'parent' => 'body', 'font_name' => o['em_font_name'] },
|
|
99
|
+
'monospace' => { 'parent' => 'body', 'font_name' => 'Courier' },
|
|
100
|
+
'preformatted' => {
|
|
101
|
+
'parent' => 'body', 'font_name' => 'Courier', 'font_size' => 9,
|
|
102
|
+
'align' => 'left', 'line_spacing' => 1.0, 'margin_bottom' => 6
|
|
103
|
+
},
|
|
104
|
+
'identifier' => { 'parent' => 'body', 'font_name' => o['identifier_font_name'] },
|
|
105
|
+
'term' => style_from('term-style').merge('parent' => 'body', 'font_name' => o['term_font_name']),
|
|
106
|
+
'xref' => style_from('xref-style').merge('parent' => 'body'),
|
|
107
|
+
'link' => style_from('link-style').merge('parent' => 'body', 'font_name' => o['link_font_name']),
|
|
108
|
+
'bibitem' => { 'parent' => 'body', 'margin_top' => 4, 'margin_bottom' => 4 },
|
|
109
|
+
'footnote' => { 'parent' => 'body', 'font_size' => 9, 'margin_top' => 2, 'margin_bottom' => 2 }
|
|
110
|
+
}
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def table_styles
|
|
114
|
+
o = @profile['style_overrides'] || {}
|
|
115
|
+
{
|
|
116
|
+
'table' => { 'parent' => 'body', 'font_size' => 10 },
|
|
117
|
+
'table_cell' => { 'parent' => 'table', 'margin_top' => 2, 'margin_bottom' => 2 },
|
|
118
|
+
'table_header_cell' => { 'parent' => 'table_cell', 'font_name' => o['table_header_cell_font_name'] },
|
|
119
|
+
'figure_caption' => style_from('figure-name-style').merge(
|
|
120
|
+
'parent' => 'body', 'font_size' => 10, 'align' => 'center'
|
|
121
|
+
)
|
|
122
|
+
}
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def structure_styles
|
|
126
|
+
toc_styles.merge(cover_styles).merge(section_styles)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def toc_styles
|
|
130
|
+
{
|
|
131
|
+
'toc_title' => style_from('toc-title-style').merge('parent' => 'body'),
|
|
132
|
+
'toc_entry' => { 'parent' => 'body', 'font_size' => 11 },
|
|
133
|
+
'toc_entry_sub' => { 'parent' => 'toc_entry', 'margin_left' => 12 }
|
|
134
|
+
}
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def cover_styles
|
|
138
|
+
o = @profile['style_overrides'] || {}
|
|
139
|
+
{
|
|
140
|
+
'cover_docidentifier' => {
|
|
141
|
+
'parent' => 'body', 'font_name' => o['cover_docidentifier_font_name'], 'font_size' => 28,
|
|
142
|
+
'align' => 'center', 'fill_color' => '#221E1F'
|
|
143
|
+
},
|
|
144
|
+
'cover_title' => {
|
|
145
|
+
'parent' => 'body', 'font_name' => o['cover_title_font_name'], 'font_size' => 16, 'align' => 'center'
|
|
146
|
+
},
|
|
147
|
+
'cover_title_part' => { 'parent' => 'cover_title', 'font_size' => 14 },
|
|
148
|
+
'cover_title_other' => {
|
|
149
|
+
'parent' => 'body', 'font_name' => o['cover_title_other_font_name'], 'font_size' => 11, 'align' => 'center'
|
|
150
|
+
},
|
|
151
|
+
'cover_org' => { 'parent' => 'body', 'font_name' => o['cover_org_font_name'], 'font_size' => 13,
|
|
152
|
+
'align' => 'right' },
|
|
153
|
+
'cover_label' => {
|
|
154
|
+
'parent' => 'body', 'font_name' => o['cover_label_font_name'], 'font_size' => 13,
|
|
155
|
+
'align' => 'right', 'fill_color' => '#221E1F'
|
|
156
|
+
},
|
|
157
|
+
'doc_title' => {
|
|
158
|
+
'parent' => 'body', 'font_name' => o['strong_font_name'], 'font_size' => 18,
|
|
159
|
+
'align' => 'center', 'margin_bottom' => 36
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def heading_styles
|
|
165
|
+
(1..6).to_h do |level|
|
|
166
|
+
attrs = style_from('title-style', 'title-style', level: level)
|
|
167
|
+
attrs['parent'] = 'body'
|
|
168
|
+
attrs['font_name'] = (@profile['style_overrides'] || {})['strong_font_name']
|
|
169
|
+
attrs['keep_together'] = true
|
|
170
|
+
attrs['align'] = 'left' if level == 1
|
|
171
|
+
["heading_#{level}", attrs]
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def list_styles
|
|
176
|
+
o = @profile['style_overrides'] || {}
|
|
177
|
+
attrs = style_from(nil, 'list-style').merge('parent' => 'body')
|
|
178
|
+
{
|
|
179
|
+
'list_bullet' => attrs,
|
|
180
|
+
'list_ordered' => attrs,
|
|
181
|
+
'list_marker' => { 'parent' => 'body', 'font_name' => o['list_marker_font_name'] }
|
|
182
|
+
}
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def section_styles
|
|
186
|
+
(1..6).to_h do |level|
|
|
187
|
+
["section_body_#{level}", { 'parent' => 'body', 'margin_left' => (level - 1) * 12 }]
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def adapter_rules
|
|
192
|
+
{
|
|
193
|
+
'generated_from' => stylesheet_name,
|
|
194
|
+
'profile' => File.basename(@profile_path),
|
|
195
|
+
'stylesheet' => stylesheet_name,
|
|
196
|
+
'metadata' => {
|
|
197
|
+
'root' => 'bibdata',
|
|
198
|
+
'fields' => @profile['metadata_fields'] || {}
|
|
199
|
+
},
|
|
200
|
+
'cover' => { 'fields' => @profile['cover_fields'] || [] },
|
|
201
|
+
'sections' => { 'container' => 'sections' },
|
|
202
|
+
'element_mapping' => @profile['element_mapping'] || {},
|
|
203
|
+
'paragraph_styles' => @profile['paragraph_styles'] || {},
|
|
204
|
+
'inline_styles' => @profile['inline_styles'] || {},
|
|
205
|
+
'span_class_styles' => @profile['span_class_styles'] || {},
|
|
206
|
+
'block_level_elements' => @profile['block_level_elements'] || [],
|
|
207
|
+
'skip_metadata_elements' => @profile['skip_metadata_elements'] || [],
|
|
208
|
+
'skip_elements' => @profile['skip_elements'] || [],
|
|
209
|
+
'tab_replacements' => @profile['tab_replacements'] || {},
|
|
210
|
+
'heading' => { 'source' => (@profile['selectors'] || {})['heading'] || 'fmt-title' },
|
|
211
|
+
'selectors' => @profile['selectors'] || {},
|
|
212
|
+
'xsl_templates' => @templates.select { |template| template['match'] }
|
|
213
|
+
}
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def flow_rules
|
|
217
|
+
{
|
|
218
|
+
'generated_from' => stylesheet_name,
|
|
219
|
+
'profile' => File.basename(@profile_path),
|
|
220
|
+
'stylesheet' => stylesheet_name,
|
|
221
|
+
'xsl_variables' => @variables,
|
|
222
|
+
'xsl_templates' => @templates.select { |template| template['name'] },
|
|
223
|
+
'page_sequences' => page_sequences,
|
|
224
|
+
'cover_content' => @profile['cover_content'] || [],
|
|
225
|
+
'section' => {
|
|
226
|
+
'heading_style' => '{{title_style_id}}',
|
|
227
|
+
'body_style' => '{{style_id}}',
|
|
228
|
+
'insert_page_break_before' => true,
|
|
229
|
+
'insert_page_break_after_section' => true
|
|
230
|
+
},
|
|
231
|
+
'content_to_flowable' => {
|
|
232
|
+
'Paragraph' => 'text_paragraph', 'Table' => 'table', 'List' => 'list',
|
|
233
|
+
'Image' => 'image', 'Preformatted' => 'preformatted', 'PageBreak' => 'page_break'
|
|
234
|
+
},
|
|
235
|
+
'style_resolution' => {
|
|
236
|
+
'paragraph' => 'resolve_from_style_id',
|
|
237
|
+
'heading' => 'resolve_from_title_style_id',
|
|
238
|
+
'table_cell' => 'resolve_from_table_cell_style',
|
|
239
|
+
'list_item' => 'resolve_from_list_style'
|
|
240
|
+
},
|
|
241
|
+
'toc' => { 'styles' => { 'entry' => 'toc_entry', 'sub' => 'toc_entry_sub' } }
|
|
242
|
+
}
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def page_sequences
|
|
246
|
+
names = @templates.filter_map { |template| template['name'] }
|
|
247
|
+
sequences = [{ 'role' => 'cover', 'header' => nil, 'footer' => nil, 'build_content' => 'cover_content' }]
|
|
248
|
+
if names.include?('back-page')
|
|
249
|
+
sequences << { 'role' => 'back_of_cover', 'header' => nil, 'footer' => nil, 'page_break_after' => true }
|
|
250
|
+
end
|
|
251
|
+
if names.any? { |name| name.include?('preface') }
|
|
252
|
+
sequences << { 'role' => 'preface', 'header' => header_template, 'footer' => '%d', 'header_align' => 'right' }
|
|
253
|
+
end
|
|
254
|
+
sequences << { 'role' => 'body', 'header' => header_template, 'footer' => '%d', 'header_align' => 'right' }
|
|
255
|
+
if names.include?('insertMainSectionsPageSequences')
|
|
256
|
+
sequences << { 'role' => 'bibliography', 'header' => header_template, 'footer' => '%d',
|
|
257
|
+
'header_align' => 'right' }
|
|
258
|
+
end
|
|
259
|
+
sequences
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def header_template
|
|
263
|
+
return nil unless @templates.any? { |template| template['name'] == 'insertHeaderEven' }
|
|
264
|
+
|
|
265
|
+
@profile['header_template']
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def extract_attribute_sets
|
|
269
|
+
@document.xpath('//xsl:attribute-set', 'xsl' => XSL_NS).to_h do |node|
|
|
270
|
+
attrs = node.xpath('./xsl:attribute', 'xsl' => XSL_NS).to_h do |attribute|
|
|
271
|
+
[attribute['name'], attribute.text.strip]
|
|
272
|
+
end
|
|
273
|
+
[node['name'], attrs]
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def extract_variables
|
|
278
|
+
@document.xpath('//xsl:variable', 'xsl' => XSL_NS).to_h do |node|
|
|
279
|
+
[node['name'], node.text.strip]
|
|
280
|
+
end.reject { |_name, value| value.empty? } # rubocop:disable Style/MultilineBlockChain -- idiomatic Hash.to_h.reject
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def extract_templates
|
|
284
|
+
@document.xpath('//xsl:template', 'xsl' => XSL_NS).map do |node|
|
|
285
|
+
attributes = node.xpath('.//xsl:attribute', 'xsl' => XSL_NS).map do |attribute|
|
|
286
|
+
condition = attribute.at_xpath('ancestor::xsl:if', 'xsl' => XSL_NS)
|
|
287
|
+
{
|
|
288
|
+
'name' => attribute['name'],
|
|
289
|
+
'value' => attribute.text.strip,
|
|
290
|
+
'test' => condition&.[]('test')
|
|
291
|
+
}.compact
|
|
292
|
+
end
|
|
293
|
+
{
|
|
294
|
+
'name' => node['name'],
|
|
295
|
+
'match' => node['match'],
|
|
296
|
+
'mode' => node['mode'],
|
|
297
|
+
'priority' => node['priority'],
|
|
298
|
+
'use_attribute_sets' => node['use-attribute-sets'],
|
|
299
|
+
'attributes' => attributes,
|
|
300
|
+
'output_elements' => node.css('*').select do |child|
|
|
301
|
+
child.namespace && child.namespace.href == FO_NS
|
|
302
|
+
end.map(&:name).uniq
|
|
303
|
+
}.compact
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def style_from(attribute_set_name, refine_name = nil, level: nil)
|
|
308
|
+
attrs = @attribute_sets.fetch(attribute_set_name, {}).dup
|
|
309
|
+
attrs.merge!(refine_attributes(refine_name, level)) if refine_name
|
|
310
|
+
attrs.each_with_object({}) do |(name, value), result|
|
|
311
|
+
key = style_key(name)
|
|
312
|
+
next unless key
|
|
313
|
+
|
|
314
|
+
result[key] = style_value(key, value)
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
def refine_attributes(name, level)
|
|
319
|
+
template = @templates.find { |item| item['name'] == "refine_#{name}" }
|
|
320
|
+
return {} unless template
|
|
321
|
+
|
|
322
|
+
attributes = {}
|
|
323
|
+
template.fetch('attributes', []).each do |attribute|
|
|
324
|
+
next unless condition_matches_level?(attribute['test'], level)
|
|
325
|
+
|
|
326
|
+
attributes[attribute['name']] = attribute['value']
|
|
327
|
+
end
|
|
328
|
+
attributes
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
def style_key(name)
|
|
332
|
+
{
|
|
333
|
+
'font-family' => 'font_name', 'font-size' => 'font_size',
|
|
334
|
+
'font-weight' => 'font_weight', 'font-style' => 'font_style',
|
|
335
|
+
'color' => 'fill_color', 'line-height' => 'line_spacing',
|
|
336
|
+
'text-align' => 'align', 'margin-left' => 'margin_left',
|
|
337
|
+
'margin-right' => 'margin_right', 'margin-top' => 'margin_top',
|
|
338
|
+
'margin-bottom' => 'margin_bottom', 'space-before' => 'margin_top',
|
|
339
|
+
'space-after' => 'margin_bottom', 'text-indent' => 'text_indent'
|
|
340
|
+
}[name]
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
def style_value(key, value)
|
|
344
|
+
return primary_font(value) if key == 'font_name'
|
|
345
|
+
return value.to_f if key == 'font_size' && value.to_s.match?(/[\d.]+pt\z/)
|
|
346
|
+
return value.to_f if key == 'line_spacing' && value.to_s.match?(/\A[\d.]+\z/)
|
|
347
|
+
return value.to_sym.to_s if key == 'align'
|
|
348
|
+
|
|
349
|
+
value
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def primary_font(value)
|
|
353
|
+
value.to_s.split(',').first.strip.gsub(/["']/, '')
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def condition_matches_level?(test, level)
|
|
357
|
+
return true unless test
|
|
358
|
+
return false unless level
|
|
359
|
+
|
|
360
|
+
match = test.match(/\$level\s*(=|>=)\s*(\d+)/)
|
|
361
|
+
return false unless match
|
|
362
|
+
|
|
363
|
+
match[1] == '=' ? level == match[2].to_i : level >= match[2].to_i
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def length(value, fallback)
|
|
367
|
+
value && !value.empty? ? "#{value}mm" : fallback
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
def yaml_file(value)
|
|
371
|
+
"# Generated from #{stylesheet_name} by scripts/xsl_to_config.rb.\n" \
|
|
372
|
+
"# DO NOT EDIT BY HAND — edit the XSL and rerun the generator.\n\n" \
|
|
373
|
+
"#{YAML.dump(value).delete_prefix("---\n")}"
|
|
374
|
+
end
|
|
375
|
+
end
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
xsl_path = ARGV[0]
|
|
379
|
+
output_dir = ARGV[1]
|
|
380
|
+
profile_arg = ARGV[2] && File.expand_path(ARGV[2])
|
|
381
|
+
|
|
382
|
+
unless xsl_path && output_dir
|
|
383
|
+
warn 'usage: xsl_to_config.rb <xsl-path> <output-dir> [profile-path]'
|
|
384
|
+
exit 2
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
XslConfigGenerator.generate(
|
|
388
|
+
xsl_path: xsl_path,
|
|
389
|
+
output_dir: output_dir,
|
|
390
|
+
profile: profile_arg || XslConfigGenerator::DEFAULT_PROFILE
|
|
391
|
+
)
|