eBookReaderSwitch/source/pdf/pdf-appearance.c

2097 lines
57 KiB
C
Raw Normal View History

#include "mupdf/fitz.h"
#include "mupdf/pdf.h"
#include <float.h>
#include <limits.h>
#include <math.h>
#include <string.h>
#include <stdio.h>
#include "annotation-icons.h"
#define REPLACEMENT 0xB7
#define CIRCLE_MAGIC 0.551915f
static float pdf_write_border_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf)
{
float w = pdf_annot_border(ctx, annot);
fz_append_printf(ctx, buf, "%g w\n", w);
return w;
}
static int pdf_write_stroke_color_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf)
{
float color[4];
int n;
pdf_annot_color(ctx, annot, &n, color);
switch (n)
{
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 int pdf_is_dark_fill_color(fz_context *ctx, pdf_annot *annot)
{
float color[4], gray;
int n;
pdf_annot_color(ctx, annot, &n, color);
switch (n)
{
default:
gray = 1;
break;
case 1:
gray = color[0];
break;
case 3:
gray = color[0] * 0.3f + color[1] * 0.59f + color[2] * 0.11f;
break;
case 4:
gray = color[0] * 0.3f + color[1] * 0.59f + color[2] * 0.11f + color[3];
gray = 1 - fz_min(gray, 1);
break;
}
return gray < 0.25f;
}
static int pdf_write_fill_color_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf)
{
float color[4];
int n;
pdf_annot_color(ctx, annot, &n, color);
switch (n)
{
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 int pdf_write_interior_fill_color_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf)
{
float color[4];
int n;
pdf_annot_interior_color(ctx, annot, &n, color);
switch (n)
{
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 int pdf_write_MK_BG_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf)
{
float color[4];
int n;
pdf_annot_MK_BG(ctx, annot, &n, color);
switch (n)
{
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 int pdf_write_MK_BC_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf)
{
float color[4];
int n;
pdf_annot_MK_BC(ctx, annot, &n, color);
switch (n)
{
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 maybe_stroke_and_fill(fz_context *ctx, fz_buffer *buf, int sc, int ic)
{
if (sc)
fz_append_string(ctx, buf, ic ? "b\n" : "s\n");
else
fz_append_string(ctx, buf, ic ? "f\n" : "n\n");
}
static void maybe_stroke(fz_context *ctx, fz_buffer *buf, int sc)
{
fz_append_string(ctx, buf, sc ? "S\n" : "n\n");
}
static fz_point rotate_vector(float angle, float x, float y)
{
float ca = cosf(angle);
float sa = sinf(angle);
return fz_make_point(x*ca - y*sa, x*sa + y*ca);
}
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)
{
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);
*rect = fz_include_point_in_rect(*rect, a);
*rect = fz_include_point_in_rect(*rect, b);
*rect = 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 void include_cap(fz_rect *rect, float x, float y, float r)
{
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
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 sc, int ic, pdf_obj *cap)
{
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);
maybe_stroke_and_fill(ctx, buf, sc, ic);
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);
maybe_stroke_and_fill(ctx, buf, sc, ic);
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);
maybe_stroke_and_fill(ctx, buf, sc, ic);
include_cap(rect, x, y, r);
}
else if (cap == PDF_NAME(OpenArrow))
{
pdf_write_arrow_appearance(ctx, buf, rect, x, y, dx, dy, w);
maybe_stroke(ctx, buf, sc);
}
else if (cap == PDF_NAME(ClosedArrow))
{
pdf_write_arrow_appearance(ctx, buf, rect, x, y, dx, dy, w);
maybe_stroke_and_fill(ctx, buf, sc, ic);
}
/* 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);
maybe_stroke(ctx, buf, sc);
*rect = fz_include_point_in_rect(*rect, a);
*rect = fz_include_point_in_rect(*rect, b);
}
else if (cap == PDF_NAME(ROpenArrow))
{
pdf_write_arrow_appearance(ctx, buf, rect, x, y, -dx, -dy, w);
maybe_stroke(ctx, buf, sc);
}
else if (cap == PDF_NAME(RClosedArrow))
{
pdf_write_arrow_appearance(ctx, buf, rect, x, y, -dx, -dy, w);
maybe_stroke_and_fill(ctx, buf, sc, ic);
}
/* PDF 1.6 */
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);
maybe_stroke(ctx, buf, sc);
*rect = fz_include_point_in_rect(*rect, a);
*rect = fz_include_point_in_rect(*rect, b);
}
}
static void
pdf_write_line_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect)
{
pdf_obj *line, *le;
fz_point a, b;
float w;
int sc;
int ic;
w = pdf_write_border_appearance(ctx, annot, buf);
sc = 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\n", a.x, a.y, b.x, b.y);
maybe_stroke(ctx, buf, sc);
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, sc, ic, pdf_array_get(ctx, le, 0));
pdf_write_line_cap_appearance(ctx, buf, rect, b.x, b.y, -dx/l, -dy/l, w, sc, ic, pdf_array_get(ctx, le, 1));
}
*rect = fz_expand_rect(*rect, fz_max(1, w));
}
static void
pdf_write_square_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect)
{
float x, y, w, h;
float lw;
int sc;
int ic;
lw = pdf_write_border_appearance(ctx, annot, buf);
sc = pdf_write_stroke_color_appearance(ctx, annot, buf);
ic = pdf_write_interior_fill_color_appearance(ctx, annot, buf);
x = rect->x0 + lw;
y = rect->y0 + lw;
w = rect->x1 - x - lw;
h = rect->y1 - y - lw;
fz_append_printf(ctx, buf, "%g %g %g %g re\n", x, y, w, h);
maybe_stroke_and_fill(ctx, buf, sc, ic);
}
static void
draw_circle(fz_context *ctx, fz_buffer *buf, float rx, float ry, float cx, float cy)
{
float mx = rx * CIRCLE_MAGIC;
float 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);
}
static void
draw_circle_in_box(fz_context *ctx, fz_buffer *buf, float lw, float x0, float y0, float x1, float y1)
{
float rx = (x1 - x0) / 2 - lw;
float ry = (y1 - y0) / 2 - lw;
float cx = x0 + lw + rx;
float cy = y0 + lw + ry;
draw_circle(ctx, buf, rx, ry, cx, cy);
}
static void
pdf_write_circle_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect)
{
float lw;
int sc;
int ic;
lw = pdf_write_border_appearance(ctx, annot, buf);
sc = pdf_write_stroke_color_appearance(ctx, annot, buf);
ic = pdf_write_interior_fill_color_appearance(ctx, annot, buf);
draw_circle_in_box(ctx, buf, lw, rect->x0, rect->y0, rect->x1, rect->y1);
maybe_stroke_and_fill(ctx, buf, sc, ic);
}
static void
pdf_write_polygon_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, int close)
{
pdf_obj *verts;
fz_point p;
int i, n;
float lw;
int sc;
lw = pdf_write_border_appearance(ctx, annot, buf);
sc = pdf_write_stroke_color_appearance(ctx, annot, buf);
*rect = fz_empty_rect;
verts = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices));
n = pdf_array_len(ctx, verts) / 2;
if (n > 0)
{
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
*rect = 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);
}
if (close)
fz_append_string(ctx, buf, "h\n");
maybe_stroke(ctx, buf, sc);
*rect = fz_expand_rect(*rect, lw);
}
}
static void
pdf_write_ink_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect)
{
pdf_obj *ink_list, *stroke;
int i, n, k, m;
float lw;
fz_point p;
int sc;
lw = pdf_write_border_appearance(ctx, annot, buf);
sc = pdf_write_stroke_color_appearance(ctx, annot, buf);
*rect = fz_empty_rect;
fz_append_printf(ctx, buf, "1 J\n1 j\n");
ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList));
n = pdf_array_len(ctx, ink_list);
for (i = 0; i < n; ++i)
{
stroke = pdf_array_get(ctx, ink_list, i);
m = pdf_array_len(ctx, stroke) / 2;
for (k = 0; k < m; ++k)
{
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
*rect = fz_include_point_in_rect(*rect, p);
fz_append_printf(ctx, buf, "%g %g %c\n", p.x, p.y, k == 0 ? 'm' : 'l');
}
if (m == 1)
fz_append_printf(ctx, buf, "%g %g %c\n", p.x, p.y, 'l');
}
maybe_stroke(ctx, buf, sc);
*rect = fz_expand_rect(*rect, lw);
}
/* Contrary to the specification, the points within a QuadPoint are NOT
* ordered in a counter-clockwise fashion starting with the lower left.
* Experiments with Adobe's implementation indicates a cross-wise
* ordering is intended: ul, ur, ll, lr.
*/
enum { UL, UR, LL, LR };
static float
extract_quad(fz_context *ctx, fz_point *quad, pdf_obj *obj, int i)
{
float dx, dy;
quad[0].x = pdf_array_get_real(ctx, obj, i+0);
quad[0].y = pdf_array_get_real(ctx, obj, i+1);
quad[1].x = pdf_array_get_real(ctx, obj, i+2);
quad[1].y = pdf_array_get_real(ctx, obj, i+3);
quad[2].x = pdf_array_get_real(ctx, obj, i+4);
quad[2].y = pdf_array_get_real(ctx, obj, i+5);
quad[3].x = pdf_array_get_real(ctx, obj, i+6);
quad[3].y = pdf_array_get_real(ctx, obj, i+7);
dx = quad[UL].x - quad[LL].x;
dy = quad[UL].y - quad[LL].y;
return sqrtf(dx * dx + dy * dy);
}
static void
union_quad(fz_rect *rect, const fz_point quad[4], float lw)
{
fz_rect qbox;
qbox.x0 = fz_min(fz_min(quad[0].x, quad[1].x), fz_min(quad[2].x, quad[3].x));
qbox.y0 = fz_min(fz_min(quad[0].y, quad[1].y), fz_min(quad[2].y, quad[3].y));
qbox.x1 = fz_max(fz_max(quad[0].x, quad[1].x), fz_max(quad[2].x, quad[3].x));
qbox.y1 = fz_max(fz_max(quad[0].y, quad[1].y), fz_max(quad[2].y, quad[3].y));
*rect = fz_union_rect(*rect, fz_expand_rect(qbox, lw));
}
static fz_point
lerp_point(fz_point a, fz_point b, float t)
{
return fz_make_point(a.x + t * (b.x - a.x), a.y + t * (b.y - a.y));
}
static void
pdf_write_highlight_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res)
{
pdf_obj *res_egs, *res_egs_h;
pdf_obj *qp;
fz_point quad[4], mquad[4], v;
float opacity, h, m, dx, dy, vn;
int i, n;
*rect = fz_empty_rect;
/* /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);
pdf_write_fill_color_appearance(ctx, annot, buf);
fz_append_printf(ctx, buf, "/H gs\n");
qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
n = pdf_array_len(ctx, qp);
if (n > 0)
{
for (i = 0; i < n; i += 8)
{
h = extract_quad(ctx, quad, qp, i);
m = h / 4.2425f; /* magic number that matches adobe's appearance */
dx = quad[LR].x - quad[LL].x;
dy = quad[LR].y - quad[LL].y;
vn = sqrtf(dx * dx + dy * dy);
v = fz_make_point(dx * m / vn, dy * m / vn);
mquad[LL].x = quad[LL].x - v.x - v.y;
mquad[LL].y = quad[LL].y - v.y + v.x;
mquad[UL].x = quad[UL].x - v.x + v.y;
mquad[UL].y = quad[UL].y - v.y - v.x;
mquad[LR].x = quad[LR].x + v.x - v.y;
mquad[LR].y = quad[LR].y + v.y + v.x;
mquad[UR].x = quad[UR].x + v.x + v.y;
mquad[UR].y = quad[UR].y + v.y - v.x;
fz_append_printf(ctx, buf, "%g %g m\n", quad[LL].x, quad[LL].y);
fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n",
mquad[LL].x, mquad[LL].y,
mquad[UL].x, mquad[UL].y,
quad[UL].x, quad[UL].y);
fz_append_printf(ctx, buf, "%g %g l\n", quad[UR].x, quad[UR].y);
fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n",
mquad[UR].x, mquad[UR].y,
mquad[LR].x, mquad[LR].y,
quad[LR].x, quad[LR].y);
fz_append_printf(ctx, buf, "f\n");
union_quad(rect, quad, h/16);
union_quad(rect, mquad, 0);
}
}
}
static void
pdf_write_underline_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect)
{
fz_point quad[4], a, b;
float h;
pdf_obj *qp;
int i, n;
*rect = fz_empty_rect;
pdf_write_stroke_color_appearance(ctx, annot, buf);
qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
n = pdf_array_len(ctx, qp);
if (n > 0)
{
for (i = 0; i < n; i += 8)
{
/* 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. */
h = extract_quad(ctx, quad, qp, i);
a = lerp_point(quad[LL], quad[UL], 1/7.0f);
b = lerp_point(quad[LR], quad[UR], 1/7.0f);
fz_append_printf(ctx, buf, "%g w\n", h/16);
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_printf(ctx, buf, "S\n");
union_quad(rect, quad, h/16);
}
}
}
static void
pdf_write_strike_out_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect)
{
fz_point quad[4], a, b;
float h;
pdf_obj *qp;
int i, n;
pdf_write_stroke_color_appearance(ctx, annot, buf);
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)
{
/* 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. */
h = extract_quad(ctx, quad, qp, i);
a = lerp_point(quad[LL], quad[UL], 3/7.0f);
b = lerp_point(quad[LR], quad[UR], 3/7.0f);
fz_append_printf(ctx, buf, "%g w\n", h/16);
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_printf(ctx, buf, "S\n");
union_quad(rect, quad, h/16);
}
}
}
static void
pdf_write_squiggly_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect)
{
fz_point quad[4], a, b, c, v;
float h, x, w;
pdf_obj *qp;
int i, n;
*rect = fz_empty_rect;
pdf_write_stroke_color_appearance(ctx, annot, buf);
qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
n = pdf_array_len(ctx, qp);
if (n > 0)
{
for (i = 0; i < n; i += 8)
{
int up = 1;
h = extract_quad(ctx, quad, qp, i);
v = fz_make_point(quad[LR].x - quad[LL].x, quad[LR].y - quad[LL].y);
w = sqrtf(v.x * v.x + v.y * v.y);
x = 0;
fz_append_printf(ctx, buf, "%g w\n", h/16);
fz_append_printf(ctx, buf, "%g %g m\n", quad[LL].x, quad[LL].y);
while (x < w)
{
x += h/7;
a = lerp_point(quad[LL], quad[LR], x/w);
if (up)
{
b = lerp_point(quad[UL], quad[UR], x/w);
c = lerp_point(a, b, 1/7.0f);
fz_append_printf(ctx, buf, "%g %g l\n", c.x, c.y);
}
else
fz_append_printf(ctx, buf, "%g %g l\n", a.x, a.y);
up = !up;
}
fz_append_printf(ctx, buf, "S\n");
union_quad(rect, quad, h/16);
}
}
}
static void
pdf_write_redact_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect)
{
fz_point quad[4];
pdf_obj *qp;
int i, n;
fz_append_printf(ctx, buf, "1 0 0 RG\n");
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)
{
extract_quad(ctx, quad, qp, i);
fz_append_printf(ctx, buf, "%g %g m\n", quad[LL].x, quad[LL].y);
fz_append_printf(ctx, buf, "%g %g l\n", quad[LR].x, quad[LR].y);
fz_append_printf(ctx, buf, "%g %g l\n", quad[UR].x, quad[UR].y);
fz_append_printf(ctx, buf, "%g %g l\n", quad[UL].x, quad[UL].y);
fz_append_printf(ctx, buf, "s\n");
union_quad(rect, quad, 1);
}
}
else
{
fz_append_printf(ctx, buf, "%g %g m\n", rect->x0+1, rect->y0+1);
fz_append_printf(ctx, buf, "%g %g l\n", rect->x1-1, rect->y0+1);
fz_append_printf(ctx, buf, "%g %g l\n", rect->x1-1, rect->y1-1);
fz_append_printf(ctx, buf, "%g %g l\n", rect->x0+1, rect->y1-1);
fz_append_printf(ctx, buf, "s\n");
}
}
static void
pdf_write_caret_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, fz_rect *bbox)
{
float xc = (rect->x0 + rect->x1) / 2;
float yc = (rect->y0 + rect->y1) / 2;
pdf_write_fill_color_appearance(ctx, annot, buf);
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\n");
*rect = fz_make_rect(xc - 10, yc - 7, xc + 10, yc + 7);
*bbox = fz_make_rect(0, 0, 20, 14);
}
static void
pdf_write_icon_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, fz_rect *bbox)
{
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\nb\n");
fz_append_string(ctx, buf, "1 0 0 -1 4 12 cm\n");
if (pdf_is_dark_fill_color(ctx, annot))
fz_append_string(ctx, buf, "1 g\n");
else
fz_append_string(ctx, buf, "0 g\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
fz_append_string(ctx, buf, icon_star);
*rect = fz_make_rect(xc - 9, yc - 9, xc + 9, yc + 9);
*bbox = fz_make_rect(0, 0, 16, 16);
}
static float
measure_simple_string(fz_context *ctx, fz_font *font, const char *text)
{
float w = 0;
while (*text)
{
int c, g;
text += fz_chartorune(&c, text);
c = fz_windows_1252_from_unicode(c);
if (c < 0) c = REPLACEMENT;
g = fz_encode_character(ctx, font, c);
w += fz_advance_glyph(ctx, font, g, 0);
}
return w;
}
static void
write_simple_string(fz_context *ctx, fz_buffer *buf, const char *a, const char *b)
{
fz_append_byte(ctx, buf, '(');
while (a < b)
{
int c;
a += fz_chartorune(&c, a);
c = fz_windows_1252_from_unicode(c);
if (c < 0) c = REPLACEMENT;
if (c == '(' || c == ')' || c == '\\')
fz_append_byte(ctx, buf, '\\');
fz_append_byte(ctx, buf, c);
}
fz_append_byte(ctx, buf, ')');
}
static void
write_stamp_string(fz_context *ctx, fz_buffer *buf, fz_font *font, const char *text)
{
write_simple_string(ctx, buf, text, text+strlen(text));
}
static void
write_stamp(fz_context *ctx, fz_buffer *buf, fz_font *font, const char *text, float y, float h)
{
float tw = measure_simple_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");
}
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_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)
{
/* /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);
rotate = fz_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))
{
write_stamp(ctx, buf, font, "FOR PUBLIC", 26, 18);
write_stamp(ctx, buf, font, "RELEASE", 8.5f, 18);
}
else if (name == PDF_NAME(NotApproved))
write_stamp(ctx, buf, font, "NOT APPROVED", 17, 20);
else if (name == PDF_NAME(NotForPublicRelease))
{
write_stamp(ctx, buf, font, "NOT FOR", 26, 18);
write_stamp(ctx, buf, font, "PUBLIC RELEASE", 8.5, 18);
}
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)
fz_drop_font(ctx, font);
fz_catch(ctx)
fz_rethrow(ctx);
*bbox = fz_make_rect(0, 0, 190, 50);
if (xs > ys)
{
float xc = (rect->x1+rect->x0) / 2;
rect->x0 = xc - 95 * ys;
rect->x1 = xc + 95 * ys;
}
else
{
float yc = (rect->y1+rect->y0) / 2;
rect->y0 = yc - 25 * xs;
rect->y1 = yc + 25 * xs;
}
}
static float
break_simple_string(fz_context *ctx, fz_font *font, float size, const char *a, const char **endp, float maxw)
{
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 == ' ')
{
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;
}
return *endp = a, x;
}
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)
{
const char *b;
float px = 0, x = 0, w;
while (*a)
{
w = break_simple_string(ctx, font, size, a, &b, maxw);
if (b > a)
{
if (q > 0)
{
if (q == 1)
x = (maxw - w) / 2;
else
x = (maxw - w);
fz_append_printf(ctx, buf, "%g %g Td ", x - px, -size);
}
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");
}
}
}
static void
write_comb_string(fz_context *ctx, fz_buffer *buf, const char *a, const char *b, fz_font *font, float cell_w)
{
float gw, pad, carry = 0;
fz_append_byte(ctx, buf, '[');
while (a < b)
{
int c, g;
a += fz_chartorune(&c, a);
c = fz_windows_1252_from_unicode(c);
if (c < 0) c = REPLACEMENT;
g = fz_encode_character(ctx, font, c);
gw = fz_advance_glyph(ctx, font, g, 0) * 1000;
pad = (cell_w - gw) / 2;
fz_append_printf(ctx, buf, "%g", -(carry + pad));
carry = pad;
fz_append_byte(ctx, buf, '(');
if (c == '(' || c == ')' || c == '\\')
fz_append_byte(ctx, buf, '\\');
fz_append_byte(ctx, buf, c);
fz_append_byte(ctx, buf, ')');
}
fz_append_string(ctx, buf, "] TJ\n");
}
static void
layout_comb_string(fz_context *ctx, fz_layout_block *out, float x, float y,
const char *a, const char *b, fz_font *font, float size, float cell_w)
{
int n, c, g;
int first = 1;
float w;
if (a == b)
fz_add_layout_line(ctx, out, x + cell_w / 2, y, size, a);
while (a < b)
{
n = fz_chartorune(&c, a);
c = fz_windows_1252_from_unicode(c);
if (c < 0) c = REPLACEMENT;
g = fz_encode_character(ctx, font, c);
w = fz_advance_glyph(ctx, font, g, 0) * size;
if (first)
{
fz_add_layout_line(ctx, out, x + (cell_w - w) / 2, y, size, a);
first = 0;
}
fz_add_layout_char(ctx, out, x + (cell_w - w) / 2, w, a);
a += n;
x += cell_w;
}
}
static void
layout_simple_string(fz_context *ctx, fz_layout_block *out, fz_font *font, float size,
float x, float y, const char *a, const char *b)
{
float w;
int n, c, g;
fz_add_layout_line(ctx, out, x, y, size, a);
while (a < b)
{
n = fz_chartorune(&c, a);
c = fz_windows_1252_from_unicode(c);
if (c < 0) c = REPLACEMENT;
g = fz_encode_character(ctx, font, c);
w = fz_advance_glyph(ctx, font, g, 0) * size;
fz_add_layout_char(ctx, out, x, w, a);
a += n;
x += w;
}
}
static void
layout_simple_string_with_quadding(fz_context *ctx, fz_layout_block *out,
fz_font *font, float size, float lineheight,
float xorig, float y, const char *a, float maxw, int q)
{
const char *b;
float x = 0, w;
int add_line_at_end = 0;
if (!*a)
add_line_at_end = 1;
while (*a)
{
w = break_simple_string(ctx, font, size, a, &b, maxw);
if (b > a)
{
if (q > 0)
{
if (q == 1)
x = (maxw - w) / 2;
else
x = (maxw - w);
}
if (b[-1] == '\n' || b[-1] == '\r')
{
layout_simple_string(ctx, out, font, size, xorig+x, y, a, b-1);
add_line_at_end = 1;
}
else
{
layout_simple_string(ctx, out, font, size, xorig+x, y, a, b);
add_line_at_end = 0;
}
a = b;
y -= lineheight;
}
}
if (add_line_at_end)
fz_add_layout_line(ctx, out, xorig, y, size, a);
}
static const char *full_font_name(const char **name)
{
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";
}
static void
write_variable_text(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, pdf_obj **res,
const char *text, const char *fontname, float size, float color[3], int q,
float w, float h, float padding, float baseline, float lineheight,
int multiline, int comb, int adjust_baseline)
{
pdf_obj *res_font;
fz_font *font;
w -= padding * 2;
h -= padding * 2;
font = fz_new_base14_font(ctx, full_font_name(&fontname));
fz_try(ctx)
{
if (size == 0)
{
if (multiline)
size = 12;
else
{
size = w / measure_simple_string(ctx, font, text);
if (size > h)
size = h;
}
}
lineheight = size * lineheight;
baseline = size * baseline;
if (adjust_baseline)
{
/* Make sure baseline is inside rectangle */
if (baseline + 0.2f * size > h)
baseline = h - 0.2f * size;
}
/* /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));
fz_append_string(ctx, buf, "BT\n");
fz_append_printf(ctx, buf, "%g %g %g rg\n", color[0], color[1], color[2]);
fz_append_printf(ctx, buf, "/%s %g Tf\n", fontname, size);
if (multiline)
{
fz_append_printf(ctx, buf, "%g TL\n", lineheight);
fz_append_printf(ctx, buf, "%g %g Td\n", padding, padding+h-baseline+lineheight);
write_simple_string_with_quadding(ctx, buf, font, size, text, w, q);
}
else if (comb > 0)
{
float ty = (h - size) / 2;
fz_append_printf(ctx, buf, "%g %g Td\n", padding, padding+h-baseline-ty);
write_comb_string(ctx, buf, text, text + strlen(text), font, (w * 1000 / size) / comb);
}
else
{
float tx = 0, ty = (h - size) / 2;
if (q > 0)
{
float tw = measure_simple_string(ctx, font, text) * size;
if (q == 1)
tx = (w - tw) / 2;
else
tx = (w - tw);
}
fz_append_printf(ctx, buf, "%g %g Td\n", padding+tx, padding+h-baseline-ty);
write_simple_string(ctx, buf, text, text + strlen(text));
fz_append_printf(ctx, buf, " Tj\n");
}
fz_append_string(ctx, buf, "ET\n");
}
fz_always(ctx)
fz_drop_font(ctx, font);
fz_catch(ctx)
fz_rethrow(ctx);
}
static void
layout_variable_text(fz_context *ctx, fz_layout_block *out,
const char *text, const char *fontname, float size, int q,
float x, float y, float w, float h, float padding, float baseline, float lineheight,
int multiline, int comb, int adjust_baseline)
{
fz_font *font;
w -= padding * 2;
h -= padding * 2;
font = fz_new_base14_font(ctx, full_font_name(&fontname));
fz_try(ctx)
{
if (size == 0)
{
if (multiline)
size = 12;
else
{
size = w / measure_simple_string(ctx, font, text);
if (size > h)
size = h;
}
}
lineheight = size * lineheight;
baseline = size * baseline;
if (adjust_baseline)
{
/* Make sure baseline is inside rectangle */
if (baseline + 0.2f * size > h)
baseline = h - 0.2f * size;
}
if (multiline)
{
x += padding;
y += padding + h - baseline;
layout_simple_string_with_quadding(ctx, out, font, size, lineheight, x, y, text, w, q);
}
else if (comb > 0)
{
float ty = (h - size) / 2;
x += padding;
y += padding + h - baseline - ty;
layout_comb_string(ctx, out, x, y, text, text + strlen(text), font, size, w / comb);
}
else
{
float tx = 0, ty = (h - size) / 2;
if (q > 0)
{
float tw = measure_simple_string(ctx, font, text) * size;
if (q == 1)
tx = (w - tw) / 2;
else
tx = (w - tw);
}
x += padding + tx;
y += padding + h - baseline - ty;
layout_simple_string(ctx, out, font, size, x, y, text, text + strlen(text));
}
}
fz_always(ctx)
fz_drop_font(ctx, font);
fz_catch(ctx)
fz_rethrow(ctx);
}
static void
pdf_write_free_text_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf,
fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res)
{
const char *font;
float size, color[3];
const char *text;
float w, h, t, b;
int q, r;
/* /Rotate is an undocumented annotation property supported by Adobe */
text = pdf_annot_contents(ctx, annot);
r = pdf_dict_get_int(ctx, annot->obj, PDF_NAME(Rotate));
q = pdf_annot_quadding(ctx, annot);
pdf_annot_default_appearance(ctx, annot, &font, &size, color);
w = rect->x1 - rect->x0;
h = rect->y1 - rect->y0;
if (r == 90 || r == 270)
t = h, h = w, w = t;
*matrix = fz_rotate(r);
*bbox = fz_make_rect(0, 0, w, h);
if (pdf_write_fill_color_appearance(ctx, annot, buf))
fz_append_printf(ctx, buf, "0 0 %g %g re\nf\n", w, h);
b = pdf_write_border_appearance(ctx, annot, buf);
if (b > 0)
{
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", b/2, b/2, w-b, h-b);
}
fz_append_printf(ctx, buf, "%g %g %g %g re\nW\nn\n", b, b, w-b*2, h-b*2);
write_variable_text(ctx, annot, buf, res, text, font, size, color, q, w, h, b*2,
0.8f, 1.2f, 1, 0, 0);
}
static void
pdf_write_tx_widget_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf,
fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res,
const char *text, int ff)
{
const char *font;
float size, color[3];
float w, h, t, b;
int has_bc = 0;
int q, r;
r = pdf_dict_get_int(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(R));
q = pdf_annot_quadding(ctx, annot);
pdf_annot_default_appearance(ctx, annot, &font, &size, color);
w = rect->x1 - rect->x0;
h = rect->y1 - rect->y0;
r = r % 360;
if (r == 90 || r == 270)
t = h, h = w, w = t;
*matrix = fz_rotate(r);
*bbox = fz_make_rect(0, 0, w, h);
fz_append_string(ctx, buf, "/Tx BMC\nq\n");
if (pdf_write_MK_BG_appearance(ctx, annot, buf))
fz_append_printf(ctx, buf, "0 0 %g %g re\nf\n", w, h);
b = pdf_write_border_appearance(ctx, annot, buf);
if (b > 0 && pdf_write_MK_BC_appearance(ctx, annot, buf))
{
fz_append_printf(ctx, buf, "%g %g %g %g re\ns\n", b/2, b/2, w-b, h-b);
has_bc = 1;
}
fz_append_printf(ctx, buf, "%g %g %g %g re\nW\nn\n", b, b, w-b*2, h-b*2);
if (ff & PDF_TX_FIELD_IS_MULTILINE)
{
write_variable_text(ctx, annot, buf, res, text, font, size, color, q, w, h, b*2,
1.116f, 1.116f, 1, 0, 1);
}
else if (ff & PDF_TX_FIELD_IS_COMB)
{
int maxlen = pdf_to_int(ctx, pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(MaxLen)));
if (has_bc && maxlen > 1)
{
float cell_w = (w - 2 * b) / maxlen;
int i;
for (i = 1; i < maxlen; ++i)
{
float x = b + cell_w * i;
fz_append_printf(ctx, buf, "%g %g m %g %g l s\n", x, b, x, h-b);
}
}
write_variable_text(ctx, annot, buf, res, text, font, size, color, q, w, h, 0,
0.8f, 1.2f, 0, maxlen, 0);
}
else
{
write_variable_text(ctx, annot, buf, res, text, font, size, color, q, w, h, b*2,
0.8f, 1.2f, 0, 0, 0);
}
fz_append_string(ctx, buf, "Q\nEMC\n");
}
fz_layout_block *
pdf_layout_text_widget(fz_context *ctx, pdf_annot *annot)
{
fz_layout_block *out;
const char *font;
const char *text;
fz_rect rect;
float size, color[3];
float w, h, t, b, x, y;
int q, r;
int ff;
rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect));
text = pdf_field_value(ctx, annot->obj);
ff = pdf_field_flags(ctx, annot->obj);
b = pdf_annot_border(ctx, annot);
r = pdf_dict_get_int(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(R));
q = pdf_annot_quadding(ctx, annot);
pdf_annot_default_appearance(ctx, annot, &font, &size, color);
w = rect.x1 - rect.x0;
h = rect.y1 - rect.y0;
r = r % 360;
if (r == 90 || r == 270)
t = h, h = w, w = t;
x = rect.x0;
y = rect.y0;
out = fz_new_layout(ctx);
fz_try(ctx)
{
pdf_page_transform(ctx, annot->page, NULL, &out->matrix);
out->matrix = fz_concat(out->matrix, fz_rotate(r));
out->inv_matrix = fz_invert_matrix(out->matrix);
if (ff & PDF_TX_FIELD_IS_MULTILINE)
{
layout_variable_text(ctx, out, text, font, size, q, x, y, w, h, b*2, 1.116f, 1.116f, 1, 0, 1);
}
else if (ff & PDF_TX_FIELD_IS_COMB)
{
int maxlen = pdf_to_int(ctx, pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(MaxLen)));
layout_variable_text(ctx, out, text, font, size, q, x, y, w, h, 0, 0.8f, 1.2f, 0, maxlen, 0);
}
else
{
layout_variable_text(ctx, out, text, font, size, q, x, y, w, h, b*2, 0.8f, 1.2f, 0, 0, 0);
}
}
fz_catch(ctx)
{
fz_drop_layout(ctx, out);
fz_rethrow(ctx);
}
return out;
}
static void
pdf_write_ch_widget_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf,
fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res)
{
int ff = pdf_field_flags(ctx, annot->obj);
if (ff & PDF_CH_FIELD_IS_COMBO)
{
/* TODO: Pop-down arrow */
pdf_write_tx_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res,
pdf_field_value(ctx, annot->obj), 0);
}
else
{
fz_buffer *text = fz_new_buffer(ctx, 1024);
fz_try(ctx)
{
pdf_obj *opt = pdf_dict_get(ctx, annot->obj, PDF_NAME(Opt));
int i = pdf_dict_get_int(ctx, annot->obj, PDF_NAME(TI));
int n = pdf_array_len(ctx, opt);
/* TODO: Scrollbar */
/* TODO: Highlight selected items */
if (i < 0)
i = 0;
for (; i < n; ++i)
{
pdf_obj *val = pdf_array_get(ctx, opt, i);
if (pdf_is_array(ctx, val))
fz_append_string(ctx, text, pdf_array_get_text_string(ctx, val, 1));
else
fz_append_string(ctx, text, pdf_to_text_string(ctx, val));
fz_append_byte(ctx, text, '\n');
}
pdf_write_tx_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res,
fz_string_from_buffer(ctx, text), PDF_TX_FIELD_IS_MULTILINE);
}
fz_always(ctx)
fz_drop_buffer(ctx, text);
fz_catch(ctx)
fz_rethrow(ctx);
}
}
static void
pdf_write_sig_widget_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf,
fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res)
{
float x0 = rect->x0 + 1;
float y0 = rect->y0 + 1;
float x1 = rect->x1 - 1;
float y1 = rect->y1 - 1;
float w = x1 - x0;
float h = y1 - y0;
fz_append_printf(ctx, buf, "1 w\n0 G\n");
fz_append_printf(ctx, buf, "%g %g %g %g re\n", x0, y0, w, h);
fz_append_printf(ctx, buf, "%g %g m %g %g l\n", x0, y0, x1, y1);
fz_append_printf(ctx, buf, "%g %g m %g %g l\n", x1, y0, x0, y1);
fz_append_printf(ctx, buf, "s\n");
*bbox = *rect;
*matrix = fz_identity;
}
static void
pdf_write_widget_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf,
fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res)
{
pdf_obj *ft = pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(FT));
if (pdf_name_eq(ctx, ft, PDF_NAME(Tx)))
{
int ff = pdf_field_flags(ctx, annot->obj);
char *format = NULL;
const char *text = NULL;
if (!annot->ignore_trigger_events)
{
format = pdf_field_event_format(ctx, annot->page->doc, annot->obj);
if (format)
text = format;
else
text = pdf_field_value(ctx, annot->obj);
}
else
{
text = pdf_field_value(ctx, annot->obj);
}
fz_try(ctx)
pdf_write_tx_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res, text, ff);
fz_always(ctx)
fz_free(ctx, format);
fz_catch(ctx)
fz_rethrow(ctx);
}
else if (pdf_name_eq(ctx, ft, PDF_NAME(Ch)))
{
pdf_write_ch_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res);
}
else if (pdf_name_eq(ctx, ft, PDF_NAME(Sig)))
{
pdf_write_sig_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res);
}
else
{
fz_throw(ctx, FZ_ERROR_GENERIC, "cannot create appearance stream for %s widgets", pdf_to_name(ctx, ft));
}
}
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)
{
switch (pdf_annot_type(ctx, annot))
{
default:
fz_throw(ctx, FZ_ERROR_GENERIC, "cannot create appearance stream for %s annotations",
pdf_dict_get_name(ctx, annot->obj, PDF_NAME(Subtype)));
case PDF_ANNOT_WIDGET:
pdf_write_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res);
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_REDACT:
pdf_write_redact_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, bbox, matrix, res);
break;
}
}
static pdf_obj *draw_push_button(fz_context *ctx, pdf_annot *annot, fz_rect bbox, fz_matrix matrix, float w, float h,
const char *caption, const char *font, float size, float color[3],
int down)
{
pdf_obj *ap, *res = NULL;
fz_buffer *buf;
float bc[3] = { 0, 0, 0 };
float bg[3] = { 0.8f, 0.8f, 0.8f };
float hi[3], sh[3];
int has_bg, has_bc;
float b;
int i;
buf = fz_new_buffer(ctx, 1024);
fz_var(res);
fz_try(ctx)
{
b = pdf_annot_border(ctx, annot);
has_bc = pdf_annot_MK_BC_rgb(ctx, annot, bc);
has_bg = pdf_annot_MK_BG_rgb(ctx, annot, bg);
for (i = 0; i < 3; ++i)
{
if (down)
{
sh[i] = 1 - (1 - bg[i]) / 2;
hi[i] = bg[i] / 2;
}
else
{
hi[i] = 1 - (1 - bg[i]) / 2;
sh[i] = bg[i] / 2;
}
}
fz_append_string(ctx, buf, "q\n");
fz_append_printf(ctx, buf, "%g w\n", b);
if (has_bg)
{
fz_append_printf(ctx, buf, "%g %g %g rg\n", bg[0], bg[1], bg[2]);
fz_append_printf(ctx, buf, "0 0 %g %g re\nf\n", 0, 0, w, h);
}
if (has_bc && b > 0)
{
fz_append_printf(ctx, buf, "%g %g %g RG\n", bc[0], bc[1], bc[2]);
fz_append_printf(ctx, buf, "%g %g %g %g re\nS\n", b/2, b/2, w-b, h-b);
}
if (has_bg)
{
fz_append_printf(ctx, buf, "%g %g %g rg\n", hi[0], hi[1], hi[2]);
fz_append_printf(ctx, buf, "%g %g m %g %g l %g %g l %g %g l %g %g l %g %g l f\n",
b, b, b, h-b, w-b, h-b, w-b-2, h-b-2, b+2, h-b-2, b+2, b+2);
fz_append_printf(ctx, buf, "%g %g %g rg\n", sh[0], sh[1], sh[2]);
fz_append_printf(ctx, buf, "%g %g m %g %g l %g %g l %g %g l %g %g l %g %g l f\n",
b, b, b+2, b+2, w-b-2, b+2, w-b-2, h-b-2, w-b, h-b, w-b, b);
}
if (down)
fz_append_string(ctx, buf, "1 0 0 1 2 -2 cm\n");
write_variable_text(ctx, annot, buf, &res, caption, font, size, color, 1, w, h, b+6, 0.8f, 1.2f, 0, 0, 0);
fz_append_string(ctx, buf, "Q\n");
ap = pdf_new_xobject(ctx, annot->page->doc, bbox, matrix, res, buf);
}
fz_always(ctx)
{
pdf_drop_obj(ctx, res);
fz_drop_buffer(ctx, buf);
}
fz_catch(ctx)
fz_rethrow(ctx);
return ap;
}
static pdf_obj *draw_radio_button(fz_context *ctx, pdf_annot *annot, fz_rect bbox, fz_matrix matrix, float w, float h, int yes)
{
pdf_obj *ap;
fz_buffer *buf;
float b;
buf = fz_new_buffer(ctx, 1024);
fz_try(ctx)
{
fz_append_string(ctx, buf, "q\n");
if (pdf_write_MK_BG_appearance(ctx, annot, buf))
{
draw_circle_in_box(ctx, buf, 0, 0, 0, w, h);
fz_append_string(ctx, buf, "f\n");
}
b = pdf_write_border_appearance(ctx, annot, buf);
if (b > 0 && pdf_write_MK_BC_appearance(ctx, annot, buf))
{
draw_circle_in_box(ctx, buf, b, 0, 0, w, h);
fz_append_string(ctx, buf, "s\n");
}
if (yes)
{
fz_append_string(ctx, buf, "0 g\n");
draw_circle(ctx, buf, (w-b*2)/4, (h-b*2)/4, w/2, h/2);
fz_append_string(ctx, buf, "f\n");
}
fz_append_string(ctx, buf, "Q\n");
ap = pdf_new_xobject(ctx, annot->page->doc, bbox, matrix, NULL, buf);
}
fz_always(ctx)
fz_drop_buffer(ctx, buf);
fz_catch(ctx)
fz_rethrow(ctx);
return ap;
}
static pdf_obj *draw_check_button(fz_context *ctx, pdf_annot *annot, fz_rect bbox, fz_matrix matrix, float w, float h, int yes)
{
float black[3] = { 0, 0, 0 };
pdf_obj *ap, *res = NULL;
fz_buffer *buf;
float b;
fz_var(res);
buf = fz_new_buffer(ctx, 1024);
fz_try(ctx)
{
fz_append_string(ctx, buf, "q\n");
if (pdf_write_MK_BG_appearance(ctx, annot, buf))
fz_append_printf(ctx, buf, "0 0 %g %g re\nf\n", w, h);
b = pdf_write_border_appearance(ctx, annot, buf);
if (b > 0 && pdf_write_MK_BC_appearance(ctx, annot, buf))
fz_append_printf(ctx, buf, "%g %g %g %g re\nS\n", b/2, b/2, w-b, h-b);
if (yes)
write_variable_text(ctx, annot, buf, &res, "3", "ZaDb", h, black, 0, w, h, b+h/10, 0.8f, 1.2f, 0, 0, 0);
fz_append_string(ctx, buf, "Q\n");
ap = pdf_new_xobject(ctx, annot->page->doc, bbox, matrix, res, buf);
}
fz_always(ctx)
{
pdf_drop_obj(ctx, res);
fz_drop_buffer(ctx, buf);
}
fz_catch(ctx)
fz_rethrow(ctx);
return ap;
}
static void pdf_update_button_appearance(fz_context *ctx, pdf_annot *annot)
{
int ff = pdf_field_flags(ctx, annot->obj);
fz_rect rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect));
fz_matrix matrix;
fz_rect bbox;
float w, h, t;
int r;
r = pdf_dict_get_int(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(R));
w = rect.x1 - rect.x0;
h = rect.y1 - rect.y0;
r = r % 360;
if (r == 90 || r == 270)
t = h, h = w, w = t;
matrix = fz_rotate(r);
bbox = fz_make_rect(0, 0, w, h);
if (ff & PDF_BTN_FIELD_IS_PUSHBUTTON)
{
pdf_obj *ap_n = NULL;
pdf_obj *ap_d = NULL;
fz_var(ap_n);
fz_var(ap_d);
fz_try(ctx)
{
pdf_obj *ap, *MK, *CA, *AC;
const char *font;
const char *label;
float size, color[3];
pdf_annot_default_appearance(ctx, annot, &font, &size, color);
MK = pdf_dict_get(ctx, annot->obj, PDF_NAME(MK));
CA = pdf_dict_get(ctx, MK, PDF_NAME(CA));
AC = pdf_dict_get(ctx, MK, PDF_NAME(AC));
label = pdf_to_text_string(ctx, CA);
ap_n = draw_push_button(ctx, annot, bbox, matrix, w, h, label, font, size, color, 0);
label = pdf_to_text_string(ctx, AC ? AC : CA);
ap_d = draw_push_button(ctx, annot, bbox, matrix, w, h, label, font, size, color, 1);
ap = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(AP), 2);
pdf_dict_put(ctx, ap, PDF_NAME(N), ap_n);
pdf_dict_put(ctx, ap, PDF_NAME(D), ap_d);
pdf_drop_obj(ctx, annot->ap);
if (annot->is_hot && annot->is_active)
annot->ap = pdf_keep_obj(ctx, ap_d);
else
annot->ap = pdf_keep_obj(ctx, ap_n);
annot->has_new_ap = 1;
}
fz_always(ctx)
{
pdf_drop_obj(ctx, ap_n);
pdf_drop_obj(ctx, ap_d);
}
fz_catch(ctx)
fz_rethrow(ctx);
}
else
{
pdf_obj *as_yes = NULL;
pdf_obj *ap_off = NULL;
pdf_obj *ap_yes = NULL;
fz_var(ap_off);
fz_var(ap_yes);
fz_var(as_yes);
fz_try(ctx)
{
pdf_obj *ap, *ap_n, *as;
if (w > h) w = h;
if (h > w) h = w;
if (ff & PDF_BTN_FIELD_IS_RADIO)
{
ap_off = draw_radio_button(ctx, annot, bbox, matrix, w, h, 0);
ap_yes = draw_radio_button(ctx, annot, bbox, matrix, w, h, 1);
}
else
{
ap_off = draw_check_button(ctx, annot, bbox, matrix, w, h, 0);
ap_yes = draw_check_button(ctx, annot, bbox, matrix, w, h, 1);
}
as = pdf_dict_get(ctx, annot->obj, PDF_NAME(AS));
if (!as)
{
pdf_dict_put(ctx, annot->obj, PDF_NAME(AS), PDF_NAME(Off));
as = PDF_NAME(Off);
}
if (as == PDF_NAME(Off))
as_yes = pdf_keep_obj(ctx, pdf_button_field_on_state(ctx, annot->obj));
else
as_yes = pdf_keep_obj(ctx, as);
ap = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(AP), 2);
ap_n = pdf_dict_put_dict(ctx, ap, PDF_NAME(N), 2);
pdf_dict_put(ctx, ap_n, PDF_NAME(Off), ap_off);
pdf_dict_put(ctx, ap_n, as_yes, ap_yes);
pdf_drop_obj(ctx, annot->ap);
if (as == PDF_NAME(Off))
annot->ap = pdf_keep_obj(ctx, ap_off);
else
annot->ap = pdf_keep_obj(ctx, ap_yes);
annot->has_new_ap = 1;
}
fz_always(ctx)
{
pdf_drop_obj(ctx, as_yes);
pdf_drop_obj(ctx, ap_yes);
pdf_drop_obj(ctx, ap_off);
}
fz_catch(ctx)
{
fz_rethrow(ctx);
}
}
}
void pdf_update_signature_appearance(fz_context *ctx, pdf_annot *annot, const char *name, const char *dn, const char *date)
{
pdf_obj *ap, *new_ap_n, *res_font;
char tmp[500];
fz_font *helv = NULL;
fz_font *zadb = NULL;
pdf_obj *res = NULL;
fz_buffer *buf;
fz_rect rect;
float w, h, size, name_w;
fz_var(helv);
fz_var(zadb);
fz_var(res);
buf = fz_new_buffer(ctx, 1024);
fz_try(ctx)
{
if (name && dn)
{
helv = fz_new_base14_font(ctx, "Helvetica");
zadb = fz_new_base14_font(ctx, "ZapfDingbats");
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(Helv), pdf_add_simple_font(ctx, annot->page->doc, helv, 0));
pdf_dict_put_drop(ctx, res_font, PDF_NAME(ZaDb), pdf_add_simple_font(ctx, annot->page->doc, zadb, 0));
rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect));
w = (rect.x1 - rect.x0) / 2;
h = (rect.y1 - rect.y0);
/* Use flower symbol from ZapfDingbats as sigil */
fz_append_printf(ctx, buf, "q 1 0.8 0.8 rg BT /ZaDb %g Tf %g %g Td (`) Tj ET Q\n",
h*1.1f,
rect.x0 + w - (h*0.4f),
rect.y0 + h*0.1f);
/* Name */
name_w = measure_simple_string(ctx, helv, name);
size = fz_min(fz_min((w - 4) / name_w, h), 24);
fz_append_string(ctx, buf, "BT\n");
fz_append_printf(ctx, buf, "/Helv %g Tf\n", size);
fz_append_printf(ctx, buf, "%g %g Td\n", rect.x0+2, rect.y1 - size*0.8f - (h-size)/2);
write_simple_string(ctx, buf, name, name + strlen(name));
fz_append_string(ctx, buf, " Tj\n");
fz_append_string(ctx, buf, "ET\n");
/* Information text */
size = fz_min(fz_min((w / 12), h / 6), 16);
fz_append_string(ctx, buf, "BT\n");
fz_append_printf(ctx, buf, "/Helv %g Tf\n", size);
fz_append_printf(ctx, buf, "%g TL\n", size);
fz_append_printf(ctx, buf, "%g %g Td\n", rect.x0+w+2, rect.y1);
fz_snprintf(tmp, sizeof tmp, "Digitally signed by %s", name);
write_simple_string_with_quadding(ctx, buf, helv, size, tmp, w-4, 0);
fz_snprintf(tmp, sizeof tmp, "DN: %s", dn);
write_simple_string_with_quadding(ctx, buf, helv, size, tmp, w-4, 0);
if (date)
{
fz_snprintf(tmp, sizeof tmp, "Date: %s", date);
write_simple_string_with_quadding(ctx, buf, helv, size, tmp, w-4, 0);
}
fz_append_string(ctx, buf, "ET\n");
}
else
{
rect.x0 = rect.y0 = 0;
rect.x1 = rect.y1 = 100;
res = pdf_new_dict(ctx, annot->page->doc, 0);
fz_append_string(ctx, buf, "% DSBlank\n");
}
/* Update the AP/N stream */
ap = pdf_dict_get(ctx, annot->obj, PDF_NAME(AP));
if (!ap)
ap = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(AP), 1);
new_ap_n = pdf_new_xobject(ctx, annot->page->doc, rect, fz_identity, res, buf);
pdf_drop_obj(ctx, annot->ap);
annot->ap = new_ap_n;
annot->has_new_ap = 1;
pdf_dict_put(ctx, ap, PDF_NAME(N), new_ap_n);
}
fz_always(ctx)
{
fz_drop_font(ctx, helv);
fz_drop_font(ctx, zadb);
pdf_drop_obj(ctx, res);
fz_drop_buffer(ctx, buf);
}
fz_catch(ctx)
{
fz_rethrow(ctx);
}
}
/*
Recreate the appearance stream for an annotation, if necessary.
*/
void pdf_update_appearance(fz_context *ctx, pdf_annot *annot)
{
pdf_obj *subtype;
pdf_obj *ap, *ap_n, *as;
subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
if (subtype == PDF_NAME(Popup))
return;
if (subtype == PDF_NAME(Link))
return;
/* Check if the field is dirtied by JS events */
if (pdf_obj_is_dirty(ctx, annot->obj))
annot->needs_new_ap = 1;
/* Check if the current appearance has been swapped */
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 (annot->is_hot && annot->is_active && subtype == PDF_NAME(Widget))
{
pdf_obj *ap_d = pdf_dict_get(ctx, ap, PDF_NAME(D));
if (ap_d)
ap_n = ap_d;
}
if (!pdf_is_stream(ctx, ap_n))
ap_n = pdf_dict_get(ctx, ap_n, as);
if (annot->ap != ap_n)
{
pdf_drop_obj(ctx, annot->ap);
annot->ap = NULL;
if (pdf_is_stream(ctx, ap_n))
annot->ap = pdf_keep_obj(ctx, ap_n);
annot->has_new_ap = 1;
}
if (!annot->ap || annot->needs_new_ap)
{
fz_rect rect, bbox;
fz_matrix matrix = fz_identity;
fz_buffer *buf;
pdf_obj *res = NULL;
pdf_obj *new_ap_n = NULL;
fz_var(res);
fz_var(new_ap_n);
annot->needs_new_ap = 0;
/* Special case for Btn widgets that need multiple appearance streams. */
if (pdf_name_eq(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype)), PDF_NAME(Widget)))
{
if (pdf_name_eq(ctx, pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(FT)), PDF_NAME(Btn)))
{
pdf_update_button_appearance(ctx, annot);
pdf_clean_obj(ctx, annot->obj);
return;
}
}
buf = fz_new_buffer(ctx, 1024);
fz_try(ctx)
{
rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(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)
{
if (!ap)
{
ap = pdf_new_dict(ctx, annot->page->doc, 1);
pdf_dict_put_drop(ctx, annot->obj, PDF_NAME(AP), ap);
}
new_ap_n = pdf_new_xobject(ctx, annot->page->doc, bbox, matrix, res, buf);
pdf_dict_put(ctx, ap, PDF_NAME(N), new_ap_n);
}
else
{
new_ap_n = pdf_keep_obj(ctx, ap_n);
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, new_ap_n);
annot->has_new_ap = 1;
}
fz_always(ctx)
{
fz_drop_buffer(ctx, buf);
pdf_drop_obj(ctx, res);
pdf_drop_obj(ctx, new_ap_n);
}
fz_catch(ctx)
{
fz_warn(ctx, "cannot create appearance stream");
}
}
pdf_clean_obj(ctx, annot->obj);
}
/*
Regenerate any appearance streams that are out of date and check for
cases where a different appearance stream should be selected because of
state changes.
Note that a call to pdf_pass_event for one page may lead to changes on
any other, so an app should call pdf_update_annot for every annotation
it currently displays. Also it is important that the pdf_annot object
is the one used to last render the annotation. If instead the app were
to drop the page or annotations and reload them then a call to
pdf_update_annot would not reliably be able to report all changed
annotations.
Returns true if the annotation appearance has changed since the last time
pdf_update_annot was called or the annotation was first loaded.
*/
int
pdf_update_annot(fz_context *ctx, pdf_annot *annot)
{
int changed;
pdf_update_appearance(ctx, annot);
changed = annot->has_new_ap;
annot->has_new_ap = 0;
return changed;
}