xlsx_producer.cpp 111 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512
  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 <cmath>
  24. #include <numeric> // for std::accumulate
  25. #include <string>
  26. #include <type_traits>
  27. #include <unordered_set>
  28. #include <xlnt/cell/cell.hpp>
  29. #include <xlnt/cell/hyperlink.hpp>
  30. #include <xlnt/packaging/manifest.hpp>
  31. #include <xlnt/utils/numeric.hpp>
  32. #include <xlnt/utils/path.hpp>
  33. #include <xlnt/utils/scoped_enum_hash.hpp>
  34. #include <xlnt/workbook/workbook.hpp>
  35. #include <xlnt/workbook/workbook_view.hpp>
  36. #include <xlnt/worksheet/header_footer.hpp>
  37. #include <xlnt/worksheet/worksheet.hpp>
  38. #include <detail/constants.hpp>
  39. #include <detail/header_footer/header_footer_code.hpp>
  40. #include <detail/implementations/workbook_impl.hpp>
  41. #include <detail/serialization/custom_value_traits.hpp>
  42. #include <detail/serialization/defined_name.hpp>
  43. #include <detail/serialization/vector_streambuf.hpp>
  44. #include <detail/serialization/xlsx_producer.hpp>
  45. #include <detail/serialization/zstream.hpp>
  46. namespace {
  47. std::vector<std::pair<std::string, std::string>> core_property_namespace(xlnt::core_property type)
  48. {
  49. using xlnt::constants;
  50. using xlnt::core_property;
  51. if (type == core_property::created
  52. || type == core_property::modified)
  53. {
  54. return {{constants::ns("dcterms"), "dcterms"},
  55. {constants::ns("xsi"), "xsi"}};
  56. }
  57. else if (type == core_property::title
  58. || type == core_property::subject
  59. || type == core_property::creator
  60. || type == core_property::description)
  61. {
  62. return {{constants::ns("dc"), "dc"}};
  63. }
  64. else if (type == core_property::keywords)
  65. {
  66. return {{constants::ns("core-properties"), "cp"},
  67. {constants::ns("vt"), "vt"}};
  68. }
  69. return {{constants::ns("core-properties"), "cp"}};
  70. }
  71. } // namespace
  72. namespace xlnt {
  73. namespace detail {
  74. xlsx_producer::xlsx_producer(const workbook &target)
  75. : source_(target),
  76. current_part_stream_(nullptr),
  77. current_cell_(nullptr),
  78. current_worksheet_(nullptr)
  79. {
  80. }
  81. xlsx_producer::~xlsx_producer()
  82. {
  83. end_part();
  84. archive_.reset();
  85. }
  86. void xlsx_producer::write(std::ostream &destination)
  87. {
  88. archive_.reset(new ozstream(destination));
  89. populate_archive(false);
  90. }
  91. void xlsx_producer::open(std::ostream &destination)
  92. {
  93. archive_.reset(new ozstream(destination));
  94. populate_archive(true);
  95. }
  96. cell xlsx_producer::add_cell(const cell_reference &ref)
  97. {
  98. current_cell_->column_ = ref.column();
  99. current_cell_->row_ = ref.row();
  100. return cell(current_cell_);
  101. }
  102. worksheet xlsx_producer::add_worksheet(const std::string &title)
  103. {
  104. current_worksheet_->title_ = title;
  105. return worksheet(current_worksheet_);
  106. }
  107. // Part Writing Methods
  108. void xlsx_producer::populate_archive(bool streaming)
  109. {
  110. streaming_ = streaming;
  111. write_content_types();
  112. const auto root_rels = source_.manifest().relationships(path("/"));
  113. write_relationships(root_rels, path("/"));
  114. for (auto &rel : root_rels)
  115. {
  116. // thumbnail is binary content so we don't want to open an xml serializer stream
  117. if (rel.type() == relationship_type::thumbnail)
  118. {
  119. write_image(rel.target().path());
  120. continue;
  121. }
  122. begin_part(rel.target().path());
  123. if (rel.type() == relationship_type::core_properties)
  124. {
  125. write_core_properties(rel);
  126. }
  127. else if (rel.type() == relationship_type::extended_properties)
  128. {
  129. write_extended_properties(rel);
  130. }
  131. else if (rel.type() == relationship_type::custom_properties)
  132. {
  133. write_custom_properties(rel);
  134. }
  135. else if (rel.type() == relationship_type::office_document)
  136. {
  137. write_workbook(rel);
  138. }
  139. }
  140. // Unknown Parts
  141. void write_unknown_parts();
  142. void write_unknown_relationships();
  143. end_part();
  144. }
  145. void xlsx_producer::end_part()
  146. {
  147. if (current_part_serializer_)
  148. {
  149. current_part_serializer_.reset();
  150. }
  151. current_part_streambuf_.reset();
  152. }
  153. void xlsx_producer::begin_part(const path &part)
  154. {
  155. end_part();
  156. current_part_streambuf_ = archive_->open(part);
  157. current_part_stream_.rdbuf(current_part_streambuf_.get());
  158. auto xml_serializer = new xml::serializer(current_part_stream_, part.string(), 0);
  159. xml_serializer->xml_decl("1.0", "UTF-8", "yes");
  160. current_part_serializer_.reset(xml_serializer);
  161. }
  162. // Package Parts
  163. void xlsx_producer::write_content_types()
  164. {
  165. const auto content_types_path = path("[Content_Types].xml");
  166. begin_part(content_types_path);
  167. const auto xmlns = "http://schemas.openxmlformats.org/package/2006/content-types";
  168. write_start_element(xmlns, "Types");
  169. write_namespace(xmlns, "");
  170. for (const auto &extension : source_.manifest().extensions_with_default_types())
  171. {
  172. write_start_element(xmlns, "Default");
  173. write_attribute("Extension", extension);
  174. write_attribute("ContentType", source_.manifest().default_type(extension));
  175. write_end_element(xmlns, "Default");
  176. }
  177. for (const auto &part : source_.manifest().parts_with_overriden_types())
  178. {
  179. write_start_element(xmlns, "Override");
  180. write_attribute("PartName", part.resolve(path("/")).string());
  181. write_attribute("ContentType", source_.manifest().override_type(part));
  182. write_end_element(xmlns, "Override");
  183. }
  184. write_end_element(xmlns, "Types");
  185. }
  186. void xlsx_producer::write_property(const std::string &name, const variant &value,
  187. const std::string &ns, bool custom, std::size_t pid)
  188. {
  189. if (custom)
  190. {
  191. write_start_element(ns, "property");
  192. write_attribute("name", name);
  193. }
  194. else
  195. {
  196. write_start_element(ns, name);
  197. }
  198. switch (value.value_type())
  199. {
  200. case variant::type::null: {
  201. break;
  202. }
  203. case variant::type::boolean: {
  204. if (custom)
  205. {
  206. write_attribute("fmtid", "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}");
  207. write_attribute("pid", pid);
  208. write_start_element(constants::ns("vt"), "bool");
  209. }
  210. write_characters(value.get<bool>() ? "true" : "false");
  211. if (custom)
  212. {
  213. write_end_element(constants::ns("vt"), "bool");
  214. }
  215. break;
  216. }
  217. case variant::type::i4: {
  218. if (custom)
  219. {
  220. write_attribute("fmtid", "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}");
  221. write_attribute("pid", pid);
  222. write_start_element(constants::ns("vt"), "i4");
  223. }
  224. write_characters(value.get<std::int32_t>());
  225. if (custom)
  226. {
  227. write_end_element(constants::ns("vt"), "i4");
  228. }
  229. break;
  230. }
  231. case variant::type::lpstr: {
  232. if (custom)
  233. {
  234. write_attribute("fmtid", "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}");
  235. write_attribute("pid", pid);
  236. write_start_element(constants::ns("vt"), "lpwstr");
  237. }
  238. if (!custom && ns == constants::ns("dcterms") && (name == "created" || name == "modified"))
  239. {
  240. write_attribute(xml::qname(constants::ns("xsi"), "type"), "dcterms:W3CDTF");
  241. }
  242. write_characters(value.get<std::string>());
  243. if (custom)
  244. {
  245. write_end_element(constants::ns("vt"), "lpwstr");
  246. }
  247. break;
  248. }
  249. case variant::type::date: {
  250. write_attribute(xml::qname(constants::ns("xsi"), "type"), "dcterms:W3CDTF");
  251. write_characters(value.get<datetime>().to_iso_string());
  252. break;
  253. }
  254. case variant::type::vector: {
  255. write_start_element(constants::ns("vt"), "vector");
  256. auto vector = value.get<std::vector<variant>>();
  257. std::unordered_set<variant::type, scoped_enum_hash<variant::type>> types;
  258. for (const auto &element : vector)
  259. {
  260. types.insert(element.value_type());
  261. }
  262. const auto is_mixed = types.size() > 1;
  263. const auto vector_type = !is_mixed ? to_string(*types.begin()) : "variant";
  264. write_attribute("size", vector.size());
  265. write_attribute("baseType", vector_type);
  266. for (std::size_t i = 0; i < vector.size(); ++i)
  267. {
  268. const auto &vector_element = vector.at(i);
  269. if (is_mixed)
  270. {
  271. write_start_element(constants::ns("vt"), "variant");
  272. }
  273. if (vector_element.value_type() == variant::type::lpstr)
  274. {
  275. write_element(constants::ns("vt"), "lpstr", vector_element.get<std::string>());
  276. }
  277. else if (vector_element.value_type() == variant::type::i4)
  278. {
  279. write_element(constants::ns("vt"), "i4", vector_element.get<std::int32_t>());
  280. }
  281. if (is_mixed)
  282. {
  283. write_end_element(constants::ns("vt"), "variant");
  284. }
  285. }
  286. write_end_element(constants::ns("vt"), "vector");
  287. break;
  288. }
  289. }
  290. if (custom)
  291. {
  292. write_end_element(ns, "property");
  293. }
  294. else
  295. {
  296. write_end_element(ns, name);
  297. }
  298. }
  299. void xlsx_producer::write_core_properties(const relationship & /*rel*/)
  300. {
  301. write_start_element(constants::ns("core-properties"), "coreProperties");
  302. auto core_properties = source_.core_properties();
  303. std::unordered_map<std::string, std::string> namespaces;
  304. write_namespace(constants::ns("core-properties"), "cp");
  305. for (const auto &prop : core_properties)
  306. {
  307. for (const auto &ns : core_property_namespace(prop))
  308. {
  309. if (namespaces.count(ns.first) > 0) continue;
  310. write_namespace(ns.first, ns.second);
  311. namespaces.emplace(ns);
  312. }
  313. }
  314. for (const auto &prop : core_properties)
  315. {
  316. write_property(to_string(prop), source_.core_property(prop),
  317. core_property_namespace(prop).front().first, false, 0);
  318. }
  319. write_end_element(constants::ns("core-properties"), "coreProperties");
  320. }
  321. void xlsx_producer::write_extended_properties(const relationship & /*rel*/)
  322. {
  323. write_start_element(constants::ns("extended-properties"), "Properties");
  324. write_namespace(constants::ns("extended-properties"), "");
  325. if (source_.has_extended_property(extended_property::heading_pairs)
  326. || source_.has_extended_property(extended_property::titles_of_parts))
  327. {
  328. write_namespace(constants::ns("vt"), "vt");
  329. }
  330. for (const auto &prop : source_.extended_properties())
  331. {
  332. write_property(to_string(prop), source_.extended_property(prop),
  333. constants::ns("extended-properties"), false, 0);
  334. }
  335. write_end_element(constants::ns("extended-properties"), "Properties");
  336. }
  337. void xlsx_producer::write_custom_properties(const relationship & /*rel*/)
  338. {
  339. write_start_element(constants::ns("custom-properties"), "Properties");
  340. write_namespace(constants::ns("custom-properties"), "");
  341. write_namespace(constants::ns("vt"), "vt");
  342. auto pid = std::size_t(2); // why does this start at 2?
  343. for (const auto &prop : source_.custom_properties())
  344. {
  345. write_property(prop, source_.custom_property(prop),
  346. constants::ns("custom-properties"), true, pid++);
  347. }
  348. write_end_element(constants::ns("custom-properties"), "Properties");
  349. }
  350. // Write SpreadsheetML-Specific Package Parts
  351. void xlsx_producer::write_workbook(const relationship &rel)
  352. {
  353. std::size_t num_visible = 0;
  354. std::vector<defined_name> defined_names;
  355. for (auto ws : source_)
  356. {
  357. if (!ws.has_page_setup() || ws.page_setup().sheet_state() == sheet_state::visible)
  358. {
  359. num_visible++;
  360. }
  361. auto title_ref = "'" + ws.title() + "'!";
  362. if (ws.has_auto_filter())
  363. {
  364. defined_name name;
  365. name.sheet_id = ws.id();
  366. name.name = "_xlnm._FilterDatabase";
  367. name.hidden = true;
  368. name.value = title_ref + range_reference::make_absolute(ws.auto_filter()).to_string();
  369. defined_names.push_back(name);
  370. }
  371. if (ws.has_print_area())
  372. {
  373. defined_name name;
  374. name.sheet_id = ws.id();
  375. name.name = "_xlnm.Print_Area";
  376. name.hidden = false;
  377. name.value = title_ref + range_reference::make_absolute(ws.print_area()).to_string();
  378. defined_names.push_back(name);
  379. }
  380. if (ws.has_print_titles())
  381. {
  382. defined_name name;
  383. name.sheet_id = ws.id();
  384. name.name = "_xlnm.Print_Titles";
  385. name.hidden = false;
  386. auto cols = ws.print_title_cols();
  387. if (cols.is_set())
  388. {
  389. name.value = title_ref + "$" + cols.get().first.column_string()
  390. + ":" + "$" + cols.get().second.column_string();
  391. }
  392. auto rows = ws.print_title_rows();
  393. if (rows.is_set())
  394. {
  395. if (!name.value.empty())
  396. {
  397. name.value.push_back(',');
  398. }
  399. name.value += title_ref + "$" + std::to_string(rows.get().first)
  400. + ":" + "$" + std::to_string(rows.get().second);
  401. }
  402. defined_names.push_back(name);
  403. }
  404. }
  405. if (num_visible == 0)
  406. {
  407. throw no_visible_worksheets();
  408. }
  409. static const auto &xmlns = constants::ns("workbook");
  410. static const auto &xmlns_r = constants::ns("r");
  411. static const auto &xmlns_s = constants::ns("spreadsheetml");
  412. static const auto &xmlns_mx = constants::ns("mx");
  413. static const auto &xmlns_x15ac = constants::ns("x15ac");
  414. static const auto &xmlns_x15 = constants::ns("x15");
  415. static const auto &xmlns_mc = constants::ns("mc");
  416. write_start_element(xmlns, "workbook");
  417. write_namespace(xmlns, "");
  418. write_namespace(xmlns_r, "r");
  419. if (source_.d_->abs_path_.is_set())
  420. {
  421. write_namespace(xmlns_mc, "mc");
  422. write_namespace(xmlns_x15, "x15");
  423. write_attribute(xml::qname(xmlns_mc, "Ignorable"), "x15");
  424. }
  425. if (source_.has_file_version())
  426. {
  427. write_start_element(xmlns, "fileVersion");
  428. write_attribute("appName", source_.app_name());
  429. write_attribute("lastEdited", source_.last_edited());
  430. write_attribute("lowestEdited", source_.lowest_edited());
  431. write_attribute("rupBuild", source_.rup_build());
  432. write_end_element(xmlns, "fileVersion");
  433. }
  434. write_start_element(xmlns, "workbookPr");
  435. if (source_.has_code_name())
  436. {
  437. write_attribute("codeName", source_.code_name());
  438. }
  439. if (source_.base_date() == calendar::mac_1904)
  440. {
  441. write_attribute("date1904", "1");
  442. }
  443. write_end_element(xmlns, "workbookPr");
  444. if (source_.d_->abs_path_.is_set())
  445. {
  446. write_start_element(xmlns_mc, "AlternateContent");
  447. write_namespace(xmlns_mc, "mc");
  448. write_start_element(xmlns_mc, "Choice");
  449. write_attribute("Requires", "x15");
  450. write_start_element(xmlns_x15ac, "absPath");
  451. write_namespace(xmlns_x15ac, "x15ac");
  452. write_attribute("url", source_.d_->abs_path_.get());
  453. write_end_element(xmlns_x15ac, "absPath");
  454. write_end_element(xmlns_mc, "Choice");
  455. write_end_element(xmlns_mc, "AlternateContent");
  456. }
  457. if (source_.has_view())
  458. {
  459. write_start_element(xmlns, "bookViews");
  460. write_start_element(xmlns, "workbookView");
  461. const auto &view = source_.view();
  462. if (view.active_tab.is_set() && view.active_tab.get() != std::size_t(0))
  463. {
  464. write_attribute("activeTab", view.active_tab.get());
  465. }
  466. if (!view.auto_filter_date_grouping)
  467. {
  468. write_attribute("autoFilterDateGrouping", write_bool(view.auto_filter_date_grouping));
  469. }
  470. if (view.first_sheet.is_set())
  471. {
  472. write_attribute("firstSheet", view.first_sheet.get());
  473. }
  474. if (view.minimized)
  475. {
  476. write_attribute("minimized", write_bool(view.minimized));
  477. }
  478. if (!view.show_horizontal_scroll)
  479. {
  480. write_attribute("showHorizontalScroll", write_bool(view.show_horizontal_scroll));
  481. }
  482. if (!view.show_sheet_tabs)
  483. {
  484. write_attribute("showSheetTabs", write_bool(view.show_sheet_tabs));
  485. }
  486. if (!view.show_vertical_scroll)
  487. {
  488. write_attribute("showVerticalScroll", write_bool(view.show_vertical_scroll));
  489. }
  490. if (!view.visible)
  491. {
  492. write_attribute("visibility", write_bool(view.visible));
  493. }
  494. if (view.x_window.is_set())
  495. {
  496. write_attribute("xWindow", view.x_window.get());
  497. }
  498. if (view.y_window.is_set())
  499. {
  500. write_attribute("yWindow", view.y_window.get());
  501. }
  502. if (view.window_width.is_set())
  503. {
  504. write_attribute("windowWidth", view.window_width.get());
  505. }
  506. if (view.window_height.is_set())
  507. {
  508. write_attribute("windowHeight", view.window_height.get());
  509. }
  510. if (view.tab_ratio.is_set())
  511. {
  512. write_attribute("tabRatio", view.tab_ratio.get());
  513. }
  514. write_end_element(xmlns, "workbookView");
  515. write_end_element(xmlns, "bookViews");
  516. }
  517. write_start_element(xmlns, "sheets");
  518. #pragma clang diagnostic push
  519. #pragma clang diagnostic ignored "-Wrange-loop-analysis"
  520. for (const auto ws : source_)
  521. {
  522. auto sheet_rel_id = source_.d_->sheet_title_rel_id_map_[ws.title()];
  523. auto sheet_rel = source_.d_->manifest_.relationship(rel.target().path(), sheet_rel_id);
  524. write_start_element(xmlns, "sheet");
  525. write_attribute("name", ws.title());
  526. write_attribute("sheetId", ws.id());
  527. if (ws.has_page_setup() && ws.sheet_state() == xlnt::sheet_state::hidden)
  528. {
  529. write_attribute("state", "hidden");
  530. }
  531. write_attribute(xml::qname(xmlns_r, "id"), sheet_rel_id);
  532. write_end_element(xmlns, "sheet");
  533. }
  534. #pragma clang diagnostic pop
  535. write_end_element(xmlns, "sheets");
  536. if (!defined_names.empty())
  537. {
  538. write_start_element(xmlns, "definedNames");
  539. for (auto name : defined_names)
  540. {
  541. write_start_element(xmlns, "definedName");
  542. write_attribute("name", name.name);
  543. if (name.hidden)
  544. {
  545. write_attribute("hidden", write_bool(true));
  546. }
  547. write_attribute("localSheetId", std::to_string(name.sheet_id - 1)); // 0-indexed for some reason
  548. write_characters(name.value);
  549. write_end_element(xmlns, "definedName");
  550. }
  551. write_end_element(xmlns, "definedNames");
  552. }
  553. if (source_.has_calculation_properties())
  554. {
  555. write_start_element(xmlns, "calcPr");
  556. write_attribute("calcId", source_.calculation_properties().calc_id);
  557. //write_attribute("calcMode", "auto");
  558. //write_attribute("fullCalcOnLoad", "1");
  559. write_attribute("concurrentCalc", write_bool(source_.calculation_properties().concurrent_calc));
  560. write_end_element(xmlns, "calcPr");
  561. }
  562. if (!source_.named_ranges().empty())
  563. {
  564. write_start_element(xmlns, "definedNames");
  565. for (auto &named_range : source_.named_ranges())
  566. {
  567. write_start_element(xmlns_s, "definedName");
  568. write_namespace(xmlns_s, "s");
  569. write_attribute("name", named_range.name());
  570. const auto &target = named_range.targets().front();
  571. write_characters("'" + target.first.title() + "\'!" + target.second.to_string());
  572. write_end_element(xmlns_s, "definedName");
  573. }
  574. write_end_element(xmlns, "definedNames");
  575. }
  576. if (source_.d_->arch_id_flags_.is_set())
  577. {
  578. write_start_element(xmlns, "extLst");
  579. write_start_element(xmlns, "ext");
  580. write_namespace(xmlns_mx, "mx");
  581. write_attribute("uri", "{7523E5D3-25F3-A5E0-1632-64F254C22452}");
  582. write_start_element(xmlns_mx, "ArchID");
  583. write_attribute("Flags", source_.d_->arch_id_flags_.get());
  584. write_end_element(xmlns_mx, "ArchID");
  585. write_end_element(xmlns, "ext");
  586. write_end_element(xmlns, "extLst");
  587. }
  588. write_end_element(xmlns, "workbook");
  589. auto workbook_rels = source_.manifest().relationships(rel.target().path());
  590. write_relationships(workbook_rels, rel.target().path());
  591. for (const auto &child_rel : workbook_rels)
  592. {
  593. if (child_rel.type() == relationship_type::calculation_chain)
  594. {
  595. // We don't yet have a VBA interpreter which can evaluate formulas.
  596. // If we write an outdated calculate chain, Excel will treat the XLSX
  597. // as corrupt. As a workaround, we keep the relationship but don't
  598. // write the calculation chain file so Excel will recalculate all formulae
  599. // on load.
  600. continue;
  601. }
  602. auto child_target_path = child_rel.target().path();
  603. path archive_path(child_rel.source().path().parent().append(child_target_path));
  604. // write binary
  605. if (child_rel.type() == relationship_type::vbaproject)
  606. {
  607. write_binary(archive_path);
  608. continue;
  609. }
  610. // write xml
  611. begin_part(archive_path);
  612. switch (child_rel.type())
  613. {
  614. case relationship_type::chartsheet:
  615. write_chartsheet(child_rel);
  616. break;
  617. case relationship_type::connections:
  618. write_connections(child_rel);
  619. break;
  620. case relationship_type::custom_xml_mappings:
  621. write_custom_xml_mappings(child_rel);
  622. break;
  623. case relationship_type::dialogsheet:
  624. write_dialogsheet(child_rel);
  625. break;
  626. case relationship_type::external_workbook_references:
  627. write_external_workbook_references(child_rel);
  628. break;
  629. case relationship_type::pivot_table:
  630. write_pivot_table(child_rel);
  631. break;
  632. case relationship_type::shared_string_table:
  633. write_shared_string_table(child_rel);
  634. break;
  635. case relationship_type::shared_workbook_revision_headers:
  636. write_shared_workbook_revision_headers(child_rel);
  637. break;
  638. case relationship_type::stylesheet:
  639. write_styles(child_rel);
  640. break;
  641. case relationship_type::theme:
  642. write_theme(child_rel);
  643. break;
  644. case relationship_type::volatile_dependencies:
  645. write_volatile_dependencies(child_rel);
  646. break;
  647. case relationship_type::worksheet:
  648. write_worksheet(child_rel);
  649. break;
  650. case relationship_type::calculation_chain:
  651. break;
  652. case relationship_type::office_document:
  653. break;
  654. case relationship_type::thumbnail:
  655. break;
  656. case relationship_type::extended_properties:
  657. break;
  658. case relationship_type::core_properties:
  659. break;
  660. case relationship_type::hyperlink:
  661. break;
  662. case relationship_type::comments:
  663. break;
  664. case relationship_type::vml_drawing:
  665. break;
  666. case relationship_type::unknown:
  667. break;
  668. case relationship_type::custom_properties:
  669. break;
  670. case relationship_type::printer_settings:
  671. break;
  672. case relationship_type::custom_property:
  673. break;
  674. case relationship_type::drawings:
  675. break;
  676. case relationship_type::pivot_table_cache_definition:
  677. break;
  678. case relationship_type::pivot_table_cache_records:
  679. break;
  680. case relationship_type::query_table:
  681. break;
  682. case relationship_type::shared_workbook:
  683. break;
  684. case relationship_type::revision_log:
  685. break;
  686. case relationship_type::shared_workbook_user_data:
  687. break;
  688. case relationship_type::single_cell_table_definitions:
  689. break;
  690. case relationship_type::table_definition:
  691. break;
  692. case relationship_type::vbaproject:
  693. break;
  694. case relationship_type::image:
  695. break;
  696. }
  697. }
  698. }
  699. // Write Workbook Relationship Target Parts
  700. void xlsx_producer::write_chartsheet(const relationship & /*rel*/)
  701. {
  702. write_start_element(constants::ns("spreadsheetml"), "chartsheet");
  703. write_start_element(constants::ns("spreadsheetml"), "chartsheet");
  704. }
  705. void xlsx_producer::write_connections(const relationship & /*rel*/)
  706. {
  707. write_start_element(constants::ns("spreadsheetml"), "connections");
  708. write_end_element(constants::ns("spreadsheetml"), "connections");
  709. }
  710. void xlsx_producer::write_custom_xml_mappings(const relationship & /*rel*/)
  711. {
  712. write_start_element(constants::ns("spreadsheetml"), "MapInfo");
  713. write_end_element(constants::ns("spreadsheetml"), "MapInfo");
  714. }
  715. void xlsx_producer::write_dialogsheet(const relationship & /*rel*/)
  716. {
  717. write_start_element(constants::ns("spreadsheetml"), "dialogsheet");
  718. write_end_element(constants::ns("spreadsheetml"), "dialogsheet");
  719. }
  720. void xlsx_producer::write_external_workbook_references(const relationship & /*rel*/)
  721. {
  722. write_start_element(constants::ns("spreadsheetml"), "externalLink");
  723. write_end_element(constants::ns("spreadsheetml"), "externalLink");
  724. }
  725. void xlsx_producer::write_pivot_table(const relationship & /*rel*/)
  726. {
  727. write_start_element(constants::ns("spreadsheetml"), "pivotTableDefinition");
  728. write_end_element(constants::ns("spreadsheetml"), "pivotTableDefinition");
  729. }
  730. void xlsx_producer::write_rich_text(const std::string &ns, const xlnt::rich_text &text)
  731. {
  732. if (text.runs().size() == 1 && !text.runs().at(0).second.is_set())
  733. {
  734. write_start_element(ns, "t");
  735. write_characters(text.plain_text(), text.runs().front().preserve_space);
  736. write_end_element(ns, "t");
  737. }
  738. else
  739. {
  740. for (const auto &run : text.runs())
  741. {
  742. write_start_element(ns, "r");
  743. if (run.second.is_set())
  744. {
  745. write_start_element(ns, "rPr");
  746. if (run.second.get().bold())
  747. {
  748. write_start_element(ns, "b");
  749. write_end_element(ns, "b");
  750. }
  751. if (run.second.get().has_size())
  752. {
  753. write_start_element(ns, "sz");
  754. write_attribute<double>("val", run.second.get().size());
  755. write_end_element(ns, "sz");
  756. }
  757. if (run.second.get().has_color())
  758. {
  759. write_start_element(ns, "color");
  760. write_color(run.second.get().color());
  761. write_end_element(ns, "color");
  762. }
  763. if (run.second.get().has_name())
  764. {
  765. write_start_element(ns, "rFont");
  766. write_attribute("val", run.second.get().name());
  767. write_end_element(ns, "rFont");
  768. }
  769. if (run.second.get().has_family())
  770. {
  771. write_start_element(ns, "family");
  772. write_attribute("val", run.second.get().family());
  773. write_end_element(ns, "family");
  774. }
  775. if (run.second.get().has_scheme())
  776. {
  777. write_start_element(ns, "scheme");
  778. write_attribute("val", run.second.get().scheme());
  779. write_end_element(ns, "scheme");
  780. }
  781. write_end_element(ns, "rPr");
  782. }
  783. write_element(ns, "t", run.first, run.preserve_space);
  784. write_end_element(ns, "r");
  785. }
  786. }
  787. for (const auto &run : text.phonetic_runs())
  788. {
  789. write_start_element(ns, "rPh");
  790. write_attribute("sb", run.start);
  791. write_attribute("eb", run.end);
  792. write_start_element(ns, "t");
  793. write_characters(run.text, run.preserve_space);
  794. write_end_element(ns, "t");
  795. write_end_element(ns, "rPh");
  796. }
  797. if (text.has_phonetic_properties())
  798. {
  799. const auto &phonetic_properties = text.phonetic_properties();
  800. write_start_element(ns, "phoneticPr");
  801. write_attribute("fontId", phonetic_properties.font_id());
  802. if (text.phonetic_properties().has_type())
  803. {
  804. const auto type = phonetic_properties.type();
  805. write_attribute("type", phonetic_properties.type_as_string(type));
  806. }
  807. if (text.phonetic_properties().has_alignment())
  808. {
  809. const auto alignment = phonetic_properties.alignment();
  810. write_attribute("alignment", phonetic_properties.alignment_as_string(alignment));
  811. }
  812. write_end_element(ns, "phoneticPr");
  813. }
  814. }
  815. void xlsx_producer::write_shared_string_table(const relationship & /*rel*/)
  816. {
  817. static const auto &xmlns = constants::ns("spreadsheetml");
  818. write_start_element(xmlns, "sst");
  819. write_namespace(xmlns, "");
  820. // todo: is there a more elegant way to get this number?
  821. std::size_t string_count = 0;
  822. for (const auto ws : source_)
  823. {
  824. auto dimension = ws.calculate_dimension();
  825. auto current_cell = dimension.top_left();
  826. while (current_cell.row() <= dimension.bottom_right().row())
  827. {
  828. while (current_cell.column() <= dimension.bottom_right().column())
  829. {
  830. auto c_iter = ws.d_->cell_map_.find(current_cell);
  831. if (c_iter != ws.d_->cell_map_.end() && c_iter->second.type_ == cell_type::shared_string)
  832. {
  833. ++string_count;
  834. }
  835. current_cell.column_index(current_cell.column_index() + 1);
  836. }
  837. current_cell.row(current_cell.row() + 1);
  838. current_cell.column_index(dimension.top_left().column_index());
  839. }
  840. }
  841. write_attribute("count", string_count);
  842. write_attribute("uniqueCount", source_.shared_strings().size());
  843. for (const auto &text : source_.shared_strings())
  844. {
  845. write_start_element(xmlns, "si");
  846. write_rich_text(xmlns, text);
  847. write_end_element(xmlns, "si");
  848. }
  849. write_end_element(xmlns, "sst");
  850. }
  851. void xlsx_producer::write_shared_workbook_revision_headers(const relationship & /*rel*/)
  852. {
  853. write_start_element(constants::ns("spreadsheetml"), "headers");
  854. write_end_element(constants::ns("spreadsheetml"), "headers");
  855. }
  856. void xlsx_producer::write_shared_workbook(const relationship & /*rel*/)
  857. {
  858. write_start_element(constants::ns("spreadsheetml"), "revisions");
  859. write_end_element(constants::ns("spreadsheetml"), "revisions");
  860. }
  861. void xlsx_producer::write_shared_workbook_user_data(const relationship & /*rel*/)
  862. {
  863. write_start_element(constants::ns("spreadsheetml"), "users");
  864. write_end_element(constants::ns("spreadsheetml"), "users");
  865. }
  866. void xlsx_producer::write_font(const font &f)
  867. {
  868. static const auto &xmlns = constants::ns("spreadsheetml");
  869. write_start_element(xmlns, "font");
  870. if (f.bold())
  871. {
  872. write_start_element(xmlns, "b");
  873. write_end_element(xmlns, "b");
  874. }
  875. if (f.italic())
  876. {
  877. write_start_element(xmlns, "i");
  878. write_end_element(xmlns, "i");
  879. }
  880. if (f.strikethrough())
  881. {
  882. write_start_element(xmlns, "strike");
  883. write_end_element(xmlns, "strike");
  884. }
  885. if (f.underlined())
  886. {
  887. write_start_element(xmlns, "u");
  888. if (f.underline() != font::underline_style::single)
  889. {
  890. write_attribute("val", f.underline());
  891. }
  892. write_end_element(xmlns, "u");
  893. }
  894. if (f.superscript())
  895. {
  896. write_start_element(xmlns, "vertAlign");
  897. write_attribute("val", "superscript");
  898. write_end_element(xmlns, "vertAlign");
  899. }
  900. else if (f.subscript())
  901. {
  902. write_start_element(xmlns, "vertAlign");
  903. write_attribute("val", "subscript");
  904. write_end_element(xmlns, "vertAlign");
  905. }
  906. if (f.has_size())
  907. {
  908. write_start_element(xmlns, "sz");
  909. write_attribute<double>("val", f.size());
  910. write_end_element(xmlns, "sz");
  911. }
  912. if (f.has_color())
  913. {
  914. write_start_element(xmlns, "color");
  915. write_color(f.color());
  916. write_end_element(xmlns, "color");
  917. }
  918. if (f.has_name())
  919. {
  920. write_start_element(xmlns, "name");
  921. write_attribute("val", f.name());
  922. write_end_element(xmlns, "name");
  923. }
  924. if (f.has_family())
  925. {
  926. write_start_element(xmlns, "family");
  927. write_attribute("val", f.family());
  928. write_end_element(xmlns, "family");
  929. }
  930. if (f.has_scheme())
  931. {
  932. write_start_element(xmlns, "scheme");
  933. write_attribute("val", f.scheme());
  934. write_end_element(xmlns, "scheme");
  935. }
  936. write_end_element(xmlns, "font");
  937. }
  938. void xlsx_producer::write_fill(const fill &f)
  939. {
  940. static const auto &xmlns = constants::ns("spreadsheetml");
  941. write_start_element(xmlns, "fill");
  942. if (f.type() == xlnt::fill_type::pattern)
  943. {
  944. const auto &pattern = f.pattern_fill();
  945. write_start_element(xmlns, "patternFill");
  946. write_attribute("patternType", pattern.type());
  947. if (pattern.foreground().is_set())
  948. {
  949. write_start_element(xmlns, "fgColor");
  950. write_color(pattern.foreground().get());
  951. write_end_element(xmlns, "fgColor");
  952. }
  953. if (pattern.background().is_set())
  954. {
  955. write_start_element(xmlns, "bgColor");
  956. write_color(pattern.background().get());
  957. write_end_element(xmlns, "bgColor");
  958. }
  959. write_end_element(xmlns, "patternFill");
  960. }
  961. else if (f.type() == xlnt::fill_type::gradient)
  962. {
  963. const auto &gradient = f.gradient_fill();
  964. write_start_element(xmlns, "gradientFill");
  965. write_attribute("gradientType", gradient.type());
  966. if (gradient.degree() != 0.)
  967. {
  968. write_attribute<double>("degree", gradient.degree());
  969. }
  970. if (gradient.left() != 0.)
  971. {
  972. write_attribute<double>("left", gradient.left());
  973. }
  974. if (gradient.right() != 0.)
  975. {
  976. write_attribute<double>("right", gradient.right());
  977. }
  978. if (gradient.top() != 0.)
  979. {
  980. write_attribute<double>("top", gradient.top());
  981. }
  982. if (gradient.bottom() != 0.)
  983. {
  984. write_attribute<double>("bottom", gradient.bottom());
  985. }
  986. for (const auto &stop : gradient.stops())
  987. {
  988. write_start_element(xmlns, "stop");
  989. write_attribute<double>("position", stop.first);
  990. write_start_element(xmlns, "color");
  991. write_color(stop.second);
  992. write_end_element(xmlns, "color");
  993. write_end_element(xmlns, "stop");
  994. }
  995. write_end_element(xmlns, "gradientFill");
  996. }
  997. write_end_element(xmlns, "fill");
  998. }
  999. void xlsx_producer::write_border(const border &current_border)
  1000. {
  1001. static const auto &xmlns = constants::ns("spreadsheetml");
  1002. write_start_element(xmlns, "border");
  1003. if (current_border.diagonal().is_set())
  1004. {
  1005. auto up = current_border.diagonal().get() == diagonal_direction::both
  1006. || current_border.diagonal().get() == diagonal_direction::up;
  1007. write_attribute("diagonalUp", write_bool(up));
  1008. auto down = current_border.diagonal().get() == diagonal_direction::both
  1009. || current_border.diagonal().get() == diagonal_direction::down;
  1010. write_attribute("diagonalDown", write_bool(down));
  1011. }
  1012. for (const auto &side : xlnt::border::all_sides())
  1013. {
  1014. if (current_border.side(side).is_set())
  1015. {
  1016. const auto current_side = current_border.side(side).get();
  1017. auto side_name = to_string(side);
  1018. write_start_element(xmlns, side_name);
  1019. if (current_side.style().is_set())
  1020. {
  1021. write_attribute("style", current_side.style().get());
  1022. }
  1023. if (current_side.color().is_set())
  1024. {
  1025. write_start_element(xmlns, "color");
  1026. write_color(current_side.color().get());
  1027. write_end_element(xmlns, "color");
  1028. }
  1029. write_end_element(xmlns, side_name);
  1030. }
  1031. }
  1032. write_end_element(xmlns, "border");
  1033. }
  1034. void xlsx_producer::write_styles(const relationship & /*rel*/)
  1035. {
  1036. static const auto &xmlns = constants::ns("spreadsheetml");
  1037. static const auto &xmlns_mc = constants::ns("mc");
  1038. static const auto &xmlns_x14 = constants::ns("x14");
  1039. static const auto &xmlns_x14ac = constants::ns("x14ac");
  1040. write_start_element(xmlns, "styleSheet");
  1041. write_namespace(xmlns, "");
  1042. const auto &stylesheet = source_.impl().stylesheet_.get();
  1043. auto using_namespace = [&stylesheet](const std::string &ns) {
  1044. if (ns == "x14ac")
  1045. {
  1046. return stylesheet.known_fonts_enabled;
  1047. }
  1048. return false;
  1049. };
  1050. if (using_namespace("x14ac"))
  1051. {
  1052. write_namespace(xmlns_mc, "mc");
  1053. write_namespace(xmlns_x14ac, "x14ac");
  1054. write_attribute(xml::qname(xmlns_mc, "Ignorable"), "x14ac");
  1055. }
  1056. // Number Formats
  1057. if (!stylesheet.number_formats.empty())
  1058. {
  1059. const auto &number_formats = stylesheet.number_formats;
  1060. auto num_custom = std::count_if(number_formats.begin(), number_formats.end(),
  1061. [](const number_format &nf) { return nf.id() >= 164; });
  1062. if (num_custom > 0)
  1063. {
  1064. write_start_element(xmlns, "numFmts");
  1065. write_attribute("count", num_custom);
  1066. for (const auto &num_fmt : number_formats)
  1067. {
  1068. if (num_fmt.id() < 164) continue;
  1069. write_start_element(xmlns, "numFmt");
  1070. write_attribute("numFmtId", num_fmt.id());
  1071. write_attribute("formatCode", num_fmt.format_string());
  1072. write_end_element(xmlns, "numFmt");
  1073. }
  1074. write_end_element(xmlns, "numFmts");
  1075. }
  1076. }
  1077. // Fonts
  1078. if (!stylesheet.fonts.empty())
  1079. {
  1080. const auto &fonts = stylesheet.fonts;
  1081. write_start_element(xmlns, "fonts");
  1082. write_attribute("count", fonts.size());
  1083. if (stylesheet.known_fonts_enabled)
  1084. {
  1085. auto is_known_font = [](const font &f) {
  1086. const auto known_fonts = std::vector<font>{
  1087. font().name("Calibri").family(2).size(12).color(theme_color(1)).scheme("minor")};
  1088. return std::find(known_fonts.begin(), known_fonts.end(), f) != known_fonts.end();
  1089. };
  1090. std::size_t num_known_fonts = 0;
  1091. for (const auto &current_font : fonts)
  1092. {
  1093. if (is_known_font(current_font))
  1094. {
  1095. num_known_fonts += 1;
  1096. }
  1097. }
  1098. write_attribute(xml::qname(xmlns_x14ac, "knownFonts"), num_known_fonts);
  1099. }
  1100. for (const auto &current_font : fonts)
  1101. {
  1102. write_font(current_font);
  1103. }
  1104. write_end_element(xmlns, "fonts");
  1105. }
  1106. // Fills
  1107. if (!stylesheet.fills.empty())
  1108. {
  1109. const auto &fills = stylesheet.fills;
  1110. write_start_element(xmlns, "fills");
  1111. write_attribute("count", fills.size());
  1112. for (auto &current_fill : fills)
  1113. {
  1114. write_fill(current_fill);
  1115. }
  1116. write_end_element(xmlns, "fills");
  1117. }
  1118. // Borders
  1119. if (!stylesheet.borders.empty())
  1120. {
  1121. const auto &borders = stylesheet.borders;
  1122. write_start_element(xmlns, "borders");
  1123. write_attribute("count", borders.size());
  1124. for (const auto &current_border : borders)
  1125. {
  1126. write_border(current_border);
  1127. }
  1128. write_end_element(xmlns, "borders");
  1129. }
  1130. // Style XFs
  1131. if (stylesheet.style_impls.size() > 0)
  1132. {
  1133. write_start_element(xmlns, "cellStyleXfs");
  1134. write_attribute("count", stylesheet.style_impls.size());
  1135. for (const auto &current_style_name : stylesheet.style_names)
  1136. {
  1137. const auto &current_style_impl = stylesheet.style_impls.at(current_style_name);
  1138. write_start_element(xmlns, "xf");
  1139. if (current_style_impl.number_format_id.is_set())
  1140. {
  1141. write_attribute("numFmtId", current_style_impl.number_format_id.get());
  1142. }
  1143. if (current_style_impl.font_id.is_set())
  1144. {
  1145. write_attribute("fontId", current_style_impl.font_id.get());
  1146. }
  1147. if (current_style_impl.fill_id.is_set())
  1148. {
  1149. write_attribute("fillId", current_style_impl.fill_id.get());
  1150. }
  1151. if (current_style_impl.border_id.is_set())
  1152. {
  1153. write_attribute("borderId", current_style_impl.border_id.get());
  1154. }
  1155. if (current_style_impl.number_format_id.is_set()
  1156. && current_style_impl.number_format_applied.is_set())
  1157. {
  1158. write_attribute("applyNumberFormat",
  1159. write_bool(current_style_impl.number_format_applied.get()));
  1160. }
  1161. if (current_style_impl.fill_id.is_set()
  1162. && current_style_impl.fill_applied.is_set())
  1163. {
  1164. write_attribute("applyFill",
  1165. write_bool(current_style_impl.fill_applied.get()));
  1166. }
  1167. if (current_style_impl.font_id.is_set()
  1168. && current_style_impl.font_applied.is_set())
  1169. {
  1170. write_attribute("applyFont",
  1171. write_bool(current_style_impl.font_applied.get()));
  1172. }
  1173. if (current_style_impl.border_id.is_set()
  1174. && current_style_impl.border_applied.is_set())
  1175. {
  1176. write_attribute("applyBorder",
  1177. write_bool(current_style_impl.border_applied.get()));
  1178. }
  1179. if (current_style_impl.alignment_id.is_set()
  1180. && current_style_impl.alignment_applied.is_set())
  1181. {
  1182. write_attribute("applyAlignment",
  1183. write_bool(current_style_impl.alignment_applied.get()));
  1184. }
  1185. if (current_style_impl.protection_id.is_set()
  1186. && current_style_impl.protection_applied.is_set())
  1187. {
  1188. write_attribute("applyProtection",
  1189. write_bool(current_style_impl.protection_applied.get()));
  1190. }
  1191. if (current_style_impl.pivot_button_)
  1192. {
  1193. write_attribute("pivotButton", write_bool(true));
  1194. }
  1195. if (current_style_impl.quote_prefix_)
  1196. {
  1197. write_attribute("quotePrefix", write_bool(true));
  1198. }
  1199. if (current_style_impl.alignment_id.is_set())
  1200. {
  1201. const auto &current_alignment = stylesheet.alignments[current_style_impl.alignment_id.get()];
  1202. write_start_element(xmlns, "alignment");
  1203. if (current_alignment.vertical().is_set())
  1204. {
  1205. write_attribute("vertical", current_alignment.vertical().get());
  1206. }
  1207. if (current_alignment.horizontal().is_set())
  1208. {
  1209. write_attribute("horizontal", current_alignment.horizontal().get());
  1210. }
  1211. if (current_alignment.rotation().is_set())
  1212. {
  1213. write_attribute("textRotation", current_alignment.rotation().get());
  1214. }
  1215. if (current_alignment.wrap())
  1216. {
  1217. write_attribute("wrapText", write_bool(current_alignment.wrap()));
  1218. }
  1219. if (current_alignment.indent().is_set())
  1220. {
  1221. write_attribute("indent", current_alignment.indent().get());
  1222. }
  1223. if (current_alignment.shrink())
  1224. {
  1225. write_attribute("shrinkToFit", write_bool(current_alignment.shrink()));
  1226. }
  1227. write_end_element(xmlns, "alignment");
  1228. }
  1229. if (current_style_impl.protection_id.is_set())
  1230. {
  1231. const auto &current_protection = stylesheet.protections[current_style_impl.protection_id.get()];
  1232. write_start_element(xmlns, "protection");
  1233. write_attribute("locked", write_bool(current_protection.locked()));
  1234. write_attribute("hidden", write_bool(current_protection.hidden()));
  1235. write_end_element(xmlns, "protection");
  1236. }
  1237. write_end_element(xmlns, "xf");
  1238. }
  1239. write_end_element(xmlns, "cellStyleXfs");
  1240. }
  1241. // Format XFs
  1242. write_start_element(xmlns, "cellXfs");
  1243. write_attribute("count", stylesheet.format_impls.size());
  1244. for (auto &current_format_impl : stylesheet.format_impls)
  1245. {
  1246. write_start_element(xmlns, "xf");
  1247. if (current_format_impl.number_format_id.is_set())
  1248. {
  1249. write_attribute("numFmtId", current_format_impl.number_format_id.get());
  1250. }
  1251. if (current_format_impl.font_id.is_set())
  1252. {
  1253. write_attribute("fontId", current_format_impl.font_id.get());
  1254. }
  1255. if (current_format_impl.fill_id.is_set())
  1256. {
  1257. write_attribute("fillId", current_format_impl.fill_id.get());
  1258. }
  1259. if (current_format_impl.border_id.is_set())
  1260. {
  1261. write_attribute("borderId", current_format_impl.border_id.get());
  1262. }
  1263. if (current_format_impl.style.is_set())
  1264. {
  1265. write_attribute("xfId", stylesheet.style_index(current_format_impl.style.get()));
  1266. }
  1267. if (current_format_impl.number_format_id.is_set()
  1268. && current_format_impl.number_format_applied.is_set())
  1269. {
  1270. write_attribute("applyNumberFormat",
  1271. write_bool(current_format_impl.number_format_applied.get()));
  1272. }
  1273. if (current_format_impl.fill_id.is_set()
  1274. && current_format_impl.fill_applied.is_set())
  1275. {
  1276. write_attribute("applyFill",
  1277. write_bool(current_format_impl.fill_applied.get()));
  1278. }
  1279. if (current_format_impl.font_id.is_set()
  1280. && current_format_impl.font_applied.is_set())
  1281. {
  1282. write_attribute("applyFont",
  1283. write_bool(current_format_impl.font_applied.get()));
  1284. }
  1285. if (current_format_impl.border_id.is_set()
  1286. && current_format_impl.border_applied.is_set())
  1287. {
  1288. write_attribute("applyBorder",
  1289. write_bool(current_format_impl.border_applied.get()));
  1290. }
  1291. if (current_format_impl.alignment_applied.is_set())
  1292. {
  1293. write_attribute("applyAlignment",
  1294. write_bool(current_format_impl.alignment_applied.get()));
  1295. }
  1296. if (current_format_impl.protection_applied.is_set())
  1297. {
  1298. write_attribute("applyProtection",
  1299. write_bool(current_format_impl.protection_applied.get()));
  1300. }
  1301. if (current_format_impl.pivot_button_)
  1302. {
  1303. write_attribute("pivotButton", write_bool(true));
  1304. }
  1305. if (current_format_impl.quote_prefix_)
  1306. {
  1307. write_attribute("quotePrefix", write_bool(true));
  1308. }
  1309. if (current_format_impl.alignment_id.is_set())
  1310. {
  1311. const auto &current_alignment = stylesheet.alignments[current_format_impl.alignment_id.get()];
  1312. write_start_element(xmlns, "alignment");
  1313. if (current_alignment.horizontal().is_set())
  1314. {
  1315. write_attribute("horizontal", current_alignment.horizontal().get());
  1316. }
  1317. if (current_alignment.vertical().is_set())
  1318. {
  1319. write_attribute("vertical", current_alignment.vertical().get());
  1320. }
  1321. if (current_alignment.rotation().is_set())
  1322. {
  1323. write_attribute("textRotation", current_alignment.rotation().get());
  1324. }
  1325. if (current_alignment.wrap())
  1326. {
  1327. write_attribute("wrapText", write_bool(current_alignment.wrap()));
  1328. }
  1329. if (current_alignment.indent().is_set())
  1330. {
  1331. write_attribute("indent", current_alignment.indent().get());
  1332. }
  1333. if (current_alignment.shrink())
  1334. {
  1335. write_attribute("shrinkToFit", write_bool(current_alignment.shrink()));
  1336. }
  1337. write_end_element(xmlns, "alignment");
  1338. }
  1339. if (current_format_impl.protection_id.is_set())
  1340. {
  1341. const auto &current_protection = stylesheet.protections[current_format_impl.protection_id.get()];
  1342. write_start_element(xmlns, "protection");
  1343. write_attribute("locked", write_bool(current_protection.locked()));
  1344. write_attribute("hidden", write_bool(current_protection.hidden()));
  1345. write_end_element(xmlns, "protection");
  1346. }
  1347. write_end_element(xmlns, "xf");
  1348. }
  1349. write_end_element(xmlns, "cellXfs");
  1350. // Styles
  1351. if (stylesheet.style_impls.size() > 0)
  1352. {
  1353. write_start_element(xmlns, "cellStyles");
  1354. write_attribute("count", stylesheet.style_impls.size());
  1355. std::size_t style_index = 0;
  1356. for (auto &current_style_name : stylesheet.style_names)
  1357. {
  1358. const auto &current_style = stylesheet.style_impls.at(current_style_name);
  1359. write_start_element(xmlns, "cellStyle");
  1360. write_attribute("name", current_style.name);
  1361. write_attribute("xfId", style_index++);
  1362. if (current_style.builtin_id.is_set())
  1363. {
  1364. write_attribute("builtinId", current_style.builtin_id.get());
  1365. }
  1366. if (current_style.hidden_style)
  1367. {
  1368. write_attribute("hidden", write_bool(true));
  1369. }
  1370. if (current_style.builtin_id.is_set() && current_style.custom_builtin)
  1371. {
  1372. write_attribute("customBuiltin", write_bool(current_style.custom_builtin));
  1373. }
  1374. write_end_element(xmlns, "cellStyle");
  1375. }
  1376. write_end_element(xmlns, "cellStyles");
  1377. }
  1378. // Conditional Formats
  1379. write_start_element(xmlns, "dxfs");
  1380. write_attribute("count", stylesheet.conditional_format_impls.size());
  1381. for (auto &rule : stylesheet.conditional_format_impls)
  1382. {
  1383. write_start_element(xmlns, "dxf");
  1384. if (rule.border_id.is_set())
  1385. {
  1386. const auto &current_border = stylesheet.borders.at(rule.border_id.get());
  1387. write_border(current_border);
  1388. }
  1389. if (rule.fill_id.is_set())
  1390. {
  1391. const auto &current_fill = stylesheet.fills.at(rule.fill_id.get());
  1392. write_fill(current_fill);
  1393. }
  1394. if (rule.font_id.is_set())
  1395. {
  1396. const auto &current_font = stylesheet.fonts.at(rule.font_id.get());
  1397. write_font(current_font);
  1398. }
  1399. write_end_element(xmlns, "dxf");
  1400. }
  1401. write_end_element(xmlns, "dxfs");
  1402. write_start_element(xmlns, "tableStyles");
  1403. write_attribute("count", "0");
  1404. write_attribute("defaultTableStyle", "TableStyleMedium9");
  1405. write_attribute("defaultPivotStyle", "PivotStyleMedium7");
  1406. write_end_element(xmlns, "tableStyles");
  1407. if (!stylesheet.colors.empty())
  1408. {
  1409. write_start_element(xmlns, "colors");
  1410. write_start_element(xmlns, "indexedColors");
  1411. for (auto &c : stylesheet.colors)
  1412. {
  1413. write_start_element(xmlns, "rgbColor");
  1414. write_attribute("rgb", c.rgb().hex_string());
  1415. write_end_element(xmlns, "rgbColor");
  1416. }
  1417. write_end_element(xmlns, "indexedColors");
  1418. write_end_element(xmlns, "colors");
  1419. }
  1420. auto using_extensions = stylesheet.default_slicer_style.is_set();
  1421. if (using_extensions)
  1422. {
  1423. write_start_element(xmlns, "extLst");
  1424. if (stylesheet.default_slicer_style.is_set())
  1425. {
  1426. write_start_element(xmlns, "ext");
  1427. write_namespace(xmlns_x14, "x14");
  1428. write_attribute("uri", "{EB79DEF2-80B8-43e5-95BD-54CBDDF9020C}"); // slicerStyles URI
  1429. write_start_element(xmlns_x14, "slicerStyles");
  1430. write_attribute("defaultSlicerStyle", stylesheet.default_slicer_style.get());
  1431. write_end_element(xmlns_x14, "slicerStyles");
  1432. write_end_element(xmlns, "ext");
  1433. }
  1434. write_end_element(xmlns, "extLst");
  1435. }
  1436. write_end_element(xmlns, "styleSheet");
  1437. }
  1438. void xlsx_producer::write_theme(const relationship &theme_rel)
  1439. {
  1440. static const auto &xmlns_a = constants::ns("drawingml");
  1441. static const auto &xmlns_thm15 = constants::ns("thm15");
  1442. write_start_element(xmlns_a, "theme");
  1443. write_namespace(xmlns_a, "a");
  1444. write_attribute("name", "Office Theme");
  1445. write_start_element(xmlns_a, "themeElements");
  1446. write_start_element(xmlns_a, "clrScheme");
  1447. write_attribute("name", "Office");
  1448. struct scheme_element
  1449. {
  1450. std::string name;
  1451. std::string sub_element_name;
  1452. std::string val;
  1453. };
  1454. std::vector<scheme_element> scheme_elements = {
  1455. {"dk1", "sysClr", "windowText"},
  1456. {"lt1", "sysClr", "window"},
  1457. {"dk2", "srgbClr", "44546A"},
  1458. {"lt2", "srgbClr", "E7E6E6"},
  1459. {"accent1", "srgbClr", "5B9BD5"},
  1460. {"accent2", "srgbClr", "ED7D31"},
  1461. {"accent3", "srgbClr", "A5A5A5"},
  1462. {"accent4", "srgbClr", "FFC000"},
  1463. {"accent5", "srgbClr", "4472C4"},
  1464. {"accent6", "srgbClr", "70AD47"},
  1465. {"hlink", "srgbClr", "0563C1"},
  1466. {"folHlink", "srgbClr", "954F72"},
  1467. };
  1468. for (auto element : scheme_elements)
  1469. {
  1470. write_start_element(xmlns_a, element.name);
  1471. write_start_element(xmlns_a, element.sub_element_name);
  1472. write_attribute("val", element.val);
  1473. if (element.name == "dk1")
  1474. {
  1475. write_attribute("lastClr", "000000");
  1476. }
  1477. else if (element.name == "lt1")
  1478. {
  1479. write_attribute("lastClr", "FFFFFF");
  1480. }
  1481. write_end_element(xmlns_a, element.sub_element_name);
  1482. write_end_element(xmlns_a, element.name);
  1483. }
  1484. write_end_element(xmlns_a, "clrScheme");
  1485. struct font_scheme
  1486. {
  1487. bool typeface;
  1488. std::string script;
  1489. std::string major;
  1490. std::string minor;
  1491. };
  1492. static const auto font_schemes = std::vector<font_scheme>{{true, "latin", "Calibri Light", "Calibri"},
  1493. {true, "ea", "", ""}, {true, "cs", "", ""}, {false, "Jpan", "Yu Gothic Light", "Yu Gothic"},
  1494. {false, "Hang", "\xeb\xa7\x91\xec\x9d\x80 \xea\xb3\xa0\xeb\x94\x95",
  1495. "\xeb\xa7\x91\xec\x9d\x80 \xea\xb3\xa0\xeb\x94\x95"},
  1496. {false, "Hans", "DengXian Light", "DengXian"},
  1497. {false, "Hant", "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94",
  1498. "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94"},
  1499. {false, "Arab", "Times New Roman", "Arial"}, {false, "Hebr", "Times New Roman", "Arial"},
  1500. {false, "Thai", "Tahoma", "Tahoma"}, {false, "Ethi", "Nyala", "Nyala"}, {false, "Beng", "Vrinda", "Vrinda"},
  1501. {false, "Gujr", "Shruti", "Shruti"}, {false, "Khmr", "MoolBoran", "DaunPenh"},
  1502. {false, "Knda", "Tunga", "Tunga"}, {false, "Guru", "Raavi", "Raavi"}, {false, "Cans", "Euphemia", "Euphemia"},
  1503. {false, "Cher", "Plantagenet Cherokee", "Plantagenet Cherokee"},
  1504. {false, "Yiii", "Microsoft Yi Baiti", "Microsoft Yi Baiti"},
  1505. {false, "Tibt", "Microsoft Himalaya", "Microsoft Himalaya"}, {false, "Thaa", "MV Boli", "MV Boli"},
  1506. {false, "Deva", "Mangal", "Mangal"}, {false, "Telu", "Gautami", "Gautami"}, {false, "Taml", "Latha", "Latha"},
  1507. {false, "Syrc", "Estrangelo Edessa", "Estrangelo Edessa"}, {false, "Orya", "Kalinga", "Kalinga"},
  1508. {false, "Mlym", "Kartika", "Kartika"}, {false, "Laoo", "DokChampa", "DokChampa"},
  1509. {false, "Sinh", "Iskoola Pota", "Iskoola Pota"}, {false, "Mong", "Mongolian Baiti", "Mongolian Baiti"},
  1510. {false, "Viet", "Times New Roman", "Arial"}, {false, "Uigh", "Microsoft Uighur", "Microsoft Uighur"},
  1511. {false, "Geor", "Sylfaen", "Sylfaen"}};
  1512. write_start_element(xmlns_a, "fontScheme");
  1513. write_attribute("name", "Office");
  1514. for (auto major : {true, false})
  1515. {
  1516. write_start_element(xmlns_a, major ? "majorFont" : "minorFont");
  1517. for (const auto &scheme : font_schemes)
  1518. {
  1519. const auto scheme_value = major ? scheme.major : scheme.minor;
  1520. if (scheme.typeface)
  1521. {
  1522. write_start_element(xmlns_a, scheme.script);
  1523. write_attribute("typeface", scheme_value);
  1524. if (scheme_value == "Calibri Light")
  1525. {
  1526. write_attribute("panose", "020F0302020204030204");
  1527. }
  1528. else if (scheme_value == "Calibri")
  1529. {
  1530. write_attribute("panose", "020F0502020204030204");
  1531. }
  1532. write_end_element(xmlns_a, scheme.script);
  1533. }
  1534. else
  1535. {
  1536. write_start_element(xmlns_a, "font");
  1537. write_attribute("script", scheme.script);
  1538. write_attribute("typeface", scheme_value);
  1539. write_end_element(xmlns_a, "font");
  1540. }
  1541. }
  1542. write_end_element(xmlns_a, major ? "majorFont" : "minorFont");
  1543. }
  1544. write_end_element(xmlns_a, "fontScheme");
  1545. write_start_element(xmlns_a, "fmtScheme");
  1546. write_attribute("name", "Office");
  1547. write_start_element(xmlns_a, "fillStyleLst");
  1548. write_start_element(xmlns_a, "solidFill");
  1549. write_start_element(xmlns_a, "schemeClr");
  1550. write_attribute("val", "phClr");
  1551. write_end_element(xmlns_a, "schemeClr");
  1552. write_end_element(xmlns_a, "solidFill");
  1553. write_start_element(xmlns_a, "gradFill");
  1554. write_attribute("rotWithShape", "1");
  1555. write_start_element(xmlns_a, "gsLst");
  1556. write_start_element(xmlns_a, "gs");
  1557. write_attribute("pos", "0");
  1558. write_start_element(xmlns_a, "schemeClr");
  1559. write_attribute("val", "phClr");
  1560. write_start_element(xmlns_a, "lumMod");
  1561. write_attribute("val", "110000");
  1562. write_end_element(xmlns_a, "lumMod");
  1563. write_start_element(xmlns_a, "satMod");
  1564. write_attribute("val", "105000");
  1565. write_end_element(xmlns_a, "satMod");
  1566. write_start_element(xmlns_a, "tint");
  1567. write_attribute("val", "67000");
  1568. write_end_element(xmlns_a, "tint");
  1569. write_end_element(xmlns_a, "schemeClr");
  1570. write_end_element(xmlns_a, "gs");
  1571. write_start_element(xmlns_a, "gs");
  1572. write_attribute("pos", "50000");
  1573. write_start_element(xmlns_a, "schemeClr");
  1574. write_attribute("val", "phClr");
  1575. write_start_element(xmlns_a, "lumMod");
  1576. write_attribute("val", "105000");
  1577. write_end_element(xmlns_a, "lumMod");
  1578. write_start_element(xmlns_a, "satMod");
  1579. write_attribute("val", "103000");
  1580. write_end_element(xmlns_a, "satMod");
  1581. write_start_element(xmlns_a, "tint");
  1582. write_attribute("val", "73000");
  1583. write_end_element(xmlns_a, "tint");
  1584. write_end_element(xmlns_a, "schemeClr");
  1585. write_end_element(xmlns_a, "gs");
  1586. write_start_element(xmlns_a, "gs");
  1587. write_attribute("pos", "100000");
  1588. write_start_element(xmlns_a, "schemeClr");
  1589. write_attribute("val", "phClr");
  1590. write_start_element(xmlns_a, "lumMod");
  1591. write_attribute("val", "105000");
  1592. write_end_element(xmlns_a, "lumMod");
  1593. write_start_element(xmlns_a, "satMod");
  1594. write_attribute("val", "109000");
  1595. write_end_element(xmlns_a, "satMod");
  1596. write_start_element(xmlns_a, "tint");
  1597. write_attribute("val", "81000");
  1598. write_end_element(xmlns_a, "tint");
  1599. write_end_element(xmlns_a, "schemeClr");
  1600. write_end_element(xmlns_a, "gs");
  1601. write_end_element(xmlns_a, "gsLst");
  1602. write_start_element(xmlns_a, "lin");
  1603. write_attribute("ang", "5400000");
  1604. write_attribute("scaled", "0");
  1605. write_end_element(xmlns_a, "lin");
  1606. write_end_element(xmlns_a, "gradFill");
  1607. write_start_element(xmlns_a, "gradFill");
  1608. write_attribute("rotWithShape", "1");
  1609. write_start_element(xmlns_a, "gsLst");
  1610. write_start_element(xmlns_a, "gs");
  1611. write_attribute("pos", "0");
  1612. write_start_element(xmlns_a, "schemeClr");
  1613. write_attribute("val", "phClr");
  1614. write_start_element(xmlns_a, "satMod");
  1615. write_attribute("val", "103000");
  1616. write_end_element(xmlns_a, "satMod");
  1617. write_start_element(xmlns_a, "lumMod");
  1618. write_attribute("val", "102000");
  1619. write_end_element(xmlns_a, "lumMod");
  1620. write_start_element(xmlns_a, "tint");
  1621. write_attribute("val", "94000");
  1622. write_end_element(xmlns_a, "tint");
  1623. write_end_element(xmlns_a, "schemeClr");
  1624. write_end_element(xmlns_a, "gs");
  1625. write_start_element(xmlns_a, "gs");
  1626. write_attribute("pos", "50000");
  1627. write_start_element(xmlns_a, "schemeClr");
  1628. write_attribute("val", "phClr");
  1629. write_start_element(xmlns_a, "satMod");
  1630. write_attribute("val", "110000");
  1631. write_end_element(xmlns_a, "satMod");
  1632. write_start_element(xmlns_a, "lumMod");
  1633. write_attribute("val", "100000");
  1634. write_end_element(xmlns_a, "lumMod");
  1635. write_start_element(xmlns_a, "shade");
  1636. write_attribute("val", "100000");
  1637. write_end_element(xmlns_a, "shade");
  1638. write_end_element(xmlns_a, "schemeClr");
  1639. write_end_element(xmlns_a, "gs");
  1640. write_start_element(xmlns_a, "gs");
  1641. write_attribute("pos", "100000");
  1642. write_start_element(xmlns_a, "schemeClr");
  1643. write_attribute("val", "phClr");
  1644. write_start_element(xmlns_a, "lumMod");
  1645. write_attribute("val", "99000");
  1646. write_end_element(xmlns_a, "lumMod");
  1647. write_start_element(xmlns_a, "satMod");
  1648. write_attribute("val", "120000");
  1649. write_end_element(xmlns_a, "satMod");
  1650. write_start_element(xmlns_a, "shade");
  1651. write_attribute("val", "78000");
  1652. write_end_element(xmlns_a, "shade");
  1653. write_end_element(xmlns_a, "schemeClr");
  1654. write_end_element(xmlns_a, "gs");
  1655. write_end_element(xmlns_a, "gsLst");
  1656. write_start_element(xmlns_a, "lin");
  1657. write_attribute("ang", "5400000");
  1658. write_attribute("scaled", "0");
  1659. write_end_element(xmlns_a, "lin");
  1660. write_end_element(xmlns_a, "gradFill");
  1661. write_end_element(xmlns_a, "fillStyleLst");
  1662. write_start_element(xmlns_a, "lnStyleLst");
  1663. write_start_element(xmlns_a, "ln");
  1664. write_attribute("w", "6350");
  1665. write_attribute("cap", "flat");
  1666. write_attribute("cmpd", "sng");
  1667. write_attribute("algn", "ctr");
  1668. write_start_element(xmlns_a, "solidFill");
  1669. write_start_element(xmlns_a, "schemeClr");
  1670. write_attribute("val", "phClr");
  1671. write_end_element(xmlns_a, "schemeClr");
  1672. write_end_element(xmlns_a, "solidFill");
  1673. write_start_element(xmlns_a, "prstDash");
  1674. write_attribute("val", "solid");
  1675. write_end_element(xmlns_a, "prstDash");
  1676. write_start_element(xmlns_a, "miter");
  1677. write_attribute("lim", "800000");
  1678. write_end_element(xmlns_a, "miter");
  1679. write_end_element(xmlns_a, "ln");
  1680. write_start_element(xmlns_a, "ln");
  1681. write_attribute("w", "12700");
  1682. write_attribute("cap", "flat");
  1683. write_attribute("cmpd", "sng");
  1684. write_attribute("algn", "ctr");
  1685. write_start_element(xmlns_a, "solidFill");
  1686. write_start_element(xmlns_a, "schemeClr");
  1687. write_attribute("val", "phClr");
  1688. write_end_element(xmlns_a, "schemeClr");
  1689. write_end_element(xmlns_a, "solidFill");
  1690. write_start_element(xmlns_a, "prstDash");
  1691. write_attribute("val", "solid");
  1692. write_end_element(xmlns_a, "prstDash");
  1693. write_start_element(xmlns_a, "miter");
  1694. write_attribute("lim", "800000");
  1695. write_end_element(xmlns_a, "miter");
  1696. write_end_element(xmlns_a, "ln");
  1697. write_start_element(xmlns_a, "ln");
  1698. write_attribute("w", "19050");
  1699. write_attribute("cap", "flat");
  1700. write_attribute("cmpd", "sng");
  1701. write_attribute("algn", "ctr");
  1702. write_start_element(xmlns_a, "solidFill");
  1703. write_start_element(xmlns_a, "schemeClr");
  1704. write_attribute("val", "phClr");
  1705. write_end_element(xmlns_a, "schemeClr");
  1706. write_end_element(xmlns_a, "solidFill");
  1707. write_start_element(xmlns_a, "prstDash");
  1708. write_attribute("val", "solid");
  1709. write_end_element(xmlns_a, "prstDash");
  1710. write_start_element(xmlns_a, "miter");
  1711. write_attribute("lim", "800000");
  1712. write_end_element(xmlns_a, "miter");
  1713. write_end_element(xmlns_a, "ln");
  1714. write_end_element(xmlns_a, "lnStyleLst");
  1715. write_start_element(xmlns_a, "effectStyleLst");
  1716. write_start_element(xmlns_a, "effectStyle");
  1717. write_element(xmlns_a, "effectLst", "");
  1718. write_end_element(xmlns_a, "effectStyle");
  1719. write_start_element(xmlns_a, "effectStyle");
  1720. write_element(xmlns_a, "effectLst", "");
  1721. write_end_element(xmlns_a, "effectStyle");
  1722. write_start_element(xmlns_a, "effectStyle");
  1723. write_start_element(xmlns_a, "effectLst");
  1724. write_start_element(xmlns_a, "outerShdw");
  1725. write_attribute("blurRad", "57150");
  1726. write_attribute("dist", "19050");
  1727. write_attribute("dir", "5400000");
  1728. write_attribute("algn", "ctr");
  1729. write_attribute("rotWithShape", "0");
  1730. write_start_element(xmlns_a, "srgbClr");
  1731. write_attribute("val", "000000");
  1732. write_start_element(xmlns_a, "alpha");
  1733. write_attribute("val", "63000");
  1734. write_end_element(xmlns_a, "alpha");
  1735. write_end_element(xmlns_a, "srgbClr");
  1736. write_end_element(xmlns_a, "outerShdw");
  1737. write_end_element(xmlns_a, "effectLst");
  1738. write_end_element(xmlns_a, "effectStyle");
  1739. write_end_element(xmlns_a, "effectStyleLst");
  1740. write_start_element(xmlns_a, "bgFillStyleLst");
  1741. write_start_element(xmlns_a, "solidFill");
  1742. write_start_element(xmlns_a, "schemeClr");
  1743. write_attribute("val", "phClr");
  1744. write_end_element(xmlns_a, "schemeClr");
  1745. write_end_element(xmlns_a, "solidFill");
  1746. write_start_element(xmlns_a, "solidFill");
  1747. write_start_element(xmlns_a, "schemeClr");
  1748. write_attribute("val", "phClr");
  1749. write_start_element(xmlns_a, "tint");
  1750. write_attribute("val", "95000");
  1751. write_end_element(xmlns_a, "tint");
  1752. write_start_element(xmlns_a, "satMod");
  1753. write_attribute("val", "170000");
  1754. write_end_element(xmlns_a, "satMod");
  1755. write_end_element(xmlns_a, "schemeClr");
  1756. write_end_element(xmlns_a, "solidFill");
  1757. write_start_element(xmlns_a, "gradFill");
  1758. write_attribute("rotWithShape", "1");
  1759. write_start_element(xmlns_a, "gsLst");
  1760. write_start_element(xmlns_a, "gs");
  1761. write_attribute("pos", "0");
  1762. write_start_element(xmlns_a, "schemeClr");
  1763. write_attribute("val", "phClr");
  1764. write_start_element(xmlns_a, "tint");
  1765. write_attribute("val", "93000");
  1766. write_end_element(xmlns_a, "tint");
  1767. write_start_element(xmlns_a, "satMod");
  1768. write_attribute("val", "150000");
  1769. write_end_element(xmlns_a, "satMod");
  1770. write_start_element(xmlns_a, "shade");
  1771. write_attribute("val", "98000");
  1772. write_end_element(xmlns_a, "shade");
  1773. write_start_element(xmlns_a, "lumMod");
  1774. write_attribute("val", "102000");
  1775. write_end_element(xmlns_a, "lumMod");
  1776. write_end_element(xmlns_a, "schemeClr");
  1777. write_end_element(xmlns_a, "gs");
  1778. write_start_element(xmlns_a, "gs");
  1779. write_attribute("pos", "50000");
  1780. write_start_element(xmlns_a, "schemeClr");
  1781. write_attribute("val", "phClr");
  1782. write_start_element(xmlns_a, "tint");
  1783. write_attribute("val", "98000");
  1784. write_end_element(xmlns_a, "tint");
  1785. write_start_element(xmlns_a, "satMod");
  1786. write_attribute("val", "130000");
  1787. write_end_element(xmlns_a, "satMod");
  1788. write_start_element(xmlns_a, "shade");
  1789. write_attribute("val", "90000");
  1790. write_end_element(xmlns_a, "shade");
  1791. write_start_element(xmlns_a, "lumMod");
  1792. write_attribute("val", "103000");
  1793. write_end_element(xmlns_a, "lumMod");
  1794. write_end_element(xmlns_a, "schemeClr");
  1795. write_end_element(xmlns_a, "gs");
  1796. write_start_element(xmlns_a, "gs");
  1797. write_attribute("pos", "100000");
  1798. write_start_element(xmlns_a, "schemeClr");
  1799. write_attribute("val", "phClr");
  1800. write_start_element(xmlns_a, "shade");
  1801. write_attribute("val", "63000");
  1802. write_end_element(xmlns_a, "shade");
  1803. write_start_element(xmlns_a, "satMod");
  1804. write_attribute("val", "120000");
  1805. write_end_element(xmlns_a, "satMod");
  1806. write_end_element(xmlns_a, "schemeClr");
  1807. write_end_element(xmlns_a, "gs");
  1808. write_end_element(xmlns_a, "gsLst");
  1809. write_start_element(xmlns_a, "lin");
  1810. write_attribute("ang", "5400000");
  1811. write_attribute("scaled", "0");
  1812. write_end_element(xmlns_a, "lin");
  1813. write_end_element(xmlns_a, "gradFill");
  1814. write_end_element(xmlns_a, "bgFillStyleLst");
  1815. write_end_element(xmlns_a, "fmtScheme");
  1816. write_end_element(xmlns_a, "themeElements");
  1817. write_element(xmlns_a, "objectDefaults", "");
  1818. write_element(xmlns_a, "extraClrSchemeLst", "");
  1819. write_start_element(xmlns_a, "extLst");
  1820. write_start_element(xmlns_a, "ext");
  1821. write_attribute("uri", "{05A4C25C-085E-4340-85A3-A5531E510DB2}");
  1822. write_start_element(xmlns_thm15, "themeFamily");
  1823. write_namespace(xmlns_thm15, "thm15");
  1824. write_attribute("name", "Office Theme");
  1825. write_attribute("id", "{62F939B6-93AF-4DB8-9C6B-D6C7DFDC589F}");
  1826. write_attribute("vid", "{4A3C46E8-61CC-4603-A589-7422A47A8E4A}");
  1827. write_end_element(xmlns_thm15, "themeFamily");
  1828. write_end_element(xmlns_a, "ext");
  1829. write_end_element(xmlns_a, "extLst");
  1830. write_end_element(xmlns_a, "theme");
  1831. const auto workbook_rel = source_.manifest().relationship(path("/"), relationship_type::office_document);
  1832. const auto theme_part = source_.manifest().canonicalize({workbook_rel, theme_rel});
  1833. const auto theme_rels = source_.manifest().relationships(theme_part);
  1834. if (!theme_rels.empty())
  1835. {
  1836. write_relationships(theme_rels, theme_part);
  1837. for (auto rel : theme_rels)
  1838. {
  1839. if (rel.type() == relationship_type::image)
  1840. {
  1841. const auto image_path = source_.manifest().canonicalize({workbook_rel, theme_rel, rel});
  1842. write_image(image_path);
  1843. }
  1844. }
  1845. }
  1846. }
  1847. void xlsx_producer::write_volatile_dependencies(const relationship & /*rel*/)
  1848. {
  1849. write_start_element(constants::ns("spreadsheetml"), "volTypes");
  1850. write_end_element(constants::ns("spreadsheetml"), "volTypes");
  1851. }
  1852. void xlsx_producer::write_worksheet(const relationship &rel)
  1853. {
  1854. static const auto &xmlns = constants::ns("spreadsheetml");
  1855. static const auto &xmlns_r = constants::ns("r");
  1856. static const auto &xmlns_mc = constants::ns("mc");
  1857. static const auto &xmlns_x14ac = constants::ns("x14ac");
  1858. auto worksheet_part = rel.source().path().parent().append(rel.target().path());
  1859. auto worksheet_rels = source_.manifest().relationships(worksheet_part);
  1860. auto title = std::find_if(source_.d_->sheet_title_rel_id_map_.begin(), source_.d_->sheet_title_rel_id_map_.end(),
  1861. [&](const std::pair<std::string, std::string> &p) {
  1862. return p.second == rel.id();
  1863. })->first;
  1864. auto ws = source_.sheet_by_title(title);
  1865. write_start_element(xmlns, "worksheet");
  1866. write_namespace(xmlns, "");
  1867. write_namespace(xmlns_r, "r");
  1868. auto using_namespace = [&ws](const std::string &ns) {
  1869. if (ns == "x14ac")
  1870. {
  1871. if (ws.format_properties().dy_descent.is_set())
  1872. {
  1873. return true;
  1874. }
  1875. auto highest = ws.highest_row();
  1876. for (auto row = ws.lowest_row(); row <= highest; ++row)
  1877. {
  1878. if (ws.has_row_properties(row) && ws.row_properties(row).dy_descent.is_set())
  1879. {
  1880. return true;
  1881. }
  1882. }
  1883. }
  1884. return false;
  1885. };
  1886. if (using_namespace("x14ac"))
  1887. {
  1888. write_namespace(xmlns_mc, "mc");
  1889. write_namespace(xmlns_x14ac, "x14ac");
  1890. write_attribute(xml::qname(xmlns_mc, "Ignorable"), "x14ac");
  1891. }
  1892. if (ws.d_->sheet_properties_.is_set())
  1893. {
  1894. write_start_element(xmlns, "sheetPr");
  1895. auto &props = ws.d_->sheet_properties_.get();
  1896. if (props.sync_horizontal.is_set())
  1897. {
  1898. write_attribute("syncHorizontal", props.sync_horizontal.get());
  1899. }
  1900. if (props.sync_vertical.is_set())
  1901. {
  1902. write_attribute("syncVertical", props.sync_vertical.get());
  1903. }
  1904. if (props.sync_ref.is_set())
  1905. {
  1906. write_attribute("syncRef", props.sync_ref.get().to_string());
  1907. }
  1908. if (props.transition_evaluation.is_set())
  1909. {
  1910. write_attribute("transitionEvaluation", props.transition_evaluation.get());
  1911. }
  1912. if (props.transition_entry.is_set())
  1913. {
  1914. write_attribute("transitionEntry", props.transition_entry.get());
  1915. }
  1916. if (props.published.is_set())
  1917. {
  1918. write_attribute("published", props.published.get());
  1919. }
  1920. if (props.code_name.is_set())
  1921. {
  1922. write_attribute("codeName", props.code_name.get());
  1923. }
  1924. if (props.filter_mode.is_set())
  1925. {
  1926. write_attribute("filterMode", props.filter_mode.get());
  1927. }
  1928. if (props.enable_format_condition_calculation.is_set())
  1929. {
  1930. write_attribute("enableFormatConditionsCalculation", props.enable_format_condition_calculation.get());
  1931. }
  1932. // outlinePr is optional in the spec but is being written every time?
  1933. write_start_element(xmlns, "outlinePr");
  1934. write_attribute("summaryBelow", "1");
  1935. write_attribute("summaryRight", "1");
  1936. write_end_element(xmlns, "outlinePr");
  1937. if (ws.has_page_setup())
  1938. {
  1939. write_start_element(xmlns, "pageSetUpPr");
  1940. write_attribute("fitToPage", write_bool(ws.page_setup().fit_to_page()));
  1941. write_end_element(xmlns, "pageSetUpPr");
  1942. }
  1943. write_end_element(xmlns, "sheetPr");
  1944. }
  1945. write_start_element(xmlns, "dimension");
  1946. const auto dimension = ws.calculate_dimension();
  1947. write_attribute("ref", dimension.is_single_cell() ? dimension.top_left().to_string() : dimension.to_string());
  1948. write_end_element(xmlns, "dimension");
  1949. if (ws.has_view())
  1950. {
  1951. write_start_element(xmlns, "sheetViews");
  1952. write_start_element(xmlns, "sheetView");
  1953. const auto wb_view = source_.view();
  1954. const auto view = ws.view();
  1955. if (!view.show_grid_lines())
  1956. {
  1957. write_attribute("showGridLines", write_bool(view.show_grid_lines()));
  1958. }
  1959. if ((wb_view.active_tab.is_set() && (ws.id() - 1) == wb_view.active_tab.get())
  1960. || (!wb_view.active_tab.is_set() && ws.id() == 1))
  1961. {
  1962. write_attribute("tabSelected", write_bool(true));
  1963. }
  1964. if (view.type() != sheet_view_type::normal)
  1965. {
  1966. write_attribute("view", view.type() == sheet_view_type::page_break_preview ? "pageBreakPreview" : "pageLayout");
  1967. }
  1968. if (view.has_top_left_cell())
  1969. {
  1970. write_attribute("topLeftCell", view.top_left_cell().to_string());
  1971. }
  1972. write_attribute("workbookViewId", view.id());
  1973. if (view.has_pane())
  1974. {
  1975. const auto &current_pane = view.pane();
  1976. write_start_element(xmlns, "pane"); // CT_Pane
  1977. if (current_pane.top_left_cell.is_set())
  1978. {
  1979. write_attribute("topLeftCell", current_pane.top_left_cell.get().to_string());
  1980. }
  1981. if (current_pane.x_split + 1 == current_pane.top_left_cell.get().column())
  1982. {
  1983. write_attribute("xSplit", current_pane.x_split.index);
  1984. }
  1985. if (current_pane.y_split + 1 == current_pane.top_left_cell.get().row())
  1986. {
  1987. write_attribute("ySplit", current_pane.y_split);
  1988. }
  1989. if (current_pane.active_pane != pane_corner::top_left)
  1990. {
  1991. write_attribute("activePane", current_pane.active_pane);
  1992. }
  1993. if (current_pane.state != pane_state::split)
  1994. {
  1995. write_attribute("state", current_pane.state);
  1996. }
  1997. write_end_element(xmlns, "pane");
  1998. }
  1999. for (const auto &current_selection : view.selections())
  2000. {
  2001. write_start_element(xmlns, "selection"); // CT_Selection
  2002. if (current_selection.has_active_cell())
  2003. {
  2004. write_attribute("activeCell", current_selection.active_cell().to_string());
  2005. }
  2006. if (current_selection.has_sqref())
  2007. {
  2008. const auto sqref = current_selection.sqref();
  2009. write_attribute("sqref", sqref.is_single_cell() ? sqref.top_left().to_string() : sqref.to_string());
  2010. }
  2011. if (current_selection.pane() != pane_corner::top_left)
  2012. {
  2013. write_attribute("pane", current_selection.pane());
  2014. }
  2015. write_end_element(xmlns, "selection");
  2016. }
  2017. write_end_element(xmlns, "sheetView");
  2018. write_end_element(xmlns, "sheetViews");
  2019. }
  2020. write_start_element(xmlns, "sheetFormatPr");
  2021. const auto &format_properties = ws.d_->format_properties_;
  2022. if (format_properties.base_col_width.is_set())
  2023. {
  2024. write_attribute<double>("baseColWidth",
  2025. format_properties.base_col_width.get());
  2026. }
  2027. if (format_properties.default_column_width.is_set())
  2028. {
  2029. write_attribute<double>("defaultColWidth",
  2030. format_properties.default_column_width.get());
  2031. }
  2032. write_attribute<double>("defaultRowHeight",
  2033. format_properties.default_row_height);
  2034. if (format_properties.dy_descent.is_set())
  2035. {
  2036. write_attribute<double>(xml::qname(xmlns_x14ac, "dyDescent"),
  2037. format_properties.dy_descent.get());
  2038. }
  2039. write_end_element(xmlns, "sheetFormatPr");
  2040. bool has_column_properties = false;
  2041. const auto first_column = ws.lowest_column_or_props();
  2042. const auto last_column = ws.highest_column_or_props();
  2043. for (auto column = first_column; column <= last_column; column++)
  2044. {
  2045. if (!ws.has_column_properties(column)) continue;
  2046. if (!has_column_properties)
  2047. {
  2048. write_start_element(xmlns, "cols");
  2049. has_column_properties = true;
  2050. }
  2051. const auto &props = ws.column_properties(column);
  2052. write_start_element(xmlns, "col");
  2053. write_attribute("min", column.index);
  2054. write_attribute("max", column.index);
  2055. if (props.width.is_set())
  2056. {
  2057. double width = (props.width.get() * 7 + 5) / 7;
  2058. write_attribute("width", converter_.serialise(width));
  2059. }
  2060. if (props.best_fit)
  2061. {
  2062. write_attribute("bestFit", write_bool(true));
  2063. }
  2064. if (props.style.is_set())
  2065. {
  2066. write_attribute("style", props.style.get());
  2067. }
  2068. if (props.hidden)
  2069. {
  2070. write_attribute("hidden", write_bool(true));
  2071. }
  2072. if (props.custom_width)
  2073. {
  2074. write_attribute("customWidth", write_bool(true));
  2075. }
  2076. write_end_element(xmlns, "col");
  2077. }
  2078. if (has_column_properties)
  2079. {
  2080. write_end_element(xmlns, "cols");
  2081. }
  2082. std::vector<std::pair<std::string, hyperlink>> hyperlinks;
  2083. std::vector<cell_reference> cells_with_comments;
  2084. write_start_element(xmlns, "sheetData");
  2085. auto first_row = ws.lowest_row_or_props();
  2086. auto last_row = ws.highest_row_or_props();
  2087. auto first_block_column = constants::max_column();
  2088. auto last_block_column = constants::min_column();
  2089. for (auto row = first_row; row <= last_row; ++row)
  2090. {
  2091. bool any_non_null = false;
  2092. auto first_check_row = row;
  2093. auto last_check_row = row;
  2094. auto first_row_in_block = row == first_row || row % 16 == 1;
  2095. // See note for CT_Row, span attribute about block optimization
  2096. if (first_row_in_block)
  2097. {
  2098. // reset block column range
  2099. first_block_column = constants::max_column();
  2100. last_block_column = constants::min_column();
  2101. first_check_row = row;
  2102. // round up to the next multiple of 16
  2103. last_check_row = ((row / 16) + 1) * 16;
  2104. }
  2105. for (auto check_row = first_check_row; check_row <= last_check_row; ++check_row)
  2106. {
  2107. for (auto column = dimension.top_left().column(); column <= dimension.bottom_right().column(); ++column)
  2108. {
  2109. auto ref = cell_reference(column, check_row);
  2110. auto cell = ws.d_->cell_map_.find(ref);
  2111. if (cell == ws.d_->cell_map_.end())
  2112. {
  2113. continue;
  2114. }
  2115. if (cell->second.is_garbage_collectible())
  2116. {
  2117. continue;
  2118. }
  2119. first_block_column = std::min(first_block_column, cell->second.column_);
  2120. last_block_column = std::max(last_block_column, cell->second.column_);
  2121. if (row == check_row)
  2122. {
  2123. any_non_null = true;
  2124. }
  2125. }
  2126. }
  2127. if (!any_non_null && !ws.has_row_properties(row)) continue;
  2128. write_start_element(xmlns, "row");
  2129. write_attribute("r", row);
  2130. auto span_string = std::to_string(first_block_column.index) + ":"
  2131. + std::to_string(last_block_column.index);
  2132. write_attribute("spans", span_string);
  2133. if (ws.has_row_properties(row))
  2134. {
  2135. const auto &props = ws.row_properties(row);
  2136. if (props.style.is_set())
  2137. {
  2138. write_attribute("s", props.style.get());
  2139. }
  2140. if (props.custom_format.is_set())
  2141. {
  2142. write_attribute("customFormat", write_bool(props.custom_format.get()));
  2143. }
  2144. if (props.height.is_set())
  2145. {
  2146. auto height = props.height.get();
  2147. write_attribute("ht", converter_.serialise(height));
  2148. }
  2149. if (props.hidden)
  2150. {
  2151. write_attribute("hidden", write_bool(true));
  2152. }
  2153. if (props.custom_height)
  2154. {
  2155. write_attribute("customHeight", write_bool(true));
  2156. }
  2157. if (props.dy_descent.is_set())
  2158. {
  2159. write_attribute<double>(xml::qname(xmlns_x14ac, "dyDescent"), props.dy_descent.get());
  2160. }
  2161. }
  2162. if (any_non_null)
  2163. {
  2164. for (auto column = dimension.top_left().column(); column <= dimension.bottom_right().column(); ++column)
  2165. {
  2166. if (!ws.has_cell(cell_reference(column, row))) continue;
  2167. auto cell = ws.cell(cell_reference(column, row));
  2168. if (cell.garbage_collectible()) continue;
  2169. // record data about the cell needed later
  2170. if (cell.has_comment())
  2171. {
  2172. cells_with_comments.push_back(cell.reference());
  2173. }
  2174. if (cell.has_hyperlink())
  2175. {
  2176. hyperlinks.push_back(std::make_pair(cell.reference().to_string(), cell.hyperlink()));
  2177. }
  2178. write_start_element(xmlns, "c");
  2179. // begin cell attributes
  2180. write_attribute("r", cell.reference().to_string());
  2181. if (cell.phonetics_visible())
  2182. {
  2183. write_attribute("ph", write_bool(true));
  2184. }
  2185. if (cell.has_format())
  2186. {
  2187. write_attribute("s", cell.format().d_->id);
  2188. }
  2189. switch (cell.data_type())
  2190. {
  2191. case cell::type::empty:
  2192. break;
  2193. case cell::type::boolean:
  2194. write_attribute("t", "b");
  2195. break;
  2196. case cell::type::date:
  2197. write_attribute("t", "d");
  2198. break;
  2199. case cell::type::error:
  2200. write_attribute("t", "e");
  2201. break;
  2202. case cell::type::inline_string:
  2203. write_attribute("t", "inlineStr");
  2204. break;
  2205. case cell::type::number: // default, don't write it
  2206. //write_attribute("t", "n");
  2207. break;
  2208. case cell::type::shared_string:
  2209. write_attribute("t", "s");
  2210. break;
  2211. case cell::type::formula_string:
  2212. write_attribute("t", "str");
  2213. break;
  2214. }
  2215. //write_attribute("cm", "");
  2216. //write_attribute("vm", "");
  2217. //write_attribute("ph", "");
  2218. // begin child elements
  2219. if (cell.has_formula())
  2220. {
  2221. write_element(xmlns, "f", cell.formula());
  2222. }
  2223. switch (cell.data_type())
  2224. {
  2225. case cell::type::empty:
  2226. break;
  2227. case cell::type::boolean:
  2228. write_element(xmlns, "v", write_bool(cell.value<bool>()));
  2229. break;
  2230. case cell::type::date:
  2231. write_element(xmlns, "v", cell.value<std::string>());
  2232. break;
  2233. case cell::type::error:
  2234. write_element(xmlns, "v", cell.value<std::string>());
  2235. break;
  2236. case cell::type::inline_string:
  2237. write_start_element(xmlns, "is");
  2238. write_rich_text(xmlns, cell.value<xlnt::rich_text>());
  2239. write_end_element(xmlns, "is");
  2240. break;
  2241. case cell::type::number:
  2242. write_start_element(xmlns, "v");
  2243. write_characters(converter_.serialise(cell.value<double>()));
  2244. write_end_element(xmlns, "v");
  2245. break;
  2246. case cell::type::shared_string:
  2247. write_element(xmlns, "v", static_cast<std::size_t>(cell.d_->value_numeric_));
  2248. break;
  2249. case cell::type::formula_string:
  2250. write_element(xmlns, "v", cell.value<std::string>());
  2251. break;
  2252. }
  2253. write_end_element(xmlns, "c");
  2254. }
  2255. }
  2256. write_end_element(xmlns, "row");
  2257. }
  2258. write_end_element(xmlns, "sheetData");
  2259. if (ws.has_auto_filter())
  2260. {
  2261. write_start_element(xmlns, "autoFilter");
  2262. write_attribute("ref", ws.auto_filter().to_string());
  2263. write_end_element(xmlns, "autoFilter");
  2264. }
  2265. if (!ws.merged_ranges().empty())
  2266. {
  2267. write_start_element(xmlns, "mergeCells");
  2268. write_attribute("count", ws.merged_ranges().size());
  2269. for (auto merged_range : ws.merged_ranges())
  2270. {
  2271. write_start_element(xmlns, "mergeCell");
  2272. write_attribute("ref", merged_range.to_string());
  2273. write_end_element(xmlns, "mergeCell");
  2274. }
  2275. write_end_element(xmlns, "mergeCells");
  2276. }
  2277. if (source_.impl().stylesheet_.is_set())
  2278. {
  2279. const auto &stylesheet = source_.impl().stylesheet_.get();
  2280. const auto &cf_impls = stylesheet.conditional_format_impls;
  2281. std::unordered_map<std::string, std::vector<const conditional_format_impl *>> range_map;
  2282. for (auto &cf : cf_impls)
  2283. {
  2284. if (cf.target_sheet != ws.d_) continue;
  2285. if (range_map.find(cf.target_range.to_string()) == range_map.end())
  2286. {
  2287. range_map[cf.target_range.to_string()] = {};
  2288. }
  2289. range_map[cf.target_range.to_string()].push_back(&cf);
  2290. }
  2291. for (const auto &range_rules_pair : range_map)
  2292. {
  2293. write_start_element(xmlns, "conditionalFormatting");
  2294. write_attribute("sqref", range_rules_pair.first);
  2295. std::size_t i = 1;
  2296. for (auto rule : range_rules_pair.second)
  2297. {
  2298. write_start_element(xmlns, "cfRule");
  2299. write_attribute("type", "containsText");
  2300. write_attribute("operator", "containsText");
  2301. write_attribute("dxfId", rule->differential_format_id);
  2302. write_attribute("priority", i++);
  2303. write_attribute("text", rule->when.text_comparand_);
  2304. //TODO: what does this formula mean and why is it necessary?
  2305. write_element(xmlns, "formula", "NOT(ISERROR(SEARCH(\"" + rule->when.text_comparand_ + "\",A1)))");
  2306. write_end_element(xmlns, "cfRule");
  2307. }
  2308. write_end_element(xmlns, "conditionalFormatting");
  2309. }
  2310. }
  2311. if (!hyperlinks.empty())
  2312. {
  2313. write_start_element(xmlns, "hyperlinks");
  2314. for (const auto &hyperlink : hyperlinks)
  2315. {
  2316. write_start_element(xmlns, "hyperlink");
  2317. write_attribute("ref", hyperlink.first);
  2318. if (hyperlink.second.external())
  2319. {
  2320. write_attribute(xml::qname(xmlns_r, "id"),
  2321. hyperlink.second.relationship().id());
  2322. }
  2323. else
  2324. {
  2325. write_attribute("location", hyperlink.second.target_range());
  2326. write_attribute("display", hyperlink.second.display());
  2327. }
  2328. write_end_element(xmlns, "hyperlink");
  2329. }
  2330. write_end_element(xmlns, "hyperlinks");
  2331. }
  2332. if (ws.has_phonetic_properties())
  2333. {
  2334. write_start_element(xmlns, phonetic_pr::Serialised_ID());
  2335. const auto &ph_props = ws.phonetic_properties();
  2336. write_attribute("fontId", ph_props.font_id());
  2337. if (ph_props.has_type())
  2338. {
  2339. write_attribute("type", phonetic_pr::type_as_string(ph_props.type()));
  2340. }
  2341. if (ph_props.has_alignment())
  2342. {
  2343. write_attribute("alignment", phonetic_pr::alignment_as_string(ph_props.alignment()));
  2344. }
  2345. write_end_element(xmlns, phonetic_pr::Serialised_ID());
  2346. }
  2347. if (ws.d_->print_options_.is_set())
  2348. {
  2349. auto &opts = ws.d_->print_options_.get();
  2350. write_start_element(xmlns, "printOptions");
  2351. if (opts.print_grid_lines.is_set())
  2352. {
  2353. write_attribute("gridLines", write_bool(opts.print_grid_lines.get()));
  2354. }
  2355. if (opts.grid_lines_set.is_set())
  2356. {
  2357. write_attribute("gridLineSet", write_bool(opts.grid_lines_set.get()));
  2358. }
  2359. if (opts.print_headings.is_set())
  2360. {
  2361. write_attribute("headings", write_bool(opts.print_headings.get()));
  2362. }
  2363. if (opts.horizontal_centered.is_set())
  2364. {
  2365. write_attribute("horizontalCentered", write_bool(opts.horizontal_centered.get()));
  2366. }
  2367. if (opts.vertical_centered.is_set())
  2368. {
  2369. write_attribute("verticalCentered", write_bool(opts.vertical_centered.get()));
  2370. }
  2371. write_end_element(xmlns, "printOptions");
  2372. }
  2373. if (ws.has_page_margins())
  2374. {
  2375. write_start_element(xmlns, "pageMargins");
  2376. write_attribute<double>("left", ws.page_margins().left());
  2377. write_attribute<double>("right", ws.page_margins().right());
  2378. write_attribute<double>("top", ws.page_margins().top());
  2379. write_attribute<double>("bottom", ws.page_margins().bottom());
  2380. write_attribute<double>("header", ws.page_margins().header());
  2381. write_attribute<double>("footer", ws.page_margins().footer());
  2382. write_end_element(xmlns, "pageMargins");
  2383. }
  2384. if (ws.has_page_setup())
  2385. {
  2386. const xlnt::page_setup &ps = ws.page_setup();
  2387. write_start_element(xmlns, "pageSetup");
  2388. if (ws.page_setup().orientation_.is_set())
  2389. {
  2390. write_attribute("orientation", ws.page_setup().orientation_.get());
  2391. }
  2392. if (ws.page_setup().horizontal_dpi_.is_set())
  2393. {
  2394. write_attribute("horizontalDpi", ws.page_setup().horizontal_dpi_.get());
  2395. }
  2396. if (ws.page_setup().vertical_dpi_.is_set())
  2397. {
  2398. write_attribute("verticalDpi", ws.page_setup().vertical_dpi_.get());
  2399. }
  2400. if (ps.has_paper_size())
  2401. {
  2402. write_attribute("paperSize", static_cast<std::size_t>(ps.paper_size()));
  2403. }
  2404. if (ps.has_scale())
  2405. {
  2406. write_attribute("scale", ps.scale());
  2407. }
  2408. if (ps.has_rel_id())
  2409. {
  2410. write_attribute(xml::qname(xmlns_r, "id"), ps.rel_id());
  2411. }
  2412. /*write_attribute("fitToHeight", write_bool(ws.page_setup().fit_to_height()));
  2413. write_attribute("fitToWidth", write_bool(ws.page_setup().fit_to_width()));*/
  2414. write_end_element(xmlns, "pageSetup");
  2415. }
  2416. if (ws.has_header_footer())
  2417. {
  2418. const auto hf = ws.header_footer();
  2419. write_start_element(xmlns, "headerFooter");
  2420. auto odd_header = std::string();
  2421. auto odd_footer = std::string();
  2422. auto even_header = std::string();
  2423. auto even_footer = std::string();
  2424. auto first_header = std::string();
  2425. auto first_footer = std::string();
  2426. const auto locations =
  2427. {
  2428. header_footer::location::left,
  2429. header_footer::location::center,
  2430. header_footer::location::right};
  2431. using xlnt::detail::encode_header_footer;
  2432. for (auto location : locations)
  2433. {
  2434. if (hf.different_odd_even())
  2435. {
  2436. if (hf.has_odd_even_header(location))
  2437. {
  2438. odd_header.append(encode_header_footer(hf.odd_header(location), location, converter_));
  2439. even_header.append(encode_header_footer(hf.even_header(location), location, converter_));
  2440. }
  2441. if (hf.has_odd_even_footer(location))
  2442. {
  2443. odd_footer.append(encode_header_footer(hf.odd_footer(location), location, converter_));
  2444. even_footer.append(encode_header_footer(hf.even_footer(location), location, converter_));
  2445. }
  2446. }
  2447. else
  2448. {
  2449. if (hf.has_header(location))
  2450. {
  2451. odd_header.append(encode_header_footer(hf.header(location), location, converter_));
  2452. }
  2453. if (hf.has_footer(location))
  2454. {
  2455. odd_footer.append(encode_header_footer(hf.footer(location), location, converter_));
  2456. }
  2457. }
  2458. if (hf.different_first())
  2459. {
  2460. if (hf.has_first_page_header(location))
  2461. {
  2462. first_header.append(encode_header_footer(hf.first_page_header(location), location, converter_));
  2463. }
  2464. if (hf.has_first_page_footer(location))
  2465. {
  2466. first_footer.append(encode_header_footer(hf.first_page_footer(location), location, converter_));
  2467. }
  2468. }
  2469. }
  2470. if (!odd_header.empty())
  2471. {
  2472. write_element(xmlns, "oddHeader", odd_header);
  2473. }
  2474. if (!odd_footer.empty())
  2475. {
  2476. write_element(xmlns, "oddFooter", odd_footer);
  2477. }
  2478. if (!even_header.empty())
  2479. {
  2480. write_element(xmlns, "evenHeader", even_header);
  2481. }
  2482. if (!even_footer.empty())
  2483. {
  2484. write_element(xmlns, "evenFooter", even_footer);
  2485. }
  2486. if (!first_header.empty())
  2487. {
  2488. write_element(xmlns, "firstHeader", first_header);
  2489. }
  2490. if (!first_footer.empty())
  2491. {
  2492. write_element(xmlns, "firstFooter", first_footer);
  2493. }
  2494. write_end_element(xmlns, "headerFooter");
  2495. }
  2496. if (!ws.page_break_rows().empty())
  2497. {
  2498. write_start_element(xmlns, "rowBreaks");
  2499. write_attribute("count", ws.page_break_rows().size());
  2500. write_attribute("manualBreakCount", ws.page_break_rows().size());
  2501. for (auto break_id : ws.page_break_rows())
  2502. {
  2503. write_start_element(xmlns, "brk");
  2504. write_attribute("id", break_id);
  2505. write_attribute("max", 16383);
  2506. write_attribute("man", 1);
  2507. write_end_element(xmlns, "brk");
  2508. }
  2509. write_end_element(xmlns, "rowBreaks");
  2510. }
  2511. if (!ws.page_break_columns().empty())
  2512. {
  2513. write_start_element(xmlns, "colBreaks");
  2514. write_attribute("count", ws.page_break_columns().size());
  2515. write_attribute("manualBreakCount", ws.page_break_columns().size());
  2516. for (auto break_id : ws.page_break_columns())
  2517. {
  2518. write_start_element(xmlns, "brk");
  2519. write_attribute("id", break_id.index);
  2520. write_attribute("max", 1048575);
  2521. write_attribute("man", 1);
  2522. write_end_element(xmlns, "brk");
  2523. }
  2524. write_end_element(xmlns, "colBreaks");
  2525. }
  2526. if (!worksheet_rels.empty())
  2527. {
  2528. for (const auto &child_rel : worksheet_rels)
  2529. {
  2530. if (child_rel.type() == xlnt::relationship_type::vml_drawing)
  2531. {
  2532. write_start_element(xmlns, "legacyDrawing");
  2533. write_attribute(xml::qname(xmlns_r, "id"), child_rel.id());
  2534. write_end_element(xmlns, "legacyDrawing");
  2535. }
  2536. else if (child_rel.type() == xlnt::relationship_type::drawings)
  2537. {
  2538. write_start_element(xmlns, "drawing");
  2539. write_attribute(xml::qname(xmlns_r, "id"), child_rel.id());
  2540. write_end_element(xmlns, "drawing");
  2541. }
  2542. }
  2543. }
  2544. if (ws.d_->extension_list_.is_set())
  2545. {
  2546. ws.d_->extension_list_.get().serialize(*current_part_serializer_, xmlns);
  2547. }
  2548. write_end_element(xmlns, "worksheet");
  2549. if (!worksheet_rels.empty())
  2550. {
  2551. write_relationships(worksheet_rels, worksheet_part);
  2552. for (const auto &child_rel : worksheet_rels)
  2553. {
  2554. if (child_rel.target_mode() == target_mode::external) continue;
  2555. // todo: this is ugly
  2556. path archive_path(worksheet_part.parent().append(child_rel.target().path()));
  2557. auto split_part_path = archive_path.split();
  2558. auto part_path_iter = split_part_path.begin();
  2559. while (part_path_iter != split_part_path.end())
  2560. {
  2561. if (*part_path_iter == "..")
  2562. {
  2563. part_path_iter = split_part_path.erase(part_path_iter - 1, part_path_iter + 1);
  2564. continue;
  2565. }
  2566. ++part_path_iter;
  2567. }
  2568. archive_path = std::accumulate(split_part_path.begin(), split_part_path.end(), path(""),
  2569. [](const path &a, const std::string &b) { return a.append(b); });
  2570. if (child_rel.type() == relationship_type::printer_settings)
  2571. {
  2572. write_binary(archive_path);
  2573. continue;
  2574. }
  2575. begin_part(archive_path);
  2576. if (child_rel.type() == relationship_type::comments)
  2577. {
  2578. write_comments(child_rel, ws, cells_with_comments);
  2579. }
  2580. else if (child_rel.type() == relationship_type::vml_drawing)
  2581. {
  2582. write_vml_drawings(child_rel, ws, cells_with_comments);
  2583. }
  2584. else if (child_rel.type() == relationship_type::drawings)
  2585. {
  2586. write_drawings(child_rel, ws);
  2587. }
  2588. }
  2589. }
  2590. }
  2591. // Sheet Relationship Target Parts
  2592. void xlsx_producer::write_comments(const relationship & /*rel*/, worksheet ws, const std::vector<cell_reference> &cells)
  2593. {
  2594. static const auto &xmlns = constants::ns("spreadsheetml");
  2595. write_start_element(xmlns, "comments");
  2596. write_namespace(xmlns, "");
  2597. if (!cells.empty())
  2598. {
  2599. std::unordered_map<std::string, std::size_t> authors;
  2600. for (auto cell_ref : cells)
  2601. {
  2602. auto cell = ws.cell(cell_ref);
  2603. auto author = cell.comment().author();
  2604. if (authors.find(author) == authors.end())
  2605. {
  2606. auto author_index = authors.size();
  2607. authors[author] = author_index;
  2608. }
  2609. }
  2610. write_start_element(xmlns, "authors");
  2611. for (const auto &author : authors)
  2612. {
  2613. write_start_element(xmlns, "author");
  2614. write_characters(author.first);
  2615. write_end_element(xmlns, "author");
  2616. }
  2617. write_end_element(xmlns, "authors");
  2618. write_start_element(xmlns, "commentList");
  2619. for (const auto &cell_ref : cells)
  2620. {
  2621. write_start_element(xmlns, "comment");
  2622. auto cell = ws.cell(cell_ref);
  2623. auto cell_comment = cell.comment();
  2624. write_attribute("ref", cell_ref.to_string());
  2625. auto author_id = authors.at(cell_comment.author());
  2626. write_attribute("authorId", author_id);
  2627. write_start_element(xmlns, "text");
  2628. write_rich_text(xmlns, cell_comment.text());
  2629. write_end_element(xmlns, "text");
  2630. write_end_element(xmlns, "comment");
  2631. }
  2632. write_end_element(xmlns, "commentList");
  2633. }
  2634. write_end_element(xmlns, "comments");
  2635. }
  2636. void xlsx_producer::write_vml_drawings(const relationship &rel, worksheet ws, const std::vector<cell_reference> &cells)
  2637. {
  2638. static const auto &xmlns_mv = std::string("http://macVmlSchemaUri");
  2639. static const auto &xmlns_o = std::string("urn:schemas-microsoft-com:office:office");
  2640. static const auto &xmlns_v = std::string("urn:schemas-microsoft-com:vml");
  2641. static const auto &xmlns_x = std::string("urn:schemas-microsoft-com:office:excel");
  2642. write_start_element("xml");
  2643. write_namespace(xmlns_v, "v");
  2644. write_namespace(xmlns_o, "o");
  2645. write_namespace(xmlns_x, "x");
  2646. write_namespace(xmlns_mv, "mv");
  2647. write_start_element(xmlns_o, "shapelayout");
  2648. write_attribute(xml::qname(xmlns_v, "ext"), "edit");
  2649. write_start_element(xmlns_o, "idmap");
  2650. write_attribute(xml::qname(xmlns_v, "ext"), "edit");
  2651. auto filename = rel.target().path().split_extension().first;
  2652. auto index_pos = filename.size() - 1;
  2653. while (filename[index_pos] >= '0' && filename[index_pos] <= '9')
  2654. {
  2655. index_pos--;
  2656. }
  2657. auto file_index = std::stoull(filename.substr(index_pos + 1));
  2658. write_attribute("data", file_index);
  2659. write_end_element(xmlns_o, "idmap");
  2660. write_end_element(xmlns_o, "shapelayout");
  2661. write_start_element(xmlns_v, "shapetype");
  2662. write_attribute("id", "_x0000_t202");
  2663. write_attribute("coordsize", "21600,21600");
  2664. write_attribute(xml::qname(xmlns_o, "spt"), "202");
  2665. write_attribute("path", "m0,0l0,21600,21600,21600,21600,0xe");
  2666. write_start_element(xmlns_v, "stroke");
  2667. write_attribute("joinstyle", "miter");
  2668. write_end_element(xmlns_v, "stroke");
  2669. write_start_element(xmlns_v, "path");
  2670. write_attribute("gradientshapeok", "t");
  2671. write_attribute(xml::qname(xmlns_o, "connecttype"), "rect");
  2672. write_end_element(xmlns_v, "path");
  2673. write_end_element(xmlns_v, "shapetype");
  2674. std::size_t comment_index = 0;
  2675. for (const auto &cell_ref : cells)
  2676. {
  2677. auto comment = ws.cell(cell_ref).comment();
  2678. auto shape_id = 1024 * file_index + 1 + comment_index * 2;
  2679. write_start_element(xmlns_v, "shape");
  2680. write_attribute("id", "_x0000_s" + std::to_string(shape_id));
  2681. write_attribute("type", "#_x0000_t202");
  2682. std::vector<std::pair<std::string, std::string>> style;
  2683. style.push_back({"position", "absolute"});
  2684. style.push_back({"margin-left", std::to_string(comment.left()) + "pt"});
  2685. style.push_back({"margin-top", std::to_string(comment.top()) + "pt"});
  2686. style.push_back({"width", std::to_string(comment.width()) + "pt"});
  2687. style.push_back({"height", std::to_string(comment.height()) + "pt"});
  2688. style.push_back({"z-index", std::to_string(comment_index + 1)});
  2689. style.push_back({"visibility", comment.visible() ? "visible" : "hidden"});
  2690. std::string style_string;
  2691. for (auto part : style)
  2692. {
  2693. style_string.append(part.first);
  2694. style_string.append(":");
  2695. style_string.append(part.second);
  2696. style_string.append(";");
  2697. }
  2698. write_attribute("style", style_string);
  2699. write_attribute("fillcolor", "#fbf6d6");
  2700. write_attribute("strokecolor", "#edeaa1");
  2701. write_start_element(xmlns_v, "fill");
  2702. write_attribute("color2", "#fbfe82");
  2703. write_attribute("angle", -180);
  2704. write_attribute("type", "gradient");
  2705. write_start_element(xmlns_o, "fill");
  2706. write_attribute(xml::qname(xmlns_v, "ext"), "view");
  2707. write_attribute("type", "gradientUnscaled");
  2708. write_end_element(xmlns_o, "fill");
  2709. write_end_element(xmlns_v, "fill");
  2710. write_start_element(xmlns_v, "shadow");
  2711. write_attribute("on", "t");
  2712. write_attribute("obscured", "t");
  2713. write_end_element(xmlns_v, "shadow");
  2714. write_start_element(xmlns_v, "path");
  2715. write_attribute(xml::qname(xmlns_o, "connecttype"), "none");
  2716. write_end_element(xmlns_v, "path");
  2717. write_start_element(xmlns_v, "textbox");
  2718. write_attribute("style", "mso-direction-alt:auto");
  2719. write_start_element("div");
  2720. write_attribute("style", "text-align:left");
  2721. write_characters("");
  2722. write_end_element("div");
  2723. write_end_element(xmlns_v, "textbox");
  2724. write_start_element(xmlns_x, "ClientData");
  2725. write_attribute("ObjectType", "Note");
  2726. write_start_element(xmlns_x, "MoveWithCells");
  2727. write_end_element(xmlns_x, "MoveWithCells");
  2728. write_start_element(xmlns_x, "SizeWithCells");
  2729. write_end_element(xmlns_x, "SizeWithCells");
  2730. write_start_element(xmlns_x, "Anchor");
  2731. write_characters("1, 15, 0, " + std::to_string(2 + comment_index * 4) + ", 2, 54, 4, 14");
  2732. write_end_element(xmlns_x, "Anchor");
  2733. write_start_element(xmlns_x, "AutoFill");
  2734. write_characters("False");
  2735. write_end_element(xmlns_x, "AutoFill");
  2736. write_start_element(xmlns_x, "Row");
  2737. write_characters(cell_ref.row() - 1);
  2738. write_end_element(xmlns_x, "Row");
  2739. write_start_element(xmlns_x, "Column");
  2740. write_characters(cell_ref.column_index() - 1);
  2741. write_end_element(xmlns_x, "Column");
  2742. write_end_element(xmlns_x, "ClientData");
  2743. write_end_element(xmlns_v, "shape");
  2744. ++comment_index;
  2745. }
  2746. write_end_element("xml");
  2747. }
  2748. void xlsx_producer::write_drawings(const relationship &drawing_rel, worksheet ws)
  2749. {
  2750. const auto workbook_rel = source_.manifest().relationship(path("/"), relationship_type::office_document);
  2751. const auto worksheet_rel = ws.referring_relationship();
  2752. const auto drawing_part = source_.manifest().canonicalize({workbook_rel, worksheet_rel, drawing_rel});
  2753. const auto drawing_rels = source_.manifest().relationships(drawing_part);
  2754. if (ws.d_->drawing_.is_set())
  2755. {
  2756. ws.d_->drawing_.get().serialize(*current_part_serializer_);
  2757. }
  2758. if (!drawing_rels.empty())
  2759. {
  2760. write_relationships(drawing_rels, drawing_part);
  2761. for (auto rel : drawing_rels)
  2762. {
  2763. if (rel.type() == relationship_type::image)
  2764. {
  2765. const auto image_path = source_.manifest().canonicalize({workbook_rel, worksheet_rel, rel});
  2766. if (image_path.string().find("cid:") != std::string::npos)
  2767. {
  2768. // skip cid attachments
  2769. continue;
  2770. }
  2771. write_image(image_path);
  2772. }
  2773. }
  2774. }
  2775. }
  2776. // Other Parts
  2777. void xlsx_producer::write_custom_property()
  2778. {
  2779. }
  2780. void xlsx_producer::write_unknown_parts()
  2781. {
  2782. }
  2783. void xlsx_producer::write_unknown_relationships()
  2784. {
  2785. }
  2786. void xlsx_producer::write_image(const path &image_path)
  2787. {
  2788. end_part();
  2789. vector_istreambuf buffer(source_.d_->images_.at(image_path.string()));
  2790. auto image_streambuf = archive_->open(image_path);
  2791. std::ostream(image_streambuf.get()) << &buffer;
  2792. }
  2793. void xlsx_producer::write_binary(const path &binary_path)
  2794. {
  2795. end_part();
  2796. vector_istreambuf buffer(source_.d_->binaries_.at(binary_path.string()));
  2797. auto image_streambuf = archive_->open(binary_path);
  2798. std::ostream(image_streambuf.get()) << &buffer;
  2799. }
  2800. std::string xlsx_producer::write_bool(bool boolean) const
  2801. {
  2802. return boolean ? "1" : "0";
  2803. }
  2804. void xlsx_producer::write_relationships(const std::vector<xlnt::relationship> &relationships, const path &part)
  2805. {
  2806. path parent = part.parent();
  2807. if (parent.is_absolute())
  2808. {
  2809. parent = path(parent.string().substr(1));
  2810. }
  2811. path rels_path(parent.append("_rels").append(part.filename() + ".rels").string());
  2812. begin_part(rels_path);
  2813. const auto xmlns = xlnt::constants::ns("relationships");
  2814. write_start_element(xmlns, "Relationships");
  2815. write_namespace(xmlns, "");
  2816. for (std::size_t i = 1; i <= relationships.size(); ++i)
  2817. {
  2818. auto rel_iter = std::find_if(relationships.begin(), relationships.end(),
  2819. [&i](const relationship &r) { return r.id() == "rId" + std::to_string(i); });
  2820. auto relationship = *rel_iter;
  2821. write_start_element(xmlns, "Relationship");
  2822. write_attribute("Id", relationship.id());
  2823. write_attribute("Type", relationship.type());
  2824. write_attribute("Target", relationship.target().path().string());
  2825. if (relationship.target_mode() == xlnt::target_mode::external)
  2826. {
  2827. write_attribute("TargetMode", "External");
  2828. }
  2829. write_end_element(xmlns, "Relationship");
  2830. }
  2831. write_end_element(xmlns, "Relationships");
  2832. }
  2833. void xlsx_producer::write_color(const xlnt::color &color)
  2834. {
  2835. if (color.auto_())
  2836. {
  2837. write_attribute("auto", write_bool(true));
  2838. return;
  2839. }
  2840. switch (color.type())
  2841. {
  2842. case xlnt::color_type::theme:
  2843. write_attribute("theme", color.theme().index());
  2844. break;
  2845. case xlnt::color_type::indexed:
  2846. write_attribute("indexed", color.indexed().index());
  2847. break;
  2848. case xlnt::color_type::rgb:
  2849. write_attribute("rgb", color.rgb().hex_string());
  2850. break;
  2851. }
  2852. if (color.has_tint())
  2853. {
  2854. write_attribute("tint", converter_.serialise(color.tint()));
  2855. }
  2856. }
  2857. void xlsx_producer::write_start_element(const std::string &name)
  2858. {
  2859. current_part_serializer_->start_element(name);
  2860. }
  2861. void xlsx_producer::write_start_element(const std::string &ns, const std::string &name)
  2862. {
  2863. current_part_serializer_->start_element(ns, name);
  2864. }
  2865. void xlsx_producer::write_end_element(const std::string &name)
  2866. {
  2867. current_part_serializer_->end_element();
  2868. }
  2869. void xlsx_producer::write_end_element(const std::string &ns, const std::string &name)
  2870. {
  2871. current_part_serializer_->end_element();
  2872. }
  2873. void xlsx_producer::write_namespace(const std::string &ns, const std::string &prefix)
  2874. {
  2875. current_part_serializer_->namespace_decl(ns, prefix);
  2876. }
  2877. } // namespace detail
  2878. } // namespace xlnt