Skip to content
Snippets Groups Projects
Commit 82df10e0 authored by Marius Dumitru Florea's avatar Marius Dumitru Florea
Browse files

XWIKI-19270: Add support for performing the PDF export using a browser running...

XWIKI-19270: Add support for performing the PDF export using a browser running in a Docker container
* Extend the page objects and the tests
* Modify flavor-test-misc to use the PDFDocument class in order to avoid code duplication.
parent 09f7eec7
No related branches found
No related tags found
No related merge requests found
Showing
with 237 additions and 263 deletions
......@@ -74,12 +74,6 @@
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.xwiki.platform</groupId>
<artifactId>xwiki-platform-flamingo-skin-test-pageobjects</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<testSourceDirectory>src/test/it</testSourceDirectory>
......
......@@ -23,13 +23,13 @@
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import org.xwiki.export.pdf.test.po.ExportModal;
import org.xwiki.export.pdf.test.po.PDFDocument;
import org.xwiki.export.pdf.test.po.PDFExportOptionsModal;
import org.xwiki.export.pdf.test.po.PDFImage;
import org.xwiki.flamingo.skin.test.po.ExportModal;
import org.xwiki.flamingo.skin.test.po.OtherFormatPane;
import org.xwiki.model.reference.LocalDocumentReference;
import org.xwiki.test.docker.junit5.TestConfiguration;
import org.xwiki.test.docker.junit5.TestReference;
......@@ -55,14 +55,17 @@ void exportAsPDF(TestUtils setup, TestReference testReference, TestConfiguration
{
setup.createUserAndLogin("John", "pass");
PDFExportOptionsModal exportOptions =
exportDocumentAsPDF(setup, new LocalDocumentReference(Arrays.asList("PDFExportIT", "Parent"), "WebHome"));
ViewPage viewPage =
setup.gotoPage(new LocalDocumentReference(Arrays.asList("PDFExportIT", "Parent"), "WebHome"));
PDFExportOptionsModal exportOptions = ExportModal.open(viewPage).clickExportAsPDFButton();
try (PDFDocument pdf = exportOptions.export(getHostURL(testConfiguration))) {
// We should have 3 pages: cover page, table of contents and the actual content.
assertEquals(3, pdf.getNumberOfPages());
//
// Verify the cover page.
//
String coverPageText = pdf.getTextFromPage(0);
assertTrue(coverPageText.startsWith("Parent\nVersion 1.1 authored by superadmin"),
......@@ -79,11 +82,53 @@ void exportAsPDF(TestUtils setup, TestReference testReference, TestConfiguration
assertEquals(160, coverPageImages.get(0).getWidth());
assertEquals(160, coverPageImages.get(0).getHeight());
//
// Verify the table of contents page.
// TODO
//
String tocPageText = pdf.getTextFromPage(1);
// The header shows the page title and the footer shows the page number and the page count.
assertTrue(tocPageText.startsWith("Parent\n2 / 3\n"),
"Unexpected header and footer on table of contents page: " + coverPageText);
// TODO: Fix the page order in DocumentSelectionResolver.
assertTrue(tocPageText.contains("Table of Contents\nSection 1\nChapter 1\n"),
"Unexpected table of contents: " + tocPageText);
// The table of contents should have internal links (anchors) to each section.
Map<String, String> tocPageLinks = pdf.getLinksFromPage(1);
assertEquals(2, tocPageLinks.size());
// TODO: Fix the page order in DocumentSelectionResolver.
assertEquals(Arrays.asList("HSection1", "HChapter1"),
tocPageLinks.values().stream().collect(Collectors.toList()));
// No images on the table of contents page.
assertEquals(0, pdf.getImagesFromPage(1).size());
//
// Verify the content page.
// TODOo
//
String contentPageText = pdf.getTextFromPage(2);
assertTrue(contentPageText.startsWith("Parent\n3 / 3\n"),
"Unexpected header and footer on the content page: " + contentPageText);
assertTrue(
contentPageText.contains("Chapter 1\n"
+ "Content of first chapter. Current user is xwiki:XWiki.John.\nLink to child page.\nloaded!\n"),
"Parent page content missing: " + contentPageText);
assertTrue(contentPageText.contains("Section 1\nContent of first section.\n"),
"Child page content missing: " + contentPageText);
// The content of the parent page has a link to the child page.
Map<String, String> contentPageLinks = pdf.getLinksFromPage(2);
assertEquals(1, contentPageLinks.size());
assertEquals(setup.getURL(Arrays.asList("PDFExportIT", "Parent"), "Child") + "/",
contentPageLinks.get("child page."));
// The content of the child page shows an image.
List<PDFImage> contentPageImages = pdf.getImagesFromPage(2);
assertEquals(1, contentPageImages.size());
assertEquals(512, contentPageImages.get(0).getWidth());
assertEquals(512, contentPageImages.get(0).getHeight());
}
}
......@@ -92,18 +137,4 @@ private URL getHostURL(TestConfiguration testConfiguration) throws Exception
return new URL(String.format("http://%s:%d", testConfiguration.getServletEngine().getHostIP(),
testConfiguration.getServletEngine().getPort()));
}
private PDFExportOptionsModal exportDocumentAsPDF(TestUtils setup, LocalDocumentReference documentReference)
{
ViewPage viewPage = setup.gotoPage(documentReference);
// The export modal is present but hidden on page load. We instantiate the page object before opening the modal
// in order to prevent the fade effect (see BaseModal).
new ExportModal();
viewPage.clickMoreActionsSubMenuEntry("tmExport");
new OtherFormatPane().clickExportButton("Export as PDF");
return new PDFExportOptionsModal();
}
}
......@@ -37,6 +37,13 @@
<artifactId>xwiki-platform-test-ui</artifactId>
<version>${project.version}</version>
</dependency>
<!-- We're extending the standard Export modal. -->
<dependency>
<groupId>org.xwiki.platform</groupId>
<artifactId>xwiki-platform-flamingo-skin-test-pageobjects</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Used to read and validate the generated PDF files. -->
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
......
/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xwiki.export.pdf.test.po;
import org.xwiki.flamingo.skin.test.po.OtherFormatPane;
import org.xwiki.test.ui.po.ViewPage;
/**
* The PDF Export Application is currently customizing the Export Modal, thus we're extending the standard
* {@link org.xwiki.flamingo.skin.test.po.ExportModal}.
*
* @version $Id$
* @since 14.4.2
* @since 14.5RC1
*/
public class ExportModal extends org.xwiki.flamingo.skin.test.po.ExportModal
{
/**
* Opens the export modal for the given page.
*
* @param viewPage the page for which to open the export modal
* @return the export modal
*/
public static ExportModal open(ViewPage viewPage)
{
// The export modal is present but hidden on page load. We instantiate the page object before opening the modal
// in order to prevent the fade effect (see BaseModal).
ExportModal exportModal = new ExportModal();
viewPage.clickMoreActionsSubMenuEntry("tmExport");
// The Export Modal was modified to not show the accordion, so the "Other formats" pane is visible by default.
// Wait for the tree to be ready, if present.
OtherFormatPane otherFormatPane = new OtherFormatPane();
if (otherFormatPane.isTreeAvailable()) {
otherFormatPane.getTreeElement();
}
return exportModal;
}
/**
* Clicks on the "Export as PDF" button and return the "PDF Export Options" modal.
*
* @return the "PDF Export Options" modal
*/
public PDFExportOptionsModal clickExportAsPDFButton()
{
new OtherFormatPane().clickExportButton("Export as PDF");
return new PDFExportOptionsModal();
}
}
......@@ -40,6 +40,7 @@
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLink;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDDestination;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDNamedDestination;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageDestination;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageXYZDestination;
import org.apache.pdfbox.text.PDFTextStripper;
......@@ -84,6 +85,7 @@ public int getNumberOfPages()
/**
* @param pageNumber the page number
* @return the text from the specified page
* @throws IOException if we fail to extract the page text
*/
public String getTextFromPage(int pageNumber) throws IOException
{
......@@ -95,6 +97,28 @@ public String getTextFromPage(int pageNumber) throws IOException
return textStripper.getText(this.document);
}
/**
* @return the entire text from this PDF document
* @throws IOException if we fail to extract the text
*/
public String getText() throws IOException
{
return new PDFTextStripper().getText(this.document);
}
/**
* @return a mapping between link labels and link targets
* @throws IOException if we fail to extract the links from this PDF document
*/
public Map<String, String> getLinks() throws IOException
{
Map<String, String> links = new LinkedHashMap<>();
for (int i = 0; i < this.document.getNumberOfPages(); i++) {
links.putAll(getLinksFromPage(i));
}
return links;
}
/**
* @param pageNumber the page number
* @return a mapping between link labels and link targets
......@@ -102,33 +126,38 @@ public String getTextFromPage(int pageNumber) throws IOException
*/
public Map<String, String> getLinksFromPage(int pageNumber) throws IOException
{
Map<String, String> links = new LinkedHashMap<String, String>();
for (Map.Entry<String, PDAction> entry : getLinksFromPage(this.document.getPage(pageNumber)).entrySet()) {
if (entry.getValue() instanceof PDActionGoTo) {
PDActionGoTo anchor = (PDActionGoTo) entry.getValue();
Map<String, String> links = new LinkedHashMap<>();
for (Map.Entry<String, PDAnnotationLink> entry : getLinksFromPage(this.document.getPage(pageNumber))
.entrySet()) {
PDAction action = entry.getValue().getAction();
PDDestination destination = entry.getValue().getDestination();
if (action instanceof PDActionGoTo) {
PDActionGoTo anchor = (PDActionGoTo) entry.getValue().getAction();
links.put(entry.getKey(), getDestinationText(anchor.getDestination()));
} else if (entry.getValue() instanceof PDActionURI) {
PDActionURI uri = (PDActionURI) entry.getValue();
} else if (action instanceof PDActionURI) {
PDActionURI uri = (PDActionURI) entry.getValue().getAction();
links.put(entry.getKey(), uri.getURI());
} else if (destination != null) {
links.put(entry.getKey(), getDestinationText(destination));
}
}
return links;
}
/**
* Code adapted from http://www.docjar.com/html/api/org/apache/pdfbox/examples/pdmodel/PrintURLs.java.html
* Code adapted from
* https://github.com/apache/pdfbox/blob/trunk/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/PrintURLs.java
*/
private Map<String, PDAction> getLinksFromPage(PDPage page) throws IOException
private Map<String, PDAnnotationLink> getLinksFromPage(PDPage page) throws IOException
{
Map<String, PDAction> links = new LinkedHashMap<String, PDAction>();
Map<String, PDAnnotationLink> links = new LinkedHashMap<>();
PDFTextStripperByArea stripper = new PDFTextStripperByArea();
List<PDAnnotation> annotations = page.getAnnotations();
// First setup the text extraction regions.
for (int j = 0; j < annotations.size(); j++) {
PDAnnotation annotation = annotations.get(j);
if (annotation instanceof PDAnnotationLink) {
PDAnnotationLink link = (PDAnnotationLink) annotation;
PDRectangle rect = link.getRectangle();
PDRectangle rect = annotation.getRectangle();
// Need to reposition link rectangle to match text space.
float x = rect.getLowerLeftX();
float y = rect.getUpperRightY();
......@@ -137,9 +166,8 @@ private Map<String, PDAction> getLinksFromPage(PDPage page) throws IOException
int rotation = page.getRotation();
if (rotation == 0) {
PDRectangle pageSize = page.getMediaBox();
// Area stripper uses java coordinates, not PDF coordinates.
y = pageSize.getHeight() - y;
} else if (rotation == 90) {
// Do nothing.
}
Rectangle2D.Float awtRect = new Rectangle2D.Float(x, y, width, height);
......@@ -154,7 +182,7 @@ private Map<String, PDAction> getLinksFromPage(PDPage page) throws IOException
if (annotation instanceof PDAnnotationLink) {
PDAnnotationLink link = (PDAnnotationLink) annotation;
String label = stripper.getTextForRegion(String.valueOf(j)).trim();
links.put(label, link.getAction());
links.put(label, link);
}
}
......@@ -167,10 +195,44 @@ private String getDestinationText(PDDestination destination) throws IOException
return getDestinationText((PDPageXYZDestination) destination);
} else if (destination instanceof PDPageDestination) {
return "Page " + ((PDPageDestination) destination).getPageNumber();
} else if (destination instanceof PDNamedDestination) {
return ((PDNamedDestination) destination).getNamedDestination();
}
return destination.toString();
}
private String getDestinationText(PDPageXYZDestination destination) throws IOException
{
PDFTextStripperByArea stripper = new PDFTextStripperByArea();
stripper.addRegion("destination", getRectangleBelowDestination(destination));
stripper.extractRegions(destination.getPage());
return stripper.getTextForRegion("destination").trim();
}
private Rectangle2D getRectangleBelowDestination(PDPageXYZDestination destination)
{
PDPage page = destination.getPage();
PDRectangle pageSize = page.getMediaBox();
float x = destination.getLeft();
float y = pageSize.getHeight() - destination.getTop();
float width = pageSize.getWidth();
float height = destination.getTop();
return new Rectangle2D.Float(x, y, width, height);
}
/**
* @return the images from this PDF document
* @throws IOException if we fail to extract the images
*/
public List<PDFImage> getImages() throws IOException
{
Map<String, PDImageXObject> images = new LinkedHashMap<>();
for (PDPage page : this.document.getPages()) {
images.putAll(getImagesFromPage(page));
}
return images.values().stream().map(PDFImage::new).collect(Collectors.toList());
}
/**
* @param pageNumber the page number
* @return the images from the specified page
......
......@@ -39,16 +39,10 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<groupId>org.xwiki.platform</groupId>
<artifactId>xwiki-platform-export-pdf-test-pageobjects</artifactId>
<version>${project.version}</version>
<scope>test</scope>
<exclusions>
<!-- We want to choose the SLF4J binding only when XWiki is packaged. -->
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>commons-io</groupId>
......
......@@ -19,31 +19,10 @@
*/
package org.xwiki.test.misc;
import java.awt.geom.Rectangle2D;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.pdmodel.interactive.action.PDAction;
import org.apache.pdfbox.pdmodel.interactive.action.PDActionGoTo;
import org.apache.pdfbox.pdmodel.interactive.action.PDActionURI;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLink;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDDestination;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageDestination;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageXYZDestination;
import org.apache.pdfbox.text.PDFTextStripperByArea;
import org.apache.pdfbox.tools.PDFText2HTML;
import org.xwiki.export.pdf.test.po.PDFDocument;
import junit.framework.TestCase;
......@@ -62,11 +41,14 @@ public void testExportSingleSimplePageAsPDF() throws Exception
// property which was mistaken with the title property of XWiki.PDFClass before XWIKI-7048 was fixed. The gadget
// title contains Velocity code that isn't wrapped in a Velocity macro so it is printed as is if not rendered in
// the right context.
String text = getPDFContent(new URL("http://localhost:8080/xwiki/bin/export/Dashboard/WebHome?format=pdf"));
// Note: This is the title of the Pages gadget when it's working
assertTrue("Invalid content", text.contains("Pages"));
// Note: This is the title of the Pages gadget before XWIKI-7048 was fixed
assertFalse("Invalid content", text.contains("$services.localization.render("));
URL pdfURL = new URL("http://localhost:8080/xwiki/bin/export/Dashboard/WebHome?format=pdf");
try (PDFDocument document = new PDFDocument(pdfURL)) {
String text = document.getText();
// Note: This is the title of the Pages gadget when it's working
assertTrue("Invalid content", text.contains("Pages"));
// Note: This is the title of the Pages gadget before XWIKI-7048 was fixed
assertFalse("Invalid content", text.contains("$services.localization.render("));
}
}
/**
......@@ -76,16 +58,18 @@ public void testExportSingleSimplePageAsPDF() throws Exception
*/
public void testExportContentWithAttachmentLink() throws Exception
{
URL pdfExportURL = new URL("http://localhost:8080/xwiki/bin/export/Sandbox/WebHome?format=pdf");
Map<String, String> urls = extractURLs(pdfExportURL);
assertTrue(urls.containsKey("XWikiLogo.png"));
assertEquals("http://localhost:8080/xwiki/bin/download/Sandbox/WebHome/XWikiLogo.png?rev=1.1",
urls.get("XWikiLogo.png"));
// Ideally we should be asserting for a value of 1 (for the embedded XWikiLogo.png image) but it seems the PDF
// contains 2 image objects (for some reason I don't understand ATM - they seem to be variations of the same
// image - the logo - in color, in black and white, etc).
assertEquals(2, getImages(pdfExportURL).size());
URL pdfURL = new URL("http://localhost:8080/xwiki/bin/export/Sandbox/WebHome?format=pdf");
try (PDFDocument document = new PDFDocument(pdfURL)) {
Map<String, String> links = document.getLinks();
assertTrue(links.containsKey("XWikiLogo.png"));
assertEquals("http://localhost:8080/xwiki/bin/download/Sandbox/WebHome/XWikiLogo.png?rev=1.1",
links.get("XWikiLogo.png"));
// Ideally we should be asserting for a value of 1 (for the embedded XWikiLogo.png image) but it seems the
// PDF contains 2 image objects (for some reason I don't understand ATM - they seem to be variations of the
// same image - the logo - in color, in black and white, etc).
assertEquals(2, document.getImages().size());
}
}
/**
......@@ -95,183 +79,16 @@ public void testExportContentWithAttachmentLink() throws Exception
*/
public void testTableOfContents() throws Exception
{
Map<String, String> internalLinks = extractToLinks(new URL(
"http://localhost:8080/xwiki/bin/export/Sandbox/WebHome" + "?format=pdf&pdftoc=1&attachments=1&pdfcover=0"),
0);
// Make sure we have a Table of Contents.
assertTrue(internalLinks.containsKey("Mixed list"));
// Make sure the Table of Contents links point to their corresponding heading.
for (Map.Entry<String, String> entry : internalLinks.entrySet()) {
assertTrue(entry.getValue().contains(entry.getKey()));
}
}
private String getPDFContent(URL url) throws Exception
{
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
InputStream is = connection.getInputStream();
PDDocument pdd = PDDocument.load(is);
String text;
try {
PDFText2HTML stripper = new PDFText2HTML();
text = stripper.getText(pdd);
} finally {
if (pdd != null) {
pdd.close();
}
if (is != null) {
is.close();
}
}
return text;
}
private Map<String, PDImageXObject> getImages(URL url) throws Exception
{
Map<String, PDImageXObject> results = new HashMap<>();
PDDocument document = PDDocument.load(IOUtils.toByteArray(url));
try {
for (PDPage page : document.getDocumentCatalog().getPages()) {
PDResources pdResources = page.getResources();
for (COSName name : pdResources.getXObjectNames()) {
if (pdResources.isImageXObject(name)) {
PDImageXObject pdxObjectImage = (PDImageXObject) pdResources.getXObject(name);
results.put(name.getName(), pdxObjectImage);
}
}
}
} finally {
if (document != null) {
document.close();
}
}
return results;
}
private Map<String, String> extractURLs(URL url) throws Exception
{
Map<String, String> urls = new HashMap<String, String>();
PDDocument document = null;
try {
document = PDDocument.load(IOUtils.toByteArray(url));
for (Map.Entry<String, PDAction> entry : extractLinks(document).entrySet()) {
if (entry.getValue() instanceof PDActionURI) {
PDActionURI uri = (PDActionURI) entry.getValue();
urls.put(entry.getKey(), uri.getURI());
}
}
} finally {
if (document != null) {
document.close();
}
}
return urls;
}
private Map<String, String> extractToLinks(URL url, int tocPageIndex) throws Exception
{
Map<String, String> internalLinks = new HashMap<String, String>();
PDDocument document = null;
try {
document = PDDocument.load(IOUtils.toByteArray(url));
PDPage tocPage = document.getDocumentCatalog().getPages().get(tocPageIndex);
for (Map.Entry<String, PDAction> entry : extractLinks(tocPage).entrySet()) {
if (entry.getValue() instanceof PDActionGoTo) {
PDActionGoTo anchor = (PDActionGoTo) entry.getValue();
internalLinks.put(entry.getKey(), getDestinationText(anchor.getDestination()));
}
}
} finally {
if (document != null) {
document.close();
URL pdfURL = new URL(
"http://localhost:8080/xwiki/bin/export/Sandbox/WebHome?format=pdf&pdftoc=1&attachments=1&pdfcover=0");
try (PDFDocument document = new PDFDocument(pdfURL)) {
Map<String, String> links = document.getLinksFromPage(0);
// Make sure we have a Table of Contents.
assertTrue(links.containsKey("Mixed list"));
// Make sure the Table of Contents links point to their corresponding heading.
for (Map.Entry<String, String> entry : links.entrySet()) {
assertTrue(entry.getValue().contains(entry.getKey()));
}
}
return internalLinks;
}
private Map<String, PDAction> extractLinks(PDDocument document) throws Exception
{
Map<String, PDAction> links = new HashMap<String, PDAction>();
for (PDPage page : document.getDocumentCatalog().getPages()) {
links.putAll(extractLinks(page));
}
return links;
}
/**
* Code adapted from http://www.docjar.com/html/api/org/apache/pdfbox/examples/pdmodel/PrintURLs.java.html
*/
private Map<String, PDAction> extractLinks(PDPage page) throws Exception
{
Map<String, PDAction> links = new HashMap<String, PDAction>();
PDFTextStripperByArea stripper = new PDFTextStripperByArea();
List<PDAnnotation> annotations = page.getAnnotations();
// First setup the text extraction regions.
for (int j = 0; j < annotations.size(); j++) {
PDAnnotation annotation = annotations.get(j);
if (annotation instanceof PDAnnotationLink) {
PDAnnotationLink link = (PDAnnotationLink) annotation;
PDRectangle rect = link.getRectangle();
// Need to reposition link rectangle to match text space.
float x = rect.getLowerLeftX();
float y = rect.getUpperRightY();
float width = rect.getWidth();
float height = rect.getHeight();
int rotation = page.getRotation();
if (rotation == 0) {
PDRectangle pageSize = page.getMediaBox();
y = pageSize.getHeight() - y;
} else if (rotation == 90) {
// Do nothing.
}
Rectangle2D.Float awtRect = new Rectangle2D.Float(x, y, width, height);
stripper.addRegion(String.valueOf(j), awtRect);
}
}
stripper.extractRegions(page);
for (int j = 0; j < annotations.size(); j++) {
PDAnnotation annotation = annotations.get(j);
if (annotation instanceof PDAnnotationLink) {
PDAnnotationLink link = (PDAnnotationLink) annotation;
String label = stripper.getTextForRegion(String.valueOf(j)).trim();
links.put(label, link.getAction());
}
}
return links;
}
private String getDestinationText(PDDestination destination) throws Exception
{
if (destination instanceof PDPageXYZDestination) {
return getDestinationText((PDPageXYZDestination) destination);
} else if (destination instanceof PDPageDestination) {
return "Page " + ((PDPageDestination) destination).getPageNumber();
}
return destination.toString();
}
private String getDestinationText(PDPageXYZDestination destination) throws Exception
{
PDFTextStripperByArea stripper = new PDFTextStripperByArea();
stripper.addRegion("destination", getRectangleBelowDestination(destination));
stripper.extractRegions(destination.getPage());
return stripper.getTextForRegion("destination").trim();
}
private Rectangle2D getRectangleBelowDestination(PDPageXYZDestination destination)
{
PDPage page = destination.getPage();
PDRectangle pageSize = page.getMediaBox();
float x = destination.getLeft();
float y = pageSize.getHeight() - destination.getTop();
float width = pageSize.getWidth();
float height = destination.getTop();
return new Rectangle2D.Float(x, y, width, height);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment