diff options
Diffstat (limited to 'source/pdf')
-rw-r--r-- | source/pdf/pdf-device.c | 444 | ||||
-rw-r--r-- | source/pdf/pdf-font.c | 1003 | ||||
-rw-r--r-- | source/pdf/pdf-form.c | 2 | ||||
-rw-r--r-- | source/pdf/pdf-image.c | 175 | ||||
-rw-r--r-- | source/pdf/pdf-page.c | 21 | ||||
-rw-r--r-- | source/pdf/pdf-resources.c | 295 | ||||
-rw-r--r-- | source/pdf/pdf-xref.c | 3 |
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)); |