tdf#136822: Add VeraPDF as a PDF validator

For now, only validate those tests that are
conformant with the validator

verapdf-cli-1.29.0.jar has been generated using
https://github.com/veraPDF/veraPDF-apps/pull/415
and running 'mvn clean install'
Change-Id: Ic172100a120dc0f43f6de526660961d34dc6b415

Change-Id: Icacc8ccbd938f779b506fb5e1820f10a539ab77b
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198556
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
Tested-by: Jenkins
This commit is contained in:
Xisco Fauli
2026-02-20 08:31:47 +01:00
parent 5663308424
commit 7039373704
11 changed files with 109 additions and 36 deletions
+1
View File
@@ -59,6 +59,7 @@
/bin/bffvalidator.sh
/bin/odfvalidator.sh
/bin/officeotron.sh
/bin/verapdf.sh
/hardened_runtime.xcent
/lo.xcent
/vs-code.code-workspace.template
+1
View File
@@ -252,6 +252,7 @@ $(WORKDIR)/download: $(BUILDDIR)/config_$(gb_Side).mk $(SRCDIR)/download.lst $(S
$(call fetch_Optional,OPENSYMBOL,OPENSYMBOL_TTF) \
$(call fetch_Optional,ODFVALIDATOR,ODFVALIDATOR_JAR) \
$(call fetch_Optional,OFFICEOTRON,OFFICEOTRON_JAR) \
$(call fetch_Optional,VERAPDF,VERAPDF_JAR) \
,$(call fetch_Download_item,https://dev-www.libreoffice.org/extern,$(item)))
-@mkdir -p $(TARFILE_LOCATION)/cargo
$(if $(call fetch_Optional,YRS,1),\
+1
View File
@@ -226,6 +226,7 @@ distclean : clean compilerplugins-clean mac-app-store-package.clean
$(BUILDDIR)/bin/bffvalidator.sh \
$(BUILDDIR)/bin/odfvalidator.sh \
$(BUILDDIR)/bin/officeotron.sh \
$(BUILDDIR)/bin/verapdf.sh \
$(BUILDDIR)/config.Build.log \
$(BUILDDIR)/config.Build.warn \
$(BUILDDIR)/config.log \
+2
View File
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
java -jar @TARFILE_LOCATION_NATIVE@/@VERAPDF_JAR@ --nonpdfext "$@"
+1
View File
@@ -766,6 +766,7 @@ export VCL_PLUGIN_INFO=@VCL_PLUGIN_INFO@
export VCTOOLSET=@VCTOOLSET@
export VCVER=@VCVER@
export DEVENV=@DEVENV@
export VERAPDF=@VERAPDF@
export VISIO_CFLAGS=$(gb_SPACE)@VISIO_CFLAGS@
export VISIO_LIBS=$(gb_SPACE)@VISIO_LIBS@
export WGET=@WGET@
+27
View File
@@ -6197,6 +6197,7 @@ if test "$cross_compiling" = "yes"; then
bin/bffvalidator.sh.in \
bin/odfvalidator.sh.in \
bin/officeotron.sh.in \
bin/verapdf.sh.in \
instsetoo_native/util/openoffice.lst.in \
config_host/*.in \
sysui/desktop/macosx/Info.plist.in \
@@ -9653,6 +9654,31 @@ if test "$with_export_validation" != "no"; then
else
OFFICEOTRON="sh $OFFICEOTRON"
fi
AC_PATH_PROGS(VERAPDF, verapdf)
if test -z "$VERAPDF"; then
# remember to download the verapdf with validator later
AC_MSG_NOTICE([no verapdf found, will download it])
BUILD_TYPE="$BUILD_TYPE VERAPDF"
VERAPDF="$BUILDDIR/bin/verapdf.sh"
# and fetch name of verapdf jar name from download.lst
VERAPDF_JAR=`$SED -n -e "s/^VERAPDF_JAR *:= *\(.*\) */\1/p" $SRC_ROOT/download.lst`
AC_SUBST(VERAPDF_JAR)
if test -z "$VERAPDF_JAR"; then
AC_MSG_ERROR([cannot determine verapdf jar location (--with-export-validation)])
fi
fi
if test "$build_os" = "cygwin"; then
# In case of Cygwin it will be executed from Windows,
# so we need to run bash and absolute path to validator
# so instead of "odfvalidator" it will be
# something like "bash.exe C:\cygwin\opt\lo\bin\odfvalidator"
VERAPDF="bash.exe `cygpath -m "$VERAPDF"`"
else
VERAPDF="sh $VERAPDF"
fi
fi
AC_SUBST(OFFICEOTRON)
else
@@ -16274,6 +16300,7 @@ AC_CONFIG_FILES([
bin/bffvalidator.sh
bin/odfvalidator.sh
bin/officeotron.sh
bin/verapdf.sh
instsetoo_native/util/openoffice.lst
sysui/desktop/macosx/Info.plist
sysui/desktop/macosx/LaunchConstraint.plist
+5
View File
@@ -539,6 +539,11 @@ OFFICEOTRON_JAR := officeotron-0.8.8.jar
# three static lines
# so that git cherry-pick
# will not run into conflicts
VERAPDF_SHA256SUM := bdeef807f7e883fe3ff4e0a4712dc216064ca670d5c857fbc94408266719f0f4
VERAPDF_JAR := verapdf-cli-1.29.0.jar
# three static lines
# so that git cherry-pick
# will not run into conflicts
ONLINEUPDATE_SHA256SUM := 37206cf981e8409d048b59ac5839621ea107ff49af72beb9d7769a2f41da8d90
ONLINEUPDATE_TARBALL := onlineupdate-c003be8b9727672e7d30972983b375f4c200233f-2.tar.xz
# three static lines
+4 -3
View File
@@ -28,7 +28,8 @@ enum ValidationFormat
{
OOXML,
ODF,
MSBINARY
MSBINARY,
PDF
};
enum class TestFilter
@@ -188,9 +189,9 @@ protected:
rtl::Reference<TestInteractionHandler> xInteractionHandler;
private:
void validate(const OUString& rURL, TestFilter eFilter) const;
void validate(TestFilter eFilter);
private:
bool mbSkipValidation;
OUString m_aBaseString;
+54 -33
View File
@@ -100,7 +100,7 @@ constexpr std::u16string_view grand_total = u"Grand total of errors in submitted
}
#endif
void UnoApiTest::validate(const OUString& rPath, TestFilter eFilter) const
void UnoApiTest::validate(TestFilter eFilter)
{
ValidationFormat eFormat = ValidationFormat::ODF;
if (eFilter == TestFilter::XLSX)
@@ -123,6 +123,8 @@ void UnoApiTest::validate(const OUString& rPath, TestFilter eFilter) const
eFormat = ValidationFormat::MSBINARY;
else if (eFilter == TestFilter::PPT)
eFormat = ValidationFormat::MSBINARY;
else if (eFilter == TestFilter::PDF_WRITER)
eFormat = ValidationFormat::PDF;
else
{
SAL_INFO("test", "UnoApiTest::validate: unknown filter");
@@ -139,6 +141,10 @@ void UnoApiTest::validate(const OUString& rPath, TestFilter eFilter) const
{
var = "ODFVALIDATOR";
}
else if (eFormat == ValidationFormat::PDF)
{
var = "VERAPDF";
}
else if (eFormat == ValidationFormat::MSBINARY)
{
#if HAVE_BFFVALIDATOR
@@ -175,7 +181,7 @@ void UnoApiTest::validate(const OUString& rPath, TestFilter eFilter) const
utl::TempFileNamed aOutput;
aOutput.EnableKillingFile();
OUString aOutputFile = aOutput.GetFileName();
OUString aCommand = aValidator + " " + rPath + " > " + aOutputFile + " 2>&1";
OUString aCommand = aValidator + " " + maTempFile.GetFileName() + " > " + aOutputFile + " 2>&1";
#if !defined _WIN32
// For now, this is only needed by some Linux ASan builds, so keep it simply and disable it on
@@ -199,45 +205,59 @@ void UnoApiTest::validate(const OUString& rPath, TestFilter eFilter) const
SAL_INFO("test", "UnoApiTest::validate: executing '" << aCommand << "'");
int returnValue = system(OUStringToOString(aCommand, RTL_TEXTENCODING_UTF8).getStr());
OString aContentString = loadFile(aOutput.GetURL());
OUString aContentOUString = OStringToOUString(aContentString, RTL_TEXTENCODING_UTF8);
if (eFormat == ValidationFormat::OOXML && !aContentOUString.isEmpty())
if (eFormat == ValidationFormat::PDF)
{
// check for validation errors here
sal_Int32 nIndex = aContentOUString.lastIndexOf(grand_total);
if (nIndex == -1)
SvMemoryStream aStream;
SvFileStream aFileStream(aOutput.GetURL(), StreamMode::READ);
aStream.WriteStream(aFileStream);
aStream.Seek(0);
xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream);
// Make sure the output is well-formed.
CPPUNIT_ASSERT(pXmlDoc);
assertXPath(pXmlDoc, "/report/jobs/job/validationReport", "isCompliant", u"true");
}
else
{
OString aContentString = loadFile(aOutput.GetURL());
OUString aContentOUString = OStringToOUString(aContentString, RTL_TEXTENCODING_UTF8);
if (eFormat == ValidationFormat::OOXML && !aContentOUString.isEmpty())
{
SAL_WARN("test", "no summary line");
// check for validation errors here
sal_Int32 nIndex = aContentOUString.lastIndexOf(grand_total);
if (nIndex == -1)
{
SAL_WARN("test", "no summary line");
}
else
{
sal_Int32 nStartOfNumber = nIndex + grand_total.size();
std::u16string_view aNumber = aContentOUString.subView(nStartOfNumber);
sal_Int32 nErrors = o3tl::toInt32(aNumber);
OString aMsg
= "validation error in OOXML export: Errors: " + OString::number(nErrors);
if (nErrors)
{
SAL_WARN("test", aContentOUString);
}
CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), sal_Int32(0), nErrors);
}
}
else
else if (eFormat == ValidationFormat::ODF && !aContentOUString.isEmpty())
{
sal_Int32 nStartOfNumber = nIndex + grand_total.size();
std::u16string_view aNumber = aContentOUString.subView(nStartOfNumber);
sal_Int32 nErrors = o3tl::toInt32(aNumber);
OString aMsg = "validation error in OOXML export: Errors: " + OString::number(nErrors);
if (nErrors)
if (aContentOUString.indexOf("Error") != -1 || aContentOUString.indexOf("Fatal") != -1)
{
SAL_WARN("test", aContentOUString);
CPPUNIT_FAIL(aContentString.getStr());
}
CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), sal_Int32(0), nErrors);
}
CPPUNIT_ASSERT_EQUAL_MESSAGE(
OString("failed to execute: " + OUStringToOString(aCommand, RTL_TEXTENCODING_UTF8)
+ "\n" + OUStringToOString(aContentOUString, RTL_TEXTENCODING_UTF8))
.getStr(),
0, returnValue);
}
else if (eFormat == ValidationFormat::ODF && !aContentOUString.isEmpty())
{
if (aContentOUString.indexOf("Error") != -1 || aContentOUString.indexOf("Fatal") != -1)
{
SAL_WARN("test", aContentOUString);
CPPUNIT_FAIL(aContentString.getStr());
}
}
CPPUNIT_ASSERT_EQUAL_MESSAGE(
OString("failed to execute: " + OUStringToOString(aCommand, RTL_TEXTENCODING_UTF8) + "\n"
+ OUStringToOString(aContentOUString, RTL_TEXTENCODING_UTF8))
.getStr(),
0, returnValue);
#else
(void)rPath;
(void)eFormat;
#endif
}
@@ -347,8 +367,9 @@ void UnoApiTest::save(TestFilter eFilter, const uno::Sequence<beans::PropertyVal
css::uno::Reference<frame::XStorable> xStorable(mxComponent, css::uno::UNO_QUERY_THROW);
xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
if (!mbSkipValidation)
validate(maTempFile.GetFileName(), eFilter);
// FIXME: Don't validate pdf files by default for now
if (!mbSkipValidation && eFilter != TestFilter::PDF_WRITER)
validate(eFilter);
}
void UnoApiTest::saveAndReload(TestFilter eFilter,
+3
View File
@@ -2385,6 +2385,7 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf157816)
vcl::filter::PDFDocument aDocument;
loadFromFile(u"tdf157816.fodt");
save(TestFilter::PDF_WRITER, aMediaDescriptor.getAsConstPropertyValueList());
validate(TestFilter::PDF_WRITER);
// Parse the export result.
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
@@ -2787,6 +2788,7 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf157816Link)
vcl::filter::PDFDocument aDocument;
loadFromFile(u"LinkWithFly.fodt");
save(TestFilter::PDF_WRITER, aMediaDescriptor.getAsConstPropertyValueList());
validate(TestFilter::PDF_WRITER);
// Parse the export result.
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
@@ -3178,6 +3180,7 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf142806)
vcl::filter::PDFDocument aDocument;
loadFromFile(u"LinkPages.fodt");
save(TestFilter::PDF_WRITER, aMediaDescriptor.getAsConstPropertyValueList());
validate(TestFilter::PDF_WRITER);
// Parse the export result.
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
+10
View File
@@ -1473,6 +1473,7 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf139736)
vcl::filter::PDFDocument aDocument;
loadFromFile(u"tdf139736-1.odt");
save(TestFilter::PDF_WRITER, aMediaDescriptor.getAsConstPropertyValueList());
validate(TestFilter::PDF_WRITER);
// Parse the export result.
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
@@ -1779,6 +1780,7 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf149140)
vcl::filter::PDFDocument aDocument;
loadFromFile(u"TableTH_test_LibreOfficeWriter7.3.3_HeaderRow-HeadersInTopRow.fodt");
save(TestFilter::PDF_WRITER, aMediaDescriptor.getAsConstPropertyValueList());
validate(TestFilter::PDF_WRITER);
// Parse the export result.
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
@@ -1839,6 +1841,7 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest2, testNestedSection)
vcl::filter::PDFDocument aDocument;
loadFromFile(u"nestedsection.fodt");
save(TestFilter::PDF_WRITER, aMediaDescriptor.getAsConstPropertyValueList());
validate(TestFilter::PDF_WRITER);
// Parse the export result.
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
@@ -2976,6 +2979,7 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf154982)
vcl::filter::PDFDocument aDocument;
loadFromFile(u"tdf154982.odt");
save(TestFilter::PDF_WRITER, aMediaDescriptor.getAsConstPropertyValueList());
validate(TestFilter::PDF_WRITER);
// Parse the export result.
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
@@ -3386,6 +3390,7 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf135192)
vcl::filter::PDFDocument aDocument;
loadFromFile(u"tdf135192-1.fodp");
save(TestFilter::PDF_WRITER, aMediaDescriptor.getAsConstPropertyValueList());
validate(TestFilter::PDF_WRITER);
// Parse the export result.
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
@@ -3518,6 +3523,7 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf154955)
vcl::filter::PDFDocument aDocument;
loadFromFile(u"grouped-shape.fodt");
save(TestFilter::PDF_WRITER, aMediaDescriptor.getAsConstPropertyValueList());
validate(TestFilter::PDF_WRITER);
// Parse the export result.
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
@@ -3653,6 +3659,7 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf155190)
vcl::filter::PDFDocument aDocument;
loadFromFile(u"tdf155190.odt");
save(TestFilter::PDF_WRITER, aMediaDescriptor.getAsConstPropertyValueList());
validate(TestFilter::PDF_WRITER);
// Parse the export result.
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
@@ -3743,6 +3750,7 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest2, testMediaShapeAnnot)
vcl::filter::PDFDocument aDocument;
loadFromFile(u"vid.odt");
save(TestFilter::PDF_WRITER, aMediaDescriptor.getAsConstPropertyValueList());
validate(TestFilter::PDF_WRITER);
// Parse the export result.
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
@@ -3883,6 +3891,7 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest2, testFlyFrameHyperlinkAnnot)
vcl::filter::PDFDocument aDocument;
loadFromFile(u"image-hyperlink-alttext.fodt");
save(TestFilter::PDF_WRITER, aMediaDescriptor.getAsConstPropertyValueList());
validate(TestFilter::PDF_WRITER);
// Parse the export result.
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
@@ -4019,6 +4028,7 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest2, testFormControlAnnot)
vcl::filter::PDFDocument aDocument;
loadFromFile(u"formcontrol.fodt");
save(TestFilter::PDF_WRITER, aMediaDescriptor.getAsConstPropertyValueList());
validate(TestFilter::PDF_WRITER);
// Parse the export result.
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);