asciidoctor-latex 1.5.0.dev

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.adoc +22 -0
  3. data/README.adoc +213 -0
  4. data/Rakefile +55 -0
  5. data/bin/asciidoctor-latex +22 -0
  6. data/data/asciidoc_tex_macros.tex +42 -0
  7. data/data/preamble_article.tex +45 -0
  8. data/data/preamble_book.tex +44 -0
  9. data/lib/asciidoctor-latex.rb +1 -0
  10. data/lib/asciidoctor/latex.rb +8 -0
  11. data/lib/asciidoctor/latex/chem.rb +24 -0
  12. data/lib/asciidoctor/latex/click_block.rb +121 -0
  13. data/lib/asciidoctor/latex/converter.rb +358 -0
  14. data/lib/asciidoctor/latex/core_ext/colored_string.rb +35 -0
  15. data/lib/asciidoctor/latex/dollar.rb +28 -0
  16. data/lib/asciidoctor/latex/ent_to_uni.rb +17 -0
  17. data/lib/asciidoctor/latex/environment_block.rb +190 -0
  18. data/lib/asciidoctor/latex/inject_html.rb +49 -0
  19. data/lib/asciidoctor/latex/inline_macros.rb +20 -0
  20. data/lib/asciidoctor/latex/macro_insert.rb +49 -0
  21. data/lib/asciidoctor/latex/node_processors.rb +695 -0
  22. data/lib/asciidoctor/latex/prepend_processor.rb +36 -0
  23. data/lib/asciidoctor/latex/preprocess.rb +37 -0
  24. data/lib/asciidoctor/latex/tex_block.rb +108 -0
  25. data/lib/asciidoctor/latex/tex_postprocessor.rb +44 -0
  26. data/lib/asciidoctor/latex/tex_preprocessor.rb +65 -0
  27. data/lib/asciidoctor/latex/version.rb +5 -0
  28. data/manual.adoc +285 -0
  29. data/rake/cacert.pem +3894 -0
  30. data/rake/jdk_helper.rb +105 -0
  31. data/rake/tar-licence +19 -0
  32. data/rake/tar.rb +38 -0
  33. data/rspec/README.adoc +45 -0
  34. data/rspec/a.rb +79 -0
  35. data/rspec/b.rb +111 -0
  36. data/rspec/c.rb +121 -0
  37. data/rspec/data/tex1 +65 -0
  38. data/rspec/data/tex2 +5 -0
  39. data/rspec/data/tex2.expect +5 -0
  40. data/rspec/transform.rb +36 -0
  41. data/spec/README.adoc +45 -0
  42. data/spec/a.rb +79 -0
  43. data/spec/b.rb +111 -0
  44. data/spec/c.rb +121 -0
  45. data/spec/data/foo +1 -0
  46. data/spec/data/lorem +1 -0
  47. data/spec/data/tex1 +65 -0
  48. data/spec/data/tex2 +5 -0
  49. data/spec/data/tex2.expect +5 -0
  50. data/spec/transform.rb +36 -0
  51. data/test/examples/adoc/env.adoc +16 -0
  52. data/test/examples/adoc/eq.adoc +12 -0
  53. data/test/examples/adoc/zero.adoc +3 -0
  54. data/test/examples/asciidoc-html/block_open.adoc +17 -0
  55. data/test/examples/tex/env.tex +132 -0
  56. data/test/examples/tex/eq.tex +132 -0
  57. data/test/examples/tex/zero.tex +121 -0
  58. data/test/html_test.rb +8 -0
  59. data/test/latex_test.rb +8 -0
  60. data/test/test_helper.rb +4 -0
  61. data/test_jc/admonition.adoc +4 -0
  62. data/test_jc/click.adoc +7 -0
  63. data/test_jc/env.adoc +31 -0
  64. data/test_jc/example.adoc +17 -0
  65. data/test_jc/floating_title.adoc +15 -0
  66. data/test_jc/image.adoc +84 -0
  67. data/test_jc/images/frog.jpg +0 -0
  68. data/test_jc/images/red_frog.jpeg +0 -0
  69. data/test_jc/images/yellow_frog.jpeg +0 -0
  70. data/test_jc/listing.adoc +9 -0
  71. data/test_jc/lists.adoc +20 -0
  72. data/test_jc/math.adoc +10 -0
  73. data/test_jc/preamble.adoc +14 -0
  74. data/test_jc/section-numbered.adoc +9 -0
  75. data/test_jc/section.adoc +7 -0
  76. data/test_jc/sidebar.adoc +61 -0
  77. data/test_jc/verse.adoc +15 -0
  78. data/try-out/README.adoc +348 -0
  79. data/try-out/click.adoc +108 -0
  80. data/try-out/code.adoc +122 -0
  81. data/try-out/env.adoc +139 -0
  82. data/try-out/eq-latex.adoc +37 -0
  83. data/try-out/eq-stem.adoc +41 -0
  84. data/try-out/eqno-latex.adoc +120 -0
  85. data/try-out/math_article.adoc +138 -0
  86. data/try-out/pyth-stem.adoc +52 -0
  87. data/try-out/theorem-latex.adoc +50 -0
  88. data/try-out/xref-equations.adoc +28 -0
  89. metadata +210 -0
@@ -0,0 +1,35 @@
1
+ class String
2
+
3
+ def blue
4
+ "\e[1;34m#{self}\e[0m"
5
+ end
6
+
7
+ def green
8
+ "\e[1;32m#{self}\e[0m"
9
+ end
10
+
11
+ def red
12
+ "\e[1;31m#{self}\e[0m"
13
+ end
14
+
15
+ def yellow
16
+ "\e[1;33m#{self}\e[0m"
17
+ end
18
+
19
+ def magenta
20
+ "\e[1;35m#{self}\e[0m"
21
+ end
22
+
23
+ def cyan
24
+ "\e[1;36m#{self}\e[0m"
25
+ end
26
+
27
+ def white
28
+ "\e[1;37m#{self}\e[0m"
29
+ end
30
+
31
+ def black
32
+ "\e[1;30m#{self}\e[0m"
33
+ end
34
+
35
+ end
@@ -0,0 +1,28 @@
1
+ require 'asciidoctor'
2
+ require 'asciidoctor/extensions'
3
+
4
+ # a postprocessor to map the nonsense
5
+ # string 'DOLLOD' back to '$'
6
+ #
7
+ # This is used for the HTML backend
8
+ # in conjunction with the code
9
+ # in 'tex_preprocessor' which maps '\$' to
10
+ # 'DOLLOD'
11
+ #
12
+ # There should be a better solution to the
13
+ # vexing proble of dealing with both $ ... $
14
+ # for mathematics and '\$' for currency. But
15
+ # this works and wil have to do for now.
16
+ #
17
+ # @jirutka: Advice?
18
+ #
19
+ module Asciidoctor::LaTeX
20
+ # Map DOLLOD to $
21
+ class Dollar < Asciidoctor::Extensions::Postprocessor
22
+
23
+ def process document, output
24
+ output = output.gsub('DOLLOD', '$')
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,17 @@
1
+ require 'asciidoctor'
2
+ require 'asciidoctor/extensions'
3
+ require 'htmlentities'
4
+
5
+ # Map HTML entties to their unicode equivalents
6
+ # before running LaTeX
7
+ #
8
+ module Asciidoctor::LaTeX
9
+ class EntToUni < Asciidoctor::Extensions::Postprocessor
10
+
11
+ def process document, output
12
+ coder = HTMLEntities.new
13
+ coder.decode output
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,190 @@
1
+ # Test doc: samples/env.adoc
2
+
3
+ # EnvironmentBlock implements constructs of the form
4
+ #
5
+ # [env.TYPE]
6
+ # --
7
+ # foo, bar, etc.
8
+ # --
9
+ #
10
+ # e.g.,
11
+ #
12
+ # [env.theorem]
13
+ # --
14
+ # $2 + 2 = 4$. Cool!
15
+ # --
16
+ #
17
+ # TYPE can be anything, but certain values, e.g.,
18
+ # 'equation', 'equationalign', 'code' receive
19
+ # special handling.
20
+ #
21
+ # See http://www.noteshare.io/section/environments
22
+ #
23
+ #
24
+ #
25
+
26
+
27
+ # OLD NOTES
28
+ #
29
+ # EnvironmentBlock is a first draft for a better
30
+ # way of handing a construct in Asciidoc that
31
+ # will map to LaTeX environments. See
32
+ # issue #1 in asciidoctor/asciidoctor-latex.
33
+ #
34
+ # The code below is based on @mojavelinux's
35
+ # outline. The EnvironmentBlock is called
36
+ # into action (please ... give me a more
37
+ # precise phrase here!) when text
38
+ # of the form [env.foo] is encountered.
39
+ # This is the signal to create a block
40
+ # of type environment. (Is this correct?)
41
+ # and environment-type "foo"
42
+ #
43
+ # In the act of creating an environment
44
+ # block, information extracted from
45
+ # [env.foo] is used to title the block
46
+ # as "Foo n", where n is a counter for
47
+ # environments of type "foo". The
48
+ # counter is created in a hash the first
49
+ # time an environment of that kind
50
+ # is encountered. We set
51
+ #
52
+ # counter["foo"] = 1
53
+ #
54
+ # Subsequent encounters cause the
55
+ # counter to be incremented.
56
+ #
57
+ # Later, when the backend process the AST,
58
+ # the information bundled by the
59
+ # EnvironmentBlock is used as is
60
+ # appropriate. In the case of conversion
61
+ # to LaTeX, the content of the block
62
+ # simply enclosed in delimiters as
63
+ # follows:
64
+ #
65
+ # \begin{foo} CONTENT \end{foo}
66
+ #
67
+ # Additionally, label information
68
+ # for cross-referencing is added at
69
+ # this stage.
70
+ #
71
+ # If, on the other hand, the backend
72
+ # is HTML, then the title (with numbering)
73
+ # that is extracted from [env.foo] is used
74
+ # to title the block. Additional styling
75
+ # is added so as to conform to LaTeX
76
+ # conventions: the body of the block is
77
+ # italicized.
78
+
79
+ require 'asciidoctor'
80
+ require 'asciidoctor/extensions'
81
+ require 'asciidoctor/latex/core_ext/colored_string'
82
+
83
+
84
+ module Asciidoctor::LaTeX
85
+
86
+
87
+ class EnvironmentBlock < Asciidoctor::Extensions::BlockProcessor
88
+
89
+ use_dsl
90
+
91
+ named :env
92
+ on_context :open
93
+ # parse_context_as :complex
94
+ # ^^^ The above line gave me an error. I'm not sure what do to with it.
95
+
96
+ def process parent, reader, attrs
97
+
98
+ warn "env: attributes = #{attrs}".yellow if $VERBOSE
99
+
100
+ # Get orginal title if there is one
101
+ if attrs['title']
102
+ original_title = attrs['title']
103
+ attrs['original_title'] = original_title
104
+ else
105
+ original_title = nil
106
+ end
107
+
108
+
109
+ # Ensure that role is defined
110
+ if attrs['role'] == nil
111
+ role = 'item'
112
+ else
113
+ role = attrs['role']
114
+ end
115
+
116
+ # fixme: this should not be necessary
117
+ if attrs['role'] =~ /\\/
118
+ attrs['role'] = attrs['role'].gsub(/\\/, '')
119
+ end
120
+
121
+ # Determine whether this is a numbered block
122
+ # FIXME: what if there are several options?
123
+ if attrs['options'].nil?
124
+ attrs['options'] = 'numbered'
125
+ elsif !(attrs['options'].include? 'no-number')
126
+ # attrs['options'] = 'numbered'
127
+ end
128
+
129
+
130
+ env_name = role # roles.first # FIXME: roles.first is probably best
131
+ if %w(equation equationalign chem).include? role
132
+ attrs['title'] = env_name
133
+ elsif role == 'code'
134
+ attrs['title'] = 'Listing'
135
+ elsif role == 'jsxgraph'
136
+ attrs['title'] = 'JSXGraph'
137
+ else
138
+ attrs['title'] = env_name.capitalize
139
+ end
140
+ env_title = attrs['title']
141
+
142
+
143
+ if attrs['role'] == 'code'
144
+ block = create_block parent, :listing, reader.lines, attrs
145
+ else
146
+ block = create_block parent, :environment, reader.lines, attrs
147
+ end
148
+
149
+ warn "document.references".blue + " #{parent.document.references}".cyan if $VERBOSE
150
+ warn "attrs['role'] = #{attrs['role']} and role = #{role}".red if $VERBOSE
151
+ warn "id".red + " = #{attrs['id']}".yellow if $VERBOSE
152
+
153
+ if attrs['options']['numbered']
154
+ if env_name == 'equationalign'
155
+ env_ref_prefix = 'equation'
156
+ else
157
+ env_ref_prefix = env_name
158
+ end
159
+ caption_num = parent.document.counter_increment("#{env_ref_prefix}-number", block)
160
+ attrs['caption-num'] = caption_num
161
+ caption = "#{caption_num}"
162
+ if original_title
163
+ attrs['title'] = "#{env_title} #{caption_num}: #{original_title}"
164
+ else
165
+ attrs['title'] = "#{env_title} #{caption_num}."
166
+ end
167
+ warn "eb: ".blue + "caption: #{caption}, title = #{attrs['title']}".magenta if $VERBOSE
168
+ else
169
+ attrs['title'] = "#{env_title}"
170
+ warn "eb: ".blue + "caption: #{caption}, title = #{attrs['title']}".magenta if $VERBOSE
171
+ end
172
+
173
+ if attrs['role'] == 'code'
174
+ caption = nil
175
+ end
176
+
177
+ warn "attributes are now #{attrs}" if $VERBOSE
178
+
179
+ block.assign_caption caption
180
+ if %w(equation equationalign chem).include? role
181
+ block.title = "#{caption_num}"
182
+ else
183
+ block.title = attrs['title']
184
+ end
185
+ block
186
+
187
+ end
188
+
189
+ end
190
+ end
@@ -0,0 +1,49 @@
1
+
2
+
3
+ require 'asciidoctor'
4
+ require 'asciidoctor/extensions'
5
+ require 'asciidoctor/latex/core_ext/colored_string'
6
+
7
+ #
8
+ module Asciidoctor::LaTeX
9
+
10
+ class InjectHTML < Asciidoctor::Extensions::Postprocessor
11
+
12
+ def process document, output
13
+ output = output.gsub('</head>', $click_insertion)
14
+ end
15
+
16
+ end
17
+
18
+ $click_insertion = <<EOF
19
+
20
+ <style>
21
+ .click .title { color: blue; }'
22
+ </style>
23
+
24
+
25
+ <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
26
+
27
+ <script>
28
+
29
+ $(document).ready(function(){
30
+ $('.openblock.click').click( function() { $(this).find('.content').slideToggle('200');
31
+ $.reloadMathJax() } )
32
+ $('.openblock.click').find('.content').hide()
33
+ });
34
+
35
+ $(document).ready(function(){
36
+ $('.listingblock.click').click( function() { $(this).find('.content').slideToggle('200') } )
37
+ $('.listingblock.click').find('.content').hide()
38
+ });
39
+
40
+
41
+ $(document).ready(ready);
42
+ $(document).on('page:load', ready);
43
+ </script>
44
+
45
+ </head>
46
+
47
+ EOF
48
+
49
+ end
@@ -0,0 +1,20 @@
1
+
2
+ require 'asciidoctor'
3
+ require 'asciidoctor/extensions'
4
+ require 'asciidoctor/latex/core_ext/colored_string'
5
+
6
+ # Implements constructs like chem::[2H2O + O2 -> 2H2O]
7
+ # Maps this to \( \ce{2H2O + O2 -> 2H2O} \)
8
+ #
9
+ module Asciidoctor::LaTeX
10
+
11
+ class ChemInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
12
+ use_dsl
13
+ named :chem
14
+ def process parent, target, attributes
15
+ text = attributes.values * ', ' # iky!
16
+ %(\\(\\ce{ #{text} }\\))
17
+ end
18
+ end
19
+
20
+ end
@@ -0,0 +1,49 @@
1
+ require 'asciidoctor'
2
+ require 'asciidoctor/extensions'
3
+
4
+ module Asciidoctor::LaTeX
5
+
6
+
7
+ # Prepend lines (in this case tex macro definitions) to a document
8
+
9
+
10
+ class MacroInsert < Asciidoctor::Extensions::Preprocessor
11
+
12
+ def putline line
13
+ @@line_array += [line, ""]
14
+ end
15
+
16
+ def process document, reader
17
+
18
+ file_contents = IO.read('macros.tex')
19
+ if file_contents == nil
20
+ file_contents = IO.read('public/macros.tex')
21
+ end
22
+ if file_contents
23
+ warn "In MacroInsert, file_contents: #{file_contents.length} chars read".yellow if $VERBOSE
24
+ else
25
+ warn "In MacroInsert, file_contents: NIL".yellow if $VERBOSE
26
+ end
27
+
28
+ @@line_array = []
29
+
30
+ return reader if reader.eof?
31
+
32
+ putline '++++'
33
+ putline "<div class='hide'>"
34
+ putline '\('
35
+ lines = file_contents.split("\n")
36
+ lines.each do |line|
37
+ putline line
38
+ end
39
+
40
+ putline '\)'
41
+ putline '</div>'
42
+ putline '++++'
43
+
44
+ reader.unshift_lines @@line_array
45
+ reader
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,695 @@
1
+ require 'asciidoctor'
2
+ require 'asciidoctor/latex/core_ext/colored_string'
3
+
4
+
5
+ # The classes in node_processor implement the
6
+ # latex backend for Asciidoctor-latex. This
7
+ # module is far from complete.
8
+
9
+ module Asciidoctor
10
+ class Document
11
+
12
+ # Write preamble for tex file, write closing
13
+ # \end{document}
14
+ #
15
+ # This method reads several files:
16
+ #
17
+ # - preamble: required boilerplate
18
+ #
19
+ # - asciidoc_tex_macros: needed to translate certain
20
+ # asciidoc constructs, e.g., quotes, into tex
21
+ # construct -- one TeX defintion per asciidoc construct
22
+ #
23
+ # - macros: supplied by the user. We need a good
24
+ # mechanism for identifying and reading the user's
25
+ # macro definitions. In noteshare there is a database
26
+ # field
27
+ #
28
+
29
+
30
+ def tex_process
31
+ warn "Node: #{self.class}".blue if $VERBOSE
32
+
33
+ doc = ''
34
+
35
+ # warn "document.attributes['header'] = #{document.attributes['header']}".magenta if $VERBOSE
36
+
37
+ unless embedded? or document.attributes['header']=='no'
38
+ doc << "%% Preamble %%\n"
39
+ if File.exist? 'preamble.tex'
40
+ preamble = IO.read('preamble.tex')
41
+ warn "preamble: #{preamble.length} chars".yellow
42
+ doc << preamble << "\n "
43
+ else
44
+ doc << File.open(File.join(LaTeX::DATA_DIR, "preamble_#{self.document.doctype}.tex"), 'r') { |f| f.read }
45
+ end
46
+ doc << "%% Asciidoc TeX Macros %%\n"
47
+ doc << File.open(File.join(LaTeX::DATA_DIR, 'asciidoc_tex_macros.tex'), 'r') { |f| f.read }
48
+ doc << "%% User Macros %%\n"
49
+ # doc << File.open(File.join(LaTeX::DATA_DIR, 'macros.tex'), 'r') { |f| f.read }
50
+ if File.exist? 'macros.tex'
51
+ macros = IO.read('macros.tex')
52
+ warn "macros: #{macros.length} chars".yellow
53
+ doc << macros
54
+ else
55
+ warn "Could not find file macros.tex".yellow
56
+ end
57
+ if File.exist?('myEnvironments.tex')
58
+ warn "I will take input from myEnvironments.tex".blue
59
+ doc << "\\input myEnvironments.tex\n"
60
+ else
61
+ warn "I will take input from newEnvironments.tex".blue
62
+ # doc << "\\input newEnvironments.tex\n"
63
+ end
64
+
65
+ doc << "%% Front Matter %%"
66
+ doc << "\n\n\\title\{#{self.doctitle}\}\n"
67
+ doc << "\\author\{#{self.author}\}\n"
68
+ doc << "\\date\{#{self.revdate}\}\n\n\n"
69
+ doc << "%% Begin Document %%"
70
+ # doc << "\n\n\\begin\{document\}\n"
71
+ doc << "\n\n\\begin\{document\}\n"
72
+ doc << "\\maketitle\n"
73
+ if self.attributes["toc"]
74
+ doc << "\\tableofcontents\n"
75
+ end
76
+ end
77
+
78
+
79
+
80
+ processed_content = LaTeX::TeXBlock.process_environments self.content
81
+ doc << processed_content
82
+
83
+ unless embedded?
84
+ # Now write the defnitions of the new environments
85
+ # discovered to file
86
+ warn "Writing environment definitions to file: newEnvironments.tex" if $VERBOSE
87
+ definitions = ""
88
+
89
+ $latex_environment_names.uniq.each do |name|
90
+ warn name if $VERBOSE
91
+ definitions << "\\newtheorem\{#{name}\}\{#{name.capitalize}\}" << "\n"
92
+ end
93
+
94
+ File.open('newEnvironments.tex', 'w') { |f| f.write(definitions) }
95
+
96
+ # Output
97
+ doc << "\n\\end{document}\n" unless document.attributes['header']=='no'
98
+ end
99
+
100
+ doc << "\n"
101
+ end
102
+ end
103
+
104
+ # Write TeX for each of five levels of Ascidoc section,
105
+ # .e.g. \section{Introduction} for == Introduction
106
+ class Section
107
+
108
+ def tex_process
109
+ warn ["Node:".blue, "section[#{self.level}]:".cyan, "#{self.title}"].join(" ") if $VERBOSE
110
+ doctype = self.document.doctype
111
+
112
+ tags = { 'article' => [ 'part', 'section', 'subsection', 'subsubsection', 'paragraph' ],
113
+ 'book' => [ 'part', 'chapter', 'section', 'subsection', 'subsubsection', 'paragraph' ] }
114
+
115
+ tagname = tags[doctype][self.level]
116
+ tagsuffix = self.numbered ? '' : '*'
117
+
118
+ "\\#{tagname}#{tagsuffix}\{#{self.title}\}\n\n#{self.content}\n\n"
119
+ end
120
+ end
121
+
122
+
123
+ # Write TeX \itemize or \enumerate lists
124
+ # for ulist and olist. Recurses for
125
+ # nested lists.
126
+ class List
127
+
128
+ def tex_process
129
+ warn ["Node:".blue, "#{self.node_name}[#{self.level}]".cyan, "#{self.content.count} items"].join(" ") if $VERBOSE
130
+ case self.node_name
131
+ when 'dlist'
132
+ dlist_process
133
+ when 'ulist'
134
+ ulist_process
135
+ when 'olist'
136
+ olist_process
137
+ else
138
+ warn "This Asciidoctor::List, tex_process. I don't know how to do that (#{self.node_name})" if $VERBOSE
139
+ end
140
+ end
141
+
142
+ def dlist_process
143
+ list = "\\begin{description}\n\n"
144
+ self.items.each do |terms, dd|
145
+ list << "\\item["
146
+ [*terms].each do |dt|
147
+ warn [" -- item: ".blue, "#{dt.text}"].join(" ") if $VERBOSE
148
+ list << dt.text
149
+ end
150
+ list << "]"
151
+ if dd
152
+ list << dd.text << "\n\n" if dd.text?
153
+ list << dd.content << "\n" if dd.blocks?
154
+ end
155
+ end
156
+ list << "\\end{description}\n\n"
157
+ end
158
+
159
+ def ulist_process
160
+ list = "\\begin{itemize}\n\n"
161
+ self.content.each do |item|
162
+ warn [" -- item: ".blue, "#{item.text.split("\n").first}"].join(" ") if $VERBOSE
163
+ list << "\\item #{item.text}\n\n"
164
+ list << item.content
165
+ end
166
+ list << "\\end{itemize}\n\n"
167
+ end
168
+
169
+ def olist_process
170
+ list = "\\begin{enumerate}\n\n"
171
+ self.content.each do |item|
172
+ warn [" -- item: ".blue, "#{item.text.split("\n").first}"].join(" ") if $VERBOSE
173
+ list << "\\item #{item.text}\n\n"
174
+ list << item.content
175
+ end
176
+ list << "\\end{enumerate}\n\n"
177
+ end
178
+
179
+ end
180
+
181
+ # Proces block elements of varios kinds
182
+ class Block
183
+
184
+ # STANDARD_ENVIRONMENT_NAMES = %w(theorem proposition lemma definition example problem equation)
185
+ STANDARD_ENVIRONMENT_NAMES = %w(equation)
186
+
187
+ def tex_process
188
+ warn ["Node:".blue , "#{self.blockname}".blue].join(" ") if $VERBOSE
189
+ case self.blockname
190
+ when :paragraph
191
+ paragraph_process
192
+ when :stem
193
+ stem_process
194
+ when :admonition
195
+ admonition_process
196
+ when :page_break
197
+ page_break_process
198
+ when :literal
199
+ self.literal_process
200
+ when :pass
201
+ self.pass_process
202
+ when :quote
203
+ self.quote_process
204
+ when :open
205
+ self.open_process
206
+ when :environment
207
+ self.environment_process
208
+ when :click
209
+ self.click_process
210
+ when :listing
211
+ self.listing_process
212
+ when :example
213
+ self.example_process
214
+ when :floating_title
215
+ self.floating_title_process
216
+ when :image
217
+ self.image_process
218
+ when :preamble
219
+ self.preamble_process
220
+ when :sidebar
221
+ self.sidebar_process
222
+ when :verse
223
+ self.verse_process
224
+ # when :table
225
+ # self.table_process
226
+ else
227
+ warn "This is Asciidoctor::Block, tex_process. I don't know how to do that (#{self.blockname})" if $VERBOSE if $VERBOSE
228
+ ""
229
+ end
230
+ end
231
+
232
+
233
+ def paragraph_process
234
+ warn "paragraph attributes: #{self.attributes}".red if $VERBOSE
235
+ out = ""
236
+ if self.attributes['title']
237
+ out << "\{\\bf #{self.attributes['title']}\.}" << "\n"
238
+ end
239
+ out << LaTeX::TeXPostProcess.make_substitutions(self.content) << "\n\n"
240
+ end
241
+
242
+ def stem_process
243
+ warn ["Node:".blue, "#{self.blockname}".cyan].join(" ") if $VERBOSE
244
+ warn self.content.cyan if $VERBOSE
245
+ environment = LaTeX::TeXBlock.environment_type self.content
246
+ if LaTeX::TeXBlock::INNER_TYPES.include? environment
247
+ out = "\\\[\n#{LaTeX::TeXPostProcess.stem_substitutions self.content}\n\\\]\n"
248
+ warn out.yellow if $VERBOSE
249
+ out
250
+ else
251
+ self.content
252
+ end
253
+ end
254
+
255
+ def admonition_process
256
+ warn ["Node:".blue, "#{self.blockname}".cyan, "#{self.style}:".magenta, "#{self.lines[0]}"].join(" ") if $VERBOSE
257
+ "\\admonition\{#{self.style}\}\{#{self.content}\}\n"
258
+ end
259
+
260
+ def page_break_process
261
+ warn ["Node:".blue, "#{self.blockname}".cyan].join(" ") if $VERBOSE
262
+ "\n\\vfill\\eject\n"
263
+ end
264
+
265
+ def literal_process
266
+ warn ["Node:".magenta, "#{self.blockname}".cyan].join(" ") if $VERBOSE
267
+ "\\begin\{verbatim\}\n#{self.content}\n\\end\{verbatim\}\n"
268
+ end
269
+
270
+ def pass_process
271
+ warn ["Node:".magenta, "#{self.blockname}".cyan].join(" ") if $VERBOSE
272
+ self.content
273
+ end
274
+
275
+ def quote_process
276
+ warn ["Node:".magenta, "#{self.blockname}".cyan].join(" ") if $VERBOSE
277
+ if self.attr? 'attribution'
278
+ attribution = self.attr 'attribution'
279
+ citetitle = (self.attr? 'citetitle') ? (self.attr 'citetitle') : nil
280
+
281
+ "\\begin\{aquote\}{#{attribution}#{citetitle ? ' - ' + citetitle : ''}}\n#{self.content}\\end\{aquote\}\n"
282
+ else
283
+ "\\begin\{quote\}\n#{self.content}\\end\{quote\}\n"
284
+ end
285
+ end
286
+
287
+
288
+
289
+ def environment_process
290
+
291
+ warn "begin environment_process, ".blue + "title = #{self.title}".yellow if $VERBOSE
292
+ warn "environment attributes = #{self.attributes}".red if $VERBOSE
293
+ warn "role = #{self.attributes["role"]}" if $VERBOSE
294
+
295
+ env = self.attributes["role"]
296
+
297
+ # record any environments encountered but not built=in
298
+ if !STANDARD_ENVIRONMENT_NAMES.include? env and !$latex_environment_names.include? env
299
+ # if !($latex_environment_names.include? env)
300
+ warn "env added: [#{env}]".blue if $VERBOSE
301
+ $latex_environment_names << env
302
+ end
303
+
304
+ if self.id
305
+ label = "\n\\label\{#{self.id}\}"
306
+ else
307
+ label = ""
308
+ end
309
+
310
+ warn "self.attributes['original_title'] = #{self.attributes['original_title']}".cyan if $VERBOSE
311
+
312
+ if self.attributes['original_title']
313
+ title = "\{\\rm (#{self.attributes['original_title']}) \}"
314
+ else
315
+ title = ''
316
+ end
317
+
318
+ if env == 'listing'
319
+ output = "\\begin\{#{env}\}#{label}\\begin{verbatim}\n\n#{self.content}\\end{verbatim}\n\\end\{#{env}\}\n"
320
+ elsif env == 'equationalign'
321
+ output = "\\begin\{equation\}#{label}\n\\begin\{split\}\n#{self.content}\n\\end\{split\}\n\\end\{equation\}\n"
322
+ elsif env == 'chem'
323
+ output = "\\begin\{equation\}#{label}\n\\ce\{#{self.content}\}\n\\end\{equation\}\n"
324
+ else
325
+ output = "\\begin\{#{env}\}#{title}#{label}\n#{self.content}\n\\end\{#{env}\}\n"
326
+ end
327
+
328
+
329
+
330
+ output
331
+
332
+ end
333
+
334
+ def click_process
335
+
336
+ warn "begin click_process".blue + "title = #{self.title}".yellow if $VERBOSE
337
+
338
+ click = self.attributes["role"]
339
+ # record any environments encounted but not built=in
340
+ if !STANDARD_ENVIRONMENT_NAMES.include? click
341
+ $latex_environment_names << click
342
+ end
343
+
344
+ ### XXX fixme:
345
+ click = 'note'
346
+
347
+ if self.id == nil # No label
348
+ output = "\\begin\{#{click}\}\n#{self.content}\n\\end\{#{click}\}\n"
349
+ else
350
+ output = "\\begin\{#{click}\}\n\\label\{#{self.id}\}\n#{self.content}\\end\{#{click}\}\n"
351
+ end
352
+
353
+ warn "end click_process\n".blue if $VERBOSE
354
+
355
+ output
356
+
357
+ end
358
+
359
+ def report
360
+ # Report on this node
361
+ warn ["OPEN BLOCK:".magenta, "id: #{self.id}"].join(" ")
362
+ warn ["Node:".magenta, "#{self.blockname}".cyan].join(" ")
363
+ warn ["Attributes:".magenta, "#{self.attributes}".cyan].join(" ")
364
+ warn ["Title: ".magenta, title.cyan, "style:", self.style].join(" ") if title
365
+ warn ["Content:".magenta, "#{self.content}".yellow].join(" ")
366
+ warn ["Style:".green, "#{self.style}".red].join(" ")
367
+ warn ["METHODS:".red, "#{self.methods}".yellow].join(" ")
368
+ end
369
+
370
+
371
+ # Process open blocks. Map a block of the form
372
+ #
373
+ # .Foo
374
+ # [[hocus_pocus]]
375
+ # --
376
+ # La di dah
377
+ # --
378
+ #
379
+ # to
380
+ #
381
+ # \begin{Foo}
382
+ # \label{hocus_pocus}
383
+ # La di dah
384
+ # \end{Foo}
385
+ #
386
+ # This scheme enables one to map Asciidoc blocks to
387
+ # LaTeX environments with essentally no knoweldge
388
+ # of either other than their form.
389
+ #
390
+ def open_process
391
+
392
+ report if $VERBOSE
393
+
394
+ attr = self.attributes
395
+
396
+ warn "attributes (open block): #{self.attributes}" if $VERBOSE
397
+
398
+
399
+ # Get title !- nil or make a dummy one
400
+ title = self.attributes["title"]
401
+ if title == nil
402
+ title = "Dummy"
403
+ end
404
+
405
+
406
+
407
+
408
+ # strip constructs like {counter:theorem} from the title
409
+ title = title.gsub /\{.*?\}/, ""
410
+ title = title.strip
411
+
412
+ if attr['role'] == 'text-center'
413
+ "\\begin\{center\}\n#{self.content}\\end\{center\}"
414
+ else
415
+ self.content
416
+ end
417
+
418
+
419
+ end
420
+
421
+ def listing_process
422
+ warn ["Node:".magenta, "#{self.blockname}".cyan].join(" ") if $VERBOSE
423
+ warn "attributes: #{self.attributes}".cyan if $VERBOSE
424
+ "\\begin\{verbatim\}\n#{self.content}\n\\end\{verbatim\}\n"
425
+ end
426
+
427
+ def example_process
428
+ warn "exAmple_process".yellow
429
+ warn ["Node:".magenta, "#{self.blockname}".cyan].join(" ") if $VERBOSE
430
+ warn "attributes: #{self.attributes}".cyan if $VERBOSE
431
+ # self.content_model = :verbatim
432
+ warn "content: #{self.content}".cyan if $VERBOSE
433
+ "\\begin\{verbatim\}\n#{self.content}\n\\end\{verbatim\}\n"
434
+ end
435
+
436
+
437
+ def floating_title_process
438
+ warn ["Node:".blue, "section[#{self.level}]:".cyan, "#{self.title}"].join(" ") if $VERBOSE
439
+ doctype = self.document.doctype
440
+
441
+ tags = { 'article' => [ 'part', 'section', 'subsection', 'subsubsection', 'paragraph' ],
442
+ 'book' => [ 'part', 'chapter', 'section', 'subsection', 'subsubsection', 'paragraph' ] }
443
+
444
+ tagname = tags[doctype][self.level]
445
+
446
+ "\\#{tagname}*\{#{self.title}\}\n\n#{self.content}\n\n"
447
+ end
448
+
449
+ def image_process
450
+ warn ["IXX: Node:".magenta, "#{self.blockname}".cyan].join(" ") if $VERBOSE
451
+ warn "IXX: attributes: #{self.attributes}".cyan if $VERBOSE
452
+ if self.attributes['width']
453
+ width = "#{self.attributes['width'].to_f/100.0}truein"
454
+ else
455
+ width = '2.5truein'
456
+ end
457
+ raw_image = self.attributes['target']
458
+ if document.attributes['noteshare'] == 'yes'
459
+ warn "IXX: extracting image name".red if $VERBOSE
460
+ image_rx = /image.*original\/(.*)\?/
461
+ match_data = raw_image.match image_rx
462
+ if match_data
463
+ image = match_data[1]
464
+ warn "IXX: image name: #{image}".red if $VERBOSE
465
+ else
466
+ image = "undefined"
467
+ end
468
+ else
469
+ image = raw_image
470
+ end
471
+ caption = "\\caption\{#{self.attributes['title']}\}"
472
+ refs = self.parent.document.references # [:ids]
473
+ if self.attributes['align'] == 'center'
474
+ align = '\\centering'
475
+ else
476
+ align = ''
477
+ end
478
+ float = self.attributes['float']
479
+ if float
480
+ figure_type = 'wrapfigure'
481
+ ftext_width = width # '0.45\\textwidth'
482
+ caption=''
483
+ else
484
+ figure_type = 'figure'
485
+ text_width = ''
486
+ end
487
+ case float
488
+ when 'left'
489
+ position = '{l}'
490
+ when 'right'
491
+ position = '{r}'
492
+ else
493
+ position = '[h]'
494
+ end
495
+ "\\begin{#{figure_type}}#{position}\{#{ftext_width}\}\n\\includegraphics[width=#{width}]{#{image}}\n#{caption}\n#{align}\n\\end{#{figure_type}}\n"
496
+ end
497
+
498
+ def preamble_process
499
+ "\\begin\{preamble\}\n#{self.content}\n\\end\{preamble\}\n"
500
+ end
501
+
502
+
503
+ def sidebar_process
504
+ warn "sidebar_process".yellow if $VERBOSE
505
+ warn ["Node:".magenta, "#{self.blockname}".cyan].join(" ") if $VERBOSE
506
+ warn "attributes: #{self.attributes}".cyan if $VERBOSE
507
+ "\\begin\{sidebar\}\n#{self.content}\n\\end\{sidebar\}\n"
508
+ end
509
+
510
+ def verse_process
511
+ warn "verse_process".yellow if $VERBOSE
512
+ warn ["Node:".magenta, "#{self.blockname}".cyan].join(" ") if $VERBOSE
513
+ warn "attributes: #{self.attributes}".cyan if $VERBOSE
514
+ "\\begin\{alltt\}\n#{self.content}\n\\end\{alltt\}\n"
515
+ end
516
+
517
+ end # class Block
518
+
519
+ # Process inline elements
520
+ class Inline
521
+
522
+ def tex_process
523
+ case self.node_name
524
+ when 'inline_quoted'
525
+ self.inline_quoted_process
526
+ when 'inline_anchor'
527
+ self.inline_anchor_process
528
+ when 'inline_break'
529
+ self.inline_break_process
530
+ when 'inline_footnote'
531
+ self.inline_footnote_process
532
+ else
533
+ warn "This is Asciidoctor::Inline, tex_process. I don't know how to do that (#{self.node_name})".yellow if $VERBOSE
534
+ ""
535
+ end
536
+ end
537
+
538
+ def inline_quoted_process
539
+ warn ["Node:".blue, "#{self.node_name}".cyan, "type[#{self.type}], ".green + " text: #{self.text}"].join(" ") if $VERBOSE
540
+ case self.type
541
+ when :strong
542
+ #"\\textbf\{#{self.text}\}"
543
+ self.text
544
+ when :emphasis
545
+ "\\emph\{#{self.text}\}"
546
+ when :asciimath
547
+ "\$#{LaTeX::TeXPostProcess.stem_substitutions self.text}\$"
548
+ when :monospaced
549
+ "\{\\tt #{self.text}\}"
550
+ when :unquoted
551
+ role = self.attributes["role"]
552
+ warn " -- role = #{role}".yellow if $VERBOSE
553
+ if role == "red"
554
+ "\\rolered\{ #{self.text}\}"
555
+ else
556
+ warn "This is inline_quoted_process. I don't understand role = #{role}" if $VERBOSE
557
+ end
558
+ else
559
+ "\\unknown\\{#{self.text}\\}"
560
+ end
561
+ end
562
+
563
+ def inline_anchor_process
564
+
565
+ warn ["Node:".blue, "#{self.node_name}".magenta, "type[#{self.type}], ".green + " text: #{self.text} target: #{self.target}".cyan].join(" ") if $VERBOSE
566
+
567
+ refid = self.attributes['refid']
568
+ refs = self.parent.document.references[:ids]
569
+ # FIXME: the next line is HACKISH (and it crashes the app when refs[refid]) is nil)
570
+ # FIXME: and with the fix for nil results is even more hackish
571
+ # if refs[refid]
572
+ if refs[refid]
573
+ reftext = refs[refid].gsub('.', '')
574
+ m = reftext.match /(\d*)/
575
+ if m[1] == reftext
576
+ reftext = "(#{reftext})"
577
+ end
578
+ else
579
+ reftext = ""
580
+ end
581
+ case self.type
582
+ when :link
583
+ "\\href\{#{self.target}\}\{#{self.text}\}"
584
+ when :ref
585
+ "\\label\{#{self.text.gsub(/\[(.*?)\]/, "\\1")}\}"
586
+ when :xref
587
+ #"\\ref\{#{self.target.gsub('#','')}\}"
588
+ # warn "\\hyperlink\{#{refid}\}\{#{reftext}\}".yellow
589
+ "\\hyperlink\{#{refid}\}\{#{reftext}\}"
590
+ else
591
+ warn "!! : undefined inline anchor -----------".magenta if $VERBOSE
592
+ end
593
+ end
594
+
595
+ def inline_break_process
596
+ warn ["Node:".blue, "#{self.node_name}".cyan, "type[#{self.type}], ".green + " text: #{self.text}"].join(" ") if $VERBOSE
597
+ "#{self.text} \\\\"
598
+ end
599
+
600
+ def inline_footnote_process
601
+ warn ["Node:".blue, "#{self.node_name}".cyan, "type[#{self.type}], ".green + " text: #{self.text}"].join(" ") if $VERBOSE
602
+ # warn self.content.yellow
603
+ # warn self.style.magenta
604
+ "\\footnote\{#{self.text}\}"
605
+ end
606
+
607
+ end
608
+
609
+ class Table
610
+
611
+ def tex_process
612
+ # warn "This is Asciidoctor::Table, tex_process. I don't know how to do that".yellow + " (#{self.node_name})".magenta if $VERBOSE
613
+ # table = Table.new self.parent, self.attributes
614
+ n_rows = self.rows.body.count
615
+ n_columns = self.columns.count
616
+ alignment = (['c']*n_columns).join('|')
617
+ output = "\\begin\{center\}\n"
618
+ output << "\\begin\{tabular\}\{|#{alignment}|\}\n"
619
+ output << "\\hline\n"
620
+ self.rows.body.each_with_index do |row, index|
621
+ row_array = []
622
+ row.each do |cell|
623
+ row_array << cell.content[0]
624
+ end
625
+ output << row_array.join(' & ')
626
+ output << " \\\\ \n"
627
+ end
628
+ output << "\\hline\n"
629
+ output << "\\end{tabular}\n"
630
+ output << "\\end{center}\n"
631
+ "#{output}"
632
+ end
633
+
634
+
635
+ end
636
+
637
+
638
+ module LaTeX
639
+ # TeXPostProcess cleans up undesired transformations
640
+ # inside the TeX enveronment. Strings
641
+ # &ampp;, &gt;, &lt; are mapped back to
642
+ # &, >, < and \\ is conserved.
643
+ module TeXPostProcess
644
+
645
+ def self.match_inline str
646
+ rx_tex_inline = /\$(.*?)\$/
647
+ str.scan rx_tex_inline
648
+ end
649
+
650
+ def self.match_block str
651
+ rx_tex_block = /\\\[(.*?)\\\]/m
652
+ str.scan rx_tex_block
653
+ end
654
+
655
+ def self.make_substitutions1 str
656
+ str = str.gsub("&amp;", "&")
657
+ str = str.gsub("&gt;", ">")
658
+ str = str.gsub("&lt;", "<")
659
+ end
660
+
661
+ def self.make_substitutions_in_matches matches, str
662
+ matches.each do |m|
663
+ m_str = m[0]
664
+ m_transformed = make_substitutions1 m_str
665
+ str = str.gsub(m_str,m_transformed)
666
+ end
667
+ str
668
+ end
669
+
670
+ # (1) & (2) are needed together to protect \\
671
+ # inside of matrices, etc.
672
+ def self.make_substitutions str
673
+ str = str.gsub('\\\\', '@@') # (1)
674
+ matches = match_inline str
675
+ if matches.count > 0
676
+ str = make_substitutions_in_matches matches, str
677
+ end
678
+ matches = match_block str
679
+ if matches.count > 0
680
+ str = make_substitutions_in_matches matches, str
681
+ end
682
+ str = str.tr('@','\\') # (2)
683
+ str
684
+ end
685
+
686
+ def self.stem_substitutions str
687
+ str = str.gsub('\\\\', '@@') # (1)
688
+ str = make_substitutions1 str
689
+ str = str.tr('@','\\') # (2)
690
+ str
691
+ end
692
+
693
+ end
694
+ end
695
+ end