Spaces:
Running
Running
/* poppler-page.cc: glib wrapper for poppler | |
* Copyright (C) 2005, Red Hat, Inc. | |
* | |
* This program is free software; you can redistribute it and/or modify | |
* it under the terms of the GNU General Public License as published by | |
* the Free Software Foundation; either version 2, or (at your option) | |
* any later version. | |
* | |
* This program is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
* GNU General Public License for more details. | |
* | |
* You should have received a copy of the GNU General Public License | |
* along with this program; if not, write to the Free Software | |
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. | |
*/ | |
static void _page_unrotate_xy(Page *page, double *x, double *y); | |
/** | |
* SECTION:poppler-page | |
* @short_description: Information about a page in a document | |
* @title: PopplerPage | |
*/ | |
enum | |
{ | |
PROP_0, | |
PROP_LABEL | |
}; | |
static PopplerRectangleExtended *poppler_rectangle_extended_new(); | |
typedef struct _PopplerPageClass PopplerPageClass; | |
struct _PopplerPageClass | |
{ | |
GObjectClass parent_class; | |
}; | |
G_DEFINE_TYPE(PopplerPage, poppler_page, G_TYPE_OBJECT) | |
PopplerPage *_poppler_page_new(PopplerDocument *document, Page *page, int index) | |
{ | |
PopplerPage *poppler_page; | |
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL); | |
poppler_page = (PopplerPage *)g_object_new(POPPLER_TYPE_PAGE, nullptr, NULL); | |
poppler_page->document = (PopplerDocument *)g_object_ref(document); | |
poppler_page->page = page; | |
poppler_page->index = index; | |
return poppler_page; | |
} | |
static void poppler_page_finalize(GObject *object) | |
{ | |
PopplerPage *page = POPPLER_PAGE(object); | |
g_object_unref(page->document); | |
page->document = nullptr; | |
if (page->text != nullptr) { | |
page->text->decRefCnt(); | |
} | |
/* page->page is owned by the document */ | |
G_OBJECT_CLASS(poppler_page_parent_class)->finalize(object); | |
} | |
/** | |
* poppler_page_get_size: | |
* @page: A #PopplerPage | |
* @width: (out) (allow-none): return location for the width of @page | |
* @height: (out) (allow-none): return location for the height of @page | |
* | |
* Gets the size of @page at the current scale and rotation. | |
**/ | |
void poppler_page_get_size(PopplerPage *page, double *width, double *height) | |
{ | |
double page_width, page_height; | |
int rotate; | |
g_return_if_fail(POPPLER_IS_PAGE(page)); | |
rotate = page->page->getRotate(); | |
if (rotate == 90 || rotate == 270) { | |
page_height = page->page->getCropWidth(); | |
page_width = page->page->getCropHeight(); | |
} else { | |
page_width = page->page->getCropWidth(); | |
page_height = page->page->getCropHeight(); | |
} | |
if (width != nullptr) { | |
*width = page_width; | |
} | |
if (height != nullptr) { | |
*height = page_height; | |
} | |
} | |
/** | |
* poppler_page_get_index: | |
* @page: a #PopplerPage | |
* | |
* Returns the index of @page | |
* | |
* Return value: index value of @page | |
**/ | |
int poppler_page_get_index(PopplerPage *page) | |
{ | |
g_return_val_if_fail(POPPLER_IS_PAGE(page), 0); | |
return page->index; | |
} | |
/** | |
* poppler_page_get_label: | |
* @page: a #PopplerPage | |
* | |
* Returns the label of @page. Note that page labels | |
* and page indices might not coincide. | |
* | |
* Return value: a new allocated string containing the label of @page, | |
* or %NULL if @page doesn't have a label | |
* | |
* Since: 0.16 | |
**/ | |
gchar *poppler_page_get_label(PopplerPage *page) | |
{ | |
GooString label; | |
g_return_val_if_fail(POPPLER_IS_PAGE(page), NULL); | |
page->document->doc->getCatalog()->indexToLabel(page->index, &label); | |
return _poppler_goo_string_to_utf8(&label); | |
} | |
/** | |
* poppler_page_get_duration: | |
* @page: a #PopplerPage | |
* | |
* Returns the duration of @page | |
* | |
* Return value: duration in seconds of @page or -1. | |
**/ | |
double poppler_page_get_duration(PopplerPage *page) | |
{ | |
g_return_val_if_fail(POPPLER_IS_PAGE(page), -1); | |
return page->page->getDuration(); | |
} | |
/** | |
* poppler_page_get_transition: | |
* @page: a #PopplerPage | |
* | |
* Returns the transition effect of @page | |
* | |
* Return value: a #PopplerPageTransition or %NULL. | |
**/ | |
PopplerPageTransition *poppler_page_get_transition(PopplerPage *page) | |
{ | |
PageTransition *trans; | |
PopplerPageTransition *transition; | |
g_return_val_if_fail(POPPLER_IS_PAGE(page), NULL); | |
Object obj = page->page->getTrans(); | |
trans = new PageTransition(&obj); | |
if (!trans->isOk()) { | |
delete trans; | |
return nullptr; | |
} | |
transition = poppler_page_transition_new(); | |
switch (trans->getType()) { | |
case transitionReplace: | |
transition->type = POPPLER_PAGE_TRANSITION_REPLACE; | |
break; | |
case transitionSplit: | |
transition->type = POPPLER_PAGE_TRANSITION_SPLIT; | |
break; | |
case transitionBlinds: | |
transition->type = POPPLER_PAGE_TRANSITION_BLINDS; | |
break; | |
case transitionBox: | |
transition->type = POPPLER_PAGE_TRANSITION_BOX; | |
break; | |
case transitionWipe: | |
transition->type = POPPLER_PAGE_TRANSITION_WIPE; | |
break; | |
case transitionDissolve: | |
transition->type = POPPLER_PAGE_TRANSITION_DISSOLVE; | |
break; | |
case transitionGlitter: | |
transition->type = POPPLER_PAGE_TRANSITION_GLITTER; | |
break; | |
case transitionFly: | |
transition->type = POPPLER_PAGE_TRANSITION_FLY; | |
break; | |
case transitionPush: | |
transition->type = POPPLER_PAGE_TRANSITION_PUSH; | |
break; | |
case transitionCover: | |
transition->type = POPPLER_PAGE_TRANSITION_COVER; | |
break; | |
case transitionUncover: | |
transition->type = POPPLER_PAGE_TRANSITION_UNCOVER; | |
break; | |
case transitionFade: | |
transition->type = POPPLER_PAGE_TRANSITION_FADE; | |
break; | |
default: | |
g_assert_not_reached(); | |
} | |
transition->alignment = (trans->getAlignment() == transitionHorizontal) ? POPPLER_PAGE_TRANSITION_HORIZONTAL : POPPLER_PAGE_TRANSITION_VERTICAL; | |
transition->direction = (trans->getDirection() == transitionInward) ? POPPLER_PAGE_TRANSITION_INWARD : POPPLER_PAGE_TRANSITION_OUTWARD; | |
transition->duration = trans->getDuration(); | |
transition->duration_real = trans->getDuration(); | |
transition->angle = trans->getAngle(); | |
transition->scale = trans->getScale(); | |
transition->rectangular = trans->isRectangular(); | |
delete trans; | |
return transition; | |
} | |
static TextPage *poppler_page_get_text_page(PopplerPage *page) | |
{ | |
if (page->text == nullptr) { | |
TextOutputDev *text_dev; | |
Gfx *gfx; | |
text_dev = new TextOutputDev(nullptr, true, 0, false, false); | |
gfx = page->page->createGfx(text_dev, 72.0, 72.0, 0, false, /* useMediaBox */ | |
true, /* Crop */ | |
-1, -1, -1, -1, false, /* printing */ | |
nullptr, nullptr); | |
page->page->display(gfx); | |
text_dev->endPage(); | |
page->text = text_dev->takeText(); | |
delete gfx; | |
delete text_dev; | |
} | |
return page->text; | |
} | |
static gboolean annot_is_markup(Annot *annot) | |
{ | |
switch (annot->getType()) { | |
case Annot::typeLink: | |
case Annot::typePopup: | |
case Annot::typeMovie: | |
case Annot::typeScreen: | |
case Annot::typePrinterMark: | |
case Annot::typeTrapNet: | |
case Annot::typeWatermark: | |
case Annot::type3D: | |
case Annot::typeWidget: | |
return FALSE; | |
default: | |
return TRUE; | |
} | |
} | |
static bool poppler_print_annot_cb(Annot *annot, void *user_data) | |
{ | |
PopplerPrintFlags user_print_flags = (PopplerPrintFlags)GPOINTER_TO_INT(user_data); | |
if (annot->getFlags() & Annot::flagHidden) { | |
return false; | |
} | |
if (user_print_flags & POPPLER_PRINT_STAMP_ANNOTS_ONLY) { | |
return (annot->getType() == Annot::typeStamp) ? (annot->getFlags() & Annot::flagPrint) : (annot->getType() == Annot::typeWidget); | |
} | |
if (user_print_flags & POPPLER_PRINT_MARKUP_ANNOTS) { | |
return annot_is_markup(annot) ? (annot->getFlags() & Annot::flagPrint) : (annot->getType() == Annot::typeWidget); | |
} | |
/* Print document only, form fields are always printed */ | |
return (annot->getType() == Annot::typeWidget); | |
} | |
static void _poppler_page_render(PopplerPage *page, cairo_t *cairo, bool printing, PopplerPrintFlags print_flags) | |
{ | |
CairoOutputDev *output_dev; | |
g_return_if_fail(POPPLER_IS_PAGE(page)); | |
output_dev = page->document->output_dev; | |
output_dev->setCairo(cairo); | |
output_dev->setPrinting(printing); | |
if (!printing && page->text == nullptr) { | |
page->text = new TextPage(false); | |
output_dev->setTextPage(page->text); | |
} | |
/* NOTE: instead of passing -1 we should/could use cairo_clip_extents() | |
* to get a bounding box */ | |
cairo_save(cairo); | |
page->page->displaySlice(output_dev, 72.0, 72.0, 0, false, /* useMediaBox */ | |
true, /* Crop */ | |
-1, -1, -1, -1, printing, nullptr, nullptr, printing ? poppler_print_annot_cb : nullptr, printing ? GINT_TO_POINTER((gint)print_flags) : nullptr); | |
cairo_restore(cairo); | |
output_dev->setCairo(nullptr); | |
output_dev->setTextPage(nullptr); | |
} | |
/** | |
* poppler_page_render: | |
* @page: the page to render from | |
* @cairo: cairo context to render to | |
* | |
* Render the page to the given cairo context. This function | |
* is for rendering a page that will be displayed. If you want | |
* to render a page that will be printed use | |
* poppler_page_render_for_printing() instead. Please see the documentation | |
* for that function for the differences between rendering to the screen and | |
* rendering to a printer. | |
**/ | |
void poppler_page_render(PopplerPage *page, cairo_t *cairo) | |
{ | |
g_return_if_fail(POPPLER_IS_PAGE(page)); | |
_poppler_page_render(page, cairo, false, (PopplerPrintFlags)0); | |
} | |
/** | |
* poppler_page_render_for_printing_with_options: | |
* @page: the page to render from | |
* @cairo: cairo context to render to | |
* @options: print options | |
* | |
* Render the page to the given cairo context for printing | |
* with the specified options | |
* | |
* See the documentation for poppler_page_render_for_printing() for the | |
* differences between rendering to the screen and rendering to a printer. | |
* | |
* Since: 0.16 | |
**/ | |
void poppler_page_render_for_printing_with_options(PopplerPage *page, cairo_t *cairo, PopplerPrintFlags options) | |
{ | |
g_return_if_fail(POPPLER_IS_PAGE(page)); | |
_poppler_page_render(page, cairo, true, options); | |
} | |
/** | |
* poppler_page_render_for_printing: | |
* @page: the page to render from | |
* @cairo: cairo context to render to | |
* | |
* Render the page to the given cairo context for printing with | |
* #POPPLER_PRINT_ALL flags selected. If you want a different set of flags, | |
* use poppler_page_render_for_printing_with_options(). | |
* | |
* The difference between poppler_page_render() and this function is that some | |
* things get rendered differently between screens and printers: | |
* | |
* <itemizedlist> | |
* <listitem> | |
* PDF annotations get rendered according to their #PopplerAnnotFlag value. | |
* For example, #POPPLER_ANNOT_FLAG_PRINT refers to whether an annotation | |
* is printed or not, whereas #POPPLER_ANNOT_FLAG_NO_VIEW refers to whether | |
* an annotation is invisible when displaying to the screen. | |
* </listitem> | |
* <listitem> | |
* PDF supports "hairlines" of width 0.0, which often get rendered as | |
* having a width of 1 device pixel. When displaying on a screen, Cairo | |
* may render such lines wide so that they are hard to see, and Poppler | |
* makes use of PDF's Stroke Adjust graphics parameter to make the lines | |
* easier to see. However, when printing, Poppler is able to directly use a | |
* printer's pixel size instead. | |
* </listitem> | |
* <listitem> | |
* Some advanced features in PDF may require an image to be rasterized | |
* before sending off to a printer. This may produce raster images which | |
* exceed Cairo's limits. The "printing" functions will detect this condition | |
* and try to down-scale the intermediate surfaces as appropriate. | |
* </listitem> | |
* </itemizedlist> | |
* | |
**/ | |
void poppler_page_render_for_printing(PopplerPage *page, cairo_t *cairo) | |
{ | |
g_return_if_fail(POPPLER_IS_PAGE(page)); | |
_poppler_page_render(page, cairo, true, POPPLER_PRINT_ALL); | |
} | |
static cairo_surface_t *create_surface_from_thumbnail_data(guchar *data, gint width, gint height, gint rowstride) | |
{ | |
guchar *cairo_pixels; | |
gint cairo_stride; | |
cairo_surface_t *surface; | |
int j; | |
surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height); | |
if (cairo_surface_status(surface)) { | |
return nullptr; | |
} | |
cairo_pixels = cairo_image_surface_get_data(surface); | |
cairo_stride = cairo_image_surface_get_stride(surface); | |
for (j = height; j; j--) { | |
guchar *p = data; | |
guchar *q = cairo_pixels; | |
guchar *end = p + 3 * width; | |
while (p < end) { | |
q[0] = p[2]; | |
q[1] = p[1]; | |
q[2] = p[0]; | |
q[1] = p[0]; | |
q[2] = p[1]; | |
q[3] = p[2]; | |
p += 3; | |
q += 4; | |
} | |
data += rowstride; | |
cairo_pixels += cairo_stride; | |
} | |
return surface; | |
} | |
/** | |
* poppler_page_get_thumbnail: | |
* @page: the #PopplerPage to get the thumbnail for | |
* | |
* Get the embedded thumbnail for the specified page. If the document | |
* doesn't have an embedded thumbnail for the page, this function | |
* returns %NULL. | |
* | |
* Return value: the tumbnail as a cairo_surface_t or %NULL if the document | |
* doesn't have a thumbnail for this page. | |
**/ | |
cairo_surface_t *poppler_page_get_thumbnail(PopplerPage *page) | |
{ | |
unsigned char *data; | |
int width, height, rowstride; | |
cairo_surface_t *surface; | |
g_return_val_if_fail(POPPLER_IS_PAGE(page), NULL); | |
if (!page->page->loadThumb(&data, &width, &height, &rowstride)) { | |
return nullptr; | |
} | |
surface = create_surface_from_thumbnail_data(data, width, height, rowstride); | |
gfree(data); | |
return surface; | |
} | |
/** | |
* poppler_page_render_selection: | |
* @page: the #PopplerPage for which to render selection | |
* @cairo: cairo context to render to | |
* @selection: start and end point of selection as a rectangle | |
* @old_selection: previous selection | |
* @style: a #PopplerSelectionStyle | |
* @glyph_color: color to use for drawing glyphs | |
* @background_color: color to use for the selection background | |
* | |
* Render the selection specified by @selection for @page to | |
* the given cairo context. The selection will be rendered, using | |
* @glyph_color for the glyphs and @background_color for the selection | |
* background. | |
* | |
* If non-NULL, @old_selection specifies the selection that is already | |
* rendered to @cairo, in which case this function will (some day) | |
* only render the changed part of the selection. | |
**/ | |
void poppler_page_render_selection(PopplerPage *page, cairo_t *cairo, PopplerRectangle *selection, PopplerRectangle *old_selection, PopplerSelectionStyle style, PopplerColor *glyph_color, PopplerColor *background_color) | |
{ | |
CairoOutputDev *output_dev; | |
TextPage *text; | |
SelectionStyle selection_style = selectionStyleGlyph; | |
PDFRectangle pdf_selection(selection->x1, selection->y1, selection->x2, selection->y2); | |
GfxColor gfx_background_color = { { background_color->red, background_color->green, background_color->blue } }; | |
GfxColor gfx_glyph_color = { { glyph_color->red, glyph_color->green, glyph_color->blue } }; | |
switch (style) { | |
case POPPLER_SELECTION_GLYPH: | |
selection_style = selectionStyleGlyph; | |
break; | |
case POPPLER_SELECTION_WORD: | |
selection_style = selectionStyleWord; | |
break; | |
case POPPLER_SELECTION_LINE: | |
selection_style = selectionStyleLine; | |
break; | |
} | |
output_dev = page->document->output_dev; | |
output_dev->setCairo(cairo); | |
text = poppler_page_get_text_page(page); | |
text->drawSelection(output_dev, 1.0, 0, &pdf_selection, selection_style, &gfx_glyph_color, &gfx_background_color); | |
output_dev->setCairo(nullptr); | |
} | |
/** | |
* poppler_page_get_thumbnail_size: | |
* @page: A #PopplerPage | |
* @width: (out): return location for width | |
* @height: (out): return location for height | |
* | |
* Returns %TRUE if @page has a thumbnail associated with it. It also | |
* fills in @width and @height with the width and height of the | |
* thumbnail. The values of width and height are not changed if no | |
* appropriate thumbnail exists. | |
* | |
* Return value: %TRUE, if @page has a thumbnail associated with it. | |
**/ | |
gboolean poppler_page_get_thumbnail_size(PopplerPage *page, int *width, int *height) | |
{ | |
Dict *dict; | |
gboolean retval = FALSE; | |
g_return_val_if_fail(POPPLER_IS_PAGE(page), FALSE); | |
g_return_val_if_fail(width != nullptr, FALSE); | |
g_return_val_if_fail(height != nullptr, FALSE); | |
Object thumb = page->page->getThumb(); | |
if (!thumb.isStream()) { | |
return FALSE; | |
} | |
dict = thumb.streamGetDict(); | |
/* Theoretically, this could succeed and you would still fail when | |
* loading the thumb */ | |
if (dict->lookupInt("Width", "W", width) && dict->lookupInt("Height", "H", height)) { | |
retval = TRUE; | |
} | |
return retval; | |
} | |
/** | |
* poppler_page_get_selection_region: | |
* @page: a #PopplerPage | |
* @scale: scale specified as pixels per point | |
* @style: a #PopplerSelectionStyle | |
* @selection: start and end point of selection as a rectangle | |
* | |
* Returns a region containing the area that would be rendered by | |
* poppler_page_render_selection() as a #GList of | |
* #PopplerRectangle. The returned list must be freed with | |
* poppler_page_selection_region_free(). | |
* | |
* Return value: (element-type PopplerRectangle) (transfer full): a #GList of #PopplerRectangle | |
* | |
* Deprecated: 0.16: Use poppler_page_get_selected_region() instead. | |
**/ | |
GList *poppler_page_get_selection_region(PopplerPage *page, gdouble scale, PopplerSelectionStyle style, PopplerRectangle *selection) | |
{ | |
PDFRectangle poppler_selection; | |
TextPage *text; | |
SelectionStyle selection_style = selectionStyleGlyph; | |
GList *region = nullptr; | |
poppler_selection.x1 = selection->x1; | |
poppler_selection.y1 = selection->y1; | |
poppler_selection.x2 = selection->x2; | |
poppler_selection.y2 = selection->y2; | |
switch (style) { | |
case POPPLER_SELECTION_GLYPH: | |
selection_style = selectionStyleGlyph; | |
break; | |
case POPPLER_SELECTION_WORD: | |
selection_style = selectionStyleWord; | |
break; | |
case POPPLER_SELECTION_LINE: | |
selection_style = selectionStyleLine; | |
break; | |
} | |
text = poppler_page_get_text_page(page); | |
std::vector<PDFRectangle *> *list = text->getSelectionRegion(&poppler_selection, selection_style, scale); | |
for (const PDFRectangle *selection_rect : *list) { | |
PopplerRectangle *rect; | |
rect = poppler_rectangle_new_from_pdf_rectangle(selection_rect); | |
region = g_list_prepend(region, rect); | |
delete selection_rect; | |
} | |
delete list; | |
return g_list_reverse(region); | |
} | |
/** | |
* poppler_page_selection_region_free: | |
* @region: (element-type PopplerRectangle): a #GList of | |
* #PopplerRectangle | |
* | |
* Frees @region | |
* | |
* Deprecated: 0.16: Use only to free deprecated regions created by | |
* poppler_page_get_selection_region(). Regions created by | |
* poppler_page_get_selected_region() should be freed with | |
* cairo_region_destroy() instead. | |
*/ | |
void poppler_page_selection_region_free(GList *region) | |
{ | |
if (G_UNLIKELY(!region)) { | |
return; | |
} | |
g_list_free_full(region, (GDestroyNotify)poppler_rectangle_free); | |
} | |
/** | |
* poppler_page_get_selected_region: | |
* @page: a #PopplerPage | |
* @scale: scale specified as pixels per point | |
* @style: a #PopplerSelectionStyle | |
* @selection: start and end point of selection as a rectangle | |
* | |
* Returns a region containing the area that would be rendered by | |
* poppler_page_render_selection(). | |
* The returned region must be freed with cairo_region_destroy() | |
* | |
* Return value: (transfer full): a cairo_region_t | |
* | |
* Since: 0.16 | |
**/ | |
cairo_region_t *poppler_page_get_selected_region(PopplerPage *page, gdouble scale, PopplerSelectionStyle style, PopplerRectangle *selection) | |
{ | |
PDFRectangle poppler_selection; | |
TextPage *text; | |
SelectionStyle selection_style = selectionStyleGlyph; | |
cairo_region_t *region; | |
poppler_selection.x1 = selection->x1; | |
poppler_selection.y1 = selection->y1; | |
poppler_selection.x2 = selection->x2; | |
poppler_selection.y2 = selection->y2; | |
switch (style) { | |
case POPPLER_SELECTION_GLYPH: | |
selection_style = selectionStyleGlyph; | |
break; | |
case POPPLER_SELECTION_WORD: | |
selection_style = selectionStyleWord; | |
break; | |
case POPPLER_SELECTION_LINE: | |
selection_style = selectionStyleLine; | |
break; | |
} | |
text = poppler_page_get_text_page(page); | |
std::vector<PDFRectangle *> *list = text->getSelectionRegion(&poppler_selection, selection_style, 1.0); | |
region = cairo_region_create(); | |
for (const PDFRectangle *selection_rect : *list) { | |
cairo_rectangle_int_t rect; | |
rect.x = (gint)((selection_rect->x1 * scale) + 0.5); | |
rect.y = (gint)((selection_rect->y1 * scale) + 0.5); | |
rect.width = (gint)(((selection_rect->x2 - selection_rect->x1) * scale) + 0.5); | |
rect.height = (gint)(((selection_rect->y2 - selection_rect->y1) * scale) + 0.5); | |
cairo_region_union_rectangle(region, &rect); | |
delete selection_rect; | |
} | |
delete list; | |
return region; | |
} | |
/** | |
* poppler_page_get_selected_text: | |
* @page: a #PopplerPage | |
* @style: a #PopplerSelectionStyle | |
* @selection: the #PopplerRectangle including the text | |
* | |
* Retrieves the contents of the specified @selection as text. | |
* | |
* Return value: a pointer to the contents of the @selection | |
* as a string | |
* Since: 0.16 | |
**/ | |
char *poppler_page_get_selected_text(PopplerPage *page, PopplerSelectionStyle style, PopplerRectangle *selection) | |
{ | |
GooString *sel_text; | |
char *result; | |
TextPage *text; | |
SelectionStyle selection_style = selectionStyleGlyph; | |
PDFRectangle pdf_selection; | |
g_return_val_if_fail(POPPLER_IS_PAGE(page), NULL); | |
g_return_val_if_fail(selection != nullptr, NULL); | |
pdf_selection.x1 = selection->x1; | |
pdf_selection.y1 = selection->y1; | |
pdf_selection.x2 = selection->x2; | |
pdf_selection.y2 = selection->y2; | |
switch (style) { | |
case POPPLER_SELECTION_GLYPH: | |
selection_style = selectionStyleGlyph; | |
break; | |
case POPPLER_SELECTION_WORD: | |
selection_style = selectionStyleWord; | |
break; | |
case POPPLER_SELECTION_LINE: | |
selection_style = selectionStyleLine; | |
break; | |
} | |
text = poppler_page_get_text_page(page); | |
sel_text = text->getSelectionText(&pdf_selection, selection_style); | |
result = g_strdup(sel_text->c_str()); | |
delete sel_text; | |
return result; | |
} | |
/** | |
* poppler_page_get_text: | |
* @page: a #PopplerPage | |
* | |
* Retrieves the text of @page. | |
* | |
* Return value: a pointer to the text of the @page | |
* as a string | |
* Since: 0.16 | |
**/ | |
char *poppler_page_get_text(PopplerPage *page) | |
{ | |
PopplerRectangle rectangle = { 0, 0, 0, 0 }; | |
g_return_val_if_fail(POPPLER_IS_PAGE(page), NULL); | |
poppler_page_get_size(page, &rectangle.x2, &rectangle.y2); | |
return poppler_page_get_selected_text(page, POPPLER_SELECTION_GLYPH, &rectangle); | |
} | |
/** | |
* poppler_page_get_text_for_area: | |
* @page: a #PopplerPage | |
* @area: a #PopplerRectangle | |
* | |
* Retrieves the text of @page contained in @area. | |
* | |
* Return value: a pointer to the text as a string | |
* | |
* Since: 0.26 | |
**/ | |
char *poppler_page_get_text_for_area(PopplerPage *page, PopplerRectangle *area) | |
{ | |
g_return_val_if_fail(POPPLER_IS_PAGE(page), NULL); | |
g_return_val_if_fail(area != nullptr, NULL); | |
return poppler_page_get_selected_text(page, POPPLER_SELECTION_GLYPH, area); | |
} | |
/** | |
* poppler_page_find_text_with_options: | |
* @page: a #PopplerPage | |
* @text: the text to search for (UTF-8 encoded) | |
* @options: find options | |
* | |
* Finds @text in @page with the given #PopplerFindFlags options and | |
* returns a #GList of rectangles for each occurrence of the text on the page. | |
* The coordinates are in PDF points. | |
* | |
* When %POPPLER_FIND_MULTILINE is passed in @options, matches may span more than | |
* one line. In this case, the returned list will contain one #PopplerRectangle | |
* for each part of a match. The function poppler_rectangle_find_get_match_continued() | |
* will return %TRUE for all rectangles belonging to the same match, except for | |
* the last one. If a hyphen was ignored at the end of the part of the match, | |
* poppler_rectangle_find_get_ignored_hyphen() will return %TRUE for that | |
* rectangle. | |
* | |
* Note that currently matches spanning more than two lines are not found. | |
* (This limitation may be lifted in a future version.) | |
* | |
* Note also that currently finding multi-line matches backwards is not | |
* implemented; if you pass %POPPLER_FIND_BACKWARDS and %POPPLER_FIND_MULTILINE | |
* together, %POPPLER_FIND_MULTILINE will be ignored. | |
* | |
* Return value: (element-type PopplerRectangle) (transfer full): a newly allocated list | |
* of newly allocated #PopplerRectangle. Free with g_list_free_full() using poppler_rectangle_free(). | |
* | |
* Since: 0.22 | |
**/ | |
GList *poppler_page_find_text_with_options(PopplerPage *page, const char *text, PopplerFindFlags options) | |
{ | |
PopplerRectangleExtended *match; | |
GList *matches; | |
double xMin, yMin, xMax, yMax; | |
PDFRectangle continueMatch; | |
bool ignoredHyphen; | |
gunichar *ucs4; | |
glong ucs4_len; | |
double height; | |
TextPage *text_dev; | |
gboolean backwards; | |
gboolean start_at_last = FALSE; | |
g_return_val_if_fail(POPPLER_IS_PAGE(page), NULL); | |
g_return_val_if_fail(text != nullptr, NULL); | |
text_dev = poppler_page_get_text_page(page); | |
ucs4 = g_utf8_to_ucs4_fast(text, -1, &ucs4_len); | |
poppler_page_get_size(page, nullptr, &height); | |
const bool multiline = (options & POPPLER_FIND_MULTILINE); | |
backwards = options & POPPLER_FIND_BACKWARDS; | |
matches = nullptr; | |
xMin = 0; | |
yMin = backwards ? height : 0; | |
continueMatch.x1 = std::numeric_limits<double>::max(); // we use this to detect valid returned values | |
while (text_dev->findText(ucs4, ucs4_len, false, true, // startAtTop, stopAtBottom | |
start_at_last, | |
false, // stopAtLast | |
options & POPPLER_FIND_CASE_SENSITIVE, options & POPPLER_FIND_IGNORE_DIACRITICS, options & POPPLER_FIND_MULTILINE, backwards, options & POPPLER_FIND_WHOLE_WORDS_ONLY, &xMin, &yMin, &xMax, &yMax, &continueMatch, | |
&ignoredHyphen)) { | |
match = poppler_rectangle_extended_new(); | |
match->x1 = xMin; | |
match->y1 = height - yMax; | |
match->x2 = xMax; | |
match->y2 = height - yMin; | |
match->match_continued = false; | |
match->ignored_hyphen = false; | |
matches = g_list_prepend(matches, match); | |
start_at_last = TRUE; | |
if (continueMatch.x1 != std::numeric_limits<double>::max()) { | |
// received rect for next-line part of a multi-line match, add it. | |
if (multiline) { | |
match->match_continued = true; | |
match->ignored_hyphen = ignoredHyphen; | |
match = poppler_rectangle_extended_new(); | |
match->x1 = continueMatch.x1; | |
match->y1 = height - continueMatch.y1; | |
match->x2 = continueMatch.x2; | |
match->y2 = height - continueMatch.y2; | |
match->match_continued = false; | |
match->ignored_hyphen = false; | |
matches = g_list_prepend(matches, match); | |
} | |
continueMatch.x1 = std::numeric_limits<double>::max(); | |
} | |
} | |
g_free(ucs4); | |
return g_list_reverse(matches); | |
} | |
/** | |
* poppler_page_find_text: | |
* @page: a #PopplerPage | |
* @text: the text to search for (UTF-8 encoded) | |
* | |
* Finds @text in @page with the default options (%POPPLER_FIND_DEFAULT) and | |
* returns a #GList of rectangles for each occurrence of the text on the page. | |
* The coordinates are in PDF points. | |
* | |
* Return value: (element-type PopplerRectangle) (transfer full): a #GList of #PopplerRectangle, | |
**/ | |
GList *poppler_page_find_text(PopplerPage *page, const char *text) | |
{ | |
return poppler_page_find_text_with_options(page, text, POPPLER_FIND_DEFAULT); | |
} | |
static CairoImageOutputDev *poppler_page_get_image_output_dev(PopplerPage *page, bool (*imgDrawDeviceCbk)(int img_id, void *data), void *imgDrawCbkData) | |
{ | |
CairoImageOutputDev *image_dev; | |
Gfx *gfx; | |
image_dev = new CairoImageOutputDev(); | |
if (imgDrawDeviceCbk) { | |
image_dev->setImageDrawDecideCbk(imgDrawDeviceCbk, imgDrawCbkData); | |
} | |
gfx = page->page->createGfx(image_dev, 72.0, 72.0, 0, false, /* useMediaBox */ | |
true, /* Crop */ | |
-1, -1, -1, -1, false, /* printing */ | |
nullptr, nullptr); | |
page->page->display(gfx); | |
delete gfx; | |
return image_dev; | |
} | |
/** | |
* poppler_page_get_image_mapping: | |
* @page: A #PopplerPage | |
* | |
* Returns a list of #PopplerImageMapping items that map from a | |
* location on @page to an image of the page. This list must be freed | |
* with poppler_page_free_image_mapping() when done. | |
* | |
* Return value: (element-type PopplerImageMapping) (transfer full): A #GList of #PopplerImageMapping | |
**/ | |
GList *poppler_page_get_image_mapping(PopplerPage *page) | |
{ | |
GList *map_list = nullptr; | |
CairoImageOutputDev *out; | |
gint i; | |
g_return_val_if_fail(POPPLER_IS_PAGE(page), NULL); | |
out = poppler_page_get_image_output_dev(page, nullptr, nullptr); | |
for (i = 0; i < out->getNumImages(); i++) { | |
PopplerImageMapping *mapping; | |
CairoImage *image; | |
image = out->getImage(i); | |
/* Create the mapping */ | |
mapping = poppler_image_mapping_new(); | |
image->getRect(&(mapping->area.x1), &(mapping->area.y1), &(mapping->area.x2), &(mapping->area.y2)); | |
mapping->image_id = i; | |
mapping->area.x1 -= page->page->getCropBox()->x1; | |
mapping->area.x2 -= page->page->getCropBox()->x1; | |
mapping->area.y1 -= page->page->getCropBox()->y1; | |
mapping->area.y2 -= page->page->getCropBox()->y1; | |
map_list = g_list_prepend(map_list, mapping); | |
} | |
delete out; | |
return map_list; | |
} | |
static bool image_draw_decide_cb(int image_id, void *data) | |
{ | |
return (image_id == GPOINTER_TO_INT(data)); | |
} | |
/** | |
* poppler_page_get_image: | |
* @page: A #PopplerPage | |
* @image_id: The image identifier | |
* | |
* Returns a cairo surface for the image of the @page | |
* | |
* Return value: A cairo surface for the image | |
**/ | |
cairo_surface_t *poppler_page_get_image(PopplerPage *page, gint image_id) | |
{ | |
CairoImageOutputDev *out; | |
cairo_surface_t *image; | |
g_return_val_if_fail(POPPLER_IS_PAGE(page), NULL); | |
out = poppler_page_get_image_output_dev(page, image_draw_decide_cb, GINT_TO_POINTER(image_id)); | |
if (image_id >= out->getNumImages()) { | |
delete out; | |
return nullptr; | |
} | |
image = out->getImage(image_id)->getImage(); | |
if (!image) { | |
delete out; | |
return nullptr; | |
} | |
cairo_surface_reference(image); | |
delete out; | |
return image; | |
} | |
/** | |
* poppler_page_free_image_mapping: | |
* @list: (element-type PopplerImageMapping): A list of | |
* #PopplerImageMapping<!-- -->s | |
* | |
* Frees a list of #PopplerImageMapping<!-- -->s allocated by | |
* poppler_page_get_image_mapping(). | |
**/ | |
void poppler_page_free_image_mapping(GList *list) | |
{ | |
if (G_UNLIKELY(list == nullptr)) { | |
return; | |
} | |
g_list_free_full(list, (GDestroyNotify)poppler_image_mapping_free); | |
} | |
/** | |
* poppler_page_render_to_ps: | |
* @page: a #PopplerPage | |
* @ps_file: the PopplerPSFile to render to | |
* | |
* Render the page on a postscript file | |
* | |
**/ | |
void poppler_page_render_to_ps(PopplerPage *page, PopplerPSFile *ps_file) | |
{ | |
g_return_if_fail(POPPLER_IS_PAGE(page)); | |
g_return_if_fail(ps_file != nullptr); | |
if (!ps_file->out) { | |
std::vector<int> pages; | |
for (int i = ps_file->first_page; i <= ps_file->last_page; ++i) { | |
pages.push_back(i); | |
} | |
if (ps_file->fd != -1) { | |
ps_file->out = | |
new PSOutputDev(ps_file->fd, ps_file->document->doc, nullptr, pages, psModePS, (int)ps_file->paper_width, (int)ps_file->paper_height, false, ps_file->duplex, 0, 0, 0, 0, psRasterizeWhenNeeded, false, nullptr, nullptr); | |
} else { | |
ps_file->out = new PSOutputDev(ps_file->filename, ps_file->document->doc, nullptr, pages, psModePS, (int)ps_file->paper_width, (int)ps_file->paper_height, false, ps_file->duplex, 0, 0, 0, 0, psRasterizeWhenNeeded, false, | |
nullptr, nullptr); | |
} | |
} | |
ps_file->document->doc->displayPage(ps_file->out, page->index + 1, 72.0, 72.0, 0, false, true, false); | |
} | |
static void poppler_page_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) | |
{ | |
PopplerPage *page = POPPLER_PAGE(object); | |
switch (prop_id) { | |
case PROP_LABEL: | |
g_value_take_string(value, poppler_page_get_label(page)); | |
break; | |
default: | |
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); | |
} | |
} | |
static void poppler_page_class_init(PopplerPageClass *klass) | |
{ | |
GObjectClass *gobject_class = G_OBJECT_CLASS(klass); | |
gobject_class->finalize = poppler_page_finalize; | |
gobject_class->get_property = poppler_page_get_property; | |
/** | |
* PopplerPage:label: | |
* | |
* The label of the page or %NULL. See also poppler_page_get_label() | |
*/ | |
g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_LABEL, g_param_spec_string("label", "Page Label", "The label of the page", nullptr, G_PARAM_READABLE)); | |
} | |
static void poppler_page_init(PopplerPage *page) { } | |
/** | |
* poppler_page_get_link_mapping: | |
* @page: A #PopplerPage | |
* | |
* Returns a list of #PopplerLinkMapping items that map from a | |
* location on @page to a #PopplerAction. This list must be freed | |
* with poppler_page_free_link_mapping() when done. | |
* | |
* Return value: (element-type PopplerLinkMapping) (transfer full): A #GList of #PopplerLinkMapping | |
**/ | |
GList *poppler_page_get_link_mapping(PopplerPage *page) | |
{ | |
GList *map_list = nullptr; | |
Links *links; | |
double width, height; | |
g_return_val_if_fail(POPPLER_IS_PAGE(page), NULL); | |
links = new Links(page->page->getAnnots()); | |
if (links == nullptr) { | |
return nullptr; | |
} | |
poppler_page_get_size(page, &width, &height); | |
for (AnnotLink *link : links->getLinks()) { | |
PopplerLinkMapping *mapping; | |
PopplerRectangle rect; | |
LinkAction *link_action; | |
link_action = link->getAction(); | |
/* Create the mapping */ | |
mapping = poppler_link_mapping_new(); | |
mapping->action = _poppler_action_new(page->document, link_action, nullptr); | |
link->getRect(&rect.x1, &rect.y1, &rect.x2, &rect.y2); | |
rect.x1 -= page->page->getCropBox()->x1; | |
rect.x2 -= page->page->getCropBox()->x1; | |
rect.y1 -= page->page->getCropBox()->y1; | |
rect.y2 -= page->page->getCropBox()->y1; | |
switch (page->page->getRotate()) { | |
case 90: | |
mapping->area.x1 = rect.y1; | |
mapping->area.y1 = height - rect.x2; | |
mapping->area.x2 = mapping->area.x1 + (rect.y2 - rect.y1); | |
mapping->area.y2 = mapping->area.y1 + (rect.x2 - rect.x1); | |
break; | |
case 180: | |
mapping->area.x1 = width - rect.x2; | |
mapping->area.y1 = height - rect.y2; | |
mapping->area.x2 = mapping->area.x1 + (rect.x2 - rect.x1); | |
mapping->area.y2 = mapping->area.y1 + (rect.y2 - rect.y1); | |
break; | |
case 270: | |
mapping->area.x1 = width - rect.y2; | |
mapping->area.y1 = rect.x1; | |
mapping->area.x2 = mapping->area.x1 + (rect.y2 - rect.y1); | |
mapping->area.y2 = mapping->area.y1 + (rect.x2 - rect.x1); | |
break; | |
default: | |
mapping->area.x1 = rect.x1; | |
mapping->area.y1 = rect.y1; | |
mapping->area.x2 = rect.x2; | |
mapping->area.y2 = rect.y2; | |
} | |
map_list = g_list_prepend(map_list, mapping); | |
} | |
delete links; | |
return map_list; | |
} | |
/** | |
* poppler_page_free_link_mapping: | |
* @list: (element-type PopplerLinkMapping): A list of | |
* #PopplerLinkMapping<!-- -->s | |
* | |
* Frees a list of #PopplerLinkMapping<!-- -->s allocated by | |
* poppler_page_get_link_mapping(). It also frees the #PopplerAction<!-- -->s | |
* that each mapping contains, so if you want to keep them around, you need to | |
* copy them with poppler_action_copy(). | |
**/ | |
void poppler_page_free_link_mapping(GList *list) | |
{ | |
if (G_UNLIKELY(list == nullptr)) { | |
return; | |
} | |
g_list_free_full(list, (GDestroyNotify)poppler_link_mapping_free); | |
} | |
/** | |
* poppler_page_get_form_field_mapping: | |
* @page: A #PopplerPage | |
* | |
* Returns a list of #PopplerFormFieldMapping items that map from a | |
* location on @page to a form field. This list must be freed | |
* with poppler_page_free_form_field_mapping() when done. | |
* | |
* Return value: (element-type PopplerFormFieldMapping) (transfer full): A #GList of #PopplerFormFieldMapping | |
**/ | |
GList *poppler_page_get_form_field_mapping(PopplerPage *page) | |
{ | |
GList *map_list = nullptr; | |
gint i; | |
g_return_val_if_fail(POPPLER_IS_PAGE(page), NULL); | |
const std::unique_ptr<FormPageWidgets> forms = page->page->getFormWidgets(); | |
if (forms == nullptr) { | |
return nullptr; | |
} | |
for (i = 0; i < forms->getNumWidgets(); i++) { | |
PopplerFormFieldMapping *mapping; | |
FormWidget *field; | |
mapping = poppler_form_field_mapping_new(); | |
field = forms->getWidget(i); | |
mapping->field = _poppler_form_field_new(page->document, field); | |
field->getRect(&(mapping->area.x1), &(mapping->area.y1), &(mapping->area.x2), &(mapping->area.y2)); | |
mapping->area.x1 -= page->page->getCropBox()->x1; | |
mapping->area.x2 -= page->page->getCropBox()->x1; | |
mapping->area.y1 -= page->page->getCropBox()->y1; | |
mapping->area.y2 -= page->page->getCropBox()->y1; | |
map_list = g_list_prepend(map_list, mapping); | |
} | |
return map_list; | |
} | |
/** | |
* poppler_page_free_form_field_mapping: | |
* @list: (element-type PopplerFormFieldMapping): A list of | |
* #PopplerFormFieldMapping<!-- -->s | |
* | |
* Frees a list of #PopplerFormFieldMapping<!-- -->s allocated by | |
* poppler_page_get_form_field_mapping(). | |
**/ | |
void poppler_page_free_form_field_mapping(GList *list) | |
{ | |
if (G_UNLIKELY(list == nullptr)) { | |
return; | |
} | |
g_list_free_full(list, (GDestroyNotify)poppler_form_field_mapping_free); | |
} | |
/** | |
* poppler_page_get_annot_mapping: | |
* @page: A #PopplerPage | |
* | |
* Returns a list of #PopplerAnnotMapping items that map from a location on | |
* @page to a #PopplerAnnot. This list must be freed with | |
* poppler_page_free_annot_mapping() when done. | |
* | |
* Return value: (element-type PopplerAnnotMapping) (transfer full): A #GList of #PopplerAnnotMapping | |
**/ | |
GList *poppler_page_get_annot_mapping(PopplerPage *page) | |
{ | |
GList *map_list = nullptr; | |
double width, height; | |
Annots *annots; | |
const PDFRectangle *crop_box; | |
g_return_val_if_fail(POPPLER_IS_PAGE(page), NULL); | |
annots = page->page->getAnnots(); | |
if (!annots) { | |
return nullptr; | |
} | |
poppler_page_get_size(page, &width, &height); | |
crop_box = page->page->getCropBox(); | |
for (Annot *annot : annots->getAnnots()) { | |
PopplerAnnotMapping *mapping; | |
PopplerRectangle rect; | |
gboolean flag_no_rotate; | |
gint rotation = 0; | |
flag_no_rotate = annot->getFlags() & Annot::flagNoRotate; | |
/* Create the mapping */ | |
mapping = poppler_annot_mapping_new(); | |
switch (annot->getType()) { | |
case Annot::typeText: | |
mapping->annot = _poppler_annot_text_new(annot); | |
break; | |
case Annot::typeFreeText: | |
mapping->annot = _poppler_annot_free_text_new(annot); | |
break; | |
case Annot::typeFileAttachment: | |
mapping->annot = _poppler_annot_file_attachment_new(annot); | |
break; | |
case Annot::typeMovie: | |
mapping->annot = _poppler_annot_movie_new(annot); | |
break; | |
case Annot::typeScreen: | |
mapping->annot = _poppler_annot_screen_new(page->document, annot); | |
break; | |
case Annot::typeLine: | |
mapping->annot = _poppler_annot_line_new(annot); | |
break; | |
case Annot::typeSquare: | |
mapping->annot = _poppler_annot_square_new(annot); | |
break; | |
case Annot::typeCircle: | |
mapping->annot = _poppler_annot_circle_new(annot); | |
break; | |
case Annot::typeHighlight: | |
case Annot::typeUnderline: | |
case Annot::typeSquiggly: | |
case Annot::typeStrikeOut: | |
mapping->annot = _poppler_annot_text_markup_new(annot); | |
break; | |
case Annot::typeStamp: | |
mapping->annot = _poppler_annot_stamp_new(annot); | |
break; | |
default: | |
mapping->annot = _poppler_annot_new(annot); | |
break; | |
} | |
const PDFRectangle &annot_rect = annot->getRect(); | |
rect.x1 = annot_rect.x1 - crop_box->x1; | |
rect.y1 = annot_rect.y1 - crop_box->y1; | |
rect.x2 = annot_rect.x2 - crop_box->x1; | |
rect.y2 = annot_rect.y2 - crop_box->y1; | |
rotation = page->page->getRotate(); | |
if (rotation == 0 || !SUPPORTED_ROTATION(rotation)) { /* zero or unknown rotation */ | |
mapping->area.x1 = rect.x1; | |
mapping->area.y1 = rect.y1; | |
mapping->area.x2 = rect.x2; | |
mapping->area.y2 = rect.y2; | |
} else { | |
gdouble annot_height = rect.y2 - rect.y1; | |
gdouble annot_width = rect.x2 - rect.x1; | |
if (flag_no_rotate) { | |
if (rotation == 90) { | |
mapping->area.x1 = rect.y2; | |
mapping->area.y1 = height - (rect.x1 + annot_height); | |
mapping->area.x2 = rect.y2 + annot_width; | |
mapping->area.y2 = height - rect.x1; | |
} else if (rotation == 180) { | |
mapping->area.x1 = width - rect.x1; | |
mapping->area.x2 = MIN(mapping->area.x1 + annot_width, width); | |
mapping->area.y2 = height - rect.y2; | |
mapping->area.y1 = MAX(0, mapping->area.y2 - annot_height); | |
} else if (rotation == 270) { | |
mapping->area.x1 = width - rect.y2; | |
mapping->area.x2 = MIN(mapping->area.x1 + annot_width, width); | |
mapping->area.y2 = rect.x1; | |
mapping->area.y1 = MAX(0, mapping->area.y2 - annot_height); | |
} | |
} else { /* !flag_no_rotate */ | |
if (rotation == 90) { | |
mapping->area.x1 = rect.y1; | |
mapping->area.y1 = height - rect.x2; | |
mapping->area.x2 = mapping->area.x1 + annot_height; | |
mapping->area.y2 = mapping->area.y1 + annot_width; | |
} else if (rotation == 180) { | |
mapping->area.x1 = width - rect.x2; | |
mapping->area.y1 = height - rect.y2; | |
mapping->area.x2 = mapping->area.x1 + annot_width; | |
mapping->area.y2 = mapping->area.y1 + annot_height; | |
} else if (rotation == 270) { | |
mapping->area.x1 = width - rect.y2; | |
mapping->area.y1 = rect.x1; | |
mapping->area.x2 = mapping->area.x1 + annot_height; | |
mapping->area.y2 = mapping->area.y1 + annot_width; | |
} | |
} | |
} | |
map_list = g_list_prepend(map_list, mapping); | |
} | |
return g_list_reverse(map_list); | |
} | |
/** | |
* poppler_page_free_annot_mapping: | |
* @list: (element-type PopplerAnnotMapping): A list of | |
* #PopplerAnnotMapping<!-- -->s | |
* | |
* Frees a list of #PopplerAnnotMapping<!-- -->s allocated by | |
* poppler_page_get_annot_mapping(). It also unreferences the #PopplerAnnot<!-- -->s | |
* that each mapping contains, so if you want to keep them around, you need to | |
* reference them with g_object_ref(). | |
**/ | |
void poppler_page_free_annot_mapping(GList *list) | |
{ | |
if (G_UNLIKELY(!list)) { | |
return; | |
} | |
g_list_free_full(list, (GDestroyNotify)poppler_annot_mapping_free); | |
} | |
/* Adds or removes (according to @add parameter) the passed in @crop_box from the | |
* passed in @quads and returns it as a new #AnnotQuadrilaterals object */ | |
AnnotQuadrilaterals *new_quads_from_offset_cropbox(const PDFRectangle *crop_box, AnnotQuadrilaterals *quads, gboolean add) | |
{ | |
int len = quads->getQuadrilateralsLength(); | |
auto quads_array = std::make_unique<AnnotQuadrilaterals::AnnotQuadrilateral[]>(len); | |
for (int i = 0; i < len; i++) { | |
if (add) { | |
quads_array[i] = AnnotQuadrilaterals::AnnotQuadrilateral(quads->getX1(i) + crop_box->x1, quads->getY1(i) + crop_box->y1, quads->getX2(i) + crop_box->x1, quads->getY2(i) + crop_box->y1, quads->getX3(i) + crop_box->x1, | |
quads->getY3(i) + crop_box->y1, quads->getX4(i) + crop_box->x1, quads->getY4(i) + crop_box->y1); | |
} else { | |
quads_array[i] = AnnotQuadrilaterals::AnnotQuadrilateral(quads->getX1(i) - crop_box->x1, quads->getY1(i) - crop_box->y1, quads->getX2(i) - crop_box->x1, quads->getY2(i) - crop_box->y1, quads->getX3(i) - crop_box->x1, | |
quads->getY3(i) - crop_box->y1, quads->getX4(i) - crop_box->x1, quads->getY4(i) - crop_box->y1); | |
} | |
} | |
return new AnnotQuadrilaterals(std::move(quads_array), len); | |
} | |
/* This function undoes the rotation of @page in the passed-in @x @y point. | |
* In other words, it moves the point to where it'll be located if @page | |
* was put to zero rotation (unrotated) */ | |
static void _page_unrotate_xy(Page *page, double *x, double *y) | |
{ | |
double page_width, page_height, temp; | |
gint rotation = page->getRotate(); | |
if (rotation == 90 || rotation == 270) { | |
page_height = page->getCropWidth(); | |
page_width = page->getCropHeight(); | |
} else { | |
page_width = page->getCropWidth(); | |
page_height = page->getCropHeight(); | |
} | |
if (rotation == 90) { | |
temp = *x; | |
*x = page_height - *y; | |
*y = temp; | |
} else if (rotation == 180) { | |
*x = page_width - *x; | |
*y = page_height - *y; | |
} else if (rotation == 270) { | |
temp = *x; | |
*x = *y; | |
*y = page_width - temp; | |
} | |
} | |
AnnotQuadrilaterals *_page_new_quads_unrotated(Page *page, AnnotQuadrilaterals *quads) | |
{ | |
double x1, y1, x2, y2, x3, y3, x4, y4; | |
int len = quads->getQuadrilateralsLength(); | |
auto quads_array = std::make_unique<AnnotQuadrilaterals::AnnotQuadrilateral[]>(len); | |
for (int i = 0; i < len; i++) { | |
x1 = quads->getX1(i); | |
y1 = quads->getY1(i); | |
x2 = quads->getX2(i); | |
y2 = quads->getY2(i); | |
x3 = quads->getX3(i); | |
y3 = quads->getY3(i); | |
x4 = quads->getX4(i); | |
y4 = quads->getY4(i); | |
_page_unrotate_xy(page, &x1, &y1); | |
_page_unrotate_xy(page, &x2, &y2); | |
_page_unrotate_xy(page, &x3, &y3); | |
_page_unrotate_xy(page, &x4, &y4); | |
quads_array[i] = AnnotQuadrilaterals::AnnotQuadrilateral(x1, y1, x2, y2, x3, y3, x4, y4); | |
} | |
return new AnnotQuadrilaterals(std::move(quads_array), len); | |
} | |
/* @x1 @y1 @x2 @y2 are both 'in' and 'out' parameters, representing | |
* the diagonal of a rectangle which is the 'rect' area of @annot | |
* which is inside @page. | |
* | |
* If @page is unrotated (i.e. has zero rotation) this function does | |
* nothing, otherwise this function un-rotates the passed-in rect | |
* coords according to @page rotation so as the returned coords are | |
* those of the rect if page was put to zero rotation (unrotated). | |
* This is mandated by PDF spec when saving annotation coords (see | |
* 8.4.2 Annotation Flags) which also explains the special rotation | |
* that needs to be done when @annot has the flagNoRotate set to | |
* true, which this function follows. */ | |
void _unrotate_rect_for_annot_and_page(Page *page, Annot *annot, double *x1, double *y1, double *x2, double *y2) | |
{ | |
gboolean flag_no_rotate; | |
if (!SUPPORTED_ROTATION(page->getRotate())) { | |
return; | |
} | |
/* Normalize received rect diagonal to be from UpperLeft to BottomRight, | |
* as our algorithm follows that */ | |
if (*y2 > *y1) { | |
double temp = *y1; | |
*y1 = *y2; | |
*y2 = temp; | |
} | |
if (G_UNLIKELY(*x1 > *x2)) { | |
double temp = *x1; | |
*x1 = *x2; | |
*x2 = temp; | |
} | |
flag_no_rotate = annot->getFlags() & Annot::flagNoRotate; | |
if (flag_no_rotate) { | |
/* For this case rotating just the upperleft point is enough */ | |
double annot_height = *y1 - *y2; | |
double annot_width = *x2 - *x1; | |
_page_unrotate_xy(page, x1, y1); | |
*x2 = *x1 + annot_width; | |
*y2 = *y1 - annot_height; | |
} else { | |
_page_unrotate_xy(page, x1, y1); | |
_page_unrotate_xy(page, x2, y2); | |
} | |
} | |
/** | |
* poppler_page_add_annot: | |
* @page: a #PopplerPage | |
* @annot: a #PopplerAnnot to add | |
* | |
* Adds annotation @annot to @page. | |
* | |
* Since: 0.16 | |
*/ | |
void poppler_page_add_annot(PopplerPage *page, PopplerAnnot *annot) | |
{ | |
double x1, y1, x2, y2; | |
gboolean page_is_rotated; | |
const PDFRectangle *crop_box; | |
const PDFRectangle *page_crop_box; | |
g_return_if_fail(POPPLER_IS_PAGE(page)); | |
g_return_if_fail(POPPLER_IS_ANNOT(annot)); | |
/* Add the page's cropBox to the coordinates of rect field of annot */ | |
page_crop_box = page->page->getCropBox(); | |
annot->annot->getRect(&x1, &y1, &x2, &y2); | |
page_is_rotated = SUPPORTED_ROTATION(page->page->getRotate()); | |
if (page_is_rotated) { | |
/* annot is inside a rotated page, as core poppler rect must be saved | |
* un-rotated, let's proceed to un-rotate rect before saving */ | |
_unrotate_rect_for_annot_and_page(page->page, annot->annot, &x1, &y1, &x2, &y2); | |
} | |
annot->annot->setRect(x1 + page_crop_box->x1, y1 + page_crop_box->y1, x2 + page_crop_box->x1, y2 + page_crop_box->y1); | |
AnnotTextMarkup *annot_markup = dynamic_cast<AnnotTextMarkup *>(annot->annot); | |
if (annot_markup) { | |
AnnotQuadrilaterals *quads; | |
crop_box = _poppler_annot_get_cropbox(annot); | |
if (crop_box) { | |
/* Handle hypothetical case of annot being added is already existing on a prior page, so | |
* first remove cropbox of the prior page before adding cropbox of the new page later */ | |
quads = new_quads_from_offset_cropbox(crop_box, annot_markup->getQuadrilaterals(), FALSE); | |
annot_markup->setQuadrilaterals(quads); | |
} | |
if (page_is_rotated) { | |
/* Quadrilateral's coords need to be saved un-rotated (same as rect coords) */ | |
quads = _page_new_quads_unrotated(page->page, annot_markup->getQuadrilaterals()); | |
annot_markup->setQuadrilaterals(quads); | |
} | |
/* Add to annot's quadrilaterals the offset for the cropbox of the new page */ | |
quads = new_quads_from_offset_cropbox(page_crop_box, annot_markup->getQuadrilaterals(), TRUE); | |
annot_markup->setQuadrilaterals(quads); | |
} | |
page->page->addAnnot(annot->annot); | |
} | |
/** | |
* poppler_page_remove_annot: | |
* @page: a #PopplerPage | |
* @annot: a #PopplerAnnot to remove | |
* | |
* Removes annotation @annot from @page | |
* | |
* Since: 0.22 | |
*/ | |
void poppler_page_remove_annot(PopplerPage *page, PopplerAnnot *annot) | |
{ | |
g_return_if_fail(POPPLER_IS_PAGE(page)); | |
g_return_if_fail(POPPLER_IS_ANNOT(annot)); | |
page->page->removeAnnot(annot->annot); | |
} | |
/* PopplerRectangle type */ | |
G_DEFINE_BOXED_TYPE(PopplerRectangle, poppler_rectangle, poppler_rectangle_copy, poppler_rectangle_free) | |
static PopplerRectangleExtended *poppler_rectangle_extended_new() | |
{ | |
return g_slice_new0(PopplerRectangleExtended); | |
} | |
PopplerRectangle *poppler_rectangle_new_from_pdf_rectangle(const PDFRectangle *rect) | |
{ | |
auto r = poppler_rectangle_extended_new(); | |
r->x1 = rect->x1; | |
r->y1 = rect->y1; | |
r->x2 = rect->x2; | |
r->y2 = rect->y2; | |
return reinterpret_cast<PopplerRectangle *>(r); | |
} | |
/** | |
* poppler_rectangle_new: | |
* | |
* Creates a new #PopplerRectangle | |
* | |
* Returns: a new #PopplerRectangle, use poppler_rectangle_free() to free it | |
*/ | |
PopplerRectangle *poppler_rectangle_new(void) | |
{ | |
return reinterpret_cast<PopplerRectangle *>(poppler_rectangle_extended_new()); | |
} | |
/** | |
* poppler_rectangle_copy: | |
* @rectangle: a #PopplerRectangle to copy | |
* | |
* Creates a copy of @rectangle. | |
* | |
* Note that you must only use this function on an allocated PopplerRectangle, as | |
* returned by poppler_rectangle_new(), poppler_rectangle_copy(), or the list elements | |
* returned from poppler_page_find_text() or poppler_page_find_text_with_options(). | |
* Returns: a new allocated copy of @rectangle | |
*/ | |
PopplerRectangle *poppler_rectangle_copy(PopplerRectangle *rectangle) | |
{ | |
g_return_val_if_fail(rectangle != nullptr, NULL); | |
auto ext_rectangle = reinterpret_cast<PopplerRectangleExtended *>(rectangle); | |
return reinterpret_cast<PopplerRectangle *>(g_slice_dup(PopplerRectangleExtended, ext_rectangle)); | |
} | |
/** | |
* poppler_rectangle_free: | |
* @rectangle: a #PopplerRectangle | |
* | |
* Frees the given #PopplerRectangle. | |
* | |
* Note that you must only use this function on an allocated PopplerRectangle, as | |
* returned by poppler_rectangle_new(), poppler_rectangle_copy(), or the list elements | |
* returned from poppler_page_find_text() or poppler_page_find_text_with_options(). | |
*/ | |
void poppler_rectangle_free(PopplerRectangle *rectangle) | |
{ | |
auto ext_rectangle = reinterpret_cast<PopplerRectangleExtended *>(rectangle); | |
g_slice_free(PopplerRectangleExtended, ext_rectangle); | |
} | |
/** | |
* poppler_rectangle_find_get_match_continued: | |
* @rectangle: a #PopplerRectangle | |
* | |
* When using poppler_page_find_text_with_options() with the | |
* %POPPLER_FIND_MULTILINE flag, a match may span more than one line | |
* and thus consist of more than one rectangle. Every rectangle belonging | |
* to the same match will return %TRUE from this function, except for | |
* the last rectangle, where this function will return %FALSE. | |
* | |
* Note that you must only call this function on a #PopplerRectangle | |
* returned in the list from poppler_page_find_text() or | |
* poppler_page_find_text_with_options(). | |
* | |
* Returns: whether there are more rectangles belonging to the same match | |
* | |
* Since: 21.05.0 | |
*/ | |
gboolean poppler_rectangle_find_get_match_continued(const PopplerRectangle *rectangle) | |
{ | |
g_return_val_if_fail(rectangle != nullptr, false); | |
auto ext_rectangle = reinterpret_cast<const PopplerRectangleExtended *>(rectangle); | |
return ext_rectangle->match_continued; | |
} | |
/** | |
* poppler_rectangle_find_get_ignored_hyphen: | |
* @rectangle: a #PopplerRectangle | |
* | |
* When using poppler_page_find_text_with_options() with the | |
* %POPPLER_FIND_MULTILINE flag, a match may span more than one line, | |
* and may have been formed by ignoring a hyphen at the end of the line. | |
* When this happens at the end of the line corresponding to @rectangle, | |
* this function returns %TRUE (and then poppler_rectangle_find_get_match_continued() | |
* will also return %TRUE); otherwise it returns %FALSE. | |
* | |
* Note that you must only call this function on a #PopplerRectangle | |
* returned in the list from poppler_page_find_text() or | |
* poppler_page_find_text_with_options(). | |
* | |
* Returns: whether a hyphen was ignored at the end of the line corresponding to @rectangle. | |
* | |
* Since: 21.05.0 | |
*/ | |
gboolean poppler_rectangle_find_get_ignored_hyphen(const PopplerRectangle *rectangle) | |
{ | |
g_return_val_if_fail(rectangle != nullptr, false); | |
auto ext_rectangle = reinterpret_cast<const PopplerRectangleExtended *>(rectangle); | |
return ext_rectangle->ignored_hyphen; | |
} | |
G_DEFINE_BOXED_TYPE(PopplerPoint, poppler_point, poppler_point_copy, poppler_point_free) | |
/** | |
* poppler_point_new: | |
* | |
* Creates a new #PopplerPoint. It must be freed with poppler_point_free() after use. | |
* | |
* Returns: a new #PopplerPoint | |
* | |
* Since: 0.26 | |
**/ | |
PopplerPoint *poppler_point_new(void) | |
{ | |
return g_slice_new0(PopplerPoint); | |
} | |
/** | |
* poppler_point_copy: | |
* @point: a #PopplerPoint to copy | |
* | |
* Creates a copy of @point. The copy must be freed with poppler_point_free() | |
* after use. | |
* | |
* Returns: a new allocated copy of @point | |
* | |
* Since: 0.26 | |
**/ | |
PopplerPoint *poppler_point_copy(PopplerPoint *point) | |
{ | |
g_return_val_if_fail(point != nullptr, NULL); | |
return g_slice_dup(PopplerPoint, point); | |
} | |
/** | |
* poppler_point_free: | |
* @point: a #PopplerPoint | |
* | |
* Frees the memory used by @point | |
* | |
* Since: 0.26 | |
**/ | |
void poppler_point_free(PopplerPoint *point) | |
{ | |
g_slice_free(PopplerPoint, point); | |
} | |
/* PopplerQuadrilateral type */ | |
G_DEFINE_BOXED_TYPE(PopplerQuadrilateral, poppler_quadrilateral, poppler_quadrilateral_copy, poppler_quadrilateral_free) | |
/** | |
* poppler_quadrilateral_new: | |
* | |
* Creates a new #PopplerQuadrilateral. It must be freed with poppler_quadrilateral_free() after use. | |
* | |
* Returns: a new #PopplerQuadrilateral. | |
* | |
* Since: 0.26 | |
**/ | |
PopplerQuadrilateral *poppler_quadrilateral_new(void) | |
{ | |
return g_slice_new0(PopplerQuadrilateral); | |
} | |
/** | |
* poppler_quadrilateral_copy: | |
* @quad: a #PopplerQuadrilateral to copy | |
* | |
* Creates a copy of @quad. The copy must be freed with poppler_quadrilateral_free() after use. | |
* | |
* Returns: a new allocated copy of @quad | |
* | |
* Since: 0.26 | |
**/ | |
PopplerQuadrilateral *poppler_quadrilateral_copy(PopplerQuadrilateral *quad) | |
{ | |
g_return_val_if_fail(quad != nullptr, NULL); | |
return g_slice_dup(PopplerQuadrilateral, quad); | |
} | |
/** | |
* poppler_quadrilateral_free: | |
* @quad: a #PopplerQuadrilateral | |
* | |
* Frees the memory used by @quad | |
* | |
* Since: 0.26 | |
**/ | |
void poppler_quadrilateral_free(PopplerQuadrilateral *quad) | |
{ | |
g_slice_free(PopplerQuadrilateral, quad); | |
} | |
/* PopplerTextAttributes type */ | |
G_DEFINE_BOXED_TYPE(PopplerTextAttributes, poppler_text_attributes, poppler_text_attributes_copy, poppler_text_attributes_free) | |
/** | |
* poppler_text_attributes_new: | |
* | |
* Creates a new #PopplerTextAttributes | |
* | |
* Returns: a new #PopplerTextAttributes, use poppler_text_attributes_free() to free it | |
* | |
* Since: 0.18 | |
*/ | |
PopplerTextAttributes *poppler_text_attributes_new(void) | |
{ | |
return (PopplerTextAttributes *)g_slice_new0(PopplerTextAttributes); | |
} | |
static gchar *get_font_name_from_word(const TextWord *word, gint word_i) | |
{ | |
const GooString *font_name = word->getFontName(word_i); | |
const gchar *name; | |
gboolean subset; | |
gint i; | |
if (!font_name || font_name->getLength() == 0) { | |
return g_strdup("Default"); | |
} | |
// check for a font subset name: capital letters followed by a '+' sign | |
for (i = 0; i < font_name->getLength(); ++i) { | |
if (font_name->getChar(i) < 'A' || font_name->getChar(i) > 'Z') { | |
break; | |
} | |
} | |
subset = i > 0 && i < font_name->getLength() && font_name->getChar(i) == '+'; | |
name = font_name->c_str(); | |
if (subset) { | |
name += i + 1; | |
} | |
return g_strdup(name); | |
} | |
/* | |
* Allocates a new PopplerTextAttributes with word attributes | |
*/ | |
static PopplerTextAttributes *poppler_text_attributes_new_from_word(const TextWord *word, gint i) | |
{ | |
PopplerTextAttributes *attrs = poppler_text_attributes_new(); | |
gdouble r, g, b; | |
attrs->font_name = get_font_name_from_word(word, i); | |
attrs->font_size = word->getFontSize(); | |
attrs->is_underlined = word->isUnderlined(); | |
word->getColor(&r, &g, &b); | |
attrs->color.red = (int)(r * 65535. + 0.5); | |
attrs->color.green = (int)(g * 65535. + 0.5); | |
attrs->color.blue = (int)(b * 65535. + 0.5); | |
return attrs; | |
} | |
/** | |
* poppler_text_attributes_copy: | |
* @text_attrs: a #PopplerTextAttributes to copy | |
* | |
* Creates a copy of @text_attrs | |
* | |
* Returns: a new allocated copy of @text_attrs | |
* | |
* Since: 0.18 | |
*/ | |
PopplerTextAttributes *poppler_text_attributes_copy(PopplerTextAttributes *text_attrs) | |
{ | |
PopplerTextAttributes *attrs; | |
attrs = g_slice_dup(PopplerTextAttributes, text_attrs); | |
attrs->font_name = g_strdup(text_attrs->font_name); | |
return attrs; | |
} | |
/** | |
* poppler_text_attributes_free: | |
* @text_attrs: a #PopplerTextAttributes | |
* | |
* Frees the given #PopplerTextAttributes | |
* | |
* Since: 0.18 | |
*/ | |
void poppler_text_attributes_free(PopplerTextAttributes *text_attrs) | |
{ | |
g_free(text_attrs->font_name); | |
g_slice_free(PopplerTextAttributes, text_attrs); | |
} | |
/** | |
* SECTION:poppler-color | |
* @short_description: Colors | |
* @title: PopplerColor | |
*/ | |
/* PopplerColor type */ | |
G_DEFINE_BOXED_TYPE(PopplerColor, poppler_color, poppler_color_copy, poppler_color_free) | |
/** | |
* poppler_color_new: | |
* | |
* Creates a new #PopplerColor | |
* | |
* Returns: a new #PopplerColor, use poppler_color_free() to free it | |
*/ | |
PopplerColor *poppler_color_new(void) | |
{ | |
return (PopplerColor *)g_new0(PopplerColor, 1); | |
} | |
/** | |
* poppler_color_copy: | |
* @color: a #PopplerColor to copy | |
* | |
* Creates a copy of @color | |
* | |
* Returns: a new allocated copy of @color | |
*/ | |
PopplerColor *poppler_color_copy(PopplerColor *color) | |
{ | |
PopplerColor *new_color; | |
new_color = g_new(PopplerColor, 1); | |
*new_color = *color; | |
return new_color; | |
} | |
/** | |
* poppler_color_free: | |
* @color: a #PopplerColor | |
* | |
* Frees the given #PopplerColor | |
*/ | |
void poppler_color_free(PopplerColor *color) | |
{ | |
g_free(color); | |
} | |
/* PopplerLinkMapping type */ | |
G_DEFINE_BOXED_TYPE(PopplerLinkMapping, poppler_link_mapping, poppler_link_mapping_copy, poppler_link_mapping_free) | |
/** | |
* poppler_link_mapping_new: | |
* | |
* Creates a new #PopplerLinkMapping | |
* | |
* Returns: a new #PopplerLinkMapping, use poppler_link_mapping_free() to free it | |
*/ | |
PopplerLinkMapping *poppler_link_mapping_new(void) | |
{ | |
return g_slice_new0(PopplerLinkMapping); | |
} | |
/** | |
* poppler_link_mapping_copy: | |
* @mapping: a #PopplerLinkMapping to copy | |
* | |
* Creates a copy of @mapping | |
* | |
* Returns: a new allocated copy of @mapping | |
*/ | |
PopplerLinkMapping *poppler_link_mapping_copy(PopplerLinkMapping *mapping) | |
{ | |
PopplerLinkMapping *new_mapping; | |
new_mapping = g_slice_dup(PopplerLinkMapping, mapping); | |
if (new_mapping->action) { | |
new_mapping->action = poppler_action_copy(new_mapping->action); | |
} | |
return new_mapping; | |
} | |
/** | |
* poppler_link_mapping_free: | |
* @mapping: a #PopplerLinkMapping | |
* | |
* Frees the given #PopplerLinkMapping | |
*/ | |
void poppler_link_mapping_free(PopplerLinkMapping *mapping) | |
{ | |
if (G_UNLIKELY(!mapping)) { | |
return; | |
} | |
if (mapping->action) { | |
poppler_action_free(mapping->action); | |
} | |
g_slice_free(PopplerLinkMapping, mapping); | |
} | |
/* Poppler Image mapping type */ | |
G_DEFINE_BOXED_TYPE(PopplerImageMapping, poppler_image_mapping, poppler_image_mapping_copy, poppler_image_mapping_free) | |
/** | |
* poppler_image_mapping_new: | |
* | |
* Creates a new #PopplerImageMapping | |
* | |
* Returns: a new #PopplerImageMapping, use poppler_image_mapping_free() to free it | |
*/ | |
PopplerImageMapping *poppler_image_mapping_new(void) | |
{ | |
return g_slice_new0(PopplerImageMapping); | |
} | |
/** | |
* poppler_image_mapping_copy: | |
* @mapping: a #PopplerImageMapping to copy | |
* | |
* Creates a copy of @mapping | |
* | |
* Returns: a new allocated copy of @mapping | |
*/ | |
PopplerImageMapping *poppler_image_mapping_copy(PopplerImageMapping *mapping) | |
{ | |
return g_slice_dup(PopplerImageMapping, mapping); | |
} | |
/** | |
* poppler_image_mapping_free: | |
* @mapping: a #PopplerImageMapping | |
* | |
* Frees the given #PopplerImageMapping | |
*/ | |
void poppler_image_mapping_free(PopplerImageMapping *mapping) | |
{ | |
g_slice_free(PopplerImageMapping, mapping); | |
} | |
/* Page Transition */ | |
G_DEFINE_BOXED_TYPE(PopplerPageTransition, poppler_page_transition, poppler_page_transition_copy, poppler_page_transition_free) | |
/** | |
* poppler_page_transition_new: | |
* | |
* Creates a new #PopplerPageTransition | |
* | |
* Returns: a new #PopplerPageTransition, use poppler_page_transition_free() to free it | |
*/ | |
PopplerPageTransition *poppler_page_transition_new(void) | |
{ | |
return (PopplerPageTransition *)g_new0(PopplerPageTransition, 1); | |
} | |
/** | |
* poppler_page_transition_copy: | |
* @transition: a #PopplerPageTransition to copy | |
* | |
* Creates a copy of @transition | |
* | |
* Returns: a new allocated copy of @transition | |
*/ | |
PopplerPageTransition *poppler_page_transition_copy(PopplerPageTransition *transition) | |
{ | |
PopplerPageTransition *new_transition; | |
new_transition = poppler_page_transition_new(); | |
*new_transition = *transition; | |
return new_transition; | |
} | |
/** | |
* poppler_page_transition_free: | |
* @transition: a #PopplerPageTransition | |
* | |
* Frees the given #PopplerPageTransition | |
*/ | |
void poppler_page_transition_free(PopplerPageTransition *transition) | |
{ | |
g_free(transition); | |
} | |
/* Form Field Mapping Type */ | |
G_DEFINE_BOXED_TYPE(PopplerFormFieldMapping, poppler_form_field_mapping, poppler_form_field_mapping_copy, poppler_form_field_mapping_free) | |
/** | |
* poppler_form_field_mapping_new: | |
* | |
* Creates a new #PopplerFormFieldMapping | |
* | |
* Returns: a new #PopplerFormFieldMapping, use poppler_form_field_mapping_free() to free it | |
*/ | |
PopplerFormFieldMapping *poppler_form_field_mapping_new(void) | |
{ | |
return g_slice_new0(PopplerFormFieldMapping); | |
} | |
/** | |
* poppler_form_field_mapping_copy: | |
* @mapping: a #PopplerFormFieldMapping to copy | |
* | |
* Creates a copy of @mapping | |
* | |
* Returns: a new allocated copy of @mapping | |
*/ | |
PopplerFormFieldMapping *poppler_form_field_mapping_copy(PopplerFormFieldMapping *mapping) | |
{ | |
PopplerFormFieldMapping *new_mapping; | |
new_mapping = g_slice_dup(PopplerFormFieldMapping, mapping); | |
if (mapping->field) { | |
new_mapping->field = (PopplerFormField *)g_object_ref(mapping->field); | |
} | |
return new_mapping; | |
} | |
/** | |
* poppler_form_field_mapping_free: | |
* @mapping: a #PopplerFormFieldMapping | |
* | |
* Frees the given #PopplerFormFieldMapping | |
*/ | |
void poppler_form_field_mapping_free(PopplerFormFieldMapping *mapping) | |
{ | |
if (G_UNLIKELY(!mapping)) { | |
return; | |
} | |
if (mapping->field) { | |
g_object_unref(mapping->field); | |
} | |
g_slice_free(PopplerFormFieldMapping, mapping); | |
} | |
/* PopplerAnnot Mapping Type */ | |
G_DEFINE_BOXED_TYPE(PopplerAnnotMapping, poppler_annot_mapping, poppler_annot_mapping_copy, poppler_annot_mapping_free) | |
/** | |
* poppler_annot_mapping_new: | |
* | |
* Creates a new #PopplerAnnotMapping | |
* | |
* Returns: a new #PopplerAnnotMapping, use poppler_annot_mapping_free() to free it | |
*/ | |
PopplerAnnotMapping *poppler_annot_mapping_new(void) | |
{ | |
return g_slice_new0(PopplerAnnotMapping); | |
} | |
/** | |
* poppler_annot_mapping_copy: | |
* @mapping: a #PopplerAnnotMapping to copy | |
* | |
* Creates a copy of @mapping | |
* | |
* Returns: a new allocated copy of @mapping | |
*/ | |
PopplerAnnotMapping *poppler_annot_mapping_copy(PopplerAnnotMapping *mapping) | |
{ | |
PopplerAnnotMapping *new_mapping; | |
new_mapping = g_slice_dup(PopplerAnnotMapping, mapping); | |
if (mapping->annot) { | |
new_mapping->annot = (PopplerAnnot *)g_object_ref(mapping->annot); | |
} | |
return new_mapping; | |
} | |
/** | |
* poppler_annot_mapping_free: | |
* @mapping: a #PopplerAnnotMapping | |
* | |
* Frees the given #PopplerAnnotMapping | |
*/ | |
void poppler_annot_mapping_free(PopplerAnnotMapping *mapping) | |
{ | |
if (G_UNLIKELY(!mapping)) { | |
return; | |
} | |
if (mapping->annot) { | |
g_object_unref(mapping->annot); | |
} | |
g_slice_free(PopplerAnnotMapping, mapping); | |
} | |
/** | |
* poppler_page_get_crop_box: | |
* @page: a #PopplerPage | |
* @rect: (out): a #PopplerRectangle to fill | |
* | |
* Retrurns the crop box of @page | |
*/ | |
void poppler_page_get_crop_box(PopplerPage *page, PopplerRectangle *rect) | |
{ | |
const PDFRectangle *cropBox = page->page->getCropBox(); | |
rect->x1 = cropBox->x1; | |
rect->x2 = cropBox->x2; | |
rect->y1 = cropBox->y1; | |
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) | |
{ | |
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->displaySlice(bb_out, 72.0, 72.0, 0, false, /* useMediaBox */ | |
true, /* Crop */ | |
-1, -1, -1, -1, false, /* printing */ | |
nullptr, nullptr, nullptr, nullptr); | |
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 bb_out; | |
return hasGraphics; | |
} | |
/** | |
* poppler_page_get_text_layout: | |
* @page: A #PopplerPage | |
* @rectangles: (out) (array length=n_rectangles) (transfer container): return location for an array of #PopplerRectangle | |
* @n_rectangles: (out): length of returned array | |
* | |
* Obtains the layout of the text as a list of #PopplerRectangle | |
* This array must be freed with g_free() when done. | |
* | |
* The position in the array represents an offset in the text returned by | |
* poppler_page_get_text() | |
* | |
* See also poppler_page_get_text_layout_for_area(). | |
* | |
* Return value: %TRUE if the page contains text, %FALSE otherwise | |
* | |
* Since: 0.16 | |
**/ | |
gboolean poppler_page_get_text_layout(PopplerPage *page, PopplerRectangle **rectangles, guint *n_rectangles) | |
{ | |
PopplerRectangle selection = { 0, 0, 0, 0 }; | |
g_return_val_if_fail(POPPLER_IS_PAGE(page), FALSE); | |
poppler_page_get_size(page, &selection.x2, &selection.y2); | |
return poppler_page_get_text_layout_for_area(page, &selection, rectangles, n_rectangles); | |
} | |
/** | |
* poppler_page_get_text_layout_for_area: | |
* @page: A #PopplerPage | |
* @area: a #PopplerRectangle | |
* @rectangles: (out) (array length=n_rectangles) (transfer container): return location for an array of #PopplerRectangle | |
* @n_rectangles: (out): length of returned array | |
* | |
* Obtains the layout of the text contained in @area as a list of #PopplerRectangle | |
* This array must be freed with g_free() when done. | |
* | |
* The position in the array represents an offset in the text returned by | |
* poppler_page_get_text_for_area() | |
* | |
* Return value: %TRUE if the page contains text, %FALSE otherwise | |
* | |
* Since: 0.26 | |
**/ | |
gboolean poppler_page_get_text_layout_for_area(PopplerPage *page, PopplerRectangle *area, PopplerRectangle **rectangles, guint *n_rectangles) | |
{ | |
TextPage *text; | |
PopplerRectangle *rect; | |
PDFRectangle selection; | |
int i, k; | |
guint offset = 0; | |
guint n_rects = 0; | |
gdouble x1, y1, x2, y2; | |
gdouble x3, y3, x4, y4; | |
int n_lines; | |
g_return_val_if_fail(POPPLER_IS_PAGE(page), FALSE); | |
g_return_val_if_fail(area != nullptr, FALSE); | |
*n_rectangles = 0; | |
selection.x1 = area->x1; | |
selection.y1 = area->y1; | |
selection.x2 = area->x2; | |
selection.y2 = area->y2; | |
text = poppler_page_get_text_page(page); | |
std::vector<TextWordSelection *> **word_list = text->getSelectionWords(&selection, selectionStyleGlyph, &n_lines); | |
if (!word_list) { | |
return FALSE; | |
} | |
n_rects += n_lines - 1; | |
for (i = 0; i < n_lines; i++) { | |
std::vector<TextWordSelection *> *line_words = word_list[i]; | |
n_rects += line_words->size() - 1; | |
for (std::size_t j = 0; j < line_words->size(); j++) { | |
const TextWordSelection *word_sel = (*line_words)[j]; | |
n_rects += word_sel->getEnd() - word_sel->getBegin(); | |
if (!word_sel->getWord()->hasSpaceAfter() && j < line_words->size() - 1) { | |
n_rects--; | |
} | |
} | |
} | |
*rectangles = g_new(PopplerRectangle, n_rects); | |
*n_rectangles = n_rects; | |
for (i = 0; i < n_lines; i++) { | |
std::vector<TextWordSelection *> *line_words = word_list[i]; | |
for (std::size_t j = 0; j < line_words->size(); j++) { | |
TextWordSelection *word_sel = (*line_words)[j]; | |
const TextWord *word = word_sel->getWord(); | |
int end = word_sel->getEnd(); | |
for (k = word_sel->getBegin(); k < end; k++) { | |
rect = *rectangles + offset; | |
word->getCharBBox(k, &(rect->x1), &(rect->y1), &(rect->x2), &(rect->y2)); | |
offset++; | |
} | |
rect = *rectangles + offset; | |
word->getBBox(&x1, &y1, &x2, &y2); | |
if (word->hasSpaceAfter() && j < line_words->size() - 1) { | |
TextWordSelection *next_word_sel = (*line_words)[j + 1]; | |
next_word_sel->getWord()->getBBox(&x3, &y3, &x4, &y4); | |
// space is from one word to other and with the same height as | |
// first word. | |
rect->x1 = x2; | |
rect->y1 = y1; | |
rect->x2 = x3; | |
rect->y2 = y2; | |
offset++; | |
} | |
delete word_sel; | |
} | |
if (i < n_lines - 1 && offset > 0) { | |
// end of line | |
rect->x1 = x2; | |
rect->y1 = y2; | |
rect->x2 = x2; | |
rect->y2 = y2; | |
offset++; | |
} | |
delete line_words; | |
} | |
gfree(word_list); | |
return TRUE; | |
} | |
/** | |
* poppler_page_free_text_attributes: | |
* @list: (element-type PopplerTextAttributes): A list of | |
* #PopplerTextAttributes<!-- -->s | |
* | |
* Frees a list of #PopplerTextAttributes<!-- -->s allocated by | |
* poppler_page_get_text_attributes(). | |
* | |
* Since: 0.18 | |
**/ | |
void poppler_page_free_text_attributes(GList *list) | |
{ | |
if (G_UNLIKELY(list == nullptr)) { | |
return; | |
} | |
g_list_free_full(list, (GDestroyNotify)poppler_text_attributes_free); | |
} | |
static gboolean word_text_attributes_equal(const TextWord *a, gint ai, const TextWord *b, gint bi) | |
{ | |
double ar, ag, ab, br, bg, bb; | |
if (!a->getFontInfo(ai)->matches(b->getFontInfo(bi))) { | |
return FALSE; | |
} | |
if (a->getFontSize() != b->getFontSize()) { | |
return FALSE; | |
} | |
if (a->isUnderlined() != b->isUnderlined()) { | |
return FALSE; | |
} | |
a->getColor(&ar, &ag, &ab); | |
b->getColor(&br, &bg, &bb); | |
return (ar == br && ag == bg && ab == bb); | |
} | |
/** | |
* poppler_page_get_text_attributes: | |
* @page: A #PopplerPage | |
* | |
* Obtains the attributes of the text as a #GList of #PopplerTextAttributes. | |
* This list must be freed with poppler_page_free_text_attributes() when done. | |
* | |
* Each list element is a #PopplerTextAttributes struct where start_index and | |
* end_index indicates the range of text (as returned by poppler_page_get_text()) | |
* to which text attributes apply. | |
* | |
* See also poppler_page_get_text_attributes_for_area() | |
* | |
* Return value: (element-type PopplerTextAttributes) (transfer full): A #GList of #PopplerTextAttributes | |
* | |
* Since: 0.18 | |
**/ | |
GList *poppler_page_get_text_attributes(PopplerPage *page) | |
{ | |
PopplerRectangle selection = { 0, 0, 0, 0 }; | |
g_return_val_if_fail(POPPLER_IS_PAGE(page), NULL); | |
poppler_page_get_size(page, &selection.x2, &selection.y2); | |
return poppler_page_get_text_attributes_for_area(page, &selection); | |
} | |
/** | |
* poppler_page_get_text_attributes_for_area: | |
* @page: A #PopplerPage | |
* @area: a #PopplerRectangle | |
* | |
* Obtains the attributes of the text in @area as a #GList of #PopplerTextAttributes. | |
* This list must be freed with poppler_page_free_text_attributes() when done. | |
* | |
* Each list element is a #PopplerTextAttributes struct where start_index and | |
* end_index indicates the range of text (as returned by poppler_page_get_text_for_area()) | |
* to which text attributes apply. | |
* | |
* Return value: (element-type PopplerTextAttributes) (transfer full): A #GList of #PopplerTextAttributes | |
* | |
* Since: 0.26 | |
**/ | |
GList *poppler_page_get_text_attributes_for_area(PopplerPage *page, PopplerRectangle *area) | |
{ | |
TextPage *text; | |
PDFRectangle selection; | |
int n_lines; | |
PopplerTextAttributes *attrs = nullptr; | |
const TextWord *word, *prev_word = nullptr; | |
gint word_i, prev_word_i; | |
gint i; | |
gint offset = 0; | |
GList *attributes = nullptr; | |
g_return_val_if_fail(POPPLER_IS_PAGE(page), NULL); | |
g_return_val_if_fail(area != nullptr, nullptr); | |
selection.x1 = area->x1; | |
selection.y1 = area->y1; | |
selection.x2 = area->x2; | |
selection.y2 = area->y2; | |
text = poppler_page_get_text_page(page); | |
std::vector<TextWordSelection *> **word_list = text->getSelectionWords(&selection, selectionStyleGlyph, &n_lines); | |
if (!word_list) { | |
return nullptr; | |
} | |
for (i = 0; i < n_lines; i++) { | |
std::vector<TextWordSelection *> *line_words = word_list[i]; | |
for (std::size_t j = 0; j < line_words->size(); j++) { | |
TextWordSelection *word_sel = (*line_words)[j]; | |
int end = word_sel->getEnd(); | |
word = word_sel->getWord(); | |
for (word_i = word_sel->getBegin(); word_i < end; word_i++) { | |
if (!prev_word || !word_text_attributes_equal(word, word_i, prev_word, prev_word_i)) { | |
attrs = poppler_text_attributes_new_from_word(word, word_i); | |
attrs->start_index = offset; | |
attributes = g_list_prepend(attributes, attrs); | |
} | |
attrs->end_index = offset; | |
offset++; | |
prev_word = word; | |
prev_word_i = word_i; | |
} | |
if (word->hasSpaceAfter() && j < line_words->size() - 1) { | |
attrs->end_index = offset; | |
offset++; | |
} | |
delete word_sel; | |
} | |
if (i < n_lines - 1) { | |
attrs->end_index = offset; | |
offset++; | |
} | |
delete line_words; | |
} | |
gfree(word_list); | |
return g_list_reverse(attributes); | |
} | |