summaryrefslogtreecommitdiff
path: root/source/pdf/pdf-appearance.c
diff options
context:
space:
mode:
authorTor Andersson <tor.andersson@artifex.com>2018-03-15 22:42:15 +0100
committerTor Andersson <tor.andersson@artifex.com>2018-06-22 17:05:04 +0200
commit8fc19912a4898ca6993a2acf92116e621d140f7a (patch)
tree5f85f35767de47bcd7b7a3e32ce4fb867f7735db /source/pdf/pdf-appearance.c
parent7d2407571d8c8851b67af5ddd0429467c7048b16 (diff)
downloadmupdf-8fc19912a4898ca6993a2acf92116e621d140f7a.tar.xz
Create appearance streams for annotations.
Now handles more annotation types, and does not use the pdf-write device. Handles many of the usual annotation properties, such as border width, color, interior color, line ending styles. * Ink * Highlight, Underline, Strike-Out, Squiggly * Line (with arrow-heads) * Polygon * PolyLine * Square * Circle * Caret * Text (needs better icons) * FileAttachment (needs better icons) * Sound (needs better icons) * Stamp * FreeText Partially complete: * Widget (treats everything like a plain text field) Not done, but on the to-do list: * /BS style (solid/dashed/bevel/inset/underline) * /BS dash pattern Not done and not on the list: * Movie * Screen * Printer's Mark * Trap Network * Watermark * /Border corner radii (ignored by acrobat) * /BE cloudy border effect
Diffstat (limited to 'source/pdf/pdf-appearance.c')
-rw-r--r--source/pdf/pdf-appearance.c3112
1 files changed, 821 insertions, 2291 deletions
diff --git a/source/pdf/pdf-appearance.c b/source/pdf/pdf-appearance.c
index 2bb025aa..c6c05055 100644
--- a/source/pdf/pdf-appearance.c
+++ b/source/pdf/pdf-appearance.c
@@ -4,2532 +4,1056 @@
#include <float.h>
#include <limits.h>
#include <math.h>
+#include <string.h>
-#include <ft2build.h>
-#include FT_FREETYPE_H
-#include FT_ADVANCES_H
+#include <stdio.h>
-#define MATRIX_COEFS (6)
-#define STRIKE_HEIGHT (0.375f)
-#define UNDERLINE_HEIGHT (0.075f)
-#define LINE_THICKNESS (0.07f)
-#define SMALL_FLOAT (0.00001f)
-#define LIST_SEL_COLOR_R (0.6f)
-#define LIST_SEL_COLOR_G (0.75f)
-#define LIST_SEL_COLOR_B (0.85f)
+#include "annotation-icons.h"
-enum
-{
- Q_Left = 0,
- Q_Cent = 1,
- Q_Right = 2
-};
-
-enum
-{
- BS_Solid,
- BS_Dashed,
- BS_Beveled,
- BS_Inset,
- BS_Underline
-};
-
-typedef struct font_info_s
-{
- pdf_da_info da_rec;
- pdf_font_desc *font;
- float lineheight;
-} font_info;
+#define REPLACEMENT 0xB7
+#define CIRCLE_MAGIC 0.551915f
-typedef struct text_widget_info_s
-{
- pdf_obj *dr;
- pdf_obj *col;
- font_info font_rec;
- int q;
- int multiline;
- int comb;
- int max_len;
-} text_widget_info;
-
-static const char *fmt_re = "%g %g %g %g re\n";
-static const char *fmt_f = "f\n";
-static const char *fmt_s = "s\n";
-static const char *fmt_g = "%g g\n";
-static const char *fmt_m = "%g %g m\n";
-static const char *fmt_l = "%g %g l\n";
-static const char *fmt_w = "%g w\n";
-static const char *fmt_Tx_BMC = "/Tx BMC\n";
-static const char *fmt_q = "q\n";
-static const char *fmt_W = "W\n";
-static const char *fmt_n = "n\n";
-static const char *fmt_BT = "BT\n";
-static const char *fmt_Tm = "%g %g %g %g %g %g Tm\n";
-static const char *fmt_Td = "%g %g Td\n";
-static const char *fmt_Tj = " Tj\n";
-static const char *fmt_ET = "ET\n";
-static const char *fmt_Q = "Q\n";
-static const char *fmt_EMC = "EMC\n";
-
-void pdf_da_info_fin(fz_context *ctx, pdf_da_info *di)
+static float pdf_write_border_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf)
{
- fz_free(ctx, di->font_name);
- di->font_name = NULL;
+ float w = pdf_annot_border(ctx, annot);
+ fz_append_printf(ctx, buf, "%g w\n", w);
+ return w;
}
-static void da_check_stack(float *stack, int *top)
+static int pdf_write_stroke_color_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf)
{
- if (*top == 32)
+ float color[4];
+ int n;
+ pdf_annot_color(ctx, annot, &n, color);
+ switch (n)
{
- memmove(stack, stack + 1, 31 * sizeof(stack[0]));
- *top = 31;
+ default: return 0;
+ case 1: fz_append_printf(ctx, buf, "%g G\n", color[0]); break;
+ case 3: fz_append_printf(ctx, buf, "%g %g %g RG\n", color[0], color[1], color[2]); break;
+ case 4: fz_append_printf(ctx, buf, "%g %g %g %g K\n", color[0], color[1], color[2], color[3]); break;
}
+ return 1;
}
-void pdf_parse_da(fz_context *ctx, char *da, pdf_da_info *di)
+static int pdf_write_fill_color_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf)
{
- float stack[32] = { 0.0f };
- int top = 0;
- pdf_token tok;
- char *name = NULL;
- pdf_lexbuf lbuf;
- fz_stream *str = fz_open_memory(ctx, (unsigned char *)da, strlen(da));
-
- pdf_lexbuf_init(ctx, &lbuf, PDF_LEXBUF_SMALL);
-
- fz_var(str);
- fz_var(name);
- fz_try(ctx)
- {
- for (tok = pdf_lex(ctx, str, &lbuf); tok != PDF_TOK_EOF; tok = pdf_lex(ctx, str, &lbuf))
- {
- switch (tok)
- {
- case PDF_TOK_NAME:
- fz_free(ctx, name);
- name = fz_strdup(ctx, lbuf.scratch);
- break;
-
- case PDF_TOK_INT:
- da_check_stack(stack, &top);
- stack[top] = lbuf.i;
- top ++;
- break;
-
- case PDF_TOK_REAL:
- da_check_stack(stack, &top);
- stack[top] = lbuf.f;
- top ++;
- break;
-
- case PDF_TOK_KEYWORD:
- if (!strcmp(lbuf.scratch, "Tf"))
- {
- di->font_size = stack[0];
- fz_free(ctx, di->font_name);
- di->font_name = name;
- name = NULL;
- }
- else if (!strcmp(lbuf.scratch, "rg"))
- {
- di->col[0] = stack[0];
- di->col[1] = stack[1];
- di->col[2] = stack[2];
- di->col_size = 3;
- }
- else if (!strcmp(lbuf.scratch, "g"))
- {
- di->col[0] = stack[0];
- di->col_size = 1;
- }
-
- fz_free(ctx, name);
- name = NULL;
- top = 0;
- break;
-
- default:
- break;
- }
- }
- }
- fz_always(ctx)
- {
- fz_free(ctx, name);
- fz_drop_stream(ctx, str);
- pdf_lexbuf_fin(ctx, &lbuf);
- }
- fz_catch(ctx)
+ float color[4];
+ int n;
+ pdf_annot_color(ctx, annot, &n, color);
+ switch (n)
{
- fz_rethrow(ctx);
+ default: return 0;
+ case 1: fz_append_printf(ctx, buf, "%g g\n", color[0]); break;
+ case 3: fz_append_printf(ctx, buf, "%g %g %g rg\n", color[0], color[1], color[2]); break;
+ case 4: fz_append_printf(ctx, buf, "%g %g %g %g k\n", color[0], color[1], color[2], color[3]); break;
}
+ return 1;
}
-static void get_font_info(fz_context *ctx, pdf_document *doc, pdf_obj *dr, char *da, font_info *font_rec)
+static int pdf_write_interior_fill_color_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf)
{
- pdf_font_desc *font;
- pdf_obj *fontobj;
-
- pdf_parse_da(ctx, da, &font_rec->da_rec);
- if (font_rec->da_rec.font_name == NULL)
- fz_throw(ctx, FZ_ERROR_GENERIC, "No font name in default appearance");
-
- fontobj = pdf_dict_gets(ctx, pdf_dict_get(ctx, dr, PDF_NAME(Font)), font_rec->da_rec.font_name);
- if (!fontobj)
+ float color[4];
+ int n;
+ pdf_annot_interior_color(ctx, annot, &n, color);
+ switch (n)
{
- fz_font *helv = fz_new_base14_font(ctx, "Helvetica");
- fz_warn(ctx, "form resource dictionary is missing the default appearance font");
- fontobj = pdf_add_simple_font(ctx, doc, helv, PDF_SIMPLE_ENCODING_LATIN);
- pdf_dict_puts_drop(ctx, pdf_dict_get(ctx, dr, PDF_NAME(Font)), font_rec->da_rec.font_name, fontobj);
- fz_drop_font(ctx, helv);
+ default: return 0;
+ case 1: fz_append_printf(ctx, buf, "%g g\n", color[0]); break;
+ case 3: fz_append_printf(ctx, buf, "%g %g %g rg\n", color[0], color[1], color[2]); break;
+ case 4: fz_append_printf(ctx, buf, "%g %g %g %g k\n", color[0], color[1], color[2], color[3]); break;
}
- font_rec->font = font = pdf_load_font(ctx, doc, dr, fontobj, 0);
- font_rec->lineheight = 1.0f;
- if (font && font->ascent != 0.0f && font->descent != 0.0f)
- font_rec->lineheight = (font->ascent - font->descent) / 1000.0f;
+ return 1;
}
-static void font_info_fin(fz_context *ctx, font_info *font_rec)
+static fz_point rotate_vector(float angle, float x, float y)
{
- pdf_drop_font(ctx, font_rec->font);
- font_rec->font = NULL;
- pdf_da_info_fin(ctx, &font_rec->da_rec);
+ float ca = cosf(angle);
+ float sa = sinf(angle);
+ return fz_make_point(x*ca - y*sa, x*sa + y*ca);
}
-static void get_text_widget_info(fz_context *ctx, pdf_document *doc, pdf_obj *widget, text_widget_info *info)
+static void pdf_write_arrow_appearance(fz_context *ctx, fz_buffer *buf, fz_rect *rect, float x, float y, float dx, float dy, float w)
{
- char *da = pdf_to_str_buf(ctx, pdf_get_inheritable(ctx, doc, widget, PDF_NAME(DA)));
- int ff = pdf_get_field_flags(ctx, doc, widget);
- pdf_obj *ml = pdf_get_inheritable(ctx, doc, widget, PDF_NAME(MaxLen));
-
- info->dr = pdf_get_inheritable(ctx, doc, widget, PDF_NAME(DR));
- info->col = pdf_dict_getl(ctx, widget, PDF_NAME(MK), PDF_NAME(BG), NULL);
- info->q = pdf_to_int(ctx, pdf_get_inheritable(ctx, doc, widget, PDF_NAME(Q)));
- info->multiline = (ff & Ff_Multiline) != 0;
- info->comb = (ff & (Ff_Multiline|Ff_Password|Ff_FileSelect|Ff_Comb)) == Ff_Comb;
-
- if (ml == NULL)
- info->comb = 0;
- else
- info->max_len = pdf_to_int(ctx, ml);
-
- get_font_info(ctx, doc, info->dr, da, &info->font_rec);
-}
-
-void pdf_fzbuf_print_da(fz_context *ctx, fz_buffer *fzbuf, pdf_da_info *di)
-{
- if (di->font_name != NULL && di->font_size != 0)
- fz_append_printf(ctx, fzbuf, "/%s %d Tf", di->font_name, di->font_size);
-
- switch (di->col_size)
- {
- case 1:
- fz_append_printf(ctx, fzbuf, " %g g", di->col[0]);
- break;
-
- case 3:
- fz_append_printf(ctx, fzbuf, " %g %g %g rg", di->col[0], di->col[1], di->col[2]);
- break;
-
- case 4:
- fz_append_printf(ctx, fzbuf, " %g %g %g %g k", di->col[0], di->col[1], di->col[2], di->col[3]);
- break;
-
- default:
- fz_append_string(ctx, fzbuf, " 0 g");
- break;
- }
+ float r = fz_max(1, w);
+ float angle = atan2f(dy, dx);
+ fz_point v, a, b;
+
+ v = rotate_vector(angle, 8.8f*r, 4.5f*r);
+ a = fz_make_point(x + v.x, y + v.y);
+ v = rotate_vector(angle, 8.8f*r, -4.5f*r);
+ b = fz_make_point(x + v.x, y + v.y);
+
+ fz_include_point_in_rect(rect, &a);
+ fz_include_point_in_rect(rect, &b);
+ fz_expand_rect(rect, w);
+
+ fz_append_printf(ctx, buf, "%g %g m\n", a.x, a.y);
+ fz_append_printf(ctx, buf, "%g %g l\n", x, y);
+ fz_append_printf(ctx, buf, "%g %g l\n", b.x, b.y);
}
-static fz_rect *measure_text(fz_context *ctx, pdf_document *doc, font_info *font_rec, const fz_matrix *tm, char *text, fz_rect *bbox)
+static void include_cap(fz_rect *rect, float x, float y, float r)
{
- pdf_measure_text(ctx, font_rec->font, (unsigned char *)text, strlen(text), bbox);
-
- bbox->x0 *= font_rec->da_rec.font_size * tm->a;
- bbox->y0 *= font_rec->da_rec.font_size * tm->d;
- bbox->x1 *= font_rec->da_rec.font_size * tm->a;
- bbox->y1 *= font_rec->da_rec.font_size * tm->d;
-
- return bbox;
+ rect->x0 = fz_min(rect->x0, x-r);
+ rect->y0 = fz_min(rect->y0, y-r);
+ rect->x1 = fz_max(rect->x1, x+r);
+ rect->y1 = fz_max(rect->y1, y+r);
}
-static void fzbuf_print_color(fz_context *ctx, fz_buffer *fzbuf, pdf_obj *arr, int stroke, float adj)
+static void
+pdf_write_line_cap_appearance(fz_context *ctx, fz_buffer *buf, fz_rect *rect,
+ float x, float y, float dx, float dy, float w, int ic, pdf_obj *cap)
{
- switch (pdf_array_len(ctx, arr))
- {
- case 1:
- fz_append_printf(ctx, fzbuf, stroke?"%g G\n":"%g g\n",
- pdf_to_real(ctx, pdf_array_get(ctx, arr, 0)) + adj);
- break;
- case 3:
- fz_append_printf(ctx, fzbuf, stroke?"%g %g %g RG\n":"%g %g %g rg\n",
- pdf_to_real(ctx, pdf_array_get(ctx, arr, 0)) + adj,
- pdf_to_real(ctx, pdf_array_get(ctx, arr, 1)) + adj,
- pdf_to_real(ctx, pdf_array_get(ctx, arr, 2)) + adj);
- break;
- case 4:
- fz_append_printf(ctx, fzbuf, stroke?"%g %g %g %g K\n":"%g %g %g %g k\n",
- pdf_to_real(ctx, pdf_array_get(ctx, arr, 0)),
- pdf_to_real(ctx, pdf_array_get(ctx, arr, 1)),
- pdf_to_real(ctx, pdf_array_get(ctx, arr, 2)),
- pdf_to_real(ctx, pdf_array_get(ctx, arr, 3)));
- break;
+ if (cap == PDF_NAME(Square))
+ {
+ float r = fz_max(2.5f, w * 2.5f);
+ fz_append_printf(ctx, buf, "%g %g %g %g re\n", x-r, y-r, r*2, r*2);
+ fz_append_string(ctx, buf, ic ? "b\n" : "s\n");
+ include_cap(rect, x, y, r);
+ }
+ else if (cap == PDF_NAME(Circle))
+ {
+ float r = fz_max(2.5f, w * 2.5f);
+ float m = r * CIRCLE_MAGIC;
+ fz_append_printf(ctx, buf, "%g %g m\n", x, y+r);
+ fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x+m, y+r, x+r, y+m, x+r, y);
+ fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x+r, y-m, x+m, y-r, x, y-r);
+ fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x-m, y-r, x-r, y-m, x-r, y);
+ fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x-r, y+m, x-m, y+r, x, y+r);
+ fz_append_string(ctx, buf, ic ? "b\n" : "s\n");
+ include_cap(rect, x, y, r);
+ }
+ else if (cap == PDF_NAME(Diamond))
+ {
+ float r = fz_max(2.5f, w * 2.5f);
+ fz_append_printf(ctx, buf, "%g %g m\n", x, y+r);
+ fz_append_printf(ctx, buf, "%g %g l\n", x+r, y);
+ fz_append_printf(ctx, buf, "%g %g l\n", x, y-r);
+ fz_append_printf(ctx, buf, "%g %g l\n", x-r, y);
+ fz_append_string(ctx, buf, ic ? "b\n" : "s\n");
+ include_cap(rect, x, y, r);
+ }
+ else if (cap == PDF_NAME(OpenArrow))
+ {
+ pdf_write_arrow_appearance(ctx, buf, rect, x, y, dx, dy, w);
+ fz_append_string(ctx, buf, "S\n");
+ }
+ else if (cap == PDF_NAME(ClosedArrow))
+ {
+ pdf_write_arrow_appearance(ctx, buf, rect, x, y, dx, dy, w);
+ fz_append_string(ctx, buf, ic ? "b\n" : "s\n");
+ }
+ /* PDF 1.5 */
+ else if (cap == PDF_NAME(Butt))
+ {
+ float r = fz_max(3, w * 3);
+ fz_point a = { x-dy*r, y+dx*r };
+ fz_point b = { x+dy*r, y-dx*r };
+ fz_append_printf(ctx, buf, "%g %g m\n", a.x, a.y);
+ fz_append_printf(ctx, buf, "%g %g l\n", b.x, b.y);
+ fz_append_string(ctx, buf, "S\n");
+ fz_include_point_in_rect(rect, &a);
+ fz_include_point_in_rect(rect, &b);
+ }
+ /* PDF 1.6 */
+ else if (cap == PDF_NAME(ROpenArrow))
+ {
+ pdf_write_arrow_appearance(ctx, buf, rect, x, y, -dx, -dy, w);
+ fz_append_string(ctx, buf, "S\n");
+ }
+ else if (cap == PDF_NAME(RClosedArrow))
+ {
+ pdf_write_arrow_appearance(ctx, buf, rect, x, y, -dx, -dy, w);
+ fz_append_string(ctx, buf, ic ? "b\n" : "s\n");
+ }
+ else if (cap == PDF_NAME(Slash))
+ {
+ float r = fz_max(5, w * 5);
+ float angle = atan2f(dy, dx) - (30 * FZ_PI / 180);
+ fz_point a, b, v;
+ v = rotate_vector(angle, 0, r);
+ a = fz_make_point(x + v.x, y + v.y);
+ v = rotate_vector(angle, 0, -r);
+ b = fz_make_point(x + v.x, y + v.y);
+ fz_append_printf(ctx, buf, "%g %g m\n", a.x, a.y);
+ fz_append_printf(ctx, buf, "%g %g l\n", b.x, b.y);
+ fz_append_string(ctx, buf, "S\n");
+ fz_include_point_in_rect(rect, &a);
+ fz_include_point_in_rect(rect, &b);
}
}
-static void fzbuf_print_rect_fill(fz_context *ctx, fz_buffer *fzbuf, const fz_rect *clip,
- float col[4], int col_size, const fz_matrix *tm, const fz_rect *rect)
+static void
+pdf_write_line_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect)
{
- if (clip)
- {
- fz_append_printf(ctx, fzbuf, fmt_re, clip->x0, clip->y0, clip->x1 - clip->x0, clip->y1 - clip->y0);
- fz_append_printf(ctx, fzbuf, fmt_W);
- }
- if (col_size > 0)
- {
- switch (col_size)
- {
- case 1:
- fz_append_printf(ctx, fzbuf, "%g g\n", col[0]);
- break;
- case 3:
- fz_append_printf(ctx, fzbuf, "%g %g %g rg\n", col[0], col[1], col[2]);
- break;
- case 4:
- fz_append_printf(ctx, fzbuf, "%g %g %g %g k\n", col[0], col[1], col[2], col[3]);
- break;
- default:
- break;
- }
- }
- if (tm)
- fz_append_printf(ctx, fzbuf, fmt_Tm, tm->a, tm->b, tm->c, tm->d, tm->e, tm->f);
-
- fz_append_printf(ctx, fzbuf, fmt_re, rect->x0, rect->y0, rect->x1 - rect->x0, rect->y1 - rect->y0);
- fz_append_printf(ctx, fzbuf, fmt_f);
+ pdf_obj *line, *le;
+ fz_point a, b;
+ float w;
+ int ic;
+
+ w = pdf_write_border_appearance(ctx, annot, buf);
+ pdf_write_stroke_color_appearance(ctx, annot, buf);
+ ic = pdf_write_interior_fill_color_appearance(ctx, annot, buf);
+
+ line = pdf_dict_get(ctx, annot->obj, PDF_NAME(L));
+ a.x = pdf_array_get_real(ctx, line, 0);
+ a.y = pdf_array_get_real(ctx, line, 1);
+ b.x = pdf_array_get_real(ctx, line, 2);
+ b.y = pdf_array_get_real(ctx, line, 3);
+
+ fz_append_printf(ctx, buf, "%g %g m\n%g %g l\nS\n", a.x, a.y, b.x, b.y);
+
+ rect->x0 = fz_min(a.x, b.x);
+ rect->y0 = fz_min(a.y, b.y);
+ rect->x1 = fz_max(a.x, b.x);
+ rect->y1 = fz_max(a.y, b.y);
+
+ le = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE));
+ if (pdf_array_len(ctx, le) == 2)
+ {
+ float dx = b.x - a.x;
+ float dy = b.y - a.y;
+ float l = sqrtf(dx*dx + dy*dy);
+ pdf_write_line_cap_appearance(ctx, buf, rect, a.x, a.y, dx/l, dy/l, w, ic, pdf_array_get(ctx, le, 0));
+ pdf_write_line_cap_appearance(ctx, buf, rect, b.x, b.y, -dx/l, -dy/l, w, ic, pdf_array_get(ctx, le, 1));
+ }
+ fz_expand_rect(rect, fz_max(1, w));
}
-static void fzbuf_print_text(fz_context *ctx, fz_buffer *fzbuf, const fz_rect *clip, pdf_obj *col, font_info *font_rec, const fz_matrix *tm, char *text)
+static void
+pdf_write_square_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect)
{
- fz_append_printf(ctx, fzbuf, fmt_q);
- if (clip)
- {
- fz_append_printf(ctx, fzbuf, fmt_re, clip->x0, clip->y0, clip->x1 - clip->x0, clip->y1 - clip->y0);
- fz_append_printf(ctx, fzbuf, fmt_W);
- if (col)
- {
- fzbuf_print_color(ctx, fzbuf, col, 0, 0.0f);
- fz_append_printf(ctx, fzbuf, fmt_f);
- }
- else
- {
- fz_append_printf(ctx, fzbuf, fmt_n);
- }
- }
-
- fz_append_printf(ctx, fzbuf, fmt_BT);
+ float x, y, w, h;
+ float lw;
+ int ic;
- pdf_fzbuf_print_da(ctx, fzbuf, &font_rec->da_rec);
+ lw = pdf_write_border_appearance(ctx, annot, buf);
+ pdf_write_stroke_color_appearance(ctx, annot, buf);
+ ic = pdf_write_interior_fill_color_appearance(ctx, annot, buf);
- fz_append_printf(ctx, fzbuf, "\n");
- if (tm)
- fz_append_printf(ctx, fzbuf, fmt_Tm, tm->a, tm->b, tm->c, tm->d, tm->e, tm->f);
+ x = rect->x0 + lw;
+ y = rect->y0 + lw;
+ w = rect->x1 - x - lw;
+ h = rect->y1 - y - lw;
- fz_append_pdf_string(ctx, fzbuf, text);
- fz_append_printf(ctx, fzbuf, fmt_Tj);
- fz_append_printf(ctx, fzbuf, fmt_ET);
- fz_append_printf(ctx, fzbuf, fmt_Q);
+ fz_append_printf(ctx, buf, "%g %g %g %g re\n", x, y, w, h);
+ fz_append_string(ctx, buf, ic ? "b" : "s");
}
-static fz_buffer *create_text_buffer(fz_context *ctx, const fz_rect *clip, text_widget_info *info, const fz_matrix *tm, char *text)
+static void
+pdf_write_circle_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect)
{
- fz_buffer *fzbuf = fz_new_buffer(ctx, 0);
-
- fz_try(ctx)
- {
- fz_append_printf(ctx, fzbuf, fmt_Tx_BMC);
- fzbuf_print_text(ctx, fzbuf, clip, info->col, &info->font_rec, tm, text);
- fz_append_printf(ctx, fzbuf, fmt_EMC);
- }
- fz_catch(ctx)
- {
- fz_drop_buffer(ctx, fzbuf);
- fz_rethrow(ctx);
- }
-
- return fzbuf;
+ float rx, ry, cx, cy, mx, my;
+ float lw;
+ int ic;
+
+ lw = pdf_write_border_appearance(ctx, annot, buf);
+ pdf_write_stroke_color_appearance(ctx, annot, buf);
+ ic = pdf_write_interior_fill_color_appearance(ctx, annot, buf);
+
+ rx = (rect->x1 - rect->x0) / 2 - lw;
+ ry = (rect->y1 - rect->y0) / 2 - lw;
+ cx = rect->x0 + lw + rx;
+ cy = rect->y0 + lw + ry;
+ mx = rx * CIRCLE_MAGIC;
+ my = ry * CIRCLE_MAGIC;
+
+ fz_append_printf(ctx, buf, "%g %g m\n", cx, cy+ry);
+ fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", cx+mx, cy+ry, cx+rx, cy+my, cx+rx, cy);
+ fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", cx+rx, cy-my, cx+mx, cy-ry, cx, cy-ry);
+ fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", cx-mx, cy-ry, cx-rx, cy-my, cx-rx, cy);
+ fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", cx-rx, cy+my, cx-mx, cy+ry, cx, cy+ry);
+ fz_append_string(ctx, buf, ic ? "b" : "s");
}
-static fz_buffer *create_aligned_text_buffer(fz_context *ctx, pdf_document *doc, const fz_rect *clip, text_widget_info *info, const fz_matrix *tm, char *text)
+static void
+pdf_write_polygon_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, int close)
{
- fz_matrix atm = *tm;
-
- if (info->q != Q_Left)
- {
- fz_rect rect;
-
- measure_text(ctx, doc, &info->font_rec, tm, text, &rect);
- atm.e -= info->q == Q_Right ? rect.x1 : (rect.x1 - rect.x0) / 2;
- }
-
- return create_text_buffer(ctx, clip, info, &atm, text);
-}
-
-static void measure_ascent_descent(fz_context *ctx, pdf_document *doc, font_info *finf, char *text, float *ascent, float *descent)
-{
- char *testtext = NULL;
- fz_rect bbox;
- font_info tinf = *finf;
-
- fz_var(testtext);
- fz_try(ctx)
- {
- /* Heuristic: adding "My" to text will in most cases
- * produce a measurement that will encompass all chars */
- testtext = fz_malloc(ctx, strlen(text) + 3);
- strcpy(testtext, "My");
- strcat(testtext, text);
- tinf.da_rec.font_size = 1;
- measure_text(ctx, doc, &tinf, &fz_identity, testtext, &bbox);
- *descent = -bbox.y0;
- *ascent = bbox.y1;
- }
- fz_always(ctx)
- {
- fz_free(ctx, testtext);
- }
- fz_catch(ctx)
- {
- fz_rethrow(ctx);
- }
-}
-
-typedef struct text_splitter_s
-{
- font_info *info;
- float width;
- float height;
- float scale;
- float unscaled_width;
- float fontsize;
- float lineheight;
- const char *text;
- int done;
- float x_orig;
- float y_orig;
- float x;
- float x_end;
- size_t text_start;
- size_t text_end;
- int max_lines;
- int retry;
-} text_splitter;
-
-static void text_splitter_init(text_splitter *splitter, font_info *info, const char *text, float width, float height, int variable)
-{
- float fontsize = info->da_rec.font_size;
-
- memset(splitter, 0, sizeof(*splitter));
- splitter->info = info;
- splitter->text = text;
- splitter->width = width;
- splitter->unscaled_width = width;
- splitter->height = height;
- splitter->fontsize = fontsize;
- splitter->scale = 1.0f;
- splitter->lineheight = fontsize * info->lineheight ;
- /* RJW: The cast in the following line is important, as otherwise
- * under MSVC in the variable = 0 case, splitter->max_lines becomes
- * INT_MIN. */
- splitter->max_lines = variable ? (int)(height/splitter->lineheight) : INT_MAX;
-}
-
-static void text_splitter_start_pass(text_splitter *splitter)
-{
- splitter->text_end = 0;
- splitter->x_orig = 0;
- splitter->y_orig = 0;
-}
-
-static void text_splitter_start_line(text_splitter *splitter)
-{
- splitter->x_end = 0;
-}
-
-static int text_splitter_layout(fz_context *ctx, text_splitter *splitter)
-{
- const char *text;
- float room;
- float stride;
- size_t count;
- size_t len;
- float fontsize = splitter->info->da_rec.font_size;
-
- splitter->x = splitter->x_end;
- splitter->text_start = splitter->text_end;
-
- text = splitter->text + splitter->text_start;
- room = splitter->unscaled_width - splitter->x;
-
- if (strchr("\r\n", text[0]))
- {
- /* Consume return chars and report end of line */
- splitter->text_end += strspn(text, "\r\n");
- splitter->text_start = splitter->text_end;
- splitter->done = (splitter->text[splitter->text_end] == '\0');
- return 0;
- }
- else if (text[0] == ' ')
- {
- /* Treat each space as a word */
- len = 1;
- }
- else
- {
- len = 0;
- while (text[len] != '\0' && !strchr(" \r\n", text[len]))
- len ++;
- }
+ pdf_obj *verts;
+ fz_point p;
+ int i, n;
+ float lw;
- stride = pdf_text_stride(ctx, splitter->info->font, fontsize, (unsigned char *)text, len, room, &count);
+ lw = pdf_write_border_appearance(ctx, annot, buf);
+ pdf_write_stroke_color_appearance(ctx, annot, buf);
- /* If not a single char fits although the line is empty, then force one char */
- if (count == 0 && splitter->x == 0.0f)
- stride = pdf_text_stride(ctx, splitter->info->font, fontsize, (unsigned char *)text, 1, FLT_MAX, &count);
+ *rect = fz_empty_rect;
- if (count < len && splitter->retry)
+ verts = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices));
+ n = pdf_array_len(ctx, verts) / 2;
+ if (n > 0)
{
- /* The word didn't fit and we are in retry mode. Work out the
- * least additional scaling that may help */
- float fitwidth; /* width if we force the word in */
- float hstretchwidth; /* width if we just bump by 10% */
- float vstretchwidth; /* width resulting from forcing in another line */
- float bestwidth;
-
- fitwidth = splitter->x +
- pdf_text_stride(ctx, splitter->info->font, fontsize, (unsigned char *)text, len, FLT_MAX, &count);
- /* FIXME: temporary fiddle factor. Would be better to work in integers */
- fitwidth *= 1.001f;
-
- /* Stretching by 10% is worth trying only if processing the first word on the line */
- hstretchwidth = splitter->x == 0.0f
- ? splitter->width * 1.1f / splitter->scale
- : FLT_MAX;
-
- vstretchwidth = splitter->width * (splitter->max_lines + 1) * splitter->lineheight
- / splitter->height;
-
- bestwidth = fz_min(fitwidth, fz_min(hstretchwidth, vstretchwidth));
-
- if (bestwidth == vstretchwidth)
- splitter->max_lines ++;
-
- splitter->scale = splitter->width / bestwidth;
- splitter->unscaled_width = bestwidth;
-
- splitter->retry = 0;
-
- /* Try again */
- room = splitter->unscaled_width - splitter->x;
- stride = pdf_text_stride(ctx, splitter->info->font, fontsize, (unsigned char *)text, len, room, &count);
+ for (i = 0; i < n; ++i)
+ {
+ p.x = pdf_array_get_real(ctx, verts, i*2+0);
+ p.y = pdf_array_get_real(ctx, verts, i*2+1);
+ if (i == 0)
+ {
+ rect->x0 = rect->x1 = p.x;
+ rect->y0 = rect->y1 = p.y;
+ }
+ else
+ fz_include_point_in_rect(rect, &p);
+ if (i == 0)
+ fz_append_printf(ctx, buf, "%g %g m\n", p.x, p.y);
+ else
+ fz_append_printf(ctx, buf, "%g %g l\n", p.x, p.y);
+ }
+ fz_append_string(ctx, buf, close ? "s" : "S");
+ fz_expand_rect(rect, lw);
}
-
- /* This is not the first word on the line. Best to give up on this line and push
- * the word onto the next */
- if (count < len && splitter->x > 0.0f)
- return 0;
-
- splitter->text_end = splitter->text_start + count;
- splitter->x_end = splitter->x + stride;
- splitter->done = (splitter->text[splitter->text_end] == '\0');
- return 1;
}
-static void text_splitter_move(text_splitter *splitter, float newy, float *relx, float *rely)
+static void
+pdf_write_ink_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect)
{
- *relx = splitter->x - splitter->x_orig;
- *rely = newy * splitter->lineheight - splitter->y_orig;
+ pdf_obj *ink_list, *stroke;
+ int i, n, k, m;
+ float lw;
+ fz_point p;
- splitter->x_orig = splitter->x;
- splitter->y_orig = newy * splitter->lineheight;
-}
+ lw = pdf_write_border_appearance(ctx, annot, buf);
+ pdf_write_stroke_color_appearance(ctx, annot, buf);
-static void text_splitter_retry(text_splitter *splitter)
-{
- if (splitter->retry)
- {
- /* Already tried expanding lines. Overflow must
- * be caused by carriage control */
- splitter->max_lines ++;
- splitter->retry = 0;
- splitter->unscaled_width = splitter->width * splitter->max_lines * splitter->lineheight
- / splitter->height;
- splitter->scale = splitter->width / splitter->unscaled_width;
- }
- else
- {
- splitter->retry = 1;
- }
-}
+ *rect = fz_empty_rect;
-static void fzbuf_print_text_start1(fz_context *ctx, fz_buffer *fzbuf, const fz_rect *clip, pdf_obj *col)
-{
- fz_append_printf(ctx, fzbuf, fmt_Tx_BMC);
- fz_append_printf(ctx, fzbuf, fmt_q);
-
- if (clip)
+ ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList));
+ n = pdf_array_len(ctx, ink_list);
+ for (i = 0; i < n; ++i)
{
- fz_append_printf(ctx, fzbuf, fmt_re, clip->x0, clip->y0, clip->x1 - clip->x0, clip->y1 - clip->y0);
- fz_append_printf(ctx, fzbuf, fmt_W);
- if (col)
- {
- fzbuf_print_color(ctx, fzbuf, col, 0, 0.0f);
- fz_append_printf(ctx, fzbuf, fmt_f);
- }
- else
+ stroke = pdf_array_get(ctx, ink_list, i);
+ m = pdf_array_len(ctx, stroke) / 2;
+ for (k = 0; k < m; ++k)
{
- fz_append_printf(ctx, fzbuf, fmt_n);
+ p.x = pdf_array_get_real(ctx, stroke, k*2+0);
+ p.y = pdf_array_get_real(ctx, stroke, k*2+1);
+ if (i == 0 && k == 0)
+ {
+ rect->x0 = rect->x1 = p.x;
+ rect->y0 = rect->y1 = p.y;
+ }
+ else
+ fz_include_point_in_rect(rect, &p);
+ fz_append_printf(ctx, buf, "%g %g %c\n", p.x, p.y, k == 0 ? 'm' : 'l');
}
}
+ fz_append_printf(ctx, buf, "S");
+ fz_expand_rect(rect, lw);
}
-static void fzbuf_print_text_start2(fz_context *ctx, fz_buffer *fzbuf, font_info *font, const fz_matrix *tm)
+static fz_rect pdf_rect_from_quad_point(fz_context *ctx, pdf_obj *qp, int i)
{
- fz_append_printf(ctx, fzbuf, fmt_BT);
+ fz_rect box;
- pdf_fzbuf_print_da(ctx, fzbuf, &font->da_rec);
- fz_append_printf(ctx, fzbuf, "\n");
+ /* Contrary to the specification, the points within a QuadPoint are NOT
+ * ordered in a counterclockwise fashion starting with the lower left.
+ * Experiments with Adobe's implementation indicates a cross-wise
+ * ordering is intended: ul, ur, ll, lr.
+ */
- fz_append_printf(ctx, fzbuf, fmt_Tm, tm->a, tm->b, tm->c, tm->d, tm->e, tm->f);
-}
+ box.x0 = pdf_array_get_real(ctx, qp, i+4); /* ll */
+ box.y0 = pdf_array_get_real(ctx, qp, i+5);
+ box.x1 = pdf_array_get_real(ctx, qp, i+2); /* ur */
+ box.y1 = pdf_array_get_real(ctx, qp, i+3);
-static void fzbuf_print_text_end(fz_context *ctx, fz_buffer *fzbuf)
-{
- fz_append_printf(ctx, fzbuf, fmt_ET);
- fz_append_printf(ctx, fzbuf, fmt_Q);
- fz_append_printf(ctx, fzbuf, fmt_EMC);
+ return box;
}
-static void fzbuf_print_text_word(fz_context *ctx, fz_buffer *fzbuf, float x, float y, char *text, size_t count)
+static void
+pdf_write_highlight_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res)
{
- size_t i;
+ pdf_obj *res_egs, *res_egs_h;
+ pdf_obj *qp;
+ float opacity;
+ int i, n;
- fz_append_printf(ctx, fzbuf, fmt_Td, x, y);
- fz_append_printf(ctx, fzbuf, "(");
+ *rect = fz_empty_rect;
- for (i = 0; i < count; i++)
- fz_append_printf(ctx, fzbuf, "%c", text[i]);
+ /* /Resources << /ExtGState << /H << /Type/ExtGState /BM/Multiply /CA %g >> >> >> */
+ *res = pdf_new_dict(ctx, annot->page->doc, 1);
+ res_egs = pdf_dict_put_dict(ctx, *res, PDF_NAME(ExtGState), 1);
+ res_egs_h = pdf_dict_put_dict(ctx, res_egs, PDF_NAME(H), 2);
+ pdf_dict_put(ctx, res_egs_h, PDF_NAME(Type), PDF_NAME(ExtGState));
+ pdf_dict_put(ctx, res_egs_h, PDF_NAME(BM), PDF_NAME(Multiply));
+ opacity = pdf_annot_opacity(ctx, annot);
+ if (opacity < 1)
+ pdf_dict_put_real(ctx, res_egs_h, PDF_NAME(ca), opacity);
- fz_append_printf(ctx, fzbuf, ") Tj\n");
-}
-
-static fz_buffer *create_text_appearance(fz_context *ctx, pdf_document *doc, const fz_rect *bbox, const fz_matrix *oldtm, text_widget_info *info, char *text)
-{
- int fontsize;
- int variable;
- float height, width, full_width;
- fz_buffer *fzbuf = NULL;
- fz_buffer *fztmp = NULL;
- fz_rect rect;
- fz_rect tbox;
- rect = *bbox;
-
- if (rect.x1 - rect.x0 > 3.0f && rect.y1 - rect.y0 > 3.0f)
- {
- rect.x0 += 1.0f;
- rect.x1 -= 1.0f;
- rect.y0 += 1.0f;
- rect.y1 -= 1.0f;
- }
+ pdf_write_fill_color_appearance(ctx, annot, buf);
- height = rect.y1 - rect.y0;
- width = rect.x1 - rect.x0;
- full_width = bbox->x1 - bbox->x0;
+ fz_append_printf(ctx, buf, "/H gs\n");
- fz_var(fzbuf);
- fz_var(fztmp);
- fz_try(ctx)
+ qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
+ n = pdf_array_len(ctx, qp);
+ if (n > 0)
{
- float ascent, descent;
- fz_matrix tm;
-
- variable = (info->font_rec.da_rec.font_size == 0);
- fontsize = variable
- ? (info->multiline ? 14.0f : height / info->font_rec.lineheight)
- : info->font_rec.da_rec.font_size;
-
- info->font_rec.da_rec.font_size = fontsize;
-
- measure_ascent_descent(ctx, doc, &info->font_rec, text, &ascent, &descent);
-
- if (info->multiline)
+ for (i = 0; i < n; i += 8)
{
- text_splitter splitter;
-
- text_splitter_init(&splitter, &info->font_rec, text, width, height, variable);
-
- while (!splitter.done)
- {
- /* Try a layout pass */
- int line = 0;
-
- fz_drop_buffer(ctx, fztmp);
- fztmp = NULL;
- fztmp = fz_new_buffer(ctx, 0);
-
- text_splitter_start_pass(&splitter);
-
- /* Layout unscaled text to a scaled-up width, so that
- * the scaled-down text will fit the unscaled width */
-
- while (!splitter.done && line < splitter.max_lines)
- {
- /* Layout a line */
- text_splitter_start_line(&splitter);
-
- while (!splitter.done && text_splitter_layout(ctx, &splitter))
- {
- if (splitter.text[splitter.text_start] != ' ')
- {
- float x, y;
- char *word = text+splitter.text_start;
- size_t wordlen = splitter.text_end-splitter.text_start;
-
- text_splitter_move(&splitter, -line, &x, &y);
- fzbuf_print_text_word(ctx, fztmp, x, y, word, wordlen);
- }
- }
-
- line ++;
- }
-
- if (!splitter.done)
- text_splitter_retry(&splitter);
- }
-
- fzbuf = fz_new_buffer(ctx, 0);
-
- tm.a = splitter.scale;
- tm.b = 0.0f;
- tm.c = 0.0f;
- tm.d = splitter.scale;
- tm.e = rect.x0;
- tm.f = rect.y1 - (1.0f+ascent-descent)*fontsize*splitter.scale/2.0f;
-
- fzbuf_print_text_start1(ctx, fzbuf, &rect, info->col);
- fzbuf_print_text_start2(ctx, fzbuf, &info->font_rec, &tm);
-
- fz_append_buffer(ctx, fzbuf, fztmp);
-
- fzbuf_print_text_end(ctx, fzbuf);
+ fz_rect box = pdf_rect_from_quad_point(ctx, qp, i);
+ float h = box.y1 - box.y0;
+ float m = h / 4.2425f; /* magic number that matches adobe's appearance */
+ fz_append_printf(ctx, buf, "%g %g m\n", box.x0, box.y0);
+ fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n",
+ box.x0-m, box.y0+m, box.x0-m, box.y1-m, box.x0, box.y1);
+ fz_append_printf(ctx, buf, "%g %g l\n", box.x1, box.y1);
+ fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n",
+ box.x1+m, box.y1-m, box.x1+m, box.y0+m, box.x1, box.y0);
+ fz_append_printf(ctx, buf, "f\n");
+ box.x0 -= m;
+ box.x1 += m;
+ fz_union_rect(rect, &box);
}
- else if (info->comb)
- {
- int i, n = fz_mini((int)strlen(text), info->max_len);
- float comb_width = full_width/info->max_len;
- float char_width = pdf_text_stride(ctx, info->font_rec.font, fontsize, (unsigned char *)"M", 1, FLT_MAX, NULL);
- float init_skip = (comb_width - char_width)/2.0f;
-
- fz_translate(&tm, rect.x0, rect.y1 - (height+(ascent-descent)*fontsize)/2.0f);
-
- fzbuf = fz_new_buffer(ctx, 0);
-
- fzbuf_print_text_start1(ctx, fzbuf, &rect, info->col);
- fzbuf_print_text_start2(ctx, fzbuf, &info->font_rec, &tm);
-
- for (i = 0; i < n; i++)
- fzbuf_print_text_word(ctx, fzbuf, i == 0 ? init_skip : comb_width, 0.0f, text+i, 1);
-
- fzbuf_print_text_end(ctx, fzbuf);
- }
- else
- {
- if (oldtm)
- {
- tm = *oldtm;
- }
- else
- {
- fz_translate(&tm, rect.x0, rect.y1 - (height+(ascent-descent)*fontsize)/2.0f);
-
- switch (info->q)
- {
- case Q_Right: tm.e += width; break;
- case Q_Cent: tm.e += width/2; break;
- }
- }
-
- if (variable)
- {
- measure_text(ctx, doc, &info->font_rec, &tm, text, &tbox);
-
- if (tbox.x1 - tbox.x0 > width)
- {
- /* Scale the text to fit but use the same offset
- * to keep the baseline constant */
- tm.a *= width / (tbox.x1 - tbox.x0);
- tm.d *= width / (tbox.x1 - tbox.x0);
- }
- }
-
- fzbuf = create_aligned_text_buffer(ctx, doc, &rect, info, &tm, text);
- }
- }
- fz_always(ctx)
- {
- fz_drop_buffer(ctx, fztmp);
}
- fz_catch(ctx)
- {
- fz_drop_buffer(ctx, fzbuf);
- fz_rethrow(ctx);
- }
-
- return fzbuf;
}
-static int get_matrix(fz_context *ctx, pdf_document *doc, pdf_obj *form, int q, fz_matrix *mt)
+static void
+pdf_write_underline_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect)
{
- int found = 0;
- pdf_lexbuf lbuf;
- fz_stream *str;
+ pdf_obj *qp;
+ int i, n;
- str = pdf_open_stream(ctx, form);
+ *rect = fz_empty_rect;
- pdf_lexbuf_init(ctx, &lbuf, PDF_LEXBUF_SMALL);
+ pdf_write_stroke_color_appearance(ctx, annot, buf);
- fz_try(ctx)
+ qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
+ n = pdf_array_len(ctx, qp);
+ if (n > 0)
{
- int tok;
- float coefs[MATRIX_COEFS];
- int coef_i = 0;
-
- /* Look for the text matrix Tm in the stream */
- for (tok = pdf_lex(ctx, str, &lbuf); tok != PDF_TOK_EOF; tok = pdf_lex(ctx, str, &lbuf))
- {
- if (tok == PDF_TOK_INT || tok == PDF_TOK_REAL)
- {
- if (coef_i >= MATRIX_COEFS)
- {
- int i;
- for (i = 0; i < MATRIX_COEFS-1; i++)
- coefs[i] = coefs[i+1];
-
- coef_i = MATRIX_COEFS-1;
- }
-
- coefs[coef_i++] = tok == PDF_TOK_INT ? lbuf.i : lbuf.f;
- }
- else
- {
- if (tok == PDF_TOK_KEYWORD && !strcmp(lbuf.scratch, "Tm") && coef_i == MATRIX_COEFS)
- {
- found = 1;
- mt->a = coefs[0];
- mt->b = coefs[1];
- mt->c = coefs[2];
- mt->d = coefs[3];
- mt->e = coefs[4];
- mt->f = coefs[5];
- }
-
- coef_i = 0;
- }
- }
-
- if (found)
+ for (i = 0; i < n; i += 8)
{
- fz_rect bbox;
- pdf_to_rect(ctx, pdf_dict_get(ctx, form, PDF_NAME(BBox)), &bbox);
-
- switch (q)
- {
- case Q_Left:
- mt->e = bbox.x0 + 1;
- break;
+ fz_rect box = pdf_rect_from_quad_point(ctx, qp, i);
+ float h = box.y1 - box.y0;
- case Q_Cent:
- mt->e = (bbox.x1 - bbox.x0) / 2;
- break;
+ /* Acrobat draws the line at 1/7 of the box width from the bottom
+ * of the box and 1/16 thick of the box width. */
+ fz_append_printf(ctx, buf, "%g w\n", h/16);
+ fz_append_printf(ctx, buf, "%g %g m\n", box.x0, box.y0 + h/7);
+ fz_append_printf(ctx, buf, "%g %g l\n", box.x1, box.y0 + h/7);
+ fz_append_printf(ctx, buf, "S\n");
- case Q_Right:
- mt->e = bbox.x1 - 1;
- break;
- }
+ fz_union_rect(rect, &box);
}
}
- fz_always(ctx)
- {
- fz_drop_stream(ctx, str);
- pdf_lexbuf_fin(ctx, &lbuf);
- }
- fz_catch(ctx)
- {
- fz_rethrow(ctx);
- }
-
- return found;
}
-static char *to_font_encoding(fz_context *ctx, pdf_font_desc *font, char *utf8)
+static void
+pdf_write_strike_out_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect)
{
- size_t i;
- int needs_converting = 0;
-
- /* Temporary partial solution. We are using a slow lookup in the conversion
- * below, so we avoid performing the conversion unnecessarily. We check for
- * top-bit-set chars, and convert only if they are present. We should also
- * check that the font encoding is one that agrees with utf8 from 0 to 7f,
- * but for now we get away without doing so. This is after all an improvement
- * on just strdup */
- for (i = 0; utf8[i] != '\0'; i++)
- {
- if (utf8[i] & 0x80)
- needs_converting = 1;
- }
+ pdf_obj *qp;
+ int i, n;
- /* Even if we need to convert, we cannot do so if the font has no cid_to_ucs mapping */
- if (needs_converting && font->cid_to_ucs)
- {
- char *buf = fz_malloc(ctx, strlen(utf8) + 1);
- char *bufp = buf;
+ pdf_write_stroke_color_appearance(ctx, annot, buf);
- fz_try(ctx)
+ qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
+ n = pdf_array_len(ctx, qp);
+ if (n > 0)
+ {
+ *rect = fz_empty_rect;
+ for (i = 0; i < n; i += 8)
{
- while(*utf8)
- {
- if (*utf8 & 0x80)
- {
- int rune;
-
- utf8 += fz_chartorune(&rune, utf8);
-
- /* Slow search for the cid that maps to the unicode value held in 'rune" */
- for (i = 0; i < font->cid_to_ucs_len && font->cid_to_ucs[i] != rune; i++)
- ;
+ fz_rect box = pdf_rect_from_quad_point(ctx, qp, i);
+ float h = box.y1 - box.y0;
- /* If found store the cid */
- if (i < font->cid_to_ucs_len)
- *bufp++ = (char)i;
- }
- else
- {
- *bufp++ = *utf8++;
- }
- }
+ /* Acrobat draws the line at 3/7 of the box width from the bottom
+ * of the box and 1/16 thick of the box width. */
+ fz_append_printf(ctx, buf, "%g w\n", h/16);
+ fz_append_printf(ctx, buf, "%g %g m\n", box.x0, box.y0 + 3*h/7);
+ fz_append_printf(ctx, buf, "%g %g l\n", box.x1, box.y0 + 3*h/7);
+ fz_append_printf(ctx, buf, "S\n");
- *bufp = '\0';
+ fz_union_rect(rect, &box);
}
- fz_catch(ctx)
- {
- fz_free(ctx, buf);
- fz_rethrow(ctx);
- }
-
- return buf;
- }
- else
- {
- /* If either no conversion is needed or the font has no cid_to_ucs
- * mapping then leave unconverted, although in the latter case the result
- * is likely incorrect */
- return fz_strdup(ctx, utf8);
}
}
-static void account_for_rot(fz_rect *rect, fz_matrix *mat, int rot)
+static void
+pdf_write_squiggly_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect)
{
- float width = rect->x1;
- float height = rect->y1;
+ pdf_obj *qp;
+ int i, n;
- switch (rot)
- {
- default:
- *mat = fz_identity;
- break;
- case 90:
- fz_pre_rotate(fz_translate(mat, width, 0), rot);
- rect->x1 = height;
- rect->y1 = width;
- break;
- case 180:
- fz_pre_rotate(fz_translate(mat, width, height), rot);
- break;
- case 270:
- fz_pre_rotate(fz_translate(mat, 0, height), rot);
- rect->x1 = height;
- rect->y1 = width;
- break;
- }
-}
+ *rect = fz_empty_rect;
-static void copy_resources(fz_context *ctx, pdf_obj *dst, pdf_obj *src)
-{
- int i, len;
+ pdf_write_stroke_color_appearance(ctx, annot, buf);
- len = pdf_dict_len(ctx, src);
- for (i = 0; i < len; i++)
+ qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
+ n = pdf_array_len(ctx, qp);
+ if (n > 0)
{
- pdf_obj *key = pdf_dict_get_key(ctx, src, i);
-
- if (!pdf_dict_get(ctx, dst, key))
- pdf_dict_put(ctx, dst, key, pdf_dict_get_val(ctx, src, i));
- }
-}
-
-static pdf_obj *load_or_create_form(fz_context *ctx, pdf_document *doc, pdf_obj *obj, fz_rect *rect)
-{
- pdf_obj *ap = NULL;
- fz_matrix mat;
- int rot;
- pdf_obj *formobj = NULL;
- fz_buffer *fzbuf = NULL;
- int create_form = 0;
-
- fz_var(formobj);
- fz_var(fzbuf);
- fz_try(ctx)
- {
- rot = pdf_to_int(ctx, pdf_dict_getl(ctx, obj, PDF_NAME(MK), PDF_NAME(R), NULL));
- pdf_to_rect(ctx, pdf_dict_get(ctx, obj, PDF_NAME(Rect)), rect);
- rect->x1 -= rect->x0;
- rect->y1 -= rect->y0;
- rect->x0 = rect->y0 = 0;
- account_for_rot(rect, &mat, rot);
-
- ap = pdf_dict_get(ctx, obj, PDF_NAME(AP));
- if (ap == NULL)
- {
- ap = pdf_new_dict(ctx, doc, 1);
- pdf_dict_put_drop(ctx, obj, PDF_NAME(AP), ap);
- }
-
- formobj = pdf_dict_get(ctx, ap, PDF_NAME(N));
- if (formobj == NULL)
+ for (i = 0; i < n; i += 8)
{
- formobj = pdf_new_xobject(ctx, doc, rect, &mat, NULL, NULL);
- pdf_dict_put_drop(ctx, ap, PDF_NAME(N), formobj);
- create_form = 1;
- }
+ fz_rect box = pdf_rect_from_quad_point(ctx, qp, i);
+ float h = box.y1 - box.y0;
+ float x = box.x0;
+ int up = 1;
+
+ fz_append_printf(ctx, buf, "%g w\n", h/16);
+ fz_append_printf(ctx, buf, "%g %g m\n", box.x0, box.y0);
+ while (x < box.x1)
+ {
+ x += h/7;
+ fz_append_printf(ctx, buf, "%g %g l\n", x, box.y0 + (up?h/7:0));
+ up = !up;
+ }
+ fz_append_printf(ctx, buf, "S\n");
- if (create_form)
- {
- fzbuf = fz_new_buffer(ctx, 1);
- pdf_update_stream(ctx, doc, formobj, fzbuf, 0);
+ box.x1 = x;
+ fz_expand_rect(&box, h/16);
+ fz_union_rect(rect, &box);
}
-
- copy_resources(ctx, pdf_xobject_resources(ctx, formobj), pdf_get_inheritable(ctx, doc, obj, PDF_NAME(DR)));
- }
- fz_always(ctx)
- {
- fz_drop_buffer(ctx, fzbuf);
- }
- fz_catch(ctx)
- {
- fz_rethrow(ctx);
}
-
- return pdf_keep_obj(ctx, formobj);
}
-static void update_marked_content(fz_context *ctx, pdf_document *doc, pdf_obj *form, fz_buffer *fzbuf)
+static void
+pdf_write_caret_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, fz_rect *bbox)
{
- pdf_token tok;
- pdf_lexbuf lbuf;
- fz_stream *str_outer = NULL;
- fz_stream *str_inner = NULL;
- unsigned char *buf;
- size_t len;
- fz_buffer *newbuf = NULL;
-
- pdf_lexbuf_init(ctx, &lbuf, PDF_LEXBUF_SMALL);
-
- fz_var(str_outer);
- fz_var(str_inner);
- fz_var(newbuf);
- fz_try(ctx)
- {
- int bmc_found;
- int first = 1;
+ float xc = (rect->x0 + rect->x1) / 2;
+ float yc = (rect->y0 + rect->y1) / 2;
- newbuf = fz_new_buffer(ctx, 0);
- str_outer = pdf_open_stream(ctx, form);
- len = fz_buffer_storage(ctx, fzbuf, &buf);
- str_inner = fz_open_memory(ctx, buf, len);
+ pdf_write_fill_color_appearance(ctx, annot, buf);
- /* Copy the existing appearance stream to newbuf while looking for BMC */
- for (tok = pdf_lex(ctx, str_outer, &lbuf); tok != PDF_TOK_EOF; tok = pdf_lex(ctx, str_outer, &lbuf))
- {
- if (first)
- first = 0;
- else
- fz_append_printf(ctx, newbuf, " ");
+ fz_append_string(ctx, buf, "0 0 m\n");
+ fz_append_string(ctx, buf, "10 0 10 7 10 14 c\n");
+ fz_append_string(ctx, buf, "10 7 10 0 20 0 c\n");
+ fz_append_string(ctx, buf, "f");
- pdf_append_token(ctx, newbuf, tok, &lbuf);
- if (tok == PDF_TOK_KEYWORD && !strcmp(lbuf.scratch, "BMC"))
- break;
- }
-
- bmc_found = (tok != PDF_TOK_EOF);
-
- if (bmc_found)
- {
- /* Drop Tx BMC from the replacement appearance stream */
- (void)pdf_lex(ctx, str_inner, &lbuf);
- (void)pdf_lex(ctx, str_inner, &lbuf);
- }
-
- /* Copy the replacement appearance stream to newbuf */
- for (tok = pdf_lex(ctx, str_inner, &lbuf); tok != PDF_TOK_EOF; tok = pdf_lex(ctx, str_inner, &lbuf))
- {
- fz_append_printf(ctx, newbuf, " ");
- pdf_append_token(ctx, newbuf, tok, &lbuf);
- }
-
- if (bmc_found)
- {
- /* Drop the rest of the existing appearance stream until EMC found */
- for (tok = pdf_lex(ctx, str_outer, &lbuf); tok != PDF_TOK_EOF; tok = pdf_lex(ctx, str_outer, &lbuf))
- {
- if (tok == PDF_TOK_KEYWORD && !strcmp(lbuf.scratch, "EMC"))
- break;
- }
-
- /* Copy the rest of the existing appearance stream to newbuf */
- for (tok = pdf_lex(ctx, str_outer, &lbuf); tok != PDF_TOK_EOF; tok = pdf_lex(ctx, str_outer, &lbuf))
- {
- fz_append_printf(ctx, newbuf, " ");
- pdf_append_token(ctx, newbuf, tok, &lbuf);
- }
- }
-
- /* Use newbuf in place of the existing appearance stream */
- pdf_update_stream(ctx, doc, form, newbuf, 0);
- }
- fz_always(ctx)
- {
- fz_drop_stream(ctx, str_outer);
- fz_drop_stream(ctx, str_inner);
- fz_drop_buffer(ctx, newbuf);
- pdf_lexbuf_fin(ctx, &lbuf);
- }
- fz_catch(ctx)
- {
- fz_rethrow(ctx);
- }
+ *rect = fz_make_rect(xc - 10, yc - 7, xc + 10, yc + 7);
+ *bbox = fz_make_rect(0, 0, 20, 14);
}
-static int get_border_style(fz_context *ctx, pdf_obj *obj)
+static void
+pdf_write_icon_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, fz_rect *bbox)
{
- pdf_obj *sname = pdf_dict_getl(ctx, obj, PDF_NAME(BS), PDF_NAME(S), NULL);
-
- if (pdf_name_eq(ctx, PDF_NAME(D), sname))
- return BS_Dashed;
- else if (pdf_name_eq(ctx, PDF_NAME(B), sname))
- return BS_Beveled;
- else if (pdf_name_eq(ctx, PDF_NAME(I), sname))
- return BS_Inset;
- else if (pdf_name_eq(ctx, PDF_NAME(U), sname))
- return BS_Underline;
+ const char *name;
+ float xc = (rect->x0 + rect->x1) / 2;
+ float yc = (rect->y0 + rect->y1) / 2;
+
+ if (!pdf_write_fill_color_appearance(ctx, annot, buf))
+ fz_append_string(ctx, buf, "1 g\n");
+
+ fz_append_string(ctx, buf, "1 w\n0.5 0.5 15 15 re b\n");
+ fz_append_string(ctx, buf, "0 g\n1 0 0 -1 4 12 cm\n");
+
+ name = pdf_annot_icon_name(ctx, annot);
+
+ /* Text names */
+ if (!strcmp(name, "Comment"))
+ fz_append_string(ctx, buf, icon_comment);
+ else if (!strcmp(name, "Key"))
+ fz_append_string(ctx, buf, icon_key);
+ else if (!strcmp(name, "Note"))
+ fz_append_string(ctx, buf, icon_note);
+ else if (!strcmp(name, "Help"))
+ fz_append_string(ctx, buf, icon_help);
+ else if (!strcmp(name, "NewParagraph"))
+ fz_append_string(ctx, buf, icon_new_paragraph);
+ else if (!strcmp(name, "Paragraph"))
+ fz_append_string(ctx, buf, icon_paragraph);
+ else if (!strcmp(name, "Insert"))
+ fz_append_string(ctx, buf, icon_insert);
+
+ /* FileAttachment names */
+ else if (!strcmp(name, "Graph"))
+ fz_append_string(ctx, buf, icon_graph);
+ else if (!strcmp(name, "PushPin"))
+ fz_append_string(ctx, buf, icon_push_pin);
+ else if (!strcmp(name, "Paperclip"))
+ fz_append_string(ctx, buf, icon_paperclip);
+ else if (!strcmp(name, "Tag"))
+ fz_append_string(ctx, buf, icon_tag);
+
+ /* Sound names */
+ else if (!strcmp(name, "Speaker"))
+ fz_append_string(ctx, buf, icon_speaker);
+ else if (!strcmp(name, "Mic"))
+ fz_append_string(ctx, buf, icon_mic);
+
+ /* Unknown */
else
- return BS_Solid;
-}
+ fz_append_string(ctx, buf, icon_star);
-static float get_border_width(fz_context *ctx, pdf_obj *obj)
-{
- float w = pdf_to_real(ctx, pdf_dict_getl(ctx, obj, PDF_NAME(BS), PDF_NAME(W), NULL));
- return w == 0.0f ? 1.0f : w;
+ *rect = fz_make_rect(xc - 9, yc - 9, xc + 9, yc + 9);
+ *bbox = fz_make_rect(0, 0, 16, 16);
}
-void pdf_update_text_appearance(fz_context *ctx, pdf_document *doc, pdf_obj *obj, char *eventValue)
+static float
+measure_stamp_string(fz_context *ctx, fz_font *font, const char *text)
{
- text_widget_info info;
- fz_buffer *fzbuf = NULL;
- pdf_obj *form = NULL;
- fz_matrix tm;
- fz_rect rect, form_bbox;
- int has_tm;
- char *text = NULL;
-
- memset(&info, 0, sizeof(info));
-
- fz_var(info);
- fz_var(fzbuf);
- fz_var(form);
- fz_var(text);
- fz_try(ctx)
- {
- get_text_widget_info(ctx, doc, obj, &info);
-
- if (eventValue)
- text = to_font_encoding(ctx, info.font_rec.font, eventValue);
- else
- text = pdf_field_value(ctx, doc, obj);
-
- form = load_or_create_form(ctx, doc, obj, &rect);
-
- pdf_xobject_bbox(ctx, form, &form_bbox);
- has_tm = get_matrix(ctx, doc, form, info.q, &tm);
- fzbuf = create_text_appearance(ctx, doc, &form_bbox, has_tm ? &tm : NULL, &info, text?text:"");
- update_marked_content(ctx, doc, form, fzbuf);
- }
- fz_always(ctx)
- {
- pdf_drop_obj(ctx, form);
- fz_free(ctx, text);
- fz_drop_buffer(ctx, fzbuf);
- font_info_fin(ctx, &info.font_rec);
- }
- fz_catch(ctx)
+ float w = 0;
+ while (*text)
{
- fz_warn(ctx, "update_text_appearance failed");
+ int c, g;
+ text += fz_chartorune(&c, text);
+ /* WinAnsi is close enough to Latin-1 to not matter. Use middle dot for unencodable characters. */
+ if (c >= 256) c = REPLACEMENT;
+ g = fz_encode_character(ctx, font, c);
+ w += fz_advance_glyph(ctx, font, g, 0);
}
+ return w;
}
-void pdf_update_listbox_appearance(fz_context *ctx, pdf_document *doc, pdf_obj *obj)
+static void
+write_simple_string(fz_context *ctx, fz_buffer *buf, const char *a, const char *b)
{
- text_widget_info info;
- pdf_obj *form = NULL;
- fz_buffer *fzbuf = NULL;
- fz_matrix tm;
- fz_rect clip_rect;
- fz_rect fill_rect;
- pdf_obj *valarr;
- pdf_obj *optarr;
- char *text;
- int n, m, i, j;
- char **opts = NULL;
- char **vals = NULL;
- int *sel_indices = NULL;
- char **pos;
- int index = -1;
- float height, width;
- float ascent, descent, lineheight;
- int variable;
- int fontsize;
- float items_height;
- fz_rect bbox;
- int num_sel = 0;
- int found_count;
- int val_opt_ok = 1;
- float color[4];
-
- memset(&info, 0, sizeof(info));
-
- fz_var(info);
- fz_var(form);
- fz_var(fzbuf);
- fz_var(opts);
- fz_var(vals);
- fz_var(sel_indices);
- fz_try(ctx)
- {
- optarr = pdf_dict_get(ctx, obj, PDF_NAME(Opt));
- n = pdf_array_len(ctx, optarr);
- opts = (char **)fz_malloc(ctx, n * sizeof(*opts));
- vals = (char **)fz_malloc(ctx, n * sizeof(*vals));
- sel_indices = (int*)fz_malloc(ctx, n * sizeof(int));
- for (i = 0; i < n; i++)
- {
- m = pdf_array_len(ctx, pdf_array_get(ctx, optarr, i));
- if (m == 2)
- {
- vals[i] = pdf_to_str_buf(ctx, pdf_array_get(ctx, pdf_array_get(ctx, optarr, i), 0));
- opts[i] = pdf_to_str_buf(ctx, pdf_array_get(ctx, pdf_array_get(ctx, optarr, i), 1));
- }
- else
- {
- opts[i] = pdf_to_str_buf(ctx, pdf_array_get(ctx, optarr, i));
- val_opt_ok = 0;
- }
- }
-
- /* If ANY of the entries are not an array then just use the opts not the vals. */
- if (val_opt_ok)
- pos = vals;
- else
- pos = opts;
-
- get_text_widget_info(ctx, doc, obj, &info);
- form = load_or_create_form(ctx, doc, obj, &clip_rect);
-
- /* See which ones are selected */
- valarr = pdf_get_inheritable(ctx, doc, obj, PDF_NAME(V));
- if (pdf_is_array(ctx, valarr))
- {
- num_sel = pdf_array_len(ctx, valarr);
- /* Multiple selected */
- found_count = 0;
- for (i = 0; i < num_sel; i++)
- {
- text = pdf_to_str_buf(ctx, pdf_array_get(ctx, valarr, i));
- /* See if we can find it. */
- index = -1;
- for (j = 0; j < n; j++)
- {
- if (!strcmp(text, pos[j]))
- {
- index = j;
- break;
- }
- }
- if (index > -1)
- {
- sel_indices[found_count] = index;
- found_count += 1;
- }
- }
- num_sel = found_count;
- }
- else
- {
- /* One or none selected */
- text = pdf_to_str_buf(ctx, valarr);
- num_sel = 0;
- if (text)
- {
- index = -1;
- for (i = 0; i < n; i++)
- {
- if (!strcmp(text, pos[i]))
- {
- index = i;
- break;
- }
- }
- if (index > -1)
- {
- num_sel = 1;
- sel_indices[0] = index;
- }
- }
- }
-
- if (clip_rect.x1 - clip_rect.x0 > 3.0f && clip_rect.y1 - clip_rect.y0 > 3.0f)
- {
- clip_rect.x0 += 1.0f;
- clip_rect.x1 -= 1.0f;
- clip_rect.y0 += 1.0f;
- clip_rect.y1 -= 1.0f;
- }
- height = clip_rect.y1 - clip_rect.y0;
- width = clip_rect.x1 - clip_rect.x0;
- variable = (info.font_rec.da_rec.font_size == 0);
- fontsize = variable
- ? (info.multiline ? 14.0f : height / info.font_rec.lineheight)
- : info.font_rec.da_rec.font_size;
-
- /* Get the ascent, descent across the items list. */
- ascent = 0;
- descent = 0;
- info.font_rec.da_rec.font_size = 1;
- for (i = 0; i < n; i++)
- {
- measure_text(ctx, doc, &(info.font_rec), &fz_identity, opts[i], &bbox);
- descent = fz_min(-bbox.y0, descent);
- ascent = fz_max(bbox.y1, ascent);
- }
- info.font_rec.da_rec.font_size = fontsize;
- lineheight = ascent - descent;
-
- /* Check if all items will fit. If not, then place the "selected" item at the top of our widget rect. */
- items_height = n * fontsize * lineheight;
- if (items_height <= height || !num_sel)
- fz_translate(&tm, clip_rect.x0, clip_rect.y1 - lineheight * fontsize);
- else
- fz_translate(&tm, clip_rect.x0, clip_rect.y1 + (sel_indices[0] - 1) * lineheight * fontsize);
-
- fzbuf = fz_new_buffer(ctx, 0);
- /* Start the marked content */
- fzbuf_print_text_start1(ctx, fzbuf, &clip_rect, NULL);
- /* Add the selection rects */
- if (num_sel > 0)
- {
- color[0] = LIST_SEL_COLOR_R;
- color[1] = LIST_SEL_COLOR_G;
- color[2] = LIST_SEL_COLOR_B;
- fill_rect.x0 = 0.0f;
- fill_rect.x1 = width;
- for (i = 0; i < num_sel; i++)
- {
- fill_rect.y0 = height - fontsize * lineheight * (sel_indices[i] + 1);
- fill_rect.y1 = fill_rect.y0 + lineheight * fontsize;
- fzbuf_print_rect_fill(ctx, fzbuf, NULL, color, 3, NULL, &fill_rect);
- }
- }
- /* Continue with text related details */
- fzbuf_print_text_start2(ctx, fzbuf, &info.font_rec, &tm);
- /* And now finish with the text content */
- for (i = 0; i < n; i++)
- {
- fzbuf_print_text_word(ctx, fzbuf, 0.0f, i == 0 ? 0 : -fontsize *
- lineheight, opts[i], (int)strlen(opts[i]));
- }
- fzbuf_print_text_end(ctx, fzbuf);
- update_marked_content(ctx, doc, form, fzbuf);
- }
- fz_always(ctx)
- {
- fz_free(ctx, opts);
- fz_free(ctx, vals);
- fz_free(ctx, sel_indices);
- pdf_drop_obj(ctx, form);
- fz_drop_buffer(ctx, fzbuf);
- font_info_fin(ctx, &info.font_rec);
- }
- fz_catch(ctx)
- {
- fz_warn(ctx, "update_text_appearance failed");
- }
+ fz_append_byte(ctx, buf, '(');
+ while (a < b)
+ {
+ int c;
+ a += fz_chartorune(&c, a);
+ /* WinAnsi is close enough to Latin-1 to not matter. Use middle dot for unencodable characters. */
+ if (c >= 256) c = REPLACEMENT;
+ if (c == '(' || c == ')' || c == '\\')
+ fz_append_byte(ctx, buf, '\\');
+ fz_append_byte(ctx, buf, c);
+ }
+ fz_append_byte(ctx, buf, ')');
}
-void pdf_update_combobox_appearance(fz_context *ctx, pdf_document *doc, pdf_obj *obj)
+static void
+write_stamp_string(fz_context *ctx, fz_buffer *buf, fz_font *font, const char *text)
{
- text_widget_info info;
- pdf_obj *form = NULL;
- fz_buffer *fzbuf = NULL;
- fz_matrix tm;
- fz_rect rect, form_bbox;
- int has_tm;
- pdf_obj *val;
- char *text;
-
- memset(&info, 0, sizeof(info));
-
- fz_var(info);
- fz_var(form);
- fz_var(fzbuf);
- fz_try(ctx)
- {
- get_text_widget_info(ctx, doc, obj, &info);
-
- val = pdf_get_inheritable(ctx, doc, obj, PDF_NAME(V));
-
- if (pdf_is_array(ctx, val))
- val = pdf_array_get(ctx, val, 0);
-
- text = pdf_to_str_buf(ctx, val);
-
- if (!text)
- text = "";
-
- form = load_or_create_form(ctx, doc, obj, &rect);
+ write_simple_string(ctx, buf, text, text+strlen(text));
+}
- pdf_xobject_bbox(ctx, form, &form_bbox);
- has_tm = get_matrix(ctx, doc, form, info.q, &tm);
- fzbuf = create_text_appearance(ctx, doc, &form_bbox, has_tm ? &tm : NULL, &info,
- text?text:"");
- update_marked_content(ctx, doc, form, fzbuf);
- }
- fz_always(ctx)
- {
- pdf_drop_obj(ctx, form);
- fz_drop_buffer(ctx, fzbuf);
- font_info_fin(ctx, &info.font_rec);
- }
- fz_catch(ctx)
- {
- fz_warn(ctx, "update_text_appearance failed");
- }
+static void
+write_stamp(fz_context *ctx, fz_buffer *buf, fz_font *font, const char *text, float y, float h)
+{
+ float tw = measure_stamp_string(ctx, font, text) * h;
+ fz_append_string(ctx, buf, "BT\n");
+ fz_append_printf(ctx, buf, "/Times %g Tf\n", h);
+ fz_append_printf(ctx, buf, "%g %g Td\n", (190-tw)/2, y);
+ write_stamp_string(ctx, buf, font, text);
+ fz_append_string(ctx, buf, " Tj\n");
+ fz_append_string(ctx, buf, "ET\n");
}
-void pdf_update_pushbutton_appearance(fz_context *ctx, pdf_document *doc, pdf_obj *obj)
+static void
+pdf_write_stamp_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, fz_rect *bbox, pdf_obj **res)
{
- fz_rect rect = fz_empty_rect;
- pdf_obj *form = NULL;
- fz_buffer *fzbuf = NULL;
- pdf_obj *tobj = NULL;
- font_info font_rec;
- int bstyle;
- float bwidth;
- float btotal;
-
- memset(&font_rec, 0, sizeof(font_rec));
-
- fz_var(font_rec);
- fz_var(form);
- fz_var(fzbuf);
+ fz_font *font;
+ pdf_obj *res_font;
+ pdf_obj *name;
+ float w, h, xs, ys;
+ fz_matrix rotate;
+
+ name = pdf_dict_get(ctx, annot->obj, PDF_NAME(Name));
+ if (!name)
+ name = PDF_NAME(Draft);
+
+ h = rect->y1 - rect->y0;
+ w = rect->x1 - rect->x0;
+ xs = w / 190;
+ ys = h / 50;
+
+ font = fz_new_base14_font(ctx, "Times-Bold");
fz_try(ctx)
{
- form = load_or_create_form(ctx, doc, obj, &rect);
- fzbuf = fz_new_buffer(ctx, 0);
- tobj = pdf_dict_getl(ctx, obj, PDF_NAME(MK), PDF_NAME(BG), NULL);
- if (pdf_is_array(ctx, tobj))
+ /* /Resources << /Font << /Times %d 0 R >> >> */
+ *res = pdf_new_dict(ctx, annot->page->doc, 1);
+ res_font = pdf_dict_put_dict(ctx, *res, PDF_NAME(Font), 1);
+ pdf_dict_put_drop(ctx, res_font, PDF_NAME(Times), pdf_add_simple_font(ctx, annot->page->doc, font, 0));
+
+ pdf_write_fill_color_appearance(ctx, annot, buf);
+ pdf_write_stroke_color_appearance(ctx, annot, buf);
+ fz_rotate(&rotate, 0.6f);
+ fz_append_printf(ctx, buf, "%M cm\n", &rotate);
+ fz_append_string(ctx, buf, "2 w\n2 2 186 44 re\nS\n");
+
+ if (name == PDF_NAME(Approved))
+ write_stamp(ctx, buf, font, "APPROVED", 13, 30);
+ else if (name == PDF_NAME(AsIs))
+ write_stamp(ctx, buf, font, "AS IS", 13, 30);
+ else if (name == PDF_NAME(Confidential))
+ write_stamp(ctx, buf, font, "CONFIDENTIAL", 17, 20);
+ else if (name == PDF_NAME(Departmental))
+ write_stamp(ctx, buf, font, "DEPARTMENTAL", 17, 20);
+ else if (name == PDF_NAME(Experimental))
+ write_stamp(ctx, buf, font, "EXPERIMENTAL", 17, 20);
+ else if (name == PDF_NAME(Expired))
+ write_stamp(ctx, buf, font, "EXPIRED", 13, 30);
+ else if (name == PDF_NAME(Final))
+ write_stamp(ctx, buf, font, "FINAL", 13, 30);
+ else if (name == PDF_NAME(ForComment))
+ write_stamp(ctx, buf, font, "FOR COMMENT", 17, 20);
+ else if (name == PDF_NAME(ForPublicRelease))
{
- fzbuf_print_color(ctx, fzbuf, tobj, 0, 0.0f);
- fz_append_printf(ctx, fzbuf, fmt_re,
- rect.x0, rect.y0, rect.x1, rect.y1);
- fz_append_printf(ctx, fzbuf, fmt_f);
+ write_stamp(ctx, buf, font, "FOR PUBLIC", 26, 18);
+ write_stamp(ctx, buf, font, "RELEASE", 8.5f, 18);
}
- bstyle = get_border_style(ctx, obj);
- bwidth = get_border_width(ctx, obj);
- btotal = bwidth;
- if (bstyle == BS_Beveled || bstyle == BS_Inset)
+ else if (name == PDF_NAME(NotApproved))
+ write_stamp(ctx, buf, font, "NOT APPROVED", 17, 20);
+ else if (name == PDF_NAME(NotForPublicRelease))
{
- btotal += bwidth;
-
- if (bstyle == BS_Beveled)
- fz_append_printf(ctx, fzbuf, fmt_g, 1.0f);
- else
- fz_append_printf(ctx, fzbuf, fmt_g, 0.33f);
- fz_append_printf(ctx, fzbuf, fmt_m, bwidth, bwidth);
- fz_append_printf(ctx, fzbuf, fmt_l, bwidth, rect.y1 - bwidth);
- fz_append_printf(ctx, fzbuf, fmt_l, rect.x1 - bwidth, rect.y1 - bwidth);
- fz_append_printf(ctx, fzbuf, fmt_l, rect.x1 - 2 * bwidth, rect.y1 - 2 * bwidth);
- fz_append_printf(ctx, fzbuf, fmt_l, 2 * bwidth, rect.y1 - 2 * bwidth);
- fz_append_printf(ctx, fzbuf, fmt_l, 2 * bwidth, 2 * bwidth);
- fz_append_printf(ctx, fzbuf, fmt_f);
- if (bstyle == BS_Beveled)
- fzbuf_print_color(ctx, fzbuf, tobj, 0, -0.25f);
- else
- fz_append_printf(ctx, fzbuf, fmt_g, 0.66f);
- fz_append_printf(ctx, fzbuf, fmt_m, rect.x1 - bwidth, rect.y1 - bwidth);
- fz_append_printf(ctx, fzbuf, fmt_l, rect.x1 - bwidth, bwidth);
- fz_append_printf(ctx, fzbuf, fmt_l, bwidth, bwidth);
- fz_append_printf(ctx, fzbuf, fmt_l, 2 * bwidth, 2 * bwidth);
- fz_append_printf(ctx, fzbuf, fmt_l, rect.x1 - 2 * bwidth, 2 * bwidth);
- fz_append_printf(ctx, fzbuf, fmt_l, rect.x1 - 2 * bwidth, rect.y1 - 2 * bwidth);
- fz_append_printf(ctx, fzbuf, fmt_f);
- }
-
- tobj = pdf_dict_getl(ctx, obj, PDF_NAME(MK), PDF_NAME(BC), NULL);
- if (tobj)
- {
- fzbuf_print_color(ctx, fzbuf, tobj, 1, 0.0f);
- fz_append_printf(ctx, fzbuf, fmt_w, bwidth);
- fz_append_printf(ctx, fzbuf, fmt_re,
- bwidth/2, bwidth/2,
- rect.x1 -bwidth/2, rect.y1 - bwidth/2);
- fz_append_printf(ctx, fzbuf, fmt_s);
+ write_stamp(ctx, buf, font, "NOT FOR", 26, 18);
+ write_stamp(ctx, buf, font, "PUBLIC RELEASE", 8.5, 18);
}
-
- tobj = pdf_dict_getl(ctx, obj, PDF_NAME(MK), PDF_NAME(CA), NULL);
- if (tobj)
- {
- fz_rect clip = rect;
- fz_rect bounds;
- fz_matrix mat;
- char *da = pdf_to_str_buf(ctx, pdf_get_inheritable(ctx, doc, obj, PDF_NAME(DA)));
- char *text = pdf_to_str_buf(ctx, tobj);
-
- clip.x0 += btotal;
- clip.y0 += btotal;
- clip.x1 -= btotal;
- clip.y1 -= btotal;
-
- get_font_info(ctx, doc, pdf_xobject_resources(ctx, form), da, &font_rec);
- measure_text(ctx, doc, &font_rec, &fz_identity, text, &bounds);
- fz_translate(&mat, (rect.x1 - bounds.x1)/2, (rect.y1 - bounds.y1)/2);
- fzbuf_print_text(ctx, fzbuf, &clip, NULL, &font_rec, &mat, text);
- }
-
- pdf_update_stream(ctx, doc, form, fzbuf, 0);
+ else if (name == PDF_NAME(Sold))
+ write_stamp(ctx, buf, font, "SOLD", 13, 30);
+ else if (name == PDF_NAME(TopSecret))
+ write_stamp(ctx, buf, font, "TOP SECRET", 14, 26);
+ else if (name == PDF_NAME(Draft))
+ write_stamp(ctx, buf, font, "DRAFT", 13, 30);
+ else
+ write_stamp(ctx, buf, font, pdf_to_name(ctx, name), 17, 20);
}
fz_always(ctx)
- {
- font_info_fin(ctx, &font_rec);
- fz_drop_buffer(ctx, fzbuf);
- pdf_drop_obj(ctx, form);
- }
+ fz_drop_font(ctx, font);
fz_catch(ctx)
- {
fz_rethrow(ctx);
- }
-}
-void pdf_update_text_markup_appearance(fz_context *ctx, pdf_document *doc, pdf_annot *annot, enum pdf_annot_type type)
-{
- float color[3];
- float alpha;
- float line_height;
- float line_thickness;
- pdf_obj *annotcolor;
-
- switch (type)
+ *bbox = fz_make_rect(0, 0, 190, 50);
+ if (xs > ys)
{
- case PDF_ANNOT_HIGHLIGHT:
- color[0] = 1.0f;
- color[1] = 1.0f;
- color[2] = 0.0f;
- alpha = 0.5f;
- line_thickness = 1.0f;
- line_height = 0.5f;
- break;
- case PDF_ANNOT_UNDERLINE:
- color[0] = 0.0f;
- color[1] = 0.0f;
- color[2] = 1.0f;
- alpha = 1.0f;
- line_thickness = LINE_THICKNESS;
- line_height = UNDERLINE_HEIGHT;
- break;
- case PDF_ANNOT_STRIKE_OUT:
- color[0] = 1.0f;
- color[1] = 0.0f;
- color[2] = 0.0f;
- alpha = 1.0f;
- line_thickness = LINE_THICKNESS;
- line_height = STRIKE_HEIGHT;
- break;
- default:
- return;
- }
-
- annotcolor = pdf_dict_get(ctx, annot->obj, PDF_NAME(C));
-
- if (pdf_is_array(ctx, annotcolor))
- {
- color[0] = pdf_to_int(ctx, pdf_array_get(ctx, annotcolor, 0));
- color[1] = pdf_to_int(ctx, pdf_array_get(ctx, annotcolor, 1));
- color[2] = pdf_to_int(ctx, pdf_array_get(ctx, annotcolor, 2));
- }
-
- pdf_set_markup_appearance(ctx, doc, annot, color, alpha, line_thickness, line_height);
-}
-
-void pdf_set_annot_appearance(fz_context *ctx, pdf_document *doc, pdf_annot *annot, fz_rect *rect, fz_display_list *disp_list)
-{
- pdf_obj *obj = annot->obj;
- fz_device *dev = NULL;
- fz_matrix page_ctm, inv_page_ctm;
-
- pdf_obj *resources;
- fz_buffer *contents;
-
- pdf_obj *ap_obj;
- fz_rect trect = *rect;
-
- pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
- fz_invert_matrix(&inv_page_ctm, &page_ctm);
-
- fz_transform_rect(&trect, &inv_page_ctm);
-
- pdf_dict_put_drop(ctx, obj, PDF_NAME(Rect), pdf_new_rect(ctx, doc, &trect));
-
- /* See if there is a current normal appearance */
- ap_obj = pdf_dict_getl(ctx, obj, PDF_NAME(AP), PDF_NAME(N), NULL);
- if (!pdf_is_stream(ctx, ap_obj))
- ap_obj = NULL;
-
- if (ap_obj == NULL)
- {
- ap_obj = pdf_new_xobject(ctx, doc, &trect, &fz_identity, NULL, NULL);
- pdf_dict_putl_drop(ctx, obj, ap_obj, PDF_NAME(AP), PDF_NAME(N), NULL);
+ float xc = (rect->x1+rect->x0) / 2;
+ rect->x0 = xc - 95 * ys;
+ rect->x1 = xc + 95 * ys;
}
else
{
- pdf_xref_ensure_incremental_object(ctx, doc, pdf_to_num(ctx, ap_obj));
- /* Update bounding box and matrix in reused xobject obj */
- pdf_dict_put_drop(ctx, ap_obj, PDF_NAME(BBox), pdf_new_rect(ctx, doc, &trect));
- pdf_dict_put_drop(ctx, ap_obj, PDF_NAME(Matrix), pdf_new_matrix(ctx, doc, &fz_identity));
+ float yc = (rect->y1+rect->y0) / 2;
+ rect->y0 = yc - 25 * xs;
+ rect->y1 = yc + 25 * xs;
}
-
- resources = pdf_dict_get(ctx, ap_obj, PDF_NAME(Resources));
-
- contents = fz_new_buffer(ctx, 0);
-
- fz_var(dev);
- fz_try(ctx)
- {
- dev = pdf_new_pdf_device(ctx, doc, &fz_identity, &trect, resources, contents);
- fz_run_display_list(ctx, disp_list, dev, &inv_page_ctm, &fz_infinite_rect, NULL);
- fz_close_device(ctx, dev);
- pdf_update_stream(ctx, doc, ap_obj, contents, 0);
- }
- fz_always(ctx)
- {
- fz_drop_device(ctx, dev);
- fz_drop_buffer(ctx, contents);
- }
- fz_catch(ctx)
- fz_rethrow(ctx);
-
- /* Mark the appearance as changed - required for partial update */
- annot->has_new_ap = 1;
- annot->needs_new_ap = 0;
}
-static fz_point *
-quadpoints(fz_context *ctx, pdf_document *doc, pdf_obj *annot, int *nout)
+static float
+break_simple_string(fz_context *ctx, fz_font *font, float size, const char *a, const char **endp, float maxw)
{
- pdf_obj *quad = pdf_dict_get(ctx, annot, PDF_NAME(QuadPoints));
- fz_point *qp = NULL;
- int i, n;
-
- if (!quad)
- return NULL;
-
- n = pdf_array_len(ctx, quad);
-
- if (n%8 != 0)
- return NULL;
-
- fz_var(qp);
- fz_try(ctx)
- {
- qp = fz_malloc_array(ctx, n/2, sizeof(fz_point));
-
- for (i = 0; i < n; i += 2)
+ const char *space = NULL;
+ float space_x, x = 0;
+ int c, g;
+ while (*a)
+ {
+ a += fz_chartorune(&c, a);
+ if (c >= 256)
+ c = REPLACEMENT;
+ if (c == '\n' || c == '\r')
+ break;
+ if (c == ' ')
{
- qp[i/2].x = pdf_to_real(ctx, pdf_array_get(ctx, quad, i));
- qp[i/2].y = pdf_to_real(ctx, pdf_array_get(ctx, quad, i+1));
+ space = a;
+ space_x = x;
}
+ g = fz_encode_character(ctx, font, c);
+ x += fz_advance_glyph(ctx, font, g, 0) * size;
+ if (space && x > maxw)
+ return *endp = space, space_x;
}
- fz_catch(ctx)
- {
- fz_free(ctx, qp);
- fz_rethrow(ctx);
- }
-
- *nout = n/2;
-
- return qp;
+ return *endp = a, x;
}
-void pdf_set_markup_appearance(fz_context *ctx, pdf_document *doc, pdf_annot *annot, float color[3], float alpha, float line_thickness, float line_height)
+static void
+write_simple_string_with_quadding(fz_context *ctx, fz_buffer *buf, fz_font *font, float size,
+ const char *a, float maxw, int q)
{
- fz_path *path = NULL;
- fz_stroke_state *stroke = NULL;
- fz_device *dev = NULL;
- fz_display_list *strike_list = NULL;
- int i, n;
- fz_point *qp = quadpoints(ctx, doc, annot->obj, &n);
- fz_matrix page_ctm;
-
- pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
-
- if (!qp || n <= 0)
- return;
-
- fz_var(path);
- fz_var(stroke);
- fz_var(dev);
- fz_var(strike_list);
- fz_try(ctx)
+ const char *b;
+ float px = 0, x, w;
+ while (*a)
{
- fz_rect rect = fz_empty_rect;
-
- rect.x0 = rect.x1 = qp[0].x;
- rect.y0 = rect.y1 = qp[0].y;
- for (i = 0; i < n; i++)
- fz_include_point_in_rect(&rect, &qp[i]);
-
- strike_list = fz_new_display_list(ctx, NULL);
- dev = fz_new_list_device(ctx, strike_list);
-
- for (i = 0; i < n; i += 4)
+ w = break_simple_string(ctx, font, size, a, &b, maxw);
+ if (b > a)
{
- /* Contrary to the specification, the points within a QuadPoint are NOT ordered
- * in a counterclockwise fashion. Experiments with Adobe's implementation
- * indicates a cross-wise ordering is intended: ll, lr, ul, ur. */
- fz_point ll = qp[i];
- fz_point lr = qp[i+1];
- fz_point up;
- float thickness;
-
- up.x = qp[i+2].x - qp[i].x; /* ul - ll vector */
- up.y = qp[i+2].y - qp[i].y;
-
- ll.x += line_height * up.x;
- ll.y += line_height * up.y;
- lr.x += line_height * up.x;
- lr.y += line_height * up.y;
-
- thickness = sqrtf(up.x * up.x + up.y * up.y) * line_thickness;
-
- if (!stroke || fz_abs(stroke->linewidth - thickness) < SMALL_FLOAT)
+ if (q > 0)
{
- if (stroke)
- {
- // assert(path)
- fz_stroke_path(ctx, dev, path, stroke, &page_ctm, fz_device_rgb(ctx), color, alpha, NULL);
- fz_drop_stroke_state(ctx, stroke);
- stroke = NULL;
- fz_drop_path(ctx, path);
- path = NULL;
- }
-
- stroke = fz_new_stroke_state(ctx);
- stroke->linewidth = thickness;
- path = fz_new_path(ctx);
+ if (q == 1)
+ x = (maxw - w) / 2;
+ else
+ x = (maxw - w);
+ fz_append_printf(ctx, buf, "%g %g Td ", x - px, -size);
}
-
- fz_moveto(ctx, path, ll.x, ll.y);
- fz_lineto(ctx, path, lr.x, lr.y);
- }
-
- if (stroke)
- {
- fz_stroke_path(ctx, dev, path, stroke, &page_ctm, fz_device_rgb(ctx), color, alpha, NULL);
+ if (b[-1] == '\n' || b[-1] == '\r')
+ write_simple_string(ctx, buf, a, b-1);
+ else
+ write_simple_string(ctx, buf, a, b);
+ a = b;
+ px = x;
+ fz_append_string(ctx, buf, (q > 0) ? "Tj\n" : "'\n");
}
-
- fz_close_device(ctx, dev);
-
- fz_transform_rect(&rect, &page_ctm);
- pdf_set_annot_appearance(ctx, doc, annot, &rect, strike_list);
- }
- fz_always(ctx)
- {
- fz_free(ctx, qp);
- fz_drop_device(ctx, dev);
- fz_drop_stroke_state(ctx, stroke);
- fz_drop_path(ctx, path);
- fz_drop_display_list(ctx, strike_list);
- }
- fz_catch(ctx)
- {
- fz_rethrow(ctx);
}
}
-/* Returns borrowed colorspace reference */
-static fz_colorspace *pdf_to_color(fz_context *ctx, pdf_document *doc, pdf_obj *col, float color[4])
+static const char *full_font_name(const char **name)
{
- fz_colorspace *cs;
- int i, ncol = pdf_array_len(ctx, col);
-
- switch (ncol)
- {
- case 1: cs = fz_device_gray(ctx); break;
- case 3: cs = fz_device_rgb(ctx); break;
- case 4: cs = fz_device_cmyk(ctx); break;
- default: return NULL;
- }
-
- for (i = 0; i < ncol; i++)
- color[i] = pdf_to_real(ctx, pdf_array_get(ctx, col, i));
-
- return cs;
+ if (!strcmp(*name, "Cour")) return "Courier";
+ if (!strcmp(*name, "Helv")) return "Helvetica";
+ if (!strcmp(*name, "TiRo")) return "Times-Roman";
+ if (!strcmp(*name, "Symb")) return "Symbol";
+ if (!strcmp(*name, "ZaDb")) return "ZapfDingbats";
+ return *name = "Helv", "Helvetica";
}
-void pdf_update_ink_appearance(fz_context *ctx, pdf_document *doc, pdf_annot *annot)
+static void
+write_variable_text(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, pdf_obj **res,
+ char *text, const char *fontname, float size, float color[3], int q,
+ float x, float y, float w, float h, float lw)
{
- fz_path *path = NULL;
- fz_stroke_state *stroke = NULL;
- fz_device *dev = NULL;
- fz_display_list *strike_list = NULL;
- fz_colorspace *cs = NULL;
- fz_matrix page_ctm;
-
- pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
-
- fz_var(path);
- fz_var(stroke);
- fz_var(dev);
- fz_var(strike_list);
- fz_var(cs);
+ pdf_obj *res_font;
+ fz_font *font;
+
+ font = fz_new_base14_font(ctx, full_font_name(&fontname));
fz_try(ctx)
{
- fz_rect rect = fz_empty_rect;
- float color[4];
- float width;
- pdf_obj *list;
- int n, m, i, j;
- int empty = 1;
-
- width = pdf_annot_border(ctx, annot);
- if (width == 0.0f)
- width = 1.0f;
-
- list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList));
-
- n = pdf_array_len(ctx, list);
-
- strike_list = fz_new_display_list(ctx, NULL);
- dev = fz_new_list_device(ctx, strike_list);
- path = fz_new_path(ctx);
- stroke = fz_new_stroke_state(ctx);
- stroke->linewidth = width;
- stroke->start_cap = stroke->end_cap = FZ_LINECAP_ROUND;
- stroke->linejoin = FZ_LINEJOIN_ROUND;
-
- for (i = 0; i < n; i ++)
- {
- fz_point pt_last;
- pdf_obj *arc = pdf_array_get(ctx, list, i);
- m = pdf_array_len(ctx, arc);
-
- for (j = 0; j < m-1; j += 2)
- {
- fz_point pt;
- pt.x = pdf_to_real(ctx, pdf_array_get(ctx, arc, j));
- pt.y = pdf_to_real(ctx, pdf_array_get(ctx, arc, j+1));
-
- if (i == 0 && j == 0)
- {
- rect.x0 = rect.x1 = pt.x;
- rect.y0 = rect.y1 = pt.y;
- empty = 0;
- }
- else
- {
- fz_include_point_in_rect(&rect, &pt);
- }
+ /* /Resources << /Font << /Helv %d 0 R >> >> */
+ *res = pdf_new_dict(ctx, annot->page->doc, 1);
+ res_font = pdf_dict_put_dict(ctx, *res, PDF_NAME(Font), 1);
+ pdf_dict_puts_drop(ctx, res_font, fontname, pdf_add_simple_font(ctx, annot->page->doc, font, 0));
- if (j == 0)
- fz_moveto(ctx, path, pt.x, pt.y);
- else
- fz_curvetov(ctx, path, pt_last.x, pt_last.y, (pt.x + pt_last.x) / 2, (pt.y + pt_last.y) / 2);
- pt_last = pt;
- }
- fz_lineto(ctx, path, pt_last.x, pt_last.y);
- }
-
- cs = pdf_to_color(ctx, doc, pdf_dict_get(ctx, annot->obj, PDF_NAME(C)), color);
- fz_stroke_path(ctx, dev, path, stroke, &page_ctm, cs, color, 1.0f, NULL);
-
- fz_expand_rect(&rect, width);
- /*
- Expand the rectangle by width all around. We cannot use
- fz_expand_rect because the rectangle might be empty in the
- single point case
- */
- if (!empty)
- {
- rect.x0 -= width;
- rect.y0 -= width;
- rect.x1 += width;
- rect.y1 += width;
- }
-
- fz_close_device(ctx, dev);
-
- fz_transform_rect(&rect, &page_ctm);
- pdf_set_annot_appearance(ctx, doc, annot, &rect, strike_list);
+ fz_append_printf(ctx, buf, "%g %g %g rg\n", color[0], color[1], color[2]);
+ fz_append_string(ctx, buf, "BT\n");
+ fz_append_printf(ctx, buf, "/%s %g Tf\n", fontname, size);
+ fz_append_printf(ctx, buf, "%g TL\n", size);
+ fz_append_printf(ctx, buf, "%g %g Td\n", x+lw, y+h-lw);
+ write_simple_string_with_quadding(ctx, buf, font, size, text, w-lw*2, q);
+ fz_append_string(ctx, buf, "ET\n");
}
fz_always(ctx)
- {
- fz_drop_device(ctx, dev);
- fz_drop_stroke_state(ctx, stroke);
- fz_drop_path(ctx, path);
- fz_drop_display_list(ctx, strike_list);
- }
+ fz_drop_font(ctx, font);
fz_catch(ctx)
- {
fz_rethrow(ctx);
- }
}
-static void add_text(fz_context *ctx, font_info *font_rec, fz_text *text, const char *str, size_t str_len, const fz_matrix *tm_)
+static void
+pdf_write_free_text_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res)
{
- fz_font *font = font_rec->font->font;
- fz_matrix tm = *tm_;
- int ucs, gid, n;
-
- while (str_len > 0)
- {
- n = fz_chartorune(&ucs, str);
- str += n;
- str_len -= n;
- gid = fz_encode_character(ctx, font, ucs);
- fz_show_glyph(ctx, text, font, &tm, gid, ucs, 0, 0, FZ_BIDI_NEUTRAL, FZ_LANG_UNSET);
- tm.e += fz_advance_glyph(ctx, font, gid, 0) * font_rec->da_rec.font_size;
- }
-}
+ const char *font;
+ float size, color[3];
+ char *text;
+ float x, y, w, h, lw;
+ int q;
-static fz_text *layout_text(fz_context *ctx, font_info *font_rec, char *str, float x, float y)
-{
- fz_text *text;
- fz_matrix tm;
+ q = pdf_annot_quadding(ctx, annot);
+ pdf_annot_default_appearance(ctx, annot, &font, &size, color);
- fz_scale(&tm, font_rec->da_rec.font_size, font_rec->da_rec.font_size);
- tm.e = x;
- tm.f = y;
+ x = rect->x0;
+ y = rect->y0;
+ w = rect->x1 - rect->x0;
+ h = rect->y1 - rect->y0;
- text = fz_new_text(ctx);
+ if (pdf_write_fill_color_appearance(ctx, annot, buf))
+ fz_append_printf(ctx, buf, "%g %g %g %g re\nf\n", x, y, w, h);
- fz_try(ctx)
+ lw = pdf_write_border_appearance(ctx, annot, buf);
+ if (lw > 0)
{
- add_text(ctx, font_rec, text, str, (int)strlen(str), &tm);
+ fz_append_printf(ctx, buf, "%g %g %g RG\n", color[0], color[1], color[2]);
+ fz_append_printf(ctx, buf, "%g %g %g %g re\nS\n", x+lw/2, y+lw/2, w-lw, h-lw);
+ x += size * 0.2f;
+ w -= size * 0.4f;
}
- fz_catch(ctx)
- {
- fz_drop_text(ctx, text);
- fz_rethrow(ctx);
- }
-
- return text;
-}
-
-static fz_text *fit_text(fz_context *ctx, font_info *font_rec, const char *str, fz_rect *bounds)
-{
- float width = bounds->x1 - bounds->x0;
- float height = bounds->y1 - bounds->y0;
- fz_matrix tm;
- fz_text *text = NULL;
- fz_text_span *span;
- text_splitter splitter;
- float ascender;
- /* Initially aim for one-line of text */
- font_rec->da_rec.font_size = height / font_rec->lineheight;
-
- text_splitter_init(&splitter, font_rec, str, width, height, 1);
-
- fz_var(text);
+ text = pdf_copy_annot_contents(ctx, annot);
fz_try(ctx)
- {
- int i;
- while (!splitter.done)
- {
- /* Try a layout pass */
- int line = 0;
- float font_size;
-
- fz_drop_text(ctx, text);
- text = NULL;
- font_size = font_rec->da_rec.font_size;
- fz_scale(&tm, font_size, font_size);
- tm.e = 0;
- tm.f = 0;
-
- text = fz_new_text(ctx);
-
- text_splitter_start_pass(&splitter);
-
- /* Layout unscaled text to a scaled-up width, so that
- * the scaled-down text will fit the unscaled width */
-
- while (!splitter.done && line < splitter.max_lines)
- {
- /* Layout a line */
- text_splitter_start_line(&splitter);
-
- while (!splitter.done && text_splitter_layout(ctx, &splitter))
- {
- if (splitter.text[splitter.text_start] != ' ')
- {
- float dx, dy;
- const char *word = str+splitter.text_start;
- size_t wordlen = splitter.text_end-splitter.text_start;
-
- text_splitter_move(&splitter, -line, &dx, &dy);
- tm.e += dx;
- tm.f += dy;
- add_text(ctx, font_rec, text, word, wordlen, &tm);
- }
- }
-
- line ++;
- }
-
- if (!splitter.done)
- text_splitter_retry(&splitter);
- }
-
- /* Post process text with the scale determined by the splitter
- * and with the required offset */
- for (span = text->head; span; span = span->next)
- {
- fz_pre_scale(&span->trm, splitter.scale, splitter.scale);
- ascender = font_rec->font->ascent * font_rec->da_rec.font_size * splitter.scale / 1000.0f;
- for (i = 0; i < span->len; i++)
- {
- span->items[i].x = span->items[i].x * splitter.scale + bounds->x0;
- span->items[i].y = span->items[i].y * splitter.scale + bounds->y1 - ascender;
- }
- }
- }
+ write_variable_text(ctx, annot, buf, res, text, font, size, color, q, x, y, w, h, lw);
+ fz_always(ctx)
+ fz_free(ctx, text);
fz_catch(ctx)
- {
- fz_drop_text(ctx, text);
fz_rethrow(ctx);
- }
-
- return text;
-}
-
-static void rect_center(const fz_rect *rect, fz_point *c)
-{
- c->x = (rect->x0 + rect->x1) / 2.0f;
- c->y = (rect->y0 + rect->y1) / 2.0f;
}
-static void center_rect_within_rect(const fz_rect *tofit, const fz_rect *within, fz_matrix *mat)
+static void
+pdf_write_tx_widget_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res)
{
- float xscale = (within->x1 - within->x0) / (tofit->x1 - tofit->x0);
- float yscale = (within->y1 - within->y0) / (tofit->y1 - tofit->y0);
- float scale = fz_min(xscale, yscale);
- fz_point tofit_center;
- fz_point within_center;
-
- rect_center(within, &within_center);
- rect_center(tofit, &tofit_center);
-
- /* Translate "tofit" to be centered on the origin
- * Scale "tofit" to a size that fits within "within"
- * Translate "tofit" to "within's" center
- * Do all the above in reverse order so that we can use the fz_pre_xx functions */
- fz_translate(mat, within_center.x, within_center.y);
- fz_pre_scale(mat, scale, scale);
- fz_pre_translate(mat, -tofit_center.x, -tofit_center.y);
-}
-
-static const float outline_thickness = 15.0f;
-
-static void draw_rounded_rect(fz_context *ctx, fz_path *path)
-{
- fz_moveto(ctx, path, 20.0f, 60.0f);
- fz_curveto(ctx, path, 20.0f, 30.0f, 30.0f, 20.0f, 60.0f, 20.0f);
- fz_lineto(ctx, path, 340.0f, 20.0f);
- fz_curveto(ctx, path, 370.0f, 20.0f, 380.0f, 30.0f, 380.0f, 60.0f);
- fz_lineto(ctx, path, 380.0f, 340.0f);
- fz_curveto(ctx, path, 380.0f, 370.0f, 370.0f, 380.0f, 340.0f, 380.0f);
- fz_lineto(ctx, path, 60.0f, 380.0f);
- fz_curveto(ctx, path, 30.0f, 380.0f, 20.0f, 370.0f, 20.0f, 340.0f);
- fz_closepath(ctx, path);
-}
+ const char *font;
+ float size, color[3];
+ char *text;
+ int q;
-static void draw_speech_bubble(fz_context *ctx, fz_path *path)
-{
- fz_moveto(ctx, path, 199.0f, 315.6f);
- fz_curveto(ctx, path, 35.6f, 315.6f, 27.0f, 160.8f, 130.2f, 131.77f);
- fz_curveto(ctx, path, 130.2f, 93.07f, 113.0f, 83.4f, 113.0f, 83.4f);
- fz_curveto(ctx, path, 138.8f, 73.72f, 173.2f, 83.4f, 190.4f, 122.1f);
- fz_curveto(ctx, path, 391.64f, 122.1f, 362.4f, 315.6f, 199.0f, 315.6f);
- fz_closepath(ctx, path);
-}
+ q = pdf_annot_quadding(ctx, annot);
+ pdf_annot_default_appearance(ctx, annot, &font, &size, color);
-void pdf_update_text_annot_appearance(fz_context *ctx, pdf_document *doc, pdf_annot *annot)
-{
- static float white[3] = {1.0f, 1.0f, 1.0f};
- static float yellow[3] = {1.0f, 1.0f, 0.0f};
- static float black[3] = {0.0f, 0.0f, 0.0f};
-
- fz_display_list *dlist = NULL;
- fz_device *dev = NULL;
- fz_colorspace *cs = NULL;
- fz_path *path = NULL;
- fz_stroke_state *stroke = NULL;
- fz_matrix page_ctm;
-
- pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
-
- fz_var(path);
- fz_var(stroke);
- fz_var(dlist);
- fz_var(dev);
- fz_var(cs);
+ text = pdf_field_value(ctx, annot->page->doc, annot->obj);
fz_try(ctx)
- {
- fz_rect rect;
- fz_rect bounds;
- fz_matrix tm;
-
- pdf_to_rect(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(Rect)), &rect);
- dlist = fz_new_display_list(ctx, NULL);
- dev = fz_new_list_device(ctx, dlist);
- stroke = fz_new_stroke_state(ctx);
- stroke->linewidth = outline_thickness;
- stroke->linejoin = FZ_LINEJOIN_ROUND;
-
- path = fz_new_path(ctx);
- draw_rounded_rect(ctx, path);
- fz_bound_path(ctx, path, NULL, &fz_identity, &bounds);
- fz_expand_rect(&bounds, outline_thickness);
- center_rect_within_rect(&bounds, &rect, &tm);
- fz_concat(&tm, &tm, &page_ctm);
- cs = fz_device_rgb(ctx); /* Borrowed reference */
- fz_fill_path(ctx, dev, path, 0, &tm, cs, yellow, 1.0f, NULL);
- fz_stroke_path(ctx, dev, path, stroke, &tm, cs, black, 1.0f, NULL);
- fz_drop_path(ctx, path);
- path = NULL;
-
- path = fz_new_path(ctx);
- draw_speech_bubble(ctx, path);
- fz_fill_path(ctx, dev, path, 0, &tm, cs, white, 1.0f, NULL);
- fz_stroke_path(ctx, dev, path, stroke, &tm, cs, black, 1.0f, NULL);
-
- fz_close_device(ctx, dev);
-
- fz_transform_rect(&rect, &page_ctm);
- pdf_set_annot_appearance(ctx, doc, annot, &rect, dlist);
-
- /* Drop the cached xobject from the annotation structure to
- * force a redraw on next pdf_update_page call */
- pdf_drop_obj(ctx, annot->ap);
- annot->ap = NULL;
- }
+ write_variable_text(ctx, annot, buf, res, text, font, size, color, q,
+ rect->x0, rect->y0, rect->x1-rect->x0, rect->y1-rect->y0, 0);
fz_always(ctx)
- {
- fz_drop_device(ctx, dev);
- fz_drop_display_list(ctx, dlist);
- fz_drop_stroke_state(ctx, stroke);
- fz_drop_path(ctx, path);
- }
+ fz_free(ctx, text);
fz_catch(ctx)
- {
fz_rethrow(ctx);
- }
}
-void pdf_update_free_text_annot_appearance(fz_context *ctx, pdf_document *doc, pdf_annot *annot)
+static void
+pdf_write_ch_widget_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res)
{
- pdf_obj *obj = annot->obj;
- pdf_obj *dr = pdf_dict_get(ctx, annot->page->obj, PDF_NAME(Resources));
- fz_display_list *dlist = NULL;
- fz_device *dev = NULL;
- font_info font_rec;
- fz_text *text = NULL;
- fz_matrix page_ctm;
-
- pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
-
- memset(&font_rec, 0, sizeof(font_rec));
-
- /* Set some sane defaults in case the parsing of the font_rec fails */
- font_rec.da_rec.col_size = 1; /* Default to greyscale */
- font_rec.da_rec.font_size = 12; /* Default to 12 point */
-
- fz_var(dlist);
- fz_var(dev);
- fz_var(text);
- fz_try(ctx)
- {
- char *contents = pdf_to_str_buf(ctx, pdf_dict_get(ctx, obj, PDF_NAME(Contents)));
- char *da = pdf_to_str_buf(ctx, pdf_dict_get(ctx, obj, PDF_NAME(DA)));
- fz_colorspace *cs;
- fz_point pos;
- fz_rect rect;
-
- pdf_to_rect(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(Rect)), &rect);
-
- get_font_info(ctx, doc, dr, da, &font_rec);
-
- switch (font_rec.da_rec.col_size)
- {
- default: cs = fz_device_gray(ctx); break;
- case 3: cs = fz_device_rgb(ctx); break;
- case 4: cs = fz_device_cmyk(ctx); break;
- }
-
- /* Adjust for the descender */
- pos.x = rect.x0;
- pos.y = rect.y0 - font_rec.font->descent * font_rec.da_rec.font_size / 1000.0f;
-
- text = layout_text(ctx, &font_rec, contents, pos.x, pos.y);
-
- dlist = fz_new_display_list(ctx, NULL);
- dev = fz_new_list_device(ctx, dlist);
- fz_fill_text(ctx, dev, text, &page_ctm, cs, font_rec.da_rec.col, 1.0f, NULL);
- fz_close_device(ctx, dev);
-
- fz_transform_rect(&rect, &page_ctm);
- pdf_set_annot_appearance(ctx, doc, annot, &rect, dlist);
- }
- fz_always(ctx)
- {
- fz_drop_device(ctx, dev);
- fz_drop_display_list(ctx, dlist);
- font_info_fin(ctx, &font_rec);
- fz_drop_text(ctx, text);
- }
- fz_catch(ctx)
- {
- fz_rethrow(ctx);
- }
+ pdf_write_tx_widget_appearance(ctx, annot, buf, rect, res);
}
-static void draw_logo(fz_context *ctx, fz_path *path)
+static void
+pdf_write_widget_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res)
{
- fz_moveto(ctx, path, 122.25f, 0.0f);
- fz_lineto(ctx, path, 122.25f, 14.249f);
- fz_curveto(ctx, path, 125.98f, 13.842f, 129.73f, 13.518f, 133.5f, 13.277f);
- fz_lineto(ctx, path, 133.5f, 0.0f);
- fz_lineto(ctx, path, 122.25f, 0.0f);
- fz_closepath(ctx, path);
- fz_moveto(ctx, path, 140.251f, 0.0f);
- fz_lineto(ctx, path, 140.251f, 12.935f);
- fz_curveto(ctx, path, 152.534f, 12.477f, 165.03f, 12.899f, 177.75f, 14.249f);
- fz_lineto(ctx, path, 177.75f, 21.749f);
- fz_curveto(ctx, path, 165.304f, 20.413f, 152.809f, 19.871f, 140.251f, 20.348f);
- fz_lineto(ctx, path, 140.251f, 39.0f);
- fz_lineto(ctx, path, 133.5f, 39.0f);
- fz_lineto(ctx, path, 133.5f, 20.704f);
- fz_curveto(ctx, path, 129.756f, 20.956f, 126.006f, 21.302f, 122.25f, 21.749f);
- fz_lineto(ctx, path, 122.25f, 50.999f);
- fz_lineto(ctx, path, 177.751f, 50.999f);
- fz_lineto(ctx, path, 177.751f, 0.0f);
- fz_lineto(ctx, path, 140.251f, 0.0f);
- fz_closepath(ctx, path);
- fz_moveto(ctx, path, 23.482f, 129.419f);
- fz_curveto(ctx, path, -20.999f, 199.258f, -0.418f, 292.039f, 69.42f, 336.519f);
- fz_curveto(ctx, path, 139.259f, 381.0f, 232.04f, 360.419f, 276.52f, 290.581f);
- fz_curveto(ctx, path, 321.001f, 220.742f, 300.42f, 127.961f, 230.582f, 83.481f);
- fz_curveto(ctx, path, 160.743f, 39.0f, 67.962f, 59.581f, 23.482f, 129.419f);
- fz_closepath(ctx, path);
- fz_moveto(ctx, path, 254.751f, 128.492f);
- fz_curveto(ctx, path, 303.074f, 182.82f, 295.364f, 263.762f, 237.541f, 309.165f);
- fz_curveto(ctx, path, 179.718f, 354.568f, 93.57f, 347.324f, 45.247f, 292.996f);
- fz_curveto(ctx, path, -3.076f, 238.668f, 4.634f, 157.726f, 62.457f, 112.323f);
- fz_curveto(ctx, path, 120.28f, 66.92f, 206.428f, 74.164f, 254.751f, 128.492f);
- fz_closepath(ctx, path);
- fz_moveto(ctx, path, 111.0f, 98.999f);
- fz_curveto(ctx, path, 87.424f, 106.253f, 68.25f, 122.249f, 51.75f, 144.749f);
- fz_lineto(ctx, path, 103.5f, 297.749f);
- fz_lineto(ctx, path, 213.75f, 298.499f);
- fz_curveto(ctx, path, 206.25f, 306.749f, 195.744f, 311.478f, 185.25f, 314.249f);
- fz_curveto(ctx, path, 164.22f, 319.802f, 141.22f, 319.775f, 120.0f, 314.999f);
- fz_curveto(ctx, path, 96.658f, 309.745f, 77.25f, 298.499f, 55.5f, 283.499f);
- fz_curveto(ctx, path, 69.75f, 299.249f, 84.617f, 311.546f, 102.75f, 319.499f);
- fz_curveto(ctx, path, 117.166f, 325.822f, 133.509f, 327.689f, 149.25f, 327.749f);
- fz_curveto(ctx, path, 164.21f, 327.806f, 179.924f, 326.532f, 193.5f, 320.249f);
- fz_curveto(ctx, path, 213.95f, 310.785f, 232.5f, 294.749f, 245.25f, 276.749f);
- fz_lineto(ctx, path, 227.25f, 276.749f);
- fz_curveto(ctx, path, 213.963f, 276.749f, 197.25f, 263.786f, 197.25f, 250.499f);
- fz_lineto(ctx, path, 197.25f, 112.499f);
- fz_curveto(ctx, path, 213.75f, 114.749f, 228.0f, 127.499f, 241.5f, 140.999f);
- fz_curveto(ctx, path, 231.75f, 121.499f, 215.175f, 109.723f, 197.25f, 101.249f);
- fz_curveto(ctx, path, 181.5f, 95.249f, 168.412f, 94.775f, 153.0f, 94.499f);
- fz_curveto(ctx, path, 139.42f, 94.256f, 120.75f, 95.999f, 111.0f, 98.999f);
- fz_closepath(ctx, path);
- fz_moveto(ctx, path, 125.25f, 105.749f);
- fz_lineto(ctx, path, 125.25f, 202.499f);
- fz_lineto(ctx, path, 95.25f, 117.749f);
- fz_curveto(ctx, path, 105.75f, 108.749f, 114.0f, 105.749f, 125.25f, 105.749f);
- fz_closepath(ctx, path);
+ pdf_obj *ft = pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(FT));
+ if (pdf_name_eq(ctx, ft, PDF_NAME(Tx)))
+ pdf_write_tx_widget_appearance(ctx, annot, buf, rect, res);
+ else if (pdf_name_eq(ctx, ft, PDF_NAME(Ch)))
+ pdf_write_ch_widget_appearance(ctx, annot, buf, rect, res);
+ else
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot create appearance stream for %s widgets", pdf_to_name(ctx, ft));
}
-static void insert_signature_appearance_layers(fz_context *ctx, pdf_document *doc, pdf_annot *annot)
+static void
+pdf_write_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf,
+ fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res)
{
- pdf_obj *ap = pdf_dict_getl(ctx, annot->obj, PDF_NAME(AP), PDF_NAME(N), NULL);
- pdf_obj *main_ap = NULL;
- pdf_obj *frm = NULL;
- pdf_obj *n0 = NULL;
- fz_rect bbox;
- fz_buffer *fzbuf = NULL;
-
- pdf_to_rect(ctx, pdf_dict_get(ctx, ap, PDF_NAME(BBox)), &bbox);
-
- fz_var(main_ap);
- fz_var(frm);
- fz_var(n0);
- fz_var(fzbuf);
- fz_try(ctx)
- {
- main_ap = pdf_new_xobject(ctx, doc, &bbox, &fz_identity, NULL, NULL);
- frm = pdf_new_xobject(ctx, doc, &bbox, &fz_identity, NULL, NULL);
- n0 = pdf_new_xobject(ctx, doc, &bbox, &fz_identity, NULL, NULL);
-
- pdf_dict_putl(ctx, main_ap, frm, PDF_NAME(Resources), PDF_NAME(XObject), PDF_NAME(FRM), NULL);
- fzbuf = fz_new_buffer(ctx, 8);
- fz_append_printf(ctx, fzbuf, "/FRM Do");
- pdf_update_stream(ctx, doc, main_ap, fzbuf, 0);
- fz_drop_buffer(ctx, fzbuf);
- fzbuf = NULL;
-
- pdf_dict_putl(ctx, frm, n0, PDF_NAME(Resources), PDF_NAME(XObject), PDF_NAME(n0), NULL);
- pdf_dict_putl(ctx, frm, ap, PDF_NAME(Resources), PDF_NAME(XObject), PDF_NAME(n2), NULL);
- fzbuf = fz_new_buffer(ctx, 8);
- fz_append_printf(ctx, fzbuf, "q 1 0 0 1 0 0 cm /n0 Do Q q 1 0 0 1 0 0 cm /n2 Do Q");
- pdf_update_stream(ctx, doc, frm, fzbuf, 0);
- fz_drop_buffer(ctx, fzbuf);
- fzbuf = NULL;
-
- fzbuf = fz_new_buffer(ctx, 8);
- fz_append_printf(ctx, fzbuf, "%% DSBlank");
- pdf_update_stream(ctx, doc, n0, fzbuf, 0);
- fz_drop_buffer(ctx, fzbuf);
- fzbuf = NULL;
-
- pdf_dict_putl(ctx, annot->obj, main_ap, PDF_NAME(AP), PDF_NAME(N), NULL);
- }
- fz_always(ctx)
+ switch (pdf_annot_type(ctx, annot))
{
- pdf_drop_obj(ctx, main_ap);
- pdf_drop_obj(ctx, frm);
- pdf_drop_obj(ctx, n0);
- }
- fz_catch(ctx)
- {
- fz_drop_buffer(ctx, fzbuf);
- fz_rethrow(ctx);
+ default:
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot create appearance stream");
+ case PDF_ANNOT_WIDGET:
+ pdf_write_widget_appearance(ctx, annot, buf, rect, res);
+ *matrix = fz_identity;
+ *bbox = *rect;
+ break;
+ case PDF_ANNOT_INK:
+ pdf_write_ink_appearance(ctx, annot, buf, rect);
+ *matrix = fz_identity;
+ *bbox = *rect;
+ break;
+ case PDF_ANNOT_POLYGON:
+ pdf_write_polygon_appearance(ctx, annot, buf, rect, 1);
+ *matrix = fz_identity;
+ *bbox = *rect;
+ break;
+ case PDF_ANNOT_POLY_LINE:
+ pdf_write_polygon_appearance(ctx, annot, buf, rect, 0);
+ *matrix = fz_identity;
+ *bbox = *rect;
+ break;
+ case PDF_ANNOT_LINE:
+ pdf_write_line_appearance(ctx, annot, buf, rect);
+ *matrix = fz_identity;
+ *bbox = *rect;
+ break;
+ case PDF_ANNOT_SQUARE:
+ pdf_write_square_appearance(ctx, annot, buf, rect);
+ *matrix = fz_identity;
+ *bbox = *rect;
+ break;
+ case PDF_ANNOT_CIRCLE:
+ pdf_write_circle_appearance(ctx, annot, buf, rect);
+ *matrix = fz_identity;
+ *bbox = *rect;
+ break;
+ case PDF_ANNOT_CARET:
+ pdf_write_caret_appearance(ctx, annot, buf, rect, bbox);
+ *matrix = fz_identity;
+ break;
+ case PDF_ANNOT_TEXT:
+ case PDF_ANNOT_FILE_ATTACHMENT:
+ case PDF_ANNOT_SOUND:
+ pdf_write_icon_appearance(ctx, annot, buf, rect, bbox);
+ *matrix = fz_identity;
+ break;
+ case PDF_ANNOT_HIGHLIGHT:
+ pdf_write_highlight_appearance(ctx, annot, buf, rect, res);
+ *matrix = fz_identity;
+ *bbox = *rect;
+ break;
+ case PDF_ANNOT_UNDERLINE:
+ pdf_write_underline_appearance(ctx, annot, buf, rect);
+ *matrix = fz_identity;
+ *bbox = *rect;
+ break;
+ case PDF_ANNOT_STRIKE_OUT:
+ pdf_write_strike_out_appearance(ctx, annot, buf, rect);
+ *matrix = fz_identity;
+ *bbox = *rect;
+ break;
+ case PDF_ANNOT_SQUIGGLY:
+ pdf_write_squiggly_appearance(ctx, annot, buf, rect);
+ *matrix = fz_identity;
+ *bbox = *rect;
+ break;
+ case PDF_ANNOT_STAMP:
+ pdf_write_stamp_appearance(ctx, annot, buf, rect, bbox, res);
+ *matrix = fz_identity;
+ break;
+ case PDF_ANNOT_FREE_TEXT:
+ pdf_write_free_text_appearance(ctx, annot, buf, rect, res);
+ *matrix = fz_identity;
+ *bbox = *rect;
+ break;
}
}
-/* MuPDF blue */
-static float logo_color[3] = {(float)0x25/(float)0xFF, (float)0x72/(float)0xFF, (float)0xAC/(float)0xFF};
-
-void pdf_set_signature_appearance(fz_context *ctx, pdf_document *doc, pdf_annot *annot, char *name, const char *dn, char *date)
+void pdf_update_signature_appearance(fz_context *ctx, pdf_annot *annot, const char *name, const char *text, const char *date)
{
- pdf_obj *obj = annot->obj;
- pdf_obj *dr = pdf_dict_getl(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root), PDF_NAME(AcroForm), PDF_NAME(DR), NULL);
- fz_display_list *dlist = NULL;
- fz_device *dev = NULL;
- font_info font_rec;
- fz_text *text = NULL;
- fz_path *path = NULL;
- fz_buffer *fzbuf = NULL;
- fz_matrix page_ctm;
-
- pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
-
- if (!dr)
- pdf_dict_putl_drop(ctx, pdf_trailer(ctx, doc), pdf_new_dict(ctx, doc, 1), PDF_NAME(Root), PDF_NAME(AcroForm), PDF_NAME(DR), NULL);
-
- memset(&font_rec, 0, sizeof(font_rec));
-
- fz_var(path);
- fz_var(dlist);
- fz_var(dev);
- fz_var(text);
- fz_var(fzbuf);
- fz_try(ctx)
- {
- char *da = pdf_to_str_buf(ctx, pdf_dict_get(ctx, obj, PDF_NAME(DA)));
- fz_rect annot_rect;
- fz_rect logo_bounds;
- fz_matrix logo_tm;
- fz_rect rect;
- fz_colorspace *cs = fz_device_rgb(ctx); /* Borrowed reference */
+ fz_throw(ctx, FZ_ERROR_GENERIC, "writing digital signature appearance streams is not yet implemented");
+}
- pdf_to_rect(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(Rect)), &annot_rect);
- rect = annot_rect;
+void pdf_update_appearance(fz_context *ctx, pdf_annot *annot)
+{
+ pdf_obj *subtype;
+ pdf_obj *ap, *ap_n, *as;
- dlist = fz_new_display_list(ctx, NULL);
- dev = fz_new_list_device(ctx, dlist);
+ if (annot->ap && !annot->needs_new_ap)
+ return;
- path = fz_new_path(ctx);
- draw_logo(ctx, path);
- fz_bound_path(ctx, path, NULL, &fz_identity, &logo_bounds);
- center_rect_within_rect(&logo_bounds, &rect, &logo_tm);
- fz_concat(&logo_tm, &logo_tm, &page_ctm);
- fz_fill_path(ctx, dev, path, 0, &logo_tm, cs, logo_color, 1.0f, NULL);
+ subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
+ if (subtype == PDF_NAME(Popup))
+ return;
+ if (subtype == PDF_NAME(Link))
+ return;
- get_font_info(ctx, doc, dr, da, &font_rec);
+ as = pdf_dict_get(ctx, annot->obj, PDF_NAME(AS));
+ ap = pdf_dict_get(ctx, annot->obj, PDF_NAME(AP));
+ ap_n = pdf_dict_get(ctx, ap, PDF_NAME(N));
+ if (!pdf_is_stream(ctx, ap_n))
+ ap_n = pdf_dict_get(ctx, ap_n, as);
- switch (font_rec.da_rec.col_size)
+ if (pdf_is_stream(ctx, ap_n))
+ {
+ fz_try(ctx)
{
- case 1: cs = fz_device_gray(ctx); break;
- case 3: cs = fz_device_rgb(ctx); break;
- case 4: cs = fz_device_cmyk(ctx); break;
+ pdf_drop_obj(ctx, annot->ap);
+ annot->ap = NULL;
+ annot->ap = pdf_keep_obj(ctx, ap_n);
}
-
- /* Display the name in the left-hand half of the form field */
- rect.x1 = (rect.x0 + rect.x1)/2.0f;
- text = fit_text(ctx, &font_rec, name, &rect);
- fz_fill_text(ctx, dev, text, &page_ctm, cs, font_rec.da_rec.col, 1.0f, NULL);
- fz_drop_text(ctx, text);
- text = NULL;
-
- /* Display the distinguished name in the right-hand half */
- fzbuf = fz_new_buffer(ctx, 256);
- fz_append_printf(ctx, fzbuf, "Digitally signed by %s", name);
- fz_append_printf(ctx, fzbuf, "\nDN: %s", dn);
- if (date)
- fz_append_printf(ctx, fzbuf, "\nDate: %s", date);
- rect = annot_rect;
- rect.x0 = (rect.x0 + rect.x1)/2.0f;
- text = fit_text(ctx, &font_rec, fz_string_from_buffer(ctx, fzbuf), &rect);
- fz_fill_text(ctx, dev, text, &page_ctm, cs, font_rec.da_rec.col, 1.0f, NULL);
-
- fz_close_device(ctx, dev);
-
- rect = annot_rect;
- fz_transform_rect(&rect, &page_ctm);
- pdf_set_annot_appearance(ctx, doc, annot, &rect, dlist);
-
- /* Drop the cached xobject from the annotation structure to
- * force a redraw on next pdf_update_page call */
- pdf_drop_obj(ctx, annot->ap);
- annot->ap = NULL;
-
- insert_signature_appearance_layers(ctx, doc, annot);
- }
- fz_always(ctx)
- {
- fz_drop_device(ctx, dev);
- fz_drop_display_list(ctx, dlist);
- font_info_fin(ctx, &font_rec);
- fz_drop_path(ctx, path);
- fz_drop_text(ctx, text);
- fz_drop_buffer(ctx, fzbuf);
- }
- fz_catch(ctx)
- {
- fz_rethrow(ctx);
+ fz_catch(ctx)
+ fz_warn(ctx, "cannot load appearance stream");
}
-}
-void pdf_update_appearance(fz_context *ctx, pdf_annot *annot)
-{
- pdf_document *doc = annot->page->doc;
- pdf_obj *obj = annot->obj;
- pdf_obj *ap = pdf_dict_get(ctx, obj, PDF_NAME(AP));
-
- if (!ap || !pdf_dict_get(ctx, ap, PDF_NAME(N)) || pdf_obj_is_dirty(ctx, obj) || annot->needs_new_ap)
+ if (!annot->ap || annot->needs_new_ap)
{
- enum pdf_annot_type type = pdf_annot_type(ctx, annot);
- switch (type)
+ fz_rect rect, bbox;
+ fz_matrix matrix = fz_identity;
+ fz_buffer *buf = fz_new_buffer(ctx, 1024);
+ pdf_obj *res = NULL;
+ fz_var(res);
+ fz_try(ctx)
{
- case PDF_ANNOT_WIDGET:
- switch (pdf_field_type(ctx, doc, obj))
+ pdf_to_rect(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(Rect)), &rect);
+ pdf_write_appearance(ctx, annot, buf, &rect, &bbox, &matrix, &res);
+ pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(Rect), &rect);
+
+ if (!ap_n)
{
- case PDF_WIDGET_TYPE_TEXT:
+ if (!ap)
{
- #if 0
- pdf_obj *formatting = pdf_dict_getl(ctx, obj, PDF_NAME(AA), PDF_NAME(F), NULL);
- if (formatting && doc->js)
- {
- /* Apply formatting */
- pdf_js_event e;
- e.target = obj;
- e.value = pdf_field_value(ctx, doc, obj);
- fz_try(ctx)
- {
- pdf_js_setup_event(doc->js, &e);
- }
- fz_always(ctx)
- {
- fz_free(ctx, e.value);
- }
- fz_catch(ctx)
- {
- fz_rethrow(ctx);
- }
- execute_action(ctx, doc, obj, formatting);
- /* Update appearance from JS event.value */
- pdf_update_text_appearance(ctx, doc, obj, pdf_js_get_event(doc->js)->value);
- }
- else
- #endif
- {
- /* Update appearance from field value */
- pdf_update_text_appearance(ctx, doc, obj, NULL);
- }
+ ap = pdf_new_dict(ctx, annot->page->doc, 1);
+ pdf_dict_put_drop(ctx, annot->obj, PDF_NAME(AP), ap);
}
- break;
- case PDF_WIDGET_TYPE_PUSHBUTTON:
- pdf_update_pushbutton_appearance(ctx, doc, obj);
- break;
- case PDF_WIDGET_TYPE_LISTBOX:
- pdf_update_listbox_appearance(ctx, doc, obj);
- break;
- case PDF_WIDGET_TYPE_COMBOBOX:
- pdf_update_combobox_appearance(ctx, doc, obj);
- break;
+ ap_n = pdf_new_xobject(ctx, annot->page->doc, &bbox, &matrix, res, buf);
+ pdf_dict_put_drop(ctx, ap, PDF_NAME(N), ap_n);
}
- annot->has_new_ap = 1;
- break;
- case PDF_ANNOT_TEXT:
- pdf_update_text_annot_appearance(ctx, doc, annot);
- break;
- case PDF_ANNOT_FREE_TEXT:
- pdf_update_free_text_annot_appearance(ctx, doc, annot);
- break;
- case PDF_ANNOT_STRIKE_OUT:
- case PDF_ANNOT_UNDERLINE:
- case PDF_ANNOT_HIGHLIGHT:
- pdf_update_text_markup_appearance(ctx, doc, annot, type);
- break;
- case PDF_ANNOT_INK:
- pdf_update_ink_appearance(ctx, doc, annot);
- break;
- default:
- break;
+ else
+ {
+ pdf_update_xobject(ctx, annot->page->doc, ap_n, &bbox, &matrix, res, buf);
+ }
+
+ pdf_drop_obj(ctx, annot->ap);
+ annot->ap = NULL;
+ annot->ap = pdf_keep_obj(ctx, ap_n);
}
+ fz_always(ctx)
+ {
+ fz_drop_buffer(ctx, buf);
+ pdf_drop_obj(ctx, res);
+ }
+ fz_catch(ctx)
+ fz_warn(ctx, "cannot create appearance stream");
- pdf_clean_obj(ctx, obj);
+ annot->needs_new_ap = 0;
+ annot->has_new_ap = 1;
}
}
@@ -2539,9 +1063,15 @@ pdf_update_annot(fz_context *ctx, pdf_annot *annot)
pdf_document *doc = annot->page->doc;
pdf_obj *obj, *ap, *as, *n;
- pdf_update_appearance(ctx, annot);
-
+ /* TODO: handle form field updates without using the annot pdf_obj dirty flag */
obj = annot->obj;
+ if (pdf_obj_is_dirty(ctx, obj))
+ {
+ pdf_clean_obj(ctx, obj);
+ annot->needs_new_ap = 1;
+ }
+
+ pdf_update_appearance(ctx, annot);
ap = pdf_dict_get(ctx, obj, PDF_NAME(AP));
as = pdf_dict_get(ctx, obj, PDF_NAME(AS));