? glib/poppler-features.h ? test/pdf-modification-test ? test/pdf-modification-test.cc Index: glib/poppler-document.cc =================================================================== RCS file: /cvs/poppler/poppler/glib/poppler-document.cc,v retrieving revision 1.37 diff -u -8 -p -r1.37 poppler-document.cc --- glib/poppler-document.cc 19 May 2006 22:12:38 -0000 1.37 +++ glib/poppler-document.cc 8 Sep 2006 21:25:57 -0000 @@ -1343,8 +1343,152 @@ poppler_ps_file_set_duplex (PopplerPSFil * **/ void poppler_ps_file_free (PopplerPSFile *ps_file) { g_return_if_fail (ps_file != NULL); g_object_unref (ps_file); } + + +//NeedAppearances needs to be set to 'true' in the AcroForm entry of the Catalog to enable dynamic appearance generation +void +check_for_need_appearances (PopplerDocument *document) +{ + //FIXME: static var are bad in shared libs + static GBool appearanceSet = gFalse; + if(!appearanceSet) { + XRef* xref = document->doc->getXRef(); + Object* catalog = new Object(); + Object acroForm; + Ref catRef; + catRef.gen = xref->getRootGen(); + catRef.num = xref->getRootNum(); + catalog = xref->getCatalog(catalog); + catalog->dictLookup("AcroForm", &acroForm); + Object obj1; + obj1.initBool(true); + acroForm.dictSet("NeedAppearances", &obj1); + obj1.free(); + catalog->dictSet("AcroForm", &acroForm); + document->doc->getXRef()->setModifiedObject(catalog, catRef); + appearanceSet = gTrue; + } +} + +void decode_id (int id, int *pageNum, int *fieldNum) +{ + (*pageNum) = id >> 16; + (*fieldNum) = (id << 16) >> 16; +} + +void +poppler_document_set_form_field_button_state (PopplerDocument *document, int id, int index, gboolean state) +{ + int pageNum, fieldNum; + decode_id(id, &pageNum, &fieldNum); + Catalog *catalog = document->doc->getCatalog(); + FormField *tmp = catalog->getPage(pageNum)->getForm()->getField(fieldNum); + FormFieldButton *field = NULL; + if (tmp->getType() != fieldButton) { + g_warning("poppler_document_set_form_field_button_state, invalid field type: %i\n", tmp->getType()); + return; + } + field = static_cast(tmp); + if (field) { + field->setKidState(index, (GBool)state); + //FIXME: should be moved to Form + check_for_need_appearances(document); + } +} + +gboolean +poppler_document_get_form_field_button_state (PopplerDocument *document, int id, int index) +{ + int pageNum, fieldNum; + decode_id(id, &pageNum, &fieldNum); + Catalog *catalog = document->doc->getCatalog(); + FormField *tmp = catalog->getPage(pageNum)->getForm()->getField(fieldNum); + FormFieldButton *field = NULL; + if (tmp->getType() != fieldButton) { + g_warning("poppler_document_set_form_field_button_state, invalid field type: %i\n", tmp->getType()); + return FALSE; + } + field = static_cast(tmp); + if(field) { + return (gboolean)field->getKidState(index); + } + return FALSE; +} + +void +poppler_document_set_form_field_text_content (PopplerDocument *document, int id, char *content) +{ + int pageNum, fieldNum; + decode_id(id, &pageNum, &fieldNum); + + Catalog *catalog = document->doc->getCatalog(); + FormField *tmp = catalog->getPage(pageNum)->getForm()->getField(fieldNum); + FormFieldText *field = NULL; + if (tmp->getType() != fieldText) { + g_warning("poppler_document_set_form_field_text_content, invalid field type: %i\n", tmp->getType()); + return ; + } + field = static_cast(tmp); + if (field) { + field->setContentCopy(content); + //FIXME: should be moved to Form + check_for_need_appearances(document); + } +} + +gchar * +poppler_document_get_form_field_text_content (PopplerDocument *document, int id) +{ + PopplerFormField *field = poppler_document_find_form_field_by_id(document, id); + if (field && field->type == POPPLER_FORM_FIELD_TEXT) + return field->text.content; + else { + g_warning("poppler_document_get_form_field_text_content, unknown id: %i", id); + return NULL; + } +} + +PopplerFormField * +poppler_document_find_form_field_by_id (PopplerDocument *document, int id) +{ + Catalog *catalog = document->doc->getCatalog(); + //decode id + int pageNum = id >> 16; + int fieldNum = (id << 16) >> 16; + FormField *field = catalog->getPage(pageNum)->getForm()->getField(fieldNum); + if (field != NULL) { + PopplerFormField *poppler_field = _form_field_new_from_field (field); + return poppler_field; + } else + return NULL; +} + +PopplerFormField * +poppler_document_get_form_field_button_kid (PopplerDocument *document, int id, int index) +{ + Catalog *catalog = document->doc->getCatalog(); + int pageNum = id >> 16; + int fieldNum = (id << 16) >> 16; + FormField *field = catalog->getPage(pageNum)->getForm()->getField(fieldNum); + if (field && field->getType() == fieldButton) { + FormFieldButton *btn = static_cast(field); + ButtonKid *kid = btn->getKid(index); + if (kid != NULL) { + PopplerFormField *poppler_field; + poppler_field = g_new(PopplerFormField, 1); + poppler_field->area.x1 = kid->x1; + poppler_field->area.y1 = kid->y1; + poppler_field->area.x2 = kid->x2; + poppler_field->area.y2 = kid->y2; + poppler_field->id = btn->getID(); + poppler_field->type = (PopplerFormFieldType)btn->getButtonType(); + return poppler_field; + } + } + return NULL; +} Index: glib/poppler-document.h =================================================================== RCS file: /cvs/poppler/poppler/glib/poppler-document.h,v retrieving revision 1.21 diff -u -8 -p -r1.21 poppler-document.h --- glib/poppler-document.h 19 May 2006 22:19:21 -0000 1.21 +++ glib/poppler-document.h 8 Sep 2006 21:25:59 -0000 @@ -103,16 +103,35 @@ PopplerPage *poppler_document_get_pa /* Attachments */ gboolean poppler_document_has_attachments (PopplerDocument *document); GList *poppler_document_get_attachments (PopplerDocument *document); /* Links */ PopplerDest *poppler_document_find_dest (PopplerDocument *document, const gchar *link_name); +/* Form */ +void poppler_document_set_form_field_text_content (PopplerDocument *document, + int id, + char *content); +PopplerFormField *poppler_document_find_form_field_by_id (PopplerDocument *document, + int id); +void poppler_document_set_form_field_button_state (PopplerDocument *document, int id, int index, gboolean state); + +gboolean poppler_document_get_form_field_button_state (PopplerDocument *document, int id, int index); + +gchar * +poppler_document_get_form_field_text_content (PopplerDocument *document, int id); + +PopplerFormField *poppler_document_get_form_field_button_kid (PopplerDocument *document, int id, int index); + + + + + /* Interface for getting the Index of a poppler_document */ #define POPPLER_TYPE_INDEX_ITER (poppler_index_iter_get_type ()) GType poppler_index_iter_get_type (void) G_GNUC_CONST; PopplerIndexIter *poppler_index_iter_new (PopplerDocument *document); PopplerIndexIter *poppler_index_iter_copy (PopplerIndexIter *iter); void poppler_index_iter_free (PopplerIndexIter *iter); PopplerIndexIter *poppler_index_iter_get_child (PopplerIndexIter *parent); Index: glib/poppler-page.cc =================================================================== RCS file: /cvs/poppler/poppler/glib/poppler-page.cc,v retrieving revision 1.50 diff -u -8 -p -r1.50 poppler-page.cc --- glib/poppler-page.cc 19 May 2006 21:42:54 -0000 1.50 +++ glib/poppler-page.cc 8 Sep 2006 21:25:59 -0000 @@ -980,8 +980,124 @@ poppler_link_mapping_copy (PopplerLinkMa void poppler_link_mapping_free (PopplerLinkMapping *mapping) { if (mapping) poppler_action_free (mapping->action); g_free (mapping); } + + +/* Form Type */ +GType +poppler_form_field_get_type (void) +{ + static GType our_type = 0; + if (our_type == 0) + our_type = g_boxed_type_register_static("PopplerFormField", + (GBoxedCopyFunc) poppler_form_field_copy, + (GBoxedFreeFunc) poppler_form_field_free); + return our_type; +} + +PopplerFormField* +poppler_form_field_new (void) +{ + return (PopplerFormField *) g_new0 (PopplerFormField, 1); +} + +PopplerFormField* +poppler_form_field_copy (PopplerFormField* field) +{ + PopplerFormField* new_field; + new_field = poppler_form_field_new(); + new_field = field; + return new_field; +} + +void +poppler_form_field_free (PopplerFormField* field) +{ + g_free (field); +} + +PopplerFormField * +_form_field_new_from_field (FormField* field) +{ + PopplerFormField *poppler_field = g_new(PopplerFormField, 1); + field->getRect (&(poppler_field->area.x1), &(poppler_field->area.y1), + &(poppler_field->area.x2), &(poppler_field->area.y2)); + + poppler_field->type = (PopplerFormFieldType)field->getType(); + poppler_field->id = field->getID(); + if (poppler_field->type == POPPLER_FORM_FIELD_TEXT) + poppler_field->text.content = static_cast(field)->getContentCopy(); + if (poppler_field->type == POPPLER_FORM_FIELD_BUTTON) + poppler_field->button.num_kids = static_cast(field)->getNumKids(); + return poppler_field; +} + +/** + * poppler_page_get_form_fields + **/ + +GList* +poppler_page_get_form_fields (PopplerPage *page) +{ + GList *field_list = NULL; + Form *form; + gint i; + Object obj; + + g_return_val_if_fail (POPPLER_IS_PAGE (page), NULL); + + //form = new Form(page->page->getAnnots (&obj)); + form = page->page->getForm(); + + obj.free (); + if(form == NULL) + return NULL; + + + for(i = 0; i < form->getNumFields(); i++) { + PopplerFormField *poppler_field /*= poppler_document_get_form_field_by_id(page->document,form->getField(i)->getID())*/; + FormField *field; + field = form->getField(i); + poppler_field = _form_field_new_from_field (field); + + /*poppler_field = g_new(PopplerFormField, 1); + field->getRect (&(poppler_field->area.x1), &(poppler_field->area.y1), + &(poppler_field->area.x2), &(poppler_field->area.y2)); + + poppler_field->type = (PopplerFormFieldType)field->getType(); + poppler_field->id = field->getID(); + if (poppler_field->type == POPPLER_FORM_FIELD_TEXT) + poppler_field->text.content = static_cast(field)->getContentCopy(); + if (poppler_field->type == POPPLER_FORM_FIELD_BUTTON) + poppler_field->button.num_kids = static_cast(field)->getNumKids();*/ + field_list = g_list_prepend(field_list,poppler_field); + } + return field_list; + +} + +void +poppler_page_free_form_fields (GList *list) +{ + if (list == NULL) + return; + + g_list_foreach (list, (GFunc) (poppler_form_field_free), NULL); + g_list_free (list); + +} + +void +poppler_page_get_crop_box (PopplerPage *page, PopplerRectangle *rect) +{ + PDFRectangle* cropBox = page->page->getCropBox(); + rect->x1 = cropBox->x1; + rect->x2 = cropBox->x2; + rect->y1 = cropBox->y1; + rect->y2 = cropBox->y2; +} + Index: glib/poppler-page.h =================================================================== RCS file: /cvs/poppler/poppler/glib/poppler-page.h,v retrieving revision 1.21 diff -u -8 -p -r1.21 poppler-page.h --- glib/poppler-page.h 12 Apr 2006 02:07:07 -0000 1.21 +++ glib/poppler-page.h 8 Sep 2006 21:26:00 -0000 @@ -75,16 +75,21 @@ GdkRegion * poppler_page_get_sel void poppler_page_render_selection (PopplerPage *page, gdouble scale, int rotation, GdkPixbuf *pixbuf, PopplerRectangle *selection, PopplerRectangle *old_selection, GdkColor *glyph_color, GdkColor *background_color); +GList *poppler_page_get_form_fields (PopplerPage *page); +void poppler_page_free_form_fields (GList *list); + +void poppler_page_get_crop_box (PopplerPage *page, + PopplerRectangle *rect); /* A rectangle on a page, with coordinates in PDF points. */ #define POPPLER_TYPE_RECTANGLE (poppler_rectangle_get_type ()) struct _PopplerRectangle { gdouble x1; gdouble y1; @@ -107,11 +112,64 @@ struct _PopplerLinkMapping PopplerAction *action; }; GType poppler_link_mapping_get_type (void) G_GNUC_CONST; PopplerLinkMapping *poppler_link_mapping_new (void); PopplerLinkMapping *poppler_link_mapping_copy (PopplerLinkMapping *mapping); void poppler_link_mapping_free (PopplerLinkMapping *mapping); +/* FormField */ +#define POPPLER_TYPE_FORM_FIELD (poppler_form_field_get_type ()) +struct _PopplerTextField +{ + //flags + char multitine:1; + char password:1; + char fileselect:1; + char do_not_spell_check:1; + char do_not_scroll:1; + char comb:1; + char rich_text:1; + //content + gchar *content; +}; + +struct _PopplerButtonField +{ + char no_toggle_to_of:1; + char radio:1; + char pushbutton:1; + char radios_in_unison:1; + int num_kids; +}; + +struct _PopplerChoiceField +{ + char combo:1; + char edit:1; + char sort:1; + char multi_select:1; + char do_not_spell_check:1; + char commit_on_sel_change:1; +}; + +struct _PopplerFormField +{ + PopplerRectangle area; + PopplerFormFieldType type; + int id; + union { + PopplerTextField text; + PopplerButtonField button; + PopplerChoiceField choice; + }; + +}; + +GType poppler_form_field_get_type (void) G_GNUC_CONST; +PopplerFormField *poppler_form_field_new (void); +PopplerFormField *poppler_form_field_copy (PopplerFormField *field); +void poppler_form_field_free (PopplerFormField *field); + G_END_DECLS #endif /* __POPPLER_PAGE_H__ */ Index: glib/poppler-private.h =================================================================== RCS file: /cvs/poppler/poppler/glib/poppler-private.h,v retrieving revision 1.16 diff -u -8 -p -r1.16 poppler-private.h --- glib/poppler-private.h 19 May 2006 20:54:13 -0000 1.16 +++ glib/poppler-private.h 8 Sep 2006 21:26:01 -0000 @@ -1,15 +1,16 @@ #ifndef __POPPLER_PRIVATE_H__ #define __POPPLER_PRIVATE_H__ #include #include #include #include +#include #include #include #include #include #include "poppler-attachment.h" #if defined (HAVE_CAIRO) @@ -55,16 +56,19 @@ struct _PopplerPage GObject parent_instance; PopplerDocument *document; Page *page; int index; TextOutputDev *text_dev; Gfx *gfx; }; +PopplerFormField *_form_field_new_from_field (FormField* field); + + PopplerPage *_poppler_page_new (PopplerDocument *document, Page *page, int index); PopplerAction *_poppler_action_new (PopplerDocument *document, LinkAction *link, const gchar *title); PopplerDest *_poppler_dest_new_goto (PopplerDocument *document, LinkDest *link_dest); Index: glib/poppler.h =================================================================== RCS file: /cvs/poppler/poppler/glib/poppler.h,v retrieving revision 1.12 diff -u -8 -p -r1.12 poppler.h --- glib/poppler.h 16 Apr 2006 22:59:44 -0000 1.12 +++ glib/poppler.h 8 Sep 2006 21:26:01 -0000 @@ -37,27 +37,44 @@ typedef enum typedef enum { POPPLER_ORIENTATION_PORTRAIT, POPPLER_ORIENTATION_LANDSCAPE, POPPLER_ORIENTATION_UPSIDEDOWN, POPPLER_ORIENTATION_SEASCAPE } PopplerOrientation; +/* MUST be the same than poppler/Form.h fieldType */ +typedef enum +{ + POPPLER_FORM_FIELD_BUTTON, + POPPLER_FORM_FIELD_TEXT, + POPPLER_FORM_FIELD_CHOICE, + POPPLER_FORM_FIELD_SIGNATURE, + //kid type + POPPLER_FORM_FIELD_BUTTON_CHECK, + POPPLER_FORM_FIELD_BUTTON_PUSH, + POPPLER_FORM_FIELD_BUTTON_RADIO +} PopplerFormFieldType; + + typedef struct _PopplerDocument PopplerDocument; typedef struct _PopplerIndexIter PopplerIndexIter; typedef struct _PopplerFontsIter PopplerFontsIter; typedef struct _PopplerRectangle PopplerRectangle; typedef struct _PopplerLinkMapping PopplerLinkMapping; +typedef struct _PopplerFormField PopplerFormField; typedef struct _PopplerPage PopplerPage; typedef struct _PopplerFontInfo PopplerFontInfo; typedef struct _PopplerPSFile PopplerPSFile; typedef union _PopplerAction PopplerAction; typedef struct _PopplerDest PopplerDest; - +typedef struct _PopplerTextField PopplerTextField; +typedef struct _PopplerButtonField PopplerButtonField; +typedef struct _PopplerChoiceField PopplerChoiceField; typedef enum { POPPLER_BACKEND_UNKNOWN, POPPLER_BACKEND_SPLASH, POPPLER_BACKEND_CAIRO } PopplerBackend; Index: poppler/Annot.cc =================================================================== RCS file: /cvs/poppler/poppler/poppler/Annot.cc,v retrieving revision 1.5 diff -u -8 -p -r1.5 Annot.cc --- poppler/Annot.cc 6 Feb 2006 18:50:11 -0000 1.5 +++ poppler/Annot.cc 8 Sep 2006 21:26:01 -0000 @@ -22,23 +22,22 @@ #include "Annot.h" //------------------------------------------------------------------------ // Annot //------------------------------------------------------------------------ Annot::Annot(XRef *xrefA, Dict *acroForm, Dict *dict) { Object apObj, asObj, obj1, obj2; - GBool regen, isTextField; double t; ok = gFalse; xref = xrefA; appearBuf = NULL; - + if (dict->lookup("Rect", &obj1)->isArray() && obj1.arrayGetLength() == 4) { //~ should check object types here obj1.arrayGet(0, &obj2); xMin = obj2.getNum(); obj2.free(); obj1.arrayGet(1, &obj2); yMin = obj2.getNum(); @@ -66,64 +65,66 @@ Annot::Annot(XRef *xrefA, Dict *acroForm regen = gFalse; if (acroForm) { acroForm->lookup("NeedAppearances", &obj1); if (obj1.isBool() && obj1.getBool()) { regen = gTrue; } obj1.free(); } + regen = gTrue; // check for a text-type field isTextField = dict->lookup("FT", &obj1)->isName("Tx"); obj1.free(); -#if 0 //~ appearance stream generation is not finished yet +#if 1 //~ appearance stream generation is not finished yet if (regen && isTextField) { generateAppearance(acroForm, dict); } else { #endif if (dict->lookup("AP", &apObj)->isDict()) { if (dict->lookup("AS", &asObj)->isName()) { - if (apObj.dictLookup("N", &obj1)->isDict()) { - if (obj1.dictLookupNF(asObj.getName(), &obj2)->isRef()) { - obj2.copy(&appearance); - ok = gTrue; - } else { - obj2.free(); - if (obj1.dictLookupNF("Off", &obj2)->isRef()) { - obj2.copy(&appearance); - ok = gTrue; - } - } - obj2.free(); - } - obj1.free(); + if (apObj.dictLookup("N", &obj1)->isDict()) { + if (obj1.dictLookupNF(asObj.getName(), &obj2)->isRef()) { + obj2.copy(&appearance); + ok = gTrue; + } else { + obj2.free(); + if (obj1.dictLookupNF("Off", &obj2)->isRef()) { + obj2.copy(&appearance); + ok = gTrue; + } + } + obj2.free(); + } + obj1.free(); } else { - if (apObj.dictLookupNF("N", &obj1)->isRef()) { - obj1.copy(&appearance); - ok = gTrue; - } - obj1.free(); + if (apObj.dictLookupNF("N", &obj1)->isRef()) { + obj1.copy(&appearance); + ok = gTrue; + } + obj1.free(); } asObj.free(); } apObj.free(); -#if 0 //~ appearance stream generation is not finished yet +#if 1 //~ appearance stream generation is not finished yet } #endif } Annot::~Annot() { appearance.free(); if (appearBuf) { delete appearBuf; } } + void Annot::generateAppearance(Dict *acroForm, Dict *dict) { MemStream *appearStream; Object daObj, vObj, drObj, appearDict, obj1, obj2; GooString *daStr, *daStr1, *vStr, *s; char buf[256]; double fontSize; int c; int i0, i1; @@ -133,83 +134,92 @@ void Annot::generateAppearance(Dict *acr daStr = daObj.getString(); // look for a font size //~ may want to parse the DS entry in place of this (if it exists) daStr1 = NULL; fontSize = 10; for (i1 = daStr->getLength() - 2; i1 >= 0; --i1) { if (daStr->getChar(i1) == 'T' && daStr->getChar(i1+1) == 'f') { - for (--i1; i1 >= 0 && Lexer::isSpace(daStr->getChar(i1)); --i1) ; - for (i0 = i1; i0 >= 0 && !Lexer::isSpace(daStr->getChar(i0)); --i0) ; - if (i0 >= 0) { - ++i0; - ++i1; - s = new GooString(daStr, i0, i1 - i0); - fontSize = atof(s->getCString()); - delete s; - - // autosize the font - if (fontSize == 0) { - fontSize = 0.67 * (yMax - yMin); - daStr1 = new GooString(daStr, 0, i0); - sprintf(buf, "%.2f", fontSize); - daStr1->append(buf); - daStr1->append(daStr->getCString() + i1, - daStr->getLength() - i1); - } - } - break; + for (--i1; i1 >= 0 && Lexer::isSpace(daStr->getChar(i1)); --i1) ; + for (i0 = i1; i0 >= 0 && !Lexer::isSpace(daStr->getChar(i0)); --i0) ; + if (i0 >= 0) { + ++i0; + ++i1; + s = new GooString(daStr, i0, i1 - i0); + fontSize = atof(s->getCString()); + delete s; + + // autosize the font + if (fontSize == 0) { + fontSize = 0.67 * (yMax - yMin); + daStr1 = new GooString(daStr, 0, i0); + sprintf(buf, "%.2f", fontSize); + daStr1->append(buf); + daStr1->append(daStr->getCString() + i1, + daStr->getLength() - i1); + } + } + break; } } // build the appearance stream contents appearBuf = new GooString(); appearBuf->append("/Tx BMC\n"); appearBuf->append("q BT\n"); appearBuf->append(daStr1 ? daStr1 : daStr)->append("\n"); if (dict->lookup("V", &vObj)->isString()) { - //~ handle quadding -- this requires finding the font and using - //~ the encoding and char widths - sprintf(buf, "1 0 0 1 %.2f %.2f Tm\n", 2.0, yMax - yMin - fontSize); - appearBuf->append(buf); - sprintf(buf, "%g TL\n", fontSize); - appearBuf->append(buf); - vStr = vObj.getString(); - i0 = 0; - while (i0 < vStr->getLength()) { - for (i1 = i0; - i1 < vStr->getLength() && - vStr->getChar(i1) != '\n' && vStr->getChar(i1) != '\r'; - ++i1) ; - if (i0 > 0) { - appearBuf->append("T*\n"); - } - appearBuf->append('('); - for (; i0 < i1; ++i0) { - c = vStr->getChar(i0); - if (c == '(' || c == ')' || c == '\\') { - appearBuf->append('\\'); - appearBuf->append(c); - } else if (c < 0x20 || c >= 0x80) { - sprintf(buf, "\\%03o", c); + //~ handle quadding -- this requires finding the font and using + //~ the encoding and char widths + sprintf(buf, "1 0 0 1 %.2f %.2f Tm\n", 2.0, yMax - yMin - fontSize); appearBuf->append(buf); - } else { - appearBuf->append(c); - } - } - appearBuf->append(") Tj\n"); - if (i1 + 1 < vStr->getLength() && - vStr->getChar(i1) == '\r' && vStr->getChar(i1 + 1) == '\n') { - i0 = i1 + 2; - } else { - i0 = i1 + 1; - } - } + sprintf(buf, "%g TL\n", fontSize); + appearBuf->append(buf); + //FIXME: perhaps unicode handling could be better + UGooString tmp (*vObj.getString()); + vStr = new GooString(tmp.getCString()); + i0 = 0; + while (i0 < vStr->getLength()) { + for (i1 = i0; + i1 < vStr->getLength() && + vStr->getChar(i1) != '\n' && vStr->getChar(i1) != '\r'; + ++i1) ; + if (i0 > 0) { + appearBuf->append("T*\n"); + } + appearBuf->append('('); + for (; i0 < i1; ++i0) { + c = vStr->getChar(i0); + if (c == '(' || c == ')' || c == '\\') { + appearBuf->append('\\'); + appearBuf->append(c); + } else if (c < 0x20 || c >= 0x80) { + if (c == 0xFF || c == 0xFE) { + sprintf(buf, ""); + printf("fu\n"); + } + else + sprintf(buf, "\\%03o", c); + appearBuf->append(buf); + } else { + appearBuf->append(c); + } + } + appearBuf->append(") Tj\n"); + if (i1 + 1 < vStr->getLength() && + vStr->getChar(i1) == '\r' && vStr->getChar(i1 + 1) == '\n') { + i0 = i1 + 2; + } else { + i0 = i1 + 1; + } + } + delete vStr; } + vObj.free(); appearBuf->append("ET Q\n"); appearBuf->append("EMC\n"); // build the appearance stream dictionary appearDict.initDict(xref); appearDict.dictAdd("Length", obj1.initInt(appearBuf->getLength())); appearDict.dictAdd("Subtype", obj1.initName("Form")); @@ -220,54 +230,53 @@ void Annot::generateAppearance(Dict *acr obj1.arrayAdd(obj2.initReal(yMax - yMin)); appearDict.dictAdd("BBox", &obj1); // find the resource dictionary dict->lookup("DR", &drObj); if (!drObj.isDict()) { dict->lookup("Parent", &obj1); while (obj1.isDict()) { - drObj.free(); - obj1.dictLookup("DR", &drObj); - if (drObj.isDict()) { - break; - } - obj1.dictLookup("Parent", &obj2); - obj1.free(); - obj1 = obj2; + drObj.free(); + obj1.dictLookup("DR", &drObj); + if (drObj.isDict()) { + break; + } + obj1.dictLookup("Parent", &obj2); + obj1.free(); + obj1 = obj2; } obj1.free(); if (!drObj.isDict()) { - if (acroForm) { - drObj.free(); - acroForm->lookup("DR", &drObj); - } + if (acroForm) { + drObj.free(); + acroForm->lookup("DR", &drObj); + } } } if (drObj.isDict()) { appearDict.dictAdd("Resources", drObj.copy(&obj1)); } drObj.free(); // build the appearance stream appearStream = new MemStream(appearBuf->getCString(), 0, - appearBuf->getLength(), &appearDict); + appearBuf->getLength(), &appearDict); appearance.initStream(appearStream); ok = gTrue; if (daStr1) { delete daStr1; } } daObj.free(); } void Annot::draw(Gfx *gfx) { Object obj; - if (appearance.fetch(xref, &obj)->isStream()) { gfx->doAnnot(&obj, xMin, yMin, xMax, yMax); } obj.free(); } //------------------------------------------------------------------------ // Annots @@ -284,26 +293,26 @@ Annots::Annots(XRef *xref, Catalog *cata size = 0; nAnnots = 0; acroForm = catalog->getAcroForm()->isDict() ? catalog->getAcroForm()->getDict() : NULL; if (annotsObj->isArray()) { for (i = 0; i < annotsObj->arrayGetLength(); ++i) { if (annotsObj->arrayGet(i, &obj1)->isDict()) { - annot = new Annot(xref, acroForm, obj1.getDict()); - if (annot->isOk()) { - if (nAnnots >= size) { - size += 16; - annots = (Annot **)greallocn(annots, size, sizeof(Annot *)); - } - annots[nAnnots++] = annot; - } else { - delete annot; - } + annot = new Annot(xref, acroForm, obj1.getDict()); + if (annot->isOk()) { + if (nAnnots >= size) { + size += 16; + annots = (Annot **)greallocn(annots, size, sizeof(Annot *)); + } + annots[nAnnots++] = annot; + } else { + delete annot; + } } obj1.free(); } } } Annots::~Annots() { int i; Index: poppler/Annot.h =================================================================== RCS file: /cvs/poppler/poppler/poppler/Annot.h,v retrieving revision 1.2 diff -u -8 -p -r1.2 Annot.h --- poppler/Annot.h 16 Oct 2005 14:54:17 -0000 1.2 +++ poppler/Annot.h 8 Sep 2006 21:26:02 -0000 @@ -22,33 +22,37 @@ class Catalog; //------------------------------------------------------------------------ class Annot { public: Annot(XRef *xrefA, Dict *acroForm, Dict *dict); ~Annot(); GBool isOk() { return ok; } + GBool textField() { return isTextField; } void draw(Gfx *gfx); // Get appearance object. Object *getAppearance(Object *obj) { return appearance.fetch(xref, obj); } - + double getXMin() { return xMin; } + double getYMin() { return yMin; } private: void generateAppearance(Dict *acroForm, Dict *dict); - XRef *xref; // the xref table for this PDF file - Object appearance; // a reference to the Form XObject stream - // for the normal appearance + XRef *xref; // the xref table for this PDF file + Object appearance; // a reference to the Form XObject stream + // for the normal appearance GooString *appearBuf; - double xMin, yMin, // annotation rectangle + double xMin, yMin, // annotation rectangle xMax, yMax; GBool ok; + + GBool regen, isTextField; }; //------------------------------------------------------------------------ // Annots //------------------------------------------------------------------------ class Annots { public: Index: poppler/Dict.cc =================================================================== RCS file: /cvs/poppler/poppler/poppler/Dict.cc,v retrieving revision 1.4 diff -u -8 -p -r1.4 Dict.cc --- poppler/Dict.cc 18 Jan 2006 22:32:13 -0000 1.4 +++ poppler/Dict.cc 8 Sep 2006 21:26:02 -0000 @@ -50,16 +50,47 @@ void Dict::add(const UGooString &key, Ob } entries = (DictEntry *)greallocn(entries, size, sizeof(DictEntry)); } entries[length].key = new UGooString(key); entries[length].val = *val; ++length; } +void Dict::remove(const UGooString &key) { + int i; + bool found = false; + DictEntry tmp; + if(length == 0) return; + + for(i=0; ival.free(); + e->val = *val; + } else { + add (key, val); + } +} + inline DictEntry *Dict::find(const UGooString &key) { int i; for (i = 0; i < length; ++i) { if (!key.cmp(entries[i].key)) return &entries[i]; } return NULL; Index: poppler/Dict.h =================================================================== RCS file: /cvs/poppler/poppler/poppler/Dict.h,v retrieving revision 1.3 diff -u -8 -p -r1.3 Dict.h --- poppler/Dict.h 18 Jan 2006 22:32:13 -0000 1.3 +++ poppler/Dict.h 8 Sep 2006 21:26:02 -0000 @@ -38,16 +38,20 @@ public: int incRef() { return ++ref; } int decRef() { return --ref; } // Get number of entries. int getLength() { return length; } // Add an entry void add(const UGooString &key, Object *val); + // Update the value of an existing entry, otherwise create it + void set(const UGooString &key, Object *val); + // Remove an entry. This invalidate indexes + void remove(const UGooString &key); // Check if dictionary is of specified type. GBool is(char *type); // Look up an entry and return the value. Returns a null object // if is not in the dictionary. Object *lookup(const UGooString &key, Object *obj); Object *lookupNF(const UGooString &key, Object *obj); Index: poppler/Form.cc =================================================================== RCS file: poppler/Form.cc diff -N poppler/Form.cc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ poppler/Form.cc 8 Sep 2006 21:26:02 -0000 @@ -0,0 +1,579 @@ +//======================================================================== +// +// Form.cc +// +// Copyright 1996-2003 Glyph & Cog, LLC +// Copyright 2006 Julien Rebetez +// +//======================================================================== + +#include + +#ifdef USE_GCC_PRAGMAS +#pragma implementation +#endif + +#include +#include +#include "goo/gmem.h" +#include "goo/GooString.h" +#include "Error.h" +#include "Object.h" +#include "Array.h" +#include "Dict.h" +#include "Form.h" +#include "UGooString.h" +#include "XRef.h" + + +//======================================================================== +// FormField +//======================================================================== + +FormField::FormField(XRef* xrefA, Object *aobj, unsigned id, Ref aref) { + Object obj1, obj2, obj3i; + double t; + ref = aref; + ID = id; + ok = gFalse; + xref = xrefA; + aobj->copy(&obj); + Dict *dict = obj.getDict(); + + // get rectangle + if (!dict->lookup("Rect", &obj1)->isArray()) { + error(-1, "Annotation rectangle is wrong type"); + goto err2; + } + if (!obj1.arrayGet(0, &obj2)->isNum()) { + error(-1, "Bad annotation rectangle"); + goto err1; + } + x1 = obj2.getNum(); + obj2.free(); + if (!obj1.arrayGet(1, &obj2)->isNum()) { + error(-1, "Bad annotation rectangle"); + goto err1; + } + y1 = obj2.getNum(); + obj2.free(); + if (!obj1.arrayGet(2, &obj2)->isNum()) { + error(-1, "Bad annotation rectangle"); + goto err1; + } + x2 = obj2.getNum(); + obj2.free(); + if (!obj1.arrayGet(3, &obj2)->isNum()) { + error(-1, "Bad annotation rectangle"); + goto err1; + } + y2 = obj2.getNum(); + obj2.free(); + obj1.free(); + //swap coords if needed + if (x1 > x2) { + t = x1; + x1 = x2; + x2 = t; + } + if (y1 > y2) { + t = y1; + y1 = y2; + y2 = t; + } + + + + ok = gTrue; + + err1: + obj2.free(); + err2: + obj1.free(); +} + +FormField::FormField(FormField *dest) +{ + x1 = dest->x1; + y1 = dest->y1; + x2 = dest->x2; + y2 = dest->x2; + + ok = dest->ok; + + type = dest->type; +} + +FormField::~FormField() +{ + obj.free(); +} + +//------------------------------------------------------------------------ +// FormFieldButton +//------------------------------------------------------------------------ +FormFieldButton::FormFieldButton(XRef *xrefA, Object *aobj, unsigned id, Ref ref) : FormField(xrefA, aobj, id, ref) +{ + type = fieldButton; + Object obj1; + Dict *dict = aobj->getDict(); + onStr = NULL; + + btype = fieldButtonCheck; //check box by default + if (dict->lookup("Ff", &obj1)->isInt()) { + int flags = obj1.getInt(); + if (flags & 0x10000) { //push button + btype = fieldButtonPush; + } else if (flags & 0x8000) {//radio button + btype = fieldButtonRadio; + } + } + obj1.free(); + if (dict->lookup("Kids", &obj1)->isArray()) { //we have a field of buttons + size = obj1.getArray()->getLength(); + kids = new ButtonKid[size]; + if (btype == fieldButtonPush) { + printf("%i kids of type Push Button\n", obj1.getArray()->getLength()); + } else if (btype == fieldButtonRadio) { + printf("%i kids of type Radio Button\n", obj1.getArray()->getLength()); + } + } else { //single button + printf("single button\n"); + size = 1; + kids = new ButtonKid[size]; + this->getRect(&kids[0].x1, &kids[0].y1, &kids[0].x2, &kids[0].y2); + aobj->copy(&kids[0].obj); + kids[0].ref = ref; + } + obj1.free(); + + if (dict->lookup("Opt", &obj1)->isArray()) { + printf("Opt entry found\n"); + } + obj1.free(); + + //dump AP dict + //TODO:Browse the AP dictionnary to find the name of the state(/Yes, /No) + if (dict->lookup("AP", &obj1)->isDict()) { + Dict *dict = obj1.getDict(); + int length = dict->getLength(); + printf("AP length: %i\n", length); + for(int i=0; igetKey(i)->getCString()); + dict->getVal(i, &obj2); + printf("\t\t%s\n", obj2.getTypeName()); + if(obj2.isDict()) { + Dict *dict2 = obj2.getDict(); + int l2 = dict2->getLength(); + for(int j=0; jgetVal(j, &obj3); + UGooString *key = dict2->getKey(j); + printf("\t\t/%s\n", key->getCString()); + if(strcmp(key->getCString(), "Off")) { //if we don't have Off, we have the name of the Yes state + onStr = strdup(key->getCString()); + } + obj3.free(); + } + } else if (obj2.isStream()) { + Stream *str = obj2.getStream(); + Dict *dict2 = str->getDict(); + Object obj3; + dict->lookup("Length", &obj3); + int c; + printf("\t\tstream length: %i\n", obj3.getInt()); + printf("\t\tstream content: \n"); + while ((c = str->getChar()) != EOF) { + printf("%i",c); + } + printf("\n"); + //set AS to down? + onStr = "D"; + } + obj2.free(); + } + } + obj1.free(); + + kids[0].state = gFalse; + //Find current state + if (dict->lookup("V", &obj1)->isName()) { + char *s = obj1.getName(); + if(strcmp(s, "Off")) { + kids[0].state = gTrue; + } + } else if (obj1.isArray()) { //handle the case where we have multiple choices + printf("multiple choice isn't supported yet\n"); + } else { + printf("no state set\n"); + } + //We didn't found the Yes state for the button + if(!onStr) { + error(-1, "FormFieldButton::FormFieldButton, Unable to find the On state name for the button\n"); + } +} + +GBool FormFieldButton::getKidState (int index) +{ + if(index < 0 || index > size) { + error(-1, "FormFieldButon::getKidState, out of bound index"); + return gFalse; + } + return kids[index].state; +} + +void FormFieldButton::setKidState (int index, GBool state) +{ + if(index < 0 || index > size) { + error(-1, "FormFieldButton::setKidState, out of bound index"); + return; + } + ButtonKid *kid = &kids[index]; + char* offStr = "Off"; + + kid->state = state; + Object *obj1 = new Object(); + obj1->initName(state?onStr:offStr); + printf("setKidState on %i, state %s", index, state?onStr:offStr); + kid->obj.getDict()->set("V", obj1); + obj1 = new Object(); + obj1->initName(state?onStr:offStr); + //modify the Appearance State entry as well + kid->obj.getDict()->set("AS", obj1); + + //notify the xref about the update + xref->setModifiedObject(&kid->obj, kid->ref); +} + +ButtonKid* FormFieldButton::getKid (int index) +{ + if (index < 0 || index >= size) + return NULL; + return &kids[index]; +} + +FormFieldButton::~FormFieldButton() +{ + delete [] kids; +} + +//------------------------------------------------------------------------ +// FormFieldText +//------------------------------------------------------------------------ +FormFieldText::FormFieldText(XRef *xrefA, Object *aobj, unsigned id, Ref ref) : FormField(xrefA, aobj, id, ref) +{ + type = fieldText; + content = NULL; + Dict *dict = aobj->getDict(); + Object obj1; + + if (dict->lookup("V", &obj1)->isString()) { + if(obj1.getString()->getLength() == 2 && obj1.getString()->hasUnicodeMarker()) { + printf("Form.cc:%i, skipped empty unicode string\n", __LINE__); + UGooString c(*obj1.getString()); + printf("ugoo: %s\n", c.getCString()); + content = c.getCString(); + } else { + printf("Form.cc:%i, V : %s\n", __LINE__, obj1.getString()->getCString()); + content = strdup(obj1.getString()->getCString()); + } + } + obj1.free(); + +} + +FormFieldText::~FormFieldText() +{ + +} + +void FormFieldText::setContentCopy(char* new_content) +{ + Object obj1; + + if(content) + delete [] content; + + if (new_content == NULL) + content = NULL; + else { + content = strdup(new_content); + + GooString *cont = new GooString(content); + Object *obj1 = new Object(); + obj1->initString(cont); + //printf("%s, %s, %s\n", content, cont->getCString(), obj1->getString()); + obj.getDict()->set("V", obj1); + //notify the xref about the update + xref->setModifiedObject(&obj, ref); + } +} + + +//------------------------------------------------------------------------ +// FormFieldChoice +//------------------------------------------------------------------------ +FormFieldChoice::FormFieldChoice(XRef *xrefA, Object *dict, unsigned id, Ref ref) : FormField(xrefA, dict, id, ref) +{ + type = fieldChoice; +} + +FormFieldChoice::~FormFieldChoice() +{ + +} + +//------------------------------------------------------------------------ +// FormFieldSignature +//------------------------------------------------------------------------ +FormFieldSignature::FormFieldSignature(XRef *xrefA, Object *dict, unsigned id, Ref ref): FormField(xrefA, dict, id, ref) +{ + type = fieldSignature; +} + +FormFieldSignature::~FormFieldSignature() +{ + +} + +//------------------------------------------------------------------------ +// Form +//------------------------------------------------------------------------ + +void Form::_addIfBelongsToPage (Object* fieldObj, Object* annots, Ref ref) +{ + unsigned count = 0; + FormField* field; + Object obj1, obj2; + printf("_addIfBelongsToPage: %i %i\n", ref.num, ref.gen); + if (annots->isArray()) { + for (int i = 0; i < annots->arrayGetLength(); ++i) { + //search for this object to add it to page + if ( !annots->arrayGetNF(i, &obj1)->isRef() + || obj1.getRefNum() != ref.num + || obj1.getRefGen() != ref.gen) + { + obj1.free(); + continue; + } + obj1.free(); + + if (annots->arrayGet(i, &obj1)->isDict()) { + if (obj1.dictLookup("Subtype", &obj2)->isName("Widget")) { + //Don't consider readonly field as field + //FIXME: ReadOnly may get their data from database or javascript... + Object obj3; + if (obj1.dictLookup("Ff", &obj3)->isInt()) { + int flags = obj3.getInt(); + if (flags & 0x1) + continue; + } + obj3.free(); + //end ReadOnly + + unsigned id = ((unsigned)pageNum << 16) + count; + obj2.free(); + annots->arrayGetNF(i, &obj2); //store the indirect ref for further update + Ref objRef = obj2.getRef(); + obj2.free(); + //printf("num: %i\t gen:%i\n", objRef.num, objRef.gen); + if(obj1.dictLookup("FT", &obj2)->isName("Btn")) { + field = new FormFieldButton(xref, &obj1,id,objRef); + count++; + } else if (obj2.isName("Tx")) { + field = new FormFieldText(xref, &obj1,id,objRef); + count++; + } else if (obj2.isName("Ch")) { + field = new FormFieldChoice(xref, &obj1,id,objRef); + count++; + } else if (obj2.isName("Sig")) { + field = new FormFieldSignature(xref, &obj1,id,objRef); + count++; + } else { + goto err1; + } + if (field->isOk()) { + if (numFields >= size) { + size += 16; + fields = (FormField**)greallocn(fields,size,sizeof(FormField*)); + } + fields[numFields++] = field; + } else { + delete field; + } + + } +err1: + obj2.free(); + } + obj1.free(); + } + } +} + +void Form::_handleField (Object* field, Object* annots, Ref ref) +{ + Object kids; + GBool terminal = gFalse; + if(!field->dictLookup("Kids", &kids) || kids.isNull()) { + terminal = gTrue; + printf("terminal\n"); + } else { + if(!kids.isArray()) { + error(-1, "Kids entry in Form Field Dict isn't of type array: %s", kids.getTypeName()); + terminal = gTrue; + } else { + Array* kidsArray = kids.getArray(); + for(int i=0; igetLength(); i++) { + Object kid; + Object kidRef; + kidsArray->get(i, &kid); + kidsArray->getNF(i, &kidRef); + if(!kid.isDict()) { + error(-1, "Found a non-Dict object in Form Field hierarchy"); + } else { + Object obj1; + if(kid.dictLookup("Subtype", &obj1)->isName("Widget")) { //terminal field with annots as kids + terminal = gTrue; + } else { //non-terminal, handle the kids + terminal = gFalse; + printf("non_terminal, looping through kids\n"); + _handleField(&kid, annots, kidRef.getRef()); + } + obj1.free(); + } + kid.free(); + } + } + kids.free(); + } + if (terminal) { + _addIfBelongsToPage(field, annots, ref); + } +} + +Form::Form(XRef *xrefA, Object* annots, unsigned page) +{ + FormField* field; + Object obj1, obj2; + Object catalogFields; + Object catalog; + Object acroForm; + int i; + unsigned count = 0; + xref = xrefA; + + fields = NULL; + size = 0; + numFields = 0; + pageNum = page; + + //get the fields list from Document's Catalog + /*xrefA->getCatalog(&catalog); + catalog.dictLookup("AcroForm", &acroForm); + acroForm.dictLookupNF("Fields", &catalogFields); + if (catalogFields.isArray()) { + Array *a = catalogFields.getArray(); + for (int i=0; igetLength(); i++) { + Object obj3; + Object obj3Ref; + a->get(i, &obj3); + a->getNF(i, &obj3Ref); + _handleField(&obj3, annots, obj3Ref.getRef()); + obj3.free(); + } + printf("Form Field OK\n"); + */ + if (annots->isArray()) { + for (i = 0; i < annots->arrayGetLength(); ++i) { + if (annots->arrayGet(i, &obj1)->isDict()) { + if (obj1.dictLookup("Subtype", &obj2)->isName("Widget")) { + //Don't consider readonly field as field + //FIXME: ReadOnly may get their data from database or javascript... + Object obj3; + if (obj1.dictLookup("Ff", &obj3)->isInt()) { + int flags = obj3.getInt(); + if (flags & 0x1) + continue; + } + obj3.free(); + //end ReadOnly + + unsigned id = ((unsigned)pageNum << 16) + count; + obj2.free(); + annots->arrayGetNF(i, &obj2); //store the indirect ref for further update + Ref objRef = obj2.getRef(); + obj2.free(); + //printf("num: %i\t gen:%i\n", objRef.num, objRef.gen); + if(obj1.dictLookup("FT", &obj2)->isName("Btn")) { + field = new FormFieldButton(xrefA, &obj1,id,objRef); + count++; + } else if (obj2.isName("Tx")) { + field = new FormFieldText(xrefA, &obj1,id,objRef); + count++; + } else if (obj2.isName("Ch")) { + field = new FormFieldChoice(xrefA, &obj1,id,objRef); + count++; + } else if (obj2.isName("Sig")) { + field = new FormFieldSignature(xrefA, &obj1,id,objRef); + count++; + } else { + goto err1; + } + if (field->isOk()) { + if (numFields >= size) { + size += 16; + fields = (FormField**)greallocn(fields,size,sizeof(FormField*)); + } + fields[numFields++] = field; + } else { + delete field; + } + + } +err1: + obj2.free(); + } + obj1.free(); + } + /*}*/ + } + +} + +Form::~Form() { + int i; + for(i = 0; i< numFields; ++i) + delete fields[i]; + gfree(fields); +} + +FormField *Form::find(double x, double y) const { + int i; + for (i = numFields -1; i>= 0; --i) { + if (fields[i]->inRect(x, y)) { + return fields[i]; + } + } + return NULL; +} + +FormField* Form::getFieldById (unsigned int id) const { + int i; + for (i=0; igetID() == id) { + return fields[i]; + } + } + return NULL; +} +GBool Form::onField(double x, double y) const { + int i; + for (i =0; iinRect(x,y)) + return gTrue; + } + return gFalse; +} + Index: poppler/Form.h =================================================================== RCS file: poppler/Form.h diff -N poppler/Form.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ poppler/Form.h 8 Sep 2006 21:26:03 -0000 @@ -0,0 +1,197 @@ +//======================================================================== +// +// Form.h +// +// Copyright 2006 Julien Rebetez +// Copyright 1996-2003 Glyph & Cog, LLC +// +//======================================================================== + +#ifndef FORM_H +#define FORM_H + +#ifdef USE_GCC_PRAGMAS +#pragma interface +#endif + +#include "Object.h" + +class GooString; +class UGooString; +class Array; +class Dict; + + +//------------------------------------------------------------------------ +// FormFieldType +//------------------------------------------------------------------------ + +enum FormFieldType { + fieldButton, + fieldText, + fieldChoice, + fieldSignature, + //kid type + fieldButtonCheck, + fieldButtonPush, + fieldButtonRadio +}; + +class FormField { +public: + virtual ~FormField(); + + FormField *copy() { return new FormField(this); } + + // Was the FormField created successfully ? + GBool isOk() { return ok; } + + // Check if point is inside the field rectangle + GBool inRect(double x, double y) + { return x1 <= x && x <= x2 && y1 <= y && y <= y2; } + + // Get the field rectangle + void getRect(double *xa1, double *ya1, double *xa2, double *ya2) + { *xa1 = x1; *ya1 = y1; *xa2 = x2; *ya2 = y2; } + + // Accessors. + FormFieldType getType() { return type; } + unsigned getID () { return ID; } + Object* getObj() { return &obj; } + Ref getRef() { return ref; } + +protected: + // Build a FormField from the dict, only callable from child-classes + FormField(XRef* xrefa, Object *aobj, unsigned id, Ref aref); + FormFieldType type; // field type + GBool ok; // set if created successfully + Ref ref; + Object obj; + XRef *xref; + + + +private: + FormField() {} + FormField(FormField *dest); + /* + Field ID is a 32bits integer, calculated as follow : + the first 16 bits are the field number, relative to the page + the last 16 bits are the page number + [page number | field number] + (encoding) id = (pageNum << 16) + fieldNum; + (decoding) pageNum = id >> 16; fieldNum = (id << 16) >> 16; + */ + unsigned ID; + + double x1, y1; // lower left corner + double x2, y2; // upper right corner +}; + + +//------------------------------------------------------------------------ +// FormFieldButton +//------------------------------------------------------------------------ + +struct ButtonKid +{ + double x1, y1, x2, y2; + Object obj; + Ref ref; + GBool state; +}; + +class FormFieldButton: public FormField { +public: + FormFieldButton(XRef *xrefA, Object *dict, unsigned id, Ref ref); + + int getNumKids() { return size; } + ButtonKid* getKid(int index); + FormFieldType getButtonType () { return btype; } + + void setKidState(int index, GBool state); + GBool getKidState(int index); + + virtual ~FormFieldButton(); +protected: + FormFieldType btype; + int size; + ButtonKid *kids; + char* onStr; +}; + +//------------------------------------------------------------------------ +// FormFieldText +//------------------------------------------------------------------------ + +class FormFieldText: public FormField { +public: + FormFieldText(XRef *xrefA, Object *dict, unsigned id, Ref ref); + + char* getContent() { return content; } + char* getContentCopy() { if (content == NULL) return NULL; + else return strdup(content); } + void setContentCopy(char* new_content); + + virtual ~FormFieldText(); +protected: + char* content; +}; + +//------------------------------------------------------------------------ +// FormFieldChoice +//------------------------------------------------------------------------ + +class FormFieldChoice: public FormField { +public: + FormFieldChoice(XRef *xrefA, Object *dict, unsigned id, Ref ref); + + virtual ~FormFieldChoice(); +}; + +//------------------------------------------------------------------------ +// FormFieldSignature +//------------------------------------------------------------------------ + +class FormFieldSignature: public FormField { +public: + FormFieldSignature(XRef *xrefA, Object *dict, unsigned id, Ref ref); + + virtual ~FormFieldSignature(); +}; + +//------------------------------------------------------------------------ +// Form +//------------------------------------------------------------------------ + +class Form { +public: + // Extract form from array of annotations + Form(XRef *xrefA, Object* annots, unsigned page); + + ~Form(); + + // Iterate through list of fields. + int getNumFields() const { return numFields; } + FormField* getField(int i) const { return fields[i]; } + FormField* getFieldById (unsigned int id) const; + + // If point , is in a field, return the associated field; + // else return NULL + FormField *find(double x, double y) const; + + // Return true if , is in a field. + GBool onField(double x, double y) const; + +private: + void _handleField (Object* field, Object* annots, Ref ref); + void _addIfBelongsToPage (Object* field, Object* annots, Ref ref); + + FormField** fields; + int numFields; + int size; + unsigned pageNum; + XRef* xref; +}; +#endif + Index: poppler/Makefile.am =================================================================== RCS file: /cvs/poppler/poppler/poppler/Makefile.am,v retrieving revision 1.24 diff -u -8 -p -r1.24 Makefile.am --- poppler/Makefile.am 24 Jul 2006 19:49:51 -0000 1.24 +++ poppler/Makefile.am 8 Sep 2006 21:26:03 -0000 @@ -107,16 +107,17 @@ poppler_include_HEADERS = \ Catalog.h \ CharCodeToUnicode.h \ CMap.h \ Decrypt.h \ Dict.h \ Error.h \ FontEncodingTables.h \ FontInfo.h \ + Form.h \ Function.cc \ Function.h \ Gfx.h \ GfxFont.h \ GfxState.h \ GlobalParams.h \ JArithmeticDecoder.h \ JBIG2Stream.h \ @@ -169,16 +170,17 @@ libpoppler_la_SOURCES = \ Catalog.cc \ CharCodeToUnicode.cc \ CMap.cc \ Decrypt.cc \ Dict.cc \ Error.cc \ FontEncodingTables.cc \ FontInfo.cc \ + Form.cc \ Function.cc \ Gfx.cc \ GfxFont.cc \ GfxState.cc \ GlobalParams.cc \ JArithmeticDecoder.cc \ JBIG2Stream.cc \ JPXStream.cc \ Index: poppler/Object.h =================================================================== RCS file: /cvs/poppler/poppler/poppler/Object.h,v retrieving revision 1.2 diff -u -8 -p -r1.2 Object.h --- poppler/Object.h 18 Jan 2006 22:32:13 -0000 1.2 +++ poppler/Object.h 8 Sep 2006 21:26:04 -0000 @@ -160,16 +160,17 @@ public: int arrayGetLength(); void arrayAdd(Object *elem); Object *arrayGet(int i, Object *obj); Object *arrayGetNF(int i, Object *obj); // Dict accessors. int dictGetLength(); void dictAdd(const UGooString &key, Object *val); + void dictSet(const UGooString &key, Object *val); GBool dictIs(char *dictType); Object *dictLookup(const UGooString &key, Object *obj); Object *dictLookupNF(const UGooString &key, Object *obj); UGooString *dictGetKey(int i); Object *dictGetVal(int i, Object *obj); Object *dictGetValNF(int i, Object *obj); // Stream accessors. @@ -237,16 +238,19 @@ inline Object *Object::arrayGetNF(int i, #include "Dict.h" inline int Object::dictGetLength() { return dict->getLength(); } inline void Object::dictAdd(const UGooString &key, Object *val) { dict->add(key, val); } +inline void Object::dictSet(const UGooString &key, Object *val) + { dict->set(key, val); } + inline GBool Object::dictIs(char *dictType) { return dict->is(dictType); } inline GBool Object::isDict(char *dictType) { return type == objDict && dictIs(dictType); } inline Object *Object::dictLookup(const UGooString &key, Object *obj) { return dict->lookup(key, obj); } Index: poppler/PDFDoc.cc =================================================================== RCS file: /cvs/poppler/poppler/poppler/PDFDoc.cc,v retrieving revision 1.10 diff -u -8 -p -r1.10 PDFDoc.cc --- poppler/PDFDoc.cc 18 Jan 2006 22:32:13 -0000 1.10 +++ poppler/PDFDoc.cc 8 Sep 2006 21:26:05 -0000 @@ -456,31 +456,290 @@ GBool PDFDoc::isLinearized() { obj4.free(); obj3.free(); obj2.free(); obj1.free(); delete parser; return lin; } -GBool PDFDoc::saveAs(GooString *name) { +GBool PDFDoc::saveAs(GooString *name, PDFWriteMode mode) { FILE *f; - int c; if (!(f = fopen(name->getCString(), "wb"))) { error(-1, "Couldn't open file '%s'", name->getCString()); return gFalse; } + + if (mode == writeForceRewrite) { + saveCompleteRewrite(f); + } else if (mode == writeForceIncremental) { + saveIncrementalUpdate(f); + } else { // let poppler decide + // find if we have updated objects + GBool updated = gFalse; + for(int i=0; igetNumObjects(); i++) { + if (xref->getEntry(i)->obj) { + updated = gTrue; + break; + } + } + if(updated) { + saveCompleteRewrite(f); + } else { + // simply copy the original file + int c; + str->reset(); + while ((c = str->getChar()) != EOF) { + fputc(c, f); + } + str->close(); + } + } + + fclose(f); + return gTrue; +} + +void PDFDoc::saveIncrementalUpdate (FILE *f) +{ + XRef *uxref; + int c; + //copy the original file str->reset(); while ((c = str->getChar()) != EOF) { fputc(c, f); } str->close(); - fclose(f); - return gTrue; + + uxref = new XRef(); + uxref->add(0, 65535, 0, gFalse); + int objectsCount = 0; //count the number of objects in the XRef(s) + for(int i=0; igetNumObjects(); i++) { + if ((xref->getEntry(i)->type == xrefEntryFree) && + (xref->getEntry(i)->gen == 0)) //we skip the irrelevant free objects + continue; + objectsCount++; + if (xref->getEntry(i)->obj) { //we have an updated object + Object obj1; + Ref ref; + ref.num = i; + ref.gen = xref->getEntry(i)->gen; + xref->fetch(ref.num, ref.gen, &obj1); + Guint offset = writeObject(&obj1, &ref, f); + uxref->add(ref.num, ref.gen, offset, gTrue); + obj1.free(); + } + } + if (uxref->getSize() == 0) { //we have nothing to update + delete uxref; + return; + } + + Guint uxrefOffset = ftell(f); + uxref->writeToFile(f); + + writeTrailer(uxrefOffset, objectsCount, f, gTrue); + + delete uxref; +} + +void PDFDoc::saveCompleteRewrite (FILE *f) +{ + fprintf(f, "%%PDF-%.1f\r\n",pdfVersion); + XRef *uxref = new XRef(); + uxref->add(0, 65535, 0, gFalse); + for(int i=0; igetNumObjects(); i++) { + Object obj1; + Ref ref; + XRefEntryType type = xref->getEntry(i)->type; + if (type == xrefEntryFree) { + ref.num = i; + ref.gen = xref->getEntry(i)->gen; + /* the XRef class add a lot of unrelevant free entries, we only want the significant one + and we don't want the one with num=0 because it has already been added (gen = 65535)*/ + if (ref.gen > 0 && ref.num > 0) + uxref->add(ref.num, ref.gen, 0, gFalse); + } else if (type == xrefEntryUncompressed){ + ref.num = i; + ref.gen = xref->getEntry(i)->gen; + xref->fetch(ref.num, ref.gen, &obj1); + Guint offset = writeObject(&obj1, &ref, f); + uxref->add(ref.num, ref.gen, offset, gTrue); + obj1.free(); + } else if (type == xrefEntryCompressed) { + ref.num = i; + ref.gen = 0; //compressed entries have gen == 0 + xref->fetch(ref.num, ref.gen, &obj1); + Guint offset = writeObject(&obj1, &ref, f); + uxref->add(ref.num, ref.gen, offset, gTrue); + obj1.free(); + } + } + Guint uxrefOffset = ftell(f); + uxref->writeToFile(f); + + writeTrailer(uxrefOffset, uxref->getSize(), f, gFalse); + + + delete uxref; + +} + +void PDFDoc::writeDictionnary (Dict* dict, FILE *f) +{ + Object obj1; + fprintf(f,"<< "); + for (int i=0; igetLength(); i++) { + fprintf(f,"/%s ", dict->getKey(i)->getCString()); + writeObject(dict->getValNF(i, &obj1), NULL, f); + fprintf(f,"\r\n"); + + obj1.free(); + } + fprintf(f,">>\r\n"); +} + +void PDFDoc::writeStream (Stream* str, FILE *f) +{ + int c; + fprintf(f,"stream\r\n"); + str->reset(); + for (int c=str->getChar(); c!= EOF; c=str->getChar()) { + fprintf(f,"%c", c); + } + fprintf(f,"\r\nendstream\r\n"); +} + +void PDFDoc::writeString (GooString* s, FILE* f) +{ + //write hexa string + const char* c = s->getCString(); + fprintf(f, "<"); + while(*c!='\0') { + fprintf(f, "%02x", *c); + c++; + } + fprintf(f, "> "); +} + +Guint PDFDoc::writeObject (Object* obj, Ref* ref, FILE *f) +{ + Array *array; + Object obj1; + Guint offset = ftell(f); + int tmp; + + if(ref) + fprintf(f,"%i %i obj\r\n", ref->num, ref->gen); + + switch (obj->getType()) { + case objBool: + fprintf(f,"%s ", obj->getBool()?"true":"false"); + break; + case objInt: + fprintf(f,"%i ", obj->getInt()); + break; + case objReal: + fprintf(f,"%f ", obj->getReal()); + break; + case objString: + writeString(obj->getString(), f); + break; + case objName: + fprintf(f,"/%s ", obj->getName()); + break; + case objNull: + fprintf(f, "null\r\n"); + break; + case objArray: + array = obj->getArray(); + fprintf(f,"["); + for (int i=0; igetLength(); i++) { + writeObject(array->getNF(i, &obj1), NULL,f); + obj1.free(); + } + fprintf(f,"]"); + break; + case objDict: + writeDictionnary (obj->getDict(),f); + break; + case objStream: + //we write the stream unencoded => TODO: write stream encoder + obj->getStream()->reset(); + //recalculate stream length + tmp = 0; + for (int c=obj->getStream()->getChar(); c!=EOF; c=obj->getStream()->getChar()) { + tmp++; + } + obj1.initInt(tmp); + obj->getStream()->getDict()->set("Length", &obj1); + + //Remove Stream encoding + obj->getStream()->getDict()->remove("Filter"); + obj->getStream()->getDict()->remove("DecodeParms"); + + writeDictionnary (obj->getStream()->getDict(),f); + writeStream (obj->getStream(),f); + obj1.free(); + break; + case objRef: + fprintf(f,"%i %i R ", obj->getRef().num, obj->getRef().gen); + break; + case objCmd: + printf("objCmd\n"); + fprintf(f,"null\r\n"); + break; + case objError: + printf("objError\n"); + fprintf(f,"null\r\n"); + break; + case objEOF: + printf("objEOF\n"); + fprintf(f,"null\r\n"); + break; + case objNone: + printf("objNone\n"); + fprintf(f,"null\r\n"); + break; + default: + error(-1,"Unhandled objType : %i, please report a bug with a testcase\r\n", obj->getType()); + break; + } + if (ref) + fprintf(f,"endobj\r\n\r\n"); + return offset; +} + +void PDFDoc::writeTrailer (Guint uxrefOffset, int uxrefSize, FILE* f, GBool incrUpdate) +{ + //Dict* trailerDict = xref->getTrailerDict()->getDict(); + Dict *trailerDict = new Dict(xref); + Object obj1; + obj1.initInt(uxrefSize); + trailerDict->set("Size", &obj1); + obj1.free(); + if(xref->getTrailerDict()->getDict()->lookup("ID", &obj1) != NULL) { + trailerDict->set("ID", &obj1); + obj1.free(); + } + obj1.initRef(xref->getRootNum(), xref->getRootGen()); + trailerDict->set("Root", &obj1); + obj1.free(); + + if (incrUpdate) { + obj1.initInt(xref->getLastXRefPos()); + trailerDict->set("Prev", &obj1); + obj1.free(); + } + fprintf(f, "trailer\r\n"); + writeDictionnary(trailerDict, f); + fprintf(f, "startxref\r\n"); + fprintf(f, "%i\r\n", uxrefOffset); + fprintf(f, "%%%%EOF\r\n"); } void PDFDoc::getLinks(Page *page) { Object obj; links = new Links(page->getAnnots(&obj), catalog->getBaseURI()); obj.free(); } Index: poppler/PDFDoc.h =================================================================== RCS file: /cvs/poppler/poppler/poppler/PDFDoc.h,v retrieving revision 1.7 diff -u -8 -p -r1.7 PDFDoc.h --- poppler/PDFDoc.h 18 Jan 2006 22:32:13 -0000 1.7 +++ poppler/PDFDoc.h 8 Sep 2006 21:26:05 -0000 @@ -22,16 +22,22 @@ class GooString; class BaseStream; class OutputDev; class Links; class LinkAction; class LinkDest; class Outline; +enum PDFWriteMode { + writeStandard, + writeForceRewrite, + writeForceIncremental +}; + //------------------------------------------------------------------------ // PDFDoc //------------------------------------------------------------------------ class PDFDoc { public: PDFDoc(GooString *fileNameA, GooString *ownerPassword = NULL, @@ -158,23 +164,31 @@ public: // Return the document's Info dictionary (if any). Object *getDocInfo(Object *obj) { return xref->getDocInfo(obj); } Object *getDocInfoNF(Object *obj) { return xref->getDocInfoNF(obj); } // Return the PDF version specified by the file. double getPDFVersion() { return pdfVersion; } - // Save this file with another name. - GBool saveAs(GooString *name); + // Save this file with another name + GBool saveAs(GooString *name, PDFWriteMode mode=writeStandard); // Return a pointer to the GUI (XPDFCore or WinPDFCore object). void *getGUIData() { return guiData; } private: + // Add object to current file stream and return the offset of the beginning of the object + Guint writeObject (Object *obj, Ref *ref, FILE* f); + void writeDictionnary (Dict* dict, FILE* f); + void writeStream (Stream* str, FILE* f); + void writeTrailer (Guint uxrefOffset, int uxrefSize, FILE* f, GBool incrUpdate); + void writeString (GooString* s, FILE* f); + void saveIncrementalUpdate (FILE* f); + void saveCompleteRewrite (FILE* f); GBool setup(GooString *ownerPassword, GooString *userPassword); GBool checkFooter(); void checkHeader(); GBool checkEncryption(GooString *ownerPassword, GooString *userPassword); void getLinks(Page *page); GooString *fileName; Index: poppler/Page.cc =================================================================== RCS file: /cvs/poppler/poppler/poppler/Page.cc,v retrieving revision 1.12 diff -u -8 -p -r1.12 Page.cc --- poppler/Page.cc 16 Mar 2006 22:04:56 -0000 1.12 +++ poppler/Page.cc 8 Sep 2006 21:26:06 -0000 @@ -21,16 +21,17 @@ #include "XRef.h" #include "Link.h" #include "OutputDev.h" #ifndef PDF_PARSER_ONLY #include "Gfx.h" #include "GfxState.h" #include "Annot.h" #include "TextOutputDev.h" +#include "Form.h" #endif #include "Error.h" #include "Page.h" #include "UGooString.h" //------------------------------------------------------------------------ // PageAttrs //------------------------------------------------------------------------ @@ -165,65 +166,70 @@ GBool PageAttrs::readBox(Dict *dict, cha if (obj2.isNum()) { tmp.y2 = obj2.getNum(); } else { ok = gFalse; } obj2.free(); if (ok) { if (tmp.x1 > tmp.x2) { - t = tmp.x1; tmp.x1 = tmp.x2; tmp.x2 = t; + t = tmp.x1; tmp.x1 = tmp.x2; tmp.x2 = t; } if (tmp.y1 > tmp.y2) { - t = tmp.y1; tmp.y1 = tmp.y2; tmp.y2 = t; + t = tmp.y1; tmp.y1 = tmp.y2; tmp.y2 = t; } *box = tmp; } } else { ok = gFalse; } obj1.free(); return ok; } //------------------------------------------------------------------------ // Page //------------------------------------------------------------------------ Page::Page(XRef *xrefA, int numA, Dict *pageDict, PageAttrs *attrsA) { + Object obj; ok = gTrue; xref = xrefA; num = numA; // get attributes attrs = attrsA; // transtion pageDict->lookupNF("Trans", &trans); if (!(trans.isDict() || trans.isNull())) { error(-1, "Page transition object (page %d) is wrong type (%s)", - num, trans.getTypeName()); + num, trans.getTypeName()); trans.free(); } // annotations pageDict->lookupNF("Annots", &annots); if (!(annots.isRef() || annots.isArray() || annots.isNull())) { error(-1, "Page annotations object (page %d) is wrong type (%s)", - num, annots.getTypeName()); + num, annots.getTypeName()); annots.free(); goto err2; } + // forms + form = new Form(xrefA, this->getAnnots(&obj),num); + obj.free(); + // contents pageDict->lookupNF("Contents", &contents); if (!(contents.isRef() || contents.isArray() || - contents.isNull())) { + contents.isNull())) { error(-1, "Page contents object (page %d) is wrong type (%s)", - num, contents.getTypeName()); + num, contents.getTypeName()); contents.free(); goto err1; } // thumb pageDict->lookupNF("Thumb", &thumb); if (!(thumb.isStream() || thumb.isNull() || thumb.isRef())) { error(-1, "Page thumb object (page %d) is wrong type (%s)", @@ -244,35 +250,35 @@ Page::Page(XRef *xrefA, int numA, Dict * Page::~Page() { delete attrs; annots.free(); contents.free(); } void Page::display(OutputDev *out, double hDPI, double vDPI, - int rotate, GBool useMediaBox, GBool crop, - Links *links, Catalog *catalog, - GBool (*abortCheckCbk)(void *data), - void *abortCheckCbkData, + int rotate, GBool useMediaBox, GBool crop, + Links *links, Catalog *catalog, + GBool (*abortCheckCbk)(void *data), + void *abortCheckCbkData, GBool (*annotDisplayDecideCbk)(Annot *annot, void *user_data), void *annotDisplayDecideCbkData) { displaySlice(out, hDPI, vDPI, rotate, useMediaBox, crop, -1, -1, -1, -1, links, catalog, - abortCheckCbk, abortCheckCbkData, + abortCheckCbk, abortCheckCbkData, annotDisplayDecideCbk, annotDisplayDecideCbkData); } Gfx *Page::createGfx(OutputDev *out, double hDPI, double vDPI, - int rotate, GBool useMediaBox, GBool crop, - int sliceX, int sliceY, int sliceW, int sliceH, - Links *links, Catalog *catalog, - GBool (*abortCheckCbk)(void *data), - void *abortCheckCbkData, - GBool (*annotDisplayDecideCbk)(Annot *annot, void *user_data), - void *annotDisplayDecideCbkData) { + int rotate, GBool useMediaBox, GBool crop, + int sliceX, int sliceY, int sliceW, int sliceH, + Links *links, Catalog *catalog, + GBool (*abortCheckCbk)(void *data), + void *abortCheckCbkData, + GBool (*annotDisplayDecideCbk)(Annot *annot, void *user_data), + void *annotDisplayDecideCbkData) { PDFRectangle *mediaBox, *cropBox, *baseBox; PDFRectangle box; Gfx *gfx; double kx, ky; rotate += getRotate(); if (rotate >= 360) { rotate -= 360; @@ -283,96 +289,96 @@ Gfx *Page::createGfx(OutputDev *out, dou mediaBox = getMediaBox(); cropBox = getCropBox(); if (sliceW >= 0 && sliceH >= 0) { baseBox = useMediaBox ? mediaBox : cropBox; kx = 72.0 / hDPI; ky = 72.0 / vDPI; if (rotate == 90) { if (out->upsideDown()) { - box.x1 = baseBox->x1 + ky * sliceY; - box.x2 = baseBox->x1 + ky * (sliceY + sliceH); + box.x1 = baseBox->x1 + ky * sliceY; + box.x2 = baseBox->x1 + ky * (sliceY + sliceH); } else { - box.x1 = baseBox->x2 - ky * (sliceY + sliceH); - box.x2 = baseBox->x2 - ky * sliceY; + box.x1 = baseBox->x2 - ky * (sliceY + sliceH); + box.x2 = baseBox->x2 - ky * sliceY; } box.y1 = baseBox->y1 + kx * sliceX; box.y2 = baseBox->y1 + kx * (sliceX + sliceW); } else if (rotate == 180) { box.x1 = baseBox->x2 - kx * (sliceX + sliceW); box.x2 = baseBox->x2 - kx * sliceX; if (out->upsideDown()) { - box.y1 = baseBox->y1 + ky * sliceY; - box.y2 = baseBox->y1 + ky * (sliceY + sliceH); + box.y1 = baseBox->y1 + ky * sliceY; + box.y2 = baseBox->y1 + ky * (sliceY + sliceH); } else { - box.y1 = baseBox->y2 - ky * (sliceY + sliceH); - box.y2 = baseBox->y2 - ky * sliceY; + box.y1 = baseBox->y2 - ky * (sliceY + sliceH); + box.y2 = baseBox->y2 - ky * sliceY; } } else if (rotate == 270) { if (out->upsideDown()) { - box.x1 = baseBox->x2 - ky * (sliceY + sliceH); - box.x2 = baseBox->x2 - ky * sliceY; + box.x1 = baseBox->x2 - ky * (sliceY + sliceH); + box.x2 = baseBox->x2 - ky * sliceY; } else { - box.x1 = baseBox->x1 + ky * sliceY; - box.x2 = baseBox->x1 + ky * (sliceY + sliceH); + box.x1 = baseBox->x1 + ky * sliceY; + box.x2 = baseBox->x1 + ky * (sliceY + sliceH); } box.y1 = baseBox->y2 - kx * (sliceX + sliceW); box.y2 = baseBox->y2 - kx * sliceX; } else { box.x1 = baseBox->x1 + kx * sliceX; box.x2 = baseBox->x1 + kx * (sliceX + sliceW); if (out->upsideDown()) { - box.y1 = baseBox->y2 - ky * (sliceY + sliceH); - box.y2 = baseBox->y2 - ky * sliceY; + box.y1 = baseBox->y2 - ky * (sliceY + sliceH); + box.y2 = baseBox->y2 - ky * sliceY; } else { - box.y1 = baseBox->y1 + ky * sliceY; - box.y2 = baseBox->y1 + ky * (sliceY + sliceH); + box.y1 = baseBox->y1 + ky * sliceY; + box.y2 = baseBox->y1 + ky * (sliceY + sliceH); } } } else if (useMediaBox) { box = *mediaBox; } else { box = *cropBox; crop = gFalse; } if (globalParams->getPrintCommands()) { printf("***** MediaBox = ll:%g,%g ur:%g,%g\n", - mediaBox->x1, mediaBox->y1, mediaBox->x2, mediaBox->y2); + mediaBox->x1, mediaBox->y1, mediaBox->x2, mediaBox->y2); printf("***** CropBox = ll:%g,%g ur:%g,%g\n", - cropBox->x1, cropBox->y1, cropBox->x2, cropBox->y2); + cropBox->x1, cropBox->y1, cropBox->x2, cropBox->y2); printf("***** Rotate = %d\n", attrs->getRotate()); } gfx = new Gfx(xref, out, num, attrs->getResourceDict(), - hDPI, vDPI, &box, crop ? cropBox : (PDFRectangle *)NULL, - rotate, abortCheckCbk, abortCheckCbkData); + hDPI, vDPI, &box, crop ? cropBox : (PDFRectangle *)NULL, + rotate, abortCheckCbk, abortCheckCbkData); return gfx; } void Page::displaySlice(OutputDev *out, double hDPI, double vDPI, - int rotate, GBool useMediaBox, GBool crop, - int sliceX, int sliceY, int sliceW, int sliceH, - Links *links, Catalog *catalog, - GBool (*abortCheckCbk)(void *data), - void *abortCheckCbkData, + int rotate, GBool useMediaBox, GBool crop, + int sliceX, int sliceY, int sliceW, int sliceH, + Links *links, Catalog *catalog, + GBool (*abortCheckCbk)(void *data), + void *abortCheckCbkData, GBool (*annotDisplayDecideCbk)(Annot *annot, void *user_data), void *annotDisplayDecideCbkData) { Gfx *gfx; Object obj; Link *link; Annots *annotList; int i; gfx = createGfx(out, hDPI, vDPI, rotate, useMediaBox, crop, - sliceX, sliceY, sliceW, sliceH, - links, catalog, - abortCheckCbk, abortCheckCbkData, - annotDisplayDecideCbk, annotDisplayDecideCbkData); + sliceX, sliceY, sliceW, sliceH, + links, catalog, + abortCheckCbk, abortCheckCbkData, + annotDisplayDecideCbk, annotDisplayDecideCbkData); contents.fetch(xref, &obj); if (!obj.isNull()) { gfx->saveState(); gfx->display(&obj); gfx->restoreState(); } obj.free(); @@ -394,18 +400,19 @@ void Page::displaySlice(OutputDev *out, if (annotList->getNumAnnots() > 0) { if (globalParams->getPrintCommands()) { printf("***** Annotations\n"); } for (i = 0; i < annotList->getNumAnnots(); ++i) { Annot *annot = annotList->getAnnot(i); if ((annotDisplayDecideCbk && (*annotDisplayDecideCbk)(annot, annotDisplayDecideCbkData)) || - !annotDisplayDecideCbk) - annot->draw(gfx); + !annotDisplayDecideCbk) { + annot->draw(gfx); + } } out->dump(); } delete annotList; delete gfx; } @@ -417,18 +424,18 @@ void Page::display(Gfx *gfx) { gfx->saveState(); gfx->display(&obj); gfx->restoreState(); } obj.free(); } GBool Page::loadThumb(unsigned char **data_out, - int *width_out, int *height_out, - int *rowstride_out) + int *width_out, int *height_out, + int *rowstride_out) { ImageStream *imgstr; unsigned char *pixbufdata; unsigned int pixbufdatasize; int row, col; int width, height, bits; unsigned char *p; Object obj1, fetched_thumb; @@ -442,24 +449,24 @@ GBool Page::loadThumb(unsigned char **da thumb.fetch(xref, &fetched_thumb); if (fetched_thumb.isNull()) { fetched_thumb.free(); return gFalse; } dict = fetched_thumb.streamGetDict(); str = fetched_thumb.getStream(); - + if (!dict->lookupInt("Width", "W", &width)) goto fail1; if (!dict->lookupInt("Height", "H", &height)) goto fail1; if (!dict->lookupInt("BitsPerComponent", "BPC", &bits)) goto fail1; - + /* Check for invalid dimensions and integer overflow. */ if (width <= 0 || height <= 0) goto fail1; if (width > INT_MAX / 3 / height) goto fail1; pixbufdatasize = width * height * 3; /* Get color space */ @@ -485,18 +492,18 @@ GBool Page::loadThumb(unsigned char **da if (!colorMap->isOk()) { fprintf (stderr, "Error: invalid colormap\n"); goto fail1; } pixbufdata = (unsigned char *) gmalloc(pixbufdatasize); p = pixbufdata; imgstr = new ImageStream(str, width, - colorMap->getNumPixelComps(), - colorMap->getBits()); + colorMap->getNumPixelComps(), + colorMap->getBits()); imgstr->reset(); for (row = 0; row < height; ++row) { for (col = 0; col < width; ++col) { Guchar pix[gfxColorMaxComps]; GfxRGB rgb; imgstr->getPixel(pix); colorMap->getRGB(pix, &rgb); @@ -522,17 +529,17 @@ GBool Page::loadThumb(unsigned char **da delete colorMap; fail1: fetched_thumb.free(); return success; } void Page::getDefaultCTM(double *ctm, double hDPI, double vDPI, - int rotate, GBool upsideDown) { + int rotate, GBool upsideDown) { GfxState *state; int i; rotate += getRotate(); if (rotate >= 360) { rotate -= 360; } else if (rotate < 0) { rotate += 360; } Index: poppler/Page.h =================================================================== RCS file: /cvs/poppler/poppler/poppler/Page.h,v retrieving revision 1.5 diff -u -8 -p -r1.5 Page.h --- poppler/Page.h 30 Oct 2005 20:29:05 -0000 1.5 +++ poppler/Page.h 8 Sep 2006 21:26:07 -0000 @@ -18,16 +18,17 @@ class Dict; class XRef; class OutputDev; class Links; class Catalog; class Annots; class Annot; class Gfx; +class Form; //------------------------------------------------------------------------ class PDFRectangle { public: double x1, y1, x2, y2; PDFRectangle() { x1 = y1 = x2 = y2 = 0; } @@ -146,16 +147,19 @@ public: // Get thumb. Object *getThumb(Object *obj) { return thumb.fetch(xref, obj); } GBool loadThumb(unsigned char **data, int *width, int *height, int *rowstride); // Get transition. Object *getTrans(Object *obj) { return trans.fetch(xref, obj); } + // Get form. + Form *getForm() { return form; } + Gfx *createGfx(OutputDev *out, double hDPI, double vDPI, int rotate, GBool useMediaBox, GBool crop, int sliceX, int sliceY, int sliceW, int sliceH, Links *links, Catalog *catalog, GBool (*abortCheckCbk)(void *data), void *abortCheckCbkData, GBool (*annotDisplayDecideCbk)(Annot *annot, void *user_data), void *annotDisplayDecideCbkData); @@ -190,11 +194,12 @@ private: XRef *xref; // the xref table for this PDF file int num; // page number PageAttrs *attrs; // page attributes Object annots; // annotations array Object contents; // page contents Object thumb; // page thumbnail Object trans; // page transition GBool ok; // true if page is valid + Form *form; // the form for that page }; #endif Index: poppler/XRef.cc =================================================================== RCS file: /cvs/poppler/poppler/poppler/XRef.cc,v retrieving revision 1.13 diff -u -8 -p -r1.13 XRef.cc --- poppler/XRef.cc 1 Apr 2006 11:25:57 -0000 1.13 +++ poppler/XRef.cc 8 Sep 2006 21:26:09 -0000 @@ -196,16 +196,26 @@ Object *ObjectStream::getObject(int objI } return objs[objIdx].copy(obj); } //------------------------------------------------------------------------ // XRef //------------------------------------------------------------------------ +XRef::XRef() { + ok = gTrue; + errCode = errNone; + entries = NULL; + size = 0; + streamEnds = NULL; + streamEndsLen = 0; + objStr = NULL; +} + XRef::XRef(BaseStream *strA) { Guint pos; Object obj; ok = gTrue; errCode = errNone; size = 0; entries = NULL; @@ -259,17 +269,22 @@ XRef::XRef(BaseStream *strA) { } // now set the trailer dictionary's xref pointer so we can fetch // indirect objects from it trailerDict.getDict()->setXRef(this); } XRef::~XRef() { + for(int i=0; igetObj(&obj)->isInt()) { goto err1; } entry.offset = (Guint)obj.getInt(); obj.free(); if (!parser->getObj(&obj)->isInt()) { goto err1; } entry.gen = obj.getInt(); + entry.obj = NULL; obj.free(); parser->getObj(&obj); if (obj.isCmd("n")) { entry.type = xrefEntryUncompressed; } else if (obj.isCmd("f")) { entry.type = xrefEntryFree; } else { goto err1; @@ -502,16 +519,17 @@ GBool XRef::readXRefStream(Stream *xrefS if (newSize * (int)sizeof(XRefEntry)/sizeof(XRefEntry) != newSize) { error(-1, "Invalid 'size' parameter."); return gFalse; } entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry)); for (i = size; i < newSize; ++i) { entries[i].offset = 0xffffffff; entries[i].type = xrefEntryFree; + entries[i].obj = NULL; } size = newSize; } if (!dict->lookupNF("W", &obj)->isArray() || obj.arrayGetLength() < 3) { goto err1; } @@ -596,16 +614,17 @@ GBool XRef::readXRefStreamSection(Stream if (newSize*(int)sizeof(XRefEntry)/sizeof(XRefEntry) != newSize) { error(-1, "Invalid 'size' inside xref table."); return gFalse; } entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry)); for (i = size; i < newSize; ++i) { entries[i].offset = 0xffffffff; entries[i].type = xrefEntryFree; + entries[i].obj = NULL; } size = newSize; } for (i = first; i < first + n; ++i) { if (w[0] == 0) { type = 1; } else { for (type = 0, j = 0; j < w[0]; ++j) { @@ -736,16 +755,17 @@ GBool XRef::constructXRef() { error(-1, "Invalid 'obj' parameters."); return gFalse; } entries = (XRefEntry *) greallocn(entries, newSize, sizeof(XRefEntry)); for (i = size; i < newSize; ++i) { entries[i].offset = 0xffffffff; entries[i].type = xrefEntryFree; + entries[i].obj = NULL; } size = newSize; } if (entries[num].type == xrefEntryFree || gen >= entries[num].gen) { entries[num].offset = pos - start; entries[num].gen = gen; entries[num].type = xrefEntryUncompressed; @@ -845,16 +865,20 @@ Object *XRef::fetch(int num, int gen, Ob Object obj1, obj2, obj3; // check for bogus ref - this can happen in corrupted PDF files if (num < 0 || num >= size) { goto err; } e = &entries[num]; + if(e->obj) { //check for updated object + obj = e->obj->copy(obj); + return obj; + } switch (e->type) { case xrefEntryUncompressed: if (e->gen != gen) { goto err; } obj1.initNull(); parser = new Parser(this, @@ -962,8 +986,71 @@ Guint XRef::strToUnsigned(char *s) { int i; x = 0; for (p = s, i = 0; *p && isdigit(*p) && i < 10; ++p, ++i) { x = 10 * x + (*p - '0'); } return x; } + +void XRef::add(int num, int gen, Guint offs, GBool used) { + size += 1; + entries = (XRefEntry *)greallocn(entries, size, sizeof(XRefEntry)); + XRefEntry *e = &entries[size-1]; + + e->gen = gen; + e->num = num; + e->obj = NULL; + if (used) { + e->type = xrefEntryUncompressed; + e->offset = offs; + } else { + e->type = xrefEntryFree; + e->offset = 0; + } +} + +void XRef::setModifiedObject (Object* o, Ref r) { + if (r.num < 0 || r.num >= size) { + error(-1,"XRef::setModifiedObject on unknown ref: %i, %i\n", r.num, r.gen); + return; + } + entries[r.num].obj = new Object(); + o->copy(entries[r.num].obj); +} + +//used to sort the entries +int compare (const void* a, const void* b) +{ + return (((XRefEntry*)a)->num - ((XRefEntry*)b)->num); +} + +void XRef::writeToFile(FILE* file) { + qsort(entries, size, sizeof(XRefEntry), compare); + //create free entries linked-list + if (entries[0].gen != 65535) { + error(-1, "XRef::writeToFile, entry 0 of the XRef is invalid (gen != 65535)\n"); + } + int lastFreeEntry = 0; + for (int i=0; i 65535) entries[k].gen = 65535; //cap generation number to 65535 (required by PDFReference) + fprintf(file,"%010i %05i %c\r\n", entries[k].offset, entries[k].gen, (entries[k].type==xrefEntryFree)?'f':'n'); + } + i = j-1; + } +} + Index: poppler/XRef.h =================================================================== RCS file: /cvs/poppler/poppler/poppler/XRef.h,v retrieving revision 1.6 diff -u -8 -p -r1.6 XRef.h --- poppler/XRef.h 17 Jan 2006 21:35:31 -0000 1.6 +++ poppler/XRef.h 8 Sep 2006 21:26:09 -0000 @@ -29,22 +29,26 @@ enum XRefEntryType { xrefEntryFree, xrefEntryUncompressed, xrefEntryCompressed }; struct XRefEntry { Guint offset; int gen; + int num; XRefEntryType type; + Object* obj; }; class XRef { public: + // Constructor, create an empty XRef, used for PDF writing + XRef(); // Constructor. Read xref table from stream. XRef(BaseStream *strA); // Destructor. ~XRef(); // Is xref table valid? GBool isOk() { return ok; } @@ -97,16 +101,21 @@ public: // Retuns the entry that belongs to the offset int getNumEntry(int offset) const; // Direct access. int getSize() { return size; } XRefEntry *getEntry(int i) { return &entries[i]; } Object *getTrailerDict() { return &trailerDict; } + // Write access + void setModifiedObject(Object* o, Ref r); + void add(int num, int gen, Guint offs, GBool used); + void writeToFile(FILE* f); + private: BaseStream *str; // input stream Guint start; // offset in file (to allow for garbage // at beginning of file) XRefEntry *entries; // xref entries int size; // size of array int rootNum, rootGen; // catalog dict Index: test/Makefile.am =================================================================== RCS file: /cvs/poppler/poppler/test/Makefile.am,v retrieving revision 1.7 diff -u -8 -p -r1.7 Makefile.am --- test/Makefile.am 12 Apr 2006 02:07:07 -0000 1.7 +++ test/Makefile.am 8 Sep 2006 21:26:10 -0000 @@ -21,26 +21,29 @@ pdf_inspector = \ cairo_includes = \ $(CAIRO_CFLAGS) \ $(FREETYPE_CFLAGS) endif endif +pdf_modification_test = \ + pdf-modification-test + INCLUDES = \ -I$(top_srcdir) \ -I$(top_srcdir)/poppler \ -I$(top_srcdir)/glib \ -I$(top_builddir)/glib \ $(cairo_includes) \ $(GTK_TEST_CFLAGS) -noinst_PROGRAMS = $(gtk_splash_test) $(gtk_cairo_test) $(pdf_inspector) +noinst_PROGRAMS = $(gtk_splash_test) $(gtk_cairo_test) $(pdf_inspector) $(pdf_modification_test) gtk_splash_test_SOURCES = \ gtk-splash-test.cc gtk_splash_test_LDADD = \ $(top_builddir)/poppler/libpoppler.la \ $(GTK_TEST_LIBS) @@ -58,10 +61,17 @@ pdf_inspector_SOURCES = \ pdf_inspector_LDADD = \ $(top_builddir)/poppler/libpoppler.la \ $(top_builddir)/poppler/libpoppler-cairo.la \ $(CAIRO_LIBS) \ $(FREETYPE_LIBS) \ $(GTK_TEST_LIBS) +pdf_modification_test_SOURCES = \ + pdf-modification-test.cc + +pdf_modification_test_LDADD = \ + $(top_builddir)/poppler/libpoppler.la + + EXTRA_DIST = \ pdf-operators.c