giblish 2.2.2 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +20 -53
- data/.solargraph.yml +15 -0
- data/Changelog.adoc +5 -0
- data/README.adoc +1 -1
- data/diagram-classes.png +0 -0
- data/giblish.gemspec +10 -22
- data/lib/giblish/application.rb +11 -57
- data/lib/giblish/config_builders/docid_config_builder.rb +84 -0
- data/lib/giblish/config_builders/git_index_config_builder.rb +37 -0
- data/lib/giblish/config_builders/index_config_builder.rb +53 -0
- data/lib/giblish/config_utils.rb +0 -42
- data/lib/giblish/configurator.rb +81 -163
- data/lib/giblish/docid/docid.rb +3 -3
- data/lib/giblish/gitrepos/history_pb.rb +10 -5
- data/lib/giblish/indexbuilders/depgraphbuilder.rb +1 -1
- data/lib/giblish/indexbuilders/subtree_indices.rb +3 -7
- data/lib/giblish/indexbuilders/verbatimtree.rb +1 -1
- data/lib/giblish/layout_config/html_layout_config.rb +77 -0
- data/lib/giblish/layout_config/layout_config_result.rb +35 -0
- data/lib/giblish/layout_config/pdf_layout_config.rb +89 -0
- data/lib/giblish/node_data_provider.rb +137 -0
- data/lib/giblish/resourcepaths.rb +3 -2
- data/lib/giblish/search/searchquery.rb +3 -3
- data/lib/giblish/search/textsearcher.rb +3 -3
- data/lib/giblish/subtreeinfobuilder.rb +2 -2
- data/lib/giblish/treeconverter.rb +13 -8
- data/lib/giblish/version.rb +1 -1
- metadata +31 -179
- data/.github/workflows/unit_tests.yml +0 -30
- data/.ruby-version +0 -1
- data/lib/giblish/pathtree.rb +0 -518
data/lib/giblish/configurator.rb
CHANGED
@@ -1,204 +1,122 @@
|
|
1
1
|
require_relative "config_utils"
|
2
|
-
require_relative "resourcepaths"
|
3
2
|
require_relative "docattr_providers"
|
4
|
-
require_relative "
|
5
|
-
require_relative "
|
6
|
-
require_relative "
|
7
|
-
require_relative "
|
8
|
-
require_relative "
|
9
|
-
require_relative "
|
10
|
-
require_relative "gitrepos/history_pb"
|
3
|
+
require_relative "resourcepaths"
|
4
|
+
require_relative "layout_config/html_layout_config"
|
5
|
+
require_relative "layout_config/pdf_layout_config"
|
6
|
+
require_relative "config_builders/docid_config_builder"
|
7
|
+
require_relative "config_builders/index_config_builder"
|
8
|
+
require_relative "config_builders/git_index_config_builder"
|
11
9
|
|
12
10
|
module Giblish
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
@adoc_api_opts = {backend: "html"}
|
18
|
-
@pre_builders = []
|
19
|
-
@post_builders = []
|
20
|
-
@adoc_extensions = {}
|
21
|
-
@docattr_providers = []
|
22
|
-
|
23
|
-
if resource_paths.src_resource_dir_abs && resource_paths.dst_style_path_rel
|
24
|
-
# copy local resources to dst and link the generated html with
|
25
|
-
# the given css
|
26
|
-
@pre_builders << CopyResourcesPreBuild.new(resource_paths)
|
27
|
-
|
28
|
-
# make sure generated html has relative link to the copied css
|
29
|
-
@docattr_providers << RelativeCssDocAttr.new(resource_paths.dst_style_path_rel)
|
30
|
-
elsif config_opts.server_css
|
31
|
-
# do not copy any local resources, use the given web path to link to css
|
32
|
-
@docattr_providers << AbsoluteLinkedCss.new(config_opts.server_css)
|
33
|
-
end
|
34
|
-
|
35
|
-
if config_opts.make_searchable
|
36
|
-
# enabling text search
|
37
|
-
search_provider = HeadingIndexer.new(config_opts.srcdir)
|
38
|
-
@adoc_extensions[:tree_processor] = search_provider
|
39
|
-
@post_builders << search_provider
|
40
|
-
|
41
|
-
# add search form to all docs
|
42
|
-
@adoc_extensions[:docinfo_processor] = AddSearchForm.new(config_opts.search_action_path)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
# A combined docattr_provider and post-processor that:
|
48
|
-
# - instructs asciidoctor-mathematical to use svg as format
|
49
|
-
# - removes svg cache files produced by asciidoctor-mathematical
|
50
|
-
class PdfMathPostbuilder
|
51
|
-
# called by the TreeConverter during the post_build phase
|
52
|
-
def on_postbuild(src_topdir, dst_tree, converter)
|
53
|
-
dst_top = src_topdir.pathname
|
54
|
-
dst_top.each_child do |c|
|
55
|
-
if c.basename.to_s.match?(/^stem-[0-9a-f]*\.svg$/)
|
56
|
-
Giblog.logger.debug("will remove #{c}")
|
57
|
-
c.delete
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def document_attributes(src_node, dst_node, dst_top)
|
63
|
-
{
|
64
|
-
"mathematical-format" => "svg"
|
65
|
-
}
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
class PdfLayoutConfig
|
70
|
-
attr_reader :pre_builders, :post_builders, :adoc_extensions, :adoc_api_opts, :docattr_providers
|
71
|
-
|
72
|
-
def initialize(resource_paths)
|
73
|
-
@adoc_api_opts = {backend: "pdf"}
|
74
|
-
@pre_builders = []
|
75
|
-
@post_builders = []
|
76
|
-
@adoc_extensions = {}
|
77
|
-
@docattr_providers = []
|
78
|
-
|
79
|
-
begin
|
80
|
-
require "asciidoctor-mathematical"
|
81
|
-
cc = PdfMathPostbuilder.new
|
82
|
-
@post_builders << cc
|
83
|
-
@docattr_providers << cc
|
84
|
-
rescue LoadError
|
85
|
-
Giblog.logger.warn { "Did not find asciidoctor-mathematical. stem blocks will not be rendered correctly!" }
|
86
|
-
end
|
11
|
+
# AIDEV-NOTE: Assembles configuration from specialized builders following composition pattern
|
12
|
+
class Configurator
|
13
|
+
# @return [Hash] Build options for TreeConverter
|
14
|
+
attr_reader :build_options
|
87
15
|
|
88
|
-
|
89
|
-
|
90
|
-
@docattr_providers << PdfCustomStyle.new(
|
91
|
-
resource_paths.src_style_path_abs,
|
92
|
-
*resource_paths.font_dirs_abs.to_a
|
93
|
-
)
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
16
|
+
# @return [DocAttrBuilder] Document attribute builder
|
17
|
+
attr_reader :doc_attr
|
97
18
|
|
98
|
-
|
99
|
-
|
100
|
-
class Configurator
|
101
|
-
attr_reader :build_options, :doc_attr, :config_opts
|
19
|
+
# @return [CmdLine::Options] User configuration options
|
20
|
+
attr_reader :config_opts
|
102
21
|
|
103
|
-
#
|
22
|
+
# Creates configuration by assembling layout, DocId, and index configurations.
|
23
|
+
#
|
24
|
+
# @param config_opts [CmdLine::Options] User configuration options
|
104
25
|
def initialize(config_opts)
|
105
26
|
@config_opts = config_opts
|
106
27
|
@resource_paths = ResourcePaths.new(config_opts)
|
107
28
|
|
108
|
-
|
109
|
-
|
110
|
-
post_builders: [],
|
111
|
-
adoc_api_opts: {},
|
112
|
-
# add a hash where all values are initiated as empty arrays
|
113
|
-
adoc_extensions: Hash.new { |h, k| h[k] = [] }
|
114
|
-
}
|
29
|
+
# Build layout configuration
|
30
|
+
layout_config = build_layout_config(config_opts)
|
115
31
|
|
116
|
-
#
|
32
|
+
# Initialize doc attribute builder
|
117
33
|
@doc_attr = DocAttrBuilder.new(
|
118
|
-
GiblishDefaultDocAttribs.new
|
34
|
+
GiblishDefaultDocAttribs.new,
|
35
|
+
*layout_config.docattr_providers,
|
36
|
+
CmdLineDocAttribs.new(config_opts)
|
119
37
|
)
|
120
38
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
else
|
125
|
-
raise OptionParser::InvalidArgument, "The given cmd line flags are not supported: #{config_opts.inspect}"
|
126
|
-
end
|
39
|
+
# Build feature configurations
|
40
|
+
docid_config = DocIdConfigBuilder.build(config_opts)
|
41
|
+
index_config = build_index_config(config_opts)
|
127
42
|
|
128
|
-
#
|
129
|
-
|
130
|
-
|
131
|
-
@doc_attr.add_doc_attr_providers(
|
132
|
-
*layout_config.docattr_providers, CmdLineDocAttribs.new(config_opts)
|
133
|
-
)
|
43
|
+
# Assemble final build options
|
44
|
+
@build_options = assemble_build_options(layout_config, docid_config, index_config, config_opts)
|
45
|
+
end
|
134
46
|
|
135
|
-
|
136
|
-
setup_index_generation(config_opts, @resource_paths, @build_options, @doc_attr)
|
47
|
+
private
|
137
48
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
49
|
+
# @param config_opts [CmdLine::Options]
|
50
|
+
# @return [LayoutConfigResult]
|
51
|
+
def build_layout_config(config_opts)
|
52
|
+
case config_opts
|
53
|
+
in format: "html" then HtmlLayoutConfig.build(@resource_paths, config_opts)
|
54
|
+
in format: "pdf" then PdfLayoutConfig.build(@resource_paths)
|
55
|
+
else
|
56
|
+
raise OptionParser::InvalidArgument, "The given cmd line flags are not supported: #{config_opts.inspect}"
|
144
57
|
end
|
145
|
-
|
146
|
-
# add copy of asset dirs if options stipulates this
|
147
|
-
@build_options[:post_builders] << CopyAssetDirsPostBuild.new(@config_opts) unless @config_opts.copy_asset_folders.nil?
|
148
58
|
end
|
149
59
|
|
150
|
-
|
60
|
+
# @param config_opts [CmdLine::Options]
|
61
|
+
# @return [IndexConfig]
|
62
|
+
def build_index_config(config_opts)
|
63
|
+
IndexConfigBuilder.build(config_opts, @resource_paths, @doc_attr)
|
64
|
+
end
|
151
65
|
|
152
|
-
|
153
|
-
|
66
|
+
# @param layout_config [LayoutConfigResult]
|
67
|
+
# @param docid_config [DocIdConfig]
|
68
|
+
# @param index_config [IndexConfig]
|
69
|
+
# @param config_opts [CmdLine::Options]
|
70
|
+
# @return [Hash]
|
71
|
+
def assemble_build_options(layout_config, docid_config, index_config, config_opts)
|
72
|
+
# Start with layout configuration
|
73
|
+
options = {
|
74
|
+
adoc_api_opts: layout_config.adoc_api_opts,
|
75
|
+
pre_builders: layout_config.pre_builders.dup,
|
76
|
+
post_builders: layout_config.post_builders.dup,
|
77
|
+
adoc_extensions: Hash.new { |h, k| h[k] = [] }
|
78
|
+
}
|
154
79
|
|
155
|
-
#
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
idx = SubtreeInfoBuilder.new(doc_attr, nil, adoc_src_provider, config_opts.index_basename)
|
160
|
-
build_options[:post_builders] << idx
|
161
|
-
end
|
80
|
+
# Merge layout extensions
|
81
|
+
layout_config.adoc_extensions.each do |type, instances|
|
82
|
+
options[:adoc_extensions][type] += instances
|
83
|
+
end
|
162
84
|
|
163
|
-
|
164
|
-
|
85
|
+
# Merge docid configuration
|
86
|
+
options[:pre_builders] += docid_config.pre_builders
|
87
|
+
options[:post_builders] += docid_config.post_builders
|
88
|
+
options[:adoc_extensions][:preprocessor] += docid_config.preprocessors
|
165
89
|
|
166
|
-
#
|
167
|
-
|
168
|
-
build_options[:pre_builders] << d
|
169
|
-
docid_pp = DocIdExtension::DocidProcessor.new({id_2_node: d.id_2_node})
|
170
|
-
build_options[:adoc_extensions][:preprocessor] << docid_pp
|
90
|
+
# Merge index configuration
|
91
|
+
options[:post_builders] += index_config.post_builders
|
171
92
|
|
172
|
-
#
|
173
|
-
|
93
|
+
# Add asset copying if configured
|
94
|
+
unless config_opts.copy_asset_folders.nil?
|
95
|
+
options[:post_builders] << CopyAssetDirsPostBuild.new(config_opts)
|
96
|
+
end
|
174
97
|
|
175
|
-
|
176
|
-
dg = DependencyGraphPostBuilder.new(docid_pp.node_2_ids, doc_attr, nil, nil, config_opts.graph_basename)
|
177
|
-
build_options[:post_builders] << dg
|
98
|
+
options
|
178
99
|
end
|
179
100
|
end
|
180
101
|
|
181
|
-
#
|
182
|
-
# git history.
|
102
|
+
# AIDEV-NOTE: Git-specific configurator using composition instead of inheritance
|
183
103
|
class GitRepoConfigurator < Configurator
|
104
|
+
# Creates configuration for git repository conversion with history support.
|
105
|
+
#
|
106
|
+
# @param config_opts [CmdLine::Options] User configuration options
|
107
|
+
# @param git_repo_dir [Pathname] Path to git repository root
|
184
108
|
def initialize(config_opts, git_repo_dir)
|
185
109
|
@git_repo_dir = git_repo_dir
|
186
110
|
config_opts.search_action_path ||= "/gibsearch.cgi"
|
187
111
|
super(config_opts)
|
188
112
|
end
|
189
113
|
|
190
|
-
|
191
|
-
|
192
|
-
def setup_index_generation(config_opts, resource_paths, build_options, doc_attr)
|
193
|
-
return if config_opts.no_index
|
114
|
+
private
|
194
115
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
{erb_template_path: resource_paths.idx_erb_template_abs}
|
200
|
-
)
|
201
|
-
build_options[:post_builders] << SubtreeInfoBuilder.new(doc_attr, nil, adoc_src_provider, config_opts.index_basename)
|
116
|
+
# @param config_opts [CmdLine::Options]
|
117
|
+
# @return [IndexConfig]
|
118
|
+
def build_index_config(config_opts)
|
119
|
+
GitIndexConfigBuilder.build(config_opts, @resource_paths, @doc_attr, @git_repo_dir)
|
202
120
|
end
|
203
121
|
end
|
204
122
|
end
|
data/lib/giblish/docid/docid.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require "asciidoctor"
|
2
|
-
|
2
|
+
require "gran"
|
3
3
|
|
4
4
|
module Giblish
|
5
5
|
module DocIdExtension
|
@@ -62,7 +62,7 @@ module Giblish
|
|
62
62
|
if @id_2_node.key? doc_id
|
63
63
|
Giblog.logger.warn { "Found same doc id twice: (#{doc_id}). This id will be associated with '#{path}' and _not_ with '#{@id_2_node[id]}'." }
|
64
64
|
end
|
65
|
-
|
65
|
+
doc_id.length.between?(ID_MIN_LENGTH, ID_MAX_LENGTH) && !doc_id.include?("#")
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
@@ -84,7 +84,7 @@ module Giblish
|
|
84
84
|
def initialize(opts)
|
85
85
|
raise ArgumentError, "Missing required option: :id_2_node!" unless opts.key?(:id_2_node)
|
86
86
|
|
87
|
-
super
|
87
|
+
super
|
88
88
|
@id_2_node = opts[:id_2_node]
|
89
89
|
|
90
90
|
# init new keys in the hash with an empty array
|
@@ -1,9 +1,10 @@
|
|
1
1
|
require_relative "../subtreeinfobuilder"
|
2
|
+
require_relative "../node_data_provider"
|
2
3
|
|
3
4
|
module Giblish
|
4
|
-
# Adds a 'FileHistory' instance to each
|
5
|
+
# Adds a 'FileHistory' instance to each node's data via dynamic composition.
|
5
6
|
# Users down-the-line can then call node.data.history to receive
|
6
|
-
#
|
7
|
+
# a FileHistory object.
|
7
8
|
class AddHistoryPostBuilder
|
8
9
|
def initialize(repo_root)
|
9
10
|
@git_itf = GitItf.new(repo_root)
|
@@ -15,8 +16,10 @@ module Giblish
|
|
15
16
|
|
16
17
|
dst_tree.traverse_preorder do |level, dst_node|
|
17
18
|
unless dst_node.leaf?
|
18
|
-
|
19
|
-
dst_node.data.
|
19
|
+
# Non-leaf nodes don't have conversion info yet, create NodeDataProvider with history
|
20
|
+
if dst_node.data.nil?
|
21
|
+
dst_node.data = NodeDataProvider.new(FileHistory.new(current_branch))
|
22
|
+
end
|
20
23
|
next
|
21
24
|
end
|
22
25
|
|
@@ -33,7 +36,9 @@ module Giblish
|
|
33
36
|
log_entry["sha"]
|
34
37
|
)
|
35
38
|
end
|
36
|
-
|
39
|
+
|
40
|
+
# Add history provider to existing NodeDataProvider
|
41
|
+
dst_node.data.add_provider(file_log)
|
37
42
|
end
|
38
43
|
end
|
39
44
|
end
|
@@ -90,7 +90,7 @@ module Giblish
|
|
90
90
|
# add a virtual 'gibgraph.adoc' node as the only node in a source tree
|
91
91
|
# with this object as provider of both adoc source and conversion options
|
92
92
|
v_srcpath = Pathname.new("/virtual") / "#{@basename}.adoc"
|
93
|
-
src_node = PathTree.new(v_srcpath, self).node(v_srcpath, from_root: true)
|
93
|
+
src_node = Gran::PathTree.new(v_srcpath, self).node(v_srcpath, from_root: true)
|
94
94
|
|
95
95
|
# add the destination node where the converted file will be stored
|
96
96
|
i_node = dst_tree.add_descendants(@basename)
|
@@ -100,7 +100,7 @@ module Giblish
|
|
100
100
|
HISTORY_TABLE_HEADING = <<~HISTORY_HEADER
|
101
101
|
File history::
|
102
102
|
|
103
|
-
[cols
|
103
|
+
[cols="2,3,8,3",options="header"]
|
104
104
|
|===
|
105
105
|
|Date |Author |Message |Sha1
|
106
106
|
HISTORY_HEADER
|
@@ -110,20 +110,16 @@ module Giblish
|
|
110
110
|
|===\n\n
|
111
111
|
HIST_FOOTER
|
112
112
|
|
113
|
-
def initialize(erb_template_path)
|
114
|
-
super(erb_template_path)
|
115
|
-
end
|
116
|
-
|
117
113
|
def subtitle(dst_node)
|
118
114
|
"from #{dst_node.data.branch}"
|
119
115
|
end
|
120
116
|
|
121
117
|
def document_detail_fail(node_data)
|
122
|
-
super
|
118
|
+
super + generate_history_info(node_data)
|
123
119
|
end
|
124
120
|
|
125
121
|
def document_detail(node_data)
|
126
|
-
super
|
122
|
+
super + generate_history_info(node_data)
|
127
123
|
end
|
128
124
|
|
129
125
|
def generate_history_info(node_data)
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require_relative "layout_config_result"
|
2
|
+
require_relative "../docattr_providers"
|
3
|
+
require_relative "../resourcepaths"
|
4
|
+
require_relative "../search/headingindexer"
|
5
|
+
|
6
|
+
module Giblish
|
7
|
+
# AIDEV-NOTE: Builder for HTML layout configuration following established provider pattern
|
8
|
+
class HtmlLayoutConfig
|
9
|
+
# Builds complete HTML layout configuration.
|
10
|
+
#
|
11
|
+
# @param resource_paths [ResourcePaths] Resolved paths for resources, styles, and templates
|
12
|
+
# @param config_opts [CmdLine::Options] User configuration options
|
13
|
+
# @return [LayoutConfigResult] Complete layout configuration with all components
|
14
|
+
def self.build(resource_paths, config_opts)
|
15
|
+
# AIDEV-NOTE: Create search provider once and reuse in both extensions and post_builders
|
16
|
+
search_provider = config_opts.make_searchable ? HeadingIndexer.new(config_opts.srcdir) : nil
|
17
|
+
|
18
|
+
pre_builders = build_pre_builders(resource_paths)
|
19
|
+
post_builders = build_post_builders(search_provider)
|
20
|
+
adoc_extensions = build_adoc_extensions(search_provider, config_opts)
|
21
|
+
docattr_providers = build_docattr_providers(resource_paths, config_opts)
|
22
|
+
|
23
|
+
LayoutConfigResult.new(
|
24
|
+
pre_builders: pre_builders,
|
25
|
+
post_builders: post_builders,
|
26
|
+
adoc_extensions: adoc_extensions,
|
27
|
+
adoc_api_opts: {backend: "html"},
|
28
|
+
docattr_providers: docattr_providers
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
# @param resource_paths [ResourcePaths]
|
33
|
+
# @return [Array<CopyResourcesPreBuild>]
|
34
|
+
def self.build_pre_builders(resource_paths)
|
35
|
+
builders = []
|
36
|
+
if resource_paths.src_resource_dir_abs && resource_paths.dst_style_path_rel
|
37
|
+
builders << CopyResourcesPreBuild.new(resource_paths)
|
38
|
+
end
|
39
|
+
builders
|
40
|
+
end
|
41
|
+
|
42
|
+
# @param search_provider [HeadingIndexer, nil]
|
43
|
+
# @return [Array<HeadingIndexer>]
|
44
|
+
def self.build_post_builders(search_provider)
|
45
|
+
builders = []
|
46
|
+
builders << search_provider if search_provider
|
47
|
+
builders
|
48
|
+
end
|
49
|
+
|
50
|
+
# @param search_provider [HeadingIndexer, nil]
|
51
|
+
# @param config_opts [CmdLine::Options]
|
52
|
+
# @return [Hash{Symbol => Array<Object>}]
|
53
|
+
def self.build_adoc_extensions(search_provider, config_opts)
|
54
|
+
extensions = {}
|
55
|
+
if search_provider
|
56
|
+
extensions[:tree_processor] = [search_provider]
|
57
|
+
extensions[:docinfo_processor] = [AddSearchForm.new(config_opts.search_action_path)]
|
58
|
+
end
|
59
|
+
extensions
|
60
|
+
end
|
61
|
+
|
62
|
+
# @param resource_paths [ResourcePaths]
|
63
|
+
# @param config_opts [CmdLine::Options]
|
64
|
+
# @return [Array<Object>]
|
65
|
+
def self.build_docattr_providers(resource_paths, config_opts)
|
66
|
+
providers = []
|
67
|
+
if resource_paths.src_resource_dir_abs && resource_paths.dst_style_path_rel
|
68
|
+
providers << RelativeCssDocAttr.new(resource_paths.dst_style_path_rel)
|
69
|
+
elsif config_opts.server_css
|
70
|
+
providers << AbsoluteLinkedCss.new(config_opts.server_css)
|
71
|
+
end
|
72
|
+
providers
|
73
|
+
end
|
74
|
+
|
75
|
+
private_class_method :build_pre_builders, :build_post_builders, :build_adoc_extensions, :build_docattr_providers
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Giblish
|
2
|
+
# AIDEV-NOTE: Immutable value object holding complete layout configuration
|
3
|
+
class LayoutConfigResult
|
4
|
+
# @return [Array<Object>] Pre-build phase processors
|
5
|
+
attr_reader :pre_builders
|
6
|
+
|
7
|
+
# @return [Array<Object>] Post-build phase processors
|
8
|
+
attr_reader :post_builders
|
9
|
+
|
10
|
+
# @return [Hash{Symbol => Array<Object>}] Asciidoctor extensions by type
|
11
|
+
attr_reader :adoc_extensions
|
12
|
+
|
13
|
+
# @return [Hash{Symbol => Object}] Options passed to Asciidoctor API
|
14
|
+
attr_reader :adoc_api_opts
|
15
|
+
|
16
|
+
# @return [Array<Object>] Document attribute providers
|
17
|
+
attr_reader :docattr_providers
|
18
|
+
|
19
|
+
# Creates immutable layout configuration.
|
20
|
+
#
|
21
|
+
# @param pre_builders [Array<Object>] Objects implementing on_prebuild(src_tree, dst_tree, converter)
|
22
|
+
# @param post_builders [Array<Object>] Objects implementing on_postbuild(src_tree, dst_tree, converter)
|
23
|
+
# @param adoc_extensions [Hash{Symbol => Array<Object>}] Extension type to array of extension instances
|
24
|
+
# @param adoc_api_opts [Hash{Symbol => Object}] Options passed to Asciidoctor API
|
25
|
+
# @param docattr_providers [Array<Object>] Objects implementing document_attributes(src_node, dst_node, dst_top)
|
26
|
+
def initialize(pre_builders:, post_builders:, adoc_extensions:, adoc_api_opts:, docattr_providers:)
|
27
|
+
@pre_builders = pre_builders.freeze
|
28
|
+
@post_builders = post_builders.freeze
|
29
|
+
@adoc_extensions = adoc_extensions.freeze
|
30
|
+
@adoc_api_opts = adoc_api_opts.freeze
|
31
|
+
@docattr_providers = docattr_providers.freeze
|
32
|
+
freeze
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require_relative "layout_config_result"
|
2
|
+
require_relative "../docattr_providers"
|
3
|
+
require_relative "../utils"
|
4
|
+
|
5
|
+
module Giblish
|
6
|
+
# AIDEV-NOTE: Combined docattr provider and post-processor for asciidoctor-mathematical
|
7
|
+
class PdfMathPostbuilder
|
8
|
+
# Cleans up temporary SVG files created by asciidoctor-mathematical.
|
9
|
+
#
|
10
|
+
# @param src_topdir [Gran::PathTree] Source tree root
|
11
|
+
# @param dst_tree [Gran::PathTree] Destination tree
|
12
|
+
# @param converter [TreeConverter] Converter instance
|
13
|
+
# @return [void]
|
14
|
+
def on_postbuild(src_topdir, dst_tree, converter)
|
15
|
+
dst_top = src_topdir.pathname
|
16
|
+
dst_top.each_child do |c|
|
17
|
+
if c.basename.to_s.match?(/^stem-[0-9a-f]*\.svg$/)
|
18
|
+
Giblog.logger.debug("will remove #{c}")
|
19
|
+
c.delete
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Provides document attributes for mathematical formula rendering.
|
25
|
+
#
|
26
|
+
# @param src_node [Gran::PathTree::Node] Source node
|
27
|
+
# @param dst_node [Gran::PathTree::Node] Destination node
|
28
|
+
# @param dst_top [Pathname] Destination top directory
|
29
|
+
# @return [Hash{String => String}] Document attributes
|
30
|
+
def document_attributes(src_node, dst_node, dst_top)
|
31
|
+
{"mathematical-format" => "svg"}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# AIDEV-NOTE: Builder for PDF layout configuration following established provider pattern
|
36
|
+
class PdfLayoutConfig
|
37
|
+
# Builds complete PDF layout configuration.
|
38
|
+
#
|
39
|
+
# @param resource_paths [ResourcePaths] Resolved paths for resources, styles, and fonts
|
40
|
+
# @return [LayoutConfigResult] Complete layout configuration with all components
|
41
|
+
def self.build(resource_paths)
|
42
|
+
post_builders = build_post_builders
|
43
|
+
docattr_providers = build_docattr_providers(resource_paths, post_builders)
|
44
|
+
|
45
|
+
LayoutConfigResult.new(
|
46
|
+
pre_builders: [],
|
47
|
+
post_builders: post_builders,
|
48
|
+
adoc_extensions: {},
|
49
|
+
adoc_api_opts: {backend: "pdf"},
|
50
|
+
docattr_providers: docattr_providers
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [Array<PdfMathPostbuilder>]
|
55
|
+
def self.build_post_builders
|
56
|
+
builders = []
|
57
|
+
begin
|
58
|
+
require "asciidoctor-mathematical"
|
59
|
+
builders << PdfMathPostbuilder.new
|
60
|
+
rescue LoadError
|
61
|
+
Giblog.logger.warn { "Did not find asciidoctor-mathematical. stem blocks will not be rendered correctly!" }
|
62
|
+
end
|
63
|
+
builders
|
64
|
+
end
|
65
|
+
|
66
|
+
# @param resource_paths [ResourcePaths]
|
67
|
+
# @param post_builders [Array<PdfMathPostbuilder>]
|
68
|
+
# @return [Array<Object>]
|
69
|
+
def self.build_docattr_providers(resource_paths, post_builders)
|
70
|
+
providers = []
|
71
|
+
|
72
|
+
# Add math postbuilder as provider if it exists
|
73
|
+
math_builder = post_builders.find { |b| b.is_a?(PdfMathPostbuilder) }
|
74
|
+
providers << math_builder if math_builder
|
75
|
+
|
76
|
+
# Add custom style if configured
|
77
|
+
if resource_paths.src_style_path_abs
|
78
|
+
providers << PdfCustomStyle.new(
|
79
|
+
resource_paths.src_style_path_abs,
|
80
|
+
*resource_paths.font_dirs_abs.to_a
|
81
|
+
)
|
82
|
+
end
|
83
|
+
|
84
|
+
providers
|
85
|
+
end
|
86
|
+
|
87
|
+
private_class_method :build_post_builders, :build_docattr_providers
|
88
|
+
end
|
89
|
+
end
|