summaryrefslogtreecommitdiff
path: root/source/pdf
diff options
context:
space:
mode:
Diffstat (limited to 'source/pdf')
-rw-r--r--source/pdf/pdf-device.c444
-rw-r--r--source/pdf/pdf-font.c1003
-rw-r--r--source/pdf/pdf-form.c2
-rw-r--r--source/pdf/pdf-image.c175
-rw-r--r--source/pdf/pdf-page.c21
-rw-r--r--source/pdf/pdf-resources.c295
-rw-r--r--source/pdf/pdf-xref.c3
7 files changed, 1669 insertions, 274 deletions
diff --git a/source/pdf/pdf-device.c b/source/pdf/pdf-device.c
index 85e2f27d..7cc4e5ea 100644
--- a/source/pdf/pdf-device.c
+++ b/source/pdf/pdf-device.c
@@ -36,14 +36,15 @@ struct gstate_s
fz_matrix tm;
};
-typedef struct image_entry_s image_entry;
-
-struct image_entry_s
-{
- char digest[16];
- int id;
- pdf_obj *ref;
-};
+/* The image digest information, object reference, as well as indirect reference
+ * ID are all stored in doc->resources->image, and so they are maintained
+ * through the life of the document not just this page level device. As we
+ * encounter images on a page, we will add to the hash table if they are not
+ * already present. When we have an image on a particular page, the resource
+ * dict will be updated with the proper indirect reference across the document.
+ * We do need to maintain some information as to what image resources we have
+ * already specified for this page which is the purpose of image_indices
+ */
typedef struct alpha_entry_s alpha_entry;
@@ -91,12 +92,17 @@ struct pdf_device_s
int num_imgs;
int max_imgs;
- image_entry *images;
+ int *image_indices;
+
+ int num_cid_fonts;
+ int max_cid_fonts;
+ int *font_indices;
int num_alphas;
int max_alphas;
alpha_entry *alphas;
+ /* Base font information */
int num_fonts;
int max_fonts;
font_entry *fonts;
@@ -109,208 +115,6 @@ struct pdf_device_s
#define CURRENT_GSTATE(pdev) (&(pdev)->gstates[(pdev)->num_gstates-1])
/* Helper functions */
-
-static int
-send_image(fz_context *ctx, pdf_device *pdev, fz_image *image, int mask, int smask)
-{
- fz_pixmap *pixmap = NULL;
- pdf_obj *imobj = NULL;
- pdf_obj *imref = NULL;
- fz_compressed_buffer *cbuffer = NULL;
- fz_compression_params *cp = NULL;
- fz_buffer *buffer = NULL;
- int i, num;
- fz_md5 state;
- unsigned char digest[16];
- fz_colorspace *colorspace = image->colorspace;
- pdf_document *doc = pdev->doc;
-
- /* If we can maintain compression, do so */
- cbuffer = image->buffer;
-
- fz_var(pixmap);
- fz_var(buffer);
- fz_var(imobj);
- fz_var(imref);
-
- fz_try(ctx)
- {
- if (cbuffer != NULL && cbuffer->params.type != FZ_IMAGE_PNG && cbuffer->params.type != FZ_IMAGE_TIFF)
- {
- buffer = fz_keep_buffer(ctx, cbuffer->buffer);
- cp = &cbuffer->params;
- }
- else
- {
- unsigned int size;
- int n;
- /* Currently, set to maintain resolution; should we consider
- * subsampling here according to desired output res? */
- pixmap = fz_get_pixmap_from_image(ctx, image, image->w, image->h);
- colorspace = pixmap->colorspace; /* May be different to image->colorspace! */
- n = (pixmap->n == 1 ? 1 : pixmap->n-1);
- size = image->w * image->h * n;
- buffer = fz_new_buffer(ctx, size);
- buffer->len = size;
- if (pixmap->n == 1)
- {
- memcpy(buffer->data, pixmap->samples, size);
- }
- else
- {
- /* Need to remove the alpha plane */
- unsigned char *d = buffer->data;
- unsigned char *s = pixmap->samples;
- int mod = n;
- while (size--)
- {
- *d++ = *s++;
- mod--;
- if (mod == 0)
- s++, mod = n;
- }
- }
- }
-
- fz_md5_init(&state);
- fz_md5_update(&state, buffer->data, buffer->len);
- fz_md5_final(&state, digest);
- for(i=0; i < pdev->num_imgs; i++)
- {
- if (!memcmp(&digest, pdev->images[i].digest, sizeof(digest)))
- {
- num = i;
- break;
- }
- }
-
- if (i < pdev->num_imgs)
- break;
-
- if (pdev->num_imgs == pdev->max_imgs)
- {
- int newmax = pdev->max_imgs * 2;
- if (newmax == 0)
- newmax = 4;
- pdev->images = fz_resize_array(ctx, pdev->images, newmax, sizeof(*pdev->images));
- pdev->max_imgs = newmax;
- }
- num = pdev->num_imgs++;
- memcpy(pdev->images[num].digest,digest,16);
- pdev->images[num].ref = NULL; /* Will be filled in later */
-
- imobj = pdf_new_dict(ctx, doc, 3);
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_Type, PDF_NAME_XObject);
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_Subtype, PDF_NAME_Image);
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_Width, pdf_new_int(ctx, doc, image->w));
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_Height, pdf_new_int(ctx, doc, image->h));
- if (mask)
- {}
- else if (!colorspace || colorspace->n == 1)
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_ColorSpace, PDF_NAME_DeviceGray);
- else if (colorspace->n == 3)
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_ColorSpace, PDF_NAME_DeviceRGB);
- else if (colorspace->n == 4)
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_ColorSpace, PDF_NAME_DeviceCMYK);
- if (!mask)
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_BitsPerComponent, pdf_new_int(ctx, doc, image->bpc));
- switch (cp ? cp->type : FZ_IMAGE_UNKNOWN)
- {
- case FZ_IMAGE_UNKNOWN: /* Unknown also means raw */
- default:
- break;
- case FZ_IMAGE_JPEG:
- if (cp->u.jpeg.color_transform != -1)
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_ColorTransform, pdf_new_int(ctx, doc, cp->u.jpeg.color_transform));
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_Filter, PDF_NAME_DCTDecode);
- break;
- case FZ_IMAGE_JPX:
- if (cp->u.jpx.smask_in_data)
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_SMaskInData, pdf_new_int(ctx, doc, cp->u.jpx.smask_in_data));
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_Filter, PDF_NAME_JPXDecode);
- break;
- case FZ_IMAGE_FAX:
- if (cp->u.fax.columns)
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_Columns, pdf_new_int(ctx, doc, cp->u.fax.columns));
- if (cp->u.fax.rows)
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_Rows, pdf_new_int(ctx, doc, cp->u.fax.rows));
- if (cp->u.fax.k)
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_K, pdf_new_int(ctx, doc, cp->u.fax.k));
- if (cp->u.fax.end_of_line)
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_EndOfLine, pdf_new_int(ctx, doc, cp->u.fax.end_of_line));
- if (cp->u.fax.encoded_byte_align)
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_EncodedByteAlign, pdf_new_int(ctx, doc, cp->u.fax.encoded_byte_align));
- if (cp->u.fax.end_of_block)
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_EndOfBlock, pdf_new_int(ctx, doc, cp->u.fax.end_of_block));
- if (cp->u.fax.black_is_1)
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_BlackIs1, pdf_new_int(ctx, doc, cp->u.fax.black_is_1));
- if (cp->u.fax.damaged_rows_before_error)
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_DamagedRowsBeforeError, pdf_new_int(ctx, doc, cp->u.fax.damaged_rows_before_error));
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_Filter, PDF_NAME_CCITTFaxDecode);
- break;
- case FZ_IMAGE_JBIG2:
- /* FIXME - jbig2globals */
- cp->type = FZ_IMAGE_UNKNOWN;
- break;
- case FZ_IMAGE_FLATE:
- if (cp->u.flate.columns)
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_Columns, pdf_new_int(ctx, doc, cp->u.flate.columns));
- if (cp->u.flate.colors)
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_Colors, pdf_new_int(ctx, doc, cp->u.flate.colors));
- if (cp->u.flate.predictor)
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_Predictor, pdf_new_int(ctx, doc, cp->u.flate.predictor));
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_Filter, PDF_NAME_FlateDecode);
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_BitsPerComponent, pdf_new_int(ctx, doc, image->bpc));
- break;
- case FZ_IMAGE_LZW:
- if (cp->u.lzw.columns)
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_Columns, pdf_new_int(ctx, doc, cp->u.lzw.columns));
- if (cp->u.lzw.colors)
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_Colors, pdf_new_int(ctx, doc, cp->u.lzw.colors));
- if (cp->u.lzw.predictor)
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_Predictor, pdf_new_int(ctx, doc, cp->u.lzw.predictor));
- if (cp->u.lzw.early_change)
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_EarlyChange, pdf_new_int(ctx, doc, cp->u.lzw.early_change));
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_Filter, PDF_NAME_LZWDecode);
- break;
- case FZ_IMAGE_RLD:
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_Filter, PDF_NAME_RunLengthDecode);
- break;
- }
- if (mask)
- {
- pdf_dict_put_drop(ctx, imobj, PDF_NAME_ImageMask, pdf_new_bool(ctx, doc, 1));
- }
- if (image->mask)
- {
- int smasknum = send_image(ctx, pdev, image->mask, 0, 1);
- pdf_dict_put(ctx, imobj, PDF_NAME_SMask, pdev->images[smasknum].ref);
- }
-
- imref = pdf_new_ref(ctx, doc, imobj);
- pdf_update_stream(ctx, doc, imref, buffer, 1);
-
- {
- char text[32];
- snprintf(text, sizeof(text), "XObject/Img%d", num);
- pdf_dict_putp(ctx, pdev->resources, text, imref);
- }
- pdev->images[num].ref = imref;
- }
- fz_always(ctx)
- {
- fz_drop_buffer(ctx, buffer);
- pdf_drop_obj(ctx, imobj);
- fz_drop_pixmap(ctx, pixmap);
- }
- fz_catch(ctx)
- {
- pdf_drop_obj(ctx, imref);
- fz_rethrow(ctx);
- }
- return num;
-}
-
static void
pdf_dev_stroke_state(fz_context *ctx, pdf_device *pdev, const fz_stroke_state *stroke_state)
{
@@ -550,66 +354,116 @@ pdf_dev_alpha(fz_context *ctx, pdf_device *pdev, float alpha, int stroke)
}
static void
+pdf_dev_add_font_res(fz_context *ctx, fz_device *dev, pdf_res *fres)
+{
+ char text[32];
+ pdf_device *pdev = (pdf_device*)dev;
+ int k;
+ int num;
+
+ /* Check if we already had this one */
+ for (k = 0; k < pdev->num_cid_fonts; k++)
+ {
+ if (pdev->font_indices[k] == fres->num)
+ return;
+ }
+
+ /* Not there so add to resources */
+ snprintf(text, sizeof(text), "Font/F%d", fres->num);
+ pdf_dict_putp(ctx, pdev->resources, text, fres->obj);
+
+ /* And add index to our list for this page */
+ if (pdev->num_cid_fonts == pdev->max_cid_fonts)
+ {
+ int newmax = pdev->max_cid_fonts * 2;
+ if (newmax == 0)
+ newmax = 4;
+ pdev->font_indices = fz_resize_array(ctx, pdev->image_indices, newmax, sizeof(*pdev->font_indices));
+ pdev->max_cid_fonts = newmax;
+ }
+ num = pdev->num_cid_fonts++;
+ pdev->font_indices[num] = fres->num;
+}
+
+static void
pdf_dev_font(fz_context *ctx, pdf_device *pdev, fz_font *font, float size)
{
int i;
pdf_document *doc = pdev->doc;
gstate *gs = CURRENT_GSTATE(pdev);
+ pdf_res *fres;
/* If the font is unchanged, nothing to do */
if (gs->font >= 0 && pdev->fonts[gs->font].font == font)
return;
- if (font->ft_buffer != NULL || font->ft_substitute)
- fz_throw(ctx, FZ_ERROR_GENERIC, "pdf device supports only base 14 fonts currently");
+ if (font->ft_substitute)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "pdf device does not support substitute metrics");
- /* Have we sent such a font before? */
- for (i = 0; i < pdev->num_fonts; i++)
- if (pdev->fonts[i].font == font)
- break;
+ if (font->ft_buffer != NULL && !pdf_font_writing_supported(font))
+ fz_throw(ctx, FZ_ERROR_GENERIC, "pdf device does not support font types found in this file");
- if (i == pdev->num_fonts)
+ if (font->ft_buffer != NULL)
{
- pdf_obj *o;
- pdf_obj *ref = NULL;
+ /* This will add it to the xref if needed */
+ fres = pdf_add_cid_font_res(ctx, doc, font->ft_buffer, font);
+ fz_buffer_printf(ctx, gs->buf, "/F%d %f Tf\n", fres->num, size);
- fz_var(ref);
+ /* Possibly add to page resources */
+ pdf_dev_add_font_res(ctx, (fz_device*) pdev, fres);
+ pdf_drop_obj(ctx, fres->obj);
+ }
+ else
+ {
+ /* Have the device handle the base fonts */
+ /* Have we sent such a font before? */
+ for (i = 0; i < pdev->num_fonts; i++)
+ if (pdev->fonts[i].font == font)
+ break;
- /* No. Need to make a new one */
- if (pdev->num_fonts == pdev->max_fonts)
+ if (i == pdev->num_fonts)
{
- int newmax = pdev->max_fonts * 2;
- if (newmax == 0)
- newmax = 4;
- pdev->fonts = fz_resize_array(ctx, pdev->fonts, newmax, sizeof(*pdev->fonts));
- pdev->max_fonts = newmax;
- }
- pdev->fonts[i].font = fz_keep_font(ctx, font);
+ pdf_obj *o;
+ pdf_obj *ref = NULL;
- o = pdf_new_dict(ctx, doc, 3);
- fz_try(ctx)
- {
- char text[32];
- pdf_dict_put_drop(ctx, o, PDF_NAME_Type, PDF_NAME_Font);
- pdf_dict_put_drop(ctx, o, PDF_NAME_Subtype, PDF_NAME_Type1);
- pdf_dict_put_drop(ctx, o, PDF_NAME_BaseFont, pdf_new_name(ctx, doc, font->name));
- pdf_dict_put_drop(ctx, o, PDF_NAME_Encoding, PDF_NAME_WinAnsiEncoding);
- ref = pdf_new_ref(ctx, doc, o);
- snprintf(text, sizeof(text), "Font/F%d", i);
- pdf_dict_putp(ctx, pdev->resources, text, ref);
- }
- fz_always(ctx)
- {
- pdf_drop_obj(ctx, o);
- pdf_drop_obj(ctx, ref);
- }
- fz_catch(ctx)
- {
- fz_rethrow(ctx);
+ fz_var(ref);
+
+ /* No. Need to make a new one */
+ if (pdev->num_fonts == pdev->max_fonts)
+ {
+ int newmax = pdev->max_fonts * 2;
+ if (newmax == 0)
+ newmax = 4;
+ pdev->fonts = fz_resize_array(ctx, pdev->fonts, newmax, sizeof(*pdev->fonts));
+ pdev->max_fonts = newmax;
+ }
+ pdev->fonts[i].font = fz_keep_font(ctx, font);
+
+ o = pdf_new_dict(ctx, doc, 3);
+ fz_try(ctx)
+ {
+ char text[32];
+ pdf_dict_put_drop(ctx, o, PDF_NAME_Type, PDF_NAME_Font);
+ pdf_dict_put_drop(ctx, o, PDF_NAME_Subtype, PDF_NAME_Type1);
+ pdf_dict_put_drop(ctx, o, PDF_NAME_BaseFont, pdf_new_name(ctx, doc, font->name));
+ pdf_dict_put_drop(ctx, o, PDF_NAME_Encoding, PDF_NAME_WinAnsiEncoding);
+ ref = pdf_new_ref(ctx, doc, o);
+ snprintf(text, sizeof(text), "Font/Fb%d", i);
+ pdf_dict_putp(ctx, pdev->resources, text, ref);
+ }
+ fz_always(ctx)
+ {
+ pdf_drop_obj(ctx, o);
+ pdf_drop_obj(ctx, ref);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+ pdev->num_fonts++;
}
- pdev->num_fonts++;
+ fz_buffer_printf(ctx, gs->buf, "/Fb%d %f Tf\n", i, size);
}
- fz_buffer_printf(ctx, gs->buf, "/F%d %f Tf\n", i, size);
}
static void
@@ -721,11 +575,21 @@ pdf_dev_text_span(fz_context *ctx, pdf_device *pdev, fz_text_span *span, float s
}
fz_buffer_printf(ctx, gs->buf, "<");
- for (/* i from its current value */; i < j; i++)
+ if (span->font->ft_buffer == NULL)
+ {
+ /* A standard 14 type font */
+ for (/* i from its current value */; i < j; i++)
+ {
+ fz_buffer_printf(ctx, gs->buf, "%02x", span->items[i].ucs);
+ }
+ }
+ else
{
- /* FIXME: should use it->gid, rather than it->ucs, and convert
- * to the correct encoding */
- fz_buffer_printf(ctx, gs->buf, "%02x", span->items[i].ucs);
+ /* Non-standard font. Saved as Type0 Identity-H */
+ for (/* i from its current value */; i < j; i++)
+ {
+ fz_buffer_printf(ctx, gs->buf, "%04x", span->items[i].gid);
+ }
}
fz_buffer_printf(ctx, gs->buf, "> Tj\n");
}
@@ -1040,21 +904,63 @@ pdf_dev_ignore_text(fz_context *ctx, fz_device *dev, const fz_text *text, const
}
static void
-pdf_dev_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_matrix *ctm, float alpha)
+pdf_dev_add_image_res(fz_context *ctx, fz_device *dev, pdf_res *im_res)
{
+ char text[32];
pdf_device *pdev = (pdf_device*)dev;
+ int k;
int num;
+
+ /* Check if we already had this one */
+ for (k = 0; k < pdev->num_imgs; k++)
+ {
+ if (pdev->image_indices[k] == im_res->num)
+ return;
+ }
+
+ /* Not there so add to resources */
+ snprintf(text, sizeof(text), "XObject/Img%d", im_res->num);
+ pdf_dict_putp(ctx, pdev->resources, text, im_res->obj);
+
+ /* And add index to our list for this page */
+ if (pdev->num_imgs == pdev->max_imgs)
+ {
+ int newmax = pdev->max_imgs * 2;
+ if (newmax == 0)
+ newmax = 4;
+ pdev->image_indices = fz_resize_array(ctx, pdev->image_indices, newmax, sizeof(*pdev->image_indices));
+ pdev->max_imgs = newmax;
+ }
+ num = pdev->num_imgs++;
+ pdev->image_indices[num] = im_res->num;
+}
+
+static void
+pdf_dev_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_matrix *ctm, float alpha)
+{
+ pdf_device *pdev = (pdf_device*)dev;
+ pdf_res *im_res;
gstate *gs = CURRENT_GSTATE(pdev);
fz_matrix local_ctm = *ctm;
pdf_dev_end_text(ctx, pdev);
- num = send_image(ctx, pdev, image, 0, 0);
+ im_res = pdf_add_image_res(ctx, pdev->doc, image, 0);
+ if (im_res == NULL)
+ {
+ fz_warn(ctx, "pdf_add_image_res: problem adding image resource");
+ return;
+ }
pdf_dev_alpha(ctx, pdev, alpha, 0);
+
/* PDF images are upside down, so fiddle the ctm */
fz_pre_scale(&local_ctm, 1, -1);
fz_pre_translate(&local_ctm, 0, -1);
pdf_dev_ctm(ctx, pdev, &local_ctm);
- fz_buffer_printf(ctx, gs->buf, "/Img%d Do\n", num);
+ fz_buffer_printf(ctx, gs->buf, "/Img%d Do\n", im_res->num);
+
+ /* Possibly add to page resources */
+ pdf_dev_add_image_res(ctx, dev, im_res);
+ pdf_drop_obj(ctx, im_res->obj);
}
static void
@@ -1071,20 +977,26 @@ pdf_dev_fill_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, const
fz_colorspace *colorspace, const float *color, float alpha)
{
pdf_device *pdev = (pdf_device*)dev;
+ pdf_res* im_res = NULL;
gstate *gs = CURRENT_GSTATE(pdev);
- int num;
fz_matrix local_ctm = *ctm;
pdf_dev_end_text(ctx, pdev);
- num = send_image(ctx, pdev, image, 1, 0);
+ im_res = pdf_add_image_res(ctx, pdev->doc, image, 1);
+ if (im_res == NULL)
+ {
+ fz_warn(ctx, "pdf_add_image_res: problem adding image resource");
+ return;
+ }
fz_buffer_printf(ctx, gs->buf, "q\n");
pdf_dev_alpha(ctx, pdev, alpha, 0);
pdf_dev_color(ctx, pdev, colorspace, color, 0);
+
/* PDF images are upside down, so fiddle the ctm */
fz_pre_scale(&local_ctm, 1, -1);
fz_pre_translate(&local_ctm, 0, -1);
pdf_dev_ctm(ctx, pdev, &local_ctm);
- fz_buffer_printf(ctx, gs->buf, "/Img%d Do Q\n", num);
+ fz_buffer_printf(ctx, gs->buf, "/Img%d Do Q\n", im_res->num);
}
static void
@@ -1283,11 +1195,6 @@ pdf_dev_drop_imp(fz_context *ctx, fz_device *dev)
fz_drop_font(ctx, pdev->fonts[i].font);
}
- for (i = pdev->num_imgs-1; i >= 0; i--)
- {
- pdf_drop_obj(ctx, pdev->images[i].ref);
- }
-
for (i = pdev->num_groups - 1; i >= 0; i--)
{
pdf_drop_obj(ctx, pdev->groups[i].ref);
@@ -1306,9 +1213,10 @@ pdf_dev_drop_imp(fz_context *ctx, fz_device *dev)
pdf_drop_obj(ctx, pdev->resources);
+ fz_free(ctx, pdev->font_indices);
+ fz_free(ctx, pdev->image_indices);
fz_free(ctx, pdev->groups);
fz_free(ctx, pdev->fonts);
- fz_free(ctx, pdev->images);
fz_free(ctx, pdev->alphas);
fz_free(ctx, pdev->gstates);
}
diff --git a/source/pdf/pdf-font.c b/source/pdf/pdf-font.c
index f516468d..5d8cc392 100644
--- a/source/pdf/pdf-font.c
+++ b/source/pdf/pdf-font.c
@@ -1,4 +1,5 @@
#include "mupdf/pdf.h"
+#include <zlib.h>
#include <ft2build.h>
#include FT_FREETYPE_H
@@ -1366,3 +1367,1005 @@ float pdf_text_stride(fz_context *ctx, pdf_font_desc *fontdesc, float fontsize,
return x;
}
+
+/* Populate font description. According to spec, required for Latin fonts are
+ * FontName, Flags, FontBBox, ItalicAngle, Ascent, Descent, CapHeight (Latin),
+ * StemV */
+static void
+pdf_fontdesc_init(fz_context *ctx, pdf_font_desc *fontdesc)
+{
+ FT_Face face;
+ int fterr;
+ int row_pos;
+ unsigned char *ptr;
+ unsigned int k;
+ int count = 0;
+ int width;
+
+ if (fontdesc->font == NULL || fontdesc->font->ft_face == NULL)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot create font description");
+
+ face = fontdesc->font->ft_face;
+ fontdesc->ascent = face->ascender * 1000.0f / face->units_per_EM;
+ fontdesc->descent = face->descender * 1000.0f / face->units_per_EM;
+ fontdesc->italic_angle = 0; /* 0 for now */
+
+ /* Get the cap height and stem thickness from capital O. Obviously an
+ .* issue if this is not a latin font */
+ fterr = FT_Load_Char(fontdesc->font->ft_face, 'O',
+ FT_LOAD_NO_HINTING | FT_LOAD_IGNORE_TRANSFORM);
+ if (fterr)
+ {
+ fz_warn(ctx, "freetype load char O: %s", ft_error_string(fterr));
+ return;
+ }
+ fontdesc->cap_height = face->glyph->metrics.height;
+ width = face->glyph->metrics.width;
+
+ fterr = FT_Set_Char_Size(fontdesc->font->ft_face, 1000, 1000, 300, 300);
+ if (fterr)
+ {
+ fz_warn(ctx, "freetype set char size: %s", ft_error_string(fterr));
+ return;
+ }
+ fterr = FT_Load_Char(fontdesc->font->ft_face, 'O', 0);
+ if (fterr)
+ {
+ fz_warn(ctx, "freetype load char O: %s", ft_error_string(fterr));
+ return;
+ }
+ fterr = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
+ if (fterr)
+ {
+ fz_warn(ctx, "freetype render glyph O: %s", ft_error_string(fterr));
+ return;
+ }
+ row_pos = floor(face->glyph->bitmap.rows / 2.0 + 0.5);
+ ptr = face->glyph->bitmap.buffer + row_pos * face->glyph->bitmap.pitch;
+ for (k = 0; k < face->glyph->bitmap.width; k++)
+ {
+ count += 1;
+ if (ptr[k] == 0)
+ break;
+ }
+ count -= 1;
+ fontdesc->stem_v = width * (float)count / (float)face->glyph->bitmap.width;
+ fontdesc->flags = PDF_FD_NONSYMBOLIC; /* ToDo: FixMe. Set non-symbolic always for now */
+}
+
+static void ft_width_for_simple_table(fz_context *ctx, pdf_font_desc *fontdesc,
+ int *firstcode)
+{
+ FT_ULong charcode;
+ FT_UInt gindex;
+ int size;
+ int fterr;
+
+ *firstcode = 0;
+ fterr = FT_Select_Charmap(fontdesc->font->ft_face, ft_encoding_unicode);
+ if (fterr)
+ {
+ fz_warn(ctx, "freetype select char map: %s", ft_error_string(fterr));
+ return;
+ }
+ fterr = FT_Set_Char_Size(fontdesc->font->ft_face, 1000, 1000, 72, 72);
+ if (fterr)
+ {
+ fz_warn(ctx, "freetype set char size: %s", ft_error_string(fterr));
+ return;
+ }
+ charcode = FT_Get_First_Char(fontdesc->font->ft_face, &gindex);
+ *firstcode = charcode;
+ fontdesc->font->first_width = charcode;
+ while (gindex != 0)
+ {
+ fterr = FT_Load_Char(fontdesc->font->ft_face, charcode,
+ FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM);
+ if (fterr)
+ {
+ fz_warn(ctx, "freetype load char (charcode %ld): %s", charcode, ft_error_string(fterr));
+ return;
+ }
+ size = ((FT_Face)fontdesc->font->ft_face)->glyph->metrics.horiAdvance;
+ if (charcode < 256)
+ {
+ fontdesc->font->width_table[charcode] = size;
+ fontdesc->font->last_width = charcode;
+ }
+ charcode = FT_Get_Next_Char(fontdesc->font->ft_face, charcode, &gindex);
+ }
+}
+
+/* This needs to go away and we need to denote when the buffer is or is not
+* deflated */
+static fz_buffer *deflatebuf(fz_context *ctx, unsigned char *p, int n)
+{
+ fz_buffer *buf;
+ uLongf csize;
+ int t;
+
+ buf = fz_new_buffer(ctx, compressBound(n));
+ csize = buf->cap;
+ t = compress(buf->data, &csize, p, n);
+ if (t != Z_OK)
+ {
+ fz_drop_buffer(ctx, buf);
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot deflate buffer");
+ }
+ buf->len = csize;
+ return buf;
+}
+
+static pdf_obj*
+pdf_font_stream_ref(fz_context *ctx, pdf_document *doc, fz_buffer *buf)
+{
+ pdf_obj *obj = NULL;
+ pdf_obj *newlen = NULL;
+ pdf_obj *currlen = NULL;
+ pdf_obj *ref = NULL;
+ fz_buffer *d_buf = NULL;
+
+ fz_var(obj);
+ fz_var(newlen);
+ fz_var(currlen);
+ fz_var(ref);
+
+ fz_try(ctx)
+ {
+ obj = pdf_new_dict(ctx, doc, 3);
+ pdf_dict_put_drop(ctx, obj, PDF_NAME_Filter, PDF_NAME_FlateDecode);
+ currlen = pdf_new_int(ctx, doc, buf->len);
+ d_buf = deflatebuf(ctx, buf->data, buf->len);
+ newlen = pdf_new_int(ctx, doc, d_buf->len);
+ pdf_dict_put_drop(ctx, obj, PDF_NAME_Length, newlen);
+ pdf_dict_put_drop(ctx, obj, PDF_NAME_Length1, currlen);
+ ref = pdf_new_ref(ctx, doc, obj);
+ pdf_update_stream(ctx, doc, ref, d_buf, 1);
+ }
+ fz_always(ctx)
+ {
+ fz_drop_buffer(ctx, d_buf);
+ pdf_drop_obj(ctx, obj);
+ }
+ fz_catch(ctx)
+ {
+ pdf_drop_obj(ctx, newlen);
+ pdf_drop_obj(ctx, currlen);
+ pdf_drop_obj(ctx, ref);
+ fz_rethrow(ctx);
+ }
+ return ref;
+}
+
+static pdf_obj*
+pdf_font_desc_ref(fz_context *ctx, pdf_document *doc, pdf_font_desc *fontdesc, pdf_obj *fileref)
+{
+ pdf_obj *ref = NULL;
+ pdf_obj *fdobj = NULL;
+ pdf_obj *bbox = NULL;
+ FT_Face face;
+
+ if (fontdesc->font == NULL || fontdesc->font->ft_face == NULL)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot create font description");
+ face = fontdesc->font->ft_face;
+
+ fz_try(ctx)
+ {
+ fdobj = pdf_new_dict(ctx, doc, 10);
+ pdf_dict_put_drop(ctx, fdobj, PDF_NAME_Type, PDF_NAME_FontDescriptor);
+ pdf_dict_put_drop(ctx, fdobj, PDF_NAME_FontName, pdf_new_name(ctx, doc, fontdesc->font->name));
+
+ bbox = pdf_new_array(ctx, doc, 4);
+ pdf_array_push_drop(ctx, bbox, pdf_new_real(ctx, doc, 1000.0f * fontdesc->font->bbox.x0));
+ pdf_array_push_drop(ctx, bbox, pdf_new_real(ctx, doc, 1000.0f * fontdesc->font->bbox.y0));
+ pdf_array_push_drop(ctx, bbox, pdf_new_real(ctx, doc, 1000.0f * fontdesc->font->bbox.x1));
+ pdf_array_push_drop(ctx, bbox, pdf_new_real(ctx, doc, 1000.0f * fontdesc->font->bbox.y1));
+ pdf_dict_put_drop(ctx, fdobj, PDF_NAME_FontBBox, bbox);
+ bbox = NULL;
+ pdf_dict_put_drop(ctx, fdobj, PDF_NAME_ItalicAngle, pdf_new_real(ctx, doc, fontdesc->italic_angle));
+ pdf_dict_put_drop(ctx, fdobj, PDF_NAME_Ascent, pdf_new_real(ctx, doc, fontdesc->ascent));
+ pdf_dict_put_drop(ctx, fdobj, PDF_NAME_Descent, pdf_new_real(ctx, doc, fontdesc->descent));
+ pdf_dict_put_drop(ctx, fdobj, PDF_NAME_CapHeight, pdf_new_real(ctx, doc, fontdesc->cap_height));
+ pdf_dict_put_drop(ctx, fdobj, PDF_NAME_StemV, pdf_new_real(ctx, doc, fontdesc->stem_v));
+ pdf_dict_put_drop(ctx, fdobj, PDF_NAME_Flags, pdf_new_real(ctx, doc, fontdesc->flags));
+
+ switch (ft_kind(face))
+ {
+ case TYPE1:
+ pdf_dict_put_drop(ctx, fdobj, PDF_NAME_FontFile, fileref);
+ break;
+ case TRUETYPE:
+ pdf_dict_put_drop(ctx, fdobj, PDF_NAME_FontFile2, fileref);
+ break;
+ case UNKNOWN:
+ fz_throw(ctx, FZ_ERROR_GENERIC, "Unknown font subtype");
+ break;
+ }
+ ref = pdf_new_ref(ctx, doc, fdobj);
+ }
+ fz_always(ctx)
+ {
+ pdf_drop_obj(ctx, fdobj);
+ }
+ fz_catch(ctx)
+ {
+ pdf_drop_obj(ctx, ref);
+ pdf_drop_obj(ctx, bbox);
+ fz_rethrow(ctx);
+ }
+ return ref;
+}
+
+static pdf_obj*
+pdf_font_widths_ref(fz_context *ctx, pdf_document *doc, pdf_font_desc *fontdesc)
+{
+ pdf_obj *ref = NULL;
+ pdf_obj *arr = NULL;
+ int k;
+
+ fz_var(arr);
+ fz_var(ref);
+
+ fz_try(ctx)
+ {
+ arr = pdf_new_array(ctx, doc, 256);
+ for (k = fontdesc->font->first_width; k < fontdesc->font->last_width; k++)
+ pdf_array_push_drop(ctx, arr, pdf_new_int(ctx, doc, fontdesc->font->width_table[k]));
+ ref = pdf_new_ref(ctx, doc, arr);
+ }
+ fz_always(ctx)
+ {
+ pdf_drop_obj(ctx, arr);
+ }
+ fz_catch(ctx)
+ {
+ pdf_drop_obj(ctx, ref);
+ fz_rethrow(ctx);
+ }
+ return ref;
+}
+
+static pdf_obj*
+pdf_add_cid_system_info(fz_context *ctx, pdf_document *doc)
+{
+ pdf_obj *fobj = NULL;
+ pdf_obj *ref = NULL;
+
+ fz_var(fobj);
+
+ fz_try(ctx)
+ {
+ fobj = pdf_new_dict(ctx, doc, 3);
+ pdf_dict_put_drop(ctx, fobj, PDF_NAME_Ordering, pdf_new_string(ctx, doc, "Identity", strlen("Identity")));
+ pdf_dict_put_drop(ctx, fobj, PDF_NAME_Registry, pdf_new_string(ctx, doc, "Adobe", strlen("Adobe")));
+ pdf_dict_put_drop(ctx, fobj, PDF_NAME_Supplement, pdf_new_int(ctx, doc, 0));
+ ref = pdf_new_ref(ctx, doc, fobj);
+ }
+ fz_always(ctx)
+ {
+ pdf_drop_obj(ctx, fobj);
+ }
+ fz_catch(ctx)
+ {
+ pdf_drop_obj(ctx, ref);
+ fz_rethrow(ctx);
+ }
+ return ref;
+}
+
+/* Different states of starting, same width as last, or consecutive glyph */
+enum { FW_START, FW_SAME, FW_RUN };
+
+static void
+pdf_publish_cid_widths(fz_context *ctx, pdf_document *doc, pdf_obj *fwobj, pdf_obj *run_obj, int state, int first_code, int prev_code, int prev_size)
+{
+ pdf_obj *temp_array = NULL;
+
+ fz_var(temp_array);
+
+ fz_try(ctx)
+ {
+ switch (state)
+ {
+ case FW_SAME:
+ /* Add three entries. First cid, last cid and width */
+ pdf_array_push_drop(ctx, fwobj, pdf_new_int(ctx, doc, first_code));
+ pdf_array_push_drop(ctx, fwobj, pdf_new_int(ctx, doc, prev_code));
+ pdf_array_push_drop(ctx, fwobj, pdf_new_int(ctx, doc, prev_size));
+ break;
+ case FW_RUN:
+ if (pdf_array_len(ctx, run_obj) > 0)
+ {
+ pdf_array_push_drop(ctx, fwobj, pdf_new_int(ctx, doc, first_code));
+ pdf_array_push_drop(ctx, fwobj, run_obj);
+ }
+ else
+ {
+ pdf_drop_obj(ctx, run_obj);
+ }
+ run_obj = NULL;
+ break;
+ case FW_START:
+ /* Lone wolf. Not part of a consecutive run */
+ pdf_array_push_drop(ctx, fwobj, pdf_new_int(ctx, doc, prev_code));
+ temp_array = pdf_new_array(ctx, doc, 1);
+ pdf_array_push_drop(ctx, temp_array, pdf_new_int(ctx, doc, prev_size));
+ pdf_array_push_drop(ctx, fwobj, temp_array);
+ temp_array = NULL;
+ break;
+ }
+ }
+ fz_catch(ctx)
+ {
+ pdf_drop_obj(ctx, temp_array);
+ fz_rethrow(ctx);
+ }
+}
+
+/* ToDo: Ignore the default sized characters */
+static pdf_obj*
+pdf_create_cid_widths(fz_context *ctx, pdf_document *doc, pdf_font_desc *fontdesc, fz_font *source_font)
+{
+ pdf_obj *fwobj = NULL;
+ pdf_obj *run_obj = NULL;
+ FT_ULong curr_code;
+ FT_ULong prev_code;
+ FT_UInt gindex;
+ int curr_size;
+ int prev_size;
+ int first_code;
+ int new_first_code;
+ int fterr;
+ int state = FW_START;
+ int new_state = FW_START;
+ int publish = 0;
+
+ if (fontdesc->font == NULL || fontdesc->font->ft_face == NULL || fontdesc->wmode)
+ {
+ fz_warn(ctx, "cannot compute cid widths description");
+ return NULL;
+ }
+
+ if (source_font->width_table == NULL)
+ {
+ fterr = FT_Select_Charmap(fontdesc->font->ft_face, ft_encoding_unicode);
+ if (fterr)
+ {
+ fz_warn(ctx, "freetype select char map: %s", ft_error_string(fterr));
+ return NULL;
+ }
+ fterr = FT_Set_Char_Size(fontdesc->font->ft_face, 1000, 1000, 72, 72);
+ if (fterr)
+ {
+ fz_warn(ctx, "freetype set char size: %s", ft_error_string(fterr));
+ return NULL;
+ }
+
+ /* Prime the pump. */
+ prev_code = FT_Get_First_Char(fontdesc->font->ft_face, &gindex);
+ if (gindex == 0)
+ {
+ fz_warn(ctx, "freetype FT_Get_First_Char fail: %s", ft_error_string(fterr));
+ return NULL;
+ }
+ prev_size = ((FT_Face)fontdesc->font->ft_face)->glyph->metrics.horiAdvance;
+ first_code = prev_code;
+ }
+ else
+ {
+ /* Prime the pump. */
+ prev_code = 0;
+ prev_size = source_font->width_table[0];
+ first_code = prev_code;
+ gindex = 1;
+ }
+
+ fz_var(fwobj);
+ fz_var(run_obj);
+
+ fz_try(ctx)
+ {
+ fwobj = pdf_new_array(ctx, doc, 10);
+ while (gindex != 0)
+ {
+ if (source_font->width_table == NULL)
+ {
+ curr_code = FT_Get_Next_Char(fontdesc->font->ft_face, prev_code, &gindex);
+ curr_size = ((FT_Face)fontdesc->font->ft_face)->glyph->metrics.horiAdvance;
+ }
+ else
+ {
+ curr_code = prev_code + 1;
+ if (curr_code == source_font->width_count)
+ {
+ gindex = 0;
+ curr_size = -1;
+ }
+ else
+ curr_size = source_font->width_table[curr_code];
+ }
+
+ /* Check if we need to publish or keep collecting */
+ if (prev_code == curr_code - 1)
+ {
+ /* A concecutive code. */
+ switch (state)
+ {
+ case FW_SAME:
+ if (curr_size != prev_size)
+ {
+ /* End of same widths for consecutive ids. Current will
+ * be pushed as prev. below during next iteration */
+ publish = 1;
+ run_obj = pdf_new_array(ctx, doc, 10);
+ new_state = FW_RUN;
+ /* And the new first code is our current code */
+ new_first_code = curr_code;
+ }
+ break;
+ case FW_RUN:
+ if (curr_size == prev_size)
+ {
+ /* Same width, so start a new same entry starting with
+ * the previous code. i.e. the prev size is not put
+ * in the run */
+ publish = 1;
+ new_state = FW_SAME;
+ new_first_code = prev_code;
+ }
+ else
+ {
+ /* Add prev size to run_obj */
+ pdf_array_push_drop(ctx, run_obj, pdf_new_int(ctx, doc, prev_size));
+ }
+ break;
+ case FW_START:
+ /* Starting fresh. Determine our state */
+ if (curr_size == prev_size)
+ {
+ state = FW_SAME;
+ }
+ else
+ {
+ run_obj = pdf_new_array(ctx, doc, 10);
+ pdf_array_push_drop(ctx, run_obj, pdf_new_int(ctx, doc, prev_size));
+ state = FW_RUN;
+ }
+ new_first_code = prev_code;
+ break;
+ }
+ }
+ else
+ {
+ /* Non conscecutive code. Restart */
+ if (state == FW_RUN)
+ {
+ pdf_array_push_drop(ctx, run_obj, pdf_new_int(ctx, doc, prev_size));
+ }
+ new_state = FW_START;
+ publish = 1;
+ }
+
+ if (publish)
+ {
+ pdf_publish_cid_widths(ctx, doc, fwobj, run_obj, state, first_code, prev_code, prev_size);
+ state = new_state;
+ publish = 0;
+ first_code = new_first_code;
+ }
+ prev_size = curr_size;
+ prev_code = curr_code;
+
+ /* See if we need to flush */
+ if (gindex == 0)
+ pdf_publish_cid_widths(ctx, doc, fwobj, run_obj, state, first_code, prev_code, prev_size);
+ }
+ }
+ fz_catch(ctx)
+ {
+ pdf_drop_obj(ctx, fwobj);
+ pdf_drop_obj(ctx, run_obj);
+ fz_rethrow(ctx);
+ }
+ return fwobj;
+}
+
+/* Descendant font construction used for CID font creation from ttf or Adobe type1 */
+static pdf_obj*
+pdf_add_descendant_font(fz_context *ctx, pdf_document *doc, fz_buffer *buffer, pdf_font_desc *fontdesc, fz_font *source_font)
+{
+ pdf_obj *fobj = NULL;
+ pdf_obj *fref = NULL;
+ pdf_obj *fstr_ref = NULL;
+ pdf_obj *fsys_ref = NULL;
+ pdf_obj *fdes_ref = NULL;
+ pdf_obj *fw = NULL;
+
+ const char *ps_name;
+ FT_Face face = fontdesc->font->ft_face;
+
+ fz_var(fobj);
+ fz_var(fref);
+ fz_var(fstr_ref);
+ fz_var(fsys_ref);
+ fz_var(fw);
+
+ fz_try(ctx)
+ {
+ /* refs */
+ fstr_ref = pdf_font_stream_ref(ctx, doc, buffer);
+ fdes_ref = pdf_font_desc_ref(ctx, doc, fontdesc, fstr_ref);
+ fsys_ref = pdf_add_cid_system_info(ctx, doc);
+
+ /* We may have a cid font already with width info in source font and no
+ * cmap in the ft face */
+ fw = pdf_create_cid_widths(ctx, doc, fontdesc, source_font);
+
+ /* And now the font */
+ fobj = pdf_new_dict(ctx, doc, 3);
+ pdf_dict_put_drop(ctx, fobj, PDF_NAME_Type, PDF_NAME_Font);
+ pdf_dict_put_drop(ctx, fobj, PDF_NAME_BaseFont, pdf_new_name(ctx, doc, fontdesc->font->name));
+ pdf_dict_put_drop(ctx, fobj, PDF_NAME_FontDescriptor, fdes_ref);
+ if (fw != NULL)
+ pdf_dict_put_drop(ctx, fobj, PDF_NAME_W, fw);
+ if (source_font != NULL && source_font->width_table != NULL)
+ pdf_dict_put_drop(ctx, fobj, PDF_NAME_DW, pdf_new_int(ctx, doc, source_font->width_default));
+ if ((ps_name = FT_Get_Postscript_Name(face)) != NULL)
+ pdf_dict_put_drop(ctx, fobj, PDF_NAME_BaseFont, pdf_new_string(ctx, doc, ps_name, strlen(ps_name)));
+ switch (ft_kind(face))
+ {
+ case TYPE1:
+ pdf_dict_put_drop(ctx, fobj, PDF_NAME_Subtype, PDF_NAME_CIDFontType0);
+ break;
+ case TRUETYPE:
+ pdf_dict_put_drop(ctx, fobj, PDF_NAME_Subtype, PDF_NAME_CIDFontType2);
+ break;
+ case UNKNOWN:
+ fz_throw(ctx, FZ_ERROR_GENERIC, "Unknown font subtype");
+ break;
+ }
+ pdf_dict_put_drop(ctx, fobj, PDF_NAME_CIDSystemInfo, fsys_ref);
+ fref = pdf_new_ref(ctx, doc, fobj);
+ }
+ fz_always(ctx)
+ {
+ pdf_drop_obj(ctx, fobj);
+ }
+ fz_catch(ctx)
+ {
+ pdf_drop_obj(ctx, fstr_ref);
+ pdf_drop_obj(ctx, fsys_ref);
+ pdf_drop_obj(ctx, fdes_ref);
+ pdf_drop_obj(ctx, fw);
+ pdf_drop_obj(ctx, fref);
+ fz_rethrow(ctx);
+ }
+ return fref;
+}
+
+/* Create the ToUnicode CMap. */
+static pdf_obj*
+pdf_add_cid_to_unicode(fz_context *ctx, pdf_document *doc, pdf_font_desc *fontdesc)
+{
+ fz_buffer *fzbuf = NULL;
+ pdf_obj *fref = NULL;
+ pdf_obj *fobj = NULL;
+ FT_Face face = fontdesc->font->ft_face;
+ FT_UInt glyph_index;
+ char hex_glyph[7];
+ char hex_unicode[7];
+ char listinfo[19];
+ char entry[22];
+ unsigned short *table, *seq;
+ int k;
+ int has_lock = 0;
+ int count;
+ int temp_count;
+ int pos;
+
+ fz_var(has_lock);
+ fz_var(fzbuf);
+ fz_var(fref);
+ fz_var(fobj);
+
+ fz_try(ctx)
+ {
+ fzbuf = fz_new_buffer(ctx, 0);
+
+ /* Boiler plate */
+ fz_write_buffer(ctx, fzbuf, "/CIDInit /ProcSet findresource begin\n", strlen("/CIDInit /ProcSet findresource begin\n"));
+ fz_write_buffer(ctx, fzbuf, "12 dict begin\n", strlen("12 dict begin\n"));
+ fz_write_buffer(ctx, fzbuf, "begincmap\n", strlen("begincmap\n"));
+ fz_write_buffer(ctx, fzbuf, "/CIDSystemInfo\n", strlen("/CIDSystemInfo\n"));
+ fz_write_buffer(ctx, fzbuf, "<</Registry(Adobe)\n", strlen("<</Registry(Adobe)\n"));
+ fz_write_buffer(ctx, fzbuf, "/Ordering(UCS) /Supplement 0>> def\n", strlen("/Ordering(UCS) /Supplement 0>> def\n"));
+ fz_write_buffer(ctx, fzbuf, "/CMapName /Adobe-Identity-UCS def\n", strlen("/CMapName /Adobe-Identity-UCS def\n"));
+ fz_write_buffer(ctx, fzbuf, "/CMapType 2 def\n", strlen("/CMapType 2 def\n"));
+ fz_write_buffer(ctx, fzbuf, "1 begincodespacerange\n", strlen("1 begincodespacerange\n"));
+ fz_write_buffer(ctx, fzbuf, "<0000> <FFFF>\n", strlen("<0000> <FFFF>\n"));
+ fz_write_buffer(ctx, fzbuf, "endcodespacerange\n", strlen("endcodespacerange\n"));
+
+ /* Sort via populating */
+ table = fz_calloc(ctx, 65536, sizeof(unsigned short));
+ count = 0;
+ for (k = 0; k < 65536; k++)
+ {
+ fz_lock(ctx, FZ_LOCK_FREETYPE);
+ has_lock = 1;
+ glyph_index = FT_Get_Char_Index(face, k);
+ fz_unlock(ctx, FZ_LOCK_FREETYPE);
+ has_lock = 0;
+ if (glyph_index > 0 && glyph_index < 65536)
+ {
+ if (table[glyph_index] == 0)
+ {
+ count++;
+ table[glyph_index] = k;
+ }
+ }
+ }
+
+ /* Now output non-zero entries. */
+ /* Note to have a valid CMap, the number of entries in table set can
+ * not exceed 100, so we have to break into multipe tables. Also, note
+ * that to reduce the file size we should be looking for sequential
+ * ranges. Per Adobe technical note #5411, we can't have a range
+ * cross a boundary where the high order byte changes */
+ /* First the ranges */
+ {
+ seq = fz_calloc(ctx, 65536, sizeof(unsigned short));
+ int num_seq = 0;
+ int k_start = -1;
+ int k_end = -1;
+ int match = 0;
+ char hex_start[7];
+ char hex_end[7];
+ char hex_value_start[7];
+ int j;
+
+ k = 0;
+
+ /* First find the ranges */
+ while (1)
+ {
+ if (k == 65535)
+ break;
+ if (table[k] + 1 == table[k + 1])
+ match = 1;
+ else
+ match = 0;
+
+ /* End any sequences across upper byte boundary changes */
+ if ((k & 0xff00) != ((k + 1) & 0xff00))
+ match = 0;
+
+ /* Start of a sequence */
+ if (k_start == -1 && match)
+ {
+ k_start = k;
+ k_end = k + 1;
+ }
+
+ /* In a sequence */
+ if (k_start != -1 && match)
+ {
+ k_end = k + 1;
+ }
+
+ /* Done with a sequence */
+ if (k_start != -1 && !match)
+ {
+ seq[num_seq * 2] = k_start;
+ seq[num_seq * 2 + 1] = k_end;
+ num_seq = num_seq + 1;
+ k_start = -1;
+ }
+ k = k + 1;
+ }
+
+ /* Now output the ranges, with the 100 max limit enforced */
+ if (num_seq > 0)
+ {
+ pos = 0;
+ while (num_seq > 0)
+ {
+ if (num_seq > 100)
+ temp_count = 100;
+ else
+ temp_count = num_seq;
+ num_seq = num_seq - temp_count;
+ sprintf(&listinfo[0], "%d beginbfrange\n", temp_count);
+ fz_write_buffer(ctx, fzbuf, listinfo, strlen(listinfo));
+ k = 0;
+ while (k < temp_count)
+ {
+ k = k + 1;
+ k_start = seq[pos * 2];
+ k_end = seq[pos * 2 + 1];
+ sprintf(&hex_start[0], "%04x", k_start);
+ sprintf(&hex_end[0], "%04x", k_end);
+ sprintf(&hex_value_start[0], "%04x", table[k_start]);
+ sprintf(&entry[0], "<%s> <%s> <%s>\n", hex_start, hex_end, hex_value_start);
+ fz_write_buffer(ctx, fzbuf, entry, strlen(entry));
+
+ /* Clear out these values from the table so they are not
+ * used in the single entry */
+ count = count - (k_end - k_start + 1);
+ for (j = k_start; j < k_end + 1; j++)
+ table[j] = 0;
+ pos = pos + 1;
+ }
+ fz_write_buffer(ctx, fzbuf, "endbfrange\n", strlen("endbfrange\n"));
+ }
+ }
+ }
+
+ /* The rest of the values need to be output as individuals */
+ pos = 0;
+ while (count > 0)
+ {
+ if (count > 100)
+ temp_count = 100;
+ else
+ temp_count = count;
+ count = count - temp_count;
+ sprintf(&listinfo[0], "%d beginbfchar\n", temp_count);
+ fz_write_buffer(ctx, fzbuf, listinfo, strlen(listinfo));
+ k = 0;
+ while (k < temp_count)
+ {
+ if (table[pos] > 0)
+ {
+ k = k + 1;
+ sprintf(&hex_glyph[0], "%04x", pos);
+ sprintf(&hex_unicode[0], "%04x", table[pos]);
+ sprintf(&entry[0], "<%s> <%s>\n", hex_glyph, hex_unicode);
+ fz_write_buffer(ctx, fzbuf, entry, strlen(entry));
+ }
+ pos = pos + 1;
+ }
+ fz_write_buffer(ctx, fzbuf, "endbfchar\n", strlen("endbfchar\n"));
+ }
+ fz_write_buffer(ctx, fzbuf, "endcmap\n", strlen("endcmap\n"));
+ fz_write_buffer(ctx, fzbuf, "CMapName currentdict /CMap defineresource pop\n", strlen("CMapName currentdict /CMap defineresource pop\n"));
+ fz_write_buffer(ctx, fzbuf, "end\nend\n", strlen("end\nend\n"));
+
+ fobj = pdf_new_dict(ctx, doc, 3);
+ fref = pdf_new_ref(ctx, doc, fobj);
+ pdf_update_stream(ctx, doc, fref, fzbuf, 0);
+ }
+ fz_always(ctx)
+ {
+ fz_free(ctx, table);
+ fz_free(ctx, seq);
+ fz_drop_buffer(ctx, fzbuf);
+ pdf_drop_obj(ctx, fobj);
+ }
+ fz_catch(ctx)
+ {
+ if (has_lock)
+ fz_unlock(ctx, FZ_LOCK_FREETYPE);
+ pdf_drop_obj(ctx, fref);
+ fz_rethrow(ctx);
+ }
+ return fref;
+}
+
+/* Creates CID font with Identity-H CMap and a ToUnicode CMap that is created by
+ * using the TTF cmap table "backwards" to go from the GID to a Unicode value.
+ * If this is coming from a source file, we have source_font so that we can
+ * possibly get any width information that may have been embedded in the PDF
+ * W name tag (or W2 if vertical text) */
+pdf_res*
+pdf_add_cid_font_res(fz_context *ctx, pdf_document *doc, fz_buffer *buffer, fz_font *source_font)
+{
+ pdf_obj *fobj = NULL;
+ pdf_obj *fref = NULL;
+ pdf_res *fres = NULL;
+ pdf_obj *obj_desc_ref = NULL;
+ pdf_obj *obj_tounicode_ref = NULL;
+ pdf_obj *obj_array = NULL;
+ pdf_font_desc *fontdesc = NULL;
+ fz_font *font = NULL;
+ FT_Face face;
+ FT_Error fterr;
+ int has_lock = 0;
+ unsigned char digest[16];
+
+ fz_var(fobj);
+ fz_var(fref);
+ fz_var(fres);
+ fz_var(obj_desc_ref);
+ fz_var(obj_tounicode_ref);
+ fz_var(fontdesc);
+ fz_var(font);
+ fz_var(obj_array);
+ fz_var(has_lock);
+
+ fz_try(ctx)
+ {
+ /* Before we add this font as a resource check if the same font
+ * already exists in our resources for this doc. If yes, then
+ * hand back that reference */
+ fres = pdf_resource_table_search(ctx, doc, doc->resources->font,
+ (void*)buffer, (void*)&(digest[0]));
+ if (fres == NULL)
+ {
+ /* Set up desc, width, and font file */
+ font = fz_new_font_from_memory(ctx, NULL, buffer->data, buffer->len, 0, 1);
+ fontdesc = pdf_new_font_desc(ctx);
+ fontdesc->font = font;
+ face = font->ft_face;
+ fz_lock(ctx, FZ_LOCK_FREETYPE);
+ has_lock = 1;
+ fterr = FT_Set_Char_Size(face, 1000, 1000, 72, 72);
+ if (fterr)
+ fz_warn(ctx, "pdf_add_cid_font_res (FT_Set_Char_Size): %s", ft_error_string(fterr));
+ pdf_fontdesc_init(ctx, fontdesc);
+ fz_unlock(ctx, FZ_LOCK_FREETYPE);
+ has_lock = 0;
+
+ /* Get the descendant font and the tounicode references */
+ obj_desc_ref = pdf_add_descendant_font(ctx, doc, buffer, fontdesc, source_font);
+ obj_tounicode_ref = pdf_add_cid_to_unicode(ctx, doc, fontdesc);
+
+ /* And now the font */
+ fobj = pdf_new_dict(ctx, doc, 3);
+ pdf_dict_put_drop(ctx, fobj, PDF_NAME_Type, PDF_NAME_Font);
+ pdf_dict_put_drop(ctx, fobj, PDF_NAME_Subtype, PDF_NAME_Type0);
+ pdf_dict_put_drop(ctx, fobj, PDF_NAME_BaseFont, pdf_new_name(ctx, doc, fontdesc->font->name));
+ pdf_dict_put_drop(ctx, fobj, PDF_NAME_Encoding, PDF_NAME_Identity_H);
+
+ obj_array = pdf_new_array(ctx, doc, 3);
+ pdf_array_insert_drop(ctx, obj_array, obj_desc_ref, 0);
+ pdf_dict_put_drop(ctx, fobj, PDF_NAME_DescendantFonts, obj_array);
+ pdf_dict_put_drop(ctx, fobj, PDF_NAME_ToUnicode, obj_tounicode_ref);
+ fref = pdf_new_ref(ctx, doc, fobj);
+
+ /* Add ref to our font resource hash table. */
+ fres = pdf_resource_table_put(ctx, doc->resources->font, digest, fref);
+ }
+ }
+ fz_always(ctx)
+ {
+ fz_drop_font(ctx, font);
+ if (fontdesc != NULL)
+ fontdesc->font = NULL;
+ pdf_drop_font(ctx, fontdesc);
+ pdf_drop_obj(ctx, fobj);
+ }
+ fz_catch(ctx)
+ {
+ if (has_lock)
+ fz_unlock(ctx, FZ_LOCK_FREETYPE);
+ pdf_drop_obj(ctx, obj_desc_ref);
+ pdf_drop_obj(ctx, obj_array);
+ pdf_drop_obj(ctx, obj_tounicode_ref);
+ pdf_drop_obj(ctx, fref);
+ fz_rethrow(ctx);
+ }
+ return fres;
+}
+
+/* Creates simple font */
+pdf_res*
+pdf_add_simple_font_res(fz_context *ctx, pdf_document *doc, fz_buffer *buffer)
+{
+ pdf_obj *fobj = NULL;
+ pdf_obj *fref = NULL;
+ pdf_res *fres = NULL;
+ pdf_obj *fstr_ref = NULL;
+ pdf_obj *fdes_ref = NULL;
+ pdf_obj *fwidth_ref = NULL;
+ pdf_font_desc *fontdesc;
+ fz_font *font;
+ FT_Face face;
+ FT_Error fterr;
+ const char *ps_name;
+ int firstcode;
+ int has_lock = 0;
+ unsigned char digest[16];
+
+ fz_var(fobj);
+ fz_var(fref);
+ fz_var(fres);
+ fz_var(fstr_ref);
+ fz_var(fdes_ref);
+ fz_var(fwidth_ref);
+ fz_var(fontdesc);
+ fz_var(font);
+ fz_var(has_lock);
+
+ fz_try(ctx)
+ {
+ /* Before we add this font as a resource check if the same font
+ * already exists in our resources for this doc. If yes, then
+ * hand back that reference */
+ fres = pdf_resource_table_search(ctx, doc, doc->resources->font,
+ (void*)buffer, (void*)&(digest[0]));
+ if (fres == NULL)
+ {
+ /* Set up desc, width, and font file */
+ fobj = pdf_new_dict(ctx, doc, 3);
+ font = fz_new_font_from_memory(ctx, NULL, buffer->data, buffer->len, 0, 1);
+ font->width_count = 256;
+ font->width_table = fz_calloc(ctx, font->width_count, sizeof(int));
+ fontdesc = pdf_new_font_desc(ctx);
+ fontdesc->font = font;
+ face = font->ft_face;
+ fz_lock(ctx, FZ_LOCK_FREETYPE);
+ has_lock = 1;
+ fterr = FT_Set_Char_Size(face, 1000, 1000, 72, 72);
+ if (fterr)
+ fz_warn(ctx, "pdf_add_simple_font_res: %s", ft_error_string(fterr));
+ ft_width_for_simple_table(ctx, fontdesc, &firstcode);
+ pdf_fontdesc_init(ctx, fontdesc);
+ fz_unlock(ctx, FZ_LOCK_FREETYPE);
+ has_lock = 0;
+
+ /* refs */
+ fstr_ref = pdf_font_stream_ref(ctx, doc, buffer);
+ fdes_ref = pdf_font_desc_ref(ctx, doc, fontdesc, fstr_ref);
+ fwidth_ref = pdf_font_widths_ref(ctx, doc, fontdesc);
+
+ /* And now the font */
+ pdf_dict_put_drop(ctx, fobj, PDF_NAME_Type, PDF_NAME_Font);
+ pdf_dict_put_drop(ctx, fobj, PDF_NAME_BaseFont, pdf_new_name(ctx, doc, fontdesc->font->name));
+ pdf_dict_put_drop(ctx, fobj, PDF_NAME_Encoding, PDF_NAME_WinAnsiEncoding);
+ pdf_dict_put_drop(ctx, fobj, PDF_NAME_FirstChar, pdf_new_int(ctx, doc, fontdesc->font->first_width));
+ pdf_dict_put_drop(ctx, fobj, PDF_NAME_LastChar, pdf_new_int(ctx, doc, fontdesc->font->last_width));
+ pdf_dict_put_drop(ctx, fobj, PDF_NAME_Widths, fwidth_ref);
+ pdf_dict_put_drop(ctx, fobj, PDF_NAME_FontDescriptor, fdes_ref);
+ if ((ps_name = FT_Get_Postscript_Name(face)) != NULL)
+ pdf_dict_put_drop(ctx, fobj, PDF_NAME_BaseFont, pdf_new_string(ctx, doc, ps_name, strlen(ps_name)));
+ switch (ft_kind(face))
+ {
+ case TYPE1:
+ pdf_dict_put_drop(ctx, fobj, PDF_NAME_Subtype, PDF_NAME_Type1);
+ break;
+ case TRUETYPE:
+ pdf_dict_put_drop(ctx, fobj, PDF_NAME_Subtype, PDF_NAME_TrueType);
+ break;
+ case UNKNOWN:
+ fz_throw(ctx, FZ_ERROR_GENERIC, "Unknown font subtype");
+ break;
+ }
+ fref = pdf_new_ref(ctx, doc, fobj);
+
+ /* Add ref to our font resource hash table. */
+ fres = pdf_resource_table_put(ctx, doc->resources->font, digest, fref);
+ }
+ }
+ fz_always(ctx)
+ {
+ fz_drop_font(ctx, font);
+ if (fontdesc != NULL)
+ fontdesc->font = NULL;
+ pdf_drop_font(ctx, fontdesc);
+ pdf_drop_obj(ctx, fobj);
+ }
+ fz_catch(ctx)
+ {
+ if (has_lock)
+ fz_unlock(ctx, FZ_LOCK_FREETYPE);
+ pdf_drop_obj(ctx, fstr_ref);
+ pdf_drop_obj(ctx, fdes_ref);
+ pdf_drop_obj(ctx, fwidth_ref);
+ pdf_drop_obj(ctx, fref);
+ fz_rethrow(ctx);
+ }
+ return fres;
+}
+
+int
+pdf_font_writing_supported(fz_font *font)
+{
+ if (font->ft_face == NULL)
+ return 0;
+
+ if (ft_kind(font->ft_face) == TYPE1 || ft_kind(font->ft_face) == TRUETYPE)
+ {
+ return 1;
+ }
+ return 0;
+}
diff --git a/source/pdf/pdf-form.c b/source/pdf/pdf-form.c
index 78adf542..e62c954d 100644
--- a/source/pdf/pdf-form.c
+++ b/source/pdf/pdf-form.c
@@ -23,7 +23,7 @@ static int pdf_field_dirties_document(fz_context *ctx, pdf_document *doc, pdf_ob
return 1;
}
-/* Find the point in a field hierarchy where all descendents
+/* Find the point in a field hierarchy where all descendants
* share the same name */
static pdf_obj *find_head_of_field_group(fz_context *ctx, pdf_obj *obj)
{
diff --git a/source/pdf/pdf-image.c b/source/pdf/pdf-image.c
index b48a0ba5..78828776 100644
--- a/source/pdf/pdf-image.c
+++ b/source/pdf/pdf-image.c
@@ -289,3 +289,178 @@ pdf_load_image(fz_context *ctx, pdf_document *doc, pdf_obj *dict)
pdf_store_item(ctx, dict, image, fz_image_size(ctx, image));
return image;
}
+
+pdf_res*
+pdf_add_image_res(fz_context *ctx, pdf_document *doc, fz_image *image, int mask)
+{
+ fz_pixmap *pixmap = NULL;
+ pdf_obj *imobj = NULL;
+ pdf_obj *imref = NULL;
+ pdf_res *imres = NULL;
+ fz_compressed_buffer *cbuffer = NULL;
+ fz_compression_params *cp = NULL;
+ fz_buffer *buffer = NULL;
+ fz_colorspace *colorspace = image->colorspace;
+ unsigned char digest[16];
+
+ /* If we can maintain compression, do so */
+ cbuffer = image->buffer;
+
+ fz_var(pixmap);
+ fz_var(buffer);
+ fz_var(imobj);
+ fz_var(imref);
+ fz_var(imres);
+
+ fz_try(ctx)
+ {
+ /* Before we add this image as a resource check if the same image
+ * already exists in our resources for this doc. If yes, then
+ * hand back that reference */
+ imres = pdf_resource_table_search(ctx, doc, doc->resources->image,
+ (void*)image, (void*) &(digest[0]));
+ if (imres == NULL)
+ {
+ if (cbuffer != NULL && cbuffer->params.type != FZ_IMAGE_PNG && cbuffer->params.type != FZ_IMAGE_TIFF)
+ {
+ buffer = fz_keep_buffer(ctx, cbuffer->buffer);
+ cp = &cbuffer->params;
+ }
+ else
+ {
+ unsigned int size;
+ int n;
+
+ /* Currently, set to maintain resolution; should we consider
+ * subsampling here according to desired output res? */
+ pixmap = fz_get_pixmap_from_image(ctx, image, image->w, image->h);
+ colorspace = pixmap->colorspace; /* May be different to image->colorspace! */
+ n = (pixmap->n == 1 ? 1 : pixmap->n - 1);
+ size = image->w * image->h * n;
+ buffer = fz_new_buffer(ctx, size);
+ buffer->len = size;
+ if (pixmap->n == 1)
+ {
+ memcpy(buffer->data, pixmap->samples, size);
+ }
+ else
+ {
+ /* Need to remove the alpha plane */
+ unsigned char *d = buffer->data;
+ unsigned char *s = pixmap->samples;
+ int mod = n;
+ while (size--)
+ {
+ *d++ = *s++;
+ mod--;
+ if (mod == 0)
+ s++, mod = n;
+ }
+ }
+ }
+
+ imobj = pdf_new_dict(ctx, doc, 3);
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_Type, PDF_NAME_XObject);
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_Subtype, PDF_NAME_Image);
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_Width, pdf_new_int(ctx, doc, image->w));
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_Height, pdf_new_int(ctx, doc, image->h));
+ if (mask)
+ {
+ }
+ else if (!colorspace || colorspace->n == 1)
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_ColorSpace, PDF_NAME_DeviceGray);
+ else if (colorspace->n == 3)
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_ColorSpace, PDF_NAME_DeviceRGB);
+ else if (colorspace->n == 4)
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_ColorSpace, PDF_NAME_DeviceCMYK);
+ if (!mask)
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_BitsPerComponent, pdf_new_int(ctx, doc, image->bpc));
+ switch (cp ? cp->type : FZ_IMAGE_UNKNOWN)
+ {
+ case FZ_IMAGE_UNKNOWN: /* Unknown also means raw */
+ default:
+ break;
+ case FZ_IMAGE_JPEG:
+ if (cp->u.jpeg.color_transform != -1)
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_ColorTransform, pdf_new_int(ctx, doc, cp->u.jpeg.color_transform));
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_Filter, PDF_NAME_DCTDecode);
+ break;
+ case FZ_IMAGE_JPX:
+ if (cp->u.jpx.smask_in_data)
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_SMaskInData, pdf_new_int(ctx, doc, cp->u.jpx.smask_in_data));
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_Filter, PDF_NAME_JPXDecode);
+ break;
+ case FZ_IMAGE_FAX:
+ if (cp->u.fax.columns)
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_Columns, pdf_new_int(ctx, doc, cp->u.fax.columns));
+ if (cp->u.fax.rows)
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_Rows, pdf_new_int(ctx, doc, cp->u.fax.rows));
+ if (cp->u.fax.k)
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_K, pdf_new_int(ctx, doc, cp->u.fax.k));
+ if (cp->u.fax.end_of_line)
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_EndOfLine, pdf_new_int(ctx, doc, cp->u.fax.end_of_line));
+ if (cp->u.fax.encoded_byte_align)
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_EncodedByteAlign, pdf_new_int(ctx, doc, cp->u.fax.encoded_byte_align));
+ if (cp->u.fax.end_of_block)
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_EndOfBlock, pdf_new_int(ctx, doc, cp->u.fax.end_of_block));
+ if (cp->u.fax.black_is_1)
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_BlackIs1, pdf_new_int(ctx, doc, cp->u.fax.black_is_1));
+ if (cp->u.fax.damaged_rows_before_error)
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_DamagedRowsBeforeError, pdf_new_int(ctx, doc, cp->u.fax.damaged_rows_before_error));
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_Filter, PDF_NAME_CCITTFaxDecode);
+ break;
+ case FZ_IMAGE_JBIG2:
+ /* FIXME - jbig2globals */
+ cp->type = FZ_IMAGE_UNKNOWN;
+ break;
+ case FZ_IMAGE_FLATE:
+ if (cp->u.flate.columns)
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_Columns, pdf_new_int(ctx, doc, cp->u.flate.columns));
+ if (cp->u.flate.colors)
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_Colors, pdf_new_int(ctx, doc, cp->u.flate.colors));
+ if (cp->u.flate.predictor)
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_Predictor, pdf_new_int(ctx, doc, cp->u.flate.predictor));
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_Filter, PDF_NAME_FlateDecode);
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_BitsPerComponent, pdf_new_int(ctx, doc, image->bpc));
+ break;
+ case FZ_IMAGE_LZW:
+ if (cp->u.lzw.columns)
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_Columns, pdf_new_int(ctx, doc, cp->u.lzw.columns));
+ if (cp->u.lzw.colors)
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_Colors, pdf_new_int(ctx, doc, cp->u.lzw.colors));
+ if (cp->u.lzw.predictor)
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_Predictor, pdf_new_int(ctx, doc, cp->u.lzw.predictor));
+ if (cp->u.lzw.early_change)
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_EarlyChange, pdf_new_int(ctx, doc, cp->u.lzw.early_change));
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_Filter, PDF_NAME_LZWDecode);
+ break;
+ case FZ_IMAGE_RLD:
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_Filter, PDF_NAME_RunLengthDecode);
+ break;
+ }
+ if (mask)
+ {
+ pdf_dict_put_drop(ctx, imobj, PDF_NAME_ImageMask, pdf_new_bool(ctx, doc, 1));
+ }
+ if (image->mask)
+ pdf_add_image_res(ctx, doc, image->mask, 0);
+ imref = pdf_new_ref(ctx, doc, imobj);
+ pdf_update_stream(ctx, doc, imref, buffer, 1);
+
+ /* Add ref to our image resource hash table. */
+ imres = pdf_resource_table_put(ctx, doc->resources->image, digest, imref);
+ }
+ }
+ fz_always(ctx)
+ {
+ fz_drop_buffer(ctx, buffer);
+ pdf_drop_obj(ctx, imobj);
+ fz_drop_pixmap(ctx, pixmap);
+ }
+ fz_catch(ctx)
+ {
+ pdf_drop_obj(ctx, imref);
+ fz_rethrow(ctx);
+ }
+ return imres;
+}
diff --git a/source/pdf/pdf-page.c b/source/pdf/pdf-page.c
index d893d204..30cbb4e9 100644
--- a/source/pdf/pdf-page.c
+++ b/source/pdf/pdf-page.c
@@ -697,15 +697,17 @@ pdf_delete_page_range(fz_context *ctx, pdf_document *doc, int start, int end)
}
pdf_page *
-pdf_create_page(fz_context *ctx, pdf_document *doc, fz_rect mediabox, int res, int rotate)
+pdf_create_page(fz_context *ctx, pdf_document *doc, fz_rect mediabox, fz_buffer *buffer, int rotate)
{
pdf_page *page = NULL;
- pdf_obj *pageobj;
+ pdf_obj *pageobj, *obj;
float userunit = 1;
fz_matrix ctm, tmp;
fz_rect realbox;
page = pdf_new_page(ctx, doc);
+ obj = NULL;
+ fz_var(obj);
fz_try(ctx)
{
@@ -724,7 +726,7 @@ pdf_create_page(fz_context *ctx, pdf_document *doc, fz_rect mediabox, int res, i
page->rotate = 360 - ((-page->rotate) % 360);
if (page->rotate >= 360)
page->rotate = page->rotate % 360;
- page->rotate = 90*((page->rotate + 45)/90);
+ page->rotate = 90 * ((page->rotate + 45) / 90);
if (page->rotate > 360)
page->rotate = 0;
pdf_dict_put_drop(ctx, pageobj, PDF_NAME_Rotate, pdf_new_int(ctx, doc, page->rotate));
@@ -735,12 +737,21 @@ pdf_create_page(fz_context *ctx, pdf_document *doc, fz_rect mediabox, int res, i
fz_pre_scale(fz_translate(&tmp, -realbox.x0, -realbox.y0), userunit, userunit);
fz_concat(&ctm, &ctm, &tmp);
page->ctm = ctm;
- /* Do not create a Contents, as an empty Contents dict is not
- * valid. See Bug 694712 */
+
+ if (buffer != NULL)
+ {
+ obj = pdf_new_dict(ctx, doc, 4);
+ page->contents = pdf_new_ref(ctx, doc, obj);
+ pdf_update_stream(ctx, doc, page->contents, buffer, 0);
+ pdf_drop_obj(ctx, obj);
+ obj = NULL;
+ pdf_dict_puts(ctx, pageobj, "Contents", page->contents);
+ }
}
fz_catch(ctx)
{
pdf_drop_obj(ctx, page->me);
+ pdf_drop_obj(ctx, obj);
fz_free(ctx, page);
fz_rethrow_message(ctx, "Failed to create page");
}
diff --git a/source/pdf/pdf-resources.c b/source/pdf/pdf-resources.c
new file mode 100644
index 00000000..31812b3c
--- /dev/null
+++ b/source/pdf/pdf-resources.c
@@ -0,0 +1,295 @@
+#include "mupdf/pdf.h"
+
+static void
+res_table_free(fz_context *ctx, pdf_res_table *table)
+{
+ int i, n;
+ pdf_res *res;
+
+ if (table == NULL)
+ return;
+ if (table->hash != NULL)
+ {
+ n = fz_hash_len(ctx, table->hash);
+ for (i = 0; i < n; i++)
+ {
+ void *v = fz_hash_get_val(ctx, table->hash, i);
+ if (v)
+ {
+ res = (pdf_res*)v;
+ pdf_drop_obj(ctx, res->obj);
+ fz_free(ctx, res);
+ }
+ }
+ fz_drop_hash(ctx, table->hash);
+ }
+ fz_free(ctx, table);
+}
+
+static void
+res_image_get_md5(fz_context *ctx, fz_image *image, unsigned char *digest)
+{
+ fz_pixmap *pixmap = NULL;
+ int n, size;
+ fz_buffer *buffer = NULL;
+ fz_md5 state;
+
+ fz_var(pixmap);
+ fz_var(buffer);
+
+ fz_try(ctx)
+ {
+ pixmap = fz_get_pixmap_from_image(ctx, image, 0, 0);
+ n = (pixmap->n == 1 ? 1 : pixmap->n - 1);
+ size = image->w * image->h * n;
+ buffer = fz_new_buffer(ctx, size);
+ buffer->len = size;
+ if (pixmap->n == 1)
+ {
+ memcpy(buffer->data, pixmap->samples, size);
+ }
+ else
+ {
+ /* Need to remove the alpha plane */
+ unsigned char *d = buffer->data;
+ unsigned char *s = pixmap->samples;
+ int mod = n;
+ while (size--)
+ {
+ *d++ = *s++;
+ mod--;
+ if (mod == 0)
+ s++, mod = n;
+ }
+ }
+ fz_md5_init(&state);
+ fz_md5_update(&state, buffer->data, buffer->len);
+ fz_md5_final(&state, digest);
+ }
+ fz_always(ctx)
+ {
+ fz_drop_pixmap(ctx, pixmap);
+ fz_drop_buffer(ctx, buffer);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow_message(ctx, "image md5 calculation failed");
+ }
+}
+
+/* Image specific methods */
+static void
+res_image_init(fz_context *ctx, pdf_document *doc, pdf_res_table *table)
+{
+ int len, k;
+ pdf_obj *obj;
+ pdf_obj *type;
+ pdf_res *res = NULL;
+ fz_image *image = NULL;
+ unsigned char digest[16];
+ int num = 0;
+
+ fz_var(obj);
+ fz_var(image);
+ fz_var(res);
+
+ fz_try(ctx)
+ {
+ table->hash = fz_new_hash_table(ctx, 4096, 16, -1);
+ len = pdf_count_objects(ctx, doc);
+ for (k = 1; k < len; k++)
+ {
+ obj = pdf_load_object(ctx, doc, k, 0);
+ type = pdf_dict_get(ctx, obj, PDF_NAME_Subtype);
+ if (pdf_name_eq(ctx, type, PDF_NAME_Image))
+ {
+ image = pdf_load_image(ctx, doc, obj);
+ res_image_get_md5(ctx, image, digest);
+ fz_drop_image(ctx, image);
+ image = NULL;
+
+ /* Don't allow overwrites. Number the resources for pdfwrite */
+ if (fz_hash_find(ctx, table->hash, (void *)digest) == NULL)
+ {
+ res = fz_malloc(ctx, sizeof(pdf_res));
+ res->num = num;
+ res->obj = obj;
+ num = num + 1;
+ fz_hash_insert(ctx, table->hash, (void *)digest, obj);
+ }
+ }
+ else
+ {
+ pdf_drop_obj(ctx, obj);
+ }
+ obj = NULL;
+ }
+ }
+ fz_always(ctx)
+ {
+ table->count = num;
+ fz_drop_image(ctx, image);
+ pdf_drop_obj(ctx, obj);
+ }
+ fz_catch(ctx)
+ {
+ res_table_free(ctx, table);
+ fz_rethrow_message(ctx, "image resources table failed to initialize");
+ }
+}
+
+static void*
+res_image_search(fz_context *ctx, pdf_document *doc, pdf_res_table *table, void *item,
+ void *md5)
+{
+ unsigned char digest[16];
+
+ fz_image *image = (fz_image*)item;
+ fz_hash_table *hash = table->hash;
+ pdf_res *res;
+
+ if (hash == NULL)
+ res_image_init(ctx, doc, doc->resources->image);
+ hash = doc->resources->image->hash;
+
+ /* Create md5 and see if we have the item in our table */
+ res_image_get_md5(ctx, image, digest);
+ res = fz_hash_find(ctx, hash, (void*)digest);
+
+ /* Return the digest value so that we can avoid having to recompute it when
+ * we come back to add the new resource reference */
+ if (res == NULL)
+ memcpy(md5, digest, 16);
+ else
+ pdf_keep_obj(ctx, res->obj);
+ return (void*) res;
+}
+
+/* Font specific methods */
+
+/* We do need to come up with an effective way to see what is already in the
+ * file to avoid adding to what is already there. This is avoided for pdfwrite
+ * as we check as we add each font. For adding text to an existing file though
+ * it may be more problematic */
+static void
+res_font_init(fz_context *ctx, pdf_document *doc, pdf_res_table *table)
+{
+ table->hash = fz_new_hash_table(ctx, 4096, 16, -1);
+}
+
+static void
+res_font_get_md5(fz_context *ctx, fz_buffer *buffer, unsigned char *digest)
+{
+ fz_md5 state;
+
+ fz_md5_init(&state);
+ fz_md5_update(&state, buffer->data, buffer->len);
+ fz_md5_final(&state, digest);
+}
+
+static void*
+res_font_search(fz_context *ctx, pdf_document *doc, pdf_res_table *table, void *item,
+ void *md5)
+{
+ unsigned char digest[16];
+ fz_buffer *buffer = (fz_buffer*)item;
+ fz_hash_table *hash = table->hash;
+ pdf_res *res;
+
+ if (hash == NULL)
+ res_font_init(ctx, doc, doc->resources->font);
+ hash = doc->resources->font->hash;
+
+ /* Create md5 and see if we have the item in our table */
+ res_font_get_md5(ctx, buffer, digest);
+ res = fz_hash_find(ctx, hash, (void*)digest);
+
+ /* Return the digest value so that we can avoid having to recompute it when
+ * we come back to add the new resource reference */
+ if (res == NULL)
+ memcpy(md5, digest, 16);
+ else
+ pdf_keep_obj(ctx, res->obj);
+ return (void*)res;
+}
+
+/* Accessible methods */
+void*
+pdf_resource_table_search(fz_context *ctx, pdf_document *doc, pdf_res_table *table,
+ void *item, void *md5)
+{
+ return table->search(ctx, doc, table, item, md5);
+}
+
+void*
+pdf_resource_table_put(fz_context *ctx, pdf_res_table *table, void *key, pdf_obj *obj)
+{
+ void *result;
+ pdf_res *res = NULL;
+
+ fz_var(res);
+
+ fz_try(ctx)
+ {
+ res = fz_malloc(ctx, sizeof(pdf_res));
+ res->num = table->count + 1;
+ res->obj = obj;
+ result = fz_hash_insert(ctx, table->hash, key, (void*)res);
+ if (result != NULL)
+ {
+ fz_free(ctx, res);
+ fz_warn(ctx, "warning: hash already present");
+ }
+ else
+ {
+ table->count = table->count + 1;
+ pdf_keep_obj(ctx, obj);
+ result = res;
+ }
+ }
+ fz_catch(ctx)
+ {
+ fz_free(ctx, res);
+ fz_rethrow(ctx);
+ }
+ return result;
+}
+
+void
+pdf_resource_table_free(fz_context *ctx, pdf_document *doc)
+{
+ if (doc->resources == NULL)
+ return;
+ res_table_free(ctx, doc->resources->color);
+ res_table_free(ctx, doc->resources->font);
+ res_table_free(ctx, doc->resources->image);
+ res_table_free(ctx, doc->resources->pattern);
+ res_table_free(ctx, doc->resources->shading);
+ fz_free(ctx, doc->resources);
+ doc->resources = NULL;
+}
+
+void
+pdf_resource_table_init(fz_context *ctx, pdf_document *doc)
+{
+ fz_var(doc);
+ fz_try(ctx)
+ {
+ doc->resources = fz_calloc(ctx, 1, sizeof(pdf_resource_tables));
+ doc->resources->image = fz_calloc(ctx, 1, sizeof(pdf_res_table));
+ doc->resources->image->search = res_image_search;
+ doc->resources->font = fz_calloc(ctx, 1, sizeof(pdf_res_table));
+ doc->resources->font->search = res_font_search;
+ }
+ fz_catch(ctx)
+ {
+ if (doc->resources != NULL)
+ {
+ fz_free(ctx, doc->resources->color);
+ fz_free(ctx, doc->resources->font);
+ fz_free(ctx, doc->resources);
+ doc->resources = NULL;
+ }
+ fz_rethrow_message(ctx, "resources failed to allocate");
+ }
+}
diff --git a/source/pdf/pdf-xref.c b/source/pdf/pdf-xref.c
index 3bcf5d79..32b1d6a2 100644
--- a/source/pdf/pdf-xref.c
+++ b/source/pdf/pdf-xref.c
@@ -1626,6 +1626,8 @@ pdf_close_document(fz_context *ctx, pdf_document *doc)
pdf_lexbuf_fin(ctx, &doc->lexbuf.base);
+ pdf_resource_table_free(ctx, doc);
+
fz_free(ctx, doc);
}
@@ -2744,6 +2746,7 @@ pdf_document *pdf_create_document(fz_context *ctx)
doc->num_incremental_sections = 0;
doc->xref_base = 0;
doc->disallow_new_increments = 0;
+ pdf_resource_table_init(ctx, doc);
pdf_get_populating_xref_entry(ctx, doc, 0);
trailer = pdf_new_dict(ctx, doc, 2);
pdf_dict_put_drop(ctx, trailer, PDF_NAME_Size, pdf_new_int(ctx, doc, 3));