// Copyright (C) 2004-2023 Artifex Software, Inc. // // This file is part of MuPDF. // // MuPDF is free software: you can redistribute it and/or modify it under the // terms of the GNU Affero General Public License as published by the Free // Software Foundation, either version 3 of the License, or (at your option) // any later version. // // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more // details. // // You should have received a copy of the GNU Affero General Public License // along with MuPDF. If not, see // // Alternative licensing terms are available from the licensor. // For commercial licensing, see or contact // Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, // CA 94945, U.S.A., +1(415)492-9861, for further information. #include "mupdf/fitz.h" typedef struct { fz_device super; fz_output *out; int depth; } fz_trace_device; static void fz_trace_indent(fz_context *ctx, fz_output *out, int depth) { while (depth-- > 0) fz_write_string(ctx, out, " "); } static void fz_trace_matrix(fz_context *ctx, fz_output *out, fz_matrix ctm) { fz_write_printf(ctx, out, " transform=\"%g %g %g %g %g %g\"", ctm.a, ctm.b, ctm.c, ctm.d, ctm.e, ctm.f); } static void fz_trace_color(fz_context *ctx, fz_output *out, fz_colorspace *colorspace, const float *color, float alpha) { int i, n; if (colorspace) { n = fz_colorspace_n(ctx, colorspace); fz_write_printf(ctx, out, " colorspace=\"%s\" color=\"", fz_colorspace_name(ctx, colorspace)); for (i = 0; i < n; i++) fz_write_printf(ctx, out, "%s%g", i == 0 ? "" : " ", color[i]); fz_write_printf(ctx, out, "\""); } if (alpha < 1) fz_write_printf(ctx, out, " alpha=\"%g\"", alpha); } static void fz_trace_color_params(fz_context *ctx, fz_output *out, fz_color_params color_params) { fz_write_printf(ctx, out, " ri=\"%d\" bp=\"%d\" op=\"%d\" opm=\"%d\"", color_params.ri, color_params.bp, color_params.op, color_params.opm); } static void fz_trace_text_span(fz_context *ctx, fz_output *out, fz_text_span *span, int depth) { int i; fz_trace_indent(ctx, out, depth); fz_write_printf(ctx, out, "font), span->wmode, span->bidi_level); if (span->language != FZ_LANG_UNSET) { char text[8]; fz_string_from_text_language(text, span->language); fz_write_printf(ctx, out, " lang=\"%s\"", text); } fz_write_printf(ctx, out, " trm=\"%g %g %g %g\">\n", span->trm.a, span->trm.b, span->trm.c, span->trm.d); for (i = 0; i < span->len; i++) { int ucs = span->items[i].ucs; float adv = 0; if (span->items[i].gid >= 0) { adv = fz_advance_glyph(ctx, span->font, span->items[i].gid, span->wmode); } fz_trace_indent(ctx, out, depth+1); fz_write_string(ctx, out, "items[i].ucs >= 0) { fz_write_string(ctx, out, " unicode=\""); switch (ucs) { default: if (ucs < 32) fz_write_printf(ctx, out, "&#x%x;", ucs); else fz_write_rune(ctx, out, ucs); break; case '&': fz_write_string(ctx, out, "&"); break; case '\'': fz_write_string(ctx, out, "'"); break; case '"': fz_write_string(ctx, out, """); break; case '<': fz_write_string(ctx, out, "<"); break; case '>': fz_write_string(ctx, out, ">"); break; } fz_write_string(ctx, out, "\""); } if (span->items[i].gid >= 0) { char name[32]; fz_get_glyph_name(ctx, span->font, span->items[i].gid, name, sizeof name); fz_write_printf(ctx, out, " glyph=\"%s\"", name); } fz_write_printf(ctx, out, " x=\"%g\" y=\"%g\" adv=\"%g\"/>\n", span->items[i].x, span->items[i].y, adv); } fz_trace_indent(ctx, out, depth); fz_write_string(ctx, out, "\n"); } static void fz_trace_text(fz_context *ctx, fz_output *out, const fz_text *text, int depth) { fz_text_span *span; for (span = text->head; span; span = span->next) fz_trace_text_span(ctx, out, span, depth); } static void trace_moveto(fz_context *ctx, void *dev_, float x, float y) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n", x, y); } static void trace_lineto(fz_context *ctx, void *dev_, float x, float y) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n", x, y); } static void trace_curveto(fz_context *ctx, void *dev_, float x1, float y1, float x2, float y2, float x3, float y3) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n", x1, y1, x2, y2, x3, y3); } static void trace_close(fz_context *ctx, void *dev_) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n"); } static const fz_path_walker trace_path_walker = { trace_moveto, trace_lineto, trace_curveto, trace_close }; static void fz_trace_path(fz_context *ctx, fz_trace_device *dev, const fz_path *path) { dev->depth++; fz_walk_path(ctx, path, &trace_path_walker, dev); dev->depth--; } static void fz_trace_fill_path(fz_context *ctx, fz_device *dev_, const fz_path *path, int even_odd, fz_matrix ctm, fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n"); fz_trace_path(ctx, dev, path); fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n"); } static void fz_trace_stroke_path(fz_context *ctx, fz_device *dev_, const fz_path *path, const fz_stroke_state *stroke, fz_matrix ctm, fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; int i; fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "linewidth); fz_write_printf(ctx, out, " miterlimit=\"%g\"", stroke->miterlimit); fz_write_printf(ctx, out, " linecap=\"%d,%d,%d\"", stroke->start_cap, stroke->dash_cap, stroke->end_cap); fz_write_printf(ctx, out, " linejoin=\"%d\"", stroke->linejoin); if (stroke->dash_len) { fz_write_printf(ctx, out, " dash_phase=\"%g\" dash=\"", stroke->dash_phase); for (i = 0; i < stroke->dash_len; i++) fz_write_printf(ctx, out, "%s%g", i > 0 ? " " : "", stroke->dash_list[i]); fz_write_printf(ctx, out, "\""); } fz_trace_color(ctx, out, colorspace, color, alpha); fz_trace_color_params(ctx, out, color_params); fz_trace_matrix(ctx, out, ctm); fz_write_printf(ctx, out, ">\n"); fz_trace_path(ctx, dev, path); fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n"); } static void fz_trace_clip_path(fz_context *ctx, fz_device *dev_, const fz_path *path, int even_odd, fz_matrix ctm, fz_rect scissor) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n"); fz_trace_path(ctx, dev, path); fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n"); dev->depth++; } static void fz_trace_clip_stroke_path(fz_context *ctx, fz_device *dev_, const fz_path *path, const fz_stroke_state *stroke, fz_matrix ctm, fz_rect scissor) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n"); fz_trace_path(ctx, dev, path); fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n"); dev->depth++; } static void fz_trace_fill_text(fz_context *ctx, fz_device *dev_, const fz_text *text, fz_matrix ctm, fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n"); fz_trace_text(ctx, out, text, dev->depth+1); fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n"); } static void fz_trace_stroke_text(fz_context *ctx, fz_device *dev_, const fz_text *text, const fz_stroke_state *stroke, fz_matrix ctm, fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n"); fz_trace_text(ctx, out, text, dev->depth+1); fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n"); } static void fz_trace_clip_text(fz_context *ctx, fz_device *dev_, const fz_text *text, fz_matrix ctm, fz_rect scissor) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n"); fz_trace_text(ctx, out, text, dev->depth+1); fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n"); dev->depth++; } static void fz_trace_clip_stroke_text(fz_context *ctx, fz_device *dev_, const fz_text *text, const fz_stroke_state *stroke, fz_matrix ctm, fz_rect scissor) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n"); fz_trace_text(ctx, out, text, dev->depth+1); fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n"); dev->depth++; } static void fz_trace_ignore_text(fz_context *ctx, fz_device *dev_, const fz_text *text, fz_matrix ctm) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n"); fz_trace_text(ctx, out, text, dev->depth+1); fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n"); } static void fz_trace_fill_image(fz_context *ctx, fz_device *dev_, fz_image *image, fz_matrix ctm, float alpha, fz_color_params color_params) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "colorspace) fz_write_printf(ctx, out, " colorspace=\"%s\"", fz_colorspace_name(ctx, image->colorspace)); fz_trace_color_params(ctx, out, color_params); fz_trace_matrix(ctx, out, ctm); fz_write_printf(ctx, out, " width=\"%d\" height=\"%d\"", image->w, image->h); fz_write_printf(ctx, out, "/>\n"); } static void fz_trace_fill_shade(fz_context *ctx, fz_device *dev_, fz_shade *shade, fz_matrix ctm, float alpha, fz_color_params color_params) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "matrix.a, shade->matrix.b, shade->matrix.c, shade->matrix.d, shade->matrix.e, shade->matrix.f); fz_write_printf(ctx, out, " colorspace=\"%s\"", fz_colorspace_name(ctx, shade->colorspace)); fz_trace_color_params(ctx, out, color_params); // TODO: use_background and background // TODO: use_function and function switch (shade->type) { case FZ_FUNCTION_BASED: fz_write_printf(ctx, out, " type=\"function\""); fz_write_printf(ctx, out, " function_matrix=\"%g %g %g %g %g %g\"", shade->u.f.matrix.a, shade->u.f.matrix.b, shade->u.f.matrix.c, shade->u.f.matrix.d, shade->u.f.matrix.e, shade->u.f.matrix.f); fz_write_printf(ctx, out, " domain=\"%g %g %g %g\"", shade->u.f.domain[0][0], shade->u.f.domain[0][1], shade->u.f.domain[1][0], shade->u.f.domain[1][1]); fz_write_printf(ctx, out, " samples=\"%d %d\"", shade->u.f.xdivs, shade->u.f.ydivs); fz_write_printf(ctx, out, "/>\n"); break; case FZ_LINEAR: fz_write_printf(ctx, out, " type=\"linear\""); fz_write_printf(ctx, out, " extend=\"%d %d\"", shade->u.l_or_r.extend[0], shade->u.l_or_r.extend[1]); fz_write_printf(ctx, out, " start=\"%g %g\"", shade->u.l_or_r.coords[0][0], shade->u.l_or_r.coords[0][1]); fz_write_printf(ctx, out, " end=\"%g %g\"", shade->u.l_or_r.coords[1][0], shade->u.l_or_r.coords[1][1]); fz_write_printf(ctx, out, "/>\n"); break; case FZ_RADIAL: fz_write_printf(ctx, out, " type=\"radial\""); fz_write_printf(ctx, out, " extend=\"%d %d\"", shade->u.l_or_r.extend[0], shade->u.l_or_r.extend[1]); fz_write_printf(ctx, out, " inner=\"%g %g %g\"", shade->u.l_or_r.coords[0][0], shade->u.l_or_r.coords[0][1], shade->u.l_or_r.coords[0][2]); fz_write_printf(ctx, out, " outer=\"%g %g %g\"", shade->u.l_or_r.coords[1][0], shade->u.l_or_r.coords[1][1], shade->u.l_or_r.coords[1][2]); fz_write_printf(ctx, out, "/>\n"); break; default: fz_write_printf(ctx, out, " type=\"mesh\"/>\n"); break; } } static void fz_trace_fill_image_mask(fz_context *ctx, fz_device *dev_, fz_image *image, fz_matrix ctm, fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "w, image->h); fz_write_printf(ctx, out, "/>\n"); } static void fz_trace_clip_image_mask(fz_context *ctx, fz_device *dev_, fz_image *image, fz_matrix ctm, fz_rect scissor) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "w, image->h); fz_write_printf(ctx, out, "/>\n"); dev->depth++; } static void fz_trace_pop_clip(fz_context *ctx, fz_device *dev_) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; dev->depth--; fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n"); } static void fz_trace_begin_mask(fz_context *ctx, fz_device *dev_, fz_rect bbox, int luminosity, fz_colorspace *colorspace, const float *color, fz_color_params color_params) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n"); dev->depth++; } static void fz_trace_end_mask(fz_context *ctx, fz_device *dev_) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; dev->depth--; fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n"); dev->depth++; } static void fz_trace_begin_group(fz_context *ctx, fz_device *dev_, fz_rect bbox, fz_colorspace *cs, int isolated, int knockout, int blendmode, float alpha) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n", bbox.x0, bbox.y0, bbox.x1, bbox.y1, isolated, knockout, fz_blendmode_name(blendmode), alpha); dev->depth++; } static void fz_trace_end_group(fz_context *ctx, fz_device *dev_) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; dev->depth--; fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n"); } static int fz_trace_begin_tile(fz_context *ctx, fz_device *dev_, fz_rect area, fz_rect view, float xstep, float ystep, fz_matrix ctm, int id) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n"); dev->depth++; return 0; } static void fz_trace_end_tile(fz_context *ctx, fz_device *dev_) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; dev->depth--; fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n"); } static void fz_trace_begin_layer(fz_context *ctx, fz_device *dev_, const char *name) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n", name); dev->depth++; } static void fz_trace_end_layer(fz_context *ctx, fz_device *dev_) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; dev->depth--; fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n"); } static void fz_trace_begin_structure(fz_context *ctx, fz_device *dev_, fz_structure standard, const char *raw, int uid) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; const char *str = fz_structure_to_string(standard); fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n"); dev->depth++; } static void fz_trace_end_structure(fz_context *ctx, fz_device *dev_) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; dev->depth--; fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n"); } static const char * metatext_type(fz_metatext meta) { switch (meta) { case FZ_METATEXT_ABBREVIATION: return "abbreviation"; case FZ_METATEXT_ACTUALTEXT: return "actualtext"; case FZ_METATEXT_ALT: return "alt"; case FZ_METATEXT_TITLE: return "title"; } return "????"; } static void fz_trace_begin_metatext(fz_context *ctx, fz_device *dev_, fz_metatext meta, const char *txt) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; const char *type = metatext_type(meta); fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n", type, txt ? txt : ""); dev->depth++; } static void fz_trace_end_metatext(fz_context *ctx, fz_device *dev_) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; dev->depth--; fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n"); } static void fz_trace_render_flags(fz_context *ctx, fz_device *dev_, int set, int clear) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n", set, clear); } static void fz_trace_set_default_colorspaces(fz_context *ctx, fz_device *dev_, fz_default_colorspaces *dcs) { fz_trace_device *dev = (fz_trace_device*)dev_; fz_output *out = dev->out; fz_trace_indent(ctx, out, dev->depth); fz_write_printf(ctx, out, "\n",fz_colorspace_name(ctx, fz_default_output_intent(ctx, dcs))); } fz_device *fz_new_trace_device(fz_context *ctx, fz_output *out) { fz_trace_device *dev = fz_new_derived_device(ctx, fz_trace_device); dev->super.fill_path = fz_trace_fill_path; dev->super.stroke_path = fz_trace_stroke_path; dev->super.clip_path = fz_trace_clip_path; dev->super.clip_stroke_path = fz_trace_clip_stroke_path; dev->super.fill_text = fz_trace_fill_text; dev->super.stroke_text = fz_trace_stroke_text; dev->super.clip_text = fz_trace_clip_text; dev->super.clip_stroke_text = fz_trace_clip_stroke_text; dev->super.ignore_text = fz_trace_ignore_text; dev->super.fill_shade = fz_trace_fill_shade; dev->super.fill_image = fz_trace_fill_image; dev->super.fill_image_mask = fz_trace_fill_image_mask; dev->super.clip_image_mask = fz_trace_clip_image_mask; dev->super.pop_clip = fz_trace_pop_clip; dev->super.begin_mask = fz_trace_begin_mask; dev->super.end_mask = fz_trace_end_mask; dev->super.begin_group = fz_trace_begin_group; dev->super.end_group = fz_trace_end_group; dev->super.begin_tile = fz_trace_begin_tile; dev->super.end_tile = fz_trace_end_tile; dev->super.begin_layer = fz_trace_begin_layer; dev->super.end_layer = fz_trace_end_layer; dev->super.begin_structure = fz_trace_begin_structure; dev->super.end_structure = fz_trace_end_structure; dev->super.begin_metatext = fz_trace_begin_metatext; dev->super.end_metatext = fz_trace_end_metatext; dev->super.render_flags = fz_trace_render_flags; dev->super.set_default_colorspaces = fz_trace_set_default_colorspaces; dev->out = out; return (fz_device*)dev; }