stylesheet.hpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. // Copyright (c) 2014-2021 Thomas Fussell
  2. // Copyright (c) 2010-2015 openpyxl
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE
  21. //
  22. // @license: http://www.opensource.org/licenses/mit-license.php
  23. // @author: see AUTHORS file
  24. #pragma once
  25. #include <list>
  26. #include <string>
  27. #include <vector>
  28. #include <detail/implementations/conditional_format_impl.hpp>
  29. #include <detail/implementations/format_impl.hpp>
  30. #include <detail/implementations/style_impl.hpp>
  31. #include <xlnt/cell/cell.hpp>
  32. #include <xlnt/styles/conditional_format.hpp>
  33. #include <xlnt/styles/format.hpp>
  34. #include <xlnt/styles/style.hpp>
  35. #include <xlnt/utils/exceptions.hpp>
  36. #include <xlnt/workbook/workbook.hpp>
  37. #include <xlnt/workbook/worksheet_iterator.hpp>
  38. #include <xlnt/worksheet/worksheet.hpp>
  39. namespace xlnt {
  40. namespace detail {
  41. struct stylesheet
  42. {
  43. class format create_format(bool default_format)
  44. {
  45. format_impls.push_back(format_impl());
  46. auto &impl = format_impls.back();
  47. impl.parent = this;
  48. impl.id = format_impls.size() - 1;
  49. impl.references = default_format ? 1 : 0;
  50. return xlnt::format(&impl);
  51. }
  52. class xlnt::format format(std::size_t index)
  53. {
  54. auto iter = format_impls.begin();
  55. std::advance(iter, static_cast<std::list<format_impl>::difference_type>(index));
  56. return xlnt::format(&*iter);
  57. }
  58. class style create_style(const std::string &name)
  59. {
  60. auto &impl = style_impls.emplace(name, style_impl()).first->second;
  61. impl.parent = this;
  62. impl.name = name;
  63. impl.border_id = 0;
  64. impl.fill_id = 0;
  65. impl.font_id = 0;
  66. impl.number_format_id = 0;
  67. style_names.push_back(name);
  68. return xlnt::style(&impl);
  69. }
  70. class style create_builtin_style(const std::size_t builtin_id)
  71. {
  72. // From Annex G.2
  73. static const auto names = std::unordered_map<std::size_t , std::string>
  74. {
  75. { 0, "Normal" },
  76. { 1, "RowLevel_1" },
  77. { 2, "ColLevel_1" },
  78. { 3, "Comma" },
  79. { 4, "Currency" },
  80. { 5, "Percent" },
  81. { 6, "Comma [0]" },
  82. { 7, "Currency [0]" },
  83. { 8, "Hyperlink" },
  84. { 9, "Followed Hyperlink" },
  85. { 10, "Note" },
  86. { 11, "Warning Text" },
  87. { 15, "Title" },
  88. { 16, "Heading 1" },
  89. { 17, "Heading 2" },
  90. { 18, "Heading 3" },
  91. { 19, "Heading 4" },
  92. { 20, "Input" },
  93. { 21, "Output"},
  94. { 22, "Calculation"},
  95. { 22, "Calculation" },
  96. { 23, "Check Cell" },
  97. { 24, "Linked Cell" },
  98. { 25, "Total" },
  99. { 26, "Good" },
  100. { 27, "Bad" },
  101. { 28, "Neutral" },
  102. { 29, "Accent1" },
  103. { 30, "20% - Accent1" },
  104. { 31, "40% - Accent1" },
  105. { 32, "60% - Accent1" },
  106. { 33, "Accent2" },
  107. { 34, "20% - Accent2" },
  108. { 35, "40% - Accent2" },
  109. { 36, "60% - Accent2" },
  110. { 37, "Accent3" },
  111. { 38, "20% - Accent3" },
  112. { 39, "40% - Accent3" },
  113. { 40, "60% - Accent3" },
  114. { 41, "Accent4" },
  115. { 42, "20% - Accent4" },
  116. { 43, "40% - Accent4" },
  117. { 44, "60% - Accent4" },
  118. { 45, "Accent5" },
  119. { 46, "20% - Accent5" },
  120. { 47, "40% - Accent5" },
  121. { 48, "60% - Accent5" },
  122. { 49, "Accent6" },
  123. { 50, "20% - Accent6" },
  124. { 51, "40% - Accent6" },
  125. { 52, "60% - Accent6" },
  126. { 53, "Explanatory Text" }
  127. };
  128. auto new_style = create_style(names.at(builtin_id));
  129. new_style.d_->builtin_id = builtin_id;
  130. return new_style;
  131. }
  132. class style style(const std::string &name)
  133. {
  134. if (!has_style(name)) throw key_not_found();
  135. return xlnt::style(&style_impls[name]);
  136. }
  137. bool has_style(const std::string &name)
  138. {
  139. return style_impls.count(name) > 0;
  140. }
  141. std::size_t next_custom_number_format_id() const
  142. {
  143. std::size_t id = 164;
  144. for (const auto &nf : number_formats)
  145. {
  146. if (nf.id() >= id)
  147. {
  148. id = nf.id() + 1;
  149. }
  150. }
  151. return id;
  152. }
  153. template<typename T, typename C>
  154. std::size_t find_or_add(C &container, const T &item)
  155. {
  156. #pragma GCC diagnostic push
  157. #pragma GCC diagnostic ignored "-Wsign-conversion"
  158. auto iter = std::find(container.begin(), container.end(), item);
  159. if (iter != container.end())
  160. {
  161. return std::size_t(iter - container.begin());
  162. }
  163. iter = container.emplace(container.end(), item);
  164. return std::size_t(iter - container.begin());
  165. #pragma GCC diagnostic pop
  166. }
  167. template<typename T>
  168. std::unordered_map<std::size_t, std::size_t> garbage_collect(
  169. const std::unordered_map<std::size_t, std::size_t> &reference_counts,
  170. std::vector<T> &container)
  171. {
  172. std::unordered_map<std::size_t, std::size_t> id_map;
  173. std::size_t unreferenced = 0;
  174. const auto original_size = container.size();
  175. for (std::size_t i = 0; i < original_size; ++i)
  176. {
  177. id_map[i] = i - unreferenced;
  178. if (reference_counts.count(i) == 0 || reference_counts.at(i) == 0)
  179. {
  180. container.erase(container.begin() + static_cast<typename std::vector<T>::difference_type>(i - unreferenced));
  181. unreferenced++;
  182. }
  183. }
  184. return id_map;
  185. }
  186. void garbage_collect()
  187. {
  188. if (!garbage_collection_enabled) return;
  189. auto format_iter = format_impls.begin();
  190. while (format_iter != format_impls.end())
  191. {
  192. auto &impl = *format_iter;
  193. if (impl.references != 0)
  194. {
  195. ++format_iter;
  196. }
  197. else
  198. {
  199. format_iter = format_impls.erase(format_iter);
  200. }
  201. }
  202. std::size_t new_id = 0;
  203. std::unordered_map<std::size_t, std::size_t> alignment_reference_counts;
  204. std::unordered_map<std::size_t, std::size_t> border_reference_counts;
  205. std::unordered_map<std::size_t, std::size_t> fill_reference_counts;
  206. std::unordered_map<std::size_t, std::size_t> font_reference_counts;
  207. std::unordered_map<std::size_t, std::size_t> number_format_reference_counts;
  208. std::unordered_map<std::size_t, std::size_t> protection_reference_counts;
  209. fill_reference_counts[0]++;
  210. fill_reference_counts[1]++;
  211. for (auto &impl : format_impls)
  212. {
  213. impl.id = new_id++;
  214. if (impl.alignment_id.is_set())
  215. {
  216. alignment_reference_counts[impl.alignment_id.get()]++;
  217. }
  218. if (impl.border_id.is_set())
  219. {
  220. border_reference_counts[impl.border_id.get()]++;
  221. }
  222. if (impl.fill_id.is_set())
  223. {
  224. fill_reference_counts[impl.fill_id.get()]++;
  225. }
  226. if (impl.font_id.is_set())
  227. {
  228. font_reference_counts[impl.font_id.get()]++;
  229. }
  230. if (impl.number_format_id.is_set())
  231. {
  232. number_format_reference_counts[impl.number_format_id.get()]++;
  233. }
  234. if (impl.protection_id.is_set())
  235. {
  236. protection_reference_counts[impl.protection_id.get()]++;
  237. }
  238. }
  239. for (auto &name_impl_pair : style_impls)
  240. {
  241. auto &impl = name_impl_pair.second;
  242. if (impl.alignment_id.is_set())
  243. {
  244. alignment_reference_counts[impl.alignment_id.get()]++;
  245. }
  246. if (impl.border_id.is_set())
  247. {
  248. border_reference_counts[impl.border_id.get()]++;
  249. }
  250. if (impl.fill_id.is_set())
  251. {
  252. fill_reference_counts[impl.fill_id.get()]++;
  253. }
  254. if (impl.font_id.is_set())
  255. {
  256. font_reference_counts[impl.font_id.get()]++;
  257. }
  258. if (impl.number_format_id.is_set())
  259. {
  260. number_format_reference_counts[impl.number_format_id.get()]++;
  261. }
  262. if (impl.protection_id.is_set())
  263. {
  264. protection_reference_counts[impl.protection_id.get()]++;
  265. }
  266. }
  267. auto alignment_id_map = garbage_collect(alignment_reference_counts, alignments);
  268. auto border_id_map = garbage_collect(border_reference_counts, borders);
  269. auto fill_id_map = garbage_collect(fill_reference_counts, fills);
  270. auto font_id_map = garbage_collect(font_reference_counts, fonts);
  271. auto protection_id_map = garbage_collect(protection_reference_counts, protections);
  272. for (auto &impl : format_impls)
  273. {
  274. if (impl.alignment_id.is_set())
  275. {
  276. impl.alignment_id = alignment_id_map[impl.alignment_id.get()];
  277. }
  278. if (impl.border_id.is_set())
  279. {
  280. impl.border_id = border_id_map[impl.border_id.get()];
  281. }
  282. if (impl.fill_id.is_set())
  283. {
  284. impl.fill_id = fill_id_map[impl.fill_id.get()];
  285. }
  286. if (impl.font_id.is_set())
  287. {
  288. impl.font_id = font_id_map[impl.font_id.get()];
  289. }
  290. if (impl.protection_id.is_set())
  291. {
  292. impl.protection_id = protection_id_map[impl.protection_id.get()];
  293. }
  294. }
  295. for (auto &name_impl : style_impls)
  296. {
  297. auto &impl = name_impl.second;
  298. if (impl.alignment_id.is_set())
  299. {
  300. impl.alignment_id = alignment_id_map[impl.alignment_id.get()];
  301. }
  302. if (impl.border_id.is_set())
  303. {
  304. impl.border_id = border_id_map[impl.border_id.get()];
  305. }
  306. if (impl.fill_id.is_set())
  307. {
  308. impl.fill_id = fill_id_map[impl.fill_id.get()];
  309. }
  310. if (impl.font_id.is_set())
  311. {
  312. impl.font_id = font_id_map[impl.font_id.get()];
  313. }
  314. if (impl.protection_id.is_set())
  315. {
  316. impl.protection_id = protection_id_map[impl.protection_id.get()];
  317. }
  318. }
  319. }
  320. format_impl *find_or_create(format_impl &pattern)
  321. {
  322. pattern.references = 0;
  323. std::size_t id = 0;
  324. auto iter = format_impls.begin();
  325. while (iter != format_impls.end() && !(*iter == pattern))
  326. {
  327. ++id;
  328. ++iter;
  329. }
  330. if (iter == format_impls.end())
  331. {
  332. iter = format_impls.emplace(format_impls.end(), pattern);
  333. }
  334. auto &result = *iter;
  335. result.parent = this;
  336. result.id = id;
  337. result.references++;
  338. if (id != pattern.id)
  339. {
  340. iter = format_impls.begin();
  341. std::advance(iter, static_cast<std::list<format_impl>::difference_type>(pattern.id));
  342. iter->references -= iter->references > 0 ? 1 : 0;
  343. garbage_collect();
  344. }
  345. return &result;
  346. }
  347. format_impl *find_or_create_with(format_impl *pattern, const std::string &style_name)
  348. {
  349. format_impl new_format = *pattern;
  350. new_format.style = style_name;
  351. if (pattern->references == 0)
  352. {
  353. *pattern = new_format;
  354. }
  355. return find_or_create(new_format);
  356. }
  357. format_impl *find_or_create_with(format_impl *pattern, const alignment &new_alignment, optional<bool> applied)
  358. {
  359. format_impl new_format = *pattern;
  360. new_format.alignment_id = find_or_add(alignments, new_alignment);
  361. new_format.alignment_applied = applied;
  362. if (pattern->references == 0)
  363. {
  364. *pattern = new_format;
  365. }
  366. return find_or_create(new_format);
  367. }
  368. format_impl *find_or_create_with(format_impl *pattern, const border &new_border, optional<bool> applied)
  369. {
  370. format_impl new_format = *pattern;
  371. new_format.border_id = find_or_add(borders, new_border);
  372. new_format.border_applied = applied;
  373. if (pattern->references == 0)
  374. {
  375. *pattern = new_format;
  376. }
  377. return find_or_create(new_format);
  378. }
  379. format_impl *find_or_create_with(format_impl *pattern, const fill &new_fill, optional<bool> applied)
  380. {
  381. format_impl new_format = *pattern;
  382. new_format.fill_id = find_or_add(fills, new_fill);
  383. new_format.fill_applied = applied;
  384. if (pattern->references == 0)
  385. {
  386. *pattern = new_format;
  387. }
  388. return find_or_create(new_format);
  389. }
  390. format_impl *find_or_create_with(format_impl *pattern, const font &new_font, optional<bool> applied)
  391. {
  392. format_impl new_format = *pattern;
  393. new_format.font_id = find_or_add(fonts, new_font);
  394. new_format.font_applied = applied;
  395. if (pattern->references == 0)
  396. {
  397. *pattern = new_format;
  398. }
  399. return find_or_create(new_format);
  400. }
  401. format_impl *find_or_create_with(format_impl *pattern, const number_format &new_number_format, optional<bool> applied)
  402. {
  403. format_impl new_format = *pattern;
  404. if (new_number_format.id() >= 164)
  405. {
  406. find_or_add(number_formats, new_number_format);
  407. }
  408. new_format.number_format_id = new_number_format.id();
  409. new_format.number_format_applied = applied;
  410. if (pattern->references == 0)
  411. {
  412. *pattern = new_format;
  413. }
  414. return find_or_create(new_format);
  415. }
  416. format_impl *find_or_create_with(format_impl *pattern, const protection &new_protection, optional<bool> applied)
  417. {
  418. format_impl new_format = *pattern;
  419. new_format.protection_id = find_or_add(protections, new_protection);
  420. new_format.protection_applied = applied;
  421. if (pattern->references == 0)
  422. {
  423. *pattern = new_format;
  424. }
  425. return find_or_create(new_format);
  426. }
  427. std::size_t style_index(const std::string &name) const
  428. {
  429. return static_cast<std::size_t>(std::distance(style_names.begin(),
  430. std::find(style_names.begin(), style_names.end(), name)));
  431. }
  432. void clear()
  433. {
  434. conditional_format_impls.clear();
  435. format_impls.clear();
  436. style_impls.clear();
  437. style_names.clear();
  438. alignments.clear();
  439. borders.clear();
  440. fills.clear();
  441. fonts.clear();
  442. number_formats.clear();
  443. protections.clear();
  444. colors.clear();
  445. }
  446. conditional_format add_conditional_format_rule(worksheet_impl *ws, const range_reference &ref, const condition &when)
  447. {
  448. conditional_format_impls.push_back(conditional_format_impl());
  449. auto &impl = conditional_format_impls.back();
  450. impl.when = when;
  451. impl.parent = this;
  452. impl.target_sheet = ws;
  453. impl.target_range = ref;
  454. impl.differential_format_id = conditional_format_impls.size() - 1;
  455. return xlnt::conditional_format(&impl);
  456. }
  457. workbook *parent;
  458. bool operator==(const stylesheet& rhs) const
  459. {
  460. // no equality on parent as there is only 1 stylesheet per borkbook hence would always be false
  461. return garbage_collection_enabled == rhs.garbage_collection_enabled
  462. && known_fonts_enabled == rhs.known_fonts_enabled
  463. && conditional_format_impls == rhs.conditional_format_impls
  464. && format_impls == rhs.format_impls
  465. && style_impls == rhs.style_impls
  466. && style_names == rhs.style_names
  467. && default_slicer_style == rhs.default_slicer_style
  468. && alignments == rhs.alignments
  469. && borders == rhs.borders
  470. && fills == rhs.fills
  471. && fonts == rhs.fonts
  472. && number_formats == rhs.number_formats
  473. && protections == rhs.protections
  474. && colors == rhs.colors;
  475. }
  476. bool garbage_collection_enabled = true;
  477. bool known_fonts_enabled = false;
  478. std::list<conditional_format_impl> conditional_format_impls;
  479. std::list<format_impl> format_impls;
  480. std::unordered_map<std::string, style_impl> style_impls;
  481. std::vector<std::string> style_names;
  482. optional<std::string> default_slicer_style;
  483. std::vector<alignment> alignments;
  484. std::vector<border> borders;
  485. std::vector<fill> fills;
  486. std::vector<font> fonts;
  487. std::vector<number_format> number_formats;
  488. std::vector<protection> protections;
  489. std::vector<color> colors;
  490. };
  491. } // namespace detail
  492. } // namespace xlnt