kramdown-converter-pdf 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5b24dbe64c979804933619756ff3c956eae922cb53aa00a84d974a2f1e51fe4c
4
+ data.tar.gz: 1f73a92236f30de69bab315ddc02e193b3ea76f494adbfff4e6cb76341f53de0
5
+ SHA512:
6
+ metadata.gz: e18ca1234a2c8aeb1362808bbb51734a259831ebecf932217f97250628d29294849c4ed8d4e72bee6b70d6cac67b5976d8f896b8fec25714f7392998fe5a0452
7
+ data.tar.gz: 10008adc53eb0e1e99c76e6215a30726659433612761bda35e359ddf94061cd2349fbd8133b9aa44f6874002cc337abbef3cf0792b9d13956fe72fb2f7dc6fbf
data/CONTRIBUTERS ADDED
@@ -0,0 +1,3 @@
1
+ Count Name
2
+ ======= ====
3
+ 3 Thomas Leitner <t_leitner@gmx.at>
data/COPYING ADDED
@@ -0,0 +1,30 @@
1
+ kramdown - fast, pure-Ruby Markdown-superset converter
2
+ Copyright (C) 2009-2013 Thomas Leitner <t_leitner@gmx.at>
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a
5
+ copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included
13
+ in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+
24
+ Some test cases and the benchmark files are based on test cases from
25
+ the MDTest test suite:
26
+
27
+ MDTest
28
+ Copyright (c) 2007 Michel Fortin
29
+ <http://www.michelf.com/>
30
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.1
@@ -0,0 +1,614 @@
1
+ # -*- coding: utf-8; frozen_string_literal: true -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2009-2019 Thomas Leitner <t_leitner@gmx.at>
5
+ #
6
+ # This file is part of kramdown which is licensed under the MIT.
7
+ #++
8
+ #
9
+
10
+ require 'prawn'
11
+ require 'prawn/table'
12
+ require 'kramdown/converter/base'
13
+ require 'kramdown/utils'
14
+ require 'open-uri'
15
+
16
+ module Kramdown
17
+
18
+ module Converter
19
+
20
+ # Converts an element tree to a PDF using the prawn PDF library.
21
+ #
22
+ # This basic version provides a nice starting point for customizations but can also be used
23
+ # directly.
24
+ #
25
+ # There can be the following two methods for each element type: render_TYPE(el, opts) and
26
+ # TYPE_options(el, opts) where +el+ is a kramdown element and +opts+ an hash with rendering
27
+ # options.
28
+ #
29
+ # The render_TYPE(el, opts) is used for rendering the specific element. If the element is a span
30
+ # element, it should return a hash or an array of hashes that can be used by the #formatted_text
31
+ # method of Prawn::Document. This method can then be used in block elements to actually render
32
+ # the span elements.
33
+ #
34
+ # The rendering options are passed from the parent to its child elements. This allows one to
35
+ # define general options at the top of the tree (the root element) that can later be changed or
36
+ # amended.
37
+ #
38
+ #
39
+ # Currently supports the conversion of all elements except those of the following types:
40
+ #
41
+ # :html_element, :img, :footnote
42
+ #
43
+ #
44
+ class Pdf < Base
45
+
46
+ VERSION = '1.0.1'
47
+
48
+ include Prawn::Measurements
49
+
50
+ def initialize(root, options)
51
+ super
52
+ @stack = []
53
+ @dests = {}
54
+ end
55
+
56
+ # PDF templates are applied before conversion. They should contain code to augment the
57
+ # converter object (i.e. to override the methods).
58
+ def apply_template_before?
59
+ true
60
+ end
61
+
62
+ # Returns +false+.
63
+ def apply_template_after?
64
+ false
65
+ end
66
+
67
+ DISPATCHER_RENDER = Hash.new {|h, k| h[k] = "render_#{k}" } #:nodoc:
68
+ DISPATCHER_OPTIONS = Hash.new {|h, k| h[k] = "#{k}_options" } #:nodoc:
69
+
70
+ # Invoke the special rendering method for the given element +el+.
71
+ #
72
+ # A PDF destination is also added at the current location if th element has an ID or if the
73
+ # element is of type :header and the :auto_ids option is set.
74
+ def convert(el, opts = {})
75
+ id = el.attr['id']
76
+ id = generate_id(el.options[:raw_text]) if !id && @options[:auto_ids] && el.type == :header
77
+ if !id.to_s.empty? && !@dests.key?(id)
78
+ @pdf.add_dest(id, @pdf.dest_xyz(0, @pdf.y))
79
+ @dests[id] = @pdf.dest_xyz(0, @pdf.y)
80
+ end
81
+ send(DISPATCHER_RENDER[el.type], el, opts)
82
+ end
83
+
84
+ protected
85
+
86
+ # Render the children of this element with the given options and return the results as array.
87
+ #
88
+ # Each time a child is rendered, the +TYPE_options+ method is invoked (if it exists) to get
89
+ # the specific options for the element with which the given options are updated.
90
+ def inner(el, opts)
91
+ @stack.push([el, opts])
92
+ result = el.children.map do |inner_el|
93
+ options = opts.dup
94
+ options.update(send(DISPATCHER_OPTIONS[inner_el.type], inner_el, options))
95
+ convert(inner_el, options)
96
+ end.flatten.compact
97
+ @stack.pop
98
+ result
99
+ end
100
+
101
+ # ----------------------------
102
+ # :section: Element rendering methods
103
+ # ----------------------------
104
+
105
+ def root_options(_root, _opts)
106
+ {font: 'Times-Roman', size: 12, leading: 2}
107
+ end
108
+
109
+ def render_root(root, opts)
110
+ @pdf = setup_document(root)
111
+ inner(root, root_options(root, opts))
112
+ create_outline(root)
113
+ finish_document(root)
114
+ @pdf.render
115
+ end
116
+
117
+ def header_options(el, opts)
118
+ size = opts[:size] * 1.15**(6 - el.options[:level])
119
+ {
120
+ font: "Helvetica", styles: (opts[:styles] || []) + [:bold],
121
+ size: size, bottom_padding: opts[:size], top_padding: opts[:size]
122
+ }
123
+ end
124
+
125
+ def render_header(el, opts)
126
+ render_padded_and_formatted_text(el, opts)
127
+ end
128
+
129
+ def p_options(el, opts)
130
+ bpad = (el.options[:transparent] ? opts[:leading] : opts[:size])
131
+ {align: :justify, bottom_padding: bpad}
132
+ end
133
+
134
+ def render_p(el, opts)
135
+ if el.children.size == 1 && el.children.first.type == :img
136
+ render_standalone_image(el, opts)
137
+ else
138
+ render_padded_and_formatted_text(el, opts)
139
+ end
140
+ end
141
+
142
+ def render_standalone_image(el, opts)
143
+ img = el.children.first
144
+ line = img.options[:location]
145
+
146
+ if img.attr['src'].empty?
147
+ warning("Rendering an image without a source is not possible#{line ? " (line #{line})" : ''}")
148
+ return nil
149
+ elsif img.attr['src'] !~ /\.jpe?g$|\.png$/
150
+ warning("Cannot render images other than JPEG or PNG, " \
151
+ "got #{img.attr['src']}#{line ? " on line #{line}" : ''}")
152
+ return nil
153
+ end
154
+
155
+ img_dirs = (@options[:image_directories] || ['.']).dup
156
+ begin
157
+ img_path = File.join(img_dirs.shift, img.attr['src'])
158
+ image_obj, image_info = @pdf.build_image_object(open(img_path))
159
+ rescue StandardError
160
+ img_dirs.empty? ? raise : retry
161
+ end
162
+
163
+ options = {position: :center}
164
+ if img.attr['height'] && img.attr['height'] =~ /px$/
165
+ options[:height] = img.attr['height'].to_i / (@options[:image_dpi] || 150.0) * 72
166
+ elsif img.attr['width'] && img.attr['width'] =~ /px$/
167
+ options[:width] = img.attr['width'].to_i / (@options[:image_dpi] || 150.0) * 72
168
+ else
169
+ options[:scale] = [(@pdf.bounds.width - mm2pt(20)) / image_info.width.to_f, 1].min
170
+ end
171
+
172
+ if img.attr['class'] =~ /\bright\b/
173
+ options[:position] = :right
174
+ @pdf.float { @pdf.embed_image(image_obj, image_info, options) }
175
+ else
176
+ with_block_padding(el, opts) do
177
+ @pdf.embed_image(image_obj, image_info, options)
178
+ end
179
+ end
180
+ end
181
+
182
+ def blockquote_options(_el, _opts)
183
+ {styles: [:italic]}
184
+ end
185
+
186
+ def render_blockquote(el, opts)
187
+ @pdf.indent(mm2pt(10), mm2pt(10)) { inner(el, opts) }
188
+ end
189
+
190
+ def ul_options(_el, opts)
191
+ {bottom_padding: opts[:size]}
192
+ end
193
+
194
+ def render_ul(el, opts)
195
+ with_block_padding(el, opts) do
196
+ el.children.each do |li|
197
+ @pdf.float { @pdf.formatted_text([text_hash("•", opts)]) }
198
+ @pdf.indent(mm2pt(6)) { convert(li, opts) }
199
+ end
200
+ end
201
+ end
202
+
203
+ def ol_options(_el, opts)
204
+ {bottom_padding: opts[:size]}
205
+ end
206
+
207
+ def render_ol(el, opts)
208
+ with_block_padding(el, opts) do
209
+ el.children.each_with_index do |li, index|
210
+ @pdf.float { @pdf.formatted_text([text_hash("#{index + 1}.", opts)]) }
211
+ @pdf.indent(mm2pt(6)) { convert(li, opts) }
212
+ end
213
+ end
214
+ end
215
+
216
+ def li_options(_el, _opts)
217
+ {}
218
+ end
219
+
220
+ def render_li(el, opts)
221
+ inner(el, opts)
222
+ end
223
+
224
+ def dl_options(_el, _opts)
225
+ {}
226
+ end
227
+
228
+ def render_dl(el, opts)
229
+ inner(el, opts)
230
+ end
231
+
232
+ def dt_options(_el, opts)
233
+ {styles: (opts[:styles] || []) + [:bold], bottom_padding: 0}
234
+ end
235
+
236
+ def render_dt(el, opts)
237
+ render_padded_and_formatted_text(el, opts)
238
+ end
239
+
240
+ def dd_options(_el, _opts)
241
+ {}
242
+ end
243
+
244
+ def render_dd(el, opts)
245
+ @pdf.indent(mm2pt(10)) { inner(el, opts) }
246
+ end
247
+
248
+ def math_options(_el, _opts)
249
+ {}
250
+ end
251
+
252
+ def render_math(el, opts)
253
+ if el.options[:category] == :block
254
+ @pdf.formatted_text([{text: el.value}], block_hash(opts))
255
+ else
256
+ {text: el.value}
257
+ end
258
+ end
259
+
260
+ def hr_options(_el, opts)
261
+ {top_padding: opts[:size], bottom_padding: opts[:size]}
262
+ end
263
+
264
+ def render_hr(el, opts)
265
+ with_block_padding(el, opts) do
266
+ @pdf.stroke_horizontal_line(@pdf.bounds.left + mm2pt(5), @pdf.bounds.right - mm2pt(5))
267
+ end
268
+ end
269
+
270
+ def codeblock_options(_el, opts)
271
+ {font: 'Courier', color: '880000', bottom_padding: opts[:size]}
272
+ end
273
+
274
+ def render_codeblock(el, opts)
275
+ with_block_padding(el, opts) do
276
+ @pdf.formatted_text([text_hash(el.value, opts, false)], block_hash(opts))
277
+ end
278
+ end
279
+
280
+ def table_options(_el, opts)
281
+ {bottom_padding: opts[:size]}
282
+ end
283
+
284
+ def render_table(el, opts)
285
+ data = []
286
+ el.children.each do |container|
287
+ container.children.each do |row|
288
+ data << []
289
+ row.children.each do |cell|
290
+ if cell.children.any? {|child| child.options[:category] == :block }
291
+ line = el.options[:location]
292
+ warning("Can't render tables with cells containing block " \
293
+ "elements#{line ? " (line #{line})" : ''}")
294
+ return
295
+ end
296
+ cell_data = inner(cell, opts)
297
+ data.last << cell_data.map {|c| c[:text] }.join('')
298
+ end
299
+ end
300
+ end
301
+ with_block_padding(el, opts) do
302
+ @pdf.table(data, width: @pdf.bounds.right) do
303
+ el.options[:alignment].each_with_index do |alignment, index|
304
+ columns(index).align = alignment unless alignment == :default
305
+ end
306
+ end
307
+ end
308
+ end
309
+
310
+ def text_options(_el, _opts)
311
+ {}
312
+ end
313
+
314
+ def render_text(el, opts)
315
+ text_hash(el.value.to_s, opts)
316
+ end
317
+
318
+ def em_options(_el, opts)
319
+ if opts[:styles]&.include?(:italic)
320
+ {styles: opts[:styles].reject {|i| i == :italic }}
321
+ else
322
+ {styles: (opts[:styles] || []) << :italic}
323
+ end
324
+ end
325
+
326
+ def strong_options(_el, opts)
327
+ {styles: (opts[:styles] || []) + [:bold]}
328
+ end
329
+
330
+ def a_options(el, _opts)
331
+ hash = {color: '000088'}
332
+ if el.attr['href'].start_with?('#')
333
+ hash[:anchor] = el.attr['href'].sub(/\A#/, '')
334
+ else
335
+ hash[:link] = el.attr['href']
336
+ end
337
+ hash
338
+ end
339
+
340
+ def render_em(el, opts)
341
+ inner(el, opts)
342
+ end
343
+ alias render_strong render_em
344
+ alias render_a render_em
345
+
346
+ def codespan_options(_el, _opts)
347
+ {font: 'Courier', color: '880000'}
348
+ end
349
+
350
+ def render_codespan(el, opts)
351
+ text_hash(el.value, opts)
352
+ end
353
+
354
+ def br_options(_el, _opts)
355
+ {}
356
+ end
357
+
358
+ def render_br(_el, opts)
359
+ text_hash("\n", opts, false)
360
+ end
361
+
362
+ def smart_quote_options(_el, _opts)
363
+ {}
364
+ end
365
+
366
+ def render_smart_quote(el, opts)
367
+ text_hash(smart_quote_entity(el).char, opts)
368
+ end
369
+
370
+ def typographic_sym_options(_el, _opts)
371
+ {}
372
+ end
373
+
374
+ def render_typographic_sym(el, opts)
375
+ str = if el.value == :laquo_space
376
+ ::Kramdown::Utils::Entities.entity('laquo').char +
377
+ ::Kramdown::Utils::Entities.entity('nbsp').char
378
+ elsif el.value == :raquo_space
379
+ ::Kramdown::Utils::Entities.entity('raquo').char +
380
+ ::Kramdown::Utils::Entities.entity('nbsp').char
381
+ else
382
+ ::Kramdown::Utils::Entities.entity(el.value.to_s).char
383
+ end
384
+ text_hash(str, opts)
385
+ end
386
+
387
+ def entity_options(_el, _opts)
388
+ {}
389
+ end
390
+
391
+ def render_entity(el, opts)
392
+ text_hash(el.value.char, opts)
393
+ end
394
+
395
+ def abbreviation_options(_el, _opts)
396
+ {}
397
+ end
398
+
399
+ def render_abbreviation(el, opts)
400
+ text_hash(el.value, opts)
401
+ end
402
+
403
+ def img_options(_el, _opts)
404
+ {}
405
+ end
406
+
407
+ def render_img(el, *) #:nodoc:
408
+ line = el.options[:location]
409
+ warning("Rendering span images is not supported for PDF converter#{line ? " (line #{line})" : ''}")
410
+ nil
411
+ end
412
+
413
+ def xml_comment_options(_el, _opts) #:nodoc:
414
+ {}
415
+ end
416
+ alias xml_pi_options xml_comment_options
417
+ alias comment_options xml_comment_options
418
+ alias blank_options xml_comment_options
419
+ alias footnote_options xml_comment_options
420
+ alias raw_options xml_comment_options
421
+ alias html_element_options xml_comment_options
422
+
423
+ def render_xml_comment(el, opts) #:nodoc:
424
+ # noop
425
+ end
426
+ alias render_xml_pi render_xml_comment
427
+ alias render_comment render_xml_comment
428
+ alias render_blank render_xml_comment
429
+
430
+ def render_footnote(el, *) #:nodoc:
431
+ line = el.options[:location]
432
+ warning("Rendering #{el.type} not supported for PDF converter#{line ? " (line #{line})" : ''}")
433
+ nil
434
+ end
435
+ alias render_raw render_footnote
436
+ alias render_html_element render_footnote
437
+
438
+ # ----------------------------
439
+ # :section: Organizational methods
440
+ #
441
+ # These methods are used, for example, to up the needed Prawn::Document instance or to create
442
+ # a PDF outline.
443
+ # ----------------------------
444
+
445
+ # This module gets mixed into the Prawn::Document instance.
446
+ module PrawnDocumentExtension
447
+
448
+ # Extension for the formatted box class to recognize images and move text around them.
449
+ module CustomBox
450
+
451
+ def available_width
452
+ return super unless @document.respond_to?(:converter) && @document.converter
453
+
454
+ @document.image_floats.each do |pn, _x, y, w, h|
455
+ next if @document.page_number != pn
456
+ if @at[1] + @baseline_y <= y - @document.bounds.absolute_bottom &&
457
+ (@at[1] + @baseline_y + @arranger.max_line_height +
458
+ @leading >= y - h - @document.bounds.absolute_bottom)
459
+ return @width - w
460
+ end
461
+ end
462
+
463
+ super
464
+ end
465
+
466
+ end
467
+
468
+ Prawn::Text::Formatted::Box.extensions << CustomBox
469
+
470
+ # Access the converter instance from within Prawn
471
+ attr_accessor :converter
472
+
473
+ def image_floats
474
+ @image_floats ||= []
475
+ end
476
+
477
+ # Override image embedding method for adding image positions to #image_floats.
478
+ def embed_image(pdf_obj, info, options)
479
+ # find where the image will be placed and how big it will be
480
+ w, h = info.calc_image_dimensions(options)
481
+
482
+ if options[:at]
483
+ x, y = map_to_absolute(options[:at])
484
+ else
485
+ x, y = image_position(w, h, options)
486
+ move_text_position h
487
+ end
488
+
489
+ #--> This part is new
490
+ if options[:position] == :right
491
+ image_floats << [page_number, x - 15, y, w + 15, h + 15]
492
+ end
493
+
494
+ # add a reference to the image object to the current page
495
+ # resource list and give it a label
496
+ label = "I#{next_image_id}"
497
+ state.page.xobjects[label] = pdf_obj
498
+
499
+ # add the image to the current page
500
+ instruct = "\nq\n%.3f 0 0 %.3f %.3f %.3f cm\n/%s Do\nQ"
501
+ add_content(sprintf(instruct, w, h, x, y - h, label))
502
+ end
503
+
504
+ end
505
+
506
+ # Return a hash with options that are suitable for Prawn::Document.new.
507
+ #
508
+ # Used in #setup_document.
509
+ def document_options(_root)
510
+ {
511
+ page_size: 'A4', page_layout: :portrait, margin: mm2pt(20),
512
+ info: {
513
+ Creator: 'kramdown PDF converter',
514
+ CreationDate: Time.now,
515
+ },
516
+ compress: true, optimize_objects: true
517
+ }
518
+ end
519
+
520
+ # Create a Prawn::Document object and return it.
521
+ #
522
+ # Can be used to define repeatable content or register fonts.
523
+ #
524
+ # Used in #render_root.
525
+ def setup_document(root)
526
+ doc = Prawn::Document.new(document_options(root))
527
+ doc.extend(PrawnDocumentExtension)
528
+ doc.converter = self
529
+ doc
530
+ end
531
+
532
+ # Used in #render_root.
533
+ def finish_document(root)
534
+ # no op
535
+ end
536
+
537
+ # Create the PDF outline from the header elements in the TOC.
538
+ def create_outline(root)
539
+ toc = ::Kramdown::Converter::Toc.convert(root).first
540
+
541
+ text_of_header = lambda do |el|
542
+ if el.type == :text
543
+ el.value
544
+ else
545
+ el.children.map {|c| text_of_header.call(c) }.join('')
546
+ end
547
+ end
548
+
549
+ add_section = lambda do |item, parent|
550
+ text = text_of_header.call(item.value)
551
+ destination = @dests[item.attr[:id]]
552
+ if !parent
553
+ @pdf.outline.page(title: text, destination: destination)
554
+ else
555
+ @pdf.outline.add_subsection_to(parent) do
556
+ @pdf.outline.page(title: text, destination: destination)
557
+ end
558
+ end
559
+ item.children.each {|c| add_section.call(c, text) }
560
+ end
561
+
562
+ toc.children.each do |item|
563
+ add_section.call(item, nil)
564
+ end
565
+ end
566
+
567
+ # ----------------------------
568
+ # :section: Helper methods
569
+ # ----------------------------
570
+
571
+ # Move the prawn document cursor down before and/or after yielding the given block.
572
+ #
573
+ # The :top_padding and :bottom_padding options are used for determinig the padding amount.
574
+ def with_block_padding(_el, opts)
575
+ @pdf.move_down(opts[:top_padding]) if opts.key?(:top_padding)
576
+ yield
577
+ @pdf.move_down(opts[:bottom_padding]) if opts.key?(:bottom_padding)
578
+ end
579
+
580
+ # Render the children of the given element as formatted text and respect the top/bottom
581
+ # padding (see #with_block_padding).
582
+ def render_padded_and_formatted_text(el, opts)
583
+ with_block_padding(el, opts) { @pdf.formatted_text(inner(el, opts), block_hash(opts)) }
584
+ end
585
+
586
+ # Helper function that returns a hash with valid "formatted text" options.
587
+ #
588
+ # The +text+ parameter is used as value for the :text key and if +squeeze_whitespace+ is
589
+ # +true+, all whitespace is converted into spaces.
590
+ def text_hash(text, opts, squeeze_whitespace = true)
591
+ text = text.gsub(/\s+/, ' ') if squeeze_whitespace
592
+ hash = {text: text}
593
+ [:styles, :size, :character_spacing, :font, :color, :link,
594
+ :anchor, :draw_text_callback, :callback].each do |key|
595
+ hash[key] = opts[key] if opts.key?(key)
596
+ end
597
+ hash
598
+ end
599
+
600
+ # Helper function that returns a hash with valid options for the prawn #text_box extracted
601
+ # from the given options.
602
+ def block_hash(opts)
603
+ hash = {}
604
+ [:align, :valign, :mode, :final_gap, :leading, :fallback_fonts,
605
+ :direction, :indent_paragraphs].each do |key|
606
+ hash[key] = opts[key] if opts.key?(key)
607
+ end
608
+ hash
609
+ end
610
+
611
+ end
612
+
613
+ end
614
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kramdown-converter-pdf
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Thomas Leitner
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-01-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: kramdown
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: prawn
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: prawn-table
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.2.2
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.2.2
55
+ description:
56
+ email: t_leitner@gmx.at
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - CONTRIBUTERS
62
+ - COPYING
63
+ - VERSION
64
+ - lib/kramdown/converter/pdf.rb
65
+ homepage: https://github.com/kramdown/converter-pdf
66
+ licenses:
67
+ - MIT
68
+ metadata: {}
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '2.3'
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ requirements: []
84
+ rubyforge_project:
85
+ rubygems_version: 2.7.3
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: kramdown-converter-pdf uses Prawn to convert a kramdown document to PDF
89
+ test_files: []