summaryrefslogtreecommitdiff
path: root/source/pdf/pdf-appearance.c
diff options
context:
space:
mode:
authorPaul Gardiner <paulg.artifex@glidos.net>2013-08-13 16:34:37 +0100
committerPaul Gardiner <paulg.artifex@glidos.net>2013-08-13 16:34:37 +0100
commit861f5f95873f79a564e7146662c3aa600bb07513 (patch)
tree447f3cef66b63529a9904de4073d815db7913922 /source/pdf/pdf-appearance.c
parent6ef86c509dcc9b6ed46d459bf1103baffd1593d7 (diff)
downloadmupdf-861f5f95873f79a564e7146662c3aa600bb07513.tar.xz
Add support for creating signature-field appearance streams
Diffstat (limited to 'source/pdf/pdf-appearance.c')
-rw-r--r--source/pdf/pdf-appearance.c236
1 files changed, 228 insertions, 8 deletions
diff --git a/source/pdf/pdf-appearance.c b/source/pdf/pdf-appearance.c
index f7bb56de..e7d8a65b 100644
--- a/source/pdf/pdf-appearance.c
+++ b/source/pdf/pdf-appearance.c
@@ -1656,6 +1656,24 @@ void pdf_update_ink_appearance(pdf_document *doc, pdf_annot *annot)
}
}
+static void add_text(fz_context *ctx, font_info *font_rec, fz_text *text, char *str, int str_len, float x, float y)
+{
+ fz_font *font = font_rec->font->font;
+ int mask = FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_TRANSFORM;
+
+ while (str_len--)
+ {
+ FT_Fixed adv;
+
+ /* FIXME: convert str from utf8 to WinAnsi */
+ int gid = FT_Get_Char_Index(font->ft_face, *str);
+ fz_add_text(ctx, text, gid, *str++, x, y);
+
+ FT_Get_Advance(font->ft_face, gid, mask, &adv);
+ x += ((float)adv) * font_rec->da_rec.font_size / ((FT_Face)font->ft_face)->units_per_EM;
+ }
+}
+
static fz_text *layout_text(fz_context *ctx, font_info *font_rec, char *str, float x, float y)
{
fz_matrix tm;
@@ -1668,19 +1686,90 @@ static fz_text *layout_text(fz_context *ctx, font_info *font_rec, char *str, flo
fz_try(ctx)
{
+ add_text(ctx, font_rec, text, str, strlen(str), x, y);
+ }
+ fz_catch(ctx)
+ {
+ fz_free_text(ctx, text);
+ fz_rethrow(ctx);
+ }
+
+ return text;
+}
+
+static fz_text *fit_text(fz_context *ctx, font_info *font_rec, char *str, fz_rect *bounds)
+{
+ float width = bounds->x1 - bounds->x0;
+ float height = bounds->y1 - bounds->y0;
+ fz_matrix tm;
+ fz_text *text = NULL;
+ text_splitter splitter;
+ float ascender;
+
+ /* Initially aim for one-line of text */
+ font_rec->da_rec.font_size = height / font_rec->lineheight;
- while (*str)
+ text_splitter_init(&splitter, font_rec, str, width, height, 1);
+
+ fz_var(text);
+ fz_try(ctx)
+ {
+ int i;
+ while (!splitter.done)
{
- FT_Fixed adv;
+ /* Try a layout pass */
+ int line = 0;
+ float font_size;
+ float x = 0.0;
+ float y = 0.0;
+
- /* FIXME: convert str from utf8 to WinAnsi */
- int gid = FT_Get_Char_Index(font->ft_face, *str);
- fz_add_text(ctx, text, gid, *str, x, y);
+ fz_free_text(ctx, text);
+ text = NULL;
+ font_size = font_rec->da_rec.font_size;
+ fz_scale(&tm, font_size, font_size);
+ text = fz_new_text(ctx, font_rec->font->font, &tm, 0);
- FT_Get_Advance(font->ft_face, gid, mask, &adv);
- x += ((float)adv) * font_rec->da_rec.font_size / ((FT_Face)font->ft_face)->units_per_EM;
+ text_splitter_start_pass(&splitter);
- str++;
+ /* 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;
+ char *word = str+splitter.text_start;
+ int wordlen = splitter.text_end-splitter.text_start;
+
+ text_splitter_move(&splitter, -line, &dx, &dy);
+ x += dx;
+ y += dy;
+ add_text(ctx, font_rec, text, word, wordlen, x, y);
+ }
+ }
+
+ line ++;
+ }
+
+ if (!splitter.done)
+ text_splitter_retry(&splitter);
+ }
+
+ /* Post process text with the scale determined by the splitter
+ * and with the required offst */
+ fz_pre_scale(&text->trm, splitter.scale, splitter.scale);
+ ascender = font_rec->font->ascent * font_rec->da_rec.font_size * splitter.scale / 1000.0f;
+ for (i = 0; i < text->len; i++)
+ {
+ text->items[i].x = text->items[i].x * splitter.scale + bounds->x0;
+ text->items[i].y = text->items[i].y * splitter.scale + bounds->y1 - ascender;
}
}
fz_catch(ctx)
@@ -1752,3 +1841,134 @@ void pdf_update_free_text_annot_appearance(pdf_document *doc, pdf_annot *annot)
fz_rethrow(ctx);
}
}
+
+static void draw_logo(fz_context *ctx, fz_path *path)
+{
+ /* Add signature logo here. */
+};
+
+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)
+{
+ 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 float logo_color[3] = {1.0f, 0.85f, 0.85f};
+
+void pdf_set_signature_appearance(pdf_document *doc, pdf_annot *annot, char *name, char *dn, char *date)
+{
+ fz_context *ctx = doc->ctx;
+ const fz_matrix *page_ctm = &annot->page->ctm;
+ pdf_obj *obj = annot->obj;
+ pdf_obj *dr = pdf_dict_getp(pdf_trailer(doc), "Root/AcroForm/DR");
+ fz_display_list *dlist = NULL;
+ fz_device *dev = NULL;
+ font_info font_rec;
+ fz_text *text = NULL;
+ fz_colorspace *cs = NULL;
+ fz_path *path = NULL;
+ fz_buffer *fzbuf = NULL;
+
+ if (!dr)
+ pdf_dict_putp_drop(pdf_trailer(doc), "Root/AcroForm/DR", pdf_new_dict(doc, 1));
+
+ memset(&font_rec, 0, sizeof(font_rec));
+
+ fz_var(path);
+ fz_var(dlist);
+ fz_var(dev);
+ fz_var(text);
+ fz_var(cs);
+ fz_var(fzbuf);
+ fz_try(ctx)
+ {
+ char *da = pdf_to_str_buf(pdf_dict_gets(obj, "DA"));
+ fz_rect rect = annot->rect;
+ fz_rect logo_bounds;
+ fz_matrix logo_tm;
+ unsigned char *bufstr;
+
+ dlist = fz_new_display_list(ctx);
+ dev = fz_new_list_device(ctx, dlist);
+
+ 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);
+ cs = fz_device_rgb(ctx);
+ fz_fill_path(dev, path, 0, &logo_tm, cs, logo_color, 1.0f);
+ fz_drop_colorspace(ctx, cs);
+ cs = NULL;
+
+ get_font_info(doc, dr, da, &font_rec);
+
+ switch (font_rec.da_rec.col_size)
+ {
+ case 1: cs = fz_device_gray(ctx); break;
+ case 3: cs = fz_device_rgb(ctx); break;
+ case 4: cs = fz_device_cmyk(ctx); break;
+ }
+
+ /* 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(dev, text, page_ctm, cs, font_rec.da_rec.col, 1.0f);
+ fz_free_text(ctx, text);
+ text = NULL;
+
+ /* Display the distinguished name in the right-hand half */
+ fzbuf = fz_new_buffer(ctx, 256);
+ fz_buffer_printf(ctx, fzbuf, "Digitally signed by %s\n", name);
+ fz_buffer_printf(ctx, fzbuf, "DN: %s\n", dn);
+ fz_buffer_printf(ctx, fzbuf, "Date: %s", date);
+ (void)fz_buffer_storage(ctx, fzbuf, &bufstr);
+ rect = annot->rect;
+ rect.x0 = (rect.x0 + rect.x1)/2.0f;
+ text = fit_text(ctx, &font_rec, (char *)bufstr, &rect);
+ fz_fill_text(dev, text, page_ctm, cs, font_rec.da_rec.col, 1.0f);
+
+ rect = annot->rect;
+ fz_transform_rect(&rect, page_ctm);
+ pdf_set_annot_appearance(doc, annot, &rect, dlist);
+
+ /* Drop the cached xobject from the annotation structure to
+ * force a redraw on next pdf_update_page call */
+ pdf_drop_xobject(ctx, annot->ap);
+ annot->ap = NULL;
+ }
+ fz_always(ctx)
+ {
+ fz_free_device(dev);
+ fz_drop_display_list(ctx, dlist);
+ font_info_fin(ctx, &font_rec);
+ fz_free_path(ctx, path);
+ fz_free_text(ctx, text);
+ fz_drop_colorspace(ctx, cs);
+ fz_drop_buffer(ctx, fzbuf);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+}