/* * Created on 12 gen 2016 * Copyright 2015 by Andrea Vacondio (andrea.vacondio@gmail.com). * This file is part of Sejda. * * Sejda is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Sejda 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with Sejda. If not, see <http://www.gnu.org/licenses/>. */ package org.sejda.core.service; import static org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.sejda.util.RequireUtils.requireNotBlank; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.sejda.TestUtils; import org.sejda.core.context.DefaultSejdaContext; import org.sejda.core.context.SejdaContext; import org.sejda.core.support.util.StringUtils; import org.sejda.model.exception.TaskException; import org.sejda.model.input.PdfStreamSource; import org.sejda.model.input.StreamSource; import org.sejda.model.parameter.base.TaskParameters; import org.sejda.model.task.CancellationOption; import org.sejda.model.task.Task; import org.sejda.sambox.cos.COSName; import org.sejda.sambox.pdmodel.PDDocument; import org.sejda.sambox.pdmodel.PDPage; import org.sejda.sambox.pdmodel.common.PDRectangle; import org.sejda.sambox.pdmodel.graphics.image.PDImageXObject; import org.sejda.sambox.text.PDFTextStripperByArea; import java.awt.*; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.function.Consumer; /** * @author Andrea Vacondio * */ @Ignore public abstract class BaseTaskTest<T extends TaskParameters> implements TestableTask<T> { public final TaskTestContext testContext = new TaskTestContext(); private DefaultTaskExecutionService service = new DefaultTaskExecutionService(); @Before public void setUp() throws TaskException { SejdaContext context = mock(DefaultSejdaContext.class); TestUtils.setProperty(service, "context", context); when(context.getTask(any())).thenReturn((Task) getTask()); } public void execute(TaskParameters parameters) { service.execute(parameters); } public void execute(TaskParameters parameters, CancellationOption cancellationOption) { service.execute(parameters, cancellationOption); } @After public void closeContext() { IOUtils.closeQuietly(testContext); } public PdfStreamSource shortInput() { return PdfStreamSource.newInstanceNoPassword( getClass().getClassLoader().getResourceAsStream("pdf/short-test-file.pdf"), "short-test-file.pdf"); } public PdfStreamSource regularInput() { return PdfStreamSource.newInstanceNoPassword( getClass().getClassLoader().getResourceAsStream("pdf/test-pdf.pdf"), "test-file.pdf"); } public PdfStreamSource mediumInput() { return PdfStreamSource.newInstanceNoPassword( getClass().getClassLoader().getResourceAsStream("pdf/medium_test.pdf"), "medium-test-file.pdf"); } public PdfStreamSource largeInput() { return PdfStreamSource.newInstanceNoPassword( getClass().getClassLoader().getResourceAsStream("pdf/large_test.pdf"), "large-test-file.pdf"); } public PdfStreamSource largeOutlineInput() { return PdfStreamSource.newInstanceNoPassword( getClass().getClassLoader().getResourceAsStream("pdf/large_outline.pdf"), "large-outline-test-file.pdf"); } public PdfStreamSource encryptedInput() { return PdfStreamSource.newInstanceWithPassword( getClass().getClassLoader().getResourceAsStream("pdf/encrypted_AES128_user_pwd.pdf"), "encrypted-test-file.pdf", "test"); } public PdfStreamSource formInput() { return PdfStreamSource.newInstanceNoPassword( getClass().getClassLoader().getResourceAsStream("pdf/forms/two_pages_form.pdf"), "test-form.pdf"); } public PdfStreamSource stronglyEncryptedInput() { return PdfStreamSource.newInstanceWithPassword( getClass().getClassLoader().getResourceAsStream("pdf/encrypted_AES256_user_pwd.pdf"), "strongly-encrypted-test-file.pdf", "test"); } public PdfStreamSource customInput(String path) { return PdfStreamSource.newInstanceNoPassword(getClass().getClassLoader().getResourceAsStream(path), randomAlphanumeric(16) + ".pdf"); } public PdfStreamSource customInput(String path, String name) { requireNotBlank(name, "Name cannot be blank"); return PdfStreamSource.newInstanceNoPassword(getClass().getClassLoader().getResourceAsStream(path), name); } public PdfStreamSource customEncryptedInput(String path, String password) { return PdfStreamSource.newInstanceWithPassword(getClass().getClassLoader().getResourceAsStream(path), randomAlphanumeric(16) + ".pdf", password); } public StreamSource customNonPdfInput(String path) { String extension = FilenameUtils.getExtension(path); String filename = randomAlphanumeric(16) + "." + extension; return customNonPdfInput(path, filename); } public StreamSource customNonPdfInput(String path, String filename) { return StreamSource.newInstance(getClass().getClassLoader().getResourceAsStream(path), filename); } public void withPageText(PDPage page, Consumer<String> callback) { PDFTextStripperByArea textStripper; try { textStripper = new PDFTextStripperByArea(); PDRectangle pageSize = page.getCropBox(); Rectangle cropBoxRectangle = new Rectangle(0, 0, (int) pageSize.getWidth(), (int) pageSize.getHeight()); if(page.getRotation() == 90 || page.getRotation() == 270) { cropBoxRectangle = new Rectangle(0, 0, (int) pageSize.getHeight(), (int) pageSize.getWidth()); } textStripper.setSortByPosition(true); textStripper.addRegion("area1", cropBoxRectangle); textStripper.extractRegions(page); callback.accept(textStripper.getTextForRegion("area1")); } catch (IOException e) { fail(e.getMessage()); } } public void assertPageText(PDPage page, String text) { withPageText(page, pageText -> { assertEquals(text, pageText.replaceAll("[^A-Za-z0-9]", "")); }); } public void assertPageTextContains(PDPage page, String text) { withPageText(page, pageText -> { // ignores whitespace pageText = StringUtils.normalizeWhitespace(pageText); pageText = pageText.replaceAll("\\s", ""); assertThat(pageText, containsString(text)); }); } public void assertMediaBox(PDPage page, float width, float height) { assertEquals(page.getMediaBox().getWidth(), width, 0.01); assertEquals(page.getMediaBox().getHeight(), height, 0.01); } public <T> List<T> getAnnotationsOf(PDPage page, Class<T> clazz) { return iteratorToList(page.getAnnotations().stream() .filter(a -> clazz.isInstance(a)) .map(a -> (T) a) .iterator()); } // returns 1-based page numbers public List<Integer> getPagesContainingImages(PDDocument doc) { List<Integer> result = new ArrayList<>(); for(int i = 0; i < doc.getNumberOfPages(); i++) { PDPage page = doc.getPage(i); boolean hasImages = false; for(COSName name: page.getResources().getXObjectNames()) { try { if(page.getResources().getXObject(name) instanceof PDImageXObject) { hasImages = true; } } catch (IOException e) { throw new RuntimeException(e); } } if(hasImages) { result.add(i + 1); } } return result; } public <T> List<T> iteratorToList(Iterator<T> iterator) { List<T> result = new ArrayList<>(); while(iterator.hasNext()) { result.add(iterator.next()); } return result; } }