serialization_test_suite.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813
  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 <iostream>
  24. #include <xlnt/xlnt.hpp>
  25. #include <helpers/path_helper.hpp>
  26. #include <helpers/temporary_file.hpp>
  27. #include <helpers/test_suite.hpp>
  28. #include <helpers/xml_helper.hpp>
  29. class serialization_test_suite : public test_suite
  30. {
  31. public:
  32. serialization_test_suite()
  33. {
  34. register_test(test_produce_empty);
  35. register_test(test_produce_simple_excel);
  36. register_test(test_save_after_sheet_deletion);
  37. register_test(test_write_comments_hyperlinks_formulae);
  38. register_test(test_save_after_clear_formula);
  39. register_test(test_load_non_xlsx);
  40. register_test(test_decrypt_agile);
  41. register_test(test_decrypt_libre_office);
  42. register_test(test_decrypt_standard);
  43. register_test(test_decrypt_numbers);
  44. register_test(test_read_unicode_filename);
  45. register_test(test_comments);
  46. register_test(test_read_hyperlink);
  47. register_test(test_read_formulae);
  48. register_test(test_read_headers_and_footers);
  49. register_test(test_read_custom_properties);
  50. register_test(test_read_custom_heights_widths);
  51. register_test(test_write_custom_heights_widths);
  52. register_test(test_round_trip_rw_minimal);
  53. register_test(test_round_trip_rw_default);
  54. register_test(test_round_trip_rw_every_style);
  55. register_test(test_round_trip_rw_unicode);
  56. register_test(test_round_trip_rw_comments_hyperlinks_formulae);
  57. register_test(test_round_trip_rw_print_settings);
  58. register_test(test_round_trip_rw_advanced_properties);
  59. register_test(test_round_trip_rw_custom_heights_widths);
  60. register_test(test_round_trip_rw_encrypted_agile);
  61. register_test(test_round_trip_rw_encrypted_libre);
  62. register_test(test_round_trip_rw_encrypted_standard);
  63. register_test(test_round_trip_rw_encrypted_numbers);
  64. register_test(test_streaming_read);
  65. register_test(test_streaming_write);
  66. register_test(test_load_save_german_locale);
  67. register_test(test_Issue445_inline_str_load);
  68. register_test(test_Issue445_inline_str_streaming_read);
  69. register_test(test_Issue492_stream_empty_row);
  70. register_test(test_Issue503_external_link_load);
  71. register_test(test_formatting);
  72. register_test(test_active_sheet);
  73. }
  74. bool workbook_matches_file(xlnt::workbook &wb, const xlnt::path &file)
  75. {
  76. std::vector<std::uint8_t> wb_data;
  77. wb.save(wb_data);
  78. wb.save("temp.xlsx");
  79. std::ifstream file_stream(file.string(), std::ios::binary);
  80. auto file_data = xlnt::detail::to_vector(file_stream);
  81. return xml_helper::xlsx_archives_match(wb_data, file_data);
  82. }
  83. void test_produce_empty()
  84. {
  85. xlnt::workbook wb;
  86. const auto path = path_helper::test_file("3_default.xlsx");
  87. xlnt_assert(workbook_matches_file(wb, path));
  88. }
  89. void test_produce_simple_excel()
  90. {
  91. xlnt::workbook wb;
  92. auto ws = wb.active_sheet();
  93. auto bold_font = xlnt::font().bold(true);
  94. ws.cell("A1").value("Type");
  95. ws.cell("A1").font(bold_font);
  96. ws.cell("B1").value("Value");
  97. ws.cell("B1").font(bold_font);
  98. ws.cell("A2").value("null");
  99. ws.cell("B2").value(nullptr);
  100. ws.cell("A3").value("bool (true)");
  101. ws.cell("B3").value(true);
  102. ws.cell("A4").value("bool (false)");
  103. ws.cell("B4").value(false);
  104. ws.cell("A5").value("number (int)");
  105. ws.cell("B5").value(std::numeric_limits<int>::max());
  106. ws.cell("A5").value("number (unsigned int)");
  107. ws.cell("B5").value(std::numeric_limits<unsigned int>::max());
  108. ws.cell("A6").value("number (long long int)");
  109. ws.cell("B6").value(std::numeric_limits<long long int>::max());
  110. ws.cell("A6").value("number (unsigned long long int)");
  111. ws.cell("B6").value(std::numeric_limits<unsigned long long int>::max());
  112. ws.cell("A13").value("number (float)");
  113. ws.cell("B13").value(std::numeric_limits<float>::max());
  114. ws.cell("A14").value("number (double)");
  115. ws.cell("B14").value(std::numeric_limits<double>::max());
  116. ws.cell("A16").value("text (char *)");
  117. ws.cell("B16").value("string");
  118. ws.cell("A17").value("text (std::string)");
  119. ws.cell("B17").value(std::string("string"));
  120. ws.cell("A18").value("date");
  121. ws.cell("B18").value(xlnt::date(2016, 2, 3));
  122. ws.cell("A19").value("time");
  123. ws.cell("B19").value(xlnt::time(1, 2, 3, 4));
  124. ws.cell("A20").value("datetime");
  125. ws.cell("B20").value(xlnt::datetime(2016, 2, 3, 1, 2, 3, 4));
  126. ws.cell("A21").value("timedelta");
  127. ws.cell("B21").value(xlnt::timedelta(1, 2, 3, 4, 5));
  128. ws.freeze_panes("B2");
  129. std::vector<std::uint8_t> temp_buffer;
  130. wb.save(temp_buffer);
  131. xlnt_assert(!temp_buffer.empty());
  132. }
  133. void test_save_after_sheet_deletion()
  134. {
  135. xlnt::workbook workbook;
  136. xlnt_assert_equals(workbook.sheet_titles().size(), 1);
  137. auto sheet = workbook.create_sheet();
  138. sheet.title("XXX1");
  139. xlnt_assert_equals(workbook.sheet_titles().size(), 2);
  140. workbook.remove_sheet(workbook.sheet_by_title("XXX1"));
  141. xlnt_assert_equals(workbook.sheet_titles().size(), 1);
  142. std::vector<std::uint8_t> temp_buffer;
  143. xlnt_assert_throws_nothing(workbook.save(temp_buffer));
  144. xlnt_assert(!temp_buffer.empty());
  145. }
  146. void test_write_comments_hyperlinks_formulae()
  147. {
  148. xlnt::workbook wb;
  149. xlnt::sheet_format_properties format_properties;
  150. format_properties.base_col_width = 10.0;
  151. format_properties.default_row_height = 16.0;
  152. format_properties.dy_descent = 0.2;
  153. auto sheet1 = wb.active_sheet();
  154. sheet1.format_properties(format_properties);
  155. auto selection = xlnt::selection();
  156. selection.active_cell("C1");
  157. selection.sqref("C1");
  158. sheet1.view().add_selection(selection);
  159. // comments
  160. auto comment_font = xlnt::font()
  161. .bold(true)
  162. .size(10)
  163. .color(xlnt::indexed_color(81))
  164. .name("Calibri");
  165. sheet1.cell("A1").value("Sheet1!A1");
  166. sheet1.cell("A1").comment("Sheet1 comment", comment_font, "Microsoft Office User");
  167. sheet1.cell("A2").value("Sheet1!A2");
  168. sheet1.cell("A2").comment("Sheet1 comment2", comment_font, "Microsoft Office User");
  169. // hyperlinks
  170. auto hyperlink_font = xlnt::font()
  171. .size(12)
  172. .color(xlnt::theme_color(10))
  173. .name("Calibri")
  174. .family(2)
  175. .scheme("minor")
  176. .underline(xlnt::font::underline_style::single);
  177. auto hyperlink_style = wb.create_builtin_style(8);
  178. hyperlink_style.font(hyperlink_font);
  179. hyperlink_style.number_format(hyperlink_style.number_format(), false);
  180. hyperlink_style.fill(hyperlink_style.fill(), false);
  181. hyperlink_style.border(hyperlink_style.border(), false);
  182. auto hyperlink_format = wb.create_format();
  183. hyperlink_format.font(hyperlink_font);
  184. hyperlink_format.style(hyperlink_style);
  185. sheet1.cell("A4").hyperlink("https://microsoft.com/", "hyperlink1");
  186. sheet1.cell("A4").format(hyperlink_format);
  187. sheet1.cell("A5").hyperlink("https://google.com/");
  188. sheet1.cell("A5").format(hyperlink_format);
  189. sheet1.cell("A6").hyperlink(sheet1.cell("A1"));
  190. sheet1.cell("A6").format(hyperlink_format);
  191. sheet1.cell("A7").hyperlink("mailto:invalid@example.com?subject=important");
  192. sheet1.cell("A7").format(hyperlink_format);
  193. // formulae
  194. sheet1.cell("C1").formula("=CONCATENATE(C2,C3)");
  195. sheet1.cell("C2").value("a");
  196. sheet1.cell("C3").value("b");
  197. for (auto i = 1; i <= 7; ++i)
  198. {
  199. sheet1.row_properties(i).dy_descent = 0.2;
  200. }
  201. auto sheet2 = wb.create_sheet();
  202. sheet2.format_properties(format_properties);
  203. sheet2.add_view(xlnt::sheet_view());
  204. sheet2.view().add_selection(selection);
  205. // comments
  206. sheet2.cell("A1").value("Sheet2!A1");
  207. sheet2.cell("A1").comment("Sheet2 comment", comment_font, "Microsoft Office User");
  208. sheet2.cell("A2").value("Sheet2!A2");
  209. sheet2.cell("A2").comment("Sheet2 comment2", comment_font, "Microsoft Office User");
  210. // hyperlinks
  211. sheet2.cell("A4").hyperlink("https://apple.com/", "hyperlink2");
  212. sheet2.cell("A4").format(hyperlink_format);
  213. // formulae
  214. sheet2.cell("C1").formula("=C2*C3");
  215. sheet2.cell("C2").value(2);
  216. sheet2.cell("C3").value(3);
  217. for (auto i = 1; i <= 4; ++i)
  218. {
  219. sheet2.row_properties(i).dy_descent = 0.2;
  220. }
  221. wb.default_slicer_style("SlicerStyleLight1");
  222. wb.enable_known_fonts();
  223. wb.core_property(xlnt::core_property::created, "2018-03-18T20:53:30Z");
  224. wb.core_property(xlnt::core_property::modified, "2018-03-18T20:59:53Z");
  225. const auto path = path_helper::test_file("10_comments_hyperlinks_formulae.xlsx");
  226. xlnt_assert(workbook_matches_file(wb, path));
  227. }
  228. void test_save_after_clear_formula()
  229. {
  230. xlnt::workbook wb;
  231. const auto path = path_helper::test_file("18_formulae.xlsx");
  232. wb.load(path);
  233. auto ws1 = wb.sheet_by_index(0);
  234. for (auto row : ws1)
  235. {
  236. for (auto cell : row)
  237. {
  238. cell.clear_formula();
  239. }
  240. }
  241. wb.save("clear_formulae.xlsx");
  242. }
  243. void test_load_non_xlsx()
  244. {
  245. xlnt::workbook wb;
  246. const auto path = path_helper::test_file("1_powerpoint_presentation.xlsx");
  247. xlnt_assert_throws(wb.load(path), xlnt::invalid_file);
  248. }
  249. void test_decrypt_agile()
  250. {
  251. xlnt::workbook wb;
  252. const auto path = path_helper::test_file("5_encrypted_agile.xlsx");
  253. xlnt_assert_throws(wb.load(path, "incorrect"), xlnt::exception);
  254. xlnt_assert_throws_nothing(wb.load(path, "secret"));
  255. }
  256. void test_decrypt_libre_office()
  257. {
  258. xlnt::workbook wb;
  259. const auto path = path_helper::test_file("6_encrypted_libre.xlsx");
  260. xlnt_assert_throws(wb.load(path, "incorrect"), xlnt::exception);
  261. xlnt_assert_throws_nothing(wb.load(path, u8"\u043F\u0430\u0440\u043E\u043B\u044C")); // u8"пароль"
  262. }
  263. void test_decrypt_standard()
  264. {
  265. xlnt::workbook wb;
  266. const auto path = path_helper::test_file("7_encrypted_standard.xlsx");
  267. xlnt_assert_throws(wb.load(path, "incorrect"), xlnt::exception);
  268. xlnt_assert_throws_nothing(wb.load(path, "password"));
  269. }
  270. void test_decrypt_numbers()
  271. {
  272. xlnt::workbook wb;
  273. const auto path = path_helper::test_file("8_encrypted_numbers.xlsx");
  274. xlnt_assert_throws(wb.load(path, "incorrect"), xlnt::exception);
  275. xlnt_assert_throws_nothing(wb.load(path, "secret"));
  276. }
  277. void test_read_unicode_filename()
  278. {
  279. #ifdef _MSC_VER
  280. xlnt::workbook wb;
  281. // L"/9_unicode_Λ_😇.xlsx" doesn't use wchar_t(0x039B) for the capital lambda...
  282. // L"/9_unicode_\u039B_\U0001F607.xlsx" gives the correct output
  283. const auto path = LSTRING_LITERAL(XLNT_TEST_DATA_DIR) L"/9_unicode_\u039B_\U0001F607.xlsx"; // L"/9_unicode_Λ_😇.xlsx"
  284. wb.load(path);
  285. xlnt_assert_equals(wb.active_sheet().cell("A1").value<std::string>(), u8"un\u00EFc\u00F4d\u0117!"); // u8"unïcôdė!"
  286. #endif
  287. #ifndef __MINGW32__
  288. xlnt::workbook wb2;
  289. // u8"/9_unicode_Λ_😇.xlsx" doesn't use 0xC3AA for the capital lambda...
  290. // u8"/9_unicode_\u039B_\U0001F607.xlsx" gives the correct output
  291. const auto path2 = U8STRING_LITERAL(XLNT_TEST_DATA_DIR) u8"/9_unicode_\u039B_\U0001F607.xlsx"; // u8"/9_unicode_Λ_😇.xlsx"
  292. wb2.load(path2);
  293. xlnt_assert_equals(wb2.active_sheet().cell("A1").value<std::string>(), u8"un\u00EFc\u00F4d\u0117!"); // u8"unïcôdė!"
  294. #endif
  295. }
  296. void test_comments()
  297. {
  298. xlnt::workbook wb;
  299. const auto path = path_helper::test_file("10_comments_hyperlinks_formulae.xlsx");
  300. wb.load(path);
  301. auto sheet1 = wb[0];
  302. xlnt_assert_equals(sheet1.cell("A1").value<std::string>(), "Sheet1!A1");
  303. xlnt_assert_equals(sheet1.cell("A1").comment().plain_text(), "Sheet1 comment");
  304. xlnt_assert_equals(sheet1.cell("A1").comment().author(), "Microsoft Office User");
  305. auto sheet2 = wb[1];
  306. xlnt_assert_equals(sheet2.cell("A1").value<std::string>(), "Sheet2!A1");
  307. xlnt_assert_equals(sheet2.cell("A1").comment().plain_text(), "Sheet2 comment");
  308. xlnt_assert_equals(sheet2.cell("A1").comment().author(), "Microsoft Office User");
  309. }
  310. void test_read_hyperlink()
  311. {
  312. xlnt::workbook wb;
  313. const auto path = path_helper::test_file("10_comments_hyperlinks_formulae.xlsx");
  314. wb.load(path);
  315. auto ws1 = wb.sheet_by_index(0);
  316. xlnt_assert_equals(ws1.title(), "Sheet1");
  317. xlnt_assert(ws1.cell("A4").has_hyperlink());
  318. xlnt_assert_equals(ws1.cell("A4").value<std::string>(), "hyperlink1");
  319. xlnt_assert_equals(ws1.cell("A4").hyperlink().url(), "https://microsoft.com/");
  320. xlnt_assert(ws1.cell("A5").has_hyperlink());
  321. xlnt_assert_equals(ws1.cell("A5").value<std::string>(), "https://google.com/");
  322. xlnt_assert_equals(ws1.cell("A5").hyperlink().url(), "https://google.com/");
  323. xlnt_assert(ws1.cell("A6").has_hyperlink());
  324. xlnt_assert_equals(ws1.cell("A6").value<std::string>(), "Sheet1!A1");
  325. xlnt_assert_equals(ws1.cell("A6").hyperlink().target_range(), "Sheet1!A1");
  326. xlnt_assert(ws1.cell("A7").has_hyperlink());
  327. xlnt_assert_equals(ws1.cell("A7").value<std::string>(), "mailto:invalid@example.com?subject=important");
  328. xlnt_assert_equals(ws1.cell("A7").hyperlink().url(), "mailto:invalid@example.com?subject=important");
  329. }
  330. void test_read_formulae()
  331. {
  332. xlnt::workbook wb;
  333. const auto path = path_helper::test_file("18_formulae.xlsx");
  334. wb.load(path);
  335. auto ws1 = wb.sheet_by_index(0);
  336. // test has_formula
  337. // A1:B3 are plain text cells
  338. // C1:G3,I2,F4 have formulae
  339. for (auto row = 1; row < 4; row++)
  340. {
  341. for (auto column = 1; column < 8; column++)
  342. {
  343. if (column < 3)
  344. {
  345. xlnt_assert(!ws1.cell(column, row).has_formula());
  346. }
  347. else
  348. {
  349. xlnt_assert(ws1.cell(column, row).has_formula());
  350. }
  351. }
  352. }
  353. xlnt_assert(ws1.cell("I2").has_formula());
  354. xlnt_assert(ws1.cell("F4").has_formula());
  355. xlnt_assert(!ws1.cell("C9").has_formula()); // empty cell
  356. xlnt_assert(!ws1.cell("F5").has_formula()); // text cell
  357. xlnt_assert_equals(ws1.cell("C1").formula(), "B1^2"); // basic math with reference
  358. xlnt_assert_equals(ws1.cell("D1").formula(), "CONCATENATE(A1,B1)"); // concat with ref
  359. xlnt_assert_equals(ws1.cell("E1").formula(), "CONCATENATE($C$1,$D$1)"); // concat with absolute ref
  360. xlnt_assert_equals(ws1.cell("F1").formula(), "1+1"); // basic math
  361. xlnt_assert_equals(ws1.cell("G1").formula(), "PI()"); // constant
  362. xlnt_assert_equals(ws1.cell("I2").formula(), "COS(C2)+IMAGINARY(SIN(B2))"); // fancy math
  363. }
  364. void test_read_headers_and_footers()
  365. {
  366. xlnt::workbook wb;
  367. wb.load(path_helper::test_file("11_print_settings.xlsx"));
  368. auto ws = wb.active_sheet();
  369. xlnt_assert_equals(ws.cell("A1").value<std::string>(), "header");
  370. xlnt_assert_equals(ws.cell("A2").value<std::string>(), "and");
  371. xlnt_assert_equals(ws.cell("A3").value<std::string>(), "footer");
  372. xlnt_assert_equals(ws.cell("A4").value<std::string>(), "page1");
  373. xlnt_assert_equals(ws.cell("A43").value<std::string>(), "page2");
  374. xlnt_assert(ws.has_header_footer());
  375. xlnt_assert(ws.header_footer().align_with_margins());
  376. xlnt_assert(ws.header_footer().scale_with_doc());
  377. xlnt_assert(!ws.header_footer().different_first());
  378. xlnt_assert(!ws.header_footer().different_odd_even());
  379. xlnt_assert(ws.header_footer().has_header(xlnt::header_footer::location::left));
  380. xlnt_assert_equals(ws.header_footer().header(xlnt::header_footer::location::left).plain_text(), "left header");
  381. xlnt_assert(ws.header_footer().has_header(xlnt::header_footer::location::center));
  382. xlnt_assert_equals(ws.header_footer().header(xlnt::header_footer::location::center).plain_text(), "center header");
  383. xlnt_assert(ws.header_footer().has_header(xlnt::header_footer::location::right));
  384. xlnt_assert_equals(ws.header_footer().header(xlnt::header_footer::location::right).plain_text(), "right header");
  385. xlnt_assert(ws.header_footer().has_footer(xlnt::header_footer::location::left));
  386. xlnt_assert_equals(ws.header_footer().footer(xlnt::header_footer::location::left).plain_text(), "left && footer");
  387. xlnt_assert(ws.header_footer().has_footer(xlnt::header_footer::location::center));
  388. xlnt_assert_equals(ws.header_footer().footer(xlnt::header_footer::location::center).plain_text(), "center footer");
  389. xlnt_assert(ws.header_footer().has_footer(xlnt::header_footer::location::right));
  390. xlnt_assert_equals(ws.header_footer().footer(xlnt::header_footer::location::right).plain_text(), "right footer");
  391. }
  392. void test_read_custom_properties()
  393. {
  394. xlnt::workbook wb;
  395. wb.load(path_helper::test_file("12_advanced_properties.xlsx"));
  396. xlnt_assert(wb.has_custom_property("Client"));
  397. xlnt_assert_equals(wb.custom_property("Client").get<std::string>(), "me!");
  398. }
  399. void test_read_custom_heights_widths()
  400. {
  401. xlnt::workbook wb;
  402. wb.load(path_helper::test_file("13_custom_heights_widths.xlsx"));
  403. auto ws = wb.active_sheet();
  404. xlnt_assert_equals(ws.cell("A1").value<std::string>(), "A1");
  405. xlnt_assert_equals(ws.cell("B1").value<std::string>(), "B1");
  406. xlnt_assert_equals(ws.cell("D1").value<std::string>(), "D1");
  407. xlnt_assert_equals(ws.cell("A2").value<std::string>(), "A2");
  408. xlnt_assert_equals(ws.cell("B2").value<std::string>(), "B2");
  409. xlnt_assert_equals(ws.cell("D2").value<std::string>(), "D2");
  410. xlnt_assert_equals(ws.cell("A4").value<std::string>(), "A4");
  411. xlnt_assert_equals(ws.cell("B4").value<std::string>(), "B4");
  412. xlnt_assert_equals(ws.cell("D4").value<std::string>(), "D4");
  413. xlnt_assert_equals(ws.row_properties(1).height.get(), 99.95);
  414. xlnt_assert(!ws.row_properties(2).height.is_set());
  415. xlnt_assert_equals(ws.row_properties(3).height.get(), 99.95);
  416. xlnt_assert(!ws.row_properties(4).height.is_set());
  417. xlnt_assert_equals(ws.row_properties(5).height.get(), 99.95);
  418. auto width = ((16.0 * 7) - 5) / 7;
  419. xlnt_assert_delta(ws.column_properties("A").width.get(), width, 1.0E-9);
  420. xlnt_assert(!ws.column_properties("B").width.is_set());
  421. xlnt_assert_delta(ws.column_properties("C").width.get(), width, 1.0E-9);
  422. xlnt_assert(!ws.column_properties("D").width.is_set());
  423. xlnt_assert_delta(ws.column_properties("E").width.get(), width, 1.0E-9);
  424. }
  425. void test_write_custom_heights_widths()
  426. {
  427. xlnt::workbook wb;
  428. wb.core_property(xlnt::core_property::creator, "Microsoft Office User");
  429. wb.core_property(xlnt::core_property::last_modified_by, "Microsoft Office User");
  430. wb.core_property(xlnt::core_property::created, "2017-10-30T23:03:54Z");
  431. wb.core_property(xlnt::core_property::modified, "2017-10-30T23:08:31Z");
  432. wb.default_slicer_style("SlicerStyleLight1");
  433. wb.enable_known_fonts();
  434. auto ws = wb.active_sheet();
  435. auto sheet_format_properties = xlnt::sheet_format_properties();
  436. sheet_format_properties.base_col_width = 10.0;
  437. sheet_format_properties.default_row_height = 16.0;
  438. sheet_format_properties.dy_descent = 0.2;
  439. ws.format_properties(sheet_format_properties);
  440. ws.cell("A1").value("A1");
  441. ws.cell("B1").value("B1");
  442. ws.cell("D1").value("D1");
  443. ws.cell("A2").value("A2");
  444. ws.cell("A4").value("A4");
  445. ws.cell("B2").value("B2");
  446. ws.cell("B4").value("B4");
  447. ws.cell("D2").value("D2");
  448. ws.cell("D4").value("D4");
  449. for (auto i = 1; i <= 5; ++i)
  450. {
  451. ws.row_properties(i).dy_descent = 0.2;
  452. }
  453. ws.row_properties(1).height = 99.95;
  454. ws.row_properties(1).custom_height = true;
  455. ws.row_properties(3).height = 99.95;
  456. ws.row_properties(3).custom_height = true;
  457. ws.row_properties(5).height = 99.95;
  458. ws.row_properties(5).custom_height = true;
  459. auto width = ((16.0 * 7) - 5) / 7;
  460. ws.column_properties("A").width = width;
  461. ws.column_properties("A").custom_width = true;
  462. ws.column_properties("C").width = width;
  463. ws.column_properties("C").custom_width = true;
  464. ws.column_properties("E").width = width;
  465. ws.column_properties("E").custom_width = true;
  466. xlnt_assert(workbook_matches_file(wb,
  467. path_helper::test_file("13_custom_heights_widths.xlsx")));
  468. }
  469. /// <summary>
  470. /// Read file as an XLSX-formatted ZIP file in the filesystem to a workbook,
  471. /// write the workbook back to memory, then ensure that the contents of the two files are equivalent.
  472. /// </summary>
  473. bool round_trip_matches_rw(const xlnt::path &source)
  474. {
  475. xlnt::workbook source_workbook;
  476. source_workbook.load(source);
  477. std::vector<std::uint8_t> destination;
  478. source_workbook.save(destination);
  479. source_workbook.save("temp" + source.filename());
  480. #ifdef _MSC_VER
  481. std::ifstream source_stream(source.wstring(), std::ios::binary);
  482. #else
  483. std::ifstream source_stream(source.string(), std::ios::binary);
  484. #endif
  485. return xml_helper::xlsx_archives_match(xlnt::detail::to_vector(source_stream), destination);
  486. }
  487. bool round_trip_matches_rw(const xlnt::path &source, const std::string &password)
  488. {
  489. #ifdef _MSC_VER
  490. std::ifstream source_stream(source.wstring(), std::ios::binary);
  491. #else
  492. std::ifstream source_stream(source.string(), std::ios::binary);
  493. #endif
  494. auto source_data = xlnt::detail::to_vector(source_stream);
  495. xlnt::workbook source_workbook;
  496. source_workbook.load(source_data, password);
  497. std::vector<std::uint8_t> destination_data;
  498. //source_workbook.save(destination_data, password);
  499. source_workbook.save("encrypted.xlsx", password);
  500. //xlnt::workbook temp;
  501. //temp.load("encrypted.xlsx", password);
  502. //TODO: finish implementing encryption and uncomment this
  503. //return source_data == destination_data;
  504. return true;
  505. }
  506. void test_round_trip_rw_minimal()
  507. {
  508. xlnt_assert(round_trip_matches_rw(path_helper::test_file("2_minimal.xlsx")));
  509. }
  510. void test_round_trip_rw_default()
  511. {
  512. xlnt_assert(round_trip_matches_rw(path_helper::test_file("3_default.xlsx")));
  513. }
  514. void test_round_trip_rw_every_style()
  515. {
  516. xlnt_assert(round_trip_matches_rw(path_helper::test_file("4_every_style.xlsx")));
  517. }
  518. void test_round_trip_rw_unicode()
  519. {
  520. // u8"/9_unicode_Λ_😇.xlsx" doesn't use 0xC3AA for the capital lambda...
  521. // u8"/9_unicode_\u039B_\U0001F607.xlsx" gives the correct output
  522. xlnt_assert(round_trip_matches_rw(path_helper::test_file(u8"9_unicode_\u039B_\U0001F607.xlsx")));
  523. }
  524. void test_round_trip_rw_comments_hyperlinks_formulae()
  525. {
  526. xlnt_assert(round_trip_matches_rw(path_helper::test_file("10_comments_hyperlinks_formulae.xlsx")));
  527. }
  528. void test_round_trip_rw_print_settings()
  529. {
  530. xlnt_assert(round_trip_matches_rw(path_helper::test_file("11_print_settings.xlsx")));
  531. }
  532. void test_round_trip_rw_advanced_properties()
  533. {
  534. xlnt_assert(round_trip_matches_rw(path_helper::test_file("12_advanced_properties.xlsx")));
  535. }
  536. void test_round_trip_rw_custom_heights_widths()
  537. {
  538. xlnt_assert(round_trip_matches_rw(path_helper::test_file("13_custom_heights_widths.xlsx")));
  539. }
  540. void test_round_trip_rw_encrypted_agile()
  541. {
  542. xlnt_assert(round_trip_matches_rw(path_helper::test_file("5_encrypted_agile.xlsx"), "secret"));
  543. }
  544. void test_round_trip_rw_encrypted_libre()
  545. {
  546. xlnt_assert(round_trip_matches_rw(path_helper::test_file("6_encrypted_libre.xlsx"), u8"\u043F\u0430\u0440\u043E\u043B\u044C")); // u8"пароль"
  547. }
  548. void test_round_trip_rw_encrypted_standard()
  549. {
  550. xlnt_assert(round_trip_matches_rw(path_helper::test_file("7_encrypted_standard.xlsx"), "password"));
  551. }
  552. void test_round_trip_rw_encrypted_numbers()
  553. {
  554. xlnt_assert(round_trip_matches_rw(path_helper::test_file("8_encrypted_numbers.xlsx"), "secret"));
  555. }
  556. void test_streaming_read()
  557. {
  558. const auto path = path_helper::test_file("4_every_style.xlsx");
  559. xlnt::streaming_workbook_reader reader;
  560. reader.open(xlnt::path(path));
  561. for (auto sheet_name : reader.sheet_titles())
  562. {
  563. reader.begin_worksheet(sheet_name);
  564. while (reader.has_cell())
  565. {
  566. reader.read_cell();
  567. }
  568. reader.end_worksheet();
  569. }
  570. }
  571. void test_streaming_write()
  572. {
  573. const auto path = std::string("stream-out.xlsx");
  574. xlnt::streaming_workbook_writer writer;
  575. writer.open(path);
  576. writer.add_worksheet("stream");
  577. auto b2 = writer.add_cell("B2");
  578. b2.value("B2!");
  579. auto c3 = writer.add_cell("C3");
  580. b2.value("should not change");
  581. c3.value("C3!");
  582. }
  583. void test_load_save_german_locale()
  584. {
  585. /* std::locale current(std::locale::global(std::locale("de-DE")));
  586. test_round_trip_rw_custom_heights_widths();
  587. std::locale::global(current);*/
  588. }
  589. void test_Issue445_inline_str_load()
  590. {
  591. xlnt::workbook wb;
  592. wb.load(path_helper::test_file("Issue445_inline_str.xlsx"));
  593. auto ws = wb.active_sheet();
  594. auto cell = ws.cell("A1");
  595. xlnt_assert_equals(cell.value<std::string>(), std::string("a"));
  596. }
  597. void test_Issue445_inline_str_streaming_read()
  598. {
  599. xlnt::streaming_workbook_reader wbr;
  600. wbr.open(path_helper::test_file("Issue445_inline_str.xlsx"));
  601. wbr.begin_worksheet("Sheet");
  602. xlnt_assert(wbr.has_cell());
  603. auto cell = wbr.read_cell();
  604. xlnt_assert_equals(cell.value<std::string>(), std::string("a"));
  605. }
  606. void test_Issue492_stream_empty_row()
  607. {
  608. xlnt::streaming_workbook_reader wbr;
  609. wbr.open(path_helper::test_file("Issue492_empty_row.xlsx"));
  610. wbr.begin_worksheet("BLS Data Series");
  611. xlnt_assert(wbr.has_cell());
  612. xlnt_assert_equals(wbr.read_cell().reference(), "A1");
  613. xlnt_assert(wbr.has_cell());
  614. xlnt_assert_equals(wbr.read_cell().reference(), "A2");
  615. xlnt_assert(wbr.has_cell());
  616. xlnt_assert_equals(wbr.read_cell().reference(), "A4");
  617. xlnt_assert(wbr.has_cell());
  618. xlnt_assert_equals(wbr.read_cell().reference(), "B4");
  619. xlnt_assert(!wbr.has_cell());
  620. }
  621. void test_Issue503_external_link_load()
  622. {
  623. xlnt::workbook wb;
  624. wb.load(path_helper::test_file("Issue503_external_link.xlsx"));
  625. auto ws = wb.active_sheet();
  626. auto cell = ws.cell("A1");
  627. xlnt_assert_equals(cell.value<std::string>(), std::string("WDG_IC_00000003.aut"));
  628. }
  629. void test_formatting()
  630. {
  631. xlnt::workbook wb;
  632. wb.load(path_helper::test_file("excel_test_sheet.xlsx"));
  633. auto ws = wb.active_sheet();
  634. auto cell = ws.cell("A1");
  635. xlnt_assert_equals(cell.value<std::string>(), std::string("Bolder Text mixed with normal \ntext first line Bold And Underline"));
  636. auto rt = cell.value<xlnt::rich_text>();
  637. xlnt_assert_equals(rt.runs().size(), 12);
  638. auto assert_run = [](xlnt::rich_text_run run, std::string text, std::string typeface, xlnt::color color, std::size_t size, bool bold, bool strike, xlnt::font::underline_style underline)
  639. {
  640. xlnt_assert_equals(run.first, text);
  641. xlnt_assert(run.second.is_set());
  642. auto font = run.second.get();
  643. xlnt_assert_equals(font.name(), typeface);
  644. xlnt_assert_equals(font.size(), size);
  645. xlnt_assert_equals(font.bold(), bold);
  646. xlnt_assert_equals(font.color(), color);
  647. xlnt_assert_equals(font.strikethrough(), strike);
  648. xlnt_assert_equals(font.underline(), underline);
  649. };
  650. assert_run(rt.runs()[0], "Bolder", "Calibri (Body)", xlnt::theme_color(1), 12, true, false, xlnt::font::underline_style::none);
  651. assert_run(rt.runs()[1], " Text ", "Calibri", xlnt::theme_color(1), 12, true, false, xlnt::font::underline_style::none);
  652. assert_run(rt.runs()[2], "mixed ", "Calibri", xlnt::color::red(), 12, false, false, xlnt::font::underline_style::none);
  653. assert_run(rt.runs()[3], "wit", "Calibri (Body)", xlnt::color::red(), 12, false, false, xlnt::font::underline_style::single);
  654. assert_run(rt.runs()[4], "h", "Calibri", xlnt::color::red(), 12, false, false, xlnt::font::underline_style::single);
  655. assert_run(rt.runs()[5], " ", "Calibri", xlnt::color::red(), 12, false, false, xlnt::font::underline_style::none);
  656. assert_run(rt.runs()[6], "normal", "Calibri (Body)", xlnt::color::red(), 12, false, false, xlnt::font::underline_style::none);
  657. assert_run(rt.runs()[7], " ", "Calibri", xlnt::color::red(), 12, false, false, xlnt::font::underline_style::none);
  658. assert_run(rt.runs()[8], "\n", "Calibri", xlnt::theme_color(1), 12, false, false, xlnt::font::underline_style::none);
  659. assert_run(rt.runs()[9], "text", "Helvetica Neue", xlnt::theme_color(1), 12, false, true, xlnt::font::underline_style::none);
  660. assert_run(rt.runs()[10], " first line ", "Calibri", xlnt::theme_color(1), 12, true, false, xlnt::font::underline_style::none);
  661. assert_run(rt.runs()[11], "Bold And Underline", "Calibri (Body)", xlnt::theme_color(1), 12, true, false, xlnt::font::underline_style::single);
  662. }
  663. void test_active_sheet()
  664. {
  665. xlnt::workbook wb;
  666. wb.load(path_helper::test_file("20_active_sheet.xlsx"));
  667. xlnt_assert_equals(wb.active_sheet(), wb[2]);
  668. }
  669. };
  670. static serialization_test_suite x;