svg-device.c 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418
  1. // Copyright (C) 2004-2024 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 <string.h>
  24. #include <float.h>
  25. #include <math.h>
  26. typedef struct
  27. {
  28. int pattern;
  29. fz_matrix ctm;
  30. fz_rect view;
  31. fz_rect area;
  32. fz_point step;
  33. } tile;
  34. typedef struct
  35. {
  36. int id;
  37. fz_font *font;
  38. int max_sentlist;
  39. char *sentlist;
  40. } font;
  41. typedef struct
  42. {
  43. int id;
  44. fz_image *image;
  45. } image;
  46. typedef struct
  47. {
  48. fz_device super;
  49. int text_as_text;
  50. int reuse_images;
  51. fz_output *real_out;
  52. int in_defs;
  53. fz_buffer *defs;
  54. fz_buffer *main;
  55. fz_buffer *out;
  56. int *save_id;
  57. int id;
  58. int blend_bitmask;
  59. int num_tiles;
  60. int max_tiles;
  61. tile *tiles;
  62. int num_fonts;
  63. int max_fonts;
  64. font *fonts;
  65. int num_images;
  66. int max_images;
  67. image *images;
  68. int layers;
  69. float page_width;
  70. float page_height;
  71. } svg_device;
  72. static fz_buffer *
  73. start_def(fz_context *ctx, svg_device *sdev, int need_tag)
  74. {
  75. if (sdev->in_defs > 0)
  76. {
  77. if (need_tag)
  78. fz_append_string(ctx, sdev->defs, "<defs>\n");
  79. }
  80. else
  81. {
  82. sdev->out = sdev->defs;
  83. }
  84. sdev->in_defs++;
  85. return sdev->out;
  86. }
  87. static fz_buffer *
  88. end_def(fz_context *ctx, svg_device *sdev, int need_tag)
  89. {
  90. sdev->in_defs--;
  91. if (sdev->in_defs > 0)
  92. {
  93. if (need_tag)
  94. fz_append_string(ctx, sdev->defs, "</defs>\n");
  95. }
  96. else
  97. {
  98. sdev->out = sdev->main;
  99. }
  100. return sdev->out;
  101. }
  102. /* Helper functions */
  103. struct svg_path_walker_state {
  104. fz_buffer *out;
  105. int space; // needs space
  106. float x, y; // last location
  107. int cmd; // last command
  108. };
  109. static void
  110. svg_path_emit_number(fz_context *ctx, struct svg_path_walker_state *pws, float a)
  111. {
  112. if (pws->space && a >= 0)
  113. fz_append_byte(ctx, pws->out, ' ');
  114. fz_append_printf(ctx, pws->out, "%g", a);
  115. pws->space = 1;
  116. }
  117. static void
  118. svg_path_emit_command(fz_context *ctx, struct svg_path_walker_state *pws, char cmd)
  119. {
  120. if (pws->cmd != cmd) {
  121. fz_append_byte(ctx, pws->out, cmd);
  122. pws->space = 0;
  123. pws->cmd = cmd;
  124. }
  125. }
  126. static void
  127. svg_path_moveto(fz_context *ctx, void *arg, float x, float y)
  128. {
  129. struct svg_path_walker_state *pws = arg;
  130. svg_path_emit_command(ctx, pws, 'M');
  131. svg_path_emit_number(ctx, pws, x);
  132. svg_path_emit_number(ctx, pws, y);
  133. pws->cmd = 'L';
  134. pws->x = x;
  135. pws->y = y;
  136. }
  137. static void
  138. svg_path_lineto(fz_context *ctx, void *arg, float x, float y)
  139. {
  140. struct svg_path_walker_state *pws = arg;
  141. if (pws->x == x) {
  142. svg_path_emit_command(ctx, pws, 'V');
  143. svg_path_emit_number(ctx, pws, y);
  144. } else if (pws->y == y) {
  145. svg_path_emit_command(ctx, pws, 'H');
  146. svg_path_emit_number(ctx, pws, x);
  147. } else {
  148. svg_path_emit_command(ctx, pws, 'L');
  149. svg_path_emit_number(ctx, pws, x);
  150. svg_path_emit_number(ctx, pws, y);
  151. }
  152. pws->x = x;
  153. pws->y = y;
  154. }
  155. static void
  156. svg_path_curveto(fz_context *ctx, void *arg, float x1, float y1, float x2, float y2, float x3, float y3)
  157. {
  158. struct svg_path_walker_state *pws = arg;
  159. svg_path_emit_command(ctx, pws, 'C');
  160. svg_path_emit_number(ctx, pws, x1);
  161. svg_path_emit_number(ctx, pws, y1);
  162. svg_path_emit_number(ctx, pws, x2);
  163. svg_path_emit_number(ctx, pws, y2);
  164. svg_path_emit_number(ctx, pws, x3);
  165. svg_path_emit_number(ctx, pws, y3);
  166. pws->x = x3;
  167. pws->y = y3;
  168. }
  169. static void
  170. svg_path_close(fz_context *ctx, void *arg)
  171. {
  172. struct svg_path_walker_state *pws = arg;
  173. svg_path_emit_command(ctx, arg, 'Z');
  174. pws->x = NAN;
  175. pws->y = NAN;
  176. }
  177. static const fz_path_walker svg_path_walker =
  178. {
  179. svg_path_moveto,
  180. svg_path_lineto,
  181. svg_path_curveto,
  182. svg_path_close
  183. };
  184. static void
  185. svg_dev_path(fz_context *ctx, svg_device *sdev, const fz_path *path)
  186. {
  187. struct svg_path_walker_state pws = { sdev->out, 0, NAN, NAN, 0 };
  188. fz_append_printf(ctx, sdev->out, " d=\"");
  189. fz_walk_path(ctx, path, &svg_path_walker, &pws);
  190. fz_append_printf(ctx, sdev->out, "\"");
  191. }
  192. static void
  193. svg_dev_ctm(fz_context *ctx, svg_device *sdev, fz_matrix ctm)
  194. {
  195. fz_buffer *out = sdev->out;
  196. if (ctm.a != 1.0f || ctm.b != 0 || ctm.c != 0 || ctm.d != 1.0f || ctm.e != 0 || ctm.f != 0)
  197. {
  198. fz_append_printf(ctx, out, " transform=\"matrix(%g,%g,%g,%g,%g,%g)\"",
  199. ctm.a, ctm.b, ctm.c, ctm.d, ctm.e, ctm.f);
  200. }
  201. }
  202. static void
  203. svg_dev_stroke_state(fz_context *ctx, svg_device *sdev, const fz_stroke_state *stroke_state, fz_matrix ctm)
  204. {
  205. fz_buffer *out = sdev->out;
  206. float exp;
  207. exp = fz_matrix_expansion(ctm);
  208. if (exp == 0)
  209. exp = 1;
  210. exp = stroke_state->linewidth/exp;
  211. fz_append_printf(ctx, out, " stroke-width=\"%g\"", exp);
  212. fz_append_printf(ctx, out, " stroke-linecap=\"%s\"",
  213. (stroke_state->start_cap == FZ_LINECAP_SQUARE ? "square" :
  214. (stroke_state->start_cap == FZ_LINECAP_ROUND ? "round" : "butt")));
  215. if (stroke_state->dash_len != 0)
  216. {
  217. int i;
  218. fz_append_printf(ctx, out, " stroke-dasharray=");
  219. for (i = 0; i < stroke_state->dash_len; i++)
  220. fz_append_printf(ctx, out, "%c%g", (i == 0 ? '\"' : ','), stroke_state->dash_list[i]);
  221. fz_append_printf(ctx, out, "\"");
  222. if (stroke_state->dash_phase != 0)
  223. fz_append_printf(ctx, out, " stroke-dashoffset=\"%g\"", stroke_state->dash_phase);
  224. }
  225. if (stroke_state->linejoin == FZ_LINEJOIN_MITER || stroke_state->linejoin == FZ_LINEJOIN_MITER_XPS)
  226. fz_append_printf(ctx, out, " stroke-miterlimit=\"%g\"", stroke_state->miterlimit);
  227. fz_append_printf(ctx, out, " stroke-linejoin=\"%s\"",
  228. (stroke_state->linejoin == FZ_LINEJOIN_BEVEL ? "bevel" :
  229. (stroke_state->linejoin == FZ_LINEJOIN_ROUND ? "round" : "miter")));
  230. }
  231. static unsigned int
  232. svg_hex_color(fz_context *ctx, fz_colorspace *colorspace, const float *color, fz_color_params color_params)
  233. {
  234. float rgb[3];
  235. int r, g, b;
  236. if (colorspace != fz_device_rgb(ctx))
  237. {
  238. fz_convert_color(ctx, colorspace, color, fz_device_rgb(ctx), rgb, NULL, color_params);
  239. color = rgb;
  240. }
  241. r = fz_clampi(255 * color[0] + 0.5f, 0, 255);
  242. g = fz_clampi(255 * color[1] + 0.5f, 0, 255);
  243. b = fz_clampi(255 * color[2] + 0.5f, 0, 255);
  244. return (r << 16) | (g << 8) | b;
  245. }
  246. static void
  247. svg_dev_fill_color(fz_context *ctx, svg_device *sdev, fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params)
  248. {
  249. fz_buffer *out = sdev->out;
  250. if (colorspace)
  251. {
  252. int rgb = svg_hex_color(ctx, colorspace, color, color_params);
  253. if (rgb != 0) /* black is the default value */
  254. fz_append_printf(ctx, out, " fill=\"#%06x\"", rgb);
  255. }
  256. else
  257. fz_append_printf(ctx, out, " fill=\"none\"");
  258. if (alpha != 1)
  259. fz_append_printf(ctx, out, " fill-opacity=\"%g\"", alpha);
  260. }
  261. static void
  262. svg_dev_stroke_color(fz_context *ctx, svg_device *sdev, fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params)
  263. {
  264. fz_buffer *out = sdev->out;
  265. if (colorspace)
  266. fz_append_printf(ctx, out, " fill=\"none\" stroke=\"#%06x\"", svg_hex_color(ctx, colorspace, color, color_params));
  267. else
  268. fz_append_printf(ctx, out, " fill=\"none\" stroke=\"none\"");
  269. if (alpha != 1)
  270. fz_append_printf(ctx, out, " stroke-opacity=\"%g\"", alpha);
  271. }
  272. static void
  273. svg_font_family(fz_context *ctx, char buf[], int size, const char *name)
  274. {
  275. /* Remove "ABCDEF+" prefix and "-Bold" suffix. */
  276. char *p = strchr(name, '+');
  277. if (p) fz_strlcpy(buf, p+1, size);
  278. else fz_strlcpy(buf, name, size);
  279. p = strrchr(buf, '-');
  280. if (p) *p = 0;
  281. }
  282. static int
  283. find_first_char(fz_context *ctx, const fz_text_span *span, int i)
  284. {
  285. for (; i < span->len; ++i)
  286. if (span->items[i].ucs >= 0)
  287. return i;
  288. return i;
  289. }
  290. static int
  291. find_next_line_break(fz_context *ctx, const fz_text_span *span, fz_matrix inv_tm, int i)
  292. {
  293. fz_point p, old_p;
  294. old_p.x = span->items[i].x;
  295. old_p.y = span->items[i].y;
  296. old_p = fz_transform_point(old_p, inv_tm);
  297. for (++i; i < span->len; ++i)
  298. {
  299. if (span->items[i].ucs >= 0)
  300. {
  301. p.x = span->items[i].x;
  302. p.y = span->items[i].y;
  303. p = fz_transform_point(p, inv_tm);
  304. if (span->wmode == 0)
  305. {
  306. if (p.y != old_p.y)
  307. return i;
  308. }
  309. else
  310. {
  311. if (p.x != old_p.x)
  312. return i;
  313. }
  314. old_p = p;
  315. }
  316. }
  317. return i;
  318. }
  319. static float
  320. svg_cluster_advance(fz_context *ctx, const fz_text_span *span, int i, int end)
  321. {
  322. int n = 1;
  323. while (i + n < end && span->items[i + n].gid == -1)
  324. ++n;
  325. if (n > 1)
  326. return span->items[i].adv / n;
  327. return 0; /* this value is never used (since n==1) */
  328. }
  329. static void
  330. svg_dev_text_span(fz_context *ctx, svg_device *sdev, fz_matrix ctm, const fz_text_span *span)
  331. {
  332. fz_buffer *out = sdev->out;
  333. char font_family[100];
  334. int is_bold, is_italic;
  335. fz_matrix tm, inv_tm, final_tm;
  336. fz_point p;
  337. float font_size;
  338. fz_text_item *it;
  339. int start, end, i;
  340. float cluster_advance = 0;
  341. if (span->len == 0)
  342. {
  343. fz_append_printf(ctx, out, "/>\n");
  344. return;
  345. }
  346. tm = span->trm;
  347. font_size = fz_matrix_expansion(tm);
  348. final_tm.a = tm.a / font_size;
  349. final_tm.b = tm.b / font_size;
  350. final_tm.c = -tm.c / font_size;
  351. final_tm.d = -tm.d / font_size;
  352. final_tm.e = 0;
  353. final_tm.f = 0;
  354. inv_tm = fz_invert_matrix(final_tm);
  355. final_tm = fz_concat(final_tm, ctm);
  356. tm.e = span->items[0].x;
  357. tm.f = span->items[0].y;
  358. svg_font_family(ctx, font_family, sizeof font_family, fz_font_name(ctx, span->font));
  359. is_bold = fz_font_is_bold(ctx, span->font);
  360. is_italic = fz_font_is_italic(ctx, span->font);
  361. fz_append_printf(ctx, out, " xml:space=\"preserve\"");
  362. fz_append_printf(ctx, out, " transform=\"matrix(%M)\"", &final_tm);
  363. fz_append_printf(ctx, out, " font-size=\"%g\"", font_size);
  364. fz_append_printf(ctx, out, " font-family=\"%s\"", font_family);
  365. if (is_bold) fz_append_printf(ctx, out, " font-weight=\"bold\"");
  366. if (is_italic) fz_append_printf(ctx, out, " font-style=\"italic\"");
  367. if (span->wmode != 0) fz_append_printf(ctx, out, " writing-mode=\"tb\"");
  368. fz_append_byte(ctx, out, '>');
  369. start = find_first_char(ctx, span, 0);
  370. while (start < span->len)
  371. {
  372. end = find_next_line_break(ctx, span, inv_tm, start);
  373. p.x = span->items[start].x;
  374. p.y = span->items[start].y;
  375. p = fz_transform_point(p, inv_tm);
  376. if (span->items[start].gid >= 0)
  377. cluster_advance = svg_cluster_advance(ctx, span, start, end);
  378. if (span->wmode == 0)
  379. fz_append_printf(ctx, out, "<tspan y=\"%g\" x=\"%g", p.y, p.x);
  380. else
  381. fz_append_printf(ctx, out, "<tspan x=\"%g\" y=\"%g", p.x, p.y);
  382. for (i = start + 1; i < end; ++i)
  383. {
  384. it = &span->items[i];
  385. if (it->gid >= 0)
  386. cluster_advance = svg_cluster_advance(ctx, span, i, end);
  387. if (it->ucs >= 0)
  388. {
  389. if (it->gid >= 0)
  390. {
  391. p.x = it->x;
  392. p.y = it->y;
  393. p = fz_transform_point(p, inv_tm);
  394. }
  395. else
  396. {
  397. /* we have no glyph (such as in a ligature) -- advance a bit */
  398. if (span->wmode == 0)
  399. p.x += font_size * cluster_advance;
  400. else
  401. p.y += font_size * cluster_advance;
  402. }
  403. fz_append_printf(ctx, out, " %g", span->wmode == 0 ? p.x : p.y);
  404. }
  405. }
  406. fz_append_printf(ctx, out, "\">");
  407. for (i = start; i < end; ++i)
  408. {
  409. it = &span->items[i];
  410. if (it->ucs >= 0)
  411. {
  412. int c = it->ucs;
  413. if (c >= 32 && c <= 127 && c != '<' && c != '&' && c != '>')
  414. fz_append_byte(ctx, out, c);
  415. else
  416. fz_append_printf(ctx, out, "&#x%04x;", c);
  417. }
  418. }
  419. fz_append_printf(ctx, out, "</tspan>");
  420. start = find_first_char(ctx, span, end);
  421. }
  422. fz_append_printf(ctx, out, "</text>\n");
  423. }
  424. static font *
  425. svg_dev_text_span_as_paths_defs(fz_context *ctx, fz_device *dev, fz_text_span *span, fz_matrix ctm)
  426. {
  427. svg_device *sdev = (svg_device*)dev;
  428. fz_buffer *out = sdev->out;
  429. int i, font_idx;
  430. font *fnt;
  431. for (font_idx = 0; font_idx < sdev->num_fonts; font_idx++)
  432. {
  433. if (sdev->fonts[font_idx].font == span->font)
  434. break;
  435. }
  436. if (font_idx == sdev->num_fonts)
  437. {
  438. /* New font */
  439. if (font_idx == sdev->max_fonts)
  440. {
  441. int newmax = sdev->max_fonts * 2;
  442. if (newmax == 0)
  443. newmax = 4;
  444. sdev->fonts = fz_realloc_array(ctx, sdev->fonts, newmax, font);
  445. memset(&sdev->fonts[font_idx], 0, (newmax - font_idx) * sizeof(font));
  446. sdev->max_fonts = newmax;
  447. }
  448. sdev->fonts[font_idx].id = sdev->id++;
  449. sdev->fonts[font_idx].font = fz_keep_font(ctx, span->font);
  450. sdev->num_fonts++;
  451. }
  452. fnt = &sdev->fonts[font_idx];
  453. for (i=0; i < span->len; i++)
  454. {
  455. fz_text_item *it = &span->items[i];
  456. int gid = it->gid;
  457. if (gid < 0)
  458. continue;
  459. if (gid >= fnt->max_sentlist)
  460. {
  461. int j;
  462. fnt->sentlist = fz_realloc_array(ctx, fnt->sentlist, gid+1, char);
  463. for (j = fnt->max_sentlist; j <= gid; j++)
  464. fnt->sentlist[j] = 0;
  465. fnt->max_sentlist = gid+1;
  466. }
  467. if (!fnt->sentlist[gid])
  468. {
  469. /* Need to send this one */
  470. fz_path *path;
  471. out = start_def(ctx, sdev, 1);
  472. if (fz_font_ft_face(ctx, span->font))
  473. {
  474. path = fz_outline_glyph(ctx, span->font, gid, fz_identity);
  475. if (path)
  476. {
  477. fz_append_printf(ctx, out, "<path id=\"font_%d_%d\"", fnt->id, gid);
  478. svg_dev_path(ctx, sdev, path);
  479. fz_append_printf(ctx, out, "/>\n");
  480. fz_drop_path(ctx, path);
  481. }
  482. else
  483. {
  484. fz_append_printf(ctx, out, "<g id=\"font_%d_%d\"></g>\n", fnt->id, gid);
  485. }
  486. }
  487. else if (fz_font_t3_procs(ctx, span->font))
  488. {
  489. fz_append_printf(ctx, out, "<g id=\"font_%d_%d\">\n", fnt->id, gid);
  490. fz_run_t3_glyph(ctx, span->font, gid, fz_identity, dev);
  491. fnt = &sdev->fonts[font_idx]; /* recursion may realloc the font array! */
  492. fz_append_printf(ctx, out, "</g>\n");
  493. }
  494. out = end_def(ctx, sdev, 1);
  495. fnt->sentlist[gid] = 1;
  496. }
  497. }
  498. return fnt;
  499. }
  500. static void
  501. svg_dev_data_text(fz_context *ctx, fz_buffer *out, int c)
  502. {
  503. if (c > 0)
  504. {
  505. fz_append_string(ctx, out, " data-text=\"");
  506. if (c == '&')
  507. fz_append_string(ctx, out, "&amp;");
  508. else if (c == '"')
  509. fz_append_string(ctx, out, "&quot;");
  510. else if (c >= 32 && c < 127 && c != '<' && c != '>')
  511. fz_append_byte(ctx, out, c);
  512. else if (c >= 0xD800 && c <= 0xDFFF)
  513. /* no surrogate characters in SVG */
  514. fz_append_printf(ctx, out, "&#xFFFD;");
  515. else
  516. fz_append_printf(ctx, out, "&#x%04x;", c);
  517. fz_append_byte(ctx, out, '"');
  518. }
  519. }
  520. static void
  521. svg_dev_text_span_as_paths_fill(fz_context *ctx, fz_device *dev, const fz_text_span *span, fz_matrix ctm,
  522. fz_colorspace *colorspace, const float *color, float alpha, font *fnt, fz_color_params color_params)
  523. {
  524. svg_device *sdev = (svg_device*)dev;
  525. fz_buffer *out = sdev->out;
  526. fz_matrix trm, mtx;
  527. int i;
  528. /* Rely on the fact that trm.{e,f} == 0 */
  529. trm.a = span->trm.a;
  530. trm.b = span->trm.b;
  531. trm.c = span->trm.c;
  532. trm.d = span->trm.d;
  533. trm.e = 0;
  534. trm.f = 0;
  535. for (i=0; i < span->len; i++)
  536. {
  537. fz_text_item *it = &span->items[i];
  538. int gid = it->gid;
  539. if (gid < 0)
  540. continue;
  541. trm.e = it->x;
  542. trm.f = it->y;
  543. mtx = fz_concat(trm, ctm);
  544. fz_append_string(ctx, out, "<use");
  545. svg_dev_data_text(ctx, out, it->ucs);
  546. fz_append_printf(ctx, out, " xlink:href=\"#font_%d_%d\"", fnt->id, gid);
  547. svg_dev_ctm(ctx, sdev, mtx);
  548. svg_dev_fill_color(ctx, sdev, colorspace, color, alpha, color_params);
  549. fz_append_printf(ctx, out, "/>\n");
  550. }
  551. }
  552. static void
  553. svg_dev_text_span_as_paths_stroke(fz_context *ctx, fz_device *dev, const fz_text_span *span,
  554. const fz_stroke_state *stroke, fz_matrix ctm,
  555. fz_colorspace *colorspace, const float *color, float alpha, font *fnt, fz_color_params color_params)
  556. {
  557. svg_device *sdev = (svg_device*)dev;
  558. fz_buffer *out = sdev->out;
  559. fz_matrix trm, mtx;
  560. int i;
  561. /* Rely on the fact that trm.{e,f} == 0 */
  562. trm.a = span->trm.a;
  563. trm.b = span->trm.b;
  564. trm.c = span->trm.c;
  565. trm.d = span->trm.d;
  566. trm.e = 0;
  567. trm.f = 0;
  568. for (i=0; i < span->len; i++)
  569. {
  570. fz_text_item *it = &span->items[i];
  571. int gid = it->gid;
  572. if (gid < 0)
  573. continue;
  574. trm.e = it->x;
  575. trm.f = it->y;
  576. mtx = fz_concat(trm, ctm);
  577. fz_append_string(ctx, out, "<use");
  578. svg_dev_data_text(ctx, out, it->ucs);
  579. fz_append_printf(ctx, out, " xlink:href=\"#font_%d_%d\"", fnt->id, gid);
  580. svg_dev_stroke_state(ctx, sdev, stroke, mtx);
  581. svg_dev_ctm(ctx, sdev, mtx);
  582. svg_dev_stroke_color(ctx, sdev, colorspace, color, alpha, color_params);
  583. fz_append_printf(ctx, out, "/>\n");
  584. }
  585. }
  586. /* Entry points */
  587. static void
  588. svg_dev_fill_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even_odd, fz_matrix ctm,
  589. fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params)
  590. {
  591. svg_device *sdev = (svg_device*)dev;
  592. fz_buffer *out = sdev->out;
  593. fz_append_printf(ctx, out, "<path");
  594. svg_dev_ctm(ctx, sdev, ctm);
  595. svg_dev_path(ctx, sdev, path);
  596. svg_dev_fill_color(ctx, sdev, colorspace, color, alpha, color_params);
  597. if (even_odd)
  598. fz_append_printf(ctx, out, " fill-rule=\"evenodd\"");
  599. fz_append_printf(ctx, out, "/>\n");
  600. }
  601. static void
  602. svg_dev_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, const fz_stroke_state *stroke, fz_matrix ctm,
  603. fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params)
  604. {
  605. svg_device *sdev = (svg_device*)dev;
  606. fz_buffer *out = sdev->out;
  607. fz_append_printf(ctx, out, "<path");
  608. svg_dev_ctm(ctx, sdev, ctm);
  609. svg_dev_stroke_state(ctx, sdev, stroke, fz_identity);
  610. svg_dev_stroke_color(ctx, sdev, colorspace, color, alpha, color_params);
  611. svg_dev_path(ctx, sdev, path);
  612. fz_append_printf(ctx, out, "/>\n");
  613. }
  614. static void
  615. svg_dev_clip_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even_odd, fz_matrix ctm, fz_rect scissor)
  616. {
  617. svg_device *sdev = (svg_device*)dev;
  618. fz_buffer *out;
  619. int num = sdev->id++;
  620. out = start_def(ctx, sdev, 0);
  621. fz_append_printf(ctx, out, "<clipPath id=\"clip_%d\">\n", num);
  622. fz_append_printf(ctx, out, "<path");
  623. svg_dev_ctm(ctx, sdev, ctm);
  624. svg_dev_path(ctx, sdev, path);
  625. if (even_odd)
  626. fz_append_printf(ctx, out, " clip-rule=\"evenodd\"");
  627. fz_append_printf(ctx, out, "/>\n</clipPath>\n");
  628. out = end_def(ctx, sdev, 0);
  629. fz_append_printf(ctx, out, "<g clip-path=\"url(#clip_%d)\">\n", num);
  630. }
  631. static void
  632. svg_dev_clip_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, const fz_stroke_state *stroke, fz_matrix ctm, fz_rect scissor)
  633. {
  634. svg_device *sdev = (svg_device*)dev;
  635. fz_buffer *out;
  636. fz_rect bounds;
  637. int num = sdev->id++;
  638. float white[3] = { 1, 1, 1 };
  639. bounds = fz_bound_path(ctx, path, stroke, ctm);
  640. out = start_def(ctx, sdev, 0);
  641. fz_append_printf(ctx, out, "<mask id=\"mask_%d\" x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" maskUnits=\"userSpaceOnUse\" maskContentUnits=\"userSpaceOnUse\">\n",
  642. num, bounds.x0, bounds.y0, bounds.x1 - bounds.x0, bounds.y1 - bounds.y0);
  643. fz_append_printf(ctx, out, "<path");
  644. svg_dev_ctm(ctx, sdev, ctm);
  645. svg_dev_stroke_state(ctx, sdev, stroke, fz_identity);
  646. svg_dev_stroke_color(ctx, sdev, fz_device_rgb(ctx), white, 1, fz_default_color_params);
  647. svg_dev_path(ctx, sdev, path);
  648. fz_append_printf(ctx, out, "/>\n</mask>\n");
  649. out = end_def(ctx, sdev, 0);
  650. fz_append_printf(ctx, out, "<g mask=\"url(#mask_%d)\">\n", num);
  651. }
  652. static void
  653. svg_dev_fill_text(fz_context *ctx, fz_device *dev, const fz_text *text, fz_matrix ctm,
  654. fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params)
  655. {
  656. svg_device *sdev = (svg_device*)dev;
  657. fz_buffer *out = sdev->out;
  658. font *fnt;
  659. fz_text_span *span;
  660. if (sdev->text_as_text)
  661. {
  662. for (span = text->head; span; span = span->next)
  663. {
  664. fz_append_printf(ctx, out, "<text");
  665. svg_dev_fill_color(ctx, sdev, colorspace, color, alpha, color_params);
  666. svg_dev_text_span(ctx, sdev, ctm, span);
  667. }
  668. }
  669. else
  670. {
  671. for (span = text->head; span; span = span->next)
  672. {
  673. fnt = svg_dev_text_span_as_paths_defs(ctx, dev, span, ctm);
  674. svg_dev_text_span_as_paths_fill(ctx, dev, span, ctm, colorspace, color, alpha, fnt, color_params);
  675. }
  676. }
  677. }
  678. static void
  679. svg_dev_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_stroke_state *stroke, fz_matrix ctm,
  680. fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params)
  681. {
  682. svg_device *sdev = (svg_device*)dev;
  683. fz_buffer *out = sdev->out;
  684. font *fnt;
  685. fz_text_span *span;
  686. if (sdev->text_as_text)
  687. {
  688. for (span = text->head; span; span = span->next)
  689. {
  690. fz_append_printf(ctx, out, "<text");
  691. svg_dev_fill_color(ctx, sdev, colorspace, color, alpha, color_params);
  692. svg_dev_text_span(ctx, sdev, ctm, span);
  693. }
  694. }
  695. else
  696. {
  697. for (span = text->head; span; span = span->next)
  698. {
  699. fnt = svg_dev_text_span_as_paths_defs(ctx, dev, span, ctm);
  700. svg_dev_text_span_as_paths_stroke(ctx, dev, span, stroke, ctm, colorspace, color, alpha, fnt, color_params);
  701. }
  702. }
  703. }
  704. static void
  705. svg_dev_clip_text(fz_context *ctx, fz_device *dev, const fz_text *text, fz_matrix ctm, fz_rect scissor)
  706. {
  707. svg_device *sdev = (svg_device*)dev;
  708. fz_buffer *out = sdev->out;
  709. fz_rect bounds;
  710. int num = sdev->id++;
  711. float white[3] = { 1, 1, 1 };
  712. font *fnt;
  713. fz_text_span *span;
  714. bounds = fz_bound_text(ctx, text, NULL, ctm);
  715. out = start_def(ctx, sdev, 0);
  716. fz_append_printf(ctx, out, "<mask id=\"mask_%d\" x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\"",
  717. num, bounds.x0, bounds.y0, bounds.x1 - bounds.x0, bounds.y1 - bounds.y0);
  718. fz_append_printf(ctx, out, " maskUnits=\"userSpaceOnUse\" maskContentUnits=\"userSpaceOnUse\">\n");
  719. if (sdev->text_as_text)
  720. {
  721. for (span = text->head; span; span = span->next)
  722. {
  723. fz_append_printf(ctx, out, "<text");
  724. svg_dev_fill_color(ctx, sdev, fz_device_rgb(ctx), white, 1, fz_default_color_params);
  725. svg_dev_text_span(ctx, sdev, ctm, span);
  726. }
  727. }
  728. else
  729. {
  730. for (span = text->head; span; span = span->next)
  731. {
  732. fnt = svg_dev_text_span_as_paths_defs(ctx, dev, span, ctm);
  733. svg_dev_text_span_as_paths_fill(ctx, dev, span, ctm, fz_device_rgb(ctx), white, 1.0f, fnt, fz_default_color_params);
  734. }
  735. }
  736. fz_append_printf(ctx, out, "</mask>\n");
  737. out = end_def(ctx, sdev, 0);
  738. fz_append_printf(ctx, out, "<g mask=\"url(#mask_%d)\">\n", num);
  739. }
  740. static void
  741. svg_dev_clip_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_stroke_state *stroke, fz_matrix ctm, fz_rect scissor)
  742. {
  743. svg_device *sdev = (svg_device*)dev;
  744. fz_buffer *out;
  745. fz_rect bounds;
  746. int num = sdev->id++;
  747. float white[3] = { 255, 255, 255 };
  748. font *fnt;
  749. fz_text_span *span;
  750. bounds = fz_bound_text(ctx, text, NULL, ctm);
  751. out = start_def(ctx, sdev, 0);
  752. fz_append_printf(ctx, out, "<mask id=\"mask_%d\" x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\"",
  753. num, bounds.x0, bounds.y0, bounds.x1 - bounds.x0, bounds.y1 - bounds.y0);
  754. fz_append_printf(ctx, out, " maskUnits=\"userSpaceOnUse\" maskContentUnits=\"userSpaceOnUse\">\n");
  755. if (sdev->text_as_text)
  756. {
  757. for (span = text->head; span; span = span->next)
  758. {
  759. fz_append_printf(ctx, out, "<text");
  760. svg_dev_stroke_state(ctx, sdev, stroke, fz_identity);
  761. svg_dev_stroke_color(ctx, sdev, fz_device_rgb(ctx), white, 1, fz_default_color_params);
  762. svg_dev_text_span(ctx, sdev, ctm, span);
  763. }
  764. }
  765. else
  766. {
  767. for (span = text->head; span; span = span->next)
  768. {
  769. fnt = svg_dev_text_span_as_paths_defs(ctx, dev, span, ctm);
  770. svg_dev_text_span_as_paths_stroke(ctx, dev, span, stroke, ctm, fz_device_rgb(ctx), white, 1.0f, fnt, fz_default_color_params);
  771. }
  772. }
  773. fz_append_printf(ctx, out, "</mask>\n");
  774. out = end_def(ctx, sdev, 0);
  775. fz_append_printf(ctx, out, "<g mask=\"url(#mask_%d)\">\n", num);
  776. }
  777. static void
  778. svg_dev_ignore_text(fz_context *ctx, fz_device *dev, const fz_text *text, fz_matrix ctm)
  779. {
  780. svg_device *sdev = (svg_device*)dev;
  781. fz_buffer *out = sdev->out;
  782. fz_text_span *span;
  783. float black[3] = { 0, 0, 0};
  784. if (sdev->text_as_text)
  785. {
  786. for (span = text->head; span; span = span->next)
  787. {
  788. fz_append_printf(ctx, out, "<text");
  789. svg_dev_fill_color(ctx, sdev, fz_device_rgb(ctx), black, 0.0f, fz_default_color_params);
  790. svg_dev_text_span(ctx, sdev, ctm, span);
  791. }
  792. }
  793. }
  794. /* We spot repeated images, and send them just once using
  795. * defs. Unfortunately, for pathological files, such
  796. * as the example in Bug695988, this can cause viewers to
  797. * have conniptions. We therefore have an option that is
  798. * made to avoid this (reuse-images=no). */
  799. static void
  800. svg_send_image(fz_context *ctx, svg_device *sdev, fz_image *img, fz_color_params color_params)
  801. {
  802. fz_buffer *out = sdev->out;
  803. int i;
  804. int id;
  805. if (sdev->reuse_images)
  806. {
  807. for (i = sdev->num_images-1; i >= 0; i--)
  808. if (img == sdev->images[i].image)
  809. break;
  810. if (i >= 0)
  811. {
  812. fz_append_printf(ctx, out, "<use xlink:href=\"#image_%d\" x=\"0\" y=\"0\" width=\"%d\" height=\"%d\"/>\n",
  813. sdev->images[i].id, img->w, img->h);
  814. return;
  815. }
  816. /* We need to send this image for the first time */
  817. if (sdev->num_images == sdev->max_images)
  818. {
  819. int new_max = sdev->max_images * 2;
  820. if (new_max == 0)
  821. new_max = 32;
  822. sdev->images = fz_realloc_array(ctx, sdev->images, new_max, image);
  823. sdev->max_images = new_max;
  824. }
  825. id = sdev->id++;
  826. fz_append_printf(ctx, out, "<image id=\"image_%d\" width=\"%d\" height=\"%d\" xlink:href=\"", id, img->w, img->h);
  827. fz_append_image_as_data_uri(ctx, out, img);
  828. fz_append_printf(ctx, out, "\"/>\n");
  829. sdev->images[sdev->num_images].id = id;
  830. sdev->images[sdev->num_images].image = fz_keep_image(ctx, img);
  831. sdev->num_images++;
  832. }
  833. else
  834. {
  835. fz_append_printf(ctx, out, "<image width=\"%d\" height=\"%d\" xlink:href=\"", img->w, img->h);
  836. fz_append_image_as_data_uri(ctx, out, img);
  837. fz_append_printf(ctx, out, "\"/>\n");
  838. }
  839. }
  840. static void
  841. svg_dev_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, fz_matrix ctm, float alpha, fz_color_params color_params)
  842. {
  843. svg_device *sdev = (svg_device*)dev;
  844. fz_buffer *out = sdev->out;
  845. fz_matrix local_ctm = ctm;
  846. fz_matrix scale = { 0 };
  847. if (alpha == 0)
  848. return;
  849. scale.a = 1.0f / image->w;
  850. scale.d = 1.0f / image->h;
  851. local_ctm = fz_concat(scale, ctm);
  852. fz_append_printf(ctx, out, "<g");
  853. if (alpha != 1.0f)
  854. fz_append_printf(ctx, out, " opacity=\"%g\"", alpha);
  855. svg_dev_ctm(ctx, sdev, local_ctm);
  856. fz_append_printf(ctx, out, ">\n");
  857. svg_send_image(ctx, sdev, image, color_params);
  858. fz_append_printf(ctx, out, "</g>\n");
  859. }
  860. static void
  861. svg_dev_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, fz_matrix ctm, float alpha, fz_color_params color_params)
  862. {
  863. svg_device *sdev = (svg_device*)dev;
  864. fz_buffer *out = sdev->out;
  865. fz_irect bbox;
  866. fz_pixmap *pix;
  867. fz_rect scissor = fz_device_current_scissor(ctx, dev);
  868. if (alpha == 0)
  869. return;
  870. if (fz_is_infinite_rect(scissor))
  871. {
  872. scissor.x0 = 0;
  873. scissor.x1 = sdev->page_width;
  874. scissor.y0 = 0;
  875. scissor.y1 = sdev->page_height;
  876. }
  877. bbox = fz_round_rect(fz_intersect_rect(fz_bound_shade(ctx, shade, ctm), scissor));
  878. if (fz_is_empty_irect(bbox))
  879. return;
  880. pix = fz_new_pixmap_with_bbox(ctx, fz_device_rgb(ctx), bbox, NULL, 1);
  881. fz_clear_pixmap(ctx, pix);
  882. fz_try(ctx)
  883. {
  884. fz_paint_shade(ctx, shade, NULL, ctm, pix, color_params, bbox, NULL, NULL);
  885. if (alpha != 1.0f)
  886. fz_append_printf(ctx, out, "<g opacity=\"%g\">\n", alpha);
  887. fz_append_printf(ctx, out, "<image x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" xlink:href=\"", pix->x, pix->y, pix->w, pix->h);
  888. fz_append_pixmap_as_data_uri(ctx, out, pix);
  889. fz_append_printf(ctx, out, "\"/>\n");
  890. if (alpha != 1.0f)
  891. fz_append_printf(ctx, out, "</g>\n");
  892. }
  893. fz_always(ctx)
  894. {
  895. fz_drop_pixmap(ctx, pix);
  896. }
  897. fz_catch(ctx)
  898. {
  899. fz_rethrow(ctx);
  900. }
  901. }
  902. static void
  903. svg_dev_fill_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, fz_matrix ctm,
  904. fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params)
  905. {
  906. svg_device *sdev = (svg_device*)dev;
  907. fz_buffer *out;
  908. fz_matrix local_ctm = ctm;
  909. fz_matrix scale = { 0 };
  910. int mask = sdev->id++;
  911. scale.a = 1.0f / image->w;
  912. scale.d = 1.0f / image->h;
  913. local_ctm = fz_concat(scale, ctm);
  914. out = start_def(ctx, sdev, 0);
  915. fz_append_printf(ctx, out, "<mask id=\"mask_%d\">\n", mask);
  916. svg_send_image(ctx, sdev, image, color_params);
  917. fz_append_printf(ctx, out, "</mask>\n");
  918. out = end_def(ctx, sdev, 0);
  919. fz_append_printf(ctx, out, "<rect x=\"0\" y=\"0\" width=\"%d\" height=\"%d\"", image->w, image->h);
  920. svg_dev_fill_color(ctx, sdev, colorspace, color, alpha, color_params);
  921. svg_dev_ctm(ctx, sdev, local_ctm);
  922. fz_append_printf(ctx, out, " mask=\"url(#mask_%d)\"/>\n", mask);
  923. }
  924. static void
  925. svg_dev_clip_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, fz_matrix ctm, fz_rect scissor)
  926. {
  927. svg_device *sdev = (svg_device*)dev;
  928. fz_buffer *out;
  929. fz_matrix local_ctm = ctm;
  930. fz_matrix scale = { 0 };
  931. int mask = sdev->id++;
  932. scale.a = 1.0f / image->w;
  933. scale.d = 1.0f / image->h;
  934. local_ctm = fz_concat(scale, ctm);
  935. out = start_def(ctx, sdev, 0);
  936. fz_append_printf(ctx, out, "<mask id=\"mask_%d\">\n<g", mask);
  937. svg_dev_ctm(ctx, sdev, local_ctm);
  938. fz_append_printf(ctx, out, ">\n");
  939. svg_send_image(ctx, sdev, image, fz_default_color_params/* FIXME */);
  940. fz_append_printf(ctx, out, "</g>\n</mask>\n");
  941. out = end_def(ctx, sdev, 0);
  942. fz_append_printf(ctx, out, "<g mask=\"url(#mask_%d)\">\n", mask);
  943. }
  944. static void
  945. svg_dev_pop_clip(fz_context *ctx, fz_device *dev)
  946. {
  947. svg_device *sdev = (svg_device*)dev;
  948. fz_buffer *out = sdev->out;
  949. /* FIXME */
  950. fz_append_printf(ctx, out, "</g>\n");
  951. }
  952. static void
  953. svg_dev_begin_mask(fz_context *ctx, fz_device *dev, fz_rect bbox, int luminosity, fz_colorspace *colorspace, const float *color, fz_color_params color_params)
  954. {
  955. svg_device *sdev = (svg_device*)dev;
  956. fz_buffer *out;
  957. int mask = sdev->id++;
  958. out = start_def(ctx, sdev, 0);
  959. fz_append_printf(ctx, out, "<mask id=\"mask_%d\">\n", mask);
  960. if (dev->container_len > 0)
  961. dev->container[dev->container_len-1].user = mask;
  962. }
  963. static void
  964. svg_dev_end_mask(fz_context *ctx, fz_device *dev, fz_function *tr)
  965. {
  966. svg_device *sdev = (svg_device*)dev;
  967. fz_buffer *out = sdev->out;
  968. int mask = 0;
  969. if (dev->container_len > 0)
  970. mask = dev->container[dev->container_len-1].user;
  971. if (tr)
  972. fz_warn(ctx, "Ignoring Transfer Function");
  973. fz_append_printf(ctx, out, "\"/>\n</mask>\n");
  974. out = end_def(ctx, sdev, 0);
  975. fz_append_printf(ctx, out, "<g mask=\"url(#mask_%d)\">\n", mask);
  976. }
  977. static void
  978. svg_dev_begin_group(fz_context *ctx, fz_device *dev, fz_rect bbox, fz_colorspace *cs, int isolated, int knockout, int blendmode, float alpha)
  979. {
  980. svg_device *sdev = (svg_device*)dev;
  981. fz_buffer *out = sdev->out;
  982. /* SVG only supports normal/multiply/screen/darken/lighten,
  983. * but we'll send them all, as the spec says that unrecognised
  984. * ones are treated as normal. */
  985. static char *blend_names[] = {
  986. "normal", /* FZ_BLEND_NORMAL */
  987. "multiply", /* FZ_BLEND_MULTIPLY */
  988. "screen", /* FZ_BLEND_SCREEN */
  989. "overlay", /* FZ_BLEND_OVERLAY */
  990. "darken", /* FZ_BLEND_DARKEN */
  991. "lighten", /* FZ_BLEND_LIGHTEN */
  992. "color-dodge", /* FZ_BLEND_COLOR_DODGE */
  993. "color-burn", /* FZ_BLEND_COLOR_BURN */
  994. "hard-light", /* FZ_BLEND_HARD_LIGHT */
  995. "soft-light", /* FZ_BLEND_SOFT_LIGHT */
  996. "difference", /* FZ_BLEND_DIFFERENCE */
  997. "exclusion", /* FZ_BLEND_EXCLUSION */
  998. "hue", /* FZ_BLEND_HUE */
  999. "saturation", /* FZ_BLEND_SATURATION */
  1000. "color", /* FZ_BLEND_COLOR */
  1001. "luminosity", /* FZ_BLEND_LUMINOSITY */
  1002. };
  1003. if (blendmode < FZ_BLEND_NORMAL || blendmode > FZ_BLEND_LUMINOSITY)
  1004. blendmode = FZ_BLEND_NORMAL;
  1005. if (blendmode != FZ_BLEND_NORMAL && (sdev->blend_bitmask & (1<<blendmode)) == 0)
  1006. sdev->blend_bitmask |= (1<<blendmode);
  1007. /* FIXME: Handle alpha == 0 somehow? */
  1008. /* SVG 1.1 doesn't support adequate blendmodes/knockout etc, so just ignore it for now */
  1009. if (alpha == 1)
  1010. fz_append_printf(ctx, out, "<g");
  1011. else
  1012. fz_append_printf(ctx, out, "<g opacity=\"%g\"", alpha);
  1013. if (blendmode != FZ_BLEND_NORMAL)
  1014. fz_append_printf(ctx, out, " style=\"mix-blend-mode:%s\"", blend_names[blendmode]);
  1015. fz_append_printf(ctx, out, ">\n");
  1016. }
  1017. static void
  1018. svg_dev_end_group(fz_context *ctx, fz_device *dev)
  1019. {
  1020. svg_device *sdev = (svg_device*)dev;
  1021. fz_buffer *out = sdev->out;
  1022. fz_append_printf(ctx, out, "</g>\n");
  1023. }
  1024. static int
  1025. svg_dev_begin_tile(fz_context *ctx, fz_device *dev, fz_rect area, fz_rect view, float xstep, float ystep, fz_matrix ctm, int id)
  1026. {
  1027. svg_device *sdev = (svg_device*)dev;
  1028. fz_buffer *out;
  1029. int num;
  1030. tile *t;
  1031. if (sdev->num_tiles == sdev->max_tiles)
  1032. {
  1033. int n = (sdev->num_tiles == 0 ? 4 : sdev->num_tiles * 2);
  1034. sdev->tiles = fz_realloc_array(ctx, sdev->tiles, n, tile);
  1035. sdev->max_tiles = n;
  1036. }
  1037. num = sdev->num_tiles++;
  1038. t = &sdev->tiles[num];
  1039. t->area = area;
  1040. t->view = view;
  1041. t->ctm = ctm;
  1042. t->pattern = sdev->id++;
  1043. xstep = fabsf(xstep);
  1044. ystep = fabsf(ystep);
  1045. if (xstep == 0 || ystep == 0) {
  1046. fz_warn(ctx, "Pattern cannot have x or ystep == 0.");
  1047. if (xstep == 0)
  1048. xstep = 1;
  1049. if (ystep == 0)
  1050. ystep = 1;
  1051. }
  1052. t->step.x = xstep;
  1053. t->step.y = ystep;
  1054. /* view = area of our reference tile in pattern space.
  1055. * area = area to tile into in pattern space.
  1056. * xstep/ystep = pattern repeat step in pattern space.
  1057. * All of these need to be transformed by ctm to get to device space.
  1058. * SVG only allows us to specify pattern tiles as axis aligned
  1059. * rectangles, so we send these through as is, and ensure that the
  1060. * correct matrix is used on the fill.
  1061. */
  1062. /* The first thing we do is to capture the contents of the pattern
  1063. * as a def we can reuse. */
  1064. out = start_def(ctx, sdev, 1);
  1065. fz_append_printf(ctx, out, "<g id=\"pattern_tile_%d\">\n", t->pattern);
  1066. return 0;
  1067. }
  1068. static void
  1069. svg_dev_end_tile(fz_context *ctx, fz_device *dev)
  1070. {
  1071. svg_device *sdev = (svg_device*)dev;
  1072. fz_buffer *out = sdev->out;
  1073. int num, cp = -1;
  1074. tile *t;
  1075. fz_matrix inverse;
  1076. float x, y, w, h;
  1077. if (sdev->num_tiles == 0)
  1078. return;
  1079. num = --sdev->num_tiles;
  1080. t = &sdev->tiles[num];
  1081. fz_append_printf(ctx, out, "</g>\n");
  1082. /* In svg, the reference tile is taken from (x,y) to (x+width,y+height)
  1083. * and is repeated at (x+n*width,y+m*height) for all integer n and m.
  1084. * This means that width and height generally correspond to xstep and
  1085. * ystep. There are exceptional cases where we have to break this
  1086. * though; when xstep/ystep are smaller than the width/height of the
  1087. * pattern tile, we need to render the pattern contents several times
  1088. * to ensure that the pattern tile contains everything. */
  1089. fz_append_printf(ctx, out, "<pattern id=\"pattern_%d\" patternUnits=\"userSpaceOnUse\" patternContentUnits=\"userSpaceOnUse\"",
  1090. t->pattern);
  1091. fz_append_printf(ctx, out, " x=\"0\" y=\"0\" width=\"%g\" height=\"%g\">\n",
  1092. t->step.x, t->step.y);
  1093. if (t->view.x0 > 0 || t->step.x < t->view.x1 || t->view.y0 > 0 || t->step.y < t->view.y1)
  1094. {
  1095. cp = sdev->id++;
  1096. fz_append_printf(ctx, out, "<clipPath id=\"clip_%d\">\n", cp);
  1097. fz_append_printf(ctx, out, "<path d=\"M %g %g L %g %g L %g %g L %g %g Z\"/>\n",
  1098. t->view.x0, t->view.y0,
  1099. t->view.x1, t->view.y0,
  1100. t->view.x1, t->view.y1,
  1101. t->view.x0, t->view.y1);
  1102. fz_append_printf(ctx, out, "</clipPath>\n");
  1103. fz_append_printf(ctx, out, "<g clip-path=\"url(#clip_%d)\">\n", cp);
  1104. }
  1105. /* All the pattern contents will have their own ctm applied. Let's
  1106. * undo the current one to allow for this */
  1107. inverse = fz_invert_matrix(t->ctm);
  1108. fz_append_printf(ctx, out, "<g");
  1109. svg_dev_ctm(ctx, sdev, inverse);
  1110. fz_append_printf(ctx, out, ">\n");
  1111. w = t->view.x1 - t->view.x0;
  1112. h = t->view.y1 - t->view.y0;
  1113. for (x = 0; x > -w; x -= t->step.x)
  1114. for (y = 0; y > -h; y -= t->step.y)
  1115. fz_append_printf(ctx, out, "<use x=\"%g\" y=\"%g\" xlink:href=\"#pattern_tile_%d\"/>\n", x, y, t->pattern);
  1116. fz_append_printf(ctx, out, "</g>\n");
  1117. if (cp != -1)
  1118. fz_append_printf(ctx, out, "</g>\n");
  1119. fz_append_printf(ctx, out, "</pattern>\n");
  1120. out = end_def(ctx, sdev, 1);
  1121. /* Finally, fill a rectangle with the pattern. */
  1122. fz_append_printf(ctx, out, "<rect");
  1123. svg_dev_ctm(ctx, sdev, t->ctm);
  1124. fz_append_printf(ctx, out, " fill=\"url(#pattern_%d)\" x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\"/>\n",
  1125. t->pattern, t->area.x0, t->area.y0, t->area.x1 - t->area.x0, t->area.y1 - t->area.y0);
  1126. }
  1127. static void
  1128. svg_dev_begin_layer(fz_context *ctx, fz_device *dev, const char *name)
  1129. {
  1130. svg_device *sdev = (svg_device*)dev;
  1131. fz_buffer *out = sdev->out;
  1132. sdev->layers++;
  1133. fz_append_printf(ctx, out, "<g inkscape:groupmode=\"layer\" inkscape:label=%<>\n", name ? name : "");
  1134. }
  1135. static void
  1136. svg_dev_end_layer(fz_context *ctx, fz_device *dev)
  1137. {
  1138. svg_device *sdev = (svg_device*)dev;
  1139. fz_buffer *out = sdev->out;
  1140. if (sdev->layers == 0)
  1141. return;
  1142. sdev->layers--;
  1143. fz_append_printf(ctx, out, "</g>\n");
  1144. }
  1145. static void
  1146. svg_dev_close_device(fz_context *ctx, fz_device *dev)
  1147. {
  1148. svg_device *sdev = (svg_device*)dev;
  1149. fz_output *out = sdev->real_out;
  1150. while (sdev->layers > 0)
  1151. {
  1152. fz_append_string(ctx, sdev->main, "</g>\n");
  1153. sdev->layers--;
  1154. }
  1155. if (sdev->save_id)
  1156. *sdev->save_id = sdev->id;
  1157. fz_write_string(ctx, out, "<svg");
  1158. fz_write_string(ctx, out, " xmlns=\"http://www.w3.org/2000/svg\"");
  1159. fz_write_string(ctx, out, " xmlns:xlink=\"http://www.w3.org/1999/xlink\"");
  1160. fz_write_string(ctx, out, " xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\"");
  1161. fz_write_string(ctx, out, " version=\"1.1\"");
  1162. fz_write_printf(ctx, out, " width=\"%g\" height=\"%g\" viewBox=\"0 0 %g %g\">\n",
  1163. sdev->page_width, sdev->page_height, sdev->page_width, sdev->page_height);
  1164. if (sdev->defs->len > 0)
  1165. {
  1166. fz_write_printf(ctx, out, "<defs>\n");
  1167. fz_write_buffer(ctx, out, sdev->defs);
  1168. fz_write_printf(ctx, out, "</defs>\n");
  1169. }
  1170. fz_write_buffer(ctx, out, sdev->main);
  1171. fz_write_printf(ctx, out, "</svg>\n");
  1172. }
  1173. static void
  1174. svg_dev_drop_device(fz_context *ctx, fz_device *dev)
  1175. {
  1176. svg_device *sdev = (svg_device*)dev;
  1177. int i;
  1178. fz_free(ctx, sdev->tiles);
  1179. fz_drop_buffer(ctx, sdev->defs);
  1180. fz_drop_buffer(ctx, sdev->main);
  1181. for (i = 0; i < sdev->num_fonts; i++)
  1182. {
  1183. fz_drop_font(ctx, sdev->fonts[i].font);
  1184. fz_free(ctx, sdev->fonts[i].sentlist);
  1185. }
  1186. fz_free(ctx, sdev->fonts);
  1187. for (i = 0; i < sdev->num_images; i++)
  1188. {
  1189. fz_drop_image(ctx, sdev->images[i].image);
  1190. }
  1191. fz_free(ctx, sdev->images);
  1192. }
  1193. fz_device *fz_new_svg_device_with_id(fz_context *ctx, fz_output *out, float page_width, float page_height, int text_format, int reuse_images, int *id)
  1194. {
  1195. svg_device *dev = fz_new_derived_device(ctx, svg_device);
  1196. dev->super.close_device = svg_dev_close_device;
  1197. dev->super.drop_device = svg_dev_drop_device;
  1198. dev->super.fill_path = svg_dev_fill_path;
  1199. dev->super.stroke_path = svg_dev_stroke_path;
  1200. dev->super.clip_path = svg_dev_clip_path;
  1201. dev->super.clip_stroke_path = svg_dev_clip_stroke_path;
  1202. dev->super.fill_text = svg_dev_fill_text;
  1203. dev->super.stroke_text = svg_dev_stroke_text;
  1204. dev->super.clip_text = svg_dev_clip_text;
  1205. dev->super.clip_stroke_text = svg_dev_clip_stroke_text;
  1206. dev->super.ignore_text = svg_dev_ignore_text;
  1207. dev->super.fill_shade = svg_dev_fill_shade;
  1208. dev->super.fill_image = svg_dev_fill_image;
  1209. dev->super.fill_image_mask = svg_dev_fill_image_mask;
  1210. dev->super.clip_image_mask = svg_dev_clip_image_mask;
  1211. dev->super.pop_clip = svg_dev_pop_clip;
  1212. dev->super.begin_mask = svg_dev_begin_mask;
  1213. dev->super.end_mask = svg_dev_end_mask;
  1214. dev->super.begin_group = svg_dev_begin_group;
  1215. dev->super.end_group = svg_dev_end_group;
  1216. dev->super.begin_tile = svg_dev_begin_tile;
  1217. dev->super.end_tile = svg_dev_end_tile;
  1218. dev->super.begin_layer = svg_dev_begin_layer;
  1219. dev->super.end_layer = svg_dev_end_layer;
  1220. dev->real_out = out;
  1221. dev->in_defs = 0;
  1222. dev->defs = fz_new_buffer(ctx, 4096);
  1223. dev->main = fz_new_buffer(ctx, 4096);
  1224. dev->out = dev->main;
  1225. dev->save_id = id;
  1226. dev->id = id ? *id : 1;
  1227. dev->layers = 0;
  1228. dev->text_as_text = (text_format == FZ_SVG_TEXT_AS_TEXT);
  1229. dev->reuse_images = reuse_images;
  1230. dev->page_width = page_width;
  1231. dev->page_height = page_height;
  1232. return (fz_device*)dev;
  1233. }
  1234. fz_device *fz_new_svg_device(fz_context *ctx, fz_output *out, float page_width, float page_height, int text_format, int reuse_images)
  1235. {
  1236. return fz_new_svg_device_with_id(ctx, out, page_width, page_height, text_format, reuse_images, NULL);
  1237. }