worksheet.c 159 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739
  1. /*****************************************************************************
  2. * worksheet - A library for creating Excel XLSX worksheet files.
  3. *
  4. * Used in conjunction with the libxlsxwriter library.
  5. *
  6. * Copyright 2014-2018, John McNamara, [email protected]. See LICENSE.txt.
  7. *
  8. */
  9. #include <ctype.h>
  10. #include "xlsxwriter/xmlwriter.h"
  11. #include "xlsxwriter/worksheet.h"
  12. #include "xlsxwriter/format.h"
  13. #include "xlsxwriter/utility.h"
  14. #include "xlsxwriter/relationships.h"
  15. #define LXW_STR_MAX 32767
  16. #define LXW_BUFFER_SIZE 4096
  17. #define LXW_PORTRAIT 1
  18. #define LXW_LANDSCAPE 0
  19. #define LXW_PRINT_ACROSS 1
  20. #define LXW_VALIDATION_MAX_TITLE_LENGTH 32
  21. #define LXW_VALIDATION_MAX_STRING_LENGTH 255
  22. /*
  23. * Forward declarations.
  24. */
  25. STATIC void _worksheet_write_rows(lxw_worksheet *self);
  26. STATIC int _row_cmp(lxw_row *row1, lxw_row *row2);
  27. STATIC int _cell_cmp(lxw_cell *cell1, lxw_cell *cell2);
  28. #ifndef __clang_analyzer__
  29. LXW_RB_GENERATE_ROW(lxw_table_rows, lxw_row, tree_pointers, _row_cmp);
  30. LXW_RB_GENERATE_CELL(lxw_table_cells, lxw_cell, tree_pointers, _cell_cmp);
  31. #endif
  32. /*****************************************************************************
  33. *
  34. * Private functions.
  35. *
  36. ****************************************************************************/
  37. /*
  38. * Find but don't create a row object for a given row number.
  39. */
  40. lxw_row *
  41. lxw_worksheet_find_row(lxw_worksheet *self, lxw_row_t row_num)
  42. {
  43. lxw_row row;
  44. row.row_num = row_num;
  45. return RB_FIND(lxw_table_rows, self->table, &row);
  46. }
  47. /*
  48. * Find but don't create a cell object for a given row object and col number.
  49. */
  50. lxw_cell *
  51. lxw_worksheet_find_cell(lxw_row *row, lxw_col_t col_num)
  52. {
  53. lxw_cell cell;
  54. if (!row)
  55. return NULL;
  56. cell.col_num = col_num;
  57. return RB_FIND(lxw_table_cells, row->cells, &cell);
  58. }
  59. /*
  60. * Create a new worksheet object.
  61. */
  62. lxw_worksheet *
  63. lxw_worksheet_new(lxw_worksheet_init_data *init_data)
  64. {
  65. lxw_worksheet *worksheet = calloc(1, sizeof(lxw_worksheet));
  66. GOTO_LABEL_ON_MEM_ERROR(worksheet, mem_error);
  67. worksheet->table = calloc(1, sizeof(struct lxw_table_rows));
  68. GOTO_LABEL_ON_MEM_ERROR(worksheet->table, mem_error);
  69. RB_INIT(worksheet->table);
  70. worksheet->hyperlinks = calloc(1, sizeof(struct lxw_table_rows));
  71. GOTO_LABEL_ON_MEM_ERROR(worksheet->hyperlinks, mem_error);
  72. RB_INIT(worksheet->hyperlinks);
  73. /* Initialize the cached rows. */
  74. worksheet->table->cached_row_num = LXW_ROW_MAX + 1;
  75. worksheet->hyperlinks->cached_row_num = LXW_ROW_MAX + 1;
  76. if (init_data && init_data->optimize) {
  77. worksheet->array = calloc(LXW_COL_MAX, sizeof(struct lxw_cell *));
  78. GOTO_LABEL_ON_MEM_ERROR(worksheet->array, mem_error);
  79. }
  80. worksheet->col_options =
  81. calloc(LXW_COL_META_MAX, sizeof(lxw_col_options *));
  82. worksheet->col_options_max = LXW_COL_META_MAX;
  83. GOTO_LABEL_ON_MEM_ERROR(worksheet->col_options, mem_error);
  84. worksheet->col_formats = calloc(LXW_COL_META_MAX, sizeof(lxw_format *));
  85. worksheet->col_formats_max = LXW_COL_META_MAX;
  86. GOTO_LABEL_ON_MEM_ERROR(worksheet->col_formats, mem_error);
  87. worksheet->optimize_row = calloc(1, sizeof(struct lxw_row));
  88. GOTO_LABEL_ON_MEM_ERROR(worksheet->optimize_row, mem_error);
  89. worksheet->optimize_row->height = LXW_DEF_ROW_HEIGHT;
  90. worksheet->merged_ranges = calloc(1, sizeof(struct lxw_merged_ranges));
  91. GOTO_LABEL_ON_MEM_ERROR(worksheet->merged_ranges, mem_error);
  92. STAILQ_INIT(worksheet->merged_ranges);
  93. worksheet->image_data = calloc(1, sizeof(struct lxw_image_data));
  94. GOTO_LABEL_ON_MEM_ERROR(worksheet->image_data, mem_error);
  95. STAILQ_INIT(worksheet->image_data);
  96. worksheet->chart_data = calloc(1, sizeof(struct lxw_chart_data));
  97. GOTO_LABEL_ON_MEM_ERROR(worksheet->chart_data, mem_error);
  98. STAILQ_INIT(worksheet->chart_data);
  99. worksheet->selections = calloc(1, sizeof(struct lxw_selections));
  100. GOTO_LABEL_ON_MEM_ERROR(worksheet->selections, mem_error);
  101. STAILQ_INIT(worksheet->selections);
  102. worksheet->data_validations =
  103. calloc(1, sizeof(struct lxw_data_validations));
  104. GOTO_LABEL_ON_MEM_ERROR(worksheet->data_validations, mem_error);
  105. STAILQ_INIT(worksheet->data_validations);
  106. worksheet->external_hyperlinks = calloc(1, sizeof(struct lxw_rel_tuples));
  107. GOTO_LABEL_ON_MEM_ERROR(worksheet->external_hyperlinks, mem_error);
  108. STAILQ_INIT(worksheet->external_hyperlinks);
  109. worksheet->external_drawing_links =
  110. calloc(1, sizeof(struct lxw_rel_tuples));
  111. GOTO_LABEL_ON_MEM_ERROR(worksheet->external_drawing_links, mem_error);
  112. STAILQ_INIT(worksheet->external_drawing_links);
  113. worksheet->drawing_links = calloc(1, sizeof(struct lxw_rel_tuples));
  114. GOTO_LABEL_ON_MEM_ERROR(worksheet->drawing_links, mem_error);
  115. STAILQ_INIT(worksheet->drawing_links);
  116. if (init_data && init_data->optimize) {
  117. FILE *tmpfile;
  118. tmpfile = lxw_tmpfile(init_data->tmpdir);
  119. if (!tmpfile) {
  120. LXW_ERROR("Error creating tmpfile() for worksheet in "
  121. "'constant_memory' mode.");
  122. goto mem_error;
  123. }
  124. worksheet->optimize_tmpfile = tmpfile;
  125. GOTO_LABEL_ON_MEM_ERROR(worksheet->optimize_tmpfile, mem_error);
  126. worksheet->file = worksheet->optimize_tmpfile;
  127. }
  128. /* Initialize the worksheet dimensions. */
  129. worksheet->dim_rowmax = 0;
  130. worksheet->dim_colmax = 0;
  131. worksheet->dim_rowmin = LXW_ROW_MAX;
  132. worksheet->dim_colmin = LXW_COL_MAX;
  133. worksheet->default_row_height = LXW_DEF_ROW_HEIGHT;
  134. worksheet->default_row_pixels = 20;
  135. worksheet->default_col_pixels = 64;
  136. /* Initialize the page setup properties. */
  137. worksheet->fit_height = 0;
  138. worksheet->fit_width = 0;
  139. worksheet->page_start = 0;
  140. worksheet->print_scale = 100;
  141. worksheet->fit_page = 0;
  142. worksheet->orientation = LXW_TRUE;
  143. worksheet->page_order = 0;
  144. worksheet->page_setup_changed = LXW_FALSE;
  145. worksheet->page_view = LXW_FALSE;
  146. worksheet->paper_size = 0;
  147. worksheet->vertical_dpi = 0;
  148. worksheet->horizontal_dpi = 0;
  149. worksheet->margin_left = 0.7;
  150. worksheet->margin_right = 0.7;
  151. worksheet->margin_top = 0.75;
  152. worksheet->margin_bottom = 0.75;
  153. worksheet->margin_header = 0.3;
  154. worksheet->margin_footer = 0.3;
  155. worksheet->print_gridlines = 0;
  156. worksheet->screen_gridlines = 1;
  157. worksheet->print_options_changed = 0;
  158. worksheet->zoom = 100;
  159. worksheet->zoom_scale_normal = LXW_TRUE;
  160. worksheet->show_zeros = LXW_TRUE;
  161. worksheet->outline_on = LXW_TRUE;
  162. worksheet->outline_style = LXW_TRUE;
  163. worksheet->outline_below = LXW_TRUE;
  164. worksheet->outline_right = LXW_FALSE;
  165. worksheet->tab_color = LXW_COLOR_UNSET;
  166. if (init_data) {
  167. worksheet->name = init_data->name;
  168. worksheet->quoted_name = init_data->quoted_name;
  169. worksheet->tmpdir = init_data->tmpdir;
  170. worksheet->index = init_data->index;
  171. worksheet->hidden = init_data->hidden;
  172. worksheet->sst = init_data->sst;
  173. worksheet->optimize = init_data->optimize;
  174. worksheet->active_sheet = init_data->active_sheet;
  175. worksheet->first_sheet = init_data->first_sheet;
  176. }
  177. return worksheet;
  178. mem_error:
  179. lxw_worksheet_free(worksheet);
  180. return NULL;
  181. }
  182. /*
  183. * Free a worksheet cell.
  184. */
  185. STATIC void
  186. _free_cell(lxw_cell *cell)
  187. {
  188. if (!cell)
  189. return;
  190. if (cell->type != NUMBER_CELL && cell->type != STRING_CELL
  191. && cell->type != BLANK_CELL && cell->type != BOOLEAN_CELL) {
  192. free(cell->u.string);
  193. }
  194. free(cell->user_data1);
  195. free(cell->user_data2);
  196. free(cell);
  197. }
  198. /*
  199. * Free a worksheet row.
  200. */
  201. STATIC void
  202. _free_row(lxw_row *row)
  203. {
  204. lxw_cell *cell;
  205. lxw_cell *next_cell;
  206. if (!row)
  207. return;
  208. for (cell = RB_MIN(lxw_table_cells, row->cells); cell; cell = next_cell) {
  209. next_cell = RB_NEXT(lxw_table_cells, row->cells, cell);
  210. RB_REMOVE(lxw_table_cells, row->cells, cell);
  211. _free_cell(cell);
  212. }
  213. free(row->cells);
  214. free(row);
  215. }
  216. /*
  217. * Free a worksheet image_options.
  218. */
  219. STATIC void
  220. _free_image_options(lxw_image_options *image)
  221. {
  222. if (!image)
  223. return;
  224. free(image->filename);
  225. free(image->short_name);
  226. free(image->extension);
  227. free(image->url);
  228. free(image->tip);
  229. free(image);
  230. }
  231. /*
  232. * Free a worksheet data_validation.
  233. */
  234. STATIC void
  235. _free_data_validation(lxw_data_validation *data_validation)
  236. {
  237. if (!data_validation)
  238. return;
  239. free(data_validation->value_formula);
  240. free(data_validation->maximum_formula);
  241. free(data_validation->input_title);
  242. free(data_validation->input_message);
  243. free(data_validation->error_title);
  244. free(data_validation->error_message);
  245. free(data_validation->minimum_formula);
  246. free(data_validation);
  247. }
  248. /*
  249. * Free a worksheet object.
  250. */
  251. void
  252. lxw_worksheet_free(lxw_worksheet *worksheet)
  253. {
  254. lxw_row *row;
  255. lxw_row *next_row;
  256. lxw_col_t col;
  257. lxw_merged_range *merged_range;
  258. lxw_image_options *image_options;
  259. lxw_selection *selection;
  260. lxw_data_validation *data_validation;
  261. lxw_rel_tuple *relationship;
  262. if (!worksheet)
  263. return;
  264. if (worksheet->col_options) {
  265. for (col = 0; col < worksheet->col_options_max; col++) {
  266. if (worksheet->col_options[col])
  267. free(worksheet->col_options[col]);
  268. }
  269. }
  270. free(worksheet->col_options);
  271. free(worksheet->col_sizes);
  272. free(worksheet->col_formats);
  273. if (worksheet->table) {
  274. for (row = RB_MIN(lxw_table_rows, worksheet->table); row;
  275. row = next_row) {
  276. next_row = RB_NEXT(lxw_table_rows, worksheet->table, row);
  277. RB_REMOVE(lxw_table_rows, worksheet->table, row);
  278. _free_row(row);
  279. }
  280. free(worksheet->table);
  281. }
  282. if (worksheet->hyperlinks) {
  283. for (row = RB_MIN(lxw_table_rows, worksheet->hyperlinks); row;
  284. row = next_row) {
  285. next_row = RB_NEXT(lxw_table_rows, worksheet->hyperlinks, row);
  286. RB_REMOVE(lxw_table_rows, worksheet->hyperlinks, row);
  287. _free_row(row);
  288. }
  289. free(worksheet->hyperlinks);
  290. }
  291. if (worksheet->merged_ranges) {
  292. while (!STAILQ_EMPTY(worksheet->merged_ranges)) {
  293. merged_range = STAILQ_FIRST(worksheet->merged_ranges);
  294. STAILQ_REMOVE_HEAD(worksheet->merged_ranges, list_pointers);
  295. free(merged_range);
  296. }
  297. free(worksheet->merged_ranges);
  298. }
  299. if (worksheet->image_data) {
  300. while (!STAILQ_EMPTY(worksheet->image_data)) {
  301. image_options = STAILQ_FIRST(worksheet->image_data);
  302. STAILQ_REMOVE_HEAD(worksheet->image_data, list_pointers);
  303. _free_image_options(image_options);
  304. }
  305. free(worksheet->image_data);
  306. }
  307. if (worksheet->chart_data) {
  308. while (!STAILQ_EMPTY(worksheet->chart_data)) {
  309. image_options = STAILQ_FIRST(worksheet->chart_data);
  310. STAILQ_REMOVE_HEAD(worksheet->chart_data, list_pointers);
  311. _free_image_options(image_options);
  312. }
  313. free(worksheet->chart_data);
  314. }
  315. if (worksheet->selections) {
  316. while (!STAILQ_EMPTY(worksheet->selections)) {
  317. selection = STAILQ_FIRST(worksheet->selections);
  318. STAILQ_REMOVE_HEAD(worksheet->selections, list_pointers);
  319. free(selection);
  320. }
  321. free(worksheet->selections);
  322. }
  323. if (worksheet->data_validations) {
  324. while (!STAILQ_EMPTY(worksheet->data_validations)) {
  325. data_validation = STAILQ_FIRST(worksheet->data_validations);
  326. STAILQ_REMOVE_HEAD(worksheet->data_validations, list_pointers);
  327. _free_data_validation(data_validation);
  328. }
  329. free(worksheet->data_validations);
  330. }
  331. /* TODO. Add function for freeing the relationship lists. */
  332. while (!STAILQ_EMPTY(worksheet->external_hyperlinks)) {
  333. relationship = STAILQ_FIRST(worksheet->external_hyperlinks);
  334. STAILQ_REMOVE_HEAD(worksheet->external_hyperlinks, list_pointers);
  335. free(relationship->type);
  336. free(relationship->target);
  337. free(relationship->target_mode);
  338. free(relationship);
  339. }
  340. free(worksheet->external_hyperlinks);
  341. while (!STAILQ_EMPTY(worksheet->external_drawing_links)) {
  342. relationship = STAILQ_FIRST(worksheet->external_drawing_links);
  343. STAILQ_REMOVE_HEAD(worksheet->external_drawing_links, list_pointers);
  344. free(relationship->type);
  345. free(relationship->target);
  346. free(relationship->target_mode);
  347. free(relationship);
  348. }
  349. free(worksheet->external_drawing_links);
  350. while (!STAILQ_EMPTY(worksheet->drawing_links)) {
  351. relationship = STAILQ_FIRST(worksheet->drawing_links);
  352. STAILQ_REMOVE_HEAD(worksheet->drawing_links, list_pointers);
  353. free(relationship->type);
  354. free(relationship->target);
  355. free(relationship->target_mode);
  356. free(relationship);
  357. }
  358. free(worksheet->drawing_links);
  359. if (worksheet->array) {
  360. for (col = 0; col < LXW_COL_MAX; col++) {
  361. _free_cell(worksheet->array[col]);
  362. }
  363. free(worksheet->array);
  364. }
  365. if (worksheet->optimize_row)
  366. free(worksheet->optimize_row);
  367. if (worksheet->drawing)
  368. lxw_drawing_free(worksheet->drawing);
  369. free(worksheet->hbreaks);
  370. free(worksheet->vbreaks);
  371. free(worksheet->name);
  372. free(worksheet->quoted_name);
  373. free(worksheet);
  374. worksheet = NULL;
  375. }
  376. /*
  377. * Create a new worksheet row object.
  378. */
  379. STATIC lxw_row *
  380. _new_row(lxw_row_t row_num)
  381. {
  382. lxw_row *row = calloc(1, sizeof(lxw_row));
  383. if (row) {
  384. row->row_num = row_num;
  385. row->cells = calloc(1, sizeof(struct lxw_table_cells));
  386. row->height = LXW_DEF_ROW_HEIGHT;
  387. if (row->cells)
  388. RB_INIT(row->cells);
  389. else
  390. LXW_MEM_ERROR();
  391. }
  392. else {
  393. LXW_MEM_ERROR();
  394. }
  395. return row;
  396. }
  397. /*
  398. * Create a new worksheet number cell object.
  399. */
  400. STATIC lxw_cell *
  401. _new_number_cell(lxw_row_t row_num,
  402. lxw_col_t col_num, double value, lxw_format *format)
  403. {
  404. lxw_cell *cell = calloc(1, sizeof(lxw_cell));
  405. RETURN_ON_MEM_ERROR(cell, cell);
  406. cell->row_num = row_num;
  407. cell->col_num = col_num;
  408. cell->type = NUMBER_CELL;
  409. cell->format = format;
  410. cell->u.number = value;
  411. return cell;
  412. }
  413. /*
  414. * Create a new worksheet string cell object.
  415. */
  416. STATIC lxw_cell *
  417. _new_string_cell(lxw_row_t row_num,
  418. lxw_col_t col_num, int32_t string_id, char *sst_string,
  419. lxw_format *format)
  420. {
  421. lxw_cell *cell = calloc(1, sizeof(lxw_cell));
  422. RETURN_ON_MEM_ERROR(cell, cell);
  423. cell->row_num = row_num;
  424. cell->col_num = col_num;
  425. cell->type = STRING_CELL;
  426. cell->format = format;
  427. cell->u.string_id = string_id;
  428. cell->sst_string = sst_string;
  429. return cell;
  430. }
  431. /*
  432. * Create a new worksheet inline_string cell object.
  433. */
  434. STATIC lxw_cell *
  435. _new_inline_string_cell(lxw_row_t row_num,
  436. lxw_col_t col_num, char *string, lxw_format *format)
  437. {
  438. lxw_cell *cell = calloc(1, sizeof(lxw_cell));
  439. RETURN_ON_MEM_ERROR(cell, cell);
  440. cell->row_num = row_num;
  441. cell->col_num = col_num;
  442. cell->type = INLINE_STRING_CELL;
  443. cell->format = format;
  444. cell->u.string = string;
  445. return cell;
  446. }
  447. /*
  448. * Create a new worksheet formula cell object.
  449. */
  450. STATIC lxw_cell *
  451. _new_formula_cell(lxw_row_t row_num,
  452. lxw_col_t col_num, char *formula, lxw_format *format)
  453. {
  454. lxw_cell *cell = calloc(1, sizeof(lxw_cell));
  455. RETURN_ON_MEM_ERROR(cell, cell);
  456. cell->row_num = row_num;
  457. cell->col_num = col_num;
  458. cell->type = FORMULA_CELL;
  459. cell->format = format;
  460. cell->u.string = formula;
  461. return cell;
  462. }
  463. /*
  464. * Create a new worksheet array formula cell object.
  465. */
  466. STATIC lxw_cell *
  467. _new_array_formula_cell(lxw_row_t row_num, lxw_col_t col_num, char *formula,
  468. char *range, lxw_format *format)
  469. {
  470. lxw_cell *cell = calloc(1, sizeof(lxw_cell));
  471. RETURN_ON_MEM_ERROR(cell, cell);
  472. cell->row_num = row_num;
  473. cell->col_num = col_num;
  474. cell->type = ARRAY_FORMULA_CELL;
  475. cell->format = format;
  476. cell->u.string = formula;
  477. cell->user_data1 = range;
  478. return cell;
  479. }
  480. /*
  481. * Create a new worksheet blank cell object.
  482. */
  483. STATIC lxw_cell *
  484. _new_blank_cell(lxw_row_t row_num, lxw_col_t col_num, lxw_format *format)
  485. {
  486. lxw_cell *cell = calloc(1, sizeof(lxw_cell));
  487. RETURN_ON_MEM_ERROR(cell, cell);
  488. cell->row_num = row_num;
  489. cell->col_num = col_num;
  490. cell->type = BLANK_CELL;
  491. cell->format = format;
  492. return cell;
  493. }
  494. /*
  495. * Create a new worksheet boolean cell object.
  496. */
  497. STATIC lxw_cell *
  498. _new_boolean_cell(lxw_row_t row_num, lxw_col_t col_num, int value,
  499. lxw_format *format)
  500. {
  501. lxw_cell *cell = calloc(1, sizeof(lxw_cell));
  502. RETURN_ON_MEM_ERROR(cell, cell);
  503. cell->row_num = row_num;
  504. cell->col_num = col_num;
  505. cell->type = BOOLEAN_CELL;
  506. cell->format = format;
  507. cell->u.number = value;
  508. return cell;
  509. }
  510. /*
  511. * Create a new worksheet hyperlink cell object.
  512. */
  513. STATIC lxw_cell *
  514. _new_hyperlink_cell(lxw_row_t row_num, lxw_col_t col_num,
  515. enum cell_types link_type, char *url, char *string,
  516. char *tooltip)
  517. {
  518. lxw_cell *cell = calloc(1, sizeof(lxw_cell));
  519. RETURN_ON_MEM_ERROR(cell, cell);
  520. cell->row_num = row_num;
  521. cell->col_num = col_num;
  522. cell->type = link_type;
  523. cell->u.string = url;
  524. cell->user_data1 = string;
  525. cell->user_data2 = tooltip;
  526. return cell;
  527. }
  528. /*
  529. * Get or create the row object for a given row number.
  530. */
  531. STATIC lxw_row *
  532. _get_row_list(struct lxw_table_rows *table, lxw_row_t row_num)
  533. {
  534. lxw_row *row;
  535. lxw_row *existing_row;
  536. if (table->cached_row_num == row_num)
  537. return table->cached_row;
  538. /* Create a new row and try and insert it. */
  539. row = _new_row(row_num);
  540. existing_row = RB_INSERT(lxw_table_rows, table, row);
  541. /* If existing_row is not NULL, then it already existed. Free new row */
  542. /* and return existing_row. */
  543. if (existing_row) {
  544. _free_row(row);
  545. row = existing_row;
  546. }
  547. table->cached_row = row;
  548. table->cached_row_num = row_num;
  549. return row;
  550. }
  551. /*
  552. * Get or create the row object for a given row number.
  553. */
  554. STATIC lxw_row *
  555. _get_row(lxw_worksheet *self, lxw_row_t row_num)
  556. {
  557. lxw_row *row;
  558. if (!self->optimize) {
  559. row = _get_row_list(self->table, row_num);
  560. return row;
  561. }
  562. else {
  563. if (row_num < self->optimize_row->row_num) {
  564. return NULL;
  565. }
  566. else if (row_num == self->optimize_row->row_num) {
  567. return self->optimize_row;
  568. }
  569. else {
  570. /* Flush row. */
  571. lxw_worksheet_write_single_row(self);
  572. row = self->optimize_row;
  573. row->row_num = row_num;
  574. return row;
  575. }
  576. }
  577. }
  578. /*
  579. * Insert a cell object in the cell list of a row object.
  580. */
  581. STATIC void
  582. _insert_cell_list(struct lxw_table_cells *cell_list,
  583. lxw_cell *cell, lxw_col_t col_num)
  584. {
  585. lxw_cell *existing_cell;
  586. cell->col_num = col_num;
  587. existing_cell = RB_INSERT(lxw_table_cells, cell_list, cell);
  588. /* If existing_cell is not NULL, then that cell already existed. */
  589. /* Remove existing_cell and add new one in again. */
  590. if (existing_cell) {
  591. RB_REMOVE(lxw_table_cells, cell_list, existing_cell);
  592. /* Add it in again. */
  593. RB_INSERT(lxw_table_cells, cell_list, cell);
  594. _free_cell(existing_cell);
  595. }
  596. return;
  597. }
  598. /*
  599. * Insert a cell object into the cell list or array.
  600. */
  601. STATIC void
  602. _insert_cell(lxw_worksheet *self, lxw_row_t row_num, lxw_col_t col_num,
  603. lxw_cell *cell)
  604. {
  605. lxw_row *row = _get_row(self, row_num);
  606. if (!self->optimize) {
  607. row->data_changed = LXW_TRUE;
  608. _insert_cell_list(row->cells, cell, col_num);
  609. }
  610. else {
  611. if (row) {
  612. row->data_changed = LXW_TRUE;
  613. /* Overwrite an existing cell if necessary. */
  614. if (self->array[col_num])
  615. _free_cell(self->array[col_num]);
  616. self->array[col_num] = cell;
  617. }
  618. }
  619. }
  620. /*
  621. * Insert a hyperlink object into the hyperlink list.
  622. */
  623. STATIC void
  624. _insert_hyperlink(lxw_worksheet *self, lxw_row_t row_num, lxw_col_t col_num,
  625. lxw_cell *link)
  626. {
  627. lxw_row *row = _get_row_list(self->hyperlinks, row_num);
  628. _insert_cell_list(row->cells, link, col_num);
  629. }
  630. /*
  631. * Next power of two for column reallocs. Taken from bithacks in the public
  632. * domain.
  633. */
  634. STATIC lxw_col_t
  635. _next_power_of_two(uint16_t col)
  636. {
  637. col--;
  638. col |= col >> 1;
  639. col |= col >> 2;
  640. col |= col >> 4;
  641. col |= col >> 8;
  642. col++;
  643. return col;
  644. }
  645. /*
  646. * Check that row and col are within the allowed Excel range and store max
  647. * and min values for use in other methods/elements.
  648. *
  649. * The ignore_row/ignore_col flags are used to indicate that we wish to
  650. * perform the dimension check without storing the value.
  651. */
  652. STATIC lxw_error
  653. _check_dimensions(lxw_worksheet *self,
  654. lxw_row_t row_num,
  655. lxw_col_t col_num, int8_t ignore_row, int8_t ignore_col)
  656. {
  657. if (row_num >= LXW_ROW_MAX)
  658. return LXW_ERROR_WORKSHEET_INDEX_OUT_OF_RANGE;
  659. if (col_num >= LXW_COL_MAX)
  660. return LXW_ERROR_WORKSHEET_INDEX_OUT_OF_RANGE;
  661. /* In optimization mode we don't change dimensions for rows that are */
  662. /* already written. */
  663. if (!ignore_row && !ignore_col && self->optimize) {
  664. if (row_num < self->optimize_row->row_num)
  665. return LXW_ERROR_WORKSHEET_INDEX_OUT_OF_RANGE;
  666. }
  667. if (!ignore_row) {
  668. if (row_num < self->dim_rowmin)
  669. self->dim_rowmin = row_num;
  670. if (row_num > self->dim_rowmax)
  671. self->dim_rowmax = row_num;
  672. }
  673. if (!ignore_col) {
  674. if (col_num < self->dim_colmin)
  675. self->dim_colmin = col_num;
  676. if (col_num > self->dim_colmax)
  677. self->dim_colmax = col_num;
  678. }
  679. return LXW_NO_ERROR;
  680. }
  681. /*
  682. * Comparator for the row structure red/black tree.
  683. */
  684. STATIC int
  685. _row_cmp(lxw_row *row1, lxw_row *row2)
  686. {
  687. if (row1->row_num > row2->row_num)
  688. return 1;
  689. if (row1->row_num < row2->row_num)
  690. return -1;
  691. return 0;
  692. }
  693. /*
  694. * Comparator for the cell structure red/black tree.
  695. */
  696. STATIC int
  697. _cell_cmp(lxw_cell *cell1, lxw_cell *cell2)
  698. {
  699. if (cell1->col_num > cell2->col_num)
  700. return 1;
  701. if (cell1->col_num < cell2->col_num)
  702. return -1;
  703. return 0;
  704. }
  705. /*
  706. * Hash a worksheet password. Based on the algorithm provided by Daniel Rentz
  707. * of OpenOffice.
  708. */
  709. STATIC uint16_t
  710. _hash_password(const char *password)
  711. {
  712. size_t count;
  713. uint8_t i;
  714. uint16_t hash = 0x0000;
  715. count = strlen(password);
  716. for (i = 0; i < count; i++) {
  717. uint32_t low_15;
  718. uint32_t high_15;
  719. uint32_t letter = password[i] << (i + 1);
  720. low_15 = letter & 0x7fff;
  721. high_15 = letter & (0x7fff << 15);
  722. high_15 = high_15 >> 15;
  723. letter = low_15 | high_15;
  724. hash ^= letter;
  725. }
  726. hash ^= count;
  727. hash ^= 0xCE4B;
  728. return hash;
  729. }
  730. /*
  731. * Simple replacement for libgen.h basename() for compatibility with MSVC. It
  732. * handles forward and back slashes. It doesn't copy exactly the return
  733. * format of basename().
  734. */
  735. char *
  736. lxw_basename(const char *path)
  737. {
  738. char *forward_slash;
  739. char *back_slash;
  740. if (!path)
  741. return NULL;
  742. forward_slash = strrchr(path, '/');
  743. back_slash = strrchr(path, '\\');
  744. if (!forward_slash && !back_slash)
  745. return (char *) path;
  746. if (forward_slash > back_slash)
  747. return forward_slash + 1;
  748. else
  749. return back_slash + 1;
  750. }
  751. /* Function to count the total concatenated length of the strings in a
  752. * validation list array, including commas. */
  753. size_t
  754. _validation_list_length(char **list)
  755. {
  756. uint8_t i = 0;
  757. size_t length = 0;
  758. if (!list || !list[0])
  759. return 0;
  760. while (list[i] && length <= LXW_VALIDATION_MAX_STRING_LENGTH) {
  761. /* Include commas in the length. */
  762. length += 1 + lxw_utf8_strlen(list[i]);
  763. i++;
  764. }
  765. /* Adjust the count for extraneous comma at end. */
  766. length--;
  767. return length;
  768. }
  769. /* Function to convert an array of strings into a CSV string for data
  770. * validation lists. */
  771. char *
  772. _validation_list_to_csv(char **list)
  773. {
  774. uint8_t i = 0;
  775. char *str;
  776. /* Create a buffer for the concatenated, and quoted, string. */
  777. /* Add +3 for quotes and EOL. */
  778. str = calloc(1, LXW_VALIDATION_MAX_STRING_LENGTH + 3);
  779. if (!str)
  780. return NULL;
  781. /* Add the start quote and first element. */
  782. strcat(str, "\"");
  783. strcat(str, list[0]);
  784. /* Add the other elements preceded by a comma. */
  785. i = 1;
  786. while (list[i]) {
  787. strcat(str, ",");
  788. strcat(str, list[i]);
  789. i++;
  790. }
  791. /* Add the end quote. */
  792. strcat(str, "\"");
  793. return str;
  794. }
  795. /*****************************************************************************
  796. *
  797. * XML functions.
  798. *
  799. ****************************************************************************/
  800. /*
  801. * Write the XML declaration.
  802. */
  803. STATIC void
  804. _worksheet_xml_declaration(lxw_worksheet *self)
  805. {
  806. lxw_xml_declaration(self->file);
  807. }
  808. /*
  809. * Write the <worksheet> element.
  810. */
  811. STATIC void
  812. _worksheet_write_worksheet(lxw_worksheet *self)
  813. {
  814. struct xml_attribute_list attributes;
  815. struct xml_attribute *attribute;
  816. char xmlns[] = "http://schemas.openxmlformats.org/"
  817. "spreadsheetml/2006/main";
  818. char xmlns_r[] = "http://schemas.openxmlformats.org/"
  819. "officeDocument/2006/relationships";
  820. LXW_INIT_ATTRIBUTES();
  821. LXW_PUSH_ATTRIBUTES_STR("xmlns", xmlns);
  822. LXW_PUSH_ATTRIBUTES_STR("xmlns:r", xmlns_r);
  823. lxw_xml_start_tag(self->file, "worksheet", &attributes);
  824. LXW_FREE_ATTRIBUTES();
  825. }
  826. /*
  827. * Write the <dimension> element.
  828. */
  829. STATIC void
  830. _worksheet_write_dimension(lxw_worksheet *self)
  831. {
  832. struct xml_attribute_list attributes;
  833. struct xml_attribute *attribute;
  834. char ref[LXW_MAX_CELL_RANGE_LENGTH];
  835. lxw_row_t dim_rowmin = self->dim_rowmin;
  836. lxw_row_t dim_rowmax = self->dim_rowmax;
  837. lxw_col_t dim_colmin = self->dim_colmin;
  838. lxw_col_t dim_colmax = self->dim_colmax;
  839. if (dim_rowmin == LXW_ROW_MAX && dim_colmin == LXW_COL_MAX) {
  840. /* If the rows and cols are still the defaults then no dimensions have
  841. * been set and we use the default range "A1". */
  842. lxw_rowcol_to_range(ref, 0, 0, 0, 0);
  843. }
  844. else if (dim_rowmin == LXW_ROW_MAX && dim_colmin != LXW_COL_MAX) {
  845. /* If the rows aren't set but the columns are then the dimensions have
  846. * been changed via set_column(). */
  847. lxw_rowcol_to_range(ref, 0, dim_colmin, 0, dim_colmax);
  848. }
  849. else {
  850. lxw_rowcol_to_range(ref, dim_rowmin, dim_colmin, dim_rowmax,
  851. dim_colmax);
  852. }
  853. LXW_INIT_ATTRIBUTES();
  854. LXW_PUSH_ATTRIBUTES_STR("ref", ref);
  855. lxw_xml_empty_tag(self->file, "dimension", &attributes);
  856. LXW_FREE_ATTRIBUTES();
  857. }
  858. /*
  859. * Write the <pane> element for freeze panes.
  860. */
  861. STATIC void
  862. _worksheet_write_freeze_panes(lxw_worksheet *self)
  863. {
  864. struct xml_attribute_list attributes;
  865. struct xml_attribute *attribute;
  866. lxw_selection *selection;
  867. lxw_selection *user_selection;
  868. lxw_row_t row = self->panes.first_row;
  869. lxw_col_t col = self->panes.first_col;
  870. lxw_row_t top_row = self->panes.top_row;
  871. lxw_col_t left_col = self->panes.left_col;
  872. char row_cell[LXW_MAX_CELL_NAME_LENGTH];
  873. char col_cell[LXW_MAX_CELL_NAME_LENGTH];
  874. char top_left_cell[LXW_MAX_CELL_NAME_LENGTH];
  875. char active_pane[LXW_PANE_NAME_LENGTH];
  876. /* If there is a user selection we remove it from the list and use it. */
  877. if (!STAILQ_EMPTY(self->selections)) {
  878. user_selection = STAILQ_FIRST(self->selections);
  879. STAILQ_REMOVE_HEAD(self->selections, list_pointers);
  880. }
  881. else {
  882. /* or else create a new blank selection. */
  883. user_selection = calloc(1, sizeof(lxw_selection));
  884. RETURN_VOID_ON_MEM_ERROR(user_selection);
  885. }
  886. LXW_INIT_ATTRIBUTES();
  887. lxw_rowcol_to_cell(top_left_cell, top_row, left_col);
  888. /* Set the active pane. */
  889. if (row && col) {
  890. lxw_strcpy(active_pane, "bottomRight");
  891. lxw_rowcol_to_cell(row_cell, row, 0);
  892. lxw_rowcol_to_cell(col_cell, 0, col);
  893. selection = calloc(1, sizeof(lxw_selection));
  894. if (selection) {
  895. lxw_strcpy(selection->pane, "topRight");
  896. lxw_strcpy(selection->active_cell, col_cell);
  897. lxw_strcpy(selection->sqref, col_cell);
  898. STAILQ_INSERT_TAIL(self->selections, selection, list_pointers);
  899. }
  900. selection = calloc(1, sizeof(lxw_selection));
  901. if (selection) {
  902. lxw_strcpy(selection->pane, "bottomLeft");
  903. lxw_strcpy(selection->active_cell, row_cell);
  904. lxw_strcpy(selection->sqref, row_cell);
  905. STAILQ_INSERT_TAIL(self->selections, selection, list_pointers);
  906. }
  907. selection = calloc(1, sizeof(lxw_selection));
  908. if (selection) {
  909. lxw_strcpy(selection->pane, "bottomRight");
  910. lxw_strcpy(selection->active_cell, user_selection->active_cell);
  911. lxw_strcpy(selection->sqref, user_selection->sqref);
  912. STAILQ_INSERT_TAIL(self->selections, selection, list_pointers);
  913. }
  914. }
  915. else if (col) {
  916. lxw_strcpy(active_pane, "topRight");
  917. selection = calloc(1, sizeof(lxw_selection));
  918. if (selection) {
  919. lxw_strcpy(selection->pane, "topRight");
  920. lxw_strcpy(selection->active_cell, user_selection->active_cell);
  921. lxw_strcpy(selection->sqref, user_selection->sqref);
  922. STAILQ_INSERT_TAIL(self->selections, selection, list_pointers);
  923. }
  924. }
  925. else {
  926. lxw_strcpy(active_pane, "bottomLeft");
  927. selection = calloc(1, sizeof(lxw_selection));
  928. if (selection) {
  929. lxw_strcpy(selection->pane, "bottomLeft");
  930. lxw_strcpy(selection->active_cell, user_selection->active_cell);
  931. lxw_strcpy(selection->sqref, user_selection->sqref);
  932. STAILQ_INSERT_TAIL(self->selections, selection, list_pointers);
  933. }
  934. }
  935. if (col)
  936. LXW_PUSH_ATTRIBUTES_INT("xSplit", col);
  937. if (row)
  938. LXW_PUSH_ATTRIBUTES_INT("ySplit", row);
  939. LXW_PUSH_ATTRIBUTES_STR("topLeftCell", top_left_cell);
  940. LXW_PUSH_ATTRIBUTES_STR("activePane", active_pane);
  941. if (self->panes.type == FREEZE_PANES)
  942. LXW_PUSH_ATTRIBUTES_STR("state", "frozen");
  943. else if (self->panes.type == FREEZE_SPLIT_PANES)
  944. LXW_PUSH_ATTRIBUTES_STR("state", "frozenSplit");
  945. lxw_xml_empty_tag(self->file, "pane", &attributes);
  946. free(user_selection);
  947. LXW_FREE_ATTRIBUTES();
  948. }
  949. /*
  950. * Convert column width from user units to pane split width.
  951. */
  952. STATIC uint32_t
  953. _worksheet_calculate_x_split_width(double x_split)
  954. {
  955. uint32_t width;
  956. uint32_t pixels;
  957. uint32_t points;
  958. uint32_t twips;
  959. double max_digit_width = 7.0; /* For Calabri 11. */
  960. double padding = 5.0;
  961. /* Convert to pixels. */
  962. if (x_split < 1.0) {
  963. pixels = (uint32_t) (x_split * (max_digit_width + padding) + 0.5);
  964. }
  965. else {
  966. pixels = (uint32_t) (x_split * max_digit_width + 0.5) + 5;
  967. }
  968. /* Convert to points. */
  969. points = (pixels * 3) / 4;
  970. /* Convert to twips (twentieths of a point). */
  971. twips = points * 20;
  972. /* Add offset/padding. */
  973. width = twips + 390;
  974. return width;
  975. }
  976. /*
  977. * Write the <pane> element for split panes.
  978. */
  979. STATIC void
  980. _worksheet_write_split_panes(lxw_worksheet *self)
  981. {
  982. struct xml_attribute_list attributes;
  983. struct xml_attribute *attribute;
  984. lxw_selection *selection;
  985. lxw_selection *user_selection;
  986. lxw_row_t row = self->panes.first_row;
  987. lxw_col_t col = self->panes.first_col;
  988. lxw_row_t top_row = self->panes.top_row;
  989. lxw_col_t left_col = self->panes.left_col;
  990. double x_split = self->panes.x_split;
  991. double y_split = self->panes.y_split;
  992. uint8_t has_selection = LXW_FALSE;
  993. char row_cell[LXW_MAX_CELL_NAME_LENGTH];
  994. char col_cell[LXW_MAX_CELL_NAME_LENGTH];
  995. char top_left_cell[LXW_MAX_CELL_NAME_LENGTH];
  996. char active_pane[LXW_PANE_NAME_LENGTH];
  997. /* If there is a user selection we remove it from the list and use it. */
  998. if (!STAILQ_EMPTY(self->selections)) {
  999. user_selection = STAILQ_FIRST(self->selections);
  1000. STAILQ_REMOVE_HEAD(self->selections, list_pointers);
  1001. has_selection = LXW_TRUE;
  1002. }
  1003. else {
  1004. /* or else create a new blank selection. */
  1005. user_selection = calloc(1, sizeof(lxw_selection));
  1006. RETURN_VOID_ON_MEM_ERROR(user_selection);
  1007. }
  1008. LXW_INIT_ATTRIBUTES();
  1009. /* Convert the row and col to 1/20 twip units with padding. */
  1010. if (y_split > 0.0)
  1011. y_split = (uint32_t) (20 * y_split + 300);
  1012. if (x_split > 0.0)
  1013. x_split = _worksheet_calculate_x_split_width(x_split);
  1014. /* For non-explicit topLeft definitions, estimate the cell offset based on
  1015. * the pixels dimensions. This is only a workaround and doesn't take
  1016. * adjusted cell dimensions into account.
  1017. */
  1018. if (top_row == row && left_col == col) {
  1019. top_row = (lxw_row_t) (0.5 + (y_split - 300.0) / 20.0 / 15.0);
  1020. left_col = (lxw_col_t) (0.5 + (x_split - 390.0) / 20.0 / 3.0 / 16.0);
  1021. }
  1022. lxw_rowcol_to_cell(top_left_cell, top_row, left_col);
  1023. /* If there is no selection set the active cell to the top left cell. */
  1024. if (!has_selection) {
  1025. lxw_strcpy(user_selection->active_cell, top_left_cell);
  1026. lxw_strcpy(user_selection->sqref, top_left_cell);
  1027. }
  1028. /* Set the active pane. */
  1029. if (y_split > 0.0 && x_split > 0.0) {
  1030. lxw_strcpy(active_pane, "bottomRight");
  1031. lxw_rowcol_to_cell(row_cell, top_row, 0);
  1032. lxw_rowcol_to_cell(col_cell, 0, left_col);
  1033. selection = calloc(1, sizeof(lxw_selection));
  1034. if (selection) {
  1035. lxw_strcpy(selection->pane, "topRight");
  1036. lxw_strcpy(selection->active_cell, col_cell);
  1037. lxw_strcpy(selection->sqref, col_cell);
  1038. STAILQ_INSERT_TAIL(self->selections, selection, list_pointers);
  1039. }
  1040. selection = calloc(1, sizeof(lxw_selection));
  1041. if (selection) {
  1042. lxw_strcpy(selection->pane, "bottomLeft");
  1043. lxw_strcpy(selection->active_cell, row_cell);
  1044. lxw_strcpy(selection->sqref, row_cell);
  1045. STAILQ_INSERT_TAIL(self->selections, selection, list_pointers);
  1046. }
  1047. selection = calloc(1, sizeof(lxw_selection));
  1048. if (selection) {
  1049. lxw_strcpy(selection->pane, "bottomRight");
  1050. lxw_strcpy(selection->active_cell, user_selection->active_cell);
  1051. lxw_strcpy(selection->sqref, user_selection->sqref);
  1052. STAILQ_INSERT_TAIL(self->selections, selection, list_pointers);
  1053. }
  1054. }
  1055. else if (x_split > 0.0) {
  1056. lxw_strcpy(active_pane, "topRight");
  1057. selection = calloc(1, sizeof(lxw_selection));
  1058. if (selection) {
  1059. lxw_strcpy(selection->pane, "topRight");
  1060. lxw_strcpy(selection->active_cell, user_selection->active_cell);
  1061. lxw_strcpy(selection->sqref, user_selection->sqref);
  1062. STAILQ_INSERT_TAIL(self->selections, selection, list_pointers);
  1063. }
  1064. }
  1065. else {
  1066. lxw_strcpy(active_pane, "bottomLeft");
  1067. selection = calloc(1, sizeof(lxw_selection));
  1068. if (selection) {
  1069. lxw_strcpy(selection->pane, "bottomLeft");
  1070. lxw_strcpy(selection->active_cell, user_selection->active_cell);
  1071. lxw_strcpy(selection->sqref, user_selection->sqref);
  1072. STAILQ_INSERT_TAIL(self->selections, selection, list_pointers);
  1073. }
  1074. }
  1075. if (x_split > 0.0)
  1076. LXW_PUSH_ATTRIBUTES_DBL("xSplit", x_split);
  1077. if (y_split > 0.0)
  1078. LXW_PUSH_ATTRIBUTES_DBL("ySplit", y_split);
  1079. LXW_PUSH_ATTRIBUTES_STR("topLeftCell", top_left_cell);
  1080. if (has_selection)
  1081. LXW_PUSH_ATTRIBUTES_STR("activePane", active_pane);
  1082. lxw_xml_empty_tag(self->file, "pane", &attributes);
  1083. free(user_selection);
  1084. LXW_FREE_ATTRIBUTES();
  1085. }
  1086. /*
  1087. * Write the <selection> element.
  1088. */
  1089. STATIC void
  1090. _worksheet_write_selection(lxw_worksheet *self, lxw_selection *selection)
  1091. {
  1092. struct xml_attribute_list attributes;
  1093. struct xml_attribute *attribute;
  1094. LXW_INIT_ATTRIBUTES();
  1095. if (*selection->pane)
  1096. LXW_PUSH_ATTRIBUTES_STR("pane", selection->pane);
  1097. if (*selection->active_cell)
  1098. LXW_PUSH_ATTRIBUTES_STR("activeCell", selection->active_cell);
  1099. if (*selection->sqref)
  1100. LXW_PUSH_ATTRIBUTES_STR("sqref", selection->sqref);
  1101. lxw_xml_empty_tag(self->file, "selection", &attributes);
  1102. LXW_FREE_ATTRIBUTES();
  1103. }
  1104. /*
  1105. * Write the <selection> elements.
  1106. */
  1107. STATIC void
  1108. _worksheet_write_selections(lxw_worksheet *self)
  1109. {
  1110. lxw_selection *selection;
  1111. STAILQ_FOREACH(selection, self->selections, list_pointers) {
  1112. _worksheet_write_selection(self, selection);
  1113. }
  1114. }
  1115. /*
  1116. * Write the frozen or split <pane> elements.
  1117. */
  1118. STATIC void
  1119. _worksheet_write_panes(lxw_worksheet *self)
  1120. {
  1121. if (self->panes.type == NO_PANES)
  1122. return;
  1123. else if (self->panes.type == FREEZE_PANES)
  1124. _worksheet_write_freeze_panes(self);
  1125. else if (self->panes.type == FREEZE_SPLIT_PANES)
  1126. _worksheet_write_freeze_panes(self);
  1127. else if (self->panes.type == SPLIT_PANES)
  1128. _worksheet_write_split_panes(self);
  1129. }
  1130. /*
  1131. * Write the <sheetView> element.
  1132. */
  1133. STATIC void
  1134. _worksheet_write_sheet_view(lxw_worksheet *self)
  1135. {
  1136. struct xml_attribute_list attributes;
  1137. struct xml_attribute *attribute;
  1138. LXW_INIT_ATTRIBUTES();
  1139. /* Hide screen gridlines if required */
  1140. if (!self->screen_gridlines)
  1141. LXW_PUSH_ATTRIBUTES_STR("showGridLines", "0");
  1142. /* Hide zeroes in cells. */
  1143. if (!self->show_zeros) {
  1144. LXW_PUSH_ATTRIBUTES_STR("showZeros", "0");
  1145. }
  1146. /* Display worksheet right to left for Hebrew, Arabic and others. */
  1147. if (self->right_to_left) {
  1148. LXW_PUSH_ATTRIBUTES_STR("rightToLeft", "1");
  1149. }
  1150. /* Show that the sheet tab is selected. */
  1151. if (self->selected)
  1152. LXW_PUSH_ATTRIBUTES_STR("tabSelected", "1");
  1153. /* Turn outlines off. Also required in the outlinePr element. */
  1154. if (!self->outline_on) {
  1155. LXW_PUSH_ATTRIBUTES_STR("showOutlineSymbols", "0");
  1156. }
  1157. /* Set the page view/layout mode if required. */
  1158. if (self->page_view)
  1159. LXW_PUSH_ATTRIBUTES_STR("view", "pageLayout");
  1160. /* Set the zoom level. */
  1161. if (self->zoom != 100) {
  1162. if (!self->page_view) {
  1163. LXW_PUSH_ATTRIBUTES_INT("zoomScale", self->zoom);
  1164. if (self->zoom_scale_normal)
  1165. LXW_PUSH_ATTRIBUTES_INT("zoomScaleNormal", self->zoom);
  1166. }
  1167. }
  1168. LXW_PUSH_ATTRIBUTES_STR("workbookViewId", "0");
  1169. if (self->panes.type != NO_PANES || !STAILQ_EMPTY(self->selections)) {
  1170. lxw_xml_start_tag(self->file, "sheetView", &attributes);
  1171. _worksheet_write_panes(self);
  1172. _worksheet_write_selections(self);
  1173. lxw_xml_end_tag(self->file, "sheetView");
  1174. }
  1175. else {
  1176. lxw_xml_empty_tag(self->file, "sheetView", &attributes);
  1177. }
  1178. LXW_FREE_ATTRIBUTES();
  1179. }
  1180. /*
  1181. * Write the <sheetViews> element.
  1182. */
  1183. STATIC void
  1184. _worksheet_write_sheet_views(lxw_worksheet *self)
  1185. {
  1186. lxw_xml_start_tag(self->file, "sheetViews", NULL);
  1187. /* Write the sheetView element. */
  1188. _worksheet_write_sheet_view(self);
  1189. lxw_xml_end_tag(self->file, "sheetViews");
  1190. }
  1191. /*
  1192. * Write the <sheetFormatPr> element.
  1193. */
  1194. STATIC void
  1195. _worksheet_write_sheet_format_pr(lxw_worksheet *self)
  1196. {
  1197. struct xml_attribute_list attributes;
  1198. struct xml_attribute *attribute;
  1199. LXW_INIT_ATTRIBUTES();
  1200. LXW_PUSH_ATTRIBUTES_DBL("defaultRowHeight", self->default_row_height);
  1201. if (self->default_row_height != LXW_DEF_ROW_HEIGHT)
  1202. LXW_PUSH_ATTRIBUTES_STR("customHeight", "1");
  1203. if (self->default_row_zeroed)
  1204. LXW_PUSH_ATTRIBUTES_STR("zeroHeight", "1");
  1205. if (self->outline_row_level)
  1206. LXW_PUSH_ATTRIBUTES_INT("outlineLevelRow", self->outline_row_level);
  1207. if (self->outline_col_level)
  1208. LXW_PUSH_ATTRIBUTES_INT("outlineLevelCol", self->outline_col_level);
  1209. lxw_xml_empty_tag(self->file, "sheetFormatPr", &attributes);
  1210. LXW_FREE_ATTRIBUTES();
  1211. }
  1212. /*
  1213. * Write the <sheetData> element.
  1214. */
  1215. STATIC void
  1216. _worksheet_write_sheet_data(lxw_worksheet *self)
  1217. {
  1218. if (RB_EMPTY(self->table)) {
  1219. lxw_xml_empty_tag(self->file, "sheetData", NULL);
  1220. }
  1221. else {
  1222. lxw_xml_start_tag(self->file, "sheetData", NULL);
  1223. _worksheet_write_rows(self);
  1224. lxw_xml_end_tag(self->file, "sheetData");
  1225. }
  1226. }
  1227. /*
  1228. * Write the <sheetData> element when the memory optimization is on. In which
  1229. * case we read the data stored in the temp file and rewrite it to the XML
  1230. * sheet file.
  1231. */
  1232. STATIC void
  1233. _worksheet_write_optimized_sheet_data(lxw_worksheet *self)
  1234. {
  1235. size_t read_size = 1;
  1236. char buffer[LXW_BUFFER_SIZE];
  1237. if (self->dim_rowmin == LXW_ROW_MAX) {
  1238. /* If the dimensions aren't defined then there is no data to write. */
  1239. lxw_xml_empty_tag(self->file, "sheetData", NULL);
  1240. }
  1241. else {
  1242. lxw_xml_start_tag(self->file, "sheetData", NULL);
  1243. /* Flush and rewind the temp file. */
  1244. fflush(self->optimize_tmpfile);
  1245. rewind(self->optimize_tmpfile);
  1246. while (read_size) {
  1247. read_size =
  1248. fread(buffer, 1, LXW_BUFFER_SIZE, self->optimize_tmpfile);
  1249. fwrite(buffer, 1, read_size, self->file);
  1250. }
  1251. fclose(self->optimize_tmpfile);
  1252. lxw_xml_end_tag(self->file, "sheetData");
  1253. }
  1254. }
  1255. /*
  1256. * Write the <pageMargins> element.
  1257. */
  1258. STATIC void
  1259. _worksheet_write_page_margins(lxw_worksheet *self)
  1260. {
  1261. struct xml_attribute_list attributes;
  1262. struct xml_attribute *attribute;
  1263. double left = self->margin_left;
  1264. double right = self->margin_right;
  1265. double top = self->margin_top;
  1266. double bottom = self->margin_bottom;
  1267. double header = self->margin_header;
  1268. double footer = self->margin_footer;
  1269. LXW_INIT_ATTRIBUTES();
  1270. LXW_PUSH_ATTRIBUTES_DBL("left", left);
  1271. LXW_PUSH_ATTRIBUTES_DBL("right", right);
  1272. LXW_PUSH_ATTRIBUTES_DBL("top", top);
  1273. LXW_PUSH_ATTRIBUTES_DBL("bottom", bottom);
  1274. LXW_PUSH_ATTRIBUTES_DBL("header", header);
  1275. LXW_PUSH_ATTRIBUTES_DBL("footer", footer);
  1276. lxw_xml_empty_tag(self->file, "pageMargins", &attributes);
  1277. LXW_FREE_ATTRIBUTES();
  1278. }
  1279. /*
  1280. * Write the <pageSetup> element.
  1281. * The following is an example taken from Excel.
  1282. * <pageSetup
  1283. * paperSize="9"
  1284. * scale="110"
  1285. * fitToWidth="2"
  1286. * fitToHeight="2"
  1287. * pageOrder="overThenDown"
  1288. * orientation="portrait"
  1289. * blackAndWhite="1"
  1290. * draft="1"
  1291. * horizontalDpi="200"
  1292. * verticalDpi="200"
  1293. * r:id="rId1"
  1294. * />
  1295. */
  1296. STATIC void
  1297. _worksheet_write_page_setup(lxw_worksheet *self)
  1298. {
  1299. struct xml_attribute_list attributes;
  1300. struct xml_attribute *attribute;
  1301. LXW_INIT_ATTRIBUTES();
  1302. if (!self->page_setup_changed)
  1303. return;
  1304. /* Set paper size. */
  1305. if (self->paper_size)
  1306. LXW_PUSH_ATTRIBUTES_INT("paperSize", self->paper_size);
  1307. /* Set the print_scale. */
  1308. if (self->print_scale != 100)
  1309. LXW_PUSH_ATTRIBUTES_INT("scale", self->print_scale);
  1310. /* Set the "Fit to page" properties. */
  1311. if (self->fit_page && self->fit_width != 1)
  1312. LXW_PUSH_ATTRIBUTES_INT("fitToWidth", self->fit_width);
  1313. if (self->fit_page && self->fit_height != 1)
  1314. LXW_PUSH_ATTRIBUTES_INT("fitToHeight", self->fit_height);
  1315. /* Set the page print direction. */
  1316. if (self->page_order)
  1317. LXW_PUSH_ATTRIBUTES_STR("pageOrder", "overThenDown");
  1318. /* Set start page. */
  1319. if (self->page_start > 1)
  1320. LXW_PUSH_ATTRIBUTES_INT("firstPageNumber", self->page_start);
  1321. /* Set page orientation. */
  1322. if (self->orientation)
  1323. LXW_PUSH_ATTRIBUTES_STR("orientation", "portrait");
  1324. else
  1325. LXW_PUSH_ATTRIBUTES_STR("orientation", "landscape");
  1326. /* Set start page active flag. */
  1327. if (self->page_start)
  1328. LXW_PUSH_ATTRIBUTES_INT("useFirstPageNumber", 1);
  1329. /* Set the DPI. Mainly only for testing. */
  1330. if (self->horizontal_dpi)
  1331. LXW_PUSH_ATTRIBUTES_INT("horizontalDpi", self->horizontal_dpi);
  1332. if (self->vertical_dpi)
  1333. LXW_PUSH_ATTRIBUTES_INT("verticalDpi", self->vertical_dpi);
  1334. lxw_xml_empty_tag(self->file, "pageSetup", &attributes);
  1335. LXW_FREE_ATTRIBUTES();
  1336. }
  1337. /*
  1338. * Write the <printOptions> element.
  1339. */
  1340. STATIC void
  1341. _worksheet_write_print_options(lxw_worksheet *self)
  1342. {
  1343. struct xml_attribute_list attributes;
  1344. struct xml_attribute *attribute;
  1345. if (!self->print_options_changed)
  1346. return;
  1347. LXW_INIT_ATTRIBUTES();
  1348. /* Set horizontal centering. */
  1349. if (self->hcenter) {
  1350. LXW_PUSH_ATTRIBUTES_STR("horizontalCentered", "1");
  1351. }
  1352. /* Set vertical centering. */
  1353. if (self->vcenter) {
  1354. LXW_PUSH_ATTRIBUTES_STR("verticalCentered", "1");
  1355. }
  1356. /* Enable row and column headers. */
  1357. if (self->print_headers) {
  1358. LXW_PUSH_ATTRIBUTES_STR("headings", "1");
  1359. }
  1360. /* Set printed gridlines. */
  1361. if (self->print_gridlines) {
  1362. LXW_PUSH_ATTRIBUTES_STR("gridLines", "1");
  1363. }
  1364. lxw_xml_empty_tag(self->file, "printOptions", &attributes);
  1365. LXW_FREE_ATTRIBUTES();
  1366. }
  1367. /*
  1368. * Write the <row> element.
  1369. */
  1370. STATIC void
  1371. _write_row(lxw_worksheet *self, lxw_row *row, char *spans)
  1372. {
  1373. struct xml_attribute_list attributes;
  1374. struct xml_attribute *attribute;
  1375. int32_t xf_index = 0;
  1376. double height;
  1377. if (row->format) {
  1378. xf_index = lxw_format_get_xf_index(row->format);
  1379. }
  1380. if (row->height_changed)
  1381. height = row->height;
  1382. else
  1383. height = self->default_row_height;
  1384. LXW_INIT_ATTRIBUTES();
  1385. LXW_PUSH_ATTRIBUTES_INT("r", row->row_num + 1);
  1386. if (spans)
  1387. LXW_PUSH_ATTRIBUTES_STR("spans", spans);
  1388. if (xf_index)
  1389. LXW_PUSH_ATTRIBUTES_INT("s", xf_index);
  1390. if (row->format)
  1391. LXW_PUSH_ATTRIBUTES_STR("customFormat", "1");
  1392. if (height != LXW_DEF_ROW_HEIGHT)
  1393. LXW_PUSH_ATTRIBUTES_DBL("ht", height);
  1394. if (row->hidden)
  1395. LXW_PUSH_ATTRIBUTES_STR("hidden", "1");
  1396. if (height != LXW_DEF_ROW_HEIGHT)
  1397. LXW_PUSH_ATTRIBUTES_STR("customHeight", "1");
  1398. if (row->level)
  1399. LXW_PUSH_ATTRIBUTES_INT("outlineLevel", row->level);
  1400. if (row->collapsed)
  1401. LXW_PUSH_ATTRIBUTES_STR("collapsed", "1");
  1402. if (!row->data_changed)
  1403. lxw_xml_empty_tag(self->file, "row", &attributes);
  1404. else
  1405. lxw_xml_start_tag(self->file, "row", &attributes);
  1406. LXW_FREE_ATTRIBUTES();
  1407. }
  1408. /*
  1409. * Convert the width of a cell from user's units to pixels. Excel rounds the
  1410. * column width to the nearest pixel. If the width hasn't been set by the user
  1411. * we use the default value. If the column is hidden it has a value of zero.
  1412. */
  1413. STATIC int32_t
  1414. _worksheet_size_col(lxw_worksheet *self, lxw_col_t col_num)
  1415. {
  1416. lxw_col_options *col_opt = NULL;
  1417. uint32_t pixels;
  1418. double width;
  1419. double max_digit_width = 7.0; /* For Calabri 11. */
  1420. double padding = 5.0;
  1421. lxw_col_t col_index;
  1422. /* Search for the col number in the array of col_options. Each col_option
  1423. * entry contains the start and end column for a range.
  1424. */
  1425. for (col_index = 0; col_index < self->col_options_max; col_index++) {
  1426. col_opt = self->col_options[col_index];
  1427. if (col_opt) {
  1428. if (col_num >= col_opt->firstcol && col_num <= col_opt->lastcol)
  1429. break;
  1430. else
  1431. col_opt = NULL;
  1432. }
  1433. }
  1434. if (col_opt) {
  1435. width = col_opt->width;
  1436. /* Convert to pixels. */
  1437. if (width == 0) {
  1438. pixels = 0;
  1439. }
  1440. else if (width < 1.0) {
  1441. pixels = (uint32_t) (width * (max_digit_width + padding) + 0.5);
  1442. }
  1443. else {
  1444. pixels = (uint32_t) (width * max_digit_width + 0.5) + 5;
  1445. }
  1446. }
  1447. else {
  1448. pixels = self->default_col_pixels;
  1449. }
  1450. return pixels;
  1451. }
  1452. /*
  1453. * Convert the height of a cell from user's units to pixels. If the height
  1454. * hasn't been set by the user we use the default value. If the row is hidden
  1455. * it has a value of zero.
  1456. */
  1457. STATIC int32_t
  1458. _worksheet_size_row(lxw_worksheet *self, lxw_row_t row_num)
  1459. {
  1460. lxw_row *row;
  1461. uint32_t pixels;
  1462. double height;
  1463. row = lxw_worksheet_find_row(self, row_num);
  1464. if (row) {
  1465. height = row->height;
  1466. if (height == 0)
  1467. pixels = 0;
  1468. else
  1469. pixels = (uint32_t) (4.0 / 3.0 * height);
  1470. }
  1471. else {
  1472. pixels = (uint32_t) (4.0 / 3.0 * self->default_row_height);
  1473. }
  1474. return pixels;
  1475. }
  1476. /*
  1477. * Calculate the vertices that define the position of a graphical object
  1478. * within the worksheet in pixels.
  1479. * +------------+------------+
  1480. * | A | B |
  1481. * +-----+------------+------------+
  1482. * | |(x1,y1) | |
  1483. * | 1 |(A1)._______|______ |
  1484. * | | | | |
  1485. * | | | | |
  1486. * +-----+----| BITMAP |-----+
  1487. * | | | | |
  1488. * | 2 | |______________. |
  1489. * | | | (B2)|
  1490. * | | | (x2,y2)|
  1491. * +---- +------------+------------+
  1492. *
  1493. * Example of an object that covers some of the area from cell A1 to cell B2.
  1494. * Based on the width and height of the object we need to calculate 8 vars:
  1495. *
  1496. * col_start, row_start, col_end, row_end, x1, y1, x2, y2.
  1497. *
  1498. * We also calculate the absolute x and y position of the top left vertex of
  1499. * the object. This is required for images:
  1500. *
  1501. * x_abs, y_abs
  1502. *
  1503. * The width and height of the cells that the object occupies can be variable
  1504. * and have to be taken into account.
  1505. *
  1506. * The values of col_start and row_start are passed in from the calling
  1507. * function. The values of col_end and row_end are calculated by subtracting
  1508. * the width and height of the object from the width and height of the
  1509. * underlying cells.
  1510. */
  1511. STATIC void
  1512. _worksheet_position_object_pixels(lxw_worksheet *self,
  1513. lxw_image_options *image,
  1514. lxw_drawing_object *drawing_object)
  1515. {
  1516. lxw_col_t col_start; /* Column containing upper left corner. */
  1517. int32_t x1; /* Distance to left side of object. */
  1518. lxw_row_t row_start; /* Row containing top left corner. */
  1519. int32_t y1; /* Distance to top of object. */
  1520. lxw_col_t col_end; /* Column containing lower right corner. */
  1521. double x2; /* Distance to right side of object. */
  1522. lxw_row_t row_end; /* Row containing bottom right corner. */
  1523. double y2; /* Distance to bottom of object. */
  1524. double width; /* Width of object frame. */
  1525. double height; /* Height of object frame. */
  1526. uint32_t x_abs = 0; /* Abs. distance to left side of object. */
  1527. uint32_t y_abs = 0; /* Abs. distance to top side of object. */
  1528. uint32_t i;
  1529. col_start = image->col;
  1530. row_start = image->row;
  1531. x1 = image->x_offset;
  1532. y1 = image->y_offset;
  1533. width = image->width;
  1534. height = image->height;
  1535. /* Adjust start column for negative offsets. */
  1536. while (x1 < 0 && col_start > 0) {
  1537. x1 += _worksheet_size_col(self, col_start - 1);
  1538. col_start--;
  1539. }
  1540. /* Adjust start row for negative offsets. */
  1541. while (y1 < 0 && row_start > 0) {
  1542. y1 += _worksheet_size_row(self, row_start - 1);
  1543. row_start--;
  1544. }
  1545. /* Ensure that the image isn't shifted off the page at top left. */
  1546. if (x1 < 0)
  1547. x1 = 0;
  1548. if (y1 < 0)
  1549. y1 = 0;
  1550. /* Calculate the absolute x offset of the top-left vertex. */
  1551. if (self->col_size_changed) {
  1552. for (i = 0; i < col_start; i++)
  1553. x_abs += _worksheet_size_col(self, i);
  1554. }
  1555. else {
  1556. /* Optimization for when the column widths haven't changed. */
  1557. x_abs += self->default_col_pixels * col_start;
  1558. }
  1559. x_abs += x1;
  1560. /* Calculate the absolute y offset of the top-left vertex. */
  1561. /* Store the column change to allow optimizations. */
  1562. if (self->row_size_changed) {
  1563. for (i = 0; i < row_start; i++)
  1564. y_abs += _worksheet_size_row(self, i);
  1565. }
  1566. else {
  1567. /* Optimization for when the row heights haven"t changed. */
  1568. y_abs += self->default_row_pixels * row_start;
  1569. }
  1570. y_abs += y1;
  1571. /* Adjust start col for offsets that are greater than the col width. */
  1572. while (x1 >= _worksheet_size_col(self, col_start)) {
  1573. x1 -= _worksheet_size_col(self, col_start);
  1574. col_start++;
  1575. }
  1576. /* Adjust start row for offsets that are greater than the row height. */
  1577. while (y1 >= _worksheet_size_row(self, row_start)) {
  1578. y1 -= _worksheet_size_row(self, row_start);
  1579. row_start++;
  1580. }
  1581. /* Initialize end cell to the same as the start cell. */
  1582. col_end = col_start;
  1583. row_end = row_start;
  1584. width = width + x1;
  1585. height = height + y1;
  1586. /* Subtract the underlying cell widths to find the end cell. */
  1587. while (width >= _worksheet_size_col(self, col_end)) {
  1588. width -= _worksheet_size_col(self, col_end);
  1589. col_end++;
  1590. }
  1591. /* Subtract the underlying cell heights to find the end cell. */
  1592. while (height >= _worksheet_size_row(self, row_end)) {
  1593. height -= _worksheet_size_row(self, row_end);
  1594. row_end++;
  1595. }
  1596. /* The end vertices are whatever is left from the width and height. */
  1597. x2 = width;
  1598. y2 = height;
  1599. /* Add the dimensions to the drawing object. */
  1600. drawing_object->from.col = col_start;
  1601. drawing_object->from.row = row_start;
  1602. drawing_object->from.col_offset = x1;
  1603. drawing_object->from.row_offset = y1;
  1604. drawing_object->to.col = col_end;
  1605. drawing_object->to.row = row_end;
  1606. drawing_object->to.col_offset = x2;
  1607. drawing_object->to.row_offset = y2;
  1608. drawing_object->col_absolute = x_abs;
  1609. drawing_object->row_absolute = y_abs;
  1610. }
  1611. /*
  1612. * Calculate the vertices that define the position of a graphical object
  1613. * within the worksheet in EMUs. The vertices are expressed as English
  1614. * Metric Units (EMUs). There are 12,700 EMUs per point.
  1615. * Therefore, 12,700 * 3 /4 = 9,525 EMUs per pixel.
  1616. */
  1617. STATIC void
  1618. _worksheet_position_object_emus(lxw_worksheet *self,
  1619. lxw_image_options *image,
  1620. lxw_drawing_object *drawing_object)
  1621. {
  1622. _worksheet_position_object_pixels(self, image, drawing_object);
  1623. /* Convert the pixel values to EMUs. See above. */
  1624. drawing_object->from.col_offset *= 9525;
  1625. drawing_object->from.row_offset *= 9525;
  1626. drawing_object->to.col_offset *= 9525;
  1627. drawing_object->to.row_offset *= 9525;
  1628. drawing_object->to.col_offset += 0.5;
  1629. drawing_object->to.row_offset += 0.5;
  1630. drawing_object->col_absolute *= 9525;
  1631. drawing_object->row_absolute *= 9525;
  1632. }
  1633. /*
  1634. * Set up image/drawings.
  1635. */
  1636. void
  1637. lxw_worksheet_prepare_image(lxw_worksheet *self,
  1638. uint16_t image_ref_id, uint16_t drawing_id,
  1639. lxw_image_options *image_data)
  1640. {
  1641. lxw_drawing_object *drawing_object;
  1642. lxw_rel_tuple *relationship;
  1643. double width;
  1644. double height;
  1645. char filename[LXW_FILENAME_LENGTH];
  1646. if (!self->drawing) {
  1647. self->drawing = lxw_drawing_new();
  1648. self->drawing->embedded = LXW_TRUE;
  1649. RETURN_VOID_ON_MEM_ERROR(self->drawing);
  1650. relationship = calloc(1, sizeof(lxw_rel_tuple));
  1651. GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error);
  1652. relationship->type = lxw_strdup("/drawing");
  1653. GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error);
  1654. lxw_snprintf(filename, LXW_FILENAME_LENGTH,
  1655. "../drawings/drawing%d.xml", drawing_id);
  1656. relationship->target = lxw_strdup(filename);
  1657. GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error);
  1658. STAILQ_INSERT_TAIL(self->external_drawing_links, relationship,
  1659. list_pointers);
  1660. }
  1661. drawing_object = calloc(1, sizeof(lxw_drawing_object));
  1662. RETURN_VOID_ON_MEM_ERROR(drawing_object);
  1663. drawing_object->anchor_type = LXW_ANCHOR_TYPE_IMAGE;
  1664. drawing_object->edit_as = LXW_ANCHOR_EDIT_AS_ONE_CELL;
  1665. drawing_object->description = lxw_strdup(image_data->short_name);
  1666. /* Scale to user scale. */
  1667. width = image_data->width * image_data->x_scale;
  1668. height = image_data->height * image_data->y_scale;
  1669. /* Scale by non 96dpi resolutions. */
  1670. width *= 96.0 / image_data->x_dpi;
  1671. height *= 96.0 / image_data->y_dpi;
  1672. /* Convert to the nearest pixel. */
  1673. image_data->width = width;
  1674. image_data->height = height;
  1675. _worksheet_position_object_emus(self, image_data, drawing_object);
  1676. /* Convert from pixels to emus. */
  1677. drawing_object->width = (uint32_t) (0.5 + width * 9525);
  1678. drawing_object->height = (uint32_t) (0.5 + height * 9525);
  1679. lxw_add_drawing_object(self->drawing, drawing_object);
  1680. relationship = calloc(1, sizeof(lxw_rel_tuple));
  1681. GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error);
  1682. relationship->type = lxw_strdup("/image");
  1683. GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error);
  1684. lxw_snprintf(filename, 32, "../media/image%d.%s", image_ref_id,
  1685. image_data->extension);
  1686. relationship->target = lxw_strdup(filename);
  1687. GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error);
  1688. STAILQ_INSERT_TAIL(self->drawing_links, relationship, list_pointers);
  1689. return;
  1690. mem_error:
  1691. if (relationship) {
  1692. free(relationship->type);
  1693. free(relationship->target);
  1694. free(relationship->target_mode);
  1695. free(relationship);
  1696. }
  1697. }
  1698. /*
  1699. * Set up chart/drawings.
  1700. */
  1701. void
  1702. lxw_worksheet_prepare_chart(lxw_worksheet *self,
  1703. uint16_t chart_ref_id, uint16_t drawing_id,
  1704. lxw_image_options *image_data)
  1705. {
  1706. lxw_drawing_object *drawing_object;
  1707. lxw_rel_tuple *relationship;
  1708. double width;
  1709. double height;
  1710. char filename[LXW_FILENAME_LENGTH];
  1711. if (!self->drawing) {
  1712. self->drawing = lxw_drawing_new();
  1713. self->drawing->embedded = LXW_TRUE;
  1714. RETURN_VOID_ON_MEM_ERROR(self->drawing);
  1715. relationship = calloc(1, sizeof(lxw_rel_tuple));
  1716. GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error);
  1717. relationship->type = lxw_strdup("/drawing");
  1718. GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error);
  1719. lxw_snprintf(filename, LXW_FILENAME_LENGTH,
  1720. "../drawings/drawing%d.xml", drawing_id);
  1721. relationship->target = lxw_strdup(filename);
  1722. GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error);
  1723. STAILQ_INSERT_TAIL(self->external_drawing_links, relationship,
  1724. list_pointers);
  1725. }
  1726. drawing_object = calloc(1, sizeof(lxw_drawing_object));
  1727. RETURN_VOID_ON_MEM_ERROR(drawing_object);
  1728. drawing_object->anchor_type = LXW_ANCHOR_TYPE_CHART;
  1729. drawing_object->edit_as = LXW_ANCHOR_EDIT_AS_ONE_CELL;
  1730. drawing_object->description = lxw_strdup("TODO_DESC");
  1731. /* Scale to user scale. */
  1732. width = image_data->width * image_data->x_scale;
  1733. height = image_data->height * image_data->y_scale;
  1734. /* Convert to the nearest pixel. */
  1735. image_data->width = width;
  1736. image_data->height = height;
  1737. _worksheet_position_object_emus(self, image_data, drawing_object);
  1738. /* Convert from pixels to emus. */
  1739. drawing_object->width = (uint32_t) (0.5 + width * 9525);
  1740. drawing_object->height = (uint32_t) (0.5 + height * 9525);
  1741. lxw_add_drawing_object(self->drawing, drawing_object);
  1742. relationship = calloc(1, sizeof(lxw_rel_tuple));
  1743. GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error);
  1744. relationship->type = lxw_strdup("/chart");
  1745. GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error);
  1746. lxw_snprintf(filename, 32, "../charts/chart%d.xml", chart_ref_id);
  1747. relationship->target = lxw_strdup(filename);
  1748. GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error);
  1749. STAILQ_INSERT_TAIL(self->drawing_links, relationship, list_pointers);
  1750. return;
  1751. mem_error:
  1752. if (relationship) {
  1753. free(relationship->type);
  1754. free(relationship->target);
  1755. free(relationship->target_mode);
  1756. free(relationship);
  1757. }
  1758. }
  1759. /*
  1760. * Extract width and height information from a PNG file.
  1761. */
  1762. STATIC lxw_error
  1763. _process_png(lxw_image_options *image_options)
  1764. {
  1765. uint32_t length;
  1766. uint32_t offset;
  1767. char type[4];
  1768. uint32_t width = 0;
  1769. uint32_t height = 0;
  1770. double x_dpi = 96;
  1771. double y_dpi = 96;
  1772. int fseek_err;
  1773. FILE *stream = image_options->stream;
  1774. /* Skip another 4 bytes to the end of the PNG header. */
  1775. fseek_err = fseek(stream, 4, SEEK_CUR);
  1776. if (fseek_err)
  1777. goto file_error;
  1778. while (!feof(stream)) {
  1779. /* Read the PNG length and type fields for the sub-section. */
  1780. if (fread(&length, sizeof(length), 1, stream) < 1)
  1781. break;
  1782. if (fread(&type, 1, 4, stream) < 4)
  1783. break;
  1784. /* Convert the length to network order. */
  1785. length = LXW_UINT32_NETWORK(length);
  1786. /* The offset for next fseek() is the field length + type length. */
  1787. offset = length + 4;
  1788. if (memcmp(type, "IHDR", 4) == 0) {
  1789. if (fread(&width, sizeof(width), 1, stream) < 1)
  1790. break;
  1791. if (fread(&height, sizeof(height), 1, stream) < 1)
  1792. break;
  1793. width = LXW_UINT32_NETWORK(width);
  1794. height = LXW_UINT32_NETWORK(height);
  1795. /* Reduce the offset by the length of previous freads(). */
  1796. offset -= 8;
  1797. }
  1798. if (memcmp(type, "pHYs", 4) == 0) {
  1799. uint32_t x_ppu = 0;
  1800. uint32_t y_ppu = 0;
  1801. uint8_t units = 1;
  1802. if (fread(&x_ppu, sizeof(x_ppu), 1, stream) < 1)
  1803. break;
  1804. if (fread(&y_ppu, sizeof(y_ppu), 1, stream) < 1)
  1805. break;
  1806. if (fread(&units, sizeof(units), 1, stream) < 1)
  1807. break;
  1808. if (units == 1) {
  1809. x_ppu = LXW_UINT32_NETWORK(x_ppu);
  1810. y_ppu = LXW_UINT32_NETWORK(y_ppu);
  1811. x_dpi = (double) x_ppu *0.0254;
  1812. y_dpi = (double) y_ppu *0.0254;
  1813. }
  1814. /* Reduce the offset by the length of previous freads(). */
  1815. offset -= 9;
  1816. }
  1817. if (memcmp(type, "IEND", 4) == 0)
  1818. break;
  1819. if (!feof(stream)) {
  1820. fseek_err = fseek(stream, offset, SEEK_CUR);
  1821. if (fseek_err)
  1822. goto file_error;
  1823. }
  1824. }
  1825. /* Ensure that we read some valid data from the file. */
  1826. if (width == 0)
  1827. goto file_error;
  1828. /* Set the image metadata. */
  1829. image_options->image_type = LXW_IMAGE_PNG;
  1830. image_options->width = width;
  1831. image_options->height = height;
  1832. image_options->x_dpi = x_dpi ? x_dpi : 96;
  1833. image_options->y_dpi = y_dpi ? x_dpi : 96;
  1834. image_options->extension = lxw_strdup("png");
  1835. return LXW_NO_ERROR;
  1836. file_error:
  1837. LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): "
  1838. "no size data found in file: %s.",
  1839. image_options->filename);
  1840. return LXW_ERROR_IMAGE_DIMENSIONS;
  1841. }
  1842. /*
  1843. * Extract width and height information from a JPEG file.
  1844. */
  1845. STATIC lxw_error
  1846. _process_jpeg(lxw_image_options *image_options)
  1847. {
  1848. uint16_t length;
  1849. uint16_t marker;
  1850. uint32_t offset;
  1851. uint16_t width = 0;
  1852. uint16_t height = 0;
  1853. double x_dpi = 96;
  1854. double y_dpi = 96;
  1855. int fseek_err;
  1856. FILE *stream = image_options->stream;
  1857. /* Read back 2 bytes to the end of the initial 0xFFD8 marker. */
  1858. fseek_err = fseek(stream, -2, SEEK_CUR);
  1859. if (fseek_err)
  1860. goto file_error;
  1861. /* Search through the image data and read the JPEG markers. */
  1862. while (!feof(stream)) {
  1863. /* Read the JPEG marker and length fields for the sub-section. */
  1864. if (fread(&marker, sizeof(marker), 1, stream) < 1)
  1865. break;
  1866. if (fread(&length, sizeof(length), 1, stream) < 1)
  1867. break;
  1868. /* Convert the marker and length to network order. */
  1869. marker = LXW_UINT16_NETWORK(marker);
  1870. length = LXW_UINT16_NETWORK(length);
  1871. /* The offset for next fseek() is the field length + type length. */
  1872. offset = length - 2;
  1873. /* Read the height and width in the 0xFFCn elements (except C4, C8 */
  1874. /* and CC which aren't SOF markers). */
  1875. if ((marker & 0xFFF0) == 0xFFC0 && marker != 0xFFC4
  1876. && marker != 0xFFC8 && marker != 0xFFCC) {
  1877. /* Skip 1 byte to height and width. */
  1878. fseek_err = fseek(stream, 1, SEEK_CUR);
  1879. if (fseek_err)
  1880. goto file_error;
  1881. if (fread(&height, sizeof(height), 1, stream) < 1)
  1882. break;
  1883. if (fread(&width, sizeof(width), 1, stream) < 1)
  1884. break;
  1885. height = LXW_UINT16_NETWORK(height);
  1886. width = LXW_UINT16_NETWORK(width);
  1887. offset -= 9;
  1888. }
  1889. /* Read the DPI in the 0xFFE0 element. */
  1890. if (marker == 0xFFE0) {
  1891. uint16_t x_density = 0;
  1892. uint16_t y_density = 0;
  1893. uint8_t units = 1;
  1894. fseek_err = fseek(stream, 7, SEEK_CUR);
  1895. if (fseek_err)
  1896. goto file_error;
  1897. if (fread(&units, sizeof(units), 1, stream) < 1)
  1898. break;
  1899. if (fread(&x_density, sizeof(x_density), 1, stream) < 1)
  1900. break;
  1901. if (fread(&y_density, sizeof(y_density), 1, stream) < 1)
  1902. break;
  1903. x_density = LXW_UINT16_NETWORK(x_density);
  1904. y_density = LXW_UINT16_NETWORK(y_density);
  1905. if (units == 1) {
  1906. x_dpi = x_density;
  1907. y_dpi = y_density;
  1908. }
  1909. if (units == 2) {
  1910. x_dpi = x_density * 2.54;
  1911. y_dpi = y_density * 2.54;
  1912. }
  1913. offset -= 12;
  1914. }
  1915. if (marker == 0xFFDA)
  1916. break;
  1917. if (!feof(stream)) {
  1918. fseek_err = fseek(stream, offset, SEEK_CUR);
  1919. if (fseek_err)
  1920. goto file_error;
  1921. }
  1922. }
  1923. /* Ensure that we read some valid data from the file. */
  1924. if (width == 0)
  1925. goto file_error;
  1926. /* Set the image metadata. */
  1927. image_options->image_type = LXW_IMAGE_JPEG;
  1928. image_options->width = width;
  1929. image_options->height = height;
  1930. image_options->x_dpi = x_dpi ? x_dpi : 96;
  1931. image_options->y_dpi = y_dpi ? x_dpi : 96;
  1932. image_options->extension = lxw_strdup("jpeg");
  1933. return LXW_NO_ERROR;
  1934. file_error:
  1935. LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): "
  1936. "no size data found in file: %s.",
  1937. image_options->filename);
  1938. return LXW_ERROR_IMAGE_DIMENSIONS;
  1939. }
  1940. /*
  1941. * Extract width and height information from a BMP file.
  1942. */
  1943. STATIC lxw_error
  1944. _process_bmp(lxw_image_options *image_options)
  1945. {
  1946. uint32_t width = 0;
  1947. uint32_t height = 0;
  1948. double x_dpi = 96;
  1949. double y_dpi = 96;
  1950. int fseek_err;
  1951. FILE *stream = image_options->stream;
  1952. /* Skip another 14 bytes to the start of the BMP height/width. */
  1953. fseek_err = fseek(stream, 14, SEEK_CUR);
  1954. if (fseek_err)
  1955. goto file_error;
  1956. if (fread(&width, sizeof(width), 1, stream) < 1)
  1957. width = 0;
  1958. if (fread(&height, sizeof(height), 1, stream) < 1)
  1959. height = 0;
  1960. /* Ensure that we read some valid data from the file. */
  1961. if (width == 0)
  1962. goto file_error;
  1963. height = LXW_UINT32_HOST(height);
  1964. width = LXW_UINT32_HOST(width);
  1965. /* Set the image metadata. */
  1966. image_options->image_type = LXW_IMAGE_BMP;
  1967. image_options->width = width;
  1968. image_options->height = height;
  1969. image_options->x_dpi = x_dpi;
  1970. image_options->y_dpi = y_dpi;
  1971. image_options->extension = lxw_strdup("bmp");
  1972. return LXW_NO_ERROR;
  1973. file_error:
  1974. LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): "
  1975. "no size data found in file: %s.",
  1976. image_options->filename);
  1977. return LXW_ERROR_IMAGE_DIMENSIONS;
  1978. }
  1979. /*
  1980. * Extract information from the image file such as dimension, type, filename,
  1981. * and extension.
  1982. */
  1983. STATIC lxw_error
  1984. _get_image_properties(lxw_image_options *image_options)
  1985. {
  1986. unsigned char signature[4];
  1987. /* Read 4 bytes to look for the file header/signature. */
  1988. if (fread(signature, 1, 4, image_options->stream) < 4) {
  1989. LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): "
  1990. "couldn't read file type for file: %s.",
  1991. image_options->filename);
  1992. return LXW_ERROR_IMAGE_DIMENSIONS;
  1993. }
  1994. if (memcmp(&signature[1], "PNG", 3) == 0) {
  1995. if (_process_png(image_options) != LXW_NO_ERROR)
  1996. return LXW_ERROR_IMAGE_DIMENSIONS;
  1997. }
  1998. else if (signature[0] == 0xFF && signature[1] == 0xD8) {
  1999. if (_process_jpeg(image_options) != LXW_NO_ERROR)
  2000. return LXW_ERROR_IMAGE_DIMENSIONS;
  2001. }
  2002. else if (memcmp(signature, "BM", 2) == 0) {
  2003. if (_process_bmp(image_options) != LXW_NO_ERROR)
  2004. return LXW_ERROR_IMAGE_DIMENSIONS;
  2005. }
  2006. else {
  2007. LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): "
  2008. "unsupported image format for file: %s.",
  2009. image_options->filename);
  2010. return LXW_ERROR_IMAGE_DIMENSIONS;
  2011. }
  2012. return LXW_NO_ERROR;
  2013. }
  2014. /*****************************************************************************
  2015. *
  2016. * XML file assembly functions.
  2017. *
  2018. ****************************************************************************/
  2019. /*
  2020. * Write out a number worksheet cell. Doesn't use the xml functions as an
  2021. * optimization in the inner cell writing loop.
  2022. */
  2023. STATIC void
  2024. _write_number_cell(lxw_worksheet *self, char *range,
  2025. int32_t style_index, lxw_cell *cell)
  2026. {
  2027. #ifdef USE_DOUBLE_FUNCTION
  2028. char data[LXW_ATTR_32];
  2029. lxw_sprintf_dbl(data, cell->u.number);
  2030. if (style_index)
  2031. fprintf(self->file,
  2032. "<c r=\"%s\" s=\"%d\"><v>%s</v></c>",
  2033. range, style_index, data);
  2034. else
  2035. fprintf(self->file, "<c r=\"%s\"><v>%s</v></c>", range, data);
  2036. #else
  2037. if (style_index)
  2038. fprintf(self->file,
  2039. "<c r=\"%s\" s=\"%d\"><v>%.16g</v></c>",
  2040. range, style_index, cell->u.number);
  2041. else
  2042. fprintf(self->file,
  2043. "<c r=\"%s\"><v>%.16g</v></c>", range, cell->u.number);
  2044. #endif
  2045. }
  2046. /*
  2047. * Write out a string worksheet cell. Doesn't use the xml functions as an
  2048. * optimization in the inner cell writing loop.
  2049. */
  2050. STATIC void
  2051. _write_string_cell(lxw_worksheet *self, char *range,
  2052. int32_t style_index, lxw_cell *cell)
  2053. {
  2054. if (style_index)
  2055. fprintf(self->file,
  2056. "<c r=\"%s\" s=\"%d\" t=\"s\"><v>%d</v></c>",
  2057. range, style_index, cell->u.string_id);
  2058. else
  2059. fprintf(self->file,
  2060. "<c r=\"%s\" t=\"s\"><v>%d</v></c>",
  2061. range, cell->u.string_id);
  2062. }
  2063. /*
  2064. * Write out an inline string. Doesn't use the xml functions as an
  2065. * optimization in the inner cell writing loop.
  2066. */
  2067. STATIC void
  2068. _write_inline_string_cell(lxw_worksheet *self, char *range,
  2069. int32_t style_index, lxw_cell *cell)
  2070. {
  2071. char *string = lxw_escape_data(cell->u.string);
  2072. /* Add attribute to preserve leading or trailing whitespace. */
  2073. if (isspace((unsigned char) string[0])
  2074. || isspace((unsigned char) string[strlen(string) - 1])) {
  2075. if (style_index)
  2076. fprintf(self->file,
  2077. "<c r=\"%s\" s=\"%d\" t=\"inlineStr\"><is>"
  2078. "<t xml:space=\"preserve\">%s</t></is></c>",
  2079. range, style_index, string);
  2080. else
  2081. fprintf(self->file,
  2082. "<c r=\"%s\" t=\"inlineStr\"><is>"
  2083. "<t xml:space=\"preserve\">%s</t></is></c>",
  2084. range, string);
  2085. }
  2086. else {
  2087. if (style_index)
  2088. fprintf(self->file,
  2089. "<c r=\"%s\" s=\"%d\" t=\"inlineStr\">"
  2090. "<is><t>%s</t></is></c>", range, style_index, string);
  2091. else
  2092. fprintf(self->file,
  2093. "<c r=\"%s\" t=\"inlineStr\">"
  2094. "<is><t>%s</t></is></c>", range, string);
  2095. }
  2096. free(string);
  2097. }
  2098. /*
  2099. * Write out a formula worksheet cell with a numeric result.
  2100. */
  2101. STATIC void
  2102. _write_formula_num_cell(lxw_worksheet *self, lxw_cell *cell)
  2103. {
  2104. char data[LXW_ATTR_32];
  2105. lxw_sprintf_dbl(data, cell->formula_result);
  2106. lxw_xml_data_element(self->file, "f", cell->u.string, NULL);
  2107. lxw_xml_data_element(self->file, "v", data, NULL);
  2108. }
  2109. /*
  2110. * Write out an array formula worksheet cell with a numeric result.
  2111. */
  2112. STATIC void
  2113. _write_array_formula_num_cell(lxw_worksheet *self, lxw_cell *cell)
  2114. {
  2115. struct xml_attribute_list attributes;
  2116. struct xml_attribute *attribute;
  2117. char data[LXW_ATTR_32];
  2118. LXW_INIT_ATTRIBUTES();
  2119. LXW_PUSH_ATTRIBUTES_STR("t", "array");
  2120. LXW_PUSH_ATTRIBUTES_STR("ref", cell->user_data1);
  2121. lxw_sprintf_dbl(data, cell->formula_result);
  2122. lxw_xml_data_element(self->file, "f", cell->u.string, &attributes);
  2123. lxw_xml_data_element(self->file, "v", data, NULL);
  2124. LXW_FREE_ATTRIBUTES();
  2125. }
  2126. /*
  2127. * Write out a boolean worksheet cell.
  2128. */
  2129. STATIC void
  2130. _write_boolean_cell(lxw_worksheet *self, lxw_cell *cell)
  2131. {
  2132. char data[LXW_ATTR_32];
  2133. if (cell->u.number)
  2134. data[0] = '1';
  2135. else
  2136. data[0] = '0';
  2137. data[1] = '\0';
  2138. lxw_xml_data_element(self->file, "v", data, NULL);
  2139. }
  2140. /*
  2141. * Calculate the "spans" attribute of the <row> tag. This is an XLSX
  2142. * optimization and isn't strictly required. However, it makes comparing
  2143. * files easier.
  2144. *
  2145. * The span is the same for each block of 16 rows.
  2146. */
  2147. STATIC void
  2148. _calculate_spans(struct lxw_row *row, char *span, int32_t *block_num)
  2149. {
  2150. lxw_cell *cell_min = RB_MIN(lxw_table_cells, row->cells);
  2151. lxw_cell *cell_max = RB_MAX(lxw_table_cells, row->cells);
  2152. lxw_col_t span_col_min = cell_min->col_num;
  2153. lxw_col_t span_col_max = cell_max->col_num;
  2154. lxw_col_t col_min;
  2155. lxw_col_t col_max;
  2156. *block_num = row->row_num / 16;
  2157. row = RB_NEXT(lxw_table_rows, root, row);
  2158. while (row && (int32_t) (row->row_num / 16) == *block_num) {
  2159. if (!RB_EMPTY(row->cells)) {
  2160. cell_min = RB_MIN(lxw_table_cells, row->cells);
  2161. cell_max = RB_MAX(lxw_table_cells, row->cells);
  2162. col_min = cell_min->col_num;
  2163. col_max = cell_max->col_num;
  2164. if (col_min < span_col_min)
  2165. span_col_min = col_min;
  2166. if (col_max > span_col_max)
  2167. span_col_max = col_max;
  2168. }
  2169. row = RB_NEXT(lxw_table_rows, root, row);
  2170. }
  2171. lxw_snprintf(span, LXW_MAX_CELL_RANGE_LENGTH,
  2172. "%d:%d", span_col_min + 1, span_col_max + 1);
  2173. }
  2174. /*
  2175. * Write out a generic worksheet cell.
  2176. */
  2177. STATIC void
  2178. _write_cell(lxw_worksheet *self, lxw_cell *cell, lxw_format *row_format)
  2179. {
  2180. struct xml_attribute_list attributes;
  2181. struct xml_attribute *attribute;
  2182. char range[LXW_MAX_CELL_NAME_LENGTH] = { 0 };
  2183. lxw_row_t row_num = cell->row_num;
  2184. lxw_col_t col_num = cell->col_num;
  2185. int32_t style_index = 0;
  2186. lxw_rowcol_to_cell(range, row_num, col_num);
  2187. if (cell->format) {
  2188. style_index = lxw_format_get_xf_index(cell->format);
  2189. }
  2190. else if (row_format) {
  2191. style_index = lxw_format_get_xf_index(row_format);
  2192. }
  2193. else if (col_num < self->col_formats_max && self->col_formats[col_num]) {
  2194. style_index = lxw_format_get_xf_index(self->col_formats[col_num]);
  2195. }
  2196. /* Unrolled optimization for most commonly written cell types. */
  2197. if (cell->type == NUMBER_CELL) {
  2198. _write_number_cell(self, range, style_index, cell);
  2199. return;
  2200. }
  2201. if (cell->type == STRING_CELL) {
  2202. _write_string_cell(self, range, style_index, cell);
  2203. return;
  2204. }
  2205. if (cell->type == INLINE_STRING_CELL) {
  2206. _write_inline_string_cell(self, range, style_index, cell);
  2207. return;
  2208. }
  2209. /* For other cell types use the general functions. */
  2210. LXW_INIT_ATTRIBUTES();
  2211. LXW_PUSH_ATTRIBUTES_STR("r", range);
  2212. if (style_index)
  2213. LXW_PUSH_ATTRIBUTES_INT("s", style_index);
  2214. if (cell->type == FORMULA_CELL) {
  2215. lxw_xml_start_tag(self->file, "c", &attributes);
  2216. _write_formula_num_cell(self, cell);
  2217. lxw_xml_end_tag(self->file, "c");
  2218. }
  2219. else if (cell->type == BLANK_CELL) {
  2220. lxw_xml_empty_tag(self->file, "c", &attributes);
  2221. }
  2222. else if (cell->type == BOOLEAN_CELL) {
  2223. LXW_PUSH_ATTRIBUTES_STR("t", "b");
  2224. lxw_xml_start_tag(self->file, "c", &attributes);
  2225. _write_boolean_cell(self, cell);
  2226. lxw_xml_end_tag(self->file, "c");
  2227. }
  2228. else if (cell->type == ARRAY_FORMULA_CELL) {
  2229. lxw_xml_start_tag(self->file, "c", &attributes);
  2230. _write_array_formula_num_cell(self, cell);
  2231. lxw_xml_end_tag(self->file, "c");
  2232. }
  2233. LXW_FREE_ATTRIBUTES();
  2234. }
  2235. /*
  2236. * Write out the worksheet data as a series of rows and cells.
  2237. */
  2238. STATIC void
  2239. _worksheet_write_rows(lxw_worksheet *self)
  2240. {
  2241. lxw_row *row;
  2242. lxw_cell *cell;
  2243. int32_t block_num = -1;
  2244. char spans[LXW_MAX_CELL_RANGE_LENGTH] = { 0 };
  2245. RB_FOREACH(row, lxw_table_rows, self->table) {
  2246. if (RB_EMPTY(row->cells)) {
  2247. /* Row contains no cells but has height, format or other data. */
  2248. /* Write a default span for default rows. */
  2249. if (self->default_row_set)
  2250. _write_row(self, row, "1:1");
  2251. else
  2252. _write_row(self, row, NULL);
  2253. }
  2254. else {
  2255. /* Row and cell data. */
  2256. if ((int32_t) row->row_num / 16 > block_num)
  2257. _calculate_spans(row, spans, &block_num);
  2258. _write_row(self, row, spans);
  2259. RB_FOREACH(cell, lxw_table_cells, row->cells) {
  2260. _write_cell(self, cell, row->format);
  2261. }
  2262. lxw_xml_end_tag(self->file, "row");
  2263. }
  2264. }
  2265. }
  2266. /*
  2267. * Write out the worksheet data as a single row with cells. This method is
  2268. * used when memory optimization is on. A single row is written and the data
  2269. * array is reset. That way only one row of data is kept in memory at any one
  2270. * time. We don't write span data in the optimized case since it is optional.
  2271. */
  2272. void
  2273. lxw_worksheet_write_single_row(lxw_worksheet *self)
  2274. {
  2275. lxw_row *row = self->optimize_row;
  2276. lxw_col_t col;
  2277. /* skip row if it doesn't contain row formatting, cell data or a comment. */
  2278. if (!(row->row_changed || row->data_changed))
  2279. return;
  2280. /* Write the cells if the row contains data. */
  2281. if (!row->data_changed) {
  2282. /* Row data only. No cells. */
  2283. _write_row(self, row, NULL);
  2284. }
  2285. else {
  2286. /* Row and cell data. */
  2287. _write_row(self, row, NULL);
  2288. for (col = self->dim_colmin; col <= self->dim_colmax; col++) {
  2289. if (self->array[col]) {
  2290. _write_cell(self, self->array[col], row->format);
  2291. _free_cell(self->array[col]);
  2292. self->array[col] = NULL;
  2293. }
  2294. }
  2295. lxw_xml_end_tag(self->file, "row");
  2296. }
  2297. /* Reset the row. */
  2298. row->height = LXW_DEF_ROW_HEIGHT;
  2299. row->format = NULL;
  2300. row->hidden = LXW_FALSE;
  2301. row->level = 0;
  2302. row->collapsed = LXW_FALSE;
  2303. row->data_changed = LXW_FALSE;
  2304. row->row_changed = LXW_FALSE;
  2305. }
  2306. /*
  2307. * Write the <col> element.
  2308. */
  2309. STATIC void
  2310. _worksheet_write_col_info(lxw_worksheet *self, lxw_col_options *options)
  2311. {
  2312. struct xml_attribute_list attributes;
  2313. struct xml_attribute *attribute;
  2314. double width = options->width;
  2315. uint8_t has_custom_width = LXW_TRUE;
  2316. int32_t xf_index = 0;
  2317. double max_digit_width = 7.0; /* For Calabri 11. */
  2318. double padding = 5.0;
  2319. /* Get the format index. */
  2320. if (options->format) {
  2321. xf_index = lxw_format_get_xf_index(options->format);
  2322. }
  2323. /* Check if width is the Excel default. */
  2324. if (width == LXW_DEF_COL_WIDTH) {
  2325. /* The default col width changes to 0 for hidden columns. */
  2326. if (options->hidden)
  2327. width = 0;
  2328. else
  2329. has_custom_width = LXW_FALSE;
  2330. }
  2331. /* Convert column width from user units to character width. */
  2332. if (width > 0) {
  2333. if (width < 1) {
  2334. width = (uint16_t) (((uint16_t)
  2335. (width * (max_digit_width + padding) + 0.5))
  2336. / max_digit_width * 256.0) / 256.0;
  2337. }
  2338. else {
  2339. width = (uint16_t) (((uint16_t)
  2340. (width * max_digit_width + 0.5) + padding)
  2341. / max_digit_width * 256.0) / 256.0;
  2342. }
  2343. }
  2344. LXW_INIT_ATTRIBUTES();
  2345. LXW_PUSH_ATTRIBUTES_INT("min", 1 + options->firstcol);
  2346. LXW_PUSH_ATTRIBUTES_INT("max", 1 + options->lastcol);
  2347. LXW_PUSH_ATTRIBUTES_DBL("width", width);
  2348. if (xf_index)
  2349. LXW_PUSH_ATTRIBUTES_INT("style", xf_index);
  2350. if (options->hidden)
  2351. LXW_PUSH_ATTRIBUTES_STR("hidden", "1");
  2352. if (has_custom_width)
  2353. LXW_PUSH_ATTRIBUTES_STR("customWidth", "1");
  2354. if (options->level)
  2355. LXW_PUSH_ATTRIBUTES_INT("outlineLevel", options->level);
  2356. if (options->collapsed)
  2357. LXW_PUSH_ATTRIBUTES_STR("collapsed", "1");
  2358. lxw_xml_empty_tag(self->file, "col", &attributes);
  2359. LXW_FREE_ATTRIBUTES();
  2360. }
  2361. /*
  2362. * Write the <cols> element and <col> sub elements.
  2363. */
  2364. STATIC void
  2365. _worksheet_write_cols(lxw_worksheet *self)
  2366. {
  2367. lxw_col_t col;
  2368. if (!self->col_size_changed)
  2369. return;
  2370. lxw_xml_start_tag(self->file, "cols", NULL);
  2371. for (col = 0; col < self->col_options_max; col++) {
  2372. if (self->col_options[col])
  2373. _worksheet_write_col_info(self, self->col_options[col]);
  2374. }
  2375. lxw_xml_end_tag(self->file, "cols");
  2376. }
  2377. /*
  2378. * Write the <mergeCell> element.
  2379. */
  2380. STATIC void
  2381. _worksheet_write_merge_cell(lxw_worksheet *self,
  2382. lxw_merged_range *merged_range)
  2383. {
  2384. struct xml_attribute_list attributes;
  2385. struct xml_attribute *attribute;
  2386. char ref[LXW_MAX_CELL_RANGE_LENGTH];
  2387. LXW_INIT_ATTRIBUTES();
  2388. /* Convert the merge dimensions to a cell range. */
  2389. lxw_rowcol_to_range(ref, merged_range->first_row, merged_range->first_col,
  2390. merged_range->last_row, merged_range->last_col);
  2391. LXW_PUSH_ATTRIBUTES_STR("ref", ref);
  2392. lxw_xml_empty_tag(self->file, "mergeCell", &attributes);
  2393. LXW_FREE_ATTRIBUTES();
  2394. }
  2395. /*
  2396. * Write the <mergeCells> element.
  2397. */
  2398. STATIC void
  2399. _worksheet_write_merge_cells(lxw_worksheet *self)
  2400. {
  2401. struct xml_attribute_list attributes;
  2402. struct xml_attribute *attribute;
  2403. lxw_merged_range *merged_range;
  2404. if (self->merged_range_count) {
  2405. LXW_INIT_ATTRIBUTES();
  2406. LXW_PUSH_ATTRIBUTES_INT("count", self->merged_range_count);
  2407. lxw_xml_start_tag(self->file, "mergeCells", &attributes);
  2408. STAILQ_FOREACH(merged_range, self->merged_ranges, list_pointers) {
  2409. _worksheet_write_merge_cell(self, merged_range);
  2410. }
  2411. lxw_xml_end_tag(self->file, "mergeCells");
  2412. LXW_FREE_ATTRIBUTES();
  2413. }
  2414. }
  2415. /*
  2416. * Write the <oddHeader> element.
  2417. */
  2418. STATIC void
  2419. _worksheet_write_odd_header(lxw_worksheet *self)
  2420. {
  2421. lxw_xml_data_element(self->file, "oddHeader", self->header, NULL);
  2422. }
  2423. /*
  2424. * Write the <oddFooter> element.
  2425. */
  2426. STATIC void
  2427. _worksheet_write_odd_footer(lxw_worksheet *self)
  2428. {
  2429. lxw_xml_data_element(self->file, "oddFooter", self->footer, NULL);
  2430. }
  2431. /*
  2432. * Write the <headerFooter> element.
  2433. */
  2434. STATIC void
  2435. _worksheet_write_header_footer(lxw_worksheet *self)
  2436. {
  2437. if (!self->header_footer_changed)
  2438. return;
  2439. lxw_xml_start_tag(self->file, "headerFooter", NULL);
  2440. if (self->header[0] != '\0')
  2441. _worksheet_write_odd_header(self);
  2442. if (self->footer[0] != '\0')
  2443. _worksheet_write_odd_footer(self);
  2444. lxw_xml_end_tag(self->file, "headerFooter");
  2445. }
  2446. /*
  2447. * Write the <pageSetUpPr> element.
  2448. */
  2449. STATIC void
  2450. _worksheet_write_page_set_up_pr(lxw_worksheet *self)
  2451. {
  2452. struct xml_attribute_list attributes;
  2453. struct xml_attribute *attribute;
  2454. if (!self->fit_page)
  2455. return;
  2456. LXW_INIT_ATTRIBUTES();
  2457. LXW_PUSH_ATTRIBUTES_STR("fitToPage", "1");
  2458. lxw_xml_empty_tag(self->file, "pageSetUpPr", &attributes);
  2459. LXW_FREE_ATTRIBUTES();
  2460. }
  2461. /*
  2462. * Write the <tabColor> element.
  2463. */
  2464. STATIC void
  2465. _worksheet_write_tab_color(lxw_worksheet *self)
  2466. {
  2467. struct xml_attribute_list attributes;
  2468. struct xml_attribute *attribute;
  2469. char rgb_str[LXW_ATTR_32];
  2470. if (self->tab_color == LXW_COLOR_UNSET)
  2471. return;
  2472. lxw_snprintf(rgb_str, LXW_ATTR_32, "FF%06X",
  2473. self->tab_color & LXW_COLOR_MASK);
  2474. LXW_INIT_ATTRIBUTES();
  2475. LXW_PUSH_ATTRIBUTES_STR("rgb", rgb_str);
  2476. lxw_xml_empty_tag(self->file, "tabColor", &attributes);
  2477. LXW_FREE_ATTRIBUTES();
  2478. }
  2479. /*
  2480. * Write the <outlinePr> element.
  2481. */
  2482. STATIC void
  2483. _worksheet_write_outline_pr(lxw_worksheet *self)
  2484. {
  2485. struct xml_attribute_list attributes;
  2486. struct xml_attribute *attribute;
  2487. if (!self->outline_changed)
  2488. return;
  2489. LXW_INIT_ATTRIBUTES();
  2490. if (self->outline_style)
  2491. LXW_PUSH_ATTRIBUTES_STR("applyStyles", "1");
  2492. if (!self->outline_below)
  2493. LXW_PUSH_ATTRIBUTES_STR("summaryBelow", "0");
  2494. if (!self->outline_right)
  2495. LXW_PUSH_ATTRIBUTES_STR("summaryRight", "0");
  2496. if (!self->outline_on)
  2497. LXW_PUSH_ATTRIBUTES_STR("showOutlineSymbols", "0");
  2498. lxw_xml_empty_tag(self->file, "outlinePr", &attributes);
  2499. LXW_FREE_ATTRIBUTES();
  2500. }
  2501. /*
  2502. * Write the <sheetPr> element for Sheet level properties.
  2503. */
  2504. STATIC void
  2505. _worksheet_write_sheet_pr(lxw_worksheet *self)
  2506. {
  2507. struct xml_attribute_list attributes;
  2508. struct xml_attribute *attribute;
  2509. if (!self->fit_page
  2510. && !self->filter_on
  2511. && self->tab_color == LXW_COLOR_UNSET
  2512. && !self->outline_changed && !self->vba_codename) {
  2513. return;
  2514. }
  2515. LXW_INIT_ATTRIBUTES();
  2516. if (self->vba_codename)
  2517. LXW_PUSH_ATTRIBUTES_INT("codeName", self->vba_codename);
  2518. if (self->filter_on)
  2519. LXW_PUSH_ATTRIBUTES_STR("filterMode", "1");
  2520. if (self->fit_page || self->tab_color != LXW_COLOR_UNSET
  2521. || self->outline_changed) {
  2522. lxw_xml_start_tag(self->file, "sheetPr", &attributes);
  2523. _worksheet_write_tab_color(self);
  2524. _worksheet_write_outline_pr(self);
  2525. _worksheet_write_page_set_up_pr(self);
  2526. lxw_xml_end_tag(self->file, "sheetPr");
  2527. }
  2528. else {
  2529. lxw_xml_empty_tag(self->file, "sheetPr", &attributes);
  2530. }
  2531. LXW_FREE_ATTRIBUTES();
  2532. }
  2533. /*
  2534. * Write the <brk> element.
  2535. */
  2536. STATIC void
  2537. _worksheet_write_brk(lxw_worksheet *self, uint32_t id, uint32_t max)
  2538. {
  2539. struct xml_attribute_list attributes;
  2540. struct xml_attribute *attribute;
  2541. LXW_INIT_ATTRIBUTES();
  2542. LXW_PUSH_ATTRIBUTES_INT("id", id);
  2543. LXW_PUSH_ATTRIBUTES_INT("max", max);
  2544. LXW_PUSH_ATTRIBUTES_STR("man", "1");
  2545. lxw_xml_empty_tag(self->file, "brk", &attributes);
  2546. LXW_FREE_ATTRIBUTES();
  2547. }
  2548. /*
  2549. * Write the <rowBreaks> element.
  2550. */
  2551. STATIC void
  2552. _worksheet_write_row_breaks(lxw_worksheet *self)
  2553. {
  2554. struct xml_attribute_list attributes;
  2555. struct xml_attribute *attribute;
  2556. uint16_t count = self->hbreaks_count;
  2557. uint16_t i;
  2558. if (!count)
  2559. return;
  2560. LXW_INIT_ATTRIBUTES();
  2561. LXW_PUSH_ATTRIBUTES_INT("count", count);
  2562. LXW_PUSH_ATTRIBUTES_INT("manualBreakCount", count);
  2563. lxw_xml_start_tag(self->file, "rowBreaks", &attributes);
  2564. for (i = 0; i < count; i++)
  2565. _worksheet_write_brk(self, self->hbreaks[i], LXW_COL_MAX - 1);
  2566. lxw_xml_end_tag(self->file, "rowBreaks");
  2567. LXW_FREE_ATTRIBUTES();
  2568. }
  2569. /*
  2570. * Write the <colBreaks> element.
  2571. */
  2572. STATIC void
  2573. _worksheet_write_col_breaks(lxw_worksheet *self)
  2574. {
  2575. struct xml_attribute_list attributes;
  2576. struct xml_attribute *attribute;
  2577. uint16_t count = self->vbreaks_count;
  2578. uint16_t i;
  2579. if (!count)
  2580. return;
  2581. LXW_INIT_ATTRIBUTES();
  2582. LXW_PUSH_ATTRIBUTES_INT("count", count);
  2583. LXW_PUSH_ATTRIBUTES_INT("manualBreakCount", count);
  2584. lxw_xml_start_tag(self->file, "colBreaks", &attributes);
  2585. for (i = 0; i < count; i++)
  2586. _worksheet_write_brk(self, self->vbreaks[i], LXW_ROW_MAX - 1);
  2587. lxw_xml_end_tag(self->file, "colBreaks");
  2588. LXW_FREE_ATTRIBUTES();
  2589. }
  2590. /*
  2591. * Write the <autoFilter> element.
  2592. */
  2593. STATIC void
  2594. _worksheet_write_auto_filter(lxw_worksheet *self)
  2595. {
  2596. struct xml_attribute_list attributes;
  2597. struct xml_attribute *attribute;
  2598. char range[LXW_MAX_CELL_RANGE_LENGTH];
  2599. if (!self->autofilter.in_use)
  2600. return;
  2601. lxw_rowcol_to_range(range,
  2602. self->autofilter.first_row,
  2603. self->autofilter.first_col,
  2604. self->autofilter.last_row, self->autofilter.last_col);
  2605. LXW_INIT_ATTRIBUTES();
  2606. LXW_PUSH_ATTRIBUTES_STR("ref", range);
  2607. lxw_xml_empty_tag(self->file, "autoFilter", &attributes);
  2608. LXW_FREE_ATTRIBUTES();
  2609. }
  2610. /*
  2611. * Write the <hyperlink> element for external links.
  2612. */
  2613. STATIC void
  2614. _worksheet_write_hyperlink_external(lxw_worksheet *self, lxw_row_t row_num,
  2615. lxw_col_t col_num, const char *location,
  2616. const char *tooltip, uint16_t id)
  2617. {
  2618. struct xml_attribute_list attributes;
  2619. struct xml_attribute *attribute;
  2620. char ref[LXW_MAX_CELL_NAME_LENGTH];
  2621. char r_id[LXW_MAX_ATTRIBUTE_LENGTH];
  2622. lxw_rowcol_to_cell(ref, row_num, col_num);
  2623. lxw_snprintf(r_id, LXW_ATTR_32, "rId%d", id);
  2624. LXW_INIT_ATTRIBUTES();
  2625. LXW_PUSH_ATTRIBUTES_STR("ref", ref);
  2626. LXW_PUSH_ATTRIBUTES_STR("r:id", r_id);
  2627. if (location)
  2628. LXW_PUSH_ATTRIBUTES_STR("location", location);
  2629. if (tooltip)
  2630. LXW_PUSH_ATTRIBUTES_STR("tooltip", tooltip);
  2631. lxw_xml_empty_tag(self->file, "hyperlink", &attributes);
  2632. LXW_FREE_ATTRIBUTES();
  2633. }
  2634. /*
  2635. * Write the <hyperlink> element for internal links.
  2636. */
  2637. STATIC void
  2638. _worksheet_write_hyperlink_internal(lxw_worksheet *self, lxw_row_t row_num,
  2639. lxw_col_t col_num, const char *location,
  2640. const char *display, const char *tooltip)
  2641. {
  2642. struct xml_attribute_list attributes;
  2643. struct xml_attribute *attribute;
  2644. char ref[LXW_MAX_CELL_NAME_LENGTH];
  2645. lxw_rowcol_to_cell(ref, row_num, col_num);
  2646. LXW_INIT_ATTRIBUTES();
  2647. LXW_PUSH_ATTRIBUTES_STR("ref", ref);
  2648. if (location)
  2649. LXW_PUSH_ATTRIBUTES_STR("location", location);
  2650. if (tooltip)
  2651. LXW_PUSH_ATTRIBUTES_STR("tooltip", tooltip);
  2652. if (display)
  2653. LXW_PUSH_ATTRIBUTES_STR("display", display);
  2654. lxw_xml_empty_tag(self->file, "hyperlink", &attributes);
  2655. LXW_FREE_ATTRIBUTES();
  2656. }
  2657. /*
  2658. * Process any stored hyperlinks in row/col order and write the <hyperlinks>
  2659. * element. The attributes are different for internal and external links.
  2660. */
  2661. STATIC void
  2662. _worksheet_write_hyperlinks(lxw_worksheet *self)
  2663. {
  2664. lxw_row *row;
  2665. lxw_cell *link;
  2666. lxw_rel_tuple *relationship;
  2667. if (RB_EMPTY(self->hyperlinks))
  2668. return;
  2669. /* Write the hyperlink elements. */
  2670. lxw_xml_start_tag(self->file, "hyperlinks", NULL);
  2671. RB_FOREACH(row, lxw_table_rows, self->hyperlinks) {
  2672. RB_FOREACH(link, lxw_table_cells, row->cells) {
  2673. if (link->type == HYPERLINK_URL
  2674. || link->type == HYPERLINK_EXTERNAL) {
  2675. self->rel_count++;
  2676. relationship = calloc(1, sizeof(lxw_rel_tuple));
  2677. GOTO_LABEL_ON_MEM_ERROR(relationship, mem_error);
  2678. relationship->type = lxw_strdup("/hyperlink");
  2679. GOTO_LABEL_ON_MEM_ERROR(relationship->type, mem_error);
  2680. relationship->target = lxw_strdup(link->u.string);
  2681. GOTO_LABEL_ON_MEM_ERROR(relationship->target, mem_error);
  2682. relationship->target_mode = lxw_strdup("External");
  2683. GOTO_LABEL_ON_MEM_ERROR(relationship->target_mode, mem_error);
  2684. STAILQ_INSERT_TAIL(self->external_hyperlinks, relationship,
  2685. list_pointers);
  2686. _worksheet_write_hyperlink_external(self, link->row_num,
  2687. link->col_num,
  2688. link->user_data1,
  2689. link->user_data2,
  2690. self->rel_count);
  2691. }
  2692. if (link->type == HYPERLINK_INTERNAL) {
  2693. _worksheet_write_hyperlink_internal(self, link->row_num,
  2694. link->col_num,
  2695. link->u.string,
  2696. link->user_data1,
  2697. link->user_data2);
  2698. }
  2699. }
  2700. }
  2701. lxw_xml_end_tag(self->file, "hyperlinks");
  2702. return;
  2703. mem_error:
  2704. if (relationship) {
  2705. free(relationship->type);
  2706. free(relationship->target);
  2707. free(relationship->target_mode);
  2708. free(relationship);
  2709. }
  2710. lxw_xml_end_tag(self->file, "hyperlinks");
  2711. }
  2712. /*
  2713. * Write the <sheetProtection> element.
  2714. */
  2715. STATIC void
  2716. _worksheet_write_sheet_protection(lxw_worksheet *self)
  2717. {
  2718. struct xml_attribute_list attributes;
  2719. struct xml_attribute *attribute;
  2720. struct lxw_protection *protect = &self->protection;
  2721. if (!protect->is_configured)
  2722. return;
  2723. LXW_INIT_ATTRIBUTES();
  2724. if (*protect->hash)
  2725. LXW_PUSH_ATTRIBUTES_STR("password", protect->hash);
  2726. if (!protect->no_sheet)
  2727. LXW_PUSH_ATTRIBUTES_INT("sheet", 1);
  2728. if (protect->content)
  2729. LXW_PUSH_ATTRIBUTES_INT("content", 1);
  2730. if (!protect->objects)
  2731. LXW_PUSH_ATTRIBUTES_INT("objects", 1);
  2732. if (!protect->scenarios)
  2733. LXW_PUSH_ATTRIBUTES_INT("scenarios", 1);
  2734. if (protect->format_cells)
  2735. LXW_PUSH_ATTRIBUTES_INT("formatCells", 0);
  2736. if (protect->format_columns)
  2737. LXW_PUSH_ATTRIBUTES_INT("formatColumns", 0);
  2738. if (protect->format_rows)
  2739. LXW_PUSH_ATTRIBUTES_INT("formatRows", 0);
  2740. if (protect->insert_columns)
  2741. LXW_PUSH_ATTRIBUTES_INT("insertColumns", 0);
  2742. if (protect->insert_rows)
  2743. LXW_PUSH_ATTRIBUTES_INT("insertRows", 0);
  2744. if (protect->insert_hyperlinks)
  2745. LXW_PUSH_ATTRIBUTES_INT("insertHyperlinks", 0);
  2746. if (protect->delete_columns)
  2747. LXW_PUSH_ATTRIBUTES_INT("deleteColumns", 0);
  2748. if (protect->delete_rows)
  2749. LXW_PUSH_ATTRIBUTES_INT("deleteRows", 0);
  2750. if (protect->no_select_locked_cells)
  2751. LXW_PUSH_ATTRIBUTES_INT("selectLockedCells", 1);
  2752. if (protect->sort)
  2753. LXW_PUSH_ATTRIBUTES_INT("sort", 0);
  2754. if (protect->autofilter)
  2755. LXW_PUSH_ATTRIBUTES_INT("autoFilter", 0);
  2756. if (protect->pivot_tables)
  2757. LXW_PUSH_ATTRIBUTES_INT("pivotTables", 0);
  2758. if (protect->no_select_unlocked_cells)
  2759. LXW_PUSH_ATTRIBUTES_INT("selectUnlockedCells", 1);
  2760. lxw_xml_empty_tag(self->file, "sheetProtection", &attributes);
  2761. LXW_FREE_ATTRIBUTES();
  2762. }
  2763. /*
  2764. * Write the <drawing> element.
  2765. */
  2766. STATIC void
  2767. _write_drawing(lxw_worksheet *self, uint16_t id)
  2768. {
  2769. struct xml_attribute_list attributes;
  2770. struct xml_attribute *attribute;
  2771. char r_id[LXW_MAX_ATTRIBUTE_LENGTH];
  2772. lxw_snprintf(r_id, LXW_ATTR_32, "rId%d", id);
  2773. LXW_INIT_ATTRIBUTES();
  2774. LXW_PUSH_ATTRIBUTES_STR("r:id", r_id);
  2775. lxw_xml_empty_tag(self->file, "drawing", &attributes);
  2776. LXW_FREE_ATTRIBUTES();
  2777. }
  2778. /*
  2779. * Write the <drawing> elements.
  2780. */
  2781. STATIC void
  2782. _write_drawings(lxw_worksheet *self)
  2783. {
  2784. if (!self->drawing)
  2785. return;
  2786. self->rel_count++;
  2787. _write_drawing(self, self->rel_count);
  2788. }
  2789. /*
  2790. * Write the <formula1> element for numbers.
  2791. */
  2792. STATIC void
  2793. _worksheet_write_formula1_num(lxw_worksheet *self, double number)
  2794. {
  2795. char data[LXW_ATTR_32];
  2796. lxw_sprintf_dbl(data, number);
  2797. lxw_xml_data_element(self->file, "formula1", data, NULL);
  2798. }
  2799. /*
  2800. * Write the <formula1> element for strings/formulas.
  2801. */
  2802. STATIC void
  2803. _worksheet_write_formula1_str(lxw_worksheet *self, char *str)
  2804. {
  2805. lxw_xml_data_element(self->file, "formula1", str, NULL);
  2806. }
  2807. /*
  2808. * Write the <formula2> element for numbers.
  2809. */
  2810. STATIC void
  2811. _worksheet_write_formula2_num(lxw_worksheet *self, double number)
  2812. {
  2813. char data[LXW_ATTR_32];
  2814. lxw_sprintf_dbl(data, number);
  2815. lxw_xml_data_element(self->file, "formula2", data, NULL);
  2816. }
  2817. /*
  2818. * Write the <formula2> element for strings/formulas.
  2819. */
  2820. STATIC void
  2821. _worksheet_write_formula2_str(lxw_worksheet *self, char *str)
  2822. {
  2823. lxw_xml_data_element(self->file, "formula2", str, NULL);
  2824. }
  2825. /*
  2826. * Write the <dataValidation> element.
  2827. */
  2828. STATIC void
  2829. _worksheet_write_data_validation(lxw_worksheet *self,
  2830. lxw_data_validation *validation)
  2831. {
  2832. struct xml_attribute_list attributes;
  2833. struct xml_attribute *attribute;
  2834. uint8_t is_between = 0;
  2835. LXW_INIT_ATTRIBUTES();
  2836. switch (validation->validate) {
  2837. case LXW_VALIDATION_TYPE_INTEGER:
  2838. case LXW_VALIDATION_TYPE_INTEGER_FORMULA:
  2839. LXW_PUSH_ATTRIBUTES_STR("type", "whole");
  2840. break;
  2841. case LXW_VALIDATION_TYPE_DECIMAL:
  2842. case LXW_VALIDATION_TYPE_DECIMAL_FORMULA:
  2843. LXW_PUSH_ATTRIBUTES_STR("type", "decimal");
  2844. break;
  2845. case LXW_VALIDATION_TYPE_LIST:
  2846. case LXW_VALIDATION_TYPE_LIST_FORMULA:
  2847. LXW_PUSH_ATTRIBUTES_STR("type", "list");
  2848. break;
  2849. case LXW_VALIDATION_TYPE_DATE:
  2850. case LXW_VALIDATION_TYPE_DATE_FORMULA:
  2851. case LXW_VALIDATION_TYPE_DATE_NUMBER:
  2852. LXW_PUSH_ATTRIBUTES_STR("type", "date");
  2853. break;
  2854. case LXW_VALIDATION_TYPE_TIME:
  2855. case LXW_VALIDATION_TYPE_TIME_FORMULA:
  2856. case LXW_VALIDATION_TYPE_TIME_NUMBER:
  2857. LXW_PUSH_ATTRIBUTES_STR("type", "time");
  2858. break;
  2859. case LXW_VALIDATION_TYPE_LENGTH:
  2860. case LXW_VALIDATION_TYPE_LENGTH_FORMULA:
  2861. LXW_PUSH_ATTRIBUTES_STR("type", "textLength");
  2862. break;
  2863. case LXW_VALIDATION_TYPE_CUSTOM_FORMULA:
  2864. LXW_PUSH_ATTRIBUTES_STR("type", "custom");
  2865. break;
  2866. }
  2867. switch (validation->criteria) {
  2868. case LXW_VALIDATION_CRITERIA_EQUAL_TO:
  2869. LXW_PUSH_ATTRIBUTES_STR("operator", "equal");
  2870. break;
  2871. case LXW_VALIDATION_CRITERIA_NOT_EQUAL_TO:
  2872. LXW_PUSH_ATTRIBUTES_STR("operator", "notEqual");
  2873. break;
  2874. case LXW_VALIDATION_CRITERIA_LESS_THAN:
  2875. LXW_PUSH_ATTRIBUTES_STR("operator", "lessThan");
  2876. break;
  2877. case LXW_VALIDATION_CRITERIA_LESS_THAN_OR_EQUAL_TO:
  2878. LXW_PUSH_ATTRIBUTES_STR("operator", "lessThanOrEqual");
  2879. break;
  2880. case LXW_VALIDATION_CRITERIA_GREATER_THAN:
  2881. LXW_PUSH_ATTRIBUTES_STR("operator", "greaterThan");
  2882. break;
  2883. case LXW_VALIDATION_CRITERIA_GREATER_THAN_OR_EQUAL_TO:
  2884. LXW_PUSH_ATTRIBUTES_STR("operator", "greaterThanOrEqual");
  2885. break;
  2886. case LXW_VALIDATION_CRITERIA_BETWEEN:
  2887. /* Between is the default for 2 formulas and isn't added. */
  2888. is_between = 1;
  2889. break;
  2890. case LXW_VALIDATION_CRITERIA_NOT_BETWEEN:
  2891. is_between = 1;
  2892. LXW_PUSH_ATTRIBUTES_STR("operator", "notBetween");
  2893. break;
  2894. }
  2895. if (validation->error_type == LXW_VALIDATION_ERROR_TYPE_WARNING)
  2896. LXW_PUSH_ATTRIBUTES_STR("errorStyle", "warning");
  2897. if (validation->error_type == LXW_VALIDATION_ERROR_TYPE_INFORMATION)
  2898. LXW_PUSH_ATTRIBUTES_STR("errorStyle", "information");
  2899. if (validation->ignore_blank)
  2900. LXW_PUSH_ATTRIBUTES_INT("allowBlank", 1);
  2901. if (validation->dropdown == LXW_VALIDATION_OFF)
  2902. LXW_PUSH_ATTRIBUTES_INT("showDropDown", 1);
  2903. if (validation->show_input)
  2904. LXW_PUSH_ATTRIBUTES_INT("showInputMessage", 1);
  2905. if (validation->show_error)
  2906. LXW_PUSH_ATTRIBUTES_INT("showErrorMessage", 1);
  2907. if (validation->error_title)
  2908. LXW_PUSH_ATTRIBUTES_STR("errorTitle", validation->error_title);
  2909. if (validation->error_message)
  2910. LXW_PUSH_ATTRIBUTES_STR("error", validation->error_message);
  2911. if (validation->input_title)
  2912. LXW_PUSH_ATTRIBUTES_STR("promptTitle", validation->input_title);
  2913. if (validation->input_message)
  2914. LXW_PUSH_ATTRIBUTES_STR("prompt", validation->input_message);
  2915. LXW_PUSH_ATTRIBUTES_STR("sqref", validation->sqref);
  2916. if (validation->validate == LXW_VALIDATION_TYPE_ANY)
  2917. lxw_xml_empty_tag(self->file, "dataValidation", &attributes);
  2918. else
  2919. lxw_xml_start_tag(self->file, "dataValidation", &attributes);
  2920. /* Write the formula1 and formula2 elements. */
  2921. switch (validation->validate) {
  2922. case LXW_VALIDATION_TYPE_INTEGER:
  2923. case LXW_VALIDATION_TYPE_DECIMAL:
  2924. case LXW_VALIDATION_TYPE_LENGTH:
  2925. case LXW_VALIDATION_TYPE_DATE:
  2926. case LXW_VALIDATION_TYPE_TIME:
  2927. case LXW_VALIDATION_TYPE_DATE_NUMBER:
  2928. case LXW_VALIDATION_TYPE_TIME_NUMBER:
  2929. _worksheet_write_formula1_num(self, validation->value_number);
  2930. if (is_between)
  2931. _worksheet_write_formula2_num(self,
  2932. validation->maximum_number);
  2933. break;
  2934. case LXW_VALIDATION_TYPE_INTEGER_FORMULA:
  2935. case LXW_VALIDATION_TYPE_DECIMAL_FORMULA:
  2936. case LXW_VALIDATION_TYPE_LENGTH_FORMULA:
  2937. case LXW_VALIDATION_TYPE_DATE_FORMULA:
  2938. case LXW_VALIDATION_TYPE_TIME_FORMULA:
  2939. case LXW_VALIDATION_TYPE_LIST:
  2940. case LXW_VALIDATION_TYPE_LIST_FORMULA:
  2941. case LXW_VALIDATION_TYPE_CUSTOM_FORMULA:
  2942. _worksheet_write_formula1_str(self, validation->value_formula);
  2943. if (is_between)
  2944. _worksheet_write_formula2_str(self,
  2945. validation->maximum_formula);
  2946. break;
  2947. }
  2948. if (validation->validate != LXW_VALIDATION_TYPE_ANY)
  2949. lxw_xml_end_tag(self->file, "dataValidation");
  2950. LXW_FREE_ATTRIBUTES();
  2951. }
  2952. /*
  2953. * Write the <dataValidations> element.
  2954. */
  2955. STATIC void
  2956. _worksheet_write_data_validations(lxw_worksheet *self)
  2957. {
  2958. struct xml_attribute_list attributes;
  2959. struct xml_attribute *attribute;
  2960. lxw_data_validation *data_validation;
  2961. if (self->num_validations == 0)
  2962. return;
  2963. LXW_INIT_ATTRIBUTES();
  2964. LXW_PUSH_ATTRIBUTES_INT("count", self->num_validations);
  2965. lxw_xml_start_tag(self->file, "dataValidations", &attributes);
  2966. STAILQ_FOREACH(data_validation, self->data_validations, list_pointers) {
  2967. /* Write the dataValidation element. */
  2968. _worksheet_write_data_validation(self, data_validation);
  2969. }
  2970. lxw_xml_end_tag(self->file, "dataValidations");
  2971. LXW_FREE_ATTRIBUTES();
  2972. }
  2973. /*
  2974. * Assemble and write the XML file.
  2975. */
  2976. void
  2977. lxw_worksheet_assemble_xml_file(lxw_worksheet *self)
  2978. {
  2979. /* Write the XML declaration. */
  2980. _worksheet_xml_declaration(self);
  2981. /* Write the worksheet element. */
  2982. _worksheet_write_worksheet(self);
  2983. /* Write the worksheet properties. */
  2984. _worksheet_write_sheet_pr(self);
  2985. /* Write the worksheet dimensions. */
  2986. _worksheet_write_dimension(self);
  2987. /* Write the sheet view properties. */
  2988. _worksheet_write_sheet_views(self);
  2989. /* Write the sheet format properties. */
  2990. _worksheet_write_sheet_format_pr(self);
  2991. /* Write the sheet column info. */
  2992. _worksheet_write_cols(self);
  2993. /* Write the sheetData element. */
  2994. if (!self->optimize)
  2995. _worksheet_write_sheet_data(self);
  2996. else
  2997. _worksheet_write_optimized_sheet_data(self);
  2998. /* Write the sheetProtection element. */
  2999. _worksheet_write_sheet_protection(self);
  3000. /* Write the autoFilter element. */
  3001. _worksheet_write_auto_filter(self);
  3002. /* Write the mergeCells element. */
  3003. _worksheet_write_merge_cells(self);
  3004. /* Write the dataValidations element. */
  3005. _worksheet_write_data_validations(self);
  3006. /* Write the hyperlink element. */
  3007. _worksheet_write_hyperlinks(self);
  3008. /* Write the printOptions element. */
  3009. _worksheet_write_print_options(self);
  3010. /* Write the worksheet page_margins. */
  3011. _worksheet_write_page_margins(self);
  3012. /* Write the worksheet page setup. */
  3013. _worksheet_write_page_setup(self);
  3014. /* Write the headerFooter element. */
  3015. _worksheet_write_header_footer(self);
  3016. /* Write the rowBreaks element. */
  3017. _worksheet_write_row_breaks(self);
  3018. /* Write the colBreaks element. */
  3019. _worksheet_write_col_breaks(self);
  3020. /* Write the drawing element. */
  3021. _write_drawings(self);
  3022. /* Close the worksheet tag. */
  3023. lxw_xml_end_tag(self->file, "worksheet");
  3024. }
  3025. /*****************************************************************************
  3026. *
  3027. * Public functions.
  3028. *
  3029. ****************************************************************************/
  3030. /*
  3031. * Write a number to a cell in Excel.
  3032. */
  3033. lxw_error
  3034. worksheet_write_number(lxw_worksheet *self,
  3035. lxw_row_t row_num,
  3036. lxw_col_t col_num, double value, lxw_format *format)
  3037. {
  3038. lxw_cell *cell;
  3039. lxw_error err;
  3040. err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE);
  3041. if (err)
  3042. return err;
  3043. cell = _new_number_cell(row_num, col_num, value, format);
  3044. _insert_cell(self, row_num, col_num, cell);
  3045. return LXW_NO_ERROR;
  3046. }
  3047. /*
  3048. * Write a string to an Excel file.
  3049. */
  3050. lxw_error
  3051. worksheet_write_string(lxw_worksheet *self,
  3052. lxw_row_t row_num,
  3053. lxw_col_t col_num, const char *string,
  3054. lxw_format *format)
  3055. {
  3056. lxw_cell *cell;
  3057. int32_t string_id;
  3058. char *string_copy;
  3059. struct sst_element *sst_element;
  3060. lxw_error err;
  3061. if (!string || !*string) {
  3062. /* Treat a NULL or empty string with formatting as a blank cell. */
  3063. /* Null strings without formats should be ignored. */
  3064. if (format)
  3065. return worksheet_write_blank(self, row_num, col_num, format);
  3066. else
  3067. return LXW_NO_ERROR;
  3068. }
  3069. err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE);
  3070. if (err)
  3071. return err;
  3072. if (lxw_utf8_strlen(string) > LXW_STR_MAX)
  3073. return LXW_ERROR_MAX_STRING_LENGTH_EXCEEDED;
  3074. if (!self->optimize) {
  3075. /* Get the SST element and string id. */
  3076. sst_element = lxw_get_sst_index(self->sst, string);
  3077. if (!sst_element)
  3078. return LXW_ERROR_SHARED_STRING_INDEX_NOT_FOUND;
  3079. string_id = sst_element->index;
  3080. cell = _new_string_cell(row_num, col_num, string_id,
  3081. sst_element->string, format);
  3082. }
  3083. else {
  3084. /* Look for and escape control chars in the string. */
  3085. if (strpbrk(string, "\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C"
  3086. "\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16"
  3087. "\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F")) {
  3088. string_copy = lxw_escape_control_characters(string);
  3089. }
  3090. else {
  3091. string_copy = lxw_strdup(string);
  3092. }
  3093. cell = _new_inline_string_cell(row_num, col_num, string_copy, format);
  3094. }
  3095. _insert_cell(self, row_num, col_num, cell);
  3096. return LXW_NO_ERROR;
  3097. }
  3098. /*
  3099. * Write a formula with a numerical result to a cell in Excel.
  3100. */
  3101. lxw_error
  3102. worksheet_write_formula_num(lxw_worksheet *self,
  3103. lxw_row_t row_num,
  3104. lxw_col_t col_num,
  3105. const char *formula,
  3106. lxw_format *format, double result)
  3107. {
  3108. lxw_cell *cell;
  3109. char *formula_copy;
  3110. lxw_error err;
  3111. if (!formula)
  3112. return LXW_ERROR_NULL_PARAMETER_IGNORED;
  3113. err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE);
  3114. if (err)
  3115. return err;
  3116. /* Strip leading "=" from formula. */
  3117. if (formula[0] == '=')
  3118. formula_copy = lxw_strdup(formula + 1);
  3119. else
  3120. formula_copy = lxw_strdup(formula);
  3121. cell = _new_formula_cell(row_num, col_num, formula_copy, format);
  3122. cell->formula_result = result;
  3123. _insert_cell(self, row_num, col_num, cell);
  3124. return LXW_NO_ERROR;
  3125. }
  3126. /*
  3127. * Write a formula with a default result to a cell in Excel .
  3128. */
  3129. lxw_error
  3130. worksheet_write_formula(lxw_worksheet *self,
  3131. lxw_row_t row_num,
  3132. lxw_col_t col_num, const char *formula,
  3133. lxw_format *format)
  3134. {
  3135. return worksheet_write_formula_num(self, row_num, col_num, formula,
  3136. format, 0);
  3137. }
  3138. /*
  3139. * Write a formula with a numerical result to a cell in Excel.
  3140. */
  3141. lxw_error
  3142. worksheet_write_array_formula_num(lxw_worksheet *self,
  3143. lxw_row_t first_row,
  3144. lxw_col_t first_col,
  3145. lxw_row_t last_row,
  3146. lxw_col_t last_col,
  3147. const char *formula,
  3148. lxw_format *format, double result)
  3149. {
  3150. lxw_cell *cell;
  3151. lxw_row_t tmp_row;
  3152. lxw_col_t tmp_col;
  3153. char *formula_copy;
  3154. char *range;
  3155. lxw_error err;
  3156. /* Swap last row/col with first row/col as necessary */
  3157. if (first_row > last_row) {
  3158. tmp_row = last_row;
  3159. last_row = first_row;
  3160. first_row = tmp_row;
  3161. }
  3162. if (first_col > last_col) {
  3163. tmp_col = last_col;
  3164. last_col = first_col;
  3165. first_col = tmp_col;
  3166. }
  3167. if (!formula)
  3168. return LXW_ERROR_NULL_PARAMETER_IGNORED;
  3169. /* Check that column number is valid and store the max value */
  3170. err = _check_dimensions(self, last_row, last_col, LXW_FALSE, LXW_FALSE);
  3171. if (err)
  3172. return err;
  3173. /* Define the array range. */
  3174. range = calloc(1, LXW_MAX_CELL_RANGE_LENGTH);
  3175. RETURN_ON_MEM_ERROR(range, LXW_ERROR_MEMORY_MALLOC_FAILED);
  3176. if (first_row == last_row && first_col == last_col)
  3177. lxw_rowcol_to_cell(range, first_row, last_col);
  3178. else
  3179. lxw_rowcol_to_range(range, first_row, first_col, last_row, last_col);
  3180. /* Copy and trip leading "{=" from formula. */
  3181. if (formula[0] == '{')
  3182. if (formula[1] == '=')
  3183. formula_copy = lxw_strdup(formula + 2);
  3184. else
  3185. formula_copy = lxw_strdup(formula + 1);
  3186. else
  3187. formula_copy = lxw_strdup(formula);
  3188. /* Strip trailing "}" from formula. */
  3189. if (formula_copy[strlen(formula_copy) - 1] == '}')
  3190. formula_copy[strlen(formula_copy) - 1] = '\0';
  3191. /* Create a new array formula cell object. */
  3192. cell = _new_array_formula_cell(first_row, first_col,
  3193. formula_copy, range, format);
  3194. cell->formula_result = result;
  3195. _insert_cell(self, first_row, first_col, cell);
  3196. /* Pad out the rest of the area with formatted zeroes. */
  3197. if (!self->optimize) {
  3198. for (tmp_row = first_row; tmp_row <= last_row; tmp_row++) {
  3199. for (tmp_col = first_col; tmp_col <= last_col; tmp_col++) {
  3200. if (tmp_row == first_row && tmp_col == first_col)
  3201. continue;
  3202. worksheet_write_number(self, tmp_row, tmp_col, 0, format);
  3203. }
  3204. }
  3205. }
  3206. return LXW_NO_ERROR;
  3207. }
  3208. /*
  3209. * Write an array formula with a default result to a cell in Excel .
  3210. */
  3211. lxw_error
  3212. worksheet_write_array_formula(lxw_worksheet *self,
  3213. lxw_row_t first_row,
  3214. lxw_col_t first_col,
  3215. lxw_row_t last_row,
  3216. lxw_col_t last_col,
  3217. const char *formula, lxw_format *format)
  3218. {
  3219. return worksheet_write_array_formula_num(self, first_row, first_col,
  3220. last_row, last_col, formula,
  3221. format, 0);
  3222. }
  3223. /*
  3224. * Write a blank cell with a format to a cell in Excel.
  3225. */
  3226. lxw_error
  3227. worksheet_write_blank(lxw_worksheet *self,
  3228. lxw_row_t row_num, lxw_col_t col_num,
  3229. lxw_format *format)
  3230. {
  3231. lxw_cell *cell;
  3232. lxw_error err;
  3233. /* Blank cells without formatting are ignored by Excel. */
  3234. if (!format)
  3235. return LXW_NO_ERROR;
  3236. err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE);
  3237. if (err)
  3238. return err;
  3239. cell = _new_blank_cell(row_num, col_num, format);
  3240. _insert_cell(self, row_num, col_num, cell);
  3241. return LXW_NO_ERROR;
  3242. }
  3243. /*
  3244. * Write a boolean cell with a format to a cell in Excel.
  3245. */
  3246. lxw_error
  3247. worksheet_write_boolean(lxw_worksheet *self,
  3248. lxw_row_t row_num, lxw_col_t col_num,
  3249. int value, lxw_format *format)
  3250. {
  3251. lxw_cell *cell;
  3252. lxw_error err;
  3253. err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE);
  3254. if (err)
  3255. return err;
  3256. cell = _new_boolean_cell(row_num, col_num, value, format);
  3257. _insert_cell(self, row_num, col_num, cell);
  3258. return LXW_NO_ERROR;
  3259. }
  3260. /*
  3261. * Write a date and or time to a cell in Excel.
  3262. */
  3263. lxw_error
  3264. worksheet_write_datetime(lxw_worksheet *self,
  3265. lxw_row_t row_num,
  3266. lxw_col_t col_num, lxw_datetime *datetime,
  3267. lxw_format *format)
  3268. {
  3269. lxw_cell *cell;
  3270. double excel_date;
  3271. lxw_error err;
  3272. err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE);
  3273. if (err)
  3274. return err;
  3275. excel_date = lxw_datetime_to_excel_date(datetime, LXW_EPOCH_1900);
  3276. cell = _new_number_cell(row_num, col_num, excel_date, format);
  3277. _insert_cell(self, row_num, col_num, cell);
  3278. return LXW_NO_ERROR;
  3279. }
  3280. /*
  3281. * Write a hyperlink/url to an Excel file.
  3282. */
  3283. lxw_error
  3284. worksheet_write_url_opt(lxw_worksheet *self,
  3285. lxw_row_t row_num,
  3286. lxw_col_t col_num, const char *url,
  3287. lxw_format *format, const char *string,
  3288. const char *tooltip)
  3289. {
  3290. lxw_cell *link;
  3291. char *string_copy = NULL;
  3292. char *url_copy = NULL;
  3293. char *url_external = NULL;
  3294. char *url_string = NULL;
  3295. char *tooltip_copy = NULL;
  3296. char *found_string;
  3297. lxw_error err;
  3298. size_t string_size;
  3299. size_t i;
  3300. enum cell_types link_type = HYPERLINK_URL;
  3301. if (!url || !*url)
  3302. return LXW_ERROR_NULL_PARAMETER_IGNORED;
  3303. /* Check the Excel limit of URLS per worksheet. */
  3304. if (self->hlink_count > LXW_MAX_NUMBER_URLS)
  3305. return LXW_ERROR_WORKSHEET_MAX_NUMBER_URLS_EXCEEDED;
  3306. err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE);
  3307. if (err)
  3308. return err;
  3309. /* Set the URI scheme from internal links. */
  3310. found_string = strstr(url, "internal:");
  3311. if (found_string)
  3312. link_type = HYPERLINK_INTERNAL;
  3313. /* Set the URI scheme from external links. */
  3314. found_string = strstr(url, "external:");
  3315. if (found_string)
  3316. link_type = HYPERLINK_EXTERNAL;
  3317. if (string) {
  3318. string_copy = lxw_strdup(string);
  3319. GOTO_LABEL_ON_MEM_ERROR(string_copy, mem_error);
  3320. }
  3321. else {
  3322. if (link_type == HYPERLINK_URL) {
  3323. /* Strip the mailto header. */
  3324. found_string = strstr(url, "mailto:");
  3325. if (found_string)
  3326. string_copy = lxw_strdup(url + sizeof("mailto"));
  3327. else
  3328. string_copy = lxw_strdup(url);
  3329. }
  3330. else {
  3331. string_copy = lxw_strdup(url + sizeof("__ternal"));
  3332. }
  3333. GOTO_LABEL_ON_MEM_ERROR(string_copy, mem_error);
  3334. }
  3335. if (url) {
  3336. if (link_type == HYPERLINK_URL)
  3337. url_copy = lxw_strdup(url);
  3338. else
  3339. url_copy = lxw_strdup(url + sizeof("__ternal"));
  3340. GOTO_LABEL_ON_MEM_ERROR(url_copy, mem_error);
  3341. }
  3342. if (tooltip) {
  3343. tooltip_copy = lxw_strdup(tooltip);
  3344. GOTO_LABEL_ON_MEM_ERROR(tooltip_copy, mem_error);
  3345. }
  3346. if (link_type == HYPERLINK_INTERNAL) {
  3347. url_string = lxw_strdup(string_copy);
  3348. GOTO_LABEL_ON_MEM_ERROR(url_string, mem_error);
  3349. }
  3350. /* Escape the URL. */
  3351. if (link_type == HYPERLINK_URL && strlen(url_copy) >= 3) {
  3352. uint8_t not_escaped = 1;
  3353. /* First check if the URL is already escaped by the user. */
  3354. for (i = 0; i <= strlen(url_copy) - 3; i++) {
  3355. if (url_copy[i] == '%' && isxdigit(url_copy[i + 1])
  3356. && isxdigit(url_copy[i + 2])) {
  3357. not_escaped = 0;
  3358. break;
  3359. }
  3360. }
  3361. if (not_escaped) {
  3362. url_external = calloc(1, strlen(url_copy) * 3 + 1);
  3363. GOTO_LABEL_ON_MEM_ERROR(url_external, mem_error);
  3364. for (i = 0; i <= strlen(url_copy); i++) {
  3365. switch (url_copy[i]) {
  3366. case (' '):
  3367. case ('"'):
  3368. case ('%'):
  3369. case ('<'):
  3370. case ('>'):
  3371. case ('['):
  3372. case (']'):
  3373. case ('`'):
  3374. case ('^'):
  3375. case ('{'):
  3376. case ('}'):
  3377. lxw_snprintf(url_external + strlen(url_external),
  3378. sizeof("%xx"), "%%%2x", url_copy[i]);
  3379. break;
  3380. default:
  3381. url_external[strlen(url_external)] = url_copy[i];
  3382. }
  3383. }
  3384. free(url_copy);
  3385. url_copy = lxw_strdup(url_external);
  3386. GOTO_LABEL_ON_MEM_ERROR(url_copy, mem_error);
  3387. free(url_external);
  3388. url_external = NULL;
  3389. }
  3390. }
  3391. if (link_type == HYPERLINK_EXTERNAL) {
  3392. /* External Workbook links need to be modified into the right format.
  3393. * The URL will look something like "c:\temp\file.xlsx#Sheet!A1".
  3394. * We need the part to the left of the # as the URL and the part to
  3395. * the right as the "location" string (if it exists).
  3396. */
  3397. /* For external links change the dir separator from Unix to DOS. */
  3398. for (i = 0; i <= strlen(url_copy); i++)
  3399. if (url_copy[i] == '/')
  3400. url_copy[i] = '\\';
  3401. for (i = 0; i <= strlen(string_copy); i++)
  3402. if (string_copy[i] == '/')
  3403. string_copy[i] = '\\';
  3404. found_string = strchr(url_copy, '#');
  3405. if (found_string) {
  3406. url_string = lxw_strdup(found_string + 1);
  3407. GOTO_LABEL_ON_MEM_ERROR(url_string, mem_error);
  3408. *found_string = '\0';
  3409. }
  3410. /* Look for Windows style "C:/" link or Windows share "\\" link. */
  3411. found_string = strchr(url_copy, ':');
  3412. if (!found_string)
  3413. found_string = strstr(url_copy, "\\\\");
  3414. if (found_string) {
  3415. /* Add the file:/// URI to the url if non-local. */
  3416. string_size = sizeof("file:///") + strlen(url_copy);
  3417. url_external = calloc(1, string_size);
  3418. GOTO_LABEL_ON_MEM_ERROR(url_external, mem_error);
  3419. lxw_snprintf(url_external, string_size, "file:///%s", url_copy);
  3420. }
  3421. /* Convert a ./dir/file.xlsx link to dir/file.xlsx. */
  3422. found_string = strstr(url_copy, ".\\");
  3423. if (found_string == url_copy)
  3424. memmove(url_copy, url_copy + 2, strlen(url_copy) - 1);
  3425. if (url_external) {
  3426. free(url_copy);
  3427. url_copy = lxw_strdup(url_external);
  3428. GOTO_LABEL_ON_MEM_ERROR(url_copy, mem_error);
  3429. free(url_external);
  3430. url_external = NULL;
  3431. }
  3432. }
  3433. /* Excel limits escaped URL to 255 characters. */
  3434. if (lxw_utf8_strlen(url_copy) > 255)
  3435. goto mem_error;
  3436. err = worksheet_write_string(self, row_num, col_num, string_copy, format);
  3437. if (err)
  3438. goto mem_error;
  3439. link = _new_hyperlink_cell(row_num, col_num, link_type, url_copy,
  3440. url_string, tooltip_copy);
  3441. GOTO_LABEL_ON_MEM_ERROR(link, mem_error);
  3442. _insert_hyperlink(self, row_num, col_num, link);
  3443. free(string_copy);
  3444. self->hlink_count++;
  3445. return LXW_NO_ERROR;
  3446. mem_error:
  3447. free(string_copy);
  3448. free(url_copy);
  3449. free(url_external);
  3450. free(url_string);
  3451. free(tooltip_copy);
  3452. return LXW_ERROR_MEMORY_MALLOC_FAILED;
  3453. }
  3454. /*
  3455. * Write a hyperlink/url to an Excel file.
  3456. */
  3457. lxw_error
  3458. worksheet_write_url(lxw_worksheet *self,
  3459. lxw_row_t row_num,
  3460. lxw_col_t col_num, const char *url, lxw_format *format)
  3461. {
  3462. return worksheet_write_url_opt(self, row_num, col_num, url, format, NULL,
  3463. NULL);
  3464. }
  3465. /*
  3466. * Set the properties of a single column or a range of columns with options.
  3467. */
  3468. lxw_error
  3469. worksheet_set_column_opt(lxw_worksheet *self,
  3470. lxw_col_t firstcol,
  3471. lxw_col_t lastcol,
  3472. double width,
  3473. lxw_format *format,
  3474. lxw_row_col_options *user_options)
  3475. {
  3476. lxw_col_options *copied_options;
  3477. uint8_t ignore_row = LXW_TRUE;
  3478. uint8_t ignore_col = LXW_TRUE;
  3479. uint8_t hidden = LXW_FALSE;
  3480. uint8_t level = 0;
  3481. uint8_t collapsed = LXW_FALSE;
  3482. lxw_col_t col;
  3483. lxw_error err;
  3484. if (user_options) {
  3485. hidden = user_options->hidden;
  3486. level = user_options->level;
  3487. collapsed = user_options->collapsed;
  3488. }
  3489. /* Ensure second col is larger than first. */
  3490. if (firstcol > lastcol) {
  3491. lxw_col_t tmp = firstcol;
  3492. firstcol = lastcol;
  3493. lastcol = tmp;
  3494. }
  3495. /* Ensure that the cols are valid and store max and min values.
  3496. * NOTE: The check shouldn't modify the row dimensions and should only
  3497. * modify the column dimensions in certain cases. */
  3498. if (format != NULL || (width != LXW_DEF_COL_WIDTH && hidden))
  3499. ignore_col = LXW_FALSE;
  3500. err = _check_dimensions(self, 0, firstcol, ignore_row, ignore_col);
  3501. if (!err)
  3502. err = _check_dimensions(self, 0, lastcol, ignore_row, ignore_col);
  3503. if (err)
  3504. return err;
  3505. /* Resize the col_options array if required. */
  3506. if (firstcol >= self->col_options_max) {
  3507. lxw_col_t col;
  3508. lxw_col_t old_size = self->col_options_max;
  3509. lxw_col_t new_size = _next_power_of_two(firstcol + 1);
  3510. lxw_col_options **new_ptr = realloc(self->col_options,
  3511. new_size *
  3512. sizeof(lxw_col_options *));
  3513. if (new_ptr) {
  3514. for (col = old_size; col < new_size; col++)
  3515. new_ptr[col] = NULL;
  3516. self->col_options = new_ptr;
  3517. self->col_options_max = new_size;
  3518. }
  3519. else {
  3520. return LXW_ERROR_MEMORY_MALLOC_FAILED;
  3521. }
  3522. }
  3523. /* Resize the col_formats array if required. */
  3524. if (lastcol >= self->col_formats_max) {
  3525. lxw_col_t col;
  3526. lxw_col_t old_size = self->col_formats_max;
  3527. lxw_col_t new_size = _next_power_of_two(lastcol + 1);
  3528. lxw_format **new_ptr = realloc(self->col_formats,
  3529. new_size * sizeof(lxw_format *));
  3530. if (new_ptr) {
  3531. for (col = old_size; col < new_size; col++)
  3532. new_ptr[col] = NULL;
  3533. self->col_formats = new_ptr;
  3534. self->col_formats_max = new_size;
  3535. }
  3536. else {
  3537. return LXW_ERROR_MEMORY_MALLOC_FAILED;
  3538. }
  3539. }
  3540. /* Store the column options. */
  3541. copied_options = calloc(1, sizeof(lxw_col_options));
  3542. RETURN_ON_MEM_ERROR(copied_options, LXW_ERROR_MEMORY_MALLOC_FAILED);
  3543. /* Ensure the level is <= 7). */
  3544. if (level > 7)
  3545. level = 7;
  3546. if (level > self->outline_col_level)
  3547. self->outline_col_level = level;
  3548. /* Set the column properties. */
  3549. copied_options->firstcol = firstcol;
  3550. copied_options->lastcol = lastcol;
  3551. copied_options->width = width;
  3552. copied_options->format = format;
  3553. copied_options->hidden = hidden;
  3554. copied_options->level = level;
  3555. copied_options->collapsed = collapsed;
  3556. self->col_options[firstcol] = copied_options;
  3557. /* Store the column formats for use when writing cell data. */
  3558. for (col = firstcol; col <= lastcol; col++) {
  3559. self->col_formats[col] = format;
  3560. }
  3561. /* Store the column change to allow optimizations. */
  3562. self->col_size_changed = LXW_TRUE;
  3563. return LXW_NO_ERROR;
  3564. }
  3565. /*
  3566. * Set the properties of a single column or a range of columns.
  3567. */
  3568. lxw_error
  3569. worksheet_set_column(lxw_worksheet *self,
  3570. lxw_col_t firstcol,
  3571. lxw_col_t lastcol, double width, lxw_format *format)
  3572. {
  3573. return worksheet_set_column_opt(self, firstcol, lastcol, width, format,
  3574. NULL);
  3575. }
  3576. /*
  3577. * Set the properties of a row with options.
  3578. */
  3579. lxw_error
  3580. worksheet_set_row_opt(lxw_worksheet *self,
  3581. lxw_row_t row_num,
  3582. double height,
  3583. lxw_format *format, lxw_row_col_options *user_options)
  3584. {
  3585. lxw_col_t min_col;
  3586. uint8_t hidden = LXW_FALSE;
  3587. uint8_t level = 0;
  3588. uint8_t collapsed = LXW_FALSE;
  3589. lxw_row *row;
  3590. lxw_error err;
  3591. if (user_options) {
  3592. hidden = user_options->hidden;
  3593. level = user_options->level;
  3594. collapsed = user_options->collapsed;
  3595. }
  3596. /* Use minimum col in _check_dimensions(). */
  3597. if (self->dim_colmin != LXW_COL_MAX)
  3598. min_col = self->dim_colmin;
  3599. else
  3600. min_col = 0;
  3601. err = _check_dimensions(self, row_num, min_col, LXW_FALSE, LXW_FALSE);
  3602. if (err)
  3603. return err;
  3604. /* If the height is 0 the row is hidden and the height is the default. */
  3605. if (height == 0) {
  3606. hidden = LXW_TRUE;
  3607. height = self->default_row_height;
  3608. }
  3609. /* Ensure the level is <= 7). */
  3610. if (level > 7)
  3611. level = 7;
  3612. if (level > self->outline_row_level)
  3613. self->outline_row_level = level;
  3614. /* Store the row properties. */
  3615. row = _get_row(self, row_num);
  3616. row->height = height;
  3617. row->format = format;
  3618. row->hidden = hidden;
  3619. row->level = level;
  3620. row->collapsed = collapsed;
  3621. row->row_changed = LXW_TRUE;
  3622. if (height != self->default_row_height)
  3623. row->height_changed = LXW_TRUE;
  3624. return LXW_NO_ERROR;
  3625. }
  3626. /*
  3627. * Set the properties of a row.
  3628. */
  3629. lxw_error
  3630. worksheet_set_row(lxw_worksheet *self,
  3631. lxw_row_t row_num, double height, lxw_format *format)
  3632. {
  3633. return worksheet_set_row_opt(self, row_num, height, format, NULL);
  3634. }
  3635. /*
  3636. * Merge a range of cells. The first cell should contain the data and the others
  3637. * should be blank. All cells should contain the same format.
  3638. */
  3639. lxw_error
  3640. worksheet_merge_range(lxw_worksheet *self, lxw_row_t first_row,
  3641. lxw_col_t first_col, lxw_row_t last_row,
  3642. lxw_col_t last_col, const char *string,
  3643. lxw_format *format)
  3644. {
  3645. lxw_merged_range *merged_range;
  3646. lxw_row_t tmp_row;
  3647. lxw_col_t tmp_col;
  3648. lxw_error err;
  3649. /* Excel doesn't allow a single cell to be merged */
  3650. if (first_row == last_row && first_col == last_col)
  3651. return LXW_ERROR_PARAMETER_VALIDATION;
  3652. /* Swap last row/col with first row/col as necessary */
  3653. if (first_row > last_row) {
  3654. tmp_row = last_row;
  3655. last_row = first_row;
  3656. first_row = tmp_row;
  3657. }
  3658. if (first_col > last_col) {
  3659. tmp_col = last_col;
  3660. last_col = first_col;
  3661. first_col = tmp_col;
  3662. }
  3663. /* Check that column number is valid and store the max value */
  3664. err = _check_dimensions(self, last_row, last_col, LXW_FALSE, LXW_FALSE);
  3665. if (err)
  3666. return err;
  3667. /* Store the merge range. */
  3668. merged_range = calloc(1, sizeof(lxw_merged_range));
  3669. RETURN_ON_MEM_ERROR(merged_range, LXW_ERROR_MEMORY_MALLOC_FAILED);
  3670. merged_range->first_row = first_row;
  3671. merged_range->first_col = first_col;
  3672. merged_range->last_row = last_row;
  3673. merged_range->last_col = last_col;
  3674. STAILQ_INSERT_TAIL(self->merged_ranges, merged_range, list_pointers);
  3675. self->merged_range_count++;
  3676. /* Write the first cell */
  3677. worksheet_write_string(self, first_row, first_col, string, format);
  3678. /* Pad out the rest of the area with formatted blank cells. */
  3679. for (tmp_row = first_row; tmp_row <= last_row; tmp_row++) {
  3680. for (tmp_col = first_col; tmp_col <= last_col; tmp_col++) {
  3681. if (tmp_row == first_row && tmp_col == first_col)
  3682. continue;
  3683. worksheet_write_blank(self, tmp_row, tmp_col, format);
  3684. }
  3685. }
  3686. return LXW_NO_ERROR;
  3687. }
  3688. /*
  3689. * Set the autofilter area in the worksheet.
  3690. */
  3691. lxw_error
  3692. worksheet_autofilter(lxw_worksheet *self, lxw_row_t first_row,
  3693. lxw_col_t first_col, lxw_row_t last_row,
  3694. lxw_col_t last_col)
  3695. {
  3696. lxw_row_t tmp_row;
  3697. lxw_col_t tmp_col;
  3698. lxw_error err;
  3699. /* Excel doesn't allow a single cell to be merged */
  3700. if (first_row == last_row && first_col == last_col)
  3701. return LXW_ERROR_PARAMETER_VALIDATION;
  3702. /* Swap last row/col with first row/col as necessary */
  3703. if (first_row > last_row) {
  3704. tmp_row = last_row;
  3705. last_row = first_row;
  3706. first_row = tmp_row;
  3707. }
  3708. if (first_col > last_col) {
  3709. tmp_col = last_col;
  3710. last_col = first_col;
  3711. first_col = tmp_col;
  3712. }
  3713. /* Check that column number is valid and store the max value */
  3714. err = _check_dimensions(self, last_row, last_col, LXW_FALSE, LXW_FALSE);
  3715. if (err)
  3716. return err;
  3717. self->autofilter.in_use = LXW_TRUE;
  3718. self->autofilter.first_row = first_row;
  3719. self->autofilter.first_col = first_col;
  3720. self->autofilter.last_row = last_row;
  3721. self->autofilter.last_col = last_col;
  3722. return LXW_NO_ERROR;
  3723. }
  3724. /*
  3725. * Set this worksheet as a selected worksheet, i.e. the worksheet has its tab
  3726. * highlighted.
  3727. */
  3728. void
  3729. worksheet_select(lxw_worksheet *self)
  3730. {
  3731. self->selected = LXW_TRUE;
  3732. /* Selected worksheet can't be hidden. */
  3733. self->hidden = LXW_FALSE;
  3734. }
  3735. /*
  3736. * Set this worksheet as the active worksheet, i.e. the worksheet that is
  3737. * displayed when the workbook is opened. Also set it as selected.
  3738. */
  3739. void
  3740. worksheet_activate(lxw_worksheet *self)
  3741. {
  3742. self->selected = LXW_TRUE;
  3743. self->active = LXW_TRUE;
  3744. /* Active worksheet can't be hidden. */
  3745. self->hidden = LXW_FALSE;
  3746. *self->active_sheet = self->index;
  3747. }
  3748. /*
  3749. * Set this worksheet as the first visible sheet. This is necessary
  3750. * when there are a large number of worksheets and the activated
  3751. * worksheet is not visible on the screen.
  3752. */
  3753. void
  3754. worksheet_set_first_sheet(lxw_worksheet *self)
  3755. {
  3756. /* Active worksheet can't be hidden. */
  3757. self->hidden = LXW_FALSE;
  3758. *self->first_sheet = self->index;
  3759. }
  3760. /*
  3761. * Hide this worksheet.
  3762. */
  3763. void
  3764. worksheet_hide(lxw_worksheet *self)
  3765. {
  3766. self->hidden = LXW_TRUE;
  3767. /* A hidden worksheet shouldn't be active or selected. */
  3768. self->selected = LXW_FALSE;
  3769. /* If this is active_sheet or first_sheet reset the workbook value. */
  3770. if (*self->first_sheet == self->index)
  3771. *self->first_sheet = 0;
  3772. if (*self->active_sheet == self->index)
  3773. *self->active_sheet = 0;
  3774. }
  3775. /*
  3776. * Set which cell or cells are selected in a worksheet.
  3777. */
  3778. void
  3779. worksheet_set_selection(lxw_worksheet *self,
  3780. lxw_row_t first_row, lxw_col_t first_col,
  3781. lxw_row_t last_row, lxw_col_t last_col)
  3782. {
  3783. lxw_selection *selection;
  3784. lxw_row_t tmp_row;
  3785. lxw_col_t tmp_col;
  3786. char active_cell[LXW_MAX_CELL_RANGE_LENGTH];
  3787. char sqref[LXW_MAX_CELL_RANGE_LENGTH];
  3788. /* Only allow selection to be set once to avoid freeing/re-creating it. */
  3789. if (!STAILQ_EMPTY(self->selections))
  3790. return;
  3791. /* Excel doesn't set a selection for cell A1 since it is the default. */
  3792. if (first_row == 0 && first_col == 0 && last_row == 0 && last_col == 0)
  3793. return;
  3794. selection = calloc(1, sizeof(lxw_selection));
  3795. RETURN_VOID_ON_MEM_ERROR(selection);
  3796. /* Set the cell range selection. Do this before swapping max/min to */
  3797. /* allow the selection direction to be reversed. */
  3798. lxw_rowcol_to_cell(active_cell, first_row, first_col);
  3799. /* Swap last row/col for first row/col if necessary. */
  3800. if (first_row > last_row) {
  3801. tmp_row = first_row;
  3802. first_row = last_row;
  3803. last_row = tmp_row;
  3804. }
  3805. if (first_col > last_col) {
  3806. tmp_col = first_col;
  3807. first_col = last_col;
  3808. last_col = tmp_col;
  3809. }
  3810. /* If the first and last cell are the same write a single cell. */
  3811. if ((first_row == last_row) && (first_col == last_col))
  3812. lxw_rowcol_to_cell(sqref, first_row, first_col);
  3813. else
  3814. lxw_rowcol_to_range(sqref, first_row, first_col, last_row, last_col);
  3815. lxw_strcpy(selection->pane, "");
  3816. lxw_strcpy(selection->active_cell, active_cell);
  3817. lxw_strcpy(selection->sqref, sqref);
  3818. STAILQ_INSERT_TAIL(self->selections, selection, list_pointers);
  3819. }
  3820. /*
  3821. * Set panes and mark them as frozen. With extra options.
  3822. */
  3823. void
  3824. worksheet_freeze_panes_opt(lxw_worksheet *self,
  3825. lxw_row_t first_row, lxw_col_t first_col,
  3826. lxw_row_t top_row, lxw_col_t left_col,
  3827. uint8_t type)
  3828. {
  3829. self->panes.first_row = first_row;
  3830. self->panes.first_col = first_col;
  3831. self->panes.top_row = top_row;
  3832. self->panes.left_col = left_col;
  3833. self->panes.x_split = 0.0;
  3834. self->panes.y_split = 0.0;
  3835. if (type)
  3836. self->panes.type = FREEZE_SPLIT_PANES;
  3837. else
  3838. self->panes.type = FREEZE_PANES;
  3839. }
  3840. /*
  3841. * Set panes and mark them as frozen.
  3842. */
  3843. void
  3844. worksheet_freeze_panes(lxw_worksheet *self,
  3845. lxw_row_t first_row, lxw_col_t first_col)
  3846. {
  3847. worksheet_freeze_panes_opt(self, first_row, first_col,
  3848. first_row, first_col, 0);
  3849. }
  3850. /*
  3851. * Set panes and mark them as split.With extra options.
  3852. */
  3853. void
  3854. worksheet_split_panes_opt(lxw_worksheet *self,
  3855. double y_split, double x_split,
  3856. lxw_row_t top_row, lxw_col_t left_col)
  3857. {
  3858. self->panes.first_row = 0;
  3859. self->panes.first_col = 0;
  3860. self->panes.top_row = top_row;
  3861. self->panes.left_col = left_col;
  3862. self->panes.x_split = x_split;
  3863. self->panes.y_split = y_split;
  3864. self->panes.type = SPLIT_PANES;
  3865. }
  3866. /*
  3867. * Set panes and mark them as split.
  3868. */
  3869. void
  3870. worksheet_split_panes(lxw_worksheet *self, double y_split, double x_split)
  3871. {
  3872. worksheet_split_panes_opt(self, y_split, x_split, 0, 0);
  3873. }
  3874. /*
  3875. * Set the page orientation as portrait.
  3876. */
  3877. void
  3878. worksheet_set_portrait(lxw_worksheet *self)
  3879. {
  3880. self->orientation = LXW_PORTRAIT;
  3881. self->page_setup_changed = LXW_TRUE;
  3882. }
  3883. /*
  3884. * Set the page orientation as landscape.
  3885. */
  3886. void
  3887. worksheet_set_landscape(lxw_worksheet *self)
  3888. {
  3889. self->orientation = LXW_LANDSCAPE;
  3890. self->page_setup_changed = LXW_TRUE;
  3891. }
  3892. /*
  3893. * Set the page view mode for Mac Excel.
  3894. */
  3895. void
  3896. worksheet_set_page_view(lxw_worksheet *self)
  3897. {
  3898. self->page_view = LXW_TRUE;
  3899. }
  3900. /*
  3901. * Set the paper type. Example. 1 = US Letter, 9 = A4
  3902. */
  3903. void
  3904. worksheet_set_paper(lxw_worksheet *self, uint8_t paper_size)
  3905. {
  3906. self->paper_size = paper_size;
  3907. self->page_setup_changed = LXW_TRUE;
  3908. }
  3909. /*
  3910. * Set the order in which pages are printed.
  3911. */
  3912. void
  3913. worksheet_print_across(lxw_worksheet *self)
  3914. {
  3915. self->page_order = LXW_PRINT_ACROSS;
  3916. self->page_setup_changed = LXW_TRUE;
  3917. }
  3918. /*
  3919. * Set all the page margins in inches.
  3920. */
  3921. void
  3922. worksheet_set_margins(lxw_worksheet *self, double left, double right,
  3923. double top, double bottom)
  3924. {
  3925. if (left >= 0)
  3926. self->margin_left = left;
  3927. if (right >= 0)
  3928. self->margin_right = right;
  3929. if (top >= 0)
  3930. self->margin_top = top;
  3931. if (bottom >= 0)
  3932. self->margin_bottom = bottom;
  3933. }
  3934. /*
  3935. * Set the page header caption and options.
  3936. */
  3937. lxw_error
  3938. worksheet_set_header_opt(lxw_worksheet *self, const char *string,
  3939. lxw_header_footer_options *options)
  3940. {
  3941. if (options) {
  3942. if (options->margin >= 0.0)
  3943. self->margin_header = options->margin;
  3944. }
  3945. if (!string)
  3946. return LXW_ERROR_NULL_PARAMETER_IGNORED;
  3947. if (lxw_utf8_strlen(string) >= LXW_HEADER_FOOTER_MAX)
  3948. return LXW_ERROR_255_STRING_LENGTH_EXCEEDED;
  3949. lxw_strcpy(self->header, string);
  3950. self->header_footer_changed = 1;
  3951. return LXW_NO_ERROR;
  3952. }
  3953. /*
  3954. * Set the page footer caption and options.
  3955. */
  3956. lxw_error
  3957. worksheet_set_footer_opt(lxw_worksheet *self, const char *string,
  3958. lxw_header_footer_options *options)
  3959. {
  3960. if (options) {
  3961. if (options->margin >= 0.0)
  3962. self->margin_footer = options->margin;
  3963. }
  3964. if (!string)
  3965. return LXW_ERROR_NULL_PARAMETER_IGNORED;
  3966. if (lxw_utf8_strlen(string) >= LXW_HEADER_FOOTER_MAX)
  3967. return LXW_ERROR_255_STRING_LENGTH_EXCEEDED;
  3968. lxw_strcpy(self->footer, string);
  3969. self->header_footer_changed = 1;
  3970. return LXW_NO_ERROR;
  3971. }
  3972. /*
  3973. * Set the page header caption.
  3974. */
  3975. lxw_error
  3976. worksheet_set_header(lxw_worksheet *self, const char *string)
  3977. {
  3978. return worksheet_set_header_opt(self, string, NULL);
  3979. }
  3980. /*
  3981. * Set the page footer caption.
  3982. */
  3983. lxw_error
  3984. worksheet_set_footer(lxw_worksheet *self, const char *string)
  3985. {
  3986. return worksheet_set_footer_opt(self, string, NULL);
  3987. }
  3988. /*
  3989. * Set the option to show/hide gridlines on the screen and the printed page.
  3990. */
  3991. void
  3992. worksheet_gridlines(lxw_worksheet *self, uint8_t option)
  3993. {
  3994. if (option == LXW_HIDE_ALL_GRIDLINES) {
  3995. self->print_gridlines = 0;
  3996. self->screen_gridlines = 0;
  3997. }
  3998. if (option & LXW_SHOW_SCREEN_GRIDLINES) {
  3999. self->screen_gridlines = 1;
  4000. }
  4001. if (option & LXW_SHOW_PRINT_GRIDLINES) {
  4002. self->print_gridlines = 1;
  4003. self->print_options_changed = 1;
  4004. }
  4005. }
  4006. /*
  4007. * Center the page horizontally.
  4008. */
  4009. void
  4010. worksheet_center_horizontally(lxw_worksheet *self)
  4011. {
  4012. self->print_options_changed = 1;
  4013. self->hcenter = 1;
  4014. }
  4015. /*
  4016. * Center the page horizontally.
  4017. */
  4018. void
  4019. worksheet_center_vertically(lxw_worksheet *self)
  4020. {
  4021. self->print_options_changed = 1;
  4022. self->vcenter = 1;
  4023. }
  4024. /*
  4025. * Set the option to print the row and column headers on the printed page.
  4026. */
  4027. void
  4028. worksheet_print_row_col_headers(lxw_worksheet *self)
  4029. {
  4030. self->print_headers = 1;
  4031. self->print_options_changed = 1;
  4032. }
  4033. /*
  4034. * Set the rows to repeat at the top of each printed page.
  4035. */
  4036. lxw_error
  4037. worksheet_repeat_rows(lxw_worksheet *self, lxw_row_t first_row,
  4038. lxw_row_t last_row)
  4039. {
  4040. lxw_row_t tmp_row;
  4041. lxw_error err;
  4042. if (first_row > last_row) {
  4043. tmp_row = last_row;
  4044. last_row = first_row;
  4045. first_row = tmp_row;
  4046. }
  4047. err = _check_dimensions(self, last_row, 0, LXW_IGNORE, LXW_IGNORE);
  4048. if (err)
  4049. return err;
  4050. self->repeat_rows.in_use = LXW_TRUE;
  4051. self->repeat_rows.first_row = first_row;
  4052. self->repeat_rows.last_row = last_row;
  4053. return LXW_NO_ERROR;
  4054. }
  4055. /*
  4056. * Set the columns to repeat at the left hand side of each printed page.
  4057. */
  4058. lxw_error
  4059. worksheet_repeat_columns(lxw_worksheet *self, lxw_col_t first_col,
  4060. lxw_col_t last_col)
  4061. {
  4062. lxw_col_t tmp_col;
  4063. lxw_error err;
  4064. if (first_col > last_col) {
  4065. tmp_col = last_col;
  4066. last_col = first_col;
  4067. first_col = tmp_col;
  4068. }
  4069. err = _check_dimensions(self, last_col, 0, LXW_IGNORE, LXW_IGNORE);
  4070. if (err)
  4071. return err;
  4072. self->repeat_cols.in_use = LXW_TRUE;
  4073. self->repeat_cols.first_col = first_col;
  4074. self->repeat_cols.last_col = last_col;
  4075. return LXW_NO_ERROR;
  4076. }
  4077. /*
  4078. * Set the print area in the current worksheet.
  4079. */
  4080. lxw_error
  4081. worksheet_print_area(lxw_worksheet *self, lxw_row_t first_row,
  4082. lxw_col_t first_col, lxw_row_t last_row,
  4083. lxw_col_t last_col)
  4084. {
  4085. lxw_row_t tmp_row;
  4086. lxw_col_t tmp_col;
  4087. lxw_error err;
  4088. if (first_row > last_row) {
  4089. tmp_row = last_row;
  4090. last_row = first_row;
  4091. first_row = tmp_row;
  4092. }
  4093. if (first_col > last_col) {
  4094. tmp_col = last_col;
  4095. last_col = first_col;
  4096. first_col = tmp_col;
  4097. }
  4098. err = _check_dimensions(self, last_row, last_col, LXW_IGNORE, LXW_IGNORE);
  4099. if (err)
  4100. return err;
  4101. /* Ignore max area since it is the same as no print area in Excel. */
  4102. if (first_row == 0 && first_col == 0 && last_row == LXW_ROW_MAX - 1
  4103. && last_col == LXW_COL_MAX - 1) {
  4104. return LXW_NO_ERROR;
  4105. }
  4106. self->print_area.in_use = LXW_TRUE;
  4107. self->print_area.first_row = first_row;
  4108. self->print_area.last_row = last_row;
  4109. self->print_area.first_col = first_col;
  4110. self->print_area.last_col = last_col;
  4111. return LXW_NO_ERROR;
  4112. }
  4113. /* Store the vertical and horizontal number of pages that will define the
  4114. * maximum area printed.
  4115. */
  4116. void
  4117. worksheet_fit_to_pages(lxw_worksheet *self, uint16_t width, uint16_t height)
  4118. {
  4119. self->fit_page = 1;
  4120. self->fit_width = width;
  4121. self->fit_height = height;
  4122. self->page_setup_changed = 1;
  4123. }
  4124. /*
  4125. * Set the start page number.
  4126. */
  4127. void
  4128. worksheet_set_start_page(lxw_worksheet *self, uint16_t start_page)
  4129. {
  4130. self->page_start = start_page;
  4131. }
  4132. /*
  4133. * Set the scale factor for the printed page.
  4134. */
  4135. void
  4136. worksheet_set_print_scale(lxw_worksheet *self, uint16_t scale)
  4137. {
  4138. /* Confine the scale to Excel"s range */
  4139. if (scale < 10 || scale > 400)
  4140. return;
  4141. /* Turn off "fit to page" option. */
  4142. self->fit_page = LXW_FALSE;
  4143. self->print_scale = scale;
  4144. self->page_setup_changed = LXW_TRUE;
  4145. }
  4146. /*
  4147. * Store the horizontal page breaks on a worksheet.
  4148. */
  4149. lxw_error
  4150. worksheet_set_h_pagebreaks(lxw_worksheet *self, lxw_row_t hbreaks[])
  4151. {
  4152. uint16_t count = 0;
  4153. if (hbreaks == NULL)
  4154. return LXW_ERROR_NULL_PARAMETER_IGNORED;
  4155. while (hbreaks[count])
  4156. count++;
  4157. /* The Excel 2007 specification says that the maximum number of page
  4158. * breaks is 1026. However, in practice it is actually 1023. */
  4159. if (count > LXW_BREAKS_MAX)
  4160. count = LXW_BREAKS_MAX;
  4161. self->hbreaks = calloc(count, sizeof(lxw_row_t));
  4162. RETURN_ON_MEM_ERROR(self->hbreaks, LXW_ERROR_MEMORY_MALLOC_FAILED);
  4163. memcpy(self->hbreaks, hbreaks, count * sizeof(lxw_row_t));
  4164. self->hbreaks_count = count;
  4165. return LXW_NO_ERROR;
  4166. }
  4167. /*
  4168. * Store the vertical page breaks on a worksheet.
  4169. */
  4170. lxw_error
  4171. worksheet_set_v_pagebreaks(lxw_worksheet *self, lxw_col_t vbreaks[])
  4172. {
  4173. uint16_t count = 0;
  4174. if (vbreaks == NULL)
  4175. return LXW_ERROR_NULL_PARAMETER_IGNORED;
  4176. while (vbreaks[count])
  4177. count++;
  4178. /* The Excel 2007 specification says that the maximum number of page
  4179. * breaks is 1026. However, in practice it is actually 1023. */
  4180. if (count > LXW_BREAKS_MAX)
  4181. count = LXW_BREAKS_MAX;
  4182. self->vbreaks = calloc(count, sizeof(lxw_col_t));
  4183. RETURN_ON_MEM_ERROR(self->vbreaks, LXW_ERROR_MEMORY_MALLOC_FAILED);
  4184. memcpy(self->vbreaks, vbreaks, count * sizeof(lxw_col_t));
  4185. self->vbreaks_count = count;
  4186. return LXW_NO_ERROR;
  4187. }
  4188. /*
  4189. * Set the worksheet zoom factor.
  4190. */
  4191. void
  4192. worksheet_set_zoom(lxw_worksheet *self, uint16_t scale)
  4193. {
  4194. /* Confine the scale to Excel"s range */
  4195. if (scale < 10 || scale > 400) {
  4196. LXW_WARN("worksheet_set_zoom(): "
  4197. "Zoom factor scale outside range: 10 <= zoom <= 400.");
  4198. return;
  4199. }
  4200. self->zoom = scale;
  4201. }
  4202. /*
  4203. * Hide cell zero values.
  4204. */
  4205. void
  4206. worksheet_hide_zero(lxw_worksheet *self)
  4207. {
  4208. self->show_zeros = LXW_FALSE;
  4209. }
  4210. /*
  4211. * Display the worksheet right to left for some eastern versions of Excel.
  4212. */
  4213. void
  4214. worksheet_right_to_left(lxw_worksheet *self)
  4215. {
  4216. self->right_to_left = LXW_TRUE;
  4217. }
  4218. /*
  4219. * Set the color of the worksheet tab.
  4220. */
  4221. void
  4222. worksheet_set_tab_color(lxw_worksheet *self, lxw_color_t color)
  4223. {
  4224. self->tab_color = color;
  4225. }
  4226. /*
  4227. * Set the worksheet protection flags to prevent modification of worksheet
  4228. * objects.
  4229. */
  4230. void
  4231. worksheet_protect(lxw_worksheet *self, const char *password,
  4232. lxw_protection *options)
  4233. {
  4234. struct lxw_protection *protect = &self->protection;
  4235. /* Copy any user parameters to the internal structure. */
  4236. if (options) {
  4237. protect->no_select_locked_cells = options->no_select_locked_cells;
  4238. protect->no_select_unlocked_cells = options->no_select_unlocked_cells;
  4239. protect->format_cells = options->format_cells;
  4240. protect->format_columns = options->format_columns;
  4241. protect->format_rows = options->format_rows;
  4242. protect->insert_columns = options->insert_columns;
  4243. protect->insert_rows = options->insert_rows;
  4244. protect->insert_hyperlinks = options->insert_hyperlinks;
  4245. protect->delete_columns = options->delete_columns;
  4246. protect->delete_rows = options->delete_rows;
  4247. protect->sort = options->sort;
  4248. protect->autofilter = options->autofilter;
  4249. protect->pivot_tables = options->pivot_tables;
  4250. protect->scenarios = options->scenarios;
  4251. protect->objects = options->objects;
  4252. }
  4253. if (password) {
  4254. uint16_t hash = _hash_password(password);
  4255. lxw_snprintf(protect->hash, 5, "%X", hash);
  4256. }
  4257. protect->is_configured = LXW_TRUE;
  4258. }
  4259. /*
  4260. * Set the worksheet properties for outlines and grouping.
  4261. */
  4262. void
  4263. worksheet_outline_settings(lxw_worksheet *self,
  4264. uint8_t visible,
  4265. uint8_t symbols_below,
  4266. uint8_t symbols_right, uint8_t auto_style)
  4267. {
  4268. self->outline_on = visible;
  4269. self->outline_below = symbols_below;
  4270. self->outline_right = symbols_right;
  4271. self->outline_style = auto_style;
  4272. self->outline_changed = LXW_TRUE;
  4273. }
  4274. /*
  4275. * Set the default row properties
  4276. */
  4277. void
  4278. worksheet_set_default_row(lxw_worksheet *self, double height,
  4279. uint8_t hide_unused_rows)
  4280. {
  4281. if (height < 0)
  4282. height = self->default_row_height;
  4283. if (height != self->default_row_height) {
  4284. self->default_row_height = height;
  4285. self->row_size_changed = LXW_TRUE;
  4286. }
  4287. if (hide_unused_rows)
  4288. self->default_row_zeroed = LXW_TRUE;
  4289. self->default_row_set = LXW_TRUE;
  4290. }
  4291. /*
  4292. * Insert an image into the worksheet.
  4293. */
  4294. lxw_error
  4295. worksheet_insert_image_opt(lxw_worksheet *self,
  4296. lxw_row_t row_num, lxw_col_t col_num,
  4297. const char *filename,
  4298. lxw_image_options *user_options)
  4299. {
  4300. FILE *image_stream;
  4301. char *short_name;
  4302. lxw_image_options *options;
  4303. if (!filename) {
  4304. LXW_WARN("worksheet_insert_image()/_opt(): "
  4305. "filename must be specified.");
  4306. return LXW_ERROR_NULL_PARAMETER_IGNORED;
  4307. }
  4308. /* Check that the image file exists and can be opened. */
  4309. image_stream = fopen(filename, "rb");
  4310. if (!image_stream) {
  4311. LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): "
  4312. "file doesn't exist or can't be opened: %s.",
  4313. filename);
  4314. return LXW_ERROR_PARAMETER_VALIDATION;
  4315. }
  4316. /* Get the filename from the full path to add to the Drawing object. */
  4317. short_name = lxw_basename(filename);
  4318. if (!short_name) {
  4319. LXW_WARN_FORMAT1("worksheet_insert_image()/_opt(): "
  4320. "couldn't get basename for file: %s.", filename);
  4321. fclose(image_stream);
  4322. return LXW_ERROR_PARAMETER_VALIDATION;
  4323. }
  4324. /* Create a new object to hold the image options. */
  4325. options = calloc(1, sizeof(lxw_image_options));
  4326. if (!options) {
  4327. fclose(image_stream);
  4328. return LXW_ERROR_MEMORY_MALLOC_FAILED;
  4329. }
  4330. if (user_options) {
  4331. options->x_offset = user_options->x_offset;
  4332. options->y_offset = user_options->y_offset;
  4333. options->x_scale = user_options->x_scale;
  4334. options->y_scale = user_options->y_scale;
  4335. }
  4336. /* Copy other options or set defaults. */
  4337. options->filename = lxw_strdup(filename);
  4338. options->short_name = lxw_strdup(short_name);
  4339. options->stream = image_stream;
  4340. options->row = row_num;
  4341. options->col = col_num;
  4342. if (!options->x_scale)
  4343. options->x_scale = 1;
  4344. if (!options->y_scale)
  4345. options->y_scale = 1;
  4346. if (_get_image_properties(options) == LXW_NO_ERROR) {
  4347. STAILQ_INSERT_TAIL(self->image_data, options, list_pointers);
  4348. fclose(image_stream);
  4349. return LXW_NO_ERROR;
  4350. }
  4351. else {
  4352. free(options);
  4353. fclose(image_stream);
  4354. return LXW_ERROR_IMAGE_DIMENSIONS;
  4355. }
  4356. }
  4357. /*
  4358. * Insert an image into the worksheet.
  4359. */
  4360. lxw_error
  4361. worksheet_insert_image(lxw_worksheet *self,
  4362. lxw_row_t row_num, lxw_col_t col_num,
  4363. const char *filename)
  4364. {
  4365. return worksheet_insert_image_opt(self, row_num, col_num, filename, NULL);
  4366. }
  4367. /*
  4368. * Insert an chart into the worksheet.
  4369. */
  4370. lxw_error
  4371. worksheet_insert_chart_opt(lxw_worksheet *self,
  4372. lxw_row_t row_num, lxw_col_t col_num,
  4373. lxw_chart *chart, lxw_image_options *user_options)
  4374. {
  4375. lxw_image_options *options;
  4376. lxw_chart_series *series;
  4377. if (!chart) {
  4378. LXW_WARN("worksheet_insert_chart()/_opt(): chart must be non-NULL.");
  4379. return LXW_ERROR_NULL_PARAMETER_IGNORED;
  4380. }
  4381. /* Check that the chart isn't being used more than once. */
  4382. if (chart->in_use) {
  4383. LXW_WARN("worksheet_insert_chart()/_opt(): the same chart object "
  4384. "cannot be inserted in a worksheet more than once.");
  4385. return LXW_ERROR_PARAMETER_VALIDATION;
  4386. }
  4387. /* Check that the chart has a data series. */
  4388. if (STAILQ_EMPTY(chart->series_list)) {
  4389. LXW_WARN
  4390. ("worksheet_insert_chart()/_opt(): chart must have a series.");
  4391. return LXW_ERROR_PARAMETER_VALIDATION;
  4392. }
  4393. /* Check that the chart has a 'values' series. */
  4394. STAILQ_FOREACH(series, chart->series_list, list_pointers) {
  4395. if (!series->values->formula && !series->values->sheetname) {
  4396. LXW_WARN("worksheet_insert_chart()/_opt(): chart must have a "
  4397. "'values' series.");
  4398. return LXW_ERROR_PARAMETER_VALIDATION;
  4399. }
  4400. }
  4401. /* Create a new object to hold the chart image options. */
  4402. options = calloc(1, sizeof(lxw_image_options));
  4403. RETURN_ON_MEM_ERROR(options, LXW_ERROR_MEMORY_MALLOC_FAILED);
  4404. if (user_options) {
  4405. options->x_offset = user_options->x_offset;
  4406. options->y_offset = user_options->y_offset;
  4407. options->x_scale = user_options->x_scale;
  4408. options->y_scale = user_options->y_scale;
  4409. }
  4410. /* Copy other options or set defaults. */
  4411. options->row = row_num;
  4412. options->col = col_num;
  4413. /* TODO. Read defaults from chart. */
  4414. options->width = 480;
  4415. options->height = 288;
  4416. if (!options->x_scale)
  4417. options->x_scale = 1;
  4418. if (!options->y_scale)
  4419. options->y_scale = 1;
  4420. /* Store chart references so they can be ordered in the workbook. */
  4421. options->chart = chart;
  4422. STAILQ_INSERT_TAIL(self->chart_data, options, list_pointers);
  4423. chart->in_use = LXW_TRUE;
  4424. return LXW_NO_ERROR;
  4425. }
  4426. /*
  4427. * Insert an image into the worksheet.
  4428. */
  4429. lxw_error
  4430. worksheet_insert_chart(lxw_worksheet *self,
  4431. lxw_row_t row_num, lxw_col_t col_num, lxw_chart *chart)
  4432. {
  4433. return worksheet_insert_chart_opt(self, row_num, col_num, chart, NULL);
  4434. }
  4435. /*
  4436. * Add a data validation to a worksheet, for a range. Ironically this requires
  4437. * a lot of validation of the user input.
  4438. */
  4439. lxw_error
  4440. worksheet_data_validation_range(lxw_worksheet *self, lxw_row_t first_row,
  4441. lxw_col_t first_col,
  4442. lxw_row_t last_row,
  4443. lxw_col_t last_col,
  4444. lxw_data_validation *validation)
  4445. {
  4446. lxw_data_validation *copy;
  4447. uint8_t is_between = LXW_FALSE;
  4448. uint8_t is_formula = LXW_FALSE;
  4449. uint8_t has_criteria = LXW_TRUE;
  4450. lxw_error err;
  4451. lxw_row_t tmp_row;
  4452. lxw_col_t tmp_col;
  4453. size_t length;
  4454. /* No action is required for validation type 'any' unless there are
  4455. * input messages to display.*/
  4456. if (validation->validate == LXW_VALIDATION_TYPE_ANY
  4457. && !(validation->input_title || validation->input_message)) {
  4458. return LXW_NO_ERROR;
  4459. }
  4460. /* Check for formula types. */
  4461. switch (validation->validate) {
  4462. case LXW_VALIDATION_TYPE_INTEGER_FORMULA:
  4463. case LXW_VALIDATION_TYPE_DECIMAL_FORMULA:
  4464. case LXW_VALIDATION_TYPE_LIST_FORMULA:
  4465. case LXW_VALIDATION_TYPE_LENGTH_FORMULA:
  4466. case LXW_VALIDATION_TYPE_DATE_FORMULA:
  4467. case LXW_VALIDATION_TYPE_TIME_FORMULA:
  4468. case LXW_VALIDATION_TYPE_CUSTOM_FORMULA:
  4469. is_formula = LXW_TRUE;
  4470. break;
  4471. }
  4472. /* Check for types without a criteria. */
  4473. switch (validation->validate) {
  4474. case LXW_VALIDATION_TYPE_LIST:
  4475. case LXW_VALIDATION_TYPE_LIST_FORMULA:
  4476. case LXW_VALIDATION_TYPE_ANY:
  4477. case LXW_VALIDATION_TYPE_CUSTOM_FORMULA:
  4478. has_criteria = LXW_FALSE;
  4479. break;
  4480. }
  4481. /* Check that a validation parameter has been specified
  4482. * except for 'list', 'any' and 'custom'. */
  4483. if (has_criteria && validation->criteria == LXW_VALIDATION_CRITERIA_NONE) {
  4484. LXW_WARN_FORMAT("worksheet_data_validation_cell()/_range(): "
  4485. "criteria parameter must be specified.");
  4486. return LXW_ERROR_PARAMETER_VALIDATION;
  4487. }
  4488. /* Check for "between" criteria so we can do additional checks. */
  4489. if (has_criteria
  4490. && (validation->criteria == LXW_VALIDATION_CRITERIA_BETWEEN
  4491. || validation->criteria == LXW_VALIDATION_CRITERIA_NOT_BETWEEN)) {
  4492. is_between = LXW_TRUE;
  4493. }
  4494. /* Check that formula values are non NULL. */
  4495. if (is_formula) {
  4496. if (is_between) {
  4497. if (!validation->minimum_formula) {
  4498. LXW_WARN_FORMAT("worksheet_data_validation_cell()/_range(): "
  4499. "minimum_formula parameter cannot be NULL.");
  4500. return LXW_ERROR_PARAMETER_VALIDATION;
  4501. }
  4502. if (!validation->maximum_formula) {
  4503. LXW_WARN_FORMAT("worksheet_data_validation_cell()/_range(): "
  4504. "maximum_formula parameter cannot be NULL.");
  4505. return LXW_ERROR_PARAMETER_VALIDATION;
  4506. }
  4507. }
  4508. else {
  4509. if (!validation->value_formula) {
  4510. LXW_WARN_FORMAT("worksheet_data_validation_cell()/_range(): "
  4511. "formula parameter cannot be NULL.");
  4512. return LXW_ERROR_PARAMETER_VALIDATION;
  4513. }
  4514. }
  4515. }
  4516. /* Check Excel limitations on input strings. */
  4517. if (validation->input_title) {
  4518. length = lxw_utf8_strlen(validation->input_title);
  4519. if (length > LXW_VALIDATION_MAX_TITLE_LENGTH) {
  4520. LXW_WARN_FORMAT1("worksheet_data_validation_cell()/_range(): "
  4521. "input_title length > Excel limit of %d.",
  4522. LXW_VALIDATION_MAX_TITLE_LENGTH);
  4523. return LXW_ERROR_32_STRING_LENGTH_EXCEEDED;
  4524. }
  4525. }
  4526. if (validation->error_title) {
  4527. length = lxw_utf8_strlen(validation->error_title);
  4528. if (length > LXW_VALIDATION_MAX_TITLE_LENGTH) {
  4529. LXW_WARN_FORMAT1("worksheet_data_validation_cell()/_range(): "
  4530. "error_title length > Excel limit of %d.",
  4531. LXW_VALIDATION_MAX_TITLE_LENGTH);
  4532. return LXW_ERROR_32_STRING_LENGTH_EXCEEDED;
  4533. }
  4534. }
  4535. if (validation->input_message) {
  4536. length = lxw_utf8_strlen(validation->input_message);
  4537. if (length > LXW_VALIDATION_MAX_STRING_LENGTH) {
  4538. LXW_WARN_FORMAT1("worksheet_data_validation_cell()/_range(): "
  4539. "input_message length > Excel limit of %d.",
  4540. LXW_VALIDATION_MAX_STRING_LENGTH);
  4541. return LXW_ERROR_255_STRING_LENGTH_EXCEEDED;
  4542. }
  4543. }
  4544. if (validation->error_message) {
  4545. length = lxw_utf8_strlen(validation->error_message);
  4546. if (length > LXW_VALIDATION_MAX_STRING_LENGTH) {
  4547. LXW_WARN_FORMAT1("worksheet_data_validation_cell()/_range(): "
  4548. "error_message length > Excel limit of %d.",
  4549. LXW_VALIDATION_MAX_STRING_LENGTH);
  4550. return LXW_ERROR_255_STRING_LENGTH_EXCEEDED;
  4551. }
  4552. }
  4553. if (validation->validate == LXW_VALIDATION_TYPE_LIST) {
  4554. length = _validation_list_length(validation->value_list);
  4555. if (length == 0) {
  4556. LXW_WARN_FORMAT("worksheet_data_validation_cell()/_range(): "
  4557. "list parameters cannot be zero.");
  4558. return LXW_ERROR_PARAMETER_VALIDATION;
  4559. }
  4560. if (length > LXW_VALIDATION_MAX_STRING_LENGTH) {
  4561. LXW_WARN_FORMAT1("worksheet_data_validation_cell()/_range(): "
  4562. "list length with commas > Excel limit of %d.",
  4563. LXW_VALIDATION_MAX_STRING_LENGTH);
  4564. return LXW_ERROR_255_STRING_LENGTH_EXCEEDED;
  4565. }
  4566. }
  4567. /* Swap last row/col with first row/col as necessary */
  4568. if (first_row > last_row) {
  4569. tmp_row = last_row;
  4570. last_row = first_row;
  4571. first_row = tmp_row;
  4572. }
  4573. if (first_col > last_col) {
  4574. tmp_col = last_col;
  4575. last_col = first_col;
  4576. first_col = tmp_col;
  4577. }
  4578. /* Check that dimensions are valid but don't store them. */
  4579. err = _check_dimensions(self, last_row, last_col, LXW_TRUE, LXW_TRUE);
  4580. if (err)
  4581. return err;
  4582. /* Create a copy of the parameters from the user data validation. */
  4583. copy = calloc(1, sizeof(lxw_data_validation));
  4584. GOTO_LABEL_ON_MEM_ERROR(copy, mem_error);
  4585. /* Create the data validation range. */
  4586. if (first_row == last_row && first_col == last_col)
  4587. lxw_rowcol_to_cell(copy->sqref, first_row, last_col);
  4588. else
  4589. lxw_rowcol_to_range(copy->sqref, first_row, first_col, last_row,
  4590. last_col);
  4591. /* Copy the parameters from the user data validation. */
  4592. copy->validate = validation->validate;
  4593. copy->value_number = validation->value_number;
  4594. copy->error_type = validation->error_type;
  4595. copy->dropdown = validation->dropdown;
  4596. copy->is_between = is_between;
  4597. if (has_criteria)
  4598. copy->criteria = validation->criteria;
  4599. if (is_between) {
  4600. copy->value_number = validation->minimum_number;
  4601. copy->maximum_number = validation->maximum_number;
  4602. }
  4603. /* Copy the input/error titles and messages. */
  4604. if (validation->input_title) {
  4605. copy->input_title = lxw_strdup_formula(validation->input_title);
  4606. GOTO_LABEL_ON_MEM_ERROR(copy->input_title, mem_error);
  4607. }
  4608. if (validation->input_message) {
  4609. copy->input_message = lxw_strdup_formula(validation->input_message);
  4610. GOTO_LABEL_ON_MEM_ERROR(copy->input_message, mem_error);
  4611. }
  4612. if (validation->error_title) {
  4613. copy->error_title = lxw_strdup_formula(validation->error_title);
  4614. GOTO_LABEL_ON_MEM_ERROR(copy->error_title, mem_error);
  4615. }
  4616. if (validation->error_message) {
  4617. copy->error_message = lxw_strdup_formula(validation->error_message);
  4618. GOTO_LABEL_ON_MEM_ERROR(copy->error_message, mem_error);
  4619. }
  4620. /* Copy the formula strings. */
  4621. if (is_formula) {
  4622. if (is_between) {
  4623. copy->value_formula =
  4624. lxw_strdup_formula(validation->minimum_formula);
  4625. GOTO_LABEL_ON_MEM_ERROR(copy->value_formula, mem_error);
  4626. copy->maximum_formula =
  4627. lxw_strdup_formula(validation->maximum_formula);
  4628. GOTO_LABEL_ON_MEM_ERROR(copy->maximum_formula, mem_error);
  4629. }
  4630. else {
  4631. copy->value_formula =
  4632. lxw_strdup_formula(validation->value_formula);
  4633. GOTO_LABEL_ON_MEM_ERROR(copy->value_formula, mem_error);
  4634. }
  4635. }
  4636. /* Copy the validation list as a csv string. */
  4637. if (validation->validate == LXW_VALIDATION_TYPE_LIST) {
  4638. copy->value_formula = _validation_list_to_csv(validation->value_list);
  4639. GOTO_LABEL_ON_MEM_ERROR(copy->value_formula, mem_error);
  4640. }
  4641. if (validation->validate == LXW_VALIDATION_TYPE_LIST_FORMULA) {
  4642. copy->value_formula = lxw_strdup_formula(validation->value_formula);
  4643. GOTO_LABEL_ON_MEM_ERROR(copy->value_formula, mem_error);
  4644. }
  4645. if (validation->validate == LXW_VALIDATION_TYPE_DATE
  4646. || validation->validate == LXW_VALIDATION_TYPE_TIME) {
  4647. if (is_between) {
  4648. copy->value_number =
  4649. lxw_datetime_to_excel_date(&validation->minimum_datetime,
  4650. LXW_EPOCH_1900);
  4651. copy->maximum_number =
  4652. lxw_datetime_to_excel_date(&validation->maximum_datetime,
  4653. LXW_EPOCH_1900);
  4654. }
  4655. else {
  4656. copy->value_number =
  4657. lxw_datetime_to_excel_date(&validation->value_datetime,
  4658. LXW_EPOCH_1900);
  4659. }
  4660. }
  4661. /* These options are on by default so we can't take plain booleans. */
  4662. copy->ignore_blank = validation->ignore_blank ^ 1;
  4663. copy->show_input = validation->show_input ^ 1;
  4664. copy->show_error = validation->show_error ^ 1;
  4665. STAILQ_INSERT_TAIL(self->data_validations, copy, list_pointers);
  4666. self->num_validations++;
  4667. return LXW_NO_ERROR;
  4668. mem_error:
  4669. _free_data_validation(copy);
  4670. return LXW_ERROR_MEMORY_MALLOC_FAILED;
  4671. }
  4672. /*
  4673. * Add a data validation to a worksheet, for a cell.
  4674. */
  4675. lxw_error
  4676. worksheet_data_validation_cell(lxw_worksheet *self, lxw_row_t row,
  4677. lxw_col_t col, lxw_data_validation *validation)
  4678. {
  4679. return worksheet_data_validation_range(self, row, col,
  4680. row, col, validation);
  4681. }