tedi2html.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. /*
  2. * tedi2html main code
  3. * Copyright (C) <2022> <alkeon> [alkeon@autistici.org]
  4. * Texdi is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * Texdi is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with tedi2html. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. #include <iostream>
  18. #include <string>
  19. #include <fstream>
  20. #include <sstream>
  21. #include "../tedi2lang/tedi2lang.h"
  22. #include "tedi2html.h"
  23. #include "../tedi2lang/exception.h"
  24. using namespace std;
  25. #define BLOCK 2
  26. #define IMAGE 1
  27. #define LINK 0
  28. #define NO_TAG -1
  29. tedi2html::tedi2html(tags_definition td): tedi2lang(td), _start_paragraph_tag("<p>"){}
  30. tedi2html::tedi2html(): tedi2lang(default_tags_value()), _start_paragraph_tag("<p>"){}
  31. tags_definition tedi2html::default_tags_value(){
  32. tags_definition td;
  33. td.start_heading_first_level_tag = "<h1>";
  34. td.start_heading_second_level_tag = "<h2>";
  35. td.start_heading_third_level_tag = "<h3>";
  36. td.start_heading_fourth_level_tag = "<h4>";
  37. td.end_heading_first_level_tag = "</h1>";
  38. td.end_heading_second_level_tag = "</h2>";
  39. td.end_heading_third_level_tag = "</h3>";
  40. td.end_heading_fourth_level_tag = "</h4>";
  41. td.start_list_tag = "<ul>";
  42. td.list_item_tag = "<li>";
  43. td.end_list_tag = "</ul>";
  44. td.start_container_tag = "<div class=\"";
  45. td.middle_container_tag = "\">";
  46. td.end_container_tag = "</div>";
  47. td.start_link_tag = "<a href=\"";
  48. td.middle_link_tag = "\">";
  49. td.end_link_tag = "</a>";
  50. td.start_image_tag = "<img alt=\"";
  51. td.middle_image_tag = "\" src=\"";
  52. td.end_image_tag = "\">";
  53. td.start_table_tag = "<table>";
  54. td.end_table_tag = "</table>";
  55. td.start_table_row_tag = "<tr>";
  56. td.end_table_row_tag = "</tr>";
  57. td.start_table_data_tag = "<td>";
  58. td.end_table_data_tag = "</td>";
  59. td.end_paragraph_tag = "</p>";
  60. return td;
  61. }
  62. /*
  63. * Add header, iterate through text and finish with footer
  64. *
  65. */
  66. string tedi2html::convert(string text, string header, string footer) {
  67. stringstream index(text);
  68. string line, return_text = header;
  69. _open_brackets = 0;
  70. _is_paragraph = false;
  71. _is_converting_table = false;
  72. _is_unordered_list = 0;
  73. while(getline(index, line)){
  74. _has_block = false;
  75. return_text += convert_line(line);
  76. }
  77. if(_open_brackets > 0)
  78. throw Invalid("Missing '}' in document.", "End of file");
  79. if(_is_converting_table) return_text += _end_table_tag + "\n";
  80. if(_is_paragraph) return_text += _end_paragraph_tag + "\n";
  81. return_text += footer;
  82. return return_text;
  83. }
  84. /*
  85. * Deletes heading tag and insert HTML heading tag
  86. *
  87. */
  88. string tedi2html::convert_line_heading(string& line, size_t hash_position) {
  89. int level = 0;
  90. while(found(hash_position) && line[hash_position + level] == '#')
  91. ++level;
  92. if(hash_position + level >= line.size())
  93. throw Invalid("Unexpected heading size", line);
  94. else
  95. line = line.substr(hash_position + level, line.size() - 1);
  96. line = strip_escaping(line);
  97. switch(level){
  98. case 1: line = _start_heading_first_level_tag + line + _end_heading_first_level_tag; break;
  99. case 2: line = _start_heading_second_level_tag + line + _end_heading_second_level_tag; break;
  100. case 3: line = _start_heading_third_level_tag + line + _end_heading_third_level_tag; break;
  101. case 4: line = _start_heading_fourth_level_tag + line + _end_heading_fourth_level_tag; break;
  102. default: line = _start_heading_fourth_level_tag + line + _end_heading_fourth_level_tag; break;
  103. }
  104. return (close_paragraph(_is_paragraph) + line + "\n");
  105. }
  106. // List tags
  107. string tedi2html::convert_line_list_start(string& line) {
  108. ++_is_unordered_list;
  109. return close_paragraph(_is_paragraph) + _start_list_tag + "\n";
  110. }
  111. void tedi2html::convert_line_list_item(string& line, size_t li){
  112. line = line.erase(li, 2);
  113. line = line.insert(li, close_paragraph(_is_paragraph) + _list_item_tag);
  114. }
  115. string tedi2html::convert_line_list_end(string& line) {
  116. --_is_unordered_list;
  117. return close_paragraph(_is_paragraph) + _end_list_tag + "\n";
  118. }
  119. /*
  120. * Deletes block tag and insert block tag
  121. *
  122. */
  123. void tedi2html::convert_line_block(string& line) {
  124. if(_is_paragraph){
  125. line = "</p>\n" + line;
  126. _is_paragraph = false;
  127. }
  128. size_t start_tag = get_not_escaped_tag(line, "{(");
  129. if(found(start_tag) && !is_tag_escaped(line, start_tag)) {
  130. line = line.erase(start_tag, 2);
  131. line = line.insert(start_tag, _start_container_tag);
  132. start_tag = get_not_escaped_tag(line, ") ");
  133. if(found(start_tag) && !is_tag_escaped(line, start_tag)) {
  134. line = line.erase(start_tag, 2);
  135. line = line.insert(start_tag, _middle_container_tag);
  136. size_t end_tag = correct_position(line, start_tag + _middle_container_tag.size(), '{', '}');
  137. if(found(end_tag)) {
  138. line = line.erase(end_tag, 1);
  139. line = line.insert(end_tag, _end_container_tag);
  140. } else
  141. ++_open_brackets;
  142. } else
  143. throw Invalid("Missing ')' in block tag.",line);
  144. } else
  145. throw Invalid("Missing block tag.",line);
  146. _has_block = true;
  147. }
  148. /*
  149. * Start or continue table conversion
  150. *
  151. */
  152. string tedi2html::convert_line_table(string& line, size_t first_pipe) {
  153. string return_text;
  154. if(!_is_converting_table) {
  155. if(first_pipe != line.rfind("|")) {
  156. return_text += close_paragraph(_is_paragraph) + _start_table_tag + "\n";
  157. _is_converting_table = true;
  158. return return_text + convert_line_table_row(line);
  159. } else
  160. return convert_line_ending(line);
  161. }else
  162. return return_text + convert_line_table_row(line);
  163. }
  164. /*
  165. * Check line ending and convert depending ending whitespace
  166. *
  167. */
  168. string tedi2html::convert_line_ending(string& line){
  169. if(line.size() > 1){
  170. if(line[line.size() - 1] == ' '){
  171. if(_is_paragraph) {
  172. _is_paragraph = false;
  173. return strip_escaping(line) + _end_paragraph_tag + "\n";
  174. } else {
  175. if(!_is_converting_table && !_has_block && !_is_unordered_list)
  176. return _start_paragraph_tag + strip_escaping(line) + _end_paragraph_tag + "\n";
  177. else
  178. return strip_escaping(line) + "\n";
  179. }
  180. } else if(!_is_converting_table && !_has_block && !_is_unordered_list) {
  181. if(_is_paragraph)
  182. return strip_escaping(line) + "\n";
  183. else {
  184. _is_paragraph = true;
  185. return _start_paragraph_tag + strip_escaping(line) + "\n";
  186. }
  187. } else {
  188. if(_is_paragraph) {
  189. _is_paragraph = false;
  190. return _end_paragraph_tag + "\n" + strip_escaping(line) + "\n";
  191. } else
  192. return strip_escaping(line) + "\n";
  193. }
  194. } else
  195. return line + "\n";
  196. }
  197. /*
  198. * Control tags:
  199. * <!, <>, <+. <
  200. */
  201. string tedi2html::convert_line_control_tags(string& line){
  202. if(line[1] != '!') {
  203. if(line[1] == '>') {
  204. line = line.erase(0,2);
  205. return line + "\n";
  206. } else if(line[1] == '+') {
  207. line = line.erase(0,2);
  208. if(_is_paragraph) {
  209. _is_paragraph = false;
  210. return line + _end_paragraph_tag + "\n";
  211. } else
  212. return _start_paragraph_tag + line + _end_paragraph_tag + "\n";
  213. } else {
  214. line = line.erase(0,1);
  215. return line + "\n";
  216. }
  217. } else
  218. return "";
  219. }
  220. /*
  221. * If there's an open paragraph, close it
  222. *
  223. */
  224. string tedi2html::close_paragraph(bool& is_paragraph){
  225. string p;
  226. if(is_paragraph){
  227. p = _end_paragraph_tag + "\n";
  228. is_paragraph = false;
  229. }
  230. return p;
  231. }