Improvements

April 15th, 2008

Lately Pino and me have been hacking the Annot support on poppler with very nice results. Now there are a bunch of new Annot types supported there.

And even better, qt4-bindings have started reading the annotations from the core, instead of their own parsing. This has been a major step since the work was started getting improvements on the Annot support.

Anyway, there is still a lot of job to be done.

The Evince side has been stopped lately. Some changes were needed on Evince to support correctly interactive documents. Carlos has been hacking Evince lately to get this.

That’s why I have been hacking that much poppler lately :).

poppler release today

March 12th, 2008

glib bindings are finished at last, so any application using poppler and glib bindings will have an update annot support.

Required images of the glib-annot demo:

glib-demo-1

glib-demo-2

This mean that in short, evince will have annot support.

Annot support status

February 26th, 2008

I haven’t blogged for some time. Maintaining a life is expensive on the time side. I don’t have too much spare time, and I do use this time for coding, creating something valuable.

I’m still working on annotation support, so don’t worry about it. Things has gone slowly, I know, but I think it’s have been for good.

Here goes a little status:

  • Preliminary Annot support is in poppler now, with the new architecture.
  • Annot support has been cleared and Form support has started to merge with AnnotWidget support. (thank you Julien, Carlos and Michael)
  • glib code is done and awaiting its review. Testing environment is missing though.
  • Hugo Mercier has started hacking Screen and Movie Annot support under the new architecture. Great news.

Once poppler side is finished, the real work on evince will start. The code is done since summer, but there has been some substantial changes in poppler so it will have to change, but the main work is still there.

Lovely poppler

December 11th, 2007

I’m back. The last months have been very stressful, I never thought that my life was going to change this much.

I left my job a pair of weeks ago. It didn’t match my needs anymore, I couldn’t learn more there. Since then I have been in the adventure of searching a new more interesting job.

Anyway I had some free time so I went back to the old poppler patches and started to sync them back again. After looking deeper my old code, I noticed that it is near fully compatible with the old code. There is only one thing different from the old code. In the PDF reference there is nothing or very little information about error handling. There are some default values you can use on error cases, but a lot are missing.

There is one entry in the base Annot dictionary, ‘C’, that is used for the color space and their colors. The color space is selected depending the array length.

  • 0 no color or transparent.
  • 1 device gray
  • 3 device rgb
  • 4 device cmyk

In the old code only device rgb was supported and in the case of wrong numbers in the array it used, 0 for red, 0 for green and 1 for blue. In my code as it does support more color spaces it does default all color values to 0. This is one of those magic numbers that I don’t know where they came from. There are other special features documented in the source code, but there isn’t any documentation for this and as you can suppose there isn’t any default value on the PDF reference.

Here you can download the new updates:

0001-Various-Annot-improvements.patch
0002-Prepare-code-to-Annotation-Subtype-support.patch
0003-AnnotPopup-support.patch
0004-AnnotMarkup-support.patch
0005-AnnotText-support.patch

Hacking Evince (Part I)

August 9th, 2007

Since I started writing in the blog, talking about the steps I did, I wanted to write some technical stuff here. I think anyone wanting to hack a little evince would find this useful. I want to note that everything here is based on some notes I had used in the time I have been coding, so expect some not very clear or obscure explanations.

I want to notice, that poppler has been one of the hardest parts of my work, so I don’t want to write about it until I understand it correctly.

I noticed two fields on evince where I should focus my job.

libdocument

This one is related to how documents work. Evince does work with EvDocuments, this one works as an abstraction of the real documents as PDF, dvi, etc. The features related to those EvDocuments must be defined as interfaces that will implement the back ends that will use them.

As annotations are a new feature from the PDF documents, the first step is create EvAnnotation, a GObject that will carry the data related to the annotations to Evince, so I defined the fields from the data as properties of the object.

Defining it as a GObject and its class attributes.

ev-annotation.h

/* EvAnnotation */
typedef struct _EvAnnotation            EvAnnotation;
typedef struct _EvAnnotationClass       EvAnnotationClass;
typedef struct _EvAnnotationPrivate     EvAnnotationPrivate;

struct _EvAnnotation {
        GObject              base_instance;
        EvAnnotationPrivate *priv;
};

EvAnnotation     *ev_annotation_new      (const gchar *contents,
                                          const gchar *annot_name,
                                          const gchar *modified,
                                          guint        flags,
                                          const gchar *appear_state,
                                          GdkColor    *color);

ev-annotation.c

struct _EvAnnotationPrivate {
        gchar       *contents;
        gchar       *annot_name;
        gchar       *modified;
        guint        flags;
        gchar       *appear_state;
        GdkColor    *color;
};

G_DEFINE_TYPE (EvAnnotation, ev_annotation, G_TYPE_OBJECT)

EvAnnotation *
ev_annotation_new (const gchar *contents,
                   const gchar *annot_name,
                   const gchar *modified,
                   guint        flags,
                   const gchar *appear_state,
                   GdkColor    *color)
{
        EvAnnotation *annot;

        annot = g_object_new (EV_TYPE_ANNOTATION,
                              “contents”, contents,
                              “annot_name”, annot_name,
                              “modified”, modified,
                              “flags”, flags,
                              “appear_state”, appear_state,
                              “color”, color,
                              NULL);
        return annot;
}

Nothing special to anyone familiar with GObject, defining the object, object attributes as properties, class init and finalize, etc. After doing that, I then created an interface to be used on the back ends that will use annotations.

ev-document-annotations.h

struct _EvDocumentAnnotationsIface
{
        GTypeInterface base_iface;

        /* Methods  */
        GList        *(* get_annotations)          (EvDocumentAnnotations *document_annots,
                                                    gint                   page);
};

GType         ev_document_annotations_get_type                 (void);
GList        *ev_document_annotations_get_annotations          (EvDocumentAnnotations *document_annots,
                                                                gint                   page);

ev-document-annotations.c

GList *
ev_document_annotations_get_annotations (EvDocumentAnnotations *document_annotations,
                                         gint                   page)
{
        EvDocumentAnnotationsIface *iface = EV_DOCUMENT_ANNOTATIONS_GET_IFACE (document_annotations);
        GList *retval;

        retval = iface->get_annotations (document_annotations, page);

        return retval;
}

So we have the GObject defined and the interface to be implemented on the back ends so we can retrieve the data from any type of document that supports them. At the moment only PDF back end, poppler, does support annotations, but the idea is to extend this support to other back ends in the future.

static void pdf_document_document_annotations_iface_init (EvDocumentAnnotationsIface *iface);

G_DEFINE_TYPE_WITH_CODE (PdfDocument, pdf_document, G_TYPE_OBJECT,
                         {
				 ...
				 G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_ANNOTATIONS,
				            pdf_document_document_annotations_iface_init);
				 ...
			 });

static void
pdf_document_document_annotations_iface_init (EvDocumentAnnotationsIface *iface)
{
        iface->get_annotations = pdf_document_annotations_get_annotations;
}

As we can see the interface is implemented and pdf_document_annotations_get_annotations will be the function that will catch the data from poppler, and convert it properly, so a PopplerAnnotation will become EvAnnotation.

Converting a PopplerAnnotation into a EvAnnotation

static GList *
pdf_document_annotations_get_annotations (EvDocumentAnnotations *document_annotations,
                                          gint                   page)
{
        GList *retval = NULL;
        PdfDocument *pdf_document;
        PopplerPage *poppler_page;
        GList *mapping_list;
        GList *list;
        gdouble height;

        pdf_document = PDF_DOCUMENT (document_annotations);
        poppler_page = poppler_document_get_page (pdf_document->document, page);
        mapping_list = poppler_page_get_annot_mapping (poppler_page);
        poppler_page_get_size (poppler_page, NULL, &height);

        for (list = mapping_list; list; list = list->next) {
                PopplerAnnotMapping *annot_mapping;
                PopplerAnnot *poppler_annot;
                EvAnnotationMapping *ev_annotation_mapping;
                GdkColor *color;

                annot_mapping = (PopplerAnnotMapping *) list->data;
                poppler_annot = annot_mapping->annot;

                ev_annotation_mapping = g_new (EvAnnotationMapping, 1);

                switch (poppler_annot->type) {
                        case POPPLER_ANNOT_TEXT:
                                EvAnnotationText *annot_text;
                                EvAnnotationMarkupReply reply_to;
                                EvAnnotationExternalData ex_data;
                                EvAnnotationTextIcon icon;
                                EvAnnotationTextState state;

                                color = g_new0 (GdkColor, 1);
                                color->red = (guint16) poppler_annot->color.red;
                                color->green = (guint16) poppler_annot->color.green;
                                color->blue = (guint16) poppler_annot->color.blue;

                                reply_to = (EvAnnotationMarkupReply) poppler_annot->reply_to;
                                ex_data = (EvAnnotationExternalData) poppler_annot->ex_data;
                                icon = (EvAnnotationTextIcon) poppler_annot->annot_text.icon;
                                state = (EvAnnotationTextState) poppler_annot->annot_text.state;

                                annot_text = ev_annotation_text_new (poppler_annot->contents,
                                                                     poppler_annot->name,
                                                                     poppler_annot->modified,
                                                                     poppler_annot->flags,
                                                                     poppler_annot->appear_state,
                                                                     color,
                                                                     poppler_annot->label,
                                                                     poppler_annot->opacity,
                                                                     poppler_annot->date,
                                                                     poppler_annot->subject,
                                                                     reply_to,
                                                                     ex_data,
                                                                     poppler_annot->annot_text.open,
                                                                     icon,
                                                                     state);

                                g_free (color);

                                ev_annotation_mapping->annotation = EV_ANNOTATION (annot_text);
                                break;
                        default:
                                EvAnnotation *annot;

                                color = g_new0 (GdkColor, 1);
                                color->red = (guint16) poppler_annot->color.red;
                                color->green = (guint16) poppler_annot->color.green;
                                color->blue = (guint16) poppler_annot->color.blue;

                                annot = ev_annotation_new (poppler_annot->contents,
                                                           poppler_annot->name,
                                                           poppler_annot->modified,
                                                           poppler_annot->flags,
                                                           poppler_annot->appear_state,
                                                           color);
                                g_free (color);

                                ev_annotation_mapping->annotation = annot;
                                break;
                }
                ev_annotation_mapping->x1 = annot_mapping->area.x1;
                ev_annotation_mapping->x2 = annot_mapping->area.x2;
                /* Invert this for X-style coordinates */
                ev_annotation_mapping->y1 = height - annot_mapping->area.y2;
                ev_annotation_mapping->y2 = height - annot_mapping->area.y1;

                retval = g_list_prepend (retval, ev_annotation_mapping);
        }

        poppler_page_free_annot_mapping (mapping_list);
        g_object_unref (poppler_page);

        return retval;
}

Two things I want to talk about. First the use of mappings. Surely you have noticed that annotations are surrounded by another structure called EvAnnotationMapping. Annotations have the coordinates of their area represented by the Rect field in the PDF file. As there are other objects in the documents that have an area representing their space, mappings of the coordinates are used to handle them better. So the coordinate data are taken out from the annotation and mapping data is filled with it.

The second one are these four lines (plus the five comment line):

                ev_annotation_mapping->x1 = annot_mapping->area.x1;
                ev_annotation_mapping->x2 = annot_mapping->area.x2;
                /* Invert this for X-style coordinates */
                ev_annotation_mapping->y1 = height - annot_mapping->area.y2;
                ev_annotation_mapping->y2 = height - annot_mapping->area.y1;

Evince coordinate system isn’t the same as the one used on the PDF documents, and it has to be converted, and that’s why we are playing with the mapping coordinates.

What’s next ? We now must start with the evinces second field, the shell or evinces core, but that will the next time :).

Working in the university.

August 7th, 2007

Yes, I’m still alive. Sorry for the time I have been without updating the site.

Some days ago, after some little problems about where I should be and what equipment should I have, at last I got a place in the university. So quickly and a lot job to be done, I went back to work. It was a nice notice, we still don’t have an internet connection and after talking with our isp, seems that we will not have any connection until the summer is over.

I have been looking my “old” code inserting it in the new releases of poppler and evince. Evince code was slightly changed too, and I noticed it late so I wasted some time getting the old code working again. University firewall didn’t help neither when I tried to do a simple checkout from the poppler cvs and the evince svn repositories.

The project status.

In the poppler side, writing support still isn’t it, but I’m doing some job with the future in mind. I’m implementing some setters in the annotations objects so once writing support is in, job will be done.

As interesting wishes, it would be nice if forms widget code were merged with AnnotWidget code and the Link code with the AnnotLink ones.

In the evince side, as you can see in the screencast, it does receive the data from the annotations. Now that data should be applied and used to create some popup widgets where the user could see related data and write his own data in the future.

At first sight, the idea used by Carlos and Julien to implement forms is about implementing getters and setters for every control used by forms. I have been talking with Carlos and that is the way I must work. First I will have to finish the poppler side thought.

Update: Looks like the screencast isn’t very well done. I will have to look deeper to Istanbul.

Back to work

July 18th, 2007

It has been a while since I updated the site the last time. University exams are over and I have moved to a new house.
I was waiting to get some interesting progresses so I could write here about them but I had some problems these days.

Once I moved to the new house with my house mates, we decided to get an internet connection. I thought it would be easy and fast, but after some problems with our ISP, we are still waiting.

I have tried working at home, getting the new sources at job, so I could get in sync and update my code, but things were slow and I couldn’t work at the expected level. I have talked with my project chief Juanan and he is going to get me a place where I could work at university, so I will be fully back to work in short.

About the project, I had some improvements at home. Coordinate system problem is fixed, and there is a little work done on the annotation interactivity side. Now that form support has been officially included, I have some examples to be applied to my code. In the poppler side, the patches (1, 2) still haven’t been looked, so I will have to wait in this one.

Expect updates in short with some interesting images :)

Development stop.

June 11th, 2007

I have passed this last week cleaning and debugging the poppler side. Without taking in account some missing bits related to stream entries, I have let it very clean and I think that it is a nice improvement in the annotations side. New annotations are easy to implement having the PDF specification in one side and parsing out the entries in the code.

The glib side is clean too. I have been doing some tests for the implementation so I was sure everything was working fine. One of the problems I have encountered was about GdkColor. Reading the PDF specification you can see that entries related to color are arrays with a variable numbers on them depending on the color space.

  • 0 values: No color/transparent.
  • 1 value: Device Gray
  • 3 value: Device RGB
  • 4 value: Device CMYK

I was mostly interested in the RGB color space. In this case arrays have 3 double values between 0.0 and 1.0. When implementing the glib side, I thought about using GdkColor as the container for those 3 values. So when taking out the data I did allocate memory for a GdkColor structure and fill it with the correct values. As an example:


poppler_annot->color = g_new0 (GdkColor, 1);
poppler_annot->color->red = annot->getRed ();
poppler_annot->color->green = annot->getGreen ();
poppler_annot->color->blue = annot->getBlue ();
list = g_list_prepend (list, poppler_annot);

I was using a GList as a container for every annotation I get from the core. I don’t know exactly what I was doing wrong but after freeing all the elements in a pair of list and the list themselves related to different pages in a document, the program crashed giving me a memory corruption in the list. It take me a pair of hours debugging the code to find and fix the problem and now I’m using another approach to store the colors.

About development stop. I will not fully stop but university exams are coming so I have decided to work less on the project until I finnish them. So the first step has been to submit a new patch updating the poppler core and another one related to glib bindings.

You can take a look to the poppler core patch and the glib side patch.

Reading annotations!

May 31st, 2007

So at last, I can read the text of the annotations! ;)

Annotations text

It is somewhat a funny image.

Lately I have been learning and playing a little with Cairo. It remembered me the days I started learning OpenGL, moving coordinates around before drawing anything. In the image, I only have colored the rectangles with their proper colors and show the text available in the Contents entry in the annotations. So progress has been slow in the evince side.

In the poppler side, I have been clearing more the code, so now it’s more stable than it was. I have added partial Text Annot support on the glib bindings. I will try to implement Popup Annots next.

The first patch

May 29th, 2007

Today Summer of Code 2007 has officially began and I have just send my first patch for poppler. With this patch I have finished the first step to support annotations on poppler, or maybe I should say improve the actual support.

Actually there is support for Link and Widget type annotations, but in a limited way. Their main functionality is implemented as you can see opening any PDF file with links on evince, but they are missing some fields inherited from the base Annot dictionary. At some point in the future I should merge the actual Link implementation, cleaning the code.

In the other side poppler has Widget annotations support. I think these were implemented as a side effect of the Acroforms support, the great job done by Julien Rebetez the last year. It is missing some fields inherited from the base Annot dictionary as Link annotations, but it does work with streams, something I still haven’t done. I should merge the actual implementation too, so poppler could get nice support for those annotation types.

So what is the new annotation support missing ? Mainly these:

  • Stream entries.
  • XObject entries.
  • Multitype entries.

Here goes a new patch for those interested on looking and testing the new changes.