asciidoctor-multipage 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/asciidoctor-multipage.rb +561 -0
  3. metadata +118 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b64a5fbb964951f440f2fa2c0d49cb1df7ace58be958a0e1f4ce280dca9e4591
4
+ data.tar.gz: 51c273c008afa11caad90d1b808e65919e2764f092891cd6d149934dcce322ae
5
+ SHA512:
6
+ metadata.gz: fc96577284cc71802f971196a99536f2ba3fc87dc97dad4833f40722cb8844819ca5b9132e2f1e63adf16701a9a987d1a14e89c76944b264ff8438f63775ec3e
7
+ data.tar.gz: dfee8f462b3cb3d81c9384f04e96fbdcb7c9c621ebdda69152d8dabd8fe55597d34315794aa0065d4d30edc07cf71e02abf3b5433191249e74e9c512ab6f697c
@@ -0,0 +1,561 @@
1
+ # coding: utf-8
2
+
3
+ require 'asciidoctor/converter/html5'
4
+
5
+ class Asciidoctor::AbstractBlock
6
+ # Allow navigation links HTML to be saved and retrieved
7
+ attr_accessor :nav_links
8
+ end
9
+
10
+ class Asciidoctor::AbstractNode
11
+ # Is this node (self) of interest when generating a TOC for node?
12
+ def related_to?(node)
13
+ return true if self.level == 0
14
+ node_tree = []
15
+ current = node
16
+ while current.class != Asciidoctor::Document
17
+ node_tree << current
18
+ current = current.parent
19
+ end
20
+ if node_tree.include?(self) ||
21
+ node_tree.include?(self.parent)
22
+ return true
23
+ end
24
+ # If this is a leaf page, include all child sections in TOC
25
+ if node.mplevel == :leaf
26
+ self_tree = []
27
+ current = self
28
+ while current && current.level >= node.level
29
+ self_tree << current
30
+ current = current.parent
31
+ end
32
+ return true if self_tree.include?(node)
33
+ end
34
+ return false
35
+ end
36
+ end
37
+
38
+ class Asciidoctor::Document
39
+ # Allow writing to the :catalog attribute in order to duplicate refs list to
40
+ # new pages
41
+ attr_writer :catalog
42
+
43
+ # Allow the section type to be saved (for when a Section becomes a Document)
44
+ attr_accessor :mplevel
45
+
46
+ # Allow the current Document to be marked as processed by this extension
47
+ attr_accessor :processed
48
+
49
+ # Allow saving of section number for use later. This is necessary for when a
50
+ # branch or leaf Section becomes a Document during chunking and ancestor
51
+ # nodes are no longer accessible.
52
+ attr_writer :sectnum
53
+
54
+ # Override the AbstractBlock sections?() check to enable the Table Of
55
+ # Contents. This extension may generate short pages that would normally have
56
+ # no need for a TOC. However, we override the Html5Converter outline() in
57
+ # order to generate a custom TOC for each page with entries that span the
58
+ # entire document.
59
+ def sections?
60
+ return true
61
+ end
62
+
63
+ # Return the saved section number for this Document object (which was
64
+ # originally a Section)
65
+ def sectnum(delimiter = nil, append = nil)
66
+ @sectnum
67
+ end
68
+ end
69
+
70
+ class Asciidoctor::Section
71
+ # Allow the section type (:root, :branch, :leaf) to be saved for each section
72
+ attr_accessor :mplevel
73
+
74
+ # Extend sectnum() to use the Document's saved sectnum. Document objects
75
+ # normally do not have sectnums, but here Documents are generated from
76
+ # Sections. The sectnum is saved in section() below.
77
+ def sectnum(delimiter = '.', append = nil)
78
+ append ||= (append == false ? '' : delimiter)
79
+ if @level == 1
80
+ %(#{@number}#{append})
81
+ elsif @level > 1
82
+ if @parent.class == Asciidoctor::Section ||
83
+ (@mplevel && @parent.class == Asciidoctor::Document)
84
+ %(#{@parent.sectnum(delimiter)}#{@number}#{append})
85
+ else
86
+ %(#{@number}#{append})
87
+ end
88
+ else # @level == 0
89
+ %(#{Asciidoctor::Helpers.int_to_roman @number}#{append})
90
+ end
91
+ end
92
+ end
93
+
94
+ class MultipageHtml5Converter < Asciidoctor::Converter::Html5Converter
95
+ include Asciidoctor
96
+ include Asciidoctor::Converter
97
+ include Asciidoctor::Writer
98
+
99
+ register_for 'multipage_html5'
100
+
101
+ attr_accessor :pages
102
+
103
+ def initialize(backend, opts = {})
104
+ @xml_mode = false
105
+ @void_element_slash = nil
106
+ super
107
+ @stylesheets = Stylesheets.instance
108
+ @pages = []
109
+ end
110
+
111
+ # Add navigation links to the page (from nav_links)
112
+ def add_nav_links(page)
113
+ block = Asciidoctor::Block.new(parent = page,
114
+ :paragraph,
115
+ opts = {:source => page.nav_links})
116
+ block.add_role('nav-footer')
117
+ page << block
118
+ end
119
+
120
+ def convert(node, transform = nil, opts = {})
121
+ transform ||= node.node_name
122
+ opts.empty? ? (send transform, node) : (send transform, node, opts)
123
+ end
124
+
125
+ # Process Document (either the original full document or a processed page)
126
+ def document(node)
127
+ if node.processed
128
+ # This node can now be handled by Html5Converter.
129
+ super
130
+ else
131
+ # This node is the original full document which has not yet been
132
+ # processed; this is the entry point for the extension.
133
+
134
+ # Turn off extensions to avoid running them twice.
135
+ # FIXME: DocinfoProcessor, InlineMacroProcessor, and Postprocessor
136
+ # extensions should be retained. Is this possible with the API?
137
+ #Asciidoctor::Extensions.unregister_all
138
+
139
+ # Check toclevels and multipage-level attributes
140
+ mplevel = node.document.attr('multipage-level', 1).to_i
141
+ toclevels = node.document.attr('toclevels', 2).to_i
142
+ if toclevels < mplevel
143
+ logger.warn 'toclevels attribute should be >= multipage-level'
144
+ end
145
+ if mplevel < 0
146
+ logger.warn 'multipage-level attribute must be >= 0'
147
+ mplevel = 0
148
+ end
149
+ node.document.set_attribute('multipage-level', mplevel.to_s)
150
+
151
+ # Set multipage chunk types
152
+ set_multipage_attrs(node)
153
+
154
+ # Set the "id" attribute for the Document, using the "docname", which is
155
+ # based on the file name. Then register the document ID using the
156
+ # document title. This allows cross-references to refer to (1) the
157
+ # top-level document itself or (2) anchors in top-level content (blocks
158
+ # that are specified before any sections).
159
+ node.id = node.attributes['docname']
160
+ node.register(:refs, [node.id,
161
+ (Inline.new(parent = node,
162
+ context = :anchor,
163
+ text = node.doctitle,
164
+ opts = {:type => :ref,
165
+ :id => node.id})),
166
+ node.doctitle])
167
+
168
+ # Generate navigation links for all pages
169
+ generate_nav_links(node)
170
+
171
+ # Create and save a skeleton document for generating the TOC lists.
172
+ @@full_outline = new_outline_doc(node)
173
+ # Save the document catalog to use for each part/chapter page.
174
+ @catalog = node.catalog
175
+
176
+ # Retain any book intro blocks, delete others, and add a list of sections
177
+ # for the book landing page.
178
+ parts_list = Asciidoctor::List.new(node, :ulist)
179
+ node.blocks.delete_if do |block|
180
+ if block.context == :section
181
+ part = block
182
+ part.convert
183
+ text = %(<<#{part.id},#{part.captioned_title}>>)
184
+ if desc = block.attr('desc') then text << %( – #{desc}) end
185
+ parts_list << Asciidoctor::ListItem.new(parts_list, text)
186
+ end
187
+ end
188
+ node << parts_list
189
+
190
+ # Add navigation links
191
+ add_nav_links(node)
192
+
193
+ # Mark page as processed and return converted result
194
+ node.processed = true
195
+ node.convert
196
+ end
197
+ end
198
+
199
+ # Process Document in embeddable mode (either the original full document or a
200
+ # processed page)
201
+ def embedded(node)
202
+ if node.processed
203
+ # This node can now be handled by Html5Converter.
204
+ super
205
+ else
206
+ # This node is the original full document which has not yet been
207
+ # processed; it can be handled by document().
208
+ document(node)
209
+ end
210
+ end
211
+
212
+ # Generate navigation links for all pages in document; save HTML to nav_links
213
+ def generate_nav_links(doc)
214
+ pages = doc.find_by(context: :section) {|section|
215
+ [:root, :branch, :leaf].include?(section.mplevel)}
216
+ pages.insert(0, doc)
217
+ pages.each do |page|
218
+ page_index = pages.find_index(page)
219
+ links = []
220
+ if page.mplevel != :root
221
+ previous_page = pages[page_index-1]
222
+ parent_page = page.parent
223
+ home_page = doc
224
+ # NOTE, there are some non-breaking spaces (U+00A0) below.
225
+ if previous_page != parent_page
226
+ links << %(← Previous: <<#{previous_page.id}>>)
227
+ end
228
+ links << %(↑ Up: <<#{parent_page.id}>>)
229
+ links << %(⌂ Home: <<#{home_page.id}>>) if home_page != parent_page
230
+ end
231
+ if page_index != pages.length-1
232
+ next_page = pages[page_index+1]
233
+ links << %(Next: <<#{next_page.id}>> →)
234
+ end
235
+ block = Asciidoctor::Block.new(parent = doc,
236
+ context = :paragraph,
237
+ opts = {:source => links.join(' | '),
238
+ :subs => :default})
239
+ page.nav_links = block.content
240
+ end
241
+ return
242
+ end
243
+
244
+ # Generate the actual HTML outline for the TOC. This method is analogous to
245
+ # Html5Converter outline().
246
+ def generate_outline(node, opts = {})
247
+ # This is the same as Html5Converter outline()
248
+ return unless node.sections?
249
+ sectnumlevels = opts[:sectnumlevels] || (node.document.attr 'sectnumlevels', 3).to_i
250
+ toclevels = opts[:toclevels] || (node.document.attr 'toclevels', 2).to_i
251
+ sections = node.sections
252
+ result = [%(<ul class="sectlevel#{sections[0].level}">)]
253
+ sections.each do |section|
254
+ slevel = section.level
255
+ if section.caption
256
+ stitle = section.captioned_title
257
+ elsif section.numbered && slevel <= sectnumlevels
258
+ stitle = %(#{section.sectnum} #{section.title})
259
+ else
260
+ stitle = section.title
261
+ end
262
+ stitle = stitle.gsub DropAnchorRx, '' if stitle.include? '<a'
263
+
264
+ # But add a special style for current page in TOC
265
+ if section.id == opts[:page_id]
266
+ stitle = %(<span class="toc-current">#{stitle}</span>)
267
+ end
268
+
269
+ # And we also need to find the parent page of the target node
270
+ current = section
271
+ until current.mplevel != :content
272
+ current = current.parent
273
+ end
274
+ parent_chapter = current
275
+
276
+ # If the target is the top-level section of the parent page, there is no
277
+ # need to include the anchor.
278
+ if parent_chapter.id == section.id
279
+ link = %(#{parent_chapter.id}.html)
280
+ else
281
+ link = %(#{parent_chapter.id}.html##{section.id})
282
+ end
283
+ result << %(<li><a href="#{link}">#{stitle}</a>)
284
+
285
+ # Finish in a manner similar to Html5Converter outline()
286
+ if slevel < toclevels &&
287
+ (child_toc_level = generate_outline section,
288
+ :toclevels => toclevels,
289
+ :secnumlevels => sectnumlevels,
290
+ :page_id => opts[:page_id])
291
+ result << child_toc_level
292
+ end
293
+ result << '</li>'
294
+ end
295
+ result << '</ul>'
296
+ result.join LF
297
+ end
298
+
299
+ # Include chapter pages in cross-reference links. This method overrides for
300
+ # the :xref node type only.
301
+ def inline_anchor(node)
302
+ if node.type == :xref
303
+ # This is the same as super...
304
+ if (path = node.attributes['path'])
305
+ attrs = (append_link_constraint_attrs node, node.role ? [%( class="#{node.role}")] : []).join
306
+ text = node.text || path
307
+ else
308
+ attrs = node.role ? %( class="#{node.role}") : ''
309
+ unless (text = node.text)
310
+ refid = node.attributes['refid']
311
+ if AbstractNode === (ref = (@refs ||= node.document.catalog[:refs])[refid])
312
+ text = (ref.xreftext node.attr('xrefstyle')) || %([#{refid}])
313
+ else
314
+ text = %([#{refid}])
315
+ end
316
+ end
317
+ end
318
+
319
+ # But we also need to find the parent page of the target node.
320
+ current = node.document.catalog[:refs][node.attributes['refid']]
321
+ until current.respond_to?(:mplevel) && current.mplevel != :content
322
+ return %(<a href="#{node.target}"#{attrs}>#{text}</a>) if !current
323
+ current = current.parent
324
+ end
325
+ parent_page = current
326
+
327
+ # If the target is the top-level section of the parent page, there is no
328
+ # need to include the anchor.
329
+ if "##{parent_page.id}" == node.target
330
+ target = "#{parent_page.id}.html"
331
+ else
332
+ target = "#{parent_page.id}.html#{node.target}"
333
+ end
334
+
335
+ %(<a href="#{target}"#{attrs}>#{text}</a>)
336
+ else
337
+ # Other anchor types can be handled as normal.
338
+ super
339
+ end
340
+ end
341
+
342
+ # From node, create a skeleton document that will be used to generate the
343
+ # TOC. This is first used to create a full skeleton (@@full_outline) from the
344
+ # original document (for_page=nil). Then it is used for each individual page
345
+ # to create a second skeleton from the first. In this way, TOC entries are
346
+ # included that are not part of the current page, or excluded if not
347
+ # applicable for the current page.
348
+ def new_outline_doc(node, new_parent:nil, for_page:nil)
349
+ if node.class == Document
350
+ new_document = Document.new([])
351
+ new_document.mplevel = node.mplevel
352
+ new_document.id = node.id
353
+ new_document.set_attr('sectnumlevels', node.attr(:sectnumlevels))
354
+ new_document.set_attr('toclevels', node.attr(:toclevels))
355
+ new_parent = new_document
356
+ node.sections.each do |section|
357
+ new_outline_doc(section, new_parent: new_parent,
358
+ for_page: for_page)
359
+ end
360
+ # Include the node if either (1) we are creating the full skeleton from the
361
+ # original document or (2) the node is applicable to the current page.
362
+ elsif !for_page ||
363
+ node.related_to?(for_page)
364
+ new_section = Section.new(parent = new_parent,
365
+ level = node.level,
366
+ numbered = node.numbered)
367
+ new_section.id = node.id
368
+ new_section.caption = node.caption
369
+ new_section.title = node.title
370
+ new_section.mplevel = node.mplevel
371
+ new_parent << new_section
372
+ new_parent.sections.last.number = node.number
373
+ new_parent = new_section
374
+ node.sections.each do |section|
375
+ new_outline_doc(section, new_parent: new_parent,
376
+ for_page: for_page)
377
+ end
378
+ end
379
+ return new_document
380
+ end
381
+
382
+ # Override Html5Converter outline() to return a custom TOC outline
383
+ def outline(node, opts = {})
384
+ doc = node.document
385
+ # Find this node in the @@full_outline skeleton document
386
+ page_node = @@full_outline.find_by(id: node.id).first
387
+ # Create a skeleton document for this particular page
388
+ custom_outline_doc = new_outline_doc(@@full_outline, for_page: page_node)
389
+ opts[:page_id] = node.id
390
+ # Generate an extra TOC entry for the root page. Add additional styling if
391
+ # the current page is the root page.
392
+ root_file = %(#{doc.attr('docname')}#{doc.attr('outfilesuffix')})
393
+ root_link = %(<a href="#{root_file}">#{doc.doctitle}</a>)
394
+ classes = ['toc-root']
395
+ classes << 'toc-current' if node.id == doc.attr('docname')
396
+ root = %(<span class="#{classes.join(' ')}">#{root_link}</span>)
397
+ # Create and return the HTML
398
+ %(<p>#{root}</p>#{generate_outline(custom_outline_doc, opts)})
399
+ end
400
+
401
+ # Change node parent to new parent recursively
402
+ def reparent(node, parent)
403
+ node.parent = parent
404
+ if node.context == :dlist
405
+ node.find_by(context: :list_item).each do |block|
406
+ reparent(block, node)
407
+ end
408
+ else
409
+ node.blocks.each do |block|
410
+ reparent(block, node)
411
+ if block.context == :table
412
+ block.columns.each do |col|
413
+ col.parent = col.parent
414
+ end
415
+ block.rows.body.each do |row|
416
+ row.each do |cell|
417
+ cell.parent = cell.parent
418
+ end
419
+ end
420
+ end
421
+ end
422
+ end
423
+ end
424
+
425
+ # Process a Section. Each Section will either be split off into its own page
426
+ # or processed as normal by Html5Converter.
427
+ def section(node)
428
+ doc = node.document
429
+ if doc.processed
430
+ # This node can now be handled by Html5Converter.
431
+ super
432
+ else
433
+ # This node is from the original document and has not yet been processed.
434
+
435
+ # Create a new page for this section
436
+ page = Asciidoctor::Document.new([],
437
+ {:attributes => doc.attributes.clone,
438
+ :doctype => doc.doctype,
439
+ :header_footer => !doc.attr?(:embedded),
440
+ :safe => doc.safe})
441
+ # Retain webfonts attribute (why is doc.attributes.clone not adequate?)
442
+ page.set_attr('webfonts', doc.attr(:webfonts))
443
+ # Save sectnum for use later (a Document object normally has no sectnum)
444
+ if node.parent.respond_to?(:numbered) && node.parent.numbered
445
+ page.sectnum = node.parent.sectnum
446
+ end
447
+
448
+ # Process node according to mplevel
449
+ if node.mplevel == :branch
450
+ # Retain any part intro blocks, delete others, and add a list
451
+ # of sections for the part landing page.
452
+ chapters_list = Asciidoctor::List.new(node, :ulist)
453
+ node.blocks.delete_if do |block|
454
+ if block.context == :section
455
+ chapter = block
456
+ chapter.convert
457
+ text = %(<<#{chapter.id},#{chapter.captioned_title}>>)
458
+ # NOTE, there is a non-breaking space (Unicode U+00A0) below.
459
+ if desc = block.attr('desc') then text << %( – #{desc}) end
460
+ chapters_list << Asciidoctor::ListItem.new(chapters_list, text)
461
+ true
462
+ end
463
+ end
464
+ # Add chapters list to node, reparent node to new page, add
465
+ # node to page, mark as processed, and add page to @pages.
466
+ node << chapters_list
467
+ reparent(node, page)
468
+ page.blocks << node
469
+ else # :leaf
470
+ # Reparent node to new page, add node to page, mark as
471
+ # processed, and add page to @pages.
472
+ reparent(node, page)
473
+ page.blocks << node
474
+ end
475
+
476
+ # Add navigation links using saved HTML
477
+ page.nav_links = node.nav_links
478
+ add_nav_links(page)
479
+
480
+ # Mark page as processed and add to collection of pages
481
+ @pages << page
482
+ page.id = node.id
483
+ page.catalog = @catalog
484
+ page.mplevel = node.mplevel
485
+ page.processed = true
486
+ end
487
+ end
488
+
489
+ # Add multipage attribute to all sections in node, recursively.
490
+ def set_multipage_attrs(node)
491
+ doc = node.document
492
+ node.mplevel = :root if node.class == Asciidoctor::Document
493
+ node.sections.each do |section|
494
+ # Check custom multipage-level attribute on section
495
+ if section.attr?('multipage-level', nil, false) &&
496
+ section.attr('multipage-level').to_i <
497
+ node.attr('multipage-level').to_i
498
+ logger.warn %(multipage-level value specified for "#{section.id}" ) +
499
+ %(section cannot be less than the parent section value)
500
+ section.set_attr('multipage-level', nil)
501
+ end
502
+ # Propogate custom multipage-level value to child node
503
+ if !section.attr?('multipage-level', nil, false) &&
504
+ node.attr('multipage-level') != doc.attr('multipage-level')
505
+ section.set_attr('multipage-level', node.attr('multipage-level'))
506
+ end
507
+ # Set section type
508
+ if section.level < section.attr('multipage-level').to_i
509
+ section.mplevel = :branch
510
+ elsif section.level == section.attr('multipage-level').to_i
511
+ section.mplevel = :leaf
512
+ else
513
+ section.mplevel = :content
514
+ end
515
+ # Set multipage attribute on child sections now.
516
+ set_multipage_attrs(section)
517
+ end
518
+ end
519
+
520
+ # Convert each page and write it to file. Use filenames based on IDs.
521
+ def write(output, target)
522
+ # Write primary (book) landing page
523
+ ::File.open(target, 'w') do |f|
524
+ f.write(output)
525
+ end
526
+ # Write remaining part/chapter pages
527
+ outdir = ::File.dirname(target)
528
+ ext = ::File.extname(target)
529
+ target_name = ::File.basename(target, ext)
530
+ @pages.each do |doc|
531
+ chapter_target = doc.id + ext
532
+ outfile = ::File.join(outdir, chapter_target)
533
+ ::File.open(outfile, 'w') do |f|
534
+ f.write(doc.convert)
535
+ end
536
+ end
537
+ end
538
+ end
539
+
540
+ class MultipageHtml5CSS < Asciidoctor::Extensions::DocinfoProcessor
541
+ use_dsl
542
+ at_location :head
543
+
544
+ def process doc
545
+ css = []
546
+ # Style Table Of Contents entry for current page
547
+ css << %(.toc-current{font-weight: bold;})
548
+ # Style Table Of Contents entry for root page
549
+ css << %(.toc-root{font-family: "Open Sans","DejaVu Sans",sans-serif;
550
+ font-size: 0.9em;})
551
+ # Style navigation bar at bottom of each page
552
+ css << %(#content{display: flex; flex-direction: column; flex: 1 1 auto;}
553
+ .nav-footer{text-align: center; margin-top: auto;}
554
+ .nav-footer > p > a {white-space: nowrap;})
555
+ %(<style>#{css.join(' ')}</style>)
556
+ end
557
+ end
558
+
559
+ Asciidoctor::Extensions.register do
560
+ docinfo_processor MultipageHtml5CSS
561
+ end
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: asciidoctor-multipage
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Owen T. Heisler
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-01-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '5'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '13'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '13'
55
+ - !ruby/object:Gem::Dependency
56
+ name: asciidoctor
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 1.5.7.1
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 1.5.7.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: thread_safe
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">"
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">"
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: An Asciidoctor extension that generates HTML output using multiple pages
84
+ email:
85
+ - writer@owenh.net
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - lib/asciidoctor-multipage.rb
91
+ homepage: https://github.com/owenh000/asciidoctor-multipage
92
+ licenses:
93
+ - MIT
94
+ metadata:
95
+ bug_tracker_uri: https://github.com/owenh000/asciidoctor-multipage/issues
96
+ homepage_uri: https://github.com/owenh000/asciidoctor-multipage
97
+ source_code_uri: https://github.com/owenh000/asciidoctor-multipage
98
+ post_install_message:
99
+ rdoc_options: []
100
+ require_paths:
101
+ - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '2.5'
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ requirements: []
113
+ rubyforge_project:
114
+ rubygems_version: 2.7.6.2
115
+ signing_key:
116
+ specification_version: 4
117
+ summary: Asciidoctor multipage HTML output extension
118
+ test_files: []