gitdown 0.0.1

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 (416) hide show
  1. data/AUTHORS +1 -0
  2. data/COPYING +24 -0
  3. data/GPL +674 -0
  4. data/README +43 -0
  5. data/Rakefile +370 -0
  6. data/VERSION +1 -0
  7. data/benchmark/benchmark.rb +34 -0
  8. data/benchmark/benchmark.sh +74 -0
  9. data/benchmark/generate_data.rb +119 -0
  10. data/benchmark/mdbasics.text +306 -0
  11. data/benchmark/mdsyntax.text +888 -0
  12. data/benchmark/testing.sh +9 -0
  13. data/benchmark/timing.sh +10 -0
  14. data/bin/kramdown +78 -0
  15. data/data/kramdown/document.html +18 -0
  16. data/data/kramdown/document.latex +43 -0
  17. data/doc/default.scss.css +530 -0
  18. data/doc/default.template +80 -0
  19. data/doc/documentation.page +71 -0
  20. data/doc/index.page +98 -0
  21. data/doc/installation.page +88 -0
  22. data/doc/links.markdown +6 -0
  23. data/doc/news.feed +10 -0
  24. data/doc/news.page +28 -0
  25. data/doc/quickref.page +585 -0
  26. data/doc/syntax.page +1644 -0
  27. data/doc/tests.page +52 -0
  28. data/doc/virtual +2 -0
  29. data/lib/kramdown.rb +23 -0
  30. data/lib/kramdown/compatibility.rb +35 -0
  31. data/lib/kramdown/converter.rb +41 -0
  32. data/lib/kramdown/converter/base.rb +169 -0
  33. data/lib/kramdown/converter/html.rb +410 -0
  34. data/lib/kramdown/converter/kramdown.rb +422 -0
  35. data/lib/kramdown/converter/latex.rb +607 -0
  36. data/lib/kramdown/converter/toc.rb +82 -0
  37. data/lib/kramdown/document.rb +117 -0
  38. data/lib/kramdown/element.rb +524 -0
  39. data/lib/kramdown/error.rb +30 -0
  40. data/lib/kramdown/options.rb +373 -0
  41. data/lib/kramdown/parser.rb +40 -0
  42. data/lib/kramdown/parser/base.rb +136 -0
  43. data/lib/kramdown/parser/github_markdown.rb +44 -0
  44. data/lib/kramdown/parser/github_markdown/github_codeblock.rb +44 -0
  45. data/lib/kramdown/parser/html.rb +570 -0
  46. data/lib/kramdown/parser/kramdown.rb +338 -0
  47. data/lib/kramdown/parser/kramdown/abbreviation.rb +71 -0
  48. data/lib/kramdown/parser/kramdown/autolink.rb +53 -0
  49. data/lib/kramdown/parser/kramdown/blank_line.rb +43 -0
  50. data/lib/kramdown/parser/kramdown/block_boundary.rb +46 -0
  51. data/lib/kramdown/parser/kramdown/blockquote.rb +51 -0
  52. data/lib/kramdown/parser/kramdown/codeblock.rb +63 -0
  53. data/lib/kramdown/parser/kramdown/codespan.rb +56 -0
  54. data/lib/kramdown/parser/kramdown/emphasis.rb +70 -0
  55. data/lib/kramdown/parser/kramdown/eob.rb +39 -0
  56. data/lib/kramdown/parser/kramdown/escaped_chars.rb +38 -0
  57. data/lib/kramdown/parser/kramdown/extensions.rb +204 -0
  58. data/lib/kramdown/parser/kramdown/footnote.rb +74 -0
  59. data/lib/kramdown/parser/kramdown/header.rb +68 -0
  60. data/lib/kramdown/parser/kramdown/horizontal_rule.rb +39 -0
  61. data/lib/kramdown/parser/kramdown/html.rb +169 -0
  62. data/lib/kramdown/parser/kramdown/html_entity.rb +44 -0
  63. data/lib/kramdown/parser/kramdown/line_break.rb +38 -0
  64. data/lib/kramdown/parser/kramdown/link.rb +148 -0
  65. data/lib/kramdown/parser/kramdown/list.rb +240 -0
  66. data/lib/kramdown/parser/kramdown/math.rb +64 -0
  67. data/lib/kramdown/parser/kramdown/paragraph.rb +63 -0
  68. data/lib/kramdown/parser/kramdown/smart_quotes.rb +214 -0
  69. data/lib/kramdown/parser/kramdown/table.rb +178 -0
  70. data/lib/kramdown/parser/kramdown/typographic_symbol.rb +52 -0
  71. data/lib/kramdown/parser/markdown.rb +69 -0
  72. data/lib/kramdown/utils.rb +37 -0
  73. data/lib/kramdown/utils/entities.rb +348 -0
  74. data/lib/kramdown/utils/html.rb +85 -0
  75. data/lib/kramdown/utils/ordered_hash.rb +100 -0
  76. data/lib/kramdown/version.rb +28 -0
  77. data/setup.rb +1585 -0
  78. data/test/run_tests.rb +59 -0
  79. data/test/test_files.rb +197 -0
  80. data/test/testcases/block/01_blank_line/spaces.html +1 -0
  81. data/test/testcases/block/01_blank_line/spaces.text +3 -0
  82. data/test/testcases/block/01_blank_line/tabs.html +1 -0
  83. data/test/testcases/block/01_blank_line/tabs.text +6 -0
  84. data/test/testcases/block/02_eob/beginning.html +1 -0
  85. data/test/testcases/block/02_eob/beginning.text +3 -0
  86. data/test/testcases/block/02_eob/end.html +1 -0
  87. data/test/testcases/block/02_eob/end.text +3 -0
  88. data/test/testcases/block/02_eob/middle.html +1 -0
  89. data/test/testcases/block/02_eob/middle.text +5 -0
  90. data/test/testcases/block/03_paragraph/indented.html +18 -0
  91. data/test/testcases/block/03_paragraph/indented.text +19 -0
  92. data/test/testcases/block/03_paragraph/no_newline_at_end.html +5 -0
  93. data/test/testcases/block/03_paragraph/no_newline_at_end.text +5 -0
  94. data/test/testcases/block/03_paragraph/one_para.html +1 -0
  95. data/test/testcases/block/03_paragraph/one_para.text +1 -0
  96. data/test/testcases/block/03_paragraph/two_para.html +4 -0
  97. data/test/testcases/block/03_paragraph/two_para.text +4 -0
  98. data/test/testcases/block/04_header/atx_header.html +37 -0
  99. data/test/testcases/block/04_header/atx_header.text +34 -0
  100. data/test/testcases/block/04_header/atx_header_no_newline_at_end.html +1 -0
  101. data/test/testcases/block/04_header/atx_header_no_newline_at_end.text +1 -0
  102. data/test/testcases/block/04_header/setext_header.html +30 -0
  103. data/test/testcases/block/04_header/setext_header.html.19 +30 -0
  104. data/test/testcases/block/04_header/setext_header.text +36 -0
  105. data/test/testcases/block/04_header/setext_header_no_newline_at_end.html +1 -0
  106. data/test/testcases/block/04_header/setext_header_no_newline_at_end.text +2 -0
  107. data/test/testcases/block/04_header/with_auto_id_prefix.html +3 -0
  108. data/test/testcases/block/04_header/with_auto_id_prefix.options +2 -0
  109. data/test/testcases/block/04_header/with_auto_id_prefix.text +3 -0
  110. data/test/testcases/block/04_header/with_auto_ids.html +17 -0
  111. data/test/testcases/block/04_header/with_auto_ids.options +1 -0
  112. data/test/testcases/block/04_header/with_auto_ids.text +19 -0
  113. data/test/testcases/block/05_blockquote/indented.html +25 -0
  114. data/test/testcases/block/05_blockquote/indented.text +14 -0
  115. data/test/testcases/block/05_blockquote/lazy.html +34 -0
  116. data/test/testcases/block/05_blockquote/lazy.text +20 -0
  117. data/test/testcases/block/05_blockquote/nested.html +10 -0
  118. data/test/testcases/block/05_blockquote/nested.text +6 -0
  119. data/test/testcases/block/05_blockquote/no_newline_at_end.html +4 -0
  120. data/test/testcases/block/05_blockquote/no_newline_at_end.text +2 -0
  121. data/test/testcases/block/05_blockquote/very_long_line.html +3 -0
  122. data/test/testcases/block/05_blockquote/very_long_line.text +1 -0
  123. data/test/testcases/block/05_blockquote/with_code_blocks.html +15 -0
  124. data/test/testcases/block/05_blockquote/with_code_blocks.text +11 -0
  125. data/test/testcases/block/06_codeblock/error.html +4 -0
  126. data/test/testcases/block/06_codeblock/error.text +4 -0
  127. data/test/testcases/block/06_codeblock/lazy.html +4 -0
  128. data/test/testcases/block/06_codeblock/lazy.text +5 -0
  129. data/test/testcases/block/06_codeblock/no_newline_at_end.html +2 -0
  130. data/test/testcases/block/06_codeblock/no_newline_at_end.text +1 -0
  131. data/test/testcases/block/06_codeblock/no_newline_at_end_1.html +2 -0
  132. data/test/testcases/block/06_codeblock/no_newline_at_end_1.text +2 -0
  133. data/test/testcases/block/06_codeblock/normal.html +13 -0
  134. data/test/testcases/block/06_codeblock/normal.text +10 -0
  135. data/test/testcases/block/06_codeblock/tilde_syntax.html +7 -0
  136. data/test/testcases/block/06_codeblock/tilde_syntax.text +9 -0
  137. data/test/testcases/block/06_codeblock/whitespace.html +3 -0
  138. data/test/testcases/block/06_codeblock/whitespace.text +3 -0
  139. data/test/testcases/block/06_codeblock/with_blank_line.html +13 -0
  140. data/test/testcases/block/06_codeblock/with_blank_line.text +12 -0
  141. data/test/testcases/block/06_codeblock/with_eob_marker.html +6 -0
  142. data/test/testcases/block/06_codeblock/with_eob_marker.text +5 -0
  143. data/test/testcases/block/06_codeblock/with_ial.html +6 -0
  144. data/test/testcases/block/06_codeblock/with_ial.text +5 -0
  145. data/test/testcases/block/07_horizontal_rule/error.html +7 -0
  146. data/test/testcases/block/07_horizontal_rule/error.html.19 +7 -0
  147. data/test/testcases/block/07_horizontal_rule/error.text +7 -0
  148. data/test/testcases/block/07_horizontal_rule/normal.html +17 -0
  149. data/test/testcases/block/07_horizontal_rule/normal.text +17 -0
  150. data/test/testcases/block/07_horizontal_rule/sepspaces.html +3 -0
  151. data/test/testcases/block/07_horizontal_rule/sepspaces.text +3 -0
  152. data/test/testcases/block/07_horizontal_rule/septabs.html +3 -0
  153. data/test/testcases/block/07_horizontal_rule/septabs.text +3 -0
  154. data/test/testcases/block/08_list/escaping.html +17 -0
  155. data/test/testcases/block/08_list/escaping.text +17 -0
  156. data/test/testcases/block/08_list/item_ial.html +10 -0
  157. data/test/testcases/block/08_list/item_ial.text +8 -0
  158. data/test/testcases/block/08_list/lazy.html +39 -0
  159. data/test/testcases/block/08_list/lazy.text +29 -0
  160. data/test/testcases/block/08_list/list_and_hr.html +9 -0
  161. data/test/testcases/block/08_list/list_and_hr.text +5 -0
  162. data/test/testcases/block/08_list/list_and_others.html +40 -0
  163. data/test/testcases/block/08_list/list_and_others.text +26 -0
  164. data/test/testcases/block/08_list/mixed.html +117 -0
  165. data/test/testcases/block/08_list/mixed.text +66 -0
  166. data/test/testcases/block/08_list/nested.html +17 -0
  167. data/test/testcases/block/08_list/nested.text +7 -0
  168. data/test/testcases/block/08_list/other_first_element.html +39 -0
  169. data/test/testcases/block/08_list/other_first_element.text +18 -0
  170. data/test/testcases/block/08_list/simple_ol.html +19 -0
  171. data/test/testcases/block/08_list/simple_ol.text +13 -0
  172. data/test/testcases/block/08_list/simple_ul.html +48 -0
  173. data/test/testcases/block/08_list/simple_ul.text +36 -0
  174. data/test/testcases/block/08_list/single_item.html +3 -0
  175. data/test/testcases/block/08_list/single_item.text +1 -0
  176. data/test/testcases/block/08_list/special_cases.html +55 -0
  177. data/test/testcases/block/08_list/special_cases.text +35 -0
  178. data/test/testcases/block/09_html/comment.html +18 -0
  179. data/test/testcases/block/09_html/comment.text +15 -0
  180. data/test/testcases/block/09_html/content_model/deflists.html +6 -0
  181. data/test/testcases/block/09_html/content_model/deflists.options +1 -0
  182. data/test/testcases/block/09_html/content_model/deflists.text +6 -0
  183. data/test/testcases/block/09_html/content_model/tables.html +14 -0
  184. data/test/testcases/block/09_html/content_model/tables.options +1 -0
  185. data/test/testcases/block/09_html/content_model/tables.text +14 -0
  186. data/test/testcases/block/09_html/html_and_codeblocks.html +15 -0
  187. data/test/testcases/block/09_html/html_and_codeblocks.options +1 -0
  188. data/test/testcases/block/09_html/html_and_codeblocks.text +13 -0
  189. data/test/testcases/block/09_html/html_and_headers.html +5 -0
  190. data/test/testcases/block/09_html/html_and_headers.text +6 -0
  191. data/test/testcases/block/09_html/html_to_native/code.html +10 -0
  192. data/test/testcases/block/09_html/html_to_native/code.text +9 -0
  193. data/test/testcases/block/09_html/html_to_native/comment.html +7 -0
  194. data/test/testcases/block/09_html/html_to_native/comment.text +8 -0
  195. data/test/testcases/block/09_html/html_to_native/emphasis.html +6 -0
  196. data/test/testcases/block/09_html/html_to_native/emphasis.text +6 -0
  197. data/test/testcases/block/09_html/html_to_native/entity.html +1 -0
  198. data/test/testcases/block/09_html/html_to_native/entity.text +1 -0
  199. data/test/testcases/block/09_html/html_to_native/header.html +6 -0
  200. data/test/testcases/block/09_html/html_to_native/header.options +2 -0
  201. data/test/testcases/block/09_html/html_to_native/header.text +6 -0
  202. data/test/testcases/block/09_html/html_to_native/list_dl.html +8 -0
  203. data/test/testcases/block/09_html/html_to_native/list_dl.text +8 -0
  204. data/test/testcases/block/09_html/html_to_native/list_ol.html +15 -0
  205. data/test/testcases/block/09_html/html_to_native/list_ol.text +17 -0
  206. data/test/testcases/block/09_html/html_to_native/list_ul.html +19 -0
  207. data/test/testcases/block/09_html/html_to_native/list_ul.text +22 -0
  208. data/test/testcases/block/09_html/html_to_native/options +1 -0
  209. data/test/testcases/block/09_html/html_to_native/paragraph.html +3 -0
  210. data/test/testcases/block/09_html/html_to_native/paragraph.text +4 -0
  211. data/test/testcases/block/09_html/html_to_native/table_normal.html +12 -0
  212. data/test/testcases/block/09_html/html_to_native/table_normal.text +12 -0
  213. data/test/testcases/block/09_html/html_to_native/table_simple.html +48 -0
  214. data/test/testcases/block/09_html/html_to_native/table_simple.text +56 -0
  215. data/test/testcases/block/09_html/html_to_native/typography.html +1 -0
  216. data/test/testcases/block/09_html/html_to_native/typography.html.19 +1 -0
  217. data/test/testcases/block/09_html/html_to_native/typography.text +1 -0
  218. data/test/testcases/block/09_html/invalid_html_1.html +5 -0
  219. data/test/testcases/block/09_html/invalid_html_1.text +5 -0
  220. data/test/testcases/block/09_html/invalid_html_2.html +5 -0
  221. data/test/testcases/block/09_html/invalid_html_2.text +5 -0
  222. data/test/testcases/block/09_html/markdown_attr.html +38 -0
  223. data/test/testcases/block/09_html/markdown_attr.text +38 -0
  224. data/test/testcases/block/09_html/not_parsed.html +24 -0
  225. data/test/testcases/block/09_html/not_parsed.text +24 -0
  226. data/test/testcases/block/09_html/parse_as_raw.html +35 -0
  227. data/test/testcases/block/09_html/parse_as_raw.htmlinput +34 -0
  228. data/test/testcases/block/09_html/parse_as_raw.options +1 -0
  229. data/test/testcases/block/09_html/parse_as_raw.text +33 -0
  230. data/test/testcases/block/09_html/parse_as_span.html +12 -0
  231. data/test/testcases/block/09_html/parse_as_span.htmlinput +12 -0
  232. data/test/testcases/block/09_html/parse_as_span.options +1 -0
  233. data/test/testcases/block/09_html/parse_as_span.text +9 -0
  234. data/test/testcases/block/09_html/parse_block_html.html +21 -0
  235. data/test/testcases/block/09_html/parse_block_html.options +1 -0
  236. data/test/testcases/block/09_html/parse_block_html.text +17 -0
  237. data/test/testcases/block/09_html/processing_instruction.html +13 -0
  238. data/test/testcases/block/09_html/processing_instruction.text +12 -0
  239. data/test/testcases/block/09_html/simple.html +64 -0
  240. data/test/testcases/block/09_html/simple.html.19 +64 -0
  241. data/test/testcases/block/09_html/simple.options +1 -0
  242. data/test/testcases/block/09_html/simple.text +59 -0
  243. data/test/testcases/block/10_ald/simple.html +2 -0
  244. data/test/testcases/block/10_ald/simple.text +8 -0
  245. data/test/testcases/block/11_ial/auto_id_and_ial.html +1 -0
  246. data/test/testcases/block/11_ial/auto_id_and_ial.options +1 -0
  247. data/test/testcases/block/11_ial/auto_id_and_ial.text +2 -0
  248. data/test/testcases/block/11_ial/nested.html +11 -0
  249. data/test/testcases/block/11_ial/nested.text +15 -0
  250. data/test/testcases/block/11_ial/simple.html +25 -0
  251. data/test/testcases/block/11_ial/simple.text +34 -0
  252. data/test/testcases/block/12_extension/comment.html +8 -0
  253. data/test/testcases/block/12_extension/comment.text +12 -0
  254. data/test/testcases/block/12_extension/ignored.html +8 -0
  255. data/test/testcases/block/12_extension/ignored.text +8 -0
  256. data/test/testcases/block/12_extension/nomarkdown.html +10 -0
  257. data/test/testcases/block/12_extension/nomarkdown.kramdown +20 -0
  258. data/test/testcases/block/12_extension/nomarkdown.latex +13 -0
  259. data/test/testcases/block/12_extension/nomarkdown.text +21 -0
  260. data/test/testcases/block/12_extension/options.html +21 -0
  261. data/test/testcases/block/12_extension/options.text +21 -0
  262. data/test/testcases/block/12_extension/options2.html +10 -0
  263. data/test/testcases/block/12_extension/options2.text +5 -0
  264. data/test/testcases/block/12_extension/options3.html +7 -0
  265. data/test/testcases/block/12_extension/options3.text +7 -0
  266. data/test/testcases/block/13_definition_list/definition_at_beginning.html +1 -0
  267. data/test/testcases/block/13_definition_list/definition_at_beginning.text +1 -0
  268. data/test/testcases/block/13_definition_list/item_ial.html +12 -0
  269. data/test/testcases/block/13_definition_list/item_ial.text +8 -0
  270. data/test/testcases/block/13_definition_list/multiple_terms.html +13 -0
  271. data/test/testcases/block/13_definition_list/multiple_terms.text +10 -0
  272. data/test/testcases/block/13_definition_list/no_def_list.html +2 -0
  273. data/test/testcases/block/13_definition_list/no_def_list.text +2 -0
  274. data/test/testcases/block/13_definition_list/para_wrapping.html +10 -0
  275. data/test/testcases/block/13_definition_list/para_wrapping.text +6 -0
  276. data/test/testcases/block/13_definition_list/separated_by_eob.html +8 -0
  277. data/test/testcases/block/13_definition_list/separated_by_eob.text +5 -0
  278. data/test/testcases/block/13_definition_list/simple.html +8 -0
  279. data/test/testcases/block/13_definition_list/simple.text +7 -0
  280. data/test/testcases/block/13_definition_list/styled_terms.html +4 -0
  281. data/test/testcases/block/13_definition_list/styled_terms.text +2 -0
  282. data/test/testcases/block/13_definition_list/too_much_space.html +3 -0
  283. data/test/testcases/block/13_definition_list/too_much_space.text +4 -0
  284. data/test/testcases/block/13_definition_list/with_blocks.html +38 -0
  285. data/test/testcases/block/13_definition_list/with_blocks.text +24 -0
  286. data/test/testcases/block/14_table/errors.html +8 -0
  287. data/test/testcases/block/14_table/errors.text +9 -0
  288. data/test/testcases/block/14_table/escaping.html +52 -0
  289. data/test/testcases/block/14_table/escaping.text +19 -0
  290. data/test/testcases/block/14_table/footer.html +65 -0
  291. data/test/testcases/block/14_table/footer.text +25 -0
  292. data/test/testcases/block/14_table/header.html +96 -0
  293. data/test/testcases/block/14_table/header.text +32 -0
  294. data/test/testcases/block/14_table/no_table.html +3 -0
  295. data/test/testcases/block/14_table/no_table.text +3 -0
  296. data/test/testcases/block/14_table/simple.html +177 -0
  297. data/test/testcases/block/14_table/simple.html.19 +177 -0
  298. data/test/testcases/block/14_table/simple.text +49 -0
  299. data/test/testcases/block/14_table/table_with_footnote.html +25 -0
  300. data/test/testcases/block/14_table/table_with_footnote.latex +11 -0
  301. data/test/testcases/block/14_table/table_with_footnote.text +6 -0
  302. data/test/testcases/block/15_math/normal.html +26 -0
  303. data/test/testcases/block/15_math/normal.text +28 -0
  304. data/test/testcases/block/16_toc/no_toc.html +33 -0
  305. data/test/testcases/block/16_toc/no_toc.options +1 -0
  306. data/test/testcases/block/16_toc/no_toc.text +16 -0
  307. data/test/testcases/block/16_toc/toc_levels.html +24 -0
  308. data/test/testcases/block/16_toc/toc_levels.options +1 -0
  309. data/test/testcases/block/16_toc/toc_levels.text +16 -0
  310. data/test/testcases/block/17_github_codeblock/backtick_syntax.html +7 -0
  311. data/test/testcases/block/17_github_codeblock/backtick_syntax.text +9 -0
  312. data/test/testcases/block/17_github_codeblock/error.html +4 -0
  313. data/test/testcases/block/17_github_codeblock/error.text +4 -0
  314. data/test/testcases/block/17_github_codeblock/no_newline_at_end.html +2 -0
  315. data/test/testcases/block/17_github_codeblock/no_newline_at_end.text +3 -0
  316. data/test/testcases/encoding.html +46 -0
  317. data/test/testcases/encoding.text +28 -0
  318. data/test/testcases/span/01_link/empty.html +5 -0
  319. data/test/testcases/span/01_link/empty.text +5 -0
  320. data/test/testcases/span/01_link/image_in_a.html +5 -0
  321. data/test/testcases/span/01_link/image_in_a.text +5 -0
  322. data/test/testcases/span/01_link/imagelinks.html +14 -0
  323. data/test/testcases/span/01_link/imagelinks.text +16 -0
  324. data/test/testcases/span/01_link/inline.html +46 -0
  325. data/test/testcases/span/01_link/inline.html.19 +46 -0
  326. data/test/testcases/span/01_link/inline.text +48 -0
  327. data/test/testcases/span/01_link/link_defs.html +9 -0
  328. data/test/testcases/span/01_link/link_defs.text +26 -0
  329. data/test/testcases/span/01_link/links_with_angle_brackets.html +3 -0
  330. data/test/testcases/span/01_link/links_with_angle_brackets.text +3 -0
  331. data/test/testcases/span/01_link/reference.html +36 -0
  332. data/test/testcases/span/01_link/reference.html.19 +36 -0
  333. data/test/testcases/span/01_link/reference.text +50 -0
  334. data/test/testcases/span/02_emphasis/empty.html +3 -0
  335. data/test/testcases/span/02_emphasis/empty.text +3 -0
  336. data/test/testcases/span/02_emphasis/errors.html +9 -0
  337. data/test/testcases/span/02_emphasis/errors.text +9 -0
  338. data/test/testcases/span/02_emphasis/nesting.html +38 -0
  339. data/test/testcases/span/02_emphasis/nesting.text +33 -0
  340. data/test/testcases/span/02_emphasis/normal.html +46 -0
  341. data/test/testcases/span/02_emphasis/normal.text +46 -0
  342. data/test/testcases/span/03_codespan/empty.html +5 -0
  343. data/test/testcases/span/03_codespan/empty.text +5 -0
  344. data/test/testcases/span/03_codespan/errors.html +1 -0
  345. data/test/testcases/span/03_codespan/errors.text +1 -0
  346. data/test/testcases/span/03_codespan/highlighting.html +1 -0
  347. data/test/testcases/span/03_codespan/highlighting.text +1 -0
  348. data/test/testcases/span/03_codespan/normal.html +16 -0
  349. data/test/testcases/span/03_codespan/normal.text +16 -0
  350. data/test/testcases/span/04_footnote/definitions.html +17 -0
  351. data/test/testcases/span/04_footnote/definitions.latex +17 -0
  352. data/test/testcases/span/04_footnote/definitions.text +24 -0
  353. data/test/testcases/span/04_footnote/footnote_nr.html +12 -0
  354. data/test/testcases/span/04_footnote/footnote_nr.latex +2 -0
  355. data/test/testcases/span/04_footnote/footnote_nr.options +1 -0
  356. data/test/testcases/span/04_footnote/footnote_nr.text +4 -0
  357. data/test/testcases/span/04_footnote/markers.html +46 -0
  358. data/test/testcases/span/04_footnote/markers.latex +23 -0
  359. data/test/testcases/span/04_footnote/markers.text +26 -0
  360. data/test/testcases/span/05_html/across_lines.html +1 -0
  361. data/test/testcases/span/05_html/across_lines.text +2 -0
  362. data/test/testcases/span/05_html/invalid.html +1 -0
  363. data/test/testcases/span/05_html/invalid.text +1 -0
  364. data/test/testcases/span/05_html/link_with_mailto.html +1 -0
  365. data/test/testcases/span/05_html/link_with_mailto.text +1 -0
  366. data/test/testcases/span/05_html/markdown_attr.html +6 -0
  367. data/test/testcases/span/05_html/markdown_attr.text +6 -0
  368. data/test/testcases/span/05_html/normal.html +34 -0
  369. data/test/testcases/span/05_html/normal.text +34 -0
  370. data/test/testcases/span/abbreviations/abbrev.html +8 -0
  371. data/test/testcases/span/abbreviations/abbrev.text +15 -0
  372. data/test/testcases/span/abbreviations/abbrev_defs.html +2 -0
  373. data/test/testcases/span/abbreviations/abbrev_defs.text +5 -0
  374. data/test/testcases/span/autolinks/url_links.html +12 -0
  375. data/test/testcases/span/autolinks/url_links.text +12 -0
  376. data/test/testcases/span/escaped_chars/normal.html +47 -0
  377. data/test/testcases/span/escaped_chars/normal.text +47 -0
  378. data/test/testcases/span/extension/comment.html +6 -0
  379. data/test/testcases/span/extension/comment.text +6 -0
  380. data/test/testcases/span/extension/ignored.html +1 -0
  381. data/test/testcases/span/extension/ignored.text +1 -0
  382. data/test/testcases/span/extension/nomarkdown.html +1 -0
  383. data/test/testcases/span/extension/nomarkdown.text +1 -0
  384. data/test/testcases/span/extension/options.html +1 -0
  385. data/test/testcases/span/extension/options.text +1 -0
  386. data/test/testcases/span/ial/simple.html +6 -0
  387. data/test/testcases/span/ial/simple.text +6 -0
  388. data/test/testcases/span/line_breaks/normal.html +11 -0
  389. data/test/testcases/span/line_breaks/normal.latex +12 -0
  390. data/test/testcases/span/line_breaks/normal.text +11 -0
  391. data/test/testcases/span/math/normal.html +5 -0
  392. data/test/testcases/span/math/normal.text +5 -0
  393. data/test/testcases/span/text_substitutions/entities.html +6 -0
  394. data/test/testcases/span/text_substitutions/entities.options +1 -0
  395. data/test/testcases/span/text_substitutions/entities.text +6 -0
  396. data/test/testcases/span/text_substitutions/entities_as_char.html +1 -0
  397. data/test/testcases/span/text_substitutions/entities_as_char.html.19 +1 -0
  398. data/test/testcases/span/text_substitutions/entities_as_char.options +1 -0
  399. data/test/testcases/span/text_substitutions/entities_as_char.text +1 -0
  400. data/test/testcases/span/text_substitutions/entities_as_input.html +1 -0
  401. data/test/testcases/span/text_substitutions/entities_as_input.options +1 -0
  402. data/test/testcases/span/text_substitutions/entities_as_input.text +1 -0
  403. data/test/testcases/span/text_substitutions/entities_numeric.html +1 -0
  404. data/test/testcases/span/text_substitutions/entities_numeric.options +1 -0
  405. data/test/testcases/span/text_substitutions/entities_numeric.text +1 -0
  406. data/test/testcases/span/text_substitutions/entities_symbolic.html +1 -0
  407. data/test/testcases/span/text_substitutions/entities_symbolic.options +1 -0
  408. data/test/testcases/span/text_substitutions/entities_symbolic.text +1 -0
  409. data/test/testcases/span/text_substitutions/greaterthan.html +1 -0
  410. data/test/testcases/span/text_substitutions/greaterthan.text +1 -0
  411. data/test/testcases/span/text_substitutions/lowerthan.html +1 -0
  412. data/test/testcases/span/text_substitutions/lowerthan.text +1 -0
  413. data/test/testcases/span/text_substitutions/typography.html +18 -0
  414. data/test/testcases/span/text_substitutions/typography.html.19 +18 -0
  415. data/test/testcases/span/text_substitutions/typography.text +18 -0
  416. metadata +817 -0
@@ -0,0 +1,422 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
5
+ #
6
+ # This file is part of kramdown.
7
+ #
8
+ # kramdown is free software: you can redistribute it and/or modify
9
+ # it under the terms of the GNU General Public License as published by
10
+ # the Free Software Foundation, either version 3 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # This program is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
20
+ #++
21
+ #
22
+
23
+ require 'rexml/parsers/baseparser'
24
+
25
+ module Kramdown
26
+
27
+ module Converter
28
+
29
+ # Converts an element tree to the kramdown format.
30
+ class Kramdown < Base
31
+
32
+ # :stopdoc:
33
+
34
+ include ::Kramdown::Utils::Html
35
+
36
+ def initialize(root, options)
37
+ super
38
+ @linkrefs = []
39
+ @footnotes = []
40
+ @abbrevs = []
41
+ @stack = []
42
+ end
43
+
44
+ def convert(el, opts = {:indent => 0})
45
+ res = send("convert_#{el.type}", el, opts)
46
+ if ![:html_element, :li, :dd, :td].include?(el.type) && (ial = ial_for_element(el))
47
+ res << ial
48
+ res << "\n\n" if Element.category(el) == :block
49
+ elsif [:ul, :dl, :ol, :codeblock].include?(el.type) && opts[:next] &&
50
+ ([el.type, :codeblock].include?(opts[:next].type) ||
51
+ (opts[:next].type == :blank && opts[:nnext] && [el.type, :codeblock].include?(opts[:nnext].type)))
52
+ res << "^\n\n"
53
+ elsif Element.category(el) == :block &&
54
+ ![:li, :dd, :dt, :td, :th, :tr, :thead, :tbody, :tfoot, :blank].include?(el.type) &&
55
+ (el.type != :html_element || @stack.last.type != :html_element) &&
56
+ (el.type != :p || !el.options[:transparent])
57
+ res << "\n"
58
+ end
59
+ res
60
+ end
61
+
62
+ def inner(el, opts = {:indent => 0})
63
+ @stack.push(el)
64
+ result = ''
65
+ el.children.each_with_index do |inner_el, index|
66
+ options = opts.dup
67
+ options[:index] = index
68
+ options[:prev] = (index == 0 ? nil : el.children[index-1])
69
+ options[:pprev] = (index <= 1 ? nil : el.children[index-2])
70
+ options[:next] = (index == el.children.length - 1 ? nil : el.children[index+1])
71
+ options[:nnext] = (index >= el.children.length - 2 ? nil : el.children[index+2])
72
+ result << convert(inner_el, options)
73
+ end
74
+ @stack.pop
75
+ result
76
+ end
77
+
78
+ def convert_blank(el, opts)
79
+ ""
80
+ end
81
+
82
+ ESCAPED_CHAR_RE = /(\$\$|[\\*_`\[\]\{"'|])|^[ ]{0,3}(:)/
83
+
84
+ def convert_text(el, opts)
85
+ if opts[:raw_text]
86
+ el.value
87
+ else
88
+ el.value.gsub(/\A\n/) do
89
+ opts[:prev] && opts[:prev].type == :br ? '' : "\n"
90
+ end.gsub(/\s+/, ' ').gsub(ESCAPED_CHAR_RE) { "\\#{$1 || $2}" }
91
+ end
92
+ end
93
+
94
+ def convert_p(el, opts)
95
+ w = @options[:line_width] - opts[:indent].to_s.to_i
96
+ first, second, *rest = inner(el, opts).strip.gsub(/(.{1,#{w}})( +|$\n?)/, "\\1\n").split(/\n/)
97
+ first.gsub!(/^(?:(#|>)|(\d+)\.|([+-]\s))/) { $1 || $3 ? "\\#{$1 || $3}" : "#{$2}\\."} if first
98
+ second.gsub!(/^([=-]+\s*?)$/, "\\\1") if second
99
+ [first, second, *rest].compact.join("\n") + "\n"
100
+ end
101
+
102
+
103
+ def convert_codeblock(el, opts)
104
+ el.value.split(/\n/).map {|l| l.empty? ? " " : " #{l}"}.join("\n") + "\n"
105
+ end
106
+
107
+ def convert_blockquote(el, opts)
108
+ opts[:indent] += 2
109
+ inner(el, opts).chomp.split(/\n/).map {|l| "> #{l}"}.join("\n") << "\n"
110
+ end
111
+
112
+ def convert_header(el, opts)
113
+ res = ''
114
+ res << "#{'#' * el.options[:level]} #{inner(el, opts)}"
115
+ res << " {##{el.attr['id']}}" if el.attr['id']
116
+ res << "\n"
117
+ end
118
+
119
+ def convert_hr(el, opts)
120
+ "* * *\n"
121
+ end
122
+
123
+ def convert_ul(el, opts)
124
+ inner(el, opts).sub(/\n+\Z/, "\n")
125
+ end
126
+ alias :convert_ol :convert_ul
127
+ alias :convert_dl :convert_ul
128
+
129
+ def convert_li(el, opts)
130
+ sym, width = if @stack.last.type == :ul
131
+ ['* ', el.children.first.type == :codeblock ? 4 : 2]
132
+ else
133
+ ["#{opts[:index] + 1}.".ljust(4), 4]
134
+ end
135
+ if ial = ial_for_element(el)
136
+ sym << ial << " "
137
+ end
138
+
139
+ opts[:indent] += width
140
+ text = inner(el, opts)
141
+ newlines = text.scan(/\n*\Z/).first
142
+ first, *last = text.split(/\n/)
143
+ last = last.map {|l| " "*width + l}.join("\n")
144
+ text = first + (last.empty? ? "" : "\n") + last + newlines
145
+ if el.children.first.type == :p && !el.children.first.options[:transparent]
146
+ res = "#{sym}#{text}"
147
+ res << "^\n" if el.children.size == 1 && @stack.last.children.last == el &&
148
+ (@stack.last.children.any? {|c| c.children.first.type != :p} || @stack.last.children.size == 1)
149
+ res
150
+ elsif el.children.first.type == :codeblock
151
+ "#{sym}\n #{text}"
152
+ else
153
+ "#{sym}#{text}"
154
+ end
155
+ end
156
+
157
+ def convert_dd(el, opts)
158
+ sym, width = ": ", (el.children.first.type == :codeblock ? 4 : 2)
159
+ if ial = ial_for_element(el)
160
+ sym << ial << " "
161
+ end
162
+
163
+ opts[:indent] += width
164
+ text = inner(el, opts)
165
+ newlines = text.scan(/\n*\Z/).first
166
+ first, *last = text.split(/\n/)
167
+ last = last.map {|l| " "*width + l}.join("\n")
168
+ text = first + (last.empty? ? "" : "\n") + last + newlines
169
+ text.chomp! if text =~ /\n\n\Z/ && opts[:next] && opts[:next].type == :dd
170
+ text << "\n" if text !~ /\n\n\Z/ && opts[:next] && opts[:next].type == :dt
171
+ if el.children.first.type == :p && !el.children.first.options[:transparent]
172
+ "\n#{sym}#{text}"
173
+ elsif el.children.first.type == :codeblock
174
+ "#{sym}\n #{text}"
175
+ else
176
+ "#{sym}#{text}"
177
+ end
178
+ end
179
+
180
+ def convert_dt(el, opts)
181
+ inner(el, opts) << "\n"
182
+ end
183
+
184
+ HTML_TAGS_WITH_BODY=['div', 'script', 'iframe', 'textarea']
185
+
186
+ def convert_html_element(el, opts)
187
+ markdown_attr = el.options[:category] == :block && el.children.any? do |c|
188
+ c.type != :html_element && (c.type != :p || !c.options[:transparent]) && Element.category(c) == :block
189
+ end
190
+ opts[:force_raw_text] = true if %w{script pre code}.include?(el.value)
191
+ opts[:raw_text] = opts[:force_raw_text] || opts[:block_raw_text] || (el.options[:category] != :span && !markdown_attr)
192
+ opts[:block_raw_text] = true if el.options[:category] == :block && opts[:raw_text]
193
+ res = inner(el, opts)
194
+ if el.options[:category] == :span
195
+ "<#{el.value}#{html_attributes(el.attr)}" << (!res.empty? || HTML_TAGS_WITH_BODY.include?(el.value) ? ">#{res}</#{el.value}>" : " />")
196
+ else
197
+ output = ''
198
+ output << "<#{el.value}#{html_attributes(el.attr)}"
199
+ output << " markdown=\"1\"" if markdown_attr
200
+ if !res.empty? && el.options[:content_model] != :block
201
+ output << ">#{res}</#{el.value}>"
202
+ elsif !res.empty?
203
+ output << ">\n#{res}" << "</#{el.value}>"
204
+ elsif HTML_TAGS_WITH_BODY.include?(el.value)
205
+ output << "></#{el.value}>"
206
+ else
207
+ output << " />"
208
+ end
209
+ output << "\n" if @stack.last.type != :html_element || @stack.last.options[:content_model] != :raw
210
+ output
211
+ end
212
+ end
213
+
214
+ def convert_xml_comment(el, opts)
215
+ if el.options[:category] == :block && (@stack.last.type != :html_element || @stack.last.options[:content_model] != :raw)
216
+ el.value + "\n"
217
+ else
218
+ el.value.dup
219
+ end
220
+ end
221
+ alias :convert_xml_pi :convert_xml_comment
222
+
223
+ def convert_table(el, opts)
224
+ opts[:alignment] = el.options[:alignment]
225
+ inner(el, opts)
226
+ end
227
+
228
+ def convert_thead(el, opts)
229
+ rows = inner(el, opts)
230
+ if opts[:alignment].all? {|a| a == :default}
231
+ "#{rows}|" + "-"*10 + "\n"
232
+ else
233
+ "#{rows}| " + opts[:alignment].map do |a|
234
+ case a
235
+ when :left then ":-"
236
+ when :right then "-:"
237
+ when :center then ":-:"
238
+ when :default then "-"
239
+ end
240
+ end.join(' ') + "\n"
241
+ end
242
+ end
243
+
244
+ def convert_tbody(el, opts)
245
+ res = ''
246
+ res << inner(el, opts)
247
+ res << '|' << '-'*10 << "\n" if opts[:next] && opts[:next].type == :tbody
248
+ res
249
+ end
250
+
251
+ def convert_tfoot(el, opts)
252
+ "|" + "="*10 + "\n#{inner(el, opts)}"
253
+ end
254
+
255
+ def convert_tr(el, opts)
256
+ "| " + el.children.map {|c| convert(c, opts)}.join(" | ") + " |\n"
257
+ end
258
+
259
+ def convert_td(el, opts)
260
+ inner(el, opts)
261
+ end
262
+
263
+ def convert_comment(el, opts)
264
+ if el.options[:category] == :block
265
+ "{::comment}\n#{el.value}\n{:/}\n"
266
+ else
267
+ "{::comment}#{el.value}{:/}"
268
+ end
269
+ end
270
+
271
+ def convert_br(el, opts)
272
+ " \n"
273
+ end
274
+
275
+ def convert_a(el, opts)
276
+ if el.attr['href'].empty?
277
+ "[#{inner(el, opts)}]()"
278
+ elsif el.attr['href'] =~ /^(?:http|ftp)/ || el.attr['href'].count("()") > 0
279
+ index = if link_el = @linkrefs.find {|c| c.attr['href'] == el.attr['href']}
280
+ @linkrefs.index(link_el) + 1
281
+ else
282
+ @linkrefs << el
283
+ @linkrefs.size
284
+ end
285
+ "[#{inner(el, opts)}][#{index}]"
286
+ else
287
+ title = el.attr['title'].to_s.empty? ? '' : ' "' + el.attr['title'].gsub(/"/, "&quot;") + '"'
288
+ "[#{inner(el, opts)}](#{el.attr['href']}#{title})"
289
+ end
290
+ end
291
+
292
+ def convert_img(el, opts)
293
+ if el.attr['src'].empty?
294
+ "![#{el.attr['alt']}]()"
295
+ else
296
+ title = (el.attr['title'] ? ' "' + el.attr['title'].gsub(/"/, "&quot;") + '"' : '')
297
+ link = if el.attr['src'].count("()") > 0
298
+ "<#{el.attr['src']}>"
299
+ else
300
+ el.attr['src']
301
+ end
302
+ "![#{el.attr['alt']}](#{link}#{title})"
303
+ end
304
+ end
305
+
306
+ def convert_codespan(el, opts)
307
+ delim = (el.value.scan(/`+/).max || '') + '`'
308
+ "#{delim}#{' ' if delim.size > 1}#{el.value}#{' ' if delim.size > 1}#{delim}"
309
+ end
310
+
311
+ def convert_footnote(el, opts)
312
+ @footnotes << [el.options[:name], el.value]
313
+ "[^#{el.options[:name]}]"
314
+ end
315
+
316
+ def convert_raw(el, opts)
317
+ attr = (el.options[:type] || []).join(' ')
318
+ attr = " type=\"#{attr}\"" if attr.length > 0
319
+ if @stack.last.type == :html_element
320
+ el.value
321
+ elsif el.options[:category] == :block
322
+ "{::nomarkdown#{attr}}\n#{el.value}\n{:/}\n"
323
+ else
324
+ "{::nomarkdown#{attr}}#{el.value}{:/}"
325
+ end
326
+ end
327
+
328
+ def convert_em(el, opts)
329
+ "*#{inner(el, opts)}*"
330
+ end
331
+
332
+ def convert_strong(el, opts)
333
+ "**#{inner(el, opts)}**"
334
+ end
335
+
336
+ def convert_entity(el, opts)
337
+ entity_to_str(el.value, el.options[:original])
338
+ end
339
+
340
+ TYPOGRAPHIC_SYMS = {
341
+ :mdash => '---', :ndash => '--', :hellip => '...',
342
+ :laquo_space => '<< ', :raquo_space => ' >>',
343
+ :laquo => '<<', :raquo => '>>'
344
+ }
345
+ def convert_typographic_sym(el, opts)
346
+ TYPOGRAPHIC_SYMS[el.value]
347
+ end
348
+
349
+ def convert_smart_quote(el, opts)
350
+ el.value.to_s =~ /[rl]dquo/ ? "\"" : "'"
351
+ end
352
+
353
+ def convert_math(el, opts)
354
+ (@stack.last.type == :p && opts[:prev].nil? ? "\\" : '') + "$$#{el.value}$$" + (el.options[:category] == :block ? "\n" : '')
355
+ end
356
+
357
+ def convert_abbreviation(el, opts)
358
+ el.value
359
+ end
360
+
361
+ def convert_root(el, opts)
362
+ res = inner(el, opts)
363
+ res << create_link_defs
364
+ res << create_footnote_defs
365
+ res << create_abbrev_defs
366
+ res
367
+ end
368
+
369
+ def create_link_defs
370
+ res = ''
371
+ res << "\n\n" if @linkrefs.size > 0
372
+ @linkrefs.each_with_index do |el, i|
373
+ title = el.attr['title']
374
+ res << "[#{i+1}]: #{el.attr['href']} #{title ? '"' + title.gsub(/"/, "&quot;") + '"' : ''}\n"
375
+ end
376
+ res
377
+ end
378
+
379
+ def create_footnote_defs
380
+ res = ''
381
+ @footnotes.each do |name, data|
382
+ res << "[^#{name}]:\n"
383
+ res << inner(data).chomp.split(/\n/).map {|l| " #{l}"}.join("\n") + "\n\n"
384
+ end
385
+ res
386
+ end
387
+
388
+ def create_abbrev_defs
389
+ return '' unless @root.options[:abbrev_defs]
390
+ res = ''
391
+ @root.options[:abbrev_defs].each do |name, text|
392
+ res << "*[#{name}]: #{text}\n"
393
+ end
394
+ res
395
+ end
396
+
397
+ # Return the IAL containing the attributes of the element +el+.
398
+ def ial_for_element(el)
399
+ res = el.attr.map do |k,v|
400
+ next if [:img, :a].include?(el.type) && ['href', 'src', 'alt', 'title'].include?(k)
401
+ next if el.type == :header && k == 'id'
402
+ if v.nil?
403
+ ''
404
+ elsif k == 'class'
405
+ " " + v.split(/\s+/).map {|w| ".#{w}"}.join(" ")
406
+ elsif k == 'id'
407
+ " ##{v}"
408
+ else
409
+ " #{k}=\"#{v.to_s}\""
410
+ end
411
+ end.compact.join('')
412
+ res = "toc" + (res.strip.empty? ? '' : " #{res}") if (el.type == :ul || el.type == :ol) &&
413
+ (el.options[:ial][:refs].include?('toc') rescue nil)
414
+ res.strip.empty? ? nil : "{:#{res}}"
415
+ end
416
+
417
+ # :startdoc:
418
+
419
+ end
420
+
421
+ end
422
+ end
@@ -0,0 +1,607 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
5
+ #
6
+ # This file is part of kramdown.
7
+ #
8
+ # kramdown is free software: you can redistribute it and/or modify
9
+ # it under the terms of the GNU General Public License as published by
10
+ # the Free Software Foundation, either version 3 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # This program is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
20
+ #++
21
+ #
22
+
23
+ require 'set'
24
+
25
+ module Kramdown
26
+
27
+ module Converter
28
+
29
+ # Converts an element tree to LaTeX.
30
+ #
31
+ # This converter uses ideas from other Markdown-to-LaTeX converters like Pandoc and Maruku.
32
+ #
33
+ # You can customize this converter by sub-classing it and overriding the +convert_NAME+ methods.
34
+ # Each such method takes the following parameters:
35
+ #
36
+ # [+el+] The element of type +NAME+ to be converted.
37
+ #
38
+ # [+opts+] A hash containing processing options that are passed down from parent elements. The
39
+ # key :parent is always set and contains the parent element as value.
40
+ #
41
+ # The return value of such a method has to be a string containing the element +el+ formatted
42
+ # correctly as LaTeX markup.
43
+ class Latex < Base
44
+
45
+ # Initialize the LaTeX converter with the +root+ element and the conversion +options+.
46
+ def initialize(root, options)
47
+ super
48
+ #TODO: set the footnote counter at the beginning of the document
49
+ @options[:footnote_nr]
50
+ @data[:packages] = Set.new
51
+ end
52
+
53
+ # Dispatch the conversion of the element +el+ to a +convert_TYPE+ method using the +type+ of
54
+ # the element.
55
+ def convert(el, opts = {})
56
+ send("convert_#{el.type}", el, opts)
57
+ end
58
+
59
+ # Return the converted content of the children of +el+ as a string.
60
+ def inner(el, opts)
61
+ result = ''
62
+ options = opts.dup.merge(:parent => el)
63
+ el.children.each_with_index do |inner_el, index|
64
+ options[:index] = index
65
+ options[:result] = result
66
+ result << send("convert_#{inner_el.type}", inner_el, options)
67
+ end
68
+ result
69
+ end
70
+
71
+ def convert_root(el, opts)
72
+ inner(el, opts)
73
+ end
74
+
75
+ def convert_blank(el, opts)
76
+ opts[:result] =~ /\n\n\Z|\A\Z/ ? "" : "\n"
77
+ end
78
+
79
+ def convert_text(el, opts)
80
+ escape(el.value)
81
+ end
82
+
83
+ def convert_p(el, opts)
84
+ if el.children.size == 1 && el.children.first.type == :img && !(img = convert_img(el.children.first, opts)).empty?
85
+ convert_standalone_image(el, opts, img)
86
+ else
87
+ "#{latex_link_target(el)}#{inner(el, opts)}\n\n"
88
+ end
89
+ end
90
+
91
+ # Helper method used by +convert_p+ to convert a paragraph that only contains a single :img
92
+ # element.
93
+ def convert_standalone_image(el, opts, img)
94
+ attrs = attribute_list(el)
95
+ "\\begin{figure}#{attrs}\n\\begin{center}\n#{img}\n\\end{center}\n\\caption{#{escape(el.children.first.attr['alt'])}}\n#{latex_link_target(el, true)}\n\\end{figure}#{attrs}\n"
96
+ end
97
+
98
+ def convert_codeblock(el, opts)
99
+ show_whitespace = el.attr['class'].to_s =~ /\bshow-whitespaces\b/
100
+ lang = el.attr['lang']
101
+ if show_whitespace || lang
102
+ options = []
103
+ options << "showspaces=%s,showtabs=%s" % (show_whitespace ? ['true', 'true'] : ['false', 'false'])
104
+ options << "language=#{lang}" if lang
105
+ options << "basicstyle=\\ttfamily\\footnotesize,columns=fixed,frame=tlbr"
106
+ id = el.attr['id']
107
+ options << "label=#{id}" if id
108
+ attrs = attribute_list(el)
109
+ "#{latex_link_target(el)}\\begin{lstlisting}[#{options.join(',')}]\n#{el.value}\n\\end{lstlisting}#{attrs}\n"
110
+ else
111
+ "#{latex_link_target(el)}\\begin{verbatim}#{el.value}\\end{verbatim}\n"
112
+ end
113
+ end
114
+
115
+ def convert_blockquote(el, opts)
116
+ latex_environment(el.children.size > 1 ? 'quotation' : 'quote', el, inner(el, opts))
117
+ end
118
+
119
+ def convert_header(el, opts)
120
+ type = @options[:latex_headers][el.options[:level] - 1]
121
+ if ((id = el.attr['id']) ||
122
+ (@options[:auto_ids] && (id = generate_id(el.options[:raw_text])))) && in_toc?(el)
123
+ "\\#{type}{#{inner(el, opts)}}\\hypertarget{#{id}}{}\\label{#{id}}\n\n"
124
+ else
125
+ "\\#{type}*{#{inner(el, opts)}}\n\n"
126
+ end
127
+ end
128
+
129
+ def convert_hr(el, opts)
130
+ attrs = attribute_list(el)
131
+ "#{latex_link_target(el)}\\begin{center}#{attrs}\n\\rule{3in}{0.4pt}\n\\end{center}#{attrs}\n"
132
+ end
133
+
134
+ def convert_ul(el, opts)
135
+ if !@data[:has_toc] && (el.options[:ial][:refs].include?('toc') rescue nil)
136
+ @data[:has_toc] = true
137
+ '\tableofcontents'
138
+ else
139
+ latex_environment(el.type == :ul ? 'itemize' : 'enumerate', el, inner(el, opts))
140
+ end
141
+ end
142
+ alias :convert_ol :convert_ul
143
+
144
+ def convert_dl(el, opts)
145
+ latex_environment('description', el, inner(el, opts))
146
+ end
147
+
148
+ def convert_li(el, opts)
149
+ "\\item #{latex_link_target(el, true)}#{inner(el, opts).sub(/\n+\Z/, '')}\n"
150
+ end
151
+
152
+ def convert_dt(el, opts)
153
+ "\\item[#{inner(el, opts)}] "
154
+ end
155
+
156
+ def convert_dd(el, opts)
157
+ "#{latex_link_target(el)}#{inner(el, opts)}\n\n"
158
+ end
159
+
160
+ def convert_html_element(el, opts)
161
+ if el.value == 'i'
162
+ "\\emph{#{inner(el, opts)}}"
163
+ elsif el.value == 'b'
164
+ "\\emph{#{inner(el, opts)}}"
165
+ else
166
+ warning("Can't convert HTML element")
167
+ ''
168
+ end
169
+ end
170
+
171
+ def convert_xml_comment(el, opts)
172
+ el.value.split(/\n/).map {|l| "% #{l}"}.join("\n") + "\n"
173
+ end
174
+
175
+ def convert_xml_pi(el, opts)
176
+ warning("Can't convert XML PI")
177
+ ''
178
+ end
179
+
180
+ TABLE_ALIGNMENT_CHAR = {:default => 'l', :left => 'l', :center => 'c', :right => 'r'} # :nodoc:
181
+
182
+ def convert_table(el, opts)
183
+ @data[:packages] << 'longtable'
184
+ align = el.options[:alignment].map {|a| TABLE_ALIGNMENT_CHAR[a]}.join('|')
185
+ attrs = attribute_list(el)
186
+ "#{latex_link_target(el)}\\begin{longtable}{|#{align}|}#{attrs}\n\\hline\n#{inner(el, opts)}\\hline\n\\end{longtable}#{attrs}\n\n"
187
+ end
188
+
189
+ def convert_thead(el, opts)
190
+ "#{inner(el, opts)}\\hline\n"
191
+ end
192
+
193
+ def convert_tbody(el, opts)
194
+ inner(el, opts)
195
+ end
196
+
197
+ def convert_tfoot(el, opts)
198
+ "\\hline \\hline \n#{inner(el, opts)}"
199
+ end
200
+
201
+ def convert_tr(el, opts)
202
+ el.children.map {|c| send("convert_#{c.type}", c, opts)}.join(' & ') + "\\\\\n"
203
+ end
204
+
205
+ def convert_td(el, opts)
206
+ inner(el, opts)
207
+ end
208
+
209
+ def convert_comment(el, opts)
210
+ el.value.split(/\n/).map {|l| "% #{l}"}.join("\n") + "\n"
211
+ end
212
+
213
+ def convert_br(el, opts)
214
+ res = "\\newline"
215
+ res << "\n" if (c = opts[:parent].children[opts[:index]+1]) && (c.type != :text || c.value !~ /^\s*\n/)
216
+ res
217
+ end
218
+
219
+ def convert_a(el, opts)
220
+ url = el.attr['href']
221
+ if url =~ /^#/
222
+ "\\hyperlink{#{url[1..-1]}}{#{inner(el, opts)}}"
223
+ else
224
+ "\\href{#{url}}{#{inner(el, opts)}}"
225
+ end
226
+ end
227
+
228
+ def convert_img(el, opts)
229
+ if el.attr['src'] =~ /^(https?|ftps?):\/\//
230
+ warning("Cannot include non-local image")
231
+ ''
232
+ elsif !el.attr['src'].empty?
233
+ @data[:packages] << 'graphicx'
234
+ "#{latex_link_target(el)}\\includegraphics{#{el.attr['src']}}"
235
+ else
236
+ warning("Cannot include image with empty path")
237
+ ''
238
+ end
239
+ end
240
+
241
+ def convert_codespan(el, opts)
242
+ "{\\tt #{latex_link_target(el)}#{escape(el.value)}}"
243
+ end
244
+
245
+ def convert_footnote(el, opts)
246
+ @data[:packages] << 'fancyvrb'
247
+ "\\footnote{#{inner(el.value, opts).rstrip}}"
248
+ end
249
+
250
+ def convert_raw(el, opts)
251
+ if !el.options[:type] || el.options[:type].empty? || el.options[:type].include?('latex')
252
+ el.value + (el.options[:category] == :block ? "\n" : '')
253
+ else
254
+ ''
255
+ end
256
+ end
257
+
258
+ def convert_em(el, opts)
259
+ "\\emph{#{latex_link_target(el)}#{inner(el, opts)}}"
260
+ end
261
+
262
+ def convert_strong(el, opts)
263
+ "\\textbf{#{latex_link_target(el)}#{inner(el, opts)}}"
264
+ end
265
+
266
+ # Inspired by Maruku: entity conversion table based on the one from htmltolatex
267
+ # (http://sourceforge.net/projects/htmltolatex/), with some small adjustments/additions
268
+ ENTITY_CONV_TABLE = {
269
+ 913 => ['$A$'],
270
+ 914 => ['$B$'],
271
+ 915 => ['$\Gamma$'],
272
+ 916 => ['$\Delta$'],
273
+ 917 => ['$E$'],
274
+ 918 => ['$Z$'],
275
+ 919 => ['$H$'],
276
+ 920 => ['$\Theta$'],
277
+ 921 => ['$I$'],
278
+ 922 => ['$K$'],
279
+ 923 => ['$\Lambda$'],
280
+ 924 => ['$M$'],
281
+ 925 => ['$N$'],
282
+ 926 => ['$\Xi$'],
283
+ 927 => ['$O$'],
284
+ 928 => ['$\Pi$'],
285
+ 929 => ['$P$'],
286
+ 931 => ['$\Sigma$'],
287
+ 932 => ['$T$'],
288
+ 933 => ['$Y$'],
289
+ 934 => ['$\Phi$'],
290
+ 935 => ['$X$'],
291
+ 936 => ['$\Psi$'],
292
+ 937 => ['$\Omega$'],
293
+ 945 => ['$\alpha$'],
294
+ 946 => ['$\beta$'],
295
+ 947 => ['$\gamma$'],
296
+ 948 => ['$\delta$'],
297
+ 949 => ['$\epsilon$'],
298
+ 950 => ['$\zeta$'],
299
+ 951 => ['$\eta$'],
300
+ 952 => ['$\theta$'],
301
+ 953 => ['$\iota$'],
302
+ 954 => ['$\kappa$'],
303
+ 955 => ['$\lambda$'],
304
+ 956 => ['$\mu$'],
305
+ 957 => ['$\nu$'],
306
+ 958 => ['$\xi$'],
307
+ 959 => ['$o$'],
308
+ 960 => ['$\pi$'],
309
+ 961 => ['$\rho$'],
310
+ 963 => ['$\sigma$'],
311
+ 964 => ['$\tau$'],
312
+ 965 => ['$\upsilon$'],
313
+ 966 => ['$\phi$'],
314
+ 967 => ['$\chi$'],
315
+ 968 => ['$\psi$'],
316
+ 969 => ['$\omega$'],
317
+ 962 => ['$\varsigma$'],
318
+ 977 => ['$\vartheta$'],
319
+ 982 => ['$\varpi$'],
320
+ 8230 => ['\ldots'],
321
+ 8242 => ['$\prime$'],
322
+ 8254 => ['-'],
323
+ 8260 => ['/'],
324
+ 8472 => ['$\wp$'],
325
+ 8465 => ['$\Im$'],
326
+ 8476 => ['$\Re$'],
327
+ 8501 => ['$\aleph$'],
328
+ 8226 => ['$\bullet$'],
329
+ 8482 => ['$^{\rm TM}$'],
330
+ 8592 => ['$\leftarrow$'],
331
+ 8594 => ['$\rightarrow$'],
332
+ 8593 => ['$\uparrow$'],
333
+ 8595 => ['$\downarrow$'],
334
+ 8596 => ['$\leftrightarrow$'],
335
+ 8629 => ['$\hookleftarrow$'],
336
+ 8657 => ['$\Uparrow$'],
337
+ 8659 => ['$\Downarrow$'],
338
+ 8656 => ['$\Leftarrow$'],
339
+ 8658 => ['$\Rightarrow$'],
340
+ 8660 => ['$\Leftrightarrow$'],
341
+ 8704 => ['$\forall$'],
342
+ 8706 => ['$\partial$'],
343
+ 8707 => ['$\exists$'],
344
+ 8709 => ['$\emptyset$'],
345
+ 8711 => ['$\nabla$'],
346
+ 8712 => ['$\in$'],
347
+ 8715 => ['$\ni$'],
348
+ 8713 => ['$\notin$'],
349
+ 8721 => ['$\sum$'],
350
+ 8719 => ['$\prod$'],
351
+ 8722 => ['$-$'],
352
+ 8727 => ['$\ast$'],
353
+ 8730 => ['$\surd$'],
354
+ 8733 => ['$\propto$'],
355
+ 8734 => ['$\infty$'],
356
+ 8736 => ['$\angle$'],
357
+ 8743 => ['$\wedge$'],
358
+ 8744 => ['$\vee$'],
359
+ 8745 => ['$\cup$'],
360
+ 8746 => ['$\cap$'],
361
+ 8747 => ['$\int$'],
362
+ 8756 => ['$\therefore$', 'amssymb'],
363
+ 8764 => ['$\sim$'],
364
+ 8776 => ['$\approx$'],
365
+ 8773 => ['$\cong$'],
366
+ 8800 => ['$\neq$'],
367
+ 8801 => ['$\equiv$'],
368
+ 8804 => ['$\leq$'],
369
+ 8805 => ['$\geq$'],
370
+ 8834 => ['$\subset$'],
371
+ 8835 => ['$\supset$'],
372
+ 8838 => ['$\subseteq$'],
373
+ 8839 => ['$\supseteq$'],
374
+ 8836 => ['$\nsubset$', 'amssymb'],
375
+ 8853 => ['$\oplus$'],
376
+ 8855 => ['$\otimes$'],
377
+ 8869 => ['$\perp$'],
378
+ 8901 => ['$\cdot$'],
379
+ 8968 => ['$\rceil$'],
380
+ 8969 => ['$\lceil$'],
381
+ 8970 => ['$\lfloor$'],
382
+ 8971 => ['$\rfloor$'],
383
+ 9001 => ['$\rangle$'],
384
+ 9002 => ['$\langle$'],
385
+ 9674 => ['$\lozenge$', 'amssymb'],
386
+ 9824 => ['$\spadesuit$'],
387
+ 9827 => ['$\clubsuit$'],
388
+ 9829 => ['$\heartsuit$'],
389
+ 9830 => ['$\diamondsuit$'],
390
+ 38 => ['\&'],
391
+ 34 => ['"'],
392
+ 39 => ['\''],
393
+ 169 => ['\copyright'],
394
+ 60 => ['\textless'],
395
+ 62 => ['\textgreater'],
396
+ 338 => ['\OE'],
397
+ 339 => ['\oe'],
398
+ 352 => ['\v{S}'],
399
+ 353 => ['\v{s}'],
400
+ 376 => ['\"Y'],
401
+ 710 => ['\textasciicircum'],
402
+ 732 => ['\textasciitilde'],
403
+ 8211 => ['--'],
404
+ 8212 => ['---'],
405
+ 8216 => ['`'],
406
+ 8217 => ['\''],
407
+ 8220 => ['``'],
408
+ 8221 => ['\'\''],
409
+ 8224 => ['\dag'],
410
+ 8225 => ['\ddag'],
411
+ 8240 => ['\permil', 'wasysym'],
412
+ 8364 => ['\euro', 'eurosym'],
413
+ 8249 => ['\guilsinglleft'],
414
+ 8250 => ['\guilsinglright'],
415
+ 8218 => ['\quotesinglbase', 'mathcomp'],
416
+ 8222 => ['\quotedblbase', 'mathcomp'],
417
+ 402 => ['\textflorin', 'mathcomp'],
418
+ 381 => ['\v{Z}'],
419
+ 382 => ['\v{z}'],
420
+ 160 => ['\nolinebreak'],
421
+ 161 => ['\textexclamdown'],
422
+ 163 => ['\pounds'],
423
+ 164 => ['\currency', 'wasysym'],
424
+ 165 => ['\textyen', 'textcomp'],
425
+ 166 => ['\brokenvert', 'wasysym'],
426
+ 167 => ['\S'],
427
+ 171 => ['\guillemotleft'],
428
+ 187 => ['\guillemotright'],
429
+ 174 => ['\textregistered'],
430
+ 170 => ['\textordfeminine'],
431
+ 172 => ['$\neg$'],
432
+ 176 => ['$\degree$', 'mathabx'],
433
+ 177 => ['$\pm$'],
434
+ 180 => ['\''],
435
+ 181 => ['$\mu$'],
436
+ 182 => ['\P'],
437
+ 183 => ['$\cdot$'],
438
+ 186 => ['\textordmasculine'],
439
+ 162 => ['\cent', 'wasysym'],
440
+ 185 => ['$^1$'],
441
+ 178 => ['$^2$'],
442
+ 179 => ['$^3$'],
443
+ 189 => ['$\frac{1}{2}$'],
444
+ 188 => ['$\frac{1}{4}$'],
445
+ 190 => ['$\frac{3}{4}'],
446
+ 192 => ['\`A'],
447
+ 193 => ['\\\'A'],
448
+ 194 => ['\^A'],
449
+ 195 => ['\~A'],
450
+ 196 => ['\"A'],
451
+ 197 => ['\AA'],
452
+ 198 => ['\AE'],
453
+ 199 => ['\cC'],
454
+ 200 => ['\`E'],
455
+ 201 => ['\\\'E'],
456
+ 202 => ['\^E'],
457
+ 203 => ['\"E'],
458
+ 204 => ['\`I'],
459
+ 205 => ['\\\'I'],
460
+ 206 => ['\^I'],
461
+ 207 => ['\"I'],
462
+ 208 => ['$\eth$', 'amssymb'],
463
+ 209 => ['\~N'],
464
+ 210 => ['\`O'],
465
+ 211 => ['\\\'O'],
466
+ 212 => ['\^O'],
467
+ 213 => ['\~O'],
468
+ 214 => ['\"O'],
469
+ 215 => ['$\times$'],
470
+ 216 => ['\O'],
471
+ 217 => ['\`U'],
472
+ 218 => ['\\\'U'],
473
+ 219 => ['\^U'],
474
+ 220 => ['\"U'],
475
+ 221 => ['\\\'Y'],
476
+ 222 => ['\Thorn', 'wasysym'],
477
+ 223 => ['\ss'],
478
+ 224 => ['\`a'],
479
+ 225 => ['\\\'a'],
480
+ 226 => ['\^a'],
481
+ 227 => ['\~a'],
482
+ 228 => ['\"a'],
483
+ 229 => ['\aa'],
484
+ 230 => ['\ae'],
485
+ 231 => ['\cc'],
486
+ 232 => ['\`e'],
487
+ 233 => ['\\\'e'],
488
+ 234 => ['\^e'],
489
+ 235 => ['\"e'],
490
+ 236 => ['\`i'],
491
+ 237 => ['\\\'i'],
492
+ 238 => ['\^i'],
493
+ 239 => ['\"i'],
494
+ 240 => ['$\eth$'],
495
+ 241 => ['\~n'],
496
+ 242 => ['\`o'],
497
+ 243 => ['\\\'o'],
498
+ 244 => ['\^o'],
499
+ 245 => ['\~o'],
500
+ 246 => ['\"o'],
501
+ 247 => ['$\divide$'],
502
+ 248 => ['\o'],
503
+ 249 => ['\`u'],
504
+ 250 => ['\\\'u'],
505
+ 251 => ['\^u'],
506
+ 252 => ['\"u'],
507
+ 253 => ['\\\'y'],
508
+ 254 => ['\thorn', 'wasysym'],
509
+ 255 => ['\"y'],
510
+ 8201 => ['\thinspace'],
511
+ 8194 => ['\hskip .5em\relax'],
512
+ 8195 => ['\quad'],
513
+ } # :nodoc:
514
+ ENTITY_CONV_TABLE.each {|k,v| ENTITY_CONV_TABLE[k][0].insert(-1, '{}')}
515
+
516
+ def entity_to_latex(entity)
517
+ text, package = ENTITY_CONV_TABLE[entity.code_point]
518
+ if text
519
+ @data[:packages] << package if package
520
+ text
521
+ else
522
+ warning("Couldn't find entity with code #{entity.code_point} in substitution table!")
523
+ ''
524
+ end
525
+ end
526
+
527
+ def convert_entity(el, opts)
528
+ entity_to_latex(el.value)
529
+ end
530
+
531
+ TYPOGRAPHIC_SYMS = {
532
+ :mdash => '---', :ndash => '--', :hellip => '\ldots{}',
533
+ :laquo_space => '\guillemotleft{}~', :raquo_space => '~\guillemotright{}',
534
+ :laquo => '\guillemotleft{}', :raquo => '\guillemotright{}'
535
+ } # :nodoc:
536
+ def convert_typographic_sym(el, opts)
537
+ TYPOGRAPHIC_SYMS[el.value]
538
+ end
539
+
540
+ def convert_smart_quote(el, opts)
541
+ res = entity_to_latex(smart_quote_entity(el)).chomp('{}')
542
+ res << "{}" if ((nel = opts[:parent].children[opts[:index]+1]) && nel.type == :smart_quote) || res =~ /\w$/
543
+ res
544
+ end
545
+
546
+ def convert_math(el, opts)
547
+ @data[:packages] += %w[amssymb amsmath amsthm amsfonts]
548
+ if el.options[:category] == :block
549
+ if el.value =~ /\A\s*\\begin\{/
550
+ el.value
551
+ else
552
+ latex_environment('displaymath', el, el.value)
553
+ end
554
+ else
555
+ "$#{el.value}$"
556
+ end
557
+ end
558
+
559
+ def convert_abbreviation(el, opts)
560
+ el.value
561
+ end
562
+
563
+ # Wrap the +text+ inside a LaTeX environment of type +type+. The element +el+ is passed on to
564
+ # the method #attribute_list -- the resulting string is appended to both the \\begin and the
565
+ # \\end lines of the LaTeX environment for easier post-processing of LaTeX environments.
566
+ def latex_environment(type, el, text)
567
+ attrs = attribute_list(el)
568
+ "\\begin{#{type}}#{latex_link_target(el)}#{attrs}\n#{text.rstrip}\n\\end{#{type}}#{attrs}\n"
569
+ end
570
+
571
+ # Return a string containing a valid \hypertarget command if the element has an ID defined, or
572
+ # +nil+ otherwise. If the parameter +add_label+ is +true+, a \label command will also be used
573
+ # additionally to the \hypertarget command.
574
+ def latex_link_target(el, add_label = false)
575
+ if (id = el.attr['id'])
576
+ "\\hypertarget{#{id}}{}" << (add_label ? "\\label{#{id}}" : '')
577
+ else
578
+ nil
579
+ end
580
+ end
581
+
582
+ # Return a LaTeX comment containing all attributes as 'key="value"' pairs.
583
+ def attribute_list(el)
584
+ attrs = el.attr.map {|k,v| v.nil? ? '' : " #{k}=\"#{v.to_s}\""}.compact.sort.join('')
585
+ attrs = " % #{attrs}" if !attrs.empty?
586
+ attrs
587
+ end
588
+
589
+ ESCAPE_MAP = {
590
+ "^" => "\\^{}",
591
+ "\\" => "\\textbackslash{}",
592
+ "~" => "\\ensuremath{\\sim}",
593
+ "|" => "\\textbar{}",
594
+ "<" => "\\textless{}",
595
+ ">" => "\\textgreater{}"
596
+ }.merge(Hash[*("{}$%&_#".scan(/./).map {|c| [c, "\\#{c}"]}.flatten)]) # :nodoc:
597
+ ESCAPE_RE = Regexp.union(*ESCAPE_MAP.collect {|k,v| k}) # :nodoc:
598
+
599
+ # Escape the special LaTeX characters in the string +str+.
600
+ def escape(str)
601
+ str.gsub(ESCAPE_RE) {|m| ESCAPE_MAP[m]}
602
+ end
603
+
604
+ end
605
+
606
+ end
607
+ end