header_footer_code.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541
  1. // Copyright (c) 2014-2021 Thomas Fussell
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy
  4. // of this software and associated documentation files (the "Software"), to deal
  5. // in the Software without restriction, including without limitation the rights
  6. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. // copies of the Software, and to permit persons to whom the Software is
  8. // furnished to do so, subject to the following conditions:
  9. //
  10. // The above copyright notice and this permission notice shall be included in
  11. // all copies or substantial portions of the Software.
  12. //
  13. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. // THE SOFTWARE
  20. //
  21. // @license: http://www.opensource.org/licenses/mit-license.php
  22. // @author: see AUTHORS file
  23. #include <detail/header_footer/header_footer_code.hpp>
  24. //#include <detail/numeric_utils.hpp>
  25. namespace xlnt {
  26. namespace detail {
  27. std::array<xlnt::optional<xlnt::rich_text>, 3> decode_header_footer(const std::string &hf_string, const number_serialiser &serialiser)
  28. {
  29. std::array<xlnt::optional<xlnt::rich_text>, 3> result;
  30. if (hf_string.empty())
  31. {
  32. return result;
  33. }
  34. enum class hf_code
  35. {
  36. left_section, // &L
  37. center_section, // &C
  38. right_section, // &R
  39. current_page_number, // &P
  40. total_page_number, // &N
  41. font_size, // &#
  42. text_font_color, // &KRRGGBB or &KTTSNN
  43. text_strikethrough, // &S
  44. text_superscript, // &X
  45. text_subscript, // &Y
  46. date, // &D
  47. time, // &T
  48. picture_as_background, // &G
  49. text_single_underline, // &U
  50. text_double_underline, // &E
  51. workbook_file_path, // &Z
  52. workbook_file_name, // &F
  53. sheet_tab_name, // &A
  54. add_to_page_number, // &+
  55. subtract_from_page_number, // &-
  56. text_font_name, // &"font name,font type"
  57. bold_font_style, // &B
  58. italic_font_style, // &I
  59. outline_style, // &O
  60. shadow_style, // &H
  61. text // everything else
  62. };
  63. struct hf_token
  64. {
  65. hf_code code = hf_code::text;
  66. std::string value;
  67. };
  68. std::vector<hf_token> tokens;
  69. std::size_t position = 0;
  70. while (position < hf_string.size())
  71. {
  72. hf_token token;
  73. auto next_ampersand = hf_string.find('&', position + 1);
  74. token.value = hf_string.substr(position, next_ampersand - position);
  75. auto next_position = next_ampersand;
  76. if (hf_string[position] == '&')
  77. {
  78. token.value.clear();
  79. next_position = position + 2;
  80. auto first_code_char = hf_string[position + 1];
  81. if (first_code_char == '"')
  82. {
  83. auto end_quote_index = hf_string.find('"', position + 2);
  84. next_position = end_quote_index + 1;
  85. token.value = hf_string.substr(position + 2, end_quote_index - position - 2); // remove quotes
  86. token.code = hf_code::text_font_name;
  87. }
  88. else if (first_code_char == '&')
  89. {
  90. token.value = "&&"; // escaped ampersand
  91. }
  92. else if (first_code_char == 'L')
  93. {
  94. token.code = hf_code::left_section;
  95. }
  96. else if (first_code_char == 'C')
  97. {
  98. token.code = hf_code::center_section;
  99. }
  100. else if (first_code_char == 'R')
  101. {
  102. token.code = hf_code::right_section;
  103. }
  104. else if (first_code_char == 'P')
  105. {
  106. token.code = hf_code::current_page_number;
  107. }
  108. else if (first_code_char == 'N')
  109. {
  110. token.code = hf_code::total_page_number;
  111. }
  112. else if (std::string("0123456789").find(hf_string[position + 1]) != std::string::npos)
  113. {
  114. token.code = hf_code::font_size;
  115. next_position = hf_string.find_first_not_of("0123456789", position + 1);
  116. token.value = hf_string.substr(position + 1, next_position - position - 1);
  117. }
  118. else if (first_code_char == 'K')
  119. {
  120. if (hf_string[position + 4] == '+' || hf_string[position + 4] == '-')
  121. {
  122. token.value = hf_string.substr(position + 2, 5);
  123. next_position = position + 7;
  124. }
  125. else
  126. {
  127. token.value = hf_string.substr(position + 2, 6);
  128. next_position = position + 8;
  129. }
  130. token.code = hf_code::text_font_color;
  131. }
  132. else if (first_code_char == 'S')
  133. {
  134. token.code = hf_code::text_strikethrough;
  135. }
  136. else if (first_code_char == 'X')
  137. {
  138. token.code = hf_code::text_superscript;
  139. }
  140. else if (first_code_char == 'Y')
  141. {
  142. token.code = hf_code::text_subscript;
  143. }
  144. else if (first_code_char == 'D')
  145. {
  146. token.code = hf_code::date;
  147. }
  148. else if (first_code_char == 'T')
  149. {
  150. token.code = hf_code::time;
  151. }
  152. else if (first_code_char == 'G')
  153. {
  154. token.code = hf_code::picture_as_background;
  155. }
  156. else if (first_code_char == 'U')
  157. {
  158. token.code = hf_code::text_single_underline;
  159. }
  160. else if (first_code_char == 'E')
  161. {
  162. token.code = hf_code::text_double_underline;
  163. }
  164. else if (first_code_char == 'Z')
  165. {
  166. token.code = hf_code::workbook_file_path;
  167. }
  168. else if (first_code_char == 'F')
  169. {
  170. token.code = hf_code::workbook_file_name;
  171. }
  172. else if (first_code_char == 'A')
  173. {
  174. token.code = hf_code::sheet_tab_name;
  175. }
  176. else if (first_code_char == '+')
  177. {
  178. token.code = hf_code::add_to_page_number;
  179. }
  180. else if (first_code_char == '-')
  181. {
  182. token.code = hf_code::subtract_from_page_number;
  183. }
  184. else if (first_code_char == 'B')
  185. {
  186. token.code = hf_code::bold_font_style;
  187. }
  188. else if (first_code_char == 'I')
  189. {
  190. token.code = hf_code::italic_font_style;
  191. }
  192. else if (first_code_char == 'O')
  193. {
  194. token.code = hf_code::outline_style;
  195. }
  196. else if (first_code_char == 'H')
  197. {
  198. token.code = hf_code::shadow_style;
  199. }
  200. }
  201. position = next_position;
  202. tokens.push_back(token);
  203. }
  204. const auto parse_section = [&tokens, &result, &serialiser](hf_code code) {
  205. std::vector<hf_code> end_codes{hf_code::left_section, hf_code::center_section, hf_code::right_section};
  206. end_codes.erase(std::find(end_codes.begin(), end_codes.end(), code));
  207. std::size_t start_index = 0;
  208. while (start_index < tokens.size() && tokens[start_index].code != code)
  209. {
  210. ++start_index;
  211. }
  212. if (start_index == tokens.size())
  213. {
  214. return;
  215. }
  216. ++start_index; // skip the section code
  217. std::size_t end_index = start_index;
  218. while (end_index < tokens.size()
  219. && std::find(end_codes.begin(), end_codes.end(), tokens[end_index].code) == end_codes.end())
  220. {
  221. ++end_index;
  222. }
  223. xlnt::rich_text current_text;
  224. xlnt::rich_text_run current_run;
  225. // todo: all this nice parsing and the codes are just being turned back into text representations
  226. // It would be nice to create an interface for the library to read and write these codes
  227. for (auto i = start_index; i < end_index; ++i)
  228. {
  229. const auto &current_token = tokens[i];
  230. if (current_token.code == hf_code::text)
  231. {
  232. current_run.first = current_run.first + current_token.value;
  233. continue;
  234. }
  235. if (!current_run.first.empty())
  236. {
  237. current_text.add_run(current_run);
  238. current_run = xlnt::rich_text_run();
  239. }
  240. switch (current_token.code)
  241. {
  242. case hf_code::text: {
  243. break; // already handled above
  244. }
  245. case hf_code::left_section: {
  246. break; // used below
  247. }
  248. case hf_code::center_section: {
  249. break; // used below
  250. }
  251. case hf_code::right_section: {
  252. break; // used below
  253. }
  254. case hf_code::current_page_number: {
  255. current_run.first = current_run.first + "&P";
  256. break;
  257. }
  258. case hf_code::total_page_number: {
  259. current_run.first = current_run.first + "&N";
  260. break;
  261. }
  262. case hf_code::font_size: {
  263. if (!current_run.second.is_set())
  264. {
  265. current_run.second = xlnt::font();
  266. }
  267. current_run.second.get().size(serialiser.deserialise(current_token.value));
  268. break;
  269. }
  270. case hf_code::text_font_color: {
  271. if (current_token.value.size() == 6)
  272. {
  273. if (!current_run.second.is_set())
  274. {
  275. current_run.second = xlnt::font();
  276. }
  277. current_run.second.get().color(xlnt::rgb_color(current_token.value));
  278. }
  279. break;
  280. }
  281. case hf_code::text_strikethrough: {
  282. break;
  283. }
  284. case hf_code::text_superscript: {
  285. break;
  286. }
  287. case hf_code::text_subscript: {
  288. break;
  289. }
  290. case hf_code::date: {
  291. current_run.first = current_run.first + "&D";
  292. break;
  293. }
  294. case hf_code::time: {
  295. current_run.first = current_run.first + "&T";
  296. break;
  297. }
  298. case hf_code::picture_as_background: {
  299. current_run.first = current_run.first + "&G";
  300. break;
  301. }
  302. case hf_code::text_single_underline: {
  303. if (!current_run.second.is_set())
  304. {
  305. current_run.second = xlnt::font();
  306. }
  307. current_run.second.get().underline(font::underline_style::single);
  308. break;
  309. }
  310. case hf_code::text_double_underline: {
  311. break;
  312. }
  313. case hf_code::workbook_file_path: {
  314. current_run.first = current_run.first + "&Z";
  315. break;
  316. }
  317. case hf_code::workbook_file_name: {
  318. current_run.first = current_run.first + "&F";
  319. break;
  320. }
  321. case hf_code::sheet_tab_name: {
  322. current_run.first = current_run.first + "&A";
  323. break;
  324. }
  325. case hf_code::add_to_page_number: {
  326. break;
  327. }
  328. case hf_code::subtract_from_page_number: {
  329. break;
  330. }
  331. case hf_code::text_font_name: {
  332. auto comma_index = current_token.value.find(',');
  333. auto font_name = current_token.value.substr(0, comma_index);
  334. if (!current_run.second.is_set())
  335. {
  336. current_run.second = xlnt::font();
  337. }
  338. if (font_name != "-")
  339. {
  340. current_run.second.get().name(font_name);
  341. }
  342. if (comma_index != std::string::npos)
  343. {
  344. auto font_type = current_token.value.substr(comma_index + 1);
  345. if (font_type == "Bold")
  346. {
  347. current_run.second.get().bold(true);
  348. }
  349. else if (font_type == "Italic")
  350. {
  351. // TODO
  352. }
  353. else if (font_type == "BoldItalic")
  354. {
  355. current_run.second.get().bold(true);
  356. }
  357. }
  358. break;
  359. }
  360. case hf_code::bold_font_style: {
  361. if (!current_run.second.is_set())
  362. {
  363. current_run.second = xlnt::font();
  364. }
  365. current_run.second.get().bold(true);
  366. break;
  367. }
  368. case hf_code::italic_font_style: {
  369. break;
  370. }
  371. case hf_code::outline_style: {
  372. break;
  373. }
  374. case hf_code::shadow_style: {
  375. break;
  376. }
  377. }
  378. }
  379. if (!current_run.first.empty())
  380. {
  381. current_text.add_run(current_run);
  382. }
  383. auto location_index =
  384. static_cast<std::size_t>(code == hf_code::left_section ? 0 : code == hf_code::center_section ? 1 : 2);
  385. if (!current_text.plain_text().empty())
  386. {
  387. result[location_index] = current_text;
  388. }
  389. };
  390. parse_section(hf_code::left_section);
  391. parse_section(hf_code::center_section);
  392. parse_section(hf_code::right_section);
  393. return result;
  394. }
  395. std::string encode_header_footer(const rich_text &t, header_footer::location where, const number_serialiser &serialiser)
  396. {
  397. const auto location_code_map =
  398. std::unordered_map<header_footer::location,
  399. std::string, scoped_enum_hash<header_footer::location>>{
  400. {header_footer::location::left, "&L"},
  401. {header_footer::location::center, "&C"},
  402. {header_footer::location::right, "&R"},
  403. };
  404. auto encoded = location_code_map.at(where);
  405. for (const auto &run : t.runs())
  406. {
  407. if (run.first.empty()) continue;
  408. if (run.second.is_set())
  409. {
  410. if (run.second.get().has_name())
  411. {
  412. encoded.push_back('&');
  413. encoded.push_back('"');
  414. encoded.append(run.second.get().name());
  415. encoded.push_back(',');
  416. if (run.second.get().bold())
  417. {
  418. encoded.append("Bold");
  419. }
  420. else
  421. {
  422. encoded.append("Regular");
  423. }
  424. // todo: BoldItalic?
  425. encoded.push_back('"');
  426. }
  427. else if (run.second.get().bold())
  428. {
  429. encoded.append("&B");
  430. }
  431. if (run.second.get().has_size())
  432. {
  433. encoded.push_back('&');
  434. encoded.append(serialiser.serialise(run.second.get().size()));
  435. }
  436. if (run.second.get().underlined())
  437. {
  438. switch (run.second.get().underline())
  439. {
  440. case font::underline_style::single:
  441. case font::underline_style::single_accounting:
  442. encoded.append("&U");
  443. break;
  444. case font::underline_style::double_:
  445. case font::underline_style::double_accounting:
  446. encoded.append("&E");
  447. break;
  448. case font::underline_style::none:
  449. break;
  450. }
  451. }
  452. if (run.second.get().has_color())
  453. {
  454. encoded.push_back('&');
  455. encoded.push_back('K');
  456. encoded.append(run.second.get().color().rgb().hex_string().substr(2));
  457. }
  458. }
  459. encoded.append(run.first);
  460. }
  461. return encoded;
  462. };
  463. } // namespace detail
  464. } // namespace xlnt