Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/source/pdf/pdf-op-buffer.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/source/pdf/pdf-op-buffer.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,1578 @@ +// Copyright (C) 2004-2025 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 <https://www.gnu.org/licenses/agpl-3.0.en.html> +// +// Alternative licensing terms are available from the licensor. +// For commercial licensing, see <https://www.artifex.com/> or contact +// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, +// CA 94129, USA, for further information. + +#include "mupdf/fitz.h" +#include "mupdf/pdf.h" + +typedef struct resources_stack +{ + struct resources_stack *next; + pdf_obj *res; +} resources_stack; + +typedef struct +{ + pdf_processor super; + fz_output *out; + int ahxencode; + int extgstate; + int newlines; + int balance; + pdf_obj *res; + pdf_obj *last_res; + resources_stack *rstack; + int sep; +} pdf_output_processor; + +/* general graphics state */ + +static void +post_op(fz_context *ctx, pdf_output_processor *proc) +{ + if (proc->newlines) + proc->sep = '\n'; + else + proc->sep = 1; +} + +static inline void separate(fz_context *ctx, pdf_output_processor *proc) +{ + if (!proc->sep) + return; + + if (proc->sep == '\n') + fz_write_byte(ctx, proc->out, '\n'); + else + fz_write_byte(ctx, proc->out, ' '); +} + +static void +pdf_out_w(fz_context *ctx, pdf_processor *proc_, float linewidth) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + if (proc->extgstate != 0) + return; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%g w", linewidth); + post_op(ctx, proc); +} + +static void +pdf_out_j(fz_context *ctx, pdf_processor *proc_, int linejoin) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + if (proc->extgstate != 0) + return; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%d j", linejoin); + post_op(ctx, proc); +} + +static void +pdf_out_J(fz_context *ctx, pdf_processor *proc_, int linecap) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + if (proc->extgstate != 0) + return; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%d J", linecap); + post_op(ctx, proc); +} + +static void +pdf_out_M(fz_context *ctx, pdf_processor *proc_, float a) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + if (proc->extgstate != 0) + return; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%g M", a); + post_op(ctx, proc); +} + +static void +pdf_out_d(fz_context *ctx, pdf_processor *proc_, pdf_obj *array, float phase) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + int ahx = proc->ahxencode; + + if (proc->extgstate != 0) + return; + + pdf_print_encrypted_obj(ctx, proc->out, array, 1, ahx, NULL, 0, 0, &proc->sep); + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%g d", phase); + post_op(ctx, proc); +} + +static void +pdf_out_ri(fz_context *ctx, pdf_processor *proc_, const char *intent) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + if (proc->extgstate != 0) + return; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%n ri", intent); + post_op(ctx, proc); +} + +static void +pdf_out_i(fz_context *ctx, pdf_processor *proc_, float flatness) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + if (proc->extgstate != 0) + return; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%g i", flatness); + post_op(ctx, proc); +} + +static void +pdf_out_gs_begin(fz_context *ctx, pdf_processor *proc_, const char *name, pdf_obj *extgstate) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + proc->extgstate = 1; + + fz_write_printf(ctx, proc->out, "%n gs", name); + post_op(ctx, proc); +} + +static void +pdf_out_gs_end(fz_context *ctx, pdf_processor *proc) +{ + ((pdf_output_processor*)proc)->extgstate = 0; +} + +/* special graphics state */ + +static void +pdf_out_q(fz_context *ctx, pdf_processor *proc_) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + proc->balance++; + + separate(ctx, proc); + fz_write_string(ctx, proc->out, "q"); + post_op(ctx, proc); +} + +static void +pdf_out_Q(fz_context *ctx, pdf_processor *proc_) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + proc->balance--; + if (proc->balance < 0) + fz_warn(ctx, "gstate underflow (too many Q operators)"); + + separate(ctx, proc); + fz_write_string(ctx, proc->out, "Q"); + post_op(ctx, proc); +} + +static void +pdf_out_cm(fz_context *ctx, pdf_processor *proc_, float a, float b, float c, float d, float e, float f) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%g %g %g %g %g %g cm", a, b, c, d, e, f); + post_op(ctx, proc); +} + +/* path construction */ + +static void +pdf_out_m(fz_context *ctx, pdf_processor *proc_, float x, float y) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%g %g m", x, y); + post_op(ctx, proc); +} + +static void +pdf_out_l(fz_context *ctx, pdf_processor *proc_, float x, float y) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%g %g l", x, y); + post_op(ctx, proc); +} + +static void +pdf_out_c(fz_context *ctx, pdf_processor *proc_, float x1, float y1, float x2, float y2, float x3, float y3) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%g %g %g %g %g %g c", x1, y1, x2, y2, x3, y3); + post_op(ctx, proc); +} + +static void +pdf_out_v(fz_context *ctx, pdf_processor *proc_, float x2, float y2, float x3, float y3) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%g %g %g %g v", x2, y2, x3, y3); + post_op(ctx, proc); +} + +static void +pdf_out_y(fz_context *ctx, pdf_processor *proc_, float x1, float y1, float x3, float y3) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%g %g %g %g y", x1, y1, x3, y3); + post_op(ctx, proc); +} + +static void +pdf_out_h(fz_context *ctx, pdf_processor *proc_) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_string(ctx, proc->out, "h"); + post_op(ctx, proc); +} + +static void +pdf_out_re(fz_context *ctx, pdf_processor *proc_, float x, float y, float w, float h) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%g %g %g %g re", x, y, w, h); + post_op(ctx, proc); +} + +/* path painting */ + +static void +pdf_out_S(fz_context *ctx, pdf_processor *proc_) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_string(ctx, proc->out, "S"); + post_op(ctx, proc); +} + +static void +pdf_out_s(fz_context *ctx, pdf_processor *proc_) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_string(ctx, proc->out, "s"); + post_op(ctx, proc); +} + +static void +pdf_out_F(fz_context *ctx, pdf_processor *proc_) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_string(ctx, proc->out, "F"); + post_op(ctx, proc); +} + +static void +pdf_out_f(fz_context *ctx, pdf_processor *proc_) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_string(ctx, proc->out, "f"); + post_op(ctx, proc); +} + +static void +pdf_out_fstar(fz_context *ctx, pdf_processor *proc_) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_string(ctx, proc->out, "f*"); + post_op(ctx, proc); +} + +static void +pdf_out_B(fz_context *ctx, pdf_processor *proc_) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_string(ctx, proc->out, "B"); + post_op(ctx, proc); +} + +static void +pdf_out_Bstar(fz_context *ctx, pdf_processor *proc_) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_string(ctx, proc->out, "B*"); + post_op(ctx, proc); +} + +static void +pdf_out_b(fz_context *ctx, pdf_processor *proc_) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_string(ctx, proc->out, "b"); + post_op(ctx, proc); +} + +static void +pdf_out_bstar(fz_context *ctx, pdf_processor *proc_) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_string(ctx, proc->out, "b*"); + post_op(ctx, proc); +} + +static void +pdf_out_n(fz_context *ctx, pdf_processor *proc_) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_string(ctx, proc->out, "n"); + post_op(ctx, proc); +} + +/* clipping paths */ + +static void +pdf_out_W(fz_context *ctx, pdf_processor *proc_) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_string(ctx, proc->out, "W"); + post_op(ctx, proc); +} + +static void +pdf_out_Wstar(fz_context *ctx, pdf_processor *proc_) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_string(ctx, proc->out, "W*"); + post_op(ctx, proc); +} + +/* text objects */ + +static void +pdf_out_BT(fz_context *ctx, pdf_processor *proc_) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_string(ctx, proc->out, "BT"); + post_op(ctx, proc); +} + +static void +pdf_out_ET(fz_context *ctx, pdf_processor *proc_) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_string(ctx, proc->out, "ET"); + post_op(ctx, proc); +} + +/* text state */ + +static void +pdf_out_Tc(fz_context *ctx, pdf_processor *proc_, float charspace) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%g Tc", charspace); + post_op(ctx, proc); +} + +static void +pdf_out_Tw(fz_context *ctx, pdf_processor *proc_, float wordspace) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%g Tw", wordspace); + post_op(ctx, proc); +} + +static void +pdf_out_Tz(fz_context *ctx, pdf_processor *proc_, float scale) +{ + /* scale is exactly as read from the file. */ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%g Tz", scale); + post_op(ctx, proc); +} + +static void +pdf_out_TL(fz_context *ctx, pdf_processor *proc_, float leading) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%g TL", leading); + post_op(ctx, proc); +} + +static void +pdf_out_Tf(fz_context *ctx, pdf_processor *proc_, const char *name, pdf_font_desc *font, float size) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + if (proc->extgstate != 0) + return; + + fz_write_printf(ctx, proc->out, "%n %g Tf", name, size); + post_op(ctx, proc); +} + +static void +pdf_out_Tr(fz_context *ctx, pdf_processor *proc_, int render) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%d Tr", render); + post_op(ctx, proc); +} + +static void +pdf_out_Ts(fz_context *ctx, pdf_processor *proc_, float rise) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%g Ts", rise); + post_op(ctx, proc); +} + +/* text positioning */ + +static void +pdf_out_Td(fz_context *ctx, pdf_processor *proc_, float tx, float ty) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%g %g Td", tx, ty); + post_op(ctx, proc); +} + +static void +pdf_out_TD(fz_context *ctx, pdf_processor *proc_, float tx, float ty) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%g %g TD", tx, ty); + post_op(ctx, proc); +} + +static void +pdf_out_Tm(fz_context *ctx, pdf_processor *proc_, float a, float b, float c, float d, float e, float f) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%g %g %g %g %g %g Tm", a, b, c, d, e, f); + post_op(ctx, proc); +} + +static void +pdf_out_Tstar(fz_context *ctx, pdf_processor *proc_) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_string(ctx, proc->out, "T*"); + post_op(ctx, proc); +} + +/* text showing */ + +static void +fz_write_pdf_string(fz_context *ctx, fz_output *out, const unsigned char *str, size_t len) +{ + size_t i; + + for (i = 0; i < len; ++i) + if (str[i] < 32 || str[i] >= 127) + break; + + if (i < len) + { + fz_write_byte(ctx, out, '<'); + for (i = 0; i < len; ++i) + { + unsigned char c = str[i]; + fz_write_byte(ctx, out, "0123456789abcdef"[(c>>4)&15]); + fz_write_byte(ctx, out, "0123456789abcdef"[(c)&15]); + } + fz_write_byte(ctx, out, '>'); + } + else + { + fz_write_byte(ctx, out, '('); + for (i = 0; i < len; ++i) + { + unsigned char c = str[i]; + if (c == '(' || c == ')' || c == '\\') + fz_write_byte(ctx, out, '\\'); + fz_write_byte(ctx, out, c); + } + fz_write_byte(ctx, out, ')'); + } +} + +static void +pdf_out_TJ(fz_context *ctx, pdf_processor *proc_, pdf_obj *array) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + int ahx = proc->ahxencode; + + pdf_print_encrypted_obj(ctx, proc->out, array, 1, ahx, NULL, 0, 0, &proc->sep); + separate(ctx, proc); + fz_write_string(ctx, proc->out, "TJ"); + post_op(ctx, proc); +} + +static void +pdf_out_Tj(fz_context *ctx, pdf_processor *proc_, char *str, size_t len) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_pdf_string(ctx, proc->out, (const unsigned char *)str, len); + fz_write_string(ctx, proc->out, "Tj"); + post_op(ctx, proc); +} + +static void +pdf_out_squote(fz_context *ctx, pdf_processor *proc_, char *str, size_t len) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_pdf_string(ctx, proc->out, (const unsigned char *)str, len); + fz_write_string(ctx, proc->out, "'"); + post_op(ctx, proc); +} + +static void +pdf_out_dquote(fz_context *ctx, pdf_processor *proc_, float aw, float ac, char *str, size_t len) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%g %g ", aw, ac); + fz_write_pdf_string(ctx, proc->out, (const unsigned char *)str, len); + fz_write_string(ctx, proc->out, "\""); + post_op(ctx, proc); +} + +/* type 3 fonts */ + +static void +pdf_out_d0(fz_context *ctx, pdf_processor *proc_, float wx, float wy) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%g %g d0", wx, wy); + post_op(ctx, proc); +} + +static void +pdf_out_d1(fz_context *ctx, pdf_processor *proc_, float wx, float wy, float llx, float lly, float urx, float ury) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%g %g %g %g %g %g d1", wx, wy, llx, lly, urx, ury); + post_op(ctx, proc); +} + +/* color */ + +static void +pdf_out_CS(fz_context *ctx, pdf_processor *proc_, const char *name, fz_colorspace *cs) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%n CS", name); + post_op(ctx, proc); +} + +static void +pdf_out_cs(fz_context *ctx, pdf_processor *proc_, const char *name, fz_colorspace *cs) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%n cs", name); + post_op(ctx, proc); +} + +static void +pdf_out_SC_pattern(fz_context *ctx, pdf_processor *proc_, const char *name, pdf_pattern *pat, int n, float *color) +{ + int i; + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + for (i = 0; i < n; ++i) + fz_write_printf(ctx, proc->out, "%g ", color[i]); + fz_write_printf(ctx, proc->out, "%n SCN", name); + post_op(ctx, proc); +} + +static void +pdf_out_sc_pattern(fz_context *ctx, pdf_processor *proc_, const char *name, pdf_pattern *pat, int n, float *color) +{ + int i; + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + for (i = 0; i < n; ++i) + fz_write_printf(ctx, proc->out, "%g ", color[i]); + fz_write_printf(ctx, proc->out, "%n scn", name); + post_op(ctx, proc); +} + +static void +pdf_out_SC_shade(fz_context *ctx, pdf_processor *proc_, const char *name, fz_shade *shade) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%n SCN", name); + post_op(ctx, proc); +} + +static void +pdf_out_sc_shade(fz_context *ctx, pdf_processor *proc_, const char *name, fz_shade *shade) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%n scn", name); + post_op(ctx, proc); +} + +static void +pdf_out_SC_color(fz_context *ctx, pdf_processor *proc_, int n, float *color) +{ + int i; + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + for (i = 0; i < n; ++i) + fz_write_printf(ctx, proc->out, "%g ", color[i]); + fz_write_string(ctx, proc->out, "SCN"); + post_op(ctx, proc); +} + +static void +pdf_out_sc_color(fz_context *ctx, pdf_processor *proc_, int n, float *color) +{ + int i; + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + for (i = 0; i < n; ++i) + fz_write_printf(ctx, proc->out, "%g ", color[i]); + fz_write_string(ctx, proc->out, "scn"); + post_op(ctx, proc); +} + +static void +pdf_out_G(fz_context *ctx, pdf_processor *proc_, float g) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%g G", g); + post_op(ctx, proc); +} + +static void +pdf_out_g(fz_context *ctx, pdf_processor *proc_, float g) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%g g", g); + post_op(ctx, proc); +} + +static void +pdf_out_RG(fz_context *ctx, pdf_processor *proc_, float r, float g, float b) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%g %g %g RG", r, g, b); + post_op(ctx, proc); +} + +static void +pdf_out_rg(fz_context *ctx, pdf_processor *proc_, float r, float g, float b) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%g %g %g rg", r, g, b); + post_op(ctx, proc); +} + +static void +pdf_out_K(fz_context *ctx, pdf_processor *proc_, float c, float m, float y, float k) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%g %g %g %g K", c, m, y, k); + post_op(ctx, proc); +} + +static void +pdf_out_k(fz_context *ctx, pdf_processor *proc_, float c, float m, float y, float k) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%g %g %g %g k", c, m, y, k); + post_op(ctx, proc); +} + +/* shadings, images, xobjects */ + +static void +pdf_out_BI(fz_context *ctx, pdf_processor *proc_, fz_image *img, const char *colorspace) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + fz_output *out = proc->out; + int ahx = proc->ahxencode; + fz_compressed_buffer *cbuf; + fz_buffer *buf = NULL; + int i, w, h, bpc; + unsigned char *data; + size_t len; + fz_pixmap *pix = NULL; + fz_colorspace *cs; + int type; + + if (img == NULL) + return; + cbuf = fz_compressed_image_buffer(ctx, img); + if (cbuf == NULL) + { + pix = fz_get_pixmap_from_image(ctx, img, NULL, NULL, &w, &h); + bpc = 8; + cs = pix->colorspace; + type = FZ_IMAGE_RAW; + } + else + { + buf = cbuf->buffer; + if (buf == NULL) + return; + w = img->w; + h = img->h; + bpc = img->bpc; + cs = img->colorspace; + type = cbuf->params.type; + } + + fz_try(ctx) + { + separate(ctx, proc); + fz_write_string(ctx, out, "BI "); + fz_write_printf(ctx, out, "/W %d", w); + fz_write_printf(ctx, out, "/H %d", h); + fz_write_printf(ctx, out, "/BPC %d", bpc); + if (img->imagemask) + fz_write_string(ctx, out, "/IM true"); + else if (cs == fz_device_gray(ctx)) + fz_write_string(ctx, out, "/CS/G"); + else if (cs == fz_device_rgb(ctx)) + fz_write_string(ctx, out, "/CS/RGB"); + else if (cs == fz_device_cmyk(ctx)) + fz_write_string(ctx, out, "/CS/CMYK"); + else if (cs) + fz_write_printf(ctx, out, "/CS%n", colorspace); + else + fz_throw(ctx, FZ_ERROR_ARGUMENT, "BI operator can only show ImageMask, Gray, RGB, or CMYK images"); + if (img->interpolate) + fz_write_string(ctx, out, "/I true"); + fz_write_string(ctx, out, "/D["); + for (i = 0; i < img->n * 2; ++i) + { + if (i > 0) + fz_write_byte(ctx, out, ' '); + fz_write_printf(ctx, out, "%g", img->decode[i]); + } + fz_write_string(ctx, out, "]"); + proc->sep = 0; + + switch (type) + { + default: + fz_throw(ctx, FZ_ERROR_ARGUMENT, "unknown compressed buffer type"); + break; + + case FZ_IMAGE_JPEG: + fz_write_string(ctx, out, ahx ? "/F[/AHx/DCT]" : "/F/DCT"); + proc->sep = !ahx; + if (cbuf->params.u.jpeg.color_transform >= 0) + { + fz_write_printf(ctx, out, "/DP<</ColorTransform %d>>", cbuf->params.u.jpeg.color_transform); + proc->sep = 0; + } + if (cbuf->params.u.jpeg.invert_cmyk && img->n == 4) + { + fz_write_string(ctx, out, "/D[1 0 1 0 1 0 1 0]"); + proc->sep = 0; + } + break; + + case FZ_IMAGE_FAX: + fz_write_string(ctx, out, ahx ? "/F[/AHx/CCF]/DP[null<<" : "/F/CCF/DP<<"); + fz_write_printf(ctx, out, "/K %d", cbuf->params.u.fax.k); + if (cbuf->params.u.fax.columns != 1728) + fz_write_printf(ctx, out, "/Columns %d", cbuf->params.u.fax.columns); + if (cbuf->params.u.fax.rows > 0) + fz_write_printf(ctx, out, "/Rows %d", cbuf->params.u.fax.rows); + if (cbuf->params.u.fax.end_of_line) + fz_write_string(ctx, out, "/EndOfLine true"); + if (cbuf->params.u.fax.encoded_byte_align) + fz_write_string(ctx, out, "/EncodedByteAlign true"); + if (!cbuf->params.u.fax.end_of_block) + fz_write_string(ctx, out, "/EndOfBlock false"); + if (cbuf->params.u.fax.black_is_1) + fz_write_string(ctx, out, "/BlackIs1 true"); + if (cbuf->params.u.fax.damaged_rows_before_error > 0) + fz_write_printf(ctx, out, "/DamagedRowsBeforeError %d", + cbuf->params.u.fax.damaged_rows_before_error); + fz_write_string(ctx, out, ahx ? ">>]" : ">>"); + proc->sep = 0; + break; + + case FZ_IMAGE_RAW: + if (ahx) + { + fz_write_string(ctx, out, "/F/AHx"); + proc->sep = 1; + } + break; + + case FZ_IMAGE_RLD: + fz_write_string(ctx, out, ahx ? "/F[/AHx/RL]" : "/F/RL"); + proc->sep = !ahx; + break; + + case FZ_IMAGE_FLATE: + fz_write_string(ctx, out, ahx ? "/F[/AHx/Fl]" : "/F/Fl"); + proc->sep = !ahx; + if (cbuf->params.u.flate.predictor > 1) + { + fz_write_string(ctx, out, ahx ? "/DP[null<<" : "/DP<<"); + fz_write_printf(ctx, out, "/Predictor %d", cbuf->params.u.flate.predictor); + if (cbuf->params.u.flate.columns != 1) + fz_write_printf(ctx, out, "/Columns %d", cbuf->params.u.flate.columns); + if (cbuf->params.u.flate.colors != 1) + fz_write_printf(ctx, out, "/Colors %d", cbuf->params.u.flate.colors); + if (cbuf->params.u.flate.bpc != 8) + fz_write_printf(ctx, out, "/BitsPerComponent %d", cbuf->params.u.flate.bpc); + fz_write_string(ctx, out, ahx ? ">>]" : ">>"); + proc->sep = 0; + } + break; + + case FZ_IMAGE_LZW: + fz_write_string(ctx, out, ahx ? "/F[/AHx/LZW]" : "/F/LZW"); + proc->sep = !ahx; + if (cbuf->params.u.lzw.predictor > 1) + { + fz_write_string(ctx, out, ahx ? "/DP[null<<" : "/DP<<"); + fz_write_printf(ctx, out, "/Predictor %d", cbuf->params.u.lzw.predictor); + if (cbuf->params.u.lzw.columns != 1) + fz_write_printf(ctx, out, "/Columns %d", cbuf->params.u.lzw.columns); + if (cbuf->params.u.lzw.colors != 1) + fz_write_printf(ctx, out, "/Colors %d", cbuf->params.u.lzw.colors); + if (cbuf->params.u.lzw.bpc != 8) + fz_write_printf(ctx, out, "/BitsPerComponent %d", cbuf->params.u.lzw.bpc); + if (cbuf->params.u.lzw.early_change != 1) + fz_write_printf(ctx, out, "/EarlyChange %d", cbuf->params.u.lzw.early_change); + fz_write_string(ctx, out, ahx ? ">>]" : ">>"); + proc->sep = 0; + } + break; + + case FZ_IMAGE_BROTLI: + fz_write_string(ctx, out, ahx ? "/F[/AHx/Br]\n" : "/F/Br\n"); + if (cbuf->params.u.brotli.predictor > 1) + { + fz_write_string(ctx, out, ahx ? "/DP[null<<\n" : "/DP<<\n"); + fz_write_printf(ctx, out, "/Predictor %d\n", cbuf->params.u.brotli.predictor); + if (cbuf->params.u.brotli.columns != 1) + fz_write_printf(ctx, out, "/Columns %d\n", cbuf->params.u.brotli.columns); + if (cbuf->params.u.brotli.colors != 1) + fz_write_printf(ctx, out, "/Colors %d\n", cbuf->params.u.brotli.colors); + if (cbuf->params.u.brotli.bpc != 8) + fz_write_printf(ctx, out, "/BitsPerComponent %d\n", cbuf->params.u.brotli.bpc); + fz_write_string(ctx, out, ahx ? ">>]\n" : ">>\n"); + } + break; + } + + separate(ctx, proc); + fz_write_string(ctx, out, "ID "); + if (buf) + len = fz_buffer_storage(ctx, buf, &data); + else + { + data = pix->samples; + len = ((size_t)w) * h * pix->n; + } + if (ahx) + { + size_t z; + for (z = 0; z < len; ++z) + { + int c = data[z]; + fz_write_byte(ctx, out, "0123456789abcdef"[(c >> 4) & 0xf]); + fz_write_byte(ctx, out, "0123456789abcdef"[c & 0xf]); + if ((z & 31) == 31) + fz_write_byte(ctx, out, '\n'); + } + fz_write_byte(ctx, out, '>'); + } + else + { + fz_write_data(ctx, out, data, len); + } + fz_write_string(ctx, out, " EI"); + proc->sep = 1; + } + fz_always(ctx) + fz_drop_pixmap(ctx, pix); + fz_catch(ctx) + fz_rethrow(ctx); +} + +static void +pdf_out_sh(fz_context *ctx, pdf_processor *proc_, const char *name, fz_shade *shade) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%n sh", name); + post_op(ctx, proc); +} + +static void +pdf_out_Do_image(fz_context *ctx, pdf_processor *proc_, const char *name, fz_image *image) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%n Do", name); + post_op(ctx, proc); +} + +static void +pdf_out_Do_form(fz_context *ctx, pdf_processor *proc_, const char *name, pdf_obj *xobj) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%n Do", name); + post_op(ctx, proc); +} + +/* marked content */ + +static void +pdf_out_MP(fz_context *ctx, pdf_processor *proc_, const char *tag) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%n MP", tag); + post_op(ctx, proc); +} + +static void +pdf_out_DP(fz_context *ctx, pdf_processor *proc_, const char *tag, pdf_obj *raw, pdf_obj *cooked) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + int ahx = proc->ahxencode; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%n", tag); + proc->sep = 1; + pdf_print_encrypted_obj(ctx, proc->out, raw, 1, ahx, NULL, 0, 0, &proc->sep); + separate(ctx, proc); + fz_write_string(ctx, proc->out, "DP"); + post_op(ctx, proc); +} + +static void +pdf_out_BMC(fz_context *ctx, pdf_processor *proc_, const char *tag) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%n BMC", tag); + post_op(ctx, proc); +} + +static void +pdf_out_BDC(fz_context *ctx, pdf_processor *proc_, const char *tag, pdf_obj *raw, pdf_obj *cooked) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + int ahx = proc->ahxencode; + + separate(ctx, proc); + fz_write_printf(ctx, proc->out, "%n", tag); + proc->sep = 1; + pdf_print_encrypted_obj(ctx, proc->out, raw, 1, ahx, NULL, 0, 0, &proc->sep); + separate(ctx, proc); + fz_write_string(ctx, proc->out, "BDC"); + post_op(ctx, proc); +} + +static void +pdf_out_EMC(fz_context *ctx, pdf_processor *proc_) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_string(ctx, proc->out, "EMC"); + post_op(ctx, proc); +} + +/* compatibility */ + +static void +pdf_out_BX(fz_context *ctx, pdf_processor *proc_) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_string(ctx, proc->out, "BX"); + post_op(ctx, proc); +} + +static void +pdf_out_EX(fz_context *ctx, pdf_processor *proc_) +{ + pdf_output_processor *proc = (pdf_output_processor *)proc_; + + separate(ctx, proc); + fz_write_string(ctx, proc->out, "EX"); + post_op(ctx, proc); +} + +static void +pdf_close_output_processor(fz_context *ctx, pdf_processor *proc_) +{ + pdf_output_processor *proc = (pdf_output_processor*)proc_; + fz_output *out = proc->out; + + /* Add missing 'Q' operators to get back to zero. */ + /* We can't prepend missing 'q' operators to guarantee we don't underflow. */ + while (proc->balance > 0) + { + proc->balance--; + separate(ctx, proc); + fz_write_byte(ctx, out, 'Q'); + post_op(ctx, proc); + } + + fz_close_output(ctx, out); +} + +static void +pdf_drop_output_processor(fz_context *ctx, pdf_processor *proc) +{ + pdf_output_processor *p = (pdf_output_processor *)proc; + + fz_drop_output(ctx, p->out); +} + +static void +pdf_reset_output_processor(fz_context *ctx, pdf_processor *proc) +{ + pdf_output_processor *p = (pdf_output_processor *)proc; + + fz_reset_output(ctx, p->out); +} + +static void +pdf_out_push_resources(fz_context *ctx, pdf_processor *proc, pdf_obj *res) +{ + pdf_output_processor *p = (pdf_output_processor *)proc; + resources_stack *stk = fz_malloc_struct(ctx, resources_stack); + + stk->next = p->rstack; + p->rstack = stk; + stk->res = pdf_keep_obj(ctx, res); +} + +static pdf_obj * +pdf_out_pop_resources(fz_context *ctx, pdf_processor *proc) +{ + pdf_output_processor *p = (pdf_output_processor *)proc; + resources_stack *stk = p->rstack; + pdf_obj *res = stk->res; + + p->rstack = stk->next; + fz_free(ctx, stk); + + return res; +} + +pdf_processor * +pdf_new_output_processor(fz_context *ctx, fz_output *out, int ahxencode, int newlines) +{ + pdf_output_processor *proc = pdf_new_processor(ctx, sizeof *proc); + + proc->super.close_processor = pdf_close_output_processor; + proc->super.drop_processor = pdf_drop_output_processor; + proc->super.reset_processor = pdf_reset_output_processor; + + proc->super.push_resources = pdf_out_push_resources; + proc->super.pop_resources = pdf_out_pop_resources; + + /* general graphics state */ + proc->super.op_w = pdf_out_w; + proc->super.op_j = pdf_out_j; + proc->super.op_J = pdf_out_J; + proc->super.op_M = pdf_out_M; + proc->super.op_d = pdf_out_d; + proc->super.op_ri = pdf_out_ri; + proc->super.op_i = pdf_out_i; + proc->super.op_gs_begin = pdf_out_gs_begin; + proc->super.op_gs_end = pdf_out_gs_end; + + /* transparency graphics state */ + proc->super.op_gs_BM = NULL; + proc->super.op_gs_CA = NULL; + proc->super.op_gs_ca = NULL; + proc->super.op_gs_SMask = NULL; + + /* special graphics state */ + proc->super.op_q = pdf_out_q; + proc->super.op_Q = pdf_out_Q; + proc->super.op_cm = pdf_out_cm; + + /* path construction */ + proc->super.op_m = pdf_out_m; + proc->super.op_l = pdf_out_l; + proc->super.op_c = pdf_out_c; + proc->super.op_v = pdf_out_v; + proc->super.op_y = pdf_out_y; + proc->super.op_h = pdf_out_h; + proc->super.op_re = pdf_out_re; + + /* path painting */ + proc->super.op_S = pdf_out_S; + proc->super.op_s = pdf_out_s; + proc->super.op_F = pdf_out_F; + proc->super.op_f = pdf_out_f; + proc->super.op_fstar = pdf_out_fstar; + proc->super.op_B = pdf_out_B; + proc->super.op_Bstar = pdf_out_Bstar; + proc->super.op_b = pdf_out_b; + proc->super.op_bstar = pdf_out_bstar; + proc->super.op_n = pdf_out_n; + + /* clipping paths */ + proc->super.op_W = pdf_out_W; + proc->super.op_Wstar = pdf_out_Wstar; + + /* text objects */ + proc->super.op_BT = pdf_out_BT; + proc->super.op_ET = pdf_out_ET; + + /* text state */ + proc->super.op_Tc = pdf_out_Tc; + proc->super.op_Tw = pdf_out_Tw; + proc->super.op_Tz = pdf_out_Tz; + proc->super.op_TL = pdf_out_TL; + proc->super.op_Tf = pdf_out_Tf; + proc->super.op_Tr = pdf_out_Tr; + proc->super.op_Ts = pdf_out_Ts; + + /* text positioning */ + proc->super.op_Td = pdf_out_Td; + proc->super.op_TD = pdf_out_TD; + proc->super.op_Tm = pdf_out_Tm; + proc->super.op_Tstar = pdf_out_Tstar; + + /* text showing */ + proc->super.op_TJ = pdf_out_TJ; + proc->super.op_Tj = pdf_out_Tj; + proc->super.op_squote = pdf_out_squote; + proc->super.op_dquote = pdf_out_dquote; + + /* type 3 fonts */ + proc->super.op_d0 = pdf_out_d0; + proc->super.op_d1 = pdf_out_d1; + + /* color */ + proc->super.op_CS = pdf_out_CS; + proc->super.op_cs = pdf_out_cs; + proc->super.op_SC_color = pdf_out_SC_color; + proc->super.op_sc_color = pdf_out_sc_color; + proc->super.op_SC_pattern = pdf_out_SC_pattern; + proc->super.op_sc_pattern = pdf_out_sc_pattern; + proc->super.op_SC_shade = pdf_out_SC_shade; + proc->super.op_sc_shade = pdf_out_sc_shade; + + proc->super.op_G = pdf_out_G; + proc->super.op_g = pdf_out_g; + proc->super.op_RG = pdf_out_RG; + proc->super.op_rg = pdf_out_rg; + proc->super.op_K = pdf_out_K; + proc->super.op_k = pdf_out_k; + + /* shadings, images, xobjects */ + proc->super.op_BI = pdf_out_BI; + proc->super.op_sh = pdf_out_sh; + proc->super.op_Do_image = pdf_out_Do_image; + proc->super.op_Do_form = pdf_out_Do_form; + + /* marked content */ + proc->super.op_MP = pdf_out_MP; + proc->super.op_DP = pdf_out_DP; + proc->super.op_BMC = pdf_out_BMC; + proc->super.op_BDC = pdf_out_BDC; + proc->super.op_EMC = pdf_out_EMC; + + /* compatibility */ + proc->super.op_BX = pdf_out_BX; + proc->super.op_EX = pdf_out_EX; + + /* extgstate */ + proc->super.op_gs_OP = NULL; + proc->super.op_gs_op = NULL; + proc->super.op_gs_OPM = NULL; + proc->super.op_gs_UseBlackPtComp = NULL; + + proc->out = out; + proc->ahxencode = ahxencode; + proc->newlines = newlines; + + proc->super.requirements = PDF_PROCESSOR_REQUIRES_DECODED_IMAGES; + + proc->balance = 0; + + return (pdf_processor*)proc; +} + +pdf_processor * +pdf_new_buffer_processor(fz_context *ctx, fz_buffer *buffer, int ahxencode, int newlines) +{ + pdf_processor *proc = NULL; + fz_output *out = fz_new_output_with_buffer(ctx, buffer); + fz_try(ctx) + { + proc = pdf_new_output_processor(ctx, out, ahxencode, newlines); + } + fz_catch(ctx) + { + fz_drop_output(ctx, out); + fz_rethrow(ctx); + } + return proc; +} + +/* Simplified processor that only counts matching q/Q pairs. */ + +typedef struct +{ + pdf_processor super; + int *balance; + int *min_q; + int *min_op_q; + int first; + int ending; +} pdf_balance_processor; + +static void +pdf_balance_q(fz_context *ctx, pdf_processor *proc_) +{ + pdf_balance_processor *proc = (pdf_balance_processor*)proc_; + (*proc->balance)++; +} + +static void +pdf_balance_Q(fz_context *ctx, pdf_processor *proc_) +{ + pdf_balance_processor *proc = (pdf_balance_processor*)proc_; + + if (proc->ending) + return; + + (*proc->balance)--; + if (*proc->balance < *proc->min_q) + *proc->min_q = *proc->balance; +} + +static void +pdf_balance_void(fz_context *ctx, pdf_processor *proc_) +{ + pdf_balance_processor *proc = (pdf_balance_processor*)proc_; + if (*proc->balance < *proc->min_op_q) + *proc->min_op_q = *proc->balance; +} + +#define BALANCE { pdf_balance_void(ctx, p); } + +static void pdf_balance_string(fz_context *ctx, pdf_processor *p, const char *x) BALANCE +static void pdf_balance_int(fz_context *ctx, pdf_processor *p, int x) BALANCE +static void pdf_balance_float(fz_context *ctx, pdf_processor *p, float x) BALANCE +static void pdf_balance_float2(fz_context *ctx, pdf_processor *p, float x, float y) BALANCE +static void pdf_balance_float3(fz_context *ctx, pdf_processor *p, float x, float y, float z) BALANCE +static void pdf_balance_float4(fz_context *ctx, pdf_processor *p, float x, float y, float z, float w) BALANCE +static void pdf_balance_float6(fz_context *ctx, pdf_processor *p, float a, float b, float c, float d, float e, float f) BALANCE + +static void pdf_balance_d(fz_context *ctx, pdf_processor *p, pdf_obj *array, float phase) BALANCE +static void pdf_balance_gs_begin(fz_context *ctx, pdf_processor *p, const char *name, pdf_obj *extgstate) BALANCE +static void pdf_balance_Tf(fz_context *ctx, pdf_processor *p, const char *name, pdf_font_desc *font, float size) BALANCE +static void pdf_balance_TJ(fz_context *ctx, pdf_processor *p, pdf_obj *array) BALANCE +static void pdf_balance_Tj(fz_context *ctx, pdf_processor *p, char *str, size_t len) BALANCE +static void pdf_balance_squote(fz_context *ctx, pdf_processor *p, char *str, size_t len) BALANCE +static void pdf_balance_dquote(fz_context *ctx, pdf_processor *p, float aw, float ac, char *str, size_t len) BALANCE +static void pdf_balance_cs(fz_context *ctx, pdf_processor *p, const char *name, fz_colorspace *cs) BALANCE +static void pdf_balance_sc_pattern(fz_context *ctx, pdf_processor *p, const char *name, pdf_pattern *pat, int n, float *color) BALANCE +static void pdf_balance_sc_shade(fz_context *ctx, pdf_processor *p, const char *name, fz_shade *shade) BALANCE +static void pdf_balance_sc_color(fz_context *ctx, pdf_processor *p, int n, float *color) BALANCE +static void pdf_balance_BDC(fz_context *ctx, pdf_processor *p, const char *tag, pdf_obj *raw, pdf_obj *cooked) BALANCE +static void pdf_balance_BI(fz_context *ctx, pdf_processor *p, fz_image *img, const char *colorspace) BALANCE +static void pdf_balance_sh(fz_context *ctx, pdf_processor *p, const char *name, fz_shade *shade) BALANCE +static void pdf_balance_Do_image(fz_context *ctx, pdf_processor *p, const char *name, fz_image *image) BALANCE +static void pdf_balance_Do_form(fz_context *ctx, pdf_processor *p, const char *name, pdf_obj *xobj) BALANCE + +static void pdf_balance_EOD(fz_context *ctx, pdf_processor *p) +{ + pdf_balance_processor *proc = (pdf_balance_processor *)p; + + proc->ending = 1; +} + +static pdf_processor * +pdf_new_balance_processor(fz_context *ctx, int *balance, int *min_q, int *min_op_q) +{ + pdf_balance_processor *proc = pdf_new_processor(ctx, sizeof *proc); + + proc->super.op_q = pdf_balance_q; + proc->super.op_Q = pdf_balance_Q; + + /* general graphics state */ + proc->super.op_w = pdf_balance_float; + proc->super.op_j = pdf_balance_int; + proc->super.op_J = pdf_balance_int; + proc->super.op_M = pdf_balance_float; + proc->super.op_d = pdf_balance_d; + proc->super.op_ri = pdf_balance_string; + proc->super.op_i = pdf_balance_float; + proc->super.op_gs_begin = pdf_balance_gs_begin; + + /* special graphics state */ + proc->super.op_cm = pdf_balance_float6; + + /* path construction */ + proc->super.op_m = pdf_balance_float2; + proc->super.op_l = pdf_balance_float2; + proc->super.op_c = pdf_balance_float6; + proc->super.op_v = pdf_balance_float4; + proc->super.op_y = pdf_balance_float4; + proc->super.op_h = pdf_balance_void; + proc->super.op_re = pdf_balance_float4; + + /* path painting */ + proc->super.op_S = pdf_balance_void; + proc->super.op_s = pdf_balance_void; + proc->super.op_F = pdf_balance_void; + proc->super.op_f = pdf_balance_void; + proc->super.op_fstar = pdf_balance_void; + proc->super.op_B = pdf_balance_void; + proc->super.op_Bstar = pdf_balance_void; + proc->super.op_b = pdf_balance_void; + proc->super.op_bstar = pdf_balance_void; + proc->super.op_n = pdf_balance_void; + + /* clipping paths */ + proc->super.op_W = pdf_balance_void; + proc->super.op_Wstar = pdf_balance_void; + + /* text objects */ + proc->super.op_BT = pdf_balance_void; + proc->super.op_ET = pdf_balance_void; + + /* text state */ + proc->super.op_Tc = pdf_balance_float; + proc->super.op_Tw = pdf_balance_float; + proc->super.op_Tz = pdf_balance_float; + proc->super.op_TL = pdf_balance_float; + proc->super.op_Tf = pdf_balance_Tf; + proc->super.op_Tr = pdf_balance_int; + proc->super.op_Ts = pdf_balance_float; + + /* text positioning */ + proc->super.op_Td = pdf_balance_float2; + proc->super.op_TD = pdf_balance_float2; + proc->super.op_Tm = pdf_balance_float6; + proc->super.op_Tstar = pdf_balance_void; + + /* text showing */ + proc->super.op_TJ = pdf_balance_TJ; + proc->super.op_Tj = pdf_balance_Tj; + proc->super.op_squote = pdf_balance_squote; + proc->super.op_dquote = pdf_balance_dquote; + + /* type 3 fonts */ + proc->super.op_d0 = pdf_balance_float2; + proc->super.op_d1 = pdf_balance_float6; + + /* color */ + proc->super.op_CS = pdf_balance_cs; + proc->super.op_cs = pdf_balance_cs; + proc->super.op_SC_color = pdf_balance_sc_color; + proc->super.op_sc_color = pdf_balance_sc_color; + proc->super.op_SC_pattern = pdf_balance_sc_pattern; + proc->super.op_sc_pattern = pdf_balance_sc_pattern; + proc->super.op_SC_shade = pdf_balance_sc_shade; + proc->super.op_sc_shade = pdf_balance_sc_shade; + + proc->super.op_G = pdf_balance_float; + proc->super.op_g = pdf_balance_float; + proc->super.op_RG = pdf_balance_float3; + proc->super.op_rg = pdf_balance_float3; + proc->super.op_K = pdf_balance_float4; + proc->super.op_k = pdf_balance_float4; + + /* shadings, images, xobjects */ + proc->super.op_BI = pdf_balance_BI; + proc->super.op_sh = pdf_balance_sh; + proc->super.op_Do_image = pdf_balance_Do_image; + proc->super.op_Do_form = pdf_balance_Do_form; + + /* marked content */ + proc->super.op_MP = pdf_balance_string; + proc->super.op_DP = pdf_balance_BDC; + proc->super.op_BMC = pdf_balance_string; + proc->super.op_BDC = pdf_balance_BDC; + proc->super.op_EMC = pdf_balance_void; + + /* compatibility */ + proc->super.op_BX = pdf_balance_void; + proc->super.op_EX = pdf_balance_void; + + proc->super.op_EOD = pdf_balance_EOD; + + proc->balance = balance; + proc->min_q = min_q; + proc->min_op_q = min_op_q; + + return (pdf_processor*)proc; +} + +void +pdf_count_q_balance(fz_context *ctx, pdf_document *doc, pdf_obj *res, pdf_obj *stm, int *prepend, int *append) +{ + pdf_processor *proc; + + int end_q = 0; + int min_q = 0; + int min_op_q = 1; + + proc = pdf_new_balance_processor(ctx, &end_q, &min_q, &min_op_q); + fz_try(ctx) + { + pdf_process_raw_contents(ctx, proc, doc, res, stm, NULL); + pdf_close_processor(ctx, proc); + } + fz_always(ctx) + pdf_drop_processor(ctx, proc); + fz_catch(ctx) + fz_rethrow(ctx); + + /* normally zero, but in bad files there could be more Q than q */ + *prepend = -min_q; + + /* how many Q are missing at the end */ + *append = end_q - min_q; + + /* if there are unguarded operators we must add one level of q/Q around everything */ + if (min_op_q == min_q) + { + *prepend += 1; + *append += 1; + } +}
