Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  1. #include "fitz.h"
  2. #include "muxps.h"
  3.  
  4. static fz_point
  5. fz_currentpoint(fz_path *path)
  6. {
  7.         fz_point c, m;
  8.         int i;
  9.  
  10.         c.x = c.y = m.x = m.y = 0;
  11.         i = 0;
  12.  
  13.         while (i < path->len)
  14.         {
  15.                 switch (path->items[i++].k)
  16.                 {
  17.                 case FZ_MOVETO:
  18.                         m.x = c.x = path->items[i++].v;
  19.                         m.y = c.y = path->items[i++].v;
  20.                         break;
  21.                 case FZ_LINETO:
  22.                         c.x = path->items[i++].v;
  23.                         c.y = path->items[i++].v;
  24.                         break;
  25.                 case FZ_CURVETO:
  26.                         i += 4;
  27.                         c.x = path->items[i++].v;
  28.                         c.y = path->items[i++].v;
  29.                         break;
  30.                 case FZ_CLOSE_PATH:
  31.                         c = m;
  32.                 }
  33.         }
  34.  
  35.         return c;
  36. }
  37.  
  38. /* Draw an arc segment transformed by the matrix, we approximate with straight
  39.  * line segments. We cannot use the fz_arc function because they only draw
  40.  * circular arcs, we need to transform the line to make them elliptical but
  41.  * without transforming the line width.
  42.  */
  43. static void
  44. xps_draw_arc_segment(fz_path *path, fz_matrix mtx, float th0, float th1, int iscw)
  45. {
  46.         float t, d;
  47.         fz_point p;
  48.  
  49.         while (th1 < th0)
  50.                 th1 += (float)M_PI * 2;
  51.  
  52.         d = (float)M_PI / 180; /* 1-degree precision */
  53.  
  54.         if (iscw)
  55.         {
  56.                 p.x = cosf(th0);
  57.                 p.y = sinf(th0);
  58.                 p = fz_transform_point(mtx, p);
  59.                 fz_lineto(path, p.x, p.y);
  60.                 for (t = th0; t < th1; t += d)
  61.                 {
  62.                         p.x = cosf(t);
  63.                         p.y = sinf(t);
  64.                         p = fz_transform_point(mtx, p);
  65.                         fz_lineto(path, p.x, p.y);
  66.                 }
  67.                 p.x = cosf(th1);
  68.                 p.y = sinf(th1);
  69.                 p = fz_transform_point(mtx, p);
  70.                 fz_lineto(path, p.x, p.y);
  71.         }
  72.         else
  73.         {
  74.                 th0 += (float)M_PI * 2;
  75.                 p.x = cosf(th0);
  76.                 p.y = sinf(th0);
  77.                 p = fz_transform_point(mtx, p);
  78.                 fz_lineto(path, p.x, p.y);
  79.                 for (t = th0; t > th1; t -= d)
  80.                 {
  81.                         p.x = cosf(t);
  82.                         p.y = sinf(t);
  83.                         p = fz_transform_point(mtx, p);
  84.                         fz_lineto(path, p.x, p.y);
  85.                 }
  86.                 p.x = cosf(th1);
  87.                 p.y = sinf(th1);
  88.                 p = fz_transform_point(mtx, p);
  89.                 fz_lineto(path, p.x, p.y);
  90.         }
  91. }
  92.  
  93. /* Given two vectors find the angle between them. */
  94. static float
  95. angle_between(const fz_point u, const fz_point v)
  96. {
  97.         float det = u.x * v.y - u.y * v.x;
  98.         float sign = (det < 0 ? -1 : 1);
  99.         float magu = u.x * u.x + u.y * u.y;
  100.         float magv = v.x * v.x + v.y * v.y;
  101.         float udotv = u.x * v.x + u.y * v.y;
  102.         float t = udotv / (magu * magv);
  103.         /* guard against rounding errors when near |1| (where acos will return NaN) */
  104.         if (t < -1) t = -1;
  105.         if (t > 1) t = 1;
  106.         return sign * acosf(t);
  107. }
  108.  
  109. static void
  110. xps_draw_arc(fz_path *path,
  111.                 float size_x, float size_y, float rotation_angle,
  112.                 int is_large_arc, int is_clockwise,
  113.                 float point_x, float point_y)
  114. {
  115.         fz_matrix rotmat, revmat;
  116.         fz_matrix mtx;
  117.         fz_point pt;
  118.         float rx, ry;
  119.         float x1, y1, x2, y2;
  120.         float x1t, y1t;
  121.         float cxt, cyt, cx, cy;
  122.         float t1, t2, t3;
  123.         float sign;
  124.         float th1, dth;
  125.  
  126.         pt = fz_currentpoint(path);
  127.         x1 = pt.x;
  128.         y1 = pt.y;
  129.         x2 = point_x;
  130.         y2 = point_y;
  131.         rx = size_x;
  132.         ry = size_y;
  133.  
  134.         if (is_clockwise != is_large_arc)
  135.                 sign = 1;
  136.         else
  137.                 sign = -1;
  138.  
  139.         rotmat = fz_rotate(rotation_angle);
  140.         revmat = fz_rotate(-rotation_angle);
  141.  
  142.         /* http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes */
  143.         /* Conversion from endpoint to center parameterization */
  144.  
  145.         /* F.6.6.1 -- ensure radii are positive and non-zero */
  146.         rx = fabsf(rx);
  147.         ry = fabsf(ry);
  148.         if (rx < 0.001f || ry < 0.001f)
  149.         {
  150.                 fz_lineto(path, x2, y2);
  151.                 return;
  152.         }
  153.  
  154.         /* F.6.5.1 */
  155.         pt.x = (x1 - x2) / 2;
  156.         pt.y = (y1 - y2) / 2;
  157.         pt = fz_transform_vector(revmat, pt);
  158.         x1t = pt.x;
  159.         y1t = pt.y;
  160.  
  161.         /* F.6.6.2 -- ensure radii are large enough */
  162.         t1 = (x1t * x1t) / (rx * rx) + (y1t * y1t) / (ry * ry);
  163.         if (t1 > 1)
  164.         {
  165.                 rx = rx * sqrtf(t1);
  166.                 ry = ry * sqrtf(t1);
  167.         }
  168.  
  169.         /* F.6.5.2 */
  170.         t1 = (rx * rx * ry * ry) - (rx * rx * y1t * y1t) - (ry * ry * x1t * x1t);
  171.         t2 = (rx * rx * y1t * y1t) + (ry * ry * x1t * x1t);
  172.         t3 = t1 / t2;
  173.         /* guard against rounding errors; sqrt of negative numbers is bad for your health */
  174.         if (t3 < 0) t3 = 0;
  175.         t3 = sqrtf(t3);
  176.  
  177.         cxt = sign * t3 * (rx * y1t) / ry;
  178.         cyt = sign * t3 * -(ry * x1t) / rx;
  179.  
  180.         /* F.6.5.3 */
  181.         pt.x = cxt;
  182.         pt.y = cyt;
  183.         pt = fz_transform_vector(rotmat, pt);
  184.         cx = pt.x + (x1 + x2) / 2;
  185.         cy = pt.y + (y1 + y2) / 2;
  186.  
  187.         /* F.6.5.4 */
  188.         {
  189.                 fz_point coord1, coord2, coord3, coord4;
  190.                 coord1.x = 1;
  191.                 coord1.y = 0;
  192.                 coord2.x = (x1t - cxt) / rx;
  193.                 coord2.y = (y1t - cyt) / ry;
  194.                 coord3.x = (x1t - cxt) / rx;
  195.                 coord3.y = (y1t - cyt) / ry;
  196.                 coord4.x = (-x1t - cxt) / rx;
  197.                 coord4.y = (-y1t - cyt) / ry;
  198.                 th1 = angle_between(coord1, coord2);
  199.                 dth = angle_between(coord3, coord4);
  200.                 if (dth < 0 && !is_clockwise)
  201.                         dth += (((float)M_PI / 180) * 360);
  202.                 if (dth > 0 && is_clockwise)
  203.                         dth -= (((float)M_PI / 180) * 360);
  204.         }
  205.  
  206.         mtx = fz_identity;
  207.         mtx = fz_concat(fz_translate(cx, cy), mtx);
  208.         mtx = fz_concat(fz_rotate(rotation_angle), mtx);
  209.         mtx = fz_concat(fz_scale(rx, ry), mtx);
  210.         xps_draw_arc_segment(path, mtx, th1, th1 + dth, is_clockwise);
  211.  
  212.         fz_lineto(path, point_x, point_y);
  213. }
  214.  
  215. /*
  216.  * Parse an abbreviated geometry string, and call
  217.  * ghostscript moveto/lineto/curveto functions to
  218.  * build up a path.
  219.  */
  220.  
  221. static fz_path *
  222. xps_parse_abbreviated_geometry(xps_context *ctx, char *geom, int *fill_rule)
  223. {
  224.         fz_path *path;
  225.         char **args;
  226.         char **pargs;
  227.         char *s = geom;
  228.         fz_point pt;
  229.         int i, n;
  230.         int cmd, old;
  231.         float x1, y1, x2, y2, x3, y3;
  232.         float smooth_x, smooth_y; /* saved cubic bezier control point for smooth curves */
  233.         int reset_smooth;
  234.  
  235.         path = fz_new_path();
  236.  
  237.         args = fz_calloc(strlen(geom) + 1, sizeof(char*));
  238.         pargs = args;
  239.  
  240.         while (*s)
  241.         {
  242.                 if ((*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z'))
  243.                 {
  244.                         *pargs++ = s++;
  245.                 }
  246.                 else if ((*s >= '0' && *s <= '9') || *s == '.' || *s == '+' || *s == '-' || *s == 'e' || *s == 'E')
  247.                 {
  248.                         *pargs++ = s;
  249.                         while ((*s >= '0' && *s <= '9') || *s == '.' || *s == '+' || *s == '-' || *s == 'e' || *s == 'E')
  250.                                 s ++;
  251.                 }
  252.                 else
  253.                 {
  254.                         s++;
  255.                 }
  256.         }
  257.  
  258.         pargs[0] = s;
  259.         pargs[1] = 0;
  260.  
  261.         n = pargs - args;
  262.         i = 0;
  263.  
  264.         old = 0;
  265.  
  266.         reset_smooth = 1;
  267.         smooth_x = 0;
  268.         smooth_y = 0;
  269.  
  270.         while (i < n)
  271.         {
  272.                 cmd = args[i][0];
  273.                 if (cmd == '+' || cmd == '.' || cmd == '-' || (cmd >= '0' && cmd <= '9'))
  274.                         cmd = old; /* it's a number, repeat old command */
  275.                 else
  276.                         i ++;
  277.  
  278.                 if (reset_smooth)
  279.                 {
  280.                         smooth_x = 0;
  281.                         smooth_y = 0;
  282.                 }
  283.  
  284.                 reset_smooth = 1;
  285.  
  286.                 switch (cmd)
  287.                 {
  288.                 case 'F':
  289.                         *fill_rule = atoi(args[i]);
  290.                         i ++;
  291.                         break;
  292.  
  293.                 case 'M':
  294.                         fz_moveto(path, fz_atof(args[i]), fz_atof(args[i+1]));
  295.                         i += 2;
  296.                         break;
  297.                 case 'm':
  298.                         pt = fz_currentpoint(path);
  299.                         fz_moveto(path, pt.x + fz_atof(args[i]), pt.y + fz_atof(args[i+1]));
  300.                         i += 2;
  301.                         break;
  302.  
  303.                 case 'L':
  304.                         fz_lineto(path, fz_atof(args[i]), fz_atof(args[i+1]));
  305.                         i += 2;
  306.                         break;
  307.                 case 'l':
  308.                         pt = fz_currentpoint(path);
  309.                         fz_lineto(path, pt.x + fz_atof(args[i]), pt.y + fz_atof(args[i+1]));
  310.                         i += 2;
  311.                         break;
  312.  
  313.                 case 'H':
  314.                         pt = fz_currentpoint(path);
  315.                         fz_lineto(path, fz_atof(args[i]), pt.y);
  316.                         i += 1;
  317.                         break;
  318.                 case 'h':
  319.                         pt = fz_currentpoint(path);
  320.                         fz_lineto(path, pt.x + fz_atof(args[i]), pt.y);
  321.                         i += 1;
  322.                         break;
  323.  
  324.                 case 'V':
  325.                         pt = fz_currentpoint(path);
  326.                         fz_lineto(path, pt.x, fz_atof(args[i]));
  327.                         i += 1;
  328.                         break;
  329.                 case 'v':
  330.                         pt = fz_currentpoint(path);
  331.                         fz_lineto(path, pt.x, pt.y + fz_atof(args[i]));
  332.                         i += 1;
  333.                         break;
  334.  
  335.                 case 'C':
  336.                         x1 = fz_atof(args[i+0]);
  337.                         y1 = fz_atof(args[i+1]);
  338.                         x2 = fz_atof(args[i+2]);
  339.                         y2 = fz_atof(args[i+3]);
  340.                         x3 = fz_atof(args[i+4]);
  341.                         y3 = fz_atof(args[i+5]);
  342.                         fz_curveto(path, x1, y1, x2, y2, x3, y3);
  343.                         i += 6;
  344.                         reset_smooth = 0;
  345.                         smooth_x = x3 - x2;
  346.                         smooth_y = y3 - y2;
  347.                         break;
  348.  
  349.                 case 'c':
  350.                         pt = fz_currentpoint(path);
  351.                         x1 = fz_atof(args[i+0]) + pt.x;
  352.                         y1 = fz_atof(args[i+1]) + pt.y;
  353.                         x2 = fz_atof(args[i+2]) + pt.x;
  354.                         y2 = fz_atof(args[i+3]) + pt.y;
  355.                         x3 = fz_atof(args[i+4]) + pt.x;
  356.                         y3 = fz_atof(args[i+5]) + pt.y;
  357.                         fz_curveto(path, x1, y1, x2, y2, x3, y3);
  358.                         i += 6;
  359.                         reset_smooth = 0;
  360.                         smooth_x = x3 - x2;
  361.                         smooth_y = y3 - y2;
  362.                         break;
  363.  
  364.                 case 'S':
  365.                         pt = fz_currentpoint(path);
  366.                         x1 = fz_atof(args[i+0]);
  367.                         y1 = fz_atof(args[i+1]);
  368.                         x2 = fz_atof(args[i+2]);
  369.                         y2 = fz_atof(args[i+3]);
  370.                         fz_curveto(path, pt.x + smooth_x, pt.y + smooth_y, x1, y1, x2, y2);
  371.                         i += 4;
  372.                         reset_smooth = 0;
  373.                         smooth_x = x2 - x1;
  374.                         smooth_y = y2 - y1;
  375.                         break;
  376.  
  377.                 case 's':
  378.                         pt = fz_currentpoint(path);
  379.                         x1 = fz_atof(args[i+0]) + pt.x;
  380.                         y1 = fz_atof(args[i+1]) + pt.y;
  381.                         x2 = fz_atof(args[i+2]) + pt.x;
  382.                         y2 = fz_atof(args[i+3]) + pt.y;
  383.                         fz_curveto(path, pt.x + smooth_x, pt.y + smooth_y, x1, y1, x2, y2);
  384.                         i += 4;
  385.                         reset_smooth = 0;
  386.                         smooth_x = x2 - x1;
  387.                         smooth_y = y2 - y1;
  388.                         break;
  389.  
  390.                 case 'Q':
  391.                         pt = fz_currentpoint(path);
  392.                         x1 = fz_atof(args[i+0]);
  393.                         y1 = fz_atof(args[i+1]);
  394.                         x2 = fz_atof(args[i+2]);
  395.                         y2 = fz_atof(args[i+3]);
  396.                         fz_curveto(path,
  397.                                 (pt.x + 2 * x1) / 3, (pt.y + 2 * y1) / 3,
  398.                                 (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3,
  399.                                 x2, y2);
  400.                         i += 4;
  401.                         break;
  402.                 case 'q':
  403.                         pt = fz_currentpoint(path);
  404.                         x1 = fz_atof(args[i+0]) + pt.x;
  405.                         y1 = fz_atof(args[i+1]) + pt.y;
  406.                         x2 = fz_atof(args[i+2]) + pt.x;
  407.                         y2 = fz_atof(args[i+3]) + pt.y;
  408.                         fz_curveto(path,
  409.                                 (pt.x + 2 * x1) / 3, (pt.y + 2 * y1) / 3,
  410.                                 (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3,
  411.                                 x2, y2);
  412.                         i += 4;
  413.                         break;
  414.  
  415.                 case 'A':
  416.                         xps_draw_arc(path,
  417.                                 fz_atof(args[i+0]), fz_atof(args[i+1]), fz_atof(args[i+2]),
  418.                                 atoi(args[i+3]), atoi(args[i+4]),
  419.                                 fz_atof(args[i+5]), fz_atof(args[i+6]));
  420.                         i += 7;
  421.                         break;
  422.                 case 'a':
  423.                         pt = fz_currentpoint(path);
  424.                         xps_draw_arc(path,
  425.                                 fz_atof(args[i+0]), fz_atof(args[i+1]), fz_atof(args[i+2]),
  426.                                 atoi(args[i+3]), atoi(args[i+4]),
  427.                                 fz_atof(args[i+5]) + pt.x, fz_atof(args[i+6]) + pt.y);
  428.                         i += 7;
  429.                         break;
  430.  
  431.                 case 'Z':
  432.                 case 'z':
  433.                         fz_closepath(path);
  434.                         break;
  435.  
  436.                 default:
  437.                         /* eek */
  438.                         break;
  439.                 }
  440.  
  441.                 old = cmd;
  442.         }
  443.  
  444.         fz_free(args);
  445.         return path;
  446. }
  447.  
  448. static void
  449. xps_parse_arc_segment(fz_path *path, xml_element *root, int stroking, int *skipped_stroke)
  450. {
  451.         /* ArcSegment pretty much follows the SVG algorithm for converting an
  452.          * arc in endpoint representation to an arc in centerpoint
  453.          * representation. Once in centerpoint it can be given to the
  454.          * graphics library in the form of a postscript arc. */
  455.  
  456.         float rotation_angle;
  457.         int is_large_arc, is_clockwise;
  458.         float point_x, point_y;
  459.         float size_x, size_y;
  460.         int is_stroked;
  461.  
  462.         char *point_att = xml_att(root, "Point");
  463.         char *size_att = xml_att(root, "Size");
  464.         char *rotation_angle_att = xml_att(root, "RotationAngle");
  465.         char *is_large_arc_att = xml_att(root, "IsLargeArc");
  466.         char *sweep_direction_att = xml_att(root, "SweepDirection");
  467.         char *is_stroked_att = xml_att(root, "IsStroked");
  468.  
  469.         if (!point_att || !size_att || !rotation_angle_att || !is_large_arc_att || !sweep_direction_att)
  470.         {
  471.                 fz_warn("ArcSegment element is missing attributes");
  472.                 return;
  473.         }
  474.  
  475.         is_stroked = 1;
  476.         if (is_stroked_att && !strcmp(is_stroked_att, "false"))
  477.                         is_stroked = 0;
  478.         if (!is_stroked)
  479.                 *skipped_stroke = 1;
  480.  
  481.         sscanf(point_att, "%g,%g", &point_x, &point_y);
  482.         sscanf(size_att, "%g,%g", &size_x, &size_y);
  483.         rotation_angle = fz_atof(rotation_angle_att);
  484.         is_large_arc = !strcmp(is_large_arc_att, "true");
  485.         is_clockwise = !strcmp(sweep_direction_att, "Clockwise");
  486.  
  487.         if (stroking && !is_stroked)
  488.         {
  489.                 fz_moveto(path, point_x, point_y);
  490.                 return;
  491.         }
  492.  
  493.         xps_draw_arc(path, size_x, size_y, rotation_angle, is_large_arc, is_clockwise, point_x, point_y);
  494. }
  495.  
  496. static void
  497. xps_parse_poly_quadratic_bezier_segment(fz_path *path, xml_element *root, int stroking, int *skipped_stroke)
  498. {
  499.         char *points_att = xml_att(root, "Points");
  500.         char *is_stroked_att = xml_att(root, "IsStroked");
  501.         float x[2], y[2];
  502.         int is_stroked;
  503.         fz_point pt;
  504.         char *s;
  505.         int n;
  506.  
  507.         if (!points_att)
  508.         {
  509.                 fz_warn("PolyQuadraticBezierSegment element has no points");
  510.                 return;
  511.         }
  512.  
  513.         is_stroked = 1;
  514.         if (is_stroked_att && !strcmp(is_stroked_att, "false"))
  515.                         is_stroked = 0;
  516.         if (!is_stroked)
  517.                 *skipped_stroke = 1;
  518.  
  519.         s = points_att;
  520.         n = 0;
  521.         while (*s != 0)
  522.         {
  523.                 while (*s == ' ') s++;
  524.                 sscanf(s, "%g,%g", &x[n], &y[n]);
  525.                 while (*s != ' ' && *s != 0) s++;
  526.                 n ++;
  527.                 if (n == 2)
  528.                 {
  529.                         if (stroking && !is_stroked)
  530.                         {
  531.                                 fz_moveto(path, x[1], y[1]);
  532.                         }
  533.                         else
  534.                         {
  535.                                 pt = fz_currentpoint(path);
  536.                                 fz_curveto(path,
  537.                                                 (pt.x + 2 * x[0]) / 3, (pt.y + 2 * y[0]) / 3,
  538.                                                 (x[1] + 2 * x[0]) / 3, (y[1] + 2 * y[0]) / 3,
  539.                                                 x[1], y[1]);
  540.                         }
  541.                         n = 0;
  542.                 }
  543.         }
  544. }
  545.  
  546. static void
  547. xps_parse_poly_bezier_segment(fz_path *path, xml_element *root, int stroking, int *skipped_stroke)
  548. {
  549.         char *points_att = xml_att(root, "Points");
  550.         char *is_stroked_att = xml_att(root, "IsStroked");
  551.         float x[3], y[3];
  552.         int is_stroked;
  553.         char *s;
  554.         int n;
  555.  
  556.         if (!points_att)
  557.         {
  558.                 fz_warn("PolyBezierSegment element has no points");
  559.                 return;
  560.         }
  561.  
  562.         is_stroked = 1;
  563.         if (is_stroked_att && !strcmp(is_stroked_att, "false"))
  564.                         is_stroked = 0;
  565.         if (!is_stroked)
  566.                 *skipped_stroke = 1;
  567.  
  568.         s = points_att;
  569.         n = 0;
  570.         while (*s != 0)
  571.         {
  572.                 while (*s == ' ') s++;
  573.                 sscanf(s, "%g,%g", &x[n], &y[n]);
  574.                 while (*s != ' ' && *s != 0) s++;
  575.                 n ++;
  576.                 if (n == 3)
  577.                 {
  578.                         if (stroking && !is_stroked)
  579.                                 fz_moveto(path, x[2], y[2]);
  580.                         else
  581.                                 fz_curveto(path, x[0], y[0], x[1], y[1], x[2], y[2]);
  582.                         n = 0;
  583.                 }
  584.         }
  585. }
  586.  
  587. static void
  588. xps_parse_poly_line_segment(fz_path *path, xml_element *root, int stroking, int *skipped_stroke)
  589. {
  590.         char *points_att = xml_att(root, "Points");
  591.         char *is_stroked_att = xml_att(root, "IsStroked");
  592.         int is_stroked;
  593.         float x, y;
  594.         char *s;
  595.  
  596.         if (!points_att)
  597.         {
  598.                 fz_warn("PolyLineSegment element has no points");
  599.                 return;
  600.         }
  601.  
  602.         is_stroked = 1;
  603.         if (is_stroked_att && !strcmp(is_stroked_att, "false"))
  604.                         is_stroked = 0;
  605.         if (!is_stroked)
  606.                 *skipped_stroke = 1;
  607.  
  608.         s = points_att;
  609.         while (*s != 0)
  610.         {
  611.                 while (*s == ' ') s++;
  612.                 sscanf(s, "%g,%g", &x, &y);
  613.                 if (stroking && !is_stroked)
  614.                         fz_moveto(path, x, y);
  615.                 else
  616.                         fz_lineto(path, x, y);
  617.                 while (*s != ' ' && *s != 0) s++;
  618.         }
  619. }
  620.  
  621. static void
  622. xps_parse_path_figure(fz_path *path, xml_element *root, int stroking)
  623. {
  624.         xml_element *node;
  625.  
  626.         char *is_closed_att;
  627.         char *start_point_att;
  628.         char *is_filled_att;
  629.  
  630.         int is_closed = 0;
  631.         int is_filled = 1;
  632.         float start_x = 0;
  633.         float start_y = 0;
  634.  
  635.         int skipped_stroke = 0;
  636.  
  637.         is_closed_att = xml_att(root, "IsClosed");
  638.         start_point_att = xml_att(root, "StartPoint");
  639.         is_filled_att = xml_att(root, "IsFilled");
  640.  
  641.         if (is_closed_att)
  642.                 is_closed = !strcmp(is_closed_att, "true");
  643.         if (is_filled_att)
  644.                 is_filled = !strcmp(is_filled_att, "true");
  645.         if (start_point_att)
  646.                 sscanf(start_point_att, "%g,%g", &start_x, &start_y);
  647.  
  648.         if (!stroking && !is_filled) /* not filled, when filling */
  649.                 return;
  650.  
  651.         fz_moveto(path, start_x, start_y);
  652.  
  653.         for (node = xml_down(root); node; node = xml_next(node))
  654.         {
  655.                 if (!strcmp(xml_tag(node), "ArcSegment"))
  656.                         xps_parse_arc_segment(path, node, stroking, &skipped_stroke);
  657.                 if (!strcmp(xml_tag(node), "PolyBezierSegment"))
  658.                         xps_parse_poly_bezier_segment(path, node, stroking, &skipped_stroke);
  659.                 if (!strcmp(xml_tag(node), "PolyLineSegment"))
  660.                         xps_parse_poly_line_segment(path, node, stroking, &skipped_stroke);
  661.                 if (!strcmp(xml_tag(node), "PolyQuadraticBezierSegment"))
  662.                         xps_parse_poly_quadratic_bezier_segment(path, node, stroking, &skipped_stroke);
  663.         }
  664.  
  665.         if (is_closed)
  666.         {
  667.                 if (stroking && skipped_stroke)
  668.                         fz_lineto(path, start_x, start_y); /* we've skipped using fz_moveto... */
  669.                 else
  670.                         fz_closepath(path); /* no skipped segments, safe to closepath properly */
  671.         }
  672. }
  673.  
  674. fz_path *
  675. xps_parse_path_geometry(xps_context *ctx, xps_resource *dict, xml_element *root, int stroking, int *fill_rule)
  676. {
  677.         xml_element *node;
  678.  
  679.         char *figures_att;
  680.         char *fill_rule_att;
  681.         char *transform_att;
  682.  
  683.         xml_element *transform_tag = NULL;
  684.         xml_element *figures_tag = NULL; /* only used by resource */
  685.  
  686.         fz_matrix transform;
  687.         fz_path *path;
  688.  
  689.         figures_att = xml_att(root, "Figures");
  690.         fill_rule_att = xml_att(root, "FillRule");
  691.         transform_att = xml_att(root, "Transform");
  692.  
  693.         for (node = xml_down(root); node; node = xml_next(node))
  694.         {
  695.                 if (!strcmp(xml_tag(node), "PathGeometry.Transform"))
  696.                         transform_tag = xml_down(node);
  697.         }
  698.  
  699.         xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL);
  700.         xps_resolve_resource_reference(ctx, dict, &figures_att, &figures_tag, NULL);
  701.  
  702.         if (fill_rule_att)
  703.         {
  704.                 if (!strcmp(fill_rule_att, "NonZero"))
  705.                         *fill_rule = 1;
  706.                 if (!strcmp(fill_rule_att, "EvenOdd"))
  707.                         *fill_rule = 0;
  708.         }
  709.  
  710.         transform = fz_identity;
  711.         if (transform_att)
  712.                 xps_parse_render_transform(ctx, transform_att, &transform);
  713.         if (transform_tag)
  714.                 xps_parse_matrix_transform(ctx, transform_tag, &transform);
  715.  
  716.         if (figures_att)
  717.                 path = xps_parse_abbreviated_geometry(ctx, figures_att, fill_rule);
  718.         else
  719.                 path = fz_new_path();
  720.  
  721.         if (figures_tag)
  722.                 xps_parse_path_figure(path, figures_tag, stroking);
  723.  
  724.         for (node = xml_down(root); node; node = xml_next(node))
  725.         {
  726.                 if (!strcmp(xml_tag(node), "PathFigure"))
  727.                         xps_parse_path_figure(path, node, stroking);
  728.         }
  729.  
  730.         if (transform_att || transform_tag)
  731.                 fz_transform_path(path, transform);
  732.  
  733.         return path;
  734. }
  735.  
  736. static int
  737. xps_parse_line_cap(char *attr)
  738. {
  739.         if (attr)
  740.         {
  741.                 if (!strcmp(attr, "Flat")) return 0;
  742.                 if (!strcmp(attr, "Round")) return 1;
  743.                 if (!strcmp(attr, "Square")) return 2;
  744.                 if (!strcmp(attr, "Triangle")) return 3;
  745.         }
  746.         return 0;
  747. }
  748.  
  749. void
  750. xps_clip(xps_context *ctx, fz_matrix ctm, xps_resource *dict, char *clip_att, xml_element *clip_tag)
  751. {
  752.         fz_path *path;
  753.         int fill_rule = 0;
  754.  
  755.         if (clip_att)
  756.                 path = xps_parse_abbreviated_geometry(ctx, clip_att, &fill_rule);
  757.         else if (clip_tag)
  758.                 path = xps_parse_path_geometry(ctx, dict, clip_tag, 0, &fill_rule);
  759.         else
  760.                 path = fz_new_path();
  761.         fz_clip_path(ctx->dev, path, NULL, fill_rule == 0, ctm);
  762.         fz_free_path(path);
  763. }
  764.  
  765. /*
  766.  * Parse an XPS <Path> element, and call relevant ghostscript
  767.  * functions for drawing and/or clipping the child elements.
  768.  */
  769.  
  770. void
  771. xps_parse_path(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *root)
  772. {
  773.         xml_element *node;
  774.  
  775.         char *fill_uri;
  776.         char *stroke_uri;
  777.         char *opacity_mask_uri;
  778.  
  779.         char *transform_att;
  780.         char *clip_att;
  781.         char *data_att;
  782.         char *fill_att;
  783.         char *stroke_att;
  784.         char *opacity_att;
  785.         char *opacity_mask_att;
  786.  
  787.         xml_element *transform_tag = NULL;
  788.         xml_element *clip_tag = NULL;
  789.         xml_element *data_tag = NULL;
  790.         xml_element *fill_tag = NULL;
  791.         xml_element *stroke_tag = NULL;
  792.         xml_element *opacity_mask_tag = NULL;
  793.  
  794.         char *fill_opacity_att = NULL;
  795.         char *stroke_opacity_att = NULL;
  796.  
  797.         char *stroke_dash_array_att;
  798.         char *stroke_dash_cap_att;
  799.         char *stroke_dash_offset_att;
  800.         char *stroke_end_line_cap_att;
  801.         char *stroke_start_line_cap_att;
  802.         char *stroke_line_join_att;
  803.         char *stroke_miter_limit_att;
  804.         char *stroke_thickness_att;
  805.  
  806.         fz_stroke_state stroke;
  807.         fz_matrix transform;
  808.         float samples[32];
  809.         fz_colorspace *colorspace;
  810.         fz_path *path;
  811.         fz_rect area;
  812.         int fill_rule;
  813.  
  814.         /*
  815.          * Extract attributes and extended attributes.
  816.          */
  817.  
  818.         transform_att = xml_att(root, "RenderTransform");
  819.         clip_att = xml_att(root, "Clip");
  820.         data_att = xml_att(root, "Data");
  821.         fill_att = xml_att(root, "Fill");
  822.         stroke_att = xml_att(root, "Stroke");
  823.         opacity_att = xml_att(root, "Opacity");
  824.         opacity_mask_att = xml_att(root, "OpacityMask");
  825.  
  826.         stroke_dash_array_att = xml_att(root, "StrokeDashArray");
  827.         stroke_dash_cap_att = xml_att(root, "StrokeDashCap");
  828.         stroke_dash_offset_att = xml_att(root, "StrokeDashOffset");
  829.         stroke_end_line_cap_att = xml_att(root, "StrokeEndLineCap");
  830.         stroke_start_line_cap_att = xml_att(root, "StrokeStartLineCap");
  831.         stroke_line_join_att = xml_att(root, "StrokeLineJoin");
  832.         stroke_miter_limit_att = xml_att(root, "StrokeMiterLimit");
  833.         stroke_thickness_att = xml_att(root, "StrokeThickness");
  834.  
  835.         for (node = xml_down(root); node; node = xml_next(node))
  836.         {
  837.                 if (!strcmp(xml_tag(node), "Path.RenderTransform"))
  838.                         transform_tag = xml_down(node);
  839.                 if (!strcmp(xml_tag(node), "Path.OpacityMask"))
  840.                         opacity_mask_tag = xml_down(node);
  841.                 if (!strcmp(xml_tag(node), "Path.Clip"))
  842.                         clip_tag = xml_down(node);
  843.                 if (!strcmp(xml_tag(node), "Path.Fill"))
  844.                         fill_tag = xml_down(node);
  845.                 if (!strcmp(xml_tag(node), "Path.Stroke"))
  846.                         stroke_tag = xml_down(node);
  847.                 if (!strcmp(xml_tag(node), "Path.Data"))
  848.                         data_tag = xml_down(node);
  849.         }
  850.  
  851.         fill_uri = base_uri;
  852.         stroke_uri = base_uri;
  853.         opacity_mask_uri = base_uri;
  854.  
  855.         xps_resolve_resource_reference(ctx, dict, &data_att, &data_tag, NULL);
  856.         xps_resolve_resource_reference(ctx, dict, &clip_att, &clip_tag, NULL);
  857.         xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL);
  858.         xps_resolve_resource_reference(ctx, dict, &fill_att, &fill_tag, &fill_uri);
  859.         xps_resolve_resource_reference(ctx, dict, &stroke_att, &stroke_tag, &stroke_uri);
  860.         xps_resolve_resource_reference(ctx, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri);
  861.  
  862.         /*
  863.          * Act on the information we have gathered:
  864.          */
  865.  
  866.         if (!data_att && !data_tag)
  867.                 return;
  868.  
  869.         if (fill_tag && !strcmp(xml_tag(fill_tag), "SolidColorBrush"))
  870.         {
  871.                 fill_opacity_att = xml_att(fill_tag, "Opacity");
  872.                 fill_att = xml_att(fill_tag, "Color");
  873.                 fill_tag = NULL;
  874.         }
  875.  
  876.         if (stroke_tag && !strcmp(xml_tag(stroke_tag), "SolidColorBrush"))
  877.         {
  878.                 stroke_opacity_att = xml_att(stroke_tag, "Opacity");
  879.                 stroke_att = xml_att(stroke_tag, "Color");
  880.                 stroke_tag = NULL;
  881.         }
  882.  
  883.         stroke.start_cap = xps_parse_line_cap(stroke_start_line_cap_att);
  884.         stroke.dash_cap = xps_parse_line_cap(stroke_dash_cap_att);
  885.         stroke.end_cap = xps_parse_line_cap(stroke_end_line_cap_att);
  886.  
  887.         stroke.linejoin = 0;
  888.         if (stroke_line_join_att)
  889.         {
  890.                 if (!strcmp(stroke_line_join_att, "Miter")) stroke.linejoin = 0;
  891.                 if (!strcmp(stroke_line_join_att, "Round")) stroke.linejoin = 1;
  892.                 if (!strcmp(stroke_line_join_att, "Bevel")) stroke.linejoin = 2;
  893.         }
  894.  
  895.         stroke.miterlimit = 10;
  896.         if (stroke_miter_limit_att)
  897.                 stroke.miterlimit = fz_atof(stroke_miter_limit_att);
  898.  
  899.         stroke.linewidth = 1;
  900.         if (stroke_thickness_att)
  901.                 stroke.linewidth = fz_atof(stroke_thickness_att);
  902.  
  903.         stroke.dash_phase = 0;
  904.         stroke.dash_len = 0;
  905.         if (stroke_dash_array_att)
  906.         {
  907.                 char *s = stroke_dash_array_att;
  908.  
  909.                 if (stroke_dash_offset_att)
  910.                         stroke.dash_phase = fz_atof(stroke_dash_offset_att) * stroke.linewidth;
  911.  
  912.                 while (*s && stroke.dash_len < nelem(stroke.dash_list))
  913.                 {
  914.                         while (*s == ' ')
  915.                                 s++;
  916.                         stroke.dash_list[stroke.dash_len++] = fz_atof(s) * stroke.linewidth;
  917.                         while (*s && *s != ' ')
  918.                                 s++;
  919.                 }
  920.         }
  921.  
  922.         transform = fz_identity;
  923.         if (transform_att)
  924.                 xps_parse_render_transform(ctx, transform_att, &transform);
  925.         if (transform_tag)
  926.                 xps_parse_matrix_transform(ctx, transform_tag, &transform);
  927.         ctm = fz_concat(transform, ctm);
  928.  
  929.         if (clip_att || clip_tag)
  930.                 xps_clip(ctx, ctm, dict, clip_att, clip_tag);
  931.  
  932.         fill_rule = 0;
  933.         if (data_att)
  934.                 path = xps_parse_abbreviated_geometry(ctx, data_att, &fill_rule);
  935.         else if (data_tag)
  936.                 path = xps_parse_path_geometry(ctx, dict, data_tag, 0, &fill_rule);
  937.  
  938.         if (stroke_att || stroke_tag)
  939.                 area = fz_bound_path(path, &stroke, ctm);
  940.         else
  941.                 area = fz_bound_path(path, NULL, ctm);
  942.  
  943.         xps_begin_opacity(ctx, ctm, area, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);
  944.  
  945.         if (fill_att)
  946.         {
  947.                 xps_parse_color(ctx, base_uri, fill_att, &colorspace, samples);
  948.                 if (fill_opacity_att)
  949.                         samples[0] = fz_atof(fill_opacity_att);
  950.                 xps_set_color(ctx, colorspace, samples);
  951.  
  952.                 fz_fill_path(ctx->dev, path, fill_rule == 0, ctm,
  953.                         ctx->colorspace, ctx->color, ctx->alpha);
  954.         }
  955.  
  956.         if (fill_tag)
  957.         {
  958.                 area = fz_bound_path(path, NULL, ctm);
  959.  
  960.                 fz_clip_path(ctx->dev, path, NULL, fill_rule == 0, ctm);
  961.                 xps_parse_brush(ctx, ctm, area, fill_uri, dict, fill_tag);
  962.                 fz_pop_clip(ctx->dev);
  963.         }
  964.  
  965.         if (stroke_att)
  966.         {
  967.                 xps_parse_color(ctx, base_uri, stroke_att, &colorspace, samples);
  968.                 if (stroke_opacity_att)
  969.                         samples[0] = fz_atof(stroke_opacity_att);
  970.                 xps_set_color(ctx, colorspace, samples);
  971.  
  972.                 fz_stroke_path(ctx->dev, path, &stroke, ctm,
  973.                         ctx->colorspace, ctx->color, ctx->alpha);
  974.         }
  975.  
  976.         if (stroke_tag)
  977.         {
  978.                 fz_clip_stroke_path(ctx->dev, path, NULL, &stroke, ctm);
  979.                 xps_parse_brush(ctx, ctm, area, stroke_uri, dict, stroke_tag);
  980.                 fz_pop_clip(ctx->dev);
  981.         }
  982.  
  983.         xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);
  984.  
  985.         fz_free_path(path);
  986.         path = NULL;
  987.  
  988.         if (clip_att || clip_tag)
  989.                 fz_pop_clip(ctx->dev);
  990. }
  991.