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.
@@ -1,204 +1,122 @@
1
1
  require_relative "config_utils"
2
- require_relative "resourcepaths"
3
2
  require_relative "docattr_providers"
4
- require_relative "adocsrc_providers"
5
- require_relative "subtreeinfobuilder"
6
- require_relative "docid/docid"
7
- require_relative "search/headingindexer"
8
- require_relative "indexbuilders/depgraphbuilder"
9
- require_relative "indexbuilders/subtree_indices"
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
- class HtmlLayoutConfig
14
- attr_reader :pre_builders, :post_builders, :adoc_extensions, :adoc_api_opts, :docattr_providers
15
-
16
- def initialize(resource_paths, config_opts)
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
- if resource_paths.src_style_path_abs
89
- # generate pdf using asciidoctor-pdf with custom styling
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
- # configure all parts needed to execute the options specified by
99
- # the user
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
- # config_opts:: a Cmdline::Options instance with config info
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
- @build_options = {
109
- pre_builders: [],
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
- # Initiate the doc attribute repo used during 'run-time'
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
- layout_config = case config_opts
122
- in format: "html" then HtmlLayoutConfig.new(@resource_paths, config_opts)
123
- in format: "pdf" then PdfLayoutConfig.new(@resource_paths)
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
- # setup all options from the chosen layout configuration but
129
- # override doc attributes with ones from the supplied configuration to
130
- # ensure they have highest pref
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
- setup_docid(config_opts, @build_options, @doc_attr)
136
- setup_index_generation(config_opts, @resource_paths, @build_options, @doc_attr)
47
+ private
137
48
 
138
- # setup all pre,post, and build options
139
- @build_options[:adoc_api_opts] = layout_config.adoc_api_opts
140
- @build_options[:pre_builders] += layout_config.pre_builders
141
- @build_options[:post_builders] += layout_config.post_builders
142
- layout_config.adoc_extensions.each do |type, instance|
143
- @build_options[:adoc_extensions][type] << instance
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
- protected
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
- def setup_index_generation(config_opts, resource_paths, build_options, doc_attr)
153
- return if config_opts.no_index
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
- # setup index generation
156
- adoc_src_provider = SubtreeIndexBase.new(
157
- {erb_template_path: resource_paths.idx_erb_template_abs}
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
- def setup_docid(config_opts, build_options, doc_attr)
164
- return unless config_opts.resolve_docid
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
- # setup docid resolution
167
- d = DocIdExtension::DocidPreBuilder.new
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
- # early exit if user does not want indices
173
- return if config_opts.no_index
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
- # generate dep graph if graphviz is available
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
- # swap standard index generation from the base class to ones including
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
- protected
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
- build_options[:post_builders] << AddHistoryPostBuilder.new(@git_repo_dir)
196
-
197
- # setup index generation
198
- adoc_src_provider = SubtreeIndexGit.new(
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
@@ -1,5 +1,5 @@
1
1
  require "asciidoctor"
2
- require_relative "../pathtree"
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
- (doc_id.length.between?(ID_MIN_LENGTH, ID_MAX_LENGTH) && !doc_id.include?("#"))
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(opts)
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 file node's data delegator.
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
- # an Array of HistoryEntry objects.
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
- dst_node.data = DataDelegator.new if dst_node.data.nil?
19
- dst_node.data.add(FileHistory.new(current_branch))
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
- dst_node.data.add(file_log)
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=\"2,3,8,3\",options=\"header\"]
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(node_data) + generate_history_info(node_data)
118
+ super + generate_history_info(node_data)
123
119
  end
124
120
 
125
121
  def document_detail(node_data)
126
- super(node_data) + generate_history_info(node_data)
122
+ super + generate_history_info(node_data)
127
123
  end
128
124
 
129
125
  def generate_history_info(node_data)
@@ -16,7 +16,7 @@ module Giblish
16
16
  def source
17
17
  # output tree intro
18
18
  tree_string = +<<~DOC_HEADER
19
- [subs=\"normal\"]
19
+ [subs="normal"]
20
20
  ----
21
21
  DOC_HEADER
22
22
 
@@ -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