void xps_parse_render_transform(xps_document *doc, char *transform, fz_matrix *matrix) { float args[6]; char *s = transform; int i; args[0] = 1; args[1] = 0; args[2] = 0; args[3] = 1; args[4] = 0; args[5] = 0; for (i = 0; i < 6 && *s; i++) { args[i] = fz_atof(s); while (*s && *s != ',') s++; if (*s == ',') s++; } matrix->a = args[0]; matrix->b = args[1]; matrix->c = args[2]; matrix->d = args[3]; matrix->e = args[4]; matrix->f = args[5]; }
static float pdf_extract_font_size(pdf_xref *xref, char *appearance, char **font_name) { fz_context *ctx = xref->ctx; fz_stream *stream = fz_open_memory(ctx, appearance, strlen(appearance)); float font_size = 0; int tok, len; *font_name = NULL; do { fz_error error = pdf_lex(&tok, stream, xref->scratch, sizeof(xref->scratch), &len); if (error || tok == PDF_TOK_EOF) { fz_free(ctx, *font_name); *font_name = NULL; break; } if (tok == PDF_TOK_NAME) { fz_free(ctx, *font_name); *font_name = fz_strdup(ctx, xref->scratch); } else if (tok == PDF_TOK_REAL || tok == PDF_TOK_INT) { font_size = fz_atof(xref->scratch); } } while (tok != PDF_TOK_KEYWORD || strcmp(xref->scratch, "Tf") != 0); fz_close(stream); return font_size; }
fz_error pdf_parse_stm_obj(fz_obj **op, pdf_xref *xref, fz_stream *file, char *buf, int cap) { fz_error error; int tok; int len; error = pdf_lex(&tok, file, buf, cap, &len); if (error) return fz_rethrow(error, "cannot parse token in object stream"); switch (tok) { case PDF_TOK_OPEN_ARRAY: error = pdf_parse_array(op, xref, file, buf, cap); if (error) return fz_rethrow(error, "cannot parse object stream"); break; case PDF_TOK_OPEN_DICT: error = pdf_parse_dict(op, xref, file, buf, cap); if (error) return fz_rethrow(error, "cannot parse object stream"); break; case PDF_TOK_NAME: *op = fz_new_name(buf); break; case PDF_TOK_REAL: *op = fz_new_real(fz_atof(buf)); break; case PDF_TOK_STRING: *op = fz_new_string(buf, len); break; case PDF_TOK_TRUE: *op = fz_new_bool(1); break; case PDF_TOK_FALSE: *op = fz_new_bool(0); break; case PDF_TOK_NULL: *op = fz_new_null(); break; case PDF_TOK_INT: *op = fz_new_int(atoi(buf)); break; default: return fz_throw("unknown token in object stream"); } return fz_okay; }
/* * compute decimal integer m, exp such that: * f = m*10^exp * m is as short as possible with losing exactness * assumes special cases (NaN, +Inf, -Inf) have been handled. */ void fz_ftoa(float f, char *s, int *exp, int *neg, int *ns) { char buf[40], *p = buf; int i; for (i = 0; i < 10; ++i) { sprintf(buf, "%.*e", i, f); if (fz_atof(buf) == f) break; } if (*p == '-') { *neg = 1; ++p; } else *neg = 0; *ns = 0; while (*p && *p != 'e') { if (*p >= '0' && *p <= '9') { *ns += 1; *s++ = *p; } ++p; } *exp = fz_atoi(p+1) - (*ns) + 1; }
void xps_parse_rectangle(xps_document *doc, char *text, fz_rect *rect) { float args[4]; char *s = text; int i; args[0] = 0; args[1] = 0; args[2] = 1; args[3] = 1; for (i = 0; i < 4 && *s; i++) { args[i] = fz_atof(s); while (*s && *s != ',') s++; if (*s == ',') s++; } rect->x0 = args[0]; rect->y0 = args[1]; rect->x1 = args[0] + args[2]; rect->y1 = args[1] + args[3]; }
static void xps_load_links_in_glyphs(fz_context *ctx, xps_document *doc, const fz_matrix *ctm, char *base_uri, xps_resource *dict, fz_xml *root, fz_link **link) { char *navigate_uri_att = fz_xml_att(root, "FixedPage.NavigateUri"); if (navigate_uri_att) { char *transform_att = fz_xml_att(root, "RenderTransform"); fz_xml *transform_tag = fz_xml_down(fz_xml_find_down(root, "Path.RenderTransform")); char *bidi_level_att = fz_xml_att(root, "BidiLevel"); char *font_size_att = fz_xml_att(root, "FontRenderingEmSize"); char *font_uri_att = fz_xml_att(root, "FontUri"); char *origin_x_att = fz_xml_att(root, "OriginX"); char *origin_y_att = fz_xml_att(root, "OriginY"); char *is_sideways_att = fz_xml_att(root, "IsSideways"); char *indices_att = fz_xml_att(root, "Indices"); char *unicode_att = fz_xml_att(root, "UnicodeString"); char *style_att = fz_xml_att(root, "StyleSimulations"); int is_sideways = 0; int bidi_level = 0; fz_matrix local_ctm; fz_font *font; fz_text *text; fz_rect area; xps_resolve_resource_reference(ctx, doc, dict, &transform_att, &transform_tag, NULL); xps_parse_transform(ctx, doc, transform_att, transform_tag, &local_ctm, ctm); if (is_sideways_att) is_sideways = !strcmp(is_sideways_att, "true"); if (bidi_level_att) bidi_level = atoi(bidi_level_att); font = xps_lookup_font(ctx, doc, base_uri, font_uri_att, style_att); text = xps_parse_glyphs_imp(ctx, doc, &local_ctm, font, fz_atof(font_size_att), fz_atof(origin_x_att), fz_atof(origin_y_att), is_sideways, bidi_level, indices_att, unicode_att); fz_bound_text(ctx, text, NULL, &local_ctm, &area); fz_drop_text(ctx, text); fz_drop_font(ctx, font); xps_add_link(ctx, doc, &area, base_uri, navigate_uri_att, link); } }
void xps_begin_opacity(fz_context *ctx, xps_document *doc, const fz_matrix *ctm, const fz_rect *area, char *base_uri, xps_resource *dict, char *opacity_att, fz_xml *opacity_mask_tag) { fz_device *dev = doc->dev; float opacity; if (!opacity_att && !opacity_mask_tag) return; opacity = 1; if (opacity_att) opacity = fz_atof(opacity_att); if (opacity_mask_tag && !strcmp(fz_xml_tag(opacity_mask_tag), "SolidColorBrush")) { char *scb_opacity_att = fz_xml_att(opacity_mask_tag, "Opacity"); char *scb_color_att = fz_xml_att(opacity_mask_tag, "Color"); if (scb_opacity_att) opacity = opacity * fz_atof(scb_opacity_att); if (scb_color_att) { fz_colorspace *colorspace; float samples[FZ_MAX_COLORS]; xps_parse_color(ctx, doc, base_uri, scb_color_att, &colorspace, samples); opacity = opacity * samples[0]; } opacity_mask_tag = NULL; } if (doc->opacity_top + 1 < nelem(doc->opacity)) { doc->opacity[doc->opacity_top + 1] = doc->opacity[doc->opacity_top] * opacity; doc->opacity_top++; } if (opacity_mask_tag) { fz_begin_mask(ctx, dev, area, 0, NULL, NULL); xps_parse_brush(ctx, doc, ctm, area, base_uri, dict, opacity_mask_tag); fz_end_mask(ctx, dev); } }
void xps_begin_opacity(xps_document *doc, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, char *opacity_att, xml_element *opacity_mask_tag) { float opacity; if (!opacity_att && !opacity_mask_tag) return; opacity = 1; if (opacity_att) opacity = fz_atof(opacity_att); if (opacity_mask_tag && !strcmp(xml_tag(opacity_mask_tag), "SolidColorBrush")) { char *scb_opacity_att = xml_att(opacity_mask_tag, "Opacity"); char *scb_color_att = xml_att(opacity_mask_tag, "Color"); if (scb_opacity_att) opacity = opacity * fz_atof(scb_opacity_att); if (scb_color_att) { fz_colorspace *colorspace; float samples[32]; xps_parse_color(doc, base_uri, scb_color_att, &colorspace, samples); opacity = opacity * samples[0]; } opacity_mask_tag = NULL; } if (doc->opacity_top + 1 < nelem(doc->opacity)) { doc->opacity[doc->opacity_top + 1] = doc->opacity[doc->opacity_top] * opacity; doc->opacity_top++; } if (opacity_mask_tag) { fz_begin_mask(doc->dev, area, 0, NULL, NULL); xps_parse_brush(doc, ctm, area, base_uri, dict, opacity_mask_tag); fz_end_mask(doc->dev); } }
float svg_parse_number(const char *str, float min, float max, float inherit) { float x; if (!strcmp(str, "inherit")) return inherit; x = fz_atof(str); if (x < min) return min; if (x > max) return max; return x; }
static char * xps_parse_real_num(char *s, float *number) { char buf[64]; char *p = buf; while (is_real_num_char(*s)) *p++ = *s++; *p = 0; if (buf[0]) *number = fz_atof(buf); return s; }
static void xps_parse_arc_segment(fz_context *doc, fz_path *path, fz_xml *root, int stroking, int *skipped_stroke) { /* ArcSegment pretty much follows the SVG algorithm for converting an * arc in endpoint representation to an arc in centerpoint * representation. Once in centerpoint it can be given to the * graphics library in the form of a postscript arc. */ float rotation_angle; int is_large_arc, is_clockwise; float point_x, point_y; float size_x, size_y; int is_stroked; char *point_att = fz_xml_att(root, "Point"); char *size_att = fz_xml_att(root, "Size"); char *rotation_angle_att = fz_xml_att(root, "RotationAngle"); char *is_large_arc_att = fz_xml_att(root, "IsLargeArc"); char *sweep_direction_att = fz_xml_att(root, "SweepDirection"); char *is_stroked_att = fz_xml_att(root, "IsStroked"); if (!point_att || !size_att || !rotation_angle_att || !is_large_arc_att || !sweep_direction_att) { fz_warn(doc, "ArcSegment element is missing attributes"); return; } is_stroked = 1; if (is_stroked_att && !strcmp(is_stroked_att, "false")) is_stroked = 0; if (!is_stroked) *skipped_stroke = 1; point_x = point_y = 0; size_x = size_y = 0; xps_parse_point(point_att, &point_x, &point_y); xps_parse_point(size_att, &size_x, &size_y); rotation_angle = fz_atof(rotation_angle_att); is_large_arc = !strcmp(is_large_arc_att, "true"); is_clockwise = !strcmp(sweep_direction_att, "Clockwise"); if (stroking && !is_stroked) { fz_moveto(doc, path, point_x, point_y); return; } xps_draw_arc(doc, path, size_x, size_y, rotation_angle, is_large_arc, is_clockwise, point_x, point_y); }
void svg_parse_document_bounds(fz_context *ctx, svg_document *doc, fz_xml *root) { char *version_att; char *w_att; char *h_att; char *viewbox_att; int version; if (!fz_xml_is_tag(root, "svg")) fz_throw(ctx, FZ_ERROR_GENERIC, "expected svg element (found %s)", fz_xml_tag(root)); version_att = fz_xml_att(root, "version"); w_att = fz_xml_att(root, "width"); h_att = fz_xml_att(root, "height"); viewbox_att = fz_xml_att(root, "viewBox"); version = 10; if (version_att) version = fz_atof(version_att) * 10; if (version > 12) fz_warn(ctx, "svg document version is newer than we support"); /* If no width or height attributes, then guess from the viewbox */ if (w_att == NULL && h_att == NULL && viewbox_att != NULL) { float min_x, min_y, box_w, box_h; sscanf(viewbox_att, "%g %g %g %g", &min_x, &min_y, &box_w, &box_h); doc->width = box_w; doc->height = box_h; } else { doc->width = DEF_WIDTH; if (w_att) doc->width = svg_parse_length(w_att, doc->width, DEF_FONTSIZE); doc->height = DEF_HEIGHT; if (h_att) doc->height = svg_parse_length(h_att, doc->height, DEF_FONTSIZE); } }
const char * svg_lex_number(float *fp, const char *ss) { const char *s = ss; if (*s == '-') ++s; while (*s >= '0' && *s <= '9') ++s; if (*s == '.') { ++s; while (*s >= '0' && *s <= '9') ++s; } if (*s == 'e' || *s == 'E') { ++s; if (*s == '+' || *s == '-') ++s; while (*s >= '0' && *s <= '9') ++s; } *fp = fz_atof(ss); return s; }
void xps_parse_path(xps_document *doc, const fz_matrix *ctm, char *base_uri, xps_resource *dict, fz_xml *root) { fz_xml *node; char *fill_uri; char *stroke_uri; char *opacity_mask_uri; char *transform_att; char *clip_att; char *data_att; char *fill_att; char *stroke_att; char *opacity_att; char *opacity_mask_att; fz_xml *transform_tag = NULL; fz_xml *clip_tag = NULL; fz_xml *data_tag = NULL; fz_xml *fill_tag = NULL; fz_xml *stroke_tag = NULL; fz_xml *opacity_mask_tag = NULL; char *fill_opacity_att = NULL; char *stroke_opacity_att = NULL; char *stroke_dash_array_att; char *stroke_dash_cap_att; char *stroke_dash_offset_att; char *stroke_end_line_cap_att; char *stroke_start_line_cap_att; char *stroke_line_join_att; char *stroke_miter_limit_att; char *stroke_thickness_att; char *navigate_uri_att; fz_stroke_state *stroke = NULL; fz_matrix transform; float samples[32]; fz_colorspace *colorspace; fz_path *path = NULL; fz_path *stroke_path = NULL; fz_rect area; int fill_rule; int dash_len = 0; fz_matrix local_ctm; /* * Extract attributes and extended attributes. */ transform_att = fz_xml_att(root, "RenderTransform"); clip_att = fz_xml_att(root, "Clip"); data_att = fz_xml_att(root, "Data"); fill_att = fz_xml_att(root, "Fill"); stroke_att = fz_xml_att(root, "Stroke"); opacity_att = fz_xml_att(root, "Opacity"); opacity_mask_att = fz_xml_att(root, "OpacityMask"); stroke_dash_array_att = fz_xml_att(root, "StrokeDashArray"); stroke_dash_cap_att = fz_xml_att(root, "StrokeDashCap"); stroke_dash_offset_att = fz_xml_att(root, "StrokeDashOffset"); stroke_end_line_cap_att = fz_xml_att(root, "StrokeEndLineCap"); stroke_start_line_cap_att = fz_xml_att(root, "StrokeStartLineCap"); stroke_line_join_att = fz_xml_att(root, "StrokeLineJoin"); stroke_miter_limit_att = fz_xml_att(root, "StrokeMiterLimit"); stroke_thickness_att = fz_xml_att(root, "StrokeThickness"); navigate_uri_att = fz_xml_att(root, "FixedPage.NavigateUri"); for (node = fz_xml_down(root); node; node = fz_xml_next(node)) { if (!strcmp(fz_xml_tag(node), "Path.RenderTransform")) transform_tag = fz_xml_down(node); if (!strcmp(fz_xml_tag(node), "Path.OpacityMask")) opacity_mask_tag = fz_xml_down(node); if (!strcmp(fz_xml_tag(node), "Path.Clip")) clip_tag = fz_xml_down(node); if (!strcmp(fz_xml_tag(node), "Path.Fill")) fill_tag = fz_xml_down(node); if (!strcmp(fz_xml_tag(node), "Path.Stroke")) stroke_tag = fz_xml_down(node); if (!strcmp(fz_xml_tag(node), "Path.Data")) data_tag = fz_xml_down(node); } fill_uri = base_uri; stroke_uri = base_uri; opacity_mask_uri = base_uri; xps_resolve_resource_reference(doc, dict, &data_att, &data_tag, NULL); xps_resolve_resource_reference(doc, dict, &clip_att, &clip_tag, NULL); xps_resolve_resource_reference(doc, dict, &transform_att, &transform_tag, NULL); xps_resolve_resource_reference(doc, dict, &fill_att, &fill_tag, &fill_uri); xps_resolve_resource_reference(doc, dict, &stroke_att, &stroke_tag, &stroke_uri); xps_resolve_resource_reference(doc, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri); /* * Act on the information we have gathered: */ if (!data_att && !data_tag) return; if (fill_tag && !strcmp(fz_xml_tag(fill_tag), "SolidColorBrush")) { fill_opacity_att = fz_xml_att(fill_tag, "Opacity"); fill_att = fz_xml_att(fill_tag, "Color"); fill_tag = NULL; } if (stroke_tag && !strcmp(fz_xml_tag(stroke_tag), "SolidColorBrush")) { stroke_opacity_att = fz_xml_att(stroke_tag, "Opacity"); stroke_att = fz_xml_att(stroke_tag, "Color"); stroke_tag = NULL; } if (stroke_att || stroke_tag) { if (stroke_dash_array_att) { char *s = stroke_dash_array_att; while (*s) { while (*s == ' ') s++; if (*s) /* needed in case of a space before the last quote */ dash_len++; while (*s && *s != ' ') s++; } } stroke = fz_new_stroke_state_with_len(doc->ctx, dash_len); stroke->start_cap = xps_parse_line_cap(stroke_start_line_cap_att); stroke->dash_cap = xps_parse_line_cap(stroke_dash_cap_att); stroke->end_cap = xps_parse_line_cap(stroke_end_line_cap_att); stroke->linejoin = FZ_LINEJOIN_MITER_XPS; if (stroke_line_join_att) { if (!strcmp(stroke_line_join_att, "Miter")) stroke->linejoin = FZ_LINEJOIN_MITER_XPS; if (!strcmp(stroke_line_join_att, "Round")) stroke->linejoin = FZ_LINEJOIN_ROUND; if (!strcmp(stroke_line_join_att, "Bevel")) stroke->linejoin = FZ_LINEJOIN_BEVEL; } stroke->miterlimit = 10; if (stroke_miter_limit_att) stroke->miterlimit = fz_atof(stroke_miter_limit_att); stroke->linewidth = 1; if (stroke_thickness_att) stroke->linewidth = fz_atof(stroke_thickness_att); stroke->dash_phase = 0; stroke->dash_len = 0; if (stroke_dash_array_att) { char *s = stroke_dash_array_att; if (stroke_dash_offset_att) stroke->dash_phase = fz_atof(stroke_dash_offset_att) * stroke->linewidth; while (*s) { while (*s == ' ') s++; if (*s) /* needed in case of a space before the last quote */ stroke->dash_list[stroke->dash_len++] = fz_atof(s) * stroke->linewidth; while (*s && *s != ' ') s++; } /* cf. https://code.google.com/p/sumatrapdf/issues/detail?id=2339 */ if (dash_len > 0) { float phase_len = 0.0f; int i; for (i = 0; i < dash_len; i++) phase_len += stroke->dash_list[i]; if (phase_len == 0.0f) dash_len = 0; } stroke->dash_len = dash_len; } } transform = fz_identity; if (transform_att) xps_parse_render_transform(doc, transform_att, &transform); if (transform_tag) xps_parse_matrix_transform(doc, transform_tag, &transform); fz_concat(&local_ctm, &transform, ctm); if (clip_att || clip_tag) xps_clip(doc, &local_ctm, dict, clip_att, clip_tag); fill_rule = 0; if (data_att) path = xps_parse_abbreviated_geometry(doc, data_att, &fill_rule); else if (data_tag) { path = xps_parse_path_geometry(doc, dict, data_tag, 0, &fill_rule); if (stroke_att || stroke_tag) stroke_path = xps_parse_path_geometry(doc, dict, data_tag, 1, &fill_rule); } if (!stroke_path) stroke_path = path; if (stroke_att || stroke_tag) { fz_bound_path(doc->ctx, stroke_path, stroke, &local_ctm, &area); if (stroke_path != path && (fill_att || fill_tag)) { fz_rect bounds; fz_bound_path(doc->ctx, path, NULL, &local_ctm, &bounds); fz_union_rect(&area, &bounds); } } else fz_bound_path(doc->ctx, path, NULL, &local_ctm, &area); /* SumatraPDF: extended link support */ xps_extract_anchor_info(doc, &area, navigate_uri_att, fz_xml_att(root, "Name"), 0); navigate_uri_att = NULL; if (navigate_uri_att) xps_add_link(doc, &area, base_uri, navigate_uri_att); xps_begin_opacity(doc, &local_ctm, &area, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); if (fill_att) { xps_parse_color(doc, base_uri, fill_att, &colorspace, samples); if (fill_opacity_att) samples[0] *= fz_atof(fill_opacity_att); xps_set_color(doc, colorspace, samples); fz_fill_path(doc->dev, path, fill_rule == 0, &local_ctm, doc->colorspace, doc->color, doc->alpha); } if (fill_tag) { fz_clip_path(doc->dev, path, NULL, fill_rule == 0, &local_ctm); xps_parse_brush(doc, &local_ctm, &area, fill_uri, dict, fill_tag); fz_pop_clip(doc->dev); } if (stroke_att) { xps_parse_color(doc, base_uri, stroke_att, &colorspace, samples); if (stroke_opacity_att) samples[0] *= fz_atof(stroke_opacity_att); xps_set_color(doc, colorspace, samples); fz_stroke_path(doc->dev, stroke_path, stroke, &local_ctm, doc->colorspace, doc->color, doc->alpha); } if (stroke_tag) { fz_clip_stroke_path(doc->dev, stroke_path, NULL, stroke, &local_ctm); xps_parse_brush(doc, &local_ctm, &area, stroke_uri, dict, stroke_tag); fz_pop_clip(doc->dev); } xps_end_opacity(doc, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); if (stroke_path != path) fz_free_path(doc->ctx, stroke_path); fz_free_path(doc->ctx, path); path = NULL; fz_drop_stroke_state(doc->ctx, stroke); if (clip_att || clip_tag) fz_pop_clip(doc->dev); }
static fz_path * xps_parse_abbreviated_geometry(xps_document *doc, char *geom, int *fill_rule) { fz_path *path; char **args; char **pargs; char *s = geom; fz_point pt; int i, n; int cmd, old; float x1, y1, x2, y2, x3, y3; float smooth_x, smooth_y; /* saved cubic bezier control point for smooth curves */ int reset_smooth; path = fz_new_path(doc->ctx); args = fz_malloc_array(doc->ctx, strlen(geom) + 1, sizeof(char*)); pargs = args; while (*s) { if ((*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z')) { *pargs++ = s++; } else if ((*s >= '0' && *s <= '9') || *s == '.' || *s == '+' || *s == '-' || *s == 'e' || *s == 'E') { *pargs++ = s; while ((*s >= '0' && *s <= '9') || *s == '.' || *s == '+' || *s == '-' || *s == 'e' || *s == 'E') s ++; } else { s++; } } *pargs = s; n = pargs - args; i = 0; old = 0; reset_smooth = 1; smooth_x = 0; smooth_y = 0; while (i < n) { cmd = args[i][0]; if (cmd == '+' || cmd == '.' || cmd == '-' || (cmd >= '0' && cmd <= '9')) cmd = old; /* it's a number, repeat old command */ else i ++; if (reset_smooth) { smooth_x = 0; smooth_y = 0; } reset_smooth = 1; switch (cmd) { case 'F': if (i >= n) break; *fill_rule = atoi(args[i]); i ++; break; case 'M': if (i + 1 >= n) break; fz_moveto(doc->ctx, path, fz_atof(args[i]), fz_atof(args[i+1])); i += 2; break; case 'm': if (i + 1 >= n) break; pt = fz_currentpoint(doc->ctx, path); fz_moveto(doc->ctx, path, pt.x + fz_atof(args[i]), pt.y + fz_atof(args[i+1])); i += 2; break; case 'L': if (i + 1 >= n) break; fz_lineto(doc->ctx, path, fz_atof(args[i]), fz_atof(args[i+1])); i += 2; break; case 'l': if (i + 1 >= n) break; pt = fz_currentpoint(doc->ctx, path); fz_lineto(doc->ctx, path, pt.x + fz_atof(args[i]), pt.y + fz_atof(args[i+1])); i += 2; break; case 'H': if (i >= n) break; pt = fz_currentpoint(doc->ctx, path); fz_lineto(doc->ctx, path, fz_atof(args[i]), pt.y); i += 1; break; case 'h': if (i >= n) break; pt = fz_currentpoint(doc->ctx, path); fz_lineto(doc->ctx, path, pt.x + fz_atof(args[i]), pt.y); i += 1; break; case 'V': if (i >= n) break; pt = fz_currentpoint(doc->ctx, path); fz_lineto(doc->ctx, path, pt.x, fz_atof(args[i])); i += 1; break; case 'v': if (i >= n) break; pt = fz_currentpoint(doc->ctx, path); fz_lineto(doc->ctx, path, pt.x, pt.y + fz_atof(args[i])); i += 1; break; case 'C': if (i + 5 >= n) break; x1 = fz_atof(args[i+0]); y1 = fz_atof(args[i+1]); x2 = fz_atof(args[i+2]); y2 = fz_atof(args[i+3]); x3 = fz_atof(args[i+4]); y3 = fz_atof(args[i+5]); fz_curveto(doc->ctx, path, x1, y1, x2, y2, x3, y3); i += 6; reset_smooth = 0; smooth_x = x3 - x2; smooth_y = y3 - y2; break; case 'c': if (i + 5 >= n) break; pt = fz_currentpoint(doc->ctx, path); x1 = fz_atof(args[i+0]) + pt.x; y1 = fz_atof(args[i+1]) + pt.y; x2 = fz_atof(args[i+2]) + pt.x; y2 = fz_atof(args[i+3]) + pt.y; x3 = fz_atof(args[i+4]) + pt.x; y3 = fz_atof(args[i+5]) + pt.y; fz_curveto(doc->ctx, path, x1, y1, x2, y2, x3, y3); i += 6; reset_smooth = 0; smooth_x = x3 - x2; smooth_y = y3 - y2; break; case 'S': if (i + 3 >= n) break; pt = fz_currentpoint(doc->ctx, path); x1 = fz_atof(args[i+0]); y1 = fz_atof(args[i+1]); x2 = fz_atof(args[i+2]); y2 = fz_atof(args[i+3]); fz_curveto(doc->ctx, path, pt.x + smooth_x, pt.y + smooth_y, x1, y1, x2, y2); i += 4; reset_smooth = 0; smooth_x = x2 - x1; smooth_y = y2 - y1; break; case 's': if (i + 3 >= n) break; pt = fz_currentpoint(doc->ctx, path); x1 = fz_atof(args[i+0]) + pt.x; y1 = fz_atof(args[i+1]) + pt.y; x2 = fz_atof(args[i+2]) + pt.x; y2 = fz_atof(args[i+3]) + pt.y; fz_curveto(doc->ctx, path, pt.x + smooth_x, pt.y + smooth_y, x1, y1, x2, y2); i += 4; reset_smooth = 0; smooth_x = x2 - x1; smooth_y = y2 - y1; break; case 'Q': if (i + 3 >= n) break; pt = fz_currentpoint(doc->ctx, path); x1 = fz_atof(args[i+0]); y1 = fz_atof(args[i+1]); x2 = fz_atof(args[i+2]); y2 = fz_atof(args[i+3]); fz_curveto(doc->ctx, path, (pt.x + 2 * x1) / 3, (pt.y + 2 * y1) / 3, (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3, x2, y2); i += 4; break; case 'q': if (i + 3 >= n) break; pt = fz_currentpoint(doc->ctx, path); x1 = fz_atof(args[i+0]) + pt.x; y1 = fz_atof(args[i+1]) + pt.y; x2 = fz_atof(args[i+2]) + pt.x; y2 = fz_atof(args[i+3]) + pt.y; fz_curveto(doc->ctx, path, (pt.x + 2 * x1) / 3, (pt.y + 2 * y1) / 3, (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3, x2, y2); i += 4; break; case 'A': if (i + 6 >= n) break; xps_draw_arc(doc->ctx, path, fz_atof(args[i+0]), fz_atof(args[i+1]), fz_atof(args[i+2]), atoi(args[i+3]), atoi(args[i+4]), fz_atof(args[i+5]), fz_atof(args[i+6])); i += 7; break; case 'a': if (i + 6 >= n) break; pt = fz_currentpoint(doc->ctx, path); xps_draw_arc(doc->ctx, path, fz_atof(args[i+0]), fz_atof(args[i+1]), fz_atof(args[i+2]), atoi(args[i+3]), atoi(args[i+4]), fz_atof(args[i+5]) + pt.x, fz_atof(args[i+6]) + pt.y); i += 7; break; case 'Z': case 'z': fz_closepath(doc->ctx, path); break; default: /* eek */ fz_warn(doc->ctx, "ignoring invalid command '%c'", cmd); /* Skip any trailing numbers to avoid an infinite loop */ while (i < n && (args[i][0] == '+' || args[i][0] == '.' || args[i][0] == '-' || (args[i][0] >= '0' && args[i][0] <= '9'))) i ++; break; } old = cmd; } fz_free(doc->ctx, args); return path; }
void xps_parse_path(xps_document *doc, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *root) { xml_element *node; char *fill_uri; char *stroke_uri; char *opacity_mask_uri; char *transform_att; char *clip_att; char *data_att; char *fill_att; char *stroke_att; char *opacity_att; char *opacity_mask_att; xml_element *transform_tag = NULL; xml_element *clip_tag = NULL; xml_element *data_tag = NULL; xml_element *fill_tag = NULL; xml_element *stroke_tag = NULL; xml_element *opacity_mask_tag = NULL; char *fill_opacity_att = NULL; char *stroke_opacity_att = NULL; char *stroke_dash_array_att; char *stroke_dash_cap_att; char *stroke_dash_offset_att; char *stroke_end_line_cap_att; char *stroke_start_line_cap_att; char *stroke_line_join_att; char *stroke_miter_limit_att; char *stroke_thickness_att; char *navigate_uri_att; fz_stroke_state stroke; fz_matrix transform; float samples[32]; fz_colorspace *colorspace; fz_path *path; fz_rect area; int fill_rule; /* * Extract attributes and extended attributes. */ transform_att = xml_att(root, "RenderTransform"); clip_att = xml_att(root, "Clip"); data_att = xml_att(root, "Data"); fill_att = xml_att(root, "Fill"); stroke_att = xml_att(root, "Stroke"); opacity_att = xml_att(root, "Opacity"); opacity_mask_att = xml_att(root, "OpacityMask"); stroke_dash_array_att = xml_att(root, "StrokeDashArray"); stroke_dash_cap_att = xml_att(root, "StrokeDashCap"); stroke_dash_offset_att = xml_att(root, "StrokeDashOffset"); stroke_end_line_cap_att = xml_att(root, "StrokeEndLineCap"); stroke_start_line_cap_att = xml_att(root, "StrokeStartLineCap"); stroke_line_join_att = xml_att(root, "StrokeLineJoin"); stroke_miter_limit_att = xml_att(root, "StrokeMiterLimit"); stroke_thickness_att = xml_att(root, "StrokeThickness"); navigate_uri_att = xml_att(root, "FixedPage.NavigateUri"); for (node = xml_down(root); node; node = xml_next(node)) { if (!strcmp(xml_tag(node), "Path.RenderTransform")) transform_tag = xml_down(node); if (!strcmp(xml_tag(node), "Path.OpacityMask")) opacity_mask_tag = xml_down(node); if (!strcmp(xml_tag(node), "Path.Clip")) clip_tag = xml_down(node); if (!strcmp(xml_tag(node), "Path.Fill")) fill_tag = xml_down(node); if (!strcmp(xml_tag(node), "Path.Stroke")) stroke_tag = xml_down(node); if (!strcmp(xml_tag(node), "Path.Data")) data_tag = xml_down(node); } fill_uri = base_uri; stroke_uri = base_uri; opacity_mask_uri = base_uri; xps_resolve_resource_reference(doc, dict, &data_att, &data_tag, NULL); xps_resolve_resource_reference(doc, dict, &clip_att, &clip_tag, NULL); xps_resolve_resource_reference(doc, dict, &transform_att, &transform_tag, NULL); xps_resolve_resource_reference(doc, dict, &fill_att, &fill_tag, &fill_uri); xps_resolve_resource_reference(doc, dict, &stroke_att, &stroke_tag, &stroke_uri); xps_resolve_resource_reference(doc, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri); /* * Act on the information we have gathered: */ if (!data_att && !data_tag) return; if (fill_tag && !strcmp(xml_tag(fill_tag), "SolidColorBrush")) { fill_opacity_att = xml_att(fill_tag, "Opacity"); fill_att = xml_att(fill_tag, "Color"); fill_tag = NULL; } if (stroke_tag && !strcmp(xml_tag(stroke_tag), "SolidColorBrush")) { stroke_opacity_att = xml_att(stroke_tag, "Opacity"); stroke_att = xml_att(stroke_tag, "Color"); stroke_tag = NULL; } stroke.start_cap = xps_parse_line_cap(stroke_start_line_cap_att); stroke.dash_cap = xps_parse_line_cap(stroke_dash_cap_att); stroke.end_cap = xps_parse_line_cap(stroke_end_line_cap_att); stroke.linejoin = FZ_LINEJOIN_MITER_XPS; if (stroke_line_join_att) { if (!strcmp(stroke_line_join_att, "Miter")) stroke.linejoin = FZ_LINEJOIN_MITER_XPS; if (!strcmp(stroke_line_join_att, "Round")) stroke.linejoin = FZ_LINEJOIN_ROUND; if (!strcmp(stroke_line_join_att, "Bevel")) stroke.linejoin = FZ_LINEJOIN_BEVEL; } stroke.miterlimit = 10; if (stroke_miter_limit_att) stroke.miterlimit = fz_atof(stroke_miter_limit_att); stroke.linewidth = 1; if (stroke_thickness_att) stroke.linewidth = fz_atof(stroke_thickness_att); stroke.dash_phase = 0; stroke.dash_len = 0; if (stroke_dash_array_att) { char *s = stroke_dash_array_att; if (stroke_dash_offset_att) stroke.dash_phase = fz_atof(stroke_dash_offset_att) * stroke.linewidth; while (*s && stroke.dash_len < nelem(stroke.dash_list)) { while (*s == ' ') s++; if (*s) /* needed in case of a space before the last quote */ stroke.dash_list[stroke.dash_len++] = fz_atof(s) * stroke.linewidth; while (*s && *s != ' ') s++; } } transform = fz_identity; if (transform_att) xps_parse_render_transform(doc, transform_att, &transform); if (transform_tag) xps_parse_matrix_transform(doc, transform_tag, &transform); ctm = fz_concat(transform, ctm); if (clip_att || clip_tag) xps_clip(doc, ctm, dict, clip_att, clip_tag); fill_rule = 0; if (data_att) path = xps_parse_abbreviated_geometry(doc, data_att, &fill_rule); else if (data_tag) path = xps_parse_path_geometry(doc, dict, data_tag, 0, &fill_rule); if (stroke_att || stroke_tag) area = fz_bound_path(doc->ctx, path, &stroke, ctm); else area = fz_bound_path(doc->ctx, path, NULL, ctm); if (navigate_uri_att) xps_add_link(doc, area, base_uri, navigate_uri_att); xps_begin_opacity(doc, ctm, area, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); if (fill_att) { xps_parse_color(doc, base_uri, fill_att, &colorspace, samples); if (fill_opacity_att) samples[0] = fz_atof(fill_opacity_att); xps_set_color(doc, colorspace, samples); fz_fill_path(doc->dev, path, fill_rule == 0, ctm, doc->colorspace, doc->color, doc->alpha); } if (fill_tag) { area = fz_bound_path(doc->ctx, path, NULL, ctm); fz_clip_path(doc->dev, path, NULL, fill_rule == 0, ctm); xps_parse_brush(doc, ctm, area, fill_uri, dict, fill_tag); fz_pop_clip(doc->dev); } if (stroke_att) { xps_parse_color(doc, base_uri, stroke_att, &colorspace, samples); if (stroke_opacity_att) samples[0] = fz_atof(stroke_opacity_att); xps_set_color(doc, colorspace, samples); fz_stroke_path(doc->dev, path, &stroke, ctm, doc->colorspace, doc->color, doc->alpha); } if (stroke_tag) { fz_clip_stroke_path(doc->dev, path, NULL, &stroke, ctm); xps_parse_brush(doc, ctm, area, stroke_uri, dict, stroke_tag); fz_pop_clip(doc->dev); } xps_end_opacity(doc, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); fz_free_path(doc->ctx, path); path = NULL; if (clip_att || clip_tag) fz_pop_clip(doc->dev); }
static void xps_draw_radial_gradient(fz_context *ctx, xps_document *doc, const fz_matrix *ctm, const fz_rect *area, struct stop *stops, int count, fz_xml *root, int spread) { float x0, y0, r0; float x1, y1, r1; float xrad = 1; float yrad = 1; float invscale; int i, ma = 1; fz_matrix local_ctm = *ctm; fz_matrix inv; fz_rect local_area = *area; char *center_att = fz_xml_att(root, "Center"); char *origin_att = fz_xml_att(root, "GradientOrigin"); char *radius_x_att = fz_xml_att(root, "RadiusX"); char *radius_y_att = fz_xml_att(root, "RadiusY"); x0 = y0 = 0.0; x1 = y1 = 1.0; xrad = 1.0; yrad = 1.0; if (origin_att) xps_parse_point(ctx, doc, origin_att, &x0, &y0); if (center_att) xps_parse_point(ctx, doc, center_att, &x1, &y1); if (radius_x_att) xrad = fz_atof(radius_x_att); if (radius_y_att) yrad = fz_atof(radius_y_att); xrad = fz_max(0.01f, xrad); yrad = fz_max(0.01f, yrad); /* scale the ctm to make ellipses */ if (fz_abs(xrad) > FLT_EPSILON) { fz_pre_scale(&local_ctm, 1, yrad/xrad); } if (yrad != 0.0) { invscale = xrad / yrad; y0 = y0 * invscale; y1 = y1 * invscale; } r0 = 0; r1 = xrad; fz_transform_rect(&local_area, fz_invert_matrix(&inv, &local_ctm)); ma = fz_maxi(ma, ceilf(hypotf(local_area.x0 - x0, local_area.y0 - y0) / xrad)); ma = fz_maxi(ma, ceilf(hypotf(local_area.x1 - x0, local_area.y0 - y0) / xrad)); ma = fz_maxi(ma, ceilf(hypotf(local_area.x0 - x0, local_area.y1 - y0) / xrad)); ma = fz_maxi(ma, ceilf(hypotf(local_area.x1 - x0, local_area.y1 - y0) / xrad)); if (spread == SPREAD_REPEAT) { for (i = ma - 1; i >= 0; i--) xps_draw_one_radial_gradient(ctx, doc, &local_ctm, stops, count, 0, x0, y0, r0 + i * xrad, x1, y1, r1 + i * xrad); } else if (spread == SPREAD_REFLECT) { if ((ma % 2) != 0) ma++; for (i = ma - 2; i >= 0; i -= 2) { xps_draw_one_radial_gradient(ctx, doc, &local_ctm, stops, count, 0, x0, y0, r0 + i * xrad, x1, y1, r1 + i * xrad); xps_draw_one_radial_gradient(ctx, doc, &local_ctm, stops, count, 0, x0, y0, r0 + (i + 2) * xrad, x1, y1, r1 + i * xrad); } } else { xps_draw_one_radial_gradient(ctx, doc, &local_ctm, stops, count, 1, x0, y0, r0, x1, y1, r1); } }
void xps_parse_color(xps_document *doc, char *base_uri, char *string, fz_colorspace **csp, float *samples) { char *p; int i, n; char buf[1024]; char *profile; *csp = fz_device_rgb; samples[0] = 1; samples[1] = 0; samples[2] = 0; samples[3] = 0; if (string[0] == '#') { if (strlen(string) == 9) { samples[0] = unhex(string[1]) * 16 + unhex(string[2]); samples[1] = unhex(string[3]) * 16 + unhex(string[4]); samples[2] = unhex(string[5]) * 16 + unhex(string[6]); samples[3] = unhex(string[7]) * 16 + unhex(string[8]); } else { samples[0] = 255; samples[1] = unhex(string[1]) * 16 + unhex(string[2]); samples[2] = unhex(string[3]) * 16 + unhex(string[4]); samples[3] = unhex(string[5]) * 16 + unhex(string[6]); } samples[0] /= 255; samples[1] /= 255; samples[2] /= 255; samples[3] /= 255; } else if (string[0] == 's' && string[1] == 'c' && string[2] == '#') { if (count_commas(string) == 2) sscanf(string, "sc#%g,%g,%g", samples + 1, samples + 2, samples + 3); if (count_commas(string) == 3) sscanf(string, "sc#%g,%g,%g,%g", samples, samples + 1, samples + 2, samples + 3); } else if (strstr(string, "ContextColor ") == string) { /* Crack the string for profile name and sample values */ fz_strlcpy(buf, string, sizeof buf); profile = strchr(buf, ' '); if (!profile) { fz_warn(doc->ctx, "cannot find icc profile uri in '%s'", string); return; } *profile++ = 0; p = strchr(profile, ' '); if (!p) { fz_warn(doc->ctx, "cannot find component values in '%s'", profile); return; } *p++ = 0; n = count_commas(p) + 1; i = 0; while (i < n) { samples[i++] = fz_atof(p); p = strchr(p, ','); if (!p) break; p ++; if (*p == ' ') p ++; } while (i < n) { samples[i++] = 0; } /* TODO: load ICC profile */ switch (n) { case 2: *csp = fz_device_gray; break; case 4: *csp = fz_device_rgb; break; case 5: *csp = fz_device_cmyk; break; default: *csp = fz_device_gray; break; } } }
fz_error pdf_parse_array(fz_obj **op, pdf_xref *xref, fz_stream *file, char *buf, int cap) { fz_error error = fz_okay; fz_obj *ary = NULL; fz_obj *obj = NULL; int a = 0, b = 0, n = 0; int tok; int len; ary = fz_new_array(4); while (1) { error = pdf_lex(&tok, file, buf, cap, &len); if (error) { fz_drop_obj(ary); return fz_rethrow(error, "cannot parse array"); } if (tok != PDF_TOK_INT && tok != PDF_TOK_R) { if (n > 0) { obj = fz_new_int(a); fz_array_push(ary, obj); fz_drop_obj(obj); } if (n > 1) { obj = fz_new_int(b); fz_array_push(ary, obj); fz_drop_obj(obj); } n = 0; } if (tok == PDF_TOK_INT && n == 2) { obj = fz_new_int(a); fz_array_push(ary, obj); fz_drop_obj(obj); a = b; n --; } switch (tok) { case PDF_TOK_CLOSE_ARRAY: *op = ary; return fz_okay; case PDF_TOK_INT: if (n == 0) a = atoi(buf); if (n == 1) b = atoi(buf); n ++; break; case PDF_TOK_R: if (n != 2) { fz_drop_obj(ary); return fz_throw("cannot parse indirect reference in array"); } obj = fz_new_indirect(a, b, xref); fz_array_push(ary, obj); fz_drop_obj(obj); n = 0; break; case PDF_TOK_OPEN_ARRAY: error = pdf_parse_array(&obj, xref, file, buf, cap); if (error) { fz_drop_obj(ary); return fz_rethrow(error, "cannot parse array"); } fz_array_push(ary, obj); fz_drop_obj(obj); break; case PDF_TOK_OPEN_DICT: error = pdf_parse_dict(&obj, xref, file, buf, cap); if (error) { fz_drop_obj(ary); return fz_rethrow(error, "cannot parse array"); } fz_array_push(ary, obj); fz_drop_obj(obj); break; case PDF_TOK_NAME: obj = fz_new_name(buf); fz_array_push(ary, obj); fz_drop_obj(obj); break; case PDF_TOK_REAL: obj = fz_new_real(fz_atof(buf)); fz_array_push(ary, obj); fz_drop_obj(obj); break; case PDF_TOK_STRING: obj = fz_new_string(buf, len); fz_array_push(ary, obj); fz_drop_obj(obj); break; case PDF_TOK_TRUE: obj = fz_new_bool(1); fz_array_push(ary, obj); fz_drop_obj(obj); break; case PDF_TOK_FALSE: obj = fz_new_bool(0); fz_array_push(ary, obj); fz_drop_obj(obj); break; case PDF_TOK_NULL: obj = fz_new_null(); fz_array_push(ary, obj); fz_drop_obj(obj); break; default: fz_drop_obj(ary); return fz_throw("cannot parse token in array"); } } }
void xps_parse_path(fz_context *ctx, xps_document *doc, fz_matrix ctm, char *base_uri, xps_resource *dict, fz_xml *root) { fz_device *dev = doc->dev; fz_xml *node; char *fill_uri; char *stroke_uri; char *opacity_mask_uri; char *transform_att; char *clip_att; char *data_att; char *fill_att; char *stroke_att; char *opacity_att; char *opacity_mask_att; fz_xml *transform_tag = NULL; fz_xml *clip_tag = NULL; fz_xml *data_tag = NULL; fz_xml *fill_tag = NULL; fz_xml *stroke_tag = NULL; fz_xml *opacity_mask_tag = NULL; char *fill_opacity_att = NULL; char *stroke_opacity_att = NULL; char *stroke_dash_array_att; char *stroke_dash_cap_att; char *stroke_dash_offset_att; char *stroke_end_line_cap_att; char *stroke_start_line_cap_att; char *stroke_line_join_att; char *stroke_miter_limit_att; char *stroke_thickness_att; fz_stroke_state *stroke = NULL; float samples[FZ_MAX_COLORS]; fz_colorspace *colorspace; fz_path *path = NULL; fz_path *stroke_path = NULL; fz_rect area; int fill_rule; int dash_len = 0; /* * Extract attributes and extended attributes. */ transform_att = fz_xml_att(root, "RenderTransform"); clip_att = fz_xml_att(root, "Clip"); data_att = fz_xml_att(root, "Data"); fill_att = fz_xml_att(root, "Fill"); stroke_att = fz_xml_att(root, "Stroke"); opacity_att = fz_xml_att(root, "Opacity"); opacity_mask_att = fz_xml_att(root, "OpacityMask"); stroke_dash_array_att = fz_xml_att(root, "StrokeDashArray"); stroke_dash_cap_att = fz_xml_att(root, "StrokeDashCap"); stroke_dash_offset_att = fz_xml_att(root, "StrokeDashOffset"); stroke_end_line_cap_att = fz_xml_att(root, "StrokeEndLineCap"); stroke_start_line_cap_att = fz_xml_att(root, "StrokeStartLineCap"); stroke_line_join_att = fz_xml_att(root, "StrokeLineJoin"); stroke_miter_limit_att = fz_xml_att(root, "StrokeMiterLimit"); stroke_thickness_att = fz_xml_att(root, "StrokeThickness"); for (node = fz_xml_down(root); node; node = fz_xml_next(node)) { if (fz_xml_is_tag(node, "Path.RenderTransform")) transform_tag = fz_xml_down(node); if (fz_xml_is_tag(node, "Path.OpacityMask")) opacity_mask_tag = fz_xml_down(node); if (fz_xml_is_tag(node, "Path.Clip")) clip_tag = fz_xml_down(node); if (fz_xml_is_tag(node, "Path.Fill")) fill_tag = fz_xml_down(node); if (fz_xml_is_tag(node, "Path.Stroke")) stroke_tag = fz_xml_down(node); if (fz_xml_is_tag(node, "Path.Data")) data_tag = fz_xml_down(node); } fill_uri = base_uri; stroke_uri = base_uri; opacity_mask_uri = base_uri; xps_resolve_resource_reference(ctx, doc, dict, &data_att, &data_tag, NULL); xps_resolve_resource_reference(ctx, doc, dict, &clip_att, &clip_tag, NULL); xps_resolve_resource_reference(ctx, doc, dict, &transform_att, &transform_tag, NULL); xps_resolve_resource_reference(ctx, doc, dict, &fill_att, &fill_tag, &fill_uri); xps_resolve_resource_reference(ctx, doc, dict, &stroke_att, &stroke_tag, &stroke_uri); xps_resolve_resource_reference(ctx, doc, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri); /* * Act on the information we have gathered: */ if (!data_att && !data_tag) return; if (fz_xml_is_tag(fill_tag, "SolidColorBrush")) { fill_opacity_att = fz_xml_att(fill_tag, "Opacity"); fill_att = fz_xml_att(fill_tag, "Color"); fill_tag = NULL; } if (fz_xml_is_tag(stroke_tag, "SolidColorBrush")) { stroke_opacity_att = fz_xml_att(stroke_tag, "Opacity"); stroke_att = fz_xml_att(stroke_tag, "Color"); stroke_tag = NULL; } if (stroke_att || stroke_tag) { if (stroke_dash_array_att) { char *s = stroke_dash_array_att; while (*s) { while (*s == ' ') s++; if (*s) /* needed in case of a space before the last quote */ dash_len++; while (*s && *s != ' ') s++; } } stroke = fz_new_stroke_state_with_dash_len(ctx, dash_len); stroke->start_cap = xps_parse_line_cap(stroke_start_line_cap_att); stroke->dash_cap = xps_parse_line_cap(stroke_dash_cap_att); stroke->end_cap = xps_parse_line_cap(stroke_end_line_cap_att); stroke->linejoin = FZ_LINEJOIN_MITER_XPS; if (stroke_line_join_att) { if (!strcmp(stroke_line_join_att, "Miter")) stroke->linejoin = FZ_LINEJOIN_MITER_XPS; if (!strcmp(stroke_line_join_att, "Round")) stroke->linejoin = FZ_LINEJOIN_ROUND; if (!strcmp(stroke_line_join_att, "Bevel")) stroke->linejoin = FZ_LINEJOIN_BEVEL; } stroke->miterlimit = 10; if (stroke_miter_limit_att) stroke->miterlimit = fz_atof(stroke_miter_limit_att); stroke->linewidth = 1; if (stroke_thickness_att) stroke->linewidth = fz_atof(stroke_thickness_att); stroke->dash_phase = 0; stroke->dash_len = 0; if (stroke_dash_array_att) { char *s = stroke_dash_array_att; if (stroke_dash_offset_att) stroke->dash_phase = fz_atof(stroke_dash_offset_att) * stroke->linewidth; while (*s) { while (*s == ' ') s++; if (*s) /* needed in case of a space before the last quote */ stroke->dash_list[stroke->dash_len++] = fz_atof(s) * stroke->linewidth; while (*s && *s != ' ') s++; } if (dash_len > 0) { /* fz_stroke_path doesn't draw non-empty paths with phase length zero */ float phase_len = 0; int i; for (i = 0; i < dash_len; i++) phase_len += stroke->dash_list[i]; if (phase_len == 0) dash_len = 0; } stroke->dash_len = dash_len; } } ctm = xps_parse_transform(ctx, doc, transform_att, transform_tag, ctm); if (clip_att || clip_tag) xps_clip(ctx, doc, ctm, dict, clip_att, clip_tag); fz_try(ctx) { fill_rule = 0; if (data_att) path = xps_parse_abbreviated_geometry(ctx, doc, data_att, &fill_rule); else if (data_tag) { path = xps_parse_path_geometry(ctx, doc, dict, data_tag, 0, &fill_rule); // /home/sebras/src/jxr/fts_06xx.xps if (stroke_att || stroke_tag) stroke_path = xps_parse_path_geometry(ctx, doc, dict, data_tag, 1, &fill_rule); } if (!stroke_path) stroke_path = path; if (stroke_att || stroke_tag) { area = fz_bound_path(ctx, stroke_path, stroke, ctm); if (stroke_path != path && (fill_att || fill_tag)) { fz_rect bounds = fz_bound_path(ctx, path, NULL, ctm); area = fz_union_rect(area, bounds); } } else area = fz_bound_path(ctx, path, NULL, ctm); xps_begin_opacity(ctx, doc, ctm, area, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); if (fill_att) { xps_parse_color(ctx, doc, base_uri, fill_att, &colorspace, samples); if (fill_opacity_att) samples[0] *= fz_atof(fill_opacity_att); xps_set_color(ctx, doc, colorspace, samples); fz_fill_path(ctx, dev, path, fill_rule == 0, ctm, doc->colorspace, doc->color, doc->alpha, NULL); } if (fill_tag) { fz_clip_path(ctx, dev, path, fill_rule == 0, ctm, area); xps_parse_brush(ctx, doc, ctm, area, fill_uri, dict, fill_tag); fz_pop_clip(ctx, dev); } if (stroke_att) { xps_parse_color(ctx, doc, base_uri, stroke_att, &colorspace, samples); if (stroke_opacity_att) samples[0] *= fz_atof(stroke_opacity_att); xps_set_color(ctx, doc, colorspace, samples); fz_stroke_path(ctx, dev, stroke_path, stroke, ctm, doc->colorspace, doc->color, doc->alpha, NULL); } if (stroke_tag) { fz_clip_stroke_path(ctx, dev, stroke_path, stroke, ctm, area); xps_parse_brush(ctx, doc, ctm, area, stroke_uri, dict, stroke_tag); fz_pop_clip(ctx, dev); } xps_end_opacity(ctx, doc, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); } fz_always(ctx) { if (stroke_path != path) fz_drop_path(ctx, stroke_path); fz_drop_path(ctx, path); fz_drop_stroke_state(ctx, stroke); } fz_catch(ctx) fz_rethrow(ctx); if (clip_att || clip_tag) fz_pop_clip(ctx, dev); }
int main(int argc, char **argv) { int c; int len; char buf[128]; KeySym keysym; int oldx = 0; int oldy = 0; int resolution = -1; int pageno = 1; fd_set fds; int width = -1; int height = -1; fz_context *ctx; struct timeval now; struct timeval *timeout; struct timeval tmo_advance_delay; ctx = fz_new_context(NULL, NULL, FZ_STORE_DEFAULT); if (!ctx) { fprintf(stderr, "cannot initialise context\n"); exit(1); } pdfapp_init(ctx, &gapp); while ((c = fz_getopt(argc, argv, "p:r:A:C:W:H:S:U:")) != -1) { switch (c) { case 'C': c = strtol(fz_optarg, NULL, 16); gapp.tint = 1; gapp.tint_r = (c >> 16) & 255; gapp.tint_g = (c >> 8) & 255; gapp.tint_b = (c) & 255; break; case 'p': password = fz_optarg; break; case 'r': resolution = atoi(fz_optarg); break; case 'A': fz_set_aa_level(ctx, atoi(fz_optarg)); break; case 'W': gapp.layout_w = fz_atof(fz_optarg); break; case 'H': gapp.layout_h = fz_atof(fz_optarg); break; case 'S': gapp.layout_em = fz_atof(fz_optarg); break; case 'U': gapp.layout_css = fz_optarg; break; default: usage(); } } if (argc - fz_optind == 0) usage(); filename = argv[fz_optind++]; if (argc - fz_optind == 1) pageno = atoi(argv[fz_optind++]); winopen(); if (resolution == -1) resolution = winresolution(); if (resolution < MINRES) resolution = MINRES; if (resolution > MAXRES) resolution = MAXRES; gapp.transitions_enabled = 1; gapp.scrw = DisplayWidth(xdpy, xscr); gapp.scrh = DisplayHeight(xdpy, xscr); gapp.resolution = resolution; gapp.pageno = pageno; tmo_at.tv_sec = 0; tmo_at.tv_usec = 0; timeout = NULL; pdfapp_open(&gapp, filename, 0); FD_ZERO(&fds); signal(SIGHUP, signal_handler); while (!closing) { while (!closing && XPending(xdpy) && !transition_dirty) { XNextEvent(xdpy, &xevt); switch (xevt.type) { case Expose: dirty = 1; break; case ConfigureNotify: if (gapp.image) { if (xevt.xconfigure.width != reqw || xevt.xconfigure.height != reqh) gapp.shrinkwrap = 0; } width = xevt.xconfigure.width; height = xevt.xconfigure.height; break; case KeyPress: len = XLookupString(&xevt.xkey, buf, sizeof buf, &keysym, NULL); if (!gapp.issearching) switch (keysym) { case XK_Escape: len = 1; buf[0] = '\033'; break; case XK_Up: len = 1; buf[0] = 'k'; break; case XK_Down: len = 1; buf[0] = 'j'; break; case XK_Left: len = 1; buf[0] = 'b'; break; case XK_Right: len = 1; buf[0] = ' '; break; case XK_Page_Up: case XF86XK_Back: len = 1; buf[0] = ','; break; case XK_Page_Down: case XF86XK_Forward: len = 1; buf[0] = '.'; break; } if (xevt.xkey.state & ControlMask && keysym == XK_c) docopy(&gapp, XA_CLIPBOARD); else if (len) onkey(buf[0], xevt.xkey.state); onmouse(oldx, oldy, 0, 0, 0); break; case MotionNotify: oldx = xevt.xmotion.x; oldy = xevt.xmotion.y; onmouse(xevt.xmotion.x, xevt.xmotion.y, 0, xevt.xmotion.state, 0); break; case ButtonPress: onmouse(xevt.xbutton.x, xevt.xbutton.y, xevt.xbutton.button, xevt.xbutton.state, 1); break; case ButtonRelease: copytime = xevt.xbutton.time; onmouse(xevt.xbutton.x, xevt.xbutton.y, xevt.xbutton.button, xevt.xbutton.state, -1); break; case SelectionRequest: onselreq(xevt.xselectionrequest.requestor, xevt.xselectionrequest.selection, xevt.xselectionrequest.target, xevt.xselectionrequest.property, xevt.xselectionrequest.time); break; case ClientMessage: if (xevt.xclient.message_type == WM_RELOAD_PAGE) pdfapp_reloadpage(&gapp); else if (xevt.xclient.format == 32 && xevt.xclient.data.l[0] == WM_DELETE_WINDOW) closing = 1; break; } } if (closing) continue; if (width != -1 || height != -1) { pdfapp_onresize(&gapp, width, height); width = -1; height = -1; } if (dirty || dirtysearch) { if (dirty) winblit(&gapp); else if (dirtysearch) winblitstatusbar(&gapp); dirty = 0; transition_dirty = 0; dirtysearch = 0; pdfapp_postblit(&gapp); } if (!showingpage && !showingmessage && (tmo_at.tv_sec || tmo_at.tv_usec)) { tmo_at.tv_sec = 0; tmo_at.tv_usec = 0; timeout = NULL; } if (XPending(xdpy) || transition_dirty) continue; timeout = NULL; if (tmo_at.tv_sec || tmo_at.tv_usec) { gettimeofday(&now, NULL); timersub(&tmo_at, &now, &tmo); if (tmo.tv_sec <= 0) { tmo_at.tv_sec = 0; tmo_at.tv_usec = 0; timeout = NULL; showingpage = 0; showingmessage = 0; winrepaint(&gapp); } else timeout = &tmo; } if (advance_scheduled) { gettimeofday(&now, NULL); timersub(&tmo_advance, &now, &tmo_advance_delay); if (tmo_advance_delay.tv_sec <= 0) { /* Too late already */ onkey(' ', 0); onmouse(oldx, oldy, 0, 0, 0); advance_scheduled = 0; } else if (timeout == NULL) { timeout = &tmo_advance_delay; } else { struct timeval tmp; timersub(&tmo_advance_delay, timeout, &tmp); if (tmp.tv_sec < 0) { timeout = &tmo_advance_delay; } } } FD_SET(x11fd, &fds); if (select(x11fd + 1, &fds, NULL, NULL, timeout) < 0) { if (reloading) { pdfapp_reloadfile(&gapp); reloading = 0; } } if (!FD_ISSET(x11fd, &fds)) { if (timeout == &tmo_advance_delay) { onkey(' ', 0); onmouse(oldx, oldy, 0, 0, 0); advance_scheduled = 0; } else { tmo_at.tv_sec = 0; tmo_at.tv_usec = 0; timeout = NULL; showingpage = 0; showingmessage = 0; winrepaint(&gapp); } } } cleanup(&gapp); return 0; }
fz_error pdf_parse_dict(fz_obj **op, pdf_xref *xref, fz_stream *file, char *buf, int cap) { fz_error error = fz_okay; fz_obj *dict = NULL; fz_obj *key = NULL; fz_obj *val = NULL; int tok; int len; int a, b; dict = fz_new_dict(8); while (1) { error = pdf_lex(&tok, file, buf, cap, &len); if (error) { fz_drop_obj(dict); return fz_rethrow(error, "cannot parse dict"); } skip: if (tok == PDF_TOK_CLOSE_DICT) { *op = dict; return fz_okay; } /* for BI .. ID .. EI in content streams */ if (tok == PDF_TOK_KEYWORD && !strcmp(buf, "ID")) { *op = dict; return fz_okay; } if (tok != PDF_TOK_NAME) { fz_drop_obj(dict); return fz_throw("invalid key in dict"); } key = fz_new_name(buf); error = pdf_lex(&tok, file, buf, cap, &len); if (error) { fz_drop_obj(key); fz_drop_obj(dict); return fz_rethrow(error, "cannot parse dict"); } switch (tok) { case PDF_TOK_OPEN_ARRAY: error = pdf_parse_array(&val, xref, file, buf, cap); if (error) { fz_drop_obj(key); fz_drop_obj(dict); return fz_rethrow(error, "cannot parse dict"); } break; case PDF_TOK_OPEN_DICT: error = pdf_parse_dict(&val, xref, file, buf, cap); if (error) { fz_drop_obj(key); fz_drop_obj(dict); return fz_rethrow(error, "cannot parse dict"); } break; case PDF_TOK_NAME: val = fz_new_name(buf); break; case PDF_TOK_REAL: val = fz_new_real(fz_atof(buf)); break; case PDF_TOK_STRING: val = fz_new_string(buf, len); break; case PDF_TOK_TRUE: val = fz_new_bool(1); break; case PDF_TOK_FALSE: val = fz_new_bool(0); break; case PDF_TOK_NULL: val = fz_new_null(); break; case PDF_TOK_INT: /* 64-bit to allow for numbers > INT_MAX and overflow */ a = (int) strtoll(buf, 0, 10); error = pdf_lex(&tok, file, buf, cap, &len); if (error) { fz_drop_obj(key); fz_drop_obj(dict); return fz_rethrow(error, "cannot parse dict"); } if (tok == PDF_TOK_CLOSE_DICT || tok == PDF_TOK_NAME || (tok == PDF_TOK_KEYWORD && !strcmp(buf, "ID"))) { val = fz_new_int(a); fz_dict_put(dict, key, val); fz_drop_obj(val); fz_drop_obj(key); goto skip; } if (tok == PDF_TOK_INT) { b = atoi(buf); error = pdf_lex(&tok, file, buf, cap, &len); if (error) { fz_drop_obj(key); fz_drop_obj(dict); return fz_rethrow(error, "cannot parse dict"); } if (tok == PDF_TOK_R) { val = fz_new_indirect(a, b, xref); break; } } fz_drop_obj(key); fz_drop_obj(dict); return fz_throw("invalid indirect reference in dict"); default: fz_drop_obj(key); fz_drop_obj(dict); return fz_throw("unknown token in dict"); } fz_dict_put(dict, key, val); fz_drop_obj(val); fz_drop_obj(key); } }
void svg_parse_color(fz_context *ctx, svg_document *doc, const char *str, float *rgb) { int i, l, m, r, cmp, n; rgb[0] = 0.0f; rgb[1] = 0.0f; rgb[2] = 0.0f; /* Crack hex-coded RGB */ if (str[0] == '#') { str ++; n = strlen(str); if (n == 3 || (n > 3 && !ishex(str[3]))) { rgb[0] = (unhex(str[0]) * 16 + unhex(str[0])) / 255.0f; rgb[1] = (unhex(str[1]) * 16 + unhex(str[1])) / 255.0f; rgb[2] = (unhex(str[2]) * 16 + unhex(str[2])) / 255.0f; return; } if (n >= 6) { rgb[0] = (unhex(str[0]) * 16 + unhex(str[1])) / 255.0f; rgb[1] = (unhex(str[2]) * 16 + unhex(str[3])) / 255.0f; rgb[2] = (unhex(str[4]) * 16 + unhex(str[5])) / 255.0f; return; } return; } /* rgb(X,Y,Z) -- whitespace allowed around numbers */ else if (strstr(str, "rgb(")) { int numberlen = 0; char numberbuf[50]; str = str + 4; for (i = 0; i < 3; i++) { while (svg_is_whitespace_or_comma(*str)) str ++; if (svg_is_digit(*str)) { numberlen = 0; while (svg_is_digit(*str) && numberlen < sizeof(numberbuf) - 1) numberbuf[numberlen++] = *str++; numberbuf[numberlen] = 0; if (*str == '%') { str ++; rgb[i] = fz_atof(numberbuf) / 100.0f; } else { rgb[i] = fz_atof(numberbuf) / 255.0f; } } } return; } /* TODO: parse icc-profile(X,Y,Z,W) syntax */ /* Search for a pre-defined color */ else { char keyword[50], *p; fz_strlcpy(keyword, str, sizeof keyword); p = keyword; while (*p && *p >= 'a' && *p <= 'z') ++p; *p = 0; l = 0; r = sizeof(svg_predefined_colors) / sizeof(svg_predefined_colors[0]); while (l <= r) { m = (l + r) / 2; cmp = strcmp(svg_predefined_colors[m].name, keyword); if (cmp > 0) r = m - 1; else if (cmp < 0) l = m + 1; else { rgb[0] = svg_predefined_colors[m].red / 255.0f; rgb[1] = svg_predefined_colors[m].green / 255.0f; rgb[2] = svg_predefined_colors[m].blue / 255.0f; return; } } } }
int main(int argc, char **argv) #endif { const GLFWvidmode *video_mode; int c; while ((c = fz_getopt(argc, argv, "p:r:W:H:S:U:X")) != -1) { switch (c) { default: usage(argv[0]); break; case 'p': password = fz_optarg; break; case 'r': currentzoom = fz_atof(fz_optarg); break; case 'W': layout_w = fz_atof(fz_optarg); break; case 'H': layout_h = fz_atof(fz_optarg); break; case 'S': layout_em = fz_atof(fz_optarg); break; case 'U': layout_css = fz_optarg; break; case 'X': layout_use_doc_css = 0; break; } } if (fz_optind < argc) { fz_strlcpy(filename, argv[fz_optind], sizeof filename); } else { #ifdef _WIN32 win_install(); if (!win_open_file(filename, sizeof filename)) exit(0); #else usage(argv[0]); #endif } title = strrchr(filename, '/'); if (!title) title = strrchr(filename, '\\'); if (title) ++title; else title = filename; memset(&ui, 0, sizeof ui); search_input.p = search_input.text; search_input.q = search_input.p; search_input.end = search_input.p; glfwSetErrorCallback(on_error); if (!glfwInit()) { fprintf(stderr, "cannot initialize glfw\n"); exit(1); } video_mode = glfwGetVideoMode(glfwGetPrimaryMonitor()); screen_w = video_mode->width; screen_h = video_mode->height; window = glfwCreateWindow(DEFAULT_WINDOW_W, DEFAULT_WINDOW_H, filename, NULL, NULL); if (!window) { fprintf(stderr, "cannot create glfw window\n"); exit(1); } glfwMakeContextCurrent(window); ctx = fz_new_context(NULL, NULL, 0); fz_register_document_handlers(ctx); if (layout_css) { fz_buffer *buf = fz_read_file(ctx, layout_css); fz_set_user_css(ctx, fz_string_from_buffer(ctx, buf)); fz_drop_buffer(ctx, buf); } fz_set_use_document_css(ctx, layout_use_doc_css); has_ARB_texture_non_power_of_two = glfwExtensionSupported("GL_ARB_texture_non_power_of_two"); if (!has_ARB_texture_non_power_of_two) fz_warn(ctx, "OpenGL implementation does not support non-power of two texture sizes"); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); ui.fontsize = DEFAULT_UI_FONTSIZE; ui.baseline = DEFAULT_UI_BASELINE; ui.lineheight = DEFAULT_UI_LINEHEIGHT; ui_init_fonts(ctx, ui.fontsize); reload(); shrinkwrap(); glfwSetFramebufferSizeCallback(window, on_reshape); glfwSetCursorPosCallback(window, on_mouse_motion); glfwSetMouseButtonCallback(window, on_mouse_button); glfwSetScrollCallback(window, on_scroll); glfwSetCharModsCallback(window, on_char); glfwSetKeyCallback(window, on_key); glfwSetWindowRefreshCallback(window, on_display); glfwGetFramebufferSize(window, &window_w, &window_h); ui_needs_update = 1; while (!glfwWindowShouldClose(window)) { glfwWaitEvents(); if (ui_needs_update) run_main_loop(); } ui_finish_fonts(ctx); fz_drop_link(ctx, links); fz_drop_page(ctx, page); fz_drop_outline(ctx, outline); fz_drop_document(ctx, doc); fz_drop_context(ctx); glfwTerminate(); return 0; }
static int xps_parse_gradient_stops(fz_context *ctx, xps_document *doc, char *base_uri, fz_xml *node, struct stop *stops, int maxcount) { fz_colorspace *colorspace; float sample[FZ_MAX_COLORS]; float rgb[3]; int before, after; int count; int i; /* We may have to insert 2 extra stops when postprocessing */ maxcount -= 2; count = 0; while (node && count < maxcount) { if (fz_xml_is_tag(node, "GradientStop")) { char *offset = fz_xml_att(node, "Offset"); char *color = fz_xml_att(node, "Color"); if (offset && color) { stops[count].offset = fz_atof(offset); stops[count].index = count; xps_parse_color(ctx, doc, base_uri, color, &colorspace, sample); fz_convert_color(ctx, fz_device_rgb(ctx), rgb, colorspace, sample + 1); stops[count].r = rgb[0]; stops[count].g = rgb[1]; stops[count].b = rgb[2]; stops[count].a = sample[0]; count ++; } } node = fz_xml_next(node); } if (count == 0) { fz_warn(ctx, "gradient brush has no gradient stops"); stops[0].offset = 0; stops[0].r = 0; stops[0].g = 0; stops[0].b = 0; stops[0].a = 1; stops[1].offset = 1; stops[1].r = 1; stops[1].g = 1; stops[1].b = 1; stops[1].a = 1; return 2; } if (count == maxcount) fz_warn(ctx, "gradient brush exceeded maximum number of gradient stops"); /* Postprocess to make sure the range of offsets is 0.0 to 1.0 */ qsort(stops, count, sizeof(struct stop), cmp_stop); before = -1; after = -1; for (i = 0; i < count; i++) { if (stops[i].offset < 0) before = i; if (stops[i].offset > 1) { after = i; break; } } /* Remove all stops < 0 except the largest one */ if (before > 0) { memmove(stops, stops + before, (count - before) * sizeof(struct stop)); count -= before; } /* Remove all stops > 1 except the smallest one */ if (after >= 0) count = after + 1; /* Expand single stop to 0 .. 1 */ if (count == 1) { stops[1] = stops[0]; stops[0].offset = 0; stops[1].offset = 1; return 2; } /* First stop < 0 -- interpolate value to 0 */ if (stops[0].offset < 0) { float d = -stops[0].offset / (stops[1].offset - stops[0].offset); stops[0].offset = 0; stops[0].r = lerp(stops[0].r, stops[1].r, d); stops[0].g = lerp(stops[0].g, stops[1].g, d); stops[0].b = lerp(stops[0].b, stops[1].b, d); stops[0].a = lerp(stops[0].a, stops[1].a, d); } /* Last stop > 1 -- interpolate value to 1 */ if (stops[count-1].offset > 1) { float d = (1 - stops[count-2].offset) / (stops[count-1].offset - stops[count-2].offset); stops[count-1].offset = 1; stops[count-1].r = lerp(stops[count-2].r, stops[count-1].r, d); stops[count-1].g = lerp(stops[count-2].g, stops[count-1].g, d); stops[count-1].b = lerp(stops[count-2].b, stops[count-1].b, d); stops[count-1].a = lerp(stops[count-2].a, stops[count-1].a, d); } /* First stop > 0 -- insert a duplicate at 0 */ if (stops[0].offset > 0) { memmove(stops + 1, stops, count * sizeof(struct stop)); stops[0] = stops[1]; stops[0].offset = 0; count++; } /* Last stop < 1 -- insert a duplicate at 1 */ if (stops[count-1].offset < 1) { stops[count] = stops[count-1]; stops[count].offset = 1; count++; } return count; }
fz_error pdf_parse_ind_obj(fz_obj **op, pdf_xref *xref, fz_stream *file, char *buf, int cap, int *onum, int *ogen, int *ostmofs) { fz_error error = fz_okay; fz_obj *obj = NULL; int num = 0, gen = 0, stm_ofs; int tok; int len; int a, b; error = pdf_lex(&tok, file, buf, cap, &len); if (error) return fz_rethrow(error, "cannot parse indirect object (%d %d R)", num, gen); if (tok != PDF_TOK_INT) return fz_throw("expected object number (%d %d R)", num, gen); num = atoi(buf); error = pdf_lex(&tok, file, buf, cap, &len); if (error) return fz_rethrow(error, "cannot parse indirect object (%d %d R)", num, gen); if (tok != PDF_TOK_INT) return fz_throw("expected generation number (%d %d R)", num, gen); gen = atoi(buf); error = pdf_lex(&tok, file, buf, cap, &len); if (error) return fz_rethrow(error, "cannot parse indirect object (%d %d R)", num, gen); if (tok != PDF_TOK_OBJ) return fz_throw("expected 'obj' keyword (%d %d R)", num, gen); error = pdf_lex(&tok, file, buf, cap, &len); if (error) return fz_rethrow(error, "cannot parse indirect object (%d %d R)", num, gen); switch (tok) { case PDF_TOK_OPEN_ARRAY: error = pdf_parse_array(&obj, xref, file, buf, cap); if (error) return fz_rethrow(error, "cannot parse indirect object (%d %d R)", num, gen); break; case PDF_TOK_OPEN_DICT: error = pdf_parse_dict(&obj, xref, file, buf, cap); if (error) return fz_rethrow(error, "cannot parse indirect object (%d %d R)", num, gen); break; case PDF_TOK_NAME: obj = fz_new_name(buf); break; case PDF_TOK_REAL: obj = fz_new_real(fz_atof(buf)); break; case PDF_TOK_STRING: obj = fz_new_string(buf, len); break; case PDF_TOK_TRUE: obj = fz_new_bool(1); break; case PDF_TOK_FALSE: obj = fz_new_bool(0); break; case PDF_TOK_NULL: obj = fz_new_null(); break; case PDF_TOK_INT: a = atoi(buf); error = pdf_lex(&tok, file, buf, cap, &len); if (error) return fz_rethrow(error, "cannot parse indirect object (%d %d R)", num, gen); if (tok == PDF_TOK_STREAM || tok == PDF_TOK_ENDOBJ) { obj = fz_new_int(a); goto skip; } if (tok == PDF_TOK_INT) { b = atoi(buf); error = pdf_lex(&tok, file, buf, cap, &len); if (error) return fz_rethrow(error, "cannot parse indirect object (%d %d R)", num, gen); if (tok == PDF_TOK_R) { obj = fz_new_indirect(a, b, xref); break; } } return fz_throw("expected 'R' keyword (%d %d R)", num, gen); case PDF_TOK_ENDOBJ: obj = fz_new_null(); goto skip; default: return fz_throw("syntax error in object (%d %d R)", num, gen); } error = pdf_lex(&tok, file, buf, cap, &len); if (error) { fz_drop_obj(obj); return fz_rethrow(error, "cannot parse indirect object (%d %d R)", num, gen); } skip: if (tok == PDF_TOK_STREAM) { int c = fz_read_byte(file); while (c == ' ') c = fz_read_byte(file); if (c == '\r') { c = fz_peek_byte(file); if (c != '\n') fz_warn("line feed missing after stream begin marker (%d %d R)", num, gen); else fz_read_byte(file); } stm_ofs = fz_tell(file); } else if (tok == PDF_TOK_ENDOBJ) { stm_ofs = 0; } else { fz_warn("expected 'endobj' or 'stream' keyword (%d %d R)", num, gen); stm_ofs = 0; } if (onum) *onum = num; if (ogen) *ogen = gen; if (ostmofs) *ostmofs = stm_ofs; *op = obj; return fz_okay; }