Commit 74e70386 authored by Albert Astals Cid's avatar Albert Astals Cid

2007-02-25 Julien Rebetez <julienr@svn.gnome.org>

            reviewed by: <aacid@kde.org>

        * glib/poppler-document.cc:
        * glib/poppler-document.h:
        * glib/poppler-page.cc:
        * glib/poppler-page.h:
        * glib/poppler-private.h:
        * glib/poppler.h:
        * poppler/Annot.cc:
        * poppler/Annot.h:
        * poppler/Catalog.cc:
        * poppler/Catalog.h:
        * poppler/CharCodeToUnicode.cc:
        * poppler/CharCodeToUnicode.h:
        * poppler/Dict.cc:
        * poppler/Dict.h:
        * poppler/Form.cc:
        * poppler/Form.h:
        * poppler/GfxFont.cc:
        * poppler/GfxFont.h:
        * poppler/Makefile.am:
        * poppler/Object.h:
        * poppler/Page.cc:
        * poppler/Page.h:
        * poppler/XRef.cc:
        * poppler/XRef.h:
                Beginning of Interactive Form support:
                Add a bunch of new classes (FormWidget / FormField) to deal with form
                fields.
                Add support for object modification through XRef::setModifiedObject, as
                well as a function to write the Xref to a file, which will be used
                to implement PDF writing.
                Add some functions to glib wrapper to expose the new form features.
parent 5c4ea446
2007-02-25 Julien Rebetez <julienr@svn.gnome.org>
reviewed by: <aacid@kde.org>
* glib/poppler-document.cc:
* glib/poppler-document.h:
* glib/poppler-page.cc:
* glib/poppler-page.h:
* glib/poppler-private.h:
* glib/poppler.h:
* poppler/Annot.cc:
* poppler/Annot.h:
* poppler/Catalog.cc:
* poppler/Catalog.h:
* poppler/CharCodeToUnicode.cc:
* poppler/CharCodeToUnicode.h:
* poppler/Dict.cc:
* poppler/Dict.h:
* poppler/Form.cc:
* poppler/Form.h:
* poppler/GfxFont.cc:
* poppler/GfxFont.h:
* poppler/Makefile.am:
* poppler/Object.h:
* poppler/Page.cc:
* poppler/Page.h:
* poppler/XRef.cc:
* poppler/XRef.h:
Beginning of Interactive Form support:
Add a bunch of new classes (FormWidget / FormField) to deal with form
fields.
Add support for object modification through XRef::setModifiedObject, as
well as a function to write the Xref to a file, which will be used
to implement PDF writing.
Add some functions to glib wrapper to expose the new form features.
2007-02-18 Albert Astals Cid <aacid@kde.org>
* configure.ac: Change {datadir}/poppler to {datarootdir}/poppler so
......
......@@ -1399,3 +1399,195 @@ poppler_ps_file_free (PopplerPSFile *ps_file)
g_return_if_fail (ps_file != NULL);
g_object_unref (ps_file);
}
FormWidget *
_get_form_widget_by_id (PopplerDocument *document, unsigned id)
{
Catalog *catalog = document->doc->getCatalog();
unsigned pageNum;
unsigned fieldNum;
FormWidget::decodeID(id, &pageNum, &fieldNum);
FormWidget *field = catalog->getPage(pageNum)->getPageWidgets()->getWidget(fieldNum);
if (field != NULL) {
return field;
} else
return NULL;
}
void
poppler_document_set_form_field_button_state (PopplerDocument *document, unsigned id, gboolean state)
{
FormWidget *field = _get_form_widget_by_id(document, id);
if (field && field->getType() == formButton) {
static_cast<FormWidgetButton*>(field)->setState((GBool)state);
} else {
g_warning("poppler_document_set_form_field_button_state, unknown id: %i", id);
}
}
gboolean
poppler_document_get_form_field_button_state (PopplerDocument *document, unsigned id)
{
FormWidget *field = _get_form_widget_by_id(document, id);
if (field && field->getType() == formButton) {
return static_cast<FormWidgetButton*>(field)->getState();
} else {
g_warning("poppler_document_get_form_field_button_state, unknown id: %i", id);
}
return FALSE;
}
void
poppler_document_set_form_field_text_content (PopplerDocument *document, unsigned id, char *content, int length)
{
FormWidget *field = _get_form_widget_by_id(document, id);
if (field && field->getType() == formText) {
GooString *tmp = new GooString(content, length);
static_cast<FormWidgetText*>(field)->setContent(tmp);
delete tmp;
} else {
g_warning("poppler_document_set_form_field_text_content, unknown id: %i", id);
}
}
gchar *
poppler_document_get_form_field_text_content (PopplerDocument *document, unsigned id, int* length)
{
FormWidget *field = _get_form_widget_by_id(document, id);
if (field && field->getType() == formText) {
FormWidgetText *wid = static_cast<FormWidgetText*>(field);
if (wid->getContent()) {
*length = wid->getContent()->getLength();
return wid->getContent()->getCString();
} else {
*length = 0;
return NULL;
}
} else {
g_warning("poppler_document_get_form_field_choice_content, unknown id: %i", id);
*length = 0;
return NULL;
}
}
gchar *
poppler_document_get_form_field_choice_content (PopplerDocument *document, unsigned id, int index, int *length)
{
FormWidget *field = _get_form_widget_by_id(document, id);
if (field && field->getType() == formChoice) {
*length = static_cast<FormWidgetChoice*>(field)->getChoice(index)->getLength();
return static_cast<FormWidgetChoice*>(field)->getChoice(index)->getCString();
} else {
g_warning("poppler_document_get_form_field_choice_content, unknown id: %i", id);
return NULL;
}
}
int
poppler_document_form_field_choice_is_selected (PopplerDocument* document, unsigned id, int index)
{
FormWidget *field = _get_form_widget_by_id(document, id);
if (field && field->getType() == formChoice) {
return static_cast<FormWidgetChoice*>(field)->isSelected(index);
} else {
g_warning("poppler_document_is_form_field_choice_selected, unknown id: %i", id);
return -1;
}
}
int
poppler_document_get_form_field_choice_num_choices (PopplerDocument *document, unsigned id)
{
FormWidget *field = _get_form_widget_by_id(document, id);
if (field && field->getType() == formChoice) {
return static_cast<FormWidgetChoice*>(field)->getNumChoices();
} else {
g_warning("poppler_document_get_form_field_choice_content, unknown id: %i", id);
return -1;
}
}
void
poppler_document_form_field_choice_select (PopplerDocument* document, unsigned id, int index)
{
FormWidget *field = _get_form_widget_by_id(document, id);
if (field && field->getType() == formChoice) {
static_cast<FormWidgetChoice*>(field)->select(index);
} else {
g_warning("poppler_document_set_form_field_choice_select, unknown id: %i", id);
}
}
void
poppler_document_form_field_choice_toggle (PopplerDocument* document, unsigned id, int index)
{
FormWidget *field = _get_form_widget_by_id(document, id);
if (field && field->getType() == formChoice) {
static_cast<FormWidgetChoice*>(field)->toggle(index);
} else {
g_warning("poppler_document_form_field_choice_toggle, unknown id: %i", id);
}
}
void
poppler_document_form_field_choice_deselect_all (PopplerDocument* document, unsigned id)
{
FormWidget *field = _get_form_widget_by_id(document, id);
if (field && field->getType() == formChoice) {
static_cast<FormWidgetChoice*>(field)->deselectAll();
} else {
g_warning("poppler_document_form_field_choice_deselect_all, unknown id: %i", id);
}
}
void
poppler_document_set_form_field_choice_edit (PopplerDocument* document,
unsigned id,
char *content,
int length)
{
FormWidget *field = _get_form_widget_by_id(document, id);
if (field && field->getType() == formChoice) {
GooString *tmp = new GooString(content, length);
static_cast<FormWidgetChoice*>(field)->setEditChoice(tmp);
delete tmp;
} else {
g_warning("poppler_document_form_field_choice_set_edit, unknown id: %i", id);
}
}
gchar *
poppler_document_get_form_field_choice_edit (PopplerDocument* document,
unsigned id,
int *length)
{
FormWidget *field = _get_form_widget_by_id(document, id);
if (field && field->getType() == formChoice) {
FormWidgetChoice *choice = static_cast<FormWidgetChoice*>(field);
if (choice->getEditChoice()) {
*length = choice->getEditChoice()->getLength();
return choice->getEditChoice()->getCString();
} else
return NULL;
} else {
g_warning("poppler_document_get_form_field_choice_edit, unknown id: %i", id);
return NULL;
}
}
PopplerFormField *
poppler_document_find_form_field_by_id (PopplerDocument *document, unsigned id)
{
FormWidget *field = _get_form_widget_by_id(document, id);
if (field != NULL) {
PopplerFormField *poppler_field = _form_field_new_from_widget (field);
return poppler_field;
} else
return NULL;
}
......@@ -112,6 +112,61 @@ GList *poppler_document_get_attachments (PopplerDocument *document)
PopplerDest *poppler_document_find_dest (PopplerDocument *document,
const gchar *link_name);
/* Form */
void poppler_document_set_form_field_text_content (PopplerDocument *document,
unsigned id,
char *content,
int length);
PopplerFormField *poppler_document_find_form_field_by_id (PopplerDocument *document,
unsigned id);
void poppler_document_set_form_field_button_state (PopplerDocument *document,
unsigned id,
gboolean state);
gboolean poppler_document_get_form_field_button_state (PopplerDocument *document,
unsigned id);
gchar *
poppler_document_get_form_field_text_content (PopplerDocument *document,
unsigned id,
int *length);
int
poppler_document_get_form_field_choice_num_choices (PopplerDocument *document,
unsigned id);
gchar *
poppler_document_get_form_field_choice_content (PopplerDocument *document,
unsigned id,
int index,
int *length);
int
poppler_document_form_field_choice_is_selected (PopplerDocument* document,
unsigned id,
int index);
void
poppler_document_form_field_choice_select (PopplerDocument* document,
unsigned id,
int index);
void
poppler_document_form_field_choice_toggle (PopplerDocument* document,
unsigned id,
int index);
void
poppler_document_form_field_choice_deselect_all (PopplerDocument* document,
unsigned id);
void
poppler_document_set_form_field_choice_edit (PopplerDocument* document,
unsigned id,
char *content,
int length);
gchar *
poppler_document_get_form_field_choice_edit (PopplerDocument* document,
unsigned id,
int *length);
/* 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;
......
......@@ -1038,3 +1038,123 @@ poppler_link_mapping_free (PopplerLinkMapping *mapping)
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_widget (FormWidget* 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();
poppler_field->font_size = field->getFontSize();
if (poppler_field->type == POPPLER_FORM_FIELD_TEXT) {
FormWidgetText* wid = static_cast<FormWidgetText*>(field);
GooString *tmp = wid->getContentCopy();
poppler_field->text.content = (tmp)?tmp->getCString():NULL;
poppler_field->text.length = (tmp)?tmp->getLength():0;
poppler_field->text.multiline = wid->isMultiline();
poppler_field->text.password = wid->isPassword();
poppler_field->text.fileselect = wid->isFileSelect();
poppler_field->text.do_not_spell_check = wid->noSpellCheck();
poppler_field->text.do_not_scroll = wid->noScroll();
poppler_field->text.rich_text = wid->isRichText();
} else if (poppler_field->type == POPPLER_FORM_FIELD_BUTTON) {
poppler_field->button.state = (gboolean)static_cast<FormWidgetButton*>(field)->getState();
} else if (poppler_field->type == POPPLER_FORM_FIELD_CHOICE) {
FormWidgetChoice* wid = static_cast<FormWidgetChoice*>(field);
poppler_field->choice.combo = wid->isCombo();
poppler_field->choice.edit = wid->hasEdit();
poppler_field->choice.multi_select = wid->isMultiSelect();
poppler_field->choice.do_not_spell_check = wid->noSpellCheck();
}
return poppler_field;
}
/**
* poppler_page_get_form_fields
**/
GList*
poppler_page_get_form_fields (PopplerPage *page)
{
GList *field_list = NULL;
FormPageWidgets *form;
gint i;
Object obj;
g_return_val_if_fail (POPPLER_IS_PAGE (page), NULL);
form = page->page->getPageWidgets();
obj.free ();
if(form == NULL)
return NULL;
for(i = 0; i < form->getNumWidgets(); i++) {
PopplerFormField *poppler_field;
FormWidget *field;
field = form->getWidget(i);
poppler_field = _form_field_new_from_widget (field);
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;
}
......@@ -81,6 +81,11 @@ void poppler_page_render_selection (PopplerPage *page,
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. */
......@@ -113,6 +118,56 @@ 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 multiline: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;
int length;
};
struct _PopplerButtonField
{
//content
gboolean state;
};
struct _PopplerChoiceField
{
char combo:1;
char edit: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;
double font_size;
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__ */
......@@ -5,6 +5,7 @@
#include <PDFDoc.h>
#include <PSOutputDev.h>
#include <Link.h>
#include <Form.h>
#include <Gfx.h>
#include <FontInfo.h>
#include <TextOutputDev.h>
......@@ -60,6 +61,9 @@ struct _PopplerPage
Gfx *gfx;
};
PopplerFormField *_form_field_new_from_widget (FormWidget* field);
PopplerPage *_poppler_page_new (PopplerDocument *document,
Page *page,
int index);
......
......@@ -42,17 +42,30 @@ typedef enum
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,
} 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
{
......
This diff is collapsed.
......@@ -16,6 +16,9 @@
class XRef;
class Gfx;
class Catalog;
class CharCodeToUnicode;
class GfxFont;
class FormWidget;
//------------------------------------------------------------------------
// Annot
......@@ -24,7 +27,7 @@ class Catalog;
class Annot {
public:
Annot(XRef *xrefA, Dict *acroForm, Dict *dict);
Annot(XRef *xrefA, Dict *acroForm, Dict *dict, Ref* aref, Catalog* catalog);
~Annot();
GBool isOk() { return ok; }
......@@ -32,9 +35,15 @@ public:
// Get appearance object.
Object *getAppearance(Object *obj) { return appearance.fetch(xref, obj); }
GBool textField() { return isTextField; }
private:
double getXMin() { return xMin; }
double getYMin() { return yMin; }
double getFontSize() { return fontSize; }
private:
void writeTextString (GooString* vStr, CharCodeToUnicode* ccToUnicode, GooString* appearBuf, GfxFont* font);
void generateAppearance(Dict *acroForm, Dict *dict);
void readArrayNum(Object *pdfArray, int key, double *value);
......@@ -44,7 +53,15 @@ private:
GooString *appearBuf;
double xMin, yMin, // annotation rectangle
xMax, yMax;
double fontSize;
GBool ok;
GBool regen, isTextField;
GBool isMultiline, isListbox;
bool hasRef;
bool hidden;
Ref ref;
FormWidget* widget;
};
//------------------------------------------------------------------------
......
......@@ -25,6 +25,7 @@
#include "PageLabelInfo.h"
#include "UGooString.h"
#include "Catalog.h"
#include "Form.h"
// This define is used to limit the depth of recursive readPageTree calls
// This is needed because the page tree nodes can reference their parents
......@@ -49,12 +50,21 @@ Catalog::Catalog(XRef *xrefA) {
numPages = pagesSize = 0;
baseURI = NULL;
pageLabelInfo = NULL;
form = NULL;
xref->getCatalog(&catDict);
if (!catDict.isDict()) {
error(-1, "Catalog object is wrong type (%s)", catDict.getTypeName());
goto err1;
}
// get the AcroForm dictionary
catDict.dictLookup("AcroForm", &acroForm);
// load Forms
if (acroForm.isDict()) {
form = new Form(xref,&acroForm);
}
// read page tree
catDict.dictLookup("Pages", &pagesDict);
......@@ -158,8 +168,9 @@ Catalog::Catalog(XRef *xrefA) {
// get the outline dictionary
catDict.dictLookup("Outlines", &outline);
// get the AcroForm dictionary
catDict.dictLookup("AcroForm", &acroForm);
// perform form-related loading after all widgets have been loaded
if (form)
form->postWidgetsLoad();
catDict.free();
return;
......@@ -242,7 +253,7 @@ int Catalog::readPageTree(Dict *pagesDict, PageAttrs *attrs, int start, int call
kids.arrayGet(i, &kid);
if (kid.isDict("Page")) {
attrs2 = new PageAttrs(attrs1, kid.getDict());
page = new Page(xref, start+1, kid.getDict(), attrs2);
page = new Page(xref, start+1, kid.getDict(), attrs2, form);
if (!page->isOk()) {
++start;
goto err3;
......
......@@ -21,6 +21,7 @@ struct Ref;
class LinkDest;
class UGooString;
class PageLabelInfo;
class Form;
//------------------------------------------------------------------------
// NameTree
......@@ -151,6 +152,8 @@ public:
Object *getAcroForm() { return &acroForm; }
Form* getForm() { return form; }
enum PageMode {
pageModeNone,
pageModeOutlines,
......@@ -178,6 +181,7 @@ private:
XRef *xref; // the xref table for this PDF file
Page **pages; // array of pages
Ref *pageRefs; // object ID for each page
Form *form;
int numPages; // number of pages
int pagesSize; // size of pages array
Object dests; // named destination dictionary
......
......@@ -504,6 +504,40 @@ int CharCodeToUnicode::mapToUnicode(CharCode c, Unicode *u, int size) {
return 0;
}
int CharCodeToUnicode::mapToCharCode(Unicode* u, CharCode *c, int usize) {
//look for charcode in map
if (usize == 1) {
for (int i=0; i<mapLen; i++) {
if (map[i] == ((*u)&0xff)) {
*c = (char)map[i];
return 1;
}
}
*c = 'x';
} else {
int i, j;
//for each entry in the sMap
for (i=0; i<sMapLen; i++) {
//if the entry's unicode length isn't the same are usize, the strings
// are obviously differents
if (sMap[i].len != usize) continue;
//compare the string char by char
for (j=0; j<sMap[i].len; j++) {
if (sMap[i].u[j] != u[j]) {
continue;
}
}
//we have the same strings
if (j==sMap[i].len) {
*c = sMap[i].c;
return 1;
}
}
}
return 0;
}
//------------------------------------------------------------------------
CharCodeToUnicodeCache::CharCodeToUnicodeCache(int sizeA) {
......
......@@ -27,6 +27,7 @@ struct CharCodeToUnicodeString;
//------------------------------------------------------------------------
class CharCodeToUnicode {
friend class UnicodeToCharCode;
public:
// Read the CID-to-Unicode mapping for <collection> from the file
......@@ -66,6 +67,8 @@ public:
// Map a CharCode to Unicode.
int mapToUnicode(CharCode c, Unicode *u, int size);
int mapToCharCode(Unicode* u, CharCode *c, int usize);
// Return the mapping's length, i.e., one more than the max char
// code supported by the mapping.
CharCode getLength() { return mapLen; }
......
......@@ -65,6 +65,38 @@ inline DictEntry *Dict::find(const UGooString &key) {