kramdown 1.17.0 → 2.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of kramdown might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CONTRIBUTERS +4 -2
- data/VERSION +1 -1
- data/bin/kramdown +13 -14
- data/lib/kramdown.rb +2 -2
- data/lib/kramdown/converter.rb +6 -7
- data/lib/kramdown/converter/base.rb +18 -29
- data/lib/kramdown/converter/hash_ast.rb +4 -4
- data/lib/kramdown/converter/html.rb +82 -67
- data/lib/kramdown/converter/kramdown.rb +83 -78
- data/lib/kramdown/converter/latex.rb +53 -47
- data/lib/kramdown/converter/man.rb +22 -25
- data/lib/kramdown/converter/math_engine/mathjax.rb +10 -10
- data/lib/kramdown/converter/remove_html_tags.rb +2 -2
- data/lib/kramdown/converter/syntax_highlighter.rb +2 -2
- data/lib/kramdown/converter/syntax_highlighter/minted.rb +2 -2
- data/lib/kramdown/converter/syntax_highlighter/rouge.rb +5 -5
- data/lib/kramdown/converter/toc.rb +5 -5
- data/lib/kramdown/document.rb +9 -11
- data/lib/kramdown/element.rb +11 -9
- data/lib/kramdown/error.rb +2 -2
- data/lib/kramdown/options.rb +258 -384
- data/lib/kramdown/parser.rb +2 -3
- data/lib/kramdown/parser/base.rb +7 -6
- data/lib/kramdown/parser/html.rb +103 -95
- data/lib/kramdown/parser/kramdown.rb +32 -36
- data/lib/kramdown/parser/kramdown/abbreviation.rb +13 -10
- data/lib/kramdown/parser/kramdown/autolink.rb +3 -3
- data/lib/kramdown/parser/kramdown/blank_line.rb +2 -2
- data/lib/kramdown/parser/kramdown/block_boundary.rb +2 -2
- data/lib/kramdown/parser/kramdown/blockquote.rb +4 -5
- data/lib/kramdown/parser/kramdown/codeblock.rb +4 -5
- data/lib/kramdown/parser/kramdown/codespan.rb +5 -5
- data/lib/kramdown/parser/kramdown/emphasis.rb +6 -6
- data/lib/kramdown/parser/kramdown/eob.rb +2 -2
- data/lib/kramdown/parser/kramdown/escaped_chars.rb +2 -2
- data/lib/kramdown/parser/kramdown/extensions.rb +31 -26
- data/lib/kramdown/parser/kramdown/footnote.rb +7 -6
- data/lib/kramdown/parser/kramdown/header.rb +6 -6
- data/lib/kramdown/parser/kramdown/horizontal_rule.rb +3 -3
- data/lib/kramdown/parser/kramdown/html.rb +31 -26
- data/lib/kramdown/parser/kramdown/html_entity.rb +6 -5
- data/lib/kramdown/parser/kramdown/line_break.rb +3 -3
- data/lib/kramdown/parser/kramdown/link.rb +13 -11
- data/lib/kramdown/parser/kramdown/list.rb +38 -40
- data/lib/kramdown/parser/kramdown/math.rb +4 -5
- data/lib/kramdown/parser/kramdown/paragraph.rb +5 -5
- data/lib/kramdown/parser/kramdown/smart_quotes.rb +23 -23
- data/lib/kramdown/parser/kramdown/table.rb +18 -17
- data/lib/kramdown/parser/kramdown/typographic_symbol.rb +8 -8
- data/lib/kramdown/parser/markdown.rb +9 -8
- data/lib/kramdown/utils.rb +5 -6
- data/lib/kramdown/utils/configurable.rb +7 -6
- data/lib/kramdown/utils/entities.rb +286 -289
- data/lib/kramdown/utils/html.rb +10 -12
- data/lib/kramdown/utils/lru_cache.rb +3 -2
- data/lib/kramdown/utils/string_scanner.rb +2 -3
- data/lib/kramdown/utils/unidecoder.rb +8 -6
- data/lib/kramdown/version.rb +3 -3
- data/man/man1/kramdown.1 +3 -107
- data/test/run_tests.rb +6 -6
- data/test/test_files.rb +122 -298
- data/test/test_location.rb +8 -30
- data/test/test_string_scanner_kramdown.rb +6 -9
- data/test/testcases/block/06_codeblock/highlighting-opts.html +6 -6
- data/test/testcases/block/06_codeblock/highlighting.html +5 -6
- data/test/testcases/block/06_codeblock/with_lang_in_fenced_block.options +1 -1
- data/test/testcases/block/07_horizontal_rule/error.html +2 -2
- data/test/testcases/block/09_html/html5_attributes.html +2 -0
- data/test/testcases/block/09_html/html5_attributes.text +2 -0
- data/test/testcases/block/09_html/html_to_native/typography.html +1 -1
- data/test/testcases/block/09_html/simple.html +1 -1
- data/test/testcases/block/12_extension/options3.html +7 -6
- data/test/testcases/block/12_extension/options3.text +2 -2
- data/test/testcases/span/01_link/inline.html +1 -1
- data/test/testcases/span/01_link/reference.html +3 -3
- data/test/testcases/span/03_codespan/highlighting.html +1 -1
- data/test/testcases/span/text_substitutions/entities_as_char.html +1 -1
- metadata +5 -234
- data/Rakefile +0 -341
- data/benchmark/benchmark.rb +0 -43
- data/benchmark/benchmark.sh +0 -74
- data/benchmark/generate_data.rb +0 -119
- data/benchmark/mdbasics.text +0 -306
- data/benchmark/mdsyntax.text +0 -888
- data/benchmark/testing.sh +0 -12
- data/benchmark/timing.sh +0 -10
- data/doc/_design.scss +0 -441
- data/doc/bg.png +0 -0
- data/doc/default.scss +0 -217
- data/doc/default.template +0 -62
- data/doc/documentation.page +0 -84
- data/doc/documentation.template +0 -36
- data/doc/index.page +0 -113
- data/doc/installation.page +0 -88
- data/doc/links.markdown +0 -6
- data/doc/metainfo +0 -13
- data/doc/news.feed +0 -9
- data/doc/news.page +0 -29
- data/doc/options.page +0 -49
- data/doc/quickref.page +0 -603
- data/doc/sidebar.template +0 -22
- data/doc/sitemap.sitemap +0 -5
- data/doc/syntax.page +0 -1799
- data/doc/tests.page +0 -104
- data/doc/virtual +0 -14
- data/lib/kramdown/converter/math_engine/itex2mml.rb +0 -39
- data/lib/kramdown/converter/math_engine/katex.rb +0 -35
- data/lib/kramdown/converter/math_engine/mathjaxnode.rb +0 -56
- data/lib/kramdown/converter/math_engine/ritex.rb +0 -38
- data/lib/kramdown/converter/math_engine/sskatex.rb +0 -97
- data/lib/kramdown/converter/pdf.rb +0 -625
- data/lib/kramdown/converter/syntax_highlighter/coderay.rb +0 -81
- data/lib/kramdown/parser/gfm.rb +0 -188
- data/lib/kramdown/utils/ordered_hash.rb +0 -18
- data/setup.rb +0 -1585
- data/test/testcases/block/07_horizontal_rule/error.html.19 +0 -7
- data/test/testcases/block/09_html/html_to_native/typography.html.19 +0 -1
- data/test/testcases/block/09_html/simple.html.19 +0 -60
- data/test/testcases/block/15_math/itex2mml.html +0 -1
- data/test/testcases/block/15_math/itex2mml.options +0 -1
- data/test/testcases/block/15_math/itex2mml.text +0 -1
- data/test/testcases/block/15_math/katex.html.19 +0 -2
- data/test/testcases/block/15_math/katex.options +0 -1
- data/test/testcases/block/15_math/katex.text +0 -2
- data/test/testcases/block/15_math/mathjaxnode.html.19 +0 -27
- data/test/testcases/block/15_math/mathjaxnode.options +0 -1
- data/test/testcases/block/15_math/mathjaxnode.text +0 -1
- data/test/testcases/block/15_math/mathjaxnode_notexhints.html.19 +0 -23
- data/test/testcases/block/15_math/mathjaxnode_notexhints.options +0 -3
- data/test/testcases/block/15_math/mathjaxnode_notexhints.text +0 -1
- data/test/testcases/block/15_math/mathjaxnode_semantics.html.19 +0 -32
- data/test/testcases/block/15_math/mathjaxnode_semantics.options +0 -3
- data/test/testcases/block/15_math/mathjaxnode_semantics.text +0 -1
- data/test/testcases/block/15_math/ritex.html +0 -1
- data/test/testcases/block/15_math/ritex.options +0 -1
- data/test/testcases/block/15_math/ritex.text +0 -1
- data/test/testcases/block/15_math/sskatex.html.19 +0 -2
- data/test/testcases/block/15_math/sskatex.options +0 -1
- data/test/testcases/block/15_math/sskatex.text +0 -2
- data/test/testcases/span/01_link/inline.html.19 +0 -46
- data/test/testcases/span/01_link/reference.html.19 +0 -37
- data/test/testcases/span/math/itex2mml.html +0 -1
- data/test/testcases/span/math/itex2mml.options +0 -1
- data/test/testcases/span/math/itex2mml.text +0 -1
- data/test/testcases/span/math/katex.html.19 +0 -1
- data/test/testcases/span/math/katex.options +0 -1
- data/test/testcases/span/math/katex.text +0 -1
- data/test/testcases/span/math/mathjaxnode.html.19 +0 -27
- data/test/testcases/span/math/mathjaxnode.options +0 -1
- data/test/testcases/span/math/mathjaxnode.text +0 -1
- data/test/testcases/span/math/ritex.html +0 -1
- data/test/testcases/span/math/ritex.options +0 -1
- data/test/testcases/span/math/ritex.text +0 -1
- data/test/testcases/span/math/sskatex.html.19 +0 -1
- data/test/testcases/span/math/sskatex.options +0 -1
- data/test/testcases/span/math/sskatex.text +0 -1
- data/test/testcases/span/text_substitutions/entities_as_char.html.19 +0 -1
- data/test/testcases_gfm/atx_header.html +0 -3
- data/test/testcases_gfm/atx_header.text +0 -3
- data/test/testcases_gfm/backticks_disable_highlighting.html +0 -2
- data/test/testcases_gfm/backticks_disable_highlighting.options +0 -1
- data/test/testcases_gfm/backticks_disable_highlighting.text +0 -3
- data/test/testcases_gfm/backticks_syntax.html +0 -20
- data/test/testcases_gfm/backticks_syntax.text +0 -19
- data/test/testcases_gfm/codeblock_fenced.html +0 -20
- data/test/testcases_gfm/codeblock_fenced.options +0 -1
- data/test/testcases_gfm/codeblock_fenced.text +0 -21
- data/test/testcases_gfm/hard_line_breaks.html +0 -3
- data/test/testcases_gfm/hard_line_breaks.text +0 -3
- data/test/testcases_gfm/hard_line_breaks_off.html +0 -2
- data/test/testcases_gfm/hard_line_breaks_off.options +0 -1
- data/test/testcases_gfm/hard_line_breaks_off.text +0 -2
- data/test/testcases_gfm/header_ids.html +0 -27
- data/test/testcases_gfm/header_ids.html.19 +0 -27
- data/test/testcases_gfm/header_ids.options +0 -1
- data/test/testcases_gfm/header_ids.text +0 -27
- data/test/testcases_gfm/header_ids_with_prefix.html +0 -3
- data/test/testcases_gfm/header_ids_with_prefix.options +0 -2
- data/test/testcases_gfm/header_ids_with_prefix.text +0 -3
- data/test/testcases_gfm/no_typographic.html +0 -3
- data/test/testcases_gfm/no_typographic.html.19 +0 -3
- data/test/testcases_gfm/no_typographic.options +0 -1
- data/test/testcases_gfm/no_typographic.text +0 -3
- data/test/testcases_gfm/paragraph_end-disabled.html +0 -31
- data/test/testcases_gfm/paragraph_end-disabled.options +0 -1
- data/test/testcases_gfm/paragraph_end-disabled.text +0 -27
- data/test/testcases_gfm/paragraph_end.html +0 -38
- data/test/testcases_gfm/paragraph_end.text +0 -27
- data/test/testcases_gfm/strikethrough.html +0 -27
- data/test/testcases_gfm/strikethrough.html.19 +0 -27
- data/test/testcases_gfm/strikethrough.text +0 -27
- data/test/testcases_gfm/task_list.html +0 -40
- data/test/testcases_gfm/task_list.text +0 -26
- data/test/testcases_gfm/two_para_hard_line_breaks.html +0 -4
- data/test/testcases_gfm/two_para_hard_line_breaks.text +0 -4
data/doc/tests.page
DELETED
@@ -1,104 +0,0 @@
|
|
1
|
-
---
|
2
|
-
title: Tests and Benchmark
|
3
|
-
---
|
4
|
-
# Tests and Benchmark
|
5
|
-
|
6
|
-
## Tests
|
7
|
-
|
8
|
-
There exist several test suites for testing the correctness of a Markdown implementation. The
|
9
|
-
original [Markdown Test Suite] is the standard which one needs to test against. The [PHP Markdown
|
10
|
-
suite][MDTest] contains the original test suite and several more tests (some specifically geared
|
11
|
-
towards the extension of the PHP Markdown Extra package). I have used the latter test tool to
|
12
|
-
roughly verify that kramdown is able to parse standard Markdown. However, since the syntax used by
|
13
|
-
kramdown varies slightly from standard Markdown most of the tests fail - which is fine. When looking
|
14
|
-
at the differences one can see that the failures result from these differences.
|
15
|
-
|
16
|
-
Besides using the above mentioned test suite kramdown comes with its own set of tests which is used
|
17
|
-
to verify that the implementation matches the kramdown specification.
|
18
|
-
|
19
|
-
If you believe you have found a bug in the implementation, please follow these steps:
|
20
|
-
|
21
|
-
* Check the [syntax page](syntax.html) and see if the behaviour is not intended.
|
22
|
-
|
23
|
-
* If the behaviour is not intended and it seems that kramdown should parse some text in another
|
24
|
-
fashion, please open a [bug report] and attach two files: one with the text and one with the HTML
|
25
|
-
conversion you think is correct.
|
26
|
-
|
27
|
-
[bug report]: http://github.com/gettalong/kramdown/issues
|
28
|
-
|
29
|
-
|
30
|
-
## Benchmark
|
31
|
-
|
32
|
-
kramdown comes with a small benchmark to test how fast it is in regard to four other Ruby Markdown
|
33
|
-
implementations: Maruku, BlueFeather, BlueCloth, RDiscount and Redcarpet. The first two are written
|
34
|
-
using only Ruby, the latter three are written in C and need to be compiled.
|
35
|
-
|
36
|
-
As one can see below, kramdown is currently (September 2014) ~3x faster than Maruku, ~4.5x faster
|
37
|
-
than BlueFeather but ~30x slower than RDiscount and ~150x slower than Redcarpet:
|
38
|
-
|
39
|
-
<pre><code>
|
40
|
-
Running tests on 2014-09-16 under ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-linux]
|
41
|
-
|
42
|
-
Test using file mdsyntax.text and 20 runs
|
43
|
-
Rehearsal ----------------------------------------------------
|
44
|
-
kramdown 1.4.2 0.900000 0.010000 0.910000 ( 0.914992)
|
45
|
-
Maruku 0.7.1 2.360000 0.040000 2.400000 ( 2.403571)
|
46
|
-
BlueFeather 0.41 3.760000 0.010000 3.770000 ( 3.776421)
|
47
|
-
BlueCloth 2.2.0 0.050000 0.000000 0.050000 ( 0.050221)
|
48
|
-
RDiscount 2.1.7 0.020000 0.000000 0.020000 ( 0.024180)
|
49
|
-
redcarpet 3.1.2 0.010000 0.000000 0.010000 ( 0.004579)
|
50
|
-
------------------------------------------- total: 7.160000sec
|
51
|
-
|
52
|
-
user system total real
|
53
|
-
kramdown 1.4.2 0.830000 0.000000 0.830000 ( 0.834144)
|
54
|
-
Maruku 0.7.1 2.250000 0.030000 2.280000 ( 2.286579)
|
55
|
-
BlueFeather 0.41 3.680000 0.010000 3.690000 ( 3.693769)
|
56
|
-
BlueCloth 2.2.0 0.050000 0.000000 0.050000 ( 0.044831)
|
57
|
-
RDiscount 2.1.7 0.020000 0.000000 0.020000 ( 0.022847)
|
58
|
-
redcarpet 3.1.2 0.010000 0.000000 0.010000 ( 0.004601)
|
59
|
-
|
60
|
-
Real time of X divided by real time of kramdown
|
61
|
-
Maruku 2.7412
|
62
|
-
BlueFeather 4.4282
|
63
|
-
BlueCloth 0.0537
|
64
|
-
RDiscount 0.0274
|
65
|
-
redcarpet 0.0055
|
66
|
-
|
67
|
-
Test using file mdbasics.text and 20 runs
|
68
|
-
Rehearsal ----------------------------------------------------
|
69
|
-
kramdown 1.4.2 0.150000 0.000000 0.150000 ( 0.149989)
|
70
|
-
Maruku 0.7.1 0.580000 0.010000 0.590000 ( 0.584910)
|
71
|
-
BlueFeather 0.41 0.780000 0.000000 0.780000 ( 0.788740)
|
72
|
-
BlueCloth 2.2.0 0.020000 0.000000 0.020000 ( 0.014700)
|
73
|
-
RDiscount 2.1.7 0.000000 0.000000 0.000000 ( 0.007692)
|
74
|
-
redcarpet 3.1.2 0.010000 0.000000 0.010000 ( 0.002064)
|
75
|
-
------------------------------------------- total: 1.550000sec
|
76
|
-
|
77
|
-
user system total real
|
78
|
-
kramdown 1.4.2 0.150000 0.000000 0.150000 ( 0.151586)
|
79
|
-
Maruku 0.7.1 0.520000 0.000000 0.520000 ( 0.525707)
|
80
|
-
BlueFeather 0.41 0.770000 0.010000 0.780000 ( 0.763559)
|
81
|
-
BlueCloth 2.2.0 0.020000 0.000000 0.020000 ( 0.015509)
|
82
|
-
RDiscount 2.1.7 0.010000 0.000000 0.010000 ( 0.006545)
|
83
|
-
redcarpet 3.1.2 0.000000 0.000000 0.000000 ( 0.001475)
|
84
|
-
|
85
|
-
Real time of X divided by real time of kramdown
|
86
|
-
Maruku 3.468
|
87
|
-
BlueFeather 5.0371
|
88
|
-
BlueCloth 0.1023
|
89
|
-
RDiscount 0.0432
|
90
|
-
redcarpet 0.0097
|
91
|
-
</code></pre>
|
92
|
-
|
93
|
-
And here are some graphs which show the execution times of the various kramdown releases on
|
94
|
-
different Ruby interpreters:
|
95
|
-
|
96
|
-
![ruby 1.8.7p302]({relocatable: img/graph-ruby-1.8.7-302.png})
|
97
|
-
![ruby 1.9.3p448]({relocatable: img/graph-ruby-1.9.3p448-448.png})
|
98
|
-
![ruby 2.0.0p247]({relocatable: img/graph-ruby-2.0.0p247-247.png})
|
99
|
-
![ruby 2.1.2p95]({relocatable: img/graph-ruby-2.1.2p95-95.png})
|
100
|
-
![jruby 1.7.15]({relocatable: img/graph-jruby-1.7.15-392.png})
|
101
|
-
![rubinius 2.2.10]({relocatable: img/graph-rubinius-2.2.10-0.png})
|
102
|
-
|
103
|
-
[Markdown Test Suite]: http://daringfireball.net/projects/downloads/MarkdownTest_1.0.zip
|
104
|
-
[MDTest]: http://www.michelf.com/docs/projets/mdtest-1.0.zip
|
data/doc/virtual
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
rdoc/index.html:
|
2
|
-
title: API Documentation
|
3
|
-
|
4
|
-
/options.en.html#option-syntax-highlighter:
|
5
|
-
dest_path: options.html#option-syntax-highlighter
|
6
|
-
|
7
|
-
/options.en.html#option-syntax-highlighter-opts:
|
8
|
-
dest_path: options.html#option-syntax-highlighter-opts
|
9
|
-
|
10
|
-
/options.en.html#option-math-engine:
|
11
|
-
dest_path: options.html#option-math-engine
|
12
|
-
|
13
|
-
/options.en.html#option-math-engine-opts:
|
14
|
-
dest_path: options.html#option-math-engine-opts
|
@@ -1,39 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
#
|
3
|
-
#--
|
4
|
-
# Copyright (C) 2009-2016 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 Itex2MML library for converting math formulas to MathML.
|
13
|
-
module Itex2MML
|
14
|
-
|
15
|
-
begin
|
16
|
-
require 'itextomml'
|
17
|
-
|
18
|
-
# Itex2MML 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
|
-
parser = ::Itex2MML::Parser.new
|
27
|
-
result = (type == :block ? parser.block_filter(el.value) : parser.inline_filter(el.value))
|
28
|
-
|
29
|
-
attr = el.attr.dup
|
30
|
-
attr.delete('xmlns')
|
31
|
-
attr.delete('display')
|
32
|
-
result.insert("<math".length, converter.html_attributes(attr))
|
33
|
-
|
34
|
-
(type == :block ? "#{' '*opts[:indent]}#{result}\n" : result)
|
35
|
-
end
|
36
|
-
|
37
|
-
end
|
38
|
-
|
39
|
-
end
|
@@ -1,35 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
#
|
3
|
-
#--
|
4
|
-
# Copyright (C) 2018 Gleb Mazovetskiy <glex.spb@gmail.com>
|
5
|
-
#
|
6
|
-
# This file is part of kramdown which is licensed under the MIT.
|
7
|
-
#++
|
8
|
-
|
9
|
-
module Kramdown::Converter::MathEngine
|
10
|
-
# Uses the KaTeX gem for converting math formulas to KaTeX HTML.
|
11
|
-
module Katex
|
12
|
-
AVAILABLE = begin
|
13
|
-
require 'katex'
|
14
|
-
true
|
15
|
-
rescue LoadError
|
16
|
-
false
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.call(converter, el, opts)
|
20
|
-
display_mode = el.options[:category] == :block
|
21
|
-
result = ::Katex.render(
|
22
|
-
el.value,
|
23
|
-
display_mode: display_mode,
|
24
|
-
throw_on_error: false,
|
25
|
-
**converter.options[:math_engine_opts]
|
26
|
-
)
|
27
|
-
attr = el.attr.dup
|
28
|
-
attr.delete('xmlns')
|
29
|
-
attr.delete('display')
|
30
|
-
result.insert(result =~ /[[:space:]>]/, converter.html_attributes(attr))
|
31
|
-
result = "#{' ' * opts[:indent]}#{result}\n" if display_mode
|
32
|
-
result
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
@@ -1,56 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
#
|
3
|
-
#--
|
4
|
-
# Copyright (C) 2009-2016 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 mathjax-node-cli library for converting math formulas to MathML.
|
13
|
-
module MathjaxNode
|
14
|
-
|
15
|
-
# MathjaxNode is available if this constant is +true+.
|
16
|
-
AVAILABLE = begin
|
17
|
-
%x{node --version}[1..-2] >= '4.5'
|
18
|
-
rescue
|
19
|
-
begin
|
20
|
-
%x{nodejs --version}[1..-2] >= '4.5'
|
21
|
-
rescue
|
22
|
-
false
|
23
|
-
end
|
24
|
-
end && begin
|
25
|
-
npm = %x{npm --global --depth=1 list mathjax-node-cli 2>&1}
|
26
|
-
|
27
|
-
unless /mathjax-node-cli@/ === npm.lines.drop(1).join("\n")
|
28
|
-
npm = %x{npm --depth=1 list mathjax-node-cli 2>&1}
|
29
|
-
end
|
30
|
-
|
31
|
-
T2MPATH = File.join(npm.lines.first.strip, "node_modules/mathjax-node-cli/bin/tex2mml")
|
32
|
-
/mathjax-node-cli@/ === npm.lines.drop(1).join("\n") && File.exist?(T2MPATH)
|
33
|
-
rescue
|
34
|
-
false
|
35
|
-
end
|
36
|
-
|
37
|
-
def self.call(converter, el, opts)
|
38
|
-
type = el.options[:category]
|
39
|
-
|
40
|
-
cmd = [T2MPATH]
|
41
|
-
cmd << "--inline" unless type == :block
|
42
|
-
cmd << "--semantics" if converter.options[:math_engine_opts][:semantics] == true
|
43
|
-
cmd << "--notexhints" if converter.options[:math_engine_opts][:texhints] == false
|
44
|
-
result = IO.popen(cmd << el.value).read.strip
|
45
|
-
|
46
|
-
attr = el.attr.dup
|
47
|
-
attr.delete('xmlns')
|
48
|
-
attr.delete('display')
|
49
|
-
result.insert("<math".length, converter.html_attributes(attr))
|
50
|
-
|
51
|
-
(type == :block ? "#{' '*opts[:indent]}#{result}\n" : result)
|
52
|
-
end
|
53
|
-
|
54
|
-
end
|
55
|
-
|
56
|
-
end
|
@@ -1,38 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
#
|
3
|
-
#--
|
4
|
-
# Copyright (C) 2009-2016 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
|
-
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
|
@@ -1,97 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
#
|
3
|
-
#--
|
4
|
-
# Copyright (C) 2017 Christian Cornelssen <ccorn@1tein.de>
|
5
|
-
#
|
6
|
-
# This file is part of kramdown which is licensed under the MIT.
|
7
|
-
#++
|
8
|
-
|
9
|
-
module Kramdown::Converter::MathEngine
|
10
|
-
|
11
|
-
# Consider this a lightweight alternative to MathjaxNode. Uses KaTeX and ExecJS (via ::SsKaTeX)
|
12
|
-
# instead of MathJax and Node.js. Javascript execution context initialization is done only once.
|
13
|
-
# As a result, the performance is reasonable.
|
14
|
-
module SsKaTeX
|
15
|
-
|
16
|
-
# Indicate whether SsKaTeX may be available.
|
17
|
-
#
|
18
|
-
# This test is incomplete; it cannot test the existence of _katex_js_ nor the availability of a
|
19
|
-
# specific _js_run_ because those depend on configuration not given here. This test mainly
|
20
|
-
# indicates whether static dependencies such as the +sskatex+ and +execjs+ gems are available.
|
21
|
-
AVAILABLE = begin
|
22
|
-
require 'sskatex'
|
23
|
-
# No test for any JS engine availability here; specifics are config-dependent anyway
|
24
|
-
true
|
25
|
-
rescue LoadError
|
26
|
-
false
|
27
|
-
end
|
28
|
-
|
29
|
-
if AVAILABLE
|
30
|
-
|
31
|
-
# Class-level cache for ::SsKaTeX converter state, queried by configuration. Note: KTXC
|
32
|
-
# contents may become stale if the contents of used JS files change while the configuration
|
33
|
-
# remains unchanged.
|
34
|
-
KTXC = ::Kramdown::Utils::LRUCache.new(10)
|
35
|
-
|
36
|
-
# A logger that routes messages to the debug channel only. No need to create this dynamically.
|
37
|
-
DEBUG_LOGGER = lambda { |level, &expr| warn(expr.call) }
|
38
|
-
|
39
|
-
class << self
|
40
|
-
private
|
41
|
-
|
42
|
-
# Given a Kramdown::Converter::Base object _converter_, retrieves the logging options and
|
43
|
-
# builds an object usable for ::SsKaTeX#logger. The result is either +nil+ (no logging) or a
|
44
|
-
# +Proc+ object which, when given a _level_ (either +:verbose+ or +:debug+) and a block,
|
45
|
-
# decides whether logging is enabled, and if so, evaluates the given block for the message
|
46
|
-
# and routes that message to the appropriate channels. With <tt>level == :verbose+</tt>,
|
47
|
-
# messages are passed to _converter_.warning if the _converter_'s +:verbose+ option is set.
|
48
|
-
# All messages are passed to +warn+ if the _converter_'s +:debug+ option is set.
|
49
|
-
#
|
50
|
-
# Note that the returned logger may contain references to the given _converter_ and is not
|
51
|
-
# affected by subsequent changes in the _converter_'s logging options.
|
52
|
-
def logger(converter)
|
53
|
-
config = converter.options[:math_engine_opts]
|
54
|
-
debug = config[:debug]
|
55
|
-
if config[:verbose]
|
56
|
-
# Need a closure
|
57
|
-
lambda do |level, &expr|
|
58
|
-
verbose = (level == :verbose)
|
59
|
-
msg = expr.call if debug || verbose
|
60
|
-
warn(msg) if debug
|
61
|
-
converter.warning(msg) if verbose
|
62
|
-
end
|
63
|
-
elsif debug
|
64
|
-
DEBUG_LOGGER
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
# Given a Kramdown::Converter::Base object _converter_, return a ::SsKaTeX converter _sktx_
|
69
|
-
# that has been configured with _converter_'s +math_engine_opts+, but not for logging. Cache
|
70
|
-
# _sktx_ for reuse, without references to _converter_.
|
71
|
-
def katex_conv(converter)
|
72
|
-
config = converter.options[:math_engine_opts]
|
73
|
-
# Could .reject { |key, _| [:verbose, :debug].include?(key.to_sym) }
|
74
|
-
# because the JS engine setup can be reused for different logging settings.
|
75
|
-
# But then the +math_engine_opts+ dict would be essentially dup'ed every time,
|
76
|
-
# and late activation of logging would miss the initialization if the engine is reused.
|
77
|
-
KTXC[config] ||= ::SsKaTeX.new(config)
|
78
|
-
end
|
79
|
-
|
80
|
-
public
|
81
|
-
|
82
|
-
# The function used by kramdown for rendering TeX math to HTML
|
83
|
-
def call(converter, el, opts)
|
84
|
-
display_mode = el.options[:category]
|
85
|
-
ans = katex_conv(converter).call(el.value, display_mode == :block, &logger(converter))
|
86
|
-
attr = el.attr.dup
|
87
|
-
attr.delete('xmlns')
|
88
|
-
attr.delete('display')
|
89
|
-
ans.insert(ans =~ /[[:space:]>]/, converter.html_attributes(attr))
|
90
|
-
ans = ' ' * opts[:indent] << ans << "\n" if display_mode == :block
|
91
|
-
ans
|
92
|
-
end
|
93
|
-
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
@@ -1,625 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
#
|
3
|
-
#--
|
4
|
-
# Copyright (C) 2009-2016 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'
|
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
|
-
include Prawn::Measurements
|
47
|
-
|
48
|
-
def initialize(root, options)
|
49
|
-
super
|
50
|
-
@stack = []
|
51
|
-
@dests = {}
|
52
|
-
end
|
53
|
-
|
54
|
-
# PDF templates are applied before conversion. They should contain code to augment the
|
55
|
-
# converter object (i.e. to override the methods).
|
56
|
-
def apply_template_before?
|
57
|
-
true
|
58
|
-
end
|
59
|
-
|
60
|
-
# Returns +false+.
|
61
|
-
def apply_template_after?
|
62
|
-
false
|
63
|
-
end
|
64
|
-
|
65
|
-
DISPATCHER_RENDER = Hash.new {|h,k| h[k] = "render_#{k}"} #:nodoc:
|
66
|
-
DISPATCHER_OPTIONS = Hash.new {|h,k| h[k] = "#{k}_options"} #:nodoc:
|
67
|
-
|
68
|
-
# Invoke the special rendering method for the given element +el+.
|
69
|
-
#
|
70
|
-
# A PDF destination is also added at the current location if th element has an ID or if the
|
71
|
-
# element is of type :header and the :auto_ids option is set.
|
72
|
-
def convert(el, opts = {})
|
73
|
-
id = el.attr['id']
|
74
|
-
id = generate_id(el.options[:raw_text]) if !id && @options[:auto_ids] && el.type == :header
|
75
|
-
if !id.to_s.empty? && !@dests.has_key?(id)
|
76
|
-
@pdf.add_dest(id, @pdf.dest_xyz(0, @pdf.y))
|
77
|
-
@dests[id] = @pdf.dest_xyz(0, @pdf.y)
|
78
|
-
end
|
79
|
-
send(DISPATCHER_RENDER[el.type], el, opts)
|
80
|
-
end
|
81
|
-
|
82
|
-
protected
|
83
|
-
|
84
|
-
# Render the children of this element with the given options and return the results as array.
|
85
|
-
#
|
86
|
-
# Each time a child is rendered, the +TYPE_options+ method is invoked (if it exists) to get
|
87
|
-
# the specific options for the element with which the given options are updated.
|
88
|
-
def inner(el, opts)
|
89
|
-
@stack.push([el, opts])
|
90
|
-
result = el.children.map do |inner_el|
|
91
|
-
options = opts.dup
|
92
|
-
options.update(send(DISPATCHER_OPTIONS[inner_el.type], inner_el, options))
|
93
|
-
convert(inner_el, options)
|
94
|
-
end.flatten.compact
|
95
|
-
@stack.pop
|
96
|
-
result
|
97
|
-
end
|
98
|
-
|
99
|
-
|
100
|
-
# ----------------------------
|
101
|
-
# :section: Element rendering methods
|
102
|
-
# ----------------------------
|
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, got #{img.attr['src']}#{line ? " on line #{line}" : ''}")
|
151
|
-
return nil
|
152
|
-
end
|
153
|
-
|
154
|
-
img_dirs = (@options[:image_directories] || ['.']).dup
|
155
|
-
begin
|
156
|
-
img_path = File.join(img_dirs.shift, img.attr['src'])
|
157
|
-
image_obj, image_info = @pdf.build_image_object(open(img_path))
|
158
|
-
rescue
|
159
|
-
img_dirs.empty? ? raise : retry
|
160
|
-
end
|
161
|
-
|
162
|
-
options = {:position => :center}
|
163
|
-
if img.attr['height'] && img.attr['height'] =~ /px$/
|
164
|
-
options[:height] = img.attr['height'].to_i / (@options[:image_dpi] || 150.0) * 72
|
165
|
-
elsif img.attr['width'] && img.attr['width'] =~ /px$/
|
166
|
-
options[:width] = img.attr['width'].to_i / (@options[:image_dpi] || 150.0) * 72
|
167
|
-
else
|
168
|
-
options[:scale] =[(@pdf.bounds.width - mm2pt(20)) / image_info.width.to_f, 1].min
|
169
|
-
end
|
170
|
-
|
171
|
-
if img.attr['class'] =~ /\bright\b/
|
172
|
-
options[:position] = :right
|
173
|
-
@pdf.float { @pdf.embed_image(image_obj, image_info, options) }
|
174
|
-
else
|
175
|
-
with_block_padding(el, opts) do
|
176
|
-
@pdf.embed_image(image_obj, image_info, options)
|
177
|
-
end
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
def blockquote_options(el, opts)
|
182
|
-
{:styles => [:italic]}
|
183
|
-
end
|
184
|
-
|
185
|
-
def render_blockquote(el, opts)
|
186
|
-
@pdf.indent(mm2pt(10), mm2pt(10)) { inner(el, opts) }
|
187
|
-
end
|
188
|
-
|
189
|
-
def ul_options(el, opts)
|
190
|
-
{:bottom_padding => opts[:size]}
|
191
|
-
end
|
192
|
-
|
193
|
-
def render_ul(el, opts)
|
194
|
-
with_block_padding(el, opts) do
|
195
|
-
el.children.each do |li|
|
196
|
-
@pdf.float { @pdf.formatted_text([text_hash("•", opts)]) }
|
197
|
-
@pdf.indent(mm2pt(6)) { convert(li, opts) }
|
198
|
-
end
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
def ol_options(el, opts)
|
203
|
-
{:bottom_padding => opts[:size]}
|
204
|
-
end
|
205
|
-
|
206
|
-
def render_ol(el, opts)
|
207
|
-
with_block_padding(el, opts) do
|
208
|
-
el.children.each_with_index do |li, index|
|
209
|
-
@pdf.float { @pdf.formatted_text([text_hash("#{index+1}.", opts)]) }
|
210
|
-
@pdf.indent(mm2pt(6)) { convert(li, opts) }
|
211
|
-
end
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|
215
|
-
def li_options(el, opts)
|
216
|
-
{}
|
217
|
-
end
|
218
|
-
|
219
|
-
def render_li(el, opts)
|
220
|
-
inner(el, opts)
|
221
|
-
end
|
222
|
-
|
223
|
-
def dl_options(el, opts)
|
224
|
-
{}
|
225
|
-
end
|
226
|
-
|
227
|
-
def render_dl(el, opts)
|
228
|
-
inner(el, opts)
|
229
|
-
end
|
230
|
-
|
231
|
-
def dt_options(el, opts)
|
232
|
-
{:styles => (opts[:styles] || []) + [:bold], :bottom_padding => 0}
|
233
|
-
end
|
234
|
-
|
235
|
-
def render_dt(el, opts)
|
236
|
-
render_padded_and_formatted_text(el, opts)
|
237
|
-
end
|
238
|
-
|
239
|
-
def dd_options(el, opts)
|
240
|
-
{}
|
241
|
-
end
|
242
|
-
|
243
|
-
def render_dd(el, opts)
|
244
|
-
@pdf.indent(mm2pt(10)) { inner(el, opts) }
|
245
|
-
end
|
246
|
-
|
247
|
-
def math_options(el, opts)
|
248
|
-
{}
|
249
|
-
end
|
250
|
-
|
251
|
-
def render_math(el, opts)
|
252
|
-
if el.options[:category] == :block
|
253
|
-
@pdf.formatted_text([{:text => el.value}], block_hash(opts))
|
254
|
-
else
|
255
|
-
{:text => el.value}
|
256
|
-
end
|
257
|
-
end
|
258
|
-
|
259
|
-
def hr_options(el, opts)
|
260
|
-
{:top_padding => opts[:size], :bottom_padding => opts[:size]}
|
261
|
-
end
|
262
|
-
|
263
|
-
def render_hr(el, opts)
|
264
|
-
with_block_padding(el, opts) do
|
265
|
-
@pdf.stroke_horizontal_line(@pdf.bounds.left + mm2pt(5), @pdf.bounds.right - mm2pt(5))
|
266
|
-
end
|
267
|
-
end
|
268
|
-
|
269
|
-
def codeblock_options(el, opts)
|
270
|
-
{
|
271
|
-
:font => 'Courier', :color => '880000',
|
272
|
-
:bottom_padding => opts[:size]
|
273
|
-
}
|
274
|
-
end
|
275
|
-
|
276
|
-
def render_codeblock(el, opts)
|
277
|
-
with_block_padding(el, opts) do
|
278
|
-
@pdf.formatted_text([text_hash(el.value, opts, false)], block_hash(opts))
|
279
|
-
end
|
280
|
-
end
|
281
|
-
|
282
|
-
def table_options(el, opts)
|
283
|
-
{:bottom_padding => opts[:size]}
|
284
|
-
end
|
285
|
-
|
286
|
-
def render_table(el, opts)
|
287
|
-
data = []
|
288
|
-
el.children.each do |container|
|
289
|
-
container.children.each do |row|
|
290
|
-
data << []
|
291
|
-
row.children.each do |cell|
|
292
|
-
if cell.children.any? {|child| child.options[:category] == :block}
|
293
|
-
line = el.options[:location]
|
294
|
-
warning("Can't render tables with cells containing block elements#{line ? " (line #{line})" : ''}")
|
295
|
-
return
|
296
|
-
end
|
297
|
-
cell_data = inner(cell, opts)
|
298
|
-
data.last << cell_data.map {|c| c[:text]}.join('')
|
299
|
-
end
|
300
|
-
end
|
301
|
-
end
|
302
|
-
with_block_padding(el, opts) do
|
303
|
-
@pdf.table(data, :width => @pdf.bounds.right) do
|
304
|
-
el.options[:alignment].each_with_index do |alignment, index|
|
305
|
-
columns(index).align = alignment unless alignment == :default
|
306
|
-
end
|
307
|
-
end
|
308
|
-
end
|
309
|
-
end
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
def text_options(el, opts)
|
314
|
-
{}
|
315
|
-
end
|
316
|
-
|
317
|
-
def render_text(el, opts)
|
318
|
-
text_hash(el.value.to_s, opts)
|
319
|
-
end
|
320
|
-
|
321
|
-
def em_options(el, opts)
|
322
|
-
if opts[:styles] && opts[:styles].include?(:italic)
|
323
|
-
{:styles => opts[:styles].reject {|i| i == :italic}}
|
324
|
-
else
|
325
|
-
{:styles => (opts[:styles] || []) << :italic}
|
326
|
-
end
|
327
|
-
end
|
328
|
-
|
329
|
-
def strong_options(el, opts)
|
330
|
-
{:styles => (opts[:styles] || []) + [:bold]}
|
331
|
-
end
|
332
|
-
|
333
|
-
def a_options(el, opts)
|
334
|
-
hash = {:color => '000088'}
|
335
|
-
if el.attr['href'].start_with?('#')
|
336
|
-
hash[:anchor] = el.attr['href'].sub(/\A#/, '')
|
337
|
-
else
|
338
|
-
hash[:link] = el.attr['href']
|
339
|
-
end
|
340
|
-
hash
|
341
|
-
end
|
342
|
-
|
343
|
-
def render_em(el, opts)
|
344
|
-
inner(el, opts)
|
345
|
-
end
|
346
|
-
alias_method :render_strong, :render_em
|
347
|
-
alias_method :render_a, :render_em
|
348
|
-
|
349
|
-
def codespan_options(el, opts)
|
350
|
-
{:font => 'Courier', :color => '880000'}
|
351
|
-
end
|
352
|
-
|
353
|
-
def render_codespan(el, opts)
|
354
|
-
text_hash(el.value, opts)
|
355
|
-
end
|
356
|
-
|
357
|
-
def br_options(el, opts)
|
358
|
-
{}
|
359
|
-
end
|
360
|
-
|
361
|
-
def render_br(el, opts)
|
362
|
-
text_hash("\n", opts, false)
|
363
|
-
end
|
364
|
-
|
365
|
-
def smart_quote_options(el, opts)
|
366
|
-
{}
|
367
|
-
end
|
368
|
-
|
369
|
-
def render_smart_quote(el, opts)
|
370
|
-
text_hash(smart_quote_entity(el).char, opts)
|
371
|
-
end
|
372
|
-
|
373
|
-
def typographic_sym_options(el, opts)
|
374
|
-
{}
|
375
|
-
end
|
376
|
-
|
377
|
-
def render_typographic_sym(el, opts)
|
378
|
-
str = if el.value == :laquo_space
|
379
|
-
::Kramdown::Utils::Entities.entity('laquo').char +
|
380
|
-
::Kramdown::Utils::Entities.entity('nbsp').char
|
381
|
-
elsif el.value == :raquo_space
|
382
|
-
::Kramdown::Utils::Entities.entity('raquo').char +
|
383
|
-
::Kramdown::Utils::Entities.entity('nbsp').char
|
384
|
-
else
|
385
|
-
::Kramdown::Utils::Entities.entity(el.value.to_s).char
|
386
|
-
end
|
387
|
-
text_hash(str, opts)
|
388
|
-
end
|
389
|
-
|
390
|
-
def entity_options(el, opts)
|
391
|
-
{}
|
392
|
-
end
|
393
|
-
|
394
|
-
def render_entity(el, opts)
|
395
|
-
text_hash(el.value.char, opts)
|
396
|
-
end
|
397
|
-
|
398
|
-
def abbreviation_options(el, opts)
|
399
|
-
{}
|
400
|
-
end
|
401
|
-
|
402
|
-
def render_abbreviation(el, opts)
|
403
|
-
text_hash(el.value, opts)
|
404
|
-
end
|
405
|
-
|
406
|
-
def img_options(el, opts)
|
407
|
-
{}
|
408
|
-
end
|
409
|
-
|
410
|
-
def render_img(el, *args) #:nodoc:
|
411
|
-
line = el.options[:location]
|
412
|
-
warning("Rendering span images is not supported for PDF converter#{line ? " (line #{line})" : ''}")
|
413
|
-
nil
|
414
|
-
end
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
def xml_comment_options(el, opts) #:nodoc:
|
419
|
-
{}
|
420
|
-
end
|
421
|
-
alias_method :xml_pi_options, :xml_comment_options
|
422
|
-
alias_method :comment_options, :xml_comment_options
|
423
|
-
alias_method :blank_options, :xml_comment_options
|
424
|
-
alias_method :footnote_options, :xml_comment_options
|
425
|
-
alias_method :raw_options, :xml_comment_options
|
426
|
-
alias_method :html_element_options, :xml_comment_options
|
427
|
-
|
428
|
-
def render_xml_comment(el, opts) #:nodoc:
|
429
|
-
# noop
|
430
|
-
end
|
431
|
-
alias_method :render_xml_pi, :render_xml_comment
|
432
|
-
alias_method :render_comment, :render_xml_comment
|
433
|
-
alias_method :render_blank, :render_xml_comment
|
434
|
-
|
435
|
-
def render_footnote(el, *args) #:nodoc:
|
436
|
-
line = el.options[:location]
|
437
|
-
warning("Rendering #{el.type} not supported for PDF converter#{line ? " (line #{line})" : ''}")
|
438
|
-
nil
|
439
|
-
end
|
440
|
-
alias_method :render_raw, :render_footnote
|
441
|
-
alias_method :render_html_element, :render_footnote
|
442
|
-
|
443
|
-
|
444
|
-
# ----------------------------
|
445
|
-
# :section: Organizational methods
|
446
|
-
#
|
447
|
-
# These methods are used, for example, to up the needed Prawn::Document instance or to create
|
448
|
-
# a PDF outline.
|
449
|
-
# ----------------------------
|
450
|
-
|
451
|
-
|
452
|
-
# This module gets mixed into the Prawn::Document instance.
|
453
|
-
module PrawnDocumentExtension
|
454
|
-
|
455
|
-
# Extension for the formatted box class to recognize images and move text around them.
|
456
|
-
module CustomBox
|
457
|
-
|
458
|
-
def available_width
|
459
|
-
return super unless @document.respond_to?(:converter) && @document.converter
|
460
|
-
|
461
|
-
@document.image_floats.each do |pn, x, y, w, h|
|
462
|
-
next if @document.page_number != pn
|
463
|
-
if @at[1] + @baseline_y <= y - @document.bounds.absolute_bottom &&
|
464
|
-
(@at[1] + @baseline_y + @arranger.max_line_height + @leading >= y - h - @document.bounds.absolute_bottom)
|
465
|
-
return @width - w
|
466
|
-
end
|
467
|
-
end
|
468
|
-
|
469
|
-
return super
|
470
|
-
end
|
471
|
-
|
472
|
-
end
|
473
|
-
|
474
|
-
Prawn::Text::Formatted::Box.extensions << CustomBox
|
475
|
-
|
476
|
-
# Access the converter instance from within Prawn
|
477
|
-
attr_accessor :converter
|
478
|
-
|
479
|
-
def image_floats
|
480
|
-
@image_floats ||= []
|
481
|
-
end
|
482
|
-
|
483
|
-
# Override image embedding method for adding image positions to #image_floats.
|
484
|
-
def embed_image(pdf_obj, info, options)
|
485
|
-
# find where the image will be placed and how big it will be
|
486
|
-
w,h = info.calc_image_dimensions(options)
|
487
|
-
|
488
|
-
if options[:at]
|
489
|
-
x,y = map_to_absolute(options[:at])
|
490
|
-
else
|
491
|
-
x,y = image_position(w,h,options)
|
492
|
-
move_text_position h
|
493
|
-
end
|
494
|
-
|
495
|
-
#--> This part is new
|
496
|
-
if options[:position] == :right
|
497
|
-
image_floats << [page_number, x - 15, y, w + 15, h + 15]
|
498
|
-
end
|
499
|
-
|
500
|
-
# add a reference to the image object to the current page
|
501
|
-
# resource list and give it a label
|
502
|
-
label = "I#{next_image_id}"
|
503
|
-
state.page.xobjects.merge!(label => pdf_obj)
|
504
|
-
|
505
|
-
# add the image to the current page
|
506
|
-
instruct = "\nq\n%.3f 0 0 %.3f %.3f %.3f cm\n/%s Do\nQ"
|
507
|
-
add_content instruct % [ w, h, x, y - h, label ]
|
508
|
-
end
|
509
|
-
|
510
|
-
end
|
511
|
-
|
512
|
-
|
513
|
-
# Return a hash with options that are suitable for Prawn::Document.new.
|
514
|
-
#
|
515
|
-
# Used in #setup_document.
|
516
|
-
def document_options(root)
|
517
|
-
{
|
518
|
-
:page_size => 'A4', :page_layout => :portrait, :margin => mm2pt(20),
|
519
|
-
:info => {
|
520
|
-
:Creator => 'kramdown PDF converter',
|
521
|
-
:CreationDate => Time.now
|
522
|
-
},
|
523
|
-
:compress => true, :optimize_objects => true
|
524
|
-
}
|
525
|
-
end
|
526
|
-
|
527
|
-
# Create a Prawn::Document object and return it.
|
528
|
-
#
|
529
|
-
# Can be used to define repeatable content or register fonts.
|
530
|
-
#
|
531
|
-
# Used in #render_root.
|
532
|
-
def setup_document(root)
|
533
|
-
doc = Prawn::Document.new(document_options(root))
|
534
|
-
doc.extend(PrawnDocumentExtension)
|
535
|
-
doc.converter = self
|
536
|
-
doc
|
537
|
-
end
|
538
|
-
|
539
|
-
#
|
540
|
-
#
|
541
|
-
# Used in #render_root.
|
542
|
-
def finish_document(root)
|
543
|
-
# no op
|
544
|
-
end
|
545
|
-
|
546
|
-
# Create the PDF outline from the header elements in the TOC.
|
547
|
-
def create_outline(root)
|
548
|
-
toc = ::Kramdown::Converter::Toc.convert(root).first
|
549
|
-
|
550
|
-
text_of_header = lambda do |el|
|
551
|
-
if el.type == :text
|
552
|
-
el.value
|
553
|
-
else
|
554
|
-
el.children.map {|c| text_of_header.call(c)}.join('')
|
555
|
-
end
|
556
|
-
end
|
557
|
-
|
558
|
-
add_section = lambda do |item, parent|
|
559
|
-
text = text_of_header.call(item.value)
|
560
|
-
destination = @dests[item.attr[:id]]
|
561
|
-
if !parent
|
562
|
-
@pdf.outline.page(:title => text, :destination => destination)
|
563
|
-
else
|
564
|
-
@pdf.outline.add_subsection_to(parent) do
|
565
|
-
@pdf.outline.page(:title => text, :destination => destination)
|
566
|
-
end
|
567
|
-
end
|
568
|
-
item.children.each {|c| add_section.call(c, text)}
|
569
|
-
end
|
570
|
-
|
571
|
-
toc.children.each do |item|
|
572
|
-
add_section.call(item, nil)
|
573
|
-
end
|
574
|
-
end
|
575
|
-
|
576
|
-
|
577
|
-
# ----------------------------
|
578
|
-
# :section: Helper methods
|
579
|
-
# ----------------------------
|
580
|
-
|
581
|
-
|
582
|
-
# Move the prawn document cursor down before and/or after yielding the given block.
|
583
|
-
#
|
584
|
-
# The :top_padding and :bottom_padding options are used for determinig the padding amount.
|
585
|
-
def with_block_padding(el, opts)
|
586
|
-
@pdf.move_down(opts[:top_padding]) if opts.has_key?(:top_padding)
|
587
|
-
yield
|
588
|
-
@pdf.move_down(opts[:bottom_padding]) if opts.has_key?(:bottom_padding)
|
589
|
-
end
|
590
|
-
|
591
|
-
# Render the children of the given element as formatted text and respect the top/bottom
|
592
|
-
# padding (see #with_block_padding).
|
593
|
-
def render_padded_and_formatted_text(el, opts)
|
594
|
-
with_block_padding(el, opts) { @pdf.formatted_text(inner(el, opts), block_hash(opts)) }
|
595
|
-
end
|
596
|
-
|
597
|
-
# Helper function that returns a hash with valid "formatted text" options.
|
598
|
-
#
|
599
|
-
# The +text+ parameter is used as value for the :text key and if +squeeze_whitespace+ is
|
600
|
-
# +true+, all whitespace is converted into spaces.
|
601
|
-
def text_hash(text, opts, squeeze_whitespace = true)
|
602
|
-
text = text.gsub(/\s+/, ' ') if squeeze_whitespace
|
603
|
-
hash = {:text => text}
|
604
|
-
[:styles, :size, :character_spacing, :font, :color, :link,
|
605
|
-
:anchor, :draw_text_callback, :callback].each do |key|
|
606
|
-
hash[key] = opts[key] if opts.has_key?(key)
|
607
|
-
end
|
608
|
-
hash
|
609
|
-
end
|
610
|
-
|
611
|
-
# Helper function that returns a hash with valid options for the prawn #text_box extracted
|
612
|
-
# from the given options.
|
613
|
-
def block_hash(opts)
|
614
|
-
hash = {}
|
615
|
-
[:align, :valign, :mode, :final_gap, :leading, :fallback_fonts,
|
616
|
-
:direction, :indent_paragraphs].each do |key|
|
617
|
-
hash[key] = opts[key] if opts.has_key?(key)
|
618
|
-
end
|
619
|
-
hash
|
620
|
-
end
|
621
|
-
|
622
|
-
end
|
623
|
-
|
624
|
-
end
|
625
|
-
end
|