package edu.ncsu.dlf.model; import static org.junit.Assert.*; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.imageio.ImageIO; import edu.ncsu.dlf.model.PdfComment.Tag; import org.apache.pdfbox.io.IOUtils; import org.junit.Test; import test.TestUtils; public class TestPdfCommentExtraction { @Test public void testBlankPDF() throws Exception { Pdf pdf = loadCopyOfPdf("/blank.pdf"); try { assertEquals(0, pdf.getPDFComments().size()); } finally { pdf.close(); } } @Test public void testExoticAnnotations() throws Exception { Pdf pdf = loadCopyOfPdf("/strangeAnnotations.pdf"); String comparisonImageDir = "strangeAnnotations"; try { List<PdfComment> pdfComments = pdf.getPDFComments(); assertEquals(7, pdfComments.size()); PDFCommentMaker expectedCommentsMaker = new PDFCommentMaker() .fillComments("delete this", "Good!", "Typically standard", "Could be section 3", "[blank]", "delete this", "[blank]") .fillImages(loadExpectedImages(comparisonImageDir, 7)) .fillTitlesWithDefault(); List<ExpectedPdfComment> expectedComments = expectedCommentsMaker.asList(); expectedComments.get(4).imageDifferenceThreshold = 2.9; compare(expectedComments, pdfComments, comparisonImageDir); } finally { pdf.close(); } } @Test public void testHighlightedPDF() throws Exception { Pdf pdf = loadCopyOfPdf("/highlights.pdf"); try { List<PdfComment> pdfComments = pdf.getPDFComments(); assertEquals(5, pdfComments.size()); String[] expectedComments = new String[] { "Look at this Introduction", "Bad short words", "Paragraph", "Multi-column", "[blank]" }; BufferedImage[] expectedImages = loadExpectedImages("highlights", 5); compare(pdfComments, expectedComments, expectedImages, "highlights"); } finally { pdf.close(); } } @Test public void testPopupPDF() throws Exception { Pdf pdf = loadCopyOfPdf("/popups.pdf"); try { List<PdfComment> pdfComments = pdf.getPDFComments(); assertEquals(4, pdfComments.size()); String[] expectedComments = new String[] { "Edge one", "Edge two", "Middle of Page", "Corner" }; BufferedImage[] expectedImages = loadExpectedImages("popups", 4); compare(pdfComments, expectedComments, expectedImages, "popups"); } finally { pdf.close(); } } @Test public void testDrawingPdf() throws Exception { Pdf pdf = loadCopyOfPdf("/drawings.pdf"); try { List<PdfComment> pdfComments = pdf.getPDFComments(); assertEquals(4, pdfComments.size()); String[] expectedComments = new String[] { "[blank]", "Fix Please", "Was designificated", "[blank]" }; BufferedImage[] expectedImages = loadExpectedImages("drawings", 4); compare(pdfComments, expectedComments, expectedImages, "drawings"); } finally { pdf.close(); } } @Test public void testCommentsOfVariousIntensities() throws Exception { Pdf pdf = loadCopyOfPdf("/intenseFixes.pdf"); try { List<PdfComment> pdfComments = pdf.getPDFComments(); assertEquals(7, pdfComments.size()); String comparisonImageDir = "intenseFixes"; PDFCommentMaker expectedCommentsMaker = new PDFCommentMaker() .fillComments("[blank]", "Fix Please", "You might consider fixing this", "[blank]", "[blank]", "Fix Please", "You might consider fixing this") .fillImages(loadExpectedImages(comparisonImageDir, 7)) .fillTitlesWithDefault(); expectedCommentsMaker.setTags(1, PdfComment.Tag.MUST_FIX); expectedCommentsMaker.setTags(2, PdfComment.Tag.CONSIDER_FIX); expectedCommentsMaker.setTags(5, PdfComment.Tag.MUST_FIX); expectedCommentsMaker.setTags(6, PdfComment.Tag.CONSIDER_FIX); List<ExpectedPdfComment> expectedComments = expectedCommentsMaker.asList(); compare(expectedComments, pdfComments, comparisonImageDir); // check the colors created by updating the pdf updateCommentsWithColorsAndLinks(pdf, pdfComments); //TODO check colors or at least the updated tags. I tried rendering the page, but //PDFBox won't reflect the new colors. It has something to do with the fact that I don't //set an appearance stream in PDF.makeNewAnnotation, but that's complex and maybe a lot of effort for // little gain, as SumatraPDF and Reader can handle what is currently done. } finally { pdf.close(); } } private void updateCommentsWithColorsAndLinks(Pdf pdf, List<PdfComment> pdfComments) { int issueNumber = 1; for (PdfComment comment: pdfComments) { comment.setIssueNumber(issueNumber); issueNumber++; } Repo repo = new Repo("test-owner", "test-repo"); pdf.updateCommentsWithColorsAndLinks(pdfComments, repo); } private Pdf loadCopyOfPdf(String pathToPdf) throws IOException { InputStream fos = getClass().getResourceAsStream(pathToPdf); assertNotNull(fos); // this makes sure that any changes we make to the pdf are not persisted between tests ByteArrayInputStream copyOfPDF = new ByteArrayInputStream(IOUtils.toByteArray(fos)); InputStream commentBoxStream = getClass().getResourceAsStream("/images/comment_box.PNG"); assertNotNull(commentBoxStream); return new Pdf(copyOfPDF, commentBoxStream); } private BufferedImage[] loadExpectedImages(String resourceDir, int numImages) throws IOException { ArrayList<BufferedImage> loadedImages = new ArrayList<>(); for(int picName = 1; picName<=numImages; picName++) { BufferedImage loadedImage = ImageIO.read(getClass().getResourceAsStream("/images/"+resourceDir+"/"+picName+".png")); assertNotNull(loadedImage); loadedImages.add(loadedImage); } return loadedImages.toArray(new BufferedImage[loadedImages.size()]); } private void compare(List<PdfComment> pdfComments, String[] expectedComments, BufferedImage[] expectedImages, String foldername) { for(int i =0;i<pdfComments.size();i++) { PdfComment pdfComment = pdfComments.get(i); assertEquals("Failed Comment Comparison for comment "+i, expectedComments[i], pdfComment.getComment()); double difference = TestUtils.imagePercentDiff(expectedImages[i], pdfComment.getImage()); String possibleErrorMessage = String.format("Failed image Comparison for "+foldername+"/%d.png : difference %1.4f", i+1, difference); assertTrue(possibleErrorMessage, difference < 0.01); } } private void compare(List<ExpectedPdfComment> expected, List<PdfComment> actual, String folderName) { assertEquals(expected.size(), actual.size()); for (int i = 0; i < actual.size(); i++) { PdfComment a = actual.get(i); ExpectedPdfComment e = expected.get(i); assertEquals("Failed Comment Comparison for comment " + i, e.expectedComment, a.getComment()); assertEquals("Failed Title Comparison for comment " + i, e.expectedTitle, a.getTitle()); double difference = TestUtils.imagePercentDiff(e.expectedImage, a.getImage()); String possibleErrorMessage = String.format("Failed image Comparison for " + folderName + "/%d.png : difference %1.4f", i + 1, difference); assertTrue(possibleErrorMessage, difference < e.imageDifferenceThreshold); assertEquals("Failed tag comparison for comment "+i, e.expectedTags, a.getTags()); } } private static class PDFCommentMaker { private List<ExpectedPdfComment> builtComments = new ArrayList<>(); public PDFCommentMaker fillComments(String... expectedComments) { for (int i = 0; i < expectedComments.length; i++) { String expectedComment = expectedComments[i]; if (builtComments.size() <= i) { builtComments.add(new ExpectedPdfComment()); } builtComments.get(i).expectedComment = expectedComment; } return this; } public List<ExpectedPdfComment> asList() { validateComments(); return builtComments; } private void validateComments() { for(ExpectedPdfComment e : builtComments) { assertNotNull(e.expectedComment); assertNotNull(e.expectedImage); assertNotNull(e.expectedTitle); } } public void setTags(int index, Tag... expectedTags) { builtComments.get(index).expectedTags = Arrays.asList(expectedTags); } public PDFCommentMaker fillTitlesWithDefault() { for(ExpectedPdfComment e : builtComments) { e.expectedTitle = e.expectedComment; } return this; } public PDFCommentMaker fillImages(BufferedImage... expectedImages) { for (int i = 0; i < expectedImages.length; i++) { BufferedImage expectedImage = expectedImages[i]; if (builtComments.size() <= i) { builtComments.add(new ExpectedPdfComment()); } builtComments.get(i).expectedImage = expectedImage; } return this; } } private static class ExpectedPdfComment { public List<Tag> expectedTags = new ArrayList<>(); public BufferedImage expectedImage; public String expectedTitle; public String expectedComment; public double imageDifferenceThreshold = 0.01; } }