summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog20
-rw-r--r--configure.ac4
-rw-r--r--glib/poppler-page.cc224
-rw-r--r--glib/poppler-page.h65
-rw-r--r--glib/poppler-private.h3
-rw-r--r--poppler/CairoOutputDev.cc13
-rw-r--r--poppler/CairoOutputDev.h2
-rw-r--r--poppler/Page.cc59
-rw-r--r--poppler/Page.h12
-rw-r--r--poppler/TextOutputDev.cc414
-rw-r--r--poppler/TextOutputDev.h38
11 files changed, 743 insertions, 111 deletions
diff --git a/ChangeLog b/ChangeLog
index c067da0a..69e81ac5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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