[poppler] CMakeLists.txt glib/CMakeLists.txt glib/poppler-page.cc glib/poppler-page.h glib/reference glib/tests poppler/BBoxOutputDev.cc poppler/BBoxOutputDev.h

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Wed Apr 1 21:54:00 UTC 2020


 CMakeLists.txt                      |    2 
 glib/CMakeLists.txt                 |    1 
 glib/poppler-page.cc                |   48 ++++++
 glib/poppler-page.h                 |    3 
 glib/reference/poppler-sections.txt |    1 
 glib/tests/CMakeLists.txt           |   43 ++++++
 glib/tests/check_bb.c               |   92 +++++++++++++
 glib/tests/check_text.c             |   55 +++++++
 glib/tests/pdfdrawbb.c              |  143 ++++++++++++++++++++
 poppler/BBoxOutputDev.cc            |  251 ++++++++++++++++++++++++++++++++++++
 poppler/BBoxOutputDev.h             |   73 ++++++++++
 11 files changed, 712 insertions(+)

New commits:
commit 967a21b5c2bedd0c0debb74ae622edbc1a5b486a
Author: sgerwk <sgerwk at aol.com>
Date:   Wed Apr 1 21:53:58 2020 +0000

    bounding box of graphics in the page

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 28d46c4d..dbe6a089 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -404,6 +404,7 @@ set(poppler_SRCS
   poppler/Movie.cc
   poppler/Rendition.cc
   poppler/CertificateInfo.cc
+  poppler/BBoxOutputDev.cc
 )
 set(poppler_LIBS ${FREETYPE_LIBRARIES})
 if(ENABLE_SPLASH)
@@ -614,6 +615,7 @@ if(ENABLE_UNSTABLE_API_ABI_HEADERS)
     poppler/SecurityHandler.h
     poppler/StdinCachedFile.h
     poppler/StdinPDFDocBuilder.h
+    poppler/BBoxOutputDev.h
     poppler/UTF.h
     poppler/Sound.h
     ${CMAKE_CURRENT_BINARY_DIR}/poppler/poppler-config.h
diff --git a/glib/CMakeLists.txt b/glib/CMakeLists.txt
index 2781f091..c5aaea6f 100644
--- a/glib/CMakeLists.txt
+++ b/glib/CMakeLists.txt
@@ -20,6 +20,7 @@ configure_file(poppler-features.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/poppler-feat
 
 if (GTK_FOUND AND BUILD_GTK_TESTS)
   add_subdirectory(demo)
+  add_subdirectory(tests)
 endif ()
 
 set(poppler_glib_public_headers
diff --git a/glib/poppler-page.cc b/glib/poppler-page.cc
index 322f2d48..7ae9463e 100644
--- a/glib/poppler-page.cc
+++ b/glib/poppler-page.cc
@@ -27,6 +27,7 @@
 #include <UnicodeMap.h>
 #include <GfxState.h>
 #include <PageTransition.h>
+#include <BBoxOutputDev.h>
 #endif
 
 #include "poppler.h"
@@ -2187,6 +2188,53 @@ poppler_page_get_crop_box (PopplerPage *page, PopplerRectangle *rect)
   rect->y2 = cropBox->y2;
 }
 
+/*
+ * poppler_page_get_bounding_box:
+ * @page: A #PopplerPage
+ * @rect: (out) return the bounding box of the page
+ *
+ * Returns the bounding box of the page, a rectangle enclosing all text, vector
+ * graphics (lines, rectangles and curves) and raster images in the page.
+ * Includes invisible text but not (yet) annotations like highlights and form
+ * elements.
+ *
+ * Return value: %TRUE if the page contains graphics, %FALSE otherwise
+ *
+ * Since: 0.88
+ */
+gboolean
+poppler_page_get_bounding_box (PopplerPage *page,
+                               PopplerRectangle *rect) {
+  Gfx *gfx;
+  BBoxOutputDev *bb_out;
+  bool hasGraphics;
+
+  g_return_val_if_fail(POPPLER_IS_PAGE (page), false);
+  g_return_val_if_fail(rect != nullptr, false);
+
+  bb_out = new BBoxOutputDev(page->page->getCropBox());
+
+  gfx = page->page->createGfx(bb_out,
+                              72.0, 72.0, 0,
+                              false, /* useMediaBox */
+                              true, /* Crop */
+                              -1, -1, -1, -1,
+                              false, /* printing */
+                              nullptr, nullptr);
+  page->page->display(gfx);
+  hasGraphics = bb_out->getHasGraphics();
+  if (hasGraphics) {
+    rect->x1 = bb_out->getX1();
+    rect->y1 = bb_out->getY1();
+    rect->x2 = bb_out->getX2();
+    rect->y2 = bb_out->getY2();
+  }
+
+  delete gfx;
+  delete bb_out;
+  return hasGraphics;
+}
+
 /**
  * poppler_page_get_text_layout:
  * @page: A #PopplerPage
diff --git a/glib/poppler-page.h b/glib/poppler-page.h
index f99f0920..0ffc50c6 100644
--- a/glib/poppler-page.h
+++ b/glib/poppler-page.h
@@ -132,6 +132,9 @@ POPPLER_PUBLIC
 void 		      poppler_page_get_crop_box 	 (PopplerPage        *page,
 							  PopplerRectangle   *rect);
 POPPLER_PUBLIC
+gboolean                   poppler_page_get_bounding_box (PopplerPage           *page,
+                                                          PopplerRectangle      *rect);
+POPPLER_PUBLIC
 gboolean               poppler_page_get_text_layout      (PopplerPage        *page,
                                                           PopplerRectangle  **rectangles,
                                                           guint              *n_rectangles);
diff --git a/glib/reference/poppler-sections.txt b/glib/reference/poppler-sections.txt
index 75bf3b09..e13b3c1c 100644
--- a/glib/reference/poppler-sections.txt
+++ b/glib/reference/poppler-sections.txt
@@ -53,6 +53,7 @@ poppler_page_free_image_mapping
 poppler_page_free_link_mapping
 poppler_page_free_text_attributes
 poppler_page_get_annot_mapping
+poppler_page_get_bounding_box
 poppler_page_get_crop_box
 poppler_page_get_duration
 poppler_page_get_form_field_mapping
diff --git a/glib/tests/CMakeLists.txt b/glib/tests/CMakeLists.txt
new file mode 100644
index 00000000..ff776918
--- /dev/null
+++ b/glib/tests/CMakeLists.txt
@@ -0,0 +1,43 @@
+include_directories(
+  ${GTK3_INCLUDE_DIRS}
+)
+
+macro(POPPLER_ADD_TESTCASE exe arg1)
+  add_test(${exe}-${arg1} ${EXE} ${EXECUTABLE_OUTPUT_PATH}/poppler-check-bb ${TESTDATADIR}/unittestcases/${arg1} ${ARGN})
+endmacro(POPPLER_ADD_TESTCASE)
+
+add_definitions(${GTK3_CFLAGS_OTHER})
+add_definitions(-DTESTDATADIR=\"${TESTDATADIR}\")
+
+set(poppler_check_text_SRCS
+  check_text.c
+)
+poppler_add_unittest(poppler-check-text BUILD_GTK_TESTS ${poppler_check_text_SRCS})
+target_link_libraries(poppler-check-text poppler-glib ${GTK3_LIBRARIES})
+
+set(poppler_check_bb_SRCS
+  check_bb.c
+)
+poppler_add_test(poppler-check-bb BUILD_GTK_TESTS ${poppler_check_bb_SRCS})
+target_link_libraries(poppler-check-bb poppler-glib ${GTK3_LIBRARIES})
+poppler_add_testcase(poppler-check-bb shapes+attachments.pdf 42.5 42.5 557.5 557.5)
+poppler_add_testcase(poppler-check-bb orientation.pdf 34 34 83.74 49 793 34 808 97.19 488.02 793 561 808 34 503.61 49 56)
+poppler_add_testcase(poppler-check-bb xr01.pdf 148.71 127.85 308.11 704.57)
+poppler_add_testcase(poppler-check-bb xr02.pdf 133.77 124.81 308.11 704.57 133.77 124.80 308.11 704.57)
+poppler_add_testcase(poppler-check-bb russian.pdf 71.5 76.81 197.69 131.09)
+poppler_add_testcase(poppler-check-bb vis_policy_test.pdf 90 77.93 312.01 265.13)
+poppler_add_testcase(poppler-check-bb searchAcrossLines.pdf 107.15 105.23 523.85 691 85.04 94 538.59 762.19)
+poppler_add_testcase(poppler-check-bb deseret.pdf 56.8 57.15 109.5 72.8)
+poppler_add_testcase(poppler-check-bb fieldWithUtf16Names.pdf 56.65 56.65 264.55 83.05)
+poppler_add_testcase(poppler-check-bb bug7063.pdf 56.8 57.46 244.29 118.79)
+poppler_add_testcase(poppler-check-bb WithActualText.pdf 100 90.72 331.01 102.35)
+poppler_add_testcase(poppler-check-bb Issue637.pdf 70.87 53 293 105.37)
+poppler_add_testcase(poppler-check-bb truetype.pdf 17.5 17.5 577.5 225.62)
+poppler_add_testcase(poppler-check-bb form_set_icon.pdf -0.5 -0.5 363.34 272.63)
+poppler_add_testcase(poppler-check-bb imageretrieve+attachment.pdf 0 0 610.56 792)
+poppler_add_testcase(poppler-check-bb checkbox_issue_159.pdf 2.84 14.17 553.18 840.87)
+poppler_add_testcase(poppler-check-bb NestedLayers.pdf -1 191 613 793)
+poppler_add_testcase(poppler-check-bb A6EmbeddedFiles.pdf 17.88 17.88 558.36 755.73)
+
+add_executable(pdfdrawbb pdfdrawbb.c)
+target_link_libraries(pdfdrawbb poppler-glib)
diff --git a/glib/tests/check_bb.c b/glib/tests/check_bb.c
new file mode 100644
index 00000000..3170299a
--- /dev/null
+++ b/glib/tests/check_bb.c
@@ -0,0 +1,92 @@
+/*
+ * testing program for the boundingbox function
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+#include <poppler.h>
+
+/*
+ * compare floating-point coordinates
+ */
+int equal(double a, double b) {
+	return fabs(a - b) < 0.01;
+}
+
+/*
+ * main
+ */
+int main(int argc, char *argv[]) {
+	GFile *infile;
+	PopplerDocument *doc;
+	PopplerPage *page;
+	int npages, n;
+	gboolean hg;
+	PopplerRectangle bb, correct;
+	GError *err = NULL;
+	int argx;
+
+				/* open file */
+
+	g_print("file: %s\n", argv[1]);
+	infile = g_file_new_for_path(argv[1]);
+	if (! infile)
+		exit(EXIT_FAILURE);
+
+	doc = poppler_document_new_from_gfile(infile, NULL, NULL, &err);
+	if (doc == NULL) {
+		g_printerr("error opening pdf file: %s\n", err->message);
+		g_error_free(err);
+		exit(EXIT_FAILURE);
+	}
+
+				/* pages */
+
+	npages = poppler_document_get_n_pages(doc);
+	if (npages < 1) {
+		g_printerr("no page in document\n");
+		exit(EXIT_FAILURE);
+	}
+
+				/* check the bounding box */
+
+	argx = 2;
+	for (n = 0; n < poppler_document_get_n_pages(doc); n++) {
+		g_print("    page: %d\n", n + 1);
+
+		page = poppler_document_get_page(doc, n);
+		hg = poppler_page_get_bounding_box(page, &bb);
+		if (! hg) {
+			g_printerr("no graphics in page\n");
+			exit(EXIT_FAILURE);
+		}
+		g_print("        bounding box: %g,%g - %g,%g\n",
+			bb.x1, bb.y1, bb.x2, bb.y2);
+
+		if (argc - argx < 4) {
+			g_print("not enough arguments\n");
+			exit(EXIT_FAILURE);
+		}
+		correct.x1 = atof(argv[argx++]);
+		correct.y1 = atof(argv[argx++]);
+		correct.x2 = atof(argv[argx++]);
+		correct.y2 = atof(argv[argx++]);
+		g_print("        correct:      %g,%g - %g,%g\n",
+			correct.x1, correct.y1, correct.x2, correct.y2);
+		if (! equal(bb.x1, correct.x1) ||
+		    ! equal(bb.x2, correct.x2) ||
+		    ! equal(bb.y1, correct.y1) ||
+		    ! equal(bb.x2, correct.x2)) {
+			g_print("bounding box differs from expected\n");
+			exit(EXIT_FAILURE);
+		}
+
+		g_object_unref(page);
+	}
+
+	return EXIT_SUCCESS;
+}
+
diff --git a/glib/tests/check_text.c b/glib/tests/check_text.c
new file mode 100644
index 00000000..cf5f2463
--- /dev/null
+++ b/glib/tests/check_text.c
@@ -0,0 +1,55 @@
+/*
+ * testing program for the get_text function
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <poppler.h>
+
+/*
+ * main
+ */
+int main(int argc, char *argv[]) {
+	GFile *infile;
+	PopplerDocument *doc;
+	PopplerPage *page;
+	int npages, n;
+	char *text;
+	GError *err = NULL;
+
+				/* open file */
+
+	infile = g_file_new_for_path
+		(TESTDATADIR "/unittestcases/WithActualText.pdf");
+	if (! infile)
+		exit(EXIT_FAILURE);
+
+	doc = poppler_document_new_from_gfile(infile, NULL, NULL, &err);
+	if (doc == NULL) {
+		g_printerr("error opening pdf file: %s\n", err->message);
+		g_error_free(err);
+		exit(EXIT_FAILURE);
+	}
+
+				/* pages */
+
+	npages = poppler_document_get_n_pages(doc);
+	if (npages < 1) {
+		g_printerr("no page in document\n");
+		exit(EXIT_FAILURE);
+	}
+
+				/* check text */
+
+	n = 0;
+	page = poppler_document_get_page(doc, n);
+	text = poppler_page_get_text(page);
+	g_print("%s\n", text);
+	g_assert_cmpstr(text, ==, "The slow brown fox jumps over the black dog.");
+	g_object_unref(page);
+
+	return EXIT_SUCCESS;
+}
+
diff --git a/glib/tests/pdfdrawbb.c b/glib/tests/pdfdrawbb.c
new file mode 100644
index 00000000..c136f861
--- /dev/null
+++ b/glib/tests/pdfdrawbb.c
@@ -0,0 +1,143 @@
+/*
+ * pdfdrawbb.c
+ *
+ * draw the bounding box of each page
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <poppler.h>
+#include <cairo.h>
+#include <cairo-pdf.h>
+
+/*
+ * add suffix to a pdf filename
+ */
+char *pdfaddsuffix(char *infile, char *suffix) {
+	char *basename;
+	char *outfile;
+	char *pos;
+
+	basename = g_path_get_basename(infile);
+
+	outfile = malloc(strlen(infile) + strlen(suffix) + 10);
+	strcpy(outfile, basename);
+	g_free(basename);
+
+	pos = strrchr(outfile, '.');
+	if (pos != NULL && (! strcmp(pos, ".pdf") || ! strcmp(pos, ".PDF")))
+		*pos = '\0';
+
+	strcat(outfile, "-");
+	strcat(outfile, suffix);
+	strcat(outfile, ".pdf");
+	return outfile;
+}
+
+/*
+ * main
+ */
+int main(int argc, char *argv[]) {
+	int opt;
+	gboolean usage = FALSE;
+	char *infilename, *outfilename;
+
+	GError *err = NULL;
+	GFile *infile;
+	PopplerDocument *doc;
+	PopplerPage *page;
+	int npages, n;
+	PopplerRectangle bb;
+	gboolean hg;
+
+	gdouble width, height;
+	cairo_surface_t *surface;
+	cairo_t *cr;
+
+				/* arguments */
+
+	while ((opt = getopt(argc, argv, "h")) != -1)
+		switch(opt) {
+		case 'h':
+			usage = TRUE;
+			break;
+		}
+
+	if (! usage && argc - 1 < optind) {
+		g_print("input file name missing\n");
+		usage = TRUE;
+	}
+	if (usage) {
+		g_print("usage:\n");
+		g_print("\tpdfdrawbb");
+		g_print("[-h] file.pdf\n");
+		g_print("\t\t-h\t\tthis help\n");
+		exit(EXIT_FAILURE);
+	}
+	infilename = argv[optind];
+	if (! infilename)
+		exit(EXIT_FAILURE);
+	outfilename = pdfaddsuffix(argv[optind], "bb");
+
+				/* open file */
+
+	infile = g_file_new_for_path(infilename);
+	if (infile == NULL)
+		exit(EXIT_FAILURE);
+
+	doc = poppler_document_new_from_gfile(infile, NULL, NULL, &err);
+	if (doc == NULL) {
+		g_printerr("error opening pdf file: %s\n", err->message);
+		g_error_free(err);
+		exit(EXIT_FAILURE);
+	}
+
+				/* pages */
+
+	npages = poppler_document_get_n_pages(doc);
+	if (npages < 1) {
+		g_print("no page in document\n");
+		exit(EXIT_FAILURE);
+	}
+
+				/* copy to destination */
+
+	surface = cairo_pdf_surface_create(outfilename, width, height);
+
+	g_print("infile: %s\n", infilename);
+	g_print("outfile: %s\n", outfilename);
+
+	for (n = 0; n < npages; n++) {
+		g_print("page %d:\n", n);
+		page = poppler_document_get_page(doc, n);
+		poppler_page_get_size(page, &width, &height);
+		cairo_pdf_surface_set_size(surface, width, height);
+
+		hg = poppler_page_get_bounding_box(page, &bb);
+		if (hg)
+			g_print("bounding box %g,%g - %g,%g",
+				bb.x1, bb.y1, bb.x2, bb.y2);
+		g_print("\n");
+
+		cr = cairo_create(surface);
+		poppler_page_render_for_printing(page, cr);
+		if (hg) {
+			cairo_set_source_rgb(cr, 0.6, 0.6, 1.0);
+			cairo_rectangle(cr,
+				bb.x1, bb.y1, bb.x2 - bb.x1, bb.y2 - bb.y1);
+			cairo_stroke(cr);
+		}
+		cairo_destroy(cr);
+		cairo_surface_show_page(surface);
+
+		g_object_unref(page);
+	}
+
+	cairo_surface_destroy(surface);
+
+	return EXIT_SUCCESS;
+}
+
diff --git a/poppler/BBoxOutputDev.cc b/poppler/BBoxOutputDev.cc
new file mode 100644
index 00000000..e992d47b
--- /dev/null
+++ b/poppler/BBoxOutputDev.cc
@@ -0,0 +1,251 @@
+/*
+ * boundingbox output device
+ */
+
+#include <cmath>
+#include <BBoxOutputDev.h>
+#include <GfxFont.h>
+
+#define writingModeHorizontal 0
+#define writingModeVertical 1
+
+BBoxOutputDev::BBoxOutputDev(const PDFRectangle *cropA) :
+		BBoxOutputDev(cropA, true, true, true) {
+}
+
+BBoxOutputDev::BBoxOutputDev(const PDFRectangle *cropA,
+		bool textA, bool vectorA, bool rasterA) :
+		BBoxOutputDev(cropA, textA, vectorA, rasterA, true) {
+}
+
+BBoxOutputDev::BBoxOutputDev(const PDFRectangle *cropA,
+		bool textA, bool vectorA, bool rasterA, bool lwidthA) {
+	hasGraphics = false;
+	crop = *cropA;
+	text = textA;
+	vector = vectorA;
+	raster = rasterA;
+	lwidth = lwidthA;
+}
+
+double BBoxOutputDev::getX1() const {
+	return bb.x1;
+}
+
+double BBoxOutputDev::getY1() const {
+	return bb.y1;
+}
+
+double BBoxOutputDev::getX2() const {
+	return bb.x2;
+}
+
+double BBoxOutputDev::getY2() const {
+	return bb.y2;
+}
+
+double BBoxOutputDev::getHasGraphics() const {
+	return hasGraphics;
+}
+
+void BBoxOutputDev::endPage() {
+	bb.clipTo(&crop);
+}
+
+void BBoxOutputDev::stroke(GfxState *state) {
+	updatePath(&bb, state->getPath(), state);
+}
+
+void BBoxOutputDev::fill(GfxState *state) {
+	updatePath(&bb, state->getPath(), state);
+}
+
+void BBoxOutputDev::eoFill(GfxState *state) {
+	updatePath(&bb, state->getPath(), state);
+}
+
+void BBoxOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
+		int width, int height, bool invert,
+		bool interpolate,
+		bool inlineImg) {
+	updateImage(&bb, state);
+}
+
+void BBoxOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
+		int width, int height,
+		GfxImageColorMap *colorMap, bool interpolate,
+		const int *maskColors, bool inlineImg) {
+	updateImage(&bb, state);
+}
+
+void BBoxOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
+		int width, int height,
+		GfxImageColorMap *colorMap, bool interpolate,
+		Stream *maskStr, int maskWidth, int maskHeight,
+		bool maskInvert, bool maskInterpolate) {
+	updateImage(&bb, state);
+}
+
+void BBoxOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
+		Stream *str,
+		int width, int height,
+		GfxImageColorMap *colorMap,
+		bool interpolate,
+		Stream *maskStr,
+		int maskWidth, int maskHeight,
+		GfxImageColorMap *maskColorMap,
+		bool maskInterpolate) {
+	updateImage(&bb, state);
+}
+
+void BBoxOutputDev::drawChar(GfxState *state,
+			double x, double y,
+			double dx, double dy,
+			double originX, double originY,
+			CharCode code, int nBytes,
+			const Unicode *u, int uLen) {
+	GfxFont *font;
+	double leftent, rightent, ascent, descent;
+	const double *fm, *fb;
+	Matrix fmat;
+	double fontSize;
+	double fx, fy, nx, ny;
+
+	if (! text)
+		return;
+
+	font = state->getFont();
+	if (! font)
+		return;
+
+	if (code == (CharCode) 0x20)
+		return;
+
+	fontSize = state->getFontSize();
+
+	if (font->getType() != fontType3)
+		fmat.init(1, 0, 0, 1, 0, 0);
+	else {
+		fm = font->getFontMatrix();
+		fmat.init(fm[0], fm[1], fm[2], fm[3], fm[4], fm[5]);
+	}
+
+	fb = font->getFontBBox();
+	if (font->getWMode() == writingModeHorizontal) {
+		leftent = 0;
+		rightent = 0;
+		ascent = font->getAscent();
+		descent = font->getDescent();
+	}
+	else {
+		if (fb[0] == 0 && fb[1] == 0 && fb[2] == 0 && fb[3] == 0) {
+			leftent = -0.5;
+			rightent = 0.5;
+		}
+		else {
+			leftent = fb[1];
+			rightent = fb[3];
+		}
+		ascent = 0;
+		descent = 0;
+	}
+	if (font->getType() == fontType3) {
+		ascent *= 1000;
+		descent *= 1000;
+	}
+
+	fmat.transform(leftent, descent, &fx, &fy);
+	state->textTransformDelta(fx, fy, &nx, &ny);
+	updatePoint(&bb, nx + x, ny + y, state);
+
+	fmat.transform(rightent, ascent, &fx, &fy);
+	state->textTransformDelta(fx, fy, &nx, &ny);
+	updatePoint(&bb, nx + x, ny + y, state);
+
+	fmat.transform(leftent * fontSize, descent * fontSize, &fx, &fy);
+	state->textTransformDelta(fx, fy, &nx, &ny);
+	updatePoint(&bb, nx + x + dx, ny + y + dy, state);
+
+	fmat.transform(rightent * fontSize, ascent * fontSize, &fx, &fy);
+	state->textTransformDelta(fx, fy, &nx, &ny);
+	updatePoint(&bb, nx + x + dx, ny + y + dy, state);
+}
+
+void BBoxOutputDev::clip(GfxState *state) {
+	updateClip(state);
+}
+
+void BBoxOutputDev::eoClip(GfxState *state) {
+	updateClip(state);
+}
+
+void BBoxOutputDev::clipToStrokePath(GfxState *state) {
+	updateClip(state);
+}
+
+/* update the crop box with a new path */
+void BBoxOutputDev::updateClip(const GfxState *state) {
+	PDFRectangle box;
+	bool hg;
+	hg = hasGraphics;
+	hasGraphics = true;
+	updatePath(&box, state->getPath(), state);
+	hasGraphics = hg;
+	crop.clipTo(&box);
+}
+
+/* update the bounding box with a new point */
+void BBoxOutputDev::updatePoint(PDFRectangle *bbA,
+		double x, double y, const GfxState *state) {
+	Matrix o = {1, 0, 0, 1, 0, 0};
+	double tx, ty, fx, fy;
+
+	o.scale(1, -1);
+	o.translate(0, -state->getPageHeight());
+
+	state->transform(x, y, &tx, &ty);
+	o.transform(tx, ty, &fx, &fy);
+
+	if (! hasGraphics || bbA->x1 > fx)
+		bbA->x1 = fx;
+	if (! hasGraphics || bbA->y1 > fy)
+		bbA->y1 = fy;
+	if (! hasGraphics || bbA->x2 < fx)
+		bbA->x2 = fx;
+	if (! hasGraphics || bbA->y2 < fy)
+		bbA->y2 = fy;
+	hasGraphics = true;
+}
+
+/* update the bounding box with a new path */
+void BBoxOutputDev::updatePath(PDFRectangle *bbA,
+		const GfxPath *path, const GfxState *state) {
+	int i, j;
+	const GfxSubpath *subpath;
+	double w;
+	if (! vector)
+		return;
+	w = lwidth ? state->getLineWidth() : 0;
+	for (i = 0; i < path->getNumSubpaths(); i++) {
+		subpath = path->getSubpath(i);
+		for (j = 0; j < subpath->getNumPoints(); j++) {
+			updatePoint(bbA,
+				subpath->getX(j) - w / 2,
+				subpath->getY(j) - w / 2,
+				state);
+			updatePoint(bbA,
+				subpath->getX(j) + w / 2,
+				subpath->getY(j) + w / 2,
+				state);
+		}
+	}
+}
+
+/* update the bounding box with a new image */
+void BBoxOutputDev::updateImage(PDFRectangle *bbA, const GfxState *state) {
+	if (! raster)
+		return;
+	updatePoint(bbA, 0, 1, state);
+	updatePoint(bbA, 1, 0, state);
+}
+
diff --git a/poppler/BBoxOutputDev.h b/poppler/BBoxOutputDev.h
new file mode 100644
index 00000000..7b28a94e
--- /dev/null
+++ b/poppler/BBoxOutputDev.h
@@ -0,0 +1,73 @@
+#include <PDFDoc.h>
+#include <GfxState.h>
+#include <OutputDev.h>
+
+class BBoxOutputDev: public OutputDev {
+public:
+	bool upsideDown() override { return false; }
+	bool useDrawChar() override { return true; }
+	bool interpretType3Chars() override { return false; }
+
+	BBoxOutputDev(const PDFRectangle *cropA);
+	BBoxOutputDev(const PDFRectangle *cropA,
+		bool text, bool vector, bool raster);
+	BBoxOutputDev(const PDFRectangle *cropA,
+		bool text, bool vector, bool raster, bool lwidth);
+	void endPage() override;
+	void stroke(GfxState *state) override;
+	void fill(GfxState *state) override;
+	void eoFill(GfxState *state) override;
+	void drawChar(GfxState *state, double x, double y,
+		double dx, double dy,
+		double originX, double originY,
+		CharCode code, int nBytes,
+		const Unicode *u, int uLen) override;
+	void drawImageMask(GfxState *state, Object *ref, Stream *str,
+		int width, int height, bool invert,
+		bool interpolate,
+		bool inlineImg) override;
+	void drawImage(GfxState *state, Object *ref, Stream *str,
+		int width, int height, GfxImageColorMap *colorMap,
+		bool interpolate, const int *maskColors,
+		bool inlineImg) override;
+	void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
+		int width, int height,
+		GfxImageColorMap *colorMap, bool interpolate,
+		Stream *maskStr, int maskWidth, int maskHeight,
+		bool maskInvert, bool maskInterpolate) override;
+	void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
+		int width, int height,
+		GfxImageColorMap *colorMap,
+		bool interpolate,
+		Stream *maskStr,
+		int maskWidth, int maskHeight,
+		GfxImageColorMap *maskColorMap,
+		bool maskInterpolate) override;
+	void clip(GfxState *state) override;
+	void eoClip(GfxState *state) override;
+	void clipToStrokePath(GfxState *state) override;
+
+	double getX1() const;
+	double getY1() const;
+	double getX2() const;
+	double getY2() const;
+	double getHasGraphics() const;
+
+private:
+	PDFRectangle bb;
+	PDFRectangle crop;
+	bool hasGraphics;
+
+	bool text;
+	bool vector;
+	bool raster;
+	bool lwidth;
+
+	void updatePoint(PDFRectangle *bbA,
+		double x, double y, const GfxState *state);
+	void updatePath(PDFRectangle *bbA,
+		const GfxPath *path, const GfxState *state);
+	void updateImage(PDFRectangle *bbA, const GfxState *state);
+	void updateClip(const GfxState *state);
+};
+


More information about the poppler mailing list