asciidoctor-multipage 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []