svg2pdf.src.js 120 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259
  1. /**
  2. * Modules in this bundle
  3. * @license
  4. *
  5. * svg2pdf.js:
  6. * license: MIT (http://opensource.org/licenses/MIT)
  7. * author: yFiles for HTML Support Team <yfileshtml@yworks.com>
  8. * homepage: https://github.com/yWorks/svg2pdf.js#readme
  9. * version: 1.3.0, modified for Highcharts issue #9779
  10. *
  11. * font-family:
  12. * license: MIT (http://opensource.org/licenses/MIT)
  13. * author: Taro Hanamura <m@hanamurataro.com>
  14. * homepage: https://github.com/hanamura/font-family
  15. * version: 0.2.0
  16. *
  17. * svgpath:
  18. * license: MIT (http://opensource.org/licenses/MIT)
  19. * homepage: https://github.com/fontello/svgpath#readme
  20. * version: 2.2.1
  21. *
  22. * This header is generated by licensify (https://github.com/twada/licensify)
  23. */
  24. (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.svg2pdf = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
  25. 'use strict';
  26. module.exports = require('./lib/svgpath');
  27. },{"./lib/svgpath":6}],2:[function(require,module,exports){
  28. // Convert an arc to a sequence of cubic bézier curves
  29. //
  30. 'use strict';
  31. var TAU = Math.PI * 2;
  32. /* eslint-disable space-infix-ops */
  33. // Calculate an angle between two vectors
  34. //
  35. function vector_angle(ux, uy, vx, vy) {
  36. var sign = (ux * vy - uy * vx < 0) ? -1 : 1;
  37. var umag = Math.sqrt(ux * ux + uy * uy);
  38. var vmag = Math.sqrt(ux * ux + uy * uy);
  39. var dot = ux * vx + uy * vy;
  40. var div = dot / (umag * vmag);
  41. // rounding errors, e.g. -1.0000000000000002 can screw up this
  42. if (div > 1.0) { div = 1.0; }
  43. if (div < -1.0) { div = -1.0; }
  44. return sign * Math.acos(div);
  45. }
  46. // Convert from endpoint to center parameterization,
  47. // see http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
  48. //
  49. // Return [cx, cy, theta1, delta_theta]
  50. //
  51. function get_arc_center(x1, y1, x2, y2, fa, fs, rx, ry, sin_phi, cos_phi) {
  52. // Step 1.
  53. //
  54. // Moving an ellipse so origin will be the middlepoint between our two
  55. // points. After that, rotate it to line up ellipse axes with coordinate
  56. // axes.
  57. //
  58. var x1p = cos_phi*(x1-x2)/2 + sin_phi*(y1-y2)/2;
  59. var y1p = -sin_phi*(x1-x2)/2 + cos_phi*(y1-y2)/2;
  60. var rx_sq = rx * rx;
  61. var ry_sq = ry * ry;
  62. var x1p_sq = x1p * x1p;
  63. var y1p_sq = y1p * y1p;
  64. // Step 2.
  65. //
  66. // Compute coordinates of the centre of this ellipse (cx', cy')
  67. // in the new coordinate system.
  68. //
  69. var radicant = (rx_sq * ry_sq) - (rx_sq * y1p_sq) - (ry_sq * x1p_sq);
  70. if (radicant < 0) {
  71. // due to rounding errors it might be e.g. -1.3877787807814457e-17
  72. radicant = 0;
  73. }
  74. radicant /= (rx_sq * y1p_sq) + (ry_sq * x1p_sq);
  75. radicant = Math.sqrt(radicant) * (fa === fs ? -1 : 1);
  76. var cxp = radicant * rx/ry * y1p;
  77. var cyp = radicant * -ry/rx * x1p;
  78. // Step 3.
  79. //
  80. // Transform back to get centre coordinates (cx, cy) in the original
  81. // coordinate system.
  82. //
  83. var cx = cos_phi*cxp - sin_phi*cyp + (x1+x2)/2;
  84. var cy = sin_phi*cxp + cos_phi*cyp + (y1+y2)/2;
  85. // Step 4.
  86. //
  87. // Compute angles (theta1, delta_theta).
  88. //
  89. var v1x = (x1p - cxp) / rx;
  90. var v1y = (y1p - cyp) / ry;
  91. var v2x = (-x1p - cxp) / rx;
  92. var v2y = (-y1p - cyp) / ry;
  93. var theta1 = vector_angle(1, 0, v1x, v1y);
  94. var delta_theta = vector_angle(v1x, v1y, v2x, v2y);
  95. if (fs === 0 && delta_theta > 0) {
  96. delta_theta -= TAU;
  97. }
  98. if (fs === 1 && delta_theta < 0) {
  99. delta_theta += TAU;
  100. }
  101. return [ cx, cy, theta1, delta_theta ];
  102. }
  103. //
  104. // Approximate one unit arc segment with bézier curves,
  105. // see http://math.stackexchange.com/questions/873224
  106. //
  107. function approximate_unit_arc(theta1, delta_theta) {
  108. var alpha = 4/3 * Math.tan(delta_theta/4);
  109. var x1 = Math.cos(theta1);
  110. var y1 = Math.sin(theta1);
  111. var x2 = Math.cos(theta1 + delta_theta);
  112. var y2 = Math.sin(theta1 + delta_theta);
  113. return [ x1, y1, x1 - y1*alpha, y1 + x1*alpha, x2 + y2*alpha, y2 - x2*alpha, x2, y2 ];
  114. }
  115. module.exports = function a2c(x1, y1, x2, y2, fa, fs, rx, ry, phi) {
  116. var sin_phi = Math.sin(phi * TAU / 360);
  117. var cos_phi = Math.cos(phi * TAU / 360);
  118. // Make sure radii are valid
  119. //
  120. var x1p = cos_phi*(x1-x2)/2 + sin_phi*(y1-y2)/2;
  121. var y1p = -sin_phi*(x1-x2)/2 + cos_phi*(y1-y2)/2;
  122. if (x1p === 0 && y1p === 0) {
  123. // we're asked to draw line to itself
  124. return [];
  125. }
  126. if (rx === 0 || ry === 0) {
  127. // one of the radii is zero
  128. return [];
  129. }
  130. // Compensate out-of-range radii
  131. //
  132. rx = Math.abs(rx);
  133. ry = Math.abs(ry);
  134. var lambda = (x1p * x1p) / (rx * rx) + (y1p * y1p) / (ry * ry);
  135. if (lambda > 1) {
  136. rx *= Math.sqrt(lambda);
  137. ry *= Math.sqrt(lambda);
  138. }
  139. // Get center parameters (cx, cy, theta1, delta_theta)
  140. //
  141. var cc = get_arc_center(x1, y1, x2, y2, fa, fs, rx, ry, sin_phi, cos_phi);
  142. var result = [];
  143. var theta1 = cc[2];
  144. var delta_theta = cc[3];
  145. // Split an arc to multiple segments, so each segment
  146. // will be less than τ/4 (= 90°)
  147. //
  148. var segments = Math.max(Math.ceil(Math.abs(delta_theta) / (TAU / 4)), 1);
  149. delta_theta /= segments;
  150. for (var i = 0; i < segments; i++) {
  151. result.push(approximate_unit_arc(theta1, delta_theta));
  152. theta1 += delta_theta;
  153. }
  154. // We have a bezier approximation of a unit circle,
  155. // now need to transform back to the original ellipse
  156. //
  157. return result.map(function (curve) {
  158. for (var i = 0; i < curve.length; i += 2) {
  159. var x = curve[i + 0];
  160. var y = curve[i + 1];
  161. // scale
  162. x *= rx;
  163. y *= ry;
  164. // rotate
  165. var xp = cos_phi*x - sin_phi*y;
  166. var yp = sin_phi*x + cos_phi*y;
  167. // translate
  168. curve[i + 0] = xp + cc[0];
  169. curve[i + 1] = yp + cc[1];
  170. }
  171. return curve;
  172. });
  173. };
  174. },{}],3:[function(require,module,exports){
  175. 'use strict';
  176. /* eslint-disable space-infix-ops */
  177. // The precision used to consider an ellipse as a circle
  178. //
  179. var epsilon = 0.0000000001;
  180. // To convert degree in radians
  181. //
  182. var torad = Math.PI / 180;
  183. // Class constructor :
  184. // an ellipse centred at 0 with radii rx,ry and x - axis - angle ax.
  185. //
  186. function Ellipse(rx, ry, ax) {
  187. if (!(this instanceof Ellipse)) { return new Ellipse(rx, ry, ax); }
  188. this.rx = rx;
  189. this.ry = ry;
  190. this.ax = ax;
  191. }
  192. // Apply a linear transform m to the ellipse
  193. // m is an array representing a matrix :
  194. // - -
  195. // | m[0] m[2] |
  196. // | m[1] m[3] |
  197. // - -
  198. //
  199. Ellipse.prototype.transform = function (m) {
  200. // We consider the current ellipse as image of the unit circle
  201. // by first scale(rx,ry) and then rotate(ax) ...
  202. // So we apply ma = m x rotate(ax) x scale(rx,ry) to the unit circle.
  203. var c = Math.cos(this.ax * torad), s = Math.sin(this.ax * torad);
  204. var ma = [
  205. this.rx * (m[0]*c + m[2]*s),
  206. this.rx * (m[1]*c + m[3]*s),
  207. this.ry * (-m[0]*s + m[2]*c),
  208. this.ry * (-m[1]*s + m[3]*c)
  209. ];
  210. // ma * transpose(ma) = [ J L ]
  211. // [ L K ]
  212. // L is calculated later (if the image is not a circle)
  213. var J = ma[0]*ma[0] + ma[2]*ma[2],
  214. K = ma[1]*ma[1] + ma[3]*ma[3];
  215. // the discriminant of the characteristic polynomial of ma * transpose(ma)
  216. var D = ((ma[0]-ma[3])*(ma[0]-ma[3]) + (ma[2]+ma[1])*(ma[2]+ma[1])) *
  217. ((ma[0]+ma[3])*(ma[0]+ma[3]) + (ma[2]-ma[1])*(ma[2]-ma[1]));
  218. // the "mean eigenvalue"
  219. var JK = (J + K) / 2;
  220. // check if the image is (almost) a circle
  221. if (D < epsilon * JK) {
  222. // if it is
  223. this.rx = this.ry = Math.sqrt(JK);
  224. this.ax = 0;
  225. return this;
  226. }
  227. // if it is not a circle
  228. var L = ma[0]*ma[1] + ma[2]*ma[3];
  229. D = Math.sqrt(D);
  230. // {l1,l2} = the two eigen values of ma * transpose(ma)
  231. var l1 = JK + D/2,
  232. l2 = JK - D/2;
  233. // the x - axis - rotation angle is the argument of the l1 - eigenvector
  234. this.ax = (Math.abs(L) < epsilon && Math.abs(l1 - K) < epsilon) ?
  235. 90
  236. :
  237. Math.atan(Math.abs(L) > Math.abs(l1 - K) ?
  238. (l1 - J) / L
  239. :
  240. L / (l1 - K)
  241. ) * 180 / Math.PI;
  242. // if ax > 0 => rx = sqrt(l1), ry = sqrt(l2), else exchange axes and ax += 90
  243. if (this.ax >= 0) {
  244. // if ax in [0,90]
  245. this.rx = Math.sqrt(l1);
  246. this.ry = Math.sqrt(l2);
  247. } else {
  248. // if ax in ]-90,0[ => exchange axes
  249. this.ax += 90;
  250. this.rx = Math.sqrt(l2);
  251. this.ry = Math.sqrt(l1);
  252. }
  253. return this;
  254. };
  255. // Check if the ellipse is (almost) degenerate, i.e. rx = 0 or ry = 0
  256. //
  257. Ellipse.prototype.isDegenerate = function () {
  258. return (this.rx < epsilon * this.ry || this.ry < epsilon * this.rx);
  259. };
  260. module.exports = Ellipse;
  261. },{}],4:[function(require,module,exports){
  262. 'use strict';
  263. // combine 2 matrixes
  264. // m1, m2 - [a, b, c, d, e, g]
  265. //
  266. function combine(m1, m2) {
  267. return [
  268. m1[0] * m2[0] + m1[2] * m2[1],
  269. m1[1] * m2[0] + m1[3] * m2[1],
  270. m1[0] * m2[2] + m1[2] * m2[3],
  271. m1[1] * m2[2] + m1[3] * m2[3],
  272. m1[0] * m2[4] + m1[2] * m2[5] + m1[4],
  273. m1[1] * m2[4] + m1[3] * m2[5] + m1[5]
  274. ];
  275. }
  276. function Matrix() {
  277. if (!(this instanceof Matrix)) { return new Matrix(); }
  278. this.queue = []; // list of matrixes to apply
  279. this.cache = null; // combined matrix cache
  280. }
  281. Matrix.prototype.matrix = function (m) {
  282. if (m[0] === 1 && m[1] === 0 && m[2] === 0 && m[3] === 1 && m[4] === 0 && m[5] === 0) {
  283. return this;
  284. }
  285. this.cache = null;
  286. this.queue.push(m);
  287. return this;
  288. };
  289. Matrix.prototype.translate = function (tx, ty) {
  290. if (tx !== 0 || ty !== 0) {
  291. this.cache = null;
  292. this.queue.push([ 1, 0, 0, 1, tx, ty ]);
  293. }
  294. return this;
  295. };
  296. Matrix.prototype.scale = function (sx, sy) {
  297. if (sx !== 1 || sy !== 1) {
  298. this.cache = null;
  299. this.queue.push([ sx, 0, 0, sy, 0, 0 ]);
  300. }
  301. return this;
  302. };
  303. Matrix.prototype.rotate = function (angle, rx, ry) {
  304. var rad, cos, sin;
  305. if (angle !== 0) {
  306. this.translate(rx, ry);
  307. rad = angle * Math.PI / 180;
  308. cos = Math.cos(rad);
  309. sin = Math.sin(rad);
  310. this.queue.push([ cos, sin, -sin, cos, 0, 0 ]);
  311. this.cache = null;
  312. this.translate(-rx, -ry);
  313. }
  314. return this;
  315. };
  316. Matrix.prototype.skewX = function (angle) {
  317. if (angle !== 0) {
  318. this.cache = null;
  319. this.queue.push([ 1, 0, Math.tan(angle * Math.PI / 180), 1, 0, 0 ]);
  320. }
  321. return this;
  322. };
  323. Matrix.prototype.skewY = function (angle) {
  324. if (angle !== 0) {
  325. this.cache = null;
  326. this.queue.push([ 1, Math.tan(angle * Math.PI / 180), 0, 1, 0, 0 ]);
  327. }
  328. return this;
  329. };
  330. // Flatten queue
  331. //
  332. Matrix.prototype.toArray = function () {
  333. if (this.cache) {
  334. return this.cache;
  335. }
  336. if (!this.queue.length) {
  337. this.cache = [ 1, 0, 0, 1, 0, 0 ];
  338. return this.cache;
  339. }
  340. this.cache = this.queue[0];
  341. if (this.queue.length === 1) {
  342. return this.cache;
  343. }
  344. for (var i = 1; i < this.queue.length; i++) {
  345. this.cache = combine(this.cache, this.queue[i]);
  346. }
  347. return this.cache;
  348. };
  349. // Apply list of matrixes to (x,y) point.
  350. // If `isRelative` set, `translate` component of matrix will be skipped
  351. //
  352. Matrix.prototype.calc = function (x, y, isRelative) {
  353. var m;
  354. // Don't change point on empty transforms queue
  355. if (!this.queue.length) { return [ x, y ]; }
  356. // Calculate final matrix, if not exists
  357. //
  358. // NB. if you deside to apply transforms to point one-by-one,
  359. // they should be taken in reverse order
  360. if (!this.cache) {
  361. this.cache = this.toArray();
  362. }
  363. m = this.cache;
  364. // Apply matrix to point
  365. return [
  366. x * m[0] + y * m[2] + (isRelative ? 0 : m[4]),
  367. x * m[1] + y * m[3] + (isRelative ? 0 : m[5])
  368. ];
  369. };
  370. module.exports = Matrix;
  371. },{}],5:[function(require,module,exports){
  372. 'use strict';
  373. var paramCounts = { a: 7, c: 6, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, z: 0 };
  374. var SPECIAL_SPACES = [
  375. 0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006,
  376. 0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF
  377. ];
  378. function isSpace(ch) {
  379. return (ch === 0x0A) || (ch === 0x0D) || (ch === 0x2028) || (ch === 0x2029) || // Line terminators
  380. // White spaces
  381. (ch === 0x20) || (ch === 0x09) || (ch === 0x0B) || (ch === 0x0C) || (ch === 0xA0) ||
  382. (ch >= 0x1680 && SPECIAL_SPACES.indexOf(ch) >= 0);
  383. }
  384. function isCommand(code) {
  385. /*eslint-disable no-bitwise*/
  386. switch (code | 0x20) {
  387. case 0x6D/* m */:
  388. case 0x7A/* z */:
  389. case 0x6C/* l */:
  390. case 0x68/* h */:
  391. case 0x76/* v */:
  392. case 0x63/* c */:
  393. case 0x73/* s */:
  394. case 0x71/* q */:
  395. case 0x74/* t */:
  396. case 0x61/* a */:
  397. case 0x72/* r */:
  398. return true;
  399. }
  400. return false;
  401. }
  402. function isDigit(code) {
  403. return (code >= 48 && code <= 57); // 0..9
  404. }
  405. function isDigitStart(code) {
  406. return (code >= 48 && code <= 57) || /* 0..9 */
  407. code === 0x2B || /* + */
  408. code === 0x2D || /* - */
  409. code === 0x2E; /* . */
  410. }
  411. function State(path) {
  412. this.index = 0;
  413. this.path = path;
  414. this.max = path.length;
  415. this.result = [];
  416. this.param = 0.0;
  417. this.err = '';
  418. this.segmentStart = 0;
  419. this.data = [];
  420. }
  421. function skipSpaces(state) {
  422. while (state.index < state.max && isSpace(state.path.charCodeAt(state.index))) {
  423. state.index++;
  424. }
  425. }
  426. function scanParam(state) {
  427. var start = state.index,
  428. index = start,
  429. max = state.max,
  430. zeroFirst = false,
  431. hasCeiling = false,
  432. hasDecimal = false,
  433. hasDot = false,
  434. ch;
  435. if (index >= max) {
  436. state.err = 'SvgPath: missed param (at pos ' + index + ')';
  437. return;
  438. }
  439. ch = state.path.charCodeAt(index);
  440. if (ch === 0x2B/* + */ || ch === 0x2D/* - */) {
  441. index++;
  442. ch = (index < max) ? state.path.charCodeAt(index) : 0;
  443. }
  444. // This logic is shamelessly borrowed from Esprima
  445. // https://github.com/ariya/esprimas
  446. //
  447. if (!isDigit(ch) && ch !== 0x2E/* . */) {
  448. state.err = 'SvgPath: param should start with 0..9 or `.` (at pos ' + index + ')';
  449. return;
  450. }
  451. if (ch !== 0x2E/* . */) {
  452. zeroFirst = (ch === 0x30/* 0 */);
  453. index++;
  454. ch = (index < max) ? state.path.charCodeAt(index) : 0;
  455. if (zeroFirst && index < max) {
  456. // decimal number starts with '0' such as '09' is illegal.
  457. if (ch && isDigit(ch)) {
  458. state.err = 'SvgPath: numbers started with `0` such as `09` are ilegal (at pos ' + start + ')';
  459. return;
  460. }
  461. }
  462. while (index < max && isDigit(state.path.charCodeAt(index))) {
  463. index++;
  464. hasCeiling = true;
  465. }
  466. ch = (index < max) ? state.path.charCodeAt(index) : 0;
  467. }
  468. if (ch === 0x2E/* . */) {
  469. hasDot = true;
  470. index++;
  471. while (isDigit(state.path.charCodeAt(index))) {
  472. index++;
  473. hasDecimal = true;
  474. }
  475. ch = (index < max) ? state.path.charCodeAt(index) : 0;
  476. }
  477. if (ch === 0x65/* e */ || ch === 0x45/* E */) {
  478. if (hasDot && !hasCeiling && !hasDecimal) {
  479. state.err = 'SvgPath: invalid float exponent (at pos ' + index + ')';
  480. return;
  481. }
  482. index++;
  483. ch = (index < max) ? state.path.charCodeAt(index) : 0;
  484. if (ch === 0x2B/* + */ || ch === 0x2D/* - */) {
  485. index++;
  486. }
  487. if (index < max && isDigit(state.path.charCodeAt(index))) {
  488. while (index < max && isDigit(state.path.charCodeAt(index))) {
  489. index++;
  490. }
  491. } else {
  492. state.err = 'SvgPath: invalid float exponent (at pos ' + index + ')';
  493. return;
  494. }
  495. }
  496. state.index = index;
  497. state.param = parseFloat(state.path.slice(start, index)) + 0.0;
  498. }
  499. function finalizeSegment(state) {
  500. var cmd, cmdLC;
  501. // Process duplicated commands (without comand name)
  502. // This logic is shamelessly borrowed from Raphael
  503. // https://github.com/DmitryBaranovskiy/raphael/
  504. //
  505. cmd = state.path[state.segmentStart];
  506. cmdLC = cmd.toLowerCase();
  507. var params = state.data;
  508. if (cmdLC === 'm' && params.length > 2) {
  509. state.result.push([ cmd, params[0], params[1] ]);
  510. params = params.slice(2);
  511. cmdLC = 'l';
  512. cmd = (cmd === 'm') ? 'l' : 'L';
  513. }
  514. if (cmdLC === 'r') {
  515. state.result.push([ cmd ].concat(params));
  516. } else {
  517. while (params.length >= paramCounts[cmdLC]) {
  518. state.result.push([ cmd ].concat(params.splice(0, paramCounts[cmdLC])));
  519. if (!paramCounts[cmdLC]) {
  520. break;
  521. }
  522. }
  523. }
  524. }
  525. function scanSegment(state) {
  526. var max = state.max,
  527. cmdCode, comma_found, need_params, i;
  528. state.segmentStart = state.index;
  529. cmdCode = state.path.charCodeAt(state.index);
  530. if (!isCommand(cmdCode)) {
  531. state.err = 'SvgPath: bad command ' + state.path[state.index] + ' (at pos ' + state.index + ')';
  532. return;
  533. }
  534. need_params = paramCounts[state.path[state.index].toLowerCase()];
  535. state.index++;
  536. skipSpaces(state);
  537. state.data = [];
  538. if (!need_params) {
  539. // Z
  540. finalizeSegment(state);
  541. return;
  542. }
  543. comma_found = false;
  544. for (;;) {
  545. for (i = need_params; i > 0; i--) {
  546. scanParam(state);
  547. if (state.err.length) {
  548. return;
  549. }
  550. state.data.push(state.param);
  551. skipSpaces(state);
  552. comma_found = false;
  553. if (state.index < max && state.path.charCodeAt(state.index) === 0x2C/* , */) {
  554. state.index++;
  555. skipSpaces(state);
  556. comma_found = true;
  557. }
  558. }
  559. // after ',' param is mandatory
  560. if (comma_found) {
  561. continue;
  562. }
  563. if (state.index >= state.max) {
  564. break;
  565. }
  566. // Stop on next segment
  567. if (!isDigitStart(state.path.charCodeAt(state.index))) {
  568. break;
  569. }
  570. }
  571. finalizeSegment(state);
  572. }
  573. /* Returns array of segments:
  574. *
  575. * [
  576. * [ command, coord1, coord2, ... ]
  577. * ]
  578. */
  579. module.exports = function pathParse(svgPath) {
  580. var state = new State(svgPath);
  581. var max = state.max;
  582. skipSpaces(state);
  583. while (state.index < max && !state.err.length) {
  584. scanSegment(state);
  585. }
  586. if (state.err.length) {
  587. state.result = [];
  588. } else if (state.result.length) {
  589. if ('mM'.indexOf(state.result[0][0]) < 0) {
  590. state.err = 'SvgPath: string should start with `M` or `m`';
  591. state.result = [];
  592. } else {
  593. state.result[0][0] = 'M';
  594. }
  595. }
  596. return {
  597. err: state.err,
  598. segments: state.result
  599. };
  600. };
  601. },{}],6:[function(require,module,exports){
  602. // SVG Path transformations library
  603. //
  604. // Usage:
  605. //
  606. // SvgPath('...')
  607. // .translate(-150, -100)
  608. // .scale(0.5)
  609. // .translate(-150, -100)
  610. // .toFixed(1)
  611. // .toString()
  612. //
  613. 'use strict';
  614. var pathParse = require('./path_parse');
  615. var transformParse = require('./transform_parse');
  616. var matrix = require('./matrix');
  617. var a2c = require('./a2c');
  618. var ellipse = require('./ellipse');
  619. // Class constructor
  620. //
  621. function SvgPath(path) {
  622. if (!(this instanceof SvgPath)) { return new SvgPath(path); }
  623. var pstate = pathParse(path);
  624. // Array of path segments.
  625. // Each segment is array [command, param1, param2, ...]
  626. this.segments = pstate.segments;
  627. // Error message on parse error.
  628. this.err = pstate.err;
  629. // Transforms stack for lazy evaluation
  630. this.__stack = [];
  631. }
  632. SvgPath.prototype.__matrix = function (m) {
  633. var self = this, i;
  634. // Quick leave for empty matrix
  635. if (!m.queue.length) { return; }
  636. this.iterate(function (s, index, x, y) {
  637. var p, result, name, isRelative;
  638. switch (s[0]) {
  639. // Process 'assymetric' commands separately
  640. case 'v':
  641. p = m.calc(0, s[1], true);
  642. result = (p[0] === 0) ? [ 'v', p[1] ] : [ 'l', p[0], p[1] ];
  643. break;
  644. case 'V':
  645. p = m.calc(x, s[1], false);
  646. result = (p[0] === m.calc(x, y, false)[0]) ? [ 'V', p[1] ] : [ 'L', p[0], p[1] ];
  647. break;
  648. case 'h':
  649. p = m.calc(s[1], 0, true);
  650. result = (p[1] === 0) ? [ 'h', p[0] ] : [ 'l', p[0], p[1] ];
  651. break;
  652. case 'H':
  653. p = m.calc(s[1], y, false);
  654. result = (p[1] === m.calc(x, y, false)[1]) ? [ 'H', p[0] ] : [ 'L', p[0], p[1] ];
  655. break;
  656. case 'a':
  657. case 'A':
  658. // ARC is: ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y]
  659. // Drop segment if arc is empty (end point === start point)
  660. /*if ((s[0] === 'A' && s[6] === x && s[7] === y) ||
  661. (s[0] === 'a' && s[6] === 0 && s[7] === 0)) {
  662. return [];
  663. }*/
  664. // Transform rx, ry and the x-axis-rotation
  665. var ma = m.toArray();
  666. var e = ellipse(s[1], s[2], s[3]).transform(ma);
  667. // flip sweep-flag if matrix is not orientation-preserving
  668. if (ma[0] * ma[3] - ma[1] * ma[2] < 0) {
  669. s[5] = s[5] ? '0' : '1';
  670. }
  671. // Transform end point as usual (without translation for relative notation)
  672. p = m.calc(s[6], s[7], s[0] === 'a');
  673. // Empty arcs can be ignored by renderer, but should not be dropped
  674. // to avoid collisions with `S A S` and so on. Replace with empty line.
  675. if ((s[0] === 'A' && s[6] === x && s[7] === y) ||
  676. (s[0] === 'a' && s[6] === 0 && s[7] === 0)) {
  677. result = [ s[0] === 'a' ? 'l' : 'L', p[0], p[1] ];
  678. break;
  679. }
  680. // if the resulting ellipse is (almost) a segment ...
  681. if (e.isDegenerate()) {
  682. // replace the arc by a line
  683. result = [ s[0] === 'a' ? 'l' : 'L', p[0], p[1] ];
  684. } else {
  685. // if it is a real ellipse
  686. // s[0], s[4] and s[5] are not modified
  687. result = [ s[0], e.rx, e.ry, e.ax, s[4], s[5], p[0], p[1] ];
  688. }
  689. break;
  690. case 'm':
  691. // Edge case. The very first `m` should be processed as absolute, if happens.
  692. // Make sense for coord shift transforms.
  693. isRelative = index > 0;
  694. p = m.calc(s[1], s[2], isRelative);
  695. result = [ 'm', p[0], p[1] ];
  696. break;
  697. default:
  698. name = s[0];
  699. result = [ name ];
  700. isRelative = (name.toLowerCase() === name);
  701. // Apply transformations to the segment
  702. for (i = 1; i < s.length; i += 2) {
  703. p = m.calc(s[i], s[i + 1], isRelative);
  704. result.push(p[0], p[1]);
  705. }
  706. }
  707. self.segments[index] = result;
  708. }, true);
  709. };
  710. // Apply stacked commands
  711. //
  712. SvgPath.prototype.__evaluateStack = function () {
  713. var m, i;
  714. if (!this.__stack.length) { return; }
  715. if (this.__stack.length === 1) {
  716. this.__matrix(this.__stack[0]);
  717. this.__stack = [];
  718. return;
  719. }
  720. m = matrix();
  721. i = this.__stack.length;
  722. while (--i >= 0) {
  723. m.matrix(this.__stack[i].toArray());
  724. }
  725. this.__matrix(m);
  726. this.__stack = [];
  727. };
  728. // Convert processed SVG Path back to string
  729. //
  730. SvgPath.prototype.toString = function () {
  731. var elements = [], skipCmd, cmd;
  732. this.__evaluateStack();
  733. for (var i = 0; i < this.segments.length; i++) {
  734. // remove repeating commands names
  735. cmd = this.segments[i][0];
  736. skipCmd = i > 0 && cmd !== 'm' && cmd !== 'M' && cmd === this.segments[i - 1][0];
  737. elements = elements.concat(skipCmd ? this.segments[i].slice(1) : this.segments[i]);
  738. }
  739. return elements.join(' ')
  740. // Optimizations: remove spaces around commands & before `-`
  741. //
  742. // We could also remove leading zeros for `0.5`-like values,
  743. // but their count is too small to spend time for.
  744. .replace(/ ?([achlmqrstvz]) ?/gi, '$1')
  745. .replace(/ \-/g, '-')
  746. // workaround for FontForge SVG importing bug
  747. .replace(/zm/g, 'z m');
  748. };
  749. // Translate path to (x [, y])
  750. //
  751. SvgPath.prototype.translate = function (x, y) {
  752. this.__stack.push(matrix().translate(x, y || 0));
  753. return this;
  754. };
  755. // Scale path to (sx [, sy])
  756. // sy = sx if not defined
  757. //
  758. SvgPath.prototype.scale = function (sx, sy) {
  759. this.__stack.push(matrix().scale(sx, (!sy && (sy !== 0)) ? sx : sy));
  760. return this;
  761. };
  762. // Rotate path around point (sx [, sy])
  763. // sy = sx if not defined
  764. //
  765. SvgPath.prototype.rotate = function (angle, rx, ry) {
  766. this.__stack.push(matrix().rotate(angle, rx || 0, ry || 0));
  767. return this;
  768. };
  769. // Skew path along the X axis by `degrees` angle
  770. //
  771. SvgPath.prototype.skewX = function (degrees) {
  772. this.__stack.push(matrix().skewX(degrees));
  773. return this;
  774. };
  775. // Skew path along the Y axis by `degrees` angle
  776. //
  777. SvgPath.prototype.skewY = function (degrees) {
  778. this.__stack.push(matrix().skewY(degrees));
  779. return this;
  780. };
  781. // Apply matrix transform (array of 6 elements)
  782. //
  783. SvgPath.prototype.matrix = function (m) {
  784. this.__stack.push(matrix().matrix(m));
  785. return this;
  786. };
  787. // Transform path according to "transform" attr of SVG spec
  788. //
  789. SvgPath.prototype.transform = function (transformString) {
  790. if (!transformString.trim()) {
  791. return this;
  792. }
  793. this.__stack.push(transformParse(transformString));
  794. return this;
  795. };
  796. // Round coords with given decimal precition.
  797. // 0 by default (to integers)
  798. //
  799. SvgPath.prototype.round = function (d) {
  800. var contourStartDeltaX = 0, contourStartDeltaY = 0, deltaX = 0, deltaY = 0, l;
  801. d = d || 0;
  802. this.__evaluateStack();
  803. this.segments.forEach(function (s) {
  804. var isRelative = (s[0].toLowerCase() === s[0]);
  805. switch (s[0]) {
  806. case 'H':
  807. case 'h':
  808. if (isRelative) { s[1] += deltaX; }
  809. deltaX = s[1] - s[1].toFixed(d);
  810. s[1] = +s[1].toFixed(d);
  811. return;
  812. case 'V':
  813. case 'v':
  814. if (isRelative) { s[1] += deltaY; }
  815. deltaY = s[1] - s[1].toFixed(d);
  816. s[1] = +s[1].toFixed(d);
  817. return;
  818. case 'Z':
  819. case 'z':
  820. deltaX = contourStartDeltaX;
  821. deltaY = contourStartDeltaY;
  822. return;
  823. case 'M':
  824. case 'm':
  825. if (isRelative) {
  826. s[1] += deltaX;
  827. s[2] += deltaY;
  828. }
  829. deltaX = s[1] - s[1].toFixed(d);
  830. deltaY = s[2] - s[2].toFixed(d);
  831. contourStartDeltaX = deltaX;
  832. contourStartDeltaY = deltaY;
  833. s[1] = +s[1].toFixed(d);
  834. s[2] = +s[2].toFixed(d);
  835. return;
  836. case 'A':
  837. case 'a':
  838. // [cmd, rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y]
  839. if (isRelative) {
  840. s[6] += deltaX;
  841. s[7] += deltaY;
  842. }
  843. deltaX = s[6] - s[6].toFixed(d);
  844. deltaY = s[7] - s[7].toFixed(d);
  845. s[1] = +s[1].toFixed(d);
  846. s[2] = +s[2].toFixed(d);
  847. s[3] = +s[3].toFixed(d + 2); // better precision for rotation
  848. s[6] = +s[6].toFixed(d);
  849. s[7] = +s[7].toFixed(d);
  850. return;
  851. default:
  852. // a c l q s t
  853. l = s.length;
  854. if (isRelative) {
  855. s[l - 2] += deltaX;
  856. s[l - 1] += deltaY;
  857. }
  858. deltaX = s[l - 2] - s[l - 2].toFixed(d);
  859. deltaY = s[l - 1] - s[l - 1].toFixed(d);
  860. s.forEach(function (val, i) {
  861. if (!i) { return; }
  862. s[i] = +s[i].toFixed(d);
  863. });
  864. return;
  865. }
  866. });
  867. return this;
  868. };
  869. // Apply iterator function to all segments. If function returns result,
  870. // current segment will be replaced to array of returned segments.
  871. // If empty array is returned, current regment will be deleted.
  872. //
  873. SvgPath.prototype.iterate = function (iterator, keepLazyStack) {
  874. var segments = this.segments,
  875. replacements = {},
  876. needReplace = false,
  877. lastX = 0,
  878. lastY = 0,
  879. countourStartX = 0,
  880. countourStartY = 0;
  881. var i, j, newSegments;
  882. if (!keepLazyStack) {
  883. this.__evaluateStack();
  884. }
  885. segments.forEach(function (s, index) {
  886. var res = iterator(s, index, lastX, lastY);
  887. if (Array.isArray(res)) {
  888. replacements[index] = res;
  889. needReplace = true;
  890. }
  891. var isRelative = (s[0] === s[0].toLowerCase());
  892. // calculate absolute X and Y
  893. switch (s[0]) {
  894. case 'm':
  895. case 'M':
  896. lastX = s[1] + (isRelative ? lastX : 0);
  897. lastY = s[2] + (isRelative ? lastY : 0);
  898. countourStartX = lastX;
  899. countourStartY = lastY;
  900. return;
  901. case 'h':
  902. case 'H':
  903. lastX = s[1] + (isRelative ? lastX : 0);
  904. return;
  905. case 'v':
  906. case 'V':
  907. lastY = s[1] + (isRelative ? lastY : 0);
  908. return;
  909. case 'z':
  910. case 'Z':
  911. // That make sence for multiple contours
  912. lastX = countourStartX;
  913. lastY = countourStartY;
  914. return;
  915. default:
  916. lastX = s[s.length - 2] + (isRelative ? lastX : 0);
  917. lastY = s[s.length - 1] + (isRelative ? lastY : 0);
  918. }
  919. });
  920. // Replace segments if iterator return results
  921. if (!needReplace) { return this; }
  922. newSegments = [];
  923. for (i = 0; i < segments.length; i++) {
  924. if (typeof replacements[i] !== 'undefined') {
  925. for (j = 0; j < replacements[i].length; j++) {
  926. newSegments.push(replacements[i][j]);
  927. }
  928. } else {
  929. newSegments.push(segments[i]);
  930. }
  931. }
  932. this.segments = newSegments;
  933. return this;
  934. };
  935. // Converts segments from relative to absolute
  936. //
  937. SvgPath.prototype.abs = function () {
  938. this.iterate(function (s, index, x, y) {
  939. var name = s[0],
  940. nameUC = name.toUpperCase(),
  941. i;
  942. // Skip absolute commands
  943. if (name === nameUC) { return; }
  944. s[0] = nameUC;
  945. switch (name) {
  946. case 'v':
  947. // v has shifted coords parity
  948. s[1] += y;
  949. return;
  950. case 'a':
  951. // ARC is: ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y]
  952. // touch x, y only
  953. s[6] += x;
  954. s[7] += y;
  955. return;
  956. default:
  957. for (i = 1; i < s.length; i++) {
  958. s[i] += i % 2 ? x : y; // odd values are X, even - Y
  959. }
  960. }
  961. }, true);
  962. return this;
  963. };
  964. // Converts segments from absolute to relative
  965. //
  966. SvgPath.prototype.rel = function () {
  967. this.iterate(function (s, index, x, y) {
  968. var name = s[0],
  969. nameLC = name.toLowerCase(),
  970. i;
  971. // Skip relative commands
  972. if (name === nameLC) { return; }
  973. // Don't touch the first M to avoid potential confusions.
  974. if (index === 0 && name === 'M') { return; }
  975. s[0] = nameLC;
  976. switch (name) {
  977. case 'V':
  978. // V has shifted coords parity
  979. s[1] -= y;
  980. return;
  981. case 'A':
  982. // ARC is: ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y]
  983. // touch x, y only
  984. s[6] -= x;
  985. s[7] -= y;
  986. return;
  987. default:
  988. for (i = 1; i < s.length; i++) {
  989. s[i] -= i % 2 ? x : y; // odd values are X, even - Y
  990. }
  991. }
  992. }, true);
  993. return this;
  994. };
  995. // Converts arcs to cubic bézier curves
  996. //
  997. SvgPath.prototype.unarc = function () {
  998. this.iterate(function (s, index, x, y) {
  999. var new_segments, nextX, nextY, result = [], name = s[0];
  1000. // Skip anything except arcs
  1001. if (name !== 'A' && name !== 'a') { return null; }
  1002. if (name === 'a') {
  1003. // convert relative arc coordinates to absolute
  1004. nextX = x + s[6];
  1005. nextY = y + s[7];
  1006. } else {
  1007. nextX = s[6];
  1008. nextY = s[7];
  1009. }
  1010. new_segments = a2c(x, y, nextX, nextY, s[4], s[5], s[1], s[2], s[3]);
  1011. // Degenerated arcs can be ignored by renderer, but should not be dropped
  1012. // to avoid collisions with `S A S` and so on. Replace with empty line.
  1013. if (new_segments.length === 0) {
  1014. return [ [ s[0] === 'a' ? 'l' : 'L', s[6], s[7] ] ];
  1015. }
  1016. new_segments.forEach(function (s) {
  1017. result.push([ 'C', s[2], s[3], s[4], s[5], s[6], s[7] ]);
  1018. });
  1019. return result;
  1020. });
  1021. return this;
  1022. };
  1023. // Converts smooth curves (with missed control point) to generic curves
  1024. //
  1025. SvgPath.prototype.unshort = function () {
  1026. var segments = this.segments;
  1027. var prevControlX, prevControlY, prevSegment;
  1028. var curControlX, curControlY;
  1029. // TODO: add lazy evaluation flag when relative commands supported
  1030. this.iterate(function (s, idx, x, y) {
  1031. var name = s[0], nameUC = name.toUpperCase(), isRelative;
  1032. // First command MUST be M|m, it's safe to skip.
  1033. // Protect from access to [-1] for sure.
  1034. if (!idx) { return; }
  1035. if (nameUC === 'T') { // quadratic curve
  1036. isRelative = (name === 't');
  1037. prevSegment = segments[idx - 1];
  1038. if (prevSegment[0] === 'Q') {
  1039. prevControlX = prevSegment[1] - x;
  1040. prevControlY = prevSegment[2] - y;
  1041. } else if (prevSegment[0] === 'q') {
  1042. prevControlX = prevSegment[1] - prevSegment[3];
  1043. prevControlY = prevSegment[2] - prevSegment[4];
  1044. } else {
  1045. prevControlX = 0;
  1046. prevControlY = 0;
  1047. }
  1048. curControlX = -prevControlX;
  1049. curControlY = -prevControlY;
  1050. if (!isRelative) {
  1051. curControlX += x;
  1052. curControlY += y;
  1053. }
  1054. segments[idx] = [
  1055. isRelative ? 'q' : 'Q',
  1056. curControlX, curControlY,
  1057. s[1], s[2]
  1058. ];
  1059. } else if (nameUC === 'S') { // cubic curve
  1060. isRelative = (name === 's');
  1061. prevSegment = segments[idx - 1];
  1062. if (prevSegment[0] === 'C') {
  1063. prevControlX = prevSegment[3] - x;
  1064. prevControlY = prevSegment[4] - y;
  1065. } else if (prevSegment[0] === 'c') {
  1066. prevControlX = prevSegment[3] - prevSegment[5];
  1067. prevControlY = prevSegment[4] - prevSegment[6];
  1068. } else {
  1069. prevControlX = 0;
  1070. prevControlY = 0;
  1071. }
  1072. curControlX = -prevControlX;
  1073. curControlY = -prevControlY;
  1074. if (!isRelative) {
  1075. curControlX += x;
  1076. curControlY += y;
  1077. }
  1078. segments[idx] = [
  1079. isRelative ? 'c' : 'C',
  1080. curControlX, curControlY,
  1081. s[1], s[2], s[3], s[4]
  1082. ];
  1083. }
  1084. });
  1085. return this;
  1086. };
  1087. module.exports = SvgPath;
  1088. },{"./a2c":2,"./ellipse":3,"./matrix":4,"./path_parse":5,"./transform_parse":7}],7:[function(require,module,exports){
  1089. 'use strict';
  1090. var Matrix = require('./matrix');
  1091. var operations = {
  1092. matrix: true,
  1093. scale: true,
  1094. rotate: true,
  1095. translate: true,
  1096. skewX: true,
  1097. skewY: true
  1098. };
  1099. var CMD_SPLIT_RE = /\s*(matrix|translate|scale|rotate|skewX|skewY)\s*\(\s*(.+?)\s*\)[\s,]*/;
  1100. var PARAMS_SPLIT_RE = /[\s,]+/;
  1101. module.exports = function transformParse(transformString) {
  1102. var matrix = new Matrix();
  1103. var cmd, params;
  1104. // Split value into ['', 'translate', '10 50', '', 'scale', '2', '', 'rotate', '-45', '']
  1105. transformString.split(CMD_SPLIT_RE).forEach(function (item) {
  1106. // Skip empty elements
  1107. if (!item.length) { return; }
  1108. // remember operation
  1109. if (typeof operations[item] !== 'undefined') {
  1110. cmd = item;
  1111. return;
  1112. }
  1113. // extract params & att operation to matrix
  1114. params = item.split(PARAMS_SPLIT_RE).map(function (i) {
  1115. return +i || 0;
  1116. });
  1117. // If params count is not correct - ignore command
  1118. switch (cmd) {
  1119. case 'matrix':
  1120. if (params.length === 6) {
  1121. matrix.matrix(params);
  1122. }
  1123. return;
  1124. case 'scale':
  1125. if (params.length === 1) {
  1126. matrix.scale(params[0], params[0]);
  1127. } else if (params.length === 2) {
  1128. matrix.scale(params[0], params[1]);
  1129. }
  1130. return;
  1131. case 'rotate':
  1132. if (params.length === 1) {
  1133. matrix.rotate(params[0], 0, 0);
  1134. } else if (params.length === 3) {
  1135. matrix.rotate(params[0], params[1], params[2]);
  1136. }
  1137. return;
  1138. case 'translate':
  1139. if (params.length === 1) {
  1140. matrix.translate(params[0], 0);
  1141. } else if (params.length === 2) {
  1142. matrix.translate(params[0], params[1]);
  1143. }
  1144. return;
  1145. case 'skewX':
  1146. if (params.length === 1) {
  1147. matrix.skewX(params[0]);
  1148. }
  1149. return;
  1150. case 'skewY':
  1151. if (params.length === 1) {
  1152. matrix.skewY(params[0]);
  1153. }
  1154. return;
  1155. }
  1156. });
  1157. return matrix;
  1158. };
  1159. },{"./matrix":4}],8:[function(require,module,exports){
  1160. // parse
  1161. // =====
  1162. // states
  1163. // ------
  1164. var PLAIN = 0;
  1165. var STRINGS = 1;
  1166. var ESCAPING = 2;
  1167. var IDENTIFIER = 3;
  1168. var SEPARATING = 4;
  1169. // patterns
  1170. // --------
  1171. var identifierPattern = /[a-z0-9_-]/i;
  1172. var spacePattern = /[\s\t]/;
  1173. // ---
  1174. var parse = function(str) {
  1175. // vars
  1176. // ----
  1177. var starting = true;
  1178. var state = PLAIN;
  1179. var buffer = '';
  1180. var i = 0;
  1181. var quote;
  1182. var c;
  1183. // result
  1184. // ------
  1185. var names = [];
  1186. // parse
  1187. // -----
  1188. while (true) {
  1189. c = str[i];
  1190. if (state === PLAIN) {
  1191. if (!c && starting) {
  1192. break;
  1193. } else if (!c && !starting) {
  1194. throw new Error('Parse error');
  1195. } else if (c === '"' || c === "'") {
  1196. quote = c;
  1197. state = STRINGS;
  1198. starting = false;
  1199. } else if (spacePattern.test(c)) {
  1200. } else if (identifierPattern.test(c)) {
  1201. state = IDENTIFIER;
  1202. starting = false;
  1203. i--;
  1204. } else {
  1205. throw new Error('Parse error');
  1206. }
  1207. } else if (state === STRINGS) {
  1208. if (!c) {
  1209. throw new Error('Parse Error');
  1210. } else if (c === "\\") {
  1211. state = ESCAPING;
  1212. } else if (c === quote) {
  1213. names.push(buffer);
  1214. buffer = '';
  1215. state = SEPARATING;
  1216. } else {
  1217. buffer += c;
  1218. }
  1219. } else if (state === ESCAPING) {
  1220. if (c === quote || c === "\\") {
  1221. buffer += c;
  1222. state = STRINGS;
  1223. } else {
  1224. throw new Error('Parse error');
  1225. }
  1226. } else if (state === IDENTIFIER) {
  1227. if (!c) {
  1228. names.push(buffer);
  1229. break;
  1230. } else if (identifierPattern.test(c)) {
  1231. buffer += c;
  1232. } else if (c === ',') {
  1233. names.push(buffer);
  1234. buffer = '';
  1235. state = PLAIN;
  1236. } else if (spacePattern.test(c)) {
  1237. names.push(buffer);
  1238. buffer = '';
  1239. state = SEPARATING;
  1240. } else {
  1241. throw new Error('Parse error');
  1242. }
  1243. } else if (state === SEPARATING) {
  1244. if (!c) {
  1245. break;
  1246. } else if (c === ',') {
  1247. state = PLAIN;
  1248. } else if (spacePattern.test(c)) {
  1249. } else {
  1250. throw new Error('Parse error');
  1251. }
  1252. }
  1253. i++;
  1254. }
  1255. // result
  1256. // ------
  1257. return names;
  1258. };
  1259. // stringify
  1260. // =========
  1261. // pattern
  1262. // -------
  1263. var stringsPattern = /[^a-z0-9_-]/i;
  1264. // ---
  1265. var stringify = function(names, options) {
  1266. // quote
  1267. // -----
  1268. var quote = options && options.quote || '"';
  1269. if (quote !== '"' && quote !== "'") {
  1270. throw new Error('Quote must be `\'` or `"`');
  1271. }
  1272. var quotePattern = new RegExp(quote, 'g');
  1273. // stringify
  1274. // ---------
  1275. var safeNames = [];
  1276. for (var i = 0; i < names.length; ++i) {
  1277. var name = names[i];
  1278. if (stringsPattern.test(name)) {
  1279. name = name
  1280. .replace(/\\/g, "\\\\")
  1281. .replace(quotePattern, "\\" + quote);
  1282. name = quote + name + quote;
  1283. }
  1284. safeNames.push(name);
  1285. }
  1286. // result
  1287. // ------
  1288. return safeNames.join(', ');
  1289. };
  1290. // export
  1291. // ======
  1292. module.exports = {
  1293. parse: parse,
  1294. stringify: stringify,
  1295. };
  1296. },{}],9:[function(require,module,exports){
  1297. /**
  1298. * A class to parse color values
  1299. * @author Stoyan Stefanov <sstoo@gmail.com>
  1300. * @link http://www.phpied.com/rgb-color-parser-in-javascript/
  1301. * @license Use it if you like it
  1302. */
  1303. (function (global) {
  1304. function RGBColor(color_string)
  1305. {
  1306. this.ok = false;
  1307. // strip any leading #
  1308. if (color_string.charAt(0) == '#') { // remove # if any
  1309. color_string = color_string.substr(1,6);
  1310. }
  1311. color_string = color_string.replace(/ /g,'');
  1312. color_string = color_string.toLowerCase();
  1313. // before getting into regexps, try simple matches
  1314. // and overwrite the input
  1315. var simple_colors = {
  1316. aliceblue: 'f0f8ff',
  1317. antiquewhite: 'faebd7',
  1318. aqua: '00ffff',
  1319. aquamarine: '7fffd4',
  1320. azure: 'f0ffff',
  1321. beige: 'f5f5dc',
  1322. bisque: 'ffe4c4',
  1323. black: '000000',
  1324. blanchedalmond: 'ffebcd',
  1325. blue: '0000ff',
  1326. blueviolet: '8a2be2',
  1327. brown: 'a52a2a',
  1328. burlywood: 'deb887',
  1329. cadetblue: '5f9ea0',
  1330. chartreuse: '7fff00',
  1331. chocolate: 'd2691e',
  1332. coral: 'ff7f50',
  1333. cornflowerblue: '6495ed',
  1334. cornsilk: 'fff8dc',
  1335. crimson: 'dc143c',
  1336. cyan: '00ffff',
  1337. darkblue: '00008b',
  1338. darkcyan: '008b8b',
  1339. darkgoldenrod: 'b8860b',
  1340. darkgray: 'a9a9a9',
  1341. darkgreen: '006400',
  1342. darkkhaki: 'bdb76b',
  1343. darkmagenta: '8b008b',
  1344. darkolivegreen: '556b2f',
  1345. darkorange: 'ff8c00',
  1346. darkorchid: '9932cc',
  1347. darkred: '8b0000',
  1348. darksalmon: 'e9967a',
  1349. darkseagreen: '8fbc8f',
  1350. darkslateblue: '483d8b',
  1351. darkslategray: '2f4f4f',
  1352. darkturquoise: '00ced1',
  1353. darkviolet: '9400d3',
  1354. deeppink: 'ff1493',
  1355. deepskyblue: '00bfff',
  1356. dimgray: '696969',
  1357. dodgerblue: '1e90ff',
  1358. feldspar: 'd19275',
  1359. firebrick: 'b22222',
  1360. floralwhite: 'fffaf0',
  1361. forestgreen: '228b22',
  1362. fuchsia: 'ff00ff',
  1363. gainsboro: 'dcdcdc',
  1364. ghostwhite: 'f8f8ff',
  1365. gold: 'ffd700',
  1366. goldenrod: 'daa520',
  1367. gray: '808080',
  1368. green: '008000',
  1369. greenyellow: 'adff2f',
  1370. honeydew: 'f0fff0',
  1371. hotpink: 'ff69b4',
  1372. indianred : 'cd5c5c',
  1373. indigo : '4b0082',
  1374. ivory: 'fffff0',
  1375. khaki: 'f0e68c',
  1376. lavender: 'e6e6fa',
  1377. lavenderblush: 'fff0f5',
  1378. lawngreen: '7cfc00',
  1379. lemonchiffon: 'fffacd',
  1380. lightblue: 'add8e6',
  1381. lightcoral: 'f08080',
  1382. lightcyan: 'e0ffff',
  1383. lightgoldenrodyellow: 'fafad2',
  1384. lightgrey: 'd3d3d3',
  1385. lightgreen: '90ee90',
  1386. lightpink: 'ffb6c1',
  1387. lightsalmon: 'ffa07a',
  1388. lightseagreen: '20b2aa',
  1389. lightskyblue: '87cefa',
  1390. lightslateblue: '8470ff',
  1391. lightslategray: '778899',
  1392. lightsteelblue: 'b0c4de',
  1393. lightyellow: 'ffffe0',
  1394. lime: '00ff00',
  1395. limegreen: '32cd32',
  1396. linen: 'faf0e6',
  1397. magenta: 'ff00ff',
  1398. maroon: '800000',
  1399. mediumaquamarine: '66cdaa',
  1400. mediumblue: '0000cd',
  1401. mediumorchid: 'ba55d3',
  1402. mediumpurple: '9370d8',
  1403. mediumseagreen: '3cb371',
  1404. mediumslateblue: '7b68ee',
  1405. mediumspringgreen: '00fa9a',
  1406. mediumturquoise: '48d1cc',
  1407. mediumvioletred: 'c71585',
  1408. midnightblue: '191970',
  1409. mintcream: 'f5fffa',
  1410. mistyrose: 'ffe4e1',
  1411. moccasin: 'ffe4b5',
  1412. navajowhite: 'ffdead',
  1413. navy: '000080',
  1414. oldlace: 'fdf5e6',
  1415. olive: '808000',
  1416. olivedrab: '6b8e23',
  1417. orange: 'ffa500',
  1418. orangered: 'ff4500',
  1419. orchid: 'da70d6',
  1420. palegoldenrod: 'eee8aa',
  1421. palegreen: '98fb98',
  1422. paleturquoise: 'afeeee',
  1423. palevioletred: 'd87093',
  1424. papayawhip: 'ffefd5',
  1425. peachpuff: 'ffdab9',
  1426. peru: 'cd853f',
  1427. pink: 'ffc0cb',
  1428. plum: 'dda0dd',
  1429. powderblue: 'b0e0e6',
  1430. purple: '800080',
  1431. red: 'ff0000',
  1432. rosybrown: 'bc8f8f',
  1433. royalblue: '4169e1',
  1434. saddlebrown: '8b4513',
  1435. salmon: 'fa8072',
  1436. sandybrown: 'f4a460',
  1437. seagreen: '2e8b57',
  1438. seashell: 'fff5ee',
  1439. sienna: 'a0522d',
  1440. silver: 'c0c0c0',
  1441. skyblue: '87ceeb',
  1442. slateblue: '6a5acd',
  1443. slategray: '708090',
  1444. snow: 'fffafa',
  1445. springgreen: '00ff7f',
  1446. steelblue: '4682b4',
  1447. tan: 'd2b48c',
  1448. teal: '008080',
  1449. thistle: 'd8bfd8',
  1450. tomato: 'ff6347',
  1451. turquoise: '40e0d0',
  1452. violet: 'ee82ee',
  1453. violetred: 'd02090',
  1454. wheat: 'f5deb3',
  1455. white: 'ffffff',
  1456. whitesmoke: 'f5f5f5',
  1457. yellow: 'ffff00',
  1458. yellowgreen: '9acd32'
  1459. };
  1460. for (var key in simple_colors) {
  1461. if (color_string == key) {
  1462. color_string = simple_colors[key];
  1463. }
  1464. }
  1465. // emd of simple type-in colors
  1466. // array of color definition objects
  1467. var color_defs = [
  1468. {
  1469. re: /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/,
  1470. example: ['rgb(123, 234, 45)', 'rgb(255,234,245)'],
  1471. process: function (bits){
  1472. return [
  1473. parseInt(bits[1]),
  1474. parseInt(bits[2]),
  1475. parseInt(bits[3])
  1476. ];
  1477. }
  1478. },
  1479. {
  1480. re: /^(\w{2})(\w{2})(\w{2})$/,
  1481. example: ['#00ff00', '336699'],
  1482. process: function (bits){
  1483. return [
  1484. parseInt(bits[1], 16),
  1485. parseInt(bits[2], 16),
  1486. parseInt(bits[3], 16)
  1487. ];
  1488. }
  1489. },
  1490. {
  1491. re: /^(\w{1})(\w{1})(\w{1})$/,
  1492. example: ['#fb0', 'f0f'],
  1493. process: function (bits){
  1494. return [
  1495. parseInt(bits[1] + bits[1], 16),
  1496. parseInt(bits[2] + bits[2], 16),
  1497. parseInt(bits[3] + bits[3], 16)
  1498. ];
  1499. }
  1500. }
  1501. ];
  1502. // search through the definitions to find a match
  1503. for (var i = 0; i < color_defs.length; i++) {
  1504. var re = color_defs[i].re;
  1505. var processor = color_defs[i].process;
  1506. var bits = re.exec(color_string);
  1507. if (bits) {
  1508. var channels = processor(bits);
  1509. this.r = channels[0];
  1510. this.g = channels[1];
  1511. this.b = channels[2];
  1512. this.ok = true;
  1513. }
  1514. }
  1515. // validate/cleanup values
  1516. this.r = (this.r < 0 || isNaN(this.r)) ? 0 : ((this.r > 255) ? 255 : this.r);
  1517. this.g = (this.g < 0 || isNaN(this.g)) ? 0 : ((this.g > 255) ? 255 : this.g);
  1518. this.b = (this.b < 0 || isNaN(this.b)) ? 0 : ((this.b > 255) ? 255 : this.b);
  1519. // some getters
  1520. this.toRGB = function () {
  1521. return 'rgb(' + this.r + ', ' + this.g + ', ' + this.b + ')';
  1522. }
  1523. this.toHex = function () {
  1524. var r = this.r.toString(16);
  1525. var g = this.g.toString(16);
  1526. var b = this.b.toString(16);
  1527. if (r.length == 1) r = '0' + r;
  1528. if (g.length == 1) g = '0' + g;
  1529. if (b.length == 1) b = '0' + b;
  1530. return '#' + r + g + b;
  1531. }
  1532. // help
  1533. this.getHelpXML = function () {
  1534. var examples = new Array();
  1535. // add regexps
  1536. for (var i = 0; i < color_defs.length; i++) {
  1537. var example = color_defs[i].example;
  1538. for (var j = 0; j < example.length; j++) {
  1539. examples[examples.length] = example[j];
  1540. }
  1541. }
  1542. // add type-in colors
  1543. for (var sc in simple_colors) {
  1544. examples[examples.length] = sc;
  1545. }
  1546. var xml = document.createElement('ul');
  1547. xml.setAttribute('id', 'rgbcolor-examples');
  1548. for (var i = 0; i < examples.length; i++) {
  1549. try {
  1550. var list_item = document.createElement('li');
  1551. var list_color = new RGBColor(examples[i]);
  1552. var example_div = document.createElement('div');
  1553. example_div.style.cssText =
  1554. 'margin: 3px; '
  1555. + 'border: 1px solid black; '
  1556. + 'background:' + list_color.toHex() + '; '
  1557. + 'color:' + list_color.toHex()
  1558. ;
  1559. example_div.appendChild(document.createTextNode('test'));
  1560. var list_item_value = document.createTextNode(
  1561. ' ' + examples[i] + ' -> ' + list_color.toRGB() + ' -> ' + list_color.toHex()
  1562. );
  1563. list_item.appendChild(example_div);
  1564. list_item.appendChild(list_item_value);
  1565. xml.appendChild(list_item);
  1566. } catch(e){}
  1567. }
  1568. return xml;
  1569. }
  1570. }
  1571. if (typeof define === "function" && define.amd) {
  1572. define(function () {
  1573. return RGBColor;
  1574. });
  1575. } else if (typeof module !== "undefined" && module.exports) {
  1576. module.exports = RGBColor;
  1577. } else {
  1578. global.RGBColor = RGBColor;
  1579. }
  1580. return RGBColor;
  1581. })(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this);
  1582. },{}],10:[function(require,module,exports){
  1583. /*
  1584. The MIT License (MIT)
  1585. Copyright (c) 2015-2017 yWorks GmbH
  1586. Permission is hereby granted, free of charge, to any person obtaining a copy
  1587. of this software and associated documentation files (the "Software"), to deal
  1588. in the Software without restriction, including without limitation the rights
  1589. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  1590. copies of the Software, and to permit persons to whom the Software is
  1591. furnished to do so, subject to the following conditions:
  1592. The above copyright notice and this permission notice shall be included in all
  1593. copies or substantial portions of the Software.
  1594. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  1595. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  1596. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  1597. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  1598. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  1599. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  1600. SOFTWARE.
  1601. */
  1602. /**
  1603. * Renders an svg element to a jsPDF document.
  1604. * For accurate results a DOM document is required (mainly used for text size measurement and image format conversion)
  1605. * @param element {HTMLElement} The svg element, which will be cloned, so the original stays unchanged.
  1606. * @param pdf {jsPDF} The jsPDF object.
  1607. * @param options {object} An object that may contain render options. Currently supported are:
  1608. * scale: The global factor by which everything is scaled.
  1609. * xOffset, yOffset: Offsets that are added to every coordinate AFTER scaling (They are not
  1610. * influenced by the scale attribute).
  1611. */
  1612. (function (global) {
  1613. var RGBColor;
  1614. var SvgPath;
  1615. var FontFamily;
  1616. var _pdf; // jsPDF pdf-document
  1617. var cToQ = 2 / 3; // ratio to convert quadratic bezier curves to cubic ones
  1618. var iriReference = /url\(["']?#([^"']+)["']?\)/;
  1619. // groups: 1: mime-type (+ charset), 2: mime-type (w/o charset), 3: charset, 4: base64?, 5: body
  1620. var dataUrlRegex = /^\s*data:(([^/,;]+\/[^/,;]+)(?:;([^,;=]+=[^,;=]+))?)?(?:;(base64))?,(.*\s*)$/i;
  1621. var svgNamespaceURI = "http://www.w3.org/2000/svg";
  1622. // pathSegList is marked deprecated in chrome, so parse the d attribute manually if necessary
  1623. var getPathSegList = function (node) {
  1624. var d = node.getAttribute("d");
  1625. // Replace arcs before path segment list is handled
  1626. if (SvgPath) {
  1627. d = SvgPath(d).unshort().unarc().abs().toString();
  1628. node.setAttribute('d', d);
  1629. }
  1630. var pathSegList = node.pathSegList;
  1631. if (pathSegList) {
  1632. return pathSegList;
  1633. }
  1634. pathSegList = [];
  1635. var regex = /([a-df-zA-DF-Z])([^a-df-zA-DF-Z]*)/g,
  1636. match;
  1637. while (match = regex.exec(d)) {
  1638. var coords = parseFloats(match[2]);
  1639. var type = match[1];
  1640. var length = "zZ".indexOf(type) >= 0 ? 0 :
  1641. "hHvV".indexOf(type) >= 0 ? 1 :
  1642. "mMlLtT".indexOf(type) >= 0 ? 2 :
  1643. "sSqQ".indexOf(type) >= 0 ? 4 :
  1644. "aA".indexOf(type) >= 0 ? 7 :
  1645. "cC".indexOf(type) >= 0 ? 6 : -1;
  1646. var i = 0;
  1647. do {
  1648. var pathSeg = {pathSegTypeAsLetter: type};
  1649. switch (type) {
  1650. case "h":
  1651. case "H":
  1652. pathSeg.x = coords[i];
  1653. break;
  1654. case "v":
  1655. case "V":
  1656. pathSeg.y = coords[i];
  1657. break;
  1658. case "c":
  1659. case "C":
  1660. pathSeg.x1 = coords[i + length - 6];
  1661. pathSeg.y1 = coords[i + length - 5];
  1662. case "s":
  1663. case "S":
  1664. pathSeg.x2 = coords[i + length - 4];
  1665. pathSeg.y2 = coords[i + length - 3];
  1666. case "t":
  1667. case "T":
  1668. case "l":
  1669. case "L":
  1670. case "m":
  1671. case "M":
  1672. pathSeg.x = coords[i + length - 2];
  1673. pathSeg.y = coords[i + length - 1];
  1674. break;
  1675. case "q":
  1676. case "Q":
  1677. pathSeg.x1 = coords[i];
  1678. pathSeg.y1 = coords[i + 1];
  1679. pathSeg.x = coords[i + 2];
  1680. pathSeg.y = coords[i + 3];
  1681. break;
  1682. case "a":
  1683. case "A":
  1684. throw new Error("Cannot convert Arcs without SvgPath package");
  1685. }
  1686. pathSegList.push(pathSeg);
  1687. // "If a moveto is followed by multiple pairs of coordinates, the subsequent pairs are treated as implicit
  1688. // lineto commands"
  1689. if (type === "m") {
  1690. type = "l";
  1691. } else if (type === "M") {
  1692. type = "L";
  1693. }
  1694. i += length;
  1695. } while(i < coords.length);
  1696. }
  1697. pathSegList.getItem = function (i) {
  1698. return this[i]
  1699. };
  1700. pathSegList.numberOfItems = pathSegList.length;
  1701. return pathSegList;
  1702. };
  1703. // returns an attribute of a node, either from the node directly or from css
  1704. var getAttribute = function (node, propertyNode, propertyCss) {
  1705. propertyCss = propertyCss || propertyNode;
  1706. return node.getAttribute(propertyNode) || node.style && node.style[propertyCss];
  1707. };
  1708. /**
  1709. * @param {Element} node
  1710. * @param {string} tagsString
  1711. * @return {boolean}
  1712. */
  1713. var nodeIs = function (node, tagsString) {
  1714. return tagsString.split(",").indexOf(node.tagName.toLowerCase()) >= 0;
  1715. };
  1716. var forEachChild = function (node, fn) {
  1717. // copy list of children, as the original might be modified
  1718. var children = [];
  1719. for (var i = 0; i < node.childNodes.length; i++) {
  1720. var childNode = node.childNodes[i];
  1721. if (childNode.nodeName.charAt(0) !== "#")
  1722. children.push(childNode);
  1723. }
  1724. for (i = 0; i < children.length; i++) {
  1725. fn(i, children[i]);
  1726. }
  1727. };
  1728. var getAngle = function (from, to) {
  1729. return Math.atan2(to[1] - from[1], to[0] - from[0]);
  1730. };
  1731. function normalize(v) {
  1732. var length = Math.sqrt(v[0] * v[0] + v[1] * v[1]);
  1733. return [v[0] / length, v[1] / length];
  1734. }
  1735. function getDirectionVector(from, to) {
  1736. var v = [to[0] - from[0], to[1] - from[1]];
  1737. return normalize(v);
  1738. }
  1739. function addVectors(v1, v2) {
  1740. return [v1[0] + v2[0], v1[1] + v2[1]];
  1741. }
  1742. // mirrors p1 at p2
  1743. var mirrorPoint = function (p1, p2) {
  1744. var dx = p2[0] - p1[0];
  1745. var dy = p2[1] - p1[1];
  1746. return [p1[0] + 2 * dx, p1[1] + 2 * dy];
  1747. };
  1748. // transforms a cubic bezier control point to a quadratic one: returns from + (2/3) * (to - from)
  1749. var toCubic = function (from, to) {
  1750. return [cToQ * (to[0] - from[0]) + from[0], cToQ * (to[1] - from[1]) + from[1]];
  1751. };
  1752. // extracts a control point from a previous path segment (for t,T,s,S segments)
  1753. var getControlPointFromPrevious = function (i, from, list, prevX, prevY) {
  1754. var prev = list.getItem(i - 1);
  1755. var p2;
  1756. if (i > 0 && (prev.pathSegTypeAsLetter === "C" || prev.pathSegTypeAsLetter === "S")) {
  1757. p2 = mirrorPoint([prev.x2, prev.y2], from);
  1758. } else if (i > 0 && (prev.pathSegTypeAsLetter === "c" || prev.pathSegTypeAsLetter === "s")) {
  1759. p2 = mirrorPoint([prev.x2 + prevX, prev.y2 + prevY], from);
  1760. } else {
  1761. p2 = [from[0], from[1]];
  1762. }
  1763. return p2;
  1764. };
  1765. /**
  1766. * @param {Element} rootSvg
  1767. * @constructor
  1768. * @property {Object.<String,Element>} renderedElements
  1769. * @property {Element} rootSvg
  1770. */
  1771. function ReferencesHandler(rootSvg) {
  1772. this.renderedElements = {};
  1773. this.rootSvg = rootSvg;
  1774. }
  1775. /**
  1776. * @param {string} id
  1777. * @return {*}
  1778. */
  1779. ReferencesHandler.prototype.getRendered = function (id) {
  1780. if (this.renderedElements.hasOwnProperty(id)) {
  1781. return this.renderedElements[id];
  1782. }
  1783. var node = this.rootSvg.getElementById(id);
  1784. if (nodeIs(node, "lineargradient")) {
  1785. putGradient(node, "axial", [
  1786. node.getAttribute("x1") || 0,
  1787. node.getAttribute("y1") || 0,
  1788. node.getAttribute("x2") || 1,
  1789. node.getAttribute("y2") || 0
  1790. ]);
  1791. } else if (nodeIs(node, "radialgradient")) {
  1792. putGradient(node, "radial", [
  1793. node.getAttribute("fx") || node.getAttribute("cx") || 0.5,
  1794. node.getAttribute("fy") || node.getAttribute("cy") || 0.5,
  1795. 0,
  1796. node.getAttribute("cx") || 0.5,
  1797. node.getAttribute("cy") || 0.5,
  1798. node.getAttribute("r") || 0.5
  1799. ]);
  1800. } else if (nodeIs(node, "pattern")) {
  1801. pattern(node, this, AttributeState.default())
  1802. } else if (nodeIs(node, "marker")) {
  1803. // the transformations directly at the node are written to the pdf form object transformation matrix
  1804. var tfMatrix = computeNodeTransform(node);
  1805. var bBox = getUntransformedBBox(node);
  1806. _pdf.beginFormObject(bBox[0], bBox[1], bBox[2], bBox[3], tfMatrix);
  1807. renderChildren(node, _pdf.unitMatrix, this, false, false, AttributeState.default());
  1808. _pdf.endFormObject(node.getAttribute("id"));
  1809. } else if (!nodeIs(node, "clippath")) {
  1810. // all other nodes will be rendered as PDF form object
  1811. renderNode(node, _pdf.unitMatrix, this, true, false, AttributeState.default());
  1812. }
  1813. this.renderedElements[id] = node;
  1814. return node;
  1815. };
  1816. var AttributeState = function () {
  1817. this.xmlSpace = null;
  1818. this.color = null;
  1819. this.fill = null;
  1820. this.fillOpacity = 1.0;
  1821. // this.fillRule = null;
  1822. this.fontFamily = null;
  1823. this.fontSize = 16;
  1824. this.fontStyle = null;
  1825. // this.fontVariant = null;
  1826. this.fontWeight = null;
  1827. this.opacity = 1.0;
  1828. this.stroke = null;
  1829. this.strokeDasharray = null;
  1830. this.strokeDashoffset = null;
  1831. this.strokeLinecap = null;
  1832. this.strokeLinejoin = null;
  1833. this.strokeMiterlimit = 4.0;
  1834. this.strokeOpacity = 1.0;
  1835. this.strokeWidth = 1.0;
  1836. // this.textAlign = null;
  1837. this.textAnchor = null;
  1838. this.visibility = null;
  1839. };
  1840. AttributeState.default = function () {
  1841. var attributeState = new AttributeState();
  1842. attributeState.xmlSpace = "default";
  1843. attributeState.fill = new RGBColor("rgb(0, 0, 0)");
  1844. attributeState.fillOpacity = 1.0;
  1845. // attributeState.fillRule = "nonzero";
  1846. attributeState.fontFamily = "times";
  1847. attributeState.fontSize = 16;
  1848. attributeState.fontStyle = "normal";
  1849. // attributeState.fontVariant = "normal";
  1850. attributeState.fontWeight = "normal";
  1851. attributeState.opacity = 1.0;
  1852. attributeState.stroke = null;
  1853. attributeState.strokeDasharray = null;
  1854. attributeState.strokeDashoffset = null;
  1855. attributeState.strokeLinecap = "butt";
  1856. attributeState.strokeLinejoin = "miter";
  1857. attributeState.strokeMiterlimit = 4.0;
  1858. attributeState.strokeOpacity = 1.0;
  1859. attributeState.strokeWidth = 1.0;
  1860. // attributeState.textAlign = "start";
  1861. attributeState.textAnchor = "start";
  1862. attributeState.visibility = "visible";
  1863. return attributeState;
  1864. };
  1865. AttributeState.prototype.clone = function () {
  1866. var clone = new AttributeState();
  1867. clone.xmlSpace = this.xmlSpace;
  1868. clone.fill = this.fill;
  1869. clone.fillOpacity = this.fillOpacity;
  1870. // clone.fillRule = this.fillRule;
  1871. clone.fontFamily = this.fontFamily;
  1872. clone.fontSize = this.fontSize;
  1873. clone.fontStyle = this.fontStyle;
  1874. // clone.fontVariant = this.fontVariant;
  1875. clone.fontWeight = this.fontWeight;
  1876. clone.opacity = this.opacity;
  1877. clone.stroke = this.stroke;
  1878. clone.strokeDasharray = this.strokeDasharray;
  1879. clone.strokeDashoffset = this.strokeDashoffset;
  1880. clone.strokeLinecap = this.strokeLinecap;
  1881. clone.strokeLinejoin = this.strokeLinejoin;
  1882. clone.strokeMiterlimit = this.strokeMiterlimit;
  1883. clone.strokeOpacity = this.strokeOpacity;
  1884. clone.strokeWidth = this.strokeWidth;
  1885. // clone.textAlign = this.textAlign;
  1886. clone.textAnchor = this.textAnchor;
  1887. clone.visibility = this.visibility;
  1888. return clone;
  1889. };
  1890. /**
  1891. * @constructor
  1892. * @property {Marker[]} markers
  1893. */
  1894. function MarkerList() {
  1895. this.markers = [];
  1896. }
  1897. /**
  1898. * @param {Marker} marker
  1899. */
  1900. MarkerList.prototype.addMarker = function addMarker(marker) {
  1901. this.markers.push(marker);
  1902. };
  1903. MarkerList.prototype.draw = function (tfMatrix, refsHandler, attributeState) {
  1904. for (var i = 0; i < this.markers.length; i++) {
  1905. var marker = this.markers[i];
  1906. var tf;
  1907. var angle = marker.angle, anchor = marker.anchor;
  1908. var cos = Math.cos(angle);
  1909. var sin = Math.sin(angle);
  1910. // position at and rotate around anchor
  1911. tf = new _pdf.Matrix(cos, sin, -sin, cos, anchor[0], anchor[1]);
  1912. // scale with stroke-width
  1913. tf = _pdf.matrixMult(new _pdf.Matrix(attributeState.strokeWidth, 0, 0, attributeState.strokeWidth, 0, 0), tf);
  1914. tf = _pdf.matrixMult(tf, tfMatrix);
  1915. // as the marker is already scaled by the current line width we must not apply the line width twice!
  1916. _pdf.saveGraphicsState();
  1917. _pdf.setLineWidth(1.0);
  1918. refsHandler.getRendered(marker.id);
  1919. _pdf.doFormObject(marker.id, tf);
  1920. _pdf.restoreGraphicsState();
  1921. }
  1922. };
  1923. /**
  1924. * @param {string} id
  1925. * @param {[number,number]} anchor
  1926. * @param {number} angle
  1927. */
  1928. function Marker(id, anchor, angle) {
  1929. this.id = id;
  1930. this.anchor = anchor;
  1931. this.angle = angle;
  1932. }
  1933. function removeNewlines(str) {
  1934. return str.replace(/[\n\r]/g, "");
  1935. }
  1936. function replaceTabsBySpace(str) {
  1937. return str.replace(/[\t]/g, " ");
  1938. }
  1939. function consolidateSpaces(str) {
  1940. return str.replace(/ +/g, " ");
  1941. }
  1942. function trimLeft(str) {
  1943. return str.replace(/^\s+/,"");
  1944. }
  1945. function trimRight(str) {
  1946. return str.replace(/\s+$/,"");
  1947. }
  1948. function computeViewBoxTransform(node, viewBox, eX, eY, eWidth, eHeight) {
  1949. var vbX = viewBox[0];
  1950. var vbY = viewBox[1];
  1951. var vbWidth = viewBox[2];
  1952. var vbHeight = viewBox[3];
  1953. var scaleX = eWidth / vbWidth;
  1954. var scaleY = eHeight / vbHeight;
  1955. var align, meetOrSlice;
  1956. var preserveAspectRatio = node.getAttribute("preserveAspectRatio");
  1957. if (preserveAspectRatio) {
  1958. var alignAndMeetOrSlice = preserveAspectRatio.split(" ");
  1959. if (alignAndMeetOrSlice[0] === "defer") {
  1960. alignAndMeetOrSlice = alignAndMeetOrSlice.slice(1);
  1961. }
  1962. align = alignAndMeetOrSlice[0];
  1963. meetOrSlice = alignAndMeetOrSlice[1] || "meet";
  1964. } else {
  1965. align = "xMidYMid";
  1966. meetOrSlice = "meet"
  1967. }
  1968. if (align !== "none") {
  1969. if (meetOrSlice === "meet") {
  1970. // uniform scaling with min scale
  1971. scaleX = scaleY = Math.min(scaleX, scaleY);
  1972. } else if (meetOrSlice === "slice") {
  1973. // uniform scaling with max scale
  1974. scaleX = scaleY = Math.max(scaleX, scaleY);
  1975. }
  1976. }
  1977. var translateX = eX - (vbX * scaleX);
  1978. var translateY = eY - (vbY * scaleY);
  1979. if (align.indexOf("xMid") >= 0) {
  1980. translateX += (eWidth - vbWidth * scaleX) / 2;
  1981. } else if (align.indexOf("xMax") >= 0) {
  1982. translateX += eWidth - vbWidth * scaleX;
  1983. }
  1984. if (align.indexOf("yMid") >= 0) {
  1985. translateY += (eHeight - vbHeight * scaleY) / 2;
  1986. } else if (align.indexOf("yMax") >= 0) {
  1987. translateY += (eHeight - vbHeight * scaleY);
  1988. }
  1989. var translate = new _pdf.Matrix(1, 0, 0, 1, translateX, translateY);
  1990. var scale = new _pdf.Matrix(scaleX, 0, 0, scaleY, 0, 0);
  1991. return _pdf.matrixMult(scale, translate);
  1992. }
  1993. // computes the transform directly applied at the node (such as viewbox scaling and the "transform" atrribute)
  1994. // x,y,cx,cy,r,... are omitted
  1995. var computeNodeTransform = function (node) {
  1996. var viewBox, x, y;
  1997. var nodeTransform = _pdf.unitMatrix;
  1998. if (nodeIs(node, "svg,g")) {
  1999. x = parseFloat(node.getAttribute("x")) || 0;
  2000. y = parseFloat(node.getAttribute("y")) || 0;
  2001. viewBox = node.getAttribute("viewBox");
  2002. if (viewBox) {
  2003. var box = parseFloats(viewBox);
  2004. var width = parseFloat(node.getAttribute("width")) || box[2];
  2005. var height = parseFloat(node.getAttribute("height")) || box[3];
  2006. nodeTransform = computeViewBoxTransform(node, box, x, y, width, height)
  2007. } else {
  2008. nodeTransform = new _pdf.Matrix(1, 0, 0, 1, x, y);
  2009. }
  2010. } else if (nodeIs(node, "marker")) {
  2011. x = parseFloat(node.getAttribute("refX")) || 0;
  2012. y = parseFloat(node.getAttribute("refY")) || 0;
  2013. viewBox = node.getAttribute("viewBox");
  2014. if (viewBox) {
  2015. var bounds = parseFloats(viewBox);
  2016. bounds[0] = bounds[1] = 0; // for some reason vbX anc vbY seem to be ignored for markers
  2017. nodeTransform = computeViewBoxTransform(node,
  2018. bounds,
  2019. 0,
  2020. 0,
  2021. parseFloat(node.getAttribute("markerWidth")) || 3,
  2022. parseFloat(node.getAttribute("markerHeight")) || 3
  2023. );
  2024. nodeTransform = _pdf.matrixMult(new _pdf.Matrix(1, 0, 0, 1, -x, -y), nodeTransform);
  2025. } else {
  2026. nodeTransform = new _pdf.Matrix(1, 0, 0, 1, -x, -y);
  2027. }
  2028. }
  2029. var transformString = node.getAttribute("transform");
  2030. if (!transformString)
  2031. return nodeTransform;
  2032. else
  2033. return _pdf.matrixMult(nodeTransform, parseTransform(transformString));
  2034. };
  2035. // parses the "points" string used by polygons and returns an array of points
  2036. var parsePointsString = function (string) {
  2037. var floats = parseFloats(string);
  2038. var result = [];
  2039. for (var i = 0; i < floats.length - 1; i += 2) {
  2040. var x = floats[i];
  2041. var y = floats[i + 1];
  2042. result.push([x, y]);
  2043. }
  2044. return result;
  2045. };
  2046. // parses the "transform" string
  2047. var parseTransform = function (transformString) {
  2048. if (!transformString)
  2049. return _pdf.unitMatrix;
  2050. var mRegex = /^\s*matrix\(([^\)]+)\)\s*/,
  2051. tRegex = /^\s*translate\(([^\)]+)\)\s*/,
  2052. rRegex = /^\s*rotate\(([^\)]+)\)\s*/,
  2053. sRegex = /^\s*scale\(([^\)]+)\)\s*/,
  2054. sXRegex = /^\s*skewX\(([^\)]+)\)\s*/,
  2055. sYRegex = /^\s*skewY\(([^\)]+)\)\s*/;
  2056. var resultMatrix = _pdf.unitMatrix, m;
  2057. while (transformString.length > 0) {
  2058. var match = mRegex.exec(transformString);
  2059. if (match) {
  2060. m = parseFloats(match[1]);
  2061. resultMatrix = _pdf.matrixMult(new _pdf.Matrix(m[0], m[1], m[2], m[3], m[4], m[5]), resultMatrix);
  2062. transformString = transformString.substr(match[0].length);
  2063. }
  2064. match = rRegex.exec(transformString);
  2065. if (match) {
  2066. m = parseFloats(match[1]);
  2067. var a = Math.PI * m[0] / 180;
  2068. resultMatrix = _pdf.matrixMult(new _pdf.Matrix(Math.cos(a), Math.sin(a), -Math.sin(a), Math.cos(a), 0, 0), resultMatrix);
  2069. if (m[1] && m[2]) {
  2070. var t1 = new _pdf.Matrix(1, 0, 0, 1, m[1], m[2]);
  2071. var t2 = new _pdf.Matrix(1, 0, 0, 1, -m[1], -m[2]);
  2072. resultMatrix = _pdf.matrixMult(t2, _pdf.matrixMult(resultMatrix, t1));
  2073. }
  2074. transformString = transformString.substr(match[0].length);
  2075. }
  2076. match = tRegex.exec(transformString);
  2077. if (match) {
  2078. m = parseFloats(match[1]);
  2079. resultMatrix = _pdf.matrixMult(new _pdf.Matrix(1, 0, 0, 1, m[0], m[1] || 0), resultMatrix);
  2080. transformString = transformString.substr(match[0].length);
  2081. }
  2082. match = sRegex.exec(transformString);
  2083. if (match) {
  2084. m = parseFloats(match[1]);
  2085. if (!m[1])
  2086. m[1] = m[0];
  2087. resultMatrix = _pdf.matrixMult(new _pdf.Matrix(m[0], 0, 0, m[1], 0, 0), resultMatrix);
  2088. transformString = transformString.substr(match[0].length);
  2089. }
  2090. match = sXRegex.exec(transformString);
  2091. if (match) {
  2092. m = parseFloat(match[1]);
  2093. resultMatrix = _pdf.matrixMult(new _pdf.Matrix(1, 0, Math.tan(m), 1, 0, 0), resultMatrix);
  2094. transformString = transformString.substr(match[0].length);
  2095. }
  2096. match = sYRegex.exec(transformString);
  2097. if (match) {
  2098. m = parseFloat(match[1]);
  2099. resultMatrix = _pdf.matrixMult(new _pdf.Matrix(1, Math.tan(m), 0, 1, 0, 0), resultMatrix);
  2100. transformString = transformString.substr(match[0].length);
  2101. }
  2102. }
  2103. return resultMatrix;
  2104. };
  2105. // parses a comma, sign and/or whitespace separated string of floats and returns the single floats in an array
  2106. var parseFloats = function (str) {
  2107. var floats = [], match,
  2108. regex = /[+-]?(?:(?:\d+\.?\d*)|(?:\d*\.?\d+))(?:[eE][+-]?\d+)?/g;
  2109. while(match = regex.exec(str)) {
  2110. floats.push(parseFloat(match[0]));
  2111. }
  2112. return floats;
  2113. };
  2114. // extends RGBColor by rgba colors as RGBColor is not capable of it
  2115. var parseColor = function (colorString) {
  2116. if (colorString === "transparent") {
  2117. var transparent = new RGBColor("rgb(0,0,0)");
  2118. transparent.a = 0;
  2119. return transparent
  2120. }
  2121. var match = /\s*rgba\(((?:[^,\)]*,){3}[^,\)]*)\)\s*/.exec(colorString);
  2122. if (match) {
  2123. var floats = parseFloats(match[1]);
  2124. var color = new RGBColor("rgb(" + floats.slice(0,3).join(",") + ")");
  2125. color.a = floats[3];
  2126. return color;
  2127. } else {
  2128. return new RGBColor(colorString);
  2129. }
  2130. };
  2131. // multiplies a vector with a matrix: vec' = vec * matrix
  2132. var multVecMatrix = function (vec, matrix) {
  2133. var x = vec[0];
  2134. var y = vec[1];
  2135. return [
  2136. matrix.a * x + matrix.c * y + matrix.e,
  2137. matrix.b * x + matrix.d * y + matrix.f
  2138. ];
  2139. };
  2140. // returns the untransformed bounding box [x, y, width, height] of an svg element (quite expensive for path and polygon objects, as
  2141. // the whole points/d-string has to be processed)
  2142. var getUntransformedBBox = function (node) {
  2143. if (getAttribute(node, "display") === "none") {
  2144. return [0, 0, 0, 0];
  2145. }
  2146. var i, minX, minY, maxX, maxY, viewBox, vb, boundingBox;
  2147. var pf = parseFloat;
  2148. if (nodeIs(node, "polygon,polyline")) {
  2149. var points = parsePointsString(node.getAttribute("points"));
  2150. minX = Number.POSITIVE_INFINITY;
  2151. minY = Number.POSITIVE_INFINITY;
  2152. maxX = Number.NEGATIVE_INFINITY;
  2153. maxY = Number.NEGATIVE_INFINITY;
  2154. for (i = 0; i < points.length; i++) {
  2155. var point = points[i];
  2156. minX = Math.min(minX, point[0]);
  2157. maxX = Math.max(maxX, point[0]);
  2158. minY = Math.min(minY, point[1]);
  2159. maxY = Math.max(maxY, point[1]);
  2160. }
  2161. boundingBox = [
  2162. minX,
  2163. minY,
  2164. maxX - minX,
  2165. maxY - minY
  2166. ];
  2167. } else if (nodeIs(node, "path")) {
  2168. var list = getPathSegList(node);
  2169. minX = Number.POSITIVE_INFINITY;
  2170. minY = Number.POSITIVE_INFINITY;
  2171. maxX = Number.NEGATIVE_INFINITY;
  2172. maxY = Number.NEGATIVE_INFINITY;
  2173. var x = 0, y = 0;
  2174. var prevX, prevY, newX, newY;
  2175. var p2, p3, to;
  2176. for (i = 0; i < list.numberOfItems; i++) {
  2177. var seg = list.getItem(i);
  2178. var cmd = seg.pathSegTypeAsLetter;
  2179. switch (cmd) {
  2180. case "H":
  2181. newX = seg.x;
  2182. newY = y;
  2183. break;
  2184. case "h":
  2185. newX = seg.x + x;
  2186. newY = y;
  2187. break;
  2188. case "V":
  2189. newX = x;
  2190. newY = seg.y;
  2191. break;
  2192. case "v":
  2193. newX = x;
  2194. newY = seg.y + y;
  2195. break;
  2196. case "C":
  2197. p2 = [seg.x1, seg.y1];
  2198. p3 = [seg.x2, seg.y2];
  2199. to = [seg.x, seg.y];
  2200. break;
  2201. case "c":
  2202. p2 = [seg.x1 + x, seg.y1 + y];
  2203. p3 = [seg.x2 + x, seg.y2 + y];
  2204. to = [seg.x + x, seg.y + y];
  2205. break;
  2206. case "S":
  2207. p2 = getControlPointFromPrevious(i, [x, y], list, prevX, prevY);
  2208. p3 = [seg.x2, seg.y2];
  2209. to = [seg.x, seg.y];
  2210. break;
  2211. case "s":
  2212. p2 = getControlPointFromPrevious(i, [x, y], list, prevX, prevY);
  2213. p3 = [seg.x2 + x, seg.y2 + y];
  2214. to = [seg.x + x, seg.y + y];
  2215. break;
  2216. case "Q":
  2217. pf = [seg.x1, seg.y1];
  2218. p2 = toCubic([x, y], pf);
  2219. p3 = toCubic([seg.x, seg.y], pf);
  2220. to = [seg.x, seg.y];
  2221. break;
  2222. case "q":
  2223. pf = [seg.x1 + x, seg.y1 + y];
  2224. p2 = toCubic([x, y], pf);
  2225. p3 = toCubic([x + seg.x, y + seg.y], pf);
  2226. to = [seg.x + x, seg.y + y];
  2227. break;
  2228. case "T":
  2229. p2 = getControlPointFromPrevious(i, [x, y], list, prevX, prevY);
  2230. p2 = toCubic([x, y], pf);
  2231. p3 = toCubic([seg.x, seg.y], pf);
  2232. to = [seg.x, seg.y];
  2233. break;
  2234. case "t":
  2235. pf = getControlPointFromPrevious(i, [x, y], list, prevX, prevY);
  2236. p2 = toCubic([x, y], pf);
  2237. p3 = toCubic([x + seg.x, y + seg.y], pf);
  2238. to = [seg.x + x, seg.y + y];
  2239. break;
  2240. // TODO: A,a
  2241. }
  2242. if ("sScCqQtT".indexOf(cmd) >= 0) {
  2243. prevX = x;
  2244. prevY = y;
  2245. }
  2246. if ("MLCSQT".indexOf(cmd) >= 0) {
  2247. x = seg.x;
  2248. y = seg.y;
  2249. } else if ("mlcsqt".indexOf(cmd) >= 0) {
  2250. x = seg.x + x;
  2251. y = seg.y + y;
  2252. } else if ("zZ".indexOf(cmd) < 0) {
  2253. x = newX;
  2254. y = newY;
  2255. }
  2256. if ("CSQTcsqt".indexOf(cmd) >= 0) {
  2257. minX = Math.min(minX, x, p2[0], p3[0], to[0]);
  2258. maxX = Math.max(maxX, x, p2[0], p3[0], to[0]);
  2259. minY = Math.min(minY, y, p2[1], p3[1], to[1]);
  2260. maxY = Math.max(maxY, y, p2[1], p3[1], to[1]);
  2261. } else {
  2262. minX = Math.min(minX, x);
  2263. maxX = Math.max(maxX, x);
  2264. minY = Math.min(minY, y);
  2265. maxY = Math.max(maxY, y);
  2266. }
  2267. }
  2268. boundingBox = [
  2269. minX,
  2270. minY,
  2271. maxX - minX,
  2272. maxY - minY
  2273. ];
  2274. } else if (nodeIs(node, "svg")) {
  2275. viewBox = node.getAttribute("viewBox");
  2276. if (viewBox) {
  2277. vb = parseFloats(viewBox);
  2278. }
  2279. return [
  2280. pf(node.getAttribute("x")) || (vb && vb[0]) || 0,
  2281. pf(node.getAttribute("y")) || (vb && vb[1]) || 0,
  2282. pf(node.getAttribute("width")) || (vb && vb[2]) || 0,
  2283. pf(node.getAttribute("height")) || (vb && vb[3]) || 0
  2284. ];
  2285. } else if (nodeIs(node, "g,clippath")) {
  2286. boundingBox = [0, 0, 0, 0];
  2287. forEachChild(node, function (i, node) {
  2288. var nodeBox = getUntransformedBBox(node);
  2289. boundingBox = [
  2290. Math.min(boundingBox[0], nodeBox[0]),
  2291. Math.min(boundingBox[1], nodeBox[1]),
  2292. Math.max(boundingBox[0] + boundingBox[2], nodeBox[0] + nodeBox[2]) - Math.min(boundingBox[0], nodeBox[0]),
  2293. Math.max(boundingBox[1] + boundingBox[3], nodeBox[1] + nodeBox[3]) - Math.min(boundingBox[1], nodeBox[1])
  2294. ];
  2295. });
  2296. } else if (nodeIs(node, "marker")) {
  2297. viewBox = node.getAttribute("viewBox");
  2298. if (viewBox) {
  2299. vb = parseFloats(viewBox);
  2300. }
  2301. return [
  2302. (vb && vb[0]) || 0,
  2303. (vb && vb[1]) || 0,
  2304. (vb && vb[2]) || pf(node.getAttribute("marker-width")) || 0,
  2305. (vb && vb[3]) || pf(node.getAttribute("marker-height")) || 0
  2306. ];
  2307. } else if (nodeIs(node, "pattern")) {
  2308. return [
  2309. pf(node.getAttribute("x")) || 0,
  2310. pf(node.getAttribute("y")) || 0,
  2311. pf(node.getAttribute("width")) || 0,
  2312. pf(node.getAttribute("height")) || 0
  2313. ]
  2314. } else {
  2315. // TODO: check if there are other possible coordinate attributes
  2316. var x1 = pf(node.getAttribute("x1")) || pf(node.getAttribute("x")) || pf((node.getAttribute("cx")) - pf(node.getAttribute("r"))) || 0;
  2317. var x2 = pf(node.getAttribute("x2")) || (x1 + pf(node.getAttribute("width"))) || (pf(node.getAttribute("cx")) + pf(node.getAttribute("r"))) || 0;
  2318. var y1 = pf(node.getAttribute("y1")) || pf(node.getAttribute("y")) || (pf(node.getAttribute("cy")) - pf(node.getAttribute("r"))) || 0;
  2319. var y2 = pf(node.getAttribute("y2")) || (y1 + pf(node.getAttribute("height"))) || (pf(node.getAttribute("cy")) + pf(node.getAttribute("r"))) || 0;
  2320. boundingBox = [
  2321. Math.min(x1, x2),
  2322. Math.min(y1, y2),
  2323. Math.max(x1, x2) - Math.min(x1, x2),
  2324. Math.max(y1, y2) - Math.min(y1, y2)
  2325. ];
  2326. }
  2327. if (!nodeIs(node, "marker,svg,g")) {
  2328. // add line-width
  2329. var lineWidth = getAttribute(node, "stroke-width") || 1;
  2330. var miterLimit = getAttribute(node, "stroke-miterlimit");
  2331. // miterLength / lineWidth = 1 / sin(phi / 2)
  2332. miterLimit && (lineWidth *= 0.5 / (Math.sin(Math.PI / 12)));
  2333. return [
  2334. boundingBox[0] - lineWidth,
  2335. boundingBox[1] - lineWidth,
  2336. boundingBox[2] + 2 * lineWidth,
  2337. boundingBox[3] + 2 * lineWidth
  2338. ];
  2339. }
  2340. return boundingBox;
  2341. };
  2342. // transforms a bounding box and returns a new rect that contains it
  2343. var transformBBox = function (box, matrix) {
  2344. var bl = multVecMatrix([box[0], box[1]], matrix);
  2345. var br = multVecMatrix([box[0] + box[2], box[1]], matrix);
  2346. var tl = multVecMatrix([box[0], box[1] + box[3]], matrix);
  2347. var tr = multVecMatrix([box[0] + box[2], box[1] + box[3]], matrix);
  2348. var bottom = Math.min(bl[1], br[1], tl[1], tr[1]);
  2349. var left = Math.min(bl[0], br[0], tl[0], tr[0]);
  2350. var top = Math.max(bl[1], br[1], tl[1], tr[1]);
  2351. var right = Math.max(bl[0], br[0], tl[0], tr[0]);
  2352. return [
  2353. left,
  2354. bottom,
  2355. right - left,
  2356. top - bottom
  2357. ]
  2358. };
  2359. // draws a polygon
  2360. var polygon = function (node, refsHandler, attributeState, closed) {
  2361. if (!node.hasAttribute("points") || node.getAttribute("points") === "") {
  2362. return;
  2363. }
  2364. var points = parsePointsString(node.getAttribute("points"));
  2365. var lines = [{op: "m", c: points[0]}];
  2366. var i, angle;
  2367. for (i = 1; i < points.length; i++) {
  2368. lines.push({op: "l", c: points[i]});
  2369. }
  2370. if (closed) {
  2371. lines.push({op: "h"});
  2372. }
  2373. _pdf.path(lines);
  2374. var markerEnd = node.getAttribute("marker-end"),
  2375. markerStart = node.getAttribute("marker-start"),
  2376. markerMid = node.getAttribute("marker-mid");
  2377. if (markerStart || markerMid || markerEnd) {
  2378. var length = lines.length;
  2379. var markers = new MarkerList();
  2380. if (markerStart) {
  2381. markerStart = iriReference.exec(markerStart)[1];
  2382. angle = addVectors(getDirectionVector(lines[0].c, lines[1].c), getDirectionVector(lines[length - 2].c, lines[0].c));
  2383. markers.addMarker(new Marker(markerStart, lines[0].c, Math.atan2(angle[1], angle[0])));
  2384. }
  2385. if (markerMid) {
  2386. markerMid = iriReference.exec(markerMid)[1];
  2387. var prevAngle = getDirectionVector(lines[0].c, lines[1].c), curAngle;
  2388. for (i = 1; i < lines.length - 2; i++) {
  2389. curAngle = getDirectionVector(lines[i].c, lines[i + 1].c);
  2390. angle = addVectors(prevAngle, curAngle);
  2391. markers.addMarker(new Marker(markerMid, lines[i].c, Math.atan2(angle[1], angle[0])));
  2392. prevAngle = curAngle;
  2393. }
  2394. curAngle = getDirectionVector(lines[length - 2].c, lines[0].c);
  2395. angle = addVectors(prevAngle, curAngle);
  2396. markers.addMarker(new Marker(markerMid, lines[length - 2].c, Math.atan2(angle[1], angle[0])));
  2397. }
  2398. if (markerEnd) {
  2399. markerEnd = iriReference.exec(markerEnd)[1];
  2400. angle = addVectors(getDirectionVector(lines[0].c, lines[1].c), getDirectionVector(lines[length - 2].c, lines[0].c));
  2401. markers.addMarker(new Marker(markerEnd, lines[0].c, Math.atan2(angle[1], angle[0])));
  2402. }
  2403. markers.draw(_pdf.unitMatrix, refsHandler, attributeState);
  2404. }
  2405. };
  2406. // draws an image
  2407. var image = function (node) {
  2408. var width = parseFloat(node.getAttribute("width")),
  2409. height = parseFloat(node.getAttribute("height")),
  2410. x = parseFloat(node.getAttribute("x") || 0),
  2411. y = parseFloat(node.getAttribute("y") || 0);
  2412. var imageUrl = node.getAttribute("xlink:href") || node.getAttribute("href");
  2413. var dataUrl = imageUrl.match(dataUrlRegex);
  2414. if (dataUrl && dataUrl[2] === "image/svg+xml") {
  2415. var svgText = dataUrl[5];
  2416. if (dataUrl[4] === "base64") {
  2417. svgText = atob(svgText);
  2418. } else {
  2419. svgText = decodeURIComponent(svgText);
  2420. }
  2421. var parser = new DOMParser();
  2422. var svgElement = parser.parseFromString(svgText, "image/svg+xml").firstElementChild;
  2423. // unless preserveAspectRatio starts with "defer", the preserveAspectRatio attribute of the svg is ignored
  2424. var preserveAspectRatio = node.getAttribute("preserveAspectRatio");
  2425. if (!preserveAspectRatio
  2426. || preserveAspectRatio.indexOf("defer") < 0
  2427. || !svgElement.getAttribute("preserveAspectRatio")) {
  2428. svgElement.setAttribute("preserveAspectRatio", preserveAspectRatio);
  2429. }
  2430. svgElement.setAttribute("x", String(x));
  2431. svgElement.setAttribute("y", String(y));
  2432. svgElement.setAttribute("width", String(width));
  2433. svgElement.setAttribute("height", String(height));
  2434. renderNode(svgElement, _pdf.unitMatrix, {}, false, false, AttributeState.default());
  2435. return;
  2436. }
  2437. try {
  2438. _pdf.addImage(
  2439. imageUrl,
  2440. "", // will be ignored anyways if imageUrl is a data url
  2441. x,
  2442. y,
  2443. width,
  2444. height
  2445. );
  2446. } catch (e) {
  2447. (typeof console === "object"
  2448. && console.warn
  2449. && console.warn('svg2pdfjs: Images with external resource link are not supported! ("' + imageUrl + '")'));
  2450. }
  2451. };
  2452. // draws a path
  2453. var path = function (node, tfMatrix, refsHandler, withinClipPath, attributeState) {
  2454. var list = getPathSegList(node);
  2455. var markerEnd = node.getAttribute("marker-end"),
  2456. markerStart = node.getAttribute("marker-start"),
  2457. markerMid = node.getAttribute("marker-mid");
  2458. markerEnd && (markerEnd = iriReference.exec(markerEnd)[1]);
  2459. markerStart && (markerStart = iriReference.exec(markerStart)[1]);
  2460. markerMid && (markerMid = iriReference.exec(markerMid)[1]);
  2461. var getLinesFromPath = function () {
  2462. var x = 0, y = 0;
  2463. var x0 = x, y0 = y;
  2464. var prevX, prevY, newX, newY;
  2465. var to, p, p2, p3;
  2466. var lines = [];
  2467. var markers = new MarkerList();
  2468. var op;
  2469. var prevAngle = [0, 0], curAngle;
  2470. for (var i = 0; i < list.numberOfItems; i++) {
  2471. var seg = list.getItem(i);
  2472. var cmd = seg.pathSegTypeAsLetter;
  2473. switch (cmd) {
  2474. case "M":
  2475. x0 = x;
  2476. y0 = y;
  2477. to = [seg.x, seg.y];
  2478. op = "m";
  2479. break;
  2480. case "m":
  2481. x0 = x;
  2482. y0 = y;
  2483. to = [seg.x + x, seg.y + y];
  2484. op = "m";
  2485. break;
  2486. case "L":
  2487. to = [seg.x, seg.y];
  2488. op = "l";
  2489. break;
  2490. case "l":
  2491. to = [seg.x + x, seg.y + y];
  2492. op = "l";
  2493. break;
  2494. case "H":
  2495. to = [seg.x, y];
  2496. op = "l";
  2497. newX = seg.x;
  2498. newY = y;
  2499. break;
  2500. case "h":
  2501. to = [seg.x + x, y];
  2502. op = "l";
  2503. newX = seg.x + x;
  2504. newY = y;
  2505. break;
  2506. case "V":
  2507. to = [x, seg.y];
  2508. op = "l";
  2509. newX = x;
  2510. newY = seg.y;
  2511. break;
  2512. case "v":
  2513. to = [x, seg.y + y];
  2514. op = "l";
  2515. newX = x;
  2516. newY = seg.y + y;
  2517. break;
  2518. case "C":
  2519. p2 = [seg.x1, seg.y1];
  2520. p3 = [seg.x2, seg.y2];
  2521. to = [seg.x, seg.y];
  2522. break;
  2523. case "c":
  2524. p2 = [seg.x1 + x, seg.y1 + y];
  2525. p3 = [seg.x2 + x, seg.y2 + y];
  2526. to = [seg.x + x, seg.y + y];
  2527. break;
  2528. case "S":
  2529. p2 = getControlPointFromPrevious(i, [x, y], list, prevX, prevY);
  2530. p3 = [seg.x2, seg.y2];
  2531. to = [seg.x, seg.y];
  2532. break;
  2533. case "s":
  2534. p2 = getControlPointFromPrevious(i, [x, y], list, prevX, prevY);
  2535. p3 = [seg.x2 + x, seg.y2 + y];
  2536. to = [seg.x + x, seg.y + y];
  2537. break;
  2538. case "Q":
  2539. p = [seg.x1, seg.y1];
  2540. p2 = toCubic([x, y], p);
  2541. p3 = toCubic([seg.x, seg.y], p);
  2542. to = [seg.x, seg.y];
  2543. break;
  2544. case "q":
  2545. p = [seg.x1 + x, seg.y1 + y];
  2546. p2 = toCubic([x, y], p);
  2547. p3 = toCubic([x + seg.x, y + seg.y], p);
  2548. to = [seg.x + x, seg.y + y];
  2549. break;
  2550. case "T":
  2551. p = getControlPointFromPrevious(i, [x, y], list, prevX, prevY);
  2552. p2 = toCubic([x, y], p);
  2553. p3 = toCubic([seg.x, seg.y], p);
  2554. to = [seg.x, seg.y];
  2555. break;
  2556. case "t":
  2557. p = getControlPointFromPrevious(i, [x, y], list, prevX, prevY);
  2558. p2 = toCubic([x, y], p);
  2559. p3 = toCubic([x + seg.x, y + seg.y], p);
  2560. to = [seg.x + x, seg.y + y];
  2561. break;
  2562. // TODO: A,a
  2563. case "Z":
  2564. case "z":
  2565. x = x0;
  2566. y = y0;
  2567. lines.push({op: "h"});
  2568. break;
  2569. }
  2570. var hasStartMarker = markerStart
  2571. && (i === 1
  2572. || ("mM".indexOf(cmd) < 0 && "mM".indexOf(list.getItem(i - 1).pathSegTypeAsLetter) >= 0));
  2573. var hasEndMarker = markerEnd
  2574. && (i === list.numberOfItems - 1
  2575. || ("mM".indexOf(cmd) < 0 && "mM".indexOf(list.getItem(i + 1).pathSegTypeAsLetter) >= 0));
  2576. var hasMidMarker = markerMid
  2577. && i > 0
  2578. && !(i === 1 && "mM".indexOf(list.getItem(i - 1).pathSegTypeAsLetter) >= 0);
  2579. if ("sScCqQtT".indexOf(cmd) >= 0) {
  2580. hasStartMarker && markers.addMarker(new Marker(markerStart, [x, y], getAngle([x, y], p2)));
  2581. hasEndMarker && markers.addMarker(new Marker(markerEnd, to, getAngle(p3, to)));
  2582. if (hasMidMarker) {
  2583. curAngle = getDirectionVector([x, y], p2);
  2584. curAngle = "mM".indexOf(list.getItem(i - 1).pathSegTypeAsLetter) >= 0 ?
  2585. curAngle : normalize(addVectors(prevAngle, curAngle));
  2586. markers.addMarker(new Marker(markerMid, [x, y], Math.atan2(curAngle[1], curAngle[0])));
  2587. }
  2588. prevAngle = getDirectionVector(p3, to);
  2589. prevX = x;
  2590. prevY = y;
  2591. if (withinClipPath) {
  2592. p2 = multVecMatrix(p2, tfMatrix);
  2593. p3 = multVecMatrix(p3, tfMatrix);
  2594. to = multVecMatrix(to, tfMatrix);
  2595. }
  2596. lines.push({
  2597. op: "c", c: [
  2598. p2[0], p2[1],
  2599. p3[0], p3[1],
  2600. to[0], to[1]
  2601. ]
  2602. });
  2603. } else if ("lLhHvVmM".indexOf(cmd) >= 0) {
  2604. curAngle = getDirectionVector([x, y], to);
  2605. hasStartMarker && markers.addMarker(new Marker(markerStart, [x, y], Math.atan2(curAngle[1], curAngle[0])));
  2606. hasEndMarker && markers.addMarker(new Marker(markerEnd, to, Math.atan2(curAngle[1], curAngle[0])));
  2607. if (hasMidMarker) {
  2608. var angle = "mM".indexOf(cmd) >= 0 ?
  2609. prevAngle : "mM".indexOf(list.getItem(i - 1).pathSegTypeAsLetter) >= 0 ?
  2610. curAngle : normalize(addVectors(prevAngle, curAngle));
  2611. markers.addMarker(new Marker(markerMid, [x, y], Math.atan2(angle[1], angle[0])));
  2612. }
  2613. prevAngle = curAngle;
  2614. if (withinClipPath) {
  2615. to = multVecMatrix(to, tfMatrix);
  2616. }
  2617. lines.push({op: op, c: to});
  2618. }
  2619. if ("MLCSQT".indexOf(cmd) >= 0) {
  2620. x = seg.x;
  2621. y = seg.y;
  2622. } else if ("mlcsqt".indexOf(cmd) >= 0) {
  2623. x = seg.x + x;
  2624. y = seg.y + y;
  2625. } else if ("zZ".indexOf(cmd) < 0) {
  2626. x = newX;
  2627. y = newY;
  2628. }
  2629. }
  2630. return {lines: lines, markers: markers};
  2631. };
  2632. var lines = getLinesFromPath();
  2633. if (lines.lines.length > 0) {
  2634. _pdf.path(lines.lines);
  2635. }
  2636. if (markerEnd || markerStart || markerMid) {
  2637. lines.markers.draw(_pdf.unitMatrix, refsHandler, attributeState);
  2638. }
  2639. };
  2640. // draws the element referenced by a use node, makes use of pdf's XObjects/FormObjects so nodes are only written once
  2641. // to the pdf document. This highly reduces the file size and computation time.
  2642. var use = function (node, tfMatrix, refsHandler) {
  2643. var url = (node.getAttribute("href") || node.getAttribute("xlink:href"));
  2644. // just in case someone has the idea to use empty use-tags, wtf???
  2645. if (!url)
  2646. return;
  2647. // get the size of the referenced form object (to apply the correct scaling)
  2648. var id = url.substring(1);
  2649. refsHandler.getRendered(id);
  2650. var formObject = _pdf.getFormObject(id);
  2651. // scale and position it right
  2652. var x = node.getAttribute("x") || 0;
  2653. var y = node.getAttribute("y") || 0;
  2654. var width = node.getAttribute("width") || formObject.width;
  2655. var height = node.getAttribute("height") || formObject.height;
  2656. var t = new _pdf.Matrix(width / formObject.width || 0, 0, 0, height / formObject.height || 0, x, y);
  2657. t = _pdf.matrixMult(t, tfMatrix);
  2658. _pdf.doFormObject(id, t);
  2659. };
  2660. // draws a line
  2661. var line = function (node, refsHandler, attributeState) {
  2662. var p1 = [parseFloat(node.getAttribute('x1') || 0), parseFloat(node.getAttribute('y1') || 0)];
  2663. var p2 = [parseFloat(node.getAttribute('x2') || 0), parseFloat(node.getAttribute('y2') || 0)];
  2664. if (attributeState.stroke !== null){
  2665. _pdf.line(p1[0], p1[1], p2[0], p2[1]);
  2666. }
  2667. var markerStart = node.getAttribute("marker-start"),
  2668. markerEnd = node.getAttribute("marker-end");
  2669. if (markerStart || markerEnd) {
  2670. var markers = new MarkerList();
  2671. var angle = getAngle(p1, p2);
  2672. if (markerStart) {
  2673. markers.addMarker(new Marker(iriReference.exec(markerStart)[1], p1, angle));
  2674. }
  2675. if (markerEnd) {
  2676. markers.addMarker(new Marker(iriReference.exec(markerEnd)[1], p2, angle));
  2677. }
  2678. markers.draw(_pdf.unitMatrix, refsHandler, attributeState);
  2679. }
  2680. };
  2681. // draws a rect
  2682. var rect = function (node) {
  2683. _pdf.roundedRect(
  2684. parseFloat(node.getAttribute('x')) || 0,
  2685. parseFloat(node.getAttribute('y')) || 0,
  2686. parseFloat(node.getAttribute('width')),
  2687. parseFloat(node.getAttribute('height')),
  2688. parseFloat(node.getAttribute('rx')) || 0,
  2689. parseFloat(node.getAttribute('ry')) || 0
  2690. );
  2691. };
  2692. // draws an ellipse
  2693. var ellipse = function (node) {
  2694. _pdf.ellipse(
  2695. parseFloat(node.getAttribute('cx')) || 0,
  2696. parseFloat(node.getAttribute('cy')) || 0,
  2697. parseFloat(node.getAttribute('rx')),
  2698. parseFloat(node.getAttribute('ry'))
  2699. );
  2700. };
  2701. // draws a circle
  2702. var circle = function (node) {
  2703. var radius = parseFloat(node.getAttribute('r')) || 0;
  2704. _pdf.ellipse(
  2705. parseFloat(node.getAttribute('cx')) || 0,
  2706. parseFloat(node.getAttribute('cy')) || 0,
  2707. radius,
  2708. radius
  2709. );
  2710. };
  2711. // applies text transformations to a text node
  2712. var transformText = function (node, text) {
  2713. var textTransform = getAttribute(node, "text-transform");
  2714. switch (textTransform) {
  2715. case "uppercase": return text.toUpperCase();
  2716. case "lowercase": return text.toLowerCase();
  2717. default: return text;
  2718. // TODO: capitalize, full-width
  2719. }
  2720. };
  2721. /**
  2722. * Canvas text measuring is a lot faster than svg measuring. However, it is inaccurate for some fonts. So test each
  2723. * font once and decide if canvas is accurate enough.
  2724. * @param {string} text
  2725. * @param {string} fontFamily
  2726. * @returns {function(string, string, string, string, string)}
  2727. */
  2728. var getMeasureFunction = (function getMeasureFunction() {
  2729. /**
  2730. * @param {string} text
  2731. * @param {string} fontFamily
  2732. * @param {string} fontSize
  2733. * @param {string} fontStyle
  2734. * @param {string} fontWeight
  2735. */
  2736. function canvasTextMeasure(text, fontFamily, fontSize, fontStyle, fontWeight) {
  2737. var canvas = document.createElement("canvas");
  2738. var context = canvas.getContext("2d");
  2739. context.font = [fontStyle, fontWeight, fontSize, fontFamily].join(" ");
  2740. return context.measureText(text).width;
  2741. }
  2742. /**
  2743. * @param {string} text
  2744. * @param {string} fontFamily
  2745. * @param {string} fontSize
  2746. * @param {string} fontStyle
  2747. * @param {string} fontWeight
  2748. */
  2749. function svgTextMeasure(text, fontFamily, fontSize, fontStyle, fontWeight) {
  2750. var textNode = document.createElementNS(svgNamespaceURI, "text");
  2751. textNode.setAttribute("font-family", fontFamily);
  2752. textNode.setAttribute("font-size", fontSize);
  2753. textNode.setAttribute("font-style", fontStyle);
  2754. textNode.setAttribute("font-weight", fontWeight);
  2755. textNode.setAttributeNS("http://www.w3.org/XML/1998/namespace", "xml:space", "preserve");
  2756. textNode.appendChild(document.createTextNode(text));
  2757. var svg = document.createElementNS(svgNamespaceURI, "svg");
  2758. svg.appendChild(textNode);
  2759. svg.setAttribute("visibility", "hidden");
  2760. document.body.appendChild(svg);
  2761. var width = textNode.getBBox().width;
  2762. document.body.removeChild(svg);
  2763. return width;
  2764. }
  2765. var testString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789!\"$%&/()=?'\\+*-_.:,;^}][{#~|<>";
  2766. var epsilon = 0.1;
  2767. var measureMethods = {};
  2768. return function getMeasureFunction(fontFamily) {
  2769. var method = measureMethods[fontFamily];
  2770. if (!method) {
  2771. var fontSize = "16px";
  2772. var fontStyle = "normal";
  2773. var fontWeight = "normal";
  2774. var canvasWidth = canvasTextMeasure(testString, fontFamily, fontSize, fontStyle, fontWeight);
  2775. var svgWidth = svgTextMeasure(testString, fontFamily, fontSize, fontStyle, fontWeight);
  2776. method = Math.abs(canvasWidth - svgWidth) < epsilon ? canvasTextMeasure : svgTextMeasure;
  2777. measureMethods[fontFamily] = method;
  2778. }
  2779. return method;
  2780. }
  2781. })();
  2782. /**
  2783. * @param {string} text
  2784. * @param {AttributeState} attributeState
  2785. * @returns {number}
  2786. */
  2787. function measureTextWidth(text, attributeState) {
  2788. if (text.length === 0) {
  2789. return 0;
  2790. }
  2791. var fontFamily = attributeState.fontFamily;
  2792. var measure = getMeasureFunction(fontFamily);
  2793. return measure(text, attributeState.fontFamily, attributeState.fontSize + "px", attributeState.fontStyle, attributeState.fontWeight);
  2794. }
  2795. /**
  2796. * @param {string} text
  2797. * @param {AttributeState} attributeState
  2798. * @returns {number}
  2799. */
  2800. function getTextOffset(text, attributeState) {
  2801. var textAnchor = attributeState.textAnchor;
  2802. if (textAnchor === "start") {
  2803. return 0;
  2804. }
  2805. var width = measureTextWidth(text, attributeState);
  2806. var xOffset = 0;
  2807. switch (textAnchor) {
  2808. case "end":
  2809. xOffset = width;
  2810. break;
  2811. case "middle":
  2812. xOffset = width / 2;
  2813. break;
  2814. }
  2815. return xOffset;
  2816. }
  2817. /**
  2818. * @param {string} textAnchor
  2819. * @param {number} originX
  2820. * @param {number} originY
  2821. * @constructor
  2822. */
  2823. function TextChunk(textAnchor, originX, originY) {
  2824. this.texts = [];
  2825. this.textNodes = [];
  2826. this.textAnchor = textAnchor;
  2827. this.originX = originX;
  2828. this.originY = originY;
  2829. }
  2830. /**
  2831. * @param {SVGElement} tSpan
  2832. * @param {string} text
  2833. */
  2834. TextChunk.prototype.add = function(tSpan, text) {
  2835. this.texts.push(text);
  2836. this.textNodes.push(tSpan);
  2837. };
  2838. /**
  2839. * Outputs the chunk to pdf.
  2840. * @param {jsPDF.Matrix} transform
  2841. * @param {AttributeState} attributeState
  2842. * @returns {[number, number]} The last current text position.
  2843. */
  2844. TextChunk.prototype.put = function (transform, attributeState) {
  2845. var i, textNode;
  2846. var xs = [], ys = [], attributeStates = [];
  2847. var currentTextX = this.originX, currentTextY = this.originY;
  2848. var minX = currentTextX, maxX = currentTextX;
  2849. for (i = 0; i < this.textNodes.length; i++) {
  2850. textNode = this.textNodes[i];
  2851. var x = currentTextX;
  2852. var y = currentTextY;
  2853. if (textNode.nodeName === "#text") {
  2854. textNodeAttributeState = attributeState
  2855. } else {
  2856. var textNodeAttributeState = attributeState.clone();
  2857. var tSpanColor = getAttribute(textNode, "fill");
  2858. setTextProperties(textNode, tSpanColor && new RGBColor(tSpanColor), textNodeAttributeState);
  2859. var tSpanDx = textNode.getAttribute("dx");
  2860. if (tSpanDx !== null) {
  2861. x += toPixels(tSpanDx, textNodeAttributeState.fontSize);
  2862. }
  2863. var tSpanDy = textNode.getAttribute("dy");
  2864. if (tSpanDy !== null) {
  2865. y += toPixels(tSpanDy, textNodeAttributeState.fontSize);
  2866. }
  2867. }
  2868. attributeStates[i] = textNodeAttributeState;
  2869. xs[i] = x;
  2870. ys[i] = y;
  2871. currentTextX = x + measureTextWidth(this.texts[i], textNodeAttributeState);
  2872. currentTextY = y;
  2873. minX = Math.min(minX, x);
  2874. maxX = Math.max(maxX, currentTextX);
  2875. }
  2876. var textOffset;
  2877. switch (this.textAnchor) {
  2878. case "start": textOffset = 0; break;
  2879. case "middle": textOffset = (maxX - minX) / 2; break;
  2880. case "end": textOffset = maxX - minX; break;
  2881. }
  2882. for (i = 0; i < this.textNodes.length; i++) {
  2883. textNode = this.textNodes[i];
  2884. if (textNode.nodeName !== "#text") {
  2885. var tSpanVisibility = getAttribute(textNode, "visibility") || attributeState.visibility;
  2886. if (tSpanVisibility === "hidden") {
  2887. continue;
  2888. }
  2889. }
  2890. _pdf.saveGraphicsState();
  2891. putTextProperties(attributeStates[i], attributeState);
  2892. _pdf.text(xs[i] - textOffset, ys[i], this.texts[i], void 0, transform);
  2893. _pdf.restoreGraphicsState();
  2894. }
  2895. return [currentTextX, currentTextY];
  2896. };
  2897. /**
  2898. * Convert em, px and bare number attributes to pixel values
  2899. * @param {string} value
  2900. * @param {number} pdfFontSize
  2901. */
  2902. function toPixels(value, pdfFontSize) {
  2903. var match;
  2904. // em
  2905. match = value && value.toString().match(/^([\-0-9.]+)em$/);
  2906. if (match) {
  2907. return parseFloat(match[1]) * pdfFontSize;
  2908. }
  2909. // pixels
  2910. match = value && value.toString().match(/^([\-0-9.]+)(px|)$/);
  2911. if (match) {
  2912. return parseFloat(match[1]);
  2913. }
  2914. return 0;
  2915. }
  2916. function transformXmlSpace(trimmedText, attributeState) {
  2917. trimmedText = removeNewlines(trimmedText);
  2918. trimmedText = replaceTabsBySpace(trimmedText);
  2919. if (attributeState.xmlSpace === "default") {
  2920. trimmedText = trimmedText.trim();
  2921. trimmedText = consolidateSpaces(trimmedText);
  2922. }
  2923. return trimmedText;
  2924. }
  2925. /**
  2926. * Draws a text element and its tspan children.
  2927. * @param {SVGElement} node
  2928. * @param {jsPDF.Matrix} tfMatrix
  2929. * @param {boolean} hasFillColor
  2930. * @param {RGBColor} fillRGB
  2931. * @param {AttributeState} attributeState
  2932. */
  2933. var text = function (node, tfMatrix, hasFillColor, fillRGB, attributeState) {
  2934. _pdf.saveGraphicsState();
  2935. var dx, dy, xOffset = 0;
  2936. var pdfFontSize = _pdf.getFontSize();
  2937. var textX = toPixels(node.getAttribute('x'), pdfFontSize);
  2938. var textY = toPixels(node.getAttribute('y'), pdfFontSize);
  2939. dx = toPixels(node.getAttribute("dx"), pdfFontSize);
  2940. dy = toPixels(node.getAttribute("dy"), pdfFontSize);
  2941. var visibility = attributeState.visibility;
  2942. // when there are no tspans draw the text directly
  2943. var tSpanCount = node.childElementCount;
  2944. if (tSpanCount === 0) {
  2945. var trimmedText = transformXmlSpace(node.textContent, attributeState);
  2946. var transformedText = transformText(node, trimmedText);
  2947. xOffset = getTextOffset(transformedText, attributeState);
  2948. if (visibility === "visible") {
  2949. _pdf.text(
  2950. textX + dx - xOffset,
  2951. textY + dy,
  2952. transformedText,
  2953. void 0,
  2954. tfMatrix
  2955. );
  2956. }
  2957. } else {
  2958. // otherwise loop over tspans and position each relative to the previous one
  2959. var currentTextSegment = new TextChunk(attributeState.textAnchor, textX + dx, textY + dy);
  2960. for (var i = 0; i < node.childNodes.length; i++) {
  2961. var textNode = node.childNodes[i];
  2962. if (!textNode.textContent) {
  2963. continue;
  2964. }
  2965. var xmlSpace = attributeState.xmlSpace;
  2966. if (textNode.nodeName === "#text") {
  2967. } else if (nodeIs(textNode, "tspan")) {
  2968. var tSpan = textNode;
  2969. var lastPositions;
  2970. var tSpanAbsX = tSpan.getAttribute("x");
  2971. if (tSpanAbsX !== null) {
  2972. var x = toPixels(tSpanAbsX, pdfFontSize);
  2973. lastPositions = currentTextSegment.put(tfMatrix, attributeState);
  2974. currentTextSegment = new TextChunk(tSpan.getAttribute("text-anchor") || attributeState.textAnchor, x, lastPositions[1]);
  2975. }
  2976. var tSpanAbsY = tSpan.getAttribute("y");
  2977. if (tSpanAbsY !== null) {
  2978. var y = toPixels(tSpanAbsY, pdfFontSize);
  2979. lastPositions = currentTextSegment.put(tfMatrix, attributeState);
  2980. currentTextSegment = new TextChunk(tSpan.getAttribute("text-anchor") || attributeState.textAnchor, lastPositions[0], y);
  2981. }
  2982. var tSpanXmlSpace = tSpan.getAttribute("xml:space");
  2983. if (tSpanXmlSpace) {
  2984. xmlSpace = tSpanXmlSpace;
  2985. }
  2986. }
  2987. trimmedText = textNode.textContent;
  2988. trimmedText = removeNewlines(trimmedText);
  2989. trimmedText = replaceTabsBySpace(trimmedText);
  2990. if (xmlSpace === "default") {
  2991. if (i === 0) {
  2992. trimmedText = trimLeft(trimmedText);
  2993. }
  2994. if (i === tSpanCount - 1) {
  2995. trimmedText = trimRight(trimmedText);
  2996. }
  2997. trimmedText = consolidateSpaces(trimmedText);
  2998. }
  2999. transformedText = transformText(node, trimmedText);
  3000. currentTextSegment.add(textNode, transformedText);
  3001. }
  3002. currentTextSegment.put(tfMatrix, attributeState);
  3003. }
  3004. _pdf.restoreGraphicsState();
  3005. };
  3006. // renders all children of a node
  3007. var renderChildren = function (node, tfMatrix, refsHandler, withinDefs, withinClipPath, attributeState) {
  3008. forEachChild(node, function (i, node) {
  3009. renderNode(node, tfMatrix, refsHandler, withinDefs, withinClipPath, attributeState);
  3010. });
  3011. };
  3012. // adds a gradient to defs and the pdf document for later use, type is either "axial" or "radial"
  3013. // opacity is only supported rudimentary by averaging over all stops
  3014. // transforms are applied on use
  3015. var putGradient = function (node, type, coords) {
  3016. var colors = [];
  3017. var opacitySum = 0;
  3018. var hasOpacity = false;
  3019. var gState;
  3020. forEachChild(node, function (i, element) {
  3021. // since opacity gradients are hard to realize, average the opacity over the control points
  3022. if (element.tagName.toLowerCase() === "stop") {
  3023. var color = new RGBColor(getAttribute(element, "stop-color"));
  3024. colors.push({
  3025. offset: parseFloat(element.getAttribute("offset")),
  3026. color: [color.r, color.g, color.b]
  3027. });
  3028. var opacity = getAttribute(element, "stop-opacity");
  3029. if (opacity && opacity != 1) {
  3030. opacitySum += parseFloat(opacity);
  3031. hasOpacity = true;
  3032. }
  3033. }
  3034. });
  3035. if (hasOpacity) {
  3036. gState = new _pdf.GState({opacity: opacitySum / colors.length});
  3037. }
  3038. var pattern = new _pdf.ShadingPattern(type, coords, colors, gState);
  3039. var id = node.getAttribute("id");
  3040. _pdf.addShadingPattern(id, pattern);
  3041. };
  3042. var pattern = function (node, refsHandler, attributeState) {
  3043. var id = node.getAttribute("id");
  3044. // the transformations directly at the node are written to the pattern transformation matrix
  3045. var bBox = getUntransformedBBox(node);
  3046. var pattern = new _pdf.TilingPattern([bBox[0], bBox[1], bBox[0] + bBox[2], bBox[1] + bBox[3]], bBox[2], bBox[3],
  3047. null, _pdf.unitMatrix /* this parameter is ignored !*/);
  3048. _pdf.beginTilingPattern(pattern);
  3049. // continue without transformation
  3050. renderChildren(node, _pdf.unitMatrix, refsHandler, false, false, attributeState);
  3051. _pdf.endTilingPattern(id, pattern);
  3052. };
  3053. var fontAliases = {
  3054. "sans-serif": "helvetica",
  3055. "verdana": "helvetica",
  3056. "arial": "helvetica",
  3057. "fixed": "courier",
  3058. "monospace": "courier",
  3059. "terminal": "courier",
  3060. "serif": "times",
  3061. "cursive": "times",
  3062. "fantasy": "times"
  3063. };
  3064. /**
  3065. * @param {AttributeState} attributeState
  3066. * @param {string[]} fontFamilies
  3067. * @return {string}
  3068. */
  3069. function findFirstAvailableFontFamily(attributeState, fontFamilies) {
  3070. var fontType = "";
  3071. if (attributeState.fontWeight === "bold") {
  3072. fontType = "bold";
  3073. }
  3074. if (attributeState.fontStyle === "italic") {
  3075. fontType += "italic";
  3076. }
  3077. if (fontType === "") {
  3078. fontType = "normal";
  3079. }
  3080. var availableFonts = _pdf.getFontList();
  3081. var firstAvailable = "";
  3082. var fontIsAvailable = fontFamilies.some(function (font) {
  3083. var availableStyles = availableFonts[font];
  3084. if (availableStyles && availableStyles.indexOf(fontType) >= 0) {
  3085. firstAvailable = font;
  3086. return true;
  3087. }
  3088. font = font.toLowerCase();
  3089. if (fontAliases.hasOwnProperty(font)) {
  3090. firstAvailable = font;
  3091. return true;
  3092. }
  3093. return false;
  3094. });
  3095. if (!fontIsAvailable) {
  3096. firstAvailable = "times";
  3097. }
  3098. return firstAvailable;
  3099. }
  3100. function setTextProperties(node, fillRGB, attributeState) {
  3101. if (fillRGB && fillRGB.ok) {
  3102. attributeState.fill = fillRGB;
  3103. }
  3104. var fontWeight = getAttribute(node, "font-weight");
  3105. if (fontWeight) {
  3106. attributeState.fontWeight = fontWeight;
  3107. }
  3108. var fontStyle = getAttribute(node, "font-style");
  3109. if (fontStyle) {
  3110. attributeState.fontStyle = fontStyle;
  3111. }
  3112. var fontFamily = getAttribute(node, "font-family");
  3113. if (fontFamily) {
  3114. var fontFamilies = FontFamily.parse(fontFamily);
  3115. attributeState.fontFamily = findFirstAvailableFontFamily(attributeState, fontFamilies);
  3116. }
  3117. var fontSize = getAttribute(node, "font-size");
  3118. if (fontSize) {
  3119. attributeState.fontSize = parseFloat(fontSize);
  3120. }
  3121. var textAnchor = getAttribute(node, "text-anchor");
  3122. if (textAnchor) {
  3123. attributeState.textAnchor = textAnchor;
  3124. }
  3125. }
  3126. /**
  3127. * @param {AttributeState} attributeState
  3128. * @param {AttributeState} parentAttributeState
  3129. */
  3130. function putTextProperties(attributeState, parentAttributeState) {
  3131. if (attributeState.fontFamily !== parentAttributeState.fontFamily) {
  3132. if (fontAliases.hasOwnProperty(attributeState.fontFamily)) {
  3133. _pdf.setFont(fontAliases[attributeState.fontFamily]);
  3134. } else {
  3135. _pdf.setFont(attributeState.fontFamily);
  3136. }
  3137. }
  3138. if (attributeState.fill !== parentAttributeState.fill && attributeState.fill.ok) {
  3139. var fillRGB = attributeState.fill;
  3140. _pdf.setTextColor(fillRGB.r, fillRGB.g, fillRGB.b);
  3141. }
  3142. if (attributeState.fontWeight !== parentAttributeState.fontWeight
  3143. || attributeState.fontStyle !== parentAttributeState.fontStyle) {
  3144. var fontType = "";
  3145. if (attributeState.fontWeight === "bold") {
  3146. fontType = "bold";
  3147. }
  3148. if (attributeState.fontStyle === "italic") {
  3149. fontType += "italic";
  3150. }
  3151. if (fontType === "") {
  3152. fontType = "normal";
  3153. }
  3154. _pdf.setFontType(fontType);
  3155. }
  3156. if (attributeState.fontSize !== parentAttributeState.fontSize) {
  3157. _pdf.setFontSize(attributeState.fontSize);
  3158. }
  3159. }
  3160. /**
  3161. * Renders a svg node.
  3162. * @param node The svg element
  3163. * @param contextTransform The current transformation matrix
  3164. * @param refsHandler The handler that will render references on demand
  3165. * @param withinDefs True iff we are top-level within a defs node, so the target can be switched to an pdf form object
  3166. * @param {boolean} withinClipPath
  3167. * @param {AttributeState} attributeState Keeps track of parent attributes that are inherited automatically
  3168. */
  3169. var renderNode = function (node, contextTransform, refsHandler, withinDefs, withinClipPath, attributeState) {
  3170. var parentAttributeState = attributeState;
  3171. attributeState = attributeState.clone();
  3172. if (nodeIs(node, "defs,clippath,pattern,lineargradient,radialgradient,marker")) {
  3173. // we will only render them on demand
  3174. return;
  3175. }
  3176. if (getAttribute(node, "display") === "none") {
  3177. return;
  3178. }
  3179. var visibility = attributeState.visibility = getAttribute(node, "visibility") || attributeState.visibility;
  3180. if (visibility === "hidden" && !nodeIs(node, "svg,g,marker,a,pattern,defs,text")) {
  3181. return;
  3182. }
  3183. var tfMatrix,
  3184. hasFillColor = false,
  3185. fillRGB = null,
  3186. fill = "inherit",
  3187. stroke = "inherit",
  3188. patternOrGradient = undefined,
  3189. bBox;
  3190. //
  3191. // Decide about the render target and set the correct transformation
  3192. //
  3193. // if we are within a defs node, start a new pdf form object and draw this node and all children on that instead
  3194. // of the top-level page
  3195. var targetIsFormObject = withinDefs && !nodeIs(node, "lineargradient,radialgradient,pattern,clippath");
  3196. if (targetIsFormObject) {
  3197. // the transformations directly at the node are written to the pdf form object transformation matrix
  3198. tfMatrix = computeNodeTransform(node);
  3199. bBox = getUntransformedBBox(node);
  3200. _pdf.beginFormObject(bBox[0], bBox[1], bBox[2], bBox[3], tfMatrix);
  3201. // continue without transformation and set withinDefs to false to prevent child nodes from starting new form objects
  3202. tfMatrix = _pdf.unitMatrix;
  3203. withinDefs = false;
  3204. } else {
  3205. tfMatrix = _pdf.matrixMult(computeNodeTransform(node), contextTransform);
  3206. if (!withinClipPath) {
  3207. _pdf.saveGraphicsState();
  3208. }
  3209. }
  3210. var hasClipPath = node.hasAttribute("clip-path") && node.getAttribute("clip-path") !== "none";
  3211. if (hasClipPath) {
  3212. var clipPathId = iriReference.exec(node.getAttribute("clip-path"));
  3213. var clipPathNode = refsHandler.getRendered(clipPathId[1]);
  3214. var clipPathMatrix = tfMatrix;
  3215. if (clipPathNode.hasAttribute("clipPathUnits")
  3216. && clipPathNode.getAttribute("clipPathUnits").toLowerCase() === "objectboundingbox") {
  3217. bBox = getUntransformedBBox(node);
  3218. clipPathMatrix = _pdf.matrixMult(new _pdf.Matrix(bBox[2], 0, 0, bBox[3], bBox[0], bBox[1]), clipPathMatrix);
  3219. }
  3220. // here, browsers show different results for a "transform" attribute on the clipPath element itself:
  3221. // IE/Edge considers it, Chrome and Firefox ignore it. However, the specification lists "transform" as a valid
  3222. // attribute for clipPath elements, although not explicitly explaining its behavior. This implementation follows
  3223. // IE/Edge and considers the "transform" attribute as additional transformation within the coordinate system
  3224. // established by the "clipPathUnits" attribute.
  3225. clipPathMatrix = _pdf.matrixMult(computeNodeTransform(clipPathNode), clipPathMatrix);
  3226. _pdf.saveGraphicsState();
  3227. _pdf.setCurrentTransformationMatrix(clipPathMatrix);
  3228. renderChildren(clipPathNode, _pdf.unitMatrix, refsHandler, false, true, attributeState);
  3229. _pdf.clip().discardPath();
  3230. // as we cannot use restoreGraphicsState() to reset the transform (this would reset the clipping path, as well),
  3231. // we must append the inverse instead
  3232. _pdf.setCurrentTransformationMatrix(clipPathMatrix.inversed());
  3233. }
  3234. //
  3235. // extract fill and stroke mode
  3236. //
  3237. // fill mode
  3238. if (nodeIs(node, "g,path,rect,text,ellipse,line,circle,polygon,polyline")) {
  3239. function setDefaultColor() {
  3240. fillRGB = new RGBColor("rgb(0, 0, 0)");
  3241. hasFillColor = true;
  3242. fill = true;
  3243. }
  3244. var fillColor = getAttribute(node, "fill");
  3245. if (fillColor) {
  3246. var url = iriReference.exec(fillColor);
  3247. if (url) {
  3248. // probably a gradient or pattern (or something unsupported)
  3249. var fillUrl = url[1];
  3250. var fillNode = refsHandler.getRendered(fillUrl);
  3251. if (fillNode && nodeIs(fillNode, "lineargradient,radialgradient")) {
  3252. // matrix to convert between gradient space and user space
  3253. // for "userSpaceOnUse" this is the current transformation: tfMatrix
  3254. // for "objectBoundingBox" or default, the gradient gets scaled and transformed to the bounding box
  3255. var gradientUnitsMatrix;
  3256. if (!fillNode.hasAttribute("gradientUnits")
  3257. || fillNode.getAttribute("gradientUnits").toLowerCase() === "objectboundingbox") {
  3258. bBox || (bBox = getUntransformedBBox(node));
  3259. gradientUnitsMatrix = new _pdf.Matrix(bBox[2], 0, 0, bBox[3], bBox[0], bBox[1]);
  3260. } else {
  3261. gradientUnitsMatrix = _pdf.unitMatrix;
  3262. }
  3263. // matrix that is applied to the gradient before any other transformations
  3264. var gradientTransform = parseTransform(fillNode.getAttribute("gradientTransform"));
  3265. patternOrGradient = {
  3266. key: fillUrl,
  3267. matrix: _pdf.matrixMult(gradientTransform, gradientUnitsMatrix)
  3268. };
  3269. fill = true;
  3270. } else if (fillNode && nodeIs(fillNode, "pattern")) {
  3271. var fillBBox, y, width, height, x;
  3272. patternOrGradient = { key: fillUrl };
  3273. var patternUnitsMatrix = _pdf.unitMatrix;
  3274. if (!fillNode.hasAttribute("patternUnits")
  3275. || fillNode.getAttribute("patternUnits").toLowerCase() === "objectboundingbox") {
  3276. bBox || (bBox = getUntransformedBBox(node));
  3277. patternUnitsMatrix = new _pdf.Matrix(1, 0, 0, 1, bBox[0], bBox[1]);
  3278. // TODO: slightly inaccurate (rounding errors? line width bBoxes?)
  3279. fillBBox = getUntransformedBBox(fillNode);
  3280. x = fillBBox[0] * bBox[0];
  3281. y = fillBBox[1] * bBox[1];
  3282. width = fillBBox[2] * bBox[2];
  3283. height = fillBBox[3] * bBox[3];
  3284. patternOrGradient.boundingBox = [x, y, x + width, y + height];
  3285. patternOrGradient.xStep = width;
  3286. patternOrGradient.yStep = height;
  3287. }
  3288. var patternContentUnitsMatrix = _pdf.unitMatrix;
  3289. if (fillNode.hasAttribute("patternContentUnits")
  3290. && fillNode.getAttribute("patternContentUnits").toLowerCase() === "objectboundingbox") {
  3291. bBox || (bBox = getUntransformedBBox(node));
  3292. patternContentUnitsMatrix = new _pdf.Matrix(bBox[2], 0, 0, bBox[3], 0, 0);
  3293. fillBBox = patternOrGradient.boundingBox || getUntransformedBBox(fillNode);
  3294. x = fillBBox[0] / bBox[0];
  3295. y = fillBBox[1] / bBox[1];
  3296. width = fillBBox[2] / bBox[2];
  3297. height = fillBBox[3] / bBox[3];
  3298. patternOrGradient.boundingBox = [x, y, x + width, y + height];
  3299. patternOrGradient.xStep = width;
  3300. patternOrGradient.yStep = height;
  3301. }
  3302. var patternTransformMatrix = _pdf.unitMatrix;
  3303. if (fillNode.hasAttribute("patternTransform")) {
  3304. patternTransformMatrix = parseTransform(fillNode.getAttribute("patternTransform"));
  3305. }
  3306. var matrix = patternContentUnitsMatrix;
  3307. matrix = _pdf.matrixMult(matrix, patternUnitsMatrix);
  3308. matrix = _pdf.matrixMult(matrix, patternTransformMatrix);
  3309. matrix = _pdf.matrixMult(matrix, tfMatrix);
  3310. patternOrGradient.matrix = matrix;
  3311. fill = true;
  3312. } else {
  3313. // unsupported fill argument -> fill black
  3314. setDefaultColor();
  3315. }
  3316. } else {
  3317. // plain color
  3318. fillRGB = parseColor(fillColor);
  3319. if (fillRGB.ok) {
  3320. hasFillColor = true;
  3321. fill = true;
  3322. } else {
  3323. fill = false;
  3324. }
  3325. }
  3326. }
  3327. // opacity is realized via a pdf graphics state
  3328. var fillOpacity = 1.0, strokeOpacity = 1.0;
  3329. var nodeFillOpacity = getAttribute(node, "fill-opacity");
  3330. if (nodeFillOpacity) {
  3331. fillOpacity *= parseFloat(nodeFillOpacity);
  3332. }
  3333. if (fillRGB && typeof fillRGB.a === "number") {
  3334. fillOpacity *= fillRGB.a;
  3335. }
  3336. var nodeStrokeOpacity = getAttribute(node, "stroke-opacity");
  3337. if (nodeStrokeOpacity) {
  3338. strokeOpacity *= parseFloat(nodeStrokeOpacity);
  3339. }
  3340. if (strokeRGB && typeof strokeRGB.a === "number") {
  3341. strokeOpacity *= strokeRGB.a;
  3342. }
  3343. var nodeOpacity = getAttribute(node, "opacity");
  3344. if (nodeOpacity) {
  3345. var opacity = parseFloat(nodeOpacity);
  3346. strokeOpacity *= opacity;
  3347. fillOpacity *= opacity;
  3348. }
  3349. var hasFillOpacity = fillOpacity < 1.0;
  3350. var hasStrokeOpacity = strokeOpacity < 1.0;
  3351. if (hasFillOpacity || hasStrokeOpacity) {
  3352. var gState = {};
  3353. hasFillOpacity && (gState["opacity"] = fillOpacity);
  3354. hasStrokeOpacity && (gState["stroke-opacity"] = strokeOpacity);
  3355. _pdf.setGState(new _pdf.GState(gState));
  3356. }
  3357. }
  3358. if (nodeIs(node, "g,path,rect,ellipse,line,circle,polygon,polyline")) {
  3359. // text has no fill color, so don't apply it until here
  3360. if (hasFillColor) {
  3361. attributeState.fill = fillRGB;
  3362. _pdf.setFillColor(fillRGB.r, fillRGB.g, fillRGB.b);
  3363. }
  3364. // stroke mode
  3365. var strokeColor = getAttribute(node, "stroke");
  3366. if (strokeColor) {
  3367. var strokeWidth = getAttribute(node, "stroke-width");
  3368. if (strokeWidth !== void 0 && strokeWidth !== "") {
  3369. strokeWidth = Math.abs(parseFloat(strokeWidth));
  3370. attributeState.strokeWidth = strokeWidth;
  3371. _pdf.setLineWidth(strokeWidth);
  3372. } else {
  3373. // needed for inherited zero width strokes
  3374. strokeWidth = attributeState.strokeWidth
  3375. }
  3376. var strokeRGB = new RGBColor(strokeColor);
  3377. if (strokeRGB.ok) {
  3378. attributeState.stroke = strokeRGB;
  3379. _pdf.setDrawColor(strokeRGB.r, strokeRGB.g, strokeRGB.b);
  3380. // pdf spec states: "A line width of 0 denotes the thinnest line that can be rendered at device resolution:
  3381. // 1 device pixel wide". SVG, however, does not draw zero width lines.
  3382. stroke = strokeWidth !== 0;
  3383. }
  3384. var lineCap = getAttribute(node, "stroke-linecap");
  3385. if (lineCap) {
  3386. _pdf.setLineCap(attributeState.strokeLinecap = lineCap);
  3387. }
  3388. var lineJoin = getAttribute(node, "stroke-linejoin");
  3389. if (lineJoin) {
  3390. _pdf.setLineJoin(attributeState.strokeLinejoin = lineJoin);
  3391. }
  3392. var dashArray = getAttribute(node, "stroke-dasharray");
  3393. if (dashArray) {
  3394. dashArray = parseFloats(dashArray);
  3395. var dashOffset = parseInt(getAttribute(node, "stroke-dashoffset")) || 0;
  3396. attributeState.strokeDasharray = dashArray;
  3397. attributeState.strokeDashoffset = dashOffset;
  3398. _pdf.setLineDashPattern(dashArray, dashOffset);
  3399. }
  3400. var miterLimit = getAttribute(node, "stroke-miterlimit");
  3401. if (miterLimit !== void 0 && miterLimit !== "") {
  3402. _pdf.setLineMiterLimit(attributeState.strokeMiterlimit = parseFloat(miterLimit));
  3403. }
  3404. }
  3405. }
  3406. // inherit fill and stroke mode if not specified at this node
  3407. if (fill === "inherit") {
  3408. fill = attributeState.fill !== null;
  3409. }
  3410. if (stroke === "inherit") {
  3411. stroke = attributeState.stroke !== null;
  3412. }
  3413. var xmlSpace = node.getAttribute("xml:space");
  3414. if (xmlSpace) {
  3415. attributeState.xmlSpace = xmlSpace;
  3416. }
  3417. setTextProperties(node, fillRGB, attributeState);
  3418. putTextProperties(attributeState, parentAttributeState);
  3419. // do the actual drawing
  3420. switch (node.tagName.toLowerCase()) {
  3421. case 'svg':
  3422. case 'g':
  3423. case 'a':
  3424. renderChildren(node, tfMatrix, refsHandler, withinDefs, false, attributeState);
  3425. break;
  3426. case 'use':
  3427. use(node, tfMatrix, refsHandler);
  3428. break;
  3429. case 'line':
  3430. if (!withinClipPath) {
  3431. _pdf.setCurrentTransformationMatrix(tfMatrix);
  3432. line(node, refsHandler, attributeState);
  3433. }
  3434. break;
  3435. case 'rect':
  3436. if (!withinClipPath) {
  3437. _pdf.setCurrentTransformationMatrix(tfMatrix);
  3438. }
  3439. rect(node);
  3440. break;
  3441. case 'ellipse':
  3442. if (!withinClipPath) {
  3443. _pdf.setCurrentTransformationMatrix(tfMatrix);
  3444. }
  3445. ellipse(node);
  3446. break;
  3447. case 'circle':
  3448. if (!withinClipPath) {
  3449. _pdf.setCurrentTransformationMatrix(tfMatrix);
  3450. }
  3451. circle(node);
  3452. break;
  3453. case 'text':
  3454. text(node, tfMatrix, hasFillColor, fillRGB, attributeState);
  3455. break;
  3456. case 'path':
  3457. if (!withinClipPath) {
  3458. _pdf.setCurrentTransformationMatrix(tfMatrix);
  3459. }
  3460. path(node, tfMatrix, refsHandler, withinClipPath, attributeState);
  3461. break;
  3462. case 'polygon':
  3463. case 'polyline':
  3464. if (!withinClipPath) {
  3465. _pdf.setCurrentTransformationMatrix(tfMatrix);
  3466. }
  3467. polygon(node, refsHandler, attributeState, node.tagName.toLowerCase() === "polygon");
  3468. break;
  3469. case 'image':
  3470. _pdf.setCurrentTransformationMatrix(tfMatrix);
  3471. image(node);
  3472. break;
  3473. }
  3474. if (nodeIs(node, "path,rect,ellipse,circle,polygon,polyline") && !withinClipPath) {
  3475. if (fill && stroke) {
  3476. _pdf.fillStroke(patternOrGradient);
  3477. } else if (fill) {
  3478. _pdf.fill(patternOrGradient);
  3479. } else if (stroke) {
  3480. _pdf.stroke();
  3481. } else {
  3482. _pdf.discardPath();
  3483. }
  3484. }
  3485. // close either the formObject or the graphics context
  3486. if (targetIsFormObject) {
  3487. _pdf.endFormObject(node.getAttribute("id"));
  3488. } else if (!withinClipPath) {
  3489. _pdf.restoreGraphicsState();
  3490. }
  3491. if (hasClipPath) {
  3492. _pdf.restoreGraphicsState();
  3493. }
  3494. };
  3495. // the actual svgToPdf function (see above)
  3496. var svg2pdf = function (element, pdf, options) {
  3497. _pdf = pdf;
  3498. var k = options.scale || 1.0,
  3499. xOffset = options.xOffset || 0.0,
  3500. yOffset = options.yOffset || 0.0;
  3501. _pdf.advancedAPI(function () {
  3502. // set offsets and scale everything by k
  3503. _pdf.saveGraphicsState();
  3504. _pdf.setCurrentTransformationMatrix(new _pdf.Matrix(k, 0, 0, k, xOffset, yOffset));
  3505. // set default values that differ from pdf defaults
  3506. var attributeState = AttributeState.default();
  3507. _pdf.setLineWidth(attributeState.strokeWidth);
  3508. var fill = attributeState.fill;
  3509. _pdf.setFillColor(fill.r, fill.g, fill.b);
  3510. _pdf.setFont(attributeState.fontFamily);
  3511. _pdf.setFontSize(attributeState.fontSize);
  3512. var refsHandler = new ReferencesHandler(element);
  3513. renderNode(element.cloneNode(true), _pdf.unitMatrix, refsHandler, false, false, attributeState);
  3514. _pdf.restoreGraphicsState();
  3515. });
  3516. return _pdf;
  3517. };
  3518. if (typeof define === "function" && define.amd) {
  3519. define(["./rgbcolor", "SvgPath", "font-family"], function (rgbcolor, svgpath, fontFamily) {
  3520. RGBColor = rgbcolor;
  3521. SvgPath = svgpath;
  3522. FontFamily = fontFamily;
  3523. return svg2pdf;
  3524. });
  3525. } else if (typeof module !== "undefined" && module.exports) {
  3526. RGBColor = require("./rgbcolor.js");
  3527. SvgPath = require("SvgPath");
  3528. FontFamily = require("font-family");
  3529. module.exports = svg2pdf;
  3530. } else {
  3531. SvgPath = global.SvgPath;
  3532. RGBColor = global.RGBColor;
  3533. FontFamily = global.FontFamily;
  3534. global.svg2pdf = svg2pdf;
  3535. // for compatibility reasons
  3536. global.svgElementToPdf = svg2pdf;
  3537. }
  3538. return svg2pdf;
  3539. }(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this));
  3540. },{"./rgbcolor.js":9,"SvgPath":1,"font-family":8}]},{},[10])(10)
  3541. });
  3542. //# sourceMappingURL=svg2pdf.js.map