svg-run.c 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823
  1. // Copyright (C) 2004-2025 Artifex Software, Inc.
  2. //
  3. // This file is part of MuPDF.
  4. //
  5. // MuPDF is free software: you can redistribute it and/or modify it under the
  6. // terms of the GNU Affero General Public License as published by the Free
  7. // Software Foundation, either version 3 of the License, or (at your option)
  8. // any later version.
  9. //
  10. // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
  11. // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  12. // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
  13. // details.
  14. //
  15. // You should have received a copy of the GNU Affero General Public License
  16. // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
  17. //
  18. // Alternative licensing terms are available from the licensor.
  19. // For commercial licensing, see <https://www.artifex.com/> or contact
  20. // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
  21. // CA 94129, USA, for further information.
  22. #include "mupdf/fitz.h"
  23. #include "svg-imp.h"
  24. #include <string.h>
  25. #include <math.h>
  26. /* default page size */
  27. #define DEF_WIDTH 612
  28. #define DEF_HEIGHT 792
  29. #define DEF_FONTSIZE 12
  30. #define MAX_USE_DEPTH 100
  31. typedef struct svg_state
  32. {
  33. fz_matrix transform;
  34. fz_stroke_state *stroke;
  35. int use_depth;
  36. float viewport_w, viewport_h;
  37. float viewbox_w, viewbox_h, viewbox_size;
  38. float fontsize;
  39. float opacity;
  40. int fill_rule;
  41. int fill_is_set;
  42. float fill_color[3];
  43. float fill_opacity;
  44. int stroke_is_set;
  45. float stroke_color[3];
  46. float stroke_opacity;
  47. const char *font_family;
  48. int is_bold;
  49. int is_italic;
  50. int text_anchor;
  51. } svg_state;
  52. static void svg_parse_common(fz_context *ctx, svg_document *doc, fz_xml *node, svg_state *state);
  53. static void svg_run_element(fz_context *ctx, fz_device *dev, svg_document *doc, fz_xml *root, const svg_state *state);
  54. void svg_begin_state(fz_context *ctx, svg_state *child, const svg_state *parent)
  55. {
  56. memcpy(child, parent, sizeof(svg_state));
  57. child->stroke = fz_clone_stroke_state(ctx, parent->stroke);
  58. }
  59. void svg_end_state(fz_context *ctx, svg_state *child)
  60. {
  61. fz_drop_stroke_state(ctx, child->stroke);
  62. }
  63. static void svg_fill(fz_context *ctx, fz_device *dev, svg_document *doc, fz_path *path, svg_state *state)
  64. {
  65. float opacity = state->opacity * state->fill_opacity;
  66. if (path)
  67. fz_fill_path(ctx, dev, path, state->fill_rule, state->transform, fz_device_rgb(ctx), state->fill_color, opacity, fz_default_color_params);
  68. }
  69. static void svg_stroke(fz_context *ctx, fz_device *dev, svg_document *doc, fz_path *path, svg_state *state)
  70. {
  71. float opacity = state->opacity * state->stroke_opacity;
  72. if (path)
  73. fz_stroke_path(ctx, dev, path, state->stroke, state->transform, fz_device_rgb(ctx), state->stroke_color, opacity, fz_default_color_params);
  74. }
  75. static void svg_draw_path(fz_context *ctx, fz_device *dev, svg_document *doc, fz_path *path, svg_state *state)
  76. {
  77. if (state->fill_is_set)
  78. svg_fill(ctx, dev, doc, path, state);
  79. if (state->stroke_is_set)
  80. svg_stroke(ctx, dev, doc, path, state);
  81. }
  82. /*
  83. We use the MAGIC number 0.551915 as a bezier subdivision to approximate
  84. a quarter circle arc. The reasons for this can be found here:
  85. http://mechanicalexpressions.com/explore/geometric-modeling/circle-spline-approximation.pdf
  86. */
  87. static const float MAGIC_CIRCLE = 0.551915f;
  88. static void approx_circle(fz_context *ctx, fz_path *path, float cx, float cy, float rx, float ry)
  89. {
  90. float mx = rx * MAGIC_CIRCLE;
  91. float my = ry * MAGIC_CIRCLE;
  92. fz_moveto(ctx, path, cx, cy+ry);
  93. fz_curveto(ctx, path, cx + mx, cy + ry, cx + rx, cy + my, cx + rx, cy);
  94. fz_curveto(ctx, path, cx + rx, cy - my, cx + mx, cy - ry, cx, cy - ry);
  95. fz_curveto(ctx, path, cx - mx, cy - ry, cx - rx, cy - my, cx - rx, cy);
  96. fz_curveto(ctx, path, cx - rx, cy + my, cx - mx, cy + ry, cx, cy + ry);
  97. fz_closepath(ctx, path);
  98. }
  99. static void
  100. svg_run_rect(fz_context *ctx, fz_device *dev, svg_document *doc, fz_xml *node, const svg_state *inherit_state)
  101. {
  102. svg_state local_state;
  103. char *x_att = fz_xml_att(node, "x");
  104. char *y_att = fz_xml_att(node, "y");
  105. char *w_att = fz_xml_att(node, "width");
  106. char *h_att = fz_xml_att(node, "height");
  107. char *rx_att = fz_xml_att(node, "rx");
  108. char *ry_att = fz_xml_att(node, "ry");
  109. float x = 0;
  110. float y = 0;
  111. float w = 0;
  112. float h = 0;
  113. float rx = 0;
  114. float ry = 0;
  115. fz_path *path = NULL;
  116. fz_var(path);
  117. fz_try(ctx)
  118. {
  119. svg_begin_state(ctx, &local_state, inherit_state);
  120. svg_parse_common(ctx, doc, node, &local_state);
  121. if (x_att) x = svg_parse_length(x_att, local_state.viewbox_w, local_state.fontsize);
  122. if (y_att) y = svg_parse_length(y_att, local_state.viewbox_h, local_state.fontsize);
  123. if (w_att) w = svg_parse_length(w_att, local_state.viewbox_w, local_state.fontsize);
  124. if (h_att) h = svg_parse_length(h_att, local_state.viewbox_h, local_state.fontsize);
  125. if (rx_att) rx = svg_parse_length(rx_att, local_state.viewbox_w, local_state.fontsize);
  126. if (ry_att) ry = svg_parse_length(ry_att, local_state.viewbox_h, local_state.fontsize);
  127. if (rx_att && !ry_att)
  128. ry = rx;
  129. if (ry_att && !rx_att)
  130. rx = ry;
  131. if (rx > w * 0.5f)
  132. rx = w * 0.5f;
  133. if (ry > h * 0.5f)
  134. ry = h * 0.5f;
  135. if (w <= 0 || h <= 0)
  136. return;
  137. path = fz_new_path(ctx);
  138. if (rx == 0 || ry == 0)
  139. {
  140. fz_moveto(ctx, path, x, y);
  141. fz_lineto(ctx, path, x + w, y);
  142. fz_lineto(ctx, path, x + w, y + h);
  143. fz_lineto(ctx, path, x, y + h);
  144. }
  145. else
  146. {
  147. float rxs = rx * MAGIC_CIRCLE;
  148. float rys = rx * MAGIC_CIRCLE;
  149. fz_moveto(ctx, path, x + w - rx, y);
  150. fz_curveto(ctx, path, x + w - rxs, y, x + w, y + rys, x + w, y + ry);
  151. fz_lineto(ctx, path, x + w, y + h - ry);
  152. fz_curveto(ctx, path, x + w, y + h - rys, x + w - rxs, y + h, x + w - rx, y + h);
  153. fz_lineto(ctx, path, x + rx, y + h);
  154. fz_curveto(ctx, path, x + rxs, y + h, x, y + h - rys, x, y + h - rx);
  155. fz_lineto(ctx, path, x, y + rx);
  156. fz_curveto(ctx, path, x, y + rxs, x + rxs, y, x + rx, y);
  157. }
  158. fz_closepath(ctx, path);
  159. svg_draw_path(ctx, dev, doc, path, &local_state);
  160. }
  161. fz_always(ctx)
  162. {
  163. fz_drop_path(ctx, path);
  164. svg_end_state(ctx, &local_state);
  165. }
  166. fz_catch(ctx)
  167. fz_rethrow(ctx);
  168. }
  169. static void
  170. svg_run_circle(fz_context *ctx, fz_device *dev, svg_document *doc, fz_xml *node, const svg_state *inherit_state)
  171. {
  172. svg_state local_state;
  173. char *cx_att = fz_xml_att(node, "cx");
  174. char *cy_att = fz_xml_att(node, "cy");
  175. char *r_att = fz_xml_att(node, "r");
  176. float cx = 0;
  177. float cy = 0;
  178. float r = 0;
  179. fz_path *path = NULL;
  180. fz_var(path);
  181. fz_try(ctx)
  182. {
  183. svg_begin_state(ctx, &local_state, inherit_state);
  184. svg_parse_common(ctx, doc, node, &local_state);
  185. if (cx_att) cx = svg_parse_length(cx_att, local_state.viewbox_w, local_state.fontsize);
  186. if (cy_att) cy = svg_parse_length(cy_att, local_state.viewbox_h, local_state.fontsize);
  187. if (r_att) r = svg_parse_length(r_att, local_state.viewbox_size, 12);
  188. if (r > 0)
  189. {
  190. path = fz_new_path(ctx);
  191. approx_circle(ctx, path, cx, cy, r, r);
  192. svg_draw_path(ctx, dev, doc, path, &local_state);
  193. }
  194. }
  195. fz_always(ctx)
  196. {
  197. fz_drop_path(ctx, path);
  198. svg_end_state(ctx, &local_state);
  199. }
  200. fz_catch(ctx)
  201. fz_rethrow(ctx);
  202. }
  203. static void
  204. svg_run_ellipse(fz_context *ctx, fz_device *dev, svg_document *doc, fz_xml *node, const svg_state *inherit_state)
  205. {
  206. svg_state local_state;
  207. char *cx_att = fz_xml_att(node, "cx");
  208. char *cy_att = fz_xml_att(node, "cy");
  209. char *rx_att = fz_xml_att(node, "rx");
  210. char *ry_att = fz_xml_att(node, "ry");
  211. float cx = 0;
  212. float cy = 0;
  213. float rx = 0;
  214. float ry = 0;
  215. fz_path *path = NULL;
  216. fz_var(path);
  217. fz_try(ctx)
  218. {
  219. svg_begin_state(ctx, &local_state, inherit_state);
  220. svg_parse_common(ctx, doc, node, &local_state);
  221. if (cx_att) cx = svg_parse_length(cx_att, local_state.viewbox_w, local_state.fontsize);
  222. if (cy_att) cy = svg_parse_length(cy_att, local_state.viewbox_h, local_state.fontsize);
  223. if (rx_att) rx = svg_parse_length(rx_att, local_state.viewbox_w, local_state.fontsize);
  224. if (ry_att) ry = svg_parse_length(ry_att, local_state.viewbox_h, local_state.fontsize);
  225. if (rx > 0 && ry > 0)
  226. {
  227. path = fz_new_path(ctx);
  228. approx_circle(ctx, path, cx, cy, rx, ry);
  229. svg_draw_path(ctx, dev, doc, path, &local_state);
  230. }
  231. }
  232. fz_always(ctx)
  233. {
  234. fz_drop_path(ctx, path);
  235. svg_end_state(ctx, &local_state);
  236. }
  237. fz_catch(ctx)
  238. fz_rethrow(ctx);
  239. }
  240. static void
  241. svg_run_line(fz_context *ctx, fz_device *dev, svg_document *doc, fz_xml *node, const svg_state *inherit_state)
  242. {
  243. svg_state local_state;
  244. fz_path *path = NULL;
  245. char *x1_att = fz_xml_att(node, "x1");
  246. char *y1_att = fz_xml_att(node, "y1");
  247. char *x2_att = fz_xml_att(node, "x2");
  248. char *y2_att = fz_xml_att(node, "y2");
  249. float x1 = 0;
  250. float y1 = 0;
  251. float x2 = 0;
  252. float y2 = 0;
  253. fz_var(path);
  254. fz_try(ctx)
  255. {
  256. svg_begin_state(ctx, &local_state, inherit_state);
  257. svg_parse_common(ctx, doc, node, &local_state);
  258. if (x1_att) x1 = svg_parse_length(x1_att, local_state.viewbox_w, local_state.fontsize);
  259. if (y1_att) y1 = svg_parse_length(y1_att, local_state.viewbox_h, local_state.fontsize);
  260. if (x2_att) x2 = svg_parse_length(x2_att, local_state.viewbox_w, local_state.fontsize);
  261. if (y2_att) y2 = svg_parse_length(y2_att, local_state.viewbox_h, local_state.fontsize);
  262. if (local_state.stroke_is_set)
  263. {
  264. path = fz_new_path(ctx);
  265. fz_moveto(ctx, path, x1, y1);
  266. fz_lineto(ctx, path, x2, y2);
  267. svg_stroke(ctx, dev, doc, path, &local_state);
  268. }
  269. }
  270. fz_always(ctx)
  271. {
  272. fz_drop_path(ctx, path);
  273. svg_end_state(ctx, &local_state);
  274. }
  275. fz_catch(ctx)
  276. fz_rethrow(ctx);
  277. }
  278. static fz_path *
  279. svg_parse_polygon_imp(fz_context *ctx, svg_document *doc, fz_xml *node, int doclose)
  280. {
  281. fz_path *path;
  282. const char *str = fz_xml_att(node, "points");
  283. float number;
  284. float args[2];
  285. int nargs;
  286. int isfirst;
  287. if (!str)
  288. return NULL;
  289. isfirst = 1;
  290. nargs = 0;
  291. path = fz_new_path(ctx);
  292. fz_try(ctx)
  293. {
  294. while (*str)
  295. {
  296. while (svg_is_whitespace_or_comma(*str))
  297. str ++;
  298. if (svg_is_digit(*str))
  299. {
  300. str = svg_lex_number(&number, str);
  301. args[nargs++] = number;
  302. }
  303. if (nargs == 2)
  304. {
  305. if (isfirst)
  306. {
  307. fz_moveto(ctx, path, args[0], args[1]);
  308. isfirst = 0;
  309. }
  310. else
  311. {
  312. fz_lineto(ctx, path, args[0], args[1]);
  313. }
  314. nargs = 0;
  315. }
  316. }
  317. }
  318. fz_catch(ctx)
  319. {
  320. fz_drop_path(ctx, path);
  321. fz_rethrow(ctx);
  322. }
  323. return path;
  324. }
  325. static void
  326. svg_run_polyline(fz_context *ctx, fz_device *dev, svg_document *doc, fz_xml *node, const svg_state *inherit_state)
  327. {
  328. svg_state local_state;
  329. fz_path *path = NULL;
  330. fz_var(path);
  331. fz_try(ctx)
  332. {
  333. svg_begin_state(ctx, &local_state, inherit_state);
  334. svg_parse_common(ctx, doc, node, &local_state);
  335. if (local_state.stroke_is_set)
  336. {
  337. path = svg_parse_polygon_imp(ctx, doc, node, 0);
  338. svg_stroke(ctx, dev, doc, path, &local_state);
  339. }
  340. }
  341. fz_always(ctx)
  342. {
  343. fz_drop_path(ctx, path);
  344. svg_end_state(ctx, &local_state);
  345. }
  346. fz_catch(ctx)
  347. fz_rethrow(ctx);
  348. }
  349. static void
  350. svg_run_polygon(fz_context *ctx, fz_device *dev, svg_document *doc, fz_xml *node, const svg_state *inherit_state)
  351. {
  352. svg_state local_state;
  353. fz_path *path = NULL;
  354. fz_var(path);
  355. fz_try(ctx)
  356. {
  357. svg_begin_state(ctx, &local_state, inherit_state);
  358. svg_parse_common(ctx, doc, node, &local_state);
  359. path = svg_parse_polygon_imp(ctx, doc, node, 1);
  360. svg_draw_path(ctx, dev, doc, path, &local_state);
  361. }
  362. fz_always(ctx)
  363. {
  364. fz_drop_path(ctx, path);
  365. svg_end_state(ctx, &local_state);
  366. }
  367. fz_catch(ctx)
  368. fz_rethrow(ctx);
  369. }
  370. static void
  371. svg_add_arc_segment(fz_context *ctx, fz_path *path, fz_matrix mtx, float th0, float th1, int iscw)
  372. {
  373. float t, d;
  374. fz_point p;
  375. while (th1 < th0)
  376. th1 += FZ_PI * 2;
  377. d = FZ_PI / 180; /* 1-degree precision */
  378. if (iscw)
  379. {
  380. for (t = th0 + d; t < th1 - d/2; t += d)
  381. {
  382. p = fz_transform_point_xy(cosf(t), sinf(t), mtx);
  383. fz_lineto(ctx, path, p.x, p.y);
  384. }
  385. }
  386. else
  387. {
  388. th0 += FZ_PI * 2;
  389. for (t = th0 - d; t > th1 + d/2; t -= d)
  390. {
  391. p = fz_transform_point_xy(cosf(t), sinf(t), mtx);
  392. fz_lineto(ctx, path, p.x, p.y);
  393. }
  394. }
  395. }
  396. static float
  397. angle_between(const fz_point u, const fz_point v)
  398. {
  399. float det = u.x * v.y - u.y * v.x;
  400. float sign = (det < 0 ? -1 : 1);
  401. float magu = u.x * u.x + u.y * u.y;
  402. float magv = v.x * v.x + v.y * v.y;
  403. float udotv = u.x * v.x + u.y * v.y;
  404. float t = udotv / (magu * magv);
  405. /* guard against rounding errors when near |1| (where acos will return NaN) */
  406. if (t < -1) t = -1;
  407. if (t > 1) t = 1;
  408. return sign * acosf(t);
  409. }
  410. static void
  411. svg_add_arc(fz_context *ctx, fz_path *path,
  412. float size_x, float size_y, float rotation_angle,
  413. int is_large_arc, int is_clockwise,
  414. float point_x, float point_y)
  415. {
  416. fz_matrix rotmat, revmat;
  417. fz_matrix mtx;
  418. fz_point pt;
  419. float rx, ry;
  420. float x1, y1, x2, y2;
  421. float x1t, y1t;
  422. float cxt, cyt, cx, cy;
  423. float t1, t2, t3;
  424. float sign;
  425. float th1, dth;
  426. pt = fz_currentpoint(ctx, path);
  427. x1 = pt.x;
  428. y1 = pt.y;
  429. x2 = point_x;
  430. y2 = point_y;
  431. rx = size_x;
  432. ry = size_y;
  433. if (is_clockwise != is_large_arc)
  434. sign = 1;
  435. else
  436. sign = -1;
  437. rotmat = fz_rotate(rotation_angle);
  438. revmat = fz_rotate(-rotation_angle);
  439. /* http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes */
  440. /* Conversion from endpoint to center parameterization */
  441. /* F.6.6.1 -- ensure radii are positive and non-zero */
  442. rx = fabsf(rx);
  443. ry = fabsf(ry);
  444. if (rx < 0.001f || ry < 0.001f || (x1 == x2 && y1 == y2))
  445. {
  446. fz_lineto(ctx, path, x2, y2);
  447. return;
  448. }
  449. /* F.6.5.1 */
  450. pt.x = (x1 - x2) / 2;
  451. pt.y = (y1 - y2) / 2;
  452. pt = fz_transform_vector(pt, revmat);
  453. x1t = pt.x;
  454. y1t = pt.y;
  455. /* F.6.6.2 -- ensure radii are large enough */
  456. t1 = (x1t * x1t) / (rx * rx) + (y1t * y1t) / (ry * ry);
  457. if (t1 > 1)
  458. {
  459. rx = rx * sqrtf(t1);
  460. ry = ry * sqrtf(t1);
  461. }
  462. /* F.6.5.2 */
  463. t1 = (rx * rx * ry * ry) - (rx * rx * y1t * y1t) - (ry * ry * x1t * x1t);
  464. t2 = (rx * rx * y1t * y1t) + (ry * ry * x1t * x1t);
  465. t3 = t1 / t2;
  466. /* guard against rounding errors; sqrt of negative numbers is bad for your health */
  467. if (t3 < 0) t3 = 0;
  468. t3 = sqrtf(t3);
  469. cxt = sign * t3 * (rx * y1t) / ry;
  470. cyt = sign * t3 * -(ry * x1t) / rx;
  471. /* F.6.5.3 */
  472. pt.x = cxt;
  473. pt.y = cyt;
  474. pt = fz_transform_vector(pt, rotmat);
  475. cx = pt.x + (x1 + x2) / 2;
  476. cy = pt.y + (y1 + y2) / 2;
  477. /* F.6.5.4 */
  478. {
  479. fz_point coord1, coord2, coord3, coord4;
  480. coord1.x = 1;
  481. coord1.y = 0;
  482. coord2.x = (x1t - cxt) / rx;
  483. coord2.y = (y1t - cyt) / ry;
  484. coord3.x = (x1t - cxt) / rx;
  485. coord3.y = (y1t - cyt) / ry;
  486. coord4.x = (-x1t - cxt) / rx;
  487. coord4.y = (-y1t - cyt) / ry;
  488. th1 = angle_between(coord1, coord2);
  489. dth = angle_between(coord3, coord4);
  490. if (dth < 0 && !is_clockwise)
  491. dth += ((FZ_PI / 180) * 360);
  492. if (dth > 0 && is_clockwise)
  493. dth -= ((FZ_PI / 180) * 360);
  494. }
  495. mtx = fz_pre_scale(fz_pre_rotate(fz_translate(cx, cy), rotation_angle), rx, ry);
  496. svg_add_arc_segment(ctx, path, mtx, th1, th1 + dth, is_clockwise);
  497. fz_lineto(ctx, path, point_x, point_y);
  498. }
  499. static void
  500. svg_parse_path_data(fz_context *ctx, fz_path *path, const char *str)
  501. {
  502. fz_point p;
  503. float x1, y1, x2, y2;
  504. int cmd;
  505. float number;
  506. float args[7];
  507. int nargs;
  508. /* saved control point for smooth curves */
  509. int reset_smooth = 1;
  510. float smooth_x = 0.0f;
  511. float smooth_y = 0.0f;
  512. cmd = 0;
  513. nargs = 0;
  514. fz_moveto(ctx, path, 0.0f, 0.0f); /* for the case of opening 'm' */
  515. while (*str)
  516. {
  517. while (svg_is_whitespace_or_comma(*str))
  518. str ++;
  519. /* arcto flag arguments are 1-character 0 or 1 */
  520. if ((cmd == 'a' || cmd == 'A') && (nargs == 3 || nargs == 4) && (*str == '0' || *str == '1'))
  521. {
  522. args[nargs++] = *str++ - '0';
  523. }
  524. else if (svg_is_digit(*str))
  525. {
  526. str = svg_lex_number(&number, str);
  527. if (nargs == nelem(args))
  528. {
  529. fz_warn(ctx, "stack overflow in path data");
  530. return;
  531. }
  532. args[nargs++] = number;
  533. }
  534. else if (svg_is_alpha(*str))
  535. {
  536. if (nargs != 0)
  537. {
  538. fz_warn(ctx, "syntax error in path data (wrong number of parameters to '%c')", cmd);
  539. return;
  540. }
  541. cmd = *str++;
  542. }
  543. else if (*str == 0)
  544. {
  545. return;
  546. }
  547. else
  548. {
  549. fz_warn(ctx, "syntax error in path data: '%c'", *str);
  550. return;
  551. }
  552. if (reset_smooth)
  553. {
  554. smooth_x = 0.0f;
  555. smooth_y = 0.0f;
  556. }
  557. reset_smooth = 1;
  558. switch (cmd)
  559. {
  560. case 'M':
  561. if (nargs == 2)
  562. {
  563. fz_moveto(ctx, path, args[0], args[1]);
  564. nargs = 0;
  565. cmd = 'L'; /* implicit lineto after */
  566. }
  567. break;
  568. case 'm':
  569. if (nargs == 2)
  570. {
  571. p = fz_currentpoint(ctx, path);
  572. fz_moveto(ctx, path, p.x + args[0], p.y + args[1]);
  573. nargs = 0;
  574. cmd = 'l'; /* implicit lineto after */
  575. }
  576. break;
  577. case 'Z':
  578. case 'z':
  579. if (nargs == 0)
  580. {
  581. fz_closepath(ctx, path);
  582. }
  583. break;
  584. case 'L':
  585. if (nargs == 2)
  586. {
  587. fz_lineto(ctx, path, args[0], args[1]);
  588. nargs = 0;
  589. }
  590. break;
  591. case 'l':
  592. if (nargs == 2)
  593. {
  594. p = fz_currentpoint(ctx, path);
  595. fz_lineto(ctx, path, p.x + args[0], p.y + args[1]);
  596. nargs = 0;
  597. }
  598. break;
  599. case 'H':
  600. if (nargs == 1)
  601. {
  602. p = fz_currentpoint(ctx, path);
  603. fz_lineto(ctx, path, args[0], p.y);
  604. nargs = 0;
  605. }
  606. break;
  607. case 'h':
  608. if (nargs == 1)
  609. {
  610. p = fz_currentpoint(ctx, path);
  611. fz_lineto(ctx, path, p.x + args[0], p.y);
  612. nargs = 0;
  613. }
  614. break;
  615. case 'V':
  616. if (nargs == 1)
  617. {
  618. p = fz_currentpoint(ctx, path);
  619. fz_lineto(ctx, path, p.x, args[0]);
  620. nargs = 0;
  621. }
  622. break;
  623. case 'v':
  624. if (nargs == 1)
  625. {
  626. p = fz_currentpoint(ctx, path);
  627. fz_lineto(ctx, path, p.x, p.y + args[0]);
  628. nargs = 0;
  629. }
  630. break;
  631. case 'C':
  632. reset_smooth = 0;
  633. if (nargs == 6)
  634. {
  635. fz_curveto(ctx, path, args[0], args[1], args[2], args[3], args[4], args[5]);
  636. smooth_x = args[4] - args[2];
  637. smooth_y = args[5] - args[3];
  638. nargs = 0;
  639. }
  640. break;
  641. case 'c':
  642. reset_smooth = 0;
  643. if (nargs == 6)
  644. {
  645. p = fz_currentpoint(ctx, path);
  646. fz_curveto(ctx, path,
  647. p.x + args[0], p.y + args[1],
  648. p.x + args[2], p.y + args[3],
  649. p.x + args[4], p.y + args[5]);
  650. smooth_x = args[4] - args[2];
  651. smooth_y = args[5] - args[3];
  652. nargs = 0;
  653. }
  654. break;
  655. case 'S':
  656. reset_smooth = 0;
  657. if (nargs == 4)
  658. {
  659. p = fz_currentpoint(ctx, path);
  660. fz_curveto(ctx, path,
  661. p.x + smooth_x, p.y + smooth_y,
  662. args[0], args[1],
  663. args[2], args[3]);
  664. smooth_x = args[2] - args[0];
  665. smooth_y = args[3] - args[1];
  666. nargs = 0;
  667. }
  668. break;
  669. case 's':
  670. reset_smooth = 0;
  671. if (nargs == 4)
  672. {
  673. p = fz_currentpoint(ctx, path);
  674. fz_curveto(ctx, path,
  675. p.x + smooth_x, p.y + smooth_y,
  676. p.x + args[0], p.y + args[1],
  677. p.x + args[2], p.y + args[3]);
  678. smooth_x = args[2] - args[0];
  679. smooth_y = args[3] - args[1];
  680. nargs = 0;
  681. }
  682. break;
  683. case 'Q':
  684. reset_smooth = 0;
  685. if (nargs == 4)
  686. {
  687. p = fz_currentpoint(ctx, path);
  688. x1 = args[0];
  689. y1 = args[1];
  690. x2 = args[2];
  691. y2 = args[3];
  692. fz_curveto(ctx, path,
  693. (p.x + 2 * x1) / 3, (p.y + 2 * y1) / 3,
  694. (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3,
  695. x2, y2);
  696. smooth_x = x2 - x1;
  697. smooth_y = y2 - y1;
  698. nargs = 0;
  699. }
  700. break;
  701. case 'q':
  702. reset_smooth = 0;
  703. if (nargs == 4)
  704. {
  705. p = fz_currentpoint(ctx, path);
  706. x1 = args[0] + p.x;
  707. y1 = args[1] + p.y;
  708. x2 = args[2] + p.x;
  709. y2 = args[3] + p.y;
  710. fz_curveto(ctx, path,
  711. (p.x + 2 * x1) / 3, (p.y + 2 * y1) / 3,
  712. (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3,
  713. x2, y2);
  714. smooth_x = x2 - x1;
  715. smooth_y = y2 - y1;
  716. nargs = 0;
  717. }
  718. break;
  719. case 'T':
  720. reset_smooth = 0;
  721. if (nargs == 2)
  722. {
  723. p = fz_currentpoint(ctx, path);
  724. x1 = p.x + smooth_x;
  725. y1 = p.y + smooth_y;
  726. x2 = args[0];
  727. y2 = args[1];
  728. fz_curveto(ctx, path,
  729. (p.x + 2 * x1) / 3, (p.y + 2 * y1) / 3,
  730. (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3,
  731. x2, y2);
  732. smooth_x = x2 - x1;
  733. smooth_y = y2 - y1;
  734. nargs = 0;
  735. }
  736. break;
  737. case 't':
  738. reset_smooth = 0;
  739. if (nargs == 2)
  740. {
  741. p = fz_currentpoint(ctx, path);
  742. x1 = p.x + smooth_x;
  743. y1 = p.y + smooth_y;
  744. x2 = args[0] + p.x;
  745. y2 = args[1] + p.y;
  746. fz_curveto(ctx, path,
  747. (p.x + 2 * x1) / 3, (p.y + 2 * y1) / 3,
  748. (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3,
  749. x2, y2);
  750. smooth_x = x2 - x1;
  751. smooth_y = y2 - y1;
  752. nargs = 0;
  753. }
  754. break;
  755. case 'A':
  756. if (nargs == 7)
  757. {
  758. svg_add_arc(ctx, path, args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
  759. nargs = 0;
  760. }
  761. break;
  762. case 'a':
  763. if (nargs == 7)
  764. {
  765. p = fz_currentpoint(ctx, path);
  766. svg_add_arc(ctx, path, args[0], args[1], args[2], args[3], args[4], args[5] + p.x, args[6] + p.y);
  767. nargs = 0;
  768. }
  769. break;
  770. case 0:
  771. if (nargs != 0)
  772. {
  773. fz_warn(ctx, "path data must begin with a command");
  774. return;
  775. }
  776. break;
  777. default:
  778. fz_warn(ctx, "unrecognized command in path data: '%c'", cmd);
  779. return;
  780. }
  781. }
  782. }
  783. static void
  784. svg_run_path(fz_context *ctx, fz_device *dev, svg_document *doc, fz_xml *node, const svg_state *inherit_state)
  785. {
  786. svg_state local_state;
  787. fz_path *path = NULL;
  788. const char *d_att = fz_xml_att(node, "d");
  789. /* unused: char *path_length_att = fz_xml_att(node, "pathLength"); */
  790. fz_var(path);
  791. fz_try(ctx)
  792. {
  793. svg_begin_state(ctx, &local_state, inherit_state);
  794. svg_parse_common(ctx, doc, node, &local_state);
  795. if (d_att)
  796. {
  797. path = fz_new_path(ctx);
  798. svg_parse_path_data(ctx, path, d_att);
  799. svg_draw_path(ctx, dev, doc, path, &local_state);
  800. }
  801. }
  802. fz_always(ctx)
  803. {
  804. fz_drop_path(ctx, path);
  805. svg_end_state(ctx, &local_state);
  806. }
  807. fz_catch(ctx)
  808. fz_rethrow(ctx);
  809. }
  810. /* svg, symbol, image, foreignObject establish new viewports */
  811. static void
  812. svg_parse_viewport(fz_context *ctx, svg_document *doc, fz_xml *node, svg_state *state)
  813. {
  814. char *w_att = fz_xml_att(node, "width");
  815. char *h_att = fz_xml_att(node, "height");
  816. if (w_att)
  817. state->viewport_w = svg_parse_length(w_att, state->viewbox_w, state->fontsize);
  818. if (h_att)
  819. state->viewport_h = svg_parse_length(h_att, state->viewbox_h, state->fontsize);
  820. }
  821. static void
  822. svg_lex_viewbox(const char *s, float *x, float *y, float *w, float *h)
  823. {
  824. *x = *y = *w = *h = 0;
  825. while (svg_is_whitespace_or_comma(*s)) ++s;
  826. if (svg_is_digit(*s)) s = svg_lex_number(x, s);
  827. while (svg_is_whitespace_or_comma(*s)) ++s;
  828. if (svg_is_digit(*s)) s = svg_lex_number(y, s);
  829. while (svg_is_whitespace_or_comma(*s)) ++s;
  830. if (svg_is_digit(*s)) s = svg_lex_number(w, s);
  831. while (svg_is_whitespace_or_comma(*s)) ++s;
  832. if (svg_is_digit(*s)) s = svg_lex_number(h, s);
  833. }
  834. static int
  835. svg_parse_preserve_aspect_ratio(const char *att, int *x, int *y)
  836. {
  837. *x = *y = 1;
  838. if (strstr(att, "none")) return 0;
  839. if (strstr(att, "xMin")) *x = 0;
  840. if (strstr(att, "xMid")) *x = 1;
  841. if (strstr(att, "xMax")) *x = 2;
  842. if (strstr(att, "YMin")) *y = 0;
  843. if (strstr(att, "YMid")) *y = 1;
  844. if (strstr(att, "YMax")) *y = 2;
  845. return 1;
  846. }
  847. /* svg, symbol, image, foreignObject plus marker, pattern, view can use viewBox to set the transform */
  848. static void
  849. svg_parse_viewbox(fz_context *ctx, svg_document *doc, fz_xml *node, svg_state *state)
  850. {
  851. char *viewbox_att = fz_xml_att(node, "viewBox");
  852. char *preserve_att = fz_xml_att(node, "preserveAspectRatio");
  853. if (viewbox_att)
  854. {
  855. /* scale and translate to fit [minx miny minx+w miny+h] to [0 0 viewport.w viewport.h] */
  856. float min_x, min_y, box_w, box_h, sx, sy;
  857. int align_x=1, align_y=1, preserve=1;
  858. float pad_x=0, pad_y=0;
  859. svg_lex_viewbox(viewbox_att, &min_x, &min_y, &box_w, &box_h);
  860. sx = state->viewport_w / box_w;
  861. sy = state->viewport_h / box_h;
  862. if (preserve_att)
  863. preserve = svg_parse_preserve_aspect_ratio(preserve_att, &align_x, &align_y);
  864. if (preserve)
  865. {
  866. sx = sy = fz_min(sx, sy);
  867. if (align_x == 1) pad_x = (box_w * sx - state->viewport_w) / 2;
  868. if (align_x == 2) pad_x = (box_w * sx - state->viewport_w);
  869. if (align_y == 1) pad_y = (box_h * sy - state->viewport_h) / 2;
  870. if (align_y == 2) pad_y = (box_h * sy - state->viewport_h);
  871. state->transform = fz_concat(fz_translate(-pad_x, -pad_y), state->transform);
  872. }
  873. state->transform = fz_concat(fz_scale(sx, sy), state->transform);
  874. state->transform = fz_concat(fz_translate(-min_x, -min_y), state->transform);
  875. state->viewbox_w = box_w;
  876. state->viewbox_h = box_h;
  877. state->viewbox_size = sqrtf(box_w*box_w + box_h*box_h) / sqrtf(2);
  878. }
  879. }
  880. static const char *linecap_table[] = { "butt", "round", "square" };
  881. static const char *linejoin_table[] = { "miter", "round", "bevel" };
  882. /* parse transform and presentation attributes */
  883. static void
  884. svg_parse_common(fz_context *ctx, svg_document *doc, fz_xml *node, svg_state *state)
  885. {
  886. fz_stroke_state *stroke = state->stroke;
  887. char *transform_att = fz_xml_att(node, "transform");
  888. char *font_size_att = fz_xml_att(node, "font-size");
  889. char *style_att = fz_xml_att(node, "style");
  890. // TODO: clip, clip-path, clip-rule
  891. char *opacity_att = fz_xml_att(node, "opacity");
  892. char *fill_att = fz_xml_att(node, "fill");
  893. char *fill_rule_att = fz_xml_att(node, "fill-rule");
  894. char *fill_opacity_att = fz_xml_att(node, "fill-opacity");
  895. char *stroke_att = fz_xml_att(node, "stroke");
  896. char *stroke_opacity_att = fz_xml_att(node, "stroke-opacity");
  897. char *stroke_width_att = fz_xml_att(node, "stroke-width");
  898. char *stroke_linecap_att = fz_xml_att(node, "stroke-linecap");
  899. char *stroke_linejoin_att = fz_xml_att(node, "stroke-linejoin");
  900. char *stroke_miterlimit_att = fz_xml_att(node, "stroke-miterlimit");
  901. // TODO: stroke-dasharray, stroke-dashoffset
  902. // TODO: marker, marker-start, marker-mid, marker-end
  903. // TODO: overflow
  904. // TODO: mask
  905. /* Dirty hack scans of CSS style */
  906. if (style_att)
  907. {
  908. svg_parse_color_from_style(ctx, doc, style_att,
  909. &state->fill_is_set, state->fill_color,
  910. &state->stroke_is_set, state->stroke_color);
  911. }
  912. if (transform_att)
  913. {
  914. state->transform = svg_parse_transform(ctx, doc, transform_att, state->transform);
  915. }
  916. if (font_size_att)
  917. {
  918. state->fontsize = svg_parse_length(font_size_att, state->fontsize, state->fontsize);
  919. }
  920. else
  921. {
  922. state->fontsize = svg_parse_number_from_style(ctx, doc, style_att, "font-size", state->fontsize);
  923. }
  924. if (opacity_att)
  925. {
  926. state->opacity = svg_parse_number(opacity_att, 0, 1, state->opacity);
  927. }
  928. if (fill_att)
  929. {
  930. if (!strcmp(fill_att, "none"))
  931. {
  932. state->fill_is_set = 0;
  933. }
  934. else
  935. {
  936. state->fill_is_set = 1;
  937. svg_parse_color(ctx, doc, fill_att, state->fill_color);
  938. }
  939. }
  940. if (fill_opacity_att)
  941. state->fill_opacity = svg_parse_number(fill_opacity_att, 0, 1, state->fill_opacity);
  942. if (fill_rule_att)
  943. {
  944. if (!strcmp(fill_rule_att, "nonzero"))
  945. state->fill_rule = 0;
  946. if (!strcmp(fill_rule_att, "evenodd"))
  947. state->fill_rule = 1;
  948. }
  949. if (stroke_att)
  950. {
  951. if (!strcmp(stroke_att, "none"))
  952. {
  953. state->stroke_is_set = 0;
  954. }
  955. else
  956. {
  957. state->stroke_is_set = 1;
  958. svg_parse_color(ctx, doc, stroke_att, state->stroke_color);
  959. }
  960. }
  961. if (stroke_opacity_att)
  962. state->stroke_opacity = svg_parse_number(stroke_opacity_att, 0, 1, state->stroke_opacity);
  963. if (stroke_width_att)
  964. {
  965. if (!strcmp(stroke_width_att, "inherit"))
  966. ;
  967. else
  968. stroke->linewidth = svg_parse_length(stroke_width_att, state->viewbox_size, state->fontsize);
  969. }
  970. else
  971. {
  972. stroke->linewidth = svg_parse_number_from_style(ctx, doc, style_att, "stroke-width", state->stroke->linewidth);
  973. }
  974. if (stroke_linecap_att)
  975. {
  976. if (!strcmp(stroke_linecap_att, "butt"))
  977. stroke->start_cap = FZ_LINECAP_BUTT;
  978. if (!strcmp(stroke_linecap_att, "round"))
  979. stroke->start_cap = FZ_LINECAP_ROUND;
  980. if (!strcmp(stroke_linecap_att, "square"))
  981. stroke->start_cap = FZ_LINECAP_SQUARE;
  982. }
  983. else
  984. {
  985. stroke->start_cap = svg_parse_enum_from_style(ctx, doc, style_att, "stroke-linecap",
  986. nelem(linecap_table), linecap_table, FZ_LINECAP_BUTT);
  987. }
  988. stroke->dash_cap = stroke->start_cap;
  989. stroke->end_cap = stroke->start_cap;
  990. if (stroke_linejoin_att)
  991. {
  992. if (!strcmp(stroke_linejoin_att, "miter"))
  993. stroke->linejoin = FZ_LINEJOIN_MITER;
  994. if (!strcmp(stroke_linejoin_att, "round"))
  995. stroke->linejoin = FZ_LINEJOIN_ROUND;
  996. if (!strcmp(stroke_linejoin_att, "bevel"))
  997. stroke->linejoin = FZ_LINEJOIN_BEVEL;
  998. }
  999. else
  1000. {
  1001. stroke->linejoin = svg_parse_enum_from_style(ctx, doc, style_att, "stroke-linejoin",
  1002. nelem(linejoin_table), linejoin_table, FZ_LINEJOIN_MITER);
  1003. }
  1004. if (stroke_miterlimit_att)
  1005. {
  1006. if (!strcmp(stroke_miterlimit_att, "inherit"))
  1007. ;
  1008. else
  1009. stroke->miterlimit = svg_parse_length(stroke_miterlimit_att, state->viewbox_size, state->fontsize);
  1010. }
  1011. else
  1012. {
  1013. stroke->miterlimit = svg_parse_number_from_style(ctx, doc, style_att, "stroke-miterlimit", state->stroke->miterlimit);
  1014. }
  1015. }
  1016. static void
  1017. svg_parse_font_attributes(fz_context *ctx, svg_document *doc, fz_xml *node, svg_state *state, char *buf, int buf_size)
  1018. {
  1019. char *style_att = fz_xml_att(node, "style");
  1020. char *font_family_att = fz_xml_att(node, "font-family");
  1021. char *font_weight_att = fz_xml_att(node, "font-weight");
  1022. char *font_style_att = fz_xml_att(node, "font-style");
  1023. char *text_anchor_att = fz_xml_att(node, "text-anchor");
  1024. if (font_family_att)
  1025. fz_strlcpy(buf, font_family_att, buf_size);
  1026. else
  1027. svg_parse_string_from_style(ctx, doc, style_att, "font-family", buf, buf_size, state->font_family);
  1028. state->font_family = buf;
  1029. if (font_weight_att)
  1030. {
  1031. state->is_bold = atoi(font_weight_att) > 400;
  1032. if (!strcmp(font_weight_att, "bold")) state->is_bold = 1;
  1033. if (!strcmp(font_weight_att, "bolder")) state->is_bold = 1;
  1034. }
  1035. else
  1036. {
  1037. static const char *is_bold_table[] = {
  1038. "normal", "100", "200", "300", "400", "bold", "bolder", "500", "600", "700", "800", "900"
  1039. };
  1040. state->is_bold = svg_parse_enum_from_style(ctx, doc, style_att, "font-weight",
  1041. nelem(is_bold_table), is_bold_table, state->is_bold ? 5 : 0) >= 5;
  1042. }
  1043. if (font_style_att)
  1044. {
  1045. state->is_italic = 0;
  1046. if (!strcmp(font_style_att, "italic")) state->is_italic = 1;
  1047. if (!strcmp(font_style_att, "oblique")) state->is_italic = 1;
  1048. }
  1049. else
  1050. {
  1051. static const char *is_italic_table[] = {
  1052. "normal", "italic", "oblique"
  1053. };
  1054. state->is_italic = svg_parse_enum_from_style(ctx, doc, style_att, "font-style",
  1055. nelem(is_italic_table), is_italic_table, state->is_italic) >= 1;
  1056. }
  1057. if (text_anchor_att)
  1058. {
  1059. state->text_anchor = 0;
  1060. if (!strcmp(text_anchor_att, "middle")) state->text_anchor = 1;
  1061. if (!strcmp(text_anchor_att, "end")) state->text_anchor = 2;
  1062. }
  1063. else
  1064. {
  1065. static const char *text_anchor_table[] = {
  1066. "start", "middle", "end"
  1067. };
  1068. state->text_anchor = svg_parse_enum_from_style(ctx, doc, style_att, "text-anchor",
  1069. nelem(text_anchor_table), text_anchor_table, state->text_anchor);
  1070. }
  1071. }
  1072. static void
  1073. svg_run_svg(fz_context *ctx, fz_device *dev, svg_document *doc, fz_xml *root, const svg_state *inherit_state)
  1074. {
  1075. svg_state local_state;
  1076. fz_xml *node;
  1077. char *w_att = fz_xml_att(root, "width");
  1078. char *h_att = fz_xml_att(root, "height");
  1079. char *viewbox_att = fz_xml_att(root, "viewBox");
  1080. fz_try(ctx)
  1081. {
  1082. svg_begin_state(ctx, &local_state, inherit_state);
  1083. /* get default viewport from viewBox if width and/or height is missing */
  1084. if (viewbox_att && (!w_att || !h_att))
  1085. {
  1086. float x, y;
  1087. svg_lex_viewbox(viewbox_att, &x, &y, &local_state.viewbox_w, &local_state.viewbox_h);
  1088. if (!w_att) local_state.viewport_w = local_state.viewbox_w;
  1089. if (!h_att) local_state.viewport_h = local_state.viewbox_h;
  1090. }
  1091. svg_parse_viewport(ctx, doc, root, &local_state);
  1092. svg_parse_viewbox(ctx, doc, root, &local_state);
  1093. svg_parse_common(ctx, doc, root, &local_state);
  1094. for (node = fz_xml_down(root); node; node = fz_xml_next(node))
  1095. svg_run_element(ctx, dev, doc, node, &local_state);
  1096. }
  1097. fz_always(ctx)
  1098. svg_end_state(ctx, &local_state);
  1099. fz_catch(ctx)
  1100. fz_rethrow(ctx);
  1101. }
  1102. static void
  1103. svg_run_g(fz_context *ctx, fz_device *dev, svg_document *doc, fz_xml *root, const svg_state *inherit_state)
  1104. {
  1105. svg_state local_state;
  1106. fz_xml *node;
  1107. fz_try(ctx)
  1108. {
  1109. svg_begin_state(ctx, &local_state, inherit_state);
  1110. svg_parse_common(ctx, doc, root, &local_state);
  1111. for (node = fz_xml_down(root); node; node = fz_xml_next(node))
  1112. svg_run_element(ctx, dev, doc, node, &local_state);
  1113. }
  1114. fz_always(ctx)
  1115. svg_end_state(ctx, &local_state);
  1116. fz_catch(ctx)
  1117. fz_rethrow(ctx);
  1118. }
  1119. static void
  1120. svg_run_use_symbol(fz_context *ctx, fz_device *dev, svg_document *doc, fz_xml *use, fz_xml *symbol, const svg_state *inherit_state)
  1121. {
  1122. svg_state local_state;
  1123. fz_xml *node;
  1124. fz_try(ctx)
  1125. {
  1126. svg_begin_state(ctx, &local_state, inherit_state);
  1127. svg_parse_viewport(ctx, doc, use, &local_state);
  1128. svg_parse_viewbox(ctx, doc, use, &local_state);
  1129. for (node = fz_xml_down(symbol); node; node = fz_xml_next(node))
  1130. svg_run_element(ctx, dev, doc, node, &local_state);
  1131. }
  1132. fz_always(ctx)
  1133. svg_end_state(ctx, &local_state);
  1134. fz_catch(ctx)
  1135. fz_rethrow(ctx);
  1136. }
  1137. static int
  1138. is_use_cycle(fz_xml *use, fz_xml *symbol)
  1139. {
  1140. /* If "use" is a direct child of "symbol", we have a recursive symbol/use definition! */
  1141. while (use)
  1142. {
  1143. if (use == symbol)
  1144. return 1;
  1145. use = fz_xml_up(use);
  1146. }
  1147. return 0;
  1148. }
  1149. static void
  1150. svg_run_use(fz_context *ctx, fz_device *dev, svg_document *doc, fz_xml *root, const svg_state *inherit_state)
  1151. {
  1152. svg_state local_state;
  1153. char *href_att = fz_xml_att_alt(root, "xlink:href", "href");
  1154. char *x_att = fz_xml_att(root, "x");
  1155. char *y_att = fz_xml_att(root, "y");
  1156. fz_xml *linked = NULL;
  1157. float x = 0;
  1158. float y = 0;
  1159. fz_try(ctx)
  1160. {
  1161. svg_begin_state(ctx, &local_state, inherit_state);
  1162. if (++local_state.use_depth > MAX_USE_DEPTH)
  1163. {
  1164. fz_warn(ctx, "svg: too much recursion");
  1165. break;
  1166. }
  1167. svg_parse_common(ctx, doc, root, &local_state);
  1168. if (x_att) x = svg_parse_length(x_att, local_state.viewbox_w, local_state.fontsize);
  1169. if (y_att) y = svg_parse_length(y_att, local_state.viewbox_h, local_state.fontsize);
  1170. local_state.transform = fz_concat(fz_translate(x, y), local_state.transform);
  1171. if (href_att && href_att[0] == '#')
  1172. {
  1173. linked = fz_tree_lookup(ctx, doc->idmap, href_att + 1);
  1174. if (linked)
  1175. {
  1176. if (is_use_cycle(root, linked))
  1177. fz_warn(ctx, "svg: cyclic <use> reference");
  1178. if (fz_xml_is_tag(linked, "symbol"))
  1179. svg_run_use_symbol(ctx, dev, doc, root, linked, &local_state);
  1180. else
  1181. svg_run_element(ctx, dev, doc, linked, &local_state);
  1182. }
  1183. else
  1184. {
  1185. fz_warn(ctx, "svg: cannot find linked symbol");
  1186. }
  1187. }
  1188. else
  1189. {
  1190. fz_warn(ctx, "svg: cannot find linked symbol");
  1191. }
  1192. }
  1193. fz_always(ctx)
  1194. svg_end_state(ctx, &local_state);
  1195. fz_catch(ctx)
  1196. fz_rethrow(ctx);
  1197. }
  1198. static void
  1199. svg_run_image(fz_context *ctx, fz_device *dev, svg_document *doc, fz_xml *root, const svg_state *inherit_state)
  1200. {
  1201. svg_state local_state;
  1202. float x=0, y=0, w=0, h=0;
  1203. const char *data;
  1204. static const char *jpeg_uri = "data:image/jpeg;base64,";
  1205. static const char *png_uri = "data:image/png;base64,";
  1206. char *href_att = fz_xml_att_alt(root, "xlink:href", "href");
  1207. char *x_att = fz_xml_att(root, "x");
  1208. char *y_att = fz_xml_att(root, "y");
  1209. char *w_att = fz_xml_att(root, "width");
  1210. char *h_att = fz_xml_att(root, "height");
  1211. fz_try(ctx)
  1212. {
  1213. svg_begin_state(ctx, &local_state, inherit_state);
  1214. svg_parse_common(ctx, doc, root, &local_state);
  1215. if (x_att) x = svg_parse_length(x_att, local_state.viewbox_w, local_state.fontsize);
  1216. if (y_att) y = svg_parse_length(y_att, local_state.viewbox_h, local_state.fontsize);
  1217. if (w_att) w = svg_parse_length(w_att, local_state.viewbox_w, local_state.fontsize);
  1218. if (h_att) h = svg_parse_length(h_att, local_state.viewbox_h, local_state.fontsize);
  1219. if (w <= 0 || h <= 0)
  1220. break; // out of try-catch
  1221. if (!href_att)
  1222. break; // out of try-catch
  1223. local_state.transform = fz_concat(fz_translate(x, y), local_state.transform);
  1224. local_state.transform = fz_concat(fz_scale(w, h), local_state.transform);
  1225. if (!strncmp(href_att, jpeg_uri, strlen(jpeg_uri)))
  1226. data = href_att + strlen(jpeg_uri);
  1227. else if (!strncmp(href_att, png_uri, strlen(png_uri)))
  1228. data = href_att + strlen(png_uri);
  1229. else
  1230. data = NULL;
  1231. if (data)
  1232. {
  1233. fz_image *img = NULL;
  1234. fz_buffer *buf;
  1235. fz_var(img);
  1236. buf = fz_new_buffer_from_base64(ctx, data, 0);
  1237. fz_try(ctx)
  1238. {
  1239. fz_matrix orient;
  1240. img = fz_new_image_from_buffer(ctx, buf);
  1241. orient = fz_image_orientation_matrix(ctx, img);
  1242. local_state.transform = fz_concat(orient, local_state.transform);
  1243. fz_fill_image(ctx, dev, img, local_state.transform, 1, fz_default_color_params);
  1244. }
  1245. fz_always(ctx)
  1246. {
  1247. fz_drop_buffer(ctx, buf);
  1248. fz_drop_image(ctx, img);
  1249. }
  1250. fz_catch(ctx)
  1251. {
  1252. fz_rethrow_if(ctx, FZ_ERROR_SYSTEM);
  1253. fz_report_error(ctx);
  1254. fz_warn(ctx, "svg: ignoring embedded image '%s'", href_att);
  1255. }
  1256. }
  1257. else if (doc->zip)
  1258. {
  1259. char path[2048];
  1260. fz_buffer *buf = NULL;
  1261. fz_image *img = NULL;
  1262. fz_var(buf);
  1263. fz_var(img);
  1264. fz_strlcpy(path, doc->base_uri, sizeof path);
  1265. fz_strlcat(path, "/", sizeof path);
  1266. fz_strlcat(path, href_att, sizeof path);
  1267. fz_urldecode(path);
  1268. fz_try(ctx)
  1269. {
  1270. fz_matrix orient;
  1271. buf = fz_read_archive_entry(ctx, doc->zip, path);
  1272. img = fz_new_image_from_buffer(ctx, buf);
  1273. orient = fz_image_orientation_matrix(ctx, img);
  1274. local_state.transform = fz_concat(orient, local_state.transform);
  1275. fz_fill_image(ctx, dev, img, local_state.transform, 1, fz_default_color_params);
  1276. }
  1277. fz_always(ctx)
  1278. {
  1279. fz_drop_buffer(ctx, buf);
  1280. fz_drop_image(ctx, img);
  1281. }
  1282. fz_catch(ctx)
  1283. {
  1284. fz_rethrow_if(ctx, FZ_ERROR_SYSTEM);
  1285. fz_report_error(ctx);
  1286. fz_warn(ctx, "svg: ignoring external image '%s'", href_att);
  1287. }
  1288. }
  1289. else
  1290. {
  1291. fz_warn(ctx, "svg: ignoring external image '%s'", href_att);
  1292. }
  1293. }
  1294. fz_always(ctx)
  1295. svg_end_state(ctx, &local_state);
  1296. fz_catch(ctx)
  1297. fz_rethrow(ctx);
  1298. }
  1299. static fz_font *
  1300. svg_load_font(fz_context *ctx, const svg_state *state)
  1301. {
  1302. int bold = state->is_bold;
  1303. int italic = state->is_italic;
  1304. int mono = 0;
  1305. int serif = 1;
  1306. /* scan font-family property for common fallback names */
  1307. if (!mono && strstr(state->font_family, "monospace")) mono = 1;
  1308. if (!mono && strstr(state->font_family, "Courier")) mono = 1;
  1309. if (serif && strstr(state->font_family, "sans-serif")) serif = 0;
  1310. if (serif && strstr(state->font_family, "Arial")) serif = 0;
  1311. if (serif && strstr(state->font_family, "Helvetica")) serif = 0;
  1312. if (mono) {
  1313. if (bold) {
  1314. if (italic) return fz_new_base14_font(ctx, "Courier-BoldOblique");
  1315. else return fz_new_base14_font(ctx, "Courier-Bold");
  1316. } else {
  1317. if (italic) return fz_new_base14_font(ctx, "Courier-Oblique");
  1318. else return fz_new_base14_font(ctx, "Courier");
  1319. }
  1320. } else if (serif) {
  1321. if (bold) {
  1322. if (italic) return fz_new_base14_font(ctx, "Times-BoldItalic");
  1323. else return fz_new_base14_font(ctx, "Times-Bold");
  1324. } else {
  1325. if (italic) return fz_new_base14_font(ctx, "Times-Italic");
  1326. else return fz_new_base14_font(ctx, "Times-Roman");
  1327. }
  1328. } else {
  1329. if (bold) {
  1330. if (italic) return fz_new_base14_font(ctx, "Helvetica-BoldOblique");
  1331. else return fz_new_base14_font(ctx, "Helvetica-Bold");
  1332. } else {
  1333. if (italic) return fz_new_base14_font(ctx, "Helvetica-Oblique");
  1334. else return fz_new_base14_font(ctx, "Helvetica");
  1335. }
  1336. }
  1337. }
  1338. static fz_matrix
  1339. svg_run_text_string(fz_context *ctx, fz_device *dev, fz_matrix trm, const char *s, const svg_state *state)
  1340. {
  1341. fz_font *font = NULL;
  1342. fz_text *text = NULL;
  1343. fz_var(font);
  1344. fz_var(text);
  1345. fz_try(ctx)
  1346. {
  1347. font = svg_load_font(ctx, state);
  1348. text = fz_new_text(ctx);
  1349. if (state->text_anchor > 0)
  1350. {
  1351. fz_matrix adv = fz_measure_string(ctx, font, trm, s, 0, 0, FZ_BIDI_LTR, FZ_LANG_UNSET);
  1352. if (state->text_anchor == 1)
  1353. trm.e -= (adv.e - trm.e) / 2;
  1354. else if (state->text_anchor == 2)
  1355. trm.e -= (adv.e - trm.e);
  1356. }
  1357. trm = fz_show_string(ctx, text, font, trm, s, 0, 0, FZ_BIDI_LTR, FZ_LANG_UNSET);
  1358. if (state->fill_is_set)
  1359. fz_fill_text(ctx, dev, text,
  1360. state->transform,
  1361. fz_device_rgb(ctx), state->fill_color,
  1362. state->opacity,
  1363. fz_default_color_params);
  1364. if (state->stroke_is_set)
  1365. fz_stroke_text(ctx, dev, text,
  1366. state->stroke,
  1367. state->transform,
  1368. fz_device_rgb(ctx), state->stroke_color,
  1369. state->opacity,
  1370. fz_default_color_params);
  1371. if (!state->fill_is_set && !state->stroke_is_set)
  1372. fz_ignore_text(ctx, dev, text, state->transform);
  1373. }
  1374. fz_always(ctx)
  1375. {
  1376. fz_drop_text(ctx, text);
  1377. fz_drop_font(ctx, font);
  1378. }
  1379. fz_catch(ctx)
  1380. {
  1381. fz_rethrow(ctx);
  1382. }
  1383. return trm;
  1384. }
  1385. static void
  1386. svg_collapse_whitespace(char *start, int is_first, int is_last)
  1387. {
  1388. int c, last_c = (is_first ? ' ' : 0);
  1389. char *s, *p;
  1390. s = p = start;
  1391. while ((c = *s++) != 0)
  1392. {
  1393. if (c == '\n' || c == '\r')
  1394. continue;
  1395. if (c == '\t')
  1396. c = ' ';
  1397. if (c == ' ' && last_c == ' ')
  1398. continue;
  1399. *p++ = last_c = c;
  1400. }
  1401. if (is_last && p > start && p[-1] == ' ')
  1402. --p;
  1403. *p = 0;
  1404. }
  1405. static fz_matrix
  1406. svg_run_text(fz_context *ctx, fz_device *dev, svg_document *doc, fz_xml *root, const svg_state *inherit_state,
  1407. float x, float y, int is_first, int is_last)
  1408. {
  1409. svg_state local_state;
  1410. char font_family[100];
  1411. fz_xml *node;
  1412. fz_matrix trm;
  1413. int cif, cil;
  1414. char *text;
  1415. char *x_att = fz_xml_att(root, "x");
  1416. char *y_att = fz_xml_att(root, "y");
  1417. char *dx_att = fz_xml_att(root, "dx");
  1418. char *dy_att = fz_xml_att(root, "dy");
  1419. fz_try(ctx)
  1420. {
  1421. svg_begin_state(ctx, &local_state, inherit_state);
  1422. svg_parse_common(ctx, doc, root, &local_state);
  1423. svg_parse_font_attributes(ctx, doc, root, &local_state, font_family, sizeof font_family);
  1424. trm = fz_scale(local_state.fontsize, -local_state.fontsize);
  1425. trm.e = x;
  1426. trm.f = y;
  1427. if (x_att) trm.e = svg_parse_length(x_att, local_state.viewbox_w, local_state.fontsize);
  1428. if (y_att) trm.f = svg_parse_length(y_att, local_state.viewbox_h, local_state.fontsize);
  1429. if (dx_att) trm.e += svg_parse_length(dx_att, local_state.viewbox_w, local_state.fontsize);
  1430. if (dy_att) trm.f += svg_parse_length(dy_att, local_state.viewbox_h, local_state.fontsize);
  1431. cif = is_first;
  1432. for (node = fz_xml_down(root); node; node = fz_xml_next(node))
  1433. {
  1434. cil = is_last && !fz_xml_next(node);
  1435. text = fz_xml_text(node);
  1436. if (text)
  1437. {
  1438. svg_collapse_whitespace(text, cif, cil);
  1439. trm = svg_run_text_string(ctx, dev, trm, text, &local_state);
  1440. }
  1441. else if (fz_xml_is_tag(node, "tspan"))
  1442. trm = svg_run_text(ctx, dev, doc, node, &local_state, trm.e, trm.f, cif, cil);
  1443. else if (fz_xml_is_tag(node, "textPath"))
  1444. trm = svg_run_text(ctx, dev, doc, node, &local_state, trm.e, trm.f, cif, cil);
  1445. cif = 0;
  1446. }
  1447. }
  1448. fz_always(ctx)
  1449. svg_end_state(ctx, &local_state);
  1450. fz_catch(ctx)
  1451. fz_rethrow(ctx);
  1452. return trm;
  1453. }
  1454. static void
  1455. svg_run_element(fz_context *ctx, fz_device *dev, svg_document *doc, fz_xml *root, const svg_state *state)
  1456. {
  1457. if (fz_xml_is_tag(root, "svg"))
  1458. svg_run_svg(ctx, dev, doc, root, state);
  1459. else if (fz_xml_is_tag(root, "g"))
  1460. svg_run_g(ctx, dev, doc, root, state);
  1461. else if (fz_xml_is_tag(root, "title"))
  1462. ;
  1463. else if (fz_xml_is_tag(root, "desc"))
  1464. ;
  1465. else if (fz_xml_is_tag(root, "defs"))
  1466. ;
  1467. else if (fz_xml_is_tag(root, "symbol"))
  1468. ;
  1469. else if (fz_xml_is_tag(root, "use"))
  1470. svg_run_use(ctx, dev, doc, root, state);
  1471. else if (fz_xml_is_tag(root, "path"))
  1472. svg_run_path(ctx, dev, doc, root, state);
  1473. else if (fz_xml_is_tag(root, "rect"))
  1474. svg_run_rect(ctx, dev, doc, root, state);
  1475. else if (fz_xml_is_tag(root, "circle"))
  1476. svg_run_circle(ctx, dev, doc, root, state);
  1477. else if (fz_xml_is_tag(root, "ellipse"))
  1478. svg_run_ellipse(ctx, dev, doc, root, state);
  1479. else if (fz_xml_is_tag(root, "line"))
  1480. svg_run_line(ctx, dev, doc, root, state);
  1481. else if (fz_xml_is_tag(root, "polyline"))
  1482. svg_run_polyline(ctx, dev, doc, root, state);
  1483. else if (fz_xml_is_tag(root, "polygon"))
  1484. svg_run_polygon(ctx, dev, doc, root, state);
  1485. else if (fz_xml_is_tag(root, "image"))
  1486. svg_run_image(ctx, dev, doc, root, state);
  1487. else if (fz_xml_is_tag(root, "text"))
  1488. svg_run_text(ctx, dev, doc, root, state, 0, 0, 1, 1);
  1489. else
  1490. {
  1491. /* ignore unrecognized tags */
  1492. }
  1493. }
  1494. void
  1495. svg_parse_document_bounds(fz_context *ctx, svg_document *doc, fz_xml *root)
  1496. {
  1497. char *version_att;
  1498. char *w_att;
  1499. char *h_att;
  1500. char *viewbox_att;
  1501. int version;
  1502. if (!fz_xml_is_tag(root, "svg"))
  1503. fz_throw(ctx, FZ_ERROR_SYNTAX, "expected svg element (found %s)", fz_xml_tag(root));
  1504. version_att = fz_xml_att(root, "version");
  1505. w_att = fz_xml_att(root, "width");
  1506. h_att = fz_xml_att(root, "height");
  1507. viewbox_att = fz_xml_att(root, "viewBox");
  1508. version = 10;
  1509. if (version_att)
  1510. version = fz_atof(version_att) * 10;
  1511. if (version > 12)
  1512. fz_warn(ctx, "svg document version is newer than we support");
  1513. /* If no width or height attributes, then guess from the viewbox */
  1514. if (w_att == NULL && h_att == NULL && viewbox_att != NULL)
  1515. {
  1516. float min_x, min_y, box_w, box_h;
  1517. svg_lex_viewbox(viewbox_att, &min_x, &min_y, &box_w, &box_h);
  1518. doc->width = box_w;
  1519. doc->height = box_h;
  1520. }
  1521. else
  1522. {
  1523. doc->width = DEF_WIDTH;
  1524. if (w_att)
  1525. doc->width = svg_parse_length(w_att, doc->width, DEF_FONTSIZE);
  1526. doc->height = DEF_HEIGHT;
  1527. if (h_att)
  1528. doc->height = svg_parse_length(h_att, doc->height, DEF_FONTSIZE);
  1529. }
  1530. }
  1531. void
  1532. svg_run_document(fz_context *ctx, svg_document *doc, fz_xml *root, fz_device *dev, fz_matrix ctm)
  1533. {
  1534. svg_state state;
  1535. svg_parse_document_bounds(ctx, doc, root);
  1536. /* Initial graphics state */
  1537. state.transform = ctm;
  1538. state.stroke = fz_new_stroke_state(ctx);
  1539. state.use_depth = 0;
  1540. state.viewport_w = DEF_WIDTH;
  1541. state.viewport_h = DEF_HEIGHT;
  1542. state.viewbox_w = DEF_WIDTH;
  1543. state.viewbox_h = DEF_HEIGHT;
  1544. state.viewbox_size = sqrtf(DEF_WIDTH*DEF_WIDTH + DEF_HEIGHT*DEF_HEIGHT) / sqrtf(2);
  1545. state.fontsize = 12;
  1546. state.opacity = 1;
  1547. state.fill_rule = 0;
  1548. state.fill_is_set = 1;
  1549. state.fill_color[0] = 0;
  1550. state.fill_color[1] = 0;
  1551. state.fill_color[2] = 0;
  1552. state.fill_opacity = 1;
  1553. state.stroke_is_set = 0;
  1554. state.stroke_color[0] = 0;
  1555. state.stroke_color[1] = 0;
  1556. state.stroke_color[2] = 0;
  1557. state.stroke_opacity = 1;
  1558. state.font_family = "serif";
  1559. state.is_bold = 0;
  1560. state.is_italic = 0;
  1561. state.text_anchor = 0;
  1562. fz_try(ctx)
  1563. {
  1564. svg_run_svg(ctx, dev, doc, root, &state);
  1565. }
  1566. fz_always(ctx)
  1567. {
  1568. fz_drop_stroke_state(ctx, state.stroke);
  1569. }
  1570. fz_catch(ctx)
  1571. {
  1572. fz_rethrow(ctx);
  1573. }
  1574. }