motion-kramdown 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +84 -0
  3. data/lib/kramdown/compatibility.rb +36 -0
  4. data/lib/kramdown/converter/base.rb +259 -0
  5. data/lib/kramdown/converter/html.rb +461 -0
  6. data/lib/kramdown/converter/kramdown.rb +423 -0
  7. data/lib/kramdown/converter/latex.rb +600 -0
  8. data/lib/kramdown/converter/math_engine/itex2mml.rb +39 -0
  9. data/lib/kramdown/converter/math_engine/mathjax.rb +33 -0
  10. data/lib/kramdown/converter/math_engine/ritex.rb +38 -0
  11. data/lib/kramdown/converter/pdf.rb +624 -0
  12. data/lib/kramdown/converter/remove_html_tags.rb +53 -0
  13. data/lib/kramdown/converter/syntax_highlighter/coderay.rb +78 -0
  14. data/lib/kramdown/converter/syntax_highlighter/rouge.rb +37 -0
  15. data/lib/kramdown/converter/toc.rb +69 -0
  16. data/lib/kramdown/converter.rb +69 -0
  17. data/lib/kramdown/document.rb +144 -0
  18. data/lib/kramdown/element.rb +515 -0
  19. data/lib/kramdown/error.rb +17 -0
  20. data/lib/kramdown/options.rb +584 -0
  21. data/lib/kramdown/parser/base.rb +130 -0
  22. data/lib/kramdown/parser/gfm.rb +55 -0
  23. data/lib/kramdown/parser/html.rb +575 -0
  24. data/lib/kramdown/parser/kramdown/abbreviation.rb +67 -0
  25. data/lib/kramdown/parser/kramdown/autolink.rb +37 -0
  26. data/lib/kramdown/parser/kramdown/blank_line.rb +30 -0
  27. data/lib/kramdown/parser/kramdown/block_boundary.rb +33 -0
  28. data/lib/kramdown/parser/kramdown/blockquote.rb +39 -0
  29. data/lib/kramdown/parser/kramdown/codeblock.rb +56 -0
  30. data/lib/kramdown/parser/kramdown/codespan.rb +44 -0
  31. data/lib/kramdown/parser/kramdown/emphasis.rb +61 -0
  32. data/lib/kramdown/parser/kramdown/eob.rb +26 -0
  33. data/lib/kramdown/parser/kramdown/escaped_chars.rb +25 -0
  34. data/lib/kramdown/parser/kramdown/extensions.rb +201 -0
  35. data/lib/kramdown/parser/kramdown/footnote.rb +56 -0
  36. data/lib/kramdown/parser/kramdown/header.rb +59 -0
  37. data/lib/kramdown/parser/kramdown/horizontal_rule.rb +27 -0
  38. data/lib/kramdown/parser/kramdown/html.rb +160 -0
  39. data/lib/kramdown/parser/kramdown/html_entity.rb +33 -0
  40. data/lib/kramdown/parser/kramdown/line_break.rb +25 -0
  41. data/lib/kramdown/parser/kramdown/link.rb +139 -0
  42. data/lib/kramdown/parser/kramdown/list.rb +256 -0
  43. data/lib/kramdown/parser/kramdown/math.rb +54 -0
  44. data/lib/kramdown/parser/kramdown/paragraph.rb +54 -0
  45. data/lib/kramdown/parser/kramdown/smart_quotes.rb +174 -0
  46. data/lib/kramdown/parser/kramdown/table.rb +171 -0
  47. data/lib/kramdown/parser/kramdown/typographic_symbol.rb +44 -0
  48. data/lib/kramdown/parser/kramdown.rb +359 -0
  49. data/lib/kramdown/parser/markdown.rb +56 -0
  50. data/lib/kramdown/parser.rb +27 -0
  51. data/lib/kramdown/utils/configurable.rb +44 -0
  52. data/lib/kramdown/utils/entities.rb +347 -0
  53. data/lib/kramdown/utils/html.rb +75 -0
  54. data/lib/kramdown/utils/ordered_hash.rb +87 -0
  55. data/lib/kramdown/utils/string_scanner.rb +74 -0
  56. data/lib/kramdown/utils/unidecoder.rb +51 -0
  57. data/lib/kramdown/utils.rb +58 -0
  58. data/lib/kramdown/version.rb +15 -0
  59. data/lib/kramdown.rb +10 -0
  60. data/lib/motion-kramdown.rb +47 -0
  61. data/lib/rubymotion/encodings.rb +37 -0
  62. data/lib/rubymotion/rexml_shim.rb +25 -0
  63. data/lib/rubymotion/set.rb +1349 -0
  64. data/lib/rubymotion/version.rb +6 -0
  65. data/spec/document_tree.rb +48 -0
  66. data/spec/gfm_to_html.rb +95 -0
  67. data/spec/helpers/it_behaves_like.rb +27 -0
  68. data/spec/helpers/option_file.rb +46 -0
  69. data/spec/helpers/spec_options.rb +37 -0
  70. data/spec/helpers/tidy.rb +12 -0
  71. data/spec/html_to_html.rb +40 -0
  72. data/spec/html_to_kramdown_to_html.rb +46 -0
  73. data/spec/kramdown_to_xxx.rb +40 -0
  74. data/spec/test_location.rb +203 -0
  75. data/spec/test_string_scanner_kramdown.rb +19 -0
  76. data/spec/text_to_kramdown_to_html.rb +52 -0
  77. data/spec/text_to_latex.rb +33 -0
  78. metadata +164 -0
@@ -0,0 +1,38 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2009-2014 Thomas Leitner <t_leitner@gmx.at>
5
+ #
6
+ # This file is part of kramdown which is licensed under the MIT.
7
+ #++
8
+ #
9
+
10
+ module Kramdown::Converter::MathEngine
11
+
12
+ # Uses the Ritex library for converting math formulas to MathML.
13
+ module Ritex
14
+
15
+ begin
16
+ # RM require 'ritex'
17
+
18
+ # Ritex is available if this constant is +true+.
19
+ AVAILABLE = true
20
+ rescue LoadError
21
+ AVAILABLE = false # :nodoc:
22
+ end
23
+
24
+ def self.call(converter, el, opts)
25
+ type = el.options[:category]
26
+ result = ::Ritex::Parser.new.parse(el.value, :display => (type == :block))
27
+
28
+ attr = el.attr.dup
29
+ attr.delete('xmlns')
30
+ attr.delete('display')
31
+ result.insert("<math".length, converter.html_attributes(attr))
32
+
33
+ (type == :block ? "#{' '*opts[:indent]}#{result}\n" : result)
34
+ end
35
+
36
+ end
37
+
38
+ end
@@ -0,0 +1,624 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2009-2014 Thomas Leitner <t_leitner@gmx.at>
5
+ #
6
+ # This file is part of kramdown which is licensed under the MIT.
7
+ #++
8
+ #
9
+
10
+ # RM require 'prawn'
11
+ # RM require 'prawn/table'
12
+ # RM require 'kramdown/utils/entities'
13
+ # RM require 'open-uri'
14
+
15
+ module Kramdown
16
+
17
+ module Converter
18
+
19
+ # Converts an element tree to a PDF using the prawn PDF library.
20
+ #
21
+ # This basic version provides a nice starting point for customizations but can also be used
22
+ # directly.
23
+ #
24
+ # There can be the following two methods for each element type: render_TYPE(el, opts) and
25
+ # TYPE_options(el, opts) where +el+ is a kramdown element and +opts+ an hash with rendering
26
+ # options.
27
+ #
28
+ # The render_TYPE(el, opts) is used for rendering the specific element. If the element is a span
29
+ # element, it should return a hash or an array of hashes that can be used by the #formatted_text
30
+ # method of Prawn::Document. This method can then be used in block elements to actually render
31
+ # the span elements.
32
+ #
33
+ # The rendering options are passed from the parent to its child elements. This allows one to
34
+ # define general options at the top of the tree (the root element) that can later be changed or
35
+ # amended.
36
+ #
37
+ #
38
+ # Currently supports the conversion of all elements except those of the following types:
39
+ #
40
+ # :html_element, :img, :footnote
41
+ #
42
+ #
43
+ class Pdf < Base
44
+
45
+ include Prawn::Measurements
46
+
47
+ def initialize(root, options)
48
+ super
49
+ @stack = []
50
+ @dests = {}
51
+ end
52
+
53
+ # PDF templates are applied before conversion. They should contain code to augment the
54
+ # converter object (i.e. to override the methods).
55
+ def apply_template_before?
56
+ true
57
+ end
58
+
59
+ # Returns +false+.
60
+ def apply_template_after?
61
+ false
62
+ end
63
+
64
+ DISPATCHER_RENDER = Hash.new {|h,k| h[k] = "render_#{k}"} #:nodoc:
65
+ DISPATCHER_OPTIONS = Hash.new {|h,k| h[k] = "#{k}_options"} #:nodoc:
66
+
67
+ # Invoke the special rendering method for the given element +el+.
68
+ #
69
+ # A PDF destination is also added at the current location if th element has an ID or if the
70
+ # element is of type :header and the :auto_ids option is set.
71
+ def convert(el, opts = {})
72
+ id = el.attr['id']
73
+ id = generate_id(el.options[:raw_text]) if !id && @options[:auto_ids] && el.type == :header
74
+ if !id.to_s.empty? && !@dests.has_key?(id)
75
+ @pdf.add_dest(id, @pdf.dest_xyz(0, @pdf.y))
76
+ @dests[id] = @pdf.dest_xyz(0, @pdf.y)
77
+ end
78
+ send(DISPATCHER_RENDER[el.type], el, opts)
79
+ end
80
+
81
+ protected
82
+
83
+ # Render the children of this element with the given options and return the results as array.
84
+ #
85
+ # Each time a child is rendered, the +TYPE_options+ method is invoked (if it exists) to get
86
+ # the specific options for the element with which the given options are updated.
87
+ def inner(el, opts)
88
+ @stack.push([el, opts])
89
+ result = el.children.map do |inner_el|
90
+ options = opts.dup
91
+ options.update(send(DISPATCHER_OPTIONS[inner_el.type], inner_el, options))
92
+ convert(inner_el, options)
93
+ end.flatten.compact
94
+ @stack.pop
95
+ result
96
+ end
97
+
98
+
99
+ # ----------------------------
100
+ # :section: Element rendering methods
101
+ # ----------------------------
102
+
103
+
104
+ def root_options(root, opts)
105
+ {:font => 'Times-Roman', :size => 12, :leading => 2}
106
+ end
107
+
108
+ def render_root(root, opts)
109
+ @pdf = setup_document(root)
110
+ inner(root, root_options(root, opts))
111
+ create_outline(root)
112
+ finish_document(root)
113
+ @pdf.render
114
+ end
115
+
116
+ def header_options(el, opts)
117
+ size = opts[:size] * 1.15**(6 - el.options[:level])
118
+ {
119
+ :font => "Helvetica", :styles => (opts[:styles] || []) + [:bold],
120
+ :size => size, :bottom_padding => opts[:size], :top_padding => opts[:size]
121
+ }
122
+ end
123
+
124
+ def render_header(el, opts)
125
+ render_padded_and_formatted_text(el, opts)
126
+ end
127
+
128
+ def p_options(el, opts)
129
+ bpad = (el.options[:transparent] ? opts[:leading] : opts[:size])
130
+ {:align => :justify, :bottom_padding => bpad}
131
+ end
132
+
133
+ def render_p(el, opts)
134
+ if el.children.size == 1 && el.children.first.type == :img
135
+ render_standalone_image(el, opts)
136
+ else
137
+ render_padded_and_formatted_text(el, opts)
138
+ end
139
+ end
140
+
141
+ def render_standalone_image(el, opts)
142
+ img = el.children.first
143
+ line = img.options[:location]
144
+
145
+ if img.attr['src'].empty?
146
+ warning("Rendering an image without a source is not possible#{line ? " (line #{line})" : ''}")
147
+ return nil
148
+ elsif img.attr['src'] !~ /\.jpe?g$|\.png$/
149
+ warning("Cannot render images other than JPEG or PNG, got #{img.attr['src']}#{line ? " on line #{line}" : ''}")
150
+ return nil
151
+ end
152
+
153
+ img_dirs = (@options[:image_directories] || ['.']).dup
154
+ begin
155
+ img_path = File.join(img_dirs.shift, img.attr['src'])
156
+ image_obj, image_info = @pdf.build_image_object(open(img_path))
157
+ rescue
158
+ img_dirs.empty? ? raise : retry
159
+ end
160
+
161
+ options = {:position => :center}
162
+ if img.attr['height'] && img.attr['height'] =~ /px$/
163
+ options[:height] = img.attr['height'].to_i / (@options[:image_dpi] || 150.0) * 72
164
+ elsif img.attr['width'] && img.attr['width'] =~ /px$/
165
+ options[:width] = img.attr['width'].to_i / (@options[:image_dpi] || 150.0) * 72
166
+ else
167
+ options[:scale] =[(@pdf.bounds.width - mm2pt(20)) / image_info.width.to_f, 1].min
168
+ end
169
+
170
+ if img.attr['class'] =~ /\bright\b/
171
+ options[:position] = :right
172
+ @pdf.float { @pdf.embed_image(image_obj, image_info, options) }
173
+ else
174
+ with_block_padding(el, opts) do
175
+ @pdf.embed_image(image_obj, image_info, options)
176
+ end
177
+ end
178
+ end
179
+
180
+ def blockquote_options(el, opts)
181
+ {:styles => [:italic]}
182
+ end
183
+
184
+ def render_blockquote(el, opts)
185
+ @pdf.indent(mm2pt(10), mm2pt(10)) { inner(el, opts) }
186
+ end
187
+
188
+ def ul_options(el, opts)
189
+ {:bottom_padding => opts[:size]}
190
+ end
191
+
192
+ def render_ul(el, opts)
193
+ with_block_padding(el, opts) do
194
+ el.children.each do |li|
195
+ @pdf.float { @pdf.formatted_text([text_hash("•", opts)]) }
196
+ @pdf.indent(mm2pt(6)) { convert(li, opts) }
197
+ end
198
+ end
199
+ end
200
+
201
+ def ol_options(el, opts)
202
+ {:bottom_padding => opts[:size]}
203
+ end
204
+
205
+ def render_ol(el, opts)
206
+ with_block_padding(el, opts) do
207
+ el.children.each_with_index do |li, index|
208
+ @pdf.float { @pdf.formatted_text([text_hash("#{index+1}.", opts)]) }
209
+ @pdf.indent(mm2pt(6)) { convert(li, opts) }
210
+ end
211
+ end
212
+ end
213
+
214
+ def li_options(el, opts)
215
+ {}
216
+ end
217
+
218
+ def render_li(el, opts)
219
+ inner(el, opts)
220
+ end
221
+
222
+ def dl_options(el, opts)
223
+ {}
224
+ end
225
+
226
+ def render_dl(el, opts)
227
+ inner(el, opts)
228
+ end
229
+
230
+ def dt_options(el, opts)
231
+ {:styles => (opts[:styles] || []) + [:bold], :bottom_padding => 0}
232
+ end
233
+
234
+ def render_dt(el, opts)
235
+ render_padded_and_formatted_text(el, opts)
236
+ end
237
+
238
+ def dd_options(el, opts)
239
+ {}
240
+ end
241
+
242
+ def render_dd(el, opts)
243
+ @pdf.indent(mm2pt(10)) { inner(el, opts) }
244
+ end
245
+
246
+ def math_options(el, opts)
247
+ {}
248
+ end
249
+
250
+ def render_math(el, opts)
251
+ if el.options[:category] == :block
252
+ @pdf.formatted_text([{:text => el.value}], block_hash(opts))
253
+ else
254
+ {:text => el.value}
255
+ end
256
+ end
257
+
258
+ def hr_options(el, opts)
259
+ {:top_padding => opts[:size], :bottom_padding => opts[:size]}
260
+ end
261
+
262
+ def render_hr(el, opts)
263
+ with_block_padding(el, opts) do
264
+ @pdf.stroke_horizontal_line(@pdf.bounds.left + mm2pt(5), @pdf.bounds.right - mm2pt(5))
265
+ end
266
+ end
267
+
268
+ def codeblock_options(el, opts)
269
+ {
270
+ :font => 'Courier', :color => '880000',
271
+ :bottom_padding => opts[:size]
272
+ }
273
+ end
274
+
275
+ def render_codeblock(el, opts)
276
+ with_block_padding(el, opts) do
277
+ @pdf.formatted_text([text_hash(el.value, opts, false)], block_hash(opts))
278
+ end
279
+ end
280
+
281
+ def table_options(el, opts)
282
+ {:bottom_padding => opts[:size]}
283
+ end
284
+
285
+ def render_table(el, opts)
286
+ data = []
287
+ el.children.each do |container|
288
+ container.children.each do |row|
289
+ data << []
290
+ row.children.each do |cell|
291
+ if cell.children.any? {|child| child.options[:category] == :block}
292
+ line = el.options[:location]
293
+ warning("Can't render tables with cells containing block 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
+
311
+
312
+ def text_options(el, opts)
313
+ {}
314
+ end
315
+
316
+ def render_text(el, opts)
317
+ text_hash(el.value.to_s, opts)
318
+ end
319
+
320
+ def em_options(el, opts)
321
+ if opts[:styles] && opts[:styles].include?(:italic)
322
+ {:styles => opts[:styles].reject {|i| i == :italic}}
323
+ else
324
+ {:styles => (opts[:styles] || []) << :italic}
325
+ end
326
+ end
327
+
328
+ def strong_options(el, opts)
329
+ {:styles => (opts[:styles] || []) + [:bold]}
330
+ end
331
+
332
+ def a_options(el, opts)
333
+ hash = {:color => '000088'}
334
+ if el.attr['href'].start_with?('#')
335
+ hash[:anchor] = el.attr['href'].sub(/\A#/, '')
336
+ else
337
+ hash[:link] = el.attr['href']
338
+ end
339
+ hash
340
+ end
341
+
342
+ def render_em(el, opts)
343
+ inner(el, opts)
344
+ end
345
+ alias_method :render_strong, :render_em
346
+ alias_method :render_a, :render_em
347
+
348
+ def codespan_options(el, opts)
349
+ {:font => 'Courier', :color => '880000'}
350
+ end
351
+
352
+ def render_codespan(el, opts)
353
+ text_hash(el.value, opts)
354
+ end
355
+
356
+ def br_options(el, opts)
357
+ {}
358
+ end
359
+
360
+ def render_br(el, opts)
361
+ text_hash("\n", opts, false)
362
+ end
363
+
364
+ def smart_quote_options(el, opts)
365
+ {}
366
+ end
367
+
368
+ def render_smart_quote(el, opts)
369
+ text_hash(smart_quote_entity(el).char, opts)
370
+ end
371
+
372
+ def typographic_sym_options(el, opts)
373
+ {}
374
+ end
375
+
376
+ def render_typographic_sym(el, opts)
377
+ str = if el.value == :laquo_space
378
+ ::Kramdown::Utils::Entities.entity('laquo').char +
379
+ ::Kramdown::Utils::Entities.entity('nbsp').char
380
+ elsif el.value == :raquo_space
381
+ ::Kramdown::Utils::Entities.entity('raquo').char +
382
+ ::Kramdown::Utils::Entities.entity('nbsp').char
383
+ else
384
+ ::Kramdown::Utils::Entities.entity(el.value.to_s).char
385
+ end
386
+ text_hash(str, opts)
387
+ end
388
+
389
+ def entity_options(el, opts)
390
+ {}
391
+ end
392
+
393
+ def render_entity(el, opts)
394
+ text_hash(el.value.char, opts)
395
+ end
396
+
397
+ def abbreviation_options(el, opts)
398
+ {}
399
+ end
400
+
401
+ def render_abbreviation(el, opts)
402
+ text_hash(el.value, opts)
403
+ end
404
+
405
+ def img_options(el, opts)
406
+ {}
407
+ end
408
+
409
+ def render_img(el, *args) #:nodoc:
410
+ line = el.options[:location]
411
+ warning("Rendering span images is not supported for PDF converter#{line ? " (line #{line})" : ''}")
412
+ nil
413
+ end
414
+
415
+
416
+
417
+ def xml_comment_options(el, opts) #:nodoc:
418
+ {}
419
+ end
420
+ alias_method :xml_pi_options, :xml_comment_options
421
+ alias_method :comment_options, :xml_comment_options
422
+ alias_method :blank_options, :xml_comment_options
423
+ alias_method :footnote_options, :xml_comment_options
424
+ alias_method :raw_options, :xml_comment_options
425
+ alias_method :html_element_options, :xml_comment_options
426
+
427
+ def render_xml_comment(el, opts) #:nodoc:
428
+ # noop
429
+ end
430
+ alias_method :render_xml_pi, :render_xml_comment
431
+ alias_method :render_comment, :render_xml_comment
432
+ alias_method :render_blank, :render_xml_comment
433
+
434
+ def render_footnote(el, *args) #:nodoc:
435
+ line = el.options[:location]
436
+ warning("Rendering #{el.type} not supported for PDF converter#{line ? " (line #{line})" : ''}")
437
+ nil
438
+ end
439
+ alias_method :render_raw, :render_footnote
440
+ alias_method :render_html_element, :render_footnote
441
+
442
+
443
+ # ----------------------------
444
+ # :section: Organizational methods
445
+ #
446
+ # These methods are used, for example, to up the needed Prawn::Document instance or to create
447
+ # a PDF outline.
448
+ # ----------------------------
449
+
450
+
451
+ # This module gets mixed into the Prawn::Document instance.
452
+ module PrawnDocumentExtension
453
+
454
+ # Extension for the formatted box class to recognize images and move text around them.
455
+ module CustomBox
456
+
457
+ def available_width
458
+ return super unless @document.respond_to?(:converter) && @document.converter
459
+
460
+ @document.image_floats.each do |pn, x, y, w, h|
461
+ next if @document.page_number != pn
462
+ if @at[1] + @baseline_y <= y - @document.bounds.absolute_bottom &&
463
+ (@at[1] + @baseline_y + @arranger.max_line_height + @leading >= y - h - @document.bounds.absolute_bottom)
464
+ return @width - w
465
+ end
466
+ end
467
+
468
+ return super
469
+ end
470
+
471
+ end
472
+
473
+ Prawn::Text::Formatted::Box.extensions << CustomBox
474
+
475
+ # Access the converter instance from within Prawn
476
+ attr_accessor :converter
477
+
478
+ def image_floats
479
+ @image_floats ||= []
480
+ end
481
+
482
+ # Override image embedding method for adding image positions to #image_floats.
483
+ def embed_image(pdf_obj, info, options)
484
+ # find where the image will be placed and how big it will be
485
+ w,h = info.calc_image_dimensions(options)
486
+
487
+ if options[:at]
488
+ x,y = map_to_absolute(options[:at])
489
+ else
490
+ x,y = image_position(w,h,options)
491
+ move_text_position h
492
+ end
493
+
494
+ #--> This part is new
495
+ if options[:position] == :right
496
+ image_floats << [page_number, x - 15, y, w + 15, h + 15]
497
+ end
498
+
499
+ # add a reference to the image object to the current page
500
+ # resource list and give it a label
501
+ label = "I#{next_image_id}"
502
+ state.page.xobjects.merge!(label => pdf_obj)
503
+
504
+ # add the image to the current page
505
+ instruct = "\nq\n%.3f 0 0 %.3f %.3f %.3f cm\n/%s Do\nQ"
506
+ add_content instruct % [ w, h, x, y - h, label ]
507
+ end
508
+
509
+ end
510
+
511
+
512
+ # Return a hash with options that are suitable for Prawn::Document.new.
513
+ #
514
+ # Used in #setup_document.
515
+ def document_options(root)
516
+ {
517
+ :page_size => 'A4', :page_layout => :portrait, :margin => mm2pt(20),
518
+ :info => {
519
+ :Creator => 'kramdown PDF converter',
520
+ :CreationDate => Time.now
521
+ },
522
+ :compress => true, :optimize_objects => true
523
+ }
524
+ end
525
+
526
+ # Create a Prawn::Document object and return it.
527
+ #
528
+ # Can be used to define repeatable content or register fonts.
529
+ #
530
+ # Used in #render_root.
531
+ def setup_document(root)
532
+ doc = Prawn::Document.new(document_options(root))
533
+ doc.extend(PrawnDocumentExtension)
534
+ doc.converter = self
535
+ doc
536
+ end
537
+
538
+ #
539
+ #
540
+ # Used in #render_root.
541
+ def finish_document(root)
542
+ # no op
543
+ end
544
+
545
+ # Create the PDF outline from the header elements in the TOC.
546
+ def create_outline(root)
547
+ toc = ::Kramdown::Converter::Toc.convert(root).first
548
+
549
+ text_of_header = lambda do |el|
550
+ if el.type == :text
551
+ el.value
552
+ else
553
+ el.children.map {|c| text_of_header.call(c)}.join('')
554
+ end
555
+ end
556
+
557
+ add_section = lambda do |item, parent|
558
+ text = text_of_header.call(item.value)
559
+ destination = @dests[item.attr[:id]]
560
+ if !parent
561
+ @pdf.outline.page(:title => text, :destination => destination)
562
+ else
563
+ @pdf.outline.add_subsection_to(parent) do
564
+ @pdf.outline.page(:title => text, :destination => destination)
565
+ end
566
+ end
567
+ item.children.each {|c| add_section.call(c, text)}
568
+ end
569
+
570
+ toc.children.each do |item|
571
+ add_section.call(item, nil)
572
+ end
573
+ end
574
+
575
+
576
+ # ----------------------------
577
+ # :section: Helper methods
578
+ # ----------------------------
579
+
580
+
581
+ # Move the prawn document cursor down before and/or after yielding the given block.
582
+ #
583
+ # The :top_padding and :bottom_padding options are used for determinig the padding amount.
584
+ def with_block_padding(el, opts)
585
+ @pdf.move_down(opts[:top_padding]) if opts.has_key?(:top_padding)
586
+ yield
587
+ @pdf.move_down(opts[:bottom_padding]) if opts.has_key?(:bottom_padding)
588
+ end
589
+
590
+ # Render the children of the given element as formatted text and respect the top/bottom
591
+ # padding (see #with_block_padding).
592
+ def render_padded_and_formatted_text(el, opts)
593
+ with_block_padding(el, opts) { @pdf.formatted_text(inner(el, opts), block_hash(opts)) }
594
+ end
595
+
596
+ # Helper function that returns a hash with valid "formatted text" options.
597
+ #
598
+ # The +text+ parameter is used as value for the :text key and if +squeeze_whitespace+ is
599
+ # +true+, all whitespace is converted into spaces.
600
+ def text_hash(text, opts, squeeze_whitespace = true)
601
+ text = text.gsub(/\s+/, ' ') if squeeze_whitespace
602
+ hash = {:text => text}
603
+ [:styles, :size, :character_spacing, :font, :color, :link,
604
+ :anchor, :draw_text_callback, :callback].each do |key|
605
+ hash[key] = opts[key] if opts.has_key?(key)
606
+ end
607
+ hash
608
+ end
609
+
610
+ # Helper function that returns a hash with valid options for the prawn #text_box extracted
611
+ # from the given options.
612
+ def block_hash(opts)
613
+ hash = {}
614
+ [:align, :valign, :mode, :final_gap, :leading, :fallback_fonts,
615
+ :direction, :indent_paragraphs].each do |key|
616
+ hash[key] = opts[key] if opts.has_key?(key)
617
+ end
618
+ hash
619
+ end
620
+
621
+ end
622
+
623
+ end
624
+ end
@@ -0,0 +1,53 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2009-2014 Thomas Leitner <t_leitner@gmx.at>
5
+ #
6
+ # This file is part of kramdown which is licensed under the MIT.
7
+ #++
8
+ #
9
+
10
+ module Kramdown
11
+
12
+ module Converter
13
+
14
+ # Removes all block (and optionally span) level HTML tags from the element tree.
15
+ #
16
+ # This converter can be used on parsed HTML documents to get an element tree that will only
17
+ # contain native kramdown elements.
18
+ #
19
+ # *Note* that the returned element tree may not be fully conformant (i.e. the content models of
20
+ # *some elements may be violated)!
21
+ #
22
+ # This converter modifies the given tree in-place and returns it.
23
+ class RemoveHtmlTags < Base
24
+
25
+ def initialize(root, options)
26
+ super
27
+ @options[:template] = ''
28
+ end
29
+
30
+ def convert(el)
31
+ children = el.children.dup
32
+ index = 0
33
+ while index < children.length
34
+ if [:xml_pi].include?(children[index].type) ||
35
+ (children[index].type == :html_element && %w[style script].include?(children[index].value))
36
+ children[index..index] = []
37
+ elsif children[index].type == :html_element &&
38
+ ((@options[:remove_block_html_tags] && children[index].options[:category] == :block) ||
39
+ (@options[:remove_span_html_tags] && children[index].options[:category] == :span))
40
+ children[index..index] = children[index].children
41
+ else
42
+ convert(children[index])
43
+ index += 1
44
+ end
45
+ end
46
+ el.children = children
47
+ el
48
+ end
49
+
50
+ end
51
+
52
+ end
53
+ end