summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--include/mupdf/fitz/font.h2
-rw-r--r--include/mupdf/pdf/document.h5
-rw-r--r--include/mupdf/pdf/font.h6
-rw-r--r--include/mupdf/pdf/resource.h42
-rw-r--r--platform/win32/libmupdf.vcproj8
-rw-r--r--platform/win32/mutool.vcproj4
-rw-r--r--platform/x11/pdfapp.c2
-rw-r--r--resources/pdf/names.txt2
-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
-rw-r--r--source/tools/mudraw.c2
-rw-r--r--source/tools/mutool.c2
-rw-r--r--source/tools/pdfcreate.c516
19 files changed, 2258 insertions, 278 deletions
diff --git a/Makefile b/Makefile
index b742f7d8..7dfeed61 100644
--- a/Makefile
+++ b/Makefile
@@ -232,7 +232,7 @@ $(OUT)/cmapdump.o : include/mupdf/pdf/cmap.h source/pdf/pdf-cmap.c source/pdf/pd
# --- Tools and Apps ---
MUTOOL := $(addprefix $(OUT)/, mutool)
-MUTOOL_OBJ := $(addprefix $(OUT)/tools/, mutool.o mudraw.o pdfclean.o pdfextract.o pdfinfo.o pdfposter.o pdfshow.o pdfpages.o)
+MUTOOL_OBJ := $(addprefix $(OUT)/tools/, mutool.o mudraw.o pdfclean.o pdfcreate.o pdfextract.o pdfinfo.o pdfposter.o pdfshow.o pdfpages.o)
$(MUTOOL_OBJ): $(FITZ_HDR) $(PDF_HDR)
$(MUTOOL) : $(MUPDF_LIB) $(THIRD_LIB)
$(MUTOOL) : $(MUTOOL_OBJ)
diff --git a/include/mupdf/fitz/font.h b/include/mupdf/fitz/font.h
index 51aa201e..ea992112 100644
--- a/include/mupdf/fitz/font.h
+++ b/include/mupdf/fitz/font.h
@@ -68,6 +68,8 @@ struct fz_font_s
int width_count;
short width_default; /* in 1000 units */
short *width_table; /* in 1000 units */
+ int first_width;
+ int last_width;
/* cached glyph metrics */
float *advance_cache;
diff --git a/include/mupdf/pdf/document.h b/include/mupdf/pdf/document.h
index 525b7b76..7f6a63d9 100644
--- a/include/mupdf/pdf/document.h
+++ b/include/mupdf/pdf/document.h
@@ -12,6 +12,7 @@ typedef struct pdf_annot_s pdf_annot;
typedef struct pdf_widget_s pdf_widget;
typedef struct pdf_hotspot_s pdf_hotspot;
typedef struct pdf_js_s pdf_js;
+typedef struct pdf_resource_tables_s pdf_resource_tables;
enum
{
@@ -255,6 +256,8 @@ struct pdf_document_s
int num_type3_fonts;
int max_type3_fonts;
fz_font **type3_fonts;
+
+ pdf_resource_tables *resources;
};
/*
@@ -266,7 +269,7 @@ struct pdf_document_s
*/
pdf_document *pdf_create_document(fz_context *ctx);
-pdf_page *pdf_create_page(fz_context *ctx, pdf_document *doc, fz_rect rect, int res, int rotate);
+pdf_page *pdf_create_page(fz_context *ctx, pdf_document *doc, fz_rect rect, fz_buffer *buffer, int rotate);
void pdf_insert_page(fz_context *ctx, pdf_document *doc, pdf_page *page, int at);
diff --git a/include/mupdf/pdf/font.h b/include/mupdf/pdf/font.h
index 17902198..f472f188 100644
--- a/include/mupdf/pdf/font.h
+++ b/include/mupdf/pdf/font.h
@@ -63,6 +63,7 @@ struct pdf_font_desc_s
float cap_height;
float x_height;
float missing_width;
+ float stem_v;
/* Encoding (CMap) */
pdf_cmap *encoding;
@@ -121,4 +122,9 @@ float pdf_text_stride(fz_context *ctx, pdf_font_desc *fontdesc, float fontsize,
void pdf_run_glyph(fz_context *ctx, pdf_document *doc, pdf_obj *resources, fz_buffer *contents, fz_device *dev, const fz_matrix *ctm, void *gstate, int nestedDepth);
+pdf_res* pdf_add_simple_font_res(fz_context *ctx, pdf_document *doc, fz_buffer *buffer);
+pdf_res* pdf_add_cid_font_res(fz_context *ctx, pdf_document *doc, fz_buffer *buffer, fz_font *font);
+
+int pdf_font_writing_supported(fz_font *font);
+
#endif
diff --git a/include/mupdf/pdf/resource.h b/include/mupdf/pdf/resource.h
index 868bd168..443123ee 100644
--- a/include/mupdf/pdf/resource.h
+++ b/include/mupdf/pdf/resource.h
@@ -9,6 +9,46 @@ void *pdf_find_item(fz_context *ctx, fz_store_drop_fn *drop, pdf_obj *key);
void pdf_remove_item(fz_context *ctx, fz_store_drop_fn *drop, pdf_obj *key);
/*
+ * Structures used for managing resource locations and avoiding multiple
+ * occurrences when resources are added to the document. The search for existing
+ * resources will be performed when we are first trying to add an item. Object
+ * refs are stored in an fz_hash_table structure using a hash of the md5 sum of
+ * the data, enabling rapid lookup.
+ */
+typedef struct pdf_res_s pdf_res;
+
+struct pdf_res_s
+{
+ int num;
+ pdf_obj *obj;
+};
+
+typedef struct pdf_res_table_s pdf_res_table;
+typedef void* (pdf_res_search_fn)(fz_context *ctx, pdf_document *doc, pdf_res_table *list,
+ void *item, void *val);
+
+struct pdf_res_table_s
+{
+ int count;
+ fz_hash_table *hash;
+ pdf_res_search_fn *search;
+};
+
+struct pdf_resource_tables_s
+{
+ pdf_res_table *image;
+ pdf_res_table *font;
+ pdf_res_table *color;
+ pdf_res_table *pattern;
+ pdf_res_table *shading;
+};
+
+void* pdf_resource_table_search(fz_context *ctx, pdf_document *doc, pdf_res_table *table, void *item, void *md5);
+void pdf_resource_table_init(fz_context *ctx, pdf_document *doc);
+void pdf_resource_table_free(fz_context *ctx, pdf_document *doc);
+void* pdf_resource_table_put(fz_context *ctx, pdf_res_table *table, void *key, pdf_obj *obj);
+
+/*
* Functions, Colorspaces, Shadings and Images
*/
@@ -24,6 +64,8 @@ int pdf_is_jpx_image(fz_context *ctx, pdf_obj *dict);
fz_image *pdf_load_image(fz_context *ctx, pdf_document *doc, pdf_obj *obj);
+pdf_res* pdf_add_image_res(fz_context *ctx, pdf_document *doc, fz_image *image, int mask);
+
/*
* Pattern
*/
diff --git a/platform/win32/libmupdf.vcproj b/platform/win32/libmupdf.vcproj
index 2b444ac6..0fdf568d 100644
--- a/platform/win32/libmupdf.vcproj
+++ b/platform/win32/libmupdf.vcproj
@@ -1238,6 +1238,14 @@
RelativePath="..\..\source\pdf\pdf-repair.c"
>
</File>
+ <File
+ RelativePath="..\..\source\pdf\pdf-resources.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\source\pdf\pdf-resources.c"
+ >
+ </File>
<File
RelativePath="..\..\source\pdf\pdf-run.c"
>
diff --git a/platform/win32/mutool.vcproj b/platform/win32/mutool.vcproj
index eec5372b..d3a30200 100644
--- a/platform/win32/mutool.vcproj
+++ b/platform/win32/mutool.vcproj
@@ -474,6 +474,10 @@
RelativePath="..\..\source\tools\pdfclean.c"
>
</File>
+ <File
+ RelativePath="..\..\source\tools\pdfcreate.c"
+ >
+ </File>
<File
RelativePath="..\..\source\tools\pdfextract.c"
>
diff --git a/platform/x11/pdfapp.c b/platform/x11/pdfapp.c
index 6b227ab3..0742d66b 100644
--- a/platform/x11/pdfapp.c
+++ b/platform/x11/pdfapp.c
@@ -255,7 +255,7 @@ static int make_fake_doc(pdfapp_t *app)
bounds.x1 = app->winw;
bounds.y1 = app->winh;
- newpage = pdf_create_page(ctx, pdf, bounds, 72, 0);
+ newpage = pdf_create_page(ctx, pdf, bounds, NULL, 0);
dev = pdf_page_write(ctx, pdf, newpage);
diff --git a/resources/pdf/names.txt b/resources/pdf/names.txt
index 1eeb3fa1..a173b6bb 100644
--- a/resources/pdf/names.txt
+++ b/resources/pdf/names.txt
@@ -300,12 +300,14 @@ Squiggly
Stamp
Standard
StdCF
+StemV
StmF
StrF
StrikeOut
SubFilter
Subtype
Subtype2
+Supplement
T
TR
TR2
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));
diff --git a/source/tools/mudraw.c b/source/tools/mudraw.c
index 47aab044..d63f936c 100644
--- a/source/tools/mudraw.c
+++ b/source/tools/mudraw.c
@@ -387,7 +387,7 @@ static void drawpage(fz_context *ctx, fz_document *doc, int pagenum)
tbounds = bounds;
fz_transform_rect(&tbounds, &ctm);
- newpage = pdf_create_page(ctx, pdfout, bounds, 72, 0);
+ newpage = pdf_create_page(ctx, pdfout, bounds, NULL, 0);
fz_try(ctx)
{
diff --git a/source/tools/mutool.c b/source/tools/mutool.c
index 6b61dab4..c662ffef 100644
--- a/source/tools/mutool.c
+++ b/source/tools/mutool.c
@@ -15,6 +15,7 @@ int pdfinfo_main(int argc, char *argv[]);
int pdfposter_main(int argc, char *argv[]);
int pdfshow_main(int argc, char *argv[]);
int pdfpages_main(int argc, char *argv[]);
+int pdfcreate_main(int argc, char *argv[]);
static struct {
int (*func)(int argc, char *argv[]);
@@ -28,6 +29,7 @@ static struct {
{ pdfpages_main, "pages", "show information about pdf pages" },
{ pdfposter_main, "poster", "split large page into many tiles" },
{ pdfshow_main, "show", "show internal pdf objects" },
+ { pdfcreate_main, "create", "create pdf document" },
};
static int
diff --git a/source/tools/pdfcreate.c b/source/tools/pdfcreate.c
new file mode 100644
index 00000000..4e4a622e
--- /dev/null
+++ b/source/tools/pdfcreate.c
@@ -0,0 +1,516 @@
+/*
+ * PDF creation tool: Tool for creating pdf content.
+ *
+ * Simple test bed to work with adding content and creating PDFs
+ *
+ */
+
+#include "mupdf/pdf.h"
+
+#define MAX_IMAGES 10
+#define MAX_FONTS 10
+#define MAX_REF_NAME 32
+
+enum { RES_FONT, RES_XOBJECT };
+
+static void usage(void)
+{
+ fprintf(stderr,
+ "usage: mutool create [-o output.pdf] [fonts][images] contents\n"
+ "\t-f\tfont label:font file\n"
+ "\t-i\timage label:image file\n"
+ "\tcontents file, defines page size, graphics, references fonts and images\n"
+ );
+ exit(1);
+}
+
+/* Simple structures to hold and manage contents */
+typedef struct doc_content_s doc_content;
+typedef struct resources_s resources;
+typedef struct page_resource_xref_s page_resource_xref;
+
+struct page_resource_xref_s
+{
+ char *res_name;
+ char res_ref[MAX_REF_NAME];
+ pdf_obj *obj;
+ int type;
+};
+
+struct resources_s
+{
+ char *name;
+ int ref;
+};
+
+struct doc_content_s
+{
+ fz_stream *stm;
+ int num_pages;
+ fz_point *page_sizes;
+ fz_off_t *content_offsets;
+ int *content_lengths;
+ resources *fonts;
+ int num_fonts;
+ resources *images;
+ int num_images;
+ page_resource_xref **ref_im_resources;
+ page_resource_xref **ref_font_resources;
+ int *num_page_im_res;
+ int *num_page_font_res;
+};
+
+/* Look for the presence of image or font refererences in the content */
+static void check_for_reference(fz_context *ctx, resources *resource, int num_res, char *buffer, int res_type, doc_content *doc, int page_num)
+{
+ int i;
+ char * pch;
+ int length;
+ char name[MAX_REF_NAME];
+ int res_num;
+
+ for (i = 0; i < num_res; i++)
+ {
+ pch = strchr(resource[i].name, ':');
+ if (pch != NULL)
+ {
+ length = pch - resource[i].name;
+ if (length < sizeof(name))
+ {
+ memcpy(name, resource[i].name, length);
+ name[length] = 0;
+ pch = strstr(buffer, name);
+ if (pch != NULL)
+ {
+ /* Resource reference is in the content for this document. Mark
+ * it as such so that we know to add it to the document */
+ resource[i].ref = 1;
+
+ /* Also note this page has a reference to this name so
+ * we can add it to the page resource list. Select from
+ * font or image */
+ if (res_type == RES_FONT)
+ {
+ res_num = doc->num_page_font_res[page_num];
+ doc->ref_font_resources[page_num][res_num].obj = NULL; /* Set later */
+ doc->ref_font_resources[page_num][res_num].res_name = resource[i].name;
+ doc->ref_font_resources[page_num][res_num].type = res_type;
+ memcpy(doc->ref_font_resources[page_num][res_num].res_ref, name, sizeof(name));
+ doc->num_page_font_res[page_num] += 1;
+ }
+ else
+ {
+ res_num = doc->num_page_im_res[page_num];
+ doc->ref_im_resources[page_num][res_num].obj = NULL; /* Set later */
+ doc->ref_im_resources[page_num][res_num].res_name = resource[i].name;
+ doc->ref_im_resources[page_num][res_num].type = res_type;
+ memcpy(doc->ref_im_resources[page_num][res_num].res_ref, name, sizeof(name));
+ doc->num_page_im_res[page_num] += 1;
+ }
+ }
+ }
+ else
+ fz_throw(ctx, FZ_ERROR_GENERIC, "Image/Font indirect name too long");
+ }
+ else
+ fz_throw(ctx, FZ_ERROR_GENERIC, "Internal parsing error");
+ }
+}
+
+static void drop_ref_objs(fz_context *ctx, page_resource_xref *refs, int num_refs)
+{
+ int i;
+
+ for (i = 0; i < num_refs; i++)
+ pdf_drop_obj(ctx, refs[i].obj);
+}
+
+static void free_contents(fz_context *ctx, doc_content *content)
+{
+ int i;
+
+ fz_free(ctx, content->content_offsets);
+ fz_free(ctx, content->content_lengths);
+ fz_free(ctx, content->page_sizes);
+ for (i = 0; i < content->num_pages; i++)
+ {
+ drop_ref_objs(ctx, content->ref_font_resources[i], content->num_page_font_res[i]);
+ fz_free(ctx, content->ref_font_resources[i]);
+ }
+ fz_free(ctx, content->ref_font_resources);
+ for (i = 0; i < content->num_pages; i++)
+ {
+ drop_ref_objs(ctx, content->ref_im_resources[i], content->num_page_im_res[i]);
+ fz_free(ctx, content->ref_im_resources[i]);
+ }
+ fz_free(ctx, content->num_page_im_res);
+ fz_free(ctx, content->num_page_font_res);
+ fz_free(ctx, content->ref_im_resources);
+ content->content_offsets = NULL;
+ content->page_sizes = NULL;
+ content->content_lengths = NULL;
+ fz_drop_stream(ctx, content->stm);
+}
+
+/* This is a VERY simple format to give us something to play with
+ * in terms of defining pages and content. Here we parse the contents
+ * defining our page sizes and the command locations for each page.
+ * The format is as follows:
+ * 1) Comment lines are preceded by %
+ * 2) Number of pages is indicated at the begining with /Pages #
+ * 3) Each page is indicated by /Page # [X Y] (# is zero based)
+ * 4) The content is in the form of simple PDF content stream that
+ * may included various drawing commands and reference the
+ * image and font resources.
+ * */
+static void init_parse_contents(fz_context *ctx, char *content_fn, doc_content *content)
+{
+ fz_stream *stm;
+ char buf[1024];
+ int page_count = 0;
+ fz_off_t pre_off;
+ int i;
+
+ fz_var(stm);
+
+ fz_try(ctx)
+ {
+ stm = fz_open_file(ctx, content_fn);
+ while (1)
+ {
+ pre_off = fz_tell(ctx, stm);
+ fz_read_line(ctx, stm, buf, sizeof buf);
+ if (buf[0] == '\0')
+ {
+ if (content->num_pages > (page_count + 1))
+ fz_throw(ctx, FZ_ERROR_GENERIC, "Missing defined pages");
+ else
+ {
+ content->content_lengths[page_count - 1] =
+ fz_tell(ctx, stm) - content->content_offsets[page_count - 1];
+ }
+ break;
+ }
+ if (buf[0] != '%')
+ {
+ if (strncmp(buf, "/Pages", strlen("/Pages")) == 0)
+ {
+ if ((content->num_pages = atoi(&(buf[strlen("/Pages")]))) <= 0)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "Page count invalid");
+ content->content_offsets = fz_malloc_array(ctx, content->num_pages, sizeof(fz_off_t));
+ content->content_lengths = fz_malloc_array(ctx, content->num_pages, sizeof(int));
+ content->page_sizes = fz_malloc_array(ctx, content->num_pages, sizeof(fz_point));
+ content->num_page_im_res = fz_calloc(ctx, content->num_pages, sizeof(int));
+ content->num_page_font_res = fz_calloc(ctx, content->num_pages, sizeof(int));
+ content->ref_font_resources = fz_malloc_array(ctx, content->num_pages, sizeof(page_resource_xref*));
+ content->ref_im_resources = fz_malloc_array(ctx, content->num_pages, sizeof(page_resource_xref*));
+ for (i = 0; i < content->num_pages; i++)
+ {
+ content->ref_font_resources[i] = fz_malloc_array(ctx, MAX_FONTS, sizeof(page_resource_xref));
+ content->ref_im_resources[i] = fz_malloc_array(ctx, MAX_FONTS, sizeof(page_resource_xref));
+ }
+ }
+ else if (strncmp(buf, "/Page", strlen("/Page")) == 0)
+ {
+ int page_num;
+ if (page_count > 0)
+ {
+ content->content_lengths[page_count - 1] =
+ (int)(pre_off - content->content_offsets[page_count - 1]);
+ }
+ if (sscanf(&(buf[strlen("/Page")]), "%d [%f %f]", &page_num,
+ &(content->page_sizes[page_count].x), &(content->page_sizes[page_count].y)) != 3)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "Page size invalid");
+ if (page_num < 0 || page_num >= content->num_pages)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "Page value invalid");
+ if (content->page_sizes[page_count].x < 0 || content->page_sizes[page_count].y < 0)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "Page dimensions invalid");
+ content->content_offsets[page_count] = fz_tell(ctx, stm);
+ page_count++;
+ }
+ else
+ {
+ check_for_reference(ctx, content->images, content->num_images, buf, RES_XOBJECT, content, page_count - 1);
+ check_for_reference(ctx, content->fonts, content->num_fonts, buf, RES_FONT, content, page_count - 1);
+ }
+ }
+ }
+ }
+ fz_catch(ctx)
+ {
+ free_contents(ctx, content);
+ fz_drop_stream(ctx, stm);
+ fz_rethrow(ctx);
+ }
+ content->stm = stm;
+}
+
+/* Get the page contents */
+static int get_page_contents(fz_context *ctx, int page_num, doc_content *content,
+ unsigned char *buffer)
+{
+ int size = content->content_lengths[page_num];
+
+ if (buffer == NULL)
+ return size;
+ fz_seek(ctx, content->stm, content->content_offsets[page_num], SEEK_SET);
+ fz_read(ctx, content->stm, buffer, size);
+ return size;
+}
+
+static void update_res(fz_context *ctx, int num_pages, int *num_res, page_resource_xref **page_resource,
+ char *res_name, pdf_obj *obj)
+{
+ int j, i;
+
+ for (j = 0; j < num_pages; j++)
+ for (i = 0; i < num_res[j]; i++)
+ if (strcmp(res_name, page_resource[j][i].res_name) == 0)
+ page_resource[j][i].obj = obj;
+}
+
+static pdf_obj* create_page_res_dict(fz_context *ctx, pdf_document *pdf,
+ page_resource_xref *ref_res, const char type[], int num_items)
+{
+ pdf_obj *dict = NULL;
+ int i;
+
+ if (num_items <= 0)
+ return NULL;
+
+ fz_var(dict);
+
+ fz_try(ctx)
+ {
+ dict = pdf_new_dict(ctx, pdf, num_items);
+ for (i = 0; i < num_items; i++)
+ {
+ char text[32];
+ snprintf(text, sizeof(text), "%s/%s", type, ref_res[i].res_ref);
+ pdf_dict_putp(ctx, dict, text, ref_res[i].obj);
+ }
+ }
+ fz_catch(ctx)
+ {
+ pdf_drop_obj(ctx, dict);
+ fz_rethrow(ctx);
+ }
+ return dict;
+}
+
+static int create_pdf(fz_context *ctx, char *output, resources fonts[], int num_fonts,
+ resources images[], int num_images, char *contents)
+{
+ fz_rect bounds;
+ pdf_document *pdf = NULL;
+ pdf_page *newpage = NULL;
+ unsigned char *buffer = NULL;
+ fz_buffer *fz_buf = NULL;
+ fz_buffer *im_font_buff = NULL;
+ fz_image *image = NULL;
+ pdf_obj *font_dict = NULL;
+ pdf_obj *im_dict = NULL;
+ pdf_res *im_res = NULL;
+ pdf_res *font_res = NULL;
+ pdf_write_options opts = { 0 };
+ doc_content content = { 0 };
+ int k;
+ int length;
+ char *pch;
+
+ fz_var(pdf);
+ fz_var(newpage);
+ fz_var(buffer);
+ fz_var(fz_buf);
+ fz_var(im_font_buff);
+ fz_var(image);
+ fz_var(font_dict);
+ fz_var(im_dict);
+ fz_var(im_res);
+ fz_var(font_res);
+
+ fz_try(ctx)
+ {
+ pdf = pdf_create_document(ctx);
+ content.num_fonts = num_fonts;
+ content.num_images = num_images;
+ content.fonts = fonts;
+ content.images = images;
+ init_parse_contents(ctx, contents, &content);
+
+ /* Add the resources, getting the reference numbers in the process. */
+ for (k = 0; k < content.num_images; k++)
+ {
+ if (content.images[k].ref)
+ {
+ /* Get the fz_image */
+ pch = strchr(content.images[k].name, ':');
+ if (pch != NULL)
+ {
+ im_font_buff = fz_read_file(ctx, &(pch[1]));
+ image = fz_new_image_from_buffer(ctx, im_font_buff);
+ fz_drop_buffer(ctx, im_font_buff);
+ im_font_buff = NULL;
+ im_res = pdf_add_image_res(ctx, pdf, image, 0);
+ fz_drop_image(ctx, image);
+ image = NULL;
+
+ /* Look through our image page resources and update the
+ * indirect reference number. Here we don't use the numbers
+ * set by the doc resource holder (i.e im_res->num) since we
+ * are using our own content specified for pdfcreate */
+ update_res(ctx, content.num_pages, content.num_page_im_res,
+ content.ref_im_resources, content.images[k].name, im_res->obj);
+ }
+ else
+ fz_throw(ctx, FZ_ERROR_GENERIC, "Image indirect name too long");
+ }
+ }
+ for (k = 0; k < content.num_fonts; k++)
+ {
+ if (content.fonts[k].ref)
+ {
+ pch = strchr(content.fonts[k].name, ':');
+ if (pch != NULL)
+ {
+ im_font_buff = fz_read_file(ctx, &(pch[1]));
+ font_res = pdf_add_simple_font_res(ctx, pdf, im_font_buff);
+ fz_drop_buffer(ctx, im_font_buff);
+ im_font_buff = NULL;
+
+ /* Look through our font page resources and update the indirect
+ * reference number */
+ update_res(ctx, content.num_pages, content.num_page_font_res,
+ content.ref_font_resources, content.fonts[k].name, font_res->obj);
+ }
+ else
+ fz_throw(ctx, FZ_ERROR_GENERIC, "Font indirect name too long");
+ }
+ }
+
+ /* Now the page contents */
+ for (k = 0; k < content.num_pages; k++)
+ {
+ bounds.x0 = 0;
+ bounds.y0 = 0;
+ bounds.x1 = content.page_sizes[k].x;
+ bounds.y1 = content.page_sizes[k].y;
+
+ length = get_page_contents(ctx, k, &content, NULL);
+ if (length > 0)
+ {
+ buffer = fz_malloc(ctx, length);
+ length = get_page_contents(ctx, k, &content, buffer);
+ fz_buf = fz_new_buffer_from_data(ctx, buffer, length);
+ buffer = NULL;
+ newpage = pdf_create_page(ctx, pdf, bounds, fz_buf, 0);
+ /* Create the dicts for the page resources */
+ font_dict = create_page_res_dict(ctx, pdf, content.ref_font_resources[k],
+ "Font", content.num_page_font_res[k]);
+ im_dict = create_page_res_dict(ctx, pdf, content.ref_im_resources[k],
+ "XObject", content.num_page_im_res[k]);
+ if (im_dict != NULL)
+ {
+ pdf_dict_puts(ctx, newpage->me, "Resources", im_dict);
+ pdf_drop_obj(ctx, im_dict);
+ }
+ if (font_dict != NULL)
+ {
+ pdf_dict_puts(ctx, newpage->me, "Resources", font_dict);
+ pdf_drop_obj(ctx, font_dict);
+ }
+ fz_drop_buffer(ctx, fz_buf);
+ fz_buf = NULL;
+ }
+ else
+ {
+ newpage = pdf_create_page(ctx, pdf, bounds, NULL, 0);
+ }
+ pdf_insert_page(ctx, pdf, newpage, INT_MAX);
+ pdf_drop_page(ctx, newpage);
+ newpage = NULL;
+ }
+ pdf_save_document(ctx, pdf, output, &opts);
+ }
+ fz_always(ctx)
+ {
+ pdf_drop_page(ctx, newpage);
+ pdf_close_document(ctx, pdf);
+ free_contents(ctx, &content);
+ fz_free(ctx, buffer);
+ fz_drop_buffer(ctx, fz_buf);
+ fz_drop_buffer(ctx, im_font_buff);
+ fz_drop_image(ctx, image);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+ return 0;
+}
+
+int pdfcreate_main(int argc, char **argv)
+{
+ char *outfile = "out.pdf";
+ resources fonts[MAX_FONTS];
+ resources images[MAX_IMAGES];
+ char *contents = "";
+ int nfonts = 0;
+ int nimages = 0;
+ int c;
+ int errors = 0;
+ fz_context *ctx;
+
+ while ((c = fz_getopt(argc, argv, "f:i:o:")) != -1)
+ {
+ switch (c)
+ {
+ case 'f':
+ if (nfonts == MAX_FONTS)
+ {
+ fprintf(stderr, "max number of fonts exceeded\n");
+ exit(1);
+ }
+ fonts[nfonts].name = fz_optarg;
+ fonts[nfonts].ref = 0;
+ nfonts++;
+ break;
+ case 'i':
+ if (nimages == MAX_IMAGES)
+ {
+ fprintf(stderr, "max number of images exceeded\n");
+ exit(1);
+ }
+ images[nimages].name = fz_optarg;
+ images[nimages].ref = 0;
+ nimages++;
+ break;
+ case 'o': outfile = fz_optarg; break;
+ default: usage(); break;
+ }
+ }
+
+ if (fz_optind == argc)
+ usage();
+
+ contents = argv[fz_optind++];
+
+ ctx = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED);
+ if (!ctx)
+ {
+ fprintf(stderr, "cannot initialise context\n");
+ exit(1);
+ }
+
+ fz_try(ctx)
+ {
+ create_pdf(ctx, outfile, fonts, nfonts, images, nimages, contents);
+ }
+ fz_catch(ctx)
+ {
+ errors++;
+ }
+ fz_drop_context(ctx);
+
+ return errors != 0;
+}