| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753 |
- unit Img32.SVG.Reader;
- (*******************************************************************************
- * Author : Angus Johnson *
- * Version : 4.8 *
- * Date : 12 January 2025 *
- * Website : http://www.angusj.com *
- * Copyright : Angus Johnson 2019-2025 *
- * *
- * Purpose : Read SVG 2.0 files *
- * *
- * License : Use, modification & distribution is subject to *
- * Boost Software License Ver 1 *
- * http://www.boost.org/LICENSE_1_0.txt *
- *******************************************************************************)
- interface
- {$I Img32.inc}
- uses
- SysUtils, Classes, Types, Math,
- {$IFDEF XPLAT_GENERICS} Generics.Collections, Generics.Defaults,{$ENDIF}
- Img32, Img32.SVG.Core, Img32.SVG.Path, Img32.Vector,
- Img32.Draw, Img32.Text, Img32.Transform;
- {$IFDEF ZEROBASEDSTR}
- {$ZEROBASEDSTRINGS OFF}
- {$ENDIF}
- type
- TBaseElement = class;
- TElementClass = class of TBaseElement;
- TDrawData = record
- currentColor : TColor32;
- fillColor : TColor32;
- fillOpacity : double;
- fillRule : TFillRule;
- fillEl : UTF8String;
- strokeColor : TColor32;
- strokeOpacity : double;
- strokeWidth : TValue;
- strokeCap : TEndStyle;
- strokeJoin : TJoinStyle;
- strokeMitLim : double;
- strokeEl : UTF8String;
- dashArray : TArrayOfDouble;
- dashOffset : double;
- fontInfo : TSVGFontInfo;
- markerStart : UTF8String;
- markerMiddle : UTF8String;
- markerEnd : UTF8String;
- filterElRef : UTF8String;
- maskElRef : UTF8String;
- clipElRef : UTF8String;
- matrix : TMatrixD;
- visible : Boolean;
- useEl : TBaseElement; // to check for and prevent <USE> recursion
- bounds : TRectD;
- end;
- PSvgIdNameHashMapItem = ^TSvgIdNameHashMapItem;
- TSvgIdNameHashMapItem = record
- Hash: Cardinal;
- Next: Integer;
- Name: UTF8String;
- Element: TBaseElement;
- end;
- TSvgIdNameHashMap = class(TObject)
- private
- FItems: array of TSvgIdNameHashMapItem;
- FBuckets: TArrayOfInteger;
- FCount: Integer;
- FMod: Cardinal;
- procedure Grow;
- function FindItemIndex(const Name: UTF8String): Integer;
- public
- procedure AddOrIgnore(const idName: UTF8String; element: TBaseElement);
- function FindElement(const idName: UTF8String): TBaseElement;
- procedure Clear;
- end;
- TSvgReader = class;
- TBaseElement = class
- private
- fParent : TBaseElement;
- fXmlEl : TSvgXmlEl;
- fSvgReader : TSvgReader;
- {$IFDEF XPLAT_GENERICS}
- fChilds : TList<TBaseElement>;
- {$ELSE}
- fChilds : TList;
- {$ENDIF}
- fId : UTF8String;
- fDrawData : TDrawData; // currently both static and dynamic vars
- function FindRefElement(const refname: UTF8String): TBaseElement;
- function GetChildCount: integer;
- function GetChild(index: integer): TBaseElement;
- function FindChild(const idName: UTF8String): TBaseElement;
- protected
- elRectWH : TValueRecWH; // multifunction variable
- function IsFirstChild: Boolean;
- procedure LoadAttributes;
- procedure LoadAttribute(attrib: PSvgAttrib);
- function LoadContent: Boolean; virtual;
- // GetRelFracLimit: ie when to assume untyped vals are relative vals
- function GetRelFracLimit: double; virtual;
- procedure Draw(image: TImage32; drawDat: TDrawData); virtual;
- procedure DrawChildren(image: TImage32; const drawDat: TDrawData);
- public
- constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); virtual;
- destructor Destroy; override;
- property Child[index: integer]: TBaseElement read GetChild; default;
- property ChildCount: integer read GetChildCount;
- property DrawData: TDrawData read fDrawData write fDrawData;
- property Id: UTF8String read fId;
- end;
- TShapeElement = class(TBaseElement)
- protected
- hasPaths : Boolean;
- pathsLoaded : Boolean;
- drawPathsO : TPathsD; //open only
- drawPathsC : TPathsD; //closed only
- function GetBounds: TRectD; virtual;
- function HasMarkers: Boolean;
- procedure GetPaths(const drawDat: TDrawData); virtual;
- // GetSimplePath: is only required for markers
- function GetSimplePath(const drawDat: TDrawData): TPathsD; virtual;
- procedure DrawFilled(img: TImage32;
- const paths: TPathsD; drawDat: TDrawData);
- procedure DrawStroke(img: TImage32;
- const paths: TPathsD; drawDat: TDrawData; isClosed: Boolean);
- procedure DrawMarkers(img: TImage32; drawDat: TDrawData);
- procedure Draw(image: TImage32; drawDat: TDrawData); override;
- public
- constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
- end;
- TSvgElement = class(TShapeElement)
- protected
- procedure Draw(image: TImage32; drawDat: TDrawData); override;
- public
- viewboxWH : TRectWH;
- function Width : TValue;
- function Height : TValue;
- end;
- TSvgReader = class
- private
- fSvgParser : TSvgParser;
- fBkgndColor : TColor32;
- fBackgndImage : TImage32;
- fTempImage : TImage32;
- fBlurQuality : integer;
- fIdList : TSvgIdNameHashMap;
- fLinGradRenderer : TLinearGradientRenderer;
- fRadGradRenderer : TSvgRadialGradientRenderer;
- fCustomRendererCache: TCustomRendererCache;
- fRootElement : TSvgElement;
- fFontCache : TFontCache;
- fUsePropScale : Boolean;
- fSimpleDraw : Boolean;
- fSimpleDrawList : TList;
- function LoadInternal: Boolean;
- function GetIsEmpty: Boolean;
- function GetTempImage: TImage32;
- procedure SetBlurQuality(quality: integer);
- protected
- userSpaceBounds : TRectD;
- currentColor : TColor32;
- procedure GetBestFont(const svgFontInfo: TSVGFontInfo);
- property RadGradRenderer: TSvgRadialGradientRenderer read fRadGradRenderer;
- property LinGradRenderer: TLinearGradientRenderer read fLinGradRenderer;
- property BackgndImage : TImage32 read fBackgndImage;
- property TempImage : TImage32 read GetTempImage;
- public
- constructor Create;
- destructor Destroy; override;
- procedure Clear;
- procedure DrawImage(img: TImage32; scaleToImage: Boolean);
- function LoadFromStream(stream: TStream): Boolean;
- function LoadFromFile(const filename: string): Boolean;
- function LoadFromString(const str: string): Boolean;
- // The following two methods are deprecated and intended only for ...
- // https://github.com/EtheaDev/SVGIconImageList
- procedure SetOverrideFillColor(color: TColor32); //deprecated;
- procedure SetOverrideStrokeColor(color: TColor32); //deprecated;
- function FindElement(const idName: UTF8String): TBaseElement;
- property BackgroundColor : TColor32 read fBkgndColor write fBkgndColor;
- property BlurQuality : integer read fBlurQuality write SetBlurQuality;
- property IsEmpty : Boolean read GetIsEmpty;
- // KeepAspectRatio: this property has also been added for the convenience of
- // the third-party SVGIconImageList. (IMHO it should always = true)
- property KeepAspectRatio: Boolean
- read fUsePropScale write fUsePropScale;
- property RootElement : TSvgElement read fRootElement;
- // RecordSimpleDraw: record simple drawing instructions
- property RecordSimpleDraw: Boolean read fSimpleDraw write fSimpleDraw;
- // SimpleDrawList: list of PSimpleDrawData records;
- property SimpleDrawList : TList read fSimpleDrawList;
- end;
- PSimpleDrawData = ^TSimpleDrawData;
- TSimpleDrawData = record
- paths : TPathsD;
- fillRule : TFillRule;
- color : TColor32;
- tag : integer;
- end;
- var
- // https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/width
- defaultSvgWidth: integer = 300;
- // https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/height
- defaultSvgHeight: integer = 150;
- implementation
- uses
- Img32.Extra, Img32.Clipper2;
- type
- TFourDoubles = array [0..3] of double;
- TDefsElement = class(TBaseElement)
- public
- constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
- end;
- TStyleElement = class(TBaseElement)
- public
- constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
- end;
- // TImageElement only supports *embedded* jpg & png images.
- // And it requires Img32.Fmt.JPG & Img32.Fmt.PNG to be included
- // in the USES clause of at least one of the application's units.
- // (nb: If using the FMX framework, then add Img32.FMX instead of
- // Img32.Fmt.JPG & Img32.Fmt.PNG to the USES clause.)
- TImageElement = class(TBaseElement)
- private
- fRefEl: UTF8String;
- fImage: TImage32;
- fTransparent: Boolean;
- protected
- procedure Draw(image: TImage32; drawDat: TDrawData); override;
- public
- destructor Destroy; override;
- end;
- TGroupElement = class(TShapeElement)
- protected
- procedure Draw(image: TImage32; drawDat: TDrawData); override;
- end;
- TSwitchElement = class(TShapeElement)
- protected
- procedure Draw(image: TImage32; drawDat: TDrawData); override;
- end;
- TUseElement = class(TShapeElement)
- private
- callerUse: TBaseElement;
- function ValidateNonRecursion(el: TBaseElement): Boolean;
- protected
- fRefEl: UTF8String;
- procedure GetPaths(const drawDat: TDrawData); override;
- procedure Draw(img: TImage32; drawDat: TDrawData); override;
- end;
- TMaskElement = class(TShapeElement)
- protected
- maskRec: TRect;
- procedure GetPaths(const drawDat: TDrawData); override;
- procedure ApplyMask(img: TImage32; const drawDat: TDrawData);
- end;
- TSymbolElement = class(TShapeElement)
- protected
- viewboxWH: TRectWH;
- public
- constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
- end;
- //-------------------------------------
- TPathElement = class(TShapeElement)
- private
- fSvgPaths : TSvgPath;
- procedure Flatten(index: integer; scalePending: double;
- out path: TPathD; out isClosed: Boolean);
- protected
- function GetBounds: TRectD; override;
- procedure ParseDAttrib(const value: UTF8String);
- procedure GetPaths(const drawDat: TDrawData); override;
- function GetSimplePath(const drawDat: TDrawData): TPathsD; override;
- public
- constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
- destructor Destroy; override;
- end;
- TPolyElement = class(TShapeElement) //polyline or polygon
- protected
- path : TPathD;
- function GetBounds: TRectD; override;
- procedure ParsePoints(const value: UTF8String);
- procedure GetPaths(const drawDat: TDrawData); override;
- function GetSimplePath(const drawDat: TDrawData): TPathsD; override;
- end;
- TLineElement = class(TShapeElement)
- protected
- path : TPathD;
- function GetBounds: TRectD; override;
- procedure GetPaths(const drawDat: TDrawData); override;
- function GetSimplePath(const drawDat: TDrawData): TPathsD; override;
- public
- constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
- end;
- TCircleElement = class(TShapeElement)
- protected
- bounds : TRectD;
- centerPt : TValuePt;
- radius : TValue;
- function GetBounds: TRectD; override;
- procedure GetPaths(const drawDat: TDrawData); override;
- public
- constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
- end;
- TEllipseElement = class(TShapeElement)
- protected
- bounds : TRectD;
- centerPt : TValuePt;
- radius : TValuePt;
- function GetBounds: TRectD; override;
- procedure GetPaths(const drawDat: TDrawData); override;
- public
- constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
- end;
- TRectElement = class(TShapeElement)
- protected
- radius : TValuePt;
- function GetBounds: TRectD; override;
- procedure GetPaths(const drawDat: TDrawData); override;
- function GetSimplePath(const drawDat: TDrawData): TPathsD; override;
- public
- constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
- end;
- TTSpanElement = class;
- // TTextElement: although a TShapeElement descendant, it's really just
- // a container for other TShapeElements (<tspan>, <textpath> etc).
- TTextElement = class(TShapeElement)
- protected
- offset : TValuePt;
- textDx : double;
- angle : TArrayOfDouble;
- currentPt : TPointD;
- currSpanEl : TTSpanElement; //the current 'real' <tspan>
- lastChrSpc : Boolean;
- procedure Draw(img: TImage32; drawDat: TDrawData); override;
- public
- constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
- end;
- TTextSubElement = class(TShapeElement)
- protected
- offset : TValuePt;
- textEl : TTextElement;
- function GetTextEl: TTextElement;
- end;
- TTSpanElement = class(TTextSubElement)
- protected
- chunkDx : double;
- angle : TArrayOfDouble;
- procedure GetPaths(const drawDat: TDrawData); override;
- public
- procedure Draw(image: TImage32; drawDat: TDrawData); override;
- end;
- TTextPathElement = class(TTextSubElement)
- private
- pathEl: TPathElement;
- scale: double;
- protected
- pathName : UTF8String; //name (id) of path element
- procedure GetPathsInternal(el: TBaseElement; const drawDat: TDrawData);
- procedure GetPaths(const drawDat: TDrawData); override;
- function GetBounds: TRectD; override;
- public
- procedure Draw(image: TImage32; drawDat: TDrawData); override;
- end;
- TTextAreaElement = class(TShapeElement)
- protected
- procedure GetPaths(const drawDat: TDrawData); override;
- end;
- TMarkerElement = class(TShapeElement)
- private
- fPoints : TPathD;
- protected
- refPt : TValuePt;
- angle : double;
- angle2 : double;
- markerBoxWH : TRectWH;
- autoStartReverse : Boolean;
- procedure SetEndPoint(const pt: TPointD; angle: double);
- function SetMiddlePoints(const points: TPathD): Boolean;
- procedure Draw(img: TImage32; drawDat: TDrawData); override;
- public
- constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
- end;
- TSvgColorStop = record
- offset : double;
- color : TColor32;
- end;
- TSvgColorStops = array of TSvgColorStop;
- TFillElement = class(TBaseElement)
- protected
- refEl : UTF8String;
- units : Cardinal;
- function GetRelFracLimit: double; override;
- end;
- TPatternElement = class(TFillElement)
- protected
- ImgRenderer : TImageRenderer;
- pattBoxWH : TRectWH;
- function PrepareRenderer(renderer: TImageRenderer;
- drawDat: TDrawData): Boolean; virtual;
- public
- constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
- destructor Destroy; override;
- end;
- //nb: gradients with objectBoundingBox should not be applied to
- //elements without width and height.
- TGradientElement = class(TFillElement)
- protected
- stops : TSvgColorStops;
- spreadMethod : TGradientFillStyle;
- function LoadContent: Boolean; override;
- procedure AddStop(color: TColor32; offset: double);
- procedure AssignTo(other: TBaseElement); virtual;
- function PrepareRenderer(renderer: TCustomGradientRenderer;
- drawDat: TDrawData): Boolean; virtual;
- procedure AddColorStopsToRenderer(renderer: TCustomGradientRenderer);
- end;
- TRadGradElement = class(TGradientElement)
- protected
- radius: TValuePt;
- F, C: TValuePt;
- procedure AssignTo(other: TBaseElement); override;
- function PrepareRenderer(renderer: TCustomGradientRenderer;
- drawDat: TDrawData): Boolean; override;
- public
- constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
- end;
- TLinGradElement = class(TGradientElement)
- protected
- startPt, endPt: TValuePt;
- procedure AssignTo(other: TBaseElement); override;
- function PrepareRenderer(renderer: TCustomGradientRenderer;
- drawDat: TDrawData): Boolean; override;
- public
- constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
- end;
- TGradStopElement = class(TBaseElement)
- protected
- offset: double;
- color: TColor32;
- public
- constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
- end;
- TFilterElement = class(TBaseElement)
- private
- fSrcImg : TImage32;
- fLastImg : TImage32;
- fScale : double;
- fFilterBounds : TRect;
- fObjectBounds : TRect;
- fImages : array of TImage32;
- fNames : array of UTF8String;
- protected
- procedure Clear;
- function GetRelFracLimit: double; override;
- function GetAdjustedBounds(const bounds: TRectD): TRectD;
- function FindNamedImage(const name: UTF8String): TImage32;
- function AddNamedImage(const name: UTF8String): TImage32;
- function GetNamedImage(const name: UTF8String; isIn: Boolean): TImage32;
- procedure Apply(img: TImage32;
- const filterBounds: TRect; const matrix: TMatrixD);
- public
- constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
- destructor Destroy; override;
- end;
- TFeBaseElement = class(TBaseElement)
- private
- function GetParentAsFilterEl: TFilterElement;
- protected
- in1: UTF8String;
- in2: UTF8String;
- res: UTF8String;
- srcImg, dstImg: TImage32;
- srcRec, dstRec: TRect;
- function GetSrcAndDst: Boolean;
- function GetBounds(img: TImage32): TRect;
- procedure Apply; virtual; abstract;
- property ParentFilterEl: TFilterElement read GetParentAsFilterEl;
- end;
- TFeBlendElement = class(TFeBaseElement)
- protected
- procedure Apply; override;
- end;
- TFeImageElement = class(TFeBaseElement)
- private
- refEl: UTF8String;
- fImage: TImage32;
- protected
- procedure Apply; override;
- public
- destructor Destroy; override;
- end;
- TCompositeOp = (coOver, coIn, coOut, coAtop, coXOR, coArithmetic);
- TFeCompositeElement = class(TFeBaseElement)
- protected
- fourKs: TFourDoubles; //arithmetic constants
- compositeOp: TCompositeOp;
- procedure Apply; override;
- public
- constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
- end;
- TFeColorMatrixElement = class(TFeBaseElement)
- protected
- values: TArrayOfDouble;
- procedure Apply; override;
- end;
- TFuncType = (ftIdentity, ftTable, ftDiscrete, ftLinear, ftGamma);
- TFeComponentTransferElement = class(TFeBaseElement)
- protected
- procedure Apply; override;
- end;
- TFeComponentTransferChild = class(TBaseElement)
- protected
- bytes: TArrayOfByte;
- protected
- funcType: TFuncType;
- intercept: double;
- slope: double;
- tableValues: TArrayOfDouble;
- end;
- TFeFuncRElement = class(TFeComponentTransferChild)
- end;
- TFeFuncGElement = class(TFeComponentTransferChild)
- end;
- TFeFuncBElement = class(TFeComponentTransferChild)
- end;
- TFeFuncAElement = class(TFeComponentTransferChild)
- end;
- TFeDefuseLightElement = class(TFeBaseElement)
- protected
- color : TColor32;
- surfaceScale : double;
- diffuseConst : double;
- kernelSize : integer;
- procedure Apply; override;
- end;
- TFeDropShadowElement = class(TFeBaseElement)
- protected
- stdDev : double;
- offset : TValuePt;
- floodColor : TColor32;
- procedure Apply; override;
- public
- constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
- end;
- TFeFloodElement = class(TFeBaseElement)
- protected
- floodColor : TColor32;
- procedure Apply; override;
- public
- constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
- end;
- TFeGaussElement = class(TFeBaseElement)
- protected
- stdDev: double;
- procedure Apply; override;
- public
- constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
- end;
- TFeMergeElement = class(TFeBaseElement)
- protected
- procedure Apply; override;
- end;
- TFeMergeNodeElement = class(TFeBaseElement)
- protected
- procedure Apply; override;
- end;
- TFeOffsetElement = class(TFeBaseElement)
- protected
- offset : TValuePt;
- procedure Apply; override;
- end;
- TFePointLightElement = class(TFeBaseElement)
- protected
- z : double;
- end;
- TFeSpecLightElement = class(TFeBaseElement)
- protected
- exponent : double;
- color : TColor32;
- procedure Apply; override;
- end;
- TClipPathElement = class(TShapeElement)
- protected
- units: Cardinal;
- procedure GetPaths(const drawDat: TDrawData); override;
- public
- constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
- end;
- //-------------------------------------
- const
- buffSize = 32;
- clAlphaSet = $00010101;
- SourceImage : UTF8String = 'SourceGraphic';
- //SourceAlpha : UTF8String = 'SourceAlpha';
- tmpFilterImg : UTF8String = 'tmp';
- //https://www.w3.org/TR/css-fonts-3/#font-family-prop
- emptyDrawInfo: TDrawData =
- (currentColor: clInvalid;
- fillColor: clInvalid; fillOpacity: InvalidD;
- fillRule: frNegative; fillEl: '';
- strokeColor: clInvalid; strokeOpacity: InvalidD;
- strokeWidth: (rawVal: InvalidD; unitType: utNumber);
- strokeCap: esPolygon; strokeJoin: jsMiter; strokeMitLim: 0.0; strokeEl : '';
- dashArray: nil; dashOffset: 0;
- fontInfo: (family: tfUnknown; familyNames: nil; size: 0; spacing: 0.0;
- spacesInText: sitUndefined; textLength: 0; italic: sfsUndefined;
- weight: -1; align: staUndefined; decoration: fdUndefined;
- baseShift: (rawVal: InvalidD; unitType: utNumber));
- markerStart: ''; markerMiddle: ''; markerEnd: '';
- filterElRef: ''; maskElRef: ''; clipElRef: '';
- matrix: ((1, 0, 0),(0, 1, 0),(0, 0, 1)); visible: true;
- useEl: nil; bounds: (Left:0; Top:0; Right:0; Bottom:0));
- var
- //defaultFontHeight: this size will be used to retrieve ALL glyph contours
- //(and later scaled as necessary). This relatively large default ensures
- //that contours will have adequate detail.
- defaultFontHeight: double = 20.0;
- //------------------------------------------------------------------------------
- // Miscellaneous functions ...
- //------------------------------------------------------------------------------
- function HashToElementClass(hash: Cardinal): TElementClass;
- begin
- case hash of
- hClippath : Result := TClipPathElement;
- hCircle : Result := TCircleElement;
- hDefs : Result := TDefsElement;
- hEllipse : Result := TEllipseElement;
- hFilter : Result := TFilterElement;
- hfeBlend : Result := TFeBlendElement;
- hfeColorMatrix : Result := TFeColorMatrixElement;
- hFeComponentTransfer : Result := TFeComponentTransferElement;
- hFeFuncR : Result := TFeFuncRElement;
- hFeFuncG : Result := TFeFuncGElement;
- hFeFuncB : Result := TFeFuncBElement;
- hFeFuncA : Result := TFeFuncAElement;
- hfeComposite : Result := TFeCompositeElement;
- hfeDefuseLighting : Result := TFeDefuseLightElement;
- hfeDropShadow : Result := TFeDropShadowElement;
- hfeFlood : Result := TFeFloodElement;
- hFeGaussianBlur : Result := TFeGaussElement;
- hFeImage : Result := TFeImageElement;
- hfeMerge : Result := TFeMergeElement;
- hfeMergeNode : Result := TFeMergeNodeElement;
- hfeOffset : Result := TFeOffsetElement;
- hfePointLight : Result := TFePointLightElement;
- hfeSpecularLighting : Result := TFeSpecLightElement;
- hG : Result := TGroupElement;
- hImage : Result := TImageElement;
- hLine : Result := TLineElement;
- hLineargradient : Result := TLinGradElement;
- hMarker : Result := TMarkerElement;
- hMask : Result := TMaskElement;
- hPath : Result := TPathElement;
- hPattern : Result := TPatternElement;
- hPolyline : Result := TPolyElement;
- hPolygon : Result := TPolyElement;
- hRadialgradient : Result := TRadGradElement;
- hRect : Result := TRectElement;
- hStop : Result := TGradStopElement;
- hStyle : Result := TStyleElement;
- hSvg : Result := TSvgElement;
- hSwitch : Result := TSwitchElement;
- hSymbol : Result := TSymbolElement;
- hText : Result := TTextElement;
- hTextArea : Result := TTextAreaElement;
- hTextPath : Result := TTextPathElement;
- hTSpan : Result := TTSpanElement;
- hUse : Result := TUseElement;
- else Result := TBaseElement; //use generic class
- end;
- end;
- //------------------------------------------------------------------------------
- procedure UpdateDrawInfo(var drawDat: TDrawData; thisElement: TBaseElement);
- begin
- with thisElement.fDrawData do
- begin
- if currentColor <> clInvalid then
- thisElement.fSvgReader.currentColor := currentColor;
- if fillRule <> frNegative then
- drawDat.fillRule := fillRule;
- if (fillColor = clCurrent) then
- drawDat.fillColor := thisElement.fSvgReader.currentColor
- else if (fillColor <> clInvalid) then
- drawDat.fillColor := fillColor;
- if fillOpacity <> InvalidD then
- drawDat.fillOpacity := fillOpacity;
- if (fillEl <> '') then
- drawDat.fillEl := fillEl;
- if (strokeColor = clCurrent) then
- drawDat.strokeColor := thisElement.fSvgReader.currentColor
- else if strokeColor <> clInvalid then
- drawDat.strokeColor := strokeColor;
- if strokeOpacity <> InvalidD then
- drawDat.strokeOpacity := strokeOpacity;
- if strokeWidth.IsValid then
- drawDat.strokeWidth := strokeWidth;
- if strokeCap = esPolygon then
- drawDat.strokeCap := strokeCap;
- if strokeJoin <> jsMiter then
- drawDat.strokeJoin := strokeJoin;
- if strokeMitLim > 0 then
- drawDat.strokeMitLim := strokeMitLim;
- if Assigned(dashArray) then
- drawDat.dashArray := Copy(dashArray, 0, Length(dashArray));
- if dashOffset <> 0 then
- drawDat.dashOffset := dashOffset;
- if (strokeEl <> '') then
- drawDat.strokeEl := strokeEl;
- if (clipElRef <> '') then
- drawDat.clipElRef := clipElRef;
- if (maskElRef <> '') then
- drawDat.maskElRef := maskElRef;
- if (filterElRef <> '') then
- drawDat.filterElRef := filterElRef;
- if not IsIdentityMatrix(matrix) then
- MatrixMultiply2(matrix, drawDat.matrix);
- end;
- end;
- //------------------------------------------------------------------------------
- procedure UpdateFontInfo(var drawDat: TDrawData; thisElement: TBaseElement);
- begin
- with thisElement.fDrawData do
- begin
- if fontInfo.family <> tfUnknown then
- begin
- drawDat.fontInfo.family := fontInfo.family;
- drawDat.fontInfo.familyNames := nil;
- end;
- if Assigned(fontInfo.familyNames) then
- drawDat.fontInfo.familyNames := fontInfo.familyNames;
- if fontInfo.size > 0 then
- drawDat.fontInfo.size := fontInfo.size;
- if fontInfo.spacing <> 0 then
- drawDat.fontInfo.spacing := fontInfo.spacing;
- if fontInfo.textLength > 0 then
- drawDat.fontInfo.textLength := fontInfo.textLength;
- if (fontInfo.italic <> sfsUndefined) then
- drawDat.fontInfo.italic := fontInfo.italic;
- if (fontInfo.weight <> -1) then
- drawDat.fontInfo.weight := fontInfo.weight;
- if fontInfo.align <> staUndefined then
- drawDat.fontInfo.align := fontInfo.align;
- if fontInfo.spacesInText <> sitUndefined then
- drawDat.fontInfo.spacesInText := fontInfo.spacesInText;
- if (thisElement is TTextElement) or
- (fontInfo.decoration <> fdUndefined) then
- drawDat.fontInfo.decoration := fontInfo.decoration;
- if fontInfo.baseShift.IsValid then
- drawDat.fontInfo.baseShift := fontInfo.baseShift;
- end;
- end;
- //------------------------------------------------------------------------------
- function IsFilled(const drawDat: TDrawData): Boolean;
- begin
- with drawDat do
- Result := (fillOpacity <> 0) and
- ((fillColor <> clNone32) or (fillEl <> ''));
- end;
- //------------------------------------------------------------------------------
- function IsStroked(const drawDat: TDrawData): Boolean;
- begin
- with drawDat do
- if (strokeOpacity = 0) then
- Result := false
- else if (strokeEl = '') and
- ((strokeColor = clNone32) or (strokeColor = clInvalid)) then
- Result := false
- else
- Result := ((strokeWidth.rawVal = InvalidD) or (strokeWidth.rawVal > 0));
- end;
- //------------------------------------------------------------------------------
- function MergeColorAndOpacity(color: TColor32; opacity: double): TColor32;
- begin
- if (opacity < 0) or (opacity >= 1.0) then Result := color or $FF000000
- else if opacity = 0 then Result := clNone32
- else Result := (color and $FFFFFF) + Round(opacity * 255) shl 24;
- end;
- //------------------------------------------------------------------------------
- function UTF8StringToFloat(const ansiValue: UTF8String;
- out value: double): Boolean;
- var
- c: PUTF8Char;
- begin
- c := PUTF8Char(ansiValue);
- Result := ParseNextNum(c, c + Length(ansiValue), false, value);
- end;
- //------------------------------------------------------------------------------
- function UTF8StringToFloatEx(const ansiValue: UTF8String;
- var value: double; out measureUnit: TUnitType): Boolean;
- var
- c: PUTF8Char;
- begin
- c := PUTF8Char(ansiValue);
- Result := ParseNextNumEx(c, c + Length(ansiValue), false, value, measureUnit);
- end;
- //------------------------------------------------------------------------------
- procedure UTF8StringToOpacity(const ansiValue: UTF8String; var color: TColor32);
- var
- opacity: double;
- begin
- if color = clNone32 then
- begin
- color := clAlphaSet;
- Exit;
- end;
- if color = clInvalid then color := clNone32;
- if not UTF8StringToFloat(ansiValue, opacity) then Exit;
- with TARGB(color) do
- if (opacity <= 0) then
- begin
- if Color = clNone32 then Color := clAlphaSet
- else A := 0;
- end
- else if (opacity >= 1) then A := 255
- else A := Round(255 * opacity);
- end;
- //------------------------------------------------------------------------------
- // Note: This MatrixApply() is a function, whereas in Img32.Transform it's a procedure.
- function MatrixApply(const paths: TPathsD; const matrix: TMatrixD): TPathsD; overload;
- var
- i,j,len,len2: integer;
- pp,rr: PPointD;
- begin
- if not Assigned(paths) then
- Result := nil
- else if IsIdentityMatrix(matrix) then
- Result := CopyPaths(paths)
- else
- begin
- len := Length(paths);
- SetLength(Result, len);
- for i := 0 to len -1 do
- begin
- len2 := Length(paths[i]);
- NewPointDArray(Result[i], len2, True);
- if len2 = 0 then Continue;
- pp := @paths[i][0];
- rr := @Result[i][0];
- for j := 0 to High(paths[i]) do
- begin
- rr.X := pp.X * matrix[0, 0] + pp.Y * matrix[1, 0] + matrix[2, 0];
- rr.Y := pp.X * matrix[0, 1] + pp.Y * matrix[1, 1] + matrix[2, 1];
- inc(pp); inc(rr);
- end;
- end;
- end;
- end;
- //------------------------------------------------------------------------------
- function FixSpaces(const text: UnicodeString; trimLeadingSpace: Boolean): UnicodeString;
- var
- i,j, len: integer;
- begin
- //changes \r\n\t chars to spaces
- //and trims consecutive spaces
- len := Length(text);
- SetLength(Result, len);
- if len = 0 then Exit;
- if trimLeadingSpace then
- begin
- i := 1;
- while (i <= len) and (text[i] <= #32) do inc(i);
- if i > len then
- begin
- Result := '';
- Exit;
- end;
- Result[1] := text[i];
- inc(i);
- end else
- begin
- // allow a single leading space char
- if text[1] <= #32 then
- Result[1] := #32
- else
- Result[1] := text[1];
- i := 2;
- end;
- j := 1;
- for i := i to len do
- begin
- if (text[i] <= #32) then
- begin
- if (Result[j] = #32) then Continue;
- inc(j);
- Result[j] := #32;
- end else
- begin
- inc(j);
- Result[j] := text[i];
- end;
- end;
- SetLength(Result, j);
- end;
- //------------------------------------------------------------------------------
- function IsBlankText(const text: UnicodeString): Boolean;
- var
- i: integer;
- begin
- Result := false;
- for i := 1 to Length(text) do
- if (text[i] > #32) and (text[i] <> #160) then Exit;
- Result := true;
- end;
- //------------------------------------------------------------------------------
- function SvgTextAlignToTextAlign(svgAlign: TSvgTextAlign): TTextAlign;
- begin
- case svgAlign of
- staCenter: Result := taCenter;
- staRight: Result := taRight;
- staJustify: Result := taJustify;
- else Result := taLeft;
- end;
- end;
- //------------------------------------------------------------------------------
- // TSvgIdNameHashMap
- //------------------------------------------------------------------------------
- procedure TSvgIdNameHashMap.Grow;
- var
- Len, I: Integer;
- Index: Integer;
- begin
- Len := Length(FItems);
- if Len < 5 then
- Len := 5
- else
- Len := Len * 2;
- SetLength(FItems, Len);
- FMod := Cardinal(Len);
- if not Odd(FMod) then
- Inc(FMod);
- SetLengthUninit(FBuckets, FMod);
- FillChar(FBuckets[0], FMod * SizeOf(FBuckets[0]), $FF);
- // Rehash
- for I := 0 to FCount - 1 do
- begin
- Index := (FItems[I].Hash and $7FFFFFFF) mod FMod;
- FItems[I].Next := FBuckets[Index];
- FBuckets[Index] := I;
- end;
- end;
- //------------------------------------------------------------------------------
- function TSvgIdNameHashMap.FindItemIndex(const Name: UTF8String): Integer;
- var
- hash: Cardinal;
- begin
- Result := -1;
- if FMod = 0 then Exit;
- Hash := GetHash(Name);
- Result := FBuckets[(Hash and $7FFFFFFF) mod FMod];
- while (Result <> -1) and
- ((FItems[Result].Hash <> Hash) or
- not IsSameUTF8String(FItems[Result].Name, Name)) do
- Result := FItems[Result].Next;
- end;
- //------------------------------------------------------------------------------
- procedure TSvgIdNameHashMap.AddOrIgnore(const idName: UTF8String; element: TBaseElement);
- var
- Index: Integer;
- Hash: Cardinal;
- Item: PSvgIdNameHashMapItem;
- Bucket: PInteger;
- begin
- Index := FindItemIndex(idName);
- if Index >= 0 then
- Exit; // already exists so ignore;
- // add new item
- if FCount = Length(FItems) then Grow;
- Index := FCount;
- Inc(FCount);
- Hash := GetHash(idName);
- Bucket := @FBuckets[(Hash and $7FFFFFFF) mod FMod];
- Item := @FItems[Index];
- Item.Next := Bucket^;
- Item.Hash := Hash;
- Item.Name := idName;
- Item.Element := element;
- Bucket^ := Index;
- end;
- //------------------------------------------------------------------------------
- function TSvgIdNameHashMap.FindElement(const idName: UTF8String): TBaseElement;
- var
- Index: Integer;
- begin
- if FCount = 0 then
- Result := nil
- else
- begin
- Index := FindItemIndex(idName);
- if Index < 0 then
- Result := nil else
- Result := FItems[Index].Element
- end;
- end;
- //------------------------------------------------------------------------------
- procedure TSvgIdNameHashMap.Clear;
- begin
- FCount := 0;
- FMod := 0;
- FItems := nil;
- FBuckets := nil;
- end;
- //------------------------------------------------------------------------------
- // TDefsElement
- //------------------------------------------------------------------------------
- constructor TDefsElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
- begin
- inherited;
- fDrawData.visible := false;
- end;
- //------------------------------------------------------------------------------
- // TStyleElement
- //------------------------------------------------------------------------------
- constructor TStyleElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
- begin
- inherited;
- fDrawData.visible := false;
- // See ParseStyleElementContent in Img32.Core.
- end;
- //------------------------------------------------------------------------------
- // TImageElement
- //------------------------------------------------------------------------------
- function TrimAnySpaces(const s: UTF8String): UTF8String;
- var
- i, j, len: integer;
- dst: PUTF8Char;
- begin
- len := Length(s);
- SetLength(Result, len);
- dst := PUTF8Char(Pointer(Result));
- j := 0;
- for i := 1 to len do
- if s[i] > #32 then
- begin
- dst[j] := s[i];
- inc(j);
- end;
- if j <> len then
- SetLength(Result, j);
- end;
- //------------------------------------------------------------------------------
- procedure ReadRefElImage(const refEl: UTF8String; out img: TImage32);
- var
- len, offset: integer;
- s: UTF8String;
- ms: TMemoryStream;
- c: PUTF8Char;
- begin
- img := nil;
- // unfortunately white spaces are sometimes found inside encoded base64
- s := TrimAnySpaces(refEl);
- len := Length(s);
- // currently only accepts **embedded** images
- if (len = 0) then Exit;
- c := PUTF8Char(s);
- if not Match(c, 'data:image/') then Exit;
- if Match(@c[11], 'jpg;base64,') then offset := 22
- else if Match(@c[11], 'jpeg;base64,') then offset := 23
- else if Match(@c[11], 'png;base64,') then offset := 22
- else Exit;
- ms := TMemoryStream.Create;
- try
- if not Base64Decode(@c[offset], len -offset, ms) then Exit;
- img := TImage32.Create;
- if not img.LoadFromStream(ms) then
- begin
- FreeAndNil(img);
- Exit;
- end;
- finally
- ms.Free;
- end;
- end;
- //------------------------------------------------------------------------------
- // TImageElement
- //------------------------------------------------------------------------------
- destructor TImageElement.Destroy;
- begin
- if Assigned(fImage) then fImage.Free;
- inherited Destroy;
- end;
- //------------------------------------------------------------------------------
- procedure TImageElement.Draw(image: TImage32; drawDat: TDrawData);
- var
- dstRecD: TRectD;
- tmp: TImage32;
- tmpScale: TPointD;
- begin
- dstRecD := Self.elRectWH.GetRectD(0,0);
- MatrixMultiply2(fDrawData.matrix, drawDat.matrix);
- MatrixApply(drawDat.matrix, dstRecD);
- if (fRefEl <> '') and not Assigned(fImage) then
- begin
- ReadRefElImage(fRefEl, fImage);
- if Assigned(fImage) then
- begin
- fRefEl := ''; // ie avoid reloading fImage
- fTransparent := fImage.HasTransparency;
- end;
- end;
- if fImage <> nil then
- begin
- if elRectWH.IsValid then
- begin
- tmpScale.X := elRectWH.width.rawVal / fImage.Width;
- tmpScale.Y := elRectWH.Height.rawVal / fImage.Height;
- MatrixScale(drawDat.matrix, tmpScale.X, tmpScale.Y);
- end;
- tmp := TImage32.Create();
- try
- tmp.AssignSettings(fImage);
- MatrixApply(drawDat.matrix, fImage, tmp);
- // CopyBlend is slower than Copy, so only use it if we have a
- // transparent image.
- if fTransparent then
- image.CopyBlend(tmp, tmp.Bounds, Rect(dstRecD), BlendToAlphaLine)
- else
- image.Copy(tmp, tmp.Bounds, Rect(dstRecD));
- finally
- tmp.Free;
- end;
- end;
- end;
- //------------------------------------------------------------------------------
- // TGroupElement
- //------------------------------------------------------------------------------
- procedure TGroupElement.Draw(image: TImage32; drawDat: TDrawData);
- var
- clipEl : TClipPathElement;
- maskEl : TMaskElement;
- tmpImg : TImage32;
- clipPaths : TPathsD;
- clipRec : TRect;
- dstClipRec: TRect;
- offsetX, offsetY: integer;
- fr: TFillRule;
- begin
- if fChilds.Count = 0 then Exit;
- UpdateDrawInfo(drawDat, self);
- UpdateFontInfo(drawDat, self);
- if drawDat.fillRule = frNegative then
- drawDat.fillRule := frNonZero;
- maskEl := TMaskElement(FindRefElement(drawDat.maskElRef));
- clipEl := TClipPathElement(FindRefElement(drawDat.clipElRef));
- if Assigned(clipEl) then
- begin
- drawDat.clipElRef := '';
- with clipEl do
- begin
- GetPaths(drawDat);
- clipPaths := CopyPaths(drawPathsC);
- AppendPath(clipPaths, drawPathsO);
- MatrixApply(drawDat.matrix, clipPaths);
- clipRec := Img32.Vector.GetBounds(clipPaths);
- end;
- if IsEmptyRect(clipRec) then Exit;
- dstClipRec := clipRec; // save for blending tmpImg to image
- // Translate the clipPaths, clipRec and matrix
- // to minimize the size of the mask image.
- offsetX := clipRec.Left;
- offsetY := clipRec.Top;
- if offsetX < 0 then offsetX := 0;
- if offsetY < 0 then offsetY := 0;
- if (offsetX > 0) or (offsetY > 0) then
- begin
- MatrixTranslate(drawDat.matrix, -offsetX, -offsetY); // for DrawChildren
- clipPaths := TranslatePath(clipPaths, -offsetX, -offsetY);
- TranslateRect(clipRec, -offsetX, -offsetY);
- end;
- //nb: it's not safe to use fReader.TempImage when calling DrawChildren
- tmpImg := TImage32.Create(Min(image.Width, clipRec.Right), Min(image.Height, clipRec.Bottom));
- try
- DrawChildren(tmpImg, drawDat);
- if clipEl.fDrawData.fillRule = frNegative then
- fr := frNonZero else
- fr := clipEl.fDrawData.fillRule;
- EraseOutsidePaths(tmpImg, clipPaths, fr, clipRec, fSvgReader.fCustomRendererCache);
- image.CopyBlend(tmpImg, clipRec, dstClipRec, BlendToAlphaLine);
- finally
- tmpImg.Free;
- end;
- end
- else if Assigned(maskEl) then
- begin
- drawDat.maskElRef := '';
- with maskEl do
- begin
- GetPaths(drawDat);
- clipRec := maskRec;
- end;
- // Translate the maskRec, the matix and the clipRec to minimize
- // the size of the mask image.
- dstClipRec := clipRec; // save for blending tmpImg to image
- offsetX := -clipRec.Left;
- offsetY := -clipRec.Top;
- if offsetX > 0 then offsetX := 0;
- if offsetY > 0 then offsetY := 0;
- if (offsetX < 0) or (offsetY < 0) then
- begin
- MatrixTranslate(drawDat.matrix, offsetX, offsetY); // for DrawChildren
- TranslateRect(clipRec, offsetX, offsetY);
- TranslateRect(maskEl.maskRec, offsetX, offsetY);
- end;
- tmpImg := TImage32.Create(Min(image.Width, clipRec.Right), Min(image.Height, clipRec.Bottom));
- try
- DrawChildren(tmpImg, drawDat);
- TMaskElement(maskEl).ApplyMask(tmpImg, drawDat);
- image.CopyBlend(tmpImg, clipRec, dstClipRec, BlendToAlphaLine);
- finally
- tmpImg.Free;
- end;
- end else
- DrawChildren(image, drawDat);
- end;
- //------------------------------------------------------------------------------
- // TSwitchElement
- //------------------------------------------------------------------------------
- procedure TSwitchElement.Draw(image: TImage32; drawDat: TDrawData);
- var
- i: integer;
- begin
- for i := 0 to fChilds.Count -1 do
- if TBaseElement(fChilds[i]) is TShapeElement then
- with TShapeElement(fChilds[i]) do
- if fDrawData.visible then
- begin
- Draw(image, drawDat);
- break; //break after the first successful drawing
- end;
- end;
- //------------------------------------------------------------------------------
- // TUseElement
- //------------------------------------------------------------------------------
- procedure TUseElement.GetPaths(const drawDat: TDrawData);
- var
- el: TBaseElement;
- dx, dy: double;
- begin
- if pathsLoaded or (fRefEl = '') then Exit;
- el := FindRefElement(fRefEl);
- if not Assigned(el) or not (el is TShapeElement) then Exit;
- pathsLoaded := true;
- with TShapeElement(el) do
- begin
- GetPaths(drawDat);
- self.drawPathsC := CopyPaths(drawPathsC);
- self.drawPathsO := CopyPaths(drawPathsO);
- end;
- if elRectWH.left.IsValid then
- dx := elRectWH.left.rawVal else
- dx := 0;
- if elRectWH.top.IsValid then
- dy := elRectWH.top.rawVal else
- dy := 0;
- if (dx <> 0) or (dy <> 0) then
- begin
- drawPathsC := TranslatePath(drawPathsC, dx, dy);
- drawPathsO := TranslatePath(drawPathsO, dx, dy);
- end;
- end;
- //------------------------------------------------------------------------------
- function TUseElement.ValidateNonRecursion(el: TBaseElement): Boolean;
- begin
- Result := false;
- while assigned(el) do
- begin
- if (el = Self) then Exit;
- if not (el is TUseElement) then break; //shouldn't happen
- el := TUseElement(el).callerUse;
- end;
- Result := true;
- end;
- //------------------------------------------------------------------------------
- procedure TUseElement.Draw(img: TImage32; drawDat: TDrawData);
- var
- el: TBaseElement;
- s, dx, dy: double;
- scale, scale2: TPointD;
- mat: TMatrixD;
- begin
- //make sure there's not recursion, either directly or indirectly
- if not ValidateNonRecursion(drawDat.useEl) then Exit;
- callerUse := drawDat.useEl;
- drawDat.useEl := self;
- el := FindRefElement(fRefEl);
- if not Assigned(el) then Exit;
- UpdateDrawInfo(drawDat, self); //nb: <use> attribs override el's.
- MatrixExtractScale(drawDat.matrix, scale.X, scale.Y);
- if elRectWH.left.IsValid then dx := elRectWH.left.rawVal else dx := 0;
- if elRectWH.top.IsValid then dy := elRectWH.top.rawVal else dy := 0;
- if (dx <> 0) or (dy <> 0) then
- begin
- mat := IdentityMatrix;
- MatrixTranslate(mat, dx, dy);
- MatrixMultiply2(mat, drawDat.matrix);
- end;
- if el is TSymbolElement then
- begin
- with TSymbolElement(el) do
- begin
- if not viewboxWH.IsEmpty then
- begin
- //scale the symbol according to its width and height attributes
- if elRectWH.width.IsValid and elRectWH.height.IsValid then
- begin
- scale2.X := elRectWH.width.rawVal / viewboxWH.Width;
- scale2.Y := elRectWH.height.rawVal / viewboxWH.Height;
- if scale2.Y < scale2.X then s := scale2.Y else s := scale2.X;
- //the following 3 lines will scale without translating
- mat := IdentityMatrix;
- MatrixScale(mat, s, s);
- MatrixMultiply2(mat, drawDat.matrix);
- drawDat.bounds := RectD(0,0,viewboxWH.Width, viewboxWH.Height);
- end;
- if self.elRectWH.width.IsValid and
- self.elRectWH.height.IsValid then
- begin
- with viewboxWH do
- begin
- dx := -Left/Width * self.elRectWH.width.rawVal;
- dy := -Top/Height * self.elRectWH.height.rawVal;
- //scale <symbol> proportionally to fill the <use> element
- scale2.X := self.elRectWH.width.rawVal / Width;
- scale2.Y := self.elRectWH.height.rawVal / Height;
- if scale2.Y < scale2.X then s := scale2.Y else s := scale2.X;
- end;
- mat := IdentityMatrix;
- MatrixScale(mat, s, s);
- MatrixTranslate(mat, dx, dy);
- MatrixMultiply2(mat, drawDat.matrix);
- //now center after scaling
- if scale2.X > scale2.Y then
- begin
- if scale2.X > 1 then
- begin
- s := (self.elRectWH.width.rawVal - viewboxWH.Width) * 0.5;
- MatrixTranslate(drawDat.matrix, s * scale.X, 0);
- end;
- end else if scale2.Y > 1 then
- begin
- s := (self.elRectWH.height.rawVal - viewboxWH.Height) * 0.5;
- MatrixTranslate(drawDat.matrix, 0, s * scale.Y);
- end;
- end;
- end;
- DrawChildren(img, drawDat);
- end;
- end
- else if el is TImageElement then
- el.Draw(img, drawDat)
- else if el is TShapeElement then
- el.Draw(img, drawDat);
- end;
- //------------------------------------------------------------------------------
- // TMaskElement
- //------------------------------------------------------------------------------
- procedure TMaskElement.GetPaths(const drawDat: TDrawData);
- var
- i : integer;
- el : TShapeElement;
- begin
- maskRec := NullRect;
- for i := 0 to fChilds.Count -1 do
- if TBaseElement(fChilds[i]) is TShapeElement then
- begin
- el := TShapeElement(fChilds[i]);
- el.GetPaths(drawDat);
- maskRec := Img32.Vector.UnionRect(maskRec, Img32.Vector.GetBounds(el.drawPathsC));
- Img32.Vector.UnionRect(maskRec, Img32.Vector.GetBounds(el.drawPathsO));
- end;
- MatrixApply(drawDat.matrix, maskRec);
- end;
- //------------------------------------------------------------------------------
- procedure TMaskElement.ApplyMask(img: TImage32; const drawDat: TDrawData);
- var
- tmpImg: TImage32;
- begin
- tmpImg := TImage32.Create(Min(img.Width, maskRec.Right), Min(img.Height, maskRec.Bottom));
- try
- DrawChildren(tmpImg, drawDat);
- img.CopyBlend(tmpImg, maskRec, maskRec, BlendBlueChannelLine);
- finally
- tmpImg.Free;
- end;
- end;
- //------------------------------------------------------------------------------
- // TSymbolElement
- //------------------------------------------------------------------------------
- constructor TSymbolElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
- begin
- inherited;
- fDrawData.visible := false;
- end;
- //------------------------------------------------------------------------------
- // TGradElement
- //------------------------------------------------------------------------------
- function TGradientElement.LoadContent: Boolean;
- var
- i: integer;
- begin
- Result := inherited LoadContent;
- for i := 0 to fChilds.Count -1 do
- if TBaseElement(fChilds[i]) is TGradStopElement then
- with TGradStopElement(fChilds[i]) do
- AddStop(color, offset);
- end;
- //------------------------------------------------------------------------------
- procedure TGradientElement.AddStop(color: TColor32; offset: double);
- var
- len: integer;
- begin
- //if a stop is less than previous stops, it is set equal to the largest stop.
- //If two stops are equal the last stop controls the color from that point.
- len := Length(stops);
- if (len > 0) and (stops[len-1].offset > offset) then
- offset := stops[len-1].offset;
- setLength(stops, len+1);
- stops[len].offset := Min(1,Max(0, offset));
- stops[len].color := color;
- end;
- //------------------------------------------------------------------------------
- procedure TGradientElement.AssignTo(other: TBaseElement);
- var
- i, len: integer;
- begin
- if not Assigned(other) or not (other is TGradientElement) then Exit;
- inherited;
- with TGradientElement(other) do
- begin
- if units = 0 then
- units := Self.units;
- if Length(stops) = 0 then
- begin
- len := Length(self.stops);
- SetLength(stops, len);
- for i := 0 to len -1 do
- stops[i] := Self.stops[i];
- end;
- if IsIdentityMatrix(fDrawData.matrix) then
- fDrawData.matrix := self.fDrawData.matrix;
- end;
- end;
- //------------------------------------------------------------------------------
- function TGradientElement.PrepareRenderer(
- renderer: TCustomGradientRenderer; drawDat: TDrawData): Boolean;
- var
- el: TBaseElement;
- begin
- if (refEl <> '') then
- begin
- el := FindRefElement(refEl);
- if Assigned(el) and (el is TGradientElement) then
- TGradientElement(el).AssignTo(self);
- end;
- Result := Length(stops) > 0;
- end;
- //------------------------------------------------------------------------------
- procedure TGradientElement.AddColorStopsToRenderer(renderer: TCustomGradientRenderer);
- var
- i, hiStops: Integer;
- begin
- hiStops := High(stops);
- if (hiStops = 0) or (renderer = nil) then Exit;
- // If vector boundary-stops are implicit, then boundary
- // and adjacent inner stop (explicit) should have the
- // same color
- if stops[0].offset > 0 then
- with stops[0] do
- renderer.InsertColorStop(offset, color);
- for i := 1 to hiStops -1 do
- with stops[i] do
- renderer.InsertColorStop(offset, color);
- // If vector boundary-stops are implicit, then boundary
- // and adjacent inner stop (explicit) should have the
- // same color
- if stops[hiStops].offset < 1 then
- with stops[hiStops] do
- renderer.InsertColorStop(offset, color);
- end;
- //------------------------------------------------------------------------------
- // TRadGradElement
- //------------------------------------------------------------------------------
- constructor TRadGradElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
- begin
- inherited;
- radius.Init;
- F.Init;
- C.Init;
- end;
- //------------------------------------------------------------------------------
- procedure TRadGradElement.AssignTo(other: TBaseElement);
- begin
- if not Assigned(other) or not (other is TGradientElement) then Exit;
- inherited;
- if other is TRadGradElement then
- with TRadGradElement(other) do
- begin
- if not radius.IsValid then radius := self.radius;
- if not C.IsValid then C := self.C;
- if not F.IsValid then F := self.F;
- end;
- end;
- //------------------------------------------------------------------------------
- function TRadGradElement.PrepareRenderer(renderer: TCustomGradientRenderer;
- drawDat: TDrawData): Boolean;
- var
- hiStops: integer;
- cp, fp, r: TPointD;
- scale, scale2: TPointD;
- rec2, rec3: TRectD;
- begin
- inherited PrepareRenderer(renderer, drawDat);
- hiStops := High(stops);
- Result := hiStops >= 0;
- if not Result then Exit;
- if units = hUserSpaceOnUse then
- rec2 := fSvgReader.userSpaceBounds else
- rec2 := drawDat.bounds;
- if radius.IsValid then
- begin
- if radius.X.HasFontUnits then
- r := radius.GetPoint(drawDat.fontInfo.size, GetRelFracLimit) else
- r := radius.GetPoint(rec2, GetRelFracLimit);
- end else
- begin
- r.X := rec2.Width * 0.5;
- r.Y := rec2.Height * 0.5;
- end;
- MatrixExtractScale(drawDat.matrix, scale.X, scale.Y);
- MatrixExtractScale(fDrawData.matrix, scale2.X, scale2.Y);
- r := ScalePoint(r, scale.X * scale2.X, scale.Y * scale2.Y);
- if C.IsValid then
- begin
- if C.X.HasFontUnits then
- cp := C.GetPoint(drawDat.fontInfo.size, GetRelFracLimit) else
- cp := C.GetPoint(rec2, GetRelFracLimit);
- cp := TranslatePoint(cp, rec2.Left, rec2.Top);
- end else
- cp := rec2.MidPoint;
- MatrixApply(fDrawData.matrix, cp);
- MatrixApply(drawDat.matrix, cp);
- rec3 := RectD(cp.X-r.X, cp.Y-r.Y, cp.X+r.X, cp.Y+r.Y);
- if F.IsValid then
- begin
- if F.X.HasFontUnits then
- fp := F.GetPoint(drawDat.fontInfo.size, GetRelFracLimit) else
- fp := F.GetPoint(rec2, GetRelFracLimit);
- fp := TranslatePoint(fp, rec2.Left, rec2.Top);
- MatrixApply(fDrawData.matrix, fp);
- MatrixApply(drawDat.matrix, fp);
- end else
- fp := MidPoint(rec3);
- with renderer as TSvgRadialGradientRenderer do
- begin
- SetParameters(Rect(rec3), Point(fp),
- stops[0].color, stops[hiStops].color, spreadMethod);
- AddColorStopsToRenderer(renderer);
- end;
- end;
- //------------------------------------------------------------------------------
- // TLinGradElement
- //------------------------------------------------------------------------------
- constructor TLinGradElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
- begin
- inherited;
- startPt.Init;
- endPt.Init;
- end;
- //------------------------------------------------------------------------------
- procedure TLinGradElement.AssignTo(other: TBaseElement);
- begin
- if not Assigned(other) or not (other is TGradientElement) then Exit;
- inherited;
- if other is TLinGradElement then
- with TLinGradElement(other) do
- begin
- if not startPt.IsValid then startPt := self.startPt;
- if not endPt.IsValid then endPt := self.endPt;
- end;
- end;
- //------------------------------------------------------------------------------
- function TLinGradElement.PrepareRenderer(
- renderer: TCustomGradientRenderer; drawDat: TDrawData): Boolean;
- var
- pt1, pt2: TPointD;
- hiStops: integer;
- rec2: TRectD;
- begin
- inherited PrepareRenderer(renderer, drawDat);
- hiStops := High(stops);
- Result := (hiStops >= 0);
- if not Result then Exit;
- //w3c-coords-units-01-b.svg
- //if gradientUnits=objectBoundingBox (default) then all values must be
- //percentages. Also... when the object's bounding box is not square, the
- //gradient may render non-perpendicular relative to the gradient vector
- //unless the gradient vector is vertical or horizontal.
- //https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientUnits
- if units = hUserSpaceOnUse then
- rec2 := fSvgReader.userSpaceBounds else
- rec2 := drawDat.bounds;
- with TLinearGradientRenderer(renderer) do
- begin
- if startPt.X.HasFontUnits then
- pt1 := startPt.GetPoint(drawDat.fontInfo.size, GetRelFracLimit) else
- pt1 := startPt.GetPoint(rec2, GetRelFracLimit);
- if (startPt.X.unitType <> utPixel) or
- (units <> hUserSpaceOnUse) then
- pt1.X := pt1.X + rec2.Left;
- if (startPt.Y.unitType <> utPixel) or
- (units <> hUserSpaceOnUse) then
- pt1.Y := pt1.Y + rec2.Top;
- MatrixApply(fDrawData.matrix, pt1);
- MatrixApply(drawDat.matrix, pt1);
- if not endPt.X.IsValid then
- pt2.X := rec2.Width else
- pt2.X := endPt.X.GetValue(rec2.Width, GetRelFracLimit);
- pt2.Y := endPt.Y.GetValue(rec2.Height, GetRelFracLimit);
- pt2 := TranslatePoint(pt2, rec2.Left, rec2.Top);
- MatrixApply(fDrawData.matrix, pt2);
- MatrixApply(drawDat.matrix, pt2);
- if (units <> hUserSpaceOnUse) and
- ((pt2.X <> pt1.X) or (pt2.Y <> pt1.Y)) then
- begin
- //skew the gradient
- end;
- SetParameters(pt1, pt2, stops[0].color,
- stops[hiStops].color, spreadMethod);
- AddColorStopsToRenderer(renderer);
- end;
- end;
- //------------------------------------------------------------------------------
- // TGradStopElement
- //------------------------------------------------------------------------------
- constructor TGradStopElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
- begin
- inherited;
- color := clBlack32;
- end;
- //------------------------------------------------------------------------------
- // TFilterElement
- //------------------------------------------------------------------------------
- constructor TFilterElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
- begin
- inherited;
- fDrawData.visible := false;
- elRectWH.Init;
- end;
- //------------------------------------------------------------------------------
- destructor TFilterElement.Destroy;
- begin
- Clear;
- inherited;
- end;
- //------------------------------------------------------------------------------
- procedure TFilterElement.Clear;
- var
- i: integer;
- begin
- for i := 0 to High(fImages) do
- fImages[i].Free;
- fImages := nil;
- fNames := nil;
- fLastImg := nil;
- end;
- //------------------------------------------------------------------------------
- function TFilterElement.GetRelFracLimit: double;
- begin
- // assume fractional values below 2.5 are always relative
- Result := 2.5;
- end;
- //------------------------------------------------------------------------------
- function TFilterElement.GetAdjustedBounds(const bounds: TRectD): TRectD;
- var
- recWH: TRectWH;
- delta: TSizeD;
- d: double;
- pt: TPointD;
- i: integer;
- hasOffset: Boolean;
- begin
- fObjectBounds := Rect(bounds);
- if elRectWH.IsValid then
- begin
- recWH := elRectWH.GetRectWH(bounds, GetRelFracLimit);
- Result.Left := bounds.Left + recWH.Left;
- Result.Top := bounds.Top + recWH.Top;
- Result.Right := Result.Left + recWH.Width;
- Result.Bottom := Result.Top + recWH.Height;
- end else
- begin
- Result := bounds;
- //when the filter's width and height are undefined then limit the filter
- //margin to 20% of the bounds when just blurring, not also offsetting.
- hasOffset := false;
- delta.cx := 0; delta.cy := 0;
- for i := 0 to ChildCount -1 do
- begin
- if Child[i] is TFeGaussElement then
- begin
- d := TFeGaussElement(Child[i]).stdDev * 3 * fScale;
- delta.cx := delta.cx + d;
- delta.cy := delta.cy + d;
- end
- else if Child[i] is TFeDropShadowElement then
- with TFeDropShadowElement(Child[i]) do
- begin
- d := stdDev * 0.75 * fScale;
- pt := offset.GetPoint(bounds, 1);
- delta.cx := delta.cx + d + Abs(pt.X) * fScale;
- delta.cy := delta.cy + d + Abs(pt.Y) * fScale;
- hasOffset := true;
- end
- else if Child[i] is TFeOffsetElement then
- with TFeOffsetElement(Child[i]) do
- begin
- pt := offset.GetPoint(bounds, 1);
- delta.cx := delta.cx + Abs(pt.X) * fScale;
- delta.cy := delta.cy + Abs(pt.Y) * fScale;
- hasOffset := true;
- end;
- end;
- if (delta.cx = InvalidD) or (delta.cy = InvalidD) then Exit;
- //limit the filter margin to 20% if only blurring
- if not hasOffset then
- with delta, bounds do
- begin
- if cx > Width * 0.2 then cx := Width * 0.2;
- if cy > Height * 0.2 then cy := Height * 0.2;
- end;
- Img32.Vector.InflateRect(Result, delta.cx, delta.cy);
- end;
- end;
- //------------------------------------------------------------------------------
- function TFilterElement.FindNamedImage(const name: UTF8String): TImage32;
- var
- i, len: integer;
- begin
- Result := nil;
- len := Length(fNames);
- for i := 0 to len -1 do
- if name = fNames[i] then
- begin
- Result := fImages[i];
- Break;
- end;
- end;
- //------------------------------------------------------------------------------
- function TFilterElement.AddNamedImage(const name: UTF8String): TImage32;
- var
- len, w, h: integer;
- begin
- len := Length(fNames);
- SetLength(fNames, len+1);
- SetLength(fImages, len+1);
- RectWidthHeight(fFilterBounds, w, h);
- Result := TImage32.Create(w, h);
- fImages[len] := Result;
- fNames[len] := name;
- end;
- //------------------------------------------------------------------------------
- function TFilterElement.GetNamedImage(const name: UTF8String; isIn: Boolean): TImage32;
- begin
- Result := FindNamedImage(name);
- if not Assigned(Result) then
- Result := AddNamedImage(name)
- else if not isIn then
- Exit;
- case GetHash(name) of
- hBackgroundImage:
- Result.Copy(fSvgReader.BackgndImage, fFilterBounds, Result.Bounds);
- hBackgroundAlpha:
- begin
- Result.Copy(fSvgReader.BackgndImage, fFilterBounds, Result.Bounds);
- Result.SetRGB(clNone32, Result.Bounds);
- end;
- hSourceGraphic:
- Result.Copy(fSrcImg, fFilterBounds, Result.Bounds);
- hSourceAlpha:
- begin
- Result.Copy(fSrcImg, fFilterBounds, Result.Bounds);
- Result.SetRGB(clBlack32, Result.Bounds);
- end;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure TFilterElement.Apply(img: TImage32;
- const filterBounds: TRect; const matrix: TMatrixD);
- var
- i: integer;
- begin
- MatrixExtractScale(matrix, fScale);
- fFilterBounds := filterBounds;
- Types.IntersectRect(fObjectBounds, fObjectBounds, img.Bounds);
- fSrcImg := img;
- try
- for i := 0 to fChilds.Count -1 do
- begin
- case TBaseElement(fChilds[i]).fXmlEl.hash of
- hfeBlend : TFeBlendElement(fChilds[i]).Apply;
- hfeColorMatrix : TFeColorMatrixElement(fChilds[i]).Apply;
- hFeComponentTransfer : TFeComponentTransferElement(fChilds[i]).Apply;
- hfeComposite : TFeCompositeElement(fChilds[i]).Apply;
- hfeDefuseLighting : TFeDefuseLightElement(fChilds[i]).Apply;
- hfeDropShadow : TFeDropShadowElement(fChilds[i]).Apply;
- hfeFlood : TFeFloodElement(fChilds[i]).Apply;
- hfeImage : TFeImageElement(fChilds[i]).Apply;
- hFeGaussianBlur : TFeGaussElement(fChilds[i]).Apply;
- hfeMerge : TFeMergeElement(fChilds[i]).Apply;
- hfeOffset : TFeOffsetElement(fChilds[i]).Apply;
- hfeSpecularLighting : TFeSpecLightElement(fChilds[i]).Apply;
- end;
- end;
- if Assigned(fLastImg) then
- fSrcImg.Copy(fLastImg, fLastImg.Bounds, fFilterBounds);
- finally
- Clear;
- end;
- end;
- //------------------------------------------------------------------------------
- // TFeBaseElement
- //------------------------------------------------------------------------------
- function TFeBaseElement.GetParentAsFilterEl: TFilterElement;
- var
- el: TBaseElement;
- begin
- el := fParent;
- while Assigned(el) and not (el is TFilterElement) do
- el := el.fParent;
- if not Assigned(el) then
- Result := nil else
- Result := TFilterElement(el);
- end;
- //------------------------------------------------------------------------------
- function TFeBaseElement.GetBounds(img: TImage32): TRect;
- var
- pfe: TFilterElement;
- begin
- pfe := ParentFilterEl;
- if img = pfe.fSrcImg then
- Result := pfe.fFilterBounds else
- Result := img.Bounds;
- end;
- //------------------------------------------------------------------------------
- function TFeBaseElement.GetSrcAndDst: Boolean;
- var
- pfe: TFilterElement;
- begin
- pfe := ParentFilterEl;
- if (in1 <> '') then
- srcImg := pfe.GetNamedImage(in1, true)
- else if Assigned(pfe.fLastImg) then
- srcImg := pfe.fLastImg
- else
- srcImg := pfe.GetNamedImage(SourceImage, false);
- if (res <> '') then
- dstImg := pfe.GetNamedImage(res, false) else
- dstImg := pfe.GetNamedImage(SourceImage, false);
- Result := Assigned(srcImg) and Assigned(dstImg);
- if not Result then Exit;
- pfe.fLastImg := dstImg;
- srcRec := GetBounds(srcImg);
- dstRec := GetBounds(dstImg);
- end;
- //------------------------------------------------------------------------------
- // TFeBlendElement
- //------------------------------------------------------------------------------
- procedure TFeBlendElement.Apply;
- var
- pfe: TFilterElement;
- srcImg2, dstImg2: TImage32;
- srcRec2, dstRec2: TRect;
- begin
- if not GetSrcAndDst then Exit;
- pfe := ParentFilterEl;
- if (in2 = '') then Exit;
- if dstImg = srcImg then
- dstImg2 := pfe.AddNamedImage(tmpFilterImg) else
- dstImg2 := dstImg;
- dstRec2 := GetBounds(dstImg2);
- srcImg2 := pfe.GetNamedImage(in2, true);
- srcRec2 := GetBounds(srcImg2);
- dstImg2.CopyBlend(srcImg2, srcRec2, dstRec2, BlendToAlphaLine);
- dstImg2.CopyBlend(srcImg, srcRec, dstRec2, BlendToAlphaLine);
- if dstImg = srcImg then
- dstImg.Copy(dstImg2, dstRec2, dstRec);
- end;
- //------------------------------------------------------------------------------
- // TFeImageElement
- //------------------------------------------------------------------------------
- destructor TFeImageElement.Destroy;
- begin
- fImage.Free;
- inherited Destroy;
- end;
- //------------------------------------------------------------------------------
- procedure TFeImageElement.Apply;
- begin
- if GetSrcAndDst then
- begin
- if refEl <> '' then
- ReadRefElImage(refEl, fImage); // also clears refEl
- if fImage <> nil then
- dstImg.Copy(fImage, fImage.Bounds, dstRec);
- end;
- end;
- //------------------------------------------------------------------------------
- // TFeCompositeElement
- //------------------------------------------------------------------------------
- constructor TFeCompositeElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
- begin
- inherited;
- fourKs[0] := InvalidD; fourKs[1] := InvalidD;
- fourKs[2] := InvalidD; fourKs[3] := InvalidD;
- end;
- //------------------------------------------------------------------------------
- procedure Arithmetic(p1, p2, r: PColor32; const ks: array of byte);
- var
- c1 : PARGB absolute p1;
- c2 : PARGB absolute p2;
- res : PARGB absolute r;
- begin
- res.A := (((c1.A xor 255) * (c2.A xor 255)) shr 8) xor 255;
- res.R := ClampByte((ks[0] * c1.R * c2.R +
- ks[1] * c1.R * 255 + ks[2] * c2.R * 255 + ks[3] * 65025) shr 16);
- res.G := ClampByte((ks[0] * c1.G * c2.G +
- ks[1] * c1.G * 255 + ks[2] * c2.G * 255 + ks[3] * 65025) shr 16);
- res.B := ClampByte((ks[0] * c1.B * c2.B +
- ks[1] * c1.B * 255 + ks[2] * c2.B * 255 + ks[3] * 65025) shr 16);
- end;
- //------------------------------------------------------------------------------
- procedure ArithmeticBlend(src1, src2, dst: TImage32;
- const recS1, recS2, recDst: TRect; const ks: TFourDoubles);
- var
- kk: array[0..3] of byte;
- w,h, w2,h2, w3,h3, i,j: integer;
- p1,p2,r: PColor32;
- begin
- RectWidthHeight(recS1, w, h);
- RectWidthHeight(recS2, w2, h2);
- RectWidthHeight(recDst, w3, h3);
- if (w2 <> w) or (w3 <> w) or (h2 <> h) or (h3 <> h) or
- (ks[0] = InvalidD) or (ks[1] = InvalidD) or
- (ks[2] = InvalidD) or (ks[3] = InvalidD) then Exit;
- for i := 0 to 3 do
- kk[i] := ClampByte(ks[i]*255);
- for i := 0 to h -1 do
- begin
- p1 := @src1.Pixels[(recS1.Top + i) * src1.Width + recS1.Left];
- p2 := @src2.Pixels[(recS2.Top + i) * src2.Width + recS2.Left];
- r := @dst.Pixels[(recDst.Top + i) * dst.Width + recDst.Left];
- for j := 0 to w -1 do
- begin
- Arithmetic(p1, p2, r, kk);
- inc(p1); inc(p2); inc(r);
- end;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure TFeCompositeElement.Apply;
- var
- pfe: TFilterElement;
- srcImg2, dstImg2: TImage32;
- srcRec2, dstRec2: TRect;
- begin
- if not GetSrcAndDst then Exit;
- pfe := ParentFilterEl;
- if (in2 = '') then Exit;
- srcImg2 := pfe.GetNamedImage(in2, true);
- srcRec2 := GetBounds(srcImg2); //either filter bounds or image bounds
- if (dstImg = srcImg) or (dstImg = srcImg2) then
- dstImg2 := pfe.AddNamedImage(tmpFilterImg) else
- dstImg2 := dstImg;
- dstRec2 := GetBounds(dstImg2); //either filter bounds or image bounds
- case compositeOp of
- coIn:
- begin
- dstImg2.Copy(srcImg, srcRec, dstRec2);
- dstImg2.CopyBlend(srcImg2, srcRec2, dstRec2, BlendMaskLine);
- end;
- coOut:
- begin
- dstImg2.Copy(srcImg, srcRec, dstRec2);
- dstImg2.CopyBlend(srcImg2, srcRec2, dstRec2, BlendInvertedMaskLine);
- end;
- coAtop:
- begin
- dstImg2.Copy(srcImg2, srcRec2, dstRec2);
- dstImg2.CopyBlend(srcImg, srcRec, dstRec2, BlendToAlphaLine);
- dstImg2.CopyBlend(srcImg2, srcRec2, dstRec2, BlendMaskLine);
- end;
- coXOR:
- begin
- dstImg2.Copy(srcImg2, srcRec2, dstRec2);
- dstImg2.CopyBlend(srcImg, srcRec, dstRec2, BlendToAlphaLine);
- dstImg2.CopyBlend(srcImg2, srcRec2, dstRec2, BlendInvertedMaskLine);
- end;
- coArithmetic:
- begin
- ArithmeticBlend(srcImg, srcImg2, dstImg2,
- srcRec, srcRec2, dstRec2, fourKs);
- end;
- else //coOver
- begin
- dstImg2.CopyBlend(srcImg2, srcRec2, dstRec2, BlendToAlphaLine);
- dstImg2.CopyBlend(srcImg, srcRec, dstRec2, BlendToAlphaLine);
- end;
- end;
- if (dstImg <> dstImg2) then
- dstImg.Copy(dstImg2, dstRec2, dstRec);
- end;
- //------------------------------------------------------------------------------
- // TFeColorMatrixElement
- //------------------------------------------------------------------------------
- type
- TColorMatrix = array[0..19] of Byte;
- function ApplyColorMatrix(color: TColor32; const mat: TColorMatrix): TColor32;
- var
- clrIn : TARGB absolute color;
- clrOut: TARGB absolute Result;
- begin
- clrOut.R := ClampByte(MulBytes(mat[0],clrIn.R) + MulBytes(mat[1],clrIn.G) +
- MulBytes(mat[2],clrIn.B) + MulBytes(mat[3],clrIn.A) + mat[4]);
- clrOut.G := ClampByte(MulBytes(mat[5],clrIn.R) + MulBytes(mat[6],clrIn.G) +
- MulBytes(mat[7],clrIn.B) + MulBytes(mat[8],clrIn.A) + mat[9]);
- clrOut.B := ClampByte(MulBytes(mat[10],clrIn.R) + MulBytes(mat[11],clrIn.G) +
- MulBytes(mat[12],clrIn.B) + MulBytes(mat[13],clrIn.A) + mat[14]);
- clrOut.A := ClampByte(MulBytes(mat[15],clrIn.R) + MulBytes(mat[16],clrIn.G) +
- MulBytes(mat[17],clrIn.B) + MulBytes(mat[18],clrIn.A) + mat[19]);
- end;
- //------------------------------------------------------------------------------
- procedure TFeColorMatrixElement.Apply;
- var
- i,j, dx1,dx2: integer;
- colorMatrix: TColorMatrix;
- p1, p2: PColor32;
- begin
- if not GetSrcAndDst or not Assigned(values) then Exit;
- for i := 0 to 19 do
- colorMatrix[i] := ClampByte(Integer(Round(values[i]*255)));
- dx1 := srcImg.Width - RectWidth(srcRec);
- dx2 := dstImg.Width - RectWidth(dstRec);
- p1 := @srcImg.Pixels[srcRec.Top * srcImg.Width + srcRec.Left];
- p2 := @dstImg.Pixels[dstRec.Top * dstImg.Width + dstRec.Left];
- for i := srcRec.Top to srcRec.Bottom -1 do
- begin
- for j := srcRec.Left to srcRec.Right -1 do
- begin
- p2^ := ApplyColorMatrix(p1^, colorMatrix);
- inc(p1); inc(p2);
- end;
- inc(p1, dx1); inc(p2, dx2);
- end;
- end;
- //------------------------------------------------------------------------------
- // TFeComponentTransferElement
- //------------------------------------------------------------------------------
- procedure TFeComponentTransferElement.Apply;
- var
- i,j,k, dx1,dx2: integer;
- d: double;
- rangeSize: integer;
- p1: PColor32;
- p2: PARGB;
- childFuncs: array[0..3] of TFeComponentTransferChild;
- begin
- if not GetSrcAndDst or (ChildCount = 0) then Exit;
- for i := 0 to 3 do childFuncs[i] := nil;
- for i := 0 to ChildCount -1 do
- begin
- if Child[i] is TFeFuncBElement then
- childFuncs[0] := TFeFuncBElement(Child[i])
- else if Child[i] is TFeFuncGElement then
- childFuncs[1] := TFeFuncGElement(Child[i])
- else if Child[i] is TFeFuncRElement then
- childFuncs[2] := TFeFuncRElement(Child[i])
- else if Child[i] is TFeFuncAElement then
- childFuncs[3] := TFeFuncAElement(Child[i]);
- end;
- // build each childFuncs' bytes array
- for k := 0 to 3 do
- with childFuncs[k] do
- begin
- if not Assigned(childFuncs[k]) then Continue;
- case funcType of
- ftDiscrete:
- begin
- if Length(tableValues) = 0 then Continue;
- SetLength(bytes, 256);
- rangeSize := 256 div Length(tableValues);
- for i:= 0 to High(tableValues) do
- for j:= 0 to rangeSize -1 do
- bytes[i*rangeSize + j] := ClampByte(tableValues[i] * 255);
- end;
- ftTable:
- begin
- if Length(tableValues) < 2 then Continue;
- SetLength(bytes, 256);
- rangeSize := 256 div (Length(tableValues) -1);
- for i:= 0 to High(tableValues) -1 do
- begin
- intercept := tableValues[i];
- slope := (tableValues[i+1] - intercept) / rangeSize;
- for j:= 0 to rangeSize -1 do
- bytes[i*rangeSize + j] := ClampByte((j * slope + intercept) * 255);
- end;
- end;
- ftLinear:
- begin
- SetLength(bytes, 256);
- d := intercept * 255;
- for i:= 0 to 255 do
- bytes[i] := ClampByte(i * slope + d);
- end;
- end;
- end;
- for k := 0 to 3 do
- if Assigned(childFuncs[k]) and not Assigned(childFuncs[k].bytes) then
- childFuncs[k] := nil;
- dx1 := srcImg.Width - RectWidth(srcRec);
- dx2 := dstImg.Width - RectWidth(dstRec);
- p1 := @srcImg.Pixels[srcRec.Top * srcImg.Width + srcRec.Left];
- p2 := @dstImg.Pixels[dstRec.Top * dstImg.Width + dstRec.Left];
- for i := srcRec.Top to srcRec.Bottom -1 do
- begin
- for j := srcRec.Left to srcRec.Right -1 do
- begin
- p2.Color := p1^;
- if Assigned(childFuncs[0]) then p2.B := childFuncs[0].bytes[p2.B];
- if Assigned(childFuncs[1]) then p2.G := childFuncs[1].bytes[p2.G];
- if Assigned(childFuncs[2]) then p2.R := childFuncs[2].bytes[p2.R];
- if Assigned(childFuncs[3]) then p2.A := childFuncs[3].bytes[p2.A];
- inc(p1); inc(p2);
- end;
- inc(p1, dx1); inc(p2, dx2);
- end;
- end;
- //------------------------------------------------------------------------------
- // TFeDefuseLightElement
- //------------------------------------------------------------------------------
- procedure TFeDefuseLightElement.Apply;
- begin
- //not implemented
- if not GetSrcAndDst then Exit;
- if srcImg <> dstImg then
- dstImg.Copy(srcImg, srcRec, dstRec);
- end;
- //------------------------------------------------------------------------------
- // TFeDropShadowElement
- //------------------------------------------------------------------------------
- constructor TFeDropShadowElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
- begin
- inherited;
- stdDev := InvalidD;
- floodColor := clInvalid;
- offset.X.SetValue(0);
- offset.Y.SetValue(0);
- end;
- //------------------------------------------------------------------------------
- procedure TFeDropShadowElement.Apply;
- var
- alpha: Byte;
- off: TPointD;
- dstOffRec: TRect;
- pfe: TFilterElement;
- dropShadImg: TImage32;
- begin
- if not GetSrcAndDst then Exit;
- pfe := ParentFilterEl;
- dropShadImg := pfe.GetNamedImage(tmpFilterImg, false);
- dropShadImg.Copy(srcImg, srcRec, dropShadImg.Bounds);
- off := offset.GetPoint(RectD(pfe.fObjectBounds), GetRelFracLimit);
- off := ScalePoint(off, pfe.fScale);
- dstOffRec := dstRec;
- with Point(off) do TranslateRect(dstOffRec, X, Y);
- dstImg.Copy(srcImg, srcRec, dstOffRec);
- dstImg.SetRGB(floodColor);
- alpha := GetAlpha(floodColor);
- if (alpha > 0) and (alpha < 255) then
- dstImg.ReduceOpacity(alpha);
- if stdDev > 0 then
- FastGaussianBlur(dstImg, dstRec,
- Ceil(stdDev *0.75 * ParentFilterEl.fScale) , 1);
- dstImg.CopyBlend(dropShadImg, dropShadImg.Bounds, dstRec, BlendToAlphaLine);
- end;
- //------------------------------------------------------------------------------
- // TFeFloodElement
- //------------------------------------------------------------------------------
- constructor TFeFloodElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
- begin
- inherited;
- floodColor := clInvalid;
- end;
- //------------------------------------------------------------------------------
- procedure TFeFloodElement.Apply;
- var
- rec: TRect;
- begin
- if not GetSrcAndDst then Exit;
- if elRectWH.IsValid then
- rec := Rect(elRectWH.GetRectD(RectD(srcRec), GetRelFracLimit)) else
- rec := dstRec;
- dstImg.FillRect(rec, floodColor);
- end;
- //------------------------------------------------------------------------------
- // TFeGaussElement
- //------------------------------------------------------------------------------
- constructor TFeGaussElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
- begin
- inherited;
- stdDev := InvalidD;
- end;
- //------------------------------------------------------------------------------
- procedure TFeGaussElement.Apply;
- begin
- if (stdDev = InvalidD) or not GetSrcAndDst then Exit;
- if srcImg <> dstImg then
- dstImg.Copy(srcImg, srcRec, dstRec);
- //GaussianBlur(dstImg, dstRec, Round(stdDev * ParentFilterEl.fScale));
- // FastGaussianBlur is a very good approximation and also much faster.
- // However, empirically stdDev/2 more closely emulates other renderers.
- FastGaussianBlur(dstImg, dstRec, Ceil(stdDev/2 * ParentFilterEl.fScale));
- end;
- //------------------------------------------------------------------------------
- // TFeMergeElement
- //------------------------------------------------------------------------------
- procedure TFeMergeElement.Apply;
- var
- i: integer;
- tmpImg: TImage32;
- pfe: TFilterElement;
- begin
- tmpImg := nil;
- if not GetSrcAndDst then Exit;
- pfe := ParentFilterEl;
- for i := 0 to fChilds.Count -1 do
- if TBaseElement(fChilds[i]) is TFeMergeNodeElement then
- with TFeMergeNodeElement(fChilds[i]) do
- begin
- if not GetSrcAndDst then Continue;
- if Assigned(tmpImg) then
- tmpImg.CopyBlend(srcImg, srcRec, tmpImg.Bounds, BlendToAlphaLine)
- else if srcImg = pfe.fSrcImg then
- tmpImg := pfe.GetNamedImage(SourceImage, false)
- else
- tmpImg := srcImg;
- end;
- dstImg.Copy(tmpImg, tmpImg.Bounds, dstRec);
- pfe.fLastImg := dstImg;
- end;
- //------------------------------------------------------------------------------
- // TFeMergeNodeElement
- //------------------------------------------------------------------------------
- procedure TFeMergeNodeElement.Apply;
- begin
- //should never get here ;)
- end;
- //------------------------------------------------------------------------------
- // TFeOffsetElement
- //------------------------------------------------------------------------------
- procedure TFeOffsetElement.Apply;
- var
- off: TPointD;
- dstOffRec: TRect;
- tmpImg: TImage32;
- pfe: TFilterElement;
- begin
- if not GetSrcAndDst then Exit;
- pfe := ParentFilterEl;
- off := offset.GetPoint(RectD(pfe.fObjectBounds), GetRelFracLimit);
- off := ScalePoint(off, pfe.fScale);
- dstOffRec := dstRec;
- with Point(off) do TranslateRect(dstOffRec, X, Y);
- if srcImg = dstImg then
- begin
- tmpImg := pfe.GetNamedImage(tmpFilterImg, false);
- tmpImg.Copy(srcImg, srcRec, tmpImg.Bounds);
- dstImg.Clear(dstRec);
- dstImg.Copy(tmpImg, tmpImg.Bounds, dstOffRec);
- end else
- begin
- dstImg.Clear(dstRec);
- dstImg.Copy(srcImg, srcRec, dstOffRec);
- end;
- end;
- //------------------------------------------------------------------------------
- // TFeSpecLightElement
- //------------------------------------------------------------------------------
- procedure TFeSpecLightElement.Apply;
- begin
- //not implemented
- if not GetSrcAndDst then Exit;
- if srcImg <> dstImg then
- dstImg.Copy(srcImg, srcRec, dstRec);
- end;
- //------------------------------------------------------------------------------
- // TClipPathElement
- //------------------------------------------------------------------------------
- constructor TClipPathElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
- begin
- inherited;
- fDrawData.visible := false;
- end;
- //------------------------------------------------------------------------------
- procedure TClipPathElement.GetPaths(const drawDat: TDrawData);
- var
- i: integer;
- begin
- if pathsLoaded then Exit;
- pathsLoaded := true;
- for i := 0 to fChilds.Count -1 do
- if TBaseElement(fChilds[i]) is TShapeElement then
- with TShapeElement(fChilds[i]) do
- begin
- GetPaths(drawDat);
- AppendPath(self.drawPathsO, drawPathsO);
- AppendPath(self.drawPathsC, drawPathsC);
- // apply child's matrix ...
- MatrixApply(DrawData.matrix, self.drawPathsC);
- MatrixApply(DrawData.matrix, self.drawPathsO);
- end;
- // apply <clippath>'s matrix ...
- MatrixApply(DrawData.matrix, drawPathsC);
- MatrixApply(DrawData.matrix, drawPathsO);
- end;
- //------------------------------------------------------------------------------
- // TShapeElement
- //------------------------------------------------------------------------------
- constructor TShapeElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
- begin
- inherited;
- elRectWH.Init;
- hasPaths := true;
- fDrawData.visible := true;
- if fXmlEl.name = '' then Exit;
- end;
- //------------------------------------------------------------------------------
- function TShapeElement.GetBounds: TRectD;
- var
- i: integer;
- begin
- Result := UnionRect(GetBoundsD(drawPathsC), GetBoundsD(drawPathsO));
- if Result.IsEmpty then
- begin
- Result := NullRectD;
- for i := 0 to fChilds.Count -1 do
- if TBaseElement(Child[i]) is TShapeElement then
- Result := UnionRect(Result, TShapeElement(Child[i]).GetBounds);
- end;
- end;
- //------------------------------------------------------------------------------
- function TShapeElement.HasMarkers: Boolean;
- begin
- Result := IsStroked(fDrawData) and ((fDrawData.markerStart <> '') or
- (fDrawData.markerMiddle <> '') or (fDrawData.markerEnd <> ''));
- end;
- //------------------------------------------------------------------------------
- procedure TShapeElement.Draw(image: TImage32; drawDat: TDrawData);
- var
- d : double;
- img : TImage32;
- stroked : Boolean;
- filled : Boolean;
- tmpRec : TRectD;
- clipRec : TRectD;
- clipRec2 : TRect;
- clipPathEl : TBaseElement;
- filterEl : TBaseElement;
- maskEl : TBaseElement;
- clipPaths : TPathsD;
- fillPaths : TPathsD;
- di : TDrawData;
- useTmpImage : Boolean;
- begin
- UpdateDrawInfo(drawDat, self);
- filled := IsFilled(drawDat);
- stroked := IsStroked(drawDat);
- GetPaths(drawDat);
- if not (filled or stroked) or not hasPaths then
- begin
- inherited;
- Exit;
- end;
- tmpRec := GetBounds;
- if not tmpRec.IsEmpty then
- drawDat.bounds := tmpRec;
- img := image;
- clipRec2 := NullRect;
- maskEl := FindRefElement(drawDat.maskElRef);
- clipPathEl := FindRefElement(drawDat.clipElRef);
- filterEl := FindRefElement(drawDat.filterElRef);
- useTmpImage :=
- Assigned(clipPathEl) or Assigned(filterEl) or Assigned(maskEl);
- if useTmpImage then
- begin
- img := fSvgReader.TempImage;
- //get special effects bounds
- if Assigned(clipPathEl) then
- begin
- drawDat.clipElRef := '';
- di := drawDat;
- with TClipPathElement(clipPathEl) do
- begin
- GetPaths(di);
- clipPaths := drawPathsC;
- AppendPath(clipPaths, drawPathsO);
- clipPaths := MatrixApply(clipPaths, di.matrix);
- clipRec := GetBoundsD(clipPaths);
- end;
- end
- else if Assigned(maskEl) then
- begin
- drawDat.maskElRef := '';
- with TMaskElement(maskEl) do
- begin
- GetPaths(drawDat);
- clipRec := RectD(maskRec);
- end;
- end else
- begin
- clipRec := drawDat.bounds;
- if clipRec.IsEmpty and (drawDat.fontInfo.textLength > 0) and
- (self is TTextPathElement) then
- begin
- clipRec.Left := fParent.elRectWH.left.rawVal;
- clipRec.Bottom := fParent.elRectWH.top.rawVal;
- clipRec.Right := clipRec.Left + drawDat.fontInfo.textLength;
- clipRec.Top := clipRec.Bottom - drawDat.fontInfo.size;
- end;
- if stroked and drawDat.strokeWidth.IsValid then
- begin
- with drawDat.strokeWidth do
- if HasFontUnits then
- d := GetValue(drawDat.fontInfo.size, GetRelFracLimit) else
- d := GetValueXY(clipRec, GetRelFracLimit);
- Img32.Vector.InflateRect(clipRec, d * 0.5, d * 0.5);
- end;
- if Assigned(filterEl) then
- begin
- drawDat.filterElRef := '';
- with TFilterElement(filterEl) do
- begin
- MatrixExtractScale(DrawData.matrix, fScale);
- clipRec := GetAdjustedBounds(clipRec);
- end;
- end;
- MatrixApply(drawDat.matrix, clipRec);
- end;
- clipRec2 := Rect(clipRec);
- Types.IntersectRect(clipRec2, clipRec2, img.Bounds);
- if IsEmptyRect(clipRec2) then Exit;
- if image <> fSvgReader.TempImage then
- img.Clear(clipRec2);
- end;
- if not IsValidMatrix(drawDat.matrix) then
- raise Exception.Create('Invalid matrix found when drawing element');
- if Assigned(drawPathsC) or Assigned(drawPathsO) then
- begin
- if filled then
- begin
- // it's slightly more efficient to apply the matrix here
- // rather than inside DrawFilled().
- fillPaths := drawPathsC;
- if Assigned(drawPathsO) then
- AppendPath(fillPaths, drawPathsO);
- fillPaths := MatrixApply(fillPaths, drawDat.matrix);
- DrawFilled(img, fillPaths, drawDat);
- end;
- if stroked then
- begin
- // it's slightly more efficient to apply the matrix
- // inside DrawStroke() rather than here.
- if Assigned(drawPathsC) then
- DrawStroke(img, drawPathsC, drawDat, true);
- if stroked and Assigned(drawPathsO) then
- DrawStroke(img, drawPathsO, drawDat, false);
- end;
- end;
- if Assigned(filterEl) then
- with TFilterElement(filterEl) do
- Apply(img, clipRec2, drawDat.matrix);
- if Assigned(maskEl) then
- TMaskElement(maskEl).ApplyMask(img, drawDat)
- else if Assigned(clipPathEl) then
- with TClipPathElement(clipPathEl) do
- begin
- if fDrawData.fillRule = frNegative then
- EraseOutsidePaths(img, clipPaths, frNonZero, clipRec2,
- fSvgReader.fCustomRendererCache) else
- EraseOutsidePaths(img, clipPaths, fDrawData.fillRule, clipRec2,
- fSvgReader.fCustomRendererCache);
- end;
- if useTmpImage and (img <> image) then
- image.CopyBlend(img, clipRec2, clipRec2, BlendToAlphaLine);
- //todo: enable "paint-order" to change filled/stroked/marker paint order
- if HasMarkers then DrawMarkers(img, drawDat);
- inherited; // DrawChildren
- end;
- //------------------------------------------------------------------------------
- procedure TShapeElement.DrawMarkers(img: TImage32; drawDat: TDrawData);
- var
- i,j: integer;
- scale, sw: double;
- markerEl: TBaseElement;
- markerPaths: TPathsD;
- pt1, pt2: TPointD;
- di: TDrawData;
- begin
- markerPaths := GetSimplePath(drawDat);
- markerPaths := StripNearDuplicates(markerPaths, 0.01, false);
- if not Assigned(markerPaths) then Exit;
- MatrixApply(drawDat.matrix, markerPaths);
- di := emptyDrawInfo;
- //prepare to scale the markers by the stroke width
- with fDrawData.strokeWidth do
- if not IsValid then sw := 1
- else if HasFontUnits then
- sw := GetValue(drawDat.fontInfo.size, GetRelFracLimit)
- else sw := GetValueXY(drawDat.bounds, GetRelFracLimit);
- MatrixExtractScale(drawDat.matrix, scale);
- MatrixScale(di.matrix, sw * scale);
- if (fDrawData.markerStart <> '') then
- begin
- markerEl := FindRefElement(fDrawData.markerStart);
- if Assigned(markerEl) and (markerEl is TMarkerElement) then
- with TMarkerElement(markerEl) do
- begin
- for i := 0 to High(markerPaths) do
- begin
- if Length(markerPaths[i]) < 2 then Continue;
- pt1 := markerPaths[i][0];
- pt2 := markerPaths[i][1];
- if autoStartReverse then
- SetEndPoint(pt1, GetAngle(pt2, pt1)) else
- SetEndPoint(pt1, GetAngle(pt1, pt2));
- Draw(img, di);
- end;
- end;
- end;
- if (fDrawData.markerMiddle <> '') then
- begin
- markerEl := FindRefElement(fDrawData.markerMiddle);
- if Assigned(markerEl) and (markerEl is TMarkerElement) then
- with TMarkerElement(markerEl) do
- for i := 0 to High(markerPaths) do
- if SetMiddlePoints(markerPaths[i]) then
- Draw(img, di);
- end;
- if (fDrawData.markerEnd <> '') then
- begin
- markerEl := FindRefElement(fDrawData.markerEnd);
- if Assigned(markerEl) and (markerEl is TMarkerElement) then
- with TMarkerElement(markerEl) do
- begin
- for i := 0 to High(markerPaths) do
- begin
- j := High(markerPaths[i]);
- if j < 1 then Continue;
- pt1 := markerPaths[i][j];
- pt2 := markerPaths[i][j-1];
- SetEndPoint(pt1, GetAngle(pt2, pt1));
- Draw(img, di);
- end;
- end;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure TShapeElement.GetPaths(const drawDat: TDrawData);
- var
- i: integer;
- begin
- for i := 0 to fChilds.Count -1 do
- if TBaseElement(fChilds[i]) is TShapeElement then
- TShapeElement(fChilds[i]).GetPaths(drawDat);
- end;
- //------------------------------------------------------------------------------
- function TShapeElement.GetSimplePath(const drawDat: TDrawData): TPathsD;
- begin
- Result := nil;
- end;
- //------------------------------------------------------------------------------
- procedure TShapeElement.DrawFilled(img: TImage32;
- const paths: TPathsD; drawDat: TDrawData);
- var
- refEl: TBaseElement;
- rec: TRect;
- opacity: Byte;
- begin
- if not assigned(paths) then Exit;
- if drawDat.fillColor = clCurrent then
- drawDat.fillColor := fSvgReader.currentColor;
- if drawDat.fillRule = frNegative then
- drawDat.fillRule := frNonZero;
- if not IsValid(drawDat.fillOpacity) then
- opacity := 255 else
- opacity := ClampByte(drawDat.fillOpacity * 255);
- if (drawDat.fillEl <> '') then
- begin
- refEl := FindRefElement(drawDat.fillEl);
- if Assigned(refEl) and (refEl is TFillElement) then
- begin
- if refEl is TRadGradElement then
- begin
- with TRadGradElement(refEl) do
- begin
- fSvgReader.RadGradRenderer.Opacity := opacity;
- if PrepareRenderer(fSvgReader.RadGradRenderer, drawDat) then
- DrawPolygon(img, paths, drawDat.fillRule, fSvgReader.RadGradRenderer);
- end;
- end
- else if refEl is TLinGradElement then
- begin
- with TLinGradElement(refEl) do
- begin
- fSvgReader.LinGradRenderer.Opacity := opacity;
- if PrepareRenderer(fSvgReader.LinGradRenderer, drawDat) then
- DrawPolygon(img, paths, drawDat.fillRule, fSvgReader.LinGradRenderer);
- end;
- end
- else if refEl is TPatternElement then
- begin
- with TPatternElement(refEl) do
- if PrepareRenderer(ImgRenderer, drawDat) then
- begin
- rec := img32.Vector.GetBounds(paths);
- ImgRenderer.Offset := rec.TopLeft;
- DrawPolygon(img, paths, drawDat.fillRule, ImgRenderer);
- end;
- end;
- end;
- end
- else if drawDat.fillColor = clInvalid then
- begin
- DrawPolygon(img, paths, drawDat.fillRule,
- MergeColorAndOpacity(clBlack32, drawDat.fillOpacity),
- fSvgReader.fCustomRendererCache);
- end
- else
- with drawDat do
- begin
- DrawPolygon(img, paths, fillRule,
- MergeColorAndOpacity(fillColor, fillOpacity),
- fSvgReader.fCustomRendererCache);
- end;
- end;
- //------------------------------------------------------------------------------
- procedure TShapeElement.DrawStroke(img: TImage32;
- const paths: TPathsD; drawDat: TDrawData; isClosed: Boolean);
- var
- i: integer;
- dashOffset, sw: double;
- dashArray: TArrayOfDouble;
- miterLim, scale: Double;
- strokeClr: TColor32;
- strokePaths: TPathsD;
- refEl: TBaseElement;
- endStyle: TEndStyle;
- joinStyle: TJoinStyle;
- bounds: TRectD;
- paths2: TPathsD;
- opacity: Byte;
- begin
- if not Assigned(paths) then Exit;
- MatrixExtractScale(drawDat.matrix, scale);
- joinStyle := fDrawData.strokeJoin;
- bounds := fSvgReader.userSpaceBounds;
- with drawDat.strokeWidth do
- begin
- if not IsValid then
- sw := 1
- else if HasFontUnits then
- sw := GetValue(drawDat.fontInfo.size, GetRelFracLimit)
- else
- sw := GetValueXY(bounds, 0);
- end;
- miterLim := drawDat.strokeMitLim;
- if drawDat.strokeColor = clCurrent then
- drawDat.strokeColor := fSvgReader.currentColor;
- if Length(drawDat.dashArray) > 0 then
- dashArray := ScaleDashArray(drawDat.dashArray, scale) else
- dashArray := nil;
- dashOffset := drawDat.dashOffset;
- with drawDat do
- strokeClr := MergeColorAndOpacity(strokeColor, strokeOpacity);
- if not IsValid(drawDat.strokeOpacity) then
- opacity := 255 else
- opacity := ClampByte(drawDat.strokeOpacity * 255);
- if isClosed then
- begin
- if Assigned(dashArray) then
- begin
- if joinStyle = jsRound then
- endStyle := esRound else
- endStyle := esButt;
- dashArray := ScaleDashArray(drawDat.dashArray, 1); // ie. don't scale yet!
- strokePaths := nil;
- for i := 0 to High(paths) do
- begin
- paths2 := GetDashedPath(paths[i], true, dashArray, @dashOffset);
- AppendPath(strokePaths, paths2);
- end;
- strokePaths :=
- RoughOutline(strokePaths, sw, joinStyle, endStyle, miterLim, scale);
- end else
- begin
- endStyle := esPolygon;
- strokePaths :=
- RoughOutline(paths, sw, joinStyle, endStyle, miterLim, scale);
- end;
- end else
- begin
- if fDrawData.strokeCap = esPolygon then
- endStyle := esButt else
- endStyle := fDrawData.strokeCap;
- if Assigned(dashArray) then
- begin
- strokePaths := MatrixApply(paths, drawDat.matrix);
- DrawDashedLine(img, strokePaths, dashArray,
- @dashOffset, sw * scale, strokeClr, endStyle, jsAuto,
- fSvgReader.fCustomRendererCache);
- Exit;
- end;
- strokePaths :=
- RoughOutline(paths, sw, joinStyle, endStyle, miterLim, scale);
- end;
- strokePaths := MatrixApply(strokePaths, drawDat.matrix);
- if (drawDat.strokeEl <> '') then
- begin
- refEl := FindRefElement(drawDat.strokeEl);
- if not Assigned(refEl) then Exit;
- if refEl is TRadGradElement then
- begin
- with TRadGradElement(refEl) do
- begin
- fSvgReader.RadGradRenderer.Opacity := opacity;
- PrepareRenderer(fSvgReader.RadGradRenderer, drawDat);
- end;
- DrawPolygon(img, strokePaths, frNonZero, fSvgReader.RadGradRenderer);
- end
- else if refEl is TLinGradElement then
- begin
- with TLinGradElement(refEl) do
- begin
- fSvgReader.LinGradRenderer.Opacity := opacity;
- PrepareRenderer(fSvgReader.LinGradRenderer, drawDat);
- end;
- DrawPolygon(img, strokePaths, frNonZero, fSvgReader.LinGradRenderer);
- end
- else if refEl is TPatternElement then
- with TPatternElement(refEl) do
- begin
- imgRenderer.Opacity := opacity;
- PrepareRenderer(imgRenderer, drawDat);
- DrawLine(img, strokePaths, 1, imgRenderer, esPolygon, joinStyle, scale);
- DrawPolygon(img, strokePaths, frNonZero, imgRenderer);
- end;
- end else
- begin
- DrawPolygon(img, strokePaths,
- frNonZero, strokeClr, fSvgReader.fCustomRendererCache);
- end;
- end;
- //------------------------------------------------------------------------------
- // TPathElement
- //------------------------------------------------------------------------------
- constructor TPathElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
- begin
- inherited;
- fSvgPaths := TSvgPath.Create;
- end;
- //------------------------------------------------------------------------------
- destructor TPathElement.Destroy;
- begin
- fSvgPaths.Free;
- inherited;
- end;
- //------------------------------------------------------------------------------
- function TPathElement.GetBounds: TRectD;
- var
- i: integer;
- begin
- Result := NullRectD;
- for i := 0 to fSvgPaths.Count -1 do
- Result := UnionRect(Result, fSvgPaths[i].GetBounds);
- end;
- //------------------------------------------------------------------------------
- procedure TPathElement.ParseDAttrib(const value: UTF8String);
- begin
- fSvgPaths.Parse(value);
- end;
- //------------------------------------------------------------------------------
- procedure TPathElement.Flatten(index: integer; scalePending: double;
- out path: TPathD; out isClosed: Boolean);
- begin
- isClosed := fSvgPaths[index].isClosed;
- path := fSvgPaths[index].GetFlattenedPath(scalePending);
- end;
- //------------------------------------------------------------------------------
- procedure TPathElement.GetPaths(const drawDat: TDrawData);
- var
- i: integer;
- scalePending: double;
- isClosed: Boolean;
- path: TPathD;
- begin
- if pathsLoaded then Exit;
- pathsLoaded := true;
- MatrixExtractScale(drawDat.matrix, scalePending);
- for i := 0 to fSvgPaths.Count -1 do
- begin
- Flatten(i, scalePending, path, isClosed);
- if not Assigned(path) then Continue;
- if isClosed then
- AppendPath(drawPathsC, path) else
- AppendPath(drawPathsO, path);
- end;
- end;
- //------------------------------------------------------------------------------
- function TPathElement.GetSimplePath(const drawDat: TDrawData): TPathsD;
- var
- i: integer;
- begin
- Result := nil;
- SetLength(Result, fSvgPaths.Count);
- for i := 0 to fSvgPaths.Count -1 do
- Result[i] := fSvgPaths[i].GetSimplePath;
- end;
- //------------------------------------------------------------------------------
- // TPolyElement
- //------------------------------------------------------------------------------
- function TPolyElement.GetBounds: TRectD;
- begin
- Result := GetBoundsD(path);
- end;
- //------------------------------------------------------------------------------
- procedure TPolyElement.GetPaths(const drawDat: TDrawData);
- begin
- if pathsLoaded or not Assigned(path) then Exit;
- pathsLoaded := true;
- if (fXmlEl.hash = hPolygon) then
- begin
- AppendPath(drawPathsC, path); //hPolygon
- end else
- begin
- AppendPath(drawPathsO, path); //hPolyline
- end;
- end;
- //------------------------------------------------------------------------------
- function TPolyElement.GetSimplePath(const drawDat: TDrawData): TPathsD;
- begin
- Result := nil;
- AppendPath(Result, path);
- end;
- //------------------------------------------------------------------------------
- procedure TPolyElement.ParsePoints(const value: UTF8String);
- var
- currCnt, currCap: integer;
- procedure AddPoint(const pt: TPointD);
- begin
- if currCnt = currCap then
- begin
- currCap := currCap + buffSize;
- SetLength(path, currCap);
- end;
- path[currCnt] := pt;
- inc(currCnt);
- end;
- var
- pt: TPointD;
- c, endC: PUTF8Char;
- begin
- currCnt := 0;
- currCap := buffSize;
- c := PUTF8Char(value);
- endC := c + Length(value);
- SetLength(path, currCap);
- while IsNumPending(c, endC, true) and
- ParseNextNum(c, endC, true, pt.X) and
- ParseNextNum(c, endC, true, pt.Y) do
- AddPoint(pt);
- SetLength(path, currCnt);
- end;
- //------------------------------------------------------------------------------
- // TLineElement
- //------------------------------------------------------------------------------
- constructor TLineElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
- begin
- inherited;
- NewPointDArray(path, 2, True);
- path[0] := NullPointD; path[1] := NullPointD;
- end;
- //------------------------------------------------------------------------------
- function TLineElement.GetBounds: TRectD;
- begin
- Result := GetBoundsD(path);
- end;
- //------------------------------------------------------------------------------
- procedure TLineElement.GetPaths(const drawDat: TDrawData);
- begin
- if pathsLoaded then Exit;
- pathsLoaded := true;
- AppendPath(drawPathsO, path);
- end;
- //------------------------------------------------------------------------------
- function TLineElement.GetSimplePath(const drawDat: TDrawData): TPathsD;
- begin
- Result := nil;
- AppendPath(Result, path);
- end;
- //------------------------------------------------------------------------------
- // TCircleElement
- //------------------------------------------------------------------------------
- constructor TCircleElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
- begin
- inherited;
- bounds := NullRectD;
- centerPt.Init;
- radius.Init;
- end;
- //------------------------------------------------------------------------------
- function TCircleElement.GetBounds: TRectD;
- begin
- Result := bounds;
- end;
- //------------------------------------------------------------------------------
- procedure TCircleElement.GetPaths(const drawDat: TDrawData);
- var
- scalePending : double;
- pt : TPointD;
- path : TPathD;
- r: double;
- begin
- if pathsLoaded or not radius.IsValid then Exit;
- pathsLoaded := true;
- r := radius.GetValueXY(drawDat.bounds, GetRelFracLimit);
- pt := centerPt.GetPoint(drawDat.bounds, GetRelFracLimit);
- MatrixExtractScale(drawDat.matrix, scalePending);
- bounds := RectD(pt.X -r, pt.Y -r, pt.X +r, pt.Y +r);
- path := Ellipse(bounds, scalePending);
- AppendPath(drawPathsC, path);
- end;
- //------------------------------------------------------------------------------
- // TEllipseElement
- //------------------------------------------------------------------------------
- constructor TEllipseElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
- begin
- inherited;
- centerPt.Init;
- radius.Init;
- end;
- //------------------------------------------------------------------------------
- function TEllipseElement.GetBounds: TRectD;
- begin
- Result := bounds;
- end;
- //------------------------------------------------------------------------------
- procedure TEllipseElement.GetPaths(const drawDat: TDrawData);
- var
- scalePending : double;
- path : TPathD;
- rad : TPointD;
- centPt : TPointD;
- begin
- if pathsLoaded then Exit;
- pathsLoaded := true;
- rad := radius.GetPoint(drawDat.bounds, GetRelFracLimit);
- centPt := centerPt.GetPoint(drawDat.bounds, GetRelFracLimit);
- with centPt do
- bounds := RectD(X -rad.X, Y -rad.Y, X +rad.X, Y +rad.Y);
- MatrixExtractScale(drawDat.matrix, scalePending);
- path := Ellipse(bounds, scalePending);
- AppendPath(drawPathsC, path);
- end;
- //------------------------------------------------------------------------------
- // TRectElement
- //------------------------------------------------------------------------------
- constructor TRectElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
- begin
- inherited;
- radius.Init;
- elRectWH.width.SetValue(100, utPercent);
- elRectWH.height.SetValue(100, utPercent);
- end;
- //------------------------------------------------------------------------------
- function TRectElement.GetBounds: TRectD;
- begin
- Result := elRectWH.GetRectD(NullRectD, GetRelFracLimit);
- end;
- //------------------------------------------------------------------------------
- procedure TRectElement.GetPaths(const drawDat: TDrawData);
- var
- radXY : TPointD;
- bounds: TRectD;
- path : TPathD;
- begin
- if pathsLoaded then Exit;
- if elRectWH.width.HasFontUnits then
- bounds := elRectWH.GetRectD(drawDat.fontInfo.size, GetRelFracLimit) else
- bounds := elRectWH.GetRectD(drawDat.bounds, GetRelFracLimit);
- if bounds.IsEmpty then Exit;
- pathsLoaded := true;
- radXY := radius.GetPoint(bounds, GetRelFracLimit);
- if (radXY.X > 0) or (radXY.Y > 0) then
- begin
- if (radXY.X <= 0) then radXY.X := radXY.Y
- else if (radXY.Y <= 0) then radXY.Y := radXY.X;
- path := RoundRect(bounds, radXY);
- end else
- path := Rectangle(bounds);
- AppendPath(drawPathsC, path);
- end;
- //------------------------------------------------------------------------------
- function TRectElement.GetSimplePath(const drawDat: TDrawData): TPathsD;
- var
- rec: TRectD;
- begin
- Result := nil;
- rec := elRectWH.GetRectD(drawDat.bounds, GetRelFracLimit);
- if not rec.IsEmpty then
- AppendPath(Result, Rectangle(rec));
- end;
- //------------------------------------------------------------------------------
- // TTextElement
- //------------------------------------------------------------------------------
- constructor TTextElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
- begin
- inherited;
- offset.Init;
- hasPaths := false;
- end;
- //------------------------------------------------------------------------------
- procedure TTextElement.Draw(img: TImage32; drawDat: TDrawData);
- begin
- UpdateDrawInfo(drawDat, self);
- UpdateFontInfo(drawDat, self);
- fSvgReader.GetBestFont(drawDat.FontInfo);
- if not Assigned(fSvgReader.fFontCache) then Exit;
- if drawDat.fontInfo.size = 0 then drawDat.fontInfo.size := 16;
- if offset.X.IsValid then
- currentPt.X := offset.X.rawVal
- else if elRectWH.left.IsValid then
- currentPt.X := elRectWH.left.rawVal
- else
- currentPt.X := 0;
- if offset.Y.IsValid then
- currentPt.Y := offset.Y.rawVal
- else if elRectWH.top.IsValid then
- currentPt.Y := elRectWH.top.rawVal
- else
- currentPt.Y := 0;
- lastChrSpc := false;
- textDx := 0;
- currSpanEl := nil;
- //get child paths (which also updates currentPt)
- GetPaths(drawDat);
- DrawChildren(img, drawDat);
- end;
- //------------------------------------------------------------------------------
- // TTextSubElement
- //------------------------------------------------------------------------------
- function TTextSubElement.GetTextEl: TTextElement;
- var
- el: TBaseElement;
- begin
- if not Assigned(textEl) then
- begin
- el := fParent;
- while Assigned(el) and not (el is TTextElement) do
- el := el.fParent;
- if Assigned(el) then
- textEl := TTextElement(el);
- end;
- Result := textEl;
- end;
- //------------------------------------------------------------------------------
- // TTSpanElement
- //------------------------------------------------------------------------------
- procedure TTSpanElement.GetPaths(const drawDat: TDrawData);
- var
- tmpX, startX, fontScale, fontSize, bs: double;
- i,j, len : integer;
- di : TDrawData;
- s : UnicodeString;
- mat : TMatrixD;
- tmpPaths : TPathsD;
- codepoints: TArrayOfCardinal;
- angles : TArrayOfDouble;
- glyphInfo : PGlyphInfo;
- glyphRec : TRectD;
- begin
- // 1. We only want to process this method once even though it's called twice,
- // first indirectly by TTextElement.Draw, and then by TTSpanElement.Draw.
- // 2. This method isn't called when <tspan> is a sub-element of <textpath>.
- if pathsLoaded then Exit;
- pathsLoaded := true;
- di := drawDat;
- if ChildCount > 0 then
- begin
- UpdateDrawInfo(di, self);
- UpdateFontInfo(di, self);
- end;
- if drawDat.FontInfo.size = 0 then
- fontSize := 16.0 else
- fontSize := drawDat.FontInfo.size;
- fSvgReader.GetBestFont(di.FontInfo);
- if not Assigned(fSvgReader.fFontCache) then Exit;
- GetTextEl;
- if not Assigned(textEl) or
- (textEl.currentPt.X = InvalidD) or
- (textEl.currentPt.Y = InvalidD) then Exit;
- //by not changing the fontCache.FontHeight, the quality of
- //small font render improves very significantly (though of course
- //this requires additional glyph scaling and offsetting).
- fontScale := fontSize / fSvgReader.fFontCache.FontHeight;
- if elRectWH.left.IsValid then
- textEl.currentPt.X := elRectWH.left.rawVal;
- if elRectWH.top.IsValid then
- textEl.currentPt.Y := elRectWH.top.rawVal;
- if offset.X.IsValid then
- textEl.currentPt.X := textEl.currentPt.X + offset.X.GetValue(0, 0);
- if offset.Y.IsValid then
- textEl.currentPt.Y := textEl.currentPt.Y + offset.Y.GetValue(0, 0);
- // only 'virtual' (dummy) <tspan> elements are self-closing, and
- // mostly their parents are 'real' <tspan> elements. However,
- // virtual <tspan> elements can also have <text> element parents.
- if not fXmlEl.selfClosed then
- begin
- textEl.currSpanEl := self;
- angles := nil;
- end
- else if (fParent is TTSpanElement) then
- begin
- if Assigned(TTSpanElement(fParent).angle) then
- angles := TTSpanElement(fParent).angle else
- angles := textEl.angle;
- end else
- begin
- angles := textEl.angle;
- textEl.currSpanEl := nil;
- end;
- chunkDx := 0;
- if (Length(fXmlEl.text) > 0) and (fontSize > 1) then
- begin
- // this should be a virtual (dummy) <tspan> element
- //assert(fXmlEl.selfClosed);
- s := DecodeUtf8ToUnicode(HtmlDecode(fXmlEl.text));
- // don't allow a dup. spaces or a space at the beginning of a text
- s := FixSpaces(s, textEl.lastChrSpc or
- ((fParent = textEl) and (self = textEl.Child[0])));
- if IsBlankText(s) then
- begin
- drawPathsC := nil;
- // don't allow duplicate spaces or a space at the beginning of text
- if textEl.lastChrSpc or (self = textEl.Child[0]) then Exit;
- tmpX := fSvgReader.fFontCache.GetSpaceWidth;
- textEl.lastChrSpc := true;
- end
- else if Assigned(angles) then
- begin
- drawPathsC := nil;
- tmpPaths := nil;
- tmpX := 0;
- codepoints := fSvgReader.fFontCache.GetTextCodePoints(s);
- // make sure 'angles' is at least the length of codepoints
- len := Length(codepoints);
- if len > Length(angles) then
- begin
- j := High(angles);
- SetLength(angles, len); // extend angles
- for i := j +1 to len -1 do angles[i] := angles[j];
- end;
- textEl.lastChrSpc := (codepoints[len -1] = 32);
- // now get each rotated glyph and append to drawPathsC ...
- for i := 0 to len -1 do
- begin
- glyphInfo := fSvgReader.fFontCache.GetGlyphInfo(codepoints[i]);
- if Assigned(glyphInfo.paths) then
- begin
- glyphRec := GetBoundsD(glyphInfo.paths);
- tmpPaths := RotatePath(glyphInfo.paths, glyphRec.MidPoint, angles[i]);
- if i > 0 then
- tmpPaths := TranslatePath(tmpPaths, tmpX, 0);
- AppendPath(drawPathsC, tmpPaths);
- end;
- tmpX := tmpX + glyphInfo.hmtx.advanceWidth * fSvgReader.fFontCache.Scale;
- end;
- end else
- begin
- drawPathsC := fSvgReader.fFontCache.GetTextOutline(0, 0, s, tmpX);
- textEl.lastChrSpc := s[length(s)] = space;
- end;
- chunkDx := tmpX * fontScale;
- if Assigned(textEl.currSpanEl) then
- with textEl.currSpanEl do
- chunkDx := chunkDx + self.chunkDx;
- textEl.textDx := textEl.textDx + chunkDx;
- with textEl.currentPt do
- begin
- startX := X;
- X := X + chunkDx;
- end;
- if Assigned(drawPathsC) then // eg. unassigned if a space char
- begin
- with drawDat.fontInfo do
- if not baseShift.IsValid then
- bs := 0 else
- bs := baseShift.GetValue(size, GetRelFracLimit);
- mat := IdentityMatrix;
- MatrixScale(mat, fontScale);
- MatrixTranslate(mat, startX, textEl.currentPt.Y - bs);
- MatrixApply(mat, drawPathsC);
- end;
- end;
- // nested <tspan> elements are always possible,
- // except when self is a pseudo 'selfClosed' <tspan> element
- inherited GetPaths(di); // gets any children paths
- end;
- //------------------------------------------------------------------------------
- procedure TTSpanElement.Draw(image: TImage32; drawDat: TDrawData);
- var
- stroked : Boolean;
- filled : Boolean;
- tmpRec : TRect;
- fillPaths : TPathsD;
- begin
- if ChildCount = 0 then
- fDrawData := fParent.fDrawData
- else
- begin
- UpdateDrawInfo(drawDat, self);
- UpdateFontInfo(drawDat, self);
- end;
- if not fXmlEl.selfClosed then
- begin
- // DrawChildren and exit ...
- inherited;
- Exit;
- end;
- filled := IsFilled(drawDat);
- stroked := IsStroked(drawDat);
- if Assigned(drawPathsC) and Assigned(textEl) then
- begin
- // a <tspan> element that contains text (and a path) must be virtual.
- // But its parent may be another <tspan>, or a <text> or a <textarea>.
- tmpRec := Rect(GetBounds);
- if not IsEmptyRect(tmpRec) then
- drawDat.bounds := RectD(tmpRec);
- if (fParent is TTSpanElement) and fParent.elRectWH.left.IsValid then
- begin
- case drawDat.FontInfo.align of
- staCenter: drawPathsC := TranslatePath(drawPathsC, -chunkDx * 0.5, 0);
- staRight: drawPathsC := TranslatePath(drawPathsC, -chunkDx, 0);
- end;
- end
- else if textEl.textDx <> 0 then
- begin
- case drawDat.FontInfo.align of
- staCenter: drawPathsC := TranslatePath(drawPathsC, -textEl.textDx * 0.5, 0);
- staRight: drawPathsC := TranslatePath(drawPathsC, -textEl.textDx, 0);
- end;
- end;
- // todo - 1. implement paint-order - fill stroke vs stroke fill
- // 2. wavy and colored underlines
- if stroked then
- begin
- // it's slightly more efficient to apply the matrix
- // inside DrawStroke() rather than here.
- DrawStroke(image, drawPathsC, drawDat, true);
- end;
- if filled then
- begin
- // it's slightly more efficient to apply the matrix here
- // rather than inside DrawFilled().
- fillPaths := MatrixApply(drawPathsC, drawDat.matrix);
- DrawFilled(image, fillPaths, drawDat);
- end;
- end;
- end;
- //------------------------------------------------------------------------------
- // TTextPathElement
- //------------------------------------------------------------------------------
- function GetPathDistance(const path: TPathD): double;
- var
- i: integer;
- begin
- Result := 0;
- for i := 1 to High(path) do
- Result := Result + Distance(path[i-1], path[i]);
- end;
- //------------------------------------------------------------------------------
- procedure TTextPathElement.GetPathsInternal(el: TBaseElement;
- const drawDat: TDrawData);
- var
- i, len : integer;
- charsThatFit : integer;
- spacing : double;
- textWidth : double;
- outX : double;
- spanEl : TTSpanElement;
- dd : TDrawData;
- unicodeText : UnicodeString;
- pathDist : double;
- mat : TMatrixD;
- tmpPath : TPathD;
- tmpPaths : TPathsD;
- isClosed : Boolean;
- begin
- if not (el is TTSpanElement) then Exit;
- spanEl := TTSpanElement(el);
- if Assigned(spanEl.drawPathsC) then Exit;
- spanEl.pathsLoaded := true;
- spanEl.GetTextEl;
- dd := drawDat;
- UpdateDrawInfo(dd, el);
- UpdateFontInfo(dd, el);
- if spanEl.offset.X.IsValid then
- textEl.currentPt.X := Max(0, textEl.currentPt.X +
- Round(spanEl.offset.X.rawVal / scale));
- if spanEl.offset.Y.IsValid then
- textEl.currentPt.Y := textEl.currentPt.Y +
- Round(spanEl.offset.Y.rawVal / scale);
- if spanEl.fXmlEl.text = '' then
- begin
- // nb: recursive
- for i := 0 to spanEl.ChildCount -1 do
- GetPathsInternal(spanEl.Child[i], dd);
- Exit;
- end;
- // nb: <tspan> elements that own text will always be pseudo <tspan> elements.
- // Pseudo <tspan> elements have been created inside real <tspan> elements to
- // provide a reliable way to manage text mixed with nested <tspan> elements.
- //trim CRLFs and multiple spaces
- unicodeText := DecodeUtf8ToUnicode(HtmlDecode(spanEl.fXmlEl.text));
- if dd.fontInfo.spacesInText <> sitPreserve then
- unicodeText := TrimMultiSpacesUnicode(unicodeText) else
- unicodeText := StripNewlines(unicodeText);
- //adjust glyph spacing when fFontInfo.textLength is assigned.
- spacing := dd.FontInfo.spacing /scale;
- len := Length(unicodeText);
- if (len < 2) then spacing := 0
- else if (dd.FontInfo.align = staJustify) and
- (pathEl.fsvgPaths.count = 1) then
- with TPathElement(pathEl) do
- begin
- Flatten(0, scale, tmpPath, isClosed);
- pathDist := GetPathDistance(tmpPath);
- textWidth := fSvgReader.fFontCache.GetTextWidth(unicodeText);
- spacing := (pathDist/scale) - textWidth;
- spacing := spacing / (len -1);
- end
- else if (dd.FontInfo.textLength > 0) then
- begin
- textWidth := fSvgReader.fFontCache.GetTextWidth(unicodeText);
- spacing := (dd.FontInfo.textLength/scale) - textWidth;
- spacing := spacing / (len -1);
- end;
- with pathEl do
- begin
- mat := fDrawData.matrix;
- MatrixScale(mat, 1/scale);
- for i := 0 to fSvgPaths.Count -1 do
- begin
- Flatten(i, scale, tmpPath, isClosed);
- //'path' is temporarily scaled to accommodate fReader.fFontCache's
- //static fontheight. The returned glyphs will be de-scaled later.
- MatrixApply(mat, tmpPath);
- tmpPaths := GetTextOutlineOnPath(unicodeText, tmpPath,
- fSvgReader.fFontCache, taLeft, textEl.currentPt.X,
- textEl.currentPt.Y, spacing, charsThatFit, outX);
- AppendPath(spanEl.drawPathsC, tmpPaths);
- textEl.currentPt.X := outX;
- if charsThatFit = Length(unicodeText) then Break;
- Delete(unicodeText, 1, charsThatFit);
- textEl.currentPt := NullPointD;
- end;
- end;
- spanEl.drawPathsC := ScalePath(spanEl.drawPathsC, scale);
- for i := 0 to spanEl.ChildCount -1 do
- GetPathsInternal(spanEl.Child[i], dd);
- end;
- //------------------------------------------------------------------------------
- procedure TTextPathElement.GetPaths(const drawDat: TDrawData);
- var
- i: integer;
- dd: TDrawData;
- el: TBaseElement;
- begin
- if pathsLoaded or not Assigned(fSvgReader.fFontCache) then Exit;
- pathsLoaded := true;
- GetTextEl;
- if not Assigned(textEl) then Exit;
- dd := drawDat;
- UpdateDrawInfo(dd, self);
- UpdateFontInfo(dd, self);
- el := FindRefElement(pathName);
- if not (el is TPathElement) then Exit;
- pathEl := TPathElement(el);
- fSvgReader.GetBestFont(dd.FontInfo);
- scale := dd.FontInfo.size/fSvgReader.fFontCache.FontHeight;
- if offset.X.IsValid then
- textEl.currentPt.X := Max(0,
- textEl.currentPt.X +
- Round(offset.X.GetValue(dd.bounds.Width, 1) / scale));
- if offset.Y.IsValid then
- textEl.currentPt.Y :=
- textEl.currentPt.Y + Round(offset.Y.rawVal / scale);
- // nb: recursive
- for i := 0 to ChildCount -1 do
- GetPathsInternal(Child[i], drawDat);
- end;
- //------------------------------------------------------------------------------
- function TTextPathElement.GetBounds: TRectD;
- var
- textEl: TTextElement;
- begin
- textEl := TTextElement(fParent);
- {$IFDEF UNICODE}
- if IsBlankText(UTF8ToUnicodeString(fXmlEl.text)) then
- {$ELSE}
- if IsBlankText(Utf8Decode(fXmlEl.text)) then
- {$ENDIF}
- Result := textEl.fDrawData.bounds else
- Result := inherited GetBounds;
- end;
- //------------------------------------------------------------------------------
- procedure TTextPathElement.Draw(image: TImage32; drawDat: TDrawData);
- begin
- UpdateFontInfo(drawDat, self);
- inherited;
- end;
- //------------------------------------------------------------------------------
- // TTextAreaElement
- //------------------------------------------------------------------------------
- procedure TTextAreaElement.GetPaths(const drawDat: TDrawData);
- var
- scale : double;
- fontSize : double;
- lnHeight : double;
- di : TDrawData;
- mat : TMatrixD;
- text : Utf8String;
- s : UnicodeString;
- textRec : TRectD;
- const
- margin = 1;
- begin
- if pathsLoaded then Exit;
- text := fXmlEl.text;
- if not elRectWH.width.IsValid or not elRectWH.height.IsValid or
- (text = '') then Exit;
- pathsLoaded := true;
- di := drawDat;
- UpdateDrawInfo(di, self);
- if drawDat.FontInfo.size = 0 then
- fontSize := 16.0 else
- fontSize := drawDat.FontInfo.size;
- fSvgReader.GetBestFont(di.FontInfo);
- if not Assigned(fSvgReader.fFontCache) then Exit;
- scale := fontSize / fSvgReader.fFontCache.FontHeight;
- s := DecodeUtf8ToUnicode(HtmlDecode(text));
- s := FixSpaces(s, false);
- s := StringReplace(s, '<tbreak/>', #10, [rfReplaceAll, rfIgnoreCase]);
- lnHeight := fSvgReader.fFontCache.LineHeight;
- textRec := elRectWH.GetRectD(di.bounds.Width, di.bounds.Height, 1);
- textRec := ScaleRect(textRec, 1/scale);
- InflateRect(textRec, -margin, -margin);
- with TChunkedText.Create(s, fSvgReader.fFontCache) do
- try
- // and compress the lineheight a little
- drawPathsC := GetTextGlyphs(Rect(textRec), taLeft, tvaTop, 0, lnHeight * 0.8);
- finally
- Free;
- end;
- mat := IdentityMatrix;
- MatrixScale(mat, scale);
- MatrixApply(mat, drawPathsC);
- end;
- //------------------------------------------------------------------------------
- // TMarkerElement
- //------------------------------------------------------------------------------
- constructor TMarkerElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
- begin
- inherited;
- fDrawData.visible := false;
- end;
- //------------------------------------------------------------------------------
- procedure TMarkerElement.Draw(img: TImage32; drawDat: TDrawData);
- var
- i, len: integer;
- l,t,w,h,scale, a, a2: double;
- mat: TMatrixD;
- angles: TArrayOfDouble;
- begin
- UpdateDrawInfo(drawDat, self);
- mat := drawDat.matrix;
- if elRectWH.width.IsValid and elRectWH.height.IsValid and
- not markerBoxWH.IsEmpty then
- begin
- w := elRectWH.width.rawVal;
- h := elRectWH.height.rawVal;
- //currently assume preserve aspect ratio
- scale := Min(w/markerBoxWH.Width, h/markerBoxWH.Height);
- MatrixScale(mat, scale, scale);
- end;
- if refPt.X.IsValid and refPt.Y.IsValid then
- begin
- l := refPt.X.rawVal;
- t := refPt.Y.rawVal;
- MatrixExtractScale(mat, scale);
- MatrixTranslate(mat, -l * scale, -t * scale);
- end;
- len := Length(fPoints);
- if len = 0 then Exit;
- SetLength(angles, len);
- angles[0] := angle;
- a := angle;
- for i := 0 to len -2 do
- begin
- a2 := GetAngle(fPoints[i], fPoints[i+1]);
- angles[i] := Average(a, a2);
- a := a2;
- end;
- if len > 1 then
- angles[len -1] := Average(a, angle2);
- //for each 'point' draw the marker
- for i := 0 to len -1 do
- begin
- drawDat.matrix := mat;
- MatrixRotate(drawDat.matrix, NullPointD, angles[i]);
- MatrixTranslate(drawDat.matrix, fPoints[i].X, fPoints[i].Y);
- DrawChildren(img, drawDat);
- end;
- end;
- //------------------------------------------------------------------------------
- procedure TMarkerElement.SetEndPoint(const pt: TPointD; angle: double);
- begin
- NewPointDArray(fPoints, 1, True);
- fPoints[0] := pt;
- self.angle := angle;
- end;
- //------------------------------------------------------------------------------
- function TMarkerElement.SetMiddlePoints(const points: TPathD): Boolean;
- var
- len: integer;
- begin
- len := Length(points);
- Result := len > 2;
- if Result then
- begin
- angle := GetAngle(Points[0],Points[1]);
- angle2 := GetAngle(Points[len-2],Points[len-1]);
- Self.fPoints := Copy(points, 1, len -2);
- end;
- end;
- //------------------------------------------------------------------------------
- // TFillElement
- //------------------------------------------------------------------------------
- function TFillElement.GetRelFracLimit: double;
- begin
- //always assume fractional values below 1 are relative
- Result := 1.0;
- end;
- //------------------------------------------------------------------------------
- // TPatternElement
- //------------------------------------------------------------------------------
- constructor TPatternElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
- begin
- inherited;
- imgRenderer := TImageRenderer.Create;
- elRectWH.Init;
- pattBoxWH.Width := InvalidD;
- pattBoxWH.Height := InvalidD;
- fDrawData.visible := false;
- end;
- //------------------------------------------------------------------------------
- destructor TPatternElement.Destroy;
- begin
- imgRenderer.Free;
- inherited;
- end;
- //------------------------------------------------------------------------------
- function TPatternElement.PrepareRenderer(renderer: TImageRenderer;
- drawDat: TDrawData): Boolean;
- var
- i : integer;
- recWH : TRectWH;
- el : TBaseElement;
- rec : TRectD;
- mat : TMatrixD;
- sx,sy : double;
- scale : TPointD;
- begin
- Result := false;
- MatrixExtractScale(drawDat.matrix, scale.X, scale.Y);
- if units = hUserSpaceOnUse then
- rec := fSvgReader.userSpaceBounds else
- rec := drawDat.bounds;
- //todo: implement patternUnits & patternContentUnits too
- sx := 1; sy := 1;
- if elRectWH.Width.IsValid and elRectWH.Height.IsValid then
- begin
- recWH := elRectWH.GetRectWH(rec, GetRelFracLimit);
- if recWH.IsEmpty then Exit;
- //also scale if necessary
- if not pattBoxWH.IsEmpty then
- begin
- sx := recWH.Width/pattBoxWH.Width;
- sy := recWH.Height/pattBoxWH.Height;
- end;
- end
- else if not pattBoxWH.IsEmpty then
- begin
- recWH.Width := pattBoxWH.Width;
- recWH.Height := pattBoxWH.Width;
- end else
- Exit;
- renderer.Image.SetSize(
- Round(recWH.Width * scale.X),
- Round(recWH.Height * scale.Y));
- Result := true;
- mat := IdentityMatrix;
- MatrixScale(mat, scale.X * sx, scale.Y * sy);
- if (refEl <> '') then
- begin
- el := FindRefElement(refEl);
- if Assigned(el) and (el is TShapeElement) then
- with TShapeElement(el) do
- begin
- drawDat := fDrawData;
- drawDat.matrix := mat;
- drawDat.bounds := recWH.RectD;
- Draw(renderer.Image, drawDat);
- end;
- end;
- for i := 0 to fChilds.Count -1 do
- if TBaseElement(fChilds[i]) is TShapeElement then
- with TShapeElement(fChilds[i]) do
- begin
- drawDat := fDrawData;
- drawDat.matrix := mat;
- drawDat.bounds := rec;
- Draw(renderer.Image, drawDat);
- end
- else if TBaseElement(fChilds[i]) is TImageElement then
- with TImageElement(fChilds[i]) do
- begin
- drawDat := fDrawData;
- drawDat.matrix := mat;
- drawDat.bounds := rec;
- Draw(renderer.Image, drawDat);
- end;
- end;
- //------------------------------------------------------------------------------
- // TSvgElement
- //------------------------------------------------------------------------------
- procedure TSvgElement.Draw(image: TImage32; drawDat: TDrawData);
- var
- sx, sy: double;
- dd: TDrawData;
- begin
- dd := drawDat;
- if (fSvgReader.RootElement <> self) then
- begin
- if (elRectWH.left.rawVal <> InvalidD) or
- (elRectWH.top.rawVal <> InvalidD) then
- begin
- MatrixExtractScale(dd.matrix, sx, sy);
- MatrixTranslate(dd.matrix,
- elRectWH.left.rawVal * sx,
- elRectWH.top.rawVal *sy);
- end;
- if not viewboxWH.IsEmpty then
- begin
- sx := fSvgReader.BackgndImage.Width / viewboxWH.Width;
- sy := fSvgReader.BackgndImage.Height / viewboxWH.Height;
- MatrixScale(dd.matrix, sx, sy);
- end;
- end;
- DrawChildren(image, dd);
- end;
- //------------------------------------------------------------------------------
- function TSvgElement.Width: TValue;
- begin
- Result := elRectWH.Width;
- end;
- //------------------------------------------------------------------------------
- function TSvgElement.Height: TValue;
- begin
- Result := elRectWH.Height;
- end;
- //------------------------------------------------------------------------------
- // TBaseElement
- //------------------------------------------------------------------------------
- constructor TBaseElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
- begin
- {$IFDEF XPLAT_GENERICS}
- fChilds := TList<TBaseElement>.create;
- {$ELSE}
- fChilds := TList.Create;
- {$ENDIF}
- fXmlEl := svgEl;
- self.fParent := parent;
- elRectWH.Init;
- fDrawData := emptyDrawInfo;
- if Assigned(parent) then
- begin
- fDrawData.strokeCap := parent.fDrawData.strokeCap;
- fDrawData.strokeJoin := parent.fDrawData.strokeJoin;
- fSvgReader := parent.fSvgReader;
- end;
- end;
- //------------------------------------------------------------------------------
- destructor TBaseElement.Destroy;
- var
- i: integer;
- begin
- for i := 0 to fChilds.Count -1 do
- TBaseElement(fChilds[i]).Free;
- fChilds.Free;
- inherited;
- end;
- //------------------------------------------------------------------------------
- function TBaseElement.IsFirstChild: Boolean;
- begin
- Result := not Assigned(fParent) or (self = fParent.fChilds[0]);
- end;
- //------------------------------------------------------------------------------
- procedure TBaseElement.Draw(image: TImage32; drawDat: TDrawData);
- begin
- DrawChildren(image, drawDat);
- end;
- //------------------------------------------------------------------------------
- procedure TBaseElement.DrawChildren(image: TImage32; const drawDat: TDrawData);
- var
- i: integer;
- begin
- for i := 0 to fChilds.Count -1 do
- with TBaseElement(fChilds[i]) do
- if fDrawData.visible then Draw(image, drawDat);
- end;
- //------------------------------------------------------------------------------
- function TBaseElement.GetChildCount: integer;
- begin
- Result := fChilds.Count;
- end;
- //------------------------------------------------------------------------------
- function TBaseElement.FindChild(const idName: UTF8String): TBaseElement;
- var
- i: integer;
- begin
- if Match(self.fId, idName) then
- begin
- Result := self;
- Exit;
- end;
- Result := nil;
- for i := 0 to ChildCount -1 do
- begin
- Result := Child[i].FindChild(idName);
- if Assigned(Result) then Break;
- end;
- end;
- //------------------------------------------------------------------------------
- function TBaseElement.GetChild(index: integer): TBaseElement;
- begin
- if (index < 0) or (index >= fChilds.count) then
- Result := nil else
- Result := TBaseElement(fChilds[index]);
- end;
- //------------------------------------------------------------------------------
- function TBaseElement.FindRefElement(const refname: UTF8String): TBaseElement;
- var
- len: integer;
- c, endC: PUTF8Char;
- ref: UTF8String;
- begin
- result := nil;
- len := Length(refname);
- if len = 0 then Exit;
- c := PUTF8Char(Pointer(refname));
- endC := c + len;
- if Match(c, 'url(') then
- begin
- inc(c, 4);
- dec(endC); //removes trailing ')'
- end;
- if c^ = '#' then inc(c);
- if c = PUTF8Char(Pointer(refname)) then
- Result := fSvgReader.fIdList.FindElement(refname)
- else
- begin
- ToUTF8String(c, endC, ref);
- Result := fSvgReader.fIdList.FindElement(ref);
- end;
- end;
- //------------------------------------------------------------------------------
- // dozens of function to process various element attributes
- //------------------------------------------------------------------------------
- procedure Id_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- aOwnerEl.fId := value;
- aOwnerEl.fSvgReader.fIdList.AddOrIgnore(value, aOwnerEl);
- end;
- //------------------------------------------------------------------------------
- procedure In_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- if aOwnerEl is TFeBaseElement then
- TFeBaseElement(aOwnerEl).in1 := value;
- end;
- //------------------------------------------------------------------------------
- procedure In2_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- if aOwnerEl is TFeBaseElement then
- TFeBaseElement(aOwnerEl).in2 := value;
- end;
- //------------------------------------------------------------------------------
- procedure Intercept_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- c, endC: PUTF8Char;
- val: double;
- begin
- if (value = '') or not (aOwnerEl is TFeComponentTransferChild) then Exit;
- c := PUTF8Char(value);
- endC := c + Length(value);
- with TFeComponentTransferChild(aOwnerEl) do
- if ParseNextNum(c, endC, false, val) then
- intercept := val else
- intercept := 1;
- end;
- //------------------------------------------------------------------------------
- procedure Slope_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- c, endC: PUTF8Char;
- val: double;
- begin
- if (value = '') or not (aOwnerEl is TFeComponentTransferChild) then Exit;
- c := PUTF8Char(value);
- endC := c + Length(value);
- with TFeComponentTransferChild(aOwnerEl) do
- if ParseNextNum(c, endC, false, val) then
- slope := val else
- slope := 1;
- end;
- //------------------------------------------------------------------------------
- procedure TableValues_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- c, endC: PUTF8Char;
- val: double;
- len: integer;
- begin
- if (value = '') or not (aOwnerEl is TFeComponentTransferChild) then
- Exit;
- with TFeComponentTransferChild(aOwnerEl) do
- begin
- len := 0;
- tableValues := nil;
- c := PUTF8Char(value);
- endC := c + Length(value);
- while ParseNextNum(c, endC, true, val) do
- begin
- SetLength(tableValues, len +1);
- tableValues[len] := val;
- inc(len);
- end;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure Type_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- if (value = '') or not (aOwnerEl is TFeComponentTransferChild) then Exit;
- with TFeComponentTransferChild(aOwnerEl) do
- begin
- case value[1] of
- 'D','d': funcType := ftDiscrete;
- 'G','g': funcType := ftGamma;
- 'L','l': funcType := ftLinear;
- 'T','t': funcType := ftTable;
- else funcType := ftIdentity;
- end;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure LetterSpacing_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- with aOwnerEl do
- UTF8StringToFloat(value, fDrawData.FontInfo.spacing);
- end;
- //------------------------------------------------------------------------------
- procedure Href_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- case aOwnerEl.fXmlEl.Hash of
- hFeImage:
- TFeImageElement(aOwnerEl).refEl := ExtractRef(value);
- hImage:
- TImageElement(aOwnerEl).fRefEl := ExtractRef(value);
- hUse:
- TUseElement(aOwnerEl).fRefEl := ExtractRef(value);
- hTextPath:
- TTextPathElement(aOwnerEl).pathName := ExtractRef(value);
- else if aOwnerEl is TFillElement then
- TFillElement(aOwnerEl).refEl := ExtractRef(value);
- end;
- end;
- //------------------------------------------------------------------------------
- procedure Space_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- case aOwnerEl.fXmlEl.Hash of
- hText: if value = 'preserve' then
- TTextPathElement(aOwnerEl).fDrawData.fontInfo.spacesInText := sitPreserve;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure BaselineShift_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- mu: TUnitType;
- val: double;
- hash: cardinal;
- c, endC: PUTF8Char;
- begin
- c := PUTF8Char(value);
- endC := c + Length(value);
- hash := ParseNextWordHash(c, endC);
- with aOwnerEl.fDrawData.FontInfo do
- case hash of
- hSuper: baseShift.SetValue(50, utPercent);
- hSub: baseShift.SetValue(-50, utPercent);
- hBaseline: baseShift.SetValue(0, utPixel);
- else
- begin
- UTF8StringToFloatEx(value, val, mu);
- baseShift.SetValue(val, mu);
- end;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure Color_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- color: TColor32;
- begin
- color := clInvalid;
- UTF8StringToColor32(value, color);
- //for setting currentcolor when drawing (eg drawing shapes)
- aOwnerEl.fDrawData.currentColor := color;
- //for setting currentcolor during element creation (eg gradient colors)
- aOwnerEl.fSvgReader.currentColor := color;
- end;
- //------------------------------------------------------------------------------
- procedure LightingColor_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- color: TColor32;
- begin
- color := clInvalid;
- UTF8StringToColor32(value, color);
- if (aOwnerEl is TFeSpecLightElement) then
- TFeSpecLightElement(aOwnerEl).color := color
- else if (aOwnerEl is TFeDefuseLightElement) then
- TFeDefuseLightElement(aOwnerEl).color := color
- end;
- //------------------------------------------------------------------------------
- procedure ClipPath_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- aOwnerEl.fDrawData.clipElRef := ExtractRef(value);
- end;
- //------------------------------------------------------------------------------
- procedure D_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- if aOwnerEl is TPathElement then
- TPathElement(aOwnerEl).ParseDAttrib(value);
- end;
- //------------------------------------------------------------------------------
- procedure Fill_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- case aOwnerEl.fXmlEl.Hash of
- hfeDropShadow:
- UTF8StringToColor32(value, TFeDropShadowElement(aOwnerEl).floodColor);
- hfeFlood:
- UTF8StringToColor32(value, TFeFloodElement(aOwnerEl).floodColor);
- else
- begin
- if Match(PUTF8Char(value), 'url(') then
- aOwnerEl.fDrawData.fillEl := ExtractRef(value)
- else
- UTF8StringToColor32(value, aOwnerEl.fDrawData.fillColor);
- end;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure FillOpacity_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- opacity: double;
- begin
- case aOwnerEl.fXmlEl.Hash of
- hfeDropShadow:
- UTF8StringToOpacity(value, TFeDropShadowElement(aOwnerEl).floodColor);
- hfeFlood:
- UTF8StringToOpacity(value, TFeFloodElement(aOwnerEl).floodColor);
- else
- begin
- UTF8StringToFloat(value, opacity);
- with aOwnerEl.fDrawData do
- begin
- if IsValid(fillOpacity) then
- fillOpacity := fillOpacity * opacity else
- fillOpacity := opacity;
- end;
- end;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure DashArray_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- c, endC: PUTF8Char;
- val: double;
- len: integer;
- begin
- c := PUTF8Char(value);
- endC := c + Length(value);
- with aOwnerEl.fDrawData do
- begin
- len := Length(dashArray);
- while ParseNextNum(c, endC, true, val) do
- begin
- SetLength(dashArray, len +1);
- dashArray[len] := val;
- inc(len);
- end;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure DashOffset_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- c, endC: PUTF8Char;
- begin
- c := PUTF8Char(value);
- endC := c + Length(value);
- with aOwnerEl.fDrawData do
- ParseNextNum(c, endC, true, dashOffset);
- end;
- //------------------------------------------------------------------------------
- procedure Display_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- if GetHash(value) = hNone then
- aOwnerEl.fDrawData.visible := false;
- end;
- //------------------------------------------------------------------------------
- procedure Font_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- GetSvgFontInfo(value, aOwnerEl.fDrawData.FontInfo);
- end;
- //------------------------------------------------------------------------------
- procedure FontFamily_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- hash: cardinal;
- c, endC: PUTF8Char;
- begin
- with aOwnerEl.fDrawData.FontInfo do
- begin
- family := tfUnknown;
- familyNames := GetCommaSeparatedArray(value);
- // get comma separated family names
- c := PUTF8Char(value);
- endC := c + Length(value);
- while ParseNextWordExHash(c, endC, hash) do
- begin
- case hash of
- hSans_045_Serif, hArial : family := tfSansSerif;
- hSerif, hTimes: family := tfSerif;
- hMonospace: family := tfMonospace;
- else Continue;
- end;
- break;
- end;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure FontSize_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- num: double;
- c, endC: PUTF8Char;
- begin
- c := PUTF8Char(value); endC := c + Length(value);
- if not ParseNextNum(c, endC, false, num) then Exit;
- aOwnerEl.fDrawData.FontInfo.size := num;
- end;
- //------------------------------------------------------------------------------
- procedure FontStyle_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- with aOwnerEl.fDrawData.FontInfo do
- if GetHash(value) = hItalic then
- italic := sfsItalic else
- italic := sfsNone;
- end;
- //------------------------------------------------------------------------------
- procedure FontWeight_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- num: double;
- hash: cardinal;
- c, endC: PUTF8Char;
- begin
- c := PUTF8Char(value);
- endC := c + Length(value);
- with aOwnerEl.fDrawData.FontInfo do
- begin
- if IsNumPending(c, endC, false) and
- ParseNextNum(c, endC, false, num) then
- weight := Round(num)
- else if ParseNextWordHash(c, endC, hash) then
- case hash of
- hBold : weight := 600;
- hNormal : weight := 400;
- hBolder : if weight >= 0 then weight := Min(900, weight + 200)
- else weight := 600;
- hLighter: if weight >= 0 then weight := Max(0, weight - 200)
- else weight := 200;
- end;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure Fx_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- if (aOwnerEl is TRadGradElement) then
- with TRadGradElement(aOwnerEl) do
- begin
- UTF8StringToFloatEx(value, F.X.rawVal, F.X.unitType);
- end;
- end;
- //------------------------------------------------------------------------------
- procedure Fy_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- if (aOwnerEl is TRadGradElement) then
- with TRadGradElement(aOwnerEl) do
- begin
- UTF8StringToFloatEx(value, F.Y.rawVal, F.Y.unitType);
- end;
- end;
- //------------------------------------------------------------------------------
- procedure TextAlign_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- with aOwnerEl.fDrawData.FontInfo do
- case GetHash(value) of
- hMiddle : align := staCenter;
- hEnd : align := staRight;
- hJustify : align := staJustify;
- else align := staLeft;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure TextDecoration_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- with aOwnerEl.fDrawData.FontInfo do
- if PosEx('underline', value) > 0 then
- decoration := fdUnderline
- else if PosEx('line-through', value) > 0 then
- decoration := fdStrikeThrough
- else
- decoration := fdNone;
- end;
- //------------------------------------------------------------------------------
- procedure TextLength_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- UTF8StringToFloat(value, aOwnerEl.fDrawData.FontInfo.textLength);
- end;
- //------------------------------------------------------------------------------
- procedure MarkerStart_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- if not (aOwnerEl is TShapeElement) then Exit;
- aOwnerEl.fDrawData.markerStart := ExtractRef(value);
- end;
- //------------------------------------------------------------------------------
- procedure MarkerMiddle_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- if not (aOwnerEl is TShapeElement) then Exit;
- aOwnerEl.fDrawData.markerMiddle := ExtractRef(value);
- end;
- //------------------------------------------------------------------------------
- procedure MarkerEnd_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- if not (aOwnerEl is TShapeElement) then Exit;
- aOwnerEl.fDrawData.markerEnd := ExtractRef(value);
- end;
- //------------------------------------------------------------------------------
- procedure Filter_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- if (aOwnerEl is TShapeElement) then
- aOwnerEl.fDrawData.filterElRef := ExtractRef(value);
- end;
- //------------------------------------------------------------------------------
- procedure Mask_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- if (aOwnerEl is TShapeElement) then
- aOwnerEl.fDrawData.maskElRef := ExtractRef(value);
- end;
- //------------------------------------------------------------------------------
- procedure Offset_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- val: TValue;
- begin
- if (aOwnerEl is TGradStopElement) then
- with TGradStopElement(aOwnerEl) do
- begin
- val.Init;
- UTF8StringToFloatEx(value, val.rawVal, val.unitType);
- offset := val.GetValue(1, GetRelFracLimit);
- end
- end;
- //------------------------------------------------------------------------------
- procedure Opacity_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- opacity: double;
- begin
- if not UTF8StringToFloat(value, opacity) then Exit;
- opacity := ClampRange(opacity, 0,1);
- with aOwnerEl.fDrawData do
- begin
- if IsValid(fillOpacity) then
- fillOpacity := fillOpacity * opacity else
- fillOpacity := opacity;
- if IsValid(strokeOpacity) then
- strokeOpacity := strokeOpacity * opacity else
- strokeOpacity := opacity;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure Operator_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- if (aOwnerEl is TFeCompositeElement) then
- with TFeCompositeElement(aOwnerEl) do
- case GetHash(value) of
- hAtop : compositeOp := coAtop;
- hIn : compositeOp := coIn;
- hOut : compositeOp := coOut;
- hOver : compositeOp := coOver;
- hXor : compositeOp := coXor;
- hArithmetic : compositeOp := coArithmetic;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure Orient_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- if (aOwnerEl is TMarkerElement) and
- (GetHash(value) = hauto_045_start_045_reverse) then
- TMarkerElement(aOwnerEl).autoStartReverse := true;
- end;
- //------------------------------------------------------------------------------
- procedure StartOffset_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- val: double;
- begin
- if (aOwnerEl is TTextPathElement) and
- UTF8StringToFloat(value, val) then
- TTextPathElement(aOwnerEl).offset.X.SetValue(val);
- end;
- //------------------------------------------------------------------------------
- procedure StopColor_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- acolor: TColor32;
- begin
- if aOwnerEl is TGradStopElement then
- begin
- acolor := clInvalid;
- UTF8StringToColor32(value, acolor);
- with TGradStopElement(aOwnerEl) do
- if acolor = clCurrent then
- color := aOwnerEl.fSvgReader.currentColor else
- color := acolor;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure StopOpacity_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- if aOwnerEl is TGradStopElement then
- UTF8StringToOpacity(value, TGradStopElement(aOwnerEl).color);
- end;
- //------------------------------------------------------------------------------
- procedure Points_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- if aOwnerEl is TPolyElement then
- TPolyElement(aOwnerEl).ParsePoints(value);
- end;
- //------------------------------------------------------------------------------
- procedure Stroke_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- if Match(PUTF8Char(value), 'url(') then
- aOwnerEl.fDrawData.strokeEl := ExtractRef(value)
- else if Match(PUTF8Char(value), 'currentcolor') then
- aOwnerEl.fDrawData.strokeColor := clCurrent
- else
- UTF8StringToColor32(value, aOwnerEl.fDrawData.strokeColor);
- end;
- //------------------------------------------------------------------------------
- procedure StrokeLineCap_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- hash: cardinal;
- c, endC: PUTF8Char;
- begin
- c := PUTF8Char(value);
- endC := c + Length(value);
- hash := ParseNextWordHash(c, endC);
- with aOwnerEl.fDrawData do
- case hash of
- hButt : strokeCap := esButt;
- hRound : strokeCap := esRound;
- hSquare : strokeCap := esSquare;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure StrokeLineJoin_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- hash: cardinal;
- c, endC: PUTF8Char;
- begin
- c := PUTF8Char(value);
- endC := c + Length(value);
- hash := ParseNextWordHash(c, endC);
- with aOwnerEl.fDrawData do
- case hash of
- hMiter : strokeJoin := jsMiter;
- hRound : strokeJoin := jsRound;
- hBevel : strokeJoin := jsSquare;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure StrokeMiterLimit_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- UTF8StringToFloat(value, aOwnerEl.fDrawData.strokeMitLim);
- end;
- //------------------------------------------------------------------------------
- procedure StrokeOpacity_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- opacity: double;
- begin
- UTF8StringToFloat(value, opacity);
- opacity := ClampRange(opacity, 0,1);
- with aOwnerEl.fDrawData do
- begin
- if IsValid(strokeOpacity) then
- strokeOpacity := strokeOpacity * opacity else
- strokeOpacity := opacity;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure StrokeWidth_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- with aOwnerEl do
- begin
- UTF8StringToFloatEx(value, fDrawData.strokewidth.rawVal,
- fDrawData.strokewidth.unitType);
- end;
- end;
- //------------------------------------------------------------------------------
- procedure FillRule_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- if LowerCaseTable[value[1]] = 'e' then
- aOwnerEl.fDrawData.fillRule := frEvenOdd else
- aOwnerEl.fDrawData.fillRule := frNonZero;
- end;
- //------------------------------------------------------------------------------
- procedure Rotate_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- i, cnt: integer;
- angle: TArrayOfDouble;
- c, endC: PUTF8Char;
- begin
- SetLength(angle, Length(value));
- c := PUTF8Char(value);
- endC := c + Length(value);
- cnt := 0;
- while ParseNextNum(c, endC, true, angle[cnt]) do inc(cnt);
- SetLength(angle, cnt);
- for i := 0 to cnt -1 do
- angle[i] := DegToRad(angle[i]);
- if aOwnerEl is TTextElement then
- TTextElement(aOwnerEl).angle := angle
- else if aOwnerEl is TTSpanElement then
- TTSpanElement(aOwnerEl).angle := angle;
- end;
- //------------------------------------------------------------------------------
- procedure Transform_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- with aOwnerEl.fDrawData do
- MatrixMultiply2(ParseTransform(value), matrix);
- end;
- //------------------------------------------------------------------------------
- procedure Values_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- cnt: integer;
- c, endC: PUTF8Char;
- begin
- if aOwnerEl is TFeColorMatrixElement then
- with TFeColorMatrixElement(aOwnerEl) do
- begin
- SetLength(values, 20);
- c := PUTF8Char(value);
- endC := c + Length(value);
- cnt := 0;
- while (cnt < 20) and ParseNextNum(c, endC, true, values[cnt]) do
- inc(cnt);
- if cnt < 20 then values := nil;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure GradientTransform_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- mat: TMatrixD;
- begin
- if not (aOwnerEl is TGradientElement) then Exit;
- mat := ParseTransform(value);
- with aOwnerEl.fDrawData do
- MatrixMultiply2(mat, matrix);
- end;
- //------------------------------------------------------------------------------
- procedure GradientUnits_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- if aOwnerEl is TFillElement then
- with TFillElement(aOwnerEl) do
- units := GetHash(value);
- end;
- //------------------------------------------------------------------------------
- procedure Viewbox_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- function LoadViewbox: TRectWH;
- var
- c, endC: PUTF8Char;
- begin
- c := PUTF8Char(value);
- endC := c + Length(value);
- with Result do
- if not ParseNextNum(c, endC, false, Left) or
- not ParseNextNum(c, endC, true, Top) or
- not ParseNextNum(c, endC, true, Width) or
- not ParseNextNum(c, endC, true, Height) then
- Result := RectWH(0,0,0,0);
- end;
- begin
- case aOwnerEl.fXmlEl.Hash of
- hSvg : TSvgElement(aOwnerEl).viewboxWH := LoadViewbox;
- hMarker : TMarkerElement(aOwnerEl).markerBoxWH := LoadViewbox;
- hSymbol : TSymbolElement(aOwnerEl).viewboxWH := LoadViewbox;
- else if aOwnerEl is TPatternElement then
- TPatternElement(aOwnerEl).pattBoxWH := LoadViewbox;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure Visibility_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- case GetHash(value) of
- hCollapse: aOwnerEl.fDrawData.visible := false;
- hHidden: aOwnerEl.fDrawData.visible := false;
- hVisible: aOwnerEl.fDrawData.visible := true;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure Height_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- mu: TUnitType;
- val: double;
- begin
- UTF8StringToFloatEx(value, val, mu);
- with aOwnerEl do
- begin
- elRectWH.height.SetValue(val, mu);
- end;
- end;
- //------------------------------------------------------------------------------
- procedure Width_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- mu: TUnitType;
- val: double;
- begin
- UTF8StringToFloatEx(value, val, mu);
- with aOwnerEl do
- begin
- elRectWH.width.SetValue(val, mu);
- end;
- end;
- //------------------------------------------------------------------------------
- procedure Cx_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- mu: TUnitType;
- val: double;
- begin
- UTF8StringToFloatEx(value, val, mu);
- case aOwnerEl.fXmlEl.Hash of
- hCircle:
- with TCircleElement(aOwnerEl) do centerPt.X.SetValue(val, mu);
- hEllipse:
- with TEllipseElement(aOwnerEl) do centerPt.X.SetValue(val, mu);
- hRadialGradient:
- with TRadGradElement(aOwnerEl) do
- begin
- C.X.SetValue(val, mu);
- end;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure Cy_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- mu: TUnitType;
- val: double;
- begin
- UTF8StringToFloatEx(value, val, mu);
- case aOwnerEl.fXmlEl.Hash of
- hCircle:
- with TCircleElement(aOwnerEl) do centerPt.Y.SetValue(val, mu);
- hEllipse:
- with TEllipseElement(aOwnerEl) do centerPt.Y.SetValue(val, mu);
- hRadialGradient:
- with TRadGradElement(aOwnerEl) do
- begin
- C.Y.SetValue(val, mu);
- end;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure Dx_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- mu: TUnitType;
- val: double;
- begin
- UTF8StringToFloatEx(value, val, mu);
- case aOwnerEl.fXmlEl.Hash of
- hfeDropShadow:
- TFeDropShadowElement(aOwnerEl).offset.X.SetValue(val, mu);
- hfeOffset:
- TFeOffsetElement(aOwnerEl).offset.X.SetValue(val, mu);
- hText:
- TTextElement(aOwnerEl).offset.X.SetValue(val, mu);
- hTSpan:
- TTSpanElement(aOwnerEl).offset.X.SetValue(val, mu);
- hTextPath:
- TTextPathElement(aOwnerEl).offset.X.SetValue(val, mu);
- end;
- end;
- //------------------------------------------------------------------------------
- procedure Dy_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- mu: TUnitType;
- val: double;
- begin
- UTF8StringToFloatEx(value, val, mu);
- case aOwnerEl.fXmlEl.Hash of
- hfeDropShadow:
- TFeDropShadowElement(aOwnerEl).offset.Y.SetValue(val, mu);
- hfeOffset:
- TFeOffsetElement(aOwnerEl).offset.Y.SetValue(val, mu);
- hText:
- TTextElement(aOwnerEl).offset.Y.SetValue(val, mu);
- hTSpan:
- TTSpanElement(aOwnerEl).offset.Y.SetValue(val, mu);
- hTextPath:
- TTextPathElement(aOwnerEl).offset.Y.SetValue(val, mu);
- end;
- end;
- //------------------------------------------------------------------------------
- procedure Result_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- begin
- if (aOwnerEl is TFeBaseElement) then
- TFeBaseElement(aOwnerEl).res := ExtractRef(value);
- end;
- //------------------------------------------------------------------------------
- procedure Rx_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- mu: TUnitType;
- val: double;
- begin
- UTF8StringToFloatEx(value, val, mu);
- case aOwnerEl.fXmlEl.Hash of
- hRect:
- with TRectElement(aOwnerEl) do
- begin
- radius.X.SetValue(val, mu);
- end;
- hCircle:
- with TCircleElement(aOwnerEl) do
- begin
- radius.SetValue(val, mu);
- end;
- hEllipse:
- with TEllipseElement(aOwnerEl) do
- begin
- radius.X.SetValue(val, mu);
- end;
- hRadialGradient:
- with TRadGradElement(aOwnerEl) do
- begin
- radius.X. SetValue(val, mu);
- radius.Y. SetValue(val, mu);
- end;
- hMarker:
- with TMarkerElement(aOwnerEl) do
- refPt.X.SetValue(val, mu);
- end;
- end;
- //------------------------------------------------------------------------------
- procedure Ry_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- mu: TUnitType;
- val: double;
- begin
- UTF8StringToFloatEx(value, val, mu);
- case aOwnerEl.fXmlEl.Hash of
- hRect:
- with TRectElement(aOwnerEl) do
- begin
- radius.Y.SetValue(val, mu);
- end;
- hEllipse:
- with TEllipseElement(aOwnerEl) do
- begin
- radius.Y.SetValue(val, mu);
- end;
- hMarker:
- with TMarkerElement(aOwnerEl) do refPt.Y.SetValue(val, mu);
- end;
- end;
- //------------------------------------------------------------------------------
- procedure SpreadMethod_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- hash: cardinal;
- c, endC: PUTF8Char;
- begin
- if not (aOwnerEl is TGradientElement) then Exit;
- c := PUTF8Char(value);
- endC := c + Length(value);
- hash := ParseNextWordHash(c, endC);
- with TGradientElement(aOwnerEl) do
- case hash of
- hPad : spreadMethod := gfsClamp;
- hReflect : spreadMethod := gfsMirror;
- hRepeat : spreadMethod := gfsRepeat;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure SpectacularExponent(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- se: double;
- begin
- if not (aOwnerEl is TFeSpecLightElement) then Exit;
- UTF8StringToFloat(value, se);
- if (se > 0) and (se < 100) then
- TFeSpecLightElement(aOwnerEl).exponent := se;
- end;
- //------------------------------------------------------------------------------
- procedure StdDev_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- sd: double;
- begin
- UTF8StringToFloat(value, sd);
- if (sd < 0) and (sd > 100) then Exit;
- case aOwnerEl.fXmlEl.Hash of
- hfeGaussianBlur:
- TFeGaussElement(aOwnerEl).stdDev := sd;
- hfeDropShadow:
- TFeDropShadowElement(aOwnerEl).stdDev := sd;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure K1_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- val: double;
- begin
- UTF8StringToFloat(value, val);
- if aOwnerEl is TFeCompositeElement then
- TFeCompositeElement(aOwnerEl).fourKs[0] := val;
- end;
- //------------------------------------------------------------------------------
- procedure K2_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- val: double;
- begin
- UTF8StringToFloat(value, val);
- if aOwnerEl is TFeCompositeElement then
- TFeCompositeElement(aOwnerEl).fourKs[1] := val;
- end;
- //------------------------------------------------------------------------------
- procedure K3_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- val: double;
- begin
- UTF8StringToFloat(value, val);
- if aOwnerEl is TFeCompositeElement then
- TFeCompositeElement(aOwnerEl).fourKs[2] := val;
- end;
- //------------------------------------------------------------------------------
- procedure K4_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- val: double;
- begin
- UTF8StringToFloat(value, val);
- if aOwnerEl is TFeCompositeElement then
- TFeCompositeElement(aOwnerEl).fourKs[3] := val;
- end;
- //------------------------------------------------------------------------------
- procedure X1_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- mu: TUnitType;
- val: double;
- begin
- UTF8StringToFloatEx(value, val, mu);
- case aOwnerEl.fXmlEl.Hash of
- hLine:
- TLineElement(aOwnerEl).path[0].X := val;
- hLinearGradient:
- with TLinGradElement(aOwnerEl) do
- begin
- startPt.X.SetValue(val, mu);
- end;
- hFilter:
- with aOwnerEl do
- begin
- elRectWH.left.SetValue(val, mu);
- end;
- else
- aOwnerEl.elRectWH.left.SetValue(val, mu);
- end;
- end;
- //------------------------------------------------------------------------------
- procedure X2_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- mu: TUnitType;
- val: double;
- begin
- UTF8StringToFloatEx(value, val, mu);
- case aOwnerEl.fXmlEl.Hash of
- hLine:
- TLineElement(aOwnerEl).path[1].X := val;
- hLinearGradient:
- with TLinGradElement(aOwnerEl) do
- begin
- endPt.X.SetValue(val, mu);
- end;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure Y1_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- mu: TUnitType;
- val: double;
- begin
- UTF8StringToFloatEx(value, val, mu);
- case aOwnerEl.fXmlEl.Hash of
- hLine:
- TLineElement(aOwnerEl).path[0].Y := val;
- hLinearGradient:
- with TLinGradElement(aOwnerEl) do
- begin
- startPt.Y.SetValue(val, mu);
- end;
- hFilter:
- with aOwnerEl do
- begin
- elRectWH.top.SetValue(val, mu);
- end;
- else
- aOwnerEl.elRectWH.top.SetValue(val, mu);
- end;
- end;
- //------------------------------------------------------------------------------
- procedure Y2_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- mu: TUnitType;
- val: double;
- begin
- UTF8StringToFloatEx(value, val, mu);
- case aOwnerEl.fXmlEl.Hash of
- hLine:
- TLineElement(aOwnerEl).path[1].Y := val;
- hLinearGradient:
- with TLinGradElement(aOwnerEl) do
- begin
- endPt.Y.SetValue(val, mu);
- end;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure Z_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
- var
- val: double;
- begin
- UTF8StringToFloat(value, val);
- if aOwnerEl is TFePointLightElement then
- TFePointLightElement(aOwnerEl).z := val;
- end;
- //------------------------------------------------------------------------------
- //------------------------------------------------------------------------------
- procedure TBaseElement.LoadAttribute(attrib: PSvgAttrib);
- begin
- with attrib^ do
- case hash of
- hbaseline_045_shift: BaselineShift_Attrib(self, value);
- hColor: Color_Attrib(self, value);
- hClip_045_path: ClipPath_Attrib(self, value);
- hCx: Cx_Attrib(self, value);
- hCy: Cy_Attrib(self, value);
- hD: D_Attrib(self, value);
- hDisplay: Display_Attrib(self, value);
- hDx: Dx_Attrib(self, value);
- hDy: Dy_Attrib(self, value);
- hStroke_045_DashArray: DashArray_Attrib(self, value);
- hStroke_045_DashOffset: DashOffset_Attrib(self, value);
- hFill: Fill_Attrib(self, value);
- hFill_045_Opacity: FillOpacity_Attrib(self, value);
- hFill_045_Rule: FillRule_Attrib(self, value);
- hFilter: Filter_Attrib(self, value);
- hflood_045_color: Fill_Attrib(self, value);
- hflood_045_opacity: FillOpacity_Attrib(self, value);
- hFont: Font_Attrib(self, value);
- hFont_045_Family: FontFamily_Attrib(self, value);
- hFont_045_Size: FontSize_Attrib(self, value);
- hFont_045_Style: FontStyle_Attrib(self, value);
- hFont_045_Weight: FontWeight_Attrib(self, value);
- hFx: Fx_Attrib(self, value);
- hFy: Fy_Attrib(self, value);
- hGradientTransform: GradientTransform_Attrib(self, value);
- hGradientUnits: GradientUnits_Attrib(self, value);
- hHeight: Height_Attrib(self, value);
- hHref: Href_Attrib(self, value);
- hId: Id_Attrib(self, value);
- hIn: In_Attrib(self, value);
- hIn2: In2_Attrib(self, value);
- hIntercept: Intercept_Attrib(self, value);
- hk1: K1_Attrib(self, value);
- hk2: K2_Attrib(self, value);
- hk3: K3_Attrib(self, value);
- hk4: K4_Attrib(self, value);
- hletter_045_spacing: LetterSpacing_Attrib(self, value);
- //hlighting_045_color: LightingColor_Attrib(self, value);
- hMarker_045_End: MarkerEnd_Attrib(self, value);
- hMarkerHeight: Height_Attrib(self, value);
- hMarker_045_Mid: MarkerMiddle_Attrib(self, value);
- hMarker_045_Start: MarkerStart_Attrib(self, value);
- hMarkerWidth: Width_Attrib(self, value);
- hMask: Mask_Attrib(self, value);
- hOffset: Offset_Attrib(self, value);
- hOpacity: Opacity_Attrib(self, value);
- hOperator: Operator_Attrib(self, value);
- hOrient: Orient_Attrib(self, value);
- hPatternUnits: GradientUnits_Attrib(self, value);
- hPatternTransform: Transform_Attrib(self, value);
- hPoints: Points_Attrib(self, value);
- hR: Rx_Attrib(self, value);
- hRefX: Rx_Attrib(self, value);
- hRefY: Ry_Attrib(self, value);
- hResult: Result_Attrib(self, value);
- hRotate: Rotate_Attrib(self, value);
- hRx: Rx_Attrib(self, value);
- hRy: Ry_Attrib(self, value);
- hspecularExponent: SpectacularExponent(self, value);
- hSlope: Slope_Attrib(self, value);
- hSpace: Space_Attrib(self, value);
- hSpreadMethod: SpreadMethod_Attrib(self, value);
- hstdDeviation: StdDev_Attrib(self, value);
- hStartOffset: StartOffset_Attrib(self, value);
- hStop_045_Color: StopColor_Attrib(self, value);
- hStop_045_Opacity: StopOpacity_Attrib(self, value);
- hStroke: Stroke_Attrib(self, value);
- hstroke_045_linecap: StrokeLineCap_Attrib(self, value);
- hstroke_045_linejoin: StrokeLineJoin_Attrib(self, value);
- hstroke_045_miterlimit: StrokeMiterLimit_Attrib(self, value);
- hStroke_045_Opacity: StrokeOpacity_Attrib(self, value);
- hStroke_045_Width: StrokeWidth_Attrib(self, value);
- hTableValues: TableValues_Attrib(self, value);
- hText_045_Anchor: TextAlign_Attrib(self, value);
- hText_045_Decoration: TextDecoration_Attrib(self, value);
- hTextLength: TextLength_Attrib(self, value);
- hTransform: Transform_Attrib(self, value);
- hType: Type_Attrib(self, value);
- hValues: Values_Attrib(self, value);
- hViewbox: Viewbox_Attrib(self, value);
- hVisibility: Visibility_Attrib(self, value);
- hWidth: Width_Attrib(self, value);
- hX: X1_Attrib(self, value);
- hX1: X1_Attrib(self, value);
- hX2: X2_Attrib(self, value);
- hXlink_058_Href: Href_Attrib(self, value);
- hY: Y1_Attrib(self, value);
- hY1: Y1_Attrib(self, value);
- hY2: Y2_Attrib(self, value);
- hZ: Z_Attrib(self, value);
- end;
- end;
- //------------------------------------------------------------------------------
- procedure TBaseElement.LoadAttributes;
- var
- i: integer;
- begin
- for i := 0 to fXmlEl.AttribCount -1 do
- LoadAttribute(PSvgAttrib(fXmlEl.attrib[i]));
- end;
- //------------------------------------------------------------------------------
- function PreferRelativeFraction(val: TValue): TTriState;
- {$IFDEF INLINE} inline; {$ENDIF}
- begin
- if (val.rawVal = InvalidD) or (val.unitType = utUnknown) then
- Result := tsUnknown
- else if val.unitType = utPercent then Result := tsYes
- else if val.unitType <> utNumber then Result := tsNo
- else if (Abs(val.rawVal) < 1) then Result := tsYes
- else Result := tsNo;
- end;
- //------------------------------------------------------------------------------
- function TBaseElement.GetRelFracLimit: double;
- begin
- //the default behaviour here is to assume untyped fractional values
- //below 1.0 are values relative (to the bounding size) BUT ONLY WHEN
- //the parent element's width or height are relative (ie percentages).
- if Assigned(fParent) and (fParent.fXmlEl.hash <> hSvg) then
- begin
- case PreferRelativeFraction(fParent.elRectWH.width) of
- tsYes: begin Result := 1.0; Exit; end;
- tsNo : begin Result := 0; Exit; end;
- end;
- case PreferRelativeFraction(fParent.elRectWH.height) of
- tsYes: begin Result := 1.0; Exit; end;
- tsNo : begin Result := 0; Exit; end;
- end;
- end;
- Result := 0;
- end;
- //------------------------------------------------------------------------------
- function TBaseElement.LoadContent: Boolean;
- var
- i : integer;
- svgEl : TSvgXmlEl;
- elClass : TElementClass;
- el : TBaseElement;
- begin
- Result := false;
- for i := 0 to fXmlEl.childs.Count -1 do
- begin
- svgEl := TSvgXmlEl(fXmlEl.childs[i]);
- if svgEl.hash = 0 then Continue;
- elClass := HashToElementClass(svgEl.hash);
- el := elClass.Create(self, svgEl);
- Self.fChilds.Add(el);
- el.LoadAttributes;
- if el.fXmlEl.childs.Count = 0 then Continue
- else if not el.LoadContent then Exit; //error
- end;
- Result := true;
- end;
- //------------------------------------------------------------------------------
- // TSvgReader
- //------------------------------------------------------------------------------
- constructor TSvgReader.Create;
- begin
- fSvgParser := TSvgParser.Create;
- fLinGradRenderer := TLinearGradientRenderer.Create;
- fRadGradRenderer := TSvgRadialGradientRenderer.Create;
- fCustomRendererCache := TCustomRendererCache.Create;
- fIdList := TSvgIdNameHashMap.Create;
- fSimpleDrawList := TList.Create;
- fBlurQuality := 1; //0: draft (faster); 1: good; 2: excellent (slow)
- currentColor := clBlack32;
- fUsePropScale := true;
- end;
- //------------------------------------------------------------------------------
- destructor TSvgReader.Destroy;
- begin
- Clear;
- fSvgParser.Free;
- fIdList.Free;
- fLinGradRenderer.Free;
- fRadGradRenderer.Free;
- fCustomRendererCache.Free;
- FreeAndNil(fFontCache);
- fSimpleDrawList.Free;
- inherited;
- end;
- //------------------------------------------------------------------------------
- procedure TSvgReader.Clear;
- var
- i: integer;
- begin
- FreeAndNil(fRootElement);
- fSvgParser.Clear;
- fIdList.Clear;
- fLinGradRenderer.Clear;
- fRadGradRenderer.Clear;
- currentColor := clBlack32;
- userSpaceBounds := NullRectD;
- for i := 0 to fSimpleDrawList.Count -1 do
- Dispose(PSimpleDrawData(fSimpleDrawList[i]));
- fSimpleDrawList.Clear;
- end;
- //------------------------------------------------------------------------------
- procedure TSvgReader.DrawImage(img: TImage32; scaleToImage: Boolean);
- var
- scale, scaleH: double;
- di: TDrawData;
- begin
- if not Assigned(fRootElement) or not assigned(img) then Exit;
- with fRootElement do
- begin
- if viewboxWH.IsEmpty then
- begin
- viewboxWH.Width := elRectWH.width.GetValue(defaultSvgWidth, 0);
- viewboxWH.height := elRectWH.height.GetValue(defaultSvgHeight, 0);
- if viewboxWH.IsEmpty then Exit; // this should never happen
- end;
- fBackgndImage := img;
- di := fDrawData;
- if di.currentColor = clInvalid then
- di.currentColor := currentColor;
- MatrixTranslate(di.matrix, -viewboxWH.Left, -viewboxWH.Top);
- //the width and height attributes generally indicate the size of the
- //rendered image unless they are percentage values. Nevertheless, these
- //values can be still overridden by the scaleToImage parameter above
- di.bounds := viewboxWH.RectD;
- userSpaceBounds := fDrawData.bounds;
- if scaleToImage and not img.IsEmpty then
- begin
- //nb: the calculated vbox.width and vbox.height are ignored here since
- //we're scaling the SVG image to the display image. However we still
- //need to call GetViewbox (above) to make sure that viewboxWH is filled.
- if fUsePropScale then
- begin
- scale := GetScaleForBestFit(
- viewboxWH.Width, viewboxWH.Height, img.Width, img.Height);
- scaleH := scale;
- end else
- begin
- scale := GetScale(viewboxWH.Width, img.Width);
- scaleH := GetScale(viewboxWH.Height, img.Height);
- end;
- MatrixScale(di.matrix, scale, scaleH);
- img.SetSize(
- Round(viewboxWH.Width * scale),
- Round(viewboxWH.Height * scaleH));
- end else
- img.SetSize(Round(viewboxWH.Width), Round(viewboxWH.Height));
- end;
- if fBkgndColor <> clNone32 then
- img.Clear(fBkgndColor);
- // // Delay the creation of the TempImage until it is actually needed.
- // // Not all SVGs need it.
- // fTempImageWidth := img.Width;
- // fTempImageHeight := img.Height;
- img.BeginUpdate;
- try
- fRootElement.Draw(img, di);
- finally
- // fTempImageWidth := 0;
- // fTempImageHeight := 0;
- FreeAndNil(fTempImage);
- img.EndUpdate;
- end;
- end;
- //------------------------------------------------------------------------------
- function TSvgReader.GetTempImage: TImage32;
- var
- Pixels: TArrayOfColor32;
- begin
- // Use an additional method to execute the dyn-array management
- // only if we create the TempImage.
- if fTempImage = nil then
- begin
- // Create an uninitialized image. It is cleared by the caller before it is used.
- NewColor32Array(Pixels, BackgndImage.Width * BackgndImage.Height, True);
- fTempImage := TImage32.Create(Pixels, BackgndImage.Width, BackgndImage.Height);
- fTempImage.BlockNotify;
- end;
- Result := fTempImage;
- end;
- //------------------------------------------------------------------------------
- function TSvgReader.LoadInternal: Boolean;
- begin
- Result := false;
- if not Assigned(fSvgParser.svgTree) or
- (fSvgParser.svgTree.hash <> hSvg) then Exit;
- fRootElement := TSvgElement.Create(nil, fSvgParser.svgTree);
- fRootElement.fSvgReader := self;
- fRootElement.LoadAttributes;
- Result := fRootElement.LoadContent;
- end;
- //------------------------------------------------------------------------------
- function TSvgReader.LoadFromFile(const filename: string): Boolean;
- begin
- Clear;
- Result := fSvgParser.LoadFromFile(filename) and LoadInternal;
- end;
- //------------------------------------------------------------------------------
- function TSvgReader.LoadFromStream(stream: TStream): Boolean;
- begin
- Clear;
- Result := fSvgParser.LoadFromStream(stream) and LoadInternal;
- end;
- //------------------------------------------------------------------------------
- function TSvgReader.LoadFromString(const str: string): Boolean;
- begin
- Clear;
- Result := fSvgParser.LoadFromString(str) and LoadInternal;
- end;
- //------------------------------------------------------------------------------
- procedure TSvgReader.SetOverrideFillColor(color: TColor32);
- var
- dd: TDrawData;
- begin
- if not Assigned(RootElement) or (color = clNone32) then Exit;
- dd := RootElement.DrawData;
- dd.fillColor := color;
- RootElement.DrawData := dd;
- end;
- //------------------------------------------------------------------------------
- procedure TSvgReader.SetOverrideStrokeColor(color: TColor32);
- var
- dd: TDrawData;
- begin
- if not Assigned(RootElement) or (color = clNone32) then Exit;
- dd := RootElement.DrawData;
- if dd.strokeColor = clInvalid then Exit;
- dd.strokeColor := color;
- RootElement.DrawData := dd;
- end;
- //------------------------------------------------------------------------------
- function TSvgReader.FindElement(const idName: UTF8String): TBaseElement;
- begin
- if Assigned(RootElement) then
- Result := RootElement.FindChild(idName) else
- Result := nil;
- end;
- //------------------------------------------------------------------------------
- procedure TSvgReader.GetBestFont(const svgFontInfo: TSVGFontInfo);
- var
- i, len: integer;
- bestFontReader: TFontReader;
- fi: TFontInfo;
- begin
- if svgFontInfo.family = tfUnknown then
- fi.family := tfSerif else
- fi.family := svgFontInfo.family;
- fi.faceName := ''; //just match to a family here, not to a specific facename
- fi.macStyles := [];
- len := Length(svgFontInfo.familyNames);
- SetLength(fi.familyNames, len);
- for i := 0 to len -1 do
- fi.familyNames[i] := svgFontInfo.familyNames[i];
- if svgFontInfo.italic = sfsItalic then
- Include(fi.macStyles, msItalic);
- if svgFontInfo.weight >= 600 then
- Include(fi.macStyles, msBold);
- bestFontReader := FontManager.GetBestMatchFont(fi);
- if not Assigned(bestFontReader) then Exit;
- if Assigned(fFontCache) then
- fFontCache.FontReader := bestFontReader else
- fFontCache := TFontCache.Create(bestFontReader, defaultFontHeight);
- fFontCache.Underlined := False;
- fFontCache.StrikeOut := False;
- case svgFontInfo.decoration of
- fdUnderline : fFontCache.Underlined := true;
- fdStrikeThrough : fFontCache.StrikeOut := true;
- end;
- end;
- //------------------------------------------------------------------------------
- procedure TSvgReader.SetBlurQuality(quality: integer);
- begin
- fBlurQuality := Max(0, Min(2, quality));
- end;
- //------------------------------------------------------------------------------
- function TSvgReader.GetIsEmpty: Boolean;
- begin
- Result := not Assigned(fRootElement);
- end;
- //------------------------------------------------------------------------------
- //------------------------------------------------------------------------------
- end.
|