123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777 |
- /*
- * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved.
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
- /*
- * Copyright notice for the EFL:
- * Copyright (C) EFL developers (see AUTHORS)
- * All rights reserved.
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
- * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
- * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- #include <cstring>
- #include <fstream>
- #include <float.h>
- #include "tvgLoader.h"
- #include "tvgXmlParser.h"
- #include "tvgSvgLoader.h"
- #include "tvgSvgSceneBuilder.h"
- #include "tvgStr.h"
- #include "tvgSvgCssStyle.h"
- #include "tvgMath.h"
- /************************************************************************/
- /* Internal Class Implementation */
- /************************************************************************/
- /*
- * According to: https://www.w3.org/TR/SVG2/coords.html#Units
- * and: https://www.w3.org/TR/css-values-4/#absolute-lengths
- */
- #define PX_PER_IN 96 //1 in = 96 px
- #define PX_PER_PC 16 //1 pc = 1/6 in -> PX_PER_IN/6
- #define PX_PER_PT 1.333333f //1 pt = 1/72 in -> PX_PER_IN/72
- #define PX_PER_MM 3.779528f //1 in = 25.4 mm -> PX_PER_IN/25.4
- #define PX_PER_CM 37.79528f //1 in = 2.54 cm -> PX_PER_IN/2.54
- typedef bool (*parseAttributes)(const char* buf, unsigned bufLength, simpleXMLAttributeCb func, const void* data);
- typedef SvgNode* (*FactoryMethod)(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func);
- typedef SvgStyleGradient* (*GradientFactoryMethod)(SvgLoaderData* loader, const char* buf, unsigned bufLength);
- static char* _skipSpace(const char* str, const char* end)
- {
- while (((end && str < end) || (!end && *str != '\0')) && isspace(*str)) {
- ++str;
- }
- return (char*) str;
- }
- static char* _copyId(const char* str)
- {
- if (!str) return nullptr;
- if (strlen(str) == 0) return nullptr;
- return strdup(str);
- }
- static const char* _skipComma(const char* content)
- {
- content = _skipSpace(content, nullptr);
- if (*content == ',') return content + 1;
- return content;
- }
- static bool _parseNumber(const char** content, float* number)
- {
- char* end = nullptr;
- *number = strToFloat(*content, &end);
- //If the start of string is not number
- if ((*content) == end) return false;
- //Skip comma if any
- *content = _skipComma(end);
- return true;
- }
- static constexpr struct
- {
- AspectRatioAlign align;
- const char* tag;
- } alignTags[] = {
- { AspectRatioAlign::XMinYMin, "xMinYMin" },
- { AspectRatioAlign::XMidYMin, "xMidYMin" },
- { AspectRatioAlign::XMaxYMin, "xMaxYMin" },
- { AspectRatioAlign::XMinYMid, "xMinYMid" },
- { AspectRatioAlign::XMidYMid, "xMidYMid" },
- { AspectRatioAlign::XMaxYMid, "xMaxYMid" },
- { AspectRatioAlign::XMinYMax, "xMinYMax" },
- { AspectRatioAlign::XMidYMax, "xMidYMax" },
- { AspectRatioAlign::XMaxYMax, "xMaxYMax" },
- };
- static void _parseAspectRatio(const char** content, AspectRatioAlign* align, AspectRatioMeetOrSlice* meetOrSlice)
- {
- if (!strcmp(*content, "none")) {
- *align = AspectRatioAlign::None;
- return;
- }
- for (unsigned int i = 0; i < sizeof(alignTags) / sizeof(alignTags[0]); i++) {
- if (!strncmp(*content, alignTags[i].tag, 8)) {
- *align = alignTags[i].align;
- *content += 8;
- *content = _skipSpace(*content, nullptr);
- break;
- }
- }
- if (!strcmp(*content, "meet")) {
- *meetOrSlice = AspectRatioMeetOrSlice::Meet;
- } else if (!strcmp(*content, "slice")) {
- *meetOrSlice = AspectRatioMeetOrSlice::Slice;
- }
- }
- /**
- * According to https://www.w3.org/TR/SVG/coords.html#Units
- */
- static float _toFloat(const SvgParser* svgParse, const char* str, SvgParserLengthType type)
- {
- float parsedValue = strToFloat(str, nullptr);
- if (strstr(str, "cm")) parsedValue *= PX_PER_CM;
- else if (strstr(str, "mm")) parsedValue *= PX_PER_MM;
- else if (strstr(str, "pt")) parsedValue *= PX_PER_PT;
- else if (strstr(str, "pc")) parsedValue *= PX_PER_PC;
- else if (strstr(str, "in")) parsedValue *= PX_PER_IN;
- else if (strstr(str, "%")) {
- if (type == SvgParserLengthType::Vertical) parsedValue = (parsedValue / 100.0) * svgParse->global.h;
- else if (type == SvgParserLengthType::Horizontal) parsedValue = (parsedValue / 100.0) * svgParse->global.w;
- else //if other then it's radius
- {
- float max = svgParse->global.w;
- if (max < svgParse->global.h)
- max = svgParse->global.h;
- parsedValue = (parsedValue / 100.0) * max;
- }
- }
- //TODO: Implement 'em', 'ex' attributes
- return parsedValue;
- }
- static float _gradientToFloat(const SvgParser* svgParse, const char* str, bool& isPercentage)
- {
- char* end = nullptr;
- float parsedValue = strToFloat(str, &end);
- isPercentage = false;
- if (strstr(str, "%")) {
- parsedValue = parsedValue / 100.0;
- isPercentage = true;
- }
- else if (strstr(str, "cm")) parsedValue *= PX_PER_CM;
- else if (strstr(str, "mm")) parsedValue *= PX_PER_MM;
- else if (strstr(str, "pt")) parsedValue *= PX_PER_PT;
- else if (strstr(str, "pc")) parsedValue *= PX_PER_PC;
- else if (strstr(str, "in")) parsedValue *= PX_PER_IN;
- //TODO: Implement 'em', 'ex' attributes
- return parsedValue;
- }
- static float _toOffset(const char* str)
- {
- char* end = nullptr;
- auto strEnd = str + strlen(str);
- float parsedValue = strToFloat(str, &end);
- end = _skipSpace(end, nullptr);
- auto ptr = strstr(str, "%");
- if (ptr) {
- parsedValue = parsedValue / 100.0;
- if (end != ptr || (end + 1) != strEnd) return 0;
- } else if (end != strEnd) return 0;
- return parsedValue;
- }
- static int _toOpacity(const char* str)
- {
- char* end = nullptr;
- float opacity = strToFloat(str, &end);
- if (end) {
- if (end[0] == '%' && end[1] == '\0') return lrint(opacity * 2.55f);
- else if (*end == '\0') return lrint(opacity * 255);
- }
- return 255;
- }
- static SvgMaskType _toMaskType(const char* str)
- {
- if (!strcmp(str, "Alpha")) return SvgMaskType::Alpha;
- return SvgMaskType::Luminance;
- }
- //The default rendering order: fill, stroke, markers
- //If any is omitted, will be rendered in its default order after the specified ones.
- static bool _toPaintOrder(const char* str)
- {
- uint8_t position = 1;
- uint8_t strokePosition = 0;
- uint8_t fillPosition = 0;
- while (*str != '\0') {
- str = _skipSpace(str, nullptr);
- if (!strncmp(str, "fill", 4)) {
- fillPosition = position++;
- str += 4;
- } else if (!strncmp(str, "stroke", 6)) {
- strokePosition = position++;
- str += 6;
- } else if (!strncmp(str, "markers", 7)) {
- str += 7;
- } else {
- return _toPaintOrder("fill stroke");
- }
- }
- if (fillPosition == 0) fillPosition = position++;
- if (strokePosition == 0) strokePosition = position++;
- return fillPosition < strokePosition;
- }
- #define _PARSE_TAG(Type, Name, Name1, Tags_Array, Default) \
- static Type _to##Name1(const char* str) \
- { \
- unsigned int i; \
- \
- for (i = 0; i < sizeof(Tags_Array) / sizeof(Tags_Array[0]); i++) { \
- if (!strcmp(str, Tags_Array[i].tag)) return Tags_Array[i].Name; \
- } \
- return Default; \
- }
- /* parse the line cap used during stroking a path.
- * Value: butt | round | square | inherit
- * Initial: butt
- * https://www.w3.org/TR/SVG/painting.html
- */
- static constexpr struct
- {
- StrokeCap lineCap;
- const char* tag;
- } lineCapTags[] = {
- { StrokeCap::Butt, "butt" },
- { StrokeCap::Round, "round" },
- { StrokeCap::Square, "square" }
- };
- _PARSE_TAG(StrokeCap, lineCap, LineCap, lineCapTags, StrokeCap::Butt)
- /* parse the line join used during stroking a path.
- * Value: miter | round | bevel | inherit
- * Initial: miter
- * https://www.w3.org/TR/SVG/painting.html
- */
- static constexpr struct
- {
- StrokeJoin lineJoin;
- const char* tag;
- } lineJoinTags[] = {
- { StrokeJoin::Miter, "miter" },
- { StrokeJoin::Round, "round" },
- { StrokeJoin::Bevel, "bevel" }
- };
- _PARSE_TAG(StrokeJoin, lineJoin, LineJoin, lineJoinTags, StrokeJoin::Miter)
- /* parse the fill rule used during filling a path.
- * Value: nonzero | evenodd | inherit
- * Initial: nonzero
- * https://www.w3.org/TR/SVG/painting.html
- */
- static constexpr struct
- {
- FillRule fillRule;
- const char* tag;
- } fillRuleTags[] = {
- { FillRule::EvenOdd, "evenodd" }
- };
- _PARSE_TAG(FillRule, fillRule, FillRule, fillRuleTags, FillRule::Winding)
- /* parse the dash pattern used during stroking a path.
- * Value: none | <dasharray> | inherit
- * Initial: none
- * https://www.w3.org/TR/SVG/painting.html
- */
- static void _parseDashArray(SvgLoaderData* loader, const char *str, SvgDash* dash)
- {
- if (!strncmp(str, "none", 4)) return;
- char *end = nullptr;
- while (*str) {
- str = _skipComma(str);
- float parsedValue = strToFloat(str, &end);
- if (str == end) break;
- if (parsedValue <= 0.0f) break;
- if (*end == '%') {
- ++end;
- //Refers to the diagonal length of the viewport.
- //https://www.w3.org/TR/SVG2/coords.html#Units
- parsedValue = (sqrtf(powf(loader->svgParse->global.w, 2) + powf(loader->svgParse->global.h, 2)) / sqrtf(2.0f)) * (parsedValue / 100.0f);
- }
- (*dash).array.push(parsedValue);
- str = end;
- }
- //If dash array size is 1, it means that dash and gap size are the same.
- if ((*dash).array.count == 1) (*dash).array.push((*dash).array[0]);
- }
- static char* _idFromUrl(const char* url)
- {
- auto open = strchr(url, '(');
- auto close = strchr(url, ')');
- if (!open || !close || open >= close) return nullptr;
- open = strchr(url, '#');
- if (!open || open >= close) return nullptr;
- ++open;
- --close;
- //trim the rest of the spaces if any
- while (open < close && *close == ' ') --close;
- //quick verification
- for (auto id = open; id < close; id++) {
- if (*id == ' ' || *id == '\'') return nullptr;
- }
- return strDuplicate(open, (close - open + 1));
- }
- static unsigned char _parseColor(const char* value, char** end)
- {
- float r;
- r = strToFloat(value, end);
- *end = _skipSpace(*end, nullptr);
- if (**end == '%') {
- r = 255 * r / 100;
- (*end)++;
- }
- *end = _skipSpace(*end, nullptr);
- if (r < 0 || r > 255) {
- *end = nullptr;
- return 0;
- }
- return lrint(r);
- }
- static constexpr struct
- {
- const char* name;
- unsigned int value;
- } colors[] = {
- { "aliceblue", 0xfff0f8ff },
- { "antiquewhite", 0xfffaebd7 },
- { "aqua", 0xff00ffff },
- { "aquamarine", 0xff7fffd4 },
- { "azure", 0xfff0ffff },
- { "beige", 0xfff5f5dc },
- { "bisque", 0xffffe4c4 },
- { "black", 0xff000000 },
- { "blanchedalmond", 0xffffebcd },
- { "blue", 0xff0000ff },
- { "blueviolet", 0xff8a2be2 },
- { "brown", 0xffa52a2a },
- { "burlywood", 0xffdeb887 },
- { "cadetblue", 0xff5f9ea0 },
- { "chartreuse", 0xff7fff00 },
- { "chocolate", 0xffd2691e },
- { "coral", 0xffff7f50 },
- { "cornflowerblue", 0xff6495ed },
- { "cornsilk", 0xfffff8dc },
- { "crimson", 0xffdc143c },
- { "cyan", 0xff00ffff },
- { "darkblue", 0xff00008b },
- { "darkcyan", 0xff008b8b },
- { "darkgoldenrod", 0xffb8860b },
- { "darkgray", 0xffa9a9a9 },
- { "darkgrey", 0xffa9a9a9 },
- { "darkgreen", 0xff006400 },
- { "darkkhaki", 0xffbdb76b },
- { "darkmagenta", 0xff8b008b },
- { "darkolivegreen", 0xff556b2f },
- { "darkorange", 0xffff8c00 },
- { "darkorchid", 0xff9932cc },
- { "darkred", 0xff8b0000 },
- { "darksalmon", 0xffe9967a },
- { "darkseagreen", 0xff8fbc8f },
- { "darkslateblue", 0xff483d8b },
- { "darkslategray", 0xff2f4f4f },
- { "darkslategrey", 0xff2f4f4f },
- { "darkturquoise", 0xff00ced1 },
- { "darkviolet", 0xff9400d3 },
- { "deeppink", 0xffff1493 },
- { "deepskyblue", 0xff00bfff },
- { "dimgray", 0xff696969 },
- { "dimgrey", 0xff696969 },
- { "dodgerblue", 0xff1e90ff },
- { "firebrick", 0xffb22222 },
- { "floralwhite", 0xfffffaf0 },
- { "forestgreen", 0xff228b22 },
- { "fuchsia", 0xffff00ff },
- { "gainsboro", 0xffdcdcdc },
- { "ghostwhite", 0xfff8f8ff },
- { "gold", 0xffffd700 },
- { "goldenrod", 0xffdaa520 },
- { "gray", 0xff808080 },
- { "grey", 0xff808080 },
- { "green", 0xff008000 },
- { "greenyellow", 0xffadff2f },
- { "honeydew", 0xfff0fff0 },
- { "hotpink", 0xffff69b4 },
- { "indianred", 0xffcd5c5c },
- { "indigo", 0xff4b0082 },
- { "ivory", 0xfffffff0 },
- { "khaki", 0xfff0e68c },
- { "lavender", 0xffe6e6fa },
- { "lavenderblush", 0xfffff0f5 },
- { "lawngreen", 0xff7cfc00 },
- { "lemonchiffon", 0xfffffacd },
- { "lightblue", 0xffadd8e6 },
- { "lightcoral", 0xfff08080 },
- { "lightcyan", 0xffe0ffff },
- { "lightgoldenrodyellow", 0xfffafad2 },
- { "lightgray", 0xffd3d3d3 },
- { "lightgrey", 0xffd3d3d3 },
- { "lightgreen", 0xff90ee90 },
- { "lightpink", 0xffffb6c1 },
- { "lightsalmon", 0xffffa07a },
- { "lightseagreen", 0xff20b2aa },
- { "lightskyblue", 0xff87cefa },
- { "lightslategray", 0xff778899 },
- { "lightslategrey", 0xff778899 },
- { "lightsteelblue", 0xffb0c4de },
- { "lightyellow", 0xffffffe0 },
- { "lime", 0xff00ff00 },
- { "limegreen", 0xff32cd32 },
- { "linen", 0xfffaf0e6 },
- { "magenta", 0xffff00ff },
- { "maroon", 0xff800000 },
- { "mediumaquamarine", 0xff66cdaa },
- { "mediumblue", 0xff0000cd },
- { "mediumorchid", 0xffba55d3 },
- { "mediumpurple", 0xff9370d8 },
- { "mediumseagreen", 0xff3cb371 },
- { "mediumslateblue", 0xff7b68ee },
- { "mediumspringgreen", 0xff00fa9a },
- { "mediumturquoise", 0xff48d1cc },
- { "mediumvioletred", 0xffc71585 },
- { "midnightblue", 0xff191970 },
- { "mintcream", 0xfff5fffa },
- { "mistyrose", 0xffffe4e1 },
- { "moccasin", 0xffffe4b5 },
- { "navajowhite", 0xffffdead },
- { "navy", 0xff000080 },
- { "oldlace", 0xfffdf5e6 },
- { "olive", 0xff808000 },
- { "olivedrab", 0xff6b8e23 },
- { "orange", 0xffffa500 },
- { "orangered", 0xffff4500 },
- { "orchid", 0xffda70d6 },
- { "palegoldenrod", 0xffeee8aa },
- { "palegreen", 0xff98fb98 },
- { "paleturquoise", 0xffafeeee },
- { "palevioletred", 0xffd87093 },
- { "papayawhip", 0xffffefd5 },
- { "peachpuff", 0xffffdab9 },
- { "peru", 0xffcd853f },
- { "pink", 0xffffc0cb },
- { "plum", 0xffdda0dd },
- { "powderblue", 0xffb0e0e6 },
- { "purple", 0xff800080 },
- { "red", 0xffff0000 },
- { "rosybrown", 0xffbc8f8f },
- { "royalblue", 0xff4169e1 },
- { "saddlebrown", 0xff8b4513 },
- { "salmon", 0xfffa8072 },
- { "sandybrown", 0xfff4a460 },
- { "seagreen", 0xff2e8b57 },
- { "seashell", 0xfffff5ee },
- { "sienna", 0xffa0522d },
- { "silver", 0xffc0c0c0 },
- { "skyblue", 0xff87ceeb },
- { "slateblue", 0xff6a5acd },
- { "slategray", 0xff708090 },
- { "slategrey", 0xff708090 },
- { "snow", 0xfffffafa },
- { "springgreen", 0xff00ff7f },
- { "steelblue", 0xff4682b4 },
- { "tan", 0xffd2b48c },
- { "teal", 0xff008080 },
- { "thistle", 0xffd8bfd8 },
- { "tomato", 0xffff6347 },
- { "turquoise", 0xff40e0d0 },
- { "violet", 0xffee82ee },
- { "wheat", 0xfff5deb3 },
- { "white", 0xffffffff },
- { "whitesmoke", 0xfff5f5f5 },
- { "yellow", 0xffffff00 },
- { "yellowgreen", 0xff9acd32 }
- };
- static void _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char** ref)
- {
- unsigned int len = strlen(str);
- char *red, *green, *blue;
- unsigned char tr, tg, tb;
- if (len == 4 && str[0] == '#') {
- //Case for "#456" should be interprete as "#445566"
- if (isxdigit(str[1]) && isxdigit(str[2]) && isxdigit(str[3])) {
- char tmp[3] = { '\0', '\0', '\0' };
- tmp[0] = str[1];
- tmp[1] = str[1];
- *r = strtol(tmp, nullptr, 16);
- tmp[0] = str[2];
- tmp[1] = str[2];
- *g = strtol(tmp, nullptr, 16);
- tmp[0] = str[3];
- tmp[1] = str[3];
- *b = strtol(tmp, nullptr, 16);
- }
- } else if (len == 7 && str[0] == '#') {
- if (isxdigit(str[1]) && isxdigit(str[2]) && isxdigit(str[3]) && isxdigit(str[4]) && isxdigit(str[5]) && isxdigit(str[6])) {
- char tmp[3] = { '\0', '\0', '\0' };
- tmp[0] = str[1];
- tmp[1] = str[2];
- *r = strtol(tmp, nullptr, 16);
- tmp[0] = str[3];
- tmp[1] = str[4];
- *g = strtol(tmp, nullptr, 16);
- tmp[0] = str[5];
- tmp[1] = str[6];
- *b = strtol(tmp, nullptr, 16);
- }
- } else if (len >= 10 && (str[0] == 'r' || str[0] == 'R') && (str[1] == 'g' || str[1] == 'G') && (str[2] == 'b' || str[2] == 'B') && str[3] == '(' && str[len - 1] == ')') {
- tr = _parseColor(str + 4, &red);
- if (red && *red == ',') {
- tg = _parseColor(red + 1, &green);
- if (green && *green == ',') {
- tb = _parseColor(green + 1, &blue);
- if (blue && blue[0] == ')' && blue[1] == '\0') {
- *r = tr;
- *g = tg;
- *b = tb;
- }
- }
- }
- } else if (ref && len >= 3 && !strncmp(str, "url", 3)) {
- if (*ref) free(*ref);
- *ref = _idFromUrl((const char*)(str + 3));
- } else {
- //Handle named color
- for (unsigned int i = 0; i < (sizeof(colors) / sizeof(colors[0])); i++) {
- if (!strcasecmp(colors[i].name, str)) {
- *r = (((uint8_t*)(&(colors[i].value)))[2]);
- *g = (((uint8_t*)(&(colors[i].value)))[1]);
- *b = (((uint8_t*)(&(colors[i].value)))[0]);
- return;
- }
- }
- }
- }
- static char* _parseNumbersArray(char* str, float* points, int* ptCount, int len)
- {
- int count = 0;
- char* end = nullptr;
- str = _skipSpace(str, nullptr);
- while ((count < len) && (isdigit(*str) || *str == '-' || *str == '+' || *str == '.')) {
- points[count++] = strToFloat(str, &end);
- str = end;
- str = _skipSpace(str, nullptr);
- if (*str == ',') ++str;
- //Eat the rest of space
- str = _skipSpace(str, nullptr);
- }
- *ptCount = count;
- return str;
- }
- enum class MatrixState {
- Unknown,
- Matrix,
- Translate,
- Rotate,
- Scale,
- SkewX,
- SkewY
- };
- #define MATRIX_DEF(Name, Value) \
- { \
- #Name, sizeof(#Name), Value \
- }
- static constexpr struct
- {
- const char* tag;
- int sz;
- MatrixState state;
- } matrixTags[] = {
- MATRIX_DEF(matrix, MatrixState::Matrix),
- MATRIX_DEF(translate, MatrixState::Translate),
- MATRIX_DEF(rotate, MatrixState::Rotate),
- MATRIX_DEF(scale, MatrixState::Scale),
- MATRIX_DEF(skewX, MatrixState::SkewX),
- MATRIX_DEF(skewY, MatrixState::SkewY)
- };
- /* parse transform attribute
- * https://www.w3.org/TR/SVG/coords.html#TransformAttribute
- */
- static Matrix* _parseTransformationMatrix(const char* value)
- {
- const int POINT_CNT = 8;
- auto matrix = (Matrix*)malloc(sizeof(Matrix));
- if (!matrix) return nullptr;
- *matrix = {1, 0, 0, 0, 1, 0, 0, 0, 1};
- float points[POINT_CNT];
- int ptCount = 0;
- char* str = (char*)value;
- char* end = str + strlen(str);
- while (str < end) {
- auto state = MatrixState::Unknown;
- if (isspace(*str) || (*str == ',')) {
- ++str;
- continue;
- }
- for (unsigned int i = 0; i < sizeof(matrixTags) / sizeof(matrixTags[0]); i++) {
- if (!strncmp(matrixTags[i].tag, str, matrixTags[i].sz - 1)) {
- state = matrixTags[i].state;
- str += (matrixTags[i].sz - 1);
- break;
- }
- }
- if (state == MatrixState::Unknown) goto error;
- str = _skipSpace(str, end);
- if (*str != '(') goto error;
- ++str;
- str = _parseNumbersArray(str, points, &ptCount, POINT_CNT);
- if (*str != ')') goto error;
- ++str;
- if (state == MatrixState::Matrix) {
- if (ptCount != 6) goto error;
- Matrix tmp = {points[0], points[2], points[4], points[1], points[3], points[5], 0, 0, 1};
- *matrix = mathMultiply(matrix, &tmp);
- } else if (state == MatrixState::Translate) {
- if (ptCount == 1) {
- Matrix tmp = {1, 0, points[0], 0, 1, 0, 0, 0, 1};
- *matrix = mathMultiply(matrix, &tmp);
- } else if (ptCount == 2) {
- Matrix tmp = {1, 0, points[0], 0, 1, points[1], 0, 0, 1};
- *matrix = mathMultiply(matrix, &tmp);
- } else goto error;
- } else if (state == MatrixState::Rotate) {
- //Transform to signed.
- points[0] = fmodf(points[0], 360.0f);
- if (points[0] < 0) points[0] += 360.0f;
- auto c = cosf(points[0] * (MATH_PI / 180.0f));
- auto s = sinf(points[0] * (MATH_PI / 180.0f));
- if (ptCount == 1) {
- Matrix tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
- *matrix = mathMultiply(matrix, &tmp);
- } else if (ptCount == 3) {
- Matrix tmp = { 1, 0, points[1], 0, 1, points[2], 0, 0, 1 };
- *matrix = mathMultiply(matrix, &tmp);
- tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
- *matrix = mathMultiply(matrix, &tmp);
- tmp = { 1, 0, -points[1], 0, 1, -points[2], 0, 0, 1 };
- *matrix = mathMultiply(matrix, &tmp);
- } else {
- goto error;
- }
- } else if (state == MatrixState::Scale) {
- if (ptCount < 1 || ptCount > 2) goto error;
- auto sx = points[0];
- auto sy = sx;
- if (ptCount == 2) sy = points[1];
- Matrix tmp = { sx, 0, 0, 0, sy, 0, 0, 0, 1 };
- *matrix = mathMultiply(matrix, &tmp);
- } else if (state == MatrixState::SkewX) {
- if (ptCount != 1) goto error;
- auto deg = tanf(points[0] * (MATH_PI / 180.0f));
- Matrix tmp = { 1, deg, 0, 0, 1, 0, 0, 0, 1 };
- *matrix = mathMultiply(matrix, &tmp);
- } else if (state == MatrixState::SkewY) {
- if (ptCount != 1) goto error;
- auto deg = tanf(points[0] * (MATH_PI / 180.0f));
- Matrix tmp = { 1, 0, 0, deg, 1, 0, 0, 0, 1 };
- *matrix = mathMultiply(matrix, &tmp);
- }
- }
- return matrix;
- error:
- if (matrix) free(matrix);
- return nullptr;
- }
- #define LENGTH_DEF(Name, Value) \
- { \
- #Name, sizeof(#Name), Value \
- }
- static void _postpone(Array<SvgNodeIdPair>& nodes, SvgNode *node, char* id)
- {
- nodes.push({node, id});
- }
- /*
- // TODO - remove?
- static constexpr struct
- {
- const char* tag;
- int sz;
- SvgLengthType type;
- } lengthTags[] = {
- LENGTH_DEF(%, SvgLengthType::Percent),
- LENGTH_DEF(px, SvgLengthType::Px),
- LENGTH_DEF(pc, SvgLengthType::Pc),
- LENGTH_DEF(pt, SvgLengthType::Pt),
- LENGTH_DEF(mm, SvgLengthType::Mm),
- LENGTH_DEF(cm, SvgLengthType::Cm),
- LENGTH_DEF(in, SvgLengthType::In)
- };
- static float _parseLength(const char* str, SvgLengthType* type)
- {
- float value;
- int sz = strlen(str);
- *type = SvgLengthType::Px;
- for (unsigned int i = 0; i < sizeof(lengthTags) / sizeof(lengthTags[0]); i++) {
- if (lengthTags[i].sz - 1 == sz && !strncmp(lengthTags[i].tag, str, sz)) *type = lengthTags[i].type;
- }
- value = svgUtilStrtof(str, nullptr);
- return value;
- }
- */
- static bool _parseStyleAttr(void* data, const char* key, const char* value);
- static bool _parseStyleAttr(void* data, const char* key, const char* value, bool style);
- static bool _attrParseSvgNode(void* data, const char* key, const char* value)
- {
- SvgLoaderData* loader = (SvgLoaderData*)data;
- SvgNode* node = loader->svgParse->node;
- SvgDocNode* doc = &(node->node.doc);
- if (!strcmp(key, "width")) {
- doc->w = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
- if (strstr(value, "%") && !(doc->viewFlag & SvgViewFlag::Viewbox)) {
- doc->viewFlag = (doc->viewFlag | SvgViewFlag::WidthInPercent);
- } else {
- doc->viewFlag = (doc->viewFlag | SvgViewFlag::Width);
- }
- } else if (!strcmp(key, "height")) {
- doc->h = _toFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
- if (strstr(value, "%") && !(doc->viewFlag & SvgViewFlag::Viewbox)) {
- doc->viewFlag = (doc->viewFlag | SvgViewFlag::HeightInPercent);
- } else {
- doc->viewFlag = (doc->viewFlag | SvgViewFlag::Height);
- }
- } else if (!strcmp(key, "viewBox")) {
- if (_parseNumber(&value, &doc->vx)) {
- if (_parseNumber(&value, &doc->vy)) {
- if (_parseNumber(&value, &doc->vw)) {
- if (_parseNumber(&value, &doc->vh)) {
- doc->viewFlag = (doc->viewFlag | SvgViewFlag::Viewbox);
- loader->svgParse->global.h = doc->vh;
- }
- loader->svgParse->global.w = doc->vw;
- }
- loader->svgParse->global.y = doc->vy;
- }
- loader->svgParse->global.x = doc->vx;
- }
- if ((doc->viewFlag & SvgViewFlag::Viewbox) && (doc->vw < 0.0f || doc->vh < 0.0f)) {
- doc->viewFlag = (SvgViewFlag)((uint32_t)doc->viewFlag & ~(uint32_t)SvgViewFlag::Viewbox);
- TVGLOG("SVG", "Negative values of the <viewBox> width and/or height - the attribute invalidated.");
- }
- if (!(doc->viewFlag & SvgViewFlag::Viewbox)) {
- loader->svgParse->global.x = loader->svgParse->global.y = 0.0f;
- loader->svgParse->global.w = loader->svgParse->global.h = 1.0f;
- }
- } else if (!strcmp(key, "preserveAspectRatio")) {
- _parseAspectRatio(&value, &doc->align, &doc->meetOrSlice);
- } else if (!strcmp(key, "style")) {
- return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
- #ifdef THORVG_LOG_ENABLED
- } else if ((!strcmp(key, "x") || !strcmp(key, "y")) && fabsf(strToFloat(value, nullptr)) > FLT_EPSILON) {
- TVGLOG("SVG", "Unsupported attributes used [Elements type: Svg][Attribute: %s][Value: %s]", key, value);
- #endif
- } else {
- return _parseStyleAttr(loader, key, value, false);
- }
- return true;
- }
- //https://www.w3.org/TR/SVGTiny12/painting.html#SpecifyingPaint
- static void _handlePaintAttr(SvgPaint* paint, const char* value)
- {
- if (!strcmp(value, "none")) {
- //No paint property
- paint->none = true;
- return;
- }
- paint->none = false;
- if (!strcmp(value, "currentColor")) {
- paint->curColor = true;
- return;
- }
- _toColor(value, &paint->color.r, &paint->color.g, &paint->color.b, &paint->url);
- }
- static void _handleColorAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
- {
- SvgStyleProperty* style = node->style;
- style->curColorSet = true;
- _toColor(value, &style->color.r, &style->color.g, &style->color.b, nullptr);
- }
- static void _handleFillAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
- {
- SvgStyleProperty* style = node->style;
- style->fill.flags = (style->fill.flags | SvgFillFlags::Paint);
- _handlePaintAttr(&style->fill.paint, value);
- }
- static void _handleStrokeAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
- {
- SvgStyleProperty* style = node->style;
- style->stroke.flags = (style->stroke.flags | SvgStrokeFlags::Paint);
- _handlePaintAttr(&style->stroke.paint, value);
- }
- static void _handleStrokeOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
- {
- node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Opacity);
- node->style->stroke.opacity = _toOpacity(value);
- }
- static void _handleStrokeDashArrayAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
- {
- node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Dash);
- _parseDashArray(loader, value, &node->style->stroke.dash);
- }
- static void _handleStrokeDashOffsetAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
- {
- node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::DashOffset);
- node->style->stroke.dash.offset = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
- }
- static void _handleStrokeWidthAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
- {
- node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Width);
- node->style->stroke.width = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
- }
- static void _handleStrokeLineCapAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
- {
- node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Cap);
- node->style->stroke.cap = _toLineCap(value);
- }
- static void _handleStrokeLineJoinAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
- {
- node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Join);
- node->style->stroke.join = _toLineJoin(value);
- }
- static void _handleStrokeMiterlimitAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
- {
- char* end = nullptr;
- const float miterlimit = strToFloat(value, &end);
- // https://www.w3.org/TR/SVG2/painting.html#LineJoin
- // - A negative value for stroke-miterlimit must be treated as an illegal value.
- if (miterlimit < 0.0f) {
- TVGERR("SVG", "A stroke-miterlimit change (%f <- %f) with a negative value is omitted.",
- node->style->stroke.miterlimit, miterlimit);
- return;
- }
- node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Miterlimit);
- node->style->stroke.miterlimit = miterlimit;
- }
- static void _handleFillRuleAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
- {
- node->style->fill.flags = (node->style->fill.flags | SvgFillFlags::FillRule);
- node->style->fill.fillRule = _toFillRule(value);
- }
- static void _handleOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
- {
- node->style->opacity = _toOpacity(value);
- }
- static void _handleFillOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
- {
- node->style->fill.flags = (node->style->fill.flags | SvgFillFlags::Opacity);
- node->style->fill.opacity = _toOpacity(value);
- }
- static void _handleTransformAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
- {
- node->transform = _parseTransformationMatrix(value);
- }
- static void _handleClipPathAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
- {
- SvgStyleProperty* style = node->style;
- int len = strlen(value);
- if (len >= 3 && !strncmp(value, "url", 3)) {
- if (style->clipPath.url) free(style->clipPath.url);
- style->clipPath.url = _idFromUrl((const char*)(value + 3));
- }
- }
- static void _handleMaskAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
- {
- SvgStyleProperty* style = node->style;
- int len = strlen(value);
- if (len >= 3 && !strncmp(value, "url", 3)) {
- if (style->mask.url) free(style->mask.url);
- style->mask.url = _idFromUrl((const char*)(value + 3));
- }
- }
- static void _handleMaskTypeAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
- {
- node->node.mask.type = _toMaskType(value);
- }
- static void _handleDisplayAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
- {
- //TODO : The display attribute can have various values as well as "none".
- // The default is "inline" which means visible and "none" means invisible.
- // Depending on the type of node, additional functionality may be required.
- // refer to https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/display
- if (!strcmp(value, "none")) node->display = false;
- else node->display = true;
- }
- static void _handlePaintOrderAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
- {
- node->style->flags = (node->style->flags | SvgStyleFlags::PaintOrder);
- node->style->paintOrder = _toPaintOrder(value);
- }
- static void _handleCssClassAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
- {
- auto cssClass = &node->style->cssClass;
- if (*cssClass && value) free(*cssClass);
- *cssClass = _copyId(value);
- bool cssClassFound = false;
- //css styling: tag.name has higher priority than .name
- if (auto cssNode = cssFindStyleNode(loader->cssStyle, *cssClass, node->type)) {
- cssClassFound = true;
- cssCopyStyleAttr(node, cssNode);
- }
- if (auto cssNode = cssFindStyleNode(loader->cssStyle, *cssClass)) {
- cssClassFound = true;
- cssCopyStyleAttr(node, cssNode);
- }
- if (!cssClassFound) _postpone(loader->nodesToStyle, node, *cssClass);
- }
- typedef void (*styleMethod)(SvgLoaderData* loader, SvgNode* node, const char* value);
- #define STYLE_DEF(Name, Name1, Flag) { #Name, sizeof(#Name), _handle##Name1##Attr, Flag }
- static constexpr struct
- {
- const char* tag;
- int sz;
- styleMethod tagHandler;
- SvgStyleFlags flag;
- } styleTags[] = {
- STYLE_DEF(color, Color, SvgStyleFlags::Color),
- STYLE_DEF(fill, Fill, SvgStyleFlags::Fill),
- STYLE_DEF(fill-rule, FillRule, SvgStyleFlags::FillRule),
- STYLE_DEF(fill-opacity, FillOpacity, SvgStyleFlags::FillOpacity),
- STYLE_DEF(opacity, Opacity, SvgStyleFlags::Opacity),
- STYLE_DEF(stroke, Stroke, SvgStyleFlags::Stroke),
- STYLE_DEF(stroke-width, StrokeWidth, SvgStyleFlags::StrokeWidth),
- STYLE_DEF(stroke-linejoin, StrokeLineJoin, SvgStyleFlags::StrokeLineJoin),
- STYLE_DEF(stroke-miterlimit, StrokeMiterlimit, SvgStyleFlags::StrokeMiterlimit),
- STYLE_DEF(stroke-linecap, StrokeLineCap, SvgStyleFlags::StrokeLineCap),
- STYLE_DEF(stroke-opacity, StrokeOpacity, SvgStyleFlags::StrokeOpacity),
- STYLE_DEF(stroke-dasharray, StrokeDashArray, SvgStyleFlags::StrokeDashArray),
- STYLE_DEF(stroke-dashoffset, StrokeDashOffset, SvgStyleFlags::StrokeDashOffset),
- STYLE_DEF(transform, Transform, SvgStyleFlags::Transform),
- STYLE_DEF(clip-path, ClipPath, SvgStyleFlags::ClipPath),
- STYLE_DEF(mask, Mask, SvgStyleFlags::Mask),
- STYLE_DEF(mask-type, MaskType, SvgStyleFlags::MaskType),
- STYLE_DEF(display, Display, SvgStyleFlags::Display),
- STYLE_DEF(paint-order, PaintOrder, SvgStyleFlags::PaintOrder)
- };
- static bool _parseStyleAttr(void* data, const char* key, const char* value, bool style)
- {
- SvgLoaderData* loader = (SvgLoaderData*)data;
- SvgNode* node = loader->svgParse->node;
- int sz;
- if (!key || !value) return false;
- //Trim the white space
- key = _skipSpace(key, nullptr);
- value = _skipSpace(value, nullptr);
- sz = strlen(key);
- for (unsigned int i = 0; i < sizeof(styleTags) / sizeof(styleTags[0]); i++) {
- if (styleTags[i].sz - 1 == sz && !strncmp(styleTags[i].tag, key, sz)) {
- bool importance = false;
- if (auto ptr = strstr(value, "!important")) {
- size_t size = ptr - value;
- while (size > 0 && isspace(value[size - 1])) {
- size--;
- }
- value = strDuplicate(value, size);
- importance = true;
- }
- if (style) {
- if (importance || !(node->style->flagsImportance & styleTags[i].flag)) {
- styleTags[i].tagHandler(loader, node, value);
- node->style->flags = (node->style->flags | styleTags[i].flag);
- }
- } else if (!(node->style->flags & styleTags[i].flag)) {
- styleTags[i].tagHandler(loader, node, value);
- }
- if (importance) {
- node->style->flagsImportance = (node->style->flags | styleTags[i].flag);
- free(const_cast<char*>(value));
- }
- return true;
- }
- }
- return false;
- }
- static bool _parseStyleAttr(void* data, const char* key, const char* value)
- {
- return _parseStyleAttr(data, key, value, true);
- }
- /* parse g node
- * https://www.w3.org/TR/SVG/struct.html#Groups
- */
- static bool _attrParseGNode(void* data, const char* key, const char* value)
- {
- SvgLoaderData* loader = (SvgLoaderData*)data;
- SvgNode* node = loader->svgParse->node;
- if (!strcmp(key, "style")) {
- return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
- } else if (!strcmp(key, "transform")) {
- node->transform = _parseTransformationMatrix(value);
- } else if (!strcmp(key, "id")) {
- if (node->id && value) free(node->id);
- node->id = _copyId(value);
- } else if (!strcmp(key, "class")) {
- _handleCssClassAttr(loader, node, value);
- } else if (!strcmp(key, "clip-path")) {
- _handleClipPathAttr(loader, node, value);
- } else if (!strcmp(key, "mask")) {
- _handleMaskAttr(loader, node, value);
- } else {
- return _parseStyleAttr(loader, key, value, false);
- }
- return true;
- }
- /* parse clipPath node
- * https://www.w3.org/TR/SVG/struct.html#Groups
- */
- static bool _attrParseClipPathNode(void* data, const char* key, const char* value)
- {
- SvgLoaderData* loader = (SvgLoaderData*)data;
- SvgNode* node = loader->svgParse->node;
- SvgClipNode* clip = &(node->node.clip);
- if (!strcmp(key, "style")) {
- return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
- } else if (!strcmp(key, "transform")) {
- node->transform = _parseTransformationMatrix(value);
- } else if (!strcmp(key, "id")) {
- if (node->id && value) free(node->id);
- node->id = _copyId(value);
- } else if (!strcmp(key, "class")) {
- _handleCssClassAttr(loader, node, value);
- } else if (!strcmp(key, "clipPathUnits")) {
- if (!strcmp(value, "objectBoundingBox")) clip->userSpace = false;
- } else {
- return _parseStyleAttr(loader, key, value, false);
- }
- return true;
- }
- static bool _attrParseMaskNode(void* data, const char* key, const char* value)
- {
- SvgLoaderData* loader = (SvgLoaderData*)data;
- SvgNode* node = loader->svgParse->node;
- SvgMaskNode* mask = &(node->node.mask);
- if (!strcmp(key, "style")) {
- return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
- } else if (!strcmp(key, "transform")) {
- node->transform = _parseTransformationMatrix(value);
- } else if (!strcmp(key, "id")) {
- if (node->id && value) free(node->id);
- node->id = _copyId(value);
- } else if (!strcmp(key, "class")) {
- _handleCssClassAttr(loader, node, value);
- } else if (!strcmp(key, "maskContentUnits")) {
- if (!strcmp(value, "objectBoundingBox")) mask->userSpace = false;
- } else if (!strcmp(key, "mask-type")) {
- mask->type = _toMaskType(value);
- } else {
- return _parseStyleAttr(loader, key, value, false);
- }
- return true;
- }
- static bool _attrParseCssStyleNode(void* data, const char* key, const char* value)
- {
- SvgLoaderData* loader = (SvgLoaderData*)data;
- SvgNode* node = loader->svgParse->node;
- if (!strcmp(key, "id")) {
- if (node->id && value) free(node->id);
- node->id = _copyId(value);
- } else {
- return _parseStyleAttr(loader, key, value, false);
- }
- return true;
- }
- static bool _attrParseSymbolNode(void* data, const char* key, const char* value)
- {
- SvgLoaderData* loader = (SvgLoaderData*)data;
- SvgNode* node = loader->svgParse->node;
- SvgSymbolNode* symbol = &(node->node.symbol);
- if (!strcmp(key, "viewBox")) {
- if (!_parseNumber(&value, &symbol->vx) || !_parseNumber(&value, &symbol->vy)) return false;
- if (!_parseNumber(&value, &symbol->vw) || !_parseNumber(&value, &symbol->vh)) return false;
- symbol->hasViewBox = true;
- } else if (!strcmp(key, "width")) {
- symbol->w = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
- symbol->hasWidth = true;
- } else if (!strcmp(key, "height")) {
- symbol->h = _toFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
- symbol->hasHeight = true;
- } else if (!strcmp(key, "preserveAspectRatio")) {
- _parseAspectRatio(&value, &symbol->align, &symbol->meetOrSlice);
- } else if (!strcmp(key, "overflow")) {
- if (!strcmp(value, "visible")) symbol->overflowVisible = true;
- } else {
- return _attrParseGNode(data, key, value);
- }
- return true;
- }
- static SvgNode* _createNode(SvgNode* parent, SvgNodeType type)
- {
- SvgNode* node = (SvgNode*)calloc(1, sizeof(SvgNode));
- if (!node) return nullptr;
- //Default fill property
- node->style = (SvgStyleProperty*)calloc(1, sizeof(SvgStyleProperty));
- if (!node->style) {
- free(node);
- return nullptr;
- }
- //Update the default value of stroke and fill
- //https://www.w3.org/TR/SVGTiny12/painting.html#SpecifyingPaint
- node->style->fill.paint.none = false;
- //Default fill opacity is 1
- node->style->fill.opacity = 255;
- node->style->opacity = 255;
- //Default current color is not set
- node->style->fill.paint.curColor = false;
- node->style->curColorSet = false;
- //Default fill rule is nonzero
- node->style->fill.fillRule = FillRule::Winding;
- //Default stroke is none
- node->style->stroke.paint.none = true;
- //Default stroke opacity is 1
- node->style->stroke.opacity = 255;
- //Default stroke current color is not set
- node->style->stroke.paint.curColor = false;
- //Default stroke width is 1
- node->style->stroke.width = 1;
- //Default line cap is butt
- node->style->stroke.cap = StrokeCap::Butt;
- //Default line join is miter
- node->style->stroke.join = StrokeJoin::Miter;
- node->style->stroke.miterlimit = 4.0f;
- node->style->stroke.scale = 1.0;
- node->style->paintOrder = _toPaintOrder("fill stroke");
- //Default display is true("inline").
- node->display = true;
- node->parent = parent;
- node->type = type;
- if (parent) parent->child.push(node);
- return node;
- }
- static SvgNode* _createDefsNode(TVG_UNUSED SvgLoaderData* loader, TVG_UNUSED SvgNode* parent, const char* buf, unsigned bufLength, TVG_UNUSED parseAttributes func)
- {
- if (loader->def && loader->doc->node.doc.defs) return loader->def;
- SvgNode* node = _createNode(nullptr, SvgNodeType::Defs);
- loader->def = node;
- loader->doc->node.doc.defs = node;
- return node;
- }
- static SvgNode* _createGNode(TVG_UNUSED SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
- {
- loader->svgParse->node = _createNode(parent, SvgNodeType::G);
- if (!loader->svgParse->node) return nullptr;
- func(buf, bufLength, _attrParseGNode, loader);
- return loader->svgParse->node;
- }
- static SvgNode* _createSvgNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
- {
- loader->svgParse->node = _createNode(parent, SvgNodeType::Doc);
- if (!loader->svgParse->node) return nullptr;
- SvgDocNode* doc = &(loader->svgParse->node->node.doc);
- loader->svgParse->global.w = 1.0f;
- loader->svgParse->global.h = 1.0f;
- doc->align = AspectRatioAlign::XMidYMid;
- doc->meetOrSlice = AspectRatioMeetOrSlice::Meet;
- doc->viewFlag = SvgViewFlag::None;
- func(buf, bufLength, _attrParseSvgNode, loader);
- if (!(doc->viewFlag & SvgViewFlag::Viewbox)) {
- if (doc->viewFlag & SvgViewFlag::Width) {
- loader->svgParse->global.w = doc->w;
- }
- if (doc->viewFlag & SvgViewFlag::Height) {
- loader->svgParse->global.h = doc->h;
- }
- }
- return loader->svgParse->node;
- }
- static SvgNode* _createMaskNode(SvgLoaderData* loader, SvgNode* parent, TVG_UNUSED const char* buf, TVG_UNUSED unsigned bufLength, parseAttributes func)
- {
- loader->svgParse->node = _createNode(parent, SvgNodeType::Mask);
- if (!loader->svgParse->node) return nullptr;
- loader->svgParse->node->node.mask.userSpace = true;
- loader->svgParse->node->node.mask.type = SvgMaskType::Luminance;
- func(buf, bufLength, _attrParseMaskNode, loader);
- return loader->svgParse->node;
- }
- static SvgNode* _createClipPathNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
- {
- loader->svgParse->node = _createNode(parent, SvgNodeType::ClipPath);
- if (!loader->svgParse->node) return nullptr;
- loader->svgParse->node->display = false;
- loader->svgParse->node->node.clip.userSpace = true;
- func(buf, bufLength, _attrParseClipPathNode, loader);
- return loader->svgParse->node;
- }
- static SvgNode* _createCssStyleNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
- {
- loader->svgParse->node = _createNode(parent, SvgNodeType::CssStyle);
- if (!loader->svgParse->node) return nullptr;
- func(buf, bufLength, _attrParseCssStyleNode, loader);
- return loader->svgParse->node;
- }
- static SvgNode* _createSymbolNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
- {
- loader->svgParse->node = _createNode(parent, SvgNodeType::Symbol);
- if (!loader->svgParse->node) return nullptr;
- loader->svgParse->node->display = false;
- loader->svgParse->node->node.symbol.align = AspectRatioAlign::XMidYMid;
- loader->svgParse->node->node.symbol.meetOrSlice = AspectRatioMeetOrSlice::Meet;
- loader->svgParse->node->node.symbol.overflowVisible = false;
- loader->svgParse->node->node.symbol.hasViewBox = false;
- loader->svgParse->node->node.symbol.hasWidth = false;
- loader->svgParse->node->node.symbol.hasHeight = false;
- loader->svgParse->node->node.symbol.vx = 0.0f;
- loader->svgParse->node->node.symbol.vy = 0.0f;
- func(buf, bufLength, _attrParseSymbolNode, loader);
- return loader->svgParse->node;
- }
- static bool _attrParsePathNode(void* data, const char* key, const char* value)
- {
- SvgLoaderData* loader = (SvgLoaderData*)data;
- SvgNode* node = loader->svgParse->node;
- SvgPathNode* path = &(node->node.path);
- if (!strcmp(key, "d")) {
- if (path->path) free(path->path);
- //Temporary: need to copy
- path->path = _copyId(value);
- } else if (!strcmp(key, "style")) {
- return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
- } else if (!strcmp(key, "clip-path")) {
- _handleClipPathAttr(loader, node, value);
- } else if (!strcmp(key, "mask")) {
- _handleMaskAttr(loader, node, value);
- } else if (!strcmp(key, "id")) {
- if (node->id && value) free(node->id);
- node->id = _copyId(value);
- } else if (!strcmp(key, "class")) {
- _handleCssClassAttr(loader, node, value);
- } else {
- return _parseStyleAttr(loader, key, value, false);
- }
- return true;
- }
- static SvgNode* _createPathNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
- {
- loader->svgParse->node = _createNode(parent, SvgNodeType::Path);
- if (!loader->svgParse->node) return nullptr;
- func(buf, bufLength, _attrParsePathNode, loader);
- return loader->svgParse->node;
- }
- static constexpr struct
- {
- const char* tag;
- SvgParserLengthType type;
- int sz;
- size_t offset;
- } circleTags[] = {
- {"cx", SvgParserLengthType::Horizontal, sizeof("cx"), offsetof(SvgCircleNode, cx)},
- {"cy", SvgParserLengthType::Vertical, sizeof("cy"), offsetof(SvgCircleNode, cy)},
- {"r", SvgParserLengthType::Other, sizeof("r"), offsetof(SvgCircleNode, r)}
- };
- /* parse the attributes for a circle element.
- * https://www.w3.org/TR/SVG/shapes.html#CircleElement
- */
- static bool _attrParseCircleNode(void* data, const char* key, const char* value)
- {
- SvgLoaderData* loader = (SvgLoaderData*)data;
- SvgNode* node = loader->svgParse->node;
- SvgCircleNode* circle = &(node->node.circle);
- unsigned char* array;
- int sz = strlen(key);
- array = (unsigned char*)circle;
- for (unsigned int i = 0; i < sizeof(circleTags) / sizeof(circleTags[0]); i++) {
- if (circleTags[i].sz - 1 == sz && !strncmp(circleTags[i].tag, key, sz)) {
- *((float*)(array + circleTags[i].offset)) = _toFloat(loader->svgParse, value, circleTags[i].type);
- return true;
- }
- }
- if (!strcmp(key, "style")) {
- return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
- } else if (!strcmp(key, "clip-path")) {
- _handleClipPathAttr(loader, node, value);
- } else if (!strcmp(key, "mask")) {
- _handleMaskAttr(loader, node, value);
- } else if (!strcmp(key, "id")) {
- if (node->id && value) free(node->id);
- node->id = _copyId(value);
- } else if (!strcmp(key, "class")) {
- _handleCssClassAttr(loader, node, value);
- } else {
- return _parseStyleAttr(loader, key, value, false);
- }
- return true;
- }
- static SvgNode* _createCircleNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
- {
- loader->svgParse->node = _createNode(parent, SvgNodeType::Circle);
- if (!loader->svgParse->node) return nullptr;
- func(buf, bufLength, _attrParseCircleNode, loader);
- return loader->svgParse->node;
- }
- static constexpr struct
- {
- const char* tag;
- SvgParserLengthType type;
- int sz;
- size_t offset;
- } ellipseTags[] = {
- {"cx", SvgParserLengthType::Horizontal, sizeof("cx"), offsetof(SvgEllipseNode, cx)},
- {"cy", SvgParserLengthType::Vertical, sizeof("cy"), offsetof(SvgEllipseNode, cy)},
- {"rx", SvgParserLengthType::Horizontal, sizeof("rx"), offsetof(SvgEllipseNode, rx)},
- {"ry", SvgParserLengthType::Vertical, sizeof("ry"), offsetof(SvgEllipseNode, ry)}
- };
- /* parse the attributes for an ellipse element.
- * https://www.w3.org/TR/SVG/shapes.html#EllipseElement
- */
- static bool _attrParseEllipseNode(void* data, const char* key, const char* value)
- {
- SvgLoaderData* loader = (SvgLoaderData*)data;
- SvgNode* node = loader->svgParse->node;
- SvgEllipseNode* ellipse = &(node->node.ellipse);
- unsigned char* array;
- int sz = strlen(key);
- array = (unsigned char*)ellipse;
- for (unsigned int i = 0; i < sizeof(ellipseTags) / sizeof(ellipseTags[0]); i++) {
- if (ellipseTags[i].sz - 1 == sz && !strncmp(ellipseTags[i].tag, key, sz)) {
- *((float*)(array + ellipseTags[i].offset)) = _toFloat(loader->svgParse, value, ellipseTags[i].type);
- return true;
- }
- }
- if (!strcmp(key, "id")) {
- if (node->id && value) free(node->id);
- node->id = _copyId(value);
- } else if (!strcmp(key, "class")) {
- _handleCssClassAttr(loader, node, value);
- } else if (!strcmp(key, "style")) {
- return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
- } else if (!strcmp(key, "clip-path")) {
- _handleClipPathAttr(loader, node, value);
- } else if (!strcmp(key, "mask")) {
- _handleMaskAttr(loader, node, value);
- } else {
- return _parseStyleAttr(loader, key, value, false);
- }
- return true;
- }
- static SvgNode* _createEllipseNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
- {
- loader->svgParse->node = _createNode(parent, SvgNodeType::Ellipse);
- if (!loader->svgParse->node) return nullptr;
- func(buf, bufLength, _attrParseEllipseNode, loader);
- return loader->svgParse->node;
- }
- static bool _attrParsePolygonPoints(const char* str, SvgPolygonNode* polygon)
- {
- float num;
- while (_parseNumber(&str, &num)) polygon->pts.push(num);
- return true;
- }
- /* parse the attributes for a polygon element.
- * https://www.w3.org/TR/SVG/shapes.html#PolylineElement
- */
- static bool _attrParsePolygonNode(void* data, const char* key, const char* value)
- {
- SvgLoaderData* loader = (SvgLoaderData*)data;
- SvgNode* node = loader->svgParse->node;
- SvgPolygonNode* polygon = nullptr;
- if (node->type == SvgNodeType::Polygon) polygon = &(node->node.polygon);
- else polygon = &(node->node.polyline);
- if (!strcmp(key, "points")) {
- return _attrParsePolygonPoints(value, polygon);
- } else if (!strcmp(key, "style")) {
- return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
- } else if (!strcmp(key, "clip-path")) {
- _handleClipPathAttr(loader, node, value);
- } else if (!strcmp(key, "mask")) {
- _handleMaskAttr(loader, node, value);
- } else if (!strcmp(key, "id")) {
- if (node->id && value) free(node->id);
- node->id = _copyId(value);
- } else if (!strcmp(key, "class")) {
- _handleCssClassAttr(loader, node, value);
- } else {
- return _parseStyleAttr(loader, key, value, false);
- }
- return true;
- }
- static SvgNode* _createPolygonNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
- {
- loader->svgParse->node = _createNode(parent, SvgNodeType::Polygon);
- if (!loader->svgParse->node) return nullptr;
- func(buf, bufLength, _attrParsePolygonNode, loader);
- return loader->svgParse->node;
- }
- static SvgNode* _createPolylineNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
- {
- loader->svgParse->node = _createNode(parent, SvgNodeType::Polyline);
- if (!loader->svgParse->node) return nullptr;
- func(buf, bufLength, _attrParsePolygonNode, loader);
- return loader->svgParse->node;
- }
- static constexpr struct
- {
- const char* tag;
- SvgParserLengthType type;
- int sz;
- size_t offset;
- } rectTags[] = {
- {"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(SvgRectNode, x)},
- {"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(SvgRectNode, y)},
- {"width", SvgParserLengthType::Horizontal, sizeof("width"), offsetof(SvgRectNode, w)},
- {"height", SvgParserLengthType::Vertical, sizeof("height"), offsetof(SvgRectNode, h)},
- {"rx", SvgParserLengthType::Horizontal, sizeof("rx"), offsetof(SvgRectNode, rx)},
- {"ry", SvgParserLengthType::Vertical, sizeof("ry"), offsetof(SvgRectNode, ry)}
- };
- /* parse the attributes for a rect element.
- * https://www.w3.org/TR/SVG/shapes.html#RectElement
- */
- static bool _attrParseRectNode(void* data, const char* key, const char* value)
- {
- SvgLoaderData* loader = (SvgLoaderData*)data;
- SvgNode* node = loader->svgParse->node;
- SvgRectNode* rect = &(node->node.rect);
- unsigned char* array;
- bool ret = true;
- int sz = strlen(key);
- array = (unsigned char*)rect;
- for (unsigned int i = 0; i < sizeof(rectTags) / sizeof(rectTags[0]); i++) {
- if (rectTags[i].sz - 1 == sz && !strncmp(rectTags[i].tag, key, sz)) {
- *((float*)(array + rectTags[i].offset)) = _toFloat(loader->svgParse, value, rectTags[i].type);
- //Case if only rx or ry is declared
- if (!strncmp(rectTags[i].tag, "rx", sz)) rect->hasRx = true;
- if (!strncmp(rectTags[i].tag, "ry", sz)) rect->hasRy = true;
- if ((rect->rx >= FLT_EPSILON) && (rect->ry < FLT_EPSILON) && rect->hasRx && !rect->hasRy) rect->ry = rect->rx;
- if ((rect->ry >= FLT_EPSILON) && (rect->rx < FLT_EPSILON) && !rect->hasRx && rect->hasRy) rect->rx = rect->ry;
- return ret;
- }
- }
- if (!strcmp(key, "id")) {
- if (node->id && value) free(node->id);
- node->id = _copyId(value);
- } else if (!strcmp(key, "class")) {
- _handleCssClassAttr(loader, node, value);
- } else if (!strcmp(key, "style")) {
- ret = simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
- } else if (!strcmp(key, "clip-path")) {
- _handleClipPathAttr(loader, node, value);
- } else if (!strcmp(key, "mask")) {
- _handleMaskAttr(loader, node, value);
- } else {
- ret = _parseStyleAttr(loader, key, value, false);
- }
- return ret;
- }
- static SvgNode* _createRectNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
- {
- loader->svgParse->node = _createNode(parent, SvgNodeType::Rect);
- if (!loader->svgParse->node) return nullptr;
- loader->svgParse->node->node.rect.hasRx = loader->svgParse->node->node.rect.hasRy = false;
- func(buf, bufLength, _attrParseRectNode, loader);
- return loader->svgParse->node;
- }
- static constexpr struct
- {
- const char* tag;
- SvgParserLengthType type;
- int sz;
- size_t offset;
- } lineTags[] = {
- {"x1", SvgParserLengthType::Horizontal, sizeof("x1"), offsetof(SvgLineNode, x1)},
- {"y1", SvgParserLengthType::Vertical, sizeof("y1"), offsetof(SvgLineNode, y1)},
- {"x2", SvgParserLengthType::Horizontal, sizeof("x2"), offsetof(SvgLineNode, x2)},
- {"y2", SvgParserLengthType::Vertical, sizeof("y2"), offsetof(SvgLineNode, y2)}
- };
- /* parse the attributes for a line element.
- * https://www.w3.org/TR/SVG/shapes.html#LineElement
- */
- static bool _attrParseLineNode(void* data, const char* key, const char* value)
- {
- SvgLoaderData* loader = (SvgLoaderData*)data;
- SvgNode* node = loader->svgParse->node;
- SvgLineNode* line = &(node->node.line);
- unsigned char* array;
- int sz = strlen(key);
- array = (unsigned char*)line;
- for (unsigned int i = 0; i < sizeof(lineTags) / sizeof(lineTags[0]); i++) {
- if (lineTags[i].sz - 1 == sz && !strncmp(lineTags[i].tag, key, sz)) {
- *((float*)(array + lineTags[i].offset)) = _toFloat(loader->svgParse, value, lineTags[i].type);
- return true;
- }
- }
- if (!strcmp(key, "id")) {
- if (node->id && value) free(node->id);
- node->id = _copyId(value);
- } else if (!strcmp(key, "class")) {
- _handleCssClassAttr(loader, node, value);
- } else if (!strcmp(key, "style")) {
- return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
- } else if (!strcmp(key, "clip-path")) {
- _handleClipPathAttr(loader, node, value);
- } else if (!strcmp(key, "mask")) {
- _handleMaskAttr(loader, node, value);
- } else {
- return _parseStyleAttr(loader, key, value, false);
- }
- return true;
- }
- static SvgNode* _createLineNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
- {
- loader->svgParse->node = _createNode(parent, SvgNodeType::Line);
- if (!loader->svgParse->node) return nullptr;
- func(buf, bufLength, _attrParseLineNode, loader);
- return loader->svgParse->node;
- }
- static char* _idFromHref(const char* href)
- {
- href = _skipSpace(href, nullptr);
- if ((*href) == '#') href++;
- return strdup(href);
- }
- static constexpr struct
- {
- const char* tag;
- SvgParserLengthType type;
- int sz;
- size_t offset;
- } imageTags[] = {
- {"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(SvgRectNode, x)},
- {"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(SvgRectNode, y)},
- {"width", SvgParserLengthType::Horizontal, sizeof("width"), offsetof(SvgRectNode, w)},
- {"height", SvgParserLengthType::Vertical, sizeof("height"), offsetof(SvgRectNode, h)},
- };
- /* parse the attributes for a image element.
- * https://www.w3.org/TR/SVG/embedded.html#ImageElement
- */
- static bool _attrParseImageNode(void* data, const char* key, const char* value)
- {
- SvgLoaderData* loader = (SvgLoaderData*)data;
- SvgNode* node = loader->svgParse->node;
- SvgImageNode* image = &(node->node.image);
- unsigned char* array;
- int sz = strlen(key);
- array = (unsigned char*)image;
- for (unsigned int i = 0; i < sizeof(imageTags) / sizeof(imageTags[0]); i++) {
- if (imageTags[i].sz - 1 == sz && !strncmp(imageTags[i].tag, key, sz)) {
- *((float*)(array + imageTags[i].offset)) = _toFloat(loader->svgParse, value, imageTags[i].type);
- return true;
- }
- }
- if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) {
- if (image->href && value) free(image->href);
- image->href = _idFromHref(value);
- } else if (!strcmp(key, "id")) {
- if (node->id && value) free(node->id);
- node->id = _copyId(value);
- } else if (!strcmp(key, "class")) {
- _handleCssClassAttr(loader, node, value);
- } else if (!strcmp(key, "style")) {
- return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
- } else if (!strcmp(key, "clip-path")) {
- _handleClipPathAttr(loader, node, value);
- } else if (!strcmp(key, "mask")) {
- _handleMaskAttr(loader, node, value);
- } else if (!strcmp(key, "transform")) {
- node->transform = _parseTransformationMatrix(value);
- } else {
- return _parseStyleAttr(loader, key, value);
- }
- return true;
- }
- static SvgNode* _createImageNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
- {
- loader->svgParse->node = _createNode(parent, SvgNodeType::Image);
- if (!loader->svgParse->node) return nullptr;
- func(buf, bufLength, _attrParseImageNode, loader);
- return loader->svgParse->node;
- }
- static SvgNode* _getDefsNode(SvgNode* node)
- {
- if (!node) return nullptr;
- while (node->parent != nullptr) {
- node = node->parent;
- }
- if (node->type == SvgNodeType::Doc) return node->node.doc.defs;
- if (node->type == SvgNodeType::Defs) return node;
- return nullptr;
- }
- static SvgNode* _findNodeById(SvgNode *node, const char* id)
- {
- if (!node) return nullptr;
- SvgNode* result = nullptr;
- if (node->id && !strcmp(node->id, id)) return node;
- if (node->child.count > 0) {
- auto child = node->child.data;
- for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
- result = _findNodeById(*child, id);
- if (result) break;
- }
- }
- return result;
- }
- static SvgNode* _findParentById(SvgNode* node, char* id, SvgNode* doc)
- {
- SvgNode *parent = node->parent;
- while (parent != nullptr && parent != doc) {
- if (parent->id && !strcmp(parent->id, id)) {
- return parent;
- }
- parent = parent->parent;
- }
- return nullptr;
- }
- static constexpr struct
- {
- const char* tag;
- SvgParserLengthType type;
- int sz;
- size_t offset;
- } useTags[] = {
- {"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(SvgUseNode, x)},
- {"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(SvgUseNode, y)},
- {"width", SvgParserLengthType::Horizontal, sizeof("width"), offsetof(SvgUseNode, w)},
- {"height", SvgParserLengthType::Vertical, sizeof("height"), offsetof(SvgUseNode, h)}
- };
- static void _cloneNode(SvgNode* from, SvgNode* parent, int depth);
- static bool _attrParseUseNode(void* data, const char* key, const char* value)
- {
- SvgLoaderData* loader = (SvgLoaderData*)data;
- SvgNode *defs, *nodeFrom, *node = loader->svgParse->node;
- char* id;
- SvgUseNode* use = &(node->node.use);
- int sz = strlen(key);
- unsigned char* array = (unsigned char*)use;
- for (unsigned int i = 0; i < sizeof(useTags) / sizeof(useTags[0]); i++) {
- if (useTags[i].sz - 1 == sz && !strncmp(useTags[i].tag, key, sz)) {
- *((float*)(array + useTags[i].offset)) = _toFloat(loader->svgParse, value, useTags[i].type);
- if (useTags[i].offset == offsetof(SvgUseNode, w)) use->isWidthSet = true;
- else if (useTags[i].offset == offsetof(SvgUseNode, h)) use->isHeightSet = true;
- return true;
- }
- }
- if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) {
- id = _idFromHref(value);
- defs = _getDefsNode(node);
- nodeFrom = _findNodeById(defs, id);
- if (nodeFrom) {
- if (!_findParentById(node, id, loader->doc)) {
- _cloneNode(nodeFrom, node, 0);
- if (nodeFrom->type == SvgNodeType::Symbol) use->symbol = nodeFrom;
- } else {
- TVGLOG("SVG", "%s is ancestor element. This reference is invalid.", id);
- }
- free(id);
- } else {
- //some svg export software include <defs> element at the end of the file
- //if so the 'from' element won't be found now and we have to repeat finding
- //after the whole file is parsed
- _postpone(loader->cloneNodes, node, id);
- }
- } else {
- return _attrParseGNode(data, key, value);
- }
- return true;
- }
- static SvgNode* _createUseNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
- {
- loader->svgParse->node = _createNode(parent, SvgNodeType::Use);
- if (!loader->svgParse->node) return nullptr;
- loader->svgParse->node->node.use.isWidthSet = false;
- loader->svgParse->node->node.use.isHeightSet = false;
- func(buf, bufLength, _attrParseUseNode, loader);
- return loader->svgParse->node;
- }
- //TODO: Implement 'text' primitive
- static constexpr struct
- {
- const char* tag;
- int sz;
- FactoryMethod tagHandler;
- } graphicsTags[] = {
- {"use", sizeof("use"), _createUseNode},
- {"circle", sizeof("circle"), _createCircleNode},
- {"ellipse", sizeof("ellipse"), _createEllipseNode},
- {"path", sizeof("path"), _createPathNode},
- {"polygon", sizeof("polygon"), _createPolygonNode},
- {"rect", sizeof("rect"), _createRectNode},
- {"polyline", sizeof("polyline"), _createPolylineNode},
- {"line", sizeof("line"), _createLineNode},
- {"image", sizeof("image"), _createImageNode}
- };
- static constexpr struct
- {
- const char* tag;
- int sz;
- FactoryMethod tagHandler;
- } groupTags[] = {
- {"defs", sizeof("defs"), _createDefsNode},
- {"g", sizeof("g"), _createGNode},
- {"svg", sizeof("svg"), _createSvgNode},
- {"mask", sizeof("mask"), _createMaskNode},
- {"clipPath", sizeof("clipPath"), _createClipPathNode},
- {"style", sizeof("style"), _createCssStyleNode},
- {"symbol", sizeof("symbol"), _createSymbolNode}
- };
- #define FIND_FACTORY(Short_Name, Tags_Array) \
- static FactoryMethod \
- _find##Short_Name##Factory(const char* name) \
- { \
- unsigned int i; \
- int sz = strlen(name); \
- \
- for (i = 0; i < sizeof(Tags_Array) / sizeof(Tags_Array[0]); i++) { \
- if (Tags_Array[i].sz - 1 == sz && !strncmp(Tags_Array[i].tag, name, sz)) { \
- return Tags_Array[i].tagHandler; \
- } \
- } \
- return nullptr; \
- }
- FIND_FACTORY(Group, groupTags)
- FIND_FACTORY(Graphics, graphicsTags)
- FillSpread _parseSpreadValue(const char* value)
- {
- auto spread = FillSpread::Pad;
- if (!strcmp(value, "reflect")) {
- spread = FillSpread::Reflect;
- } else if (!strcmp(value, "repeat")) {
- spread = FillSpread::Repeat;
- }
- return spread;
- }
- static void _handleRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
- {
- radial->cx = _gradientToFloat(loader->svgParse, value, radial->isCxPercentage);
- if (!loader->svgParse->gradient.parsedFx) {
- radial->fx = radial->cx;
- radial->isFxPercentage = radial->isCxPercentage;
- }
- }
- static void _handleRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
- {
- radial->cy = _gradientToFloat(loader->svgParse, value, radial->isCyPercentage);
- if (!loader->svgParse->gradient.parsedFy) {
- radial->fy = radial->cy;
- radial->isFyPercentage = radial->isCyPercentage;
- }
- }
- static void _handleRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
- {
- radial->fx = _gradientToFloat(loader->svgParse, value, radial->isFxPercentage);
- loader->svgParse->gradient.parsedFx = true;
- }
- static void _handleRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
- {
- radial->fy = _gradientToFloat(loader->svgParse, value, radial->isFyPercentage);
- loader->svgParse->gradient.parsedFy = true;
- }
- static void _handleRadialFrAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
- {
- radial->fr = _gradientToFloat(loader->svgParse, value, radial->isFrPercentage);
- }
- static void _handleRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
- {
- radial->r = _gradientToFloat(loader->svgParse, value, radial->isRPercentage);
- }
- static void _recalcRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
- {
- if (userSpace && !radial->isCxPercentage) radial->cx = radial->cx / loader->svgParse->global.w;
- }
- static void _recalcRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
- {
- if (userSpace && !radial->isCyPercentage) radial->cy = radial->cy / loader->svgParse->global.h;
- }
- static void _recalcRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
- {
- if (userSpace && !radial->isFxPercentage) radial->fx = radial->fx / loader->svgParse->global.w;
- }
- static void _recalcRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
- {
- if (userSpace && !radial->isFyPercentage) radial->fy = radial->fy / loader->svgParse->global.h;
- }
- static void _recalcRadialFrAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
- {
- // scaling factor based on the Units paragraph from : https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
- if (userSpace && !radial->isFrPercentage) radial->fr = radial->fr / (sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0));
- }
- static void _recalcRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
- {
- // scaling factor based on the Units paragraph from : https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
- if (userSpace && !radial->isRPercentage) radial->r = radial->r / (sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0));
- }
- static void _recalcInheritedRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
- {
- if (!radial->isCxPercentage) {
- if (userSpace) radial->cx /= loader->svgParse->global.w;
- else radial->cx *= loader->svgParse->global.w;
- }
- }
- static void _recalcInheritedRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
- {
- if (!radial->isCyPercentage) {
- if (userSpace) radial->cy /= loader->svgParse->global.h;
- else radial->cy *= loader->svgParse->global.h;
- }
- }
- static void _recalcInheritedRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
- {
- if (!radial->isFxPercentage) {
- if (userSpace) radial->fx /= loader->svgParse->global.w;
- else radial->fx *= loader->svgParse->global.w;
- }
- }
- static void _recalcInheritedRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
- {
- if (!radial->isFyPercentage) {
- if (userSpace) radial->fy /= loader->svgParse->global.h;
- else radial->fy *= loader->svgParse->global.h;
- }
- }
- static void _recalcInheritedRadialFrAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
- {
- if (!radial->isFrPercentage) {
- if (userSpace) radial->fr /= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0);
- else radial->fr *= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0);
- }
- }
- static void _recalcInheritedRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
- {
- if (!radial->isRPercentage) {
- if (userSpace) radial->r /= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0);
- else radial->r *= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0);
- }
- }
- static void _inheritRadialCxAttr(SvgStyleGradient* to, SvgStyleGradient* from)
- {
- to->radial->cx = from->radial->cx;
- to->radial->isCxPercentage = from->radial->isCxPercentage;
- to->flags = (to->flags | SvgGradientFlags::Cx);
- }
- static void _inheritRadialCyAttr(SvgStyleGradient* to, SvgStyleGradient* from)
- {
- to->radial->cy = from->radial->cy;
- to->radial->isCyPercentage = from->radial->isCyPercentage;
- to->flags = (to->flags | SvgGradientFlags::Cy);
- }
- static void _inheritRadialFxAttr(SvgStyleGradient* to, SvgStyleGradient* from)
- {
- to->radial->fx = from->radial->fx;
- to->radial->isFxPercentage = from->radial->isFxPercentage;
- to->flags = (to->flags | SvgGradientFlags::Fx);
- }
- static void _inheritRadialFyAttr(SvgStyleGradient* to, SvgStyleGradient* from)
- {
- to->radial->fy = from->radial->fy;
- to->radial->isFyPercentage = from->radial->isFyPercentage;
- to->flags = (to->flags | SvgGradientFlags::Fy);
- }
- static void _inheritRadialFrAttr(SvgStyleGradient* to, SvgStyleGradient* from)
- {
- to->radial->fr = from->radial->fr;
- to->radial->isFrPercentage = from->radial->isFrPercentage;
- to->flags = (to->flags | SvgGradientFlags::Fr);
- }
- static void _inheritRadialRAttr(SvgStyleGradient* to, SvgStyleGradient* from)
- {
- to->radial->r = from->radial->r;
- to->radial->isRPercentage = from->radial->isRPercentage;
- to->flags = (to->flags | SvgGradientFlags::R);
- }
- typedef void (*radialMethod)(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value);
- typedef void (*radialInheritMethod)(SvgStyleGradient* to, SvgStyleGradient* from);
- typedef void (*radialMethodRecalc)(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace);
- #define RADIAL_DEF(Name, Name1, Flag) \
- { \
- #Name, sizeof(#Name), _handleRadial##Name1##Attr, _inheritRadial##Name1##Attr, _recalcRadial##Name1##Attr, _recalcInheritedRadial##Name1##Attr, Flag \
- }
- static constexpr struct
- {
- const char* tag;
- int sz;
- radialMethod tagHandler;
- radialInheritMethod tagInheritHandler;
- radialMethodRecalc tagRecalc;
- radialMethodRecalc tagInheritedRecalc;
- SvgGradientFlags flag;
- } radialTags[] = {
- RADIAL_DEF(cx, Cx, SvgGradientFlags::Cx),
- RADIAL_DEF(cy, Cy, SvgGradientFlags::Cy),
- RADIAL_DEF(fx, Fx, SvgGradientFlags::Fx),
- RADIAL_DEF(fy, Fy, SvgGradientFlags::Fy),
- RADIAL_DEF(r, R, SvgGradientFlags::R),
- RADIAL_DEF(fr, Fr, SvgGradientFlags::Fr)
- };
- static bool _attrParseRadialGradientNode(void* data, const char* key, const char* value)
- {
- SvgLoaderData* loader = (SvgLoaderData*)data;
- SvgStyleGradient* grad = loader->svgParse->styleGrad;
- SvgRadialGradient* radial = grad->radial;
- int sz = strlen(key);
- for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) {
- if (radialTags[i].sz - 1 == sz && !strncmp(radialTags[i].tag, key, sz)) {
- radialTags[i].tagHandler(loader, radial, value);
- grad->flags = (grad->flags | radialTags[i].flag);
- return true;
- }
- }
- if (!strcmp(key, "id")) {
- if (grad->id && value) free(grad->id);
- grad->id = _copyId(value);
- } else if (!strcmp(key, "spreadMethod")) {
- grad->spread = _parseSpreadValue(value);
- grad->flags = (grad->flags | SvgGradientFlags::SpreadMethod);
- } else if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) {
- if (grad->ref && value) free(grad->ref);
- grad->ref = _idFromHref(value);
- } else if (!strcmp(key, "gradientUnits")) {
- if (!strcmp(value, "userSpaceOnUse")) grad->userSpace = true;
- grad->flags = (grad->flags | SvgGradientFlags::GradientUnits);
- } else if (!strcmp(key, "gradientTransform")) {
- grad->transform = _parseTransformationMatrix(value);
- } else {
- return false;
- }
- return true;
- }
- static SvgStyleGradient* _createRadialGradient(SvgLoaderData* loader, const char* buf, unsigned bufLength)
- {
- auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient)));
- loader->svgParse->styleGrad = grad;
- grad->flags = SvgGradientFlags::None;
- grad->type = SvgGradientType::Radial;
- grad->userSpace = false;
- grad->radial = (SvgRadialGradient*)calloc(1, sizeof(SvgRadialGradient));
- if (!grad->radial) {
- grad->clear();
- free(grad);
- return nullptr;
- }
- /**
- * Default values of gradient transformed into global percentage
- */
- grad->radial->cx = 0.5f;
- grad->radial->cy = 0.5f;
- grad->radial->fx = 0.5f;
- grad->radial->fy = 0.5f;
- grad->radial->r = 0.5f;
- grad->radial->isCxPercentage = true;
- grad->radial->isCyPercentage = true;
- grad->radial->isFxPercentage = true;
- grad->radial->isFyPercentage = true;
- grad->radial->isRPercentage = true;
- grad->radial->isFrPercentage = true;
- loader->svgParse->gradient.parsedFx = false;
- loader->svgParse->gradient.parsedFy = false;
- simpleXmlParseAttributes(buf, bufLength,
- _attrParseRadialGradientNode, loader);
- for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) {
- radialTags[i].tagRecalc(loader, grad->radial, grad->userSpace);
- }
- return loader->svgParse->styleGrad;
- }
- static bool _attrParseStopsStyle(void* data, const char* key, const char* value)
- {
- SvgLoaderData* loader = (SvgLoaderData*)data;
- auto stop = &loader->svgParse->gradStop;
- if (!strcmp(key, "stop-opacity")) {
- stop->a = _toOpacity(value);
- loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopOpacity);
- } else if (!strcmp(key, "stop-color")) {
- _toColor(value, &stop->r, &stop->g, &stop->b, nullptr);
- loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopColor);
- } else {
- return false;
- }
- return true;
- }
- static bool _attrParseStops(void* data, const char* key, const char* value)
- {
- SvgLoaderData* loader = (SvgLoaderData*)data;
- auto stop = &loader->svgParse->gradStop;
- if (!strcmp(key, "offset")) {
- stop->offset = _toOffset(value);
- } else if (!strcmp(key, "stop-opacity")) {
- if (!(loader->svgParse->flags & SvgStopStyleFlags::StopOpacity)) {
- stop->a = _toOpacity(value);
- }
- } else if (!strcmp(key, "stop-color")) {
- if (!(loader->svgParse->flags & SvgStopStyleFlags::StopColor)) {
- _toColor(value, &stop->r, &stop->g, &stop->b, nullptr);
- }
- } else if (!strcmp(key, "style")) {
- simpleXmlParseW3CAttribute(value, strlen(value), _attrParseStopsStyle, data);
- } else {
- return false;
- }
- return true;
- }
- static void _handleLinearX1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
- {
- linear->x1 = _gradientToFloat(loader->svgParse, value, linear->isX1Percentage);
- }
- static void _handleLinearY1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
- {
- linear->y1 = _gradientToFloat(loader->svgParse, value, linear->isY1Percentage);
- }
- static void _handleLinearX2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
- {
- linear->x2 = _gradientToFloat(loader->svgParse, value, linear->isX2Percentage);
- }
- static void _handleLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
- {
- linear->y2 = _gradientToFloat(loader->svgParse, value, linear->isY2Percentage);
- }
- static void _recalcLinearX1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
- {
- if (userSpace && !linear->isX1Percentage) linear->x1 = linear->x1 / loader->svgParse->global.w;
- }
- static void _recalcLinearY1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
- {
- if (userSpace && !linear->isY1Percentage) linear->y1 = linear->y1 / loader->svgParse->global.h;
- }
- static void _recalcLinearX2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
- {
- if (userSpace && !linear->isX2Percentage) linear->x2 = linear->x2 / loader->svgParse->global.w;
- }
- static void _recalcLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
- {
- if (userSpace && !linear->isY2Percentage) linear->y2 = linear->y2 / loader->svgParse->global.h;
- }
- static void _recalcInheritedLinearX1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
- {
- if (!linear->isX1Percentage) {
- if (userSpace) linear->x1 /= loader->svgParse->global.w;
- else linear->x1 *= loader->svgParse->global.w;
- }
- }
- static void _recalcInheritedLinearX2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
- {
- if (!linear->isX2Percentage) {
- if (userSpace) linear->x2 /= loader->svgParse->global.w;
- else linear->x2 *= loader->svgParse->global.w;
- }
- }
- static void _recalcInheritedLinearY1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
- {
- if (!linear->isY1Percentage) {
- if (userSpace) linear->y1 /= loader->svgParse->global.h;
- else linear->y1 *= loader->svgParse->global.h;
- }
- }
- static void _recalcInheritedLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
- {
- if (!linear->isY2Percentage) {
- if (userSpace) linear->y2 /= loader->svgParse->global.h;
- else linear->y2 *= loader->svgParse->global.h;
- }
- }
- static void _inheritLinearX1Attr(SvgStyleGradient* to, SvgStyleGradient* from)
- {
- to->linear->x1 = from->linear->x1;
- to->linear->isX1Percentage = from->linear->isX1Percentage;
- to->flags = (to->flags | SvgGradientFlags::X1);
- }
- static void _inheritLinearX2Attr(SvgStyleGradient* to, SvgStyleGradient* from)
- {
- to->linear->x2 = from->linear->x2;
- to->linear->isX2Percentage = from->linear->isX2Percentage;
- to->flags = (to->flags | SvgGradientFlags::X2);
- }
- static void _inheritLinearY1Attr(SvgStyleGradient* to, SvgStyleGradient* from)
- {
- to->linear->y1 = from->linear->y1;
- to->linear->isY1Percentage = from->linear->isY1Percentage;
- to->flags = (to->flags | SvgGradientFlags::Y1);
- }
- static void _inheritLinearY2Attr(SvgStyleGradient* to, SvgStyleGradient* from)
- {
- to->linear->y2 = from->linear->y2;
- to->linear->isY2Percentage = from->linear->isY2Percentage;
- to->flags = (to->flags | SvgGradientFlags::Y2);
- }
- typedef void (*Linear_Method)(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value);
- typedef void (*Linear_Inherit_Method)(SvgStyleGradient* to, SvgStyleGradient* from);
- typedef void (*Linear_Method_Recalc)(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace);
- #define LINEAR_DEF(Name, Name1, Flag) \
- { \
- #Name, sizeof(#Name), _handleLinear##Name1##Attr, _inheritLinear##Name1##Attr, _recalcLinear##Name1##Attr, _recalcInheritedLinear##Name1##Attr, Flag \
- }
- static constexpr struct
- {
- const char* tag;
- int sz;
- Linear_Method tagHandler;
- Linear_Inherit_Method tagInheritHandler;
- Linear_Method_Recalc tagRecalc;
- Linear_Method_Recalc tagInheritedRecalc;
- SvgGradientFlags flag;
- } linear_tags[] = {
- LINEAR_DEF(x1, X1, SvgGradientFlags::X1),
- LINEAR_DEF(y1, Y1, SvgGradientFlags::Y1),
- LINEAR_DEF(x2, X2, SvgGradientFlags::X2),
- LINEAR_DEF(y2, Y2, SvgGradientFlags::Y2)
- };
- static bool _attrParseLinearGradientNode(void* data, const char* key, const char* value)
- {
- SvgLoaderData* loader = (SvgLoaderData*)data;
- SvgStyleGradient* grad = loader->svgParse->styleGrad;
- SvgLinearGradient* linear = grad->linear;
- int sz = strlen(key);
- for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) {
- if (linear_tags[i].sz - 1 == sz && !strncmp(linear_tags[i].tag, key, sz)) {
- linear_tags[i].tagHandler(loader, linear, value);
- grad->flags = (grad->flags | linear_tags[i].flag);
- return true;
- }
- }
- if (!strcmp(key, "id")) {
- if (grad->id && value) free(grad->id);
- grad->id = _copyId(value);
- } else if (!strcmp(key, "spreadMethod")) {
- grad->spread = _parseSpreadValue(value);
- grad->flags = (grad->flags | SvgGradientFlags::SpreadMethod);
- } else if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) {
- if (grad->ref && value) free(grad->ref);
- grad->ref = _idFromHref(value);
- } else if (!strcmp(key, "gradientUnits")) {
- if (!strcmp(value, "userSpaceOnUse")) grad->userSpace = true;
- grad->flags = (grad->flags | SvgGradientFlags::GradientUnits);
- } else if (!strcmp(key, "gradientTransform")) {
- grad->transform = _parseTransformationMatrix(value);
- } else {
- return false;
- }
- return true;
- }
- static SvgStyleGradient* _createLinearGradient(SvgLoaderData* loader, const char* buf, unsigned bufLength)
- {
- auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient)));
- loader->svgParse->styleGrad = grad;
- grad->flags = SvgGradientFlags::None;
- grad->type = SvgGradientType::Linear;
- grad->userSpace = false;
- grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient));
- if (!grad->linear) {
- grad->clear();
- free(grad);
- return nullptr;
- }
- /**
- * Default value of x2 is 100% - transformed to the global percentage
- */
- grad->linear->x2 = 1.0f;
- grad->linear->isX2Percentage = true;
- simpleXmlParseAttributes(buf, bufLength, _attrParseLinearGradientNode, loader);
- for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) {
- linear_tags[i].tagRecalc(loader, grad->linear, grad->userSpace);
- }
- return loader->svgParse->styleGrad;
- }
- #define GRADIENT_DEF(Name, Name1) \
- { \
- #Name, sizeof(#Name), _create##Name1 \
- }
- /**
- * In the case when the gradients lengths are given as numbers (not percentages)
- * in the current user coordinate system, they are recalculated into percentages
- * related to the canvas width and height.
- */
- static constexpr struct
- {
- const char* tag;
- int sz;
- GradientFactoryMethod tagHandler;
- } gradientTags[] = {
- GRADIENT_DEF(linearGradient, LinearGradient),
- GRADIENT_DEF(radialGradient, RadialGradient)
- };
- static GradientFactoryMethod _findGradientFactory(const char* name)
- {
- int sz = strlen(name);
- for (unsigned int i = 0; i < sizeof(gradientTags) / sizeof(gradientTags[0]); i++) {
- if (gradientTags[i].sz - 1 == sz && !strncmp(gradientTags[i].tag, name, sz)) {
- return gradientTags[i].tagHandler;
- }
- }
- return nullptr;
- }
- static void _cloneGradStops(Array<Fill::ColorStop>& dst, const Array<Fill::ColorStop>& src)
- {
- for (uint32_t i = 0; i < src.count; ++i) {
- dst.push(src[i]);
- }
- }
- static void _inheritGradient(SvgLoaderData* loader, SvgStyleGradient* to, SvgStyleGradient* from)
- {
- if (!to || !from) return;
- if (!(to->flags & SvgGradientFlags::SpreadMethod) && (from->flags & SvgGradientFlags::SpreadMethod)) {
- to->spread = from->spread;
- to->flags = (to->flags | SvgGradientFlags::SpreadMethod);
- }
- bool gradUnitSet = (to->flags & SvgGradientFlags::GradientUnits);
- if (!(to->flags & SvgGradientFlags::GradientUnits) && (from->flags & SvgGradientFlags::GradientUnits)) {
- to->userSpace = from->userSpace;
- to->flags = (to->flags | SvgGradientFlags::GradientUnits);
- }
- if (!to->transform && from->transform) {
- to->transform = (Matrix*)malloc(sizeof(Matrix));
- if (to->transform) memcpy(to->transform, from->transform, sizeof(Matrix));
- }
- if (to->type == SvgGradientType::Linear) {
- for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) {
- bool coordSet = to->flags & linear_tags[i].flag;
- if (!(to->flags & linear_tags[i].flag) && (from->flags & linear_tags[i].flag)) {
- linear_tags[i].tagInheritHandler(to, from);
- }
- //GradUnits not set directly, coord set
- if (!gradUnitSet && coordSet) {
- linear_tags[i].tagRecalc(loader, to->linear, to->userSpace);
- }
- //GradUnits set, coord not set directly
- if (to->userSpace == from->userSpace) continue;
- if (gradUnitSet && !coordSet) {
- linear_tags[i].tagInheritedRecalc(loader, to->linear, to->userSpace);
- }
- }
- } else if (to->type == SvgGradientType::Radial) {
- for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) {
- bool coordSet = (to->flags & radialTags[i].flag);
- if (!(to->flags & radialTags[i].flag) && (from->flags & radialTags[i].flag)) {
- radialTags[i].tagInheritHandler(to, from);
- }
- //GradUnits not set directly, coord set
- if (!gradUnitSet && coordSet) {
- radialTags[i].tagRecalc(loader, to->radial, to->userSpace);
- //If fx and fy are not set, set cx and cy.
- if (!strcmp(radialTags[i].tag, "cx") && !(to->flags & SvgGradientFlags::Fx)) to->radial->fx = to->radial->cx;
- if (!strcmp(radialTags[i].tag, "cy") && !(to->flags & SvgGradientFlags::Fy)) to->radial->fy = to->radial->cy;
- }
- //GradUnits set, coord not set directly
- if (to->userSpace == from->userSpace) continue;
- if (gradUnitSet && !coordSet) {
- //If fx and fx are not set, do not call recalc.
- if (!strcmp(radialTags[i].tag, "fx") && !(to->flags & SvgGradientFlags::Fx)) continue;
- if (!strcmp(radialTags[i].tag, "fy") && !(to->flags & SvgGradientFlags::Fy)) continue;
- radialTags[i].tagInheritedRecalc(loader, to->radial, to->userSpace);
- }
- }
- }
- if (to->stops.empty()) _cloneGradStops(to->stops, from->stops);
- }
- static SvgStyleGradient* _cloneGradient(SvgStyleGradient* from)
- {
- if (!from) return nullptr;
- auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient)));
- if (!grad) return nullptr;
- grad->type = from->type;
- grad->id = from->id ? _copyId(from->id) : nullptr;
- grad->ref = from->ref ? _copyId(from->ref) : nullptr;
- grad->spread = from->spread;
- grad->userSpace = from->userSpace;
- grad->flags = from->flags;
- if (from->transform) {
- grad->transform = (Matrix*)calloc(1, sizeof(Matrix));
- if (grad->transform) memcpy(grad->transform, from->transform, sizeof(Matrix));
- }
- if (grad->type == SvgGradientType::Linear) {
- grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient));
- if (!grad->linear) goto error_grad_alloc;
- memcpy(grad->linear, from->linear, sizeof(SvgLinearGradient));
- } else if (grad->type == SvgGradientType::Radial) {
- grad->radial = (SvgRadialGradient*)calloc(1, sizeof(SvgRadialGradient));
- if (!grad->radial) goto error_grad_alloc;
- memcpy(grad->radial, from->radial, sizeof(SvgRadialGradient));
- }
- _cloneGradStops(grad->stops, from->stops);
- return grad;
- error_grad_alloc:
- if (grad) {
- grad->clear();
- free(grad);
- }
- return nullptr;
- }
- static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* parent)
- {
- if (parent == nullptr) return;
- //Inherit the property of parent if not present in child.
- if (!child->curColorSet) {
- child->color = parent->color;
- child->curColorSet = parent->curColorSet;
- }
- if (!(child->flags & SvgStyleFlags::PaintOrder)) {
- child->paintOrder = parent->paintOrder;
- }
- //Fill
- if (!(child->fill.flags & SvgFillFlags::Paint)) {
- child->fill.paint.color = parent->fill.paint.color;
- child->fill.paint.none = parent->fill.paint.none;
- child->fill.paint.curColor = parent->fill.paint.curColor;
- if (parent->fill.paint.url) {
- if (child->fill.paint.url) free(child->fill.paint.url);
- child->fill.paint.url = _copyId(parent->fill.paint.url);
- }
- }
- if (!(child->fill.flags & SvgFillFlags::Opacity)) {
- child->fill.opacity = parent->fill.opacity;
- }
- if (!(child->fill.flags & SvgFillFlags::FillRule)) {
- child->fill.fillRule = parent->fill.fillRule;
- }
- //Stroke
- if (!(child->stroke.flags & SvgStrokeFlags::Paint)) {
- child->stroke.paint.color = parent->stroke.paint.color;
- child->stroke.paint.none = parent->stroke.paint.none;
- child->stroke.paint.curColor = parent->stroke.paint.curColor;
- if (parent->stroke.paint.url) {
- if (child->stroke.paint.url) free(child->stroke.paint.url);
- child->stroke.paint.url = _copyId(parent->stroke.paint.url);
- }
- }
- if (!(child->stroke.flags & SvgStrokeFlags::Opacity)) {
- child->stroke.opacity = parent->stroke.opacity;
- }
- if (!(child->stroke.flags & SvgStrokeFlags::Width)) {
- child->stroke.width = parent->stroke.width;
- }
- if (!(child->stroke.flags & SvgStrokeFlags::Dash)) {
- if (parent->stroke.dash.array.count > 0) {
- child->stroke.dash.array.clear();
- child->stroke.dash.array.reserve(parent->stroke.dash.array.count);
- for (uint32_t i = 0; i < parent->stroke.dash.array.count; ++i) {
- child->stroke.dash.array.push(parent->stroke.dash.array[i]);
- }
- }
- }
- if (!(child->stroke.flags & SvgStrokeFlags::DashOffset)) {
- child->stroke.dash.offset = parent->stroke.dash.offset;
- }
- if (!(child->stroke.flags & SvgStrokeFlags::Cap)) {
- child->stroke.cap = parent->stroke.cap;
- }
- if (!(child->stroke.flags & SvgStrokeFlags::Join)) {
- child->stroke.join = parent->stroke.join;
- }
- if (!(child->stroke.flags & SvgStrokeFlags::Miterlimit)) {
- child->stroke.miterlimit = parent->stroke.miterlimit;
- }
- }
- static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from)
- {
- if (from == nullptr) return;
- //Copy the properties of 'from' only if they were explicitly set (not the default ones).
- if (from->curColorSet) {
- to->color = from->color;
- to->curColorSet = true;
- }
- if (from->flags & SvgStyleFlags::PaintOrder) {
- to->paintOrder = from->paintOrder;
- }
- //Fill
- to->fill.flags = (to->fill.flags | from->fill.flags);
- if (from->fill.flags & SvgFillFlags::Paint) {
- to->fill.paint.color = from->fill.paint.color;
- to->fill.paint.none = from->fill.paint.none;
- to->fill.paint.curColor = from->fill.paint.curColor;
- if (from->fill.paint.url) {
- if (to->fill.paint.url) free(to->fill.paint.url);
- to->fill.paint.url = _copyId(from->fill.paint.url);
- }
- }
- if (from->fill.flags & SvgFillFlags::Opacity) {
- to->fill.opacity = from->fill.opacity;
- }
- if (from->fill.flags & SvgFillFlags::FillRule) {
- to->fill.fillRule = from->fill.fillRule;
- }
- //Stroke
- to->stroke.flags = (to->stroke.flags | from->stroke.flags);
- if (from->stroke.flags & SvgStrokeFlags::Paint) {
- to->stroke.paint.color = from->stroke.paint.color;
- to->stroke.paint.none = from->stroke.paint.none;
- to->stroke.paint.curColor = from->stroke.paint.curColor;
- if (from->stroke.paint.url) {
- if (to->stroke.paint.url) free(to->stroke.paint.url);
- to->stroke.paint.url = _copyId(from->stroke.paint.url);
- }
- }
- if (from->stroke.flags & SvgStrokeFlags::Opacity) {
- to->stroke.opacity = from->stroke.opacity;
- }
- if (from->stroke.flags & SvgStrokeFlags::Width) {
- to->stroke.width = from->stroke.width;
- }
- if (from->stroke.flags & SvgStrokeFlags::Dash) {
- if (from->stroke.dash.array.count > 0) {
- to->stroke.dash.array.clear();
- to->stroke.dash.array.reserve(from->stroke.dash.array.count);
- for (uint32_t i = 0; i < from->stroke.dash.array.count; ++i) {
- to->stroke.dash.array.push(from->stroke.dash.array[i]);
- }
- }
- }
- if (from->stroke.flags & SvgStrokeFlags::DashOffset) {
- to->stroke.dash.offset = from->stroke.dash.offset;
- }
- if (from->stroke.flags & SvgStrokeFlags::Cap) {
- to->stroke.cap = from->stroke.cap;
- }
- if (from->stroke.flags & SvgStrokeFlags::Join) {
- to->stroke.join = from->stroke.join;
- }
- if (from->stroke.flags & SvgStrokeFlags::Miterlimit) {
- to->stroke.miterlimit = from->stroke.miterlimit;
- }
- }
- static void _copyAttr(SvgNode* to, const SvgNode* from)
- {
- //Copy matrix attribute
- if (from->transform) {
- to->transform = (Matrix*)malloc(sizeof(Matrix));
- if (to->transform) *to->transform = *from->transform;
- }
- //Copy style attribute
- _styleCopy(to->style, from->style);
- to->style->flags = (to->style->flags | from->style->flags);
- if (from->style->clipPath.url) {
- if (to->style->clipPath.url) free(to->style->clipPath.url);
- to->style->clipPath.url = strdup(from->style->clipPath.url);
- }
- if (from->style->mask.url) {
- if (to->style->mask.url) free(to->style->mask.url);
- to->style->mask.url = strdup(from->style->mask.url);
- }
- //Copy node attribute
- switch (from->type) {
- case SvgNodeType::Circle: {
- to->node.circle.cx = from->node.circle.cx;
- to->node.circle.cy = from->node.circle.cy;
- to->node.circle.r = from->node.circle.r;
- break;
- }
- case SvgNodeType::Ellipse: {
- to->node.ellipse.cx = from->node.ellipse.cx;
- to->node.ellipse.cy = from->node.ellipse.cy;
- to->node.ellipse.rx = from->node.ellipse.rx;
- to->node.ellipse.ry = from->node.ellipse.ry;
- break;
- }
- case SvgNodeType::Rect: {
- to->node.rect.x = from->node.rect.x;
- to->node.rect.y = from->node.rect.y;
- to->node.rect.w = from->node.rect.w;
- to->node.rect.h = from->node.rect.h;
- to->node.rect.rx = from->node.rect.rx;
- to->node.rect.ry = from->node.rect.ry;
- to->node.rect.hasRx = from->node.rect.hasRx;
- to->node.rect.hasRy = from->node.rect.hasRy;
- break;
- }
- case SvgNodeType::Line: {
- to->node.line.x1 = from->node.line.x1;
- to->node.line.y1 = from->node.line.y1;
- to->node.line.x2 = from->node.line.x2;
- to->node.line.y2 = from->node.line.y2;
- break;
- }
- case SvgNodeType::Path: {
- if (from->node.path.path) {
- if (to->node.path.path) free(to->node.path.path);
- to->node.path.path = strdup(from->node.path.path);
- }
- break;
- }
- case SvgNodeType::Polygon: {
- if ((to->node.polygon.pts.count = from->node.polygon.pts.count)) {
- to->node.polygon.pts = from->node.polygon.pts;
- }
- break;
- }
- case SvgNodeType::Polyline: {
- if ((to->node.polyline.pts.count = from->node.polyline.pts.count)) {
- to->node.polyline.pts = from->node.polyline.pts;
- }
- break;
- }
- case SvgNodeType::Image: {
- to->node.image.x = from->node.image.x;
- to->node.image.y = from->node.image.y;
- to->node.image.w = from->node.image.w;
- to->node.image.h = from->node.image.h;
- if (from->node.image.href) {
- if (to->node.image.href) free(to->node.image.href);
- to->node.image.href = strdup(from->node.image.href);
- }
- break;
- }
- case SvgNodeType::Use: {
- to->node.use.x = from->node.use.x;
- to->node.use.y = from->node.use.y;
- to->node.use.w = from->node.use.w;
- to->node.use.h = from->node.use.h;
- to->node.use.isWidthSet = from->node.use.isWidthSet;
- to->node.use.isHeightSet = from->node.use.isHeightSet;
- to->node.use.symbol = from->node.use.symbol;
- break;
- }
- default: {
- break;
- }
- }
- }
- static void _cloneNode(SvgNode* from, SvgNode* parent, int depth)
- {
- /* Exception handling: Prevent invalid SVG data input.
- The size is the arbitrary value, we need an experimental size. */
- if (depth == 8192) {
- TVGERR("SVG", "Infinite recursive call - stopped after %d calls! Svg file may be incorrectly formatted.", depth);
- return;
- }
- SvgNode* newNode;
- if (!from || !parent || from == parent) return;
- newNode = _createNode(parent, from->type);
- if (!newNode) return;
- _styleInherit(newNode->style, parent->style);
- _copyAttr(newNode, from);
- auto child = from->child.data;
- for (uint32_t i = 0; i < from->child.count; ++i, ++child) {
- _cloneNode(*child, newNode, depth + 1);
- }
- }
- static void _clonePostponedNodes(Array<SvgNodeIdPair>* cloneNodes, SvgNode* doc)
- {
- for (uint32_t i = 0; i < cloneNodes->count; ++i) {
- auto nodeIdPair = (*cloneNodes)[i];
- auto defs = _getDefsNode(nodeIdPair.node);
- auto nodeFrom = _findNodeById(defs, nodeIdPair.id);
- if (!nodeFrom) nodeFrom = _findNodeById(doc, nodeIdPair.id);
- if (!_findParentById(nodeIdPair.node, nodeIdPair.id, doc)) {
- _cloneNode(nodeFrom, nodeIdPair.node, 0);
- if (nodeFrom && nodeFrom->type == SvgNodeType::Symbol && nodeIdPair.node->type == SvgNodeType::Use) {
- nodeIdPair.node->node.use.symbol = nodeFrom;
- }
- } else {
- TVGLOG("SVG", "%s is ancestor element. This reference is invalid.", nodeIdPair.id);
- }
- free(nodeIdPair.id);
- }
- }
- static constexpr struct
- {
- const char* tag;
- size_t sz;
- } popArray[] = {
- {"g", sizeof("g")},
- {"svg", sizeof("svg")},
- {"defs", sizeof("defs")},
- {"mask", sizeof("mask")},
- {"clipPath", sizeof("clipPath")},
- {"style", sizeof("style")},
- {"symbol", sizeof("symbol")}
- };
- static void _svgLoaderParserXmlClose(SvgLoaderData* loader, const char* content)
- {
- content = _skipSpace(content, nullptr);
- for (unsigned int i = 0; i < sizeof(popArray) / sizeof(popArray[0]); i++) {
- if (!strncmp(content, popArray[i].tag, popArray[i].sz - 1)) {
- loader->stack.pop();
- break;
- }
- }
- loader->level--;
- }
- static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content, unsigned int length, bool empty)
- {
- const char* attrs = nullptr;
- int attrsLength = 0;
- int sz = length;
- char tagName[20] = "";
- FactoryMethod method;
- GradientFactoryMethod gradientMethod;
- SvgNode *node = nullptr, *parent = nullptr;
- loader->level++;
- attrs = simpleXmlFindAttributesTag(content, length);
- if (!attrs) {
- //Parse the empty tag
- attrs = content;
- while ((attrs != nullptr) && *attrs != '>') attrs++;
- if (empty) attrs--;
- }
- if (attrs) {
- //Find out the tag name starting from content till sz length
- sz = attrs - content;
- while ((sz > 0) && (isspace(content[sz - 1]))) sz--;
- if ((unsigned)sz >= sizeof(tagName)) return;
- strncpy(tagName, content, sz);
- tagName[sz] = '\0';
- attrsLength = length - sz;
- }
- if ((method = _findGroupFactory(tagName))) {
- //Group
- if (empty) return;
- if (!loader->doc) {
- if (strcmp(tagName, "svg")) return; //Not a valid svg document
- node = method(loader, nullptr, attrs, attrsLength, simpleXmlParseAttributes);
- loader->doc = node;
- } else {
- if (!strcmp(tagName, "svg")) return; //Already loaded <svg>(SvgNodeType::Doc) tag
- if (loader->stack.count > 0) parent = loader->stack.last();
- else parent = loader->doc;
- if (!strcmp(tagName, "style")) {
- // TODO: For now only the first style node is saved. After the css id selector
- // is introduced this if condition shouldin't be necessary any more
- if (!loader->cssStyle) {
- node = method(loader, nullptr, attrs, attrsLength, simpleXmlParseAttributes);
- loader->cssStyle = node;
- loader->doc->node.doc.style = node;
- loader->style = true;
- }
- } else {
- node = method(loader, parent, attrs, attrsLength, simpleXmlParseAttributes);
- }
- }
- if (!node) return;
- if (node->type != SvgNodeType::Defs || !empty) {
- loader->stack.push(node);
- }
- } else if ((method = _findGraphicsFactory(tagName))) {
- if (loader->stack.count > 0) parent = loader->stack.last();
- else parent = loader->doc;
- node = method(loader, parent, attrs, attrsLength, simpleXmlParseAttributes);
- } else if ((gradientMethod = _findGradientFactory(tagName))) {
- SvgStyleGradient* gradient;
- gradient = gradientMethod(loader, attrs, attrsLength);
- //FIXME: The current parsing structure does not distinguish end tags.
- // There is no way to know if the currently parsed gradient is in defs.
- // If a gradient is declared outside of defs after defs is set, it is included in the gradients of defs.
- // But finally, the loader has a gradient style list regardless of defs.
- // This is only to support this when multiple gradients are declared, even if no defs are declared.
- // refer to: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs
- if (loader->def && loader->doc->node.doc.defs) {
- loader->def->node.defs.gradients.push(gradient);
- } else {
- loader->gradients.push(gradient);
- }
- loader->latestGradient = gradient;
- } else if (!strcmp(tagName, "stop")) {
- if (!loader->latestGradient) {
- TVGLOG("SVG", "Stop element is used outside of the Gradient element");
- return;
- }
- /* default value for opacity */
- loader->svgParse->gradStop = {0.0f, 0, 0, 0, 255};
- loader->svgParse->flags = SvgStopStyleFlags::StopDefault;
- simpleXmlParseAttributes(attrs, attrsLength, _attrParseStops, loader);
- loader->latestGradient->stops.push(loader->svgParse->gradStop);
- } else if (!isIgnoreUnsupportedLogElements(tagName)) {
- TVGLOG("SVG", "Unsupported elements used [Elements: %s]", tagName);
- }
- }
- static void _svgLoaderParserXmlCssStyle(SvgLoaderData* loader, const char* content, unsigned int length)
- {
- char* tag;
- char* name;
- const char* attrs = nullptr;
- unsigned int attrsLength = 0;
- FactoryMethod method;
- GradientFactoryMethod gradientMethod;
- SvgNode *node = nullptr;
- while (auto next = simpleXmlParseCSSAttribute(content, length, &tag, &name, &attrs, &attrsLength)) {
- if ((method = _findGroupFactory(tag))) {
- if ((node = method(loader, loader->cssStyle, attrs, attrsLength, simpleXmlParseW3CAttribute))) node->id = _copyId(name);
- } else if ((method = _findGraphicsFactory(tag))) {
- if ((node = method(loader, loader->cssStyle, attrs, attrsLength, simpleXmlParseW3CAttribute))) node->id = _copyId(name);
- } else if ((gradientMethod = _findGradientFactory(tag))) {
- TVGLOG("SVG", "Unsupported elements used in the internal CSS style sheets [Elements: %s]", tag);
- } else if (!strcmp(tag, "stop")) {
- TVGLOG("SVG", "Unsupported elements used in the internal CSS style sheets [Elements: %s]", tag);
- } else if (!strcmp(tag, "all")) {
- if ((node = _createCssStyleNode(loader, loader->cssStyle, attrs, attrsLength, simpleXmlParseW3CAttribute))) node->id = _copyId(name);
- } else if (!isIgnoreUnsupportedLogElements(tag)) {
- TVGLOG("SVG", "Unsupported elements used in the internal CSS style sheets [Elements: %s]", tag);
- }
- length -= next - content;
- content = next;
- free(tag);
- free(name);
- }
- loader->style = false;
- }
- static bool _svgLoaderParser(void* data, SimpleXMLType type, const char* content, unsigned int length)
- {
- SvgLoaderData* loader = (SvgLoaderData*)data;
- switch (type) {
- case SimpleXMLType::Open: {
- _svgLoaderParserXmlOpen(loader, content, length, false);
- break;
- }
- case SimpleXMLType::OpenEmpty: {
- _svgLoaderParserXmlOpen(loader, content, length, true);
- break;
- }
- case SimpleXMLType::Close: {
- _svgLoaderParserXmlClose(loader, content);
- break;
- }
- case SimpleXMLType::Data:
- case SimpleXMLType::CData: {
- if (loader->style) _svgLoaderParserXmlCssStyle(loader, content, length);
- break;
- }
- case SimpleXMLType::DoctypeChild: {
- break;
- }
- case SimpleXMLType::Ignored:
- case SimpleXMLType::Comment:
- case SimpleXMLType::Doctype: {
- break;
- }
- default: {
- break;
- }
- }
- return true;
- }
- static void _inefficientNodeCheck(TVG_UNUSED SvgNode* node)
- {
- #ifdef THORVG_LOG_ENABLED
- auto type = simpleXmlNodeTypeToString(node->type);
- if (!node->display && node->type != SvgNodeType::ClipPath && node->type != SvgNodeType::Symbol) TVGLOG("SVG", "Inefficient elements used [Display is none][Node Type : %s]", type);
- if (node->style->opacity == 0) TVGLOG("SVG", "Inefficient elements used [Opacity is zero][Node Type : %s]", type);
- if (node->style->fill.opacity == 0 && node->style->stroke.opacity == 0) TVGLOG("SVG", "Inefficient elements used [Fill opacity and stroke opacity are zero][Node Type : %s]", type);
- switch (node->type) {
- case SvgNodeType::Path: {
- if (!node->node.path.path) TVGLOG("SVG", "Inefficient elements used [Empty path][Node Type : %s]", type);
- break;
- }
- case SvgNodeType::Ellipse: {
- if (node->node.ellipse.rx == 0 && node->node.ellipse.ry == 0) TVGLOG("SVG", "Inefficient elements used [Size is zero][Node Type : %s]", type);
- break;
- }
- case SvgNodeType::Polygon:
- case SvgNodeType::Polyline: {
- if (node->node.polygon.pts.count < 2) TVGLOG("SVG", "Inefficient elements used [Invalid Polygon][Node Type : %s]", type);
- break;
- }
- case SvgNodeType::Circle: {
- if (node->node.circle.r == 0) TVGLOG("SVG", "Inefficient elements used [Size is zero][Node Type : %s]", type);
- break;
- }
- case SvgNodeType::Rect: {
- if (node->node.rect.w == 0 && node->node.rect.h) TVGLOG("SVG", "Inefficient elements used [Size is zero][Node Type : %s]", type);
- break;
- }
- case SvgNodeType::Line: {
- if (node->node.line.x1 == node->node.line.x2 && node->node.line.y1 == node->node.line.y2) TVGLOG("SVG", "Inefficient elements used [Size is zero][Node Type : %s]", type);
- break;
- }
- default: break;
- }
- #endif
- }
- static void _updateStyle(SvgNode* node, SvgStyleProperty* parentStyle)
- {
- _styleInherit(node->style, parentStyle);
- _inefficientNodeCheck(node);
- auto child = node->child.data;
- for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
- _updateStyle(*child, node->style);
- }
- }
- static SvgStyleGradient* _gradientDup(SvgLoaderData* loader, Array<SvgStyleGradient*>* gradients, const char* id)
- {
- SvgStyleGradient* result = nullptr;
- auto gradList = gradients->data;
- for (uint32_t i = 0; i < gradients->count; ++i) {
- if ((*gradList)->id && !strcmp((*gradList)->id, id)) {
- result = _cloneGradient(*gradList);
- break;
- }
- ++gradList;
- }
- if (result && result->ref) {
- gradList = gradients->data;
- for (uint32_t i = 0; i < gradients->count; ++i) {
- if ((*gradList)->id && !strcmp((*gradList)->id, result->ref)) {
- _inheritGradient(loader, result, *gradList);
- break;
- }
- ++gradList;
- }
- }
- return result;
- }
- static void _updateGradient(SvgLoaderData* loader, SvgNode* node, Array<SvgStyleGradient*>* gradients)
- {
- if (node->child.count > 0) {
- auto child = node->child.data;
- for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
- _updateGradient(loader, *child, gradients);
- }
- } else {
- if (node->style->fill.paint.url) {
- auto newGrad = _gradientDup(loader, gradients, node->style->fill.paint.url);
- if (newGrad) {
- if (node->style->fill.paint.gradient) {
- node->style->fill.paint.gradient->clear();
- free(node->style->fill.paint.gradient);
- }
- node->style->fill.paint.gradient = newGrad;
- }
- }
- if (node->style->stroke.paint.url) {
- auto newGrad = _gradientDup(loader, gradients, node->style->stroke.paint.url);
- if (newGrad) {
- if (node->style->stroke.paint.gradient) {
- node->style->stroke.paint.gradient->clear();
- free(node->style->stroke.paint.gradient);
- }
- node->style->stroke.paint.gradient = newGrad;
- }
- }
- }
- }
- static void _updateComposite(SvgNode* node, SvgNode* root)
- {
- if (node->style->clipPath.url && !node->style->clipPath.node) {
- SvgNode* findResult = _findNodeById(root, node->style->clipPath.url);
- if (findResult) node->style->clipPath.node = findResult;
- }
- if (node->style->mask.url && !node->style->mask.node) {
- SvgNode* findResult = _findNodeById(root, node->style->mask.url);
- if (findResult) node->style->mask.node = findResult;
- }
- if (node->child.count > 0) {
- auto child = node->child.data;
- for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
- _updateComposite(*child, root);
- }
- }
- }
- static void _freeNodeStyle(SvgStyleProperty* style)
- {
- if (!style) return;
- //style->clipPath.node and style->mask.node has only the addresses of node. Therefore, node is released from _freeNode.
- free(style->clipPath.url);
- free(style->mask.url);
- free(style->cssClass);
- if (style->fill.paint.gradient) {
- style->fill.paint.gradient->clear();
- free(style->fill.paint.gradient);
- }
- if (style->stroke.paint.gradient) {
- style->stroke.paint.gradient->clear();
- free(style->stroke.paint.gradient);
- }
- free(style->fill.paint.url);
- free(style->stroke.paint.url);
- style->stroke.dash.array.reset();
- free(style);
- }
- static void _freeNode(SvgNode* node)
- {
- if (!node) return;
- auto child = node->child.data;
- for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
- _freeNode(*child);
- }
- node->child.reset();
- free(node->id);
- free(node->transform);
- _freeNodeStyle(node->style);
- switch (node->type) {
- case SvgNodeType::Path: {
- free(node->node.path.path);
- break;
- }
- case SvgNodeType::Polygon: {
- free(node->node.polygon.pts.data);
- break;
- }
- case SvgNodeType::Polyline: {
- free(node->node.polyline.pts.data);
- break;
- }
- case SvgNodeType::Doc: {
- _freeNode(node->node.doc.defs);
- _freeNode(node->node.doc.style);
- break;
- }
- case SvgNodeType::Defs: {
- auto gradients = node->node.defs.gradients.data;
- for (size_t i = 0; i < node->node.defs.gradients.count; ++i) {
- (*gradients)->clear();
- free(*gradients);
- ++gradients;
- }
- node->node.defs.gradients.reset();
- break;
- }
- case SvgNodeType::Image: {
- free(node->node.image.href);
- break;
- }
- default: {
- break;
- }
- }
- free(node);
- }
- static bool _svgLoaderParserForValidCheckXmlOpen(SvgLoaderData* loader, const char* content, unsigned int length)
- {
- const char* attrs = nullptr;
- int sz = length;
- char tagName[20] = "";
- FactoryMethod method;
- SvgNode *node = nullptr;
- int attrsLength = 0;
- loader->level++;
- attrs = simpleXmlFindAttributesTag(content, length);
- if (!attrs) {
- //Parse the empty tag
- attrs = content;
- while ((attrs != nullptr) && *attrs != '>') attrs++;
- }
- if (attrs) {
- sz = attrs - content;
- while ((sz > 0) && (isspace(content[sz - 1]))) sz--;
- if ((unsigned)sz >= sizeof(tagName)) return false;
- strncpy(tagName, content, sz);
- tagName[sz] = '\0';
- attrsLength = length - sz;
- }
- if ((method = _findGroupFactory(tagName))) {
- if (!loader->doc) {
- if (strcmp(tagName, "svg")) return true; //Not a valid svg document
- node = method(loader, nullptr, attrs, attrsLength, simpleXmlParseAttributes);
- loader->doc = node;
- loader->stack.push(node);
- return false;
- }
- }
- return true;
- }
- static bool _svgLoaderParserForValidCheck(void* data, SimpleXMLType type, const char* content, unsigned int length)
- {
- SvgLoaderData* loader = (SvgLoaderData*)data;
- bool res = true;;
- switch (type) {
- case SimpleXMLType::Open:
- case SimpleXMLType::OpenEmpty: {
- //If 'res' is false, it means <svg> tag is found.
- res = _svgLoaderParserForValidCheckXmlOpen(loader, content, length);
- break;
- }
- default: {
- break;
- }
- }
- return res;
- }
- void SvgLoader::clear(bool all)
- {
- //flush out the intermediate data
- free(loaderData.svgParse);
- loaderData.svgParse = nullptr;
- for (auto gradient = loaderData.gradients.begin(); gradient < loaderData.gradients.end(); ++gradient) {
- (*gradient)->clear();
- free(*gradient);
- }
- loaderData.gradients.reset();
- _freeNode(loaderData.doc);
- loaderData.doc = nullptr;
- loaderData.stack.reset();
- if (!all) return;
- for (auto p = loaderData.images.begin(); p < loaderData.images.end(); ++p) {
- free(*p);
- }
- loaderData.images.reset();
- if (copy) free((char*)content);
- delete(root);
- root = nullptr;
- size = 0;
- content = nullptr;
- copy = false;
- }
- /************************************************************************/
- /* External Class Implementation */
- /************************************************************************/
- SvgLoader::SvgLoader() : ImageLoader(FileType::Svg)
- {
- }
- SvgLoader::~SvgLoader()
- {
- this->done();
- clear();
- }
- void SvgLoader::run(unsigned tid)
- {
- //According to the SVG standard the value of the width/height of the viewbox set to 0 disables rendering
- if ((viewFlag & SvgViewFlag::Viewbox) && (fabsf(vw) <= FLT_EPSILON || fabsf(vh) <= FLT_EPSILON)) {
- TVGLOG("SVG", "The <viewBox> width and/or height set to 0 - rendering disabled.");
- root = Scene::gen().release();
- return;
- }
- if (!simpleXmlParse(content, size, true, _svgLoaderParser, &(loaderData))) return;
- if (loaderData.doc) {
- auto defs = loaderData.doc->node.doc.defs;
- if (loaderData.nodesToStyle.count > 0) cssApplyStyleToPostponeds(loaderData.nodesToStyle, loaderData.cssStyle);
- if (loaderData.cssStyle) cssUpdateStyle(loaderData.doc, loaderData.cssStyle);
- if (loaderData.cloneNodes.count > 0) _clonePostponedNodes(&loaderData.cloneNodes, loaderData.doc);
- _updateComposite(loaderData.doc, loaderData.doc);
- if (defs) _updateComposite(loaderData.doc, defs);
- _updateStyle(loaderData.doc, nullptr);
- if (defs) _updateStyle(defs, nullptr);
- if (loaderData.gradients.count > 0) _updateGradient(&loaderData, loaderData.doc, &loaderData.gradients);
- if (defs) _updateGradient(&loaderData, loaderData.doc, &defs->node.defs.gradients);
- }
- root = svgSceneBuild(loaderData, {vx, vy, vw, vh}, w, h, align, meetOrSlice, svgPath, viewFlag);
- //In case no viewbox and width/height data is provided the completion of loading
- //has to be forced, in order to establish this data based on the whole picture.
- if (!(viewFlag & SvgViewFlag::Viewbox)) {
- //Override viewbox & size again after svg loading.
- vx = loaderData.doc->node.doc.vx;
- vy = loaderData.doc->node.doc.vy;
- vw = loaderData.doc->node.doc.vw;
- vh = loaderData.doc->node.doc.vh;
- w = loaderData.doc->node.doc.w;
- h = loaderData.doc->node.doc.h;
- }
- clear(false);
- }
- bool SvgLoader::header()
- {
- //For valid check, only <svg> tag is parsed first.
- //If the <svg> tag is found, the loaded file is valid and stores viewbox information.
- //After that, the remaining content data is parsed in order with async.
- loaderData.svgParse = (SvgParser*)malloc(sizeof(SvgParser));
- if (!loaderData.svgParse) return false;
- loaderData.svgParse->flags = SvgStopStyleFlags::StopDefault;
- viewFlag = SvgViewFlag::None;
- simpleXmlParse(content, size, true, _svgLoaderParserForValidCheck, &(loaderData));
- if (loaderData.doc && loaderData.doc->type == SvgNodeType::Doc) {
- viewFlag = loaderData.doc->node.doc.viewFlag;
- align = loaderData.doc->node.doc.align;
- meetOrSlice = loaderData.doc->node.doc.meetOrSlice;
- if (viewFlag & SvgViewFlag::Viewbox) {
- vx = loaderData.doc->node.doc.vx;
- vy = loaderData.doc->node.doc.vy;
- vw = loaderData.doc->node.doc.vw;
- vh = loaderData.doc->node.doc.vh;
- if (viewFlag & SvgViewFlag::Width) w = loaderData.doc->node.doc.w;
- else {
- w = loaderData.doc->node.doc.vw;
- if (viewFlag & SvgViewFlag::WidthInPercent) {
- w *= loaderData.doc->node.doc.w;
- viewFlag = (viewFlag ^ SvgViewFlag::WidthInPercent);
- }
- viewFlag = (viewFlag | SvgViewFlag::Width);
- }
- if (viewFlag & SvgViewFlag::Height) h = loaderData.doc->node.doc.h;
- else {
- h = loaderData.doc->node.doc.vh;
- if (viewFlag & SvgViewFlag::HeightInPercent) {
- h *= loaderData.doc->node.doc.h;
- viewFlag = (viewFlag ^ SvgViewFlag::HeightInPercent);
- }
- viewFlag = (viewFlag | SvgViewFlag::Height);
- }
- //In case no viewbox and width/height data is provided the completion of loading
- //has to be forced, in order to establish this data based on the whole picture.
- } else {
- //Before loading, set default viewbox & size if they are empty
- vx = vy = 0.0f;
- if (viewFlag & SvgViewFlag::Width) {
- vw = w = loaderData.doc->node.doc.w;
- } else {
- vw = 1.0f;
- if (viewFlag & SvgViewFlag::WidthInPercent) {
- w = loaderData.doc->node.doc.w;
- } else w = 1.0f;
- }
- if (viewFlag & SvgViewFlag::Height) {
- vh = h = loaderData.doc->node.doc.h;
- } else {
- vh = 1.0f;
- if (viewFlag & SvgViewFlag::HeightInPercent) {
- h = loaderData.doc->node.doc.h;
- } else h = 1.0f;
- }
- run(0);
- }
- return true;
- }
- TVGLOG("SVG", "No SVG File. There is no <svg/>");
- return false;
- }
- bool SvgLoader::open(const char* data, uint32_t size, bool copy)
- {
- clear();
- if (copy) {
- content = (char*)malloc(size);
- if (!content) return false;
- memcpy((char*)content, data, size);
- } else content = data;
- this->size = size;
- this->copy = copy;
- return header();
- }
- bool SvgLoader::open(const string& path)
- {
- clear();
- ifstream f;
- f.open(path);
- if (!f.is_open()) return false;
- svgPath = path;
- getline(f, filePath, '\0');
- f.close();
- if (filePath.empty()) return false;
- content = filePath.c_str();
- size = filePath.size();
- return header();
- }
- bool SvgLoader::resize(Paint* paint, float w, float h)
- {
- if (!paint) return false;
- auto sx = w / this->w;
- auto sy = h / this->h;
- Matrix m = {sx, 0, 0, 0, sy, 0, 0, 0, 1};
- paint->transform(m);
- return true;
- }
- bool SvgLoader::read()
- {
- if (!content || size == 0) return false;
- //the loading has been already completed in header()
- if (root || !LoadModule::read()) return true;
- TaskScheduler::request(this);
- return true;
- }
- bool SvgLoader::close()
- {
- if (!LoadModule::close()) return false;
- this->done();
- clear();
- return true;
- }
- Paint* SvgLoader::paint()
- {
- this->done();
- auto ret = root;
- root = nullptr;
- return ret;
- }
|