Img32.SVG.Reader.pas 175 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753
  1. unit Img32.SVG.Reader;
  2. (*******************************************************************************
  3. * Author : Angus Johnson *
  4. * Version : 4.8 *
  5. * Date : 12 January 2025 *
  6. * Website : http://www.angusj.com *
  7. * Copyright : Angus Johnson 2019-2025 *
  8. * *
  9. * Purpose : Read SVG 2.0 files *
  10. * *
  11. * License : Use, modification & distribution is subject to *
  12. * Boost Software License Ver 1 *
  13. * http://www.boost.org/LICENSE_1_0.txt *
  14. *******************************************************************************)
  15. interface
  16. {$I Img32.inc}
  17. uses
  18. SysUtils, Classes, Types, Math,
  19. {$IFDEF XPLAT_GENERICS} Generics.Collections, Generics.Defaults,{$ENDIF}
  20. Img32, Img32.SVG.Core, Img32.SVG.Path, Img32.Vector,
  21. Img32.Draw, Img32.Text, Img32.Transform;
  22. {$IFDEF ZEROBASEDSTR}
  23. {$ZEROBASEDSTRINGS OFF}
  24. {$ENDIF}
  25. type
  26. TBaseElement = class;
  27. TElementClass = class of TBaseElement;
  28. TDrawData = record
  29. currentColor : TColor32;
  30. fillColor : TColor32;
  31. fillOpacity : double;
  32. fillRule : TFillRule;
  33. fillEl : UTF8String;
  34. strokeColor : TColor32;
  35. strokeOpacity : double;
  36. strokeWidth : TValue;
  37. strokeCap : TEndStyle;
  38. strokeJoin : TJoinStyle;
  39. strokeMitLim : double;
  40. strokeEl : UTF8String;
  41. dashArray : TArrayOfDouble;
  42. dashOffset : double;
  43. fontInfo : TSVGFontInfo;
  44. markerStart : UTF8String;
  45. markerMiddle : UTF8String;
  46. markerEnd : UTF8String;
  47. filterElRef : UTF8String;
  48. maskElRef : UTF8String;
  49. clipElRef : UTF8String;
  50. matrix : TMatrixD;
  51. visible : Boolean;
  52. useEl : TBaseElement; // to check for and prevent <USE> recursion
  53. bounds : TRectD;
  54. end;
  55. PSvgIdNameHashMapItem = ^TSvgIdNameHashMapItem;
  56. TSvgIdNameHashMapItem = record
  57. Hash: Cardinal;
  58. Next: Integer;
  59. Name: UTF8String;
  60. Element: TBaseElement;
  61. end;
  62. TSvgIdNameHashMap = class(TObject)
  63. private
  64. FItems: array of TSvgIdNameHashMapItem;
  65. FBuckets: TArrayOfInteger;
  66. FCount: Integer;
  67. FMod: Cardinal;
  68. procedure Grow;
  69. function FindItemIndex(const Name: UTF8String): Integer;
  70. public
  71. procedure AddOrIgnore(const idName: UTF8String; element: TBaseElement);
  72. function FindElement(const idName: UTF8String): TBaseElement;
  73. procedure Clear;
  74. end;
  75. TSvgReader = class;
  76. TBaseElement = class
  77. private
  78. fParent : TBaseElement;
  79. fXmlEl : TSvgXmlEl;
  80. fSvgReader : TSvgReader;
  81. {$IFDEF XPLAT_GENERICS}
  82. fChilds : TList<TBaseElement>;
  83. {$ELSE}
  84. fChilds : TList;
  85. {$ENDIF}
  86. fId : UTF8String;
  87. fDrawData : TDrawData; // currently both static and dynamic vars
  88. function FindRefElement(const refname: UTF8String): TBaseElement;
  89. function GetChildCount: integer;
  90. function GetChild(index: integer): TBaseElement;
  91. function FindChild(const idName: UTF8String): TBaseElement;
  92. protected
  93. elRectWH : TValueRecWH; // multifunction variable
  94. function IsFirstChild: Boolean;
  95. procedure LoadAttributes;
  96. procedure LoadAttribute(attrib: PSvgAttrib);
  97. function LoadContent: Boolean; virtual;
  98. // GetRelFracLimit: ie when to assume untyped vals are relative vals
  99. function GetRelFracLimit: double; virtual;
  100. procedure Draw(image: TImage32; drawDat: TDrawData); virtual;
  101. procedure DrawChildren(image: TImage32; const drawDat: TDrawData);
  102. public
  103. constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); virtual;
  104. destructor Destroy; override;
  105. property Child[index: integer]: TBaseElement read GetChild; default;
  106. property ChildCount: integer read GetChildCount;
  107. property DrawData: TDrawData read fDrawData write fDrawData;
  108. property Id: UTF8String read fId;
  109. end;
  110. TShapeElement = class(TBaseElement)
  111. protected
  112. hasPaths : Boolean;
  113. pathsLoaded : Boolean;
  114. drawPathsO : TPathsD; //open only
  115. drawPathsC : TPathsD; //closed only
  116. function GetBounds: TRectD; virtual;
  117. function HasMarkers: Boolean;
  118. procedure GetPaths(const drawDat: TDrawData); virtual;
  119. // GetSimplePath: is only required for markers
  120. function GetSimplePath(const drawDat: TDrawData): TPathsD; virtual;
  121. procedure DrawFilled(img: TImage32;
  122. const paths: TPathsD; drawDat: TDrawData);
  123. procedure DrawStroke(img: TImage32;
  124. const paths: TPathsD; drawDat: TDrawData; isClosed: Boolean);
  125. procedure DrawMarkers(img: TImage32; drawDat: TDrawData);
  126. procedure Draw(image: TImage32; drawDat: TDrawData); override;
  127. public
  128. constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
  129. end;
  130. TSvgElement = class(TShapeElement)
  131. protected
  132. procedure Draw(image: TImage32; drawDat: TDrawData); override;
  133. public
  134. viewboxWH : TRectWH;
  135. function Width : TValue;
  136. function Height : TValue;
  137. end;
  138. TSvgReader = class
  139. private
  140. fSvgParser : TSvgParser;
  141. fBkgndColor : TColor32;
  142. fBackgndImage : TImage32;
  143. fTempImage : TImage32;
  144. fBlurQuality : integer;
  145. fIdList : TSvgIdNameHashMap;
  146. fLinGradRenderer : TLinearGradientRenderer;
  147. fRadGradRenderer : TSvgRadialGradientRenderer;
  148. fCustomRendererCache: TCustomRendererCache;
  149. fRootElement : TSvgElement;
  150. fFontCache : TFontCache;
  151. fUsePropScale : Boolean;
  152. fSimpleDraw : Boolean;
  153. fSimpleDrawList : TList;
  154. function LoadInternal: Boolean;
  155. function GetIsEmpty: Boolean;
  156. function GetTempImage: TImage32;
  157. procedure SetBlurQuality(quality: integer);
  158. protected
  159. userSpaceBounds : TRectD;
  160. currentColor : TColor32;
  161. procedure GetBestFont(const svgFontInfo: TSVGFontInfo);
  162. property RadGradRenderer: TSvgRadialGradientRenderer read fRadGradRenderer;
  163. property LinGradRenderer: TLinearGradientRenderer read fLinGradRenderer;
  164. property BackgndImage : TImage32 read fBackgndImage;
  165. property TempImage : TImage32 read GetTempImage;
  166. public
  167. constructor Create;
  168. destructor Destroy; override;
  169. procedure Clear;
  170. procedure DrawImage(img: TImage32; scaleToImage: Boolean);
  171. function LoadFromStream(stream: TStream): Boolean;
  172. function LoadFromFile(const filename: string): Boolean;
  173. function LoadFromString(const str: string): Boolean;
  174. // The following two methods are deprecated and intended only for ...
  175. // https://github.com/EtheaDev/SVGIconImageList
  176. procedure SetOverrideFillColor(color: TColor32); //deprecated;
  177. procedure SetOverrideStrokeColor(color: TColor32); //deprecated;
  178. function FindElement(const idName: UTF8String): TBaseElement;
  179. property BackgroundColor : TColor32 read fBkgndColor write fBkgndColor;
  180. property BlurQuality : integer read fBlurQuality write SetBlurQuality;
  181. property IsEmpty : Boolean read GetIsEmpty;
  182. // KeepAspectRatio: this property has also been added for the convenience of
  183. // the third-party SVGIconImageList. (IMHO it should always = true)
  184. property KeepAspectRatio: Boolean
  185. read fUsePropScale write fUsePropScale;
  186. property RootElement : TSvgElement read fRootElement;
  187. // RecordSimpleDraw: record simple drawing instructions
  188. property RecordSimpleDraw: Boolean read fSimpleDraw write fSimpleDraw;
  189. // SimpleDrawList: list of PSimpleDrawData records;
  190. property SimpleDrawList : TList read fSimpleDrawList;
  191. end;
  192. PSimpleDrawData = ^TSimpleDrawData;
  193. TSimpleDrawData = record
  194. paths : TPathsD;
  195. fillRule : TFillRule;
  196. color : TColor32;
  197. tag : integer;
  198. end;
  199. var
  200. // https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/width
  201. defaultSvgWidth: integer = 300;
  202. // https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/height
  203. defaultSvgHeight: integer = 150;
  204. implementation
  205. uses
  206. Img32.Extra, Img32.Clipper2;
  207. type
  208. TFourDoubles = array [0..3] of double;
  209. TDefsElement = class(TBaseElement)
  210. public
  211. constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
  212. end;
  213. TStyleElement = class(TBaseElement)
  214. public
  215. constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
  216. end;
  217. // TImageElement only supports *embedded* jpg & png images.
  218. // And it requires Img32.Fmt.JPG & Img32.Fmt.PNG to be included
  219. // in the USES clause of at least one of the application's units.
  220. // (nb: If using the FMX framework, then add Img32.FMX instead of
  221. // Img32.Fmt.JPG & Img32.Fmt.PNG to the USES clause.)
  222. TImageElement = class(TBaseElement)
  223. private
  224. fRefEl: UTF8String;
  225. fImage: TImage32;
  226. fTransparent: Boolean;
  227. protected
  228. procedure Draw(image: TImage32; drawDat: TDrawData); override;
  229. public
  230. destructor Destroy; override;
  231. end;
  232. TGroupElement = class(TShapeElement)
  233. protected
  234. procedure Draw(image: TImage32; drawDat: TDrawData); override;
  235. end;
  236. TSwitchElement = class(TShapeElement)
  237. protected
  238. procedure Draw(image: TImage32; drawDat: TDrawData); override;
  239. end;
  240. TUseElement = class(TShapeElement)
  241. private
  242. callerUse: TBaseElement;
  243. function ValidateNonRecursion(el: TBaseElement): Boolean;
  244. protected
  245. fRefEl: UTF8String;
  246. procedure GetPaths(const drawDat: TDrawData); override;
  247. procedure Draw(img: TImage32; drawDat: TDrawData); override;
  248. end;
  249. TMaskElement = class(TShapeElement)
  250. protected
  251. maskRec: TRect;
  252. procedure GetPaths(const drawDat: TDrawData); override;
  253. procedure ApplyMask(img: TImage32; const drawDat: TDrawData);
  254. end;
  255. TSymbolElement = class(TShapeElement)
  256. protected
  257. viewboxWH: TRectWH;
  258. public
  259. constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
  260. end;
  261. //-------------------------------------
  262. TPathElement = class(TShapeElement)
  263. private
  264. fSvgPaths : TSvgPath;
  265. procedure Flatten(index: integer; scalePending: double;
  266. out path: TPathD; out isClosed: Boolean);
  267. protected
  268. function GetBounds: TRectD; override;
  269. procedure ParseDAttrib(const value: UTF8String);
  270. procedure GetPaths(const drawDat: TDrawData); override;
  271. function GetSimplePath(const drawDat: TDrawData): TPathsD; override;
  272. public
  273. constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
  274. destructor Destroy; override;
  275. end;
  276. TPolyElement = class(TShapeElement) //polyline or polygon
  277. protected
  278. path : TPathD;
  279. function GetBounds: TRectD; override;
  280. procedure ParsePoints(const value: UTF8String);
  281. procedure GetPaths(const drawDat: TDrawData); override;
  282. function GetSimplePath(const drawDat: TDrawData): TPathsD; override;
  283. end;
  284. TLineElement = class(TShapeElement)
  285. protected
  286. path : TPathD;
  287. function GetBounds: TRectD; override;
  288. procedure GetPaths(const drawDat: TDrawData); override;
  289. function GetSimplePath(const drawDat: TDrawData): TPathsD; override;
  290. public
  291. constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
  292. end;
  293. TCircleElement = class(TShapeElement)
  294. protected
  295. bounds : TRectD;
  296. centerPt : TValuePt;
  297. radius : TValue;
  298. function GetBounds: TRectD; override;
  299. procedure GetPaths(const drawDat: TDrawData); override;
  300. public
  301. constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
  302. end;
  303. TEllipseElement = class(TShapeElement)
  304. protected
  305. bounds : TRectD;
  306. centerPt : TValuePt;
  307. radius : TValuePt;
  308. function GetBounds: TRectD; override;
  309. procedure GetPaths(const drawDat: TDrawData); override;
  310. public
  311. constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
  312. end;
  313. TRectElement = class(TShapeElement)
  314. protected
  315. radius : TValuePt;
  316. function GetBounds: TRectD; override;
  317. procedure GetPaths(const drawDat: TDrawData); override;
  318. function GetSimplePath(const drawDat: TDrawData): TPathsD; override;
  319. public
  320. constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
  321. end;
  322. TTSpanElement = class;
  323. // TTextElement: although a TShapeElement descendant, it's really just
  324. // a container for other TShapeElements (<tspan>, <textpath> etc).
  325. TTextElement = class(TShapeElement)
  326. protected
  327. offset : TValuePt;
  328. textDx : double;
  329. angle : TArrayOfDouble;
  330. currentPt : TPointD;
  331. currSpanEl : TTSpanElement; //the current 'real' <tspan>
  332. lastChrSpc : Boolean;
  333. procedure Draw(img: TImage32; drawDat: TDrawData); override;
  334. public
  335. constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
  336. end;
  337. TTextSubElement = class(TShapeElement)
  338. protected
  339. offset : TValuePt;
  340. textEl : TTextElement;
  341. function GetTextEl: TTextElement;
  342. end;
  343. TTSpanElement = class(TTextSubElement)
  344. protected
  345. chunkDx : double;
  346. angle : TArrayOfDouble;
  347. procedure GetPaths(const drawDat: TDrawData); override;
  348. public
  349. procedure Draw(image: TImage32; drawDat: TDrawData); override;
  350. end;
  351. TTextPathElement = class(TTextSubElement)
  352. private
  353. pathEl: TPathElement;
  354. scale: double;
  355. protected
  356. pathName : UTF8String; //name (id) of path element
  357. procedure GetPathsInternal(el: TBaseElement; const drawDat: TDrawData);
  358. procedure GetPaths(const drawDat: TDrawData); override;
  359. function GetBounds: TRectD; override;
  360. public
  361. procedure Draw(image: TImage32; drawDat: TDrawData); override;
  362. end;
  363. TTextAreaElement = class(TShapeElement)
  364. protected
  365. procedure GetPaths(const drawDat: TDrawData); override;
  366. end;
  367. TMarkerElement = class(TShapeElement)
  368. private
  369. fPoints : TPathD;
  370. protected
  371. refPt : TValuePt;
  372. angle : double;
  373. angle2 : double;
  374. markerBoxWH : TRectWH;
  375. autoStartReverse : Boolean;
  376. procedure SetEndPoint(const pt: TPointD; angle: double);
  377. function SetMiddlePoints(const points: TPathD): Boolean;
  378. procedure Draw(img: TImage32; drawDat: TDrawData); override;
  379. public
  380. constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
  381. end;
  382. TSvgColorStop = record
  383. offset : double;
  384. color : TColor32;
  385. end;
  386. TSvgColorStops = array of TSvgColorStop;
  387. TFillElement = class(TBaseElement)
  388. protected
  389. refEl : UTF8String;
  390. units : Cardinal;
  391. function GetRelFracLimit: double; override;
  392. end;
  393. TPatternElement = class(TFillElement)
  394. protected
  395. ImgRenderer : TImageRenderer;
  396. pattBoxWH : TRectWH;
  397. function PrepareRenderer(renderer: TImageRenderer;
  398. drawDat: TDrawData): Boolean; virtual;
  399. public
  400. constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
  401. destructor Destroy; override;
  402. end;
  403. //nb: gradients with objectBoundingBox should not be applied to
  404. //elements without width and height.
  405. TGradientElement = class(TFillElement)
  406. protected
  407. stops : TSvgColorStops;
  408. spreadMethod : TGradientFillStyle;
  409. function LoadContent: Boolean; override;
  410. procedure AddStop(color: TColor32; offset: double);
  411. procedure AssignTo(other: TBaseElement); virtual;
  412. function PrepareRenderer(renderer: TCustomGradientRenderer;
  413. drawDat: TDrawData): Boolean; virtual;
  414. procedure AddColorStopsToRenderer(renderer: TCustomGradientRenderer);
  415. end;
  416. TRadGradElement = class(TGradientElement)
  417. protected
  418. radius: TValuePt;
  419. F, C: TValuePt;
  420. procedure AssignTo(other: TBaseElement); override;
  421. function PrepareRenderer(renderer: TCustomGradientRenderer;
  422. drawDat: TDrawData): Boolean; override;
  423. public
  424. constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
  425. end;
  426. TLinGradElement = class(TGradientElement)
  427. protected
  428. startPt, endPt: TValuePt;
  429. procedure AssignTo(other: TBaseElement); override;
  430. function PrepareRenderer(renderer: TCustomGradientRenderer;
  431. drawDat: TDrawData): Boolean; override;
  432. public
  433. constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
  434. end;
  435. TGradStopElement = class(TBaseElement)
  436. protected
  437. offset: double;
  438. color: TColor32;
  439. public
  440. constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
  441. end;
  442. TFilterElement = class(TBaseElement)
  443. private
  444. fSrcImg : TImage32;
  445. fLastImg : TImage32;
  446. fScale : double;
  447. fFilterBounds : TRect;
  448. fObjectBounds : TRect;
  449. fImages : array of TImage32;
  450. fNames : array of UTF8String;
  451. protected
  452. procedure Clear;
  453. function GetRelFracLimit: double; override;
  454. function GetAdjustedBounds(const bounds: TRectD): TRectD;
  455. function FindNamedImage(const name: UTF8String): TImage32;
  456. function AddNamedImage(const name: UTF8String): TImage32;
  457. function GetNamedImage(const name: UTF8String; isIn: Boolean): TImage32;
  458. procedure Apply(img: TImage32;
  459. const filterBounds: TRect; const matrix: TMatrixD);
  460. public
  461. constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
  462. destructor Destroy; override;
  463. end;
  464. TFeBaseElement = class(TBaseElement)
  465. private
  466. function GetParentAsFilterEl: TFilterElement;
  467. protected
  468. in1: UTF8String;
  469. in2: UTF8String;
  470. res: UTF8String;
  471. srcImg, dstImg: TImage32;
  472. srcRec, dstRec: TRect;
  473. function GetSrcAndDst: Boolean;
  474. function GetBounds(img: TImage32): TRect;
  475. procedure Apply; virtual; abstract;
  476. property ParentFilterEl: TFilterElement read GetParentAsFilterEl;
  477. end;
  478. TFeBlendElement = class(TFeBaseElement)
  479. protected
  480. procedure Apply; override;
  481. end;
  482. TFeImageElement = class(TFeBaseElement)
  483. private
  484. refEl: UTF8String;
  485. fImage: TImage32;
  486. protected
  487. procedure Apply; override;
  488. public
  489. destructor Destroy; override;
  490. end;
  491. TCompositeOp = (coOver, coIn, coOut, coAtop, coXOR, coArithmetic);
  492. TFeCompositeElement = class(TFeBaseElement)
  493. protected
  494. fourKs: TFourDoubles; //arithmetic constants
  495. compositeOp: TCompositeOp;
  496. procedure Apply; override;
  497. public
  498. constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
  499. end;
  500. TFeColorMatrixElement = class(TFeBaseElement)
  501. protected
  502. values: TArrayOfDouble;
  503. procedure Apply; override;
  504. end;
  505. TFuncType = (ftIdentity, ftTable, ftDiscrete, ftLinear, ftGamma);
  506. TFeComponentTransferElement = class(TFeBaseElement)
  507. protected
  508. procedure Apply; override;
  509. end;
  510. TFeComponentTransferChild = class(TBaseElement)
  511. protected
  512. bytes: TArrayOfByte;
  513. protected
  514. funcType: TFuncType;
  515. intercept: double;
  516. slope: double;
  517. tableValues: TArrayOfDouble;
  518. end;
  519. TFeFuncRElement = class(TFeComponentTransferChild)
  520. end;
  521. TFeFuncGElement = class(TFeComponentTransferChild)
  522. end;
  523. TFeFuncBElement = class(TFeComponentTransferChild)
  524. end;
  525. TFeFuncAElement = class(TFeComponentTransferChild)
  526. end;
  527. TFeDefuseLightElement = class(TFeBaseElement)
  528. protected
  529. color : TColor32;
  530. surfaceScale : double;
  531. diffuseConst : double;
  532. kernelSize : integer;
  533. procedure Apply; override;
  534. end;
  535. TFeDropShadowElement = class(TFeBaseElement)
  536. protected
  537. stdDev : double;
  538. offset : TValuePt;
  539. floodColor : TColor32;
  540. procedure Apply; override;
  541. public
  542. constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
  543. end;
  544. TFeFloodElement = class(TFeBaseElement)
  545. protected
  546. floodColor : TColor32;
  547. procedure Apply; override;
  548. public
  549. constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
  550. end;
  551. TFeGaussElement = class(TFeBaseElement)
  552. protected
  553. stdDev: double;
  554. procedure Apply; override;
  555. public
  556. constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
  557. end;
  558. TFeMergeElement = class(TFeBaseElement)
  559. protected
  560. procedure Apply; override;
  561. end;
  562. TFeMergeNodeElement = class(TFeBaseElement)
  563. protected
  564. procedure Apply; override;
  565. end;
  566. TFeOffsetElement = class(TFeBaseElement)
  567. protected
  568. offset : TValuePt;
  569. procedure Apply; override;
  570. end;
  571. TFePointLightElement = class(TFeBaseElement)
  572. protected
  573. z : double;
  574. end;
  575. TFeSpecLightElement = class(TFeBaseElement)
  576. protected
  577. exponent : double;
  578. color : TColor32;
  579. procedure Apply; override;
  580. end;
  581. TClipPathElement = class(TShapeElement)
  582. protected
  583. units: Cardinal;
  584. procedure GetPaths(const drawDat: TDrawData); override;
  585. public
  586. constructor Create(parent: TBaseElement; svgEl: TSvgXmlEl); override;
  587. end;
  588. //-------------------------------------
  589. const
  590. buffSize = 32;
  591. clAlphaSet = $00010101;
  592. SourceImage : UTF8String = 'SourceGraphic';
  593. //SourceAlpha : UTF8String = 'SourceAlpha';
  594. tmpFilterImg : UTF8String = 'tmp';
  595. //https://www.w3.org/TR/css-fonts-3/#font-family-prop
  596. emptyDrawInfo: TDrawData =
  597. (currentColor: clInvalid;
  598. fillColor: clInvalid; fillOpacity: InvalidD;
  599. fillRule: frNegative; fillEl: '';
  600. strokeColor: clInvalid; strokeOpacity: InvalidD;
  601. strokeWidth: (rawVal: InvalidD; unitType: utNumber);
  602. strokeCap: esPolygon; strokeJoin: jsMiter; strokeMitLim: 0.0; strokeEl : '';
  603. dashArray: nil; dashOffset: 0;
  604. fontInfo: (family: tfUnknown; familyNames: nil; size: 0; spacing: 0.0;
  605. spacesInText: sitUndefined; textLength: 0; italic: sfsUndefined;
  606. weight: -1; align: staUndefined; decoration: fdUndefined;
  607. baseShift: (rawVal: InvalidD; unitType: utNumber));
  608. markerStart: ''; markerMiddle: ''; markerEnd: '';
  609. filterElRef: ''; maskElRef: ''; clipElRef: '';
  610. matrix: ((1, 0, 0),(0, 1, 0),(0, 0, 1)); visible: true;
  611. useEl: nil; bounds: (Left:0; Top:0; Right:0; Bottom:0));
  612. var
  613. //defaultFontHeight: this size will be used to retrieve ALL glyph contours
  614. //(and later scaled as necessary). This relatively large default ensures
  615. //that contours will have adequate detail.
  616. defaultFontHeight: double = 20.0;
  617. //------------------------------------------------------------------------------
  618. // Miscellaneous functions ...
  619. //------------------------------------------------------------------------------
  620. function HashToElementClass(hash: Cardinal): TElementClass;
  621. begin
  622. case hash of
  623. hClippath : Result := TClipPathElement;
  624. hCircle : Result := TCircleElement;
  625. hDefs : Result := TDefsElement;
  626. hEllipse : Result := TEllipseElement;
  627. hFilter : Result := TFilterElement;
  628. hfeBlend : Result := TFeBlendElement;
  629. hfeColorMatrix : Result := TFeColorMatrixElement;
  630. hFeComponentTransfer : Result := TFeComponentTransferElement;
  631. hFeFuncR : Result := TFeFuncRElement;
  632. hFeFuncG : Result := TFeFuncGElement;
  633. hFeFuncB : Result := TFeFuncBElement;
  634. hFeFuncA : Result := TFeFuncAElement;
  635. hfeComposite : Result := TFeCompositeElement;
  636. hfeDefuseLighting : Result := TFeDefuseLightElement;
  637. hfeDropShadow : Result := TFeDropShadowElement;
  638. hfeFlood : Result := TFeFloodElement;
  639. hFeGaussianBlur : Result := TFeGaussElement;
  640. hFeImage : Result := TFeImageElement;
  641. hfeMerge : Result := TFeMergeElement;
  642. hfeMergeNode : Result := TFeMergeNodeElement;
  643. hfeOffset : Result := TFeOffsetElement;
  644. hfePointLight : Result := TFePointLightElement;
  645. hfeSpecularLighting : Result := TFeSpecLightElement;
  646. hG : Result := TGroupElement;
  647. hImage : Result := TImageElement;
  648. hLine : Result := TLineElement;
  649. hLineargradient : Result := TLinGradElement;
  650. hMarker : Result := TMarkerElement;
  651. hMask : Result := TMaskElement;
  652. hPath : Result := TPathElement;
  653. hPattern : Result := TPatternElement;
  654. hPolyline : Result := TPolyElement;
  655. hPolygon : Result := TPolyElement;
  656. hRadialgradient : Result := TRadGradElement;
  657. hRect : Result := TRectElement;
  658. hStop : Result := TGradStopElement;
  659. hStyle : Result := TStyleElement;
  660. hSvg : Result := TSvgElement;
  661. hSwitch : Result := TSwitchElement;
  662. hSymbol : Result := TSymbolElement;
  663. hText : Result := TTextElement;
  664. hTextArea : Result := TTextAreaElement;
  665. hTextPath : Result := TTextPathElement;
  666. hTSpan : Result := TTSpanElement;
  667. hUse : Result := TUseElement;
  668. else Result := TBaseElement; //use generic class
  669. end;
  670. end;
  671. //------------------------------------------------------------------------------
  672. procedure UpdateDrawInfo(var drawDat: TDrawData; thisElement: TBaseElement);
  673. begin
  674. with thisElement.fDrawData do
  675. begin
  676. if currentColor <> clInvalid then
  677. thisElement.fSvgReader.currentColor := currentColor;
  678. if fillRule <> frNegative then
  679. drawDat.fillRule := fillRule;
  680. if (fillColor = clCurrent) then
  681. drawDat.fillColor := thisElement.fSvgReader.currentColor
  682. else if (fillColor <> clInvalid) then
  683. drawDat.fillColor := fillColor;
  684. if fillOpacity <> InvalidD then
  685. drawDat.fillOpacity := fillOpacity;
  686. if (fillEl <> '') then
  687. drawDat.fillEl := fillEl;
  688. if (strokeColor = clCurrent) then
  689. drawDat.strokeColor := thisElement.fSvgReader.currentColor
  690. else if strokeColor <> clInvalid then
  691. drawDat.strokeColor := strokeColor;
  692. if strokeOpacity <> InvalidD then
  693. drawDat.strokeOpacity := strokeOpacity;
  694. if strokeWidth.IsValid then
  695. drawDat.strokeWidth := strokeWidth;
  696. if strokeCap = esPolygon then
  697. drawDat.strokeCap := strokeCap;
  698. if strokeJoin <> jsMiter then
  699. drawDat.strokeJoin := strokeJoin;
  700. if strokeMitLim > 0 then
  701. drawDat.strokeMitLim := strokeMitLim;
  702. if Assigned(dashArray) then
  703. drawDat.dashArray := Copy(dashArray, 0, Length(dashArray));
  704. if dashOffset <> 0 then
  705. drawDat.dashOffset := dashOffset;
  706. if (strokeEl <> '') then
  707. drawDat.strokeEl := strokeEl;
  708. if (clipElRef <> '') then
  709. drawDat.clipElRef := clipElRef;
  710. if (maskElRef <> '') then
  711. drawDat.maskElRef := maskElRef;
  712. if (filterElRef <> '') then
  713. drawDat.filterElRef := filterElRef;
  714. if not IsIdentityMatrix(matrix) then
  715. MatrixMultiply2(matrix, drawDat.matrix);
  716. end;
  717. end;
  718. //------------------------------------------------------------------------------
  719. procedure UpdateFontInfo(var drawDat: TDrawData; thisElement: TBaseElement);
  720. begin
  721. with thisElement.fDrawData do
  722. begin
  723. if fontInfo.family <> tfUnknown then
  724. begin
  725. drawDat.fontInfo.family := fontInfo.family;
  726. drawDat.fontInfo.familyNames := nil;
  727. end;
  728. if Assigned(fontInfo.familyNames) then
  729. drawDat.fontInfo.familyNames := fontInfo.familyNames;
  730. if fontInfo.size > 0 then
  731. drawDat.fontInfo.size := fontInfo.size;
  732. if fontInfo.spacing <> 0 then
  733. drawDat.fontInfo.spacing := fontInfo.spacing;
  734. if fontInfo.textLength > 0 then
  735. drawDat.fontInfo.textLength := fontInfo.textLength;
  736. if (fontInfo.italic <> sfsUndefined) then
  737. drawDat.fontInfo.italic := fontInfo.italic;
  738. if (fontInfo.weight <> -1) then
  739. drawDat.fontInfo.weight := fontInfo.weight;
  740. if fontInfo.align <> staUndefined then
  741. drawDat.fontInfo.align := fontInfo.align;
  742. if fontInfo.spacesInText <> sitUndefined then
  743. drawDat.fontInfo.spacesInText := fontInfo.spacesInText;
  744. if (thisElement is TTextElement) or
  745. (fontInfo.decoration <> fdUndefined) then
  746. drawDat.fontInfo.decoration := fontInfo.decoration;
  747. if fontInfo.baseShift.IsValid then
  748. drawDat.fontInfo.baseShift := fontInfo.baseShift;
  749. end;
  750. end;
  751. //------------------------------------------------------------------------------
  752. function IsFilled(const drawDat: TDrawData): Boolean;
  753. begin
  754. with drawDat do
  755. Result := (fillOpacity <> 0) and
  756. ((fillColor <> clNone32) or (fillEl <> ''));
  757. end;
  758. //------------------------------------------------------------------------------
  759. function IsStroked(const drawDat: TDrawData): Boolean;
  760. begin
  761. with drawDat do
  762. if (strokeOpacity = 0) then
  763. Result := false
  764. else if (strokeEl = '') and
  765. ((strokeColor = clNone32) or (strokeColor = clInvalid)) then
  766. Result := false
  767. else
  768. Result := ((strokeWidth.rawVal = InvalidD) or (strokeWidth.rawVal > 0));
  769. end;
  770. //------------------------------------------------------------------------------
  771. function MergeColorAndOpacity(color: TColor32; opacity: double): TColor32;
  772. begin
  773. if (opacity < 0) or (opacity >= 1.0) then Result := color or $FF000000
  774. else if opacity = 0 then Result := clNone32
  775. else Result := (color and $FFFFFF) + Round(opacity * 255) shl 24;
  776. end;
  777. //------------------------------------------------------------------------------
  778. function UTF8StringToFloat(const ansiValue: UTF8String;
  779. out value: double): Boolean;
  780. var
  781. c: PUTF8Char;
  782. begin
  783. c := PUTF8Char(ansiValue);
  784. Result := ParseNextNum(c, c + Length(ansiValue), false, value);
  785. end;
  786. //------------------------------------------------------------------------------
  787. function UTF8StringToFloatEx(const ansiValue: UTF8String;
  788. var value: double; out measureUnit: TUnitType): Boolean;
  789. var
  790. c: PUTF8Char;
  791. begin
  792. c := PUTF8Char(ansiValue);
  793. Result := ParseNextNumEx(c, c + Length(ansiValue), false, value, measureUnit);
  794. end;
  795. //------------------------------------------------------------------------------
  796. procedure UTF8StringToOpacity(const ansiValue: UTF8String; var color: TColor32);
  797. var
  798. opacity: double;
  799. begin
  800. if color = clNone32 then
  801. begin
  802. color := clAlphaSet;
  803. Exit;
  804. end;
  805. if color = clInvalid then color := clNone32;
  806. if not UTF8StringToFloat(ansiValue, opacity) then Exit;
  807. with TARGB(color) do
  808. if (opacity <= 0) then
  809. begin
  810. if Color = clNone32 then Color := clAlphaSet
  811. else A := 0;
  812. end
  813. else if (opacity >= 1) then A := 255
  814. else A := Round(255 * opacity);
  815. end;
  816. //------------------------------------------------------------------------------
  817. // Note: This MatrixApply() is a function, whereas in Img32.Transform it's a procedure.
  818. function MatrixApply(const paths: TPathsD; const matrix: TMatrixD): TPathsD; overload;
  819. var
  820. i,j,len,len2: integer;
  821. pp,rr: PPointD;
  822. begin
  823. if not Assigned(paths) then
  824. Result := nil
  825. else if IsIdentityMatrix(matrix) then
  826. Result := CopyPaths(paths)
  827. else
  828. begin
  829. len := Length(paths);
  830. SetLength(Result, len);
  831. for i := 0 to len -1 do
  832. begin
  833. len2 := Length(paths[i]);
  834. NewPointDArray(Result[i], len2, True);
  835. if len2 = 0 then Continue;
  836. pp := @paths[i][0];
  837. rr := @Result[i][0];
  838. for j := 0 to High(paths[i]) do
  839. begin
  840. rr.X := pp.X * matrix[0, 0] + pp.Y * matrix[1, 0] + matrix[2, 0];
  841. rr.Y := pp.X * matrix[0, 1] + pp.Y * matrix[1, 1] + matrix[2, 1];
  842. inc(pp); inc(rr);
  843. end;
  844. end;
  845. end;
  846. end;
  847. //------------------------------------------------------------------------------
  848. function FixSpaces(const text: UnicodeString; trimLeadingSpace: Boolean): UnicodeString;
  849. var
  850. i,j, len: integer;
  851. begin
  852. //changes \r\n\t chars to spaces
  853. //and trims consecutive spaces
  854. len := Length(text);
  855. SetLength(Result, len);
  856. if len = 0 then Exit;
  857. if trimLeadingSpace then
  858. begin
  859. i := 1;
  860. while (i <= len) and (text[i] <= #32) do inc(i);
  861. if i > len then
  862. begin
  863. Result := '';
  864. Exit;
  865. end;
  866. Result[1] := text[i];
  867. inc(i);
  868. end else
  869. begin
  870. // allow a single leading space char
  871. if text[1] <= #32 then
  872. Result[1] := #32
  873. else
  874. Result[1] := text[1];
  875. i := 2;
  876. end;
  877. j := 1;
  878. for i := i to len do
  879. begin
  880. if (text[i] <= #32) then
  881. begin
  882. if (Result[j] = #32) then Continue;
  883. inc(j);
  884. Result[j] := #32;
  885. end else
  886. begin
  887. inc(j);
  888. Result[j] := text[i];
  889. end;
  890. end;
  891. SetLength(Result, j);
  892. end;
  893. //------------------------------------------------------------------------------
  894. function IsBlankText(const text: UnicodeString): Boolean;
  895. var
  896. i: integer;
  897. begin
  898. Result := false;
  899. for i := 1 to Length(text) do
  900. if (text[i] > #32) and (text[i] <> #160) then Exit;
  901. Result := true;
  902. end;
  903. //------------------------------------------------------------------------------
  904. function SvgTextAlignToTextAlign(svgAlign: TSvgTextAlign): TTextAlign;
  905. begin
  906. case svgAlign of
  907. staCenter: Result := taCenter;
  908. staRight: Result := taRight;
  909. staJustify: Result := taJustify;
  910. else Result := taLeft;
  911. end;
  912. end;
  913. //------------------------------------------------------------------------------
  914. // TSvgIdNameHashMap
  915. //------------------------------------------------------------------------------
  916. procedure TSvgIdNameHashMap.Grow;
  917. var
  918. Len, I: Integer;
  919. Index: Integer;
  920. begin
  921. Len := Length(FItems);
  922. if Len < 5 then
  923. Len := 5
  924. else
  925. Len := Len * 2;
  926. SetLength(FItems, Len);
  927. FMod := Cardinal(Len);
  928. if not Odd(FMod) then
  929. Inc(FMod);
  930. SetLengthUninit(FBuckets, FMod);
  931. FillChar(FBuckets[0], FMod * SizeOf(FBuckets[0]), $FF);
  932. // Rehash
  933. for I := 0 to FCount - 1 do
  934. begin
  935. Index := (FItems[I].Hash and $7FFFFFFF) mod FMod;
  936. FItems[I].Next := FBuckets[Index];
  937. FBuckets[Index] := I;
  938. end;
  939. end;
  940. //------------------------------------------------------------------------------
  941. function TSvgIdNameHashMap.FindItemIndex(const Name: UTF8String): Integer;
  942. var
  943. hash: Cardinal;
  944. begin
  945. Result := -1;
  946. if FMod = 0 then Exit;
  947. Hash := GetHash(Name);
  948. Result := FBuckets[(Hash and $7FFFFFFF) mod FMod];
  949. while (Result <> -1) and
  950. ((FItems[Result].Hash <> Hash) or
  951. not IsSameUTF8String(FItems[Result].Name, Name)) do
  952. Result := FItems[Result].Next;
  953. end;
  954. //------------------------------------------------------------------------------
  955. procedure TSvgIdNameHashMap.AddOrIgnore(const idName: UTF8String; element: TBaseElement);
  956. var
  957. Index: Integer;
  958. Hash: Cardinal;
  959. Item: PSvgIdNameHashMapItem;
  960. Bucket: PInteger;
  961. begin
  962. Index := FindItemIndex(idName);
  963. if Index >= 0 then
  964. Exit; // already exists so ignore;
  965. // add new item
  966. if FCount = Length(FItems) then Grow;
  967. Index := FCount;
  968. Inc(FCount);
  969. Hash := GetHash(idName);
  970. Bucket := @FBuckets[(Hash and $7FFFFFFF) mod FMod];
  971. Item := @FItems[Index];
  972. Item.Next := Bucket^;
  973. Item.Hash := Hash;
  974. Item.Name := idName;
  975. Item.Element := element;
  976. Bucket^ := Index;
  977. end;
  978. //------------------------------------------------------------------------------
  979. function TSvgIdNameHashMap.FindElement(const idName: UTF8String): TBaseElement;
  980. var
  981. Index: Integer;
  982. begin
  983. if FCount = 0 then
  984. Result := nil
  985. else
  986. begin
  987. Index := FindItemIndex(idName);
  988. if Index < 0 then
  989. Result := nil else
  990. Result := FItems[Index].Element
  991. end;
  992. end;
  993. //------------------------------------------------------------------------------
  994. procedure TSvgIdNameHashMap.Clear;
  995. begin
  996. FCount := 0;
  997. FMod := 0;
  998. FItems := nil;
  999. FBuckets := nil;
  1000. end;
  1001. //------------------------------------------------------------------------------
  1002. // TDefsElement
  1003. //------------------------------------------------------------------------------
  1004. constructor TDefsElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
  1005. begin
  1006. inherited;
  1007. fDrawData.visible := false;
  1008. end;
  1009. //------------------------------------------------------------------------------
  1010. // TStyleElement
  1011. //------------------------------------------------------------------------------
  1012. constructor TStyleElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
  1013. begin
  1014. inherited;
  1015. fDrawData.visible := false;
  1016. // See ParseStyleElementContent in Img32.Core.
  1017. end;
  1018. //------------------------------------------------------------------------------
  1019. // TImageElement
  1020. //------------------------------------------------------------------------------
  1021. function TrimAnySpaces(const s: UTF8String): UTF8String;
  1022. var
  1023. i, j, len: integer;
  1024. dst: PUTF8Char;
  1025. begin
  1026. len := Length(s);
  1027. SetLength(Result, len);
  1028. dst := PUTF8Char(Pointer(Result));
  1029. j := 0;
  1030. for i := 1 to len do
  1031. if s[i] > #32 then
  1032. begin
  1033. dst[j] := s[i];
  1034. inc(j);
  1035. end;
  1036. if j <> len then
  1037. SetLength(Result, j);
  1038. end;
  1039. //------------------------------------------------------------------------------
  1040. procedure ReadRefElImage(const refEl: UTF8String; out img: TImage32);
  1041. var
  1042. len, offset: integer;
  1043. s: UTF8String;
  1044. ms: TMemoryStream;
  1045. c: PUTF8Char;
  1046. begin
  1047. img := nil;
  1048. // unfortunately white spaces are sometimes found inside encoded base64
  1049. s := TrimAnySpaces(refEl);
  1050. len := Length(s);
  1051. // currently only accepts **embedded** images
  1052. if (len = 0) then Exit;
  1053. c := PUTF8Char(s);
  1054. if not Match(c, 'data:image/') then Exit;
  1055. if Match(@c[11], 'jpg;base64,') then offset := 22
  1056. else if Match(@c[11], 'jpeg;base64,') then offset := 23
  1057. else if Match(@c[11], 'png;base64,') then offset := 22
  1058. else Exit;
  1059. ms := TMemoryStream.Create;
  1060. try
  1061. if not Base64Decode(@c[offset], len -offset, ms) then Exit;
  1062. img := TImage32.Create;
  1063. if not img.LoadFromStream(ms) then
  1064. begin
  1065. FreeAndNil(img);
  1066. Exit;
  1067. end;
  1068. finally
  1069. ms.Free;
  1070. end;
  1071. end;
  1072. //------------------------------------------------------------------------------
  1073. // TImageElement
  1074. //------------------------------------------------------------------------------
  1075. destructor TImageElement.Destroy;
  1076. begin
  1077. if Assigned(fImage) then fImage.Free;
  1078. inherited Destroy;
  1079. end;
  1080. //------------------------------------------------------------------------------
  1081. procedure TImageElement.Draw(image: TImage32; drawDat: TDrawData);
  1082. var
  1083. dstRecD: TRectD;
  1084. tmp: TImage32;
  1085. tmpScale: TPointD;
  1086. begin
  1087. dstRecD := Self.elRectWH.GetRectD(0,0);
  1088. MatrixMultiply2(fDrawData.matrix, drawDat.matrix);
  1089. MatrixApply(drawDat.matrix, dstRecD);
  1090. if (fRefEl <> '') and not Assigned(fImage) then
  1091. begin
  1092. ReadRefElImage(fRefEl, fImage);
  1093. if Assigned(fImage) then
  1094. begin
  1095. fRefEl := ''; // ie avoid reloading fImage
  1096. fTransparent := fImage.HasTransparency;
  1097. end;
  1098. end;
  1099. if fImage <> nil then
  1100. begin
  1101. if elRectWH.IsValid then
  1102. begin
  1103. tmpScale.X := elRectWH.width.rawVal / fImage.Width;
  1104. tmpScale.Y := elRectWH.Height.rawVal / fImage.Height;
  1105. MatrixScale(drawDat.matrix, tmpScale.X, tmpScale.Y);
  1106. end;
  1107. tmp := TImage32.Create();
  1108. try
  1109. tmp.AssignSettings(fImage);
  1110. MatrixApply(drawDat.matrix, fImage, tmp);
  1111. // CopyBlend is slower than Copy, so only use it if we have a
  1112. // transparent image.
  1113. if fTransparent then
  1114. image.CopyBlend(tmp, tmp.Bounds, Rect(dstRecD), BlendToAlphaLine)
  1115. else
  1116. image.Copy(tmp, tmp.Bounds, Rect(dstRecD));
  1117. finally
  1118. tmp.Free;
  1119. end;
  1120. end;
  1121. end;
  1122. //------------------------------------------------------------------------------
  1123. // TGroupElement
  1124. //------------------------------------------------------------------------------
  1125. procedure TGroupElement.Draw(image: TImage32; drawDat: TDrawData);
  1126. var
  1127. clipEl : TClipPathElement;
  1128. maskEl : TMaskElement;
  1129. tmpImg : TImage32;
  1130. clipPaths : TPathsD;
  1131. clipRec : TRect;
  1132. dstClipRec: TRect;
  1133. offsetX, offsetY: integer;
  1134. fr: TFillRule;
  1135. begin
  1136. if fChilds.Count = 0 then Exit;
  1137. UpdateDrawInfo(drawDat, self);
  1138. UpdateFontInfo(drawDat, self);
  1139. if drawDat.fillRule = frNegative then
  1140. drawDat.fillRule := frNonZero;
  1141. maskEl := TMaskElement(FindRefElement(drawDat.maskElRef));
  1142. clipEl := TClipPathElement(FindRefElement(drawDat.clipElRef));
  1143. if Assigned(clipEl) then
  1144. begin
  1145. drawDat.clipElRef := '';
  1146. with clipEl do
  1147. begin
  1148. GetPaths(drawDat);
  1149. clipPaths := CopyPaths(drawPathsC);
  1150. AppendPath(clipPaths, drawPathsO);
  1151. MatrixApply(drawDat.matrix, clipPaths);
  1152. clipRec := Img32.Vector.GetBounds(clipPaths);
  1153. end;
  1154. if IsEmptyRect(clipRec) then Exit;
  1155. dstClipRec := clipRec; // save for blending tmpImg to image
  1156. // Translate the clipPaths, clipRec and matrix
  1157. // to minimize the size of the mask image.
  1158. offsetX := clipRec.Left;
  1159. offsetY := clipRec.Top;
  1160. if offsetX < 0 then offsetX := 0;
  1161. if offsetY < 0 then offsetY := 0;
  1162. if (offsetX > 0) or (offsetY > 0) then
  1163. begin
  1164. MatrixTranslate(drawDat.matrix, -offsetX, -offsetY); // for DrawChildren
  1165. clipPaths := TranslatePath(clipPaths, -offsetX, -offsetY);
  1166. TranslateRect(clipRec, -offsetX, -offsetY);
  1167. end;
  1168. //nb: it's not safe to use fReader.TempImage when calling DrawChildren
  1169. tmpImg := TImage32.Create(Min(image.Width, clipRec.Right), Min(image.Height, clipRec.Bottom));
  1170. try
  1171. DrawChildren(tmpImg, drawDat);
  1172. if clipEl.fDrawData.fillRule = frNegative then
  1173. fr := frNonZero else
  1174. fr := clipEl.fDrawData.fillRule;
  1175. EraseOutsidePaths(tmpImg, clipPaths, fr, clipRec, fSvgReader.fCustomRendererCache);
  1176. image.CopyBlend(tmpImg, clipRec, dstClipRec, BlendToAlphaLine);
  1177. finally
  1178. tmpImg.Free;
  1179. end;
  1180. end
  1181. else if Assigned(maskEl) then
  1182. begin
  1183. drawDat.maskElRef := '';
  1184. with maskEl do
  1185. begin
  1186. GetPaths(drawDat);
  1187. clipRec := maskRec;
  1188. end;
  1189. // Translate the maskRec, the matix and the clipRec to minimize
  1190. // the size of the mask image.
  1191. dstClipRec := clipRec; // save for blending tmpImg to image
  1192. offsetX := -clipRec.Left;
  1193. offsetY := -clipRec.Top;
  1194. if offsetX > 0 then offsetX := 0;
  1195. if offsetY > 0 then offsetY := 0;
  1196. if (offsetX < 0) or (offsetY < 0) then
  1197. begin
  1198. MatrixTranslate(drawDat.matrix, offsetX, offsetY); // for DrawChildren
  1199. TranslateRect(clipRec, offsetX, offsetY);
  1200. TranslateRect(maskEl.maskRec, offsetX, offsetY);
  1201. end;
  1202. tmpImg := TImage32.Create(Min(image.Width, clipRec.Right), Min(image.Height, clipRec.Bottom));
  1203. try
  1204. DrawChildren(tmpImg, drawDat);
  1205. TMaskElement(maskEl).ApplyMask(tmpImg, drawDat);
  1206. image.CopyBlend(tmpImg, clipRec, dstClipRec, BlendToAlphaLine);
  1207. finally
  1208. tmpImg.Free;
  1209. end;
  1210. end else
  1211. DrawChildren(image, drawDat);
  1212. end;
  1213. //------------------------------------------------------------------------------
  1214. // TSwitchElement
  1215. //------------------------------------------------------------------------------
  1216. procedure TSwitchElement.Draw(image: TImage32; drawDat: TDrawData);
  1217. var
  1218. i: integer;
  1219. begin
  1220. for i := 0 to fChilds.Count -1 do
  1221. if TBaseElement(fChilds[i]) is TShapeElement then
  1222. with TShapeElement(fChilds[i]) do
  1223. if fDrawData.visible then
  1224. begin
  1225. Draw(image, drawDat);
  1226. break; //break after the first successful drawing
  1227. end;
  1228. end;
  1229. //------------------------------------------------------------------------------
  1230. // TUseElement
  1231. //------------------------------------------------------------------------------
  1232. procedure TUseElement.GetPaths(const drawDat: TDrawData);
  1233. var
  1234. el: TBaseElement;
  1235. dx, dy: double;
  1236. begin
  1237. if pathsLoaded or (fRefEl = '') then Exit;
  1238. el := FindRefElement(fRefEl);
  1239. if not Assigned(el) or not (el is TShapeElement) then Exit;
  1240. pathsLoaded := true;
  1241. with TShapeElement(el) do
  1242. begin
  1243. GetPaths(drawDat);
  1244. self.drawPathsC := CopyPaths(drawPathsC);
  1245. self.drawPathsO := CopyPaths(drawPathsO);
  1246. end;
  1247. if elRectWH.left.IsValid then
  1248. dx := elRectWH.left.rawVal else
  1249. dx := 0;
  1250. if elRectWH.top.IsValid then
  1251. dy := elRectWH.top.rawVal else
  1252. dy := 0;
  1253. if (dx <> 0) or (dy <> 0) then
  1254. begin
  1255. drawPathsC := TranslatePath(drawPathsC, dx, dy);
  1256. drawPathsO := TranslatePath(drawPathsO, dx, dy);
  1257. end;
  1258. end;
  1259. //------------------------------------------------------------------------------
  1260. function TUseElement.ValidateNonRecursion(el: TBaseElement): Boolean;
  1261. begin
  1262. Result := false;
  1263. while assigned(el) do
  1264. begin
  1265. if (el = Self) then Exit;
  1266. if not (el is TUseElement) then break; //shouldn't happen
  1267. el := TUseElement(el).callerUse;
  1268. end;
  1269. Result := true;
  1270. end;
  1271. //------------------------------------------------------------------------------
  1272. procedure TUseElement.Draw(img: TImage32; drawDat: TDrawData);
  1273. var
  1274. el: TBaseElement;
  1275. s, dx, dy: double;
  1276. scale, scale2: TPointD;
  1277. mat: TMatrixD;
  1278. begin
  1279. //make sure there's not recursion, either directly or indirectly
  1280. if not ValidateNonRecursion(drawDat.useEl) then Exit;
  1281. callerUse := drawDat.useEl;
  1282. drawDat.useEl := self;
  1283. el := FindRefElement(fRefEl);
  1284. if not Assigned(el) then Exit;
  1285. UpdateDrawInfo(drawDat, self); //nb: <use> attribs override el's.
  1286. MatrixExtractScale(drawDat.matrix, scale.X, scale.Y);
  1287. if elRectWH.left.IsValid then dx := elRectWH.left.rawVal else dx := 0;
  1288. if elRectWH.top.IsValid then dy := elRectWH.top.rawVal else dy := 0;
  1289. if (dx <> 0) or (dy <> 0) then
  1290. begin
  1291. mat := IdentityMatrix;
  1292. MatrixTranslate(mat, dx, dy);
  1293. MatrixMultiply2(mat, drawDat.matrix);
  1294. end;
  1295. if el is TSymbolElement then
  1296. begin
  1297. with TSymbolElement(el) do
  1298. begin
  1299. if not viewboxWH.IsEmpty then
  1300. begin
  1301. //scale the symbol according to its width and height attributes
  1302. if elRectWH.width.IsValid and elRectWH.height.IsValid then
  1303. begin
  1304. scale2.X := elRectWH.width.rawVal / viewboxWH.Width;
  1305. scale2.Y := elRectWH.height.rawVal / viewboxWH.Height;
  1306. if scale2.Y < scale2.X then s := scale2.Y else s := scale2.X;
  1307. //the following 3 lines will scale without translating
  1308. mat := IdentityMatrix;
  1309. MatrixScale(mat, s, s);
  1310. MatrixMultiply2(mat, drawDat.matrix);
  1311. drawDat.bounds := RectD(0,0,viewboxWH.Width, viewboxWH.Height);
  1312. end;
  1313. if self.elRectWH.width.IsValid and
  1314. self.elRectWH.height.IsValid then
  1315. begin
  1316. with viewboxWH do
  1317. begin
  1318. dx := -Left/Width * self.elRectWH.width.rawVal;
  1319. dy := -Top/Height * self.elRectWH.height.rawVal;
  1320. //scale <symbol> proportionally to fill the <use> element
  1321. scale2.X := self.elRectWH.width.rawVal / Width;
  1322. scale2.Y := self.elRectWH.height.rawVal / Height;
  1323. if scale2.Y < scale2.X then s := scale2.Y else s := scale2.X;
  1324. end;
  1325. mat := IdentityMatrix;
  1326. MatrixScale(mat, s, s);
  1327. MatrixTranslate(mat, dx, dy);
  1328. MatrixMultiply2(mat, drawDat.matrix);
  1329. //now center after scaling
  1330. if scale2.X > scale2.Y then
  1331. begin
  1332. if scale2.X > 1 then
  1333. begin
  1334. s := (self.elRectWH.width.rawVal - viewboxWH.Width) * 0.5;
  1335. MatrixTranslate(drawDat.matrix, s * scale.X, 0);
  1336. end;
  1337. end else if scale2.Y > 1 then
  1338. begin
  1339. s := (self.elRectWH.height.rawVal - viewboxWH.Height) * 0.5;
  1340. MatrixTranslate(drawDat.matrix, 0, s * scale.Y);
  1341. end;
  1342. end;
  1343. end;
  1344. DrawChildren(img, drawDat);
  1345. end;
  1346. end
  1347. else if el is TImageElement then
  1348. el.Draw(img, drawDat)
  1349. else if el is TShapeElement then
  1350. el.Draw(img, drawDat);
  1351. end;
  1352. //------------------------------------------------------------------------------
  1353. // TMaskElement
  1354. //------------------------------------------------------------------------------
  1355. procedure TMaskElement.GetPaths(const drawDat: TDrawData);
  1356. var
  1357. i : integer;
  1358. el : TShapeElement;
  1359. begin
  1360. maskRec := NullRect;
  1361. for i := 0 to fChilds.Count -1 do
  1362. if TBaseElement(fChilds[i]) is TShapeElement then
  1363. begin
  1364. el := TShapeElement(fChilds[i]);
  1365. el.GetPaths(drawDat);
  1366. maskRec := Img32.Vector.UnionRect(maskRec, Img32.Vector.GetBounds(el.drawPathsC));
  1367. Img32.Vector.UnionRect(maskRec, Img32.Vector.GetBounds(el.drawPathsO));
  1368. end;
  1369. MatrixApply(drawDat.matrix, maskRec);
  1370. end;
  1371. //------------------------------------------------------------------------------
  1372. procedure TMaskElement.ApplyMask(img: TImage32; const drawDat: TDrawData);
  1373. var
  1374. tmpImg: TImage32;
  1375. begin
  1376. tmpImg := TImage32.Create(Min(img.Width, maskRec.Right), Min(img.Height, maskRec.Bottom));
  1377. try
  1378. DrawChildren(tmpImg, drawDat);
  1379. img.CopyBlend(tmpImg, maskRec, maskRec, BlendBlueChannelLine);
  1380. finally
  1381. tmpImg.Free;
  1382. end;
  1383. end;
  1384. //------------------------------------------------------------------------------
  1385. // TSymbolElement
  1386. //------------------------------------------------------------------------------
  1387. constructor TSymbolElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
  1388. begin
  1389. inherited;
  1390. fDrawData.visible := false;
  1391. end;
  1392. //------------------------------------------------------------------------------
  1393. // TGradElement
  1394. //------------------------------------------------------------------------------
  1395. function TGradientElement.LoadContent: Boolean;
  1396. var
  1397. i: integer;
  1398. begin
  1399. Result := inherited LoadContent;
  1400. for i := 0 to fChilds.Count -1 do
  1401. if TBaseElement(fChilds[i]) is TGradStopElement then
  1402. with TGradStopElement(fChilds[i]) do
  1403. AddStop(color, offset);
  1404. end;
  1405. //------------------------------------------------------------------------------
  1406. procedure TGradientElement.AddStop(color: TColor32; offset: double);
  1407. var
  1408. len: integer;
  1409. begin
  1410. //if a stop is less than previous stops, it is set equal to the largest stop.
  1411. //If two stops are equal the last stop controls the color from that point.
  1412. len := Length(stops);
  1413. if (len > 0) and (stops[len-1].offset > offset) then
  1414. offset := stops[len-1].offset;
  1415. setLength(stops, len+1);
  1416. stops[len].offset := Min(1,Max(0, offset));
  1417. stops[len].color := color;
  1418. end;
  1419. //------------------------------------------------------------------------------
  1420. procedure TGradientElement.AssignTo(other: TBaseElement);
  1421. var
  1422. i, len: integer;
  1423. begin
  1424. if not Assigned(other) or not (other is TGradientElement) then Exit;
  1425. inherited;
  1426. with TGradientElement(other) do
  1427. begin
  1428. if units = 0 then
  1429. units := Self.units;
  1430. if Length(stops) = 0 then
  1431. begin
  1432. len := Length(self.stops);
  1433. SetLength(stops, len);
  1434. for i := 0 to len -1 do
  1435. stops[i] := Self.stops[i];
  1436. end;
  1437. if IsIdentityMatrix(fDrawData.matrix) then
  1438. fDrawData.matrix := self.fDrawData.matrix;
  1439. end;
  1440. end;
  1441. //------------------------------------------------------------------------------
  1442. function TGradientElement.PrepareRenderer(
  1443. renderer: TCustomGradientRenderer; drawDat: TDrawData): Boolean;
  1444. var
  1445. el: TBaseElement;
  1446. begin
  1447. if (refEl <> '') then
  1448. begin
  1449. el := FindRefElement(refEl);
  1450. if Assigned(el) and (el is TGradientElement) then
  1451. TGradientElement(el).AssignTo(self);
  1452. end;
  1453. Result := Length(stops) > 0;
  1454. end;
  1455. //------------------------------------------------------------------------------
  1456. procedure TGradientElement.AddColorStopsToRenderer(renderer: TCustomGradientRenderer);
  1457. var
  1458. i, hiStops: Integer;
  1459. begin
  1460. hiStops := High(stops);
  1461. if (hiStops = 0) or (renderer = nil) then Exit;
  1462. // If vector boundary-stops are implicit, then boundary
  1463. // and adjacent inner stop (explicit) should have the
  1464. // same color
  1465. if stops[0].offset > 0 then
  1466. with stops[0] do
  1467. renderer.InsertColorStop(offset, color);
  1468. for i := 1 to hiStops -1 do
  1469. with stops[i] do
  1470. renderer.InsertColorStop(offset, color);
  1471. // If vector boundary-stops are implicit, then boundary
  1472. // and adjacent inner stop (explicit) should have the
  1473. // same color
  1474. if stops[hiStops].offset < 1 then
  1475. with stops[hiStops] do
  1476. renderer.InsertColorStop(offset, color);
  1477. end;
  1478. //------------------------------------------------------------------------------
  1479. // TRadGradElement
  1480. //------------------------------------------------------------------------------
  1481. constructor TRadGradElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
  1482. begin
  1483. inherited;
  1484. radius.Init;
  1485. F.Init;
  1486. C.Init;
  1487. end;
  1488. //------------------------------------------------------------------------------
  1489. procedure TRadGradElement.AssignTo(other: TBaseElement);
  1490. begin
  1491. if not Assigned(other) or not (other is TGradientElement) then Exit;
  1492. inherited;
  1493. if other is TRadGradElement then
  1494. with TRadGradElement(other) do
  1495. begin
  1496. if not radius.IsValid then radius := self.radius;
  1497. if not C.IsValid then C := self.C;
  1498. if not F.IsValid then F := self.F;
  1499. end;
  1500. end;
  1501. //------------------------------------------------------------------------------
  1502. function TRadGradElement.PrepareRenderer(renderer: TCustomGradientRenderer;
  1503. drawDat: TDrawData): Boolean;
  1504. var
  1505. hiStops: integer;
  1506. cp, fp, r: TPointD;
  1507. scale, scale2: TPointD;
  1508. rec2, rec3: TRectD;
  1509. begin
  1510. inherited PrepareRenderer(renderer, drawDat);
  1511. hiStops := High(stops);
  1512. Result := hiStops >= 0;
  1513. if not Result then Exit;
  1514. if units = hUserSpaceOnUse then
  1515. rec2 := fSvgReader.userSpaceBounds else
  1516. rec2 := drawDat.bounds;
  1517. if radius.IsValid then
  1518. begin
  1519. if radius.X.HasFontUnits then
  1520. r := radius.GetPoint(drawDat.fontInfo.size, GetRelFracLimit) else
  1521. r := radius.GetPoint(rec2, GetRelFracLimit);
  1522. end else
  1523. begin
  1524. r.X := rec2.Width * 0.5;
  1525. r.Y := rec2.Height * 0.5;
  1526. end;
  1527. MatrixExtractScale(drawDat.matrix, scale.X, scale.Y);
  1528. MatrixExtractScale(fDrawData.matrix, scale2.X, scale2.Y);
  1529. r := ScalePoint(r, scale.X * scale2.X, scale.Y * scale2.Y);
  1530. if C.IsValid then
  1531. begin
  1532. if C.X.HasFontUnits then
  1533. cp := C.GetPoint(drawDat.fontInfo.size, GetRelFracLimit) else
  1534. cp := C.GetPoint(rec2, GetRelFracLimit);
  1535. cp := TranslatePoint(cp, rec2.Left, rec2.Top);
  1536. end else
  1537. cp := rec2.MidPoint;
  1538. MatrixApply(fDrawData.matrix, cp);
  1539. MatrixApply(drawDat.matrix, cp);
  1540. rec3 := RectD(cp.X-r.X, cp.Y-r.Y, cp.X+r.X, cp.Y+r.Y);
  1541. if F.IsValid then
  1542. begin
  1543. if F.X.HasFontUnits then
  1544. fp := F.GetPoint(drawDat.fontInfo.size, GetRelFracLimit) else
  1545. fp := F.GetPoint(rec2, GetRelFracLimit);
  1546. fp := TranslatePoint(fp, rec2.Left, rec2.Top);
  1547. MatrixApply(fDrawData.matrix, fp);
  1548. MatrixApply(drawDat.matrix, fp);
  1549. end else
  1550. fp := MidPoint(rec3);
  1551. with renderer as TSvgRadialGradientRenderer do
  1552. begin
  1553. SetParameters(Rect(rec3), Point(fp),
  1554. stops[0].color, stops[hiStops].color, spreadMethod);
  1555. AddColorStopsToRenderer(renderer);
  1556. end;
  1557. end;
  1558. //------------------------------------------------------------------------------
  1559. // TLinGradElement
  1560. //------------------------------------------------------------------------------
  1561. constructor TLinGradElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
  1562. begin
  1563. inherited;
  1564. startPt.Init;
  1565. endPt.Init;
  1566. end;
  1567. //------------------------------------------------------------------------------
  1568. procedure TLinGradElement.AssignTo(other: TBaseElement);
  1569. begin
  1570. if not Assigned(other) or not (other is TGradientElement) then Exit;
  1571. inherited;
  1572. if other is TLinGradElement then
  1573. with TLinGradElement(other) do
  1574. begin
  1575. if not startPt.IsValid then startPt := self.startPt;
  1576. if not endPt.IsValid then endPt := self.endPt;
  1577. end;
  1578. end;
  1579. //------------------------------------------------------------------------------
  1580. function TLinGradElement.PrepareRenderer(
  1581. renderer: TCustomGradientRenderer; drawDat: TDrawData): Boolean;
  1582. var
  1583. pt1, pt2: TPointD;
  1584. hiStops: integer;
  1585. rec2: TRectD;
  1586. begin
  1587. inherited PrepareRenderer(renderer, drawDat);
  1588. hiStops := High(stops);
  1589. Result := (hiStops >= 0);
  1590. if not Result then Exit;
  1591. //w3c-coords-units-01-b.svg
  1592. //if gradientUnits=objectBoundingBox (default) then all values must be
  1593. //percentages. Also... when the object's bounding box is not square, the
  1594. //gradient may render non-perpendicular relative to the gradient vector
  1595. //unless the gradient vector is vertical or horizontal.
  1596. //https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientUnits
  1597. if units = hUserSpaceOnUse then
  1598. rec2 := fSvgReader.userSpaceBounds else
  1599. rec2 := drawDat.bounds;
  1600. with TLinearGradientRenderer(renderer) do
  1601. begin
  1602. if startPt.X.HasFontUnits then
  1603. pt1 := startPt.GetPoint(drawDat.fontInfo.size, GetRelFracLimit) else
  1604. pt1 := startPt.GetPoint(rec2, GetRelFracLimit);
  1605. if (startPt.X.unitType <> utPixel) or
  1606. (units <> hUserSpaceOnUse) then
  1607. pt1.X := pt1.X + rec2.Left;
  1608. if (startPt.Y.unitType <> utPixel) or
  1609. (units <> hUserSpaceOnUse) then
  1610. pt1.Y := pt1.Y + rec2.Top;
  1611. MatrixApply(fDrawData.matrix, pt1);
  1612. MatrixApply(drawDat.matrix, pt1);
  1613. if not endPt.X.IsValid then
  1614. pt2.X := rec2.Width else
  1615. pt2.X := endPt.X.GetValue(rec2.Width, GetRelFracLimit);
  1616. pt2.Y := endPt.Y.GetValue(rec2.Height, GetRelFracLimit);
  1617. pt2 := TranslatePoint(pt2, rec2.Left, rec2.Top);
  1618. MatrixApply(fDrawData.matrix, pt2);
  1619. MatrixApply(drawDat.matrix, pt2);
  1620. if (units <> hUserSpaceOnUse) and
  1621. ((pt2.X <> pt1.X) or (pt2.Y <> pt1.Y)) then
  1622. begin
  1623. //skew the gradient
  1624. end;
  1625. SetParameters(pt1, pt2, stops[0].color,
  1626. stops[hiStops].color, spreadMethod);
  1627. AddColorStopsToRenderer(renderer);
  1628. end;
  1629. end;
  1630. //------------------------------------------------------------------------------
  1631. // TGradStopElement
  1632. //------------------------------------------------------------------------------
  1633. constructor TGradStopElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
  1634. begin
  1635. inherited;
  1636. color := clBlack32;
  1637. end;
  1638. //------------------------------------------------------------------------------
  1639. // TFilterElement
  1640. //------------------------------------------------------------------------------
  1641. constructor TFilterElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
  1642. begin
  1643. inherited;
  1644. fDrawData.visible := false;
  1645. elRectWH.Init;
  1646. end;
  1647. //------------------------------------------------------------------------------
  1648. destructor TFilterElement.Destroy;
  1649. begin
  1650. Clear;
  1651. inherited;
  1652. end;
  1653. //------------------------------------------------------------------------------
  1654. procedure TFilterElement.Clear;
  1655. var
  1656. i: integer;
  1657. begin
  1658. for i := 0 to High(fImages) do
  1659. fImages[i].Free;
  1660. fImages := nil;
  1661. fNames := nil;
  1662. fLastImg := nil;
  1663. end;
  1664. //------------------------------------------------------------------------------
  1665. function TFilterElement.GetRelFracLimit: double;
  1666. begin
  1667. // assume fractional values below 2.5 are always relative
  1668. Result := 2.5;
  1669. end;
  1670. //------------------------------------------------------------------------------
  1671. function TFilterElement.GetAdjustedBounds(const bounds: TRectD): TRectD;
  1672. var
  1673. recWH: TRectWH;
  1674. delta: TSizeD;
  1675. d: double;
  1676. pt: TPointD;
  1677. i: integer;
  1678. hasOffset: Boolean;
  1679. begin
  1680. fObjectBounds := Rect(bounds);
  1681. if elRectWH.IsValid then
  1682. begin
  1683. recWH := elRectWH.GetRectWH(bounds, GetRelFracLimit);
  1684. Result.Left := bounds.Left + recWH.Left;
  1685. Result.Top := bounds.Top + recWH.Top;
  1686. Result.Right := Result.Left + recWH.Width;
  1687. Result.Bottom := Result.Top + recWH.Height;
  1688. end else
  1689. begin
  1690. Result := bounds;
  1691. //when the filter's width and height are undefined then limit the filter
  1692. //margin to 20% of the bounds when just blurring, not also offsetting.
  1693. hasOffset := false;
  1694. delta.cx := 0; delta.cy := 0;
  1695. for i := 0 to ChildCount -1 do
  1696. begin
  1697. if Child[i] is TFeGaussElement then
  1698. begin
  1699. d := TFeGaussElement(Child[i]).stdDev * 3 * fScale;
  1700. delta.cx := delta.cx + d;
  1701. delta.cy := delta.cy + d;
  1702. end
  1703. else if Child[i] is TFeDropShadowElement then
  1704. with TFeDropShadowElement(Child[i]) do
  1705. begin
  1706. d := stdDev * 0.75 * fScale;
  1707. pt := offset.GetPoint(bounds, 1);
  1708. delta.cx := delta.cx + d + Abs(pt.X) * fScale;
  1709. delta.cy := delta.cy + d + Abs(pt.Y) * fScale;
  1710. hasOffset := true;
  1711. end
  1712. else if Child[i] is TFeOffsetElement then
  1713. with TFeOffsetElement(Child[i]) do
  1714. begin
  1715. pt := offset.GetPoint(bounds, 1);
  1716. delta.cx := delta.cx + Abs(pt.X) * fScale;
  1717. delta.cy := delta.cy + Abs(pt.Y) * fScale;
  1718. hasOffset := true;
  1719. end;
  1720. end;
  1721. if (delta.cx = InvalidD) or (delta.cy = InvalidD) then Exit;
  1722. //limit the filter margin to 20% if only blurring
  1723. if not hasOffset then
  1724. with delta, bounds do
  1725. begin
  1726. if cx > Width * 0.2 then cx := Width * 0.2;
  1727. if cy > Height * 0.2 then cy := Height * 0.2;
  1728. end;
  1729. Img32.Vector.InflateRect(Result, delta.cx, delta.cy);
  1730. end;
  1731. end;
  1732. //------------------------------------------------------------------------------
  1733. function TFilterElement.FindNamedImage(const name: UTF8String): TImage32;
  1734. var
  1735. i, len: integer;
  1736. begin
  1737. Result := nil;
  1738. len := Length(fNames);
  1739. for i := 0 to len -1 do
  1740. if name = fNames[i] then
  1741. begin
  1742. Result := fImages[i];
  1743. Break;
  1744. end;
  1745. end;
  1746. //------------------------------------------------------------------------------
  1747. function TFilterElement.AddNamedImage(const name: UTF8String): TImage32;
  1748. var
  1749. len, w, h: integer;
  1750. begin
  1751. len := Length(fNames);
  1752. SetLength(fNames, len+1);
  1753. SetLength(fImages, len+1);
  1754. RectWidthHeight(fFilterBounds, w, h);
  1755. Result := TImage32.Create(w, h);
  1756. fImages[len] := Result;
  1757. fNames[len] := name;
  1758. end;
  1759. //------------------------------------------------------------------------------
  1760. function TFilterElement.GetNamedImage(const name: UTF8String; isIn: Boolean): TImage32;
  1761. begin
  1762. Result := FindNamedImage(name);
  1763. if not Assigned(Result) then
  1764. Result := AddNamedImage(name)
  1765. else if not isIn then
  1766. Exit;
  1767. case GetHash(name) of
  1768. hBackgroundImage:
  1769. Result.Copy(fSvgReader.BackgndImage, fFilterBounds, Result.Bounds);
  1770. hBackgroundAlpha:
  1771. begin
  1772. Result.Copy(fSvgReader.BackgndImage, fFilterBounds, Result.Bounds);
  1773. Result.SetRGB(clNone32, Result.Bounds);
  1774. end;
  1775. hSourceGraphic:
  1776. Result.Copy(fSrcImg, fFilterBounds, Result.Bounds);
  1777. hSourceAlpha:
  1778. begin
  1779. Result.Copy(fSrcImg, fFilterBounds, Result.Bounds);
  1780. Result.SetRGB(clBlack32, Result.Bounds);
  1781. end;
  1782. end;
  1783. end;
  1784. //------------------------------------------------------------------------------
  1785. procedure TFilterElement.Apply(img: TImage32;
  1786. const filterBounds: TRect; const matrix: TMatrixD);
  1787. var
  1788. i: integer;
  1789. begin
  1790. MatrixExtractScale(matrix, fScale);
  1791. fFilterBounds := filterBounds;
  1792. Types.IntersectRect(fObjectBounds, fObjectBounds, img.Bounds);
  1793. fSrcImg := img;
  1794. try
  1795. for i := 0 to fChilds.Count -1 do
  1796. begin
  1797. case TBaseElement(fChilds[i]).fXmlEl.hash of
  1798. hfeBlend : TFeBlendElement(fChilds[i]).Apply;
  1799. hfeColorMatrix : TFeColorMatrixElement(fChilds[i]).Apply;
  1800. hFeComponentTransfer : TFeComponentTransferElement(fChilds[i]).Apply;
  1801. hfeComposite : TFeCompositeElement(fChilds[i]).Apply;
  1802. hfeDefuseLighting : TFeDefuseLightElement(fChilds[i]).Apply;
  1803. hfeDropShadow : TFeDropShadowElement(fChilds[i]).Apply;
  1804. hfeFlood : TFeFloodElement(fChilds[i]).Apply;
  1805. hfeImage : TFeImageElement(fChilds[i]).Apply;
  1806. hFeGaussianBlur : TFeGaussElement(fChilds[i]).Apply;
  1807. hfeMerge : TFeMergeElement(fChilds[i]).Apply;
  1808. hfeOffset : TFeOffsetElement(fChilds[i]).Apply;
  1809. hfeSpecularLighting : TFeSpecLightElement(fChilds[i]).Apply;
  1810. end;
  1811. end;
  1812. if Assigned(fLastImg) then
  1813. fSrcImg.Copy(fLastImg, fLastImg.Bounds, fFilterBounds);
  1814. finally
  1815. Clear;
  1816. end;
  1817. end;
  1818. //------------------------------------------------------------------------------
  1819. // TFeBaseElement
  1820. //------------------------------------------------------------------------------
  1821. function TFeBaseElement.GetParentAsFilterEl: TFilterElement;
  1822. var
  1823. el: TBaseElement;
  1824. begin
  1825. el := fParent;
  1826. while Assigned(el) and not (el is TFilterElement) do
  1827. el := el.fParent;
  1828. if not Assigned(el) then
  1829. Result := nil else
  1830. Result := TFilterElement(el);
  1831. end;
  1832. //------------------------------------------------------------------------------
  1833. function TFeBaseElement.GetBounds(img: TImage32): TRect;
  1834. var
  1835. pfe: TFilterElement;
  1836. begin
  1837. pfe := ParentFilterEl;
  1838. if img = pfe.fSrcImg then
  1839. Result := pfe.fFilterBounds else
  1840. Result := img.Bounds;
  1841. end;
  1842. //------------------------------------------------------------------------------
  1843. function TFeBaseElement.GetSrcAndDst: Boolean;
  1844. var
  1845. pfe: TFilterElement;
  1846. begin
  1847. pfe := ParentFilterEl;
  1848. if (in1 <> '') then
  1849. srcImg := pfe.GetNamedImage(in1, true)
  1850. else if Assigned(pfe.fLastImg) then
  1851. srcImg := pfe.fLastImg
  1852. else
  1853. srcImg := pfe.GetNamedImage(SourceImage, false);
  1854. if (res <> '') then
  1855. dstImg := pfe.GetNamedImage(res, false) else
  1856. dstImg := pfe.GetNamedImage(SourceImage, false);
  1857. Result := Assigned(srcImg) and Assigned(dstImg);
  1858. if not Result then Exit;
  1859. pfe.fLastImg := dstImg;
  1860. srcRec := GetBounds(srcImg);
  1861. dstRec := GetBounds(dstImg);
  1862. end;
  1863. //------------------------------------------------------------------------------
  1864. // TFeBlendElement
  1865. //------------------------------------------------------------------------------
  1866. procedure TFeBlendElement.Apply;
  1867. var
  1868. pfe: TFilterElement;
  1869. srcImg2, dstImg2: TImage32;
  1870. srcRec2, dstRec2: TRect;
  1871. begin
  1872. if not GetSrcAndDst then Exit;
  1873. pfe := ParentFilterEl;
  1874. if (in2 = '') then Exit;
  1875. if dstImg = srcImg then
  1876. dstImg2 := pfe.AddNamedImage(tmpFilterImg) else
  1877. dstImg2 := dstImg;
  1878. dstRec2 := GetBounds(dstImg2);
  1879. srcImg2 := pfe.GetNamedImage(in2, true);
  1880. srcRec2 := GetBounds(srcImg2);
  1881. dstImg2.CopyBlend(srcImg2, srcRec2, dstRec2, BlendToAlphaLine);
  1882. dstImg2.CopyBlend(srcImg, srcRec, dstRec2, BlendToAlphaLine);
  1883. if dstImg = srcImg then
  1884. dstImg.Copy(dstImg2, dstRec2, dstRec);
  1885. end;
  1886. //------------------------------------------------------------------------------
  1887. // TFeImageElement
  1888. //------------------------------------------------------------------------------
  1889. destructor TFeImageElement.Destroy;
  1890. begin
  1891. fImage.Free;
  1892. inherited Destroy;
  1893. end;
  1894. //------------------------------------------------------------------------------
  1895. procedure TFeImageElement.Apply;
  1896. begin
  1897. if GetSrcAndDst then
  1898. begin
  1899. if refEl <> '' then
  1900. ReadRefElImage(refEl, fImage); // also clears refEl
  1901. if fImage <> nil then
  1902. dstImg.Copy(fImage, fImage.Bounds, dstRec);
  1903. end;
  1904. end;
  1905. //------------------------------------------------------------------------------
  1906. // TFeCompositeElement
  1907. //------------------------------------------------------------------------------
  1908. constructor TFeCompositeElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
  1909. begin
  1910. inherited;
  1911. fourKs[0] := InvalidD; fourKs[1] := InvalidD;
  1912. fourKs[2] := InvalidD; fourKs[3] := InvalidD;
  1913. end;
  1914. //------------------------------------------------------------------------------
  1915. procedure Arithmetic(p1, p2, r: PColor32; const ks: array of byte);
  1916. var
  1917. c1 : PARGB absolute p1;
  1918. c2 : PARGB absolute p2;
  1919. res : PARGB absolute r;
  1920. begin
  1921. res.A := (((c1.A xor 255) * (c2.A xor 255)) shr 8) xor 255;
  1922. res.R := ClampByte((ks[0] * c1.R * c2.R +
  1923. ks[1] * c1.R * 255 + ks[2] * c2.R * 255 + ks[3] * 65025) shr 16);
  1924. res.G := ClampByte((ks[0] * c1.G * c2.G +
  1925. ks[1] * c1.G * 255 + ks[2] * c2.G * 255 + ks[3] * 65025) shr 16);
  1926. res.B := ClampByte((ks[0] * c1.B * c2.B +
  1927. ks[1] * c1.B * 255 + ks[2] * c2.B * 255 + ks[3] * 65025) shr 16);
  1928. end;
  1929. //------------------------------------------------------------------------------
  1930. procedure ArithmeticBlend(src1, src2, dst: TImage32;
  1931. const recS1, recS2, recDst: TRect; const ks: TFourDoubles);
  1932. var
  1933. kk: array[0..3] of byte;
  1934. w,h, w2,h2, w3,h3, i,j: integer;
  1935. p1,p2,r: PColor32;
  1936. begin
  1937. RectWidthHeight(recS1, w, h);
  1938. RectWidthHeight(recS2, w2, h2);
  1939. RectWidthHeight(recDst, w3, h3);
  1940. if (w2 <> w) or (w3 <> w) or (h2 <> h) or (h3 <> h) or
  1941. (ks[0] = InvalidD) or (ks[1] = InvalidD) or
  1942. (ks[2] = InvalidD) or (ks[3] = InvalidD) then Exit;
  1943. for i := 0 to 3 do
  1944. kk[i] := ClampByte(ks[i]*255);
  1945. for i := 0 to h -1 do
  1946. begin
  1947. p1 := @src1.Pixels[(recS1.Top + i) * src1.Width + recS1.Left];
  1948. p2 := @src2.Pixels[(recS2.Top + i) * src2.Width + recS2.Left];
  1949. r := @dst.Pixels[(recDst.Top + i) * dst.Width + recDst.Left];
  1950. for j := 0 to w -1 do
  1951. begin
  1952. Arithmetic(p1, p2, r, kk);
  1953. inc(p1); inc(p2); inc(r);
  1954. end;
  1955. end;
  1956. end;
  1957. //------------------------------------------------------------------------------
  1958. procedure TFeCompositeElement.Apply;
  1959. var
  1960. pfe: TFilterElement;
  1961. srcImg2, dstImg2: TImage32;
  1962. srcRec2, dstRec2: TRect;
  1963. begin
  1964. if not GetSrcAndDst then Exit;
  1965. pfe := ParentFilterEl;
  1966. if (in2 = '') then Exit;
  1967. srcImg2 := pfe.GetNamedImage(in2, true);
  1968. srcRec2 := GetBounds(srcImg2); //either filter bounds or image bounds
  1969. if (dstImg = srcImg) or (dstImg = srcImg2) then
  1970. dstImg2 := pfe.AddNamedImage(tmpFilterImg) else
  1971. dstImg2 := dstImg;
  1972. dstRec2 := GetBounds(dstImg2); //either filter bounds or image bounds
  1973. case compositeOp of
  1974. coIn:
  1975. begin
  1976. dstImg2.Copy(srcImg, srcRec, dstRec2);
  1977. dstImg2.CopyBlend(srcImg2, srcRec2, dstRec2, BlendMaskLine);
  1978. end;
  1979. coOut:
  1980. begin
  1981. dstImg2.Copy(srcImg, srcRec, dstRec2);
  1982. dstImg2.CopyBlend(srcImg2, srcRec2, dstRec2, BlendInvertedMaskLine);
  1983. end;
  1984. coAtop:
  1985. begin
  1986. dstImg2.Copy(srcImg2, srcRec2, dstRec2);
  1987. dstImg2.CopyBlend(srcImg, srcRec, dstRec2, BlendToAlphaLine);
  1988. dstImg2.CopyBlend(srcImg2, srcRec2, dstRec2, BlendMaskLine);
  1989. end;
  1990. coXOR:
  1991. begin
  1992. dstImg2.Copy(srcImg2, srcRec2, dstRec2);
  1993. dstImg2.CopyBlend(srcImg, srcRec, dstRec2, BlendToAlphaLine);
  1994. dstImg2.CopyBlend(srcImg2, srcRec2, dstRec2, BlendInvertedMaskLine);
  1995. end;
  1996. coArithmetic:
  1997. begin
  1998. ArithmeticBlend(srcImg, srcImg2, dstImg2,
  1999. srcRec, srcRec2, dstRec2, fourKs);
  2000. end;
  2001. else //coOver
  2002. begin
  2003. dstImg2.CopyBlend(srcImg2, srcRec2, dstRec2, BlendToAlphaLine);
  2004. dstImg2.CopyBlend(srcImg, srcRec, dstRec2, BlendToAlphaLine);
  2005. end;
  2006. end;
  2007. if (dstImg <> dstImg2) then
  2008. dstImg.Copy(dstImg2, dstRec2, dstRec);
  2009. end;
  2010. //------------------------------------------------------------------------------
  2011. // TFeColorMatrixElement
  2012. //------------------------------------------------------------------------------
  2013. type
  2014. TColorMatrix = array[0..19] of Byte;
  2015. function ApplyColorMatrix(color: TColor32; const mat: TColorMatrix): TColor32;
  2016. var
  2017. clrIn : TARGB absolute color;
  2018. clrOut: TARGB absolute Result;
  2019. begin
  2020. clrOut.R := ClampByte(MulBytes(mat[0],clrIn.R) + MulBytes(mat[1],clrIn.G) +
  2021. MulBytes(mat[2],clrIn.B) + MulBytes(mat[3],clrIn.A) + mat[4]);
  2022. clrOut.G := ClampByte(MulBytes(mat[5],clrIn.R) + MulBytes(mat[6],clrIn.G) +
  2023. MulBytes(mat[7],clrIn.B) + MulBytes(mat[8],clrIn.A) + mat[9]);
  2024. clrOut.B := ClampByte(MulBytes(mat[10],clrIn.R) + MulBytes(mat[11],clrIn.G) +
  2025. MulBytes(mat[12],clrIn.B) + MulBytes(mat[13],clrIn.A) + mat[14]);
  2026. clrOut.A := ClampByte(MulBytes(mat[15],clrIn.R) + MulBytes(mat[16],clrIn.G) +
  2027. MulBytes(mat[17],clrIn.B) + MulBytes(mat[18],clrIn.A) + mat[19]);
  2028. end;
  2029. //------------------------------------------------------------------------------
  2030. procedure TFeColorMatrixElement.Apply;
  2031. var
  2032. i,j, dx1,dx2: integer;
  2033. colorMatrix: TColorMatrix;
  2034. p1, p2: PColor32;
  2035. begin
  2036. if not GetSrcAndDst or not Assigned(values) then Exit;
  2037. for i := 0 to 19 do
  2038. colorMatrix[i] := ClampByte(Integer(Round(values[i]*255)));
  2039. dx1 := srcImg.Width - RectWidth(srcRec);
  2040. dx2 := dstImg.Width - RectWidth(dstRec);
  2041. p1 := @srcImg.Pixels[srcRec.Top * srcImg.Width + srcRec.Left];
  2042. p2 := @dstImg.Pixels[dstRec.Top * dstImg.Width + dstRec.Left];
  2043. for i := srcRec.Top to srcRec.Bottom -1 do
  2044. begin
  2045. for j := srcRec.Left to srcRec.Right -1 do
  2046. begin
  2047. p2^ := ApplyColorMatrix(p1^, colorMatrix);
  2048. inc(p1); inc(p2);
  2049. end;
  2050. inc(p1, dx1); inc(p2, dx2);
  2051. end;
  2052. end;
  2053. //------------------------------------------------------------------------------
  2054. // TFeComponentTransferElement
  2055. //------------------------------------------------------------------------------
  2056. procedure TFeComponentTransferElement.Apply;
  2057. var
  2058. i,j,k, dx1,dx2: integer;
  2059. d: double;
  2060. rangeSize: integer;
  2061. p1: PColor32;
  2062. p2: PARGB;
  2063. childFuncs: array[0..3] of TFeComponentTransferChild;
  2064. begin
  2065. if not GetSrcAndDst or (ChildCount = 0) then Exit;
  2066. for i := 0 to 3 do childFuncs[i] := nil;
  2067. for i := 0 to ChildCount -1 do
  2068. begin
  2069. if Child[i] is TFeFuncBElement then
  2070. childFuncs[0] := TFeFuncBElement(Child[i])
  2071. else if Child[i] is TFeFuncGElement then
  2072. childFuncs[1] := TFeFuncGElement(Child[i])
  2073. else if Child[i] is TFeFuncRElement then
  2074. childFuncs[2] := TFeFuncRElement(Child[i])
  2075. else if Child[i] is TFeFuncAElement then
  2076. childFuncs[3] := TFeFuncAElement(Child[i]);
  2077. end;
  2078. // build each childFuncs' bytes array
  2079. for k := 0 to 3 do
  2080. with childFuncs[k] do
  2081. begin
  2082. if not Assigned(childFuncs[k]) then Continue;
  2083. case funcType of
  2084. ftDiscrete:
  2085. begin
  2086. if Length(tableValues) = 0 then Continue;
  2087. SetLength(bytes, 256);
  2088. rangeSize := 256 div Length(tableValues);
  2089. for i:= 0 to High(tableValues) do
  2090. for j:= 0 to rangeSize -1 do
  2091. bytes[i*rangeSize + j] := ClampByte(tableValues[i] * 255);
  2092. end;
  2093. ftTable:
  2094. begin
  2095. if Length(tableValues) < 2 then Continue;
  2096. SetLength(bytes, 256);
  2097. rangeSize := 256 div (Length(tableValues) -1);
  2098. for i:= 0 to High(tableValues) -1 do
  2099. begin
  2100. intercept := tableValues[i];
  2101. slope := (tableValues[i+1] - intercept) / rangeSize;
  2102. for j:= 0 to rangeSize -1 do
  2103. bytes[i*rangeSize + j] := ClampByte((j * slope + intercept) * 255);
  2104. end;
  2105. end;
  2106. ftLinear:
  2107. begin
  2108. SetLength(bytes, 256);
  2109. d := intercept * 255;
  2110. for i:= 0 to 255 do
  2111. bytes[i] := ClampByte(i * slope + d);
  2112. end;
  2113. end;
  2114. end;
  2115. for k := 0 to 3 do
  2116. if Assigned(childFuncs[k]) and not Assigned(childFuncs[k].bytes) then
  2117. childFuncs[k] := nil;
  2118. dx1 := srcImg.Width - RectWidth(srcRec);
  2119. dx2 := dstImg.Width - RectWidth(dstRec);
  2120. p1 := @srcImg.Pixels[srcRec.Top * srcImg.Width + srcRec.Left];
  2121. p2 := @dstImg.Pixels[dstRec.Top * dstImg.Width + dstRec.Left];
  2122. for i := srcRec.Top to srcRec.Bottom -1 do
  2123. begin
  2124. for j := srcRec.Left to srcRec.Right -1 do
  2125. begin
  2126. p2.Color := p1^;
  2127. if Assigned(childFuncs[0]) then p2.B := childFuncs[0].bytes[p2.B];
  2128. if Assigned(childFuncs[1]) then p2.G := childFuncs[1].bytes[p2.G];
  2129. if Assigned(childFuncs[2]) then p2.R := childFuncs[2].bytes[p2.R];
  2130. if Assigned(childFuncs[3]) then p2.A := childFuncs[3].bytes[p2.A];
  2131. inc(p1); inc(p2);
  2132. end;
  2133. inc(p1, dx1); inc(p2, dx2);
  2134. end;
  2135. end;
  2136. //------------------------------------------------------------------------------
  2137. // TFeDefuseLightElement
  2138. //------------------------------------------------------------------------------
  2139. procedure TFeDefuseLightElement.Apply;
  2140. begin
  2141. //not implemented
  2142. if not GetSrcAndDst then Exit;
  2143. if srcImg <> dstImg then
  2144. dstImg.Copy(srcImg, srcRec, dstRec);
  2145. end;
  2146. //------------------------------------------------------------------------------
  2147. // TFeDropShadowElement
  2148. //------------------------------------------------------------------------------
  2149. constructor TFeDropShadowElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
  2150. begin
  2151. inherited;
  2152. stdDev := InvalidD;
  2153. floodColor := clInvalid;
  2154. offset.X.SetValue(0);
  2155. offset.Y.SetValue(0);
  2156. end;
  2157. //------------------------------------------------------------------------------
  2158. procedure TFeDropShadowElement.Apply;
  2159. var
  2160. alpha: Byte;
  2161. off: TPointD;
  2162. dstOffRec: TRect;
  2163. pfe: TFilterElement;
  2164. dropShadImg: TImage32;
  2165. begin
  2166. if not GetSrcAndDst then Exit;
  2167. pfe := ParentFilterEl;
  2168. dropShadImg := pfe.GetNamedImage(tmpFilterImg, false);
  2169. dropShadImg.Copy(srcImg, srcRec, dropShadImg.Bounds);
  2170. off := offset.GetPoint(RectD(pfe.fObjectBounds), GetRelFracLimit);
  2171. off := ScalePoint(off, pfe.fScale);
  2172. dstOffRec := dstRec;
  2173. with Point(off) do TranslateRect(dstOffRec, X, Y);
  2174. dstImg.Copy(srcImg, srcRec, dstOffRec);
  2175. dstImg.SetRGB(floodColor);
  2176. alpha := GetAlpha(floodColor);
  2177. if (alpha > 0) and (alpha < 255) then
  2178. dstImg.ReduceOpacity(alpha);
  2179. if stdDev > 0 then
  2180. FastGaussianBlur(dstImg, dstRec,
  2181. Ceil(stdDev *0.75 * ParentFilterEl.fScale) , 1);
  2182. dstImg.CopyBlend(dropShadImg, dropShadImg.Bounds, dstRec, BlendToAlphaLine);
  2183. end;
  2184. //------------------------------------------------------------------------------
  2185. // TFeFloodElement
  2186. //------------------------------------------------------------------------------
  2187. constructor TFeFloodElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
  2188. begin
  2189. inherited;
  2190. floodColor := clInvalid;
  2191. end;
  2192. //------------------------------------------------------------------------------
  2193. procedure TFeFloodElement.Apply;
  2194. var
  2195. rec: TRect;
  2196. begin
  2197. if not GetSrcAndDst then Exit;
  2198. if elRectWH.IsValid then
  2199. rec := Rect(elRectWH.GetRectD(RectD(srcRec), GetRelFracLimit)) else
  2200. rec := dstRec;
  2201. dstImg.FillRect(rec, floodColor);
  2202. end;
  2203. //------------------------------------------------------------------------------
  2204. // TFeGaussElement
  2205. //------------------------------------------------------------------------------
  2206. constructor TFeGaussElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
  2207. begin
  2208. inherited;
  2209. stdDev := InvalidD;
  2210. end;
  2211. //------------------------------------------------------------------------------
  2212. procedure TFeGaussElement.Apply;
  2213. begin
  2214. if (stdDev = InvalidD) or not GetSrcAndDst then Exit;
  2215. if srcImg <> dstImg then
  2216. dstImg.Copy(srcImg, srcRec, dstRec);
  2217. //GaussianBlur(dstImg, dstRec, Round(stdDev * ParentFilterEl.fScale));
  2218. // FastGaussianBlur is a very good approximation and also much faster.
  2219. // However, empirically stdDev/2 more closely emulates other renderers.
  2220. FastGaussianBlur(dstImg, dstRec, Ceil(stdDev/2 * ParentFilterEl.fScale));
  2221. end;
  2222. //------------------------------------------------------------------------------
  2223. // TFeMergeElement
  2224. //------------------------------------------------------------------------------
  2225. procedure TFeMergeElement.Apply;
  2226. var
  2227. i: integer;
  2228. tmpImg: TImage32;
  2229. pfe: TFilterElement;
  2230. begin
  2231. tmpImg := nil;
  2232. if not GetSrcAndDst then Exit;
  2233. pfe := ParentFilterEl;
  2234. for i := 0 to fChilds.Count -1 do
  2235. if TBaseElement(fChilds[i]) is TFeMergeNodeElement then
  2236. with TFeMergeNodeElement(fChilds[i]) do
  2237. begin
  2238. if not GetSrcAndDst then Continue;
  2239. if Assigned(tmpImg) then
  2240. tmpImg.CopyBlend(srcImg, srcRec, tmpImg.Bounds, BlendToAlphaLine)
  2241. else if srcImg = pfe.fSrcImg then
  2242. tmpImg := pfe.GetNamedImage(SourceImage, false)
  2243. else
  2244. tmpImg := srcImg;
  2245. end;
  2246. dstImg.Copy(tmpImg, tmpImg.Bounds, dstRec);
  2247. pfe.fLastImg := dstImg;
  2248. end;
  2249. //------------------------------------------------------------------------------
  2250. // TFeMergeNodeElement
  2251. //------------------------------------------------------------------------------
  2252. procedure TFeMergeNodeElement.Apply;
  2253. begin
  2254. //should never get here ;)
  2255. end;
  2256. //------------------------------------------------------------------------------
  2257. // TFeOffsetElement
  2258. //------------------------------------------------------------------------------
  2259. procedure TFeOffsetElement.Apply;
  2260. var
  2261. off: TPointD;
  2262. dstOffRec: TRect;
  2263. tmpImg: TImage32;
  2264. pfe: TFilterElement;
  2265. begin
  2266. if not GetSrcAndDst then Exit;
  2267. pfe := ParentFilterEl;
  2268. off := offset.GetPoint(RectD(pfe.fObjectBounds), GetRelFracLimit);
  2269. off := ScalePoint(off, pfe.fScale);
  2270. dstOffRec := dstRec;
  2271. with Point(off) do TranslateRect(dstOffRec, X, Y);
  2272. if srcImg = dstImg then
  2273. begin
  2274. tmpImg := pfe.GetNamedImage(tmpFilterImg, false);
  2275. tmpImg.Copy(srcImg, srcRec, tmpImg.Bounds);
  2276. dstImg.Clear(dstRec);
  2277. dstImg.Copy(tmpImg, tmpImg.Bounds, dstOffRec);
  2278. end else
  2279. begin
  2280. dstImg.Clear(dstRec);
  2281. dstImg.Copy(srcImg, srcRec, dstOffRec);
  2282. end;
  2283. end;
  2284. //------------------------------------------------------------------------------
  2285. // TFeSpecLightElement
  2286. //------------------------------------------------------------------------------
  2287. procedure TFeSpecLightElement.Apply;
  2288. begin
  2289. //not implemented
  2290. if not GetSrcAndDst then Exit;
  2291. if srcImg <> dstImg then
  2292. dstImg.Copy(srcImg, srcRec, dstRec);
  2293. end;
  2294. //------------------------------------------------------------------------------
  2295. // TClipPathElement
  2296. //------------------------------------------------------------------------------
  2297. constructor TClipPathElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
  2298. begin
  2299. inherited;
  2300. fDrawData.visible := false;
  2301. end;
  2302. //------------------------------------------------------------------------------
  2303. procedure TClipPathElement.GetPaths(const drawDat: TDrawData);
  2304. var
  2305. i: integer;
  2306. begin
  2307. if pathsLoaded then Exit;
  2308. pathsLoaded := true;
  2309. for i := 0 to fChilds.Count -1 do
  2310. if TBaseElement(fChilds[i]) is TShapeElement then
  2311. with TShapeElement(fChilds[i]) do
  2312. begin
  2313. GetPaths(drawDat);
  2314. AppendPath(self.drawPathsO, drawPathsO);
  2315. AppendPath(self.drawPathsC, drawPathsC);
  2316. // apply child's matrix ...
  2317. MatrixApply(DrawData.matrix, self.drawPathsC);
  2318. MatrixApply(DrawData.matrix, self.drawPathsO);
  2319. end;
  2320. // apply <clippath>'s matrix ...
  2321. MatrixApply(DrawData.matrix, drawPathsC);
  2322. MatrixApply(DrawData.matrix, drawPathsO);
  2323. end;
  2324. //------------------------------------------------------------------------------
  2325. // TShapeElement
  2326. //------------------------------------------------------------------------------
  2327. constructor TShapeElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
  2328. begin
  2329. inherited;
  2330. elRectWH.Init;
  2331. hasPaths := true;
  2332. fDrawData.visible := true;
  2333. if fXmlEl.name = '' then Exit;
  2334. end;
  2335. //------------------------------------------------------------------------------
  2336. function TShapeElement.GetBounds: TRectD;
  2337. var
  2338. i: integer;
  2339. begin
  2340. Result := UnionRect(GetBoundsD(drawPathsC), GetBoundsD(drawPathsO));
  2341. if Result.IsEmpty then
  2342. begin
  2343. Result := NullRectD;
  2344. for i := 0 to fChilds.Count -1 do
  2345. if TBaseElement(Child[i]) is TShapeElement then
  2346. Result := UnionRect(Result, TShapeElement(Child[i]).GetBounds);
  2347. end;
  2348. end;
  2349. //------------------------------------------------------------------------------
  2350. function TShapeElement.HasMarkers: Boolean;
  2351. begin
  2352. Result := IsStroked(fDrawData) and ((fDrawData.markerStart <> '') or
  2353. (fDrawData.markerMiddle <> '') or (fDrawData.markerEnd <> ''));
  2354. end;
  2355. //------------------------------------------------------------------------------
  2356. procedure TShapeElement.Draw(image: TImage32; drawDat: TDrawData);
  2357. var
  2358. d : double;
  2359. img : TImage32;
  2360. stroked : Boolean;
  2361. filled : Boolean;
  2362. tmpRec : TRectD;
  2363. clipRec : TRectD;
  2364. clipRec2 : TRect;
  2365. clipPathEl : TBaseElement;
  2366. filterEl : TBaseElement;
  2367. maskEl : TBaseElement;
  2368. clipPaths : TPathsD;
  2369. fillPaths : TPathsD;
  2370. di : TDrawData;
  2371. useTmpImage : Boolean;
  2372. begin
  2373. UpdateDrawInfo(drawDat, self);
  2374. filled := IsFilled(drawDat);
  2375. stroked := IsStroked(drawDat);
  2376. GetPaths(drawDat);
  2377. if not (filled or stroked) or not hasPaths then
  2378. begin
  2379. inherited;
  2380. Exit;
  2381. end;
  2382. tmpRec := GetBounds;
  2383. if not tmpRec.IsEmpty then
  2384. drawDat.bounds := tmpRec;
  2385. img := image;
  2386. clipRec2 := NullRect;
  2387. maskEl := FindRefElement(drawDat.maskElRef);
  2388. clipPathEl := FindRefElement(drawDat.clipElRef);
  2389. filterEl := FindRefElement(drawDat.filterElRef);
  2390. useTmpImage :=
  2391. Assigned(clipPathEl) or Assigned(filterEl) or Assigned(maskEl);
  2392. if useTmpImage then
  2393. begin
  2394. img := fSvgReader.TempImage;
  2395. //get special effects bounds
  2396. if Assigned(clipPathEl) then
  2397. begin
  2398. drawDat.clipElRef := '';
  2399. di := drawDat;
  2400. with TClipPathElement(clipPathEl) do
  2401. begin
  2402. GetPaths(di);
  2403. clipPaths := drawPathsC;
  2404. AppendPath(clipPaths, drawPathsO);
  2405. clipPaths := MatrixApply(clipPaths, di.matrix);
  2406. clipRec := GetBoundsD(clipPaths);
  2407. end;
  2408. end
  2409. else if Assigned(maskEl) then
  2410. begin
  2411. drawDat.maskElRef := '';
  2412. with TMaskElement(maskEl) do
  2413. begin
  2414. GetPaths(drawDat);
  2415. clipRec := RectD(maskRec);
  2416. end;
  2417. end else
  2418. begin
  2419. clipRec := drawDat.bounds;
  2420. if clipRec.IsEmpty and (drawDat.fontInfo.textLength > 0) and
  2421. (self is TTextPathElement) then
  2422. begin
  2423. clipRec.Left := fParent.elRectWH.left.rawVal;
  2424. clipRec.Bottom := fParent.elRectWH.top.rawVal;
  2425. clipRec.Right := clipRec.Left + drawDat.fontInfo.textLength;
  2426. clipRec.Top := clipRec.Bottom - drawDat.fontInfo.size;
  2427. end;
  2428. if stroked and drawDat.strokeWidth.IsValid then
  2429. begin
  2430. with drawDat.strokeWidth do
  2431. if HasFontUnits then
  2432. d := GetValue(drawDat.fontInfo.size, GetRelFracLimit) else
  2433. d := GetValueXY(clipRec, GetRelFracLimit);
  2434. Img32.Vector.InflateRect(clipRec, d * 0.5, d * 0.5);
  2435. end;
  2436. if Assigned(filterEl) then
  2437. begin
  2438. drawDat.filterElRef := '';
  2439. with TFilterElement(filterEl) do
  2440. begin
  2441. MatrixExtractScale(DrawData.matrix, fScale);
  2442. clipRec := GetAdjustedBounds(clipRec);
  2443. end;
  2444. end;
  2445. MatrixApply(drawDat.matrix, clipRec);
  2446. end;
  2447. clipRec2 := Rect(clipRec);
  2448. Types.IntersectRect(clipRec2, clipRec2, img.Bounds);
  2449. if IsEmptyRect(clipRec2) then Exit;
  2450. if image <> fSvgReader.TempImage then
  2451. img.Clear(clipRec2);
  2452. end;
  2453. if not IsValidMatrix(drawDat.matrix) then
  2454. raise Exception.Create('Invalid matrix found when drawing element');
  2455. if Assigned(drawPathsC) or Assigned(drawPathsO) then
  2456. begin
  2457. if filled then
  2458. begin
  2459. // it's slightly more efficient to apply the matrix here
  2460. // rather than inside DrawFilled().
  2461. fillPaths := drawPathsC;
  2462. if Assigned(drawPathsO) then
  2463. AppendPath(fillPaths, drawPathsO);
  2464. fillPaths := MatrixApply(fillPaths, drawDat.matrix);
  2465. DrawFilled(img, fillPaths, drawDat);
  2466. end;
  2467. if stroked then
  2468. begin
  2469. // it's slightly more efficient to apply the matrix
  2470. // inside DrawStroke() rather than here.
  2471. if Assigned(drawPathsC) then
  2472. DrawStroke(img, drawPathsC, drawDat, true);
  2473. if stroked and Assigned(drawPathsO) then
  2474. DrawStroke(img, drawPathsO, drawDat, false);
  2475. end;
  2476. end;
  2477. if Assigned(filterEl) then
  2478. with TFilterElement(filterEl) do
  2479. Apply(img, clipRec2, drawDat.matrix);
  2480. if Assigned(maskEl) then
  2481. TMaskElement(maskEl).ApplyMask(img, drawDat)
  2482. else if Assigned(clipPathEl) then
  2483. with TClipPathElement(clipPathEl) do
  2484. begin
  2485. if fDrawData.fillRule = frNegative then
  2486. EraseOutsidePaths(img, clipPaths, frNonZero, clipRec2,
  2487. fSvgReader.fCustomRendererCache) else
  2488. EraseOutsidePaths(img, clipPaths, fDrawData.fillRule, clipRec2,
  2489. fSvgReader.fCustomRendererCache);
  2490. end;
  2491. if useTmpImage and (img <> image) then
  2492. image.CopyBlend(img, clipRec2, clipRec2, BlendToAlphaLine);
  2493. //todo: enable "paint-order" to change filled/stroked/marker paint order
  2494. if HasMarkers then DrawMarkers(img, drawDat);
  2495. inherited; // DrawChildren
  2496. end;
  2497. //------------------------------------------------------------------------------
  2498. procedure TShapeElement.DrawMarkers(img: TImage32; drawDat: TDrawData);
  2499. var
  2500. i,j: integer;
  2501. scale, sw: double;
  2502. markerEl: TBaseElement;
  2503. markerPaths: TPathsD;
  2504. pt1, pt2: TPointD;
  2505. di: TDrawData;
  2506. begin
  2507. markerPaths := GetSimplePath(drawDat);
  2508. markerPaths := StripNearDuplicates(markerPaths, 0.01, false);
  2509. if not Assigned(markerPaths) then Exit;
  2510. MatrixApply(drawDat.matrix, markerPaths);
  2511. di := emptyDrawInfo;
  2512. //prepare to scale the markers by the stroke width
  2513. with fDrawData.strokeWidth do
  2514. if not IsValid then sw := 1
  2515. else if HasFontUnits then
  2516. sw := GetValue(drawDat.fontInfo.size, GetRelFracLimit)
  2517. else sw := GetValueXY(drawDat.bounds, GetRelFracLimit);
  2518. MatrixExtractScale(drawDat.matrix, scale);
  2519. MatrixScale(di.matrix, sw * scale);
  2520. if (fDrawData.markerStart <> '') then
  2521. begin
  2522. markerEl := FindRefElement(fDrawData.markerStart);
  2523. if Assigned(markerEl) and (markerEl is TMarkerElement) then
  2524. with TMarkerElement(markerEl) do
  2525. begin
  2526. for i := 0 to High(markerPaths) do
  2527. begin
  2528. if Length(markerPaths[i]) < 2 then Continue;
  2529. pt1 := markerPaths[i][0];
  2530. pt2 := markerPaths[i][1];
  2531. if autoStartReverse then
  2532. SetEndPoint(pt1, GetAngle(pt2, pt1)) else
  2533. SetEndPoint(pt1, GetAngle(pt1, pt2));
  2534. Draw(img, di);
  2535. end;
  2536. end;
  2537. end;
  2538. if (fDrawData.markerMiddle <> '') then
  2539. begin
  2540. markerEl := FindRefElement(fDrawData.markerMiddle);
  2541. if Assigned(markerEl) and (markerEl is TMarkerElement) then
  2542. with TMarkerElement(markerEl) do
  2543. for i := 0 to High(markerPaths) do
  2544. if SetMiddlePoints(markerPaths[i]) then
  2545. Draw(img, di);
  2546. end;
  2547. if (fDrawData.markerEnd <> '') then
  2548. begin
  2549. markerEl := FindRefElement(fDrawData.markerEnd);
  2550. if Assigned(markerEl) and (markerEl is TMarkerElement) then
  2551. with TMarkerElement(markerEl) do
  2552. begin
  2553. for i := 0 to High(markerPaths) do
  2554. begin
  2555. j := High(markerPaths[i]);
  2556. if j < 1 then Continue;
  2557. pt1 := markerPaths[i][j];
  2558. pt2 := markerPaths[i][j-1];
  2559. SetEndPoint(pt1, GetAngle(pt2, pt1));
  2560. Draw(img, di);
  2561. end;
  2562. end;
  2563. end;
  2564. end;
  2565. //------------------------------------------------------------------------------
  2566. procedure TShapeElement.GetPaths(const drawDat: TDrawData);
  2567. var
  2568. i: integer;
  2569. begin
  2570. for i := 0 to fChilds.Count -1 do
  2571. if TBaseElement(fChilds[i]) is TShapeElement then
  2572. TShapeElement(fChilds[i]).GetPaths(drawDat);
  2573. end;
  2574. //------------------------------------------------------------------------------
  2575. function TShapeElement.GetSimplePath(const drawDat: TDrawData): TPathsD;
  2576. begin
  2577. Result := nil;
  2578. end;
  2579. //------------------------------------------------------------------------------
  2580. procedure TShapeElement.DrawFilled(img: TImage32;
  2581. const paths: TPathsD; drawDat: TDrawData);
  2582. var
  2583. refEl: TBaseElement;
  2584. rec: TRect;
  2585. opacity: Byte;
  2586. begin
  2587. if not assigned(paths) then Exit;
  2588. if drawDat.fillColor = clCurrent then
  2589. drawDat.fillColor := fSvgReader.currentColor;
  2590. if drawDat.fillRule = frNegative then
  2591. drawDat.fillRule := frNonZero;
  2592. if not IsValid(drawDat.fillOpacity) then
  2593. opacity := 255 else
  2594. opacity := ClampByte(drawDat.fillOpacity * 255);
  2595. if (drawDat.fillEl <> '') then
  2596. begin
  2597. refEl := FindRefElement(drawDat.fillEl);
  2598. if Assigned(refEl) and (refEl is TFillElement) then
  2599. begin
  2600. if refEl is TRadGradElement then
  2601. begin
  2602. with TRadGradElement(refEl) do
  2603. begin
  2604. fSvgReader.RadGradRenderer.Opacity := opacity;
  2605. if PrepareRenderer(fSvgReader.RadGradRenderer, drawDat) then
  2606. DrawPolygon(img, paths, drawDat.fillRule, fSvgReader.RadGradRenderer);
  2607. end;
  2608. end
  2609. else if refEl is TLinGradElement then
  2610. begin
  2611. with TLinGradElement(refEl) do
  2612. begin
  2613. fSvgReader.LinGradRenderer.Opacity := opacity;
  2614. if PrepareRenderer(fSvgReader.LinGradRenderer, drawDat) then
  2615. DrawPolygon(img, paths, drawDat.fillRule, fSvgReader.LinGradRenderer);
  2616. end;
  2617. end
  2618. else if refEl is TPatternElement then
  2619. begin
  2620. with TPatternElement(refEl) do
  2621. if PrepareRenderer(ImgRenderer, drawDat) then
  2622. begin
  2623. rec := img32.Vector.GetBounds(paths);
  2624. ImgRenderer.Offset := rec.TopLeft;
  2625. DrawPolygon(img, paths, drawDat.fillRule, ImgRenderer);
  2626. end;
  2627. end;
  2628. end;
  2629. end
  2630. else if drawDat.fillColor = clInvalid then
  2631. begin
  2632. DrawPolygon(img, paths, drawDat.fillRule,
  2633. MergeColorAndOpacity(clBlack32, drawDat.fillOpacity),
  2634. fSvgReader.fCustomRendererCache);
  2635. end
  2636. else
  2637. with drawDat do
  2638. begin
  2639. DrawPolygon(img, paths, fillRule,
  2640. MergeColorAndOpacity(fillColor, fillOpacity),
  2641. fSvgReader.fCustomRendererCache);
  2642. end;
  2643. end;
  2644. //------------------------------------------------------------------------------
  2645. procedure TShapeElement.DrawStroke(img: TImage32;
  2646. const paths: TPathsD; drawDat: TDrawData; isClosed: Boolean);
  2647. var
  2648. i: integer;
  2649. dashOffset, sw: double;
  2650. dashArray: TArrayOfDouble;
  2651. miterLim, scale: Double;
  2652. strokeClr: TColor32;
  2653. strokePaths: TPathsD;
  2654. refEl: TBaseElement;
  2655. endStyle: TEndStyle;
  2656. joinStyle: TJoinStyle;
  2657. bounds: TRectD;
  2658. paths2: TPathsD;
  2659. opacity: Byte;
  2660. begin
  2661. if not Assigned(paths) then Exit;
  2662. MatrixExtractScale(drawDat.matrix, scale);
  2663. joinStyle := fDrawData.strokeJoin;
  2664. bounds := fSvgReader.userSpaceBounds;
  2665. with drawDat.strokeWidth do
  2666. begin
  2667. if not IsValid then
  2668. sw := 1
  2669. else if HasFontUnits then
  2670. sw := GetValue(drawDat.fontInfo.size, GetRelFracLimit)
  2671. else
  2672. sw := GetValueXY(bounds, 0);
  2673. end;
  2674. miterLim := drawDat.strokeMitLim;
  2675. if drawDat.strokeColor = clCurrent then
  2676. drawDat.strokeColor := fSvgReader.currentColor;
  2677. if Length(drawDat.dashArray) > 0 then
  2678. dashArray := ScaleDashArray(drawDat.dashArray, scale) else
  2679. dashArray := nil;
  2680. dashOffset := drawDat.dashOffset;
  2681. with drawDat do
  2682. strokeClr := MergeColorAndOpacity(strokeColor, strokeOpacity);
  2683. if not IsValid(drawDat.strokeOpacity) then
  2684. opacity := 255 else
  2685. opacity := ClampByte(drawDat.strokeOpacity * 255);
  2686. if isClosed then
  2687. begin
  2688. if Assigned(dashArray) then
  2689. begin
  2690. if joinStyle = jsRound then
  2691. endStyle := esRound else
  2692. endStyle := esButt;
  2693. dashArray := ScaleDashArray(drawDat.dashArray, 1); // ie. don't scale yet!
  2694. strokePaths := nil;
  2695. for i := 0 to High(paths) do
  2696. begin
  2697. paths2 := GetDashedPath(paths[i], true, dashArray, @dashOffset);
  2698. AppendPath(strokePaths, paths2);
  2699. end;
  2700. strokePaths :=
  2701. RoughOutline(strokePaths, sw, joinStyle, endStyle, miterLim, scale);
  2702. end else
  2703. begin
  2704. endStyle := esPolygon;
  2705. strokePaths :=
  2706. RoughOutline(paths, sw, joinStyle, endStyle, miterLim, scale);
  2707. end;
  2708. end else
  2709. begin
  2710. if fDrawData.strokeCap = esPolygon then
  2711. endStyle := esButt else
  2712. endStyle := fDrawData.strokeCap;
  2713. if Assigned(dashArray) then
  2714. begin
  2715. strokePaths := MatrixApply(paths, drawDat.matrix);
  2716. DrawDashedLine(img, strokePaths, dashArray,
  2717. @dashOffset, sw * scale, strokeClr, endStyle, jsAuto,
  2718. fSvgReader.fCustomRendererCache);
  2719. Exit;
  2720. end;
  2721. strokePaths :=
  2722. RoughOutline(paths, sw, joinStyle, endStyle, miterLim, scale);
  2723. end;
  2724. strokePaths := MatrixApply(strokePaths, drawDat.matrix);
  2725. if (drawDat.strokeEl <> '') then
  2726. begin
  2727. refEl := FindRefElement(drawDat.strokeEl);
  2728. if not Assigned(refEl) then Exit;
  2729. if refEl is TRadGradElement then
  2730. begin
  2731. with TRadGradElement(refEl) do
  2732. begin
  2733. fSvgReader.RadGradRenderer.Opacity := opacity;
  2734. PrepareRenderer(fSvgReader.RadGradRenderer, drawDat);
  2735. end;
  2736. DrawPolygon(img, strokePaths, frNonZero, fSvgReader.RadGradRenderer);
  2737. end
  2738. else if refEl is TLinGradElement then
  2739. begin
  2740. with TLinGradElement(refEl) do
  2741. begin
  2742. fSvgReader.LinGradRenderer.Opacity := opacity;
  2743. PrepareRenderer(fSvgReader.LinGradRenderer, drawDat);
  2744. end;
  2745. DrawPolygon(img, strokePaths, frNonZero, fSvgReader.LinGradRenderer);
  2746. end
  2747. else if refEl is TPatternElement then
  2748. with TPatternElement(refEl) do
  2749. begin
  2750. imgRenderer.Opacity := opacity;
  2751. PrepareRenderer(imgRenderer, drawDat);
  2752. DrawLine(img, strokePaths, 1, imgRenderer, esPolygon, joinStyle, scale);
  2753. DrawPolygon(img, strokePaths, frNonZero, imgRenderer);
  2754. end;
  2755. end else
  2756. begin
  2757. DrawPolygon(img, strokePaths,
  2758. frNonZero, strokeClr, fSvgReader.fCustomRendererCache);
  2759. end;
  2760. end;
  2761. //------------------------------------------------------------------------------
  2762. // TPathElement
  2763. //------------------------------------------------------------------------------
  2764. constructor TPathElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
  2765. begin
  2766. inherited;
  2767. fSvgPaths := TSvgPath.Create;
  2768. end;
  2769. //------------------------------------------------------------------------------
  2770. destructor TPathElement.Destroy;
  2771. begin
  2772. fSvgPaths.Free;
  2773. inherited;
  2774. end;
  2775. //------------------------------------------------------------------------------
  2776. function TPathElement.GetBounds: TRectD;
  2777. var
  2778. i: integer;
  2779. begin
  2780. Result := NullRectD;
  2781. for i := 0 to fSvgPaths.Count -1 do
  2782. Result := UnionRect(Result, fSvgPaths[i].GetBounds);
  2783. end;
  2784. //------------------------------------------------------------------------------
  2785. procedure TPathElement.ParseDAttrib(const value: UTF8String);
  2786. begin
  2787. fSvgPaths.Parse(value);
  2788. end;
  2789. //------------------------------------------------------------------------------
  2790. procedure TPathElement.Flatten(index: integer; scalePending: double;
  2791. out path: TPathD; out isClosed: Boolean);
  2792. begin
  2793. isClosed := fSvgPaths[index].isClosed;
  2794. path := fSvgPaths[index].GetFlattenedPath(scalePending);
  2795. end;
  2796. //------------------------------------------------------------------------------
  2797. procedure TPathElement.GetPaths(const drawDat: TDrawData);
  2798. var
  2799. i: integer;
  2800. scalePending: double;
  2801. isClosed: Boolean;
  2802. path: TPathD;
  2803. begin
  2804. if pathsLoaded then Exit;
  2805. pathsLoaded := true;
  2806. MatrixExtractScale(drawDat.matrix, scalePending);
  2807. for i := 0 to fSvgPaths.Count -1 do
  2808. begin
  2809. Flatten(i, scalePending, path, isClosed);
  2810. if not Assigned(path) then Continue;
  2811. if isClosed then
  2812. AppendPath(drawPathsC, path) else
  2813. AppendPath(drawPathsO, path);
  2814. end;
  2815. end;
  2816. //------------------------------------------------------------------------------
  2817. function TPathElement.GetSimplePath(const drawDat: TDrawData): TPathsD;
  2818. var
  2819. i: integer;
  2820. begin
  2821. Result := nil;
  2822. SetLength(Result, fSvgPaths.Count);
  2823. for i := 0 to fSvgPaths.Count -1 do
  2824. Result[i] := fSvgPaths[i].GetSimplePath;
  2825. end;
  2826. //------------------------------------------------------------------------------
  2827. // TPolyElement
  2828. //------------------------------------------------------------------------------
  2829. function TPolyElement.GetBounds: TRectD;
  2830. begin
  2831. Result := GetBoundsD(path);
  2832. end;
  2833. //------------------------------------------------------------------------------
  2834. procedure TPolyElement.GetPaths(const drawDat: TDrawData);
  2835. begin
  2836. if pathsLoaded or not Assigned(path) then Exit;
  2837. pathsLoaded := true;
  2838. if (fXmlEl.hash = hPolygon) then
  2839. begin
  2840. AppendPath(drawPathsC, path); //hPolygon
  2841. end else
  2842. begin
  2843. AppendPath(drawPathsO, path); //hPolyline
  2844. end;
  2845. end;
  2846. //------------------------------------------------------------------------------
  2847. function TPolyElement.GetSimplePath(const drawDat: TDrawData): TPathsD;
  2848. begin
  2849. Result := nil;
  2850. AppendPath(Result, path);
  2851. end;
  2852. //------------------------------------------------------------------------------
  2853. procedure TPolyElement.ParsePoints(const value: UTF8String);
  2854. var
  2855. currCnt, currCap: integer;
  2856. procedure AddPoint(const pt: TPointD);
  2857. begin
  2858. if currCnt = currCap then
  2859. begin
  2860. currCap := currCap + buffSize;
  2861. SetLength(path, currCap);
  2862. end;
  2863. path[currCnt] := pt;
  2864. inc(currCnt);
  2865. end;
  2866. var
  2867. pt: TPointD;
  2868. c, endC: PUTF8Char;
  2869. begin
  2870. currCnt := 0;
  2871. currCap := buffSize;
  2872. c := PUTF8Char(value);
  2873. endC := c + Length(value);
  2874. SetLength(path, currCap);
  2875. while IsNumPending(c, endC, true) and
  2876. ParseNextNum(c, endC, true, pt.X) and
  2877. ParseNextNum(c, endC, true, pt.Y) do
  2878. AddPoint(pt);
  2879. SetLength(path, currCnt);
  2880. end;
  2881. //------------------------------------------------------------------------------
  2882. // TLineElement
  2883. //------------------------------------------------------------------------------
  2884. constructor TLineElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
  2885. begin
  2886. inherited;
  2887. NewPointDArray(path, 2, True);
  2888. path[0] := NullPointD; path[1] := NullPointD;
  2889. end;
  2890. //------------------------------------------------------------------------------
  2891. function TLineElement.GetBounds: TRectD;
  2892. begin
  2893. Result := GetBoundsD(path);
  2894. end;
  2895. //------------------------------------------------------------------------------
  2896. procedure TLineElement.GetPaths(const drawDat: TDrawData);
  2897. begin
  2898. if pathsLoaded then Exit;
  2899. pathsLoaded := true;
  2900. AppendPath(drawPathsO, path);
  2901. end;
  2902. //------------------------------------------------------------------------------
  2903. function TLineElement.GetSimplePath(const drawDat: TDrawData): TPathsD;
  2904. begin
  2905. Result := nil;
  2906. AppendPath(Result, path);
  2907. end;
  2908. //------------------------------------------------------------------------------
  2909. // TCircleElement
  2910. //------------------------------------------------------------------------------
  2911. constructor TCircleElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
  2912. begin
  2913. inherited;
  2914. bounds := NullRectD;
  2915. centerPt.Init;
  2916. radius.Init;
  2917. end;
  2918. //------------------------------------------------------------------------------
  2919. function TCircleElement.GetBounds: TRectD;
  2920. begin
  2921. Result := bounds;
  2922. end;
  2923. //------------------------------------------------------------------------------
  2924. procedure TCircleElement.GetPaths(const drawDat: TDrawData);
  2925. var
  2926. scalePending : double;
  2927. pt : TPointD;
  2928. path : TPathD;
  2929. r: double;
  2930. begin
  2931. if pathsLoaded or not radius.IsValid then Exit;
  2932. pathsLoaded := true;
  2933. r := radius.GetValueXY(drawDat.bounds, GetRelFracLimit);
  2934. pt := centerPt.GetPoint(drawDat.bounds, GetRelFracLimit);
  2935. MatrixExtractScale(drawDat.matrix, scalePending);
  2936. bounds := RectD(pt.X -r, pt.Y -r, pt.X +r, pt.Y +r);
  2937. path := Ellipse(bounds, scalePending);
  2938. AppendPath(drawPathsC, path);
  2939. end;
  2940. //------------------------------------------------------------------------------
  2941. // TEllipseElement
  2942. //------------------------------------------------------------------------------
  2943. constructor TEllipseElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
  2944. begin
  2945. inherited;
  2946. centerPt.Init;
  2947. radius.Init;
  2948. end;
  2949. //------------------------------------------------------------------------------
  2950. function TEllipseElement.GetBounds: TRectD;
  2951. begin
  2952. Result := bounds;
  2953. end;
  2954. //------------------------------------------------------------------------------
  2955. procedure TEllipseElement.GetPaths(const drawDat: TDrawData);
  2956. var
  2957. scalePending : double;
  2958. path : TPathD;
  2959. rad : TPointD;
  2960. centPt : TPointD;
  2961. begin
  2962. if pathsLoaded then Exit;
  2963. pathsLoaded := true;
  2964. rad := radius.GetPoint(drawDat.bounds, GetRelFracLimit);
  2965. centPt := centerPt.GetPoint(drawDat.bounds, GetRelFracLimit);
  2966. with centPt do
  2967. bounds := RectD(X -rad.X, Y -rad.Y, X +rad.X, Y +rad.Y);
  2968. MatrixExtractScale(drawDat.matrix, scalePending);
  2969. path := Ellipse(bounds, scalePending);
  2970. AppendPath(drawPathsC, path);
  2971. end;
  2972. //------------------------------------------------------------------------------
  2973. // TRectElement
  2974. //------------------------------------------------------------------------------
  2975. constructor TRectElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
  2976. begin
  2977. inherited;
  2978. radius.Init;
  2979. elRectWH.width.SetValue(100, utPercent);
  2980. elRectWH.height.SetValue(100, utPercent);
  2981. end;
  2982. //------------------------------------------------------------------------------
  2983. function TRectElement.GetBounds: TRectD;
  2984. begin
  2985. Result := elRectWH.GetRectD(NullRectD, GetRelFracLimit);
  2986. end;
  2987. //------------------------------------------------------------------------------
  2988. procedure TRectElement.GetPaths(const drawDat: TDrawData);
  2989. var
  2990. radXY : TPointD;
  2991. bounds: TRectD;
  2992. path : TPathD;
  2993. begin
  2994. if pathsLoaded then Exit;
  2995. if elRectWH.width.HasFontUnits then
  2996. bounds := elRectWH.GetRectD(drawDat.fontInfo.size, GetRelFracLimit) else
  2997. bounds := elRectWH.GetRectD(drawDat.bounds, GetRelFracLimit);
  2998. if bounds.IsEmpty then Exit;
  2999. pathsLoaded := true;
  3000. radXY := radius.GetPoint(bounds, GetRelFracLimit);
  3001. if (radXY.X > 0) or (radXY.Y > 0) then
  3002. begin
  3003. if (radXY.X <= 0) then radXY.X := radXY.Y
  3004. else if (radXY.Y <= 0) then radXY.Y := radXY.X;
  3005. path := RoundRect(bounds, radXY);
  3006. end else
  3007. path := Rectangle(bounds);
  3008. AppendPath(drawPathsC, path);
  3009. end;
  3010. //------------------------------------------------------------------------------
  3011. function TRectElement.GetSimplePath(const drawDat: TDrawData): TPathsD;
  3012. var
  3013. rec: TRectD;
  3014. begin
  3015. Result := nil;
  3016. rec := elRectWH.GetRectD(drawDat.bounds, GetRelFracLimit);
  3017. if not rec.IsEmpty then
  3018. AppendPath(Result, Rectangle(rec));
  3019. end;
  3020. //------------------------------------------------------------------------------
  3021. // TTextElement
  3022. //------------------------------------------------------------------------------
  3023. constructor TTextElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
  3024. begin
  3025. inherited;
  3026. offset.Init;
  3027. hasPaths := false;
  3028. end;
  3029. //------------------------------------------------------------------------------
  3030. procedure TTextElement.Draw(img: TImage32; drawDat: TDrawData);
  3031. begin
  3032. UpdateDrawInfo(drawDat, self);
  3033. UpdateFontInfo(drawDat, self);
  3034. fSvgReader.GetBestFont(drawDat.FontInfo);
  3035. if not Assigned(fSvgReader.fFontCache) then Exit;
  3036. if drawDat.fontInfo.size = 0 then drawDat.fontInfo.size := 16;
  3037. if offset.X.IsValid then
  3038. currentPt.X := offset.X.rawVal
  3039. else if elRectWH.left.IsValid then
  3040. currentPt.X := elRectWH.left.rawVal
  3041. else
  3042. currentPt.X := 0;
  3043. if offset.Y.IsValid then
  3044. currentPt.Y := offset.Y.rawVal
  3045. else if elRectWH.top.IsValid then
  3046. currentPt.Y := elRectWH.top.rawVal
  3047. else
  3048. currentPt.Y := 0;
  3049. lastChrSpc := false;
  3050. textDx := 0;
  3051. currSpanEl := nil;
  3052. //get child paths (which also updates currentPt)
  3053. GetPaths(drawDat);
  3054. DrawChildren(img, drawDat);
  3055. end;
  3056. //------------------------------------------------------------------------------
  3057. // TTextSubElement
  3058. //------------------------------------------------------------------------------
  3059. function TTextSubElement.GetTextEl: TTextElement;
  3060. var
  3061. el: TBaseElement;
  3062. begin
  3063. if not Assigned(textEl) then
  3064. begin
  3065. el := fParent;
  3066. while Assigned(el) and not (el is TTextElement) do
  3067. el := el.fParent;
  3068. if Assigned(el) then
  3069. textEl := TTextElement(el);
  3070. end;
  3071. Result := textEl;
  3072. end;
  3073. //------------------------------------------------------------------------------
  3074. // TTSpanElement
  3075. //------------------------------------------------------------------------------
  3076. procedure TTSpanElement.GetPaths(const drawDat: TDrawData);
  3077. var
  3078. tmpX, startX, fontScale, fontSize, bs: double;
  3079. i,j, len : integer;
  3080. di : TDrawData;
  3081. s : UnicodeString;
  3082. mat : TMatrixD;
  3083. tmpPaths : TPathsD;
  3084. codepoints: TArrayOfCardinal;
  3085. angles : TArrayOfDouble;
  3086. glyphInfo : PGlyphInfo;
  3087. glyphRec : TRectD;
  3088. begin
  3089. // 1. We only want to process this method once even though it's called twice,
  3090. // first indirectly by TTextElement.Draw, and then by TTSpanElement.Draw.
  3091. // 2. This method isn't called when <tspan> is a sub-element of <textpath>.
  3092. if pathsLoaded then Exit;
  3093. pathsLoaded := true;
  3094. di := drawDat;
  3095. if ChildCount > 0 then
  3096. begin
  3097. UpdateDrawInfo(di, self);
  3098. UpdateFontInfo(di, self);
  3099. end;
  3100. if drawDat.FontInfo.size = 0 then
  3101. fontSize := 16.0 else
  3102. fontSize := drawDat.FontInfo.size;
  3103. fSvgReader.GetBestFont(di.FontInfo);
  3104. if not Assigned(fSvgReader.fFontCache) then Exit;
  3105. GetTextEl;
  3106. if not Assigned(textEl) or
  3107. (textEl.currentPt.X = InvalidD) or
  3108. (textEl.currentPt.Y = InvalidD) then Exit;
  3109. //by not changing the fontCache.FontHeight, the quality of
  3110. //small font render improves very significantly (though of course
  3111. //this requires additional glyph scaling and offsetting).
  3112. fontScale := fontSize / fSvgReader.fFontCache.FontHeight;
  3113. if elRectWH.left.IsValid then
  3114. textEl.currentPt.X := elRectWH.left.rawVal;
  3115. if elRectWH.top.IsValid then
  3116. textEl.currentPt.Y := elRectWH.top.rawVal;
  3117. if offset.X.IsValid then
  3118. textEl.currentPt.X := textEl.currentPt.X + offset.X.GetValue(0, 0);
  3119. if offset.Y.IsValid then
  3120. textEl.currentPt.Y := textEl.currentPt.Y + offset.Y.GetValue(0, 0);
  3121. // only 'virtual' (dummy) <tspan> elements are self-closing, and
  3122. // mostly their parents are 'real' <tspan> elements. However,
  3123. // virtual <tspan> elements can also have <text> element parents.
  3124. if not fXmlEl.selfClosed then
  3125. begin
  3126. textEl.currSpanEl := self;
  3127. angles := nil;
  3128. end
  3129. else if (fParent is TTSpanElement) then
  3130. begin
  3131. if Assigned(TTSpanElement(fParent).angle) then
  3132. angles := TTSpanElement(fParent).angle else
  3133. angles := textEl.angle;
  3134. end else
  3135. begin
  3136. angles := textEl.angle;
  3137. textEl.currSpanEl := nil;
  3138. end;
  3139. chunkDx := 0;
  3140. if (Length(fXmlEl.text) > 0) and (fontSize > 1) then
  3141. begin
  3142. // this should be a virtual (dummy) <tspan> element
  3143. //assert(fXmlEl.selfClosed);
  3144. s := DecodeUtf8ToUnicode(HtmlDecode(fXmlEl.text));
  3145. // don't allow a dup. spaces or a space at the beginning of a text
  3146. s := FixSpaces(s, textEl.lastChrSpc or
  3147. ((fParent = textEl) and (self = textEl.Child[0])));
  3148. if IsBlankText(s) then
  3149. begin
  3150. drawPathsC := nil;
  3151. // don't allow duplicate spaces or a space at the beginning of text
  3152. if textEl.lastChrSpc or (self = textEl.Child[0]) then Exit;
  3153. tmpX := fSvgReader.fFontCache.GetSpaceWidth;
  3154. textEl.lastChrSpc := true;
  3155. end
  3156. else if Assigned(angles) then
  3157. begin
  3158. drawPathsC := nil;
  3159. tmpPaths := nil;
  3160. tmpX := 0;
  3161. codepoints := fSvgReader.fFontCache.GetTextCodePoints(s);
  3162. // make sure 'angles' is at least the length of codepoints
  3163. len := Length(codepoints);
  3164. if len > Length(angles) then
  3165. begin
  3166. j := High(angles);
  3167. SetLength(angles, len); // extend angles
  3168. for i := j +1 to len -1 do angles[i] := angles[j];
  3169. end;
  3170. textEl.lastChrSpc := (codepoints[len -1] = 32);
  3171. // now get each rotated glyph and append to drawPathsC ...
  3172. for i := 0 to len -1 do
  3173. begin
  3174. glyphInfo := fSvgReader.fFontCache.GetGlyphInfo(codepoints[i]);
  3175. if Assigned(glyphInfo.paths) then
  3176. begin
  3177. glyphRec := GetBoundsD(glyphInfo.paths);
  3178. tmpPaths := RotatePath(glyphInfo.paths, glyphRec.MidPoint, angles[i]);
  3179. if i > 0 then
  3180. tmpPaths := TranslatePath(tmpPaths, tmpX, 0);
  3181. AppendPath(drawPathsC, tmpPaths);
  3182. end;
  3183. tmpX := tmpX + glyphInfo.hmtx.advanceWidth * fSvgReader.fFontCache.Scale;
  3184. end;
  3185. end else
  3186. begin
  3187. drawPathsC := fSvgReader.fFontCache.GetTextOutline(0, 0, s, tmpX);
  3188. textEl.lastChrSpc := s[length(s)] = space;
  3189. end;
  3190. chunkDx := tmpX * fontScale;
  3191. if Assigned(textEl.currSpanEl) then
  3192. with textEl.currSpanEl do
  3193. chunkDx := chunkDx + self.chunkDx;
  3194. textEl.textDx := textEl.textDx + chunkDx;
  3195. with textEl.currentPt do
  3196. begin
  3197. startX := X;
  3198. X := X + chunkDx;
  3199. end;
  3200. if Assigned(drawPathsC) then // eg. unassigned if a space char
  3201. begin
  3202. with drawDat.fontInfo do
  3203. if not baseShift.IsValid then
  3204. bs := 0 else
  3205. bs := baseShift.GetValue(size, GetRelFracLimit);
  3206. mat := IdentityMatrix;
  3207. MatrixScale(mat, fontScale);
  3208. MatrixTranslate(mat, startX, textEl.currentPt.Y - bs);
  3209. MatrixApply(mat, drawPathsC);
  3210. end;
  3211. end;
  3212. // nested <tspan> elements are always possible,
  3213. // except when self is a pseudo 'selfClosed' <tspan> element
  3214. inherited GetPaths(di); // gets any children paths
  3215. end;
  3216. //------------------------------------------------------------------------------
  3217. procedure TTSpanElement.Draw(image: TImage32; drawDat: TDrawData);
  3218. var
  3219. stroked : Boolean;
  3220. filled : Boolean;
  3221. tmpRec : TRect;
  3222. fillPaths : TPathsD;
  3223. begin
  3224. if ChildCount = 0 then
  3225. fDrawData := fParent.fDrawData
  3226. else
  3227. begin
  3228. UpdateDrawInfo(drawDat, self);
  3229. UpdateFontInfo(drawDat, self);
  3230. end;
  3231. if not fXmlEl.selfClosed then
  3232. begin
  3233. // DrawChildren and exit ...
  3234. inherited;
  3235. Exit;
  3236. end;
  3237. filled := IsFilled(drawDat);
  3238. stroked := IsStroked(drawDat);
  3239. if Assigned(drawPathsC) and Assigned(textEl) then
  3240. begin
  3241. // a <tspan> element that contains text (and a path) must be virtual.
  3242. // But its parent may be another <tspan>, or a <text> or a <textarea>.
  3243. tmpRec := Rect(GetBounds);
  3244. if not IsEmptyRect(tmpRec) then
  3245. drawDat.bounds := RectD(tmpRec);
  3246. if (fParent is TTSpanElement) and fParent.elRectWH.left.IsValid then
  3247. begin
  3248. case drawDat.FontInfo.align of
  3249. staCenter: drawPathsC := TranslatePath(drawPathsC, -chunkDx * 0.5, 0);
  3250. staRight: drawPathsC := TranslatePath(drawPathsC, -chunkDx, 0);
  3251. end;
  3252. end
  3253. else if textEl.textDx <> 0 then
  3254. begin
  3255. case drawDat.FontInfo.align of
  3256. staCenter: drawPathsC := TranslatePath(drawPathsC, -textEl.textDx * 0.5, 0);
  3257. staRight: drawPathsC := TranslatePath(drawPathsC, -textEl.textDx, 0);
  3258. end;
  3259. end;
  3260. // todo - 1. implement paint-order - fill stroke vs stroke fill
  3261. // 2. wavy and colored underlines
  3262. if stroked then
  3263. begin
  3264. // it's slightly more efficient to apply the matrix
  3265. // inside DrawStroke() rather than here.
  3266. DrawStroke(image, drawPathsC, drawDat, true);
  3267. end;
  3268. if filled then
  3269. begin
  3270. // it's slightly more efficient to apply the matrix here
  3271. // rather than inside DrawFilled().
  3272. fillPaths := MatrixApply(drawPathsC, drawDat.matrix);
  3273. DrawFilled(image, fillPaths, drawDat);
  3274. end;
  3275. end;
  3276. end;
  3277. //------------------------------------------------------------------------------
  3278. // TTextPathElement
  3279. //------------------------------------------------------------------------------
  3280. function GetPathDistance(const path: TPathD): double;
  3281. var
  3282. i: integer;
  3283. begin
  3284. Result := 0;
  3285. for i := 1 to High(path) do
  3286. Result := Result + Distance(path[i-1], path[i]);
  3287. end;
  3288. //------------------------------------------------------------------------------
  3289. procedure TTextPathElement.GetPathsInternal(el: TBaseElement;
  3290. const drawDat: TDrawData);
  3291. var
  3292. i, len : integer;
  3293. charsThatFit : integer;
  3294. spacing : double;
  3295. textWidth : double;
  3296. outX : double;
  3297. spanEl : TTSpanElement;
  3298. dd : TDrawData;
  3299. unicodeText : UnicodeString;
  3300. pathDist : double;
  3301. mat : TMatrixD;
  3302. tmpPath : TPathD;
  3303. tmpPaths : TPathsD;
  3304. isClosed : Boolean;
  3305. begin
  3306. if not (el is TTSpanElement) then Exit;
  3307. spanEl := TTSpanElement(el);
  3308. if Assigned(spanEl.drawPathsC) then Exit;
  3309. spanEl.pathsLoaded := true;
  3310. spanEl.GetTextEl;
  3311. dd := drawDat;
  3312. UpdateDrawInfo(dd, el);
  3313. UpdateFontInfo(dd, el);
  3314. if spanEl.offset.X.IsValid then
  3315. textEl.currentPt.X := Max(0, textEl.currentPt.X +
  3316. Round(spanEl.offset.X.rawVal / scale));
  3317. if spanEl.offset.Y.IsValid then
  3318. textEl.currentPt.Y := textEl.currentPt.Y +
  3319. Round(spanEl.offset.Y.rawVal / scale);
  3320. if spanEl.fXmlEl.text = '' then
  3321. begin
  3322. // nb: recursive
  3323. for i := 0 to spanEl.ChildCount -1 do
  3324. GetPathsInternal(spanEl.Child[i], dd);
  3325. Exit;
  3326. end;
  3327. // nb: <tspan> elements that own text will always be pseudo <tspan> elements.
  3328. // Pseudo <tspan> elements have been created inside real <tspan> elements to
  3329. // provide a reliable way to manage text mixed with nested <tspan> elements.
  3330. //trim CRLFs and multiple spaces
  3331. unicodeText := DecodeUtf8ToUnicode(HtmlDecode(spanEl.fXmlEl.text));
  3332. if dd.fontInfo.spacesInText <> sitPreserve then
  3333. unicodeText := TrimMultiSpacesUnicode(unicodeText) else
  3334. unicodeText := StripNewlines(unicodeText);
  3335. //adjust glyph spacing when fFontInfo.textLength is assigned.
  3336. spacing := dd.FontInfo.spacing /scale;
  3337. len := Length(unicodeText);
  3338. if (len < 2) then spacing := 0
  3339. else if (dd.FontInfo.align = staJustify) and
  3340. (pathEl.fsvgPaths.count = 1) then
  3341. with TPathElement(pathEl) do
  3342. begin
  3343. Flatten(0, scale, tmpPath, isClosed);
  3344. pathDist := GetPathDistance(tmpPath);
  3345. textWidth := fSvgReader.fFontCache.GetTextWidth(unicodeText);
  3346. spacing := (pathDist/scale) - textWidth;
  3347. spacing := spacing / (len -1);
  3348. end
  3349. else if (dd.FontInfo.textLength > 0) then
  3350. begin
  3351. textWidth := fSvgReader.fFontCache.GetTextWidth(unicodeText);
  3352. spacing := (dd.FontInfo.textLength/scale) - textWidth;
  3353. spacing := spacing / (len -1);
  3354. end;
  3355. with pathEl do
  3356. begin
  3357. mat := fDrawData.matrix;
  3358. MatrixScale(mat, 1/scale);
  3359. for i := 0 to fSvgPaths.Count -1 do
  3360. begin
  3361. Flatten(i, scale, tmpPath, isClosed);
  3362. //'path' is temporarily scaled to accommodate fReader.fFontCache's
  3363. //static fontheight. The returned glyphs will be de-scaled later.
  3364. MatrixApply(mat, tmpPath);
  3365. tmpPaths := GetTextOutlineOnPath(unicodeText, tmpPath,
  3366. fSvgReader.fFontCache, taLeft, textEl.currentPt.X,
  3367. textEl.currentPt.Y, spacing, charsThatFit, outX);
  3368. AppendPath(spanEl.drawPathsC, tmpPaths);
  3369. textEl.currentPt.X := outX;
  3370. if charsThatFit = Length(unicodeText) then Break;
  3371. Delete(unicodeText, 1, charsThatFit);
  3372. textEl.currentPt := NullPointD;
  3373. end;
  3374. end;
  3375. spanEl.drawPathsC := ScalePath(spanEl.drawPathsC, scale);
  3376. for i := 0 to spanEl.ChildCount -1 do
  3377. GetPathsInternal(spanEl.Child[i], dd);
  3378. end;
  3379. //------------------------------------------------------------------------------
  3380. procedure TTextPathElement.GetPaths(const drawDat: TDrawData);
  3381. var
  3382. i: integer;
  3383. dd: TDrawData;
  3384. el: TBaseElement;
  3385. begin
  3386. if pathsLoaded or not Assigned(fSvgReader.fFontCache) then Exit;
  3387. pathsLoaded := true;
  3388. GetTextEl;
  3389. if not Assigned(textEl) then Exit;
  3390. dd := drawDat;
  3391. UpdateDrawInfo(dd, self);
  3392. UpdateFontInfo(dd, self);
  3393. el := FindRefElement(pathName);
  3394. if not (el is TPathElement) then Exit;
  3395. pathEl := TPathElement(el);
  3396. fSvgReader.GetBestFont(dd.FontInfo);
  3397. scale := dd.FontInfo.size/fSvgReader.fFontCache.FontHeight;
  3398. if offset.X.IsValid then
  3399. textEl.currentPt.X := Max(0,
  3400. textEl.currentPt.X +
  3401. Round(offset.X.GetValue(dd.bounds.Width, 1) / scale));
  3402. if offset.Y.IsValid then
  3403. textEl.currentPt.Y :=
  3404. textEl.currentPt.Y + Round(offset.Y.rawVal / scale);
  3405. // nb: recursive
  3406. for i := 0 to ChildCount -1 do
  3407. GetPathsInternal(Child[i], drawDat);
  3408. end;
  3409. //------------------------------------------------------------------------------
  3410. function TTextPathElement.GetBounds: TRectD;
  3411. var
  3412. textEl: TTextElement;
  3413. begin
  3414. textEl := TTextElement(fParent);
  3415. {$IFDEF UNICODE}
  3416. if IsBlankText(UTF8ToUnicodeString(fXmlEl.text)) then
  3417. {$ELSE}
  3418. if IsBlankText(Utf8Decode(fXmlEl.text)) then
  3419. {$ENDIF}
  3420. Result := textEl.fDrawData.bounds else
  3421. Result := inherited GetBounds;
  3422. end;
  3423. //------------------------------------------------------------------------------
  3424. procedure TTextPathElement.Draw(image: TImage32; drawDat: TDrawData);
  3425. begin
  3426. UpdateFontInfo(drawDat, self);
  3427. inherited;
  3428. end;
  3429. //------------------------------------------------------------------------------
  3430. // TTextAreaElement
  3431. //------------------------------------------------------------------------------
  3432. procedure TTextAreaElement.GetPaths(const drawDat: TDrawData);
  3433. var
  3434. scale : double;
  3435. fontSize : double;
  3436. lnHeight : double;
  3437. di : TDrawData;
  3438. mat : TMatrixD;
  3439. text : Utf8String;
  3440. s : UnicodeString;
  3441. textRec : TRectD;
  3442. const
  3443. margin = 1;
  3444. begin
  3445. if pathsLoaded then Exit;
  3446. text := fXmlEl.text;
  3447. if not elRectWH.width.IsValid or not elRectWH.height.IsValid or
  3448. (text = '') then Exit;
  3449. pathsLoaded := true;
  3450. di := drawDat;
  3451. UpdateDrawInfo(di, self);
  3452. if drawDat.FontInfo.size = 0 then
  3453. fontSize := 16.0 else
  3454. fontSize := drawDat.FontInfo.size;
  3455. fSvgReader.GetBestFont(di.FontInfo);
  3456. if not Assigned(fSvgReader.fFontCache) then Exit;
  3457. scale := fontSize / fSvgReader.fFontCache.FontHeight;
  3458. s := DecodeUtf8ToUnicode(HtmlDecode(text));
  3459. s := FixSpaces(s, false);
  3460. s := StringReplace(s, '<tbreak/>', #10, [rfReplaceAll, rfIgnoreCase]);
  3461. lnHeight := fSvgReader.fFontCache.LineHeight;
  3462. textRec := elRectWH.GetRectD(di.bounds.Width, di.bounds.Height, 1);
  3463. textRec := ScaleRect(textRec, 1/scale);
  3464. InflateRect(textRec, -margin, -margin);
  3465. with TChunkedText.Create(s, fSvgReader.fFontCache) do
  3466. try
  3467. // and compress the lineheight a little
  3468. drawPathsC := GetTextGlyphs(Rect(textRec), taLeft, tvaTop, 0, lnHeight * 0.8);
  3469. finally
  3470. Free;
  3471. end;
  3472. mat := IdentityMatrix;
  3473. MatrixScale(mat, scale);
  3474. MatrixApply(mat, drawPathsC);
  3475. end;
  3476. //------------------------------------------------------------------------------
  3477. // TMarkerElement
  3478. //------------------------------------------------------------------------------
  3479. constructor TMarkerElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
  3480. begin
  3481. inherited;
  3482. fDrawData.visible := false;
  3483. end;
  3484. //------------------------------------------------------------------------------
  3485. procedure TMarkerElement.Draw(img: TImage32; drawDat: TDrawData);
  3486. var
  3487. i, len: integer;
  3488. l,t,w,h,scale, a, a2: double;
  3489. mat: TMatrixD;
  3490. angles: TArrayOfDouble;
  3491. begin
  3492. UpdateDrawInfo(drawDat, self);
  3493. mat := drawDat.matrix;
  3494. if elRectWH.width.IsValid and elRectWH.height.IsValid and
  3495. not markerBoxWH.IsEmpty then
  3496. begin
  3497. w := elRectWH.width.rawVal;
  3498. h := elRectWH.height.rawVal;
  3499. //currently assume preserve aspect ratio
  3500. scale := Min(w/markerBoxWH.Width, h/markerBoxWH.Height);
  3501. MatrixScale(mat, scale, scale);
  3502. end;
  3503. if refPt.X.IsValid and refPt.Y.IsValid then
  3504. begin
  3505. l := refPt.X.rawVal;
  3506. t := refPt.Y.rawVal;
  3507. MatrixExtractScale(mat, scale);
  3508. MatrixTranslate(mat, -l * scale, -t * scale);
  3509. end;
  3510. len := Length(fPoints);
  3511. if len = 0 then Exit;
  3512. SetLength(angles, len);
  3513. angles[0] := angle;
  3514. a := angle;
  3515. for i := 0 to len -2 do
  3516. begin
  3517. a2 := GetAngle(fPoints[i], fPoints[i+1]);
  3518. angles[i] := Average(a, a2);
  3519. a := a2;
  3520. end;
  3521. if len > 1 then
  3522. angles[len -1] := Average(a, angle2);
  3523. //for each 'point' draw the marker
  3524. for i := 0 to len -1 do
  3525. begin
  3526. drawDat.matrix := mat;
  3527. MatrixRotate(drawDat.matrix, NullPointD, angles[i]);
  3528. MatrixTranslate(drawDat.matrix, fPoints[i].X, fPoints[i].Y);
  3529. DrawChildren(img, drawDat);
  3530. end;
  3531. end;
  3532. //------------------------------------------------------------------------------
  3533. procedure TMarkerElement.SetEndPoint(const pt: TPointD; angle: double);
  3534. begin
  3535. NewPointDArray(fPoints, 1, True);
  3536. fPoints[0] := pt;
  3537. self.angle := angle;
  3538. end;
  3539. //------------------------------------------------------------------------------
  3540. function TMarkerElement.SetMiddlePoints(const points: TPathD): Boolean;
  3541. var
  3542. len: integer;
  3543. begin
  3544. len := Length(points);
  3545. Result := len > 2;
  3546. if Result then
  3547. begin
  3548. angle := GetAngle(Points[0],Points[1]);
  3549. angle2 := GetAngle(Points[len-2],Points[len-1]);
  3550. Self.fPoints := Copy(points, 1, len -2);
  3551. end;
  3552. end;
  3553. //------------------------------------------------------------------------------
  3554. // TFillElement
  3555. //------------------------------------------------------------------------------
  3556. function TFillElement.GetRelFracLimit: double;
  3557. begin
  3558. //always assume fractional values below 1 are relative
  3559. Result := 1.0;
  3560. end;
  3561. //------------------------------------------------------------------------------
  3562. // TPatternElement
  3563. //------------------------------------------------------------------------------
  3564. constructor TPatternElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
  3565. begin
  3566. inherited;
  3567. imgRenderer := TImageRenderer.Create;
  3568. elRectWH.Init;
  3569. pattBoxWH.Width := InvalidD;
  3570. pattBoxWH.Height := InvalidD;
  3571. fDrawData.visible := false;
  3572. end;
  3573. //------------------------------------------------------------------------------
  3574. destructor TPatternElement.Destroy;
  3575. begin
  3576. imgRenderer.Free;
  3577. inherited;
  3578. end;
  3579. //------------------------------------------------------------------------------
  3580. function TPatternElement.PrepareRenderer(renderer: TImageRenderer;
  3581. drawDat: TDrawData): Boolean;
  3582. var
  3583. i : integer;
  3584. recWH : TRectWH;
  3585. el : TBaseElement;
  3586. rec : TRectD;
  3587. mat : TMatrixD;
  3588. sx,sy : double;
  3589. scale : TPointD;
  3590. begin
  3591. Result := false;
  3592. MatrixExtractScale(drawDat.matrix, scale.X, scale.Y);
  3593. if units = hUserSpaceOnUse then
  3594. rec := fSvgReader.userSpaceBounds else
  3595. rec := drawDat.bounds;
  3596. //todo: implement patternUnits & patternContentUnits too
  3597. sx := 1; sy := 1;
  3598. if elRectWH.Width.IsValid and elRectWH.Height.IsValid then
  3599. begin
  3600. recWH := elRectWH.GetRectWH(rec, GetRelFracLimit);
  3601. if recWH.IsEmpty then Exit;
  3602. //also scale if necessary
  3603. if not pattBoxWH.IsEmpty then
  3604. begin
  3605. sx := recWH.Width/pattBoxWH.Width;
  3606. sy := recWH.Height/pattBoxWH.Height;
  3607. end;
  3608. end
  3609. else if not pattBoxWH.IsEmpty then
  3610. begin
  3611. recWH.Width := pattBoxWH.Width;
  3612. recWH.Height := pattBoxWH.Width;
  3613. end else
  3614. Exit;
  3615. renderer.Image.SetSize(
  3616. Round(recWH.Width * scale.X),
  3617. Round(recWH.Height * scale.Y));
  3618. Result := true;
  3619. mat := IdentityMatrix;
  3620. MatrixScale(mat, scale.X * sx, scale.Y * sy);
  3621. if (refEl <> '') then
  3622. begin
  3623. el := FindRefElement(refEl);
  3624. if Assigned(el) and (el is TShapeElement) then
  3625. with TShapeElement(el) do
  3626. begin
  3627. drawDat := fDrawData;
  3628. drawDat.matrix := mat;
  3629. drawDat.bounds := recWH.RectD;
  3630. Draw(renderer.Image, drawDat);
  3631. end;
  3632. end;
  3633. for i := 0 to fChilds.Count -1 do
  3634. if TBaseElement(fChilds[i]) is TShapeElement then
  3635. with TShapeElement(fChilds[i]) do
  3636. begin
  3637. drawDat := fDrawData;
  3638. drawDat.matrix := mat;
  3639. drawDat.bounds := rec;
  3640. Draw(renderer.Image, drawDat);
  3641. end
  3642. else if TBaseElement(fChilds[i]) is TImageElement then
  3643. with TImageElement(fChilds[i]) do
  3644. begin
  3645. drawDat := fDrawData;
  3646. drawDat.matrix := mat;
  3647. drawDat.bounds := rec;
  3648. Draw(renderer.Image, drawDat);
  3649. end;
  3650. end;
  3651. //------------------------------------------------------------------------------
  3652. // TSvgElement
  3653. //------------------------------------------------------------------------------
  3654. procedure TSvgElement.Draw(image: TImage32; drawDat: TDrawData);
  3655. var
  3656. sx, sy: double;
  3657. dd: TDrawData;
  3658. begin
  3659. dd := drawDat;
  3660. if (fSvgReader.RootElement <> self) then
  3661. begin
  3662. if (elRectWH.left.rawVal <> InvalidD) or
  3663. (elRectWH.top.rawVal <> InvalidD) then
  3664. begin
  3665. MatrixExtractScale(dd.matrix, sx, sy);
  3666. MatrixTranslate(dd.matrix,
  3667. elRectWH.left.rawVal * sx,
  3668. elRectWH.top.rawVal *sy);
  3669. end;
  3670. if not viewboxWH.IsEmpty then
  3671. begin
  3672. sx := fSvgReader.BackgndImage.Width / viewboxWH.Width;
  3673. sy := fSvgReader.BackgndImage.Height / viewboxWH.Height;
  3674. MatrixScale(dd.matrix, sx, sy);
  3675. end;
  3676. end;
  3677. DrawChildren(image, dd);
  3678. end;
  3679. //------------------------------------------------------------------------------
  3680. function TSvgElement.Width: TValue;
  3681. begin
  3682. Result := elRectWH.Width;
  3683. end;
  3684. //------------------------------------------------------------------------------
  3685. function TSvgElement.Height: TValue;
  3686. begin
  3687. Result := elRectWH.Height;
  3688. end;
  3689. //------------------------------------------------------------------------------
  3690. // TBaseElement
  3691. //------------------------------------------------------------------------------
  3692. constructor TBaseElement.Create(parent: TBaseElement; svgEl: TSvgXmlEl);
  3693. begin
  3694. {$IFDEF XPLAT_GENERICS}
  3695. fChilds := TList<TBaseElement>.create;
  3696. {$ELSE}
  3697. fChilds := TList.Create;
  3698. {$ENDIF}
  3699. fXmlEl := svgEl;
  3700. self.fParent := parent;
  3701. elRectWH.Init;
  3702. fDrawData := emptyDrawInfo;
  3703. if Assigned(parent) then
  3704. begin
  3705. fDrawData.strokeCap := parent.fDrawData.strokeCap;
  3706. fDrawData.strokeJoin := parent.fDrawData.strokeJoin;
  3707. fSvgReader := parent.fSvgReader;
  3708. end;
  3709. end;
  3710. //------------------------------------------------------------------------------
  3711. destructor TBaseElement.Destroy;
  3712. var
  3713. i: integer;
  3714. begin
  3715. for i := 0 to fChilds.Count -1 do
  3716. TBaseElement(fChilds[i]).Free;
  3717. fChilds.Free;
  3718. inherited;
  3719. end;
  3720. //------------------------------------------------------------------------------
  3721. function TBaseElement.IsFirstChild: Boolean;
  3722. begin
  3723. Result := not Assigned(fParent) or (self = fParent.fChilds[0]);
  3724. end;
  3725. //------------------------------------------------------------------------------
  3726. procedure TBaseElement.Draw(image: TImage32; drawDat: TDrawData);
  3727. begin
  3728. DrawChildren(image, drawDat);
  3729. end;
  3730. //------------------------------------------------------------------------------
  3731. procedure TBaseElement.DrawChildren(image: TImage32; const drawDat: TDrawData);
  3732. var
  3733. i: integer;
  3734. begin
  3735. for i := 0 to fChilds.Count -1 do
  3736. with TBaseElement(fChilds[i]) do
  3737. if fDrawData.visible then Draw(image, drawDat);
  3738. end;
  3739. //------------------------------------------------------------------------------
  3740. function TBaseElement.GetChildCount: integer;
  3741. begin
  3742. Result := fChilds.Count;
  3743. end;
  3744. //------------------------------------------------------------------------------
  3745. function TBaseElement.FindChild(const idName: UTF8String): TBaseElement;
  3746. var
  3747. i: integer;
  3748. begin
  3749. if Match(self.fId, idName) then
  3750. begin
  3751. Result := self;
  3752. Exit;
  3753. end;
  3754. Result := nil;
  3755. for i := 0 to ChildCount -1 do
  3756. begin
  3757. Result := Child[i].FindChild(idName);
  3758. if Assigned(Result) then Break;
  3759. end;
  3760. end;
  3761. //------------------------------------------------------------------------------
  3762. function TBaseElement.GetChild(index: integer): TBaseElement;
  3763. begin
  3764. if (index < 0) or (index >= fChilds.count) then
  3765. Result := nil else
  3766. Result := TBaseElement(fChilds[index]);
  3767. end;
  3768. //------------------------------------------------------------------------------
  3769. function TBaseElement.FindRefElement(const refname: UTF8String): TBaseElement;
  3770. var
  3771. len: integer;
  3772. c, endC: PUTF8Char;
  3773. ref: UTF8String;
  3774. begin
  3775. result := nil;
  3776. len := Length(refname);
  3777. if len = 0 then Exit;
  3778. c := PUTF8Char(Pointer(refname));
  3779. endC := c + len;
  3780. if Match(c, 'url(') then
  3781. begin
  3782. inc(c, 4);
  3783. dec(endC); //removes trailing ')'
  3784. end;
  3785. if c^ = '#' then inc(c);
  3786. if c = PUTF8Char(Pointer(refname)) then
  3787. Result := fSvgReader.fIdList.FindElement(refname)
  3788. else
  3789. begin
  3790. ToUTF8String(c, endC, ref);
  3791. Result := fSvgReader.fIdList.FindElement(ref);
  3792. end;
  3793. end;
  3794. //------------------------------------------------------------------------------
  3795. // dozens of function to process various element attributes
  3796. //------------------------------------------------------------------------------
  3797. procedure Id_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  3798. begin
  3799. aOwnerEl.fId := value;
  3800. aOwnerEl.fSvgReader.fIdList.AddOrIgnore(value, aOwnerEl);
  3801. end;
  3802. //------------------------------------------------------------------------------
  3803. procedure In_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  3804. begin
  3805. if aOwnerEl is TFeBaseElement then
  3806. TFeBaseElement(aOwnerEl).in1 := value;
  3807. end;
  3808. //------------------------------------------------------------------------------
  3809. procedure In2_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  3810. begin
  3811. if aOwnerEl is TFeBaseElement then
  3812. TFeBaseElement(aOwnerEl).in2 := value;
  3813. end;
  3814. //------------------------------------------------------------------------------
  3815. procedure Intercept_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  3816. var
  3817. c, endC: PUTF8Char;
  3818. val: double;
  3819. begin
  3820. if (value = '') or not (aOwnerEl is TFeComponentTransferChild) then Exit;
  3821. c := PUTF8Char(value);
  3822. endC := c + Length(value);
  3823. with TFeComponentTransferChild(aOwnerEl) do
  3824. if ParseNextNum(c, endC, false, val) then
  3825. intercept := val else
  3826. intercept := 1;
  3827. end;
  3828. //------------------------------------------------------------------------------
  3829. procedure Slope_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  3830. var
  3831. c, endC: PUTF8Char;
  3832. val: double;
  3833. begin
  3834. if (value = '') or not (aOwnerEl is TFeComponentTransferChild) then Exit;
  3835. c := PUTF8Char(value);
  3836. endC := c + Length(value);
  3837. with TFeComponentTransferChild(aOwnerEl) do
  3838. if ParseNextNum(c, endC, false, val) then
  3839. slope := val else
  3840. slope := 1;
  3841. end;
  3842. //------------------------------------------------------------------------------
  3843. procedure TableValues_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  3844. var
  3845. c, endC: PUTF8Char;
  3846. val: double;
  3847. len: integer;
  3848. begin
  3849. if (value = '') or not (aOwnerEl is TFeComponentTransferChild) then
  3850. Exit;
  3851. with TFeComponentTransferChild(aOwnerEl) do
  3852. begin
  3853. len := 0;
  3854. tableValues := nil;
  3855. c := PUTF8Char(value);
  3856. endC := c + Length(value);
  3857. while ParseNextNum(c, endC, true, val) do
  3858. begin
  3859. SetLength(tableValues, len +1);
  3860. tableValues[len] := val;
  3861. inc(len);
  3862. end;
  3863. end;
  3864. end;
  3865. //------------------------------------------------------------------------------
  3866. procedure Type_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  3867. begin
  3868. if (value = '') or not (aOwnerEl is TFeComponentTransferChild) then Exit;
  3869. with TFeComponentTransferChild(aOwnerEl) do
  3870. begin
  3871. case value[1] of
  3872. 'D','d': funcType := ftDiscrete;
  3873. 'G','g': funcType := ftGamma;
  3874. 'L','l': funcType := ftLinear;
  3875. 'T','t': funcType := ftTable;
  3876. else funcType := ftIdentity;
  3877. end;
  3878. end;
  3879. end;
  3880. //------------------------------------------------------------------------------
  3881. procedure LetterSpacing_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  3882. begin
  3883. with aOwnerEl do
  3884. UTF8StringToFloat(value, fDrawData.FontInfo.spacing);
  3885. end;
  3886. //------------------------------------------------------------------------------
  3887. procedure Href_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  3888. begin
  3889. case aOwnerEl.fXmlEl.Hash of
  3890. hFeImage:
  3891. TFeImageElement(aOwnerEl).refEl := ExtractRef(value);
  3892. hImage:
  3893. TImageElement(aOwnerEl).fRefEl := ExtractRef(value);
  3894. hUse:
  3895. TUseElement(aOwnerEl).fRefEl := ExtractRef(value);
  3896. hTextPath:
  3897. TTextPathElement(aOwnerEl).pathName := ExtractRef(value);
  3898. else if aOwnerEl is TFillElement then
  3899. TFillElement(aOwnerEl).refEl := ExtractRef(value);
  3900. end;
  3901. end;
  3902. //------------------------------------------------------------------------------
  3903. procedure Space_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  3904. begin
  3905. case aOwnerEl.fXmlEl.Hash of
  3906. hText: if value = 'preserve' then
  3907. TTextPathElement(aOwnerEl).fDrawData.fontInfo.spacesInText := sitPreserve;
  3908. end;
  3909. end;
  3910. //------------------------------------------------------------------------------
  3911. procedure BaselineShift_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  3912. var
  3913. mu: TUnitType;
  3914. val: double;
  3915. hash: cardinal;
  3916. c, endC: PUTF8Char;
  3917. begin
  3918. c := PUTF8Char(value);
  3919. endC := c + Length(value);
  3920. hash := ParseNextWordHash(c, endC);
  3921. with aOwnerEl.fDrawData.FontInfo do
  3922. case hash of
  3923. hSuper: baseShift.SetValue(50, utPercent);
  3924. hSub: baseShift.SetValue(-50, utPercent);
  3925. hBaseline: baseShift.SetValue(0, utPixel);
  3926. else
  3927. begin
  3928. UTF8StringToFloatEx(value, val, mu);
  3929. baseShift.SetValue(val, mu);
  3930. end;
  3931. end;
  3932. end;
  3933. //------------------------------------------------------------------------------
  3934. procedure Color_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  3935. var
  3936. color: TColor32;
  3937. begin
  3938. color := clInvalid;
  3939. UTF8StringToColor32(value, color);
  3940. //for setting currentcolor when drawing (eg drawing shapes)
  3941. aOwnerEl.fDrawData.currentColor := color;
  3942. //for setting currentcolor during element creation (eg gradient colors)
  3943. aOwnerEl.fSvgReader.currentColor := color;
  3944. end;
  3945. //------------------------------------------------------------------------------
  3946. procedure LightingColor_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  3947. var
  3948. color: TColor32;
  3949. begin
  3950. color := clInvalid;
  3951. UTF8StringToColor32(value, color);
  3952. if (aOwnerEl is TFeSpecLightElement) then
  3953. TFeSpecLightElement(aOwnerEl).color := color
  3954. else if (aOwnerEl is TFeDefuseLightElement) then
  3955. TFeDefuseLightElement(aOwnerEl).color := color
  3956. end;
  3957. //------------------------------------------------------------------------------
  3958. procedure ClipPath_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  3959. begin
  3960. aOwnerEl.fDrawData.clipElRef := ExtractRef(value);
  3961. end;
  3962. //------------------------------------------------------------------------------
  3963. procedure D_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  3964. begin
  3965. if aOwnerEl is TPathElement then
  3966. TPathElement(aOwnerEl).ParseDAttrib(value);
  3967. end;
  3968. //------------------------------------------------------------------------------
  3969. procedure Fill_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  3970. begin
  3971. case aOwnerEl.fXmlEl.Hash of
  3972. hfeDropShadow:
  3973. UTF8StringToColor32(value, TFeDropShadowElement(aOwnerEl).floodColor);
  3974. hfeFlood:
  3975. UTF8StringToColor32(value, TFeFloodElement(aOwnerEl).floodColor);
  3976. else
  3977. begin
  3978. if Match(PUTF8Char(value), 'url(') then
  3979. aOwnerEl.fDrawData.fillEl := ExtractRef(value)
  3980. else
  3981. UTF8StringToColor32(value, aOwnerEl.fDrawData.fillColor);
  3982. end;
  3983. end;
  3984. end;
  3985. //------------------------------------------------------------------------------
  3986. procedure FillOpacity_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  3987. var
  3988. opacity: double;
  3989. begin
  3990. case aOwnerEl.fXmlEl.Hash of
  3991. hfeDropShadow:
  3992. UTF8StringToOpacity(value, TFeDropShadowElement(aOwnerEl).floodColor);
  3993. hfeFlood:
  3994. UTF8StringToOpacity(value, TFeFloodElement(aOwnerEl).floodColor);
  3995. else
  3996. begin
  3997. UTF8StringToFloat(value, opacity);
  3998. with aOwnerEl.fDrawData do
  3999. begin
  4000. if IsValid(fillOpacity) then
  4001. fillOpacity := fillOpacity * opacity else
  4002. fillOpacity := opacity;
  4003. end;
  4004. end;
  4005. end;
  4006. end;
  4007. //------------------------------------------------------------------------------
  4008. procedure DashArray_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4009. var
  4010. c, endC: PUTF8Char;
  4011. val: double;
  4012. len: integer;
  4013. begin
  4014. c := PUTF8Char(value);
  4015. endC := c + Length(value);
  4016. with aOwnerEl.fDrawData do
  4017. begin
  4018. len := Length(dashArray);
  4019. while ParseNextNum(c, endC, true, val) do
  4020. begin
  4021. SetLength(dashArray, len +1);
  4022. dashArray[len] := val;
  4023. inc(len);
  4024. end;
  4025. end;
  4026. end;
  4027. //------------------------------------------------------------------------------
  4028. procedure DashOffset_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4029. var
  4030. c, endC: PUTF8Char;
  4031. begin
  4032. c := PUTF8Char(value);
  4033. endC := c + Length(value);
  4034. with aOwnerEl.fDrawData do
  4035. ParseNextNum(c, endC, true, dashOffset);
  4036. end;
  4037. //------------------------------------------------------------------------------
  4038. procedure Display_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4039. begin
  4040. if GetHash(value) = hNone then
  4041. aOwnerEl.fDrawData.visible := false;
  4042. end;
  4043. //------------------------------------------------------------------------------
  4044. procedure Font_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4045. begin
  4046. GetSvgFontInfo(value, aOwnerEl.fDrawData.FontInfo);
  4047. end;
  4048. //------------------------------------------------------------------------------
  4049. procedure FontFamily_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4050. var
  4051. hash: cardinal;
  4052. c, endC: PUTF8Char;
  4053. begin
  4054. with aOwnerEl.fDrawData.FontInfo do
  4055. begin
  4056. family := tfUnknown;
  4057. familyNames := GetCommaSeparatedArray(value);
  4058. // get comma separated family names
  4059. c := PUTF8Char(value);
  4060. endC := c + Length(value);
  4061. while ParseNextWordExHash(c, endC, hash) do
  4062. begin
  4063. case hash of
  4064. hSans_045_Serif, hArial : family := tfSansSerif;
  4065. hSerif, hTimes: family := tfSerif;
  4066. hMonospace: family := tfMonospace;
  4067. else Continue;
  4068. end;
  4069. break;
  4070. end;
  4071. end;
  4072. end;
  4073. //------------------------------------------------------------------------------
  4074. procedure FontSize_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4075. var
  4076. num: double;
  4077. c, endC: PUTF8Char;
  4078. begin
  4079. c := PUTF8Char(value); endC := c + Length(value);
  4080. if not ParseNextNum(c, endC, false, num) then Exit;
  4081. aOwnerEl.fDrawData.FontInfo.size := num;
  4082. end;
  4083. //------------------------------------------------------------------------------
  4084. procedure FontStyle_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4085. begin
  4086. with aOwnerEl.fDrawData.FontInfo do
  4087. if GetHash(value) = hItalic then
  4088. italic := sfsItalic else
  4089. italic := sfsNone;
  4090. end;
  4091. //------------------------------------------------------------------------------
  4092. procedure FontWeight_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4093. var
  4094. num: double;
  4095. hash: cardinal;
  4096. c, endC: PUTF8Char;
  4097. begin
  4098. c := PUTF8Char(value);
  4099. endC := c + Length(value);
  4100. with aOwnerEl.fDrawData.FontInfo do
  4101. begin
  4102. if IsNumPending(c, endC, false) and
  4103. ParseNextNum(c, endC, false, num) then
  4104. weight := Round(num)
  4105. else if ParseNextWordHash(c, endC, hash) then
  4106. case hash of
  4107. hBold : weight := 600;
  4108. hNormal : weight := 400;
  4109. hBolder : if weight >= 0 then weight := Min(900, weight + 200)
  4110. else weight := 600;
  4111. hLighter: if weight >= 0 then weight := Max(0, weight - 200)
  4112. else weight := 200;
  4113. end;
  4114. end;
  4115. end;
  4116. //------------------------------------------------------------------------------
  4117. procedure Fx_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4118. begin
  4119. if (aOwnerEl is TRadGradElement) then
  4120. with TRadGradElement(aOwnerEl) do
  4121. begin
  4122. UTF8StringToFloatEx(value, F.X.rawVal, F.X.unitType);
  4123. end;
  4124. end;
  4125. //------------------------------------------------------------------------------
  4126. procedure Fy_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4127. begin
  4128. if (aOwnerEl is TRadGradElement) then
  4129. with TRadGradElement(aOwnerEl) do
  4130. begin
  4131. UTF8StringToFloatEx(value, F.Y.rawVal, F.Y.unitType);
  4132. end;
  4133. end;
  4134. //------------------------------------------------------------------------------
  4135. procedure TextAlign_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4136. begin
  4137. with aOwnerEl.fDrawData.FontInfo do
  4138. case GetHash(value) of
  4139. hMiddle : align := staCenter;
  4140. hEnd : align := staRight;
  4141. hJustify : align := staJustify;
  4142. else align := staLeft;
  4143. end;
  4144. end;
  4145. //------------------------------------------------------------------------------
  4146. procedure TextDecoration_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4147. begin
  4148. with aOwnerEl.fDrawData.FontInfo do
  4149. if PosEx('underline', value) > 0 then
  4150. decoration := fdUnderline
  4151. else if PosEx('line-through', value) > 0 then
  4152. decoration := fdStrikeThrough
  4153. else
  4154. decoration := fdNone;
  4155. end;
  4156. //------------------------------------------------------------------------------
  4157. procedure TextLength_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4158. begin
  4159. UTF8StringToFloat(value, aOwnerEl.fDrawData.FontInfo.textLength);
  4160. end;
  4161. //------------------------------------------------------------------------------
  4162. procedure MarkerStart_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4163. begin
  4164. if not (aOwnerEl is TShapeElement) then Exit;
  4165. aOwnerEl.fDrawData.markerStart := ExtractRef(value);
  4166. end;
  4167. //------------------------------------------------------------------------------
  4168. procedure MarkerMiddle_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4169. begin
  4170. if not (aOwnerEl is TShapeElement) then Exit;
  4171. aOwnerEl.fDrawData.markerMiddle := ExtractRef(value);
  4172. end;
  4173. //------------------------------------------------------------------------------
  4174. procedure MarkerEnd_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4175. begin
  4176. if not (aOwnerEl is TShapeElement) then Exit;
  4177. aOwnerEl.fDrawData.markerEnd := ExtractRef(value);
  4178. end;
  4179. //------------------------------------------------------------------------------
  4180. procedure Filter_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4181. begin
  4182. if (aOwnerEl is TShapeElement) then
  4183. aOwnerEl.fDrawData.filterElRef := ExtractRef(value);
  4184. end;
  4185. //------------------------------------------------------------------------------
  4186. procedure Mask_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4187. begin
  4188. if (aOwnerEl is TShapeElement) then
  4189. aOwnerEl.fDrawData.maskElRef := ExtractRef(value);
  4190. end;
  4191. //------------------------------------------------------------------------------
  4192. procedure Offset_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4193. var
  4194. val: TValue;
  4195. begin
  4196. if (aOwnerEl is TGradStopElement) then
  4197. with TGradStopElement(aOwnerEl) do
  4198. begin
  4199. val.Init;
  4200. UTF8StringToFloatEx(value, val.rawVal, val.unitType);
  4201. offset := val.GetValue(1, GetRelFracLimit);
  4202. end
  4203. end;
  4204. //------------------------------------------------------------------------------
  4205. procedure Opacity_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4206. var
  4207. opacity: double;
  4208. begin
  4209. if not UTF8StringToFloat(value, opacity) then Exit;
  4210. opacity := ClampRange(opacity, 0,1);
  4211. with aOwnerEl.fDrawData do
  4212. begin
  4213. if IsValid(fillOpacity) then
  4214. fillOpacity := fillOpacity * opacity else
  4215. fillOpacity := opacity;
  4216. if IsValid(strokeOpacity) then
  4217. strokeOpacity := strokeOpacity * opacity else
  4218. strokeOpacity := opacity;
  4219. end;
  4220. end;
  4221. //------------------------------------------------------------------------------
  4222. procedure Operator_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4223. begin
  4224. if (aOwnerEl is TFeCompositeElement) then
  4225. with TFeCompositeElement(aOwnerEl) do
  4226. case GetHash(value) of
  4227. hAtop : compositeOp := coAtop;
  4228. hIn : compositeOp := coIn;
  4229. hOut : compositeOp := coOut;
  4230. hOver : compositeOp := coOver;
  4231. hXor : compositeOp := coXor;
  4232. hArithmetic : compositeOp := coArithmetic;
  4233. end;
  4234. end;
  4235. //------------------------------------------------------------------------------
  4236. procedure Orient_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4237. begin
  4238. if (aOwnerEl is TMarkerElement) and
  4239. (GetHash(value) = hauto_045_start_045_reverse) then
  4240. TMarkerElement(aOwnerEl).autoStartReverse := true;
  4241. end;
  4242. //------------------------------------------------------------------------------
  4243. procedure StartOffset_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4244. var
  4245. val: double;
  4246. begin
  4247. if (aOwnerEl is TTextPathElement) and
  4248. UTF8StringToFloat(value, val) then
  4249. TTextPathElement(aOwnerEl).offset.X.SetValue(val);
  4250. end;
  4251. //------------------------------------------------------------------------------
  4252. procedure StopColor_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4253. var
  4254. acolor: TColor32;
  4255. begin
  4256. if aOwnerEl is TGradStopElement then
  4257. begin
  4258. acolor := clInvalid;
  4259. UTF8StringToColor32(value, acolor);
  4260. with TGradStopElement(aOwnerEl) do
  4261. if acolor = clCurrent then
  4262. color := aOwnerEl.fSvgReader.currentColor else
  4263. color := acolor;
  4264. end;
  4265. end;
  4266. //------------------------------------------------------------------------------
  4267. procedure StopOpacity_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4268. begin
  4269. if aOwnerEl is TGradStopElement then
  4270. UTF8StringToOpacity(value, TGradStopElement(aOwnerEl).color);
  4271. end;
  4272. //------------------------------------------------------------------------------
  4273. procedure Points_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4274. begin
  4275. if aOwnerEl is TPolyElement then
  4276. TPolyElement(aOwnerEl).ParsePoints(value);
  4277. end;
  4278. //------------------------------------------------------------------------------
  4279. procedure Stroke_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4280. begin
  4281. if Match(PUTF8Char(value), 'url(') then
  4282. aOwnerEl.fDrawData.strokeEl := ExtractRef(value)
  4283. else if Match(PUTF8Char(value), 'currentcolor') then
  4284. aOwnerEl.fDrawData.strokeColor := clCurrent
  4285. else
  4286. UTF8StringToColor32(value, aOwnerEl.fDrawData.strokeColor);
  4287. end;
  4288. //------------------------------------------------------------------------------
  4289. procedure StrokeLineCap_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4290. var
  4291. hash: cardinal;
  4292. c, endC: PUTF8Char;
  4293. begin
  4294. c := PUTF8Char(value);
  4295. endC := c + Length(value);
  4296. hash := ParseNextWordHash(c, endC);
  4297. with aOwnerEl.fDrawData do
  4298. case hash of
  4299. hButt : strokeCap := esButt;
  4300. hRound : strokeCap := esRound;
  4301. hSquare : strokeCap := esSquare;
  4302. end;
  4303. end;
  4304. //------------------------------------------------------------------------------
  4305. procedure StrokeLineJoin_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4306. var
  4307. hash: cardinal;
  4308. c, endC: PUTF8Char;
  4309. begin
  4310. c := PUTF8Char(value);
  4311. endC := c + Length(value);
  4312. hash := ParseNextWordHash(c, endC);
  4313. with aOwnerEl.fDrawData do
  4314. case hash of
  4315. hMiter : strokeJoin := jsMiter;
  4316. hRound : strokeJoin := jsRound;
  4317. hBevel : strokeJoin := jsSquare;
  4318. end;
  4319. end;
  4320. //------------------------------------------------------------------------------
  4321. procedure StrokeMiterLimit_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4322. begin
  4323. UTF8StringToFloat(value, aOwnerEl.fDrawData.strokeMitLim);
  4324. end;
  4325. //------------------------------------------------------------------------------
  4326. procedure StrokeOpacity_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4327. var
  4328. opacity: double;
  4329. begin
  4330. UTF8StringToFloat(value, opacity);
  4331. opacity := ClampRange(opacity, 0,1);
  4332. with aOwnerEl.fDrawData do
  4333. begin
  4334. if IsValid(strokeOpacity) then
  4335. strokeOpacity := strokeOpacity * opacity else
  4336. strokeOpacity := opacity;
  4337. end;
  4338. end;
  4339. //------------------------------------------------------------------------------
  4340. procedure StrokeWidth_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4341. begin
  4342. with aOwnerEl do
  4343. begin
  4344. UTF8StringToFloatEx(value, fDrawData.strokewidth.rawVal,
  4345. fDrawData.strokewidth.unitType);
  4346. end;
  4347. end;
  4348. //------------------------------------------------------------------------------
  4349. procedure FillRule_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4350. begin
  4351. if LowerCaseTable[value[1]] = 'e' then
  4352. aOwnerEl.fDrawData.fillRule := frEvenOdd else
  4353. aOwnerEl.fDrawData.fillRule := frNonZero;
  4354. end;
  4355. //------------------------------------------------------------------------------
  4356. procedure Rotate_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4357. var
  4358. i, cnt: integer;
  4359. angle: TArrayOfDouble;
  4360. c, endC: PUTF8Char;
  4361. begin
  4362. SetLength(angle, Length(value));
  4363. c := PUTF8Char(value);
  4364. endC := c + Length(value);
  4365. cnt := 0;
  4366. while ParseNextNum(c, endC, true, angle[cnt]) do inc(cnt);
  4367. SetLength(angle, cnt);
  4368. for i := 0 to cnt -1 do
  4369. angle[i] := DegToRad(angle[i]);
  4370. if aOwnerEl is TTextElement then
  4371. TTextElement(aOwnerEl).angle := angle
  4372. else if aOwnerEl is TTSpanElement then
  4373. TTSpanElement(aOwnerEl).angle := angle;
  4374. end;
  4375. //------------------------------------------------------------------------------
  4376. procedure Transform_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4377. begin
  4378. with aOwnerEl.fDrawData do
  4379. MatrixMultiply2(ParseTransform(value), matrix);
  4380. end;
  4381. //------------------------------------------------------------------------------
  4382. procedure Values_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4383. var
  4384. cnt: integer;
  4385. c, endC: PUTF8Char;
  4386. begin
  4387. if aOwnerEl is TFeColorMatrixElement then
  4388. with TFeColorMatrixElement(aOwnerEl) do
  4389. begin
  4390. SetLength(values, 20);
  4391. c := PUTF8Char(value);
  4392. endC := c + Length(value);
  4393. cnt := 0;
  4394. while (cnt < 20) and ParseNextNum(c, endC, true, values[cnt]) do
  4395. inc(cnt);
  4396. if cnt < 20 then values := nil;
  4397. end;
  4398. end;
  4399. //------------------------------------------------------------------------------
  4400. procedure GradientTransform_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4401. var
  4402. mat: TMatrixD;
  4403. begin
  4404. if not (aOwnerEl is TGradientElement) then Exit;
  4405. mat := ParseTransform(value);
  4406. with aOwnerEl.fDrawData do
  4407. MatrixMultiply2(mat, matrix);
  4408. end;
  4409. //------------------------------------------------------------------------------
  4410. procedure GradientUnits_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4411. begin
  4412. if aOwnerEl is TFillElement then
  4413. with TFillElement(aOwnerEl) do
  4414. units := GetHash(value);
  4415. end;
  4416. //------------------------------------------------------------------------------
  4417. procedure Viewbox_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4418. function LoadViewbox: TRectWH;
  4419. var
  4420. c, endC: PUTF8Char;
  4421. begin
  4422. c := PUTF8Char(value);
  4423. endC := c + Length(value);
  4424. with Result do
  4425. if not ParseNextNum(c, endC, false, Left) or
  4426. not ParseNextNum(c, endC, true, Top) or
  4427. not ParseNextNum(c, endC, true, Width) or
  4428. not ParseNextNum(c, endC, true, Height) then
  4429. Result := RectWH(0,0,0,0);
  4430. end;
  4431. begin
  4432. case aOwnerEl.fXmlEl.Hash of
  4433. hSvg : TSvgElement(aOwnerEl).viewboxWH := LoadViewbox;
  4434. hMarker : TMarkerElement(aOwnerEl).markerBoxWH := LoadViewbox;
  4435. hSymbol : TSymbolElement(aOwnerEl).viewboxWH := LoadViewbox;
  4436. else if aOwnerEl is TPatternElement then
  4437. TPatternElement(aOwnerEl).pattBoxWH := LoadViewbox;
  4438. end;
  4439. end;
  4440. //------------------------------------------------------------------------------
  4441. procedure Visibility_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4442. begin
  4443. case GetHash(value) of
  4444. hCollapse: aOwnerEl.fDrawData.visible := false;
  4445. hHidden: aOwnerEl.fDrawData.visible := false;
  4446. hVisible: aOwnerEl.fDrawData.visible := true;
  4447. end;
  4448. end;
  4449. //------------------------------------------------------------------------------
  4450. procedure Height_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4451. var
  4452. mu: TUnitType;
  4453. val: double;
  4454. begin
  4455. UTF8StringToFloatEx(value, val, mu);
  4456. with aOwnerEl do
  4457. begin
  4458. elRectWH.height.SetValue(val, mu);
  4459. end;
  4460. end;
  4461. //------------------------------------------------------------------------------
  4462. procedure Width_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4463. var
  4464. mu: TUnitType;
  4465. val: double;
  4466. begin
  4467. UTF8StringToFloatEx(value, val, mu);
  4468. with aOwnerEl do
  4469. begin
  4470. elRectWH.width.SetValue(val, mu);
  4471. end;
  4472. end;
  4473. //------------------------------------------------------------------------------
  4474. procedure Cx_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4475. var
  4476. mu: TUnitType;
  4477. val: double;
  4478. begin
  4479. UTF8StringToFloatEx(value, val, mu);
  4480. case aOwnerEl.fXmlEl.Hash of
  4481. hCircle:
  4482. with TCircleElement(aOwnerEl) do centerPt.X.SetValue(val, mu);
  4483. hEllipse:
  4484. with TEllipseElement(aOwnerEl) do centerPt.X.SetValue(val, mu);
  4485. hRadialGradient:
  4486. with TRadGradElement(aOwnerEl) do
  4487. begin
  4488. C.X.SetValue(val, mu);
  4489. end;
  4490. end;
  4491. end;
  4492. //------------------------------------------------------------------------------
  4493. procedure Cy_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4494. var
  4495. mu: TUnitType;
  4496. val: double;
  4497. begin
  4498. UTF8StringToFloatEx(value, val, mu);
  4499. case aOwnerEl.fXmlEl.Hash of
  4500. hCircle:
  4501. with TCircleElement(aOwnerEl) do centerPt.Y.SetValue(val, mu);
  4502. hEllipse:
  4503. with TEllipseElement(aOwnerEl) do centerPt.Y.SetValue(val, mu);
  4504. hRadialGradient:
  4505. with TRadGradElement(aOwnerEl) do
  4506. begin
  4507. C.Y.SetValue(val, mu);
  4508. end;
  4509. end;
  4510. end;
  4511. //------------------------------------------------------------------------------
  4512. procedure Dx_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4513. var
  4514. mu: TUnitType;
  4515. val: double;
  4516. begin
  4517. UTF8StringToFloatEx(value, val, mu);
  4518. case aOwnerEl.fXmlEl.Hash of
  4519. hfeDropShadow:
  4520. TFeDropShadowElement(aOwnerEl).offset.X.SetValue(val, mu);
  4521. hfeOffset:
  4522. TFeOffsetElement(aOwnerEl).offset.X.SetValue(val, mu);
  4523. hText:
  4524. TTextElement(aOwnerEl).offset.X.SetValue(val, mu);
  4525. hTSpan:
  4526. TTSpanElement(aOwnerEl).offset.X.SetValue(val, mu);
  4527. hTextPath:
  4528. TTextPathElement(aOwnerEl).offset.X.SetValue(val, mu);
  4529. end;
  4530. end;
  4531. //------------------------------------------------------------------------------
  4532. procedure Dy_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4533. var
  4534. mu: TUnitType;
  4535. val: double;
  4536. begin
  4537. UTF8StringToFloatEx(value, val, mu);
  4538. case aOwnerEl.fXmlEl.Hash of
  4539. hfeDropShadow:
  4540. TFeDropShadowElement(aOwnerEl).offset.Y.SetValue(val, mu);
  4541. hfeOffset:
  4542. TFeOffsetElement(aOwnerEl).offset.Y.SetValue(val, mu);
  4543. hText:
  4544. TTextElement(aOwnerEl).offset.Y.SetValue(val, mu);
  4545. hTSpan:
  4546. TTSpanElement(aOwnerEl).offset.Y.SetValue(val, mu);
  4547. hTextPath:
  4548. TTextPathElement(aOwnerEl).offset.Y.SetValue(val, mu);
  4549. end;
  4550. end;
  4551. //------------------------------------------------------------------------------
  4552. procedure Result_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4553. begin
  4554. if (aOwnerEl is TFeBaseElement) then
  4555. TFeBaseElement(aOwnerEl).res := ExtractRef(value);
  4556. end;
  4557. //------------------------------------------------------------------------------
  4558. procedure Rx_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4559. var
  4560. mu: TUnitType;
  4561. val: double;
  4562. begin
  4563. UTF8StringToFloatEx(value, val, mu);
  4564. case aOwnerEl.fXmlEl.Hash of
  4565. hRect:
  4566. with TRectElement(aOwnerEl) do
  4567. begin
  4568. radius.X.SetValue(val, mu);
  4569. end;
  4570. hCircle:
  4571. with TCircleElement(aOwnerEl) do
  4572. begin
  4573. radius.SetValue(val, mu);
  4574. end;
  4575. hEllipse:
  4576. with TEllipseElement(aOwnerEl) do
  4577. begin
  4578. radius.X.SetValue(val, mu);
  4579. end;
  4580. hRadialGradient:
  4581. with TRadGradElement(aOwnerEl) do
  4582. begin
  4583. radius.X. SetValue(val, mu);
  4584. radius.Y. SetValue(val, mu);
  4585. end;
  4586. hMarker:
  4587. with TMarkerElement(aOwnerEl) do
  4588. refPt.X.SetValue(val, mu);
  4589. end;
  4590. end;
  4591. //------------------------------------------------------------------------------
  4592. procedure Ry_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4593. var
  4594. mu: TUnitType;
  4595. val: double;
  4596. begin
  4597. UTF8StringToFloatEx(value, val, mu);
  4598. case aOwnerEl.fXmlEl.Hash of
  4599. hRect:
  4600. with TRectElement(aOwnerEl) do
  4601. begin
  4602. radius.Y.SetValue(val, mu);
  4603. end;
  4604. hEllipse:
  4605. with TEllipseElement(aOwnerEl) do
  4606. begin
  4607. radius.Y.SetValue(val, mu);
  4608. end;
  4609. hMarker:
  4610. with TMarkerElement(aOwnerEl) do refPt.Y.SetValue(val, mu);
  4611. end;
  4612. end;
  4613. //------------------------------------------------------------------------------
  4614. procedure SpreadMethod_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4615. var
  4616. hash: cardinal;
  4617. c, endC: PUTF8Char;
  4618. begin
  4619. if not (aOwnerEl is TGradientElement) then Exit;
  4620. c := PUTF8Char(value);
  4621. endC := c + Length(value);
  4622. hash := ParseNextWordHash(c, endC);
  4623. with TGradientElement(aOwnerEl) do
  4624. case hash of
  4625. hPad : spreadMethod := gfsClamp;
  4626. hReflect : spreadMethod := gfsMirror;
  4627. hRepeat : spreadMethod := gfsRepeat;
  4628. end;
  4629. end;
  4630. //------------------------------------------------------------------------------
  4631. procedure SpectacularExponent(aOwnerEl: TBaseElement; const value: UTF8String);
  4632. var
  4633. se: double;
  4634. begin
  4635. if not (aOwnerEl is TFeSpecLightElement) then Exit;
  4636. UTF8StringToFloat(value, se);
  4637. if (se > 0) and (se < 100) then
  4638. TFeSpecLightElement(aOwnerEl).exponent := se;
  4639. end;
  4640. //------------------------------------------------------------------------------
  4641. procedure StdDev_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4642. var
  4643. sd: double;
  4644. begin
  4645. UTF8StringToFloat(value, sd);
  4646. if (sd < 0) and (sd > 100) then Exit;
  4647. case aOwnerEl.fXmlEl.Hash of
  4648. hfeGaussianBlur:
  4649. TFeGaussElement(aOwnerEl).stdDev := sd;
  4650. hfeDropShadow:
  4651. TFeDropShadowElement(aOwnerEl).stdDev := sd;
  4652. end;
  4653. end;
  4654. //------------------------------------------------------------------------------
  4655. procedure K1_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4656. var
  4657. val: double;
  4658. begin
  4659. UTF8StringToFloat(value, val);
  4660. if aOwnerEl is TFeCompositeElement then
  4661. TFeCompositeElement(aOwnerEl).fourKs[0] := val;
  4662. end;
  4663. //------------------------------------------------------------------------------
  4664. procedure K2_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4665. var
  4666. val: double;
  4667. begin
  4668. UTF8StringToFloat(value, val);
  4669. if aOwnerEl is TFeCompositeElement then
  4670. TFeCompositeElement(aOwnerEl).fourKs[1] := val;
  4671. end;
  4672. //------------------------------------------------------------------------------
  4673. procedure K3_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4674. var
  4675. val: double;
  4676. begin
  4677. UTF8StringToFloat(value, val);
  4678. if aOwnerEl is TFeCompositeElement then
  4679. TFeCompositeElement(aOwnerEl).fourKs[2] := val;
  4680. end;
  4681. //------------------------------------------------------------------------------
  4682. procedure K4_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4683. var
  4684. val: double;
  4685. begin
  4686. UTF8StringToFloat(value, val);
  4687. if aOwnerEl is TFeCompositeElement then
  4688. TFeCompositeElement(aOwnerEl).fourKs[3] := val;
  4689. end;
  4690. //------------------------------------------------------------------------------
  4691. procedure X1_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4692. var
  4693. mu: TUnitType;
  4694. val: double;
  4695. begin
  4696. UTF8StringToFloatEx(value, val, mu);
  4697. case aOwnerEl.fXmlEl.Hash of
  4698. hLine:
  4699. TLineElement(aOwnerEl).path[0].X := val;
  4700. hLinearGradient:
  4701. with TLinGradElement(aOwnerEl) do
  4702. begin
  4703. startPt.X.SetValue(val, mu);
  4704. end;
  4705. hFilter:
  4706. with aOwnerEl do
  4707. begin
  4708. elRectWH.left.SetValue(val, mu);
  4709. end;
  4710. else
  4711. aOwnerEl.elRectWH.left.SetValue(val, mu);
  4712. end;
  4713. end;
  4714. //------------------------------------------------------------------------------
  4715. procedure X2_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4716. var
  4717. mu: TUnitType;
  4718. val: double;
  4719. begin
  4720. UTF8StringToFloatEx(value, val, mu);
  4721. case aOwnerEl.fXmlEl.Hash of
  4722. hLine:
  4723. TLineElement(aOwnerEl).path[1].X := val;
  4724. hLinearGradient:
  4725. with TLinGradElement(aOwnerEl) do
  4726. begin
  4727. endPt.X.SetValue(val, mu);
  4728. end;
  4729. end;
  4730. end;
  4731. //------------------------------------------------------------------------------
  4732. procedure Y1_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4733. var
  4734. mu: TUnitType;
  4735. val: double;
  4736. begin
  4737. UTF8StringToFloatEx(value, val, mu);
  4738. case aOwnerEl.fXmlEl.Hash of
  4739. hLine:
  4740. TLineElement(aOwnerEl).path[0].Y := val;
  4741. hLinearGradient:
  4742. with TLinGradElement(aOwnerEl) do
  4743. begin
  4744. startPt.Y.SetValue(val, mu);
  4745. end;
  4746. hFilter:
  4747. with aOwnerEl do
  4748. begin
  4749. elRectWH.top.SetValue(val, mu);
  4750. end;
  4751. else
  4752. aOwnerEl.elRectWH.top.SetValue(val, mu);
  4753. end;
  4754. end;
  4755. //------------------------------------------------------------------------------
  4756. procedure Y2_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4757. var
  4758. mu: TUnitType;
  4759. val: double;
  4760. begin
  4761. UTF8StringToFloatEx(value, val, mu);
  4762. case aOwnerEl.fXmlEl.Hash of
  4763. hLine:
  4764. TLineElement(aOwnerEl).path[1].Y := val;
  4765. hLinearGradient:
  4766. with TLinGradElement(aOwnerEl) do
  4767. begin
  4768. endPt.Y.SetValue(val, mu);
  4769. end;
  4770. end;
  4771. end;
  4772. //------------------------------------------------------------------------------
  4773. procedure Z_Attrib(aOwnerEl: TBaseElement; const value: UTF8String);
  4774. var
  4775. val: double;
  4776. begin
  4777. UTF8StringToFloat(value, val);
  4778. if aOwnerEl is TFePointLightElement then
  4779. TFePointLightElement(aOwnerEl).z := val;
  4780. end;
  4781. //------------------------------------------------------------------------------
  4782. //------------------------------------------------------------------------------
  4783. procedure TBaseElement.LoadAttribute(attrib: PSvgAttrib);
  4784. begin
  4785. with attrib^ do
  4786. case hash of
  4787. hbaseline_045_shift: BaselineShift_Attrib(self, value);
  4788. hColor: Color_Attrib(self, value);
  4789. hClip_045_path: ClipPath_Attrib(self, value);
  4790. hCx: Cx_Attrib(self, value);
  4791. hCy: Cy_Attrib(self, value);
  4792. hD: D_Attrib(self, value);
  4793. hDisplay: Display_Attrib(self, value);
  4794. hDx: Dx_Attrib(self, value);
  4795. hDy: Dy_Attrib(self, value);
  4796. hStroke_045_DashArray: DashArray_Attrib(self, value);
  4797. hStroke_045_DashOffset: DashOffset_Attrib(self, value);
  4798. hFill: Fill_Attrib(self, value);
  4799. hFill_045_Opacity: FillOpacity_Attrib(self, value);
  4800. hFill_045_Rule: FillRule_Attrib(self, value);
  4801. hFilter: Filter_Attrib(self, value);
  4802. hflood_045_color: Fill_Attrib(self, value);
  4803. hflood_045_opacity: FillOpacity_Attrib(self, value);
  4804. hFont: Font_Attrib(self, value);
  4805. hFont_045_Family: FontFamily_Attrib(self, value);
  4806. hFont_045_Size: FontSize_Attrib(self, value);
  4807. hFont_045_Style: FontStyle_Attrib(self, value);
  4808. hFont_045_Weight: FontWeight_Attrib(self, value);
  4809. hFx: Fx_Attrib(self, value);
  4810. hFy: Fy_Attrib(self, value);
  4811. hGradientTransform: GradientTransform_Attrib(self, value);
  4812. hGradientUnits: GradientUnits_Attrib(self, value);
  4813. hHeight: Height_Attrib(self, value);
  4814. hHref: Href_Attrib(self, value);
  4815. hId: Id_Attrib(self, value);
  4816. hIn: In_Attrib(self, value);
  4817. hIn2: In2_Attrib(self, value);
  4818. hIntercept: Intercept_Attrib(self, value);
  4819. hk1: K1_Attrib(self, value);
  4820. hk2: K2_Attrib(self, value);
  4821. hk3: K3_Attrib(self, value);
  4822. hk4: K4_Attrib(self, value);
  4823. hletter_045_spacing: LetterSpacing_Attrib(self, value);
  4824. //hlighting_045_color: LightingColor_Attrib(self, value);
  4825. hMarker_045_End: MarkerEnd_Attrib(self, value);
  4826. hMarkerHeight: Height_Attrib(self, value);
  4827. hMarker_045_Mid: MarkerMiddle_Attrib(self, value);
  4828. hMarker_045_Start: MarkerStart_Attrib(self, value);
  4829. hMarkerWidth: Width_Attrib(self, value);
  4830. hMask: Mask_Attrib(self, value);
  4831. hOffset: Offset_Attrib(self, value);
  4832. hOpacity: Opacity_Attrib(self, value);
  4833. hOperator: Operator_Attrib(self, value);
  4834. hOrient: Orient_Attrib(self, value);
  4835. hPatternUnits: GradientUnits_Attrib(self, value);
  4836. hPatternTransform: Transform_Attrib(self, value);
  4837. hPoints: Points_Attrib(self, value);
  4838. hR: Rx_Attrib(self, value);
  4839. hRefX: Rx_Attrib(self, value);
  4840. hRefY: Ry_Attrib(self, value);
  4841. hResult: Result_Attrib(self, value);
  4842. hRotate: Rotate_Attrib(self, value);
  4843. hRx: Rx_Attrib(self, value);
  4844. hRy: Ry_Attrib(self, value);
  4845. hspecularExponent: SpectacularExponent(self, value);
  4846. hSlope: Slope_Attrib(self, value);
  4847. hSpace: Space_Attrib(self, value);
  4848. hSpreadMethod: SpreadMethod_Attrib(self, value);
  4849. hstdDeviation: StdDev_Attrib(self, value);
  4850. hStartOffset: StartOffset_Attrib(self, value);
  4851. hStop_045_Color: StopColor_Attrib(self, value);
  4852. hStop_045_Opacity: StopOpacity_Attrib(self, value);
  4853. hStroke: Stroke_Attrib(self, value);
  4854. hstroke_045_linecap: StrokeLineCap_Attrib(self, value);
  4855. hstroke_045_linejoin: StrokeLineJoin_Attrib(self, value);
  4856. hstroke_045_miterlimit: StrokeMiterLimit_Attrib(self, value);
  4857. hStroke_045_Opacity: StrokeOpacity_Attrib(self, value);
  4858. hStroke_045_Width: StrokeWidth_Attrib(self, value);
  4859. hTableValues: TableValues_Attrib(self, value);
  4860. hText_045_Anchor: TextAlign_Attrib(self, value);
  4861. hText_045_Decoration: TextDecoration_Attrib(self, value);
  4862. hTextLength: TextLength_Attrib(self, value);
  4863. hTransform: Transform_Attrib(self, value);
  4864. hType: Type_Attrib(self, value);
  4865. hValues: Values_Attrib(self, value);
  4866. hViewbox: Viewbox_Attrib(self, value);
  4867. hVisibility: Visibility_Attrib(self, value);
  4868. hWidth: Width_Attrib(self, value);
  4869. hX: X1_Attrib(self, value);
  4870. hX1: X1_Attrib(self, value);
  4871. hX2: X2_Attrib(self, value);
  4872. hXlink_058_Href: Href_Attrib(self, value);
  4873. hY: Y1_Attrib(self, value);
  4874. hY1: Y1_Attrib(self, value);
  4875. hY2: Y2_Attrib(self, value);
  4876. hZ: Z_Attrib(self, value);
  4877. end;
  4878. end;
  4879. //------------------------------------------------------------------------------
  4880. procedure TBaseElement.LoadAttributes;
  4881. var
  4882. i: integer;
  4883. begin
  4884. for i := 0 to fXmlEl.AttribCount -1 do
  4885. LoadAttribute(PSvgAttrib(fXmlEl.attrib[i]));
  4886. end;
  4887. //------------------------------------------------------------------------------
  4888. function PreferRelativeFraction(val: TValue): TTriState;
  4889. {$IFDEF INLINE} inline; {$ENDIF}
  4890. begin
  4891. if (val.rawVal = InvalidD) or (val.unitType = utUnknown) then
  4892. Result := tsUnknown
  4893. else if val.unitType = utPercent then Result := tsYes
  4894. else if val.unitType <> utNumber then Result := tsNo
  4895. else if (Abs(val.rawVal) < 1) then Result := tsYes
  4896. else Result := tsNo;
  4897. end;
  4898. //------------------------------------------------------------------------------
  4899. function TBaseElement.GetRelFracLimit: double;
  4900. begin
  4901. //the default behaviour here is to assume untyped fractional values
  4902. //below 1.0 are values relative (to the bounding size) BUT ONLY WHEN
  4903. //the parent element's width or height are relative (ie percentages).
  4904. if Assigned(fParent) and (fParent.fXmlEl.hash <> hSvg) then
  4905. begin
  4906. case PreferRelativeFraction(fParent.elRectWH.width) of
  4907. tsYes: begin Result := 1.0; Exit; end;
  4908. tsNo : begin Result := 0; Exit; end;
  4909. end;
  4910. case PreferRelativeFraction(fParent.elRectWH.height) of
  4911. tsYes: begin Result := 1.0; Exit; end;
  4912. tsNo : begin Result := 0; Exit; end;
  4913. end;
  4914. end;
  4915. Result := 0;
  4916. end;
  4917. //------------------------------------------------------------------------------
  4918. function TBaseElement.LoadContent: Boolean;
  4919. var
  4920. i : integer;
  4921. svgEl : TSvgXmlEl;
  4922. elClass : TElementClass;
  4923. el : TBaseElement;
  4924. begin
  4925. Result := false;
  4926. for i := 0 to fXmlEl.childs.Count -1 do
  4927. begin
  4928. svgEl := TSvgXmlEl(fXmlEl.childs[i]);
  4929. if svgEl.hash = 0 then Continue;
  4930. elClass := HashToElementClass(svgEl.hash);
  4931. el := elClass.Create(self, svgEl);
  4932. Self.fChilds.Add(el);
  4933. el.LoadAttributes;
  4934. if el.fXmlEl.childs.Count = 0 then Continue
  4935. else if not el.LoadContent then Exit; //error
  4936. end;
  4937. Result := true;
  4938. end;
  4939. //------------------------------------------------------------------------------
  4940. // TSvgReader
  4941. //------------------------------------------------------------------------------
  4942. constructor TSvgReader.Create;
  4943. begin
  4944. fSvgParser := TSvgParser.Create;
  4945. fLinGradRenderer := TLinearGradientRenderer.Create;
  4946. fRadGradRenderer := TSvgRadialGradientRenderer.Create;
  4947. fCustomRendererCache := TCustomRendererCache.Create;
  4948. fIdList := TSvgIdNameHashMap.Create;
  4949. fSimpleDrawList := TList.Create;
  4950. fBlurQuality := 1; //0: draft (faster); 1: good; 2: excellent (slow)
  4951. currentColor := clBlack32;
  4952. fUsePropScale := true;
  4953. end;
  4954. //------------------------------------------------------------------------------
  4955. destructor TSvgReader.Destroy;
  4956. begin
  4957. Clear;
  4958. fSvgParser.Free;
  4959. fIdList.Free;
  4960. fLinGradRenderer.Free;
  4961. fRadGradRenderer.Free;
  4962. fCustomRendererCache.Free;
  4963. FreeAndNil(fFontCache);
  4964. fSimpleDrawList.Free;
  4965. inherited;
  4966. end;
  4967. //------------------------------------------------------------------------------
  4968. procedure TSvgReader.Clear;
  4969. var
  4970. i: integer;
  4971. begin
  4972. FreeAndNil(fRootElement);
  4973. fSvgParser.Clear;
  4974. fIdList.Clear;
  4975. fLinGradRenderer.Clear;
  4976. fRadGradRenderer.Clear;
  4977. currentColor := clBlack32;
  4978. userSpaceBounds := NullRectD;
  4979. for i := 0 to fSimpleDrawList.Count -1 do
  4980. Dispose(PSimpleDrawData(fSimpleDrawList[i]));
  4981. fSimpleDrawList.Clear;
  4982. end;
  4983. //------------------------------------------------------------------------------
  4984. procedure TSvgReader.DrawImage(img: TImage32; scaleToImage: Boolean);
  4985. var
  4986. scale, scaleH: double;
  4987. di: TDrawData;
  4988. begin
  4989. if not Assigned(fRootElement) or not assigned(img) then Exit;
  4990. with fRootElement do
  4991. begin
  4992. if viewboxWH.IsEmpty then
  4993. begin
  4994. viewboxWH.Width := elRectWH.width.GetValue(defaultSvgWidth, 0);
  4995. viewboxWH.height := elRectWH.height.GetValue(defaultSvgHeight, 0);
  4996. if viewboxWH.IsEmpty then Exit; // this should never happen
  4997. end;
  4998. fBackgndImage := img;
  4999. di := fDrawData;
  5000. if di.currentColor = clInvalid then
  5001. di.currentColor := currentColor;
  5002. MatrixTranslate(di.matrix, -viewboxWH.Left, -viewboxWH.Top);
  5003. //the width and height attributes generally indicate the size of the
  5004. //rendered image unless they are percentage values. Nevertheless, these
  5005. //values can be still overridden by the scaleToImage parameter above
  5006. di.bounds := viewboxWH.RectD;
  5007. userSpaceBounds := fDrawData.bounds;
  5008. if scaleToImage and not img.IsEmpty then
  5009. begin
  5010. //nb: the calculated vbox.width and vbox.height are ignored here since
  5011. //we're scaling the SVG image to the display image. However we still
  5012. //need to call GetViewbox (above) to make sure that viewboxWH is filled.
  5013. if fUsePropScale then
  5014. begin
  5015. scale := GetScaleForBestFit(
  5016. viewboxWH.Width, viewboxWH.Height, img.Width, img.Height);
  5017. scaleH := scale;
  5018. end else
  5019. begin
  5020. scale := GetScale(viewboxWH.Width, img.Width);
  5021. scaleH := GetScale(viewboxWH.Height, img.Height);
  5022. end;
  5023. MatrixScale(di.matrix, scale, scaleH);
  5024. img.SetSize(
  5025. Round(viewboxWH.Width * scale),
  5026. Round(viewboxWH.Height * scaleH));
  5027. end else
  5028. img.SetSize(Round(viewboxWH.Width), Round(viewboxWH.Height));
  5029. end;
  5030. if fBkgndColor <> clNone32 then
  5031. img.Clear(fBkgndColor);
  5032. // // Delay the creation of the TempImage until it is actually needed.
  5033. // // Not all SVGs need it.
  5034. // fTempImageWidth := img.Width;
  5035. // fTempImageHeight := img.Height;
  5036. img.BeginUpdate;
  5037. try
  5038. fRootElement.Draw(img, di);
  5039. finally
  5040. // fTempImageWidth := 0;
  5041. // fTempImageHeight := 0;
  5042. FreeAndNil(fTempImage);
  5043. img.EndUpdate;
  5044. end;
  5045. end;
  5046. //------------------------------------------------------------------------------
  5047. function TSvgReader.GetTempImage: TImage32;
  5048. var
  5049. Pixels: TArrayOfColor32;
  5050. begin
  5051. // Use an additional method to execute the dyn-array management
  5052. // only if we create the TempImage.
  5053. if fTempImage = nil then
  5054. begin
  5055. // Create an uninitialized image. It is cleared by the caller before it is used.
  5056. NewColor32Array(Pixels, BackgndImage.Width * BackgndImage.Height, True);
  5057. fTempImage := TImage32.Create(Pixels, BackgndImage.Width, BackgndImage.Height);
  5058. fTempImage.BlockNotify;
  5059. end;
  5060. Result := fTempImage;
  5061. end;
  5062. //------------------------------------------------------------------------------
  5063. function TSvgReader.LoadInternal: Boolean;
  5064. begin
  5065. Result := false;
  5066. if not Assigned(fSvgParser.svgTree) or
  5067. (fSvgParser.svgTree.hash <> hSvg) then Exit;
  5068. fRootElement := TSvgElement.Create(nil, fSvgParser.svgTree);
  5069. fRootElement.fSvgReader := self;
  5070. fRootElement.LoadAttributes;
  5071. Result := fRootElement.LoadContent;
  5072. end;
  5073. //------------------------------------------------------------------------------
  5074. function TSvgReader.LoadFromFile(const filename: string): Boolean;
  5075. begin
  5076. Clear;
  5077. Result := fSvgParser.LoadFromFile(filename) and LoadInternal;
  5078. end;
  5079. //------------------------------------------------------------------------------
  5080. function TSvgReader.LoadFromStream(stream: TStream): Boolean;
  5081. begin
  5082. Clear;
  5083. Result := fSvgParser.LoadFromStream(stream) and LoadInternal;
  5084. end;
  5085. //------------------------------------------------------------------------------
  5086. function TSvgReader.LoadFromString(const str: string): Boolean;
  5087. begin
  5088. Clear;
  5089. Result := fSvgParser.LoadFromString(str) and LoadInternal;
  5090. end;
  5091. //------------------------------------------------------------------------------
  5092. procedure TSvgReader.SetOverrideFillColor(color: TColor32);
  5093. var
  5094. dd: TDrawData;
  5095. begin
  5096. if not Assigned(RootElement) or (color = clNone32) then Exit;
  5097. dd := RootElement.DrawData;
  5098. dd.fillColor := color;
  5099. RootElement.DrawData := dd;
  5100. end;
  5101. //------------------------------------------------------------------------------
  5102. procedure TSvgReader.SetOverrideStrokeColor(color: TColor32);
  5103. var
  5104. dd: TDrawData;
  5105. begin
  5106. if not Assigned(RootElement) or (color = clNone32) then Exit;
  5107. dd := RootElement.DrawData;
  5108. if dd.strokeColor = clInvalid then Exit;
  5109. dd.strokeColor := color;
  5110. RootElement.DrawData := dd;
  5111. end;
  5112. //------------------------------------------------------------------------------
  5113. function TSvgReader.FindElement(const idName: UTF8String): TBaseElement;
  5114. begin
  5115. if Assigned(RootElement) then
  5116. Result := RootElement.FindChild(idName) else
  5117. Result := nil;
  5118. end;
  5119. //------------------------------------------------------------------------------
  5120. procedure TSvgReader.GetBestFont(const svgFontInfo: TSVGFontInfo);
  5121. var
  5122. i, len: integer;
  5123. bestFontReader: TFontReader;
  5124. fi: TFontInfo;
  5125. begin
  5126. if svgFontInfo.family = tfUnknown then
  5127. fi.family := tfSerif else
  5128. fi.family := svgFontInfo.family;
  5129. fi.faceName := ''; //just match to a family here, not to a specific facename
  5130. fi.macStyles := [];
  5131. len := Length(svgFontInfo.familyNames);
  5132. SetLength(fi.familyNames, len);
  5133. for i := 0 to len -1 do
  5134. fi.familyNames[i] := svgFontInfo.familyNames[i];
  5135. if svgFontInfo.italic = sfsItalic then
  5136. Include(fi.macStyles, msItalic);
  5137. if svgFontInfo.weight >= 600 then
  5138. Include(fi.macStyles, msBold);
  5139. bestFontReader := FontManager.GetBestMatchFont(fi);
  5140. if not Assigned(bestFontReader) then Exit;
  5141. if Assigned(fFontCache) then
  5142. fFontCache.FontReader := bestFontReader else
  5143. fFontCache := TFontCache.Create(bestFontReader, defaultFontHeight);
  5144. fFontCache.Underlined := False;
  5145. fFontCache.StrikeOut := False;
  5146. case svgFontInfo.decoration of
  5147. fdUnderline : fFontCache.Underlined := true;
  5148. fdStrikeThrough : fFontCache.StrikeOut := true;
  5149. end;
  5150. end;
  5151. //------------------------------------------------------------------------------
  5152. procedure TSvgReader.SetBlurQuality(quality: integer);
  5153. begin
  5154. fBlurQuality := Max(0, Min(2, quality));
  5155. end;
  5156. //------------------------------------------------------------------------------
  5157. function TSvgReader.GetIsEmpty: Boolean;
  5158. begin
  5159. Result := not Assigned(fRootElement);
  5160. end;
  5161. //------------------------------------------------------------------------------
  5162. //------------------------------------------------------------------------------
  5163. end.