diff options
author | Kristian Høgsberg <krh@redhat.com> | 2005-06-29 21:24:48 +0000 |
---|---|---|
committer | Kristian Høgsberg <krh@redhat.com> | 2005-06-29 21:24:48 +0000 |
commit | b15a8caf003d7d0631d4f78db5ab54e55a5a000a (patch) | |
tree | 909ab064925e24c2fa8fa08e1f13df0fc961d860 | |
parent | 3b5e20465e482eb0e75a106697ee94d60aea2fdc (diff) |
2005-06-29 Kristian Høgsberg <krh@redhat.com>
* configure.ac:
* glib/poppler-page.cc:
* glib/poppler-page.h:
* glib/poppler-private.h:
* poppler/CairoOutputDev.cc:
* poppler/CairoOutputDev.h:
* poppler/Page.cc:
* poppler/Page.h:
* poppler/TextOutputDev.cc:
* poppler/TextOutputDev.h: Add support for rendering real
selection (based on text flow).
-rw-r--r-- | ChangeLog | 20 | ||||
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | glib/poppler-page.cc | 224 | ||||
-rw-r--r-- | glib/poppler-page.h | 65 | ||||
-rw-r--r-- | glib/poppler-private.h | 3 | ||||
-rw-r--r-- | poppler/CairoOutputDev.cc | 13 | ||||
-rw-r--r-- | poppler/CairoOutputDev.h | 2 | ||||
-rw-r--r-- | poppler/Page.cc | 59 | ||||
-rw-r--r-- | poppler/Page.h | 12 | ||||
-rw-r--r-- | poppler/TextOutputDev.cc | 414 | ||||
-rw-r--r-- | poppler/TextOutputDev.h | 38 |
11 files changed, 743 insertions, 111 deletions
@@ -1,7 +1,23 @@ -2005-06-28 Albert Astals Cid <aacid@kde.org> +2005-06-29 Kristian Høgsberg <krh@redhat.com> + + * configure.ac: + * glib/poppler-page.cc: + * glib/poppler-page.h: + * glib/poppler-private.h: + * poppler/CairoOutputDev.cc: + * poppler/CairoOutputDev.h: + * poppler/Page.cc: + * poppler/Page.h: + * poppler/TextOutputDev.cc: + * poppler/TextOutputDev.h: Add support for rendering real + selection (based on text flow). + +2005-06-28 Albert Astals Cid <aacid@kde.org> + * poppler/FontInfo.[cc,h]: Add FontInfo::getType() -2005-06-28 Albert Astals Cid <aacid@kde.org> +2005-06-28 Albert Astals Cid <aacid@kde.org> + * poppler/ArthurOutputDev.cc: use transformation matrix for image rendering diff --git a/configure.ac b/configure.ac index a2ad6d4f..84b0b652 100644 --- a/configure.ac +++ b/configure.ac @@ -202,9 +202,9 @@ AC_ARG_ENABLE(poppler-glib, enable_poppler_glib=$enableval, enable_poppler_glib="try") if test x$enable_poppler_glib = xyes; then - PKG_CHECK_MODULES(POPPLER_GLIB, gdk-pixbuf-2.0 glib-2.0 >= 2.4.0 fontconfig) + PKG_CHECK_MODULES(POPPLER_GLIB, gdk-2.0 >= 2.4.0 fontconfig) elif test x$enable_poppler_glib = xtry; then - PKG_CHECK_MODULES(POPPLER_GLIB, gdk-pixbuf-2.0 glib-2.0 >= 2.4.0 fontconfig, + PKG_CHECK_MODULES(POPPLER_GLIB, gdk-2.0 >= 2.4.0 fontconfig, [enable_poppler_glib="yes"], [enable_poppler_glib="no"]) fi diff --git a/glib/poppler-page.cc b/glib/poppler-page.cc index a6a630f4..7760dc35 100644 --- a/glib/poppler-page.cc +++ b/glib/poppler-page.cc @@ -88,6 +88,13 @@ _poppler_page_new (PopplerDocument *document, Page *page, int index) static void poppler_page_finalize (GObject *object) { + PopplerPage *page = POPPLER_PAGE (object); + + if (page->gfx != NULL) + delete page->gfx; + if (page->text_dev != NULL) + delete page->text_dev; + /* page->page is owned by the document */ } @@ -157,82 +164,100 @@ poppler_page_get_index (PopplerPage *page) #if defined (HAVE_CAIRO) +typedef struct { + unsigned char *cairo_data; + cairo_surface_t *surface; +} OutputDevData; + static void -cairo_render_to_pixbuf (PopplerPage *page, - int src_x, int src_y, - int src_width, int src_height, - double scale, - GdkPixbuf *pixbuf, - int dest_x, int dest_y) +poppler_page_prepare_output_dev (PopplerPage *page, + double scale, + gboolean transparent, + OutputDevData *output_dev_data) { CairoOutputDev *output_dev; - int cairo_width, cairo_height, cairo_rowstride; - int pixbuf_rowstride, pixbuf_n_channels; - guchar *pixbuf_data, *cairo_data, *dst; cairo_surface_t *surface; - int x, y; + int cairo_width, cairo_height, cairo_rowstride; + unsigned char *cairo_data; output_dev = page->document->output_dev; - cairo_width = MAX ((int)(page->page->getWidth() * scale + 0.5), 1); cairo_height = MAX ((int)(page->page->getHeight() * scale + 0.5), 1); cairo_rowstride = cairo_width * 4; cairo_data = (guchar *) gmalloc (cairo_height * cairo_rowstride); - memset (cairo_data, 0xff, cairo_height * cairo_rowstride); + if (transparent) + memset (cairo_data, 0x00, cairo_height * cairo_rowstride); + else + memset (cairo_data, 0xff, cairo_height * cairo_rowstride); + surface = cairo_image_surface_create_for_data(cairo_data, CAIRO_FORMAT_ARGB32, cairo_width, cairo_height, cairo_rowstride); + + output_dev_data->cairo_data = cairo_data; + output_dev_data->surface = surface; output_dev->setSurface (surface); +} - page->page->displaySlice(output_dev, 72.0 * scale, 72.0 * scale, - poppler_page_get_rotate (page), - gTrue, /* Crop */ - src_x, src_y, - src_width, src_height, - NULL, /* links */ - page->document->doc->getCatalog ()); +void +poppler_page_copy_to_pixbuf (PopplerPage *page, + GdkPixbuf *pixbuf, + OutputDevData *output_dev_data) +{ + int cairo_width, cairo_height, cairo_rowstride; + unsigned char *pixbuf_data, *dst, *cairo_data; + int pixbuf_rowstride, pixbuf_n_channels; + unsigned int *src; + int x, y; + + cairo_width = cairo_image_surface_get_width (output_dev_data->surface); + cairo_height = cairo_image_surface_get_height (output_dev_data->surface); + cairo_rowstride = cairo_width * 4; + cairo_data = output_dev_data->cairo_data; pixbuf_data = gdk_pixbuf_get_pixels (pixbuf); pixbuf_rowstride = gdk_pixbuf_get_rowstride (pixbuf); pixbuf_n_channels = gdk_pixbuf_get_n_channels (pixbuf); - if (dest_x + cairo_width > gdk_pixbuf_get_width (pixbuf)) - cairo_width = gdk_pixbuf_get_width (pixbuf) - dest_x; - if (dest_y + cairo_height > gdk_pixbuf_get_height (pixbuf)) - cairo_height = gdk_pixbuf_get_height (pixbuf) - dest_y; - for (y = 0; y < cairo_height; y++) { - unsigned int *src; - src = (unsigned int *) (cairo_data + y * cairo_rowstride); - dst = pixbuf_data + (dest_y + y) * pixbuf_rowstride + - dest_x * pixbuf_n_channels; + dst = pixbuf_data + y * pixbuf_rowstride; for (x = 0; x < cairo_width; x++) { dst[0] = (*src >> 16) & 0xff; dst[1] = (*src >> 8) & 0xff; dst[2] = (*src >> 0) & 0xff; + if (pixbuf_n_channels == 4) + dst[3] = (*src >> 24) & 0xff; dst += pixbuf_n_channels; src++; } } - output_dev->setSurface (NULL); - cairo_surface_destroy (surface); - gfree (cairo_data); + page->document->output_dev->setSurface (NULL); + cairo_surface_destroy (output_dev_data->surface); + gfree (output_dev_data->cairo_data); } #elif defined (HAVE_SPLASH) +typedef struct { +} OutputDevData; + static void -splash_render_to_pixbuf (PopplerPage *page, - int src_x, int src_y, - int src_width, int src_height, - double scale, - GdkPixbuf *pixbuf, - int dest_x, int dest_y) +poppler_page_prepare_output_dev (PopplerPage *page, + double scale, + gboolean transparent, + OutputDevData *output_dev_data) +{ + /* pft */ +} + +poppler_page_copy_to_pixbuf(PopplerPage *page, + GdkPixbuf *pixbuf, + OutputDevData *data) { SplashOutputDev *output_dev; SplashBitmap *bitmap; @@ -244,14 +269,6 @@ splash_render_to_pixbuf (PopplerPage *page, output_dev = page->document->output_dev; - page->page->displaySlice(output_dev, 72.0 * scale, 72.0 * scale, - poppler_page_get_rotate (page), - gTrue, /* Crop */ - src_x, src_y, - src_width, src_height, - NULL, /* links */ - page->document->doc->getCatalog ()); - bitmap = output_dev->getBitmap (); color_ptr = bitmap->getDataPtr (); @@ -314,22 +331,121 @@ poppler_page_render_to_pixbuf (PopplerPage *page, GdkPixbuf *pixbuf, int dest_x, int dest_y) { + OutputDevData data; g_return_if_fail (POPPLER_IS_PAGE (page)); g_return_if_fail (scale > 0.0); g_return_if_fail (pixbuf != NULL); -#if defined(HAVE_CAIRO) - cairo_render_to_pixbuf (page, src_x, src_y, src_width, src_height, - scale, pixbuf, dest_x, dest_y); -#elif defined(HAVE_SPLASH) - splash_render_to_pixbuf (page, src_x, src_y, src_width, src_height, - scale, pixbuf, dest_x, dest_y); -#else -#error No rendering backend available -#endif + poppler_page_prepare_output_dev (page, scale, FALSE, &data); + + page->page->displaySlice(page->document->output_dev, + 72.0 * scale, 72.0 * scale, + poppler_page_get_rotate (page), + gTrue, /* Crop */ + src_x, src_y, + src_width, src_height, + NULL, /* links */ + page->document->doc->getCatalog ()); + + poppler_page_copy_to_pixbuf (page, pixbuf, &data); +} + +static TextOutputDev * +poppler_page_get_text_output_dev (PopplerPage *page) +{ + if (page->text_dev == NULL) { + page->text_dev = new TextOutputDev (NULL, gTrue, gFalse, gFalse); + + page->gfx = page->page->createGfx(page->text_dev, + 72.0, 72.0, + poppler_page_get_rotate (page), + gTrue, /* Crop */ + -1, -1, -1, -1, + NULL, /* links */ + page->document->doc->getCatalog (), + NULL, NULL, NULL, NULL); + + page->page->display(page->gfx); + + page->text_dev->endPage(); + } + + return page->text_dev; +} + +GdkRegion * +poppler_page_get_selection_region (PopplerPage *page, + gdouble scale, + PopplerRectangle *selection) +{ + TextOutputDev *text_dev; + PDFRectangle poppler_selection; + GooList *list; + GdkRectangle rect; + GdkRegion *region; + int i; + + poppler_selection.x1 = selection->x1; + poppler_selection.y1 = selection->y1; + poppler_selection.x2 = selection->x2; + poppler_selection.y2 = selection->y2; + + text_dev = poppler_page_get_text_output_dev (page); + list = text_dev->getSelectionRegion(&poppler_selection, scale); + + region = gdk_region_new(); + + for (i = 0; i < list->getLength(); i++) { + PDFRectangle *selection_rect = (PDFRectangle *) list->get(i); + rect.x = (gint) selection_rect->x1; + rect.y = (gint) selection_rect->y1; + rect.width = (gint) (selection_rect->x2 - selection_rect->x1); + rect.height = (gint) (selection_rect->y2 - selection_rect->y1); + gdk_region_union_with_rect (region, &rect); + delete selection_rect; + } + + delete list; + + return region; } +void +poppler_page_render_selection (PopplerPage *page, + gdouble scale, + GdkPixbuf *pixbuf, + PopplerRectangle *selection, + PopplerRectangle *old_selection) +{ + TextOutputDev *text_dev; + CairoOutputDev *output_dev; + cairo_surface_t *surface; + unsigned char *cairo_data; + OutputDevData data; + PDFRectangle pdf_selection(selection->x1, selection->y1, + selection->x2, selection->y2); + + text_dev = poppler_page_get_text_output_dev (page); + output_dev = page->document->output_dev; + + poppler_page_prepare_output_dev (page, scale, TRUE, &data); + + text_dev->drawSelection (output_dev, scale, &pdf_selection); + + poppler_page_copy_to_pixbuf (page, pixbuf, &data); + + /* We'll need a function to destroy page->text_dev and page->gfx + * when the application wants to get rid of them. + * + * Two improvements: 1) make GfxFont refcounted and let TextPage and + * friends hold a reference to the GfxFonts they need so we can free + * up Gfx early. 2) use a TextPage directly when rendering the page + * so we don't have to use TextOutputDev and render a second + * time. */ +} + + static void destroy_thumb_data (guchar *pixels, gpointer data) { diff --git a/glib/poppler-page.h b/glib/poppler-page.h index 8f0d4d3c..5994089c 100644 --- a/glib/poppler-page.h +++ b/glib/poppler-page.h @@ -20,6 +20,7 @@ #define __POPPLER_PAGE_H__ #include <glib-object.h> +#include <gdk/gdkregion.h> #include <gdk-pixbuf/gdk-pixbuf.h> #include "poppler.h" @@ -33,34 +34,42 @@ G_BEGIN_DECLS GType poppler_page_get_type (void) G_GNUC_CONST; -void poppler_page_render_to_pixbuf (PopplerPage *page, - int src_x, - int src_y, - int src_width, - int src_height, - double scale, - GdkPixbuf *pixbuf, - int dest_x, - int dest_y); -void poppler_page_get_size (PopplerPage *page, - double *width, - double *height); -PopplerOrientation poppler_page_get_orientation (PopplerPage *page); -void poppler_page_set_orientation (PopplerPage *page, - PopplerOrientation orientation); -int poppler_page_get_index (PopplerPage *page); -GdkPixbuf *poppler_page_get_thumbnail (PopplerPage *page); -gboolean poppler_page_get_thumbnail_size (PopplerPage *page, - int *width, - int *height); -GList *poppler_page_find_text (PopplerPage *page, - const char *text); -void poppler_page_render_to_ps (PopplerPage *page, - PopplerPSFile *ps_file); -char *poppler_page_get_text (PopplerPage *page, - PopplerRectangle *rect); -GList *poppler_page_get_link_mapping (PopplerPage *page); -void poppler_page_free_link_mapping (GList *list); +void poppler_page_render_to_pixbuf (PopplerPage *page, + int src_x, + int src_y, + int src_width, + int src_height, + double scale, + GdkPixbuf *pixbuf, + int dest_x, + int dest_y); +void poppler_page_get_size (PopplerPage *page, + double *width, + double *height); +PopplerOrientation poppler_page_get_orientation (PopplerPage *page); +void poppler_page_set_orientation (PopplerPage *page, + PopplerOrientation orientation); +int poppler_page_get_index (PopplerPage *page); +GdkPixbuf *poppler_page_get_thumbnail (PopplerPage *page); +gboolean poppler_page_get_thumbnail_size (PopplerPage *page, + int *width, + int *height); +GList *poppler_page_find_text (PopplerPage *page, + const char *text); +void poppler_page_render_to_ps (PopplerPage *page, + PopplerPSFile *ps_file); +char *poppler_page_get_text (PopplerPage *page, + PopplerRectangle *rect); +GList *poppler_page_get_link_mapping (PopplerPage *page); +void poppler_page_free_link_mapping (GList *list); +GdkRegion * poppler_page_get_selection_region (PopplerPage *page, + gdouble scale, + PopplerRectangle *selection); +void poppler_page_render_selection (PopplerPage *page, + gdouble scale, + GdkPixbuf *pixbuf, + PopplerRectangle *selection, + PopplerRectangle *old_selection); /* A rectangle on a page, with coordinates in PDF points. */ diff --git a/glib/poppler-private.h b/glib/poppler-private.h index 4ddd8688..41ed2b84 100644 --- a/glib/poppler-private.h +++ b/glib/poppler-private.h @@ -5,6 +5,7 @@ #include <PDFDoc.h> #include <PSOutputDev.h> #include <Link.h> +#include <Gfx.h> #include <FontInfo.h> #if defined (HAVE_CAIRO) @@ -44,6 +45,8 @@ struct _PopplerPage Page *page; int index; PopplerOrientation orientation; + TextOutputDev *text_dev; + Gfx *gfx; }; PopplerPage *_poppler_page_new (PopplerDocument *document, diff --git a/poppler/CairoOutputDev.cc b/poppler/CairoOutputDev.cc index e3aa5409..ea6d78da 100644 --- a/poppler/CairoOutputDev.cc +++ b/poppler/CairoOutputDev.cc @@ -53,6 +53,7 @@ CairoOutputDev::CairoOutputDev() { FT_Init_FreeType(&ft_lib); fontEngine = NULL; + glyphs = NULL; surface = NULL; } @@ -219,7 +220,6 @@ void CairoOutputDev::updateFont(GfxState *state) { LOG(printf ("updateFont() font=%s\n", state->getFont()->getName()->getCString())); - /* Needs to be rethough, since fonts are now handled by cairo */ needFontUpdate = gFalse; currentFont = fontEngine->getFont (state->getFont(), xref); @@ -342,6 +342,9 @@ void CairoOutputDev::beginString(GfxState *state, GooString *s) { int len = s->getLength(); + if (needFontUpdate) + updateFont(state); + glyphs = (cairo_glyph_t *) gmalloc (len * sizeof (cairo_glyph_t)); glyphCount = 0; } @@ -364,8 +367,6 @@ void CairoOutputDev::endString(GfxState *state) { int render; - if (needFontUpdate) - updateFont(state); if (!currentFont) return; @@ -375,8 +376,11 @@ void CairoOutputDev::endString(GfxState *state) return; // ignore empty strings - if (glyphCount == 0) + if (glyphCount == 0) { + gfree(glyphs); + glyphs = NULL; return; + } if (!(render & 1)) { LOG (printf ("fill string\n")); @@ -405,6 +409,7 @@ void CairoOutputDev::endString(GfxState *state) } gfree (glyphs); + glyphs = NULL; } GBool CairoOutputDev::beginType3Char(GfxState *state, double x, double y, diff --git a/poppler/CairoOutputDev.h b/poppler/CairoOutputDev.h index 622b232e..7847e565 100644 --- a/poppler/CairoOutputDev.h +++ b/poppler/CairoOutputDev.h @@ -146,7 +146,7 @@ protected: FT_Library ft_lib; CairoFontEngine *fontEngine; cairo_t *cairo; - GBool needFontUpdate; // set when the font needs to be updated + GBool needFontUpdate; // set when the font needs to be updated cairo_surface_t *surface; cairo_glyph_t *glyphs; int glyphCount; diff --git a/poppler/Page.cc b/poppler/Page.cc index 1babf8ef..b0a7eae5 100644 --- a/poppler/Page.cc +++ b/poppler/Page.cc @@ -25,6 +25,7 @@ #include "Gfx.h" #include "GfxState.h" #include "Annot.h" +#include "TextOutputDev.h" #endif #include "Error.h" #include "Page.h" @@ -250,23 +251,18 @@ void Page::display(OutputDev *out, double hDPI, double vDPI, annotDisplayDecideCbk, annotDisplayDecideCbkData); } -void Page::displaySlice(OutputDev *out, double hDPI, double vDPI, - int rotate, 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) { -#ifndef PDF_PARSER_ONLY +Gfx *Page::createGfx(OutputDev *out, double hDPI, double vDPI, + int rotate, 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; PDFRectangle box; Gfx *gfx; - Object obj; - Link *link; - Annots *annotList; double kx, ky; - int i; rotate += getRotate(); if (rotate >= 360) { @@ -338,6 +334,30 @@ void Page::displaySlice(OutputDev *out, double hDPI, double vDPI, gfx = new Gfx(xref, out, num, attrs->getResourceDict(), hDPI, vDPI, &box, crop && isCropped(), cropBox, rotate, abortCheckCbk, abortCheckCbkData); + + return gfx; +} + +void Page::displaySlice(OutputDev *out, double hDPI, double vDPI, + int rotate, 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, crop, + sliceX, sliceY, sliceW, sliceH, + links, catalog, + abortCheckCbk, abortCheckCbkData, + annotDisplayDecideCbk, annotDisplayDecideCbkData); + contents.fetch(xref, &obj); if (!obj.isNull()) { gfx->saveState(); @@ -378,7 +398,18 @@ void Page::displaySlice(OutputDev *out, double hDPI, double vDPI, delete annotList; delete gfx; -#endif +} + +void Page::display(Gfx *gfx) { + Object obj; + + contents.fetch(xref, &obj); + if (!obj.isNull()) { + gfx->saveState(); + gfx->display(&obj); + gfx->restoreState(); + } + obj.free(); } GBool Page::loadThumb(unsigned char **data_out, diff --git a/poppler/Page.h b/poppler/Page.h index 07fa6cbf..4ab84a1e 100644 --- a/poppler/Page.h +++ b/poppler/Page.h @@ -22,6 +22,7 @@ class Links; class Catalog; class Annots; class Annot; +class Gfx; //------------------------------------------------------------------------ @@ -147,6 +148,15 @@ public: // Get transition. Object *getTrans(Object *obj) { return trans.fetch(xref, obj); } + Gfx *createGfx(OutputDev *out, double hDPI, double vDPI, + int rotate, 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); + // Display a page. void display(OutputDev *out, double hDPI, double vDPI, int rotate, GBool crop, @@ -166,6 +176,8 @@ public: GBool (*annotDisplayDecideCbk)(Annot *annot, void *user_data) = NULL, void *annotDisplayDecideCbkData = NULL); + void display(Gfx *gfx); + private: XRef *xref; // the xref table for this PDF file diff --git a/poppler/TextOutputDev.cc b/poppler/TextOutputDev.cc index 2decf299..fa35c4ca 100644 --- a/poppler/TextOutputDev.cc +++ b/poppler/TextOutputDev.cc @@ -31,6 +31,7 @@ #include "UnicodeTypeTable.h" #include "GfxState.h" #include "TextOutputDev.h" +#include "Page.h" #ifdef MACOS // needed for setting type/creator of MacOS files @@ -208,6 +209,7 @@ TextWord::TextWord(GfxState *state, int rotA, double x0, double y0, break; } text = NULL; + charcode = NULL; edge = NULL; len = size = 0; spaceAfter = gFalse; @@ -229,17 +231,20 @@ TextWord::TextWord(GfxState *state, int rotA, double x0, double y0, TextWord::~TextWord() { gfree(text); + gfree(charcode); gfree(edge); } void TextWord::addChar(GfxState *state, double x, double y, - double dx, double dy, Unicode u) { + double dx, double dy, CharCode c, Unicode u) { if (len == size) { size += 16; text = (Unicode *)grealloc(text, size * sizeof(Unicode)); + charcode = (Unicode *)grealloc(charcode, size * sizeof(CharCode)); edge = (double *)grealloc(edge, (size + 1) * sizeof(double)); } text[len] = u; + charcode[len] = c; switch (rot) { case 0: if (len == 0) { @@ -291,10 +296,12 @@ void TextWord::merge(TextWord *word) { if (len + word->len > size) { size = len + word->len; text = (Unicode *)grealloc(text, size * sizeof(Unicode)); + charcode = (CharCode *)grealloc(charcode, (size + 1) * sizeof(CharCode)); edge = (double *)grealloc(edge, (size + 1) * sizeof(double)); } for (i = 0; i < word->len; ++i) { text[len + i] = word->text[i]; + charcode[len + i] = word->charcode[i]; edge[len + i] = word->edge[i]; } edge[len + word->len] = word->edge[word->len]; @@ -480,8 +487,6 @@ void TextPool::addWord(TextWord *word) { TextLine::TextLine(TextBlock *blkA, int rotA, double baseA) { blk = blkA; rot = rotA; - xMin = yMin = 0; - xMax = yMax = -1; base = baseA; words = lastWord = NULL; text = NULL; @@ -491,6 +496,8 @@ TextLine::TextLine(TextBlock *blkA, int rotA, double baseA) { convertedLen = 0; hyphenated = gFalse; next = NULL; + xMin = yMin = 0; + xMax = yMax = -1; } TextLine::~TextLine() { @@ -1904,7 +1911,7 @@ void TextPage::addChar(GfxState *state, double x, double y, h1 /= uLen; } for (i = 0; i < uLen; ++i) { - curWord->addChar(state, x1 + i*w1, y1 + i*h1, w1, h1, u[i]); + curWord->addChar(state, x1 + i*w1, y1 + i*h1, w1, h1, c, u[i]); } ++curWord->charLen; ++charPos; @@ -2974,6 +2981,394 @@ GooString *TextPage::getText(double xMin, double yMin, return s; } +class TextSelectionVisitor { +public: + TextSelectionVisitor (TextPage *page); + virtual ~TextSelectionVisitor () { } + virtual void visitBlock (TextBlock *block, + TextLine *begin, + TextLine *end, + PDFRectangle *selection) = 0; + virtual void visitLine (TextLine *line, + TextWord *begin, + TextWord *end, + PDFRectangle *selection) = 0; + virtual void visitWord (TextWord *word, int begin, int end, + PDFRectangle *selection) = 0; + +protected: + TextPage *page; +}; + +TextSelectionVisitor::TextSelectionVisitor (TextPage *page) + : page(page) +{ +} + + + + +#if 0 +class TextSelectionDumper : public TextSelectionVisitor { +public: + virtual void visitBlock (TextBlock *block, + TextLine *begin, + TextLine *end, + PDFRectangle *selection) { }; + virtual void visitLine (TextLine *line, + TextWord *begin, + TextWord *end, + PDFRectangle *selection) { }; + virtual void visitWord (TextWord *word, int begin, int end, + PDFRectangle *selection); + +private: + GooString *result; + UnicodeMap *uMap; + char space[8], eol[16]; + int spaceLen, eolLen; + double height; +}; + +TextSelectionDumper::TextSelectionDumper() +{ + result = new GooString(); + uMap = globalParams->getTextEncoding(); + + // get the output encoding + if (data.uMap == NULL) + return data.result; + + data.spaceLen = data.uMap->mapUnicode(0x20, data.space, sizeof(data.space)); + data.eolLen = 0; // make gcc happy + switch (globalParams->getTextEOL()) { + case eolUnix: + data.eolLen = data.uMap->mapUnicode(0x0a, data.eol, sizeof(data.eol)); + break; + case eolDOS: + data.eolLen = data.uMap->mapUnicode(0x0d, data.eol, sizeof(data.eol)); + data.eolLen += data.uMap->mapUnicode(0x0a, data.eol + data.eolLen, + sizeof(data.eol) - data.eolLen); + break; + case eolMac: + data.eolLen = data.uMap->mapUnicode(0x0d, data.eol, sizeof(data.eol)); + break; + } +} + +TextSelectionDumper::~TextSelectionDumper() +{ + data.uMap->decRefCnt(); +} + +void TextSelectionDumper::visitWord(TextWord *word, int first, int last, + PDFRectangle *selection) +{ + for (i = first; i <= last; i++) + printf ("%c", word->text[i]); + printf ("\n"); +} + +#endif + +class TextSelectionSizer : public TextSelectionVisitor { +public: + TextSelectionSizer(TextPage *page, double scale); + ~TextSelectionSizer() { } + + virtual void visitBlock (TextBlock *block, + TextLine *begin, + TextLine *end, + PDFRectangle *selection) { }; + virtual void visitLine (TextLine *line, + TextWord *begin, + TextWord *end, + PDFRectangle *selection); + virtual void visitWord (TextWord *word, int begin, int end, + PDFRectangle *selection) { }; + + GooList *getRegion () { return list; } + +private: + GooList *list; + double scale; +}; + +TextSelectionSizer::TextSelectionSizer(TextPage *page, double scale) + : TextSelectionVisitor(page), + scale(scale) +{ + list = new GooList(); +} + +void TextSelectionSizer::visitLine (TextLine *line, + TextWord *begin, + TextWord *end, + PDFRectangle *selection) +{ + PDFRectangle *rect; + double x1, y1, x2, y2, margin; + int i; + + margin = (line->yMax - line->yMin) / 8; + x1 = floor (line->xMax * scale); + y1 = floor ((line->yMin - margin) * scale); + x2 = ceil (line->xMin * scale); + y2 = ceil ((line->yMax + margin) * scale); + + for (i = 0; i < line->len; i++) { + if (selection->x1 < line->edge[i + 1] && line->edge[i] < x1) + x1 = floor (line->edge[i]); + if (line->edge[i] < selection->x2) + x2 = ceil (line->edge[i + 1]); + } + + rect = new PDFRectangle (x1, y1, x2, y2); + list->append (rect); +} + + +class TextSelectionPainter : public TextSelectionVisitor { +public: + TextSelectionPainter(TextPage *page, + double scale, + OutputDev *out, + GfxColor &box_color, + GfxColor &glyph_color); + ~TextSelectionPainter(); + + virtual void visitBlock (TextBlock *block, + TextLine *begin, + TextLine *end, + PDFRectangle *selection) { }; + virtual void visitLine (TextLine *line, + TextWord *begin, + TextWord *end, + PDFRectangle *selection); + virtual void visitWord (TextWord *word, int begin, int end, + PDFRectangle *selection); + +private: + OutputDev *out; + GfxColor box_color, glyph_color; + GfxState *state; +}; + +TextSelectionPainter::TextSelectionPainter(TextPage *page, + double scale, + OutputDev *out, + GfxColor &box_color, + GfxColor &glyph_color) + : TextSelectionVisitor(page), + out(out), + box_color(box_color), + glyph_color(glyph_color) +{ + PDFRectangle box(0, 0, page->pageWidth, page->pageHeight); + + state = new GfxState(72 * scale, 72 * scale, &box, 0, gFalse); + + out->startPage (0, state); + + state->setTextMat(1, 0, 0, -1, 0, 0); + state->setFillColorSpace(new GfxDeviceRGBColorSpace()); + +} + +TextSelectionPainter::~TextSelectionPainter() +{ + out->endPage (); + + delete state; +} + +void TextSelectionPainter::visitLine (TextLine *line, + TextWord *begin, + TextWord *end, + PDFRectangle *selection) +{ + double x1, y1, x2, y2, margin; + int i; + + state->setFillColor(&box_color); + out->updateFillColor(state); + + margin = (line->yMax - line->yMin) / 8; + x1 = floor (line->xMax); + y1 = floor (line->yMin - margin); + x2 = ceil (line->xMin); + y2 = ceil (line->yMax + margin); + + for (i = 0; i < line->len; i++) { + if (selection->x1 < line->edge[i + 1] && line->edge[i] < x1) + x1 = floor (line->edge[i]); + if (line->edge[i] < selection->x2) + x2 = ceil (line->edge[i + 1]); + } + + state->moveTo(x1, y1); + state->lineTo(x2, y1); + state->lineTo(x2, y2); + state->lineTo(x1, y2); + state->closePath(); + + out->fill(state); + state->clearPath(); +} + +void TextSelectionPainter::visitWord (TextWord *word, int begin, int end, + PDFRectangle *selection) +{ + GooString *string; + int i; + + state->setFillColor(&glyph_color); + out->updateFillColor(state); + state->setFont(word->font->gfxFont, word->fontSize); + out->updateFont(state); + + /* The only purpose of this string is to let the output device query + * it's length. Might want to change this interface later. */ + + string = new GooString ((char *) word->charcode, end - begin); + + out->beginString(state, string); + + for (i = begin; i < end; i++) + out->drawChar(state, word->edge[i], word->base, 0, 0, 0, 0, + word->charcode[i], NULL, 0); + + out->endString(state); +} + +void TextWord::visitSelection(TextSelectionVisitor *visitor, + PDFRectangle *selection) { + int i, begin, end; + + begin = len + 1; + end = 0; + for (i = 0; i < len; i++) { + if (selection->x1 < edge[i + 1] && i < begin) + begin = i; + if (edge[i] < selection->x2) + end = i + 1; + } + + visitor->visitWord (this, begin, end, selection); +} + +void TextLine::visitSelection(TextSelectionVisitor *visitor, + PDFRectangle *selection) { + TextWord *p, *begin, *end; + + begin = NULL; + end = NULL; + for (p = words; p != NULL; p = p->next) { + if (selection->x1 < p->xMax && selection->y1 < p->yMax && begin == NULL) + begin = p; + if (selection->x2 > p->xMin && selection->y2 > p->yMin) + end = p->next; + } + + visitor->visitLine (this, begin, end, selection); + + for (p = begin; p != end; p = p->next) + p->visitSelection (visitor, selection); +} + +void TextBlock::visitSelection(TextSelectionVisitor *visitor, + PDFRectangle *selection) { + TextLine *p, *begin, *end; + PDFRectangle child_selection; + + begin = NULL; + end = NULL; + for (p = lines; p != NULL; p = p->next) { + if (selection->x1 < p->xMax && selection->y1 < p->yMax && begin == NULL) + begin = p; + if (selection->x2 > p->xMin && selection->y2 > p->yMin) + end = p->next; + } + + visitor->visitBlock (this, begin, end, selection); + + for (p = begin; p != end; p = p->next) { + if (p == begin) { + child_selection.x1 = selection->x1; + child_selection.y1 = selection->y1; + } else { + child_selection.x1 = 0; + child_selection.y1 = 0; + } + if (p->next == end) { + child_selection.x2 = selection->x2; + child_selection.y2 = selection->y2; + } else { + child_selection.x2 = page->pageWidth; + child_selection.y2 = page->pageHeight; + } + + p->visitSelection(visitor, &child_selection); + } +} + +void TextPage::visitSelection(TextSelectionVisitor *visitor, + PDFRectangle *selection) +{ + int i, begin, end; + PDFRectangle child_selection; + + begin = nBlocks; + end = 0; + for (i = 0; i < nBlocks; i++) { + if (selection->x1 < blocks[i]->xMax && + selection->y1 < blocks[i]->yMax && i < begin) + begin = i; + if (selection->x2 > blocks[i]->xMin && selection->y2 > blocks[i]->yMin) + end = i + 1; + } + + for (i = begin; i < end; i++) { + if (i == begin) { + child_selection.x1 = selection->x1; + child_selection.y1 = selection->y1; + } else { + child_selection.x1 = 0; + child_selection.y1 = 0; + } + if (i + 1 == end) { + child_selection.x2 = selection->x2; + child_selection.y2 = selection->y2; + } else { + child_selection.x2 = pageWidth; + child_selection.y2 = pageHeight; + } + + blocks[i]->visitSelection(visitor, &child_selection); + } +} + +void TextPage::drawSelection(OutputDev *out, + double scale, + PDFRectangle *selection) +{ + GfxColor box_color = { 0x7c / 255.0, 0x99 / 255.0, 0xad / 255.0 }; + GfxColor glyph_color = { 1.0, 1.0, 1.0 }; + TextSelectionPainter painter(this, scale, out, box_color, glyph_color); + + visitSelection(&painter, selection); +} + +GooList *TextPage::getSelectionRegion(PDFRectangle *selection, + double scale) { + TextSelectionSizer sizer(this, scale); + GooList *region; + + visitSelection(&sizer, selection); + + return sizer.getRegion(); +} + GBool TextPage::findCharRange(int pos, int length, double *xMin, double *yMin, double *xMax, double *yMax) { @@ -3516,6 +3911,17 @@ GooString *TextOutputDev::getText(double xMin, double yMin, return text->getText(xMin, yMin, xMax, yMax); } +void TextOutputDev::drawSelection(OutputDev *out, + double scale, + PDFRectangle *selection) { + text->drawSelection(out, scale, selection); +} + +GooList *TextOutputDev::getSelectionRegion(PDFRectangle *selection, + double scale) { + return text->getSelectionRegion(selection, scale); +} + GBool TextOutputDev::findCharRange(int pos, int length, double *xMin, double *yMin, double *xMax, double *yMax) { diff --git a/poppler/TextOutputDev.h b/poppler/TextOutputDev.h index a77c6dab..3b3d9696 100644 --- a/poppler/TextOutputDev.h +++ b/poppler/TextOutputDev.h @@ -21,12 +21,14 @@ class GooString; class GooList; +class Gfx; class GfxFont; class GfxState; class UnicodeMap; class TextBlock; class TextPage; class TextLineFrag; +class TextSelectionVisitor; //------------------------------------------------------------------------ @@ -53,6 +55,7 @@ private: friend class TextWord; friend class TextPage; + friend class TextSelectionPainter; }; //------------------------------------------------------------------------ @@ -71,7 +74,7 @@ public: // Add a character to the word. void addChar(GfxState *state, double x, double y, - double dx, double dy, Unicode u); + double dx, double dy, CharCode c, Unicode u); // Merge <word> onto the end of <this>. void merge(TextWord *word); @@ -86,6 +89,9 @@ public: static int cmpYX(const void *p1, const void *p2); + void visitSelection(TextSelectionVisitor *visitor, + PDFRectangle *selection); + #if TEXTOUT_WORD_LIST int getLength() { return len; } Unicode getChar(int idx) { return text[idx]; } @@ -107,6 +113,7 @@ private: double yMin, yMax; // bounding box y coordinates double base; // baseline x or y coordinate Unicode *text; // the text + CharCode *charcode; // glyph indices double *edge; // "near" edge x or y coord of each char // (plus one extra entry for the last char) int len; // length of text and edge arrays @@ -132,6 +139,7 @@ private: friend class TextFlow; friend class TextWordList; friend class TextPage; + friend class TextSelectionPainter; }; //------------------------------------------------------------------------ @@ -164,6 +172,8 @@ private: friend class TextPage; }; +struct TextFlowData; + //------------------------------------------------------------------------ // TextLine //------------------------------------------------------------------------ @@ -195,6 +205,9 @@ public: void coalesce(UnicodeMap *uMap); + void visitSelection(TextSelectionVisitor *visitor, + PDFRectangle *selection); + private: TextBlock *blk; // parent block @@ -219,6 +232,9 @@ private: friend class TextFlow; friend class TextWordList; friend class TextPage; + + friend class TextSelectionPainter; + friend class TextSelectionSizer; }; //------------------------------------------------------------------------ @@ -250,6 +266,9 @@ public: // primary rotation. GBool isBelow(TextBlock *blk); + void visitSelection(TextSelectionVisitor *visitor, + PDFRectangle *selection); + private: TextPage *page; // the parent page @@ -275,6 +294,7 @@ private: friend class TextFlow; friend class TextWordList; friend class TextPage; + friend class TextSelectionPainter; }; //------------------------------------------------------------------------ @@ -394,7 +414,16 @@ public: // Get the text which is inside the specified rectangle. GooString *getText(double xMin, double yMin, - double xMax, double yMax); + double xMax, double yMax); + + void visitSelection(TextSelectionVisitor *visitor, + PDFRectangle *selection); + + void drawSelection(OutputDev *out, + double scale, + PDFRectangle *selection); + + GooList *getSelectionRegion(PDFRectangle *selection, double scale); // Find a string by character position and length. If found, sets // the text bounding rectangle and returns true; otherwise returns @@ -457,6 +486,7 @@ private: friend class TextBlock; friend class TextFlow; friend class TextWordList; + friend class TextSelectionPainter; }; //------------------------------------------------------------------------ @@ -548,6 +578,10 @@ public: double *xMin, double *yMin, double *xMax, double *yMax); + void drawSelection(OutputDev *out, double scale, PDFRectangle *selection); + + GooList *getSelectionRegion(PDFRectangle *selection, double scale); + #if TEXTOUT_WORD_LIST // Build a flat word list, in content stream order (if // this->rawOrder is true), physical layout order (if |