Change the translation of the "Help" menu item to "?", so that the menu can be displa...
[reactos.git] / rosapps / smartpdf / fitz / mupdf / pdf_build.c
1 #include <fitz.h>
2 #include <mupdf.h>
3
4 void
5 pdf_initgstate(pdf_gstate *gs)
6 {
7 gs->linewidth = 1.0;
8 gs->linecap = 0;
9 gs->linejoin = 0;
10 gs->miterlimit = 10;
11 gs->dashphase = 0;
12 gs->dashlen = 0;
13 memset(gs->dashlist, 0, sizeof(gs->dashlist));
14
15 gs->stroke.kind = PDF_MCOLOR;
16 gs->stroke.cs = fz_keepcolorspace(pdf_devicegray);
17 gs->stroke.v[0] = 0;
18 gs->stroke.indexed = nil;
19 gs->stroke.pattern = nil;
20 gs->stroke.shade = nil;
21
22 gs->fill.kind = PDF_MCOLOR;
23 gs->fill.cs = fz_keepcolorspace(pdf_devicegray);
24 gs->fill.v[0] = 0;
25 gs->fill.indexed = nil;
26 gs->fill.pattern = nil;
27 gs->fill.shade = nil;
28
29 gs->charspace = 0;
30 gs->wordspace = 0;
31 gs->scale = 1;
32 gs->leading = 0;
33 gs->font = nil;
34 gs->size = -1;
35 gs->render = 0;
36 gs->rise = 0;
37
38 gs->head = nil;
39 }
40
41 fz_error *
42 pdf_setcolorspace(pdf_csi *csi, int what, fz_colorspace *cs)
43 {
44 pdf_gstate *gs = csi->gstate + csi->gtop;
45 fz_error *error;
46 pdf_material *mat;
47
48 error = pdf_flushtext(csi);
49 if (error)
50 return error;
51
52 mat = what == PDF_MFILL ? &gs->fill : &gs->stroke;
53
54 fz_dropcolorspace(mat->cs);
55
56 mat->kind = PDF_MCOLOR;
57 mat->cs = fz_keepcolorspace(cs);
58
59 mat->v[0] = 0; /* FIXME: default color */
60 mat->v[1] = 0; /* FIXME: default color */
61 mat->v[2] = 0; /* FIXME: default color */
62 mat->v[3] = 1; /* FIXME: default color */
63
64 if (!strcmp(cs->name, "Indexed"))
65 {
66 mat->kind = PDF_MINDEXED;
67 mat->indexed = (pdf_indexed*)cs;
68 mat->cs = mat->indexed->base;
69 }
70
71 if (!strcmp(cs->name, "Lab"))
72 mat->kind = PDF_MLAB;
73
74 return nil;
75 }
76
77 fz_error *
78 pdf_setcolor(pdf_csi *csi, int what, float *v)
79 {
80 pdf_gstate *gs = csi->gstate + csi->gtop;
81 fz_error *error;
82 pdf_indexed *ind;
83 pdf_material *mat;
84 int i, k;
85
86 error = pdf_flushtext(csi);
87 if (error)
88 return error;
89
90 mat = what == PDF_MFILL ? &gs->fill : &gs->stroke;
91
92 switch (mat->kind)
93 {
94 case PDF_MPATTERN:
95 if (!strcmp(mat->cs->name, "Lab"))
96 goto Llab;
97 if (!strcmp(mat->cs->name, "Indexed"))
98 goto Lindexed;
99 /* fall through */
100
101 case PDF_MCOLOR:
102 for (i = 0; i < mat->cs->n; i++)
103 mat->v[i] = v[i];
104 break;
105
106 case PDF_MLAB:
107 Llab:
108 mat->v[0] = v[0] / 100.0;
109 mat->v[1] = (v[1] + 100) / 200.0;
110 mat->v[2] = (v[2] + 100) / 200.0;
111 break;
112
113 case PDF_MINDEXED:
114 Lindexed:
115 ind = mat->indexed;
116 i = CLAMP(v[0], 0, ind->high);
117 for (k = 0; k < ind->base->n; k++)
118 mat->v[k] = ind->lookup[ind->base->n * i + k] / 255.0;
119 break;
120
121 default:
122 return fz_throw("syntaxerror: color not compatible with material");
123 }
124
125 return nil;
126 }
127
128 fz_error *
129 pdf_setpattern(pdf_csi *csi, int what, pdf_pattern *pat, float *v)
130 {
131 pdf_gstate *gs = csi->gstate + csi->gtop;
132 fz_error *error;
133 pdf_material *mat;
134
135 error = pdf_flushtext(csi);
136 if (error)
137 return error;
138
139 if (what == PDF_MFILL)
140 mat = &gs->fill;
141 else
142 mat = &gs->stroke;
143
144 // TODO: this possibly drops a pattern too many times resulting in droping pattern
145 // used in other places, so don't drop it (better leak than crash).
146 // It's possible that overzeleaus dropping happens in some other place
147 #if 0
148 if (mat->pattern)
149 pdf_droppattern(mat->pattern);
150 #endif
151
152 mat->kind = PDF_MPATTERN;
153 if (pat)
154 mat->pattern = pdf_keeppattern(pat);
155 else
156 mat->pattern = nil;
157
158 if (v)
159 return pdf_setcolor(csi, what, v);
160
161 return nil;
162 }
163
164 fz_error *
165 pdf_setshade(pdf_csi *csi, int what, fz_shade *shade)
166 {
167 pdf_gstate *gs = csi->gstate + csi->gtop;
168 fz_error *error;
169 pdf_material *mat;
170
171 error = pdf_flushtext(csi);
172 if (error)
173 return error;
174
175 mat = what == PDF_MFILL ? &gs->fill : &gs->stroke;
176
177 if (mat->shade)
178 fz_dropshade(mat->shade);
179
180 mat->kind = PDF_MSHADE;
181 mat->shade = fz_keepshade(shade);
182
183 return nil;
184 }
185
186 fz_error *
187 pdf_buildstrokepath(pdf_gstate *gs, fz_pathnode *path)
188 {
189 fz_error *error;
190 fz_stroke stroke;
191 fz_dash *dash;
192
193 stroke.linecap = gs->linecap;
194 stroke.linejoin = gs->linejoin;
195 stroke.linewidth = gs->linewidth;
196 stroke.miterlimit = gs->miterlimit;
197
198 if (gs->dashlen)
199 {
200 error = fz_newdash(&dash, gs->dashphase, gs->dashlen, gs->dashlist);
201 if (error)
202 return error;
203 }
204 else
205 dash = nil;
206
207 error = fz_endpath(path, FZ_STROKE, &stroke, dash);
208 if (error) {
209 fz_dropdash(dash);
210 return error;
211 }
212
213 return nil;
214 }
215
216 fz_error *
217 pdf_buildfillpath(pdf_gstate *gs, fz_pathnode *path, int eofill)
218 {
219 return fz_endpath(path, eofill ? FZ_EOFILL : FZ_FILL, nil, nil);
220 }
221
222 static fz_error *
223 addcolorshape(pdf_gstate *gs, fz_node *shape, fz_colorspace *cs, float *v)
224 {
225 fz_error *error;
226 fz_node *mask;
227 fz_node *solid;
228
229 error = fz_newmasknode(&mask);
230 if (error) return error;
231
232 error = fz_newsolidnode(&solid, cs, cs->n, v);
233 if (error) return error;
234
235 fz_insertnodelast(mask, shape);
236 fz_insertnodelast(mask, solid);
237 fz_insertnodelast(gs->head, mask);
238
239 return nil;
240 }
241
242 static fz_error *
243 addinvisibleshape(pdf_gstate *gs, fz_node *shape)
244 {
245 fz_error *error;
246 fz_node *mask;
247 fz_pathnode *path;
248
249 error = fz_newmasknode(&mask);
250 if (error) return error;
251
252 error = fz_newpathnode(&path);
253 if (error) return error;
254 error = fz_endpath(path, FZ_FILL, nil, nil);
255 if (error) return error;
256
257 fz_insertnodelast(mask, (fz_node*)path);
258 fz_insertnodelast(mask, shape);
259 fz_insertnodelast(gs->head, mask);
260
261 return nil;
262 }
263
264 static fz_matrix getmatrix(fz_node *node)
265 {
266 if (node->parent)
267 {
268 fz_matrix ptm = getmatrix(node->parent);
269 if (fz_istransformnode(node))
270 return fz_concat(((fz_transformnode*)node)->m, ptm);
271 return ptm;
272 }
273 if (fz_istransformnode(node))
274 return ((fz_transformnode*)node)->m;
275 return fz_identity();
276 }
277
278 static fz_error *
279 addpatternshape(pdf_gstate *gs, fz_node *shape,
280 pdf_pattern *pat, fz_colorspace *cs, float *v)
281 {
282 fz_error *error;
283 fz_node *xform;
284 fz_node *over;
285 fz_node *mask;
286 fz_node *meta;
287 fz_node *link;
288 fz_matrix ctm;
289 fz_matrix inv;
290 fz_matrix ptm;
291 fz_rect bbox;
292 fz_obj *dict;
293 int x, y, x0, y0, x1, y1;
294
295 /* patterns are painted in user space */
296 ctm = getmatrix(gs->head);
297 inv = fz_invertmatrix(ctm);
298
299 error = fz_newmasknode(&mask);
300 if (error) return error;
301
302 ptm = fz_concat(pat->matrix, fz_invertmatrix(ctm));
303 error = fz_newtransformnode(&xform, ptm);
304 if (error) return error;
305
306 error = fz_packobj(&dict, "<< /Tree %p /XStep %f /YStep %f "
307 " /Matrix[%f %f %f %f %f %f] >>",
308 pat->tree, pat->xstep, pat->ystep,
309 pat->matrix.a, pat->matrix.b,
310 pat->matrix.c, pat->matrix.d,
311 pat->matrix.e, pat->matrix.f);
312 if (error) return error;
313
314 error = fz_newmetanode(&meta, "Pattern", dict);
315 if (error) return error;
316
317 error = fz_newovernode(&over);
318 if (error) return error;
319
320 fz_insertnodelast(mask, shape);
321 fz_insertnodelast(mask, meta);
322 fz_insertnodelast(meta, xform);
323 fz_insertnodelast(xform, over);
324
325 /* get bbox of shape in pattern space for stamping */
326 ptm = fz_concat(ctm, fz_invertmatrix(pat->matrix));
327 bbox = fz_boundnode(shape, ptm);
328
329 /* expand bbox by pattern bbox */
330 bbox.x0 += pat->bbox.x0;
331 bbox.y0 += pat->bbox.y0;
332 bbox.x1 += pat->bbox.x1;
333 bbox.y1 += pat->bbox.y1;
334
335 x0 = fz_floor(bbox.x0 / pat->xstep);
336 y0 = fz_floor(bbox.y0 / pat->ystep);
337 x1 = fz_ceil(bbox.x1 / pat->xstep);
338 y1 = fz_ceil(bbox.y1 / pat->ystep);
339
340 for (y = y0; y <= y1; y++)
341 {
342 for (x = x0; x <= x1; x++)
343 {
344 ptm = fz_translate(x * pat->xstep, y * pat->ystep);
345 error = fz_newtransformnode(&xform, ptm);
346 if (error) return error;
347 error = fz_newlinknode(&link, pat->tree);
348 if (error) return error;
349 fz_insertnodelast(xform, link);
350 fz_insertnodelast(over, xform);
351 }
352 }
353
354 if (pat->ismask)
355 return addcolorshape(gs, mask, cs, v);
356
357 fz_insertnodelast(gs->head, mask);
358 return nil;
359 }
360
361 fz_error *
362 pdf_addshade(pdf_gstate *gs, fz_shade *shade)
363 {
364 fz_error *error;
365 fz_node *node;
366
367 error = fz_newshadenode(&node, shade);
368 if (error) return error;
369
370 fz_insertnodelast(gs->head, node);
371
372 return nil;
373 }
374
375 static fz_error *
376 addshadeshape(pdf_gstate *gs, fz_node *shape, fz_shade *shade)
377 {
378 fz_error *error;
379 fz_node *mask;
380 fz_node *color;
381 fz_node *xform;
382 fz_node *over;
383 fz_node *bgnd;
384 fz_matrix ctm;
385 fz_matrix inv;
386
387 ctm = getmatrix(gs->head);
388 inv = fz_invertmatrix(ctm);
389
390 error = fz_newtransformnode(&xform, inv);
391 if (error) return error;
392
393 error = fz_newmasknode(&mask);
394 if (error) return error;
395
396 error = fz_newshadenode(&color, shade);
397 if (error) return error;
398
399 if (shade->usebackground)
400 {
401 error = fz_newovernode(&over);
402 if (error) return error;
403
404 error = fz_newsolidnode(&bgnd, shade->cs, shade->cs->n, shade->background);
405 if (error) return error;
406
407 fz_insertnodelast(mask, shape);
408 fz_insertnodelast(over, bgnd);
409 fz_insertnodelast(over, color);
410 fz_insertnodelast(xform, over);
411 fz_insertnodelast(mask, xform);
412 fz_insertnodelast(gs->head, mask);
413 }
414 else
415 {
416 fz_insertnodelast(mask, shape);
417 fz_insertnodelast(xform, color);
418 fz_insertnodelast(mask, xform);
419 fz_insertnodelast(gs->head, mask);
420 }
421
422 return nil;
423 }
424
425 fz_error *
426 pdf_addfillshape(pdf_gstate *gs, fz_node *shape)
427 {
428 switch (gs->fill.kind)
429 {
430 case PDF_MNONE:
431 fz_insertnodelast(gs->head, shape);
432 return nil;
433 case PDF_MCOLOR:
434 case PDF_MLAB:
435 case PDF_MINDEXED:
436 return addcolorshape(gs, shape, gs->fill.cs, gs->fill.v);
437 case PDF_MPATTERN:
438 // this is a work-around to make http://kpdf.kde.org/stuff/nytimes-firefox-final.pdf
439 // not crash see http://blog.kowalczyk.info/forum_sumatra/topic.php?TopicId=287&Posts=0
440 if (gs->fill.pattern)
441 return addpatternshape(gs, shape, gs->fill.pattern, gs->fill.cs, gs->fill.v);
442 else
443 return nil;
444 case PDF_MSHADE:
445 return addshadeshape(gs, shape, gs->fill.shade);
446 default:
447 return fz_throw("unimplemented material");
448 }
449 }
450
451 fz_error *
452 pdf_addstrokeshape(pdf_gstate *gs, fz_node *shape)
453 {
454 switch (gs->stroke.kind)
455 {
456 case PDF_MNONE:
457 fz_insertnodelast(gs->head, shape);
458 return nil;
459 case PDF_MCOLOR:
460 case PDF_MLAB:
461 case PDF_MINDEXED:
462 return addcolorshape(gs, shape, gs->stroke.cs, gs->stroke.v);
463 case PDF_MPATTERN:
464 return addpatternshape(gs, shape, gs->stroke.pattern, gs->stroke.cs, gs->stroke.v);
465 case PDF_MSHADE:
466 return addshadeshape(gs, shape, gs->stroke.shade);
467 default:
468 return fz_throw("unimplemented material");
469 }
470 }
471
472 fz_error *
473 pdf_addclipmask(pdf_gstate *gs, fz_node *shape)
474 {
475 fz_error *error;
476 fz_node *mask;
477 fz_node *over;
478
479 error = fz_newmasknode(&mask);
480 if (error) return error;
481
482 error = fz_newovernode(&over);
483 if (error) return error;
484
485 fz_insertnodelast(mask, shape);
486 fz_insertnodelast(mask, over);
487 fz_insertnodelast(gs->head, mask);
488 gs->head = over;
489
490 return nil;
491 }
492
493 fz_error *
494 pdf_addtransform(pdf_gstate *gs, fz_node *transform)
495 {
496 fz_error *error;
497 fz_node *over;
498
499 error = fz_newovernode(&over);
500 if (error) return error;
501
502 fz_insertnodelast(gs->head, transform);
503 fz_insertnodelast(transform, over);
504 gs->head = over;
505
506 return nil;
507 }
508
509 fz_error *
510 pdf_showimage(pdf_csi *csi, pdf_image *img)
511 {
512 fz_error *error;
513 fz_node *mask;
514 fz_node *color;
515 fz_node *shape;
516
517 error = fz_newimagenode(&color, (fz_image*)img);
518 if (error)
519 return error;
520
521 if (img->super.n == 0 && img->super.a == 1)
522 {
523 error = pdf_addfillshape(csi->gstate + csi->gtop, color);
524 if (error) {
525 fz_dropnode(color);
526 return error;
527 }
528 }
529 else
530 {
531 if (img->mask)
532 {
533 error = fz_newimagenode(&shape, (fz_image*)img->mask);
534 if (error) return error;
535 error = fz_newmasknode(&mask);
536 if (error) return error;
537 fz_insertnodelast(mask, shape);
538 fz_insertnodelast(mask, color);
539 fz_insertnodelast(csi->gstate[csi->gtop].head, mask);
540 }
541 else
542 {
543 fz_insertnodelast(csi->gstate[csi->gtop].head, color);
544 }
545 }
546
547 return nil;
548 }
549
550 fz_error *
551 pdf_showpath(pdf_csi *csi,
552 int doclose, int dofill, int dostroke, int evenodd)
553 {
554 pdf_gstate *gstate = csi->gstate + csi->gtop;
555 fz_error *error;
556 fz_pathnode *spath;
557 fz_pathnode *fpath;
558
559 if (doclose)
560 {
561 error = fz_closepath(csi->path);
562 if (error) return error;
563 }
564
565 if (dofill && dostroke)
566 {
567 fpath = csi->path;
568 error = fz_clonepathnode(&spath, fpath);
569 if (error) return error;
570 }
571 else
572 {
573 spath = fpath = csi->path;
574 }
575
576 if (dofill)
577 {
578 error = pdf_buildfillpath(gstate, fpath, evenodd);
579 if (error) return error;
580 error = pdf_addfillshape(gstate, (fz_node*)fpath);
581 if (error) return error;
582 }
583
584 if (dostroke)
585 {
586 error = pdf_buildstrokepath(gstate, spath);
587 if (error) return error;
588 error = pdf_addstrokeshape(gstate, (fz_node*)spath);
589 if (error) return error;
590 }
591
592 if (csi->clip)
593 {
594 fz_pathnode *clip;
595 error = fz_clonepathnode(&clip, csi->path);
596 if (error) return error;
597 error = fz_endpath(clip, FZ_FILL, nil, nil);
598 if (error) return error;
599 error = pdf_addclipmask(gstate, (fz_node*)clip);
600 if (error) return error;
601 csi->clip = 0;
602 }
603
604 if (!dofill && !dostroke)
605 {
606 fz_dropnode((fz_node*)csi->path);
607 }
608
609 csi->path = nil;
610
611 error = fz_newpathnode(&csi->path);
612 if (error) return error;
613
614 return nil;
615 }
616
617 fz_error *
618 pdf_flushtext(pdf_csi *csi)
619 {
620 pdf_gstate *gstate = csi->gstate + csi->gtop;
621 fz_error *error;
622
623 if (csi->text)
624 {
625 switch (csi->textmode)
626 {
627 case 0: /* fill */
628 case 1: /* stroke */
629 case 2: /* stroke + fill */
630 error = pdf_addfillshape(gstate, (fz_node*)csi->text);
631 if (error)
632 return error;
633 break;
634
635 case 3: /* invisible */
636 error = addinvisibleshape(gstate, (fz_node*)csi->text);
637 if (error)
638 return error;
639 break;
640
641 case 4: /* fill + clip */
642 case 5: /* stroke + clip */
643 case 6: /* stroke + fill + clip */
644 {
645 fz_textnode *temp;
646 error = fz_clonetextnode(&temp, csi->text);
647 if (error)
648 return error;
649 error = pdf_addfillshape(gstate, (fz_node*)temp);
650 if (error)
651 return error;
652 }
653 /* fall through */
654
655 case 7: /* invisible clip */
656 if (!csi->textclip)
657 {
658 error = fz_newovernode(&csi->textclip);
659 if (error)
660 return error;
661 }
662 fz_insertnodelast(csi->textclip, (fz_node*)csi->text);
663 break;
664 }
665
666 csi->text = nil;
667 }
668
669 return nil;
670 }
671
672 fz_error *
673 showglyph(pdf_csi *csi, int cid)
674 {
675 pdf_gstate *gstate = csi->gstate + csi->gtop;
676 pdf_font *font = gstate->font;
677 fz_error *error;
678 fz_matrix tsm, trm;
679 float w0, w1, tx, ty;
680 fz_hmtx h;
681 fz_vmtx v;
682
683 tsm.a = gstate->size * gstate->scale;
684 tsm.b = 0;
685 tsm.c = 0;
686 tsm.d = gstate->size;
687 tsm.e = 0;
688 tsm.f = gstate->rise;
689
690 if (font->super.wmode == 1)
691 {
692 v = fz_getvmtx((fz_font*)font, cid);
693 tsm.e -= v.x * gstate->size / 1000.0;
694 tsm.f -= v.y * gstate->size / 1000.0;
695 }
696
697 trm = fz_concat(tsm, csi->tm);
698
699 /* flush buffered text if face or matrix or rendermode has changed */
700 if (!csi->text ||
701 ((fz_font*)font) != csi->text->font ||
702 fabs(trm.a - csi->text->trm.a) > FLT_EPSILON ||
703 fabs(trm.b - csi->text->trm.b) > FLT_EPSILON ||
704 fabs(trm.c - csi->text->trm.c) > FLT_EPSILON ||
705 fabs(trm.d - csi->text->trm.d) > FLT_EPSILON ||
706 gstate->render != csi->textmode)
707 {
708 error = pdf_flushtext(csi);
709 if (error) return error;
710
711 error = fz_newtextnode(&csi->text, (fz_font*)font);
712 if (error) return error;
713
714 csi->text->trm = trm;
715 csi->text->trm.e = 0;
716 csi->text->trm.f = 0;
717 csi->textmode = gstate->render;
718 }
719
720 /* add glyph to textobject */
721 error = fz_addtext(csi->text, cid, trm.e, trm.f);
722 if (error)
723 return error;
724
725 if (font->super.wmode == 0)
726 {
727 h = fz_gethmtx((fz_font*)font, cid);
728 w0 = h.w / 1000.0;
729 tx = (w0 * gstate->size + gstate->charspace) * gstate->scale;
730 csi->tm = fz_concat(fz_translate(tx, 0), csi->tm);
731 }
732 else
733 {
734 w1 = v.w / 1000.0;
735 ty = w1 * gstate->size + gstate->charspace;
736 csi->tm = fz_concat(fz_translate(0, ty), csi->tm);
737 }
738
739 return nil;
740 }
741
742 void
743 showspace(pdf_csi *csi, float tadj)
744 {
745 pdf_gstate *gstate = csi->gstate + csi->gtop;
746 pdf_font *font = gstate->font;
747 if (font->super.wmode == 0)
748 csi->tm = fz_concat(fz_translate(tadj * gstate->scale, 0), csi->tm);
749 else
750 csi->tm = fz_concat(fz_translate(0, tadj), csi->tm);
751 }
752
753 fz_error *
754 pdf_showtext(pdf_csi *csi, fz_obj *text)
755 {
756 pdf_gstate *gstate = csi->gstate + csi->gtop;
757 pdf_font *font = gstate->font;
758 fz_error *error;
759 unsigned char *buf;
760 unsigned char *end;
761 int i, len;
762 int cpt, cid;
763
764 if (fz_isarray(text))
765 {
766 for (i = 0; i < fz_arraylen(text); i++)
767 {
768 fz_obj *item = fz_arrayget(text, i);
769 if (fz_isstring(item))
770 {
771 error = pdf_showtext(csi, item);
772 if (error) return error;
773 }
774 else
775 {
776 showspace(csi, - fz_toreal(item) * gstate->size / 1000.0);
777 }
778 }
779 return nil;
780 }
781
782 buf = fz_tostrbuf(text);
783 len = fz_tostrlen(text);
784 end = buf + len;
785
786 while (buf < end)
787 {
788 buf = pdf_decodecmap(font->encoding, buf, &cpt);
789 cid = pdf_lookupcmap(font->encoding, cpt);
790 if (cid == -1)
791 cid = 0;
792
793 error = showglyph(csi, cid);
794 if (error)
795 return error;
796
797 if (cpt == 32)
798 showspace(csi, gstate->wordspace);
799 }
800
801 return nil;
802 }
803