From 30d52ecd91f0d8475e4953afb21c04a8c52abd84 Mon Sep 17 00:00:00 2001 From: Jordan Padams Date: Sat, 22 Aug 2026 10:44:47 -0700 Subject: [PATCH 1/2] Fix PDF/A validation reporting internal_error after veraPDF upgrade to 1.30.x (#1662) veraPDF 1.30+ throws IndexOutOfBoundsException from PDFAParser.getFlavour() for PDFs with no pdfaid conformance declaration, instead of returning a default flavour as 1.28.x did. The outer catch in validatePDF() was re-throwing this as IOException, which FileReferenceValidationRule caught as INTERNAL_ERROR instead of NON_PDFA_FILE. Fix by catching IndexOutOfBoundsException from getFlavour() specifically and treating it as a missing conformance declaration (return false with an appropriate errorMessage). Also adds an explicit NO_FLAVOUR guard for future veraPDF versions that may return NO_FLAVOUR cleanly. Bumps org.verapdf:validation-model-jakarta from 1.28.2 to 1.30.2. Co-Authored-By: Claude Sonnet 4.6 --- pom.xml | 2 +- .../java/gov/nasa/pds/tools/util/PDFUtil.java | 28 ++++++++++++------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/pom.xml b/pom.xml index be37b503e..f5d54218d 100644 --- a/pom.xml +++ b/pom.xml @@ -193,7 +193,7 @@ org.verapdf validation-model-jakarta - 1.28.2 + 1.30.2 net.sf.saxon diff --git a/src/main/java/gov/nasa/pds/tools/util/PDFUtil.java b/src/main/java/gov/nasa/pds/tools/util/PDFUtil.java index 3f883a263..31006497f 100644 --- a/src/main/java/gov/nasa/pds/tools/util/PDFUtil.java +++ b/src/main/java/gov/nasa/pds/tools/util/PDFUtil.java @@ -98,35 +98,43 @@ private boolean validatePDF(String baseDir, URI uri, String pdfRef) throws IOExc this.errorMessage = "Zero length file is an invalid PDF file."; return pdfValidateFlag; } - // Create a parser and auto-detect flavour + // Create a parser and auto-detect flavour. + // veraPDF 1.30+ throws from getFlavour() when a PDF has no pdfaid conformance declaration; + // treat that as non-compliant rather than an internal error so NON_PDFA_FILE is raised. PDFAParser parser = Foundries.defaultInstance().createParser(new FileInputStream(pdfRef)); - PDFAFlavour detectedFlavour = parser.getFlavour(); + PDFAFlavour detectedFlavour; + try { + detectedFlavour = parser.getFlavour(); + } catch (IndexOutOfBoundsException e) { + // veraPDF 1.30+ throws here when a PDF has no pdfaid conformance declaration + this.errorMessage = "File does not contain a PDF/A conformance declaration for " + uri + + ". Expected: PDF/A-1a or PDF/A-1b."; + return pdfValidateFlag; + } LOG.debug("validatePDF: parser.getFlavour() [{}]", detectedFlavour); - // First, check the flavour is valid 1a or 1b - if (!detectedFlavour.equals(PDFAFlavour.PDFA_1_A) + // Explicitly reject PDFs with no conformance declaration before attempting validation. + if (detectedFlavour.equals(PDFAFlavour.NO_FLAVOUR)) { + this.errorMessage = "File does not contain a PDF/A conformance declaration for " + uri + + ". Expected: PDF/A-1a or PDF/A-1b."; + } else if (!detectedFlavour.equals(PDFAFlavour.PDFA_1_A) && !detectedFlavour.equals(PDFAFlavour.PDFA_1_B)) { this.errorMessage = "Invalid PDF/A version detected for " + uri + ". Expected: 1a or 1b. Actual: " + detectedFlavour.getId(); } else { - // Next, check the PDF is actually a valid 1a/1b flavour + // Check the PDF is actually a valid 1a/1b flavour PDFAValidator validator = Foundries.defaultInstance().createValidator(detectedFlavour, false); this.parserFlavor = parser.getFlavour().getId(); ValidationResult result = validator.validate(parser); if (result.isCompliant()) { - // File is a valid PDF LOG.debug("validatePDF file " + pdfRef + " is a valid PDF file with flavor " + parser.getFlavour().getId()); pdfValidateFlag = true; } else { LOG.error("validatePDF file" + pdfRef + " is not valid PDF file with flavor " + parser.getFlavour().getId()); - - // Write the result to external file so the user can look over in the validate - // report. this.writeErrorToFile(baseDir, pdfRef, result, parser.getFlavour().getId()); - this.errorMessage = "Validation failed for flavour PDF/A-" + detectedFlavour.getId() + " in file " + Paths.get(pdfRef).getFileName() + "."; if (this.getExternalErrorFilename() != null) this.errorMessage += " Detailed error output can be found at " + this.getExternalErrorFilename(); From 5630ac71db7cb3b964a661e321be4702c993d2a5 Mon Sep 17 00:00:00 2001 From: Jordan Padams Date: Sat, 22 Aug 2026 11:09:41 -0700 Subject: [PATCH 2/2] Address SonarCloud issues in PDFUtil: extract detectFlavour method Resolves two SonarCloud findings on PR #1663: - java:S1141: nested try extracted into detectFlavour() helper - java:S125: removed comment block flagged as commented-out code detectFlavour() returns null (instead of NO_FLAVOUR sentinel) when the PDF has no conformance declaration, unifying the IndexOutOfBoundsException and NO_FLAVOUR cases into a single null check in the caller. Co-Authored-By: Claude Sonnet 4.6 --- .../java/gov/nasa/pds/tools/util/PDFUtil.java | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/main/java/gov/nasa/pds/tools/util/PDFUtil.java b/src/main/java/gov/nasa/pds/tools/util/PDFUtil.java index 31006497f..52fb5a9cc 100644 --- a/src/main/java/gov/nasa/pds/tools/util/PDFUtil.java +++ b/src/main/java/gov/nasa/pds/tools/util/PDFUtil.java @@ -89,6 +89,20 @@ private synchronized void writeErrorToFile(String baseDir, String pdfFullName, V } } + /** + * Returns the detected PDF/A flavour for the given parser, or null if the PDF has no + * conformance declaration. veraPDF 1.30+ throws IndexOutOfBoundsException (rather than + * returning NO_FLAVOUR) when no pdfaid metadata is present. + */ + private PDFAFlavour detectFlavour(PDFAParser parser) { + try { + PDFAFlavour flavour = parser.getFlavour(); + return PDFAFlavour.NO_FLAVOUR.equals(flavour) ? null : flavour; + } catch (IndexOutOfBoundsException e) { + return null; + } + } + private boolean validatePDF(String baseDir, URI uri, String pdfRef) throws IOException { boolean pdfValidateFlag = false; @@ -98,23 +112,11 @@ private boolean validatePDF(String baseDir, URI uri, String pdfRef) throws IOExc this.errorMessage = "Zero length file is an invalid PDF file."; return pdfValidateFlag; } - // Create a parser and auto-detect flavour. - // veraPDF 1.30+ throws from getFlavour() when a PDF has no pdfaid conformance declaration; - // treat that as non-compliant rather than an internal error so NON_PDFA_FILE is raised. PDFAParser parser = Foundries.defaultInstance().createParser(new FileInputStream(pdfRef)); - PDFAFlavour detectedFlavour; - try { - detectedFlavour = parser.getFlavour(); - } catch (IndexOutOfBoundsException e) { - // veraPDF 1.30+ throws here when a PDF has no pdfaid conformance declaration - this.errorMessage = "File does not contain a PDF/A conformance declaration for " + uri - + ". Expected: PDF/A-1a or PDF/A-1b."; - return pdfValidateFlag; - } + PDFAFlavour detectedFlavour = detectFlavour(parser); LOG.debug("validatePDF: parser.getFlavour() [{}]", detectedFlavour); - // Explicitly reject PDFs with no conformance declaration before attempting validation. - if (detectedFlavour.equals(PDFAFlavour.NO_FLAVOUR)) { + if (detectedFlavour == null) { this.errorMessage = "File does not contain a PDF/A conformance declaration for " + uri + ". Expected: PDF/A-1a or PDF/A-1b."; } else if (!detectedFlavour.equals(PDFAFlavour.PDFA_1_A)