3 This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks.
5 Copyright (C) 2002-2012 by Jin-Hwan Cho and Shunsaku Hirata,
6 the dvipdfmx project team.
8 Copyright (C) 1998, 1999 by Mark A. Wicks <mwicks@kettering.edu>
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
45 #define ENABLE_SPC_NAMESPACE 1
48 * Following "constant" converts milli-inches to
49 * device (in this case PDF stream) coordinates.
52 #define MI2DEV (0.072/pdf_dev_scale())
55 * Value for 'sh' command 'g' is interpreted as
57 * gray color value 1-g for "solid"
58 * opacity value g for "opacity"
59 * shape value g for "shape"
61 #define TPIC_MODE__FILL_SOLID 0
62 #define TPIC_MODE__FILL_OPACITY 1
63 #define TPIC_MODE__FILL_SHAPE 2
66 # define ISBLANK(c) ( (c) == ' ' || (c) == '\t' )
70 skip_blank (const char **pp, const char *endptr)
73 for ( ; p < endptr && ISBLANK(*p); p++);
85 int fill_shape; /* boolean */
94 static struct spc_tpic_ _tpic_state;
97 /* We use pdf_doc_add_page_content() here
98 * since we always draw isolated graphics.
101 tpic__clear (struct spc_tpic_ *tp)
110 tp->fill_color = 0.0;
115 create_xgstate (double a /* alpha */, int f_ais /* alpha is shape */)
119 dict = pdf_new_dict();
121 pdf_new_name("Type"),
122 pdf_new_name("ExtGState"));
136 check_resourcestatus (const char *category, const char *resname)
138 pdf_obj *dict1, *dict2;
140 dict1 = pdf_doc_current_page_resources();
144 dict2 = pdf_lookup_dict(dict1, category);
146 pdf_obj_typeof(dict2) == PDF_DICT) {
147 if (pdf_lookup_dict(dict2, resname))
154 set_linestyle (double pn, double da)
156 double dp[2]; /* dash pattern */
158 pdf_dev_setlinejoin(1);
159 pdf_dev_setmiterlimit(1.4);
160 pdf_dev_setlinewidth(pn);
163 pdf_dev_setdash(1, dp, 0);
164 pdf_dev_setlinecap(0);
165 } else if (da < 0.0) {
168 pdf_dev_setdash(2, dp, 0);
169 pdf_dev_setlinecap(1);
171 pdf_dev_setlinecap(0);
178 set_fillstyle (double g, double a, int f_ais)
186 alp = round(100.0 * a);
187 sprintf(resname, "_Tps_a%03d_", alp);
188 if (!check_resourcestatus("ExtGState", resname)) {
189 dict = create_xgstate(ROUND(0.01 * alp, 0.01), f_ais);
190 pdf_doc_add_page_resource("ExtGState",
191 resname, pdf_ref_obj(dict));
192 pdf_release_obj(dict);
194 len += sprintf(buf + len, " /%s gs", resname);
196 pdf_doc_add_page_content(buf, len); /* op: gs */
200 pdf_color *sc, *fc, new_fc;
202 pdf_color_get_current (&sc, &fc); /* get stroking and fill colors */
203 pdf_color_brighten_color(&new_fc, fc, g);
204 pdf_dev_set_nonstrokingcolor(&new_fc);
211 set_styles (struct spc_tpic_ *tp,
219 pdf_setmatrix (&M, 1.0, 0.0, 0.0, -1.0, c->x, c->y);
223 set_linestyle(pn, da);
229 if (tp->mode.fill == TPIC_MODE__FILL_SOLID || !tp->fill_color) {
230 g = 1.0 - tp->fill_color;
237 f_ais = (tp->mode.fill == TPIC_MODE__FILL_SHAPE) ? 1 : 0;
239 set_fillstyle(g, a, f_ais);
244 showpath (int f_vp, int f_fs) /* visible_path, fill_shape */
248 pdf_dev_flushpath('b', PDF_FILL_RULE_NONZERO);
250 pdf_dev_flushpath('S', PDF_FILL_RULE_NONZERO);
254 * Acrobat claims 'Q' as illegal operation when there are unfinished
255 * path (a path without path-painting operator applied)?
258 pdf_dev_flushpath('f', PDF_FILL_RULE_NONZERO);
265 #define CLOSED_PATH(s) (\
266 (s)->points[0].x == (s)->points[(s)->num_points-1].x && \
267 (s)->points[0].y == (s)->points[(s)->num_points-1].y \
271 tpic__polyline (struct spc_tpic_ *tp,
276 double pn = tp->pen_size;
277 int f_fs = tp->fill_shape;
280 /* Shading is applied only to closed path. */
281 f_fs = CLOSED_PATH(tp) ? f_fs : 0;
282 f_vp = (pn > 0.0) ? f_vp : 0;
287 set_styles(tp, c, f_fs, f_vp, pn, da);
289 pdf_dev_moveto(tp->points[0].x, tp->points[0].y);
290 for (i = 0; i < tp->num_points; i++)
291 pdf_dev_lineto(tp->points[i].x, tp->points[i].y);
293 showpath(f_vp, f_fs);
305 * "Tpic: Pic for TEX", Tim Morgan, Original by Brian Kernighan, p.20:
307 * A spline is a smooth curve guided by a set of straight lines just
308 * like the line above. It begins at the same place, ends at the same
309 * place, and in between is tangent to the mid-point of each guiding
310 * line. The syntax for a spline is identical to a (path) line except
311 * for using spline instead of line.
313 * Spline is not a curve drawn by spline-fitting points p0, p1, ..., pn,
314 * defined by tpic special "pa" command. Instead, a path defined by set
315 * of points p0, p1, ... is guiding line mentioned above.
317 * Dvipsk draws them as a straight line from p0 to q1 = (p0 + p1)/2,
318 * followed by a quadratic B-spline curve with starting point q1, (off-
319 * curve) control point p1, end point q2 = (p1 + p2)/2, ..., and a
320 * straight line from qn to pn.
324 tpic__spline (struct spc_tpic_ *tp,
330 double pn = tp->pen_size;
331 int f_fs = tp->fill_shape;
334 f_fs = CLOSED_PATH(tp) ? f_fs : 0;
335 f_vp = (pn > 0.0) ? f_vp : 0;
340 set_styles(tp, c, f_fs, f_vp, pn, da);
342 pdf_dev_moveto(tp->points[0].x, tp->points[0].y);
344 v[0] = 0.5 * (tp->points[0].x + tp->points[1].x);
345 v[1] = 0.5 * (tp->points[0].y + tp->points[1].y);
346 pdf_dev_lineto(v[0], v[1]);
347 for (i = 1; i < tp->num_points - 1; i++) {
348 /* B-spline control points */
349 v[0] = 0.5 * (tp->points[i-1].x + tp->points[i].x);
350 v[1] = 0.5 * (tp->points[i-1].y + tp->points[i].y);
351 v[2] = tp->points[i].x;
352 v[3] = tp->points[i].y;
353 v[4] = 0.5 * (tp->points[i].x + tp->points[i+1].x);
354 v[5] = 0.5 * (tp->points[i].y + tp->points[i+1].y);
355 pdf_dev_bspline(v[0], v[1], v[2], v[3], v[4], v[5]);
357 pdf_dev_lineto(tp->points[i].x, tp->points[i].y);
359 showpath(f_vp, f_fs);
369 tpic__arc (struct spc_tpic_ *tp,
373 double *v /* 6 numbers */ )
375 double pn = tp->pen_size;
376 int f_fs = tp->fill_shape;
378 f_fs = (round(fabs(v[4] - v[5]) + 0.5) >= 360) ? f_fs : 0;
379 f_vp = (pn > 0.0) ? f_vp : 0;
384 set_styles(tp, c, f_fs, f_vp, pn, da);
386 pdf_dev_arcx(v[0], v[1], v[2], v[3], v[4], v[5], +1, 0.0);
388 showpath(f_vp, f_fs);
399 spc_currentpoint (struct spc_env *spe, long *pg, pdf_coord *cp)
409 spc_handler_tpic_pn (struct spc_env *spe,
410 struct spc_arg *ap ) /* , void *dp) */
412 struct spc_tpic_ *tp = &_tpic_state;
415 ASSERT(spe && ap && tp);
417 skip_blank(&ap->curptr, ap->endptr);
418 q = parse_float_decimal(&ap->curptr, ap->endptr);
420 spc_warn(spe, "Invalid pen size specified?");
423 tp->pen_size = atof(q) * MI2DEV;
430 spc_handler_tpic_pa (struct spc_env *spe,
431 struct spc_arg *ap ) /* , void *dp) */
433 struct spc_tpic_ *tp = &_tpic_state;
438 ASSERT(spe && ap && tp);
440 skip_blank(&ap->curptr, ap->endptr);
442 i < 2 && ap->curptr < ap->endptr; i++) {
443 q = parse_float_decimal(&ap->curptr, ap->endptr);
445 spc_warn(spe, "Missing numbers for TPIC \"pa\" command.");
450 skip_blank(&ap->curptr, ap->endptr);
453 spc_warn(spe, "Invalid arg for TPIC \"pa\" command.");
457 if (tp->num_points >= tp->max_points) {
458 tp->max_points += 256;
459 tp->points = RENEW(tp->points, tp->max_points, pdf_coord);
461 tp->points[tp->num_points].x = v[0] * MI2DEV;
462 tp->points[tp->num_points].y = v[1] * MI2DEV;
469 spc_handler_tpic_fp (struct spc_env *spe,
470 struct spc_arg *ap ) /* , void *dp) */
472 struct spc_tpic_ *tp = &_tpic_state;
476 ASSERT(spe && ap && tp);
478 if (tp->num_points <= 1) {
479 spc_warn(spe, "Too few points (< 2) for polyline path.");
483 spc_currentpoint(spe, &pg, &cp);
485 return tpic__polyline(tp, &cp, 1, 0.0);
489 spc_handler_tpic_ip (struct spc_env *spe,
490 struct spc_arg *ap ) /* , void *dp) */
492 struct spc_tpic_ *tp = &_tpic_state;
496 ASSERT(spe && ap && tp);
498 if (tp->num_points <= 1) {
499 spc_warn(spe, "Too few points (< 2) for polyline path.");
503 spc_currentpoint(spe, &pg, &cp);
505 return tpic__polyline(tp, &cp, 0, 0.0);
509 spc_handler_tpic_da (struct spc_env *spe,
510 struct spc_arg *ap ) /* , void *dp) */
512 struct spc_tpic_ *tp = &_tpic_state;
518 ASSERT(spe && ap && tp);
520 skip_blank(&ap->curptr, ap->endptr);
521 q = parse_float_decimal(&ap->curptr, ap->endptr);
526 if (tp->num_points <= 1) {
527 spc_warn(spe, "Too few points (< 2) for polyline path.");
531 spc_currentpoint(spe, &pg, &cp);
533 return tpic__polyline(tp, &cp, 1, da);
537 spc_handler_tpic_dt (struct spc_env *spe,
538 struct spc_arg *ap ) /* , void *dp) */
540 struct spc_tpic_ *tp = &_tpic_state;
546 ASSERT(spe && ap && tp);
548 skip_blank(&ap->curptr, ap->endptr);
549 q = parse_float_decimal(&ap->curptr, ap->endptr);
554 if (tp->num_points <= 1) {
555 spc_warn(spe, "Too few points (< 2) for polyline path.");
559 spc_currentpoint(spe, &pg, &cp);
561 return tpic__polyline(tp, &cp, 1, da);
565 spc_handler_tpic_sp (struct spc_env *spe,
566 struct spc_arg *ap ) /* , void *dp) */
568 struct spc_tpic_ *tp = &_tpic_state;
574 ASSERT(spe && ap && tp);
576 skip_blank(&ap->curptr, ap->endptr);
577 q = parse_float_decimal(&ap->curptr, ap->endptr);
582 if (tp->num_points <= 2) {
583 spc_warn(spe, "Too few points (< 3) for spline path.");
587 spc_currentpoint(spe, &pg, &cp);
589 return tpic__spline(tp, &cp, 1, da);
593 spc_handler_tpic_ar (struct spc_env *spe,
594 struct spc_arg *ap ) /* , void *dp) */
596 struct spc_tpic_ *tp = &_tpic_state;
603 ASSERT(spe && ap && tp);
605 skip_blank(&ap->curptr, ap->endptr);
607 i < 6 && ap->curptr < ap->endptr; i++) {
608 q = parse_float_decimal(&ap->curptr, ap->endptr);
610 spc_warn(spe, "Invalid args. in TPIC \"ar\" command.");
615 skip_blank(&ap->curptr, ap->endptr);
618 spc_warn(spe, "Invalid arg for TPIC \"ar\" command.");
622 v[0] *= MI2DEV; v[1] *= MI2DEV;
623 v[2] *= MI2DEV; v[3] *= MI2DEV;
624 v[4] *= 180.0 / M_PI;
625 v[5] *= 180.0 / M_PI;
627 spc_currentpoint(spe, &pg, &cp);
629 return tpic__arc(tp, &cp, 1, 0.0, v);
633 spc_handler_tpic_ia (struct spc_env *spe,
634 struct spc_arg *ap ) /* , void *dp) */
636 struct spc_tpic_ *tp = &_tpic_state;
643 ASSERT(spe && ap && tp);
645 skip_blank(&ap->curptr, ap->endptr);
647 i < 6 && ap->curptr < ap->endptr; i++) {
648 q = parse_float_decimal(&ap->curptr, ap->endptr);
650 spc_warn(spe, "Invalid args. in TPIC \"ia\" command.");
655 skip_blank(&ap->curptr, ap->endptr);
658 spc_warn(spe, "Invalid arg for TPIC \"ia\" command.");
662 v[0] *= MI2DEV; v[1] *= MI2DEV;
663 v[2] *= MI2DEV; v[3] *= MI2DEV;
664 v[4] *= 180.0 / M_PI;
665 v[5] *= 180.0 / M_PI;
667 spc_currentpoint(spe, &pg, &cp);
669 return tpic__arc(tp, &cp, 0, 0.0, v);
673 spc_handler_tpic_sh (struct spc_env *spe,
674 struct spc_arg *ap ) /* , void *dp) */
676 struct spc_tpic_ *tp = &_tpic_state;
679 ASSERT(spe && ap && tp);
682 tp->fill_color = 0.5;
684 skip_blank(&ap->curptr, ap->endptr);
685 q = parse_float_decimal(&ap->curptr, ap->endptr);
689 if (g >= 0.0 && g <= 1.0)
692 WARN("Invalid fill color specified: %g\n", g);
701 spc_handler_tpic_wh (struct spc_env *spe,
702 struct spc_arg *ap ) /* , void *dp) */
704 struct spc_tpic_ *tp = &_tpic_state;
706 ASSERT(spe && ap && tp);
709 tp->fill_color = 0.0;
715 spc_handler_tpic_bk (struct spc_env *spe,
716 struct spc_arg *ap ) /* , void *dp) */
718 struct spc_tpic_ *tp = &_tpic_state;
720 ASSERT(spe && ap && tp);
723 tp->fill_color = 1.0;
729 spc_handler_tpic_tx (struct spc_env *spe,
730 struct spc_arg *ap ) /* , void *dp) */
732 struct spc_tpic_ *tp = &_tpic_state;
734 ASSERT(spe && ap && tp);
736 spc_warn(spe, "TPIC command \"tx\" not supported.");
743 spc_handler_tpic__init (struct spc_env *spe,
744 struct spc_arg *ap, void *dp)
746 struct spc_tpic_ *tp = dp;
749 tp->mode.fill = TPIC_MODE__FILL_SOLID;
753 tp->fill_color = 0.0;
759 if (tp->mode.fill != TPIC_MODE__FILL_SOLID && pdf_get_version() < 4) {
760 spc_warn(spe, "Tpic shading support requires PDF version 1.4.");
761 tp->mode.fill = TPIC_MODE__FILL_SOLID;
768 spc_handler_tpic__bophook (struct spc_env *spe,
769 struct spc_arg *ap, void *dp)
771 struct spc_tpic_ *tp = dp;
781 spc_handler_tpic__eophook (struct spc_env *spe,
782 struct spc_arg *ap, void *dp)
784 struct spc_tpic_ *tp = dp;
788 if (tp->num_points > 0)
789 spc_warn(spe, "Unflushed tpic path at end of the page.");
796 spc_handler_tpic__clean (struct spc_env *spe,
797 struct spc_arg *ap, void *dp)
799 struct spc_tpic_ *tp = dp;
803 if (tp->num_points > 0)
804 spc_warn(spe, "Unflushed tpic path at end of the document.");
815 tpic_set_fill_mode (int mode)
817 struct spc_tpic_ *tp = &_tpic_state;
818 tp->mode.fill = mode;
823 spc_tpic_at_begin_page (void)
825 struct spc_tpic_ *tp = &_tpic_state;
826 return spc_handler_tpic__bophook(NULL, NULL, tp);
830 spc_tpic_at_end_page (void)
832 struct spc_tpic_ *tp = &_tpic_state;
833 return spc_handler_tpic__eophook(NULL, NULL, tp);
838 spc_tpic_at_begin_document (void)
840 struct spc_tpic_ *tp = &_tpic_state;
841 return spc_handler_tpic__init(NULL, NULL, tp);
845 spc_tpic_at_end_document (void)
847 struct spc_tpic_ *tp = &_tpic_state;
848 return spc_handler_tpic__clean(NULL, NULL, tp);
853 #include "pdfparse.h" /* parse_val_ident :( */
856 spc_parse_kvpairs (struct spc_env *spe, struct spc_arg *ap)
862 dict = pdf_new_dict();
864 skip_blank(&ap->curptr, ap->endptr);
865 while (!error && ap->curptr < ap->endptr) {
866 kp = parse_val_ident(&ap->curptr, ap->endptr);
869 skip_blank(&ap->curptr, ap->endptr);
870 if (ap->curptr < ap->endptr &&
871 ap->curptr[0] == '=') {
873 skip_blank(&ap->curptr, ap->endptr);
874 if (ap->curptr == ap->endptr) {
879 vp = parse_c_string(&ap->curptr, ap->endptr);
885 pdf_new_string(vp, strlen(vp) + 1)); /* NULL terminate */
889 /* Treated as 'flag' */
896 skip_blank(&ap->curptr, ap->endptr);
900 pdf_release_obj(dict);
908 tpic_filter_getopts (pdf_obj *kp, pdf_obj *vp, void *dp)
910 struct spc_tpic_ *tp = dp;
914 ASSERT( kp && vp && tp );
916 k = pdf_name_value(kp);
917 if (!strcmp(k, "fill-mode")) {
918 if (pdf_obj_typeof(vp) != PDF_STRING) {
919 WARN("Invalid value for TPIC option fill-mode...");
922 v = pdf_string_value(vp);
923 if (!strcmp(v, "shape"))
924 tp->mode.fill = TPIC_MODE__FILL_SHAPE;
925 else if (!strcmp(v, "opacity"))
926 tp->mode.fill = TPIC_MODE__FILL_OPACITY;
927 else if (!strcmp(v, "solid"))
928 tp->mode.fill = TPIC_MODE__FILL_SOLID;
930 WARN("Invalid value for TPIC option fill-mode: %s", v);
935 WARN("Unrecognized option for TPIC special handler: %s", k);
943 spc_handler_tpic__setopts (struct spc_env *spe,
944 struct spc_arg *ap ) /* , void *dp) */
946 struct spc_tpic_ *tp = &_tpic_state;
950 dict = spc_parse_kvpairs(spe, ap);
953 error = pdf_foreach_dict(dict, tpic_filter_getopts, tp);
955 if (tp->mode.fill != TPIC_MODE__FILL_SOLID &&
956 pdf_get_version() < 4) {
957 spc_warn(spe, "Transparent fill mode requires PDF version 1.4.");
958 tp->mode.fill = TPIC_MODE__FILL_SOLID;
967 static struct spc_handler tpic_handlers[] = {
968 {"pn", spc_handler_tpic_pn},
969 {"pa", spc_handler_tpic_pa},
970 {"fp", spc_handler_tpic_fp},
971 {"ip", spc_handler_tpic_ip},
972 {"da", spc_handler_tpic_da},
973 {"dt", spc_handler_tpic_dt},
974 {"sp", spc_handler_tpic_sp},
975 {"ar", spc_handler_tpic_ar},
976 {"ia", spc_handler_tpic_ia},
977 {"sh", spc_handler_tpic_sh},
978 {"wh", spc_handler_tpic_wh},
979 {"bk", spc_handler_tpic_bk},
980 {"tx", spc_handler_tpic_tx}
984 spc_tpic_check_special (const char *buf, long len)
988 const char *p, *endptr;
994 skip_blank(&p, endptr);
995 #if ENABLE_SPC_NAMESPACE
996 if (p + strlen("tpic:") < endptr &&
997 !memcmp(p, "tpic:", strlen("tpic:")))
999 p += strlen("tpic:");
1003 q = parse_c_ident(&p, endptr);
1007 else if (q && hasnsp && !strcmp(q, "__setopt__")) {
1014 i < sizeof(tpic_handlers)/sizeof(struct spc_handler); i++) {
1015 if (!strcmp(q, tpic_handlers[i].key)) {
1028 spc_tpic_setup_handler (struct spc_handler *sph,
1029 struct spc_env *spe, struct spc_arg *ap)
1032 int i, hasnsp = 0, error = -1;
1034 ASSERT(sph && spe && ap);
1036 skip_blank(&ap->curptr, ap->endptr);
1037 #if ENABLE_SPC_NAMESPACE
1038 if (ap->curptr + strlen("tpic:") < ap->endptr &&
1039 !memcmp(ap->curptr, "tpic:", strlen("tpic:")))
1041 ap->curptr += strlen("tpic:");
1045 q = parse_c_ident(&ap->curptr, ap->endptr);
1049 else if (q && hasnsp && !strcmp(q, "__setopt__")) {
1051 ap->command = "__setopt__";
1053 sph->exec = spc_handler_tpic__setopts;
1054 skip_blank(&ap->curptr, ap->endptr);
1060 i < sizeof(tpic_handlers)/sizeof(struct spc_handler); i++) {
1061 if (!strcmp(q, tpic_handlers[i].key)) {
1062 ap->command = tpic_handlers[i].key;
1064 sph->exec = tpic_handlers[i].exec;
1065 skip_blank(&ap->curptr, ap->endptr);
1079 spc_load_tpic_special (struct spc_env *spe, pdf_obj *lopts)
1081 struct spc_def *spd;
1082 struct spc_tpic_ *sd;
1084 sd = NEW(1, struct spc_tpic_);
1086 spd = NEW(1, struct spc_def);
1089 spc_def_init (spd, &spc_handler_tpic__init);
1090 spc_def_setopts(spd, &spc_handler_tpic__setopts);
1091 spc_def_bophook(spd, &spc_handler_tpic__bophook);
1092 spc_def_eophook(spd, &spc_handler_tpic__eophook);
1093 spc_def_clean (spd, &spc_handler_tpic__clean);
1095 spc_def_func(spd, "pn", &spc_handler_tpic_pn);
1096 spc_def_func(spd, "pa", &spc_handler_tpic_pa);
1097 spc_def_func(spd, "fp", &spc_handler_tpic_fp);
1098 spc_def_func(spd, "ip", &spc_handler_tpic_ip);
1099 spc_def_func(spd, "da", &spc_handler_tpic_da);
1100 spc_def_func(spd, "dt", &spc_handler_tpic_dt);
1101 spc_def_func(spd, "sp", &spc_handler_tpic_sp);
1102 spc_def_func(spd, "ar", &spc_handler_tpic_ar);
1103 spc_def_func(spd, "ia", &spc_handler_tpic_ia);
1104 spc_def_func(spd, "sh", &spc_handler_tpic_sh);
1105 spc_def_func(spd, "wh", &spc_handler_tpic_wh);
1106 spc_def_func(spd, "bk", &spc_handler_tpic_bk);
1107 spc_def_func(spd, "tx", &spc_handler_tpic_tx);
1109 spc_add_special(spe, "tpic", spd, sd);