2023-10-30 16:09:34 +01:00
|
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
|
|
|
/*
|
|
|
|
|
* This file is part of the LibreOffice project.
|
|
|
|
|
*
|
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <sal/config.h>
|
|
|
|
|
|
|
|
|
|
#include <memory>
|
|
|
|
|
#include <string_view>
|
|
|
|
|
|
|
|
|
|
#include <config_fonts.h>
|
|
|
|
|
#include <osl/process.h>
|
|
|
|
|
|
|
|
|
|
#include <com/sun/star/frame/XStorable.hpp>
|
|
|
|
|
#include <com/sun/star/text/XDocumentIndexesSupplier.hpp>
|
|
|
|
|
#include <com/sun/star/util/XRefreshable.hpp>
|
|
|
|
|
#include <com/sun/star/beans/XPropertySet.hpp>
|
|
|
|
|
#include <com/sun/star/drawing/XShape.hpp>
|
|
|
|
|
#include <com/sun/star/text/XTextDocument.hpp>
|
|
|
|
|
#include <com/sun/star/document/XFilter.hpp>
|
|
|
|
|
#include <com/sun/star/document/XExporter.hpp>
|
|
|
|
|
#include <com/sun/star/io/XOutputStream.hpp>
|
|
|
|
|
|
|
|
|
|
#include <comphelper/scopeguard.hxx>
|
|
|
|
|
#include <comphelper/propertysequence.hxx>
|
|
|
|
|
#include <test/unoapi_test.hxx>
|
|
|
|
|
#include <unotools/mediadescriptor.hxx>
|
|
|
|
|
#include <unotools/tempfile.hxx>
|
|
|
|
|
#include <vcl/filter/pdfdocument.hxx>
|
|
|
|
|
#include <tools/zcodec.hxx>
|
|
|
|
|
#include <tools/XmlWalker.hxx>
|
|
|
|
|
#include <vcl/graphicfilter.hxx>
|
|
|
|
|
#include <basegfx/matrix/b2dhommatrix.hxx>
|
|
|
|
|
#include <unotools/streamwrap.hxx>
|
|
|
|
|
#include <rtl/math.hxx>
|
|
|
|
|
#include <o3tl/string_view.hxx>
|
|
|
|
|
|
|
|
|
|
#include <vcl/filter/PDFiumLibrary.hxx>
|
|
|
|
|
#include <comphelper/propertyvalue.hxx>
|
|
|
|
|
|
|
|
|
|
using namespace ::com::sun::star;
|
|
|
|
|
|
|
|
|
|
namespace
|
|
|
|
|
{
|
|
|
|
|
/// Tests the PDF export filter.
|
|
|
|
|
class PdfExportTest2 : public UnoApiTest
|
|
|
|
|
{
|
|
|
|
|
protected:
|
|
|
|
|
utl::MediaDescriptor aMediaDescriptor;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
PdfExportTest2()
|
|
|
|
|
: UnoApiTest("/vcl/qa/cppunit/pdfexport/data/")
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void saveAsPDF(std::u16string_view rFile);
|
|
|
|
|
void load(std::u16string_view rFile, vcl::filter::PDFDocument& rDocument,
|
|
|
|
|
bool bUseTaggedPDF = true);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void PdfExportTest2::saveAsPDF(std::u16string_view rFile)
|
|
|
|
|
{
|
|
|
|
|
// Import the bugdoc and export as PDF.
|
|
|
|
|
loadFromURL(rFile);
|
|
|
|
|
uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
|
|
|
|
|
xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PdfExportTest2::load(std::u16string_view rFile, vcl::filter::PDFDocument& rDocument,
|
|
|
|
|
bool bUseTaggedPDF)
|
|
|
|
|
{
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
uno::Sequence<beans::PropertyValue> aFilterData(
|
|
|
|
|
comphelper::InitPropertySequence({ { "UseTaggedPDF", uno::Any(bUseTaggedPDF) } }));
|
|
|
|
|
aMediaDescriptor["FilterData"] <<= aFilterData;
|
|
|
|
|
saveAsPDF(rFile);
|
|
|
|
|
|
|
|
|
|
// Parse the export result.
|
|
|
|
|
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
|
|
|
|
|
CPPUNIT_ASSERT(rDocument.Read(aStream));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf124272)
|
|
|
|
|
{
|
|
|
|
|
// Import the bugdoc and export as PDF.
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
load(u"tdf124272.odt", aDocument);
|
|
|
|
|
|
|
|
|
|
// The document has one page.
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
|
|
|
|
|
|
|
|
|
|
// The page has a stream.
|
|
|
|
|
vcl::filter::PDFObjectElement* pContents = aPages[0]->LookupObject("Contents");
|
|
|
|
|
CPPUNIT_ASSERT(pContents);
|
|
|
|
|
vcl::filter::PDFStreamElement* pStream = pContents->GetStream();
|
|
|
|
|
CPPUNIT_ASSERT(pStream);
|
|
|
|
|
SvMemoryStream& rObjectStream = pStream->GetMemory();
|
|
|
|
|
// Uncompress it.
|
|
|
|
|
SvMemoryStream aUncompressed;
|
|
|
|
|
ZCodec aZCodec;
|
|
|
|
|
aZCodec.BeginCompression();
|
|
|
|
|
rObjectStream.Seek(0);
|
|
|
|
|
aZCodec.Decompress(rObjectStream, aUncompressed);
|
|
|
|
|
CPPUNIT_ASSERT(aZCodec.EndCompression());
|
|
|
|
|
|
|
|
|
|
OString aBitmap("Q q 299.899 782.189 m\n"
|
|
|
|
|
"55.2 435.889 l 299.899 435.889 l 299.899 782.189 l\n"
|
|
|
|
|
"h");
|
|
|
|
|
|
|
|
|
|
auto pStart = static_cast<const char*>(aUncompressed.GetData());
|
|
|
|
|
const char* pEnd = pStart + aUncompressed.GetSize();
|
|
|
|
|
auto it = std::search(pStart, pEnd, aBitmap.getStr(), aBitmap.getStr() + aBitmap.getLength());
|
|
|
|
|
CPPUNIT_ASSERT(it != pEnd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf121615)
|
|
|
|
|
{
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
load(u"tdf121615.odt", aDocument);
|
|
|
|
|
|
|
|
|
|
// The document has one page.
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
|
|
|
|
|
|
|
|
|
|
// Get access to the only image on the only page.
|
|
|
|
|
vcl::filter::PDFObjectElement* pResources = aPages[0]->LookupObject("Resources");
|
|
|
|
|
CPPUNIT_ASSERT(pResources);
|
|
|
|
|
auto pXObjects
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFDictionaryElement*>(pResources->Lookup("XObject"));
|
|
|
|
|
CPPUNIT_ASSERT(pXObjects);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pXObjects->GetItems().size());
|
|
|
|
|
vcl::filter::PDFObjectElement* pXObject
|
|
|
|
|
= pXObjects->LookupObject(pXObjects->GetItems().begin()->first);
|
|
|
|
|
CPPUNIT_ASSERT(pXObject);
|
|
|
|
|
vcl::filter::PDFStreamElement* pStream = pXObject->GetStream();
|
|
|
|
|
CPPUNIT_ASSERT(pStream);
|
|
|
|
|
SvMemoryStream& rObjectStream = pStream->GetMemory();
|
|
|
|
|
|
|
|
|
|
// Load the embedded image.
|
|
|
|
|
rObjectStream.Seek(0);
|
|
|
|
|
GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
|
|
|
|
|
Graphic aGraphic;
|
|
|
|
|
sal_uInt16 format;
|
|
|
|
|
ErrCode bResult = rFilter.ImportGraphic(aGraphic, u"import", rObjectStream,
|
|
|
|
|
GRFILTER_FORMAT_DONTKNOW, &format);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, bResult);
|
|
|
|
|
|
|
|
|
|
// The image should be grayscale 8bit JPEG.
|
|
|
|
|
sal_uInt16 jpegFormat = rFilter.GetImportFormatNumberForShortName(JPG_SHORTNAME);
|
|
|
|
|
CPPUNIT_ASSERT(jpegFormat != GRFILTER_FORMAT_NOTFOUND);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(jpegFormat, format);
|
|
|
|
|
BitmapEx aBitmap = aGraphic.GetBitmapEx();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(tools::Long(200), aBitmap.GetSizePixel().Width());
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(tools::Long(300), aBitmap.GetSizePixel().Height());
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(vcl::PixelFormat::N8_BPP, aBitmap.getPixelFormat());
|
|
|
|
|
// tdf#121615 was caused by broken handling of data width with 8bit color,
|
|
|
|
|
// so the test image has some black in the bottomright corner, check it's there
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(COL_WHITE, aBitmap.GetPixelColor(0, 0));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(COL_WHITE, aBitmap.GetPixelColor(0, 299));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(COL_WHITE, aBitmap.GetPixelColor(199, 0));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(COL_BLACK, aBitmap.GetPixelColor(199, 299));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf141171)
|
|
|
|
|
{
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
load(u"tdf141171.odt", aDocument);
|
|
|
|
|
|
|
|
|
|
// The document has one page.
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
|
|
|
|
|
|
|
|
|
|
// Get access to the only image on the only page.
|
|
|
|
|
vcl::filter::PDFObjectElement* pResources = aPages[0]->LookupObject("Resources");
|
|
|
|
|
CPPUNIT_ASSERT(pResources);
|
|
|
|
|
auto pXObjects
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFDictionaryElement*>(pResources->Lookup("XObject"));
|
|
|
|
|
CPPUNIT_ASSERT(pXObjects);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pXObjects->GetItems().size());
|
|
|
|
|
vcl::filter::PDFObjectElement* pXObject
|
|
|
|
|
= pXObjects->LookupObject(pXObjects->GetItems().begin()->first);
|
|
|
|
|
CPPUNIT_ASSERT(pXObject);
|
|
|
|
|
vcl::filter::PDFStreamElement* pStream = pXObject->GetStream();
|
|
|
|
|
CPPUNIT_ASSERT(pStream);
|
|
|
|
|
SvMemoryStream& rObjectStream = pStream->GetMemory();
|
|
|
|
|
|
|
|
|
|
// Load the embedded image.
|
|
|
|
|
rObjectStream.Seek(0);
|
|
|
|
|
GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
|
|
|
|
|
Graphic aGraphic;
|
|
|
|
|
sal_uInt16 format;
|
|
|
|
|
ErrCode bResult = rFilter.ImportGraphic(aGraphic, u"import", rObjectStream,
|
|
|
|
|
GRFILTER_FORMAT_DONTKNOW, &format);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, bResult);
|
|
|
|
|
|
|
|
|
|
// The image should be grayscale 8bit JPEG.
|
|
|
|
|
sal_uInt16 jpegFormat = rFilter.GetImportFormatNumberForShortName(JPG_SHORTNAME);
|
|
|
|
|
CPPUNIT_ASSERT(jpegFormat != GRFILTER_FORMAT_NOTFOUND);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(jpegFormat, format);
|
|
|
|
|
BitmapEx aBitmap = aGraphic.GetBitmapEx();
|
|
|
|
|
Size aSize = aBitmap.GetSizePixel();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(tools::Long(878), aSize.Width());
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(tools::Long(127), aSize.Height());
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(vcl::PixelFormat::N8_BPP, aBitmap.getPixelFormat());
|
|
|
|
|
|
|
|
|
|
for (tools::Long nX = 0; nX < aSize.Width(); ++nX)
|
|
|
|
|
{
|
|
|
|
|
for (tools::Long nY = 0; nY < aSize.Height(); ++nY)
|
|
|
|
|
{
|
|
|
|
|
// Check all pixels in the image are white
|
|
|
|
|
// Without the fix in place, this test would have failed with
|
|
|
|
|
// - Expected: Color: R:255 G:255 B:255 A:0
|
|
|
|
|
// - Actual : Color: R:0 G:0 B:0 A:0
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(COL_WHITE, aBitmap.GetPixelColor(nX, nY));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf129085)
|
|
|
|
|
{
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
load(u"tdf129085.docx", aDocument);
|
|
|
|
|
|
|
|
|
|
// The document has one page.
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
|
|
|
|
|
|
|
|
|
|
// Get access to the only image on the only page.
|
|
|
|
|
vcl::filter::PDFObjectElement* pResources = aPages[0]->LookupObject("Resources");
|
|
|
|
|
CPPUNIT_ASSERT(pResources);
|
|
|
|
|
auto pXObjects
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFDictionaryElement*>(pResources->Lookup("XObject"));
|
|
|
|
|
|
|
|
|
|
// Without the fix in place, this test would have failed here
|
|
|
|
|
CPPUNIT_ASSERT(pXObjects);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pXObjects->GetItems().size());
|
|
|
|
|
vcl::filter::PDFObjectElement* pXObject
|
|
|
|
|
= pXObjects->LookupObject(pXObjects->GetItems().begin()->first);
|
|
|
|
|
CPPUNIT_ASSERT(pXObject);
|
|
|
|
|
vcl::filter::PDFStreamElement* pStream = pXObject->GetStream();
|
|
|
|
|
CPPUNIT_ASSERT(pStream);
|
|
|
|
|
SvMemoryStream& rObjectStream = pStream->GetMemory();
|
|
|
|
|
|
|
|
|
|
// Load the embedded image.
|
|
|
|
|
rObjectStream.Seek(0);
|
|
|
|
|
GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
|
|
|
|
|
Graphic aGraphic;
|
|
|
|
|
sal_uInt16 format;
|
|
|
|
|
ErrCode bResult = rFilter.ImportGraphic(aGraphic, u"import", rObjectStream,
|
|
|
|
|
GRFILTER_FORMAT_DONTKNOW, &format);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, bResult);
|
|
|
|
|
|
|
|
|
|
sal_uInt16 jpegFormat = rFilter.GetImportFormatNumberForShortName(JPG_SHORTNAME);
|
|
|
|
|
CPPUNIT_ASSERT(jpegFormat != GRFILTER_FORMAT_NOTFOUND);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(jpegFormat, format);
|
|
|
|
|
BitmapEx aBitmap = aGraphic.GetBitmapEx();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(tools::Long(884), aBitmap.GetSizePixel().Width());
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(tools::Long(925), aBitmap.GetSizePixel().Height());
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(vcl::PixelFormat::N24_BPP, aBitmap.getPixelFormat());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTocLink)
|
|
|
|
|
{
|
|
|
|
|
// Load the Writer document.
|
|
|
|
|
loadFromURL(u"toc-link.fodt");
|
|
|
|
|
|
|
|
|
|
// Update the ToC.
|
|
|
|
|
uno::Reference<text::XDocumentIndexesSupplier> xDocumentIndexesSupplier(mxComponent,
|
|
|
|
|
uno::UNO_QUERY);
|
|
|
|
|
CPPUNIT_ASSERT(xDocumentIndexesSupplier.is());
|
|
|
|
|
|
|
|
|
|
uno::Reference<util::XRefreshable> xToc(
|
|
|
|
|
xDocumentIndexesSupplier->getDocumentIndexes()->getByIndex(0), uno::UNO_QUERY);
|
|
|
|
|
CPPUNIT_ASSERT(xToc.is());
|
|
|
|
|
|
|
|
|
|
xToc->refresh();
|
|
|
|
|
|
|
|
|
|
// Save as PDF.
|
|
|
|
|
save("writer_pdf_Export");
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
|
|
|
|
|
CPPUNIT_ASSERT(pPdfPage);
|
|
|
|
|
|
|
|
|
|
// Ensure there is a link on the first page (in the ToC).
|
|
|
|
|
// Without the accompanying fix in place, this test would have failed, as the page contained no
|
|
|
|
|
// links.
|
|
|
|
|
CPPUNIT_ASSERT(pPdfPage->hasLinks());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testReduceSmallImage)
|
|
|
|
|
{
|
|
|
|
|
// Load the Writer document.
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
saveAsPDF(u"reduce-small-image.fodt");
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
|
|
|
|
|
CPPUNIT_ASSERT(pPdfPage);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getObjectCount());
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPdfPage->getObject(0);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Image, pPageObject->getType());
|
|
|
|
|
|
|
|
|
|
// Make sure we don't scale down a tiny bitmap.
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumBitmap> pBitmap = pPageObject->getImageBitmap();
|
|
|
|
|
CPPUNIT_ASSERT(pBitmap);
|
|
|
|
|
int nWidth = pBitmap->getWidth();
|
|
|
|
|
int nHeight = pBitmap->getHeight();
|
|
|
|
|
// Without the accompanying fix in place, this test would have failed with:
|
|
|
|
|
// - Expected: 16
|
|
|
|
|
// - Actual : 6
|
|
|
|
|
// i.e. the image was scaled down to 300 DPI, even if it had tiny size.
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(16, nWidth);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(16, nHeight);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf114256)
|
|
|
|
|
{
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("calc_pdf_Export");
|
|
|
|
|
saveAsPDF(u"tdf114256.ods");
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
|
|
|
|
|
CPPUNIT_ASSERT(pPdfPage);
|
|
|
|
|
|
|
|
|
|
// Without the fix in place, this test would have failed with
|
|
|
|
|
// - Expected: 13
|
|
|
|
|
// - Actual : 0
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(13, pPdfPage->getObjectCount());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf150931)
|
|
|
|
|
{
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("calc_pdf_Export");
|
|
|
|
|
saveAsPDF(u"tdf150931.ods");
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
|
|
|
|
|
CPPUNIT_ASSERT(pPdfPage);
|
|
|
|
|
|
|
|
|
|
int nPageObjectCount = pPdfPage->getObjectCount();
|
|
|
|
|
// Without the fix in place, this test would have failed with
|
|
|
|
|
// - Expected: 15
|
|
|
|
|
// - Actual : 16
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(16, nPageObjectCount);
|
|
|
|
|
|
|
|
|
|
int nYellowPathCount = 0;
|
|
|
|
|
int nBlackPathCount = 0;
|
|
|
|
|
int nGrayPathCount = 0;
|
|
|
|
|
int nRedPathCount = 0;
|
|
|
|
|
for (int i = 0; i < nPageObjectCount; ++i)
|
|
|
|
|
{
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPageObject> pPdfPageObject = pPdfPage->getObject(i);
|
|
|
|
|
if (pPdfPageObject->getType() != vcl::pdf::PDFPageObjectType::Path)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
int nSegments = pPdfPageObject->getPathSegmentCount();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(5, nSegments);
|
|
|
|
|
|
|
|
|
|
if (pPdfPageObject->getFillColor() == COL_YELLOW)
|
|
|
|
|
++nYellowPathCount;
|
|
|
|
|
else if (pPdfPageObject->getFillColor() == COL_BLACK)
|
|
|
|
|
++nBlackPathCount;
|
|
|
|
|
else if (pPdfPageObject->getFillColor() == COL_GRAY)
|
|
|
|
|
++nGrayPathCount;
|
|
|
|
|
else if (pPdfPageObject->getFillColor() == COL_LIGHTRED)
|
|
|
|
|
++nRedPathCount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(3, nYellowPathCount);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(3, nRedPathCount);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(3, nGrayPathCount);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(3, nBlackPathCount);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf147027)
|
|
|
|
|
{
|
|
|
|
|
// FIXME: the DPI check should be removed when either (1) the test is fixed to work with
|
|
|
|
|
// non-default DPI; or (2) unit tests on Windows are made to use svp VCL plugin.
|
|
|
|
|
if (!IsDefaultDPI())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Load the Calc document.
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("calc_pdf_Export");
|
|
|
|
|
saveAsPDF(u"tdf147027.ods");
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
|
|
|
|
|
CPPUNIT_ASSERT(pPdfPage);
|
|
|
|
|
|
|
|
|
|
// Without the fix in place, this test would have failed with
|
|
|
|
|
// - Expected: 778
|
|
|
|
|
// - Actual : 40
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(778, pPdfPage->getObjectCount());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf135346)
|
|
|
|
|
{
|
|
|
|
|
// Load the Calc document.
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("calc_pdf_Export");
|
|
|
|
|
saveAsPDF(u"tdf135346.ods");
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
|
|
|
|
|
CPPUNIT_ASSERT(pPdfPage);
|
|
|
|
|
|
|
|
|
|
// Without the fix in place, this test would have failed with
|
|
|
|
|
// - Expected: 56
|
|
|
|
|
// - Actual : 0
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(56, pPdfPage->getObjectCount());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf147164)
|
|
|
|
|
{
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("impress_pdf_Export");
|
|
|
|
|
saveAsPDF(u"tdf147164.odp");
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(2, pPdfDocument->getPageCount());
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/1);
|
|
|
|
|
CPPUNIT_ASSERT(pPdfPage);
|
|
|
|
|
|
|
|
|
|
// Without the fix in place, this test would have failed with
|
|
|
|
|
// - Expected: 22
|
|
|
|
|
// - Actual : 16
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(22, pPdfPage->getObjectCount());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testReduceImage)
|
|
|
|
|
{
|
|
|
|
|
// Load the Writer document.
|
|
|
|
|
loadFromURL(u"reduce-image.fodt");
|
|
|
|
|
|
|
|
|
|
// Save as PDF.
|
|
|
|
|
uno::Reference<css::lang::XMultiServiceFactory> xFactory = getMultiServiceFactory();
|
|
|
|
|
uno::Reference<document::XFilter> xFilter(
|
|
|
|
|
xFactory->createInstance("com.sun.star.document.PDFFilter"), uno::UNO_QUERY);
|
|
|
|
|
uno::Reference<document::XExporter> xExporter(xFilter, uno::UNO_QUERY);
|
|
|
|
|
xExporter->setSourceDocument(mxComponent);
|
|
|
|
|
|
|
|
|
|
SvFileStream aOutputStream(maTempFile.GetURL(), StreamMode::WRITE);
|
|
|
|
|
uno::Reference<io::XOutputStream> xOutputStream(new utl::OStreamWrapper(aOutputStream));
|
|
|
|
|
|
|
|
|
|
uno::Sequence<beans::PropertyValue> aFilterData(
|
|
|
|
|
comphelper::InitPropertySequence({ { "ReduceImageResolution", uno::Any(false) } }));
|
|
|
|
|
|
|
|
|
|
// This is intentionally in an "unlucky" order, output stream comes before filter data.
|
|
|
|
|
uno::Sequence<beans::PropertyValue> aDescriptor(comphelper::InitPropertySequence({
|
|
|
|
|
{ "FilterName", uno::Any(OUString("writer_pdf_Export")) },
|
|
|
|
|
{ "OutputStream", uno::Any(xOutputStream) },
|
|
|
|
|
{ "FilterData", uno::Any(aFilterData) },
|
|
|
|
|
}));
|
|
|
|
|
xFilter->filter(aDescriptor);
|
|
|
|
|
aOutputStream.Close();
|
|
|
|
|
|
|
|
|
|
// Parse the PDF: get the image.
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
|
|
|
|
|
CPPUNIT_ASSERT(pPdfPage);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getObjectCount());
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPdfPage->getObject(0);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Image, pPageObject->getType());
|
|
|
|
|
|
|
|
|
|
// Make sure we don't scale down a bitmap.
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumBitmap> pBitmap = pPageObject->getImageBitmap();
|
|
|
|
|
CPPUNIT_ASSERT(pBitmap);
|
|
|
|
|
int nWidth = pBitmap->getWidth();
|
|
|
|
|
int nHeight = pBitmap->getHeight();
|
|
|
|
|
// Without the accompanying fix in place, this test would have failed with:
|
|
|
|
|
// - Expected: 160
|
|
|
|
|
// - Actual : 6
|
|
|
|
|
// i.e. the image was scaled down even with ReduceImageResolution=false.
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(160, nWidth);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(160, nHeight);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testLinkWrongPage)
|
|
|
|
|
{
|
|
|
|
|
// Import the bugdoc and export as PDF.
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("impress_pdf_Export");
|
|
|
|
|
saveAsPDF(u"link-wrong-page.odp");
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
|
|
|
|
|
|
|
|
|
|
// The document has 2 pages.
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(2, pPdfDocument->getPageCount());
|
|
|
|
|
|
|
|
|
|
// First page should have 1 link (2nd slide, 1st was hidden).
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
|
|
|
|
|
CPPUNIT_ASSERT(pPdfPage);
|
|
|
|
|
|
|
|
|
|
// Without the accompanying fix in place, this test would have failed, as the link of the first
|
|
|
|
|
// page went to the second page due to the hidden first slide.
|
|
|
|
|
CPPUNIT_ASSERT(pPdfPage->hasLinks());
|
|
|
|
|
|
|
|
|
|
// Second page should have no links (3rd slide).
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage2 = pPdfDocument->openPage(/*nIndex=*/1);
|
|
|
|
|
CPPUNIT_ASSERT(pPdfPage2);
|
|
|
|
|
CPPUNIT_ASSERT(!pPdfPage2->hasLinks());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testLinkWrongPagePartial)
|
|
|
|
|
{
|
|
|
|
|
// Given a Draw document with 3 pages, a link on the 2nd page:
|
|
|
|
|
// When exporting that the 2nd and 3rd page to pdf:
|
|
|
|
|
uno::Sequence<beans::PropertyValue> aFilterData = {
|
|
|
|
|
comphelper::makePropertyValue("PageRange", OUString("2-3")),
|
|
|
|
|
};
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("draw_pdf_Export");
|
|
|
|
|
aMediaDescriptor["FilterData"] <<= aFilterData;
|
|
|
|
|
saveAsPDF(u"link-wrong-page-partial.odg");
|
|
|
|
|
|
|
|
|
|
// Then make sure the we have a link on the 1st page, but not on the 2nd one:
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(2, pPdfDocument->getPageCount());
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
|
|
|
|
|
CPPUNIT_ASSERT(pPdfPage);
|
|
|
|
|
// Without the accompanying fix in place, this test would have failed, as the link was on the
|
|
|
|
|
// 2nd page instead.
|
|
|
|
|
CPPUNIT_ASSERT(pPdfPage->hasLinks());
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage2 = pPdfDocument->openPage(/*nIndex=*/1);
|
|
|
|
|
CPPUNIT_ASSERT(pPdfPage2);
|
|
|
|
|
CPPUNIT_ASSERT(!pPdfPage2->hasLinks());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testPageRange)
|
|
|
|
|
{
|
|
|
|
|
// Given a document with 3 pages:
|
|
|
|
|
// When exporting that document to PDF, skipping the first page:
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("draw_pdf_Export");
|
|
|
|
|
aMediaDescriptor["FilterOptions"]
|
|
|
|
|
<<= OUString("{\"PageRange\":{\"type\":\"string\",\"value\":\"2-\"}}");
|
|
|
|
|
saveAsPDF(u"link-wrong-page-partial.odg");
|
|
|
|
|
|
|
|
|
|
// Then make sure the resulting PDF has 2 pages:
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
|
|
|
|
|
// Without the accompanying fix in place, this test would have failed with:
|
|
|
|
|
// - Expected: 2
|
|
|
|
|
// - Actual : 3
|
|
|
|
|
// i.e. FilterOptions was ignored.
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(2, pPdfDocument->getPageCount());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testLargePage)
|
|
|
|
|
{
|
|
|
|
|
// Import the bugdoc and export as PDF.
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("draw_pdf_Export");
|
|
|
|
|
saveAsPDF(u"6m-wide.odg");
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
|
|
|
|
|
|
|
|
|
|
// The document has 1 page.
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
|
|
|
|
|
// Check the value (not the unit) of the page size.
|
|
|
|
|
basegfx::B2DSize aSize = pPdfDocument->getPageSize(0);
|
|
|
|
|
// Without the accompanying fix in place, this test would have failed with:
|
|
|
|
|
// - Expected: 8503.94
|
|
|
|
|
// - Actual : 17007.875
|
|
|
|
|
// i.e. the value for 600 cm was larger than the 14 400 limit set in the spec.
|
|
|
|
|
CPPUNIT_ASSERT_DOUBLES_EQUAL(8503.94, aSize.getWidth(), 0.01);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testPdfImageResourceInlineXObjectRef)
|
|
|
|
|
{
|
|
|
|
|
// Create an empty document.
|
|
|
|
|
mxComponent = loadFromDesktop("private:factory/swriter");
|
|
|
|
|
uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
|
|
|
|
|
uno::Reference<text::XText> xText = xTextDocument->getText();
|
|
|
|
|
uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
|
|
|
|
|
|
|
|
|
|
// Insert the PDF image.
|
|
|
|
|
uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
|
|
|
|
|
uno::Reference<beans::XPropertySet> xGraphicObject(
|
|
|
|
|
xFactory->createInstance("com.sun.star.text.TextGraphicObject"), uno::UNO_QUERY);
|
|
|
|
|
OUString aURL = createFileURL(u"pdf-image-resource-inline-xobject-ref.pdf");
|
|
|
|
|
xGraphicObject->setPropertyValue("GraphicURL", uno::Any(aURL));
|
|
|
|
|
uno::Reference<drawing::XShape> xShape(xGraphicObject, uno::UNO_QUERY);
|
|
|
|
|
xShape->setSize(awt::Size(1000, 1000));
|
|
|
|
|
uno::Reference<text::XTextContent> xTextContent(xGraphicObject, uno::UNO_QUERY);
|
|
|
|
|
xText->insertTextContent(xCursor->getStart(), xTextContent, /*bAbsorb=*/false);
|
|
|
|
|
|
|
|
|
|
// Save as PDF.
|
|
|
|
|
uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
|
|
|
|
|
|
|
|
|
|
// Parse the export result.
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
|
|
|
|
|
|
|
|
|
|
// Make sure that the page -> form -> form has a child image.
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
|
|
|
|
|
CPPUNIT_ASSERT(pPdfPage);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getObjectCount());
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPdfPage->getObject(0);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form, pPageObject->getType());
|
|
|
|
|
// 2: white background and the actual object.
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(2, pPageObject->getFormObjectCount());
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPageObject> pFormObject = pPageObject->getFormObject(1);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form, pFormObject->getType());
|
|
|
|
|
// Without the accompanying fix in place, this test would have failed with:
|
|
|
|
|
// - Expected: 1
|
|
|
|
|
// - Actual : 0
|
|
|
|
|
// i.e. the sub-form was missing its image.
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pFormObject->getFormObjectCount());
|
|
|
|
|
|
|
|
|
|
// Check if the inner form object (original page object in the pdf image) has the correct
|
|
|
|
|
// rotation.
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPageObject> pInnerFormObject = pFormObject->getFormObject(0);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form, pInnerFormObject->getType());
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pInnerFormObject->getFormObjectCount());
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPageObject> pImage = pInnerFormObject->getFormObject(0);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Image, pImage->getType());
|
|
|
|
|
basegfx::B2DHomMatrix aMat = pInnerFormObject->getMatrix();
|
|
|
|
|
basegfx::B2DTuple aScale;
|
|
|
|
|
basegfx::B2DTuple aTranslate;
|
|
|
|
|
double fRotate = 0;
|
|
|
|
|
double fShearX = 0;
|
|
|
|
|
aMat.decompose(aScale, aTranslate, fRotate, fShearX);
|
|
|
|
|
int nRotateDeg = basegfx::rad2deg(fRotate);
|
|
|
|
|
// Without the accompanying fix in place, this test would have failed with:
|
|
|
|
|
// - Expected: -90
|
|
|
|
|
// - Actual : 0
|
|
|
|
|
// i.e. rotation was lost on pdf export.
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(-90, nRotateDeg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testDefaultVersion)
|
|
|
|
|
{
|
|
|
|
|
// Create an empty document.
|
|
|
|
|
mxComponent = loadFromDesktop("private:factory/swriter");
|
|
|
|
|
|
|
|
|
|
// Save as PDF.
|
|
|
|
|
uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
|
|
|
|
|
|
|
|
|
|
// Parse the export result.
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
|
|
|
|
|
int nFileVersion = pPdfDocument->getFileVersion();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(17, nFileVersion);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testVersion15)
|
|
|
|
|
{
|
|
|
|
|
// Create an empty document.
|
|
|
|
|
mxComponent = loadFromDesktop("private:factory/swriter");
|
|
|
|
|
|
|
|
|
|
// Save as PDF.
|
|
|
|
|
uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
|
|
|
|
|
uno::Sequence<beans::PropertyValue> aFilterData(comphelper::InitPropertySequence(
|
|
|
|
|
{ { "SelectPdfVersion", uno::Any(static_cast<sal_Int32>(15)) } }));
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
aMediaDescriptor["FilterData"] <<= aFilterData;
|
|
|
|
|
xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
|
|
|
|
|
|
|
|
|
|
// Parse the export result.
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
|
|
|
|
|
int nFileVersion = pPdfDocument->getFileVersion();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(15, nFileVersion);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check round-trip of importing and exporting the PDF with PDFium filter,
|
|
|
|
|
// which imports the PDF document as multiple PDFs as graphic object.
|
|
|
|
|
// Each page in the document has one PDF graphic object which content is
|
|
|
|
|
// the corresponding page in the PDF. When such a document is exported,
|
|
|
|
|
// the PDF graphic gets embedded into the exported PDF document (as a
|
|
|
|
|
// Form XObject).
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testMultiPagePDF)
|
|
|
|
|
{
|
|
|
|
|
// setenv only works on unix based systems
|
|
|
|
|
#ifndef _WIN32
|
|
|
|
|
// We need to enable PDFium import (and make sure to disable after the test)
|
|
|
|
|
bool bResetEnvVar = false;
|
|
|
|
|
if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
|
|
|
|
|
{
|
|
|
|
|
bResetEnvVar = true;
|
|
|
|
|
setenv("LO_IMPORT_USE_PDFIUM", "1", false);
|
|
|
|
|
}
|
|
|
|
|
comphelper::ScopeGuard aPDFiumEnvVarGuard([&]() {
|
|
|
|
|
if (bResetEnvVar)
|
|
|
|
|
unsetenv("LO_IMPORT_USE_PDFIUM");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Load the PDF and save as PDF
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
load(u"SimpleMultiPagePDF.pdf", aDocument);
|
|
|
|
|
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aPages.size());
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFObjectElement* pResources = aPages[0]->LookupObject("Resources");
|
|
|
|
|
CPPUNIT_ASSERT(pResources);
|
|
|
|
|
|
|
|
|
|
auto pXObjects
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFDictionaryElement*>(pResources->Lookup("XObject"));
|
|
|
|
|
CPPUNIT_ASSERT(pXObjects);
|
|
|
|
|
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3),
|
|
|
|
|
pXObjects->GetItems().size()); // 3 PDFs as Form XObjects
|
|
|
|
|
|
|
|
|
|
std::vector<OString> rIDs;
|
|
|
|
|
for (auto const& rPair : pXObjects->GetItems())
|
|
|
|
|
{
|
|
|
|
|
rIDs.push_back(rPair.first);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Let's check the embedded PDF pages - just make sure the size differs,
|
|
|
|
|
// which should indicate we don't have 3 times the same page.
|
|
|
|
|
|
|
|
|
|
{ // embedded PDF page 1
|
|
|
|
|
vcl::filter::PDFObjectElement* pXObject1 = pXObjects->LookupObject(rIDs[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pXObject1);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Im21"), rIDs[0]);
|
|
|
|
|
|
|
|
|
|
auto pSubtype1 = dynamic_cast<vcl::filter::PDFNameElement*>(pXObject1->Lookup("Subtype"));
|
|
|
|
|
CPPUNIT_ASSERT(pSubtype1);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Form"), pSubtype1->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pXObjectResources
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFDictionaryElement*>(pXObject1->Lookup("Resources"));
|
|
|
|
|
CPPUNIT_ASSERT(pXObjectResources);
|
|
|
|
|
auto pXObjectForms = dynamic_cast<vcl::filter::PDFDictionaryElement*>(
|
|
|
|
|
pXObjectResources->LookupElement("XObject"));
|
|
|
|
|
CPPUNIT_ASSERT(pXObjectForms);
|
|
|
|
|
vcl::filter::PDFObjectElement* pForm
|
|
|
|
|
= pXObjectForms->LookupObject(pXObjectForms->GetItems().begin()->first);
|
|
|
|
|
CPPUNIT_ASSERT(pForm);
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFStreamElement* pStream = pForm->GetStream();
|
|
|
|
|
CPPUNIT_ASSERT(pStream);
|
|
|
|
|
SvMemoryStream& rObjectStream = pStream->GetMemory();
|
|
|
|
|
rObjectStream.Seek(STREAM_SEEK_TO_BEGIN);
|
|
|
|
|
|
|
|
|
|
// Just check that the size of the page stream is what is expected.
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(sal_uInt64(230), rObjectStream.remainingSize());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{ // embedded PDF page 2
|
|
|
|
|
vcl::filter::PDFObjectElement* pXObject2 = pXObjects->LookupObject(rIDs[1]);
|
|
|
|
|
CPPUNIT_ASSERT(pXObject2);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Im27"), rIDs[1]);
|
|
|
|
|
|
|
|
|
|
auto pSubtype2 = dynamic_cast<vcl::filter::PDFNameElement*>(pXObject2->Lookup("Subtype"));
|
|
|
|
|
CPPUNIT_ASSERT(pSubtype2);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Form"), pSubtype2->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pXObjectResources
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFDictionaryElement*>(pXObject2->Lookup("Resources"));
|
|
|
|
|
CPPUNIT_ASSERT(pXObjectResources);
|
|
|
|
|
auto pXObjectForms = dynamic_cast<vcl::filter::PDFDictionaryElement*>(
|
|
|
|
|
pXObjectResources->LookupElement("XObject"));
|
|
|
|
|
CPPUNIT_ASSERT(pXObjectForms);
|
|
|
|
|
vcl::filter::PDFObjectElement* pForm
|
|
|
|
|
= pXObjectForms->LookupObject(pXObjectForms->GetItems().begin()->first);
|
|
|
|
|
CPPUNIT_ASSERT(pForm);
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFStreamElement* pStream = pForm->GetStream();
|
|
|
|
|
CPPUNIT_ASSERT(pStream);
|
|
|
|
|
SvMemoryStream& rObjectStream = pStream->GetMemory();
|
|
|
|
|
rObjectStream.Seek(STREAM_SEEK_TO_BEGIN);
|
|
|
|
|
|
|
|
|
|
// Just check that the size of the page stream is what is expected
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(sal_uInt64(309), rObjectStream.remainingSize());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{ // embedded PDF page 3
|
|
|
|
|
vcl::filter::PDFObjectElement* pXObject3 = pXObjects->LookupObject(rIDs[2]);
|
|
|
|
|
CPPUNIT_ASSERT(pXObject3);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Im5"), rIDs[2]);
|
|
|
|
|
|
|
|
|
|
auto pSubtype3 = dynamic_cast<vcl::filter::PDFNameElement*>(pXObject3->Lookup("Subtype"));
|
|
|
|
|
CPPUNIT_ASSERT(pSubtype3);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Form"), pSubtype3->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pXObjectResources
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFDictionaryElement*>(pXObject3->Lookup("Resources"));
|
|
|
|
|
CPPUNIT_ASSERT(pXObjectResources);
|
|
|
|
|
auto pXObjectForms = dynamic_cast<vcl::filter::PDFDictionaryElement*>(
|
|
|
|
|
pXObjectResources->LookupElement("XObject"));
|
|
|
|
|
CPPUNIT_ASSERT(pXObjectForms);
|
|
|
|
|
vcl::filter::PDFObjectElement* pForm
|
|
|
|
|
= pXObjectForms->LookupObject(pXObjectForms->GetItems().begin()->first);
|
|
|
|
|
CPPUNIT_ASSERT(pForm);
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFStreamElement* pStream = pForm->GetStream();
|
|
|
|
|
CPPUNIT_ASSERT(pStream);
|
|
|
|
|
SvMemoryStream& rObjectStream = pStream->GetMemory();
|
|
|
|
|
rObjectStream.Seek(STREAM_SEEK_TO_BEGIN);
|
|
|
|
|
|
|
|
|
|
// Just check that the size of the page stream is what is expected
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(sal_uInt64(193), rObjectStream.remainingSize());
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testFormFontName)
|
|
|
|
|
{
|
|
|
|
|
// Import the bugdoc and export as PDF.
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
saveAsPDF(u"form-font-name.odt");
|
|
|
|
|
|
|
|
|
|
// Parse the export result with pdfium.
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
|
|
|
|
|
|
|
|
|
|
// The document has one page.
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
|
|
|
|
|
CPPUNIT_ASSERT(pPdfPage);
|
|
|
|
|
|
|
|
|
|
// The page has one annotation.
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getAnnotationCount());
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumAnnotation> pAnnot = pPdfPage->getAnnotation(0);
|
|
|
|
|
|
|
|
|
|
// Examine the default appearance.
|
|
|
|
|
CPPUNIT_ASSERT(pAnnot->hasKey("DA"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFObjectType::String, pAnnot->getValueType("DA"));
|
|
|
|
|
OUString aDA = pAnnot->getString("DA");
|
|
|
|
|
|
|
|
|
|
// Without the accompanying fix in place, this test would have failed with:
|
|
|
|
|
// - Expected: 0 0 0 rg /TiRo 12 Tf
|
|
|
|
|
// - Actual : 0 0 0 rg /F2 12 Tf
|
|
|
|
|
// i.e. Liberation Serif was exposed as a form font as-is, without picking the closest built-in
|
|
|
|
|
// font.
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OUString("0 0 0 rg /TiRo 12 Tf"), aDA);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check we don't have duplicated objects when we reexport the PDF multiple
|
|
|
|
|
// times or the size will exponentially increase over time.
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testReexportPDF)
|
|
|
|
|
{
|
|
|
|
|
// setenv only works on unix based systems
|
|
|
|
|
#ifndef _WIN32
|
|
|
|
|
// We need to enable PDFium import (and make sure to disable after the test)
|
|
|
|
|
bool bResetEnvVar = false;
|
|
|
|
|
if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
|
|
|
|
|
{
|
|
|
|
|
bResetEnvVar = true;
|
|
|
|
|
setenv("LO_IMPORT_USE_PDFIUM", "1", false);
|
|
|
|
|
}
|
|
|
|
|
comphelper::ScopeGuard aPDFiumEnvVarGuard([&]() {
|
|
|
|
|
if (bResetEnvVar)
|
|
|
|
|
unsetenv("LO_IMPORT_USE_PDFIUM");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Load the PDF and save as PDF
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
load(u"PDFWithImages.pdf", aDocument);
|
|
|
|
|
|
|
|
|
|
// Assert that the XObject in the page resources dictionary is a reference XObject.
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
|
|
|
|
|
// The document has 2 pages.
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(2), aPages.size());
|
|
|
|
|
|
|
|
|
|
// PAGE 1
|
|
|
|
|
{
|
|
|
|
|
vcl::filter::PDFObjectElement* pResources = aPages[0]->LookupObject("Resources");
|
|
|
|
|
CPPUNIT_ASSERT(pResources);
|
|
|
|
|
|
|
|
|
|
auto pXObjects
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFDictionaryElement*>(pResources->Lookup("XObject"));
|
|
|
|
|
CPPUNIT_ASSERT(pXObjects);
|
|
|
|
|
|
|
|
|
|
std::vector<OString> rIDs;
|
|
|
|
|
for (auto const& rPair : pXObjects->GetItems())
|
|
|
|
|
rIDs.push_back(rPair.first);
|
|
|
|
|
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(2), rIDs.size());
|
|
|
|
|
|
|
|
|
|
std::vector<int> aBitmapRefs1;
|
|
|
|
|
std::vector<int> aBitmapRefs2;
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
// FORM object 1
|
|
|
|
|
OString aID = rIDs[0];
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Im14"), aID);
|
|
|
|
|
vcl::filter::PDFObjectElement* pXObject = pXObjects->LookupObject(aID);
|
|
|
|
|
CPPUNIT_ASSERT(pXObject);
|
|
|
|
|
|
|
|
|
|
auto pSubtype = dynamic_cast<vcl::filter::PDFNameElement*>(pXObject->Lookup("Subtype"));
|
|
|
|
|
CPPUNIT_ASSERT(pSubtype);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Form"), pSubtype->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pInnerResources
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFDictionaryElement*>(pXObject->Lookup("Resources"));
|
|
|
|
|
CPPUNIT_ASSERT(pInnerResources);
|
|
|
|
|
auto pInnerXObjects = dynamic_cast<vcl::filter::PDFDictionaryElement*>(
|
|
|
|
|
pInnerResources->LookupElement("XObject"));
|
|
|
|
|
CPPUNIT_ASSERT(pInnerXObjects);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(1), pInnerXObjects->GetItems().size());
|
|
|
|
|
OString aInnerObjectID = pInnerXObjects->GetItems().begin()->first;
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Im15"), aInnerObjectID);
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFObjectElement* pInnerXObject
|
|
|
|
|
= pInnerXObjects->LookupObject(aInnerObjectID);
|
|
|
|
|
CPPUNIT_ASSERT(pInnerXObject);
|
|
|
|
|
|
|
|
|
|
auto pInnerSubtype
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pInnerXObject->Lookup("Subtype"));
|
|
|
|
|
CPPUNIT_ASSERT(pInnerSubtype);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Form"), pInnerSubtype->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pInnerInnerResources = dynamic_cast<vcl::filter::PDFDictionaryElement*>(
|
|
|
|
|
pInnerXObject->Lookup("Resources"));
|
|
|
|
|
CPPUNIT_ASSERT(pInnerInnerResources);
|
|
|
|
|
auto pInnerInnerXObjects = dynamic_cast<vcl::filter::PDFDictionaryElement*>(
|
|
|
|
|
pInnerInnerResources->LookupElement("XObject"));
|
|
|
|
|
CPPUNIT_ASSERT(pInnerInnerXObjects);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(2), pInnerInnerXObjects->GetItems().size());
|
|
|
|
|
|
|
|
|
|
std::vector<OString> aBitmapIDs1;
|
|
|
|
|
for (auto const& rPair : pInnerInnerXObjects->GetItems())
|
|
|
|
|
aBitmapIDs1.push_back(rPair.first);
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Im11"), aBitmapIDs1[0]);
|
|
|
|
|
auto pRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(
|
|
|
|
|
pInnerInnerXObjects->LookupElement(aBitmapIDs1[0]));
|
|
|
|
|
CPPUNIT_ASSERT(pRef);
|
|
|
|
|
aBitmapRefs1.push_back(pRef->GetObjectValue());
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(0, pRef->GetGenerationValue());
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFObjectElement* pBitmap
|
|
|
|
|
= pInnerInnerXObjects->LookupObject(aBitmapIDs1[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pBitmap);
|
|
|
|
|
auto pBitmapSubtype
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pBitmap->Lookup("Subtype"));
|
|
|
|
|
CPPUNIT_ASSERT(pBitmapSubtype);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Image"), pBitmapSubtype->GetValue());
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Im5"), aBitmapIDs1[1]);
|
|
|
|
|
auto pRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(
|
|
|
|
|
pInnerInnerXObjects->LookupElement(aBitmapIDs1[1]));
|
|
|
|
|
CPPUNIT_ASSERT(pRef);
|
|
|
|
|
aBitmapRefs1.push_back(pRef->GetObjectValue());
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(0, pRef->GetGenerationValue());
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFObjectElement* pBitmap
|
|
|
|
|
= pInnerInnerXObjects->LookupObject(aBitmapIDs1[1]);
|
|
|
|
|
CPPUNIT_ASSERT(pBitmap);
|
|
|
|
|
auto pBitmapSubtype
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pBitmap->Lookup("Subtype"));
|
|
|
|
|
CPPUNIT_ASSERT(pBitmapSubtype);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Image"), pBitmapSubtype->GetValue());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
// FORM object 2
|
|
|
|
|
OString aID = rIDs[1];
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Im5"), aID);
|
|
|
|
|
vcl::filter::PDFObjectElement* pXObject = pXObjects->LookupObject(aID);
|
|
|
|
|
CPPUNIT_ASSERT(pXObject);
|
|
|
|
|
|
|
|
|
|
auto pSubtype = dynamic_cast<vcl::filter::PDFNameElement*>(pXObject->Lookup("Subtype"));
|
|
|
|
|
CPPUNIT_ASSERT(pSubtype);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Form"), pSubtype->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pInnerResources
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFDictionaryElement*>(pXObject->Lookup("Resources"));
|
|
|
|
|
CPPUNIT_ASSERT(pInnerResources);
|
|
|
|
|
auto pInnerXObjects = dynamic_cast<vcl::filter::PDFDictionaryElement*>(
|
|
|
|
|
pInnerResources->LookupElement("XObject"));
|
|
|
|
|
CPPUNIT_ASSERT(pInnerXObjects);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(1), pInnerXObjects->GetItems().size());
|
|
|
|
|
OString aInnerObjectID = pInnerXObjects->GetItems().begin()->first;
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Im6"), aInnerObjectID);
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFObjectElement* pInnerXObject
|
|
|
|
|
= pInnerXObjects->LookupObject(aInnerObjectID);
|
|
|
|
|
CPPUNIT_ASSERT(pInnerXObject);
|
|
|
|
|
|
|
|
|
|
auto pInnerSubtype
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pInnerXObject->Lookup("Subtype"));
|
|
|
|
|
CPPUNIT_ASSERT(pInnerSubtype);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Form"), pInnerSubtype->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pInnerInnerResources = dynamic_cast<vcl::filter::PDFDictionaryElement*>(
|
|
|
|
|
pInnerXObject->Lookup("Resources"));
|
|
|
|
|
CPPUNIT_ASSERT(pInnerInnerResources);
|
|
|
|
|
auto pInnerInnerXObjects = dynamic_cast<vcl::filter::PDFDictionaryElement*>(
|
|
|
|
|
pInnerInnerResources->LookupElement("XObject"));
|
|
|
|
|
CPPUNIT_ASSERT(pInnerInnerXObjects);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(2), pInnerInnerXObjects->GetItems().size());
|
|
|
|
|
|
|
|
|
|
std::vector<OString> aBitmapIDs2;
|
|
|
|
|
for (auto const& rPair : pInnerInnerXObjects->GetItems())
|
|
|
|
|
aBitmapIDs2.push_back(rPair.first);
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Im11"), aBitmapIDs2[0]);
|
|
|
|
|
auto pRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(
|
|
|
|
|
pInnerInnerXObjects->LookupElement(aBitmapIDs2[0]));
|
|
|
|
|
CPPUNIT_ASSERT(pRef);
|
|
|
|
|
aBitmapRefs2.push_back(pRef->GetObjectValue());
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(0, pRef->GetGenerationValue());
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFObjectElement* pBitmap
|
|
|
|
|
= pInnerInnerXObjects->LookupObject(aBitmapIDs2[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pBitmap);
|
|
|
|
|
auto pBitmapSubtype
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pBitmap->Lookup("Subtype"));
|
|
|
|
|
CPPUNIT_ASSERT(pBitmapSubtype);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Image"), pBitmapSubtype->GetValue());
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Im5"), aBitmapIDs2[1]);
|
|
|
|
|
auto pRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(
|
|
|
|
|
pInnerInnerXObjects->LookupElement(aBitmapIDs2[1]));
|
|
|
|
|
CPPUNIT_ASSERT(pRef);
|
|
|
|
|
aBitmapRefs2.push_back(pRef->GetObjectValue());
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(0, pRef->GetGenerationValue());
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFObjectElement* pBitmap
|
|
|
|
|
= pInnerInnerXObjects->LookupObject(aBitmapIDs2[1]);
|
|
|
|
|
CPPUNIT_ASSERT(pBitmap);
|
|
|
|
|
auto pBitmapSubtype
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pBitmap->Lookup("Subtype"));
|
|
|
|
|
CPPUNIT_ASSERT(pBitmapSubtype);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Image"), pBitmapSubtype->GetValue());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Ref should point to the same bitmap
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(aBitmapRefs1[0], aBitmapRefs2[0]);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(aBitmapRefs1[1], aBitmapRefs2[1]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check we correctly copy more complex resources (Fonts describing
|
|
|
|
|
// glyphs in recursive arrays) to the target PDF
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testReexportDocumentWithComplexResources)
|
|
|
|
|
{
|
|
|
|
|
// setenv only works on unix based systems
|
|
|
|
|
#ifndef _WIN32
|
|
|
|
|
// We need to enable PDFium import (and make sure to disable after the test)
|
|
|
|
|
bool bResetEnvVar = false;
|
|
|
|
|
if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
|
|
|
|
|
{
|
|
|
|
|
bResetEnvVar = true;
|
|
|
|
|
setenv("LO_IMPORT_USE_PDFIUM", "1", false);
|
|
|
|
|
}
|
|
|
|
|
comphelper::ScopeGuard aPDFiumEnvVarGuard([&]() {
|
|
|
|
|
if (bResetEnvVar)
|
|
|
|
|
unsetenv("LO_IMPORT_USE_PDFIUM");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Load the PDF and save as PDF
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
load(u"ComplexContentDictionary.pdf", aDocument);
|
|
|
|
|
|
|
|
|
|
// Assert that the XObject in the page resources dictionary is a reference XObject.
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(1), aPages.size());
|
|
|
|
|
|
|
|
|
|
// Go directly to the Font object (24 0) (number could change if we change how PDF export works)
|
|
|
|
|
auto pFont = aDocument.LookupObject(24);
|
|
|
|
|
CPPUNIT_ASSERT(pFont);
|
|
|
|
|
|
|
|
|
|
// Check it is the Font object (Type = Font)
|
|
|
|
|
auto pName
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pFont->GetDictionary()->LookupElement("Type"));
|
|
|
|
|
CPPUNIT_ASSERT(pName);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Font"), pName->GetValue());
|
|
|
|
|
|
|
|
|
|
// Check BaseFont is what we expect
|
|
|
|
|
auto pBaseFont = dynamic_cast<vcl::filter::PDFNameElement*>(
|
|
|
|
|
pFont->GetDictionary()->LookupElement("BaseFont"));
|
|
|
|
|
CPPUNIT_ASSERT(pBaseFont);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("HOTOMR+Calibri,Italic"), pBaseFont->GetValue());
|
|
|
|
|
|
|
|
|
|
// Check and get the W array
|
|
|
|
|
auto pWArray
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFArrayElement*>(pFont->GetDictionary()->LookupElement("W"));
|
|
|
|
|
CPPUNIT_ASSERT(pWArray);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(26), pWArray->GetElements().size());
|
|
|
|
|
|
|
|
|
|
// Check the content of W array
|
|
|
|
|
// ObjectCopier didn't copy this array correctly and the document
|
|
|
|
|
// had glyphs at the wrong places
|
|
|
|
|
{
|
|
|
|
|
// first 2 elements
|
|
|
|
|
auto pNumberAtIndex0 = dynamic_cast<vcl::filter::PDFNumberElement*>(pWArray->GetElement(0));
|
|
|
|
|
CPPUNIT_ASSERT(pNumberAtIndex0);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(3.0, pNumberAtIndex0->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pArrayAtIndex1 = dynamic_cast<vcl::filter::PDFArrayElement*>(pWArray->GetElement(1));
|
|
|
|
|
CPPUNIT_ASSERT(pArrayAtIndex1);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(1), pArrayAtIndex1->GetElements().size());
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
auto pNumber
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNumberElement*>(pArrayAtIndex1->GetElement(0));
|
|
|
|
|
CPPUNIT_ASSERT(pNumber);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(226.0, pNumber->GetValue());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// last 2 elements
|
|
|
|
|
auto pNumberAtIndex24
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNumberElement*>(pWArray->GetElement(24));
|
|
|
|
|
CPPUNIT_ASSERT(pNumberAtIndex24);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(894.0, pNumberAtIndex24->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pArrayAtIndex25 = dynamic_cast<vcl::filter::PDFArrayElement*>(pWArray->GetElement(25));
|
|
|
|
|
CPPUNIT_ASSERT(pArrayAtIndex25);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(2), pArrayAtIndex25->GetElements().size());
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
auto pNumber1
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNumberElement*>(pArrayAtIndex25->GetElement(0));
|
|
|
|
|
CPPUNIT_ASSERT(pNumber1);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(303.0, pNumber1->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pNumber2
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNumberElement*>(pArrayAtIndex25->GetElement(1));
|
|
|
|
|
CPPUNIT_ASSERT(pNumber2);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(303.0, pNumber2->GetValue());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Tests that at export the PDF has the PDF/UA metadata properly set
|
|
|
|
|
// when we enable PDF/UA support.
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testPdfUaMetadata)
|
|
|
|
|
{
|
|
|
|
|
// Import a basic document (document doesn't really matter)
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
|
|
|
|
|
// Enable PDF/UA
|
|
|
|
|
uno::Sequence<beans::PropertyValue> aFilterData(
|
|
|
|
|
comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
|
|
|
|
|
aMediaDescriptor["FilterData"] <<= aFilterData;
|
|
|
|
|
saveAsPDF(u"BrownFoxLazyDog.odt");
|
|
|
|
|
|
|
|
|
|
// Parse the export result.
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
|
|
|
|
|
CPPUNIT_ASSERT(aDocument.Read(aStream));
|
|
|
|
|
|
|
|
|
|
auto* pCatalog = aDocument.GetCatalog();
|
|
|
|
|
CPPUNIT_ASSERT(pCatalog);
|
|
|
|
|
auto* pCatalogDictionary = pCatalog->GetDictionary();
|
|
|
|
|
CPPUNIT_ASSERT(pCatalogDictionary);
|
|
|
|
|
auto* pMetadataObject = pCatalogDictionary->LookupObject("Metadata");
|
|
|
|
|
CPPUNIT_ASSERT(pMetadataObject);
|
|
|
|
|
auto* pMetadataDictionary = pMetadataObject->GetDictionary();
|
|
|
|
|
auto* pType
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pMetadataDictionary->LookupElement("Type"));
|
|
|
|
|
CPPUNIT_ASSERT(pType);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Metadata"), pType->GetValue());
|
|
|
|
|
|
|
|
|
|
auto* pStreamObject = pMetadataObject->GetStream();
|
|
|
|
|
CPPUNIT_ASSERT(pStreamObject);
|
|
|
|
|
auto& rStream = pStreamObject->GetMemory();
|
|
|
|
|
rStream.Seek(0);
|
|
|
|
|
|
|
|
|
|
// Search for the PDF/UA marker in the metadata
|
|
|
|
|
|
|
|
|
|
tools::XmlWalker aWalker;
|
|
|
|
|
CPPUNIT_ASSERT(aWalker.open(&rStream));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("xmpmeta"), aWalker.name());
|
|
|
|
|
|
|
|
|
|
bool bPdfUaMarkerFound = false;
|
|
|
|
|
OString aPdfUaPart;
|
|
|
|
|
|
|
|
|
|
aWalker.children();
|
|
|
|
|
while (aWalker.isValid())
|
|
|
|
|
{
|
|
|
|
|
if (aWalker.name() == "RDF"
|
|
|
|
|
&& aWalker.namespaceHref() == "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
|
|
|
|
|
{
|
|
|
|
|
aWalker.children();
|
|
|
|
|
while (aWalker.isValid())
|
|
|
|
|
{
|
|
|
|
|
if (aWalker.name() == "Description"
|
|
|
|
|
&& aWalker.namespaceHref() == "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
|
|
|
|
|
{
|
|
|
|
|
aWalker.children();
|
|
|
|
|
while (aWalker.isValid())
|
|
|
|
|
{
|
|
|
|
|
if (aWalker.name() == "part"
|
|
|
|
|
&& aWalker.namespaceHref() == "http://www.aiim.org/pdfua/ns/id/")
|
|
|
|
|
{
|
|
|
|
|
aPdfUaPart = aWalker.content();
|
|
|
|
|
bPdfUaMarkerFound = true;
|
|
|
|
|
}
|
|
|
|
|
aWalker.next();
|
|
|
|
|
}
|
|
|
|
|
aWalker.parent();
|
|
|
|
|
}
|
|
|
|
|
aWalker.next();
|
|
|
|
|
}
|
|
|
|
|
aWalker.parent();
|
|
|
|
|
}
|
|
|
|
|
aWalker.next();
|
|
|
|
|
}
|
|
|
|
|
aWalker.parent();
|
|
|
|
|
|
|
|
|
|
CPPUNIT_ASSERT(bPdfUaMarkerFound);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("1"), aPdfUaPart);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf139736)
|
|
|
|
|
{
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
|
|
|
|
|
// Enable PDF/UA
|
|
|
|
|
uno::Sequence<beans::PropertyValue> aFilterData(
|
|
|
|
|
comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) },
|
|
|
|
|
{ "SelectPdfVersion", uno::Any(sal_Int32(17)) } }));
|
|
|
|
|
aMediaDescriptor["FilterData"] <<= aFilterData;
|
|
|
|
|
saveAsPDF(u"tdf139736-1.odt");
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
|
|
|
|
|
CPPUNIT_ASSERT(aDocument.Read(aStream));
|
|
|
|
|
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFObjectElement* pContents = aPages[0]->LookupObject("Contents");
|
|
|
|
|
CPPUNIT_ASSERT(pContents);
|
|
|
|
|
vcl::filter::PDFStreamElement* pStream = pContents->GetStream();
|
|
|
|
|
CPPUNIT_ASSERT(pStream);
|
|
|
|
|
SvMemoryStream& rObjectStream = pStream->GetMemory();
|
|
|
|
|
// Uncompress it.
|
|
|
|
|
SvMemoryStream aUncompressed;
|
|
|
|
|
ZCodec aZCodec;
|
|
|
|
|
aZCodec.BeginCompression();
|
|
|
|
|
rObjectStream.Seek(0);
|
|
|
|
|
aZCodec.Decompress(rObjectStream, aUncompressed);
|
|
|
|
|
CPPUNIT_ASSERT(aZCodec.EndCompression());
|
|
|
|
|
|
|
|
|
|
auto pStart = static_cast<const char*>(aUncompressed.GetData());
|
|
|
|
|
const char* const pEnd = pStart + aUncompressed.GetSize();
|
|
|
|
|
|
|
|
|
|
enum
|
|
|
|
|
{
|
|
|
|
|
Default,
|
|
|
|
|
Artifact,
|
|
|
|
|
ArtifactProps1,
|
|
|
|
|
ArtifactProps2,
|
|
|
|
|
Tagged
|
|
|
|
|
} state
|
|
|
|
|
= Default;
|
|
|
|
|
|
|
|
|
|
auto nLine(0);
|
|
|
|
|
auto nTagged(0);
|
|
|
|
|
auto nArtifacts(0);
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
++nLine;
|
|
|
|
|
auto const pLine = ::std::find(pStart, pEnd, '\n');
|
|
|
|
|
if (pLine == pEnd)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
std::string_view const line(pStart, pLine - pStart);
|
|
|
|
|
pStart = pLine + 1;
|
|
|
|
|
if (!line.empty() && line[0] != '%')
|
|
|
|
|
{
|
|
|
|
|
::std::cerr << nLine << ": " << line << "\n";
|
|
|
|
|
if (line == "/Artifact BMC")
|
|
|
|
|
{
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, state);
|
|
|
|
|
state = Artifact;
|
|
|
|
|
++nArtifacts;
|
|
|
|
|
}
|
|
|
|
|
else if (o3tl::starts_with(line, "/Artifact <<"))
|
|
|
|
|
{
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, state);
|
|
|
|
|
// check header/footer properties
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(std::string_view("/Type/Pagination"), line.substr(12));
|
|
|
|
|
state = ArtifactProps1;
|
|
|
|
|
++nArtifacts;
|
|
|
|
|
}
|
|
|
|
|
else if (state == ArtifactProps1)
|
|
|
|
|
{
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(std::string_view("/Subtype/Header"), line);
|
|
|
|
|
state = ArtifactProps2;
|
|
|
|
|
}
|
|
|
|
|
else if (state == ArtifactProps2 && line == ">> BDC")
|
|
|
|
|
{
|
|
|
|
|
state = Artifact;
|
|
|
|
|
}
|
|
|
|
|
else if (line == "/Standard<</MCID 0>>BDC")
|
|
|
|
|
{
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, state);
|
|
|
|
|
state = Tagged;
|
|
|
|
|
++nTagged;
|
|
|
|
|
}
|
|
|
|
|
else if (line == "EMC")
|
|
|
|
|
{
|
|
|
|
|
CPPUNIT_ASSERT_MESSAGE("unexpected end", state != Default);
|
|
|
|
|
state = Default;
|
|
|
|
|
}
|
|
|
|
|
else if (nLine > 1) // first line is expected "0.1 w"
|
|
|
|
|
{
|
|
|
|
|
CPPUNIT_ASSERT_MESSAGE("unexpected content outside MCS", state != Default);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("unclosed MCS", Default, state);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nTagged)>(1), nTagged); // text in body
|
|
|
|
|
// 1 image and 1 frame and 1 header text; arbitrary number of aux stuff like borders
|
|
|
|
|
CPPUNIT_ASSERT(nArtifacts >= 3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf152231)
|
|
|
|
|
{
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
|
|
|
|
|
// Enable PDF/UA
|
|
|
|
|
uno::Sequence<beans::PropertyValue> aFilterData(
|
|
|
|
|
comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) },
|
|
|
|
|
{ "ExportNotesInMargin", uno::Any(true) },
|
|
|
|
|
{ "SelectPdfVersion", uno::Any(sal_Int32(17)) } }));
|
|
|
|
|
aMediaDescriptor["FilterData"] <<= aFilterData;
|
|
|
|
|
saveAsPDF(u"tdf152231.fodt");
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
|
|
|
|
|
CPPUNIT_ASSERT(aDocument.Read(aStream));
|
|
|
|
|
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFObjectElement* pContents = aPages[0]->LookupObject("Contents");
|
|
|
|
|
CPPUNIT_ASSERT(pContents);
|
|
|
|
|
vcl::filter::PDFStreamElement* pStream = pContents->GetStream();
|
|
|
|
|
CPPUNIT_ASSERT(pStream);
|
|
|
|
|
SvMemoryStream& rObjectStream = pStream->GetMemory();
|
|
|
|
|
// Uncompress it.
|
|
|
|
|
SvMemoryStream aUncompressed;
|
|
|
|
|
ZCodec aZCodec;
|
|
|
|
|
aZCodec.BeginCompression();
|
|
|
|
|
rObjectStream.Seek(0);
|
|
|
|
|
aZCodec.Decompress(rObjectStream, aUncompressed);
|
|
|
|
|
CPPUNIT_ASSERT(aZCodec.EndCompression());
|
|
|
|
|
|
|
|
|
|
auto pStart = static_cast<const char*>(aUncompressed.GetData());
|
|
|
|
|
const char* const pEnd = pStart + aUncompressed.GetSize();
|
|
|
|
|
|
|
|
|
|
enum
|
|
|
|
|
{
|
|
|
|
|
Default,
|
|
|
|
|
Artifact,
|
|
|
|
|
Tagged
|
|
|
|
|
} state
|
|
|
|
|
= Default;
|
|
|
|
|
|
|
|
|
|
auto nLine(0);
|
|
|
|
|
auto nTagged(0);
|
|
|
|
|
auto nArtifacts(0);
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
++nLine;
|
|
|
|
|
auto const pLine = ::std::find(pStart, pEnd, '\n');
|
|
|
|
|
if (pLine == pEnd)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
std::string_view const line(pStart, pLine - pStart);
|
|
|
|
|
pStart = pLine + 1;
|
|
|
|
|
if (!line.empty() && line[0] != '%')
|
|
|
|
|
{
|
|
|
|
|
::std::cerr << nLine << ": " << line << "\n";
|
|
|
|
|
if (line == "/Artifact BMC")
|
|
|
|
|
{
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, state);
|
|
|
|
|
state = Artifact;
|
|
|
|
|
++nArtifacts;
|
|
|
|
|
}
|
|
|
|
|
else if (o3tl::starts_with(line, "/Standard<</MCID "))
|
|
|
|
|
{
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, state);
|
|
|
|
|
state = Tagged;
|
|
|
|
|
++nTagged;
|
|
|
|
|
}
|
|
|
|
|
else if (line == "EMC")
|
|
|
|
|
{
|
|
|
|
|
CPPUNIT_ASSERT_MESSAGE("unexpected end", state != Default);
|
|
|
|
|
state = Default;
|
|
|
|
|
}
|
|
|
|
|
else if (nLine > 1) // first line is expected "0.1 w"
|
|
|
|
|
{
|
|
|
|
|
CPPUNIT_ASSERT_MESSAGE("unexpected content outside MCS", state != Default);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("unclosed MCS", Default, state);
|
|
|
|
|
CPPUNIT_ASSERT(nTagged >= 12); // text in body
|
|
|
|
|
// 1 annotation
|
|
|
|
|
CPPUNIT_ASSERT(nArtifacts >= 1);
|
|
|
|
|
|
|
|
|
|
auto nPara(0);
|
|
|
|
|
for (const auto& rDocElement : aDocument.GetElements())
|
|
|
|
|
{
|
|
|
|
|
auto pObject = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
|
|
|
|
|
if (!pObject)
|
|
|
|
|
continue;
|
|
|
|
|
auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Type"));
|
|
|
|
|
if (pType && pType->GetValue() == "StructElem")
|
|
|
|
|
{
|
|
|
|
|
auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("S"));
|
|
|
|
|
if (pS && pS->GetValue() == "Standard")
|
|
|
|
|
{
|
|
|
|
|
++nPara;
|
|
|
|
|
auto pKids = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids);
|
|
|
|
|
// one problem was that some StructElem were missing kids
|
|
|
|
|
CPPUNIT_ASSERT(!pKids->GetElements().empty());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nPara)>(12), nPara);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf152235)
|
|
|
|
|
{
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
// Enable PDF/UA
|
|
|
|
|
uno::Sequence<beans::PropertyValue> aFilterData(comphelper::InitPropertySequence(
|
|
|
|
|
{ { "PDFUACompliance", uno::Any(true) },
|
|
|
|
|
{ "Watermark", uno::Any(OUString("kendy")) },
|
|
|
|
|
// need to set a font to avoid assertions about missing "Helvetica"
|
|
|
|
|
{ "WatermarkFontName", uno::Any(OUString("Liberation Sans")) },
|
|
|
|
|
{ "SelectPdfVersion", uno::Any(sal_Int32(17)) } }));
|
|
|
|
|
aMediaDescriptor["FilterData"] <<= aFilterData;
|
|
|
|
|
mxComponent = loadFromDesktop("private:factory/swriter");
|
|
|
|
|
uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
|
|
|
|
|
xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
|
|
|
|
|
CPPUNIT_ASSERT(aDocument.Read(aStream));
|
|
|
|
|
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFObjectElement* pContents = aPages[0]->LookupObject("Contents");
|
|
|
|
|
CPPUNIT_ASSERT(pContents);
|
|
|
|
|
vcl::filter::PDFStreamElement* pStream = pContents->GetStream();
|
|
|
|
|
CPPUNIT_ASSERT(pStream);
|
|
|
|
|
SvMemoryStream& rObjectStream = pStream->GetMemory();
|
|
|
|
|
// Uncompress it.
|
|
|
|
|
SvMemoryStream aUncompressed;
|
|
|
|
|
ZCodec aZCodec;
|
|
|
|
|
aZCodec.BeginCompression();
|
|
|
|
|
rObjectStream.Seek(0);
|
|
|
|
|
aZCodec.Decompress(rObjectStream, aUncompressed);
|
|
|
|
|
CPPUNIT_ASSERT(aZCodec.EndCompression());
|
|
|
|
|
|
|
|
|
|
auto pStart = static_cast<const char*>(aUncompressed.GetData());
|
|
|
|
|
const char* const pEnd = pStart + aUncompressed.GetSize();
|
|
|
|
|
|
|
|
|
|
enum
|
|
|
|
|
{
|
|
|
|
|
Default,
|
|
|
|
|
Artifact,
|
|
|
|
|
Tagged
|
|
|
|
|
} state
|
|
|
|
|
= Default;
|
|
|
|
|
|
|
|
|
|
auto nLine(0);
|
|
|
|
|
auto nTagged(0);
|
|
|
|
|
auto nArtifacts(0);
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
++nLine;
|
|
|
|
|
auto const pLine = ::std::find(pStart, pEnd, '\n');
|
|
|
|
|
if (pLine == pEnd)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
std::string_view const line(pStart, pLine - pStart);
|
|
|
|
|
pStart = pLine + 1;
|
|
|
|
|
if (!line.empty() && line[0] != '%')
|
|
|
|
|
{
|
|
|
|
|
::std::cerr << nLine << ": " << line << "\n";
|
|
|
|
|
if (o3tl::starts_with(line, "/Artifact "))
|
|
|
|
|
{
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, state);
|
|
|
|
|
state = Artifact;
|
|
|
|
|
++nArtifacts;
|
|
|
|
|
}
|
|
|
|
|
else if (o3tl::starts_with(line, "/Standard<</MCID "))
|
|
|
|
|
{
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, state);
|
|
|
|
|
state = Tagged;
|
|
|
|
|
++nTagged;
|
|
|
|
|
}
|
|
|
|
|
else if (line == "EMC")
|
|
|
|
|
{
|
|
|
|
|
CPPUNIT_ASSERT_MESSAGE("unexpected end", state != Default);
|
|
|
|
|
state = Default;
|
|
|
|
|
}
|
|
|
|
|
else if (nLine > 1) // first line is expected "0.1 w"
|
|
|
|
|
{
|
|
|
|
|
CPPUNIT_ASSERT_MESSAGE("unexpected content outside MCS", state != Default);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("unclosed MCS", Default, state);
|
|
|
|
|
CPPUNIT_ASSERT(nTagged >= 0); // text in body
|
|
|
|
|
CPPUNIT_ASSERT(nArtifacts >= 2); // 1 watermark + 1 other thing
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf149140)
|
|
|
|
|
{
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
|
|
|
|
|
// Enable PDF/UA
|
|
|
|
|
uno::Sequence<beans::PropertyValue> aFilterData(
|
|
|
|
|
comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
|
|
|
|
|
aMediaDescriptor["FilterData"] <<= aFilterData;
|
|
|
|
|
saveAsPDF(u"TableTH_test_LibreOfficeWriter7.3.3_HeaderRow-HeadersInTopRow.fodt");
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
|
|
|
|
|
CPPUNIT_ASSERT(aDocument.Read(aStream));
|
|
|
|
|
|
|
|
|
|
// The document has one page.
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
|
|
|
|
|
|
|
|
|
|
int nTH(0);
|
|
|
|
|
for (const auto& rDocElement : aDocument.GetElements())
|
|
|
|
|
{
|
|
|
|
|
auto pObject = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
|
|
|
|
|
if (!pObject)
|
|
|
|
|
continue;
|
|
|
|
|
auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Type"));
|
|
|
|
|
if (pType && pType->GetValue() == "StructElem")
|
|
|
|
|
{
|
|
|
|
|
auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("S"));
|
|
|
|
|
if (pS && pS->GetValue() == "TH")
|
|
|
|
|
{
|
|
|
|
|
int nTable(0);
|
|
|
|
|
auto pAttrs = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject->Lookup("A"));
|
|
|
|
|
CPPUNIT_ASSERT(pAttrs != nullptr);
|
|
|
|
|
for (const auto& rAttrRef : pAttrs->GetElements())
|
|
|
|
|
{
|
|
|
|
|
auto pAttrDict = dynamic_cast<vcl::filter::PDFDictionaryElement*>(rAttrRef);
|
|
|
|
|
CPPUNIT_ASSERT(pAttrDict != nullptr);
|
|
|
|
|
auto pOwner
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pAttrDict->LookupElement("O"));
|
|
|
|
|
CPPUNIT_ASSERT(pOwner != nullptr);
|
|
|
|
|
if (pOwner->GetValue() == "Table")
|
|
|
|
|
{
|
|
|
|
|
auto pScope = dynamic_cast<vcl::filter::PDFNameElement*>(
|
|
|
|
|
pAttrDict->LookupElement("Scope"));
|
|
|
|
|
CPPUNIT_ASSERT(pScope != nullptr);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Column"), pScope->GetValue());
|
|
|
|
|
++nTable;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(int(1), nTable);
|
|
|
|
|
++nTH;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(int(6), nTH);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testNestedSection)
|
|
|
|
|
{
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
|
|
|
|
|
// Enable PDF/UA
|
|
|
|
|
uno::Sequence<beans::PropertyValue> aFilterData(
|
|
|
|
|
comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
|
|
|
|
|
aMediaDescriptor["FilterData"] <<= aFilterData;
|
|
|
|
|
saveAsPDF(u"nestedsection.fodt");
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
|
|
|
|
|
CPPUNIT_ASSERT(aDocument.Read(aStream));
|
|
|
|
|
|
|
|
|
|
// the assert needs 2 follows to reproduce => 3 pages
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aPages.size());
|
|
|
|
|
|
|
|
|
|
auto nDoc(0);
|
|
|
|
|
for (const auto& rDocElement : aDocument.GetElements())
|
|
|
|
|
{
|
|
|
|
|
auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
|
|
|
|
|
if (!pObject1)
|
|
|
|
|
continue;
|
|
|
|
|
auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
|
|
|
|
|
if (pType1 && pType1->GetValue() == "StructElem")
|
|
|
|
|
{
|
|
|
|
|
auto pS1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("S"));
|
|
|
|
|
if (pS1 && pS1->GetValue() == "Document")
|
|
|
|
|
{
|
|
|
|
|
auto pKids1 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject1->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids1);
|
|
|
|
|
// assume there are no MCID ref at this level
|
|
|
|
|
auto pKids1v = pKids1->GetElements();
|
|
|
|
|
auto pRefKid10 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids1v[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid10);
|
|
|
|
|
auto pObject10 = pRefKid10->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject10);
|
|
|
|
|
auto pType10
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject10->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType10->GetValue());
|
|
|
|
|
auto pS10 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject10->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Sect"), pS10->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pKids10 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject10->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids10);
|
|
|
|
|
// assume there are no MCID ref at this level
|
|
|
|
|
auto pKids10v = pKids10->GetElements();
|
|
|
|
|
|
|
|
|
|
auto pRefKid100 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids10v[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid100);
|
|
|
|
|
auto pObject100 = pRefKid100->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject100);
|
|
|
|
|
auto pType100
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject100->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType100->GetValue());
|
|
|
|
|
auto pS100 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject100->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Standard"), pS100->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pRefKid101 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids10v[1]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid101);
|
|
|
|
|
auto pObject101 = pRefKid101->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject101);
|
|
|
|
|
auto pType101
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject101->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType101->GetValue());
|
|
|
|
|
auto pS101 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject101->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Standard"), pS101->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pRefKid102 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids10v[2]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid102);
|
|
|
|
|
auto pObject102 = pRefKid102->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject102);
|
|
|
|
|
auto pType102
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject102->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType102->GetValue());
|
|
|
|
|
auto pS102 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject102->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Sect"), pS102->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pKids102
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFArrayElement*>(pObject102->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids102);
|
|
|
|
|
// assume there are no MCID ref at this level
|
|
|
|
|
auto pKids102v = pKids102->GetElements();
|
|
|
|
|
|
|
|
|
|
auto pRefKid1020 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids102v[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid1020);
|
|
|
|
|
auto pObject1020 = pRefKid1020->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject1020);
|
|
|
|
|
auto pType1020
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject1020->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1020->GetValue());
|
|
|
|
|
auto pS1020 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1020->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Standard"), pS1020->GetValue());
|
|
|
|
|
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(1), pKids102v.size());
|
|
|
|
|
|
|
|
|
|
auto pRefKid103 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids10v[3]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid103);
|
|
|
|
|
auto pObject103 = pRefKid103->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject103);
|
|
|
|
|
auto pType103
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject103->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType103->GetValue());
|
|
|
|
|
auto pS103 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject103->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Standard"), pS103->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pRefKid104 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids10v[4]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid104);
|
|
|
|
|
auto pObject104 = pRefKid104->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject104);
|
|
|
|
|
auto pType104
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject104->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType104->GetValue());
|
|
|
|
|
auto pS104 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject104->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Standard"), pS104->GetValue());
|
|
|
|
|
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(5), pKids10v.size());
|
|
|
|
|
|
|
|
|
|
auto pRefKid11 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids1v[1]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid11);
|
|
|
|
|
auto pObject11 = pRefKid11->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject11);
|
|
|
|
|
auto pType11
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject11->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType11->GetValue());
|
|
|
|
|
auto pS11 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject11->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Standard"), pS11->GetValue());
|
|
|
|
|
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(2), pKids1v.size());
|
|
|
|
|
++nDoc;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nDoc)>(1), nDoc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf157817)
|
|
|
|
|
{
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
|
|
|
|
|
// Enable PDF/UA
|
|
|
|
|
uno::Sequence<beans::PropertyValue> aFilterData(
|
|
|
|
|
comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
|
|
|
|
|
aMediaDescriptor["FilterData"] <<= aFilterData;
|
|
|
|
|
saveAsPDF(u"SimpleTOC.fodt");
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
|
|
|
|
|
CPPUNIT_ASSERT(aDocument.Read(aStream));
|
|
|
|
|
|
|
|
|
|
// The document has one page.
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aPages.size());
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFObjectElement* pTOC(nullptr);
|
|
|
|
|
for (const auto& rDocElement : aDocument.GetElements())
|
|
|
|
|
{
|
|
|
|
|
auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
|
|
|
|
|
if (!pObject1)
|
|
|
|
|
continue;
|
|
|
|
|
auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
|
|
|
|
|
if (pType1 && pType1->GetValue() == "StructElem")
|
|
|
|
|
{
|
|
|
|
|
auto pS1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("S"));
|
|
|
|
|
if (pS1 && pS1->GetValue() == "TOC")
|
|
|
|
|
{
|
|
|
|
|
pTOC = pObject1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT(pTOC);
|
|
|
|
|
|
|
|
|
|
auto pKidsT = dynamic_cast<vcl::filter::PDFArrayElement*>(pTOC->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKidsT);
|
|
|
|
|
// assume there are no MCID ref at this level
|
|
|
|
|
auto pKidsTv = pKidsT->GetElements();
|
|
|
|
|
auto pRefKidT0 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsTv[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKidT0);
|
|
|
|
|
auto pObjectT0 = pRefKidT0->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObjectT0);
|
|
|
|
|
auto pTypeT0 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT0->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT0->GetValue());
|
|
|
|
|
auto pST0 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT0->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Caption"), pST0->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pKidsT0 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObjectT0->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKidsT0);
|
|
|
|
|
auto pKidsT0v = pKidsT0->GetElements();
|
|
|
|
|
auto pRefKidT00 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsT0v[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKidT00);
|
|
|
|
|
auto pObjectT00 = pRefKidT00->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObjectT00);
|
|
|
|
|
auto pTypeT00 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT00->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT00->GetValue());
|
|
|
|
|
auto pST00 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT00->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Contents#20Heading"), pST00->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pRefKidT1 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsTv[1]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKidT1);
|
|
|
|
|
auto pObjectT1 = pRefKidT1->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObjectT1);
|
|
|
|
|
auto pTypeT1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT1->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT1->GetValue());
|
|
|
|
|
auto pST1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT1->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("TOCI"), pST1->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pKidsT1 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObjectT1->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKidsT1);
|
|
|
|
|
auto pKidsT1v = pKidsT1->GetElements();
|
|
|
|
|
|
|
|
|
|
auto pRefKidT10 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsT1v[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKidT10);
|
|
|
|
|
auto pObjectT10 = pRefKidT10->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObjectT10);
|
|
|
|
|
auto pTypeT10 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT10->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT10->GetValue());
|
|
|
|
|
auto pST10 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT10->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Contents#201"), pST10->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pKidsT10 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObjectT10->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKidsT10);
|
|
|
|
|
auto pKidsT10v = pKidsT10->GetElements();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(1), pKidsT10v.size());
|
|
|
|
|
|
|
|
|
|
// there is one and only one Link
|
|
|
|
|
auto pRefKidT100 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsT10v[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKidT100);
|
|
|
|
|
auto pObjectT100 = pRefKidT100->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObjectT100);
|
|
|
|
|
auto pTypeT100 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT100->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT100->GetValue());
|
|
|
|
|
auto pST100 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT100->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Link"), pST100->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pRefKidT2 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsTv[1]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKidT2);
|
|
|
|
|
auto pObjectT2 = pRefKidT2->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObjectT2);
|
|
|
|
|
auto pTypeT2 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT2->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT2->GetValue());
|
|
|
|
|
auto pST2 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT2->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("TOCI"), pST2->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pKidsT2 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObjectT2->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKidsT2);
|
|
|
|
|
auto pKidsT2v = pKidsT2->GetElements();
|
|
|
|
|
|
|
|
|
|
auto pRefKidT20 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsT2v[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKidT20);
|
|
|
|
|
auto pObjectT20 = pRefKidT20->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObjectT20);
|
|
|
|
|
auto pTypeT20 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT20->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT20->GetValue());
|
|
|
|
|
auto pST20 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT20->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Contents#201"), pST20->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pKidsT20 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObjectT20->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKidsT20);
|
|
|
|
|
auto pKidsT20v = pKidsT20->GetElements();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(1), pKidsT20v.size());
|
|
|
|
|
|
|
|
|
|
// there is one and only one Link
|
|
|
|
|
auto pRefKidT200 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsT20v[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKidT200);
|
|
|
|
|
auto pObjectT200 = pRefKidT200->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObjectT200);
|
|
|
|
|
auto pTypeT200 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT200->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT200->GetValue());
|
|
|
|
|
auto pST200 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT200->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Link"), pST200->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pRefKidT3 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsTv[1]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKidT3);
|
|
|
|
|
auto pObjectT3 = pRefKidT3->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObjectT3);
|
|
|
|
|
auto pTypeT3 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT3->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT3->GetValue());
|
|
|
|
|
auto pST3 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT3->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("TOCI"), pST3->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pKidsT3 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObjectT3->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKidsT3);
|
|
|
|
|
auto pKidsT3v = pKidsT3->GetElements();
|
|
|
|
|
|
|
|
|
|
auto pRefKidT30 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsT3v[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKidT30);
|
|
|
|
|
auto pObjectT30 = pRefKidT30->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObjectT30);
|
|
|
|
|
auto pTypeT30 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT30->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT30->GetValue());
|
|
|
|
|
auto pST30 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT30->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Contents#201"), pST30->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pKidsT30 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObjectT30->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKidsT30);
|
|
|
|
|
auto pKidsT30v = pKidsT30->GetElements();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(1), pKidsT30v.size());
|
|
|
|
|
|
|
|
|
|
// there is one and only one Link
|
|
|
|
|
auto pRefKidT300 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsT30v[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKidT300);
|
|
|
|
|
auto pObjectT300 = pRefKidT300->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObjectT300);
|
|
|
|
|
auto pTypeT300 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT300->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT300->GetValue());
|
|
|
|
|
auto pST300 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT300->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Link"), pST300->GetValue());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf135638)
|
|
|
|
|
{
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
|
|
|
|
|
// Enable PDF/UA
|
|
|
|
|
uno::Sequence<beans::PropertyValue> aFilterData(
|
|
|
|
|
comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
|
|
|
|
|
aMediaDescriptor["FilterData"] <<= aFilterData;
|
|
|
|
|
saveAsPDF(u"image-shape.fodt");
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
|
|
|
|
|
CPPUNIT_ASSERT(aDocument.Read(aStream));
|
|
|
|
|
|
|
|
|
|
// The document has one page.
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
|
|
|
|
|
|
|
|
|
|
int nFigure(0);
|
|
|
|
|
for (const auto& rDocElement : aDocument.GetElements())
|
|
|
|
|
{
|
|
|
|
|
auto pObject = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
|
|
|
|
|
if (!pObject)
|
|
|
|
|
continue;
|
|
|
|
|
auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Type"));
|
|
|
|
|
if (pType && pType->GetValue() == "StructElem")
|
|
|
|
|
{
|
|
|
|
|
auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("S"));
|
|
|
|
|
if (pS && pS->GetValue() == "Figure")
|
|
|
|
|
{
|
|
|
|
|
auto pAttrDict
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFDictionaryElement*>(pObject->Lookup("A"));
|
|
|
|
|
CPPUNIT_ASSERT(pAttrDict != nullptr);
|
|
|
|
|
auto pOwner
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pAttrDict->LookupElement("O"));
|
|
|
|
|
CPPUNIT_ASSERT(pOwner != nullptr);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Layout"), pOwner->GetValue());
|
|
|
|
|
auto pBBox
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFArrayElement*>(pAttrDict->LookupElement("BBox"));
|
|
|
|
|
CPPUNIT_ASSERT(pBBox != nullptr);
|
|
|
|
|
if (nFigure == 0)
|
|
|
|
|
{
|
|
|
|
|
CPPUNIT_ASSERT_DOUBLES_EQUAL(
|
|
|
|
|
139.5,
|
|
|
|
|
dynamic_cast<vcl::filter::PDFNumberElement*>(pBBox->GetElements()[0])
|
|
|
|
|
->GetValue(),
|
|
|
|
|
0.01);
|
|
|
|
|
CPPUNIT_ASSERT_DOUBLES_EQUAL(
|
|
|
|
|
480.3,
|
|
|
|
|
dynamic_cast<vcl::filter::PDFNumberElement*>(pBBox->GetElements()[1])
|
|
|
|
|
->GetValue(),
|
|
|
|
|
0.01);
|
|
|
|
|
CPPUNIT_ASSERT_DOUBLES_EQUAL(
|
|
|
|
|
472.5,
|
|
|
|
|
dynamic_cast<vcl::filter::PDFNumberElement*>(pBBox->GetElements()[2])
|
|
|
|
|
->GetValue(),
|
|
|
|
|
0.01);
|
|
|
|
|
CPPUNIT_ASSERT_DOUBLES_EQUAL(
|
|
|
|
|
735.3,
|
|
|
|
|
dynamic_cast<vcl::filter::PDFNumberElement*>(pBBox->GetElements()[3])
|
|
|
|
|
->GetValue(),
|
|
|
|
|
0.01);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
CPPUNIT_ASSERT_DOUBLES_EQUAL(
|
|
|
|
|
178.45,
|
|
|
|
|
dynamic_cast<vcl::filter::PDFNumberElement*>(pBBox->GetElements()[0])
|
|
|
|
|
->GetValue(),
|
|
|
|
|
0.01);
|
|
|
|
|
CPPUNIT_ASSERT_DOUBLES_EQUAL(
|
|
|
|
|
318.65,
|
|
|
|
|
dynamic_cast<vcl::filter::PDFNumberElement*>(pBBox->GetElements()[1])
|
|
|
|
|
->GetValue(),
|
|
|
|
|
0.01);
|
|
|
|
|
CPPUNIT_ASSERT_DOUBLES_EQUAL(
|
|
|
|
|
326.35,
|
|
|
|
|
dynamic_cast<vcl::filter::PDFNumberElement*>(pBBox->GetElements()[2])
|
|
|
|
|
->GetValue(),
|
|
|
|
|
0.01);
|
|
|
|
|
CPPUNIT_ASSERT_DOUBLES_EQUAL(
|
|
|
|
|
382.55,
|
|
|
|
|
dynamic_cast<vcl::filter::PDFNumberElement*>(pBBox->GetElements()[3])
|
|
|
|
|
->GetValue(),
|
|
|
|
|
0.01);
|
|
|
|
|
}
|
|
|
|
|
++nFigure;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// the first one is a Writer image, 2nd one SdrRectObj
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(int(2), nFigure);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf157703)
|
|
|
|
|
{
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
|
|
|
|
|
// Enable PDF/UA
|
|
|
|
|
uno::Sequence<beans::PropertyValue> aFilterData(
|
|
|
|
|
comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
|
|
|
|
|
aMediaDescriptor["FilterData"] <<= aFilterData;
|
|
|
|
|
saveAsPDF(u"LO_Lbl_Lbody_bug_report.fodt");
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
|
|
|
|
|
CPPUNIT_ASSERT(aDocument.Read(aStream));
|
|
|
|
|
|
|
|
|
|
// The document has one page.
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFObjectElement* pDocument(nullptr);
|
|
|
|
|
for (const auto& rDocElement : aDocument.GetElements())
|
|
|
|
|
{
|
|
|
|
|
auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
|
|
|
|
|
if (!pObject1)
|
|
|
|
|
continue;
|
|
|
|
|
auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
|
|
|
|
|
if (pType1 && pType1->GetValue() == "StructElem")
|
|
|
|
|
{
|
|
|
|
|
auto pS1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("S"));
|
|
|
|
|
if (pS1 && pS1->GetValue() == "Document")
|
|
|
|
|
{
|
|
|
|
|
pDocument = pObject1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT(pDocument);
|
|
|
|
|
|
|
|
|
|
auto pKidsD = dynamic_cast<vcl::filter::PDFArrayElement*>(pDocument->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKidsD);
|
|
|
|
|
// assume there are no MCID ref at this level
|
|
|
|
|
auto pKidsDv = pKidsD->GetElements();
|
|
|
|
|
auto pRefKidD0 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsDv[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKidD0);
|
|
|
|
|
auto pObjectD0 = pRefKidD0->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObjectD0);
|
|
|
|
|
auto pTypeD0 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectD0->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeD0->GetValue());
|
|
|
|
|
auto pSD0 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectD0->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("H1"), pSD0->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pKidsD0 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObjectD0->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKidsD0);
|
|
|
|
|
auto pKidsD0v = pKidsD0->GetElements();
|
|
|
|
|
auto pRefKidD00 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsD0v[0]);
|
|
|
|
|
// MCID for label
|
|
|
|
|
CPPUNIT_ASSERT(!pRefKidD00);
|
|
|
|
|
|
|
|
|
|
// MCID for text
|
|
|
|
|
auto pRefKidD01 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsD0v[1]);
|
|
|
|
|
CPPUNIT_ASSERT(!pRefKidD01);
|
|
|
|
|
|
|
|
|
|
auto pRefKidD1 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsDv[1]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKidD1);
|
|
|
|
|
auto pObjectD1 = pRefKidD1->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObjectD1);
|
|
|
|
|
auto pTypeD1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectD1->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeD1->GetValue());
|
|
|
|
|
auto pSD1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectD1->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("H2"), pSD1->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pKidsD1 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObjectD1->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKidsD1);
|
|
|
|
|
auto pKidsD1v = pKidsD1->GetElements();
|
|
|
|
|
|
|
|
|
|
// MCID for text
|
|
|
|
|
auto pRefKidD11 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsD1v[0]);
|
|
|
|
|
CPPUNIT_ASSERT(!pRefKidD11);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testSpans)
|
|
|
|
|
{
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
|
|
|
|
|
// Enable PDF/UA
|
|
|
|
|
uno::Sequence<beans::PropertyValue> aFilterData(
|
|
|
|
|
comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
|
|
|
|
|
aMediaDescriptor["FilterData"] <<= aFilterData;
|
|
|
|
|
saveAsPDF(u"spanlist.fodt");
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
|
|
|
|
|
CPPUNIT_ASSERT(aDocument.Read(aStream));
|
|
|
|
|
|
|
|
|
|
// The document has one page.
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aPages.size());
|
|
|
|
|
|
|
|
|
|
auto nDoc(0);
|
|
|
|
|
for (const auto& rDocElement : aDocument.GetElements())
|
|
|
|
|
{
|
|
|
|
|
auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
|
|
|
|
|
if (!pObject1)
|
|
|
|
|
continue;
|
|
|
|
|
auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
|
|
|
|
|
if (pType1 && pType1->GetValue() == "StructElem")
|
|
|
|
|
{
|
|
|
|
|
auto pS1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("S"));
|
|
|
|
|
if (pS1 && pS1->GetValue() == "Document")
|
|
|
|
|
{
|
|
|
|
|
auto pKids1 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject1->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids1);
|
|
|
|
|
// assume there are no MCID ref at this level
|
|
|
|
|
auto vKids1 = pKids1->GetElements();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(2), vKids1.size());
|
|
|
|
|
auto pRefKid10 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids1[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid10);
|
|
|
|
|
auto pObject10 = pRefKid10->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject10);
|
|
|
|
|
auto pType10
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject10->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType10->GetValue());
|
|
|
|
|
auto pS10 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject10->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("L"), pS10->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pKids10 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject10->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids10);
|
|
|
|
|
// assume there are no MCID ref at this level
|
|
|
|
|
auto vKids10 = pKids10->GetElements();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(4), vKids10.size());
|
|
|
|
|
|
|
|
|
|
auto pRefKid100 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid100);
|
|
|
|
|
auto pObject100 = pRefKid100->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject100);
|
|
|
|
|
auto pType100
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject100->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType100->GetValue());
|
|
|
|
|
auto pS100 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject100->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("LI"), pS100->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pKids100
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFArrayElement*>(pObject100->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids100);
|
|
|
|
|
// assume there are no MCID ref at this level
|
|
|
|
|
auto vKids100 = pKids100->GetElements();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(2), vKids100.size());
|
|
|
|
|
|
|
|
|
|
auto pRefKid1000 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids100[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid1000);
|
|
|
|
|
auto pObject1000 = pRefKid1000->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject1000);
|
|
|
|
|
auto pType1000
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject1000->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1000->GetValue());
|
|
|
|
|
auto pS1000 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1000->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Lbl"), pS1000->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pRefKid1001 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids100[1]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid1001);
|
|
|
|
|
auto pObject1001 = pRefKid1001->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject1001);
|
|
|
|
|
auto pType1001
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject1001->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1001->GetValue());
|
|
|
|
|
auto pS1001 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1001->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("LBody"), pS1001->GetValue());
|
|
|
|
|
auto pKids1001
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFArrayElement*>(pObject1001->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids1001);
|
|
|
|
|
// assume there are no MCID ref at this level
|
|
|
|
|
auto vKids1001 = pKids1001->GetElements();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(1), vKids1001.size());
|
|
|
|
|
|
|
|
|
|
auto pRefKid10010 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids1001[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid10010);
|
|
|
|
|
auto pObject10010 = pRefKid10010->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject10010);
|
|
|
|
|
auto pType10010
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject10010->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType10010->GetValue());
|
|
|
|
|
auto pS10010
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject10010->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Standard"), pS10010->GetValue());
|
|
|
|
|
auto pKids10010
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFArrayElement*>(pObject10010->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids10010);
|
|
|
|
|
// assume there are no MCID ref at this level
|
|
|
|
|
auto vKids10010 = pKids10010->GetElements();
|
|
|
|
|
// only one span
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(1), vKids10010.size());
|
|
|
|
|
|
|
|
|
|
auto pRefKid100100 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10010[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid100100);
|
|
|
|
|
auto pObject100100 = pRefKid100100->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject100100);
|
|
|
|
|
auto pType100100
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject100100->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType100100->GetValue());
|
|
|
|
|
auto pS100100
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject100100->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Span"), pS100100->GetValue());
|
|
|
|
|
// this span exists because of lang
|
|
|
|
|
auto pLang100100 = dynamic_cast<vcl::filter::PDFLiteralStringElement*>(
|
|
|
|
|
pObject100100->Lookup("Lang"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("en-GB"), pLang100100->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pRefKid101 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10[1]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid101);
|
|
|
|
|
auto pObject101 = pRefKid101->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject101);
|
|
|
|
|
auto pType101
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject101->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType101->GetValue());
|
|
|
|
|
auto pS101 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject101->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("LI"), pS101->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pKids101
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFArrayElement*>(pObject101->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids101);
|
|
|
|
|
// assume there are no MCID ref at this level
|
|
|
|
|
auto vKids101 = pKids101->GetElements();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(2), vKids101.size());
|
|
|
|
|
|
|
|
|
|
auto pRefKid1010 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids101[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid1010);
|
|
|
|
|
auto pObject1010 = pRefKid1010->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject1010);
|
|
|
|
|
auto pType1010
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject1010->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1010->GetValue());
|
|
|
|
|
auto pS1010 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1010->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Lbl"), pS1010->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pRefKid1011 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids101[1]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid1011);
|
|
|
|
|
auto pObject1011 = pRefKid1011->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject1011);
|
|
|
|
|
auto pType1011
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject1011->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1011->GetValue());
|
|
|
|
|
auto pS1011 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1011->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("LBody"), pS1011->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pKids1011
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFArrayElement*>(pObject1011->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids1011);
|
|
|
|
|
// assume there are no MCID ref at this level
|
|
|
|
|
auto vKids1011 = pKids1011->GetElements();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(1), vKids1011.size());
|
|
|
|
|
|
|
|
|
|
auto pRefKid10110 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids1011[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid10110);
|
|
|
|
|
auto pObject10110 = pRefKid10110->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject10110);
|
|
|
|
|
auto pType10110
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject10110->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType10110->GetValue());
|
|
|
|
|
auto pS10110
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject10110->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Standard"), pS10110->GetValue());
|
|
|
|
|
auto pKids10110
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFArrayElement*>(pObject10110->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids10110);
|
|
|
|
|
auto vKids10110 = pKids10110->GetElements();
|
|
|
|
|
// only MCIDs, no span
|
|
|
|
|
for (size_t i = 0; i < vKids10110.size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
auto pKid = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10110[i]);
|
|
|
|
|
CPPUNIT_ASSERT(!pKid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto pRefKid102 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10[2]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid102);
|
|
|
|
|
auto pObject102 = pRefKid102->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject102);
|
|
|
|
|
auto pType102
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject102->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType102->GetValue());
|
|
|
|
|
auto pS102 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject102->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("LI"), pS102->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pKids102
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFArrayElement*>(pObject102->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids102);
|
|
|
|
|
// assume there are no MCID ref at this level
|
|
|
|
|
auto vKids102 = pKids102->GetElements();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(2), vKids102.size());
|
|
|
|
|
|
|
|
|
|
auto pRefKid1020 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids102[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid1020);
|
|
|
|
|
auto pObject1020 = pRefKid1020->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject1020);
|
|
|
|
|
auto pType1020
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject1020->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1020->GetValue());
|
|
|
|
|
auto pS1020 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1020->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Lbl"), pS1020->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pRefKid1021 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids102[1]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid1021);
|
|
|
|
|
auto pObject1021 = pRefKid1021->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject1021);
|
|
|
|
|
auto pType1021
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject1021->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1021->GetValue());
|
|
|
|
|
auto pS1021 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1021->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("LBody"), pS1021->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pKids1021
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFArrayElement*>(pObject1021->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids1021);
|
|
|
|
|
// assume there are no MCID ref at this level
|
|
|
|
|
auto vKids1021 = pKids1021->GetElements();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(1), vKids1021.size());
|
|
|
|
|
|
|
|
|
|
auto pRefKid10210 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids1021[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid10210);
|
|
|
|
|
auto pObject10210 = pRefKid10210->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject10210);
|
|
|
|
|
auto pType10210
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject10210->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType10210->GetValue());
|
|
|
|
|
auto pS10210
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject10210->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Standard"), pS10210->GetValue());
|
|
|
|
|
auto pKids10210
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFArrayElement*>(pObject10210->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids10210);
|
|
|
|
|
// assume there are no MCID ref at this level
|
|
|
|
|
auto vKids10210 = pKids10210->GetElements();
|
|
|
|
|
// 2 span and a hyperlink
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(3), vKids10210.size());
|
|
|
|
|
|
|
|
|
|
auto pRefKid102100 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10210[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid102100);
|
|
|
|
|
auto pObject102100 = pRefKid102100->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject102100);
|
|
|
|
|
auto pType102100
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject102100->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType102100->GetValue());
|
|
|
|
|
auto pS102100
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject102100->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Span"), pS102100->GetValue());
|
|
|
|
|
auto pKids102100
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFArrayElement*>(pObject102100->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids102100);
|
|
|
|
|
auto vKids102100 = pKids102100->GetElements();
|
|
|
|
|
for (size_t i = 0; i < vKids102100.size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
auto pKid = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids102100[i]);
|
|
|
|
|
CPPUNIT_ASSERT(!pKid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto pRefKid102101 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10210[1]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid102101);
|
|
|
|
|
auto pObject102101 = pRefKid102101->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject102101);
|
|
|
|
|
auto pType102101
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject102101->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType102101->GetValue());
|
|
|
|
|
auto pS102101
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject102101->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Link"), pS102101->GetValue());
|
|
|
|
|
auto pKids102101
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFArrayElement*>(pObject102101->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids102101);
|
|
|
|
|
auto vKids102101 = pKids102101->GetElements();
|
|
|
|
|
auto nRef(0);
|
|
|
|
|
for (size_t i = 0; i < vKids102101.size(); ++i)
|
|
|
|
|
{
|
2023-11-02 16:34:56 +01:00
|
|
|
|
auto pKid = dynamic_cast<vcl::filter::PDFDictionaryElement*>(vKids102101[i]);
|
2023-10-30 16:09:34 +01:00
|
|
|
|
if (pKid)
|
|
|
|
|
{
|
|
|
|
|
++nRef; // annotation
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef);
|
|
|
|
|
|
|
|
|
|
auto pRefKid102102 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10210[2]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid102102);
|
|
|
|
|
auto pObject102102 = pRefKid102102->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject102102);
|
|
|
|
|
auto pType102102
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject102102->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType102102->GetValue());
|
|
|
|
|
auto pS102102
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject102102->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Span"), pS102102->GetValue());
|
|
|
|
|
auto pKids102102
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFArrayElement*>(pObject102102->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids102102);
|
|
|
|
|
auto vKids102102 = pKids102102->GetElements();
|
|
|
|
|
// there is a footnote
|
|
|
|
|
auto nFtn(0);
|
|
|
|
|
for (size_t i = 0; i < vKids102102.size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
auto pKid = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids102102[i]);
|
|
|
|
|
if (pKid)
|
|
|
|
|
{
|
|
|
|
|
auto pObject = pKid->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject);
|
|
|
|
|
auto pType
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType->GetValue());
|
|
|
|
|
auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Link"), pS->GetValue());
|
|
|
|
|
++nFtn;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nFtn)>(1), nFtn);
|
|
|
|
|
|
|
|
|
|
auto pRefKid103 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10[3]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid103);
|
|
|
|
|
auto pObject103 = pRefKid103->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject103);
|
|
|
|
|
auto pType103
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject103->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType103->GetValue());
|
|
|
|
|
auto pS103 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject103->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("LI"), pS103->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pKids103
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFArrayElement*>(pObject103->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids103);
|
|
|
|
|
// assume there are no MCID ref at this level
|
|
|
|
|
auto vKids103 = pKids103->GetElements();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(2), vKids103.size());
|
|
|
|
|
|
|
|
|
|
auto pRefKid1030 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids103[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid1030);
|
|
|
|
|
auto pObject1030 = pRefKid1030->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject1030);
|
|
|
|
|
auto pType1030
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject1030->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1030->GetValue());
|
|
|
|
|
auto pS1030 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1030->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Lbl"), pS1030->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pRefKid1031 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids103[1]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid1031);
|
|
|
|
|
auto pObject1031 = pRefKid1031->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject1031);
|
|
|
|
|
auto pType1031
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject1031->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1031->GetValue());
|
|
|
|
|
auto pS1031 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1031->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("LBody"), pS1031->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pKids1031
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFArrayElement*>(pObject1031->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids1031);
|
|
|
|
|
// assume there are no MCID ref at this level
|
|
|
|
|
auto vKids1031 = pKids1031->GetElements();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(1), vKids1031.size());
|
|
|
|
|
|
|
|
|
|
auto pRefKid10310 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids1031[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid10310);
|
|
|
|
|
auto pObject10310 = pRefKid10310->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject10310);
|
|
|
|
|
auto pType10310
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject10310->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType10310->GetValue());
|
|
|
|
|
auto pS10310
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject10310->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Standard"), pS10310->GetValue());
|
|
|
|
|
auto pKids10310
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFArrayElement*>(pObject10310->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids10310);
|
|
|
|
|
// assume there are no MCID ref at this level
|
|
|
|
|
auto vKids10310 = pKids10310->GetElements();
|
|
|
|
|
// only one span, following a MCID for some strike-out gap
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(2), vKids10310.size());
|
|
|
|
|
|
|
|
|
|
auto pRefKid103100 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10310[0]);
|
|
|
|
|
CPPUNIT_ASSERT(!pRefKid103100);
|
|
|
|
|
|
|
|
|
|
auto pRefKid103101 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10310[1]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid103101);
|
|
|
|
|
auto pObject103101 = pRefKid103101->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject103101);
|
|
|
|
|
auto pType103101
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject103101->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType103101->GetValue());
|
|
|
|
|
auto pS103101
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject103101->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Span"), pS103101->GetValue());
|
|
|
|
|
auto pDictA103101
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFDictionaryElement*>(pObject103101->Lookup("A"));
|
|
|
|
|
CPPUNIT_ASSERT(pDictA103101 != nullptr);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Layout"), dynamic_cast<vcl::filter::PDFNameElement*>(
|
|
|
|
|
pDictA103101->LookupElement("O"))
|
|
|
|
|
->GetValue());
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("LineThrough"),
|
|
|
|
|
dynamic_cast<vcl::filter::PDFNameElement*>(
|
|
|
|
|
pDictA103101->LookupElement("TextDecorationType"))
|
|
|
|
|
->GetValue());
|
|
|
|
|
|
|
|
|
|
// now the footnote container - following the list
|
|
|
|
|
auto pRefKid11 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids1[1]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid11);
|
|
|
|
|
auto pObject11 = pRefKid11->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject11);
|
|
|
|
|
auto pType11
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject11->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType11->GetValue());
|
|
|
|
|
auto pS11 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject11->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Div"), pS11->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pKids11 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject11->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids11);
|
|
|
|
|
// assume there are no MCID ref at this level
|
|
|
|
|
auto vKids11 = pKids11->GetElements();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(1), vKids11.size());
|
|
|
|
|
|
|
|
|
|
auto pRefKid110 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids11[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid110);
|
|
|
|
|
auto pObject110 = pRefKid110->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject110);
|
|
|
|
|
auto pType110
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject110->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType110->GetValue());
|
|
|
|
|
auto pS110 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject110->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Note"), pS110->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pKids110
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFArrayElement*>(pObject110->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids110);
|
|
|
|
|
// assume there are no MCID ref at this level
|
|
|
|
|
auto vKids110 = pKids110->GetElements();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(2), vKids110.size());
|
|
|
|
|
|
|
|
|
|
auto pRefKid1100 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids110[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid1100);
|
|
|
|
|
auto pObject1100 = pRefKid1100->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject1100);
|
|
|
|
|
auto pType1100
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject1100->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1100->GetValue());
|
|
|
|
|
auto pS1100 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1100->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Lbl"), pS1100->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pKids1100
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFArrayElement*>(pObject1100->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids1100);
|
|
|
|
|
// assume there are no MCID ref at this level
|
|
|
|
|
auto vKids1100 = pKids1100->GetElements();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(1), vKids1100.size());
|
|
|
|
|
|
|
|
|
|
auto pRefKid11000 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids1100[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid11000);
|
|
|
|
|
auto pObject11000 = pRefKid11000->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject11000);
|
|
|
|
|
auto pType11000
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject11000->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType11000->GetValue());
|
|
|
|
|
auto pS11000
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject11000->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Link"), pS11000->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pRefKid1101 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids110[1]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid1101);
|
|
|
|
|
auto pObject1101 = pRefKid1101->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject1101);
|
|
|
|
|
auto pType1101
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject1101->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1101->GetValue());
|
|
|
|
|
auto pS1101 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1101->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Footnote"), pS1101->GetValue());
|
|
|
|
|
|
|
|
|
|
++nDoc;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nDoc)>(1), nDoc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf157182)
|
|
|
|
|
{
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
|
|
|
|
|
uno::Sequence<beans::PropertyValue> aFilterData(comphelper::InitPropertySequence({
|
|
|
|
|
{ "PDFUACompliance", uno::Any(true) },
|
|
|
|
|
// only happens with PDF/A-1
|
|
|
|
|
{ "SelectPdfVersion", uno::Any(static_cast<sal_Int32>(1)) },
|
|
|
|
|
}));
|
|
|
|
|
aMediaDescriptor["FilterData"] <<= aFilterData;
|
|
|
|
|
|
|
|
|
|
saveAsPDF(u"transparentshape.fodp");
|
|
|
|
|
|
|
|
|
|
// just check this does not crash or assert
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf57423)
|
|
|
|
|
{
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
|
|
|
|
|
// Enable PDF/UA
|
|
|
|
|
uno::Sequence<beans::PropertyValue> aFilterData(
|
|
|
|
|
comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
|
|
|
|
|
aMediaDescriptor["FilterData"] <<= aFilterData;
|
|
|
|
|
saveAsPDF(u"Description PDF Export test .odt");
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
|
|
|
|
|
CPPUNIT_ASSERT(aDocument.Read(aStream));
|
|
|
|
|
|
|
|
|
|
// The document has one page.
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
|
|
|
|
|
|
|
|
|
|
int nFigure(0);
|
|
|
|
|
int nFormula(0);
|
|
|
|
|
int nDiv(0);
|
|
|
|
|
for (const auto& rDocElement : aDocument.GetElements())
|
|
|
|
|
{
|
|
|
|
|
auto pObject = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
|
|
|
|
|
if (!pObject)
|
|
|
|
|
continue;
|
|
|
|
|
auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Type"));
|
|
|
|
|
if (pType && pType->GetValue() == "StructElem")
|
|
|
|
|
{
|
|
|
|
|
auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("S"));
|
|
|
|
|
if (pS && pS->GetValue() == "Figure")
|
|
|
|
|
{
|
|
|
|
|
switch (nFigure)
|
|
|
|
|
{
|
|
|
|
|
case 2:
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(u"QR Code - Tells how to get to Mosegaard"_ustr,
|
|
|
|
|
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
|
|
|
|
|
*dynamic_cast<vcl::filter::PDFHexStringElement*>(
|
|
|
|
|
pObject->Lookup("Alt"))));
|
|
|
|
|
break;
|
|
|
|
|
case 0:
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(u"Title: Arrows - Description: Explains the "
|
|
|
|
|
u"different arrow appearances"_ustr,
|
|
|
|
|
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
|
|
|
|
|
*dynamic_cast<vcl::filter::PDFHexStringElement*>(
|
|
|
|
|
pObject->Lookup("Alt"))));
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(
|
|
|
|
|
u"My blue triangle - Does not need further description"_ustr,
|
|
|
|
|
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
|
|
|
|
|
*dynamic_cast<vcl::filter::PDFHexStringElement*>(
|
|
|
|
|
pObject->Lookup("Alt"))));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
++nFigure;
|
|
|
|
|
}
|
|
|
|
|
if (pS && pS->GetValue() == "Formula")
|
|
|
|
|
{
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(
|
|
|
|
|
u"Equation 1 - Now we give the full description of eq 1 here"_ustr,
|
|
|
|
|
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
|
|
|
|
|
*dynamic_cast<vcl::filter::PDFHexStringElement*>(pObject->Lookup("Alt"))));
|
|
|
|
|
++nFormula;
|
|
|
|
|
}
|
|
|
|
|
if (pS && pS->GetValue() == "Div")
|
|
|
|
|
{
|
|
|
|
|
switch (nDiv)
|
|
|
|
|
{
|
|
|
|
|
case 0:
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(u"This frame has a description"_ustr,
|
|
|
|
|
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
|
|
|
|
|
*dynamic_cast<vcl::filter::PDFHexStringElement*>(
|
|
|
|
|
pObject->Lookup("Alt"))));
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
// no properties set on this
|
|
|
|
|
CPPUNIT_ASSERT(!pObject->Lookup("Alt"));
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(u"My textbox - Has a light background"_ustr,
|
|
|
|
|
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
|
|
|
|
|
*dynamic_cast<vcl::filter::PDFHexStringElement*>(
|
|
|
|
|
pObject->Lookup("Alt"))));
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(u"Hey! There is no alternate text for Frame "
|
|
|
|
|
u"// but maybe not needed?"_ustr,
|
|
|
|
|
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
|
|
|
|
|
*dynamic_cast<vcl::filter::PDFHexStringElement*>(
|
|
|
|
|
pObject->Lookup("Alt"))));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
++nDiv;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(int(3), nFigure);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(int(1), nFormula);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(int(4), nDiv);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf154982)
|
|
|
|
|
{
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
|
|
|
|
|
// Enable PDF/UA
|
|
|
|
|
uno::Sequence<beans::PropertyValue> aFilterData(
|
|
|
|
|
comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
|
|
|
|
|
aMediaDescriptor["FilterData"] <<= aFilterData;
|
|
|
|
|
saveAsPDF(u"tdf154982.odt");
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
|
|
|
|
|
CPPUNIT_ASSERT(aDocument.Read(aStream));
|
|
|
|
|
|
|
|
|
|
// The document has one page.
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
|
|
|
|
|
|
|
|
|
|
int nFigure(0);
|
|
|
|
|
for (const auto& rDocElement : aDocument.GetElements())
|
|
|
|
|
{
|
|
|
|
|
auto pObject = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
|
|
|
|
|
if (!pObject)
|
|
|
|
|
continue;
|
|
|
|
|
auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Type"));
|
|
|
|
|
if (pType && pType->GetValue() == "StructElem")
|
|
|
|
|
{
|
|
|
|
|
auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("S"));
|
|
|
|
|
if (pS && pS->GetValue() == "Figure")
|
|
|
|
|
{
|
|
|
|
|
switch (nFigure)
|
|
|
|
|
{
|
|
|
|
|
case 0:
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(u"Here comes the signature - Please sign here"_ustr,
|
|
|
|
|
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
|
|
|
|
|
*dynamic_cast<vcl::filter::PDFHexStringElement*>(
|
|
|
|
|
pObject->Lookup("Alt"))));
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(u"Home"_ustr,
|
|
|
|
|
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
|
|
|
|
|
*dynamic_cast<vcl::filter::PDFHexStringElement*>(
|
|
|
|
|
pObject->Lookup("Alt"))));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// the problem was that the figures in the hell layer were not
|
|
|
|
|
// below their anchor paragraphs in the structure tree
|
|
|
|
|
auto pParentRef
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFReferenceElement*>(pObject->Lookup("P"));
|
|
|
|
|
CPPUNIT_ASSERT(pParentRef);
|
|
|
|
|
auto pParent(pParentRef->LookupObject());
|
|
|
|
|
CPPUNIT_ASSERT(pParent);
|
|
|
|
|
auto pParentType
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pParent->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pParentType->GetValue());
|
|
|
|
|
auto pParentS = dynamic_cast<vcl::filter::PDFNameElement*>(pParent->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Standard"), pParentS->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pPParentRef
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFReferenceElement*>(pParent->Lookup("P"));
|
|
|
|
|
CPPUNIT_ASSERT(pPParentRef);
|
|
|
|
|
auto pPParent(pPParentRef->LookupObject());
|
|
|
|
|
CPPUNIT_ASSERT(pPParent);
|
|
|
|
|
auto pPParentType
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pPParent->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pPParentType->GetValue());
|
|
|
|
|
auto pPParentS = dynamic_cast<vcl::filter::PDFNameElement*>(pPParent->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Document"), pPParentS->GetValue());
|
|
|
|
|
++nFigure;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(int(2), nFigure);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf157397)
|
|
|
|
|
{
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
|
|
|
|
|
// Enable PDF/UA
|
|
|
|
|
uno::Sequence<beans::PropertyValue> aFilterData(
|
|
|
|
|
comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
|
|
|
|
|
aMediaDescriptor["FilterData"] <<= aFilterData;
|
|
|
|
|
saveAsPDF(u"PDF_export_with_formcontrol.fodt");
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
|
|
|
|
|
CPPUNIT_ASSERT(aDocument.Read(aStream));
|
|
|
|
|
|
|
|
|
|
// The document has one page.
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFObjectElement* pDocument(nullptr);
|
|
|
|
|
for (const auto& rDocElement : aDocument.GetElements())
|
|
|
|
|
{
|
|
|
|
|
auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
|
|
|
|
|
if (!pObject1)
|
|
|
|
|
continue;
|
|
|
|
|
auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
|
|
|
|
|
if (pType1 && pType1->GetValue() == "StructElem")
|
|
|
|
|
{
|
|
|
|
|
auto pS1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("S"));
|
|
|
|
|
if (pS1 && pS1->GetValue() == "Document")
|
|
|
|
|
{
|
|
|
|
|
pDocument = pObject1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT(pDocument);
|
|
|
|
|
|
|
|
|
|
auto pKids1 = dynamic_cast<vcl::filter::PDFArrayElement*>(pDocument->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids1);
|
|
|
|
|
// assume there are no MCID ref at this level
|
|
|
|
|
auto pKids1v = pKids1->GetElements();
|
|
|
|
|
auto pRefKid12 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids1v[2]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid12);
|
|
|
|
|
auto pObject12 = pRefKid12->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject12);
|
|
|
|
|
auto pType12 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject12->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType12->GetValue());
|
|
|
|
|
auto pS12 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject12->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Text#20body"), pS12->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pKids12 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject12->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids12);
|
|
|
|
|
// assume there are no MCID ref at this level
|
|
|
|
|
auto pKids12v = pKids12->GetElements();
|
|
|
|
|
auto pRefKid120 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids12v[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid120);
|
|
|
|
|
auto pObject120 = pRefKid120->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject120);
|
|
|
|
|
auto pType120 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject120->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType120->GetValue());
|
|
|
|
|
auto pS120 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject120->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Link"), pS120->GetValue());
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
auto pKids = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject120->Lookup("K"));
|
|
|
|
|
auto nMCID(0);
|
|
|
|
|
auto nRef(0);
|
|
|
|
|
for (size_t i = 0; i < pKids->GetElements().size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
auto pNum = dynamic_cast<vcl::filter::PDFNumberElement*>(pKids->GetElement(i));
|
2023-11-02 16:34:56 +01:00
|
|
|
|
auto pObjR = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pKids->GetElement(i));
|
2023-10-30 16:09:34 +01:00
|
|
|
|
if (pNum)
|
|
|
|
|
{
|
|
|
|
|
++nMCID;
|
|
|
|
|
}
|
2023-11-02 16:34:56 +01:00
|
|
|
|
if (pObjR)
|
2023-10-30 16:09:34 +01:00
|
|
|
|
{
|
|
|
|
|
++nRef;
|
2023-11-02 16:34:56 +01:00
|
|
|
|
auto pOType
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObjR->LookupElement("Type"));
|
2023-10-30 16:09:34 +01:00
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("OBJR"), pOType->GetValue());
|
|
|
|
|
auto pAnnotRef
|
2023-11-02 16:34:56 +01:00
|
|
|
|
= dynamic_cast<vcl::filter::PDFReferenceElement*>(pObjR->LookupElement("Obj"));
|
2023-10-30 16:09:34 +01:00
|
|
|
|
auto pAnnot = pAnnotRef->LookupObject();
|
|
|
|
|
auto pAType = dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Annot"), pAType->GetValue());
|
|
|
|
|
auto pASubtype
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Link"), pASubtype->GetValue());
|
|
|
|
|
auto pAContents
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFHexStringElement*>(pAnnot->Lookup("Contents"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(
|
|
|
|
|
u"https://klexikon.zum.de/wiki/Kläranlage"_ustr,
|
|
|
|
|
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Link"), pASubtype->GetValue());
|
|
|
|
|
auto pAA = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pAnnot->Lookup("A"));
|
|
|
|
|
CPPUNIT_ASSERT(pAA);
|
|
|
|
|
auto pAAType
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pAA->LookupElement("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Action"), pAAType->GetValue());
|
|
|
|
|
auto pAAS = dynamic_cast<vcl::filter::PDFNameElement*>(pAA->LookupElement("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("URI"), pAAS->GetValue());
|
|
|
|
|
auto pAAURI = dynamic_cast<vcl::filter::PDFLiteralStringElement*>(
|
|
|
|
|
pAA->LookupElement("URI"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("https://klexikon.zum.de/wiki/Kl%C3%A4ranlage"),
|
|
|
|
|
pAAURI->GetValue());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID)>(1), nMCID);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto pRefKid13 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids1v[3]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid13);
|
|
|
|
|
auto pObject13 = pRefKid13->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject13);
|
|
|
|
|
auto pType13 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject13->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType13->GetValue());
|
|
|
|
|
auto pS13 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject13->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Text#20body"), pS13->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pKids13 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject13->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids13);
|
|
|
|
|
// assume there are no MCID ref at this level
|
|
|
|
|
auto pKids13v = pKids13->GetElements();
|
|
|
|
|
auto pRefKid130 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids13v[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid130);
|
|
|
|
|
auto pObject130 = pRefKid130->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject130);
|
|
|
|
|
auto pType130 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject130->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType130->GetValue());
|
|
|
|
|
auto pS130 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject130->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Link"), pS130->GetValue());
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
auto pKids = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject130->Lookup("K"));
|
|
|
|
|
auto nMCID(0);
|
|
|
|
|
auto nRef(0);
|
|
|
|
|
for (size_t i = 0; i < pKids->GetElements().size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
auto pNum = dynamic_cast<vcl::filter::PDFNumberElement*>(pKids->GetElement(i));
|
2023-11-02 16:34:56 +01:00
|
|
|
|
auto pObjR = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pKids->GetElement(i));
|
2023-10-30 16:09:34 +01:00
|
|
|
|
if (pNum)
|
|
|
|
|
{
|
|
|
|
|
++nMCID;
|
|
|
|
|
}
|
2023-11-02 16:34:56 +01:00
|
|
|
|
if (pObjR)
|
2023-10-30 16:09:34 +01:00
|
|
|
|
{
|
|
|
|
|
++nRef;
|
2023-11-02 16:34:56 +01:00
|
|
|
|
auto pOType
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObjR->LookupElement("Type"));
|
2023-10-30 16:09:34 +01:00
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("OBJR"), pOType->GetValue());
|
|
|
|
|
auto pAnnotRef
|
2023-11-02 16:34:56 +01:00
|
|
|
|
= dynamic_cast<vcl::filter::PDFReferenceElement*>(pObjR->LookupElement("Obj"));
|
2023-10-30 16:09:34 +01:00
|
|
|
|
auto pAnnot = pAnnotRef->LookupObject();
|
|
|
|
|
auto pAType = dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Annot"), pAType->GetValue());
|
|
|
|
|
auto pASubtype
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Link"), pASubtype->GetValue());
|
|
|
|
|
auto pAContents
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFHexStringElement*>(pAnnot->Lookup("Contents"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(
|
|
|
|
|
u"https://de.wikipedia.org/wiki/Kläranlage#Mechanische_Vorreinigung"_ustr,
|
|
|
|
|
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Link"), pASubtype->GetValue());
|
|
|
|
|
auto pAA = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pAnnot->Lookup("A"));
|
|
|
|
|
CPPUNIT_ASSERT(pAA);
|
|
|
|
|
auto pAAType
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pAA->LookupElement("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Action"), pAAType->GetValue());
|
|
|
|
|
auto pAAS = dynamic_cast<vcl::filter::PDFNameElement*>(pAA->LookupElement("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("URI"), pAAS->GetValue());
|
|
|
|
|
auto pAAURI = dynamic_cast<vcl::filter::PDFLiteralStringElement*>(
|
|
|
|
|
pAA->LookupElement("URI"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(
|
|
|
|
|
OString(
|
|
|
|
|
"https://de.wikipedia.org/wiki/Kl%C3%A4ranlage#Mechanische_Vorreinigung"),
|
|
|
|
|
pAAURI->GetValue());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID)>(1), nMCID);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto pRefKid14 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids1v[4]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid14);
|
|
|
|
|
auto pObject14 = pRefKid14->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject14);
|
|
|
|
|
auto pType14 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject14->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType14->GetValue());
|
|
|
|
|
auto pS14 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject14->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Text#20body"), pS14->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pKids14 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject14->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids14);
|
|
|
|
|
// assume there are no MCID ref at this level
|
|
|
|
|
auto pKids14v = pKids14->GetElements();
|
|
|
|
|
auto pRefKid140 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids14v[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid140);
|
|
|
|
|
auto pObject140 = pRefKid140->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject140);
|
|
|
|
|
auto pType140 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject140->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType140->GetValue());
|
|
|
|
|
auto pS140 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject140->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Link"), pS140->GetValue());
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
auto pKids = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject140->Lookup("K"));
|
|
|
|
|
auto nMCID(0);
|
|
|
|
|
auto nRef(0);
|
|
|
|
|
for (size_t i = 0; i < pKids->GetElements().size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
auto pNum = dynamic_cast<vcl::filter::PDFNumberElement*>(pKids->GetElement(i));
|
2023-11-02 16:34:56 +01:00
|
|
|
|
auto pObjR = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pKids->GetElement(i));
|
2023-10-30 16:09:34 +01:00
|
|
|
|
if (pNum)
|
|
|
|
|
{
|
|
|
|
|
++nMCID;
|
|
|
|
|
}
|
2023-11-02 16:34:56 +01:00
|
|
|
|
if (pObjR)
|
2023-10-30 16:09:34 +01:00
|
|
|
|
{
|
|
|
|
|
++nRef;
|
2023-11-02 16:34:56 +01:00
|
|
|
|
auto pOType
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObjR->LookupElement("Type"));
|
2023-10-30 16:09:34 +01:00
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("OBJR"), pOType->GetValue());
|
|
|
|
|
auto pAnnotRef
|
2023-11-02 16:34:56 +01:00
|
|
|
|
= dynamic_cast<vcl::filter::PDFReferenceElement*>(pObjR->LookupElement("Obj"));
|
2023-10-30 16:09:34 +01:00
|
|
|
|
auto pAnnot = pAnnotRef->LookupObject();
|
|
|
|
|
auto pAType = dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Annot"), pAType->GetValue());
|
|
|
|
|
auto pASubtype
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Link"), pASubtype->GetValue());
|
|
|
|
|
auto pAContents
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFHexStringElement*>(pAnnot->Lookup("Contents"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(
|
|
|
|
|
u"https://vr-easy.com/tour/usr/220113-virtuellerschulausflug/#pano=24"_ustr,
|
|
|
|
|
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Link"), pASubtype->GetValue());
|
|
|
|
|
auto pAA = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pAnnot->Lookup("A"));
|
|
|
|
|
CPPUNIT_ASSERT(pAA);
|
|
|
|
|
auto pAAType
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pAA->LookupElement("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Action"), pAAType->GetValue());
|
|
|
|
|
auto pAAS = dynamic_cast<vcl::filter::PDFNameElement*>(pAA->LookupElement("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("URI"), pAAS->GetValue());
|
|
|
|
|
auto pAAURI = dynamic_cast<vcl::filter::PDFLiteralStringElement*>(
|
|
|
|
|
pAA->LookupElement("URI"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(
|
|
|
|
|
OString("https://vr-easy.com/tour/usr/220113-virtuellerschulausflug/#pano=24"),
|
|
|
|
|
pAAURI->GetValue());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID)>(1), nMCID);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto pRefKid16 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids1v[6]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid16);
|
|
|
|
|
auto pObject16 = pRefKid16->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject16);
|
|
|
|
|
auto pType16 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject16->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType16->GetValue());
|
|
|
|
|
auto pS16 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject16->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Text#20body"), pS16->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pKids16 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject16->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids16);
|
|
|
|
|
// assume there are no MCID ref at this level
|
|
|
|
|
auto pKids16v = pKids16->GetElements();
|
|
|
|
|
auto pRefKid160 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids16v[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pRefKid160);
|
|
|
|
|
auto pObject160 = pRefKid160->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject160);
|
|
|
|
|
auto pType160 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject160->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType160->GetValue());
|
|
|
|
|
auto pS160 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject160->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Form"), pS160->GetValue());
|
|
|
|
|
auto pA160Dict = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pObject160->Lookup("A"));
|
|
|
|
|
CPPUNIT_ASSERT(pA160Dict);
|
|
|
|
|
auto pA160O = dynamic_cast<vcl::filter::PDFNameElement*>(pA160Dict->LookupElement("O"));
|
|
|
|
|
CPPUNIT_ASSERT(pA160O);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("PrintField"), pA160O->GetValue());
|
|
|
|
|
auto pA160Role = dynamic_cast<vcl::filter::PDFNameElement*>(pA160Dict->LookupElement("Role"));
|
|
|
|
|
CPPUNIT_ASSERT(pA160Role);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("tv"), pA160Role->GetValue());
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
auto pKids = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject160->Lookup("K"));
|
|
|
|
|
auto nMCID(0);
|
|
|
|
|
auto nRef(0);
|
|
|
|
|
for (size_t i = 0; i < pKids->GetElements().size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
auto pNum = dynamic_cast<vcl::filter::PDFNumberElement*>(pKids->GetElement(i));
|
2023-11-02 16:34:56 +01:00
|
|
|
|
auto pObjR = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pKids->GetElement(i));
|
2023-10-30 16:09:34 +01:00
|
|
|
|
if (pNum)
|
|
|
|
|
{
|
|
|
|
|
++nMCID;
|
|
|
|
|
}
|
2023-11-02 16:34:56 +01:00
|
|
|
|
if (pObjR)
|
2023-10-30 16:09:34 +01:00
|
|
|
|
{
|
|
|
|
|
++nRef;
|
2023-11-02 16:34:56 +01:00
|
|
|
|
auto pOType
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObjR->LookupElement("Type"));
|
2023-10-30 16:09:34 +01:00
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("OBJR"), pOType->GetValue());
|
|
|
|
|
auto pAnnotRef
|
2023-11-02 16:34:56 +01:00
|
|
|
|
= dynamic_cast<vcl::filter::PDFReferenceElement*>(pObjR->LookupElement("Obj"));
|
2023-10-30 16:09:34 +01:00
|
|
|
|
auto pAnnot = pAnnotRef->LookupObject();
|
|
|
|
|
auto pAType = dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Annot"), pAType->GetValue());
|
|
|
|
|
auto pASubtype
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Widget"), pASubtype->GetValue());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID)>(1), nMCID);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf135192)
|
|
|
|
|
{
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
|
|
|
|
|
// Enable PDF/UA
|
|
|
|
|
uno::Sequence<beans::PropertyValue> aFilterData(
|
|
|
|
|
comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
|
|
|
|
|
aMediaDescriptor["FilterData"] <<= aFilterData;
|
|
|
|
|
saveAsPDF(u"tdf135192-1.fodp");
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
|
|
|
|
|
CPPUNIT_ASSERT(aDocument.Read(aStream));
|
|
|
|
|
|
|
|
|
|
// The document has one page.
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
|
|
|
|
|
|
|
|
|
|
int nTable(0);
|
|
|
|
|
for (const auto& rDocElement : aDocument.GetElements())
|
|
|
|
|
{
|
|
|
|
|
auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
|
|
|
|
|
if (!pObject1)
|
|
|
|
|
continue;
|
|
|
|
|
auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
|
|
|
|
|
if (pType1 && pType1->GetValue() == "StructElem")
|
|
|
|
|
{
|
|
|
|
|
auto pS1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("S"));
|
|
|
|
|
if (pS1 && pS1->GetValue() == "Table")
|
|
|
|
|
{
|
|
|
|
|
int nTR(0);
|
|
|
|
|
auto pKids1 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject1->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids1);
|
|
|
|
|
// there can be additional children, such as MCID ref
|
|
|
|
|
for (auto pKid1 : pKids1->GetElements())
|
|
|
|
|
{
|
|
|
|
|
auto pRefKid1 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKid1);
|
|
|
|
|
if (pRefKid1)
|
|
|
|
|
{
|
|
|
|
|
auto pObject2 = pRefKid1->LookupObject();
|
|
|
|
|
if (pObject2)
|
|
|
|
|
{
|
|
|
|
|
auto pType2 = dynamic_cast<vcl::filter::PDFNameElement*>(
|
|
|
|
|
pObject2->Lookup("Type"));
|
|
|
|
|
if (pType2 && pType2->GetValue() == "StructElem")
|
|
|
|
|
{
|
|
|
|
|
auto pS2 = dynamic_cast<vcl::filter::PDFNameElement*>(
|
|
|
|
|
pObject2->Lookup("S"));
|
|
|
|
|
if (pS2 && pS2->GetValue() == "TR")
|
|
|
|
|
{
|
|
|
|
|
int nTD(0);
|
|
|
|
|
auto pKids2 = dynamic_cast<vcl::filter::PDFArrayElement*>(
|
|
|
|
|
pObject2->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids2);
|
|
|
|
|
for (auto pKid2 : pKids2->GetElements())
|
|
|
|
|
{
|
|
|
|
|
auto pRefKid2
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFReferenceElement*>(
|
|
|
|
|
pKid2);
|
|
|
|
|
if (pRefKid2)
|
|
|
|
|
{
|
|
|
|
|
auto pObject3 = pRefKid2->LookupObject();
|
|
|
|
|
if (pObject3)
|
|
|
|
|
{
|
|
|
|
|
auto pType3
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(
|
|
|
|
|
pObject3->Lookup("Type"));
|
|
|
|
|
if (pType3 && pType3->GetValue() == "StructElem")
|
|
|
|
|
{
|
|
|
|
|
auto pS3 = dynamic_cast<
|
|
|
|
|
vcl::filter::PDFNameElement*>(
|
|
|
|
|
pObject3->Lookup("S"));
|
|
|
|
|
if (nTR == 0 && pS3 && pS3->GetValue() == "TH")
|
|
|
|
|
{
|
|
|
|
|
int nOTable(0);
|
|
|
|
|
auto pAttrs = dynamic_cast<
|
|
|
|
|
vcl::filter::PDFArrayElement*>(
|
|
|
|
|
pObject3->Lookup("A"));
|
|
|
|
|
CPPUNIT_ASSERT(pAttrs != nullptr);
|
|
|
|
|
for (const auto& rAttrRef :
|
|
|
|
|
pAttrs->GetElements())
|
|
|
|
|
{
|
|
|
|
|
auto pAttrDict = dynamic_cast<
|
|
|
|
|
vcl::filter::PDFDictionaryElement*>(
|
|
|
|
|
rAttrRef);
|
|
|
|
|
CPPUNIT_ASSERT(pAttrDict != nullptr);
|
|
|
|
|
auto pOwner = dynamic_cast<
|
|
|
|
|
vcl::filter::PDFNameElement*>(
|
|
|
|
|
pAttrDict->LookupElement("O"));
|
|
|
|
|
CPPUNIT_ASSERT(pOwner != nullptr);
|
|
|
|
|
if (pOwner->GetValue() == "Table")
|
|
|
|
|
{
|
|
|
|
|
auto pScope = dynamic_cast<
|
|
|
|
|
vcl::filter::PDFNameElement*>(
|
|
|
|
|
pAttrDict->LookupElement(
|
|
|
|
|
"Scope"));
|
|
|
|
|
CPPUNIT_ASSERT(pScope != nullptr);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(
|
|
|
|
|
OString("Column"),
|
|
|
|
|
pScope->GetValue());
|
|
|
|
|
++nOTable;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(int(1), nOTable);
|
|
|
|
|
++nTD;
|
|
|
|
|
}
|
|
|
|
|
else if (nTR != 0 && pS3
|
|
|
|
|
&& pS3->GetValue() == "TD")
|
|
|
|
|
{
|
|
|
|
|
++nTD;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(int(3), nTD);
|
|
|
|
|
++nTR;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(int(2), nTR);
|
|
|
|
|
++nTable;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(int(1), nTable);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf154955)
|
|
|
|
|
{
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
|
|
|
|
|
// Enable PDF/UA
|
|
|
|
|
uno::Sequence<beans::PropertyValue> aFilterData(
|
|
|
|
|
comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
|
|
|
|
|
aMediaDescriptor["FilterData"] <<= aFilterData;
|
|
|
|
|
saveAsPDF(u"grouped-shape.fodt");
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
|
|
|
|
|
CPPUNIT_ASSERT(aDocument.Read(aStream));
|
|
|
|
|
|
|
|
|
|
// The document has one page.
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFObjectElement* pContents = aPages[0]->LookupObject("Contents");
|
|
|
|
|
CPPUNIT_ASSERT(pContents);
|
|
|
|
|
vcl::filter::PDFStreamElement* pStream = pContents->GetStream();
|
|
|
|
|
CPPUNIT_ASSERT(pStream);
|
|
|
|
|
SvMemoryStream& rObjectStream = pStream->GetMemory();
|
|
|
|
|
// Uncompress it.
|
|
|
|
|
SvMemoryStream aUncompressed;
|
|
|
|
|
ZCodec aZCodec;
|
|
|
|
|
aZCodec.BeginCompression();
|
|
|
|
|
rObjectStream.Seek(0);
|
|
|
|
|
aZCodec.Decompress(rObjectStream, aUncompressed);
|
|
|
|
|
CPPUNIT_ASSERT(aZCodec.EndCompression());
|
|
|
|
|
|
|
|
|
|
auto pStart = static_cast<const char*>(aUncompressed.GetData());
|
|
|
|
|
const char* const pEnd = pStart + aUncompressed.GetSize();
|
|
|
|
|
|
|
|
|
|
enum
|
|
|
|
|
{
|
|
|
|
|
Default,
|
|
|
|
|
Artifact,
|
|
|
|
|
Tagged
|
|
|
|
|
} state
|
|
|
|
|
= Default;
|
|
|
|
|
|
|
|
|
|
auto nLine(0);
|
|
|
|
|
auto nTagged(0);
|
|
|
|
|
auto nArtifacts(0);
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
++nLine;
|
|
|
|
|
auto const pLine = ::std::find(pStart, pEnd, '\n');
|
|
|
|
|
if (pLine == pEnd)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
std::string_view const line(pStart, pLine - pStart);
|
|
|
|
|
pStart = pLine + 1;
|
|
|
|
|
if (!line.empty() && line[0] != '%')
|
|
|
|
|
{
|
|
|
|
|
::std::cerr << nLine << ": " << line << "\n";
|
|
|
|
|
if (o3tl::starts_with(line, "/Artifact "))
|
|
|
|
|
{
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, state);
|
|
|
|
|
state = Artifact;
|
|
|
|
|
++nArtifacts;
|
|
|
|
|
}
|
|
|
|
|
else if (o3tl::starts_with(line, "/Figure<</MCID "))
|
|
|
|
|
{
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, state);
|
|
|
|
|
state = Tagged;
|
|
|
|
|
++nTagged;
|
|
|
|
|
}
|
|
|
|
|
else if (line == "EMC")
|
|
|
|
|
{
|
|
|
|
|
CPPUNIT_ASSERT_MESSAGE("unexpected end", state != Default);
|
|
|
|
|
state = Default;
|
|
|
|
|
}
|
|
|
|
|
else if (nLine > 1) // first line is expected "0.1 w"
|
|
|
|
|
{
|
|
|
|
|
CPPUNIT_ASSERT_MESSAGE("unexpected content outside MCS", state != Default);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("unclosed MCS", Default, state);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nTagged)>(2), nTagged);
|
|
|
|
|
CPPUNIT_ASSERT(nArtifacts >= 1);
|
|
|
|
|
|
|
|
|
|
int nFigure(0);
|
|
|
|
|
for (const auto& rDocElement : aDocument.GetElements())
|
|
|
|
|
{
|
|
|
|
|
auto pObject = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
|
|
|
|
|
if (!pObject)
|
|
|
|
|
continue;
|
|
|
|
|
auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Type"));
|
|
|
|
|
if (pType && pType->GetValue() == "StructElem")
|
|
|
|
|
{
|
|
|
|
|
auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("S"));
|
|
|
|
|
if (pS && pS->GetValue() == "Figure")
|
|
|
|
|
{
|
|
|
|
|
switch (nFigure)
|
|
|
|
|
{
|
|
|
|
|
case 0:
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(u"Two rectangles - Grouped"_ustr,
|
|
|
|
|
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
|
|
|
|
|
*dynamic_cast<vcl::filter::PDFHexStringElement*>(
|
|
|
|
|
pObject->Lookup("Alt"))));
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(u"these ones are green"_ustr,
|
|
|
|
|
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
|
|
|
|
|
*dynamic_cast<vcl::filter::PDFHexStringElement*>(
|
|
|
|
|
pObject->Lookup("Alt"))));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto pParentRef
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFReferenceElement*>(pObject->Lookup("P"));
|
|
|
|
|
CPPUNIT_ASSERT(pParentRef);
|
|
|
|
|
auto pParent(pParentRef->LookupObject());
|
|
|
|
|
CPPUNIT_ASSERT(pParent);
|
|
|
|
|
auto pParentType
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pParent->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pParentType->GetValue());
|
|
|
|
|
auto pParentS = dynamic_cast<vcl::filter::PDFNameElement*>(pParent->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Standard"), pParentS->GetValue());
|
|
|
|
|
|
|
|
|
|
++nFigure;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// the problem was that there were 4 shapes (the sub-shapes of the 2 groups)
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(int(2), nFigure);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf155190)
|
|
|
|
|
{
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
|
|
|
|
|
// Enable PDF/UA
|
|
|
|
|
uno::Sequence<beans::PropertyValue> aFilterData(
|
|
|
|
|
comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
|
|
|
|
|
aMediaDescriptor["FilterData"] <<= aFilterData;
|
|
|
|
|
|
|
|
|
|
saveAsPDF(u"tdf155190.odt");
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
|
|
|
|
|
CPPUNIT_ASSERT(aDocument.Read(aStream));
|
|
|
|
|
|
|
|
|
|
// The document has one page.
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
|
|
|
|
|
|
|
|
|
|
auto nDiv(0);
|
|
|
|
|
auto nFigure(0);
|
|
|
|
|
for (const auto& rDocElement : aDocument.GetElements())
|
|
|
|
|
{
|
|
|
|
|
auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
|
|
|
|
|
if (!pObject1)
|
|
|
|
|
continue;
|
|
|
|
|
auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
|
|
|
|
|
|
|
|
|
|
auto pS1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("S"));
|
|
|
|
|
// start with the text box
|
|
|
|
|
if (pType1 && pType1->GetValue() == "StructElem" && pS1 && pS1->GetValue() == "Div")
|
|
|
|
|
{
|
|
|
|
|
++nDiv;
|
|
|
|
|
auto pKids1 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject1->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids1);
|
|
|
|
|
for (auto pKid1 : pKids1->GetElements())
|
|
|
|
|
{
|
|
|
|
|
auto pRefKid1 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKid1);
|
|
|
|
|
if (pRefKid1)
|
|
|
|
|
{
|
|
|
|
|
auto pObject2 = pRefKid1->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject2);
|
|
|
|
|
auto pType2
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject2->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT(pType2);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType2->GetValue());
|
|
|
|
|
auto pS2 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject2->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("FigureCaption"), pS2->GetValue());
|
|
|
|
|
auto pKids2
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFArrayElement*>(pObject2->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids2);
|
|
|
|
|
// there are additional children, MCID ref
|
|
|
|
|
for (auto pKid2 : pKids2->GetElements())
|
|
|
|
|
{
|
|
|
|
|
auto pRefKid2 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKid2);
|
|
|
|
|
if (pRefKid2)
|
|
|
|
|
{
|
|
|
|
|
auto pObject3 = pRefKid2->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pObject3);
|
|
|
|
|
auto pType3 = dynamic_cast<vcl::filter::PDFNameElement*>(
|
|
|
|
|
pObject3->Lookup("Type"));
|
|
|
|
|
if (pType3 && pType3->GetValue() == "StructElem")
|
|
|
|
|
{
|
|
|
|
|
auto pS3 = dynamic_cast<vcl::filter::PDFNameElement*>(
|
|
|
|
|
pObject3->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Figure"), pS3->GetValue());
|
|
|
|
|
auto pAlt = dynamic_cast<vcl::filter::PDFHexStringElement*>(
|
|
|
|
|
pObject3->Lookup("Alt"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(
|
|
|
|
|
OUString("Picture of apples"),
|
|
|
|
|
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAlt));
|
|
|
|
|
auto pKids3 = dynamic_cast<vcl::filter::PDFArrayElement*>(
|
|
|
|
|
pObject3->Lookup("K"));
|
|
|
|
|
CPPUNIT_ASSERT(pKids3);
|
|
|
|
|
// the problem was that this didn't reference an MCID
|
|
|
|
|
CPPUNIT_ASSERT(!pKids3->GetElements().empty());
|
|
|
|
|
++nFigure;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nDiv)>(1), nDiv);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nDiv)>(1), nFigure);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testMediaShapeAnnot)
|
|
|
|
|
{
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
|
|
|
|
|
// Enable PDF/UA
|
|
|
|
|
uno::Sequence<beans::PropertyValue> aFilterData(
|
|
|
|
|
comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
|
|
|
|
|
aMediaDescriptor["FilterData"] <<= aFilterData;
|
|
|
|
|
|
|
|
|
|
saveAsPDF(u"vid.odt");
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
|
|
|
|
|
CPPUNIT_ASSERT(aDocument.Read(aStream));
|
|
|
|
|
|
|
|
|
|
// The document has one page.
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
|
|
|
|
|
|
|
|
|
|
auto pAnnots = dynamic_cast<vcl::filter::PDFArrayElement*>(aPages[0]->Lookup("Annots"));
|
|
|
|
|
CPPUNIT_ASSERT(pAnnots);
|
|
|
|
|
|
|
|
|
|
// There should be one annotation
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pAnnots->GetElements().size());
|
|
|
|
|
auto pAnnotReference
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFReferenceElement*>(pAnnots->GetElements()[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pAnnotReference);
|
|
|
|
|
// check /Annot - produced by sw
|
|
|
|
|
vcl::filter::PDFObjectElement* pAnnot = pAnnotReference->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pAnnot);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(
|
|
|
|
|
OString("Annot"),
|
|
|
|
|
static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type"))->GetValue());
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(
|
|
|
|
|
OString("Screen"),
|
|
|
|
|
static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype"))->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pA = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pAnnot->Lookup("A"));
|
|
|
|
|
CPPUNIT_ASSERT(pA);
|
|
|
|
|
auto pR = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pA->LookupElement("R"));
|
|
|
|
|
CPPUNIT_ASSERT(pR);
|
|
|
|
|
auto pC = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pR->LookupElement("C"));
|
|
|
|
|
CPPUNIT_ASSERT(pC);
|
|
|
|
|
auto pCT = dynamic_cast<vcl::filter::PDFLiteralStringElement*>(pC->LookupElement("CT"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("video/webm"), pCT->GetValue());
|
|
|
|
|
auto pD = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pC->LookupElement("D"));
|
|
|
|
|
CPPUNIT_ASSERT(pD);
|
|
|
|
|
auto pDesc = dynamic_cast<vcl::filter::PDFHexStringElement*>(pD->LookupElement("Desc"));
|
|
|
|
|
CPPUNIT_ASSERT(pDesc);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OUString("alternativloser text\nand some description"),
|
|
|
|
|
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pDesc));
|
|
|
|
|
auto pAlt = dynamic_cast<vcl::filter::PDFArrayElement*>(pC->LookupElement("Alt"));
|
|
|
|
|
CPPUNIT_ASSERT(pAlt);
|
|
|
|
|
auto pLang = dynamic_cast<vcl::filter::PDFLiteralStringElement*>(pAlt->GetElement(0));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString(""), pLang->GetValue());
|
|
|
|
|
auto pAltText = dynamic_cast<vcl::filter::PDFHexStringElement*>(pAlt->GetElement(1));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OUString("alternativloser text\nand some description"),
|
|
|
|
|
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAltText));
|
|
|
|
|
|
|
|
|
|
auto pStructParent
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNumberElement*>(pAnnot->Lookup("StructParent"));
|
|
|
|
|
CPPUNIT_ASSERT(pStructParent);
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFReferenceElement* pStructElemRef(nullptr);
|
|
|
|
|
|
|
|
|
|
// check ParentTree to find StructElem
|
|
|
|
|
auto nRoots(0);
|
|
|
|
|
for (const auto& rDocElement : aDocument.GetElements())
|
|
|
|
|
{
|
|
|
|
|
auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
|
|
|
|
|
if (!pObject1)
|
|
|
|
|
continue;
|
|
|
|
|
auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
|
|
|
|
|
if (pType1 && pType1->GetValue() == "StructTreeRoot")
|
|
|
|
|
{
|
|
|
|
|
++nRoots;
|
|
|
|
|
auto pParentTree
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFReferenceElement*>(pObject1->Lookup("ParentTree"));
|
|
|
|
|
CPPUNIT_ASSERT(pParentTree);
|
|
|
|
|
auto pNumTree = pParentTree->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pNumTree);
|
|
|
|
|
auto pNums = dynamic_cast<vcl::filter::PDFArrayElement*>(pNumTree->Lookup("Nums"));
|
|
|
|
|
CPPUNIT_ASSERT(pNums);
|
|
|
|
|
auto nFound(0);
|
|
|
|
|
for (size_t i = 0; i < pNums->GetElements().size(); i += 2)
|
|
|
|
|
{
|
|
|
|
|
auto pI = dynamic_cast<vcl::filter::PDFNumberElement*>(pNums->GetElement(i));
|
|
|
|
|
if (pI->GetValue() == pStructParent->GetValue())
|
|
|
|
|
{
|
|
|
|
|
++nFound;
|
|
|
|
|
CPPUNIT_ASSERT(i < pNums->GetElements().size() - 1);
|
|
|
|
|
pStructElemRef
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFReferenceElement*>(pNums->GetElement(i + 1));
|
|
|
|
|
CPPUNIT_ASSERT(pStructElemRef);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nFound)>(1), nFound);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRoots)>(1), nRoots);
|
|
|
|
|
|
|
|
|
|
// check /StructElem - produced by drawinglayer
|
|
|
|
|
CPPUNIT_ASSERT(pStructElemRef);
|
|
|
|
|
auto pStructElem(pStructElemRef->LookupObject());
|
|
|
|
|
CPPUNIT_ASSERT(pStructElem);
|
|
|
|
|
|
|
|
|
|
auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pStructElem->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType->GetValue());
|
|
|
|
|
auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pStructElem->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Annot"), pS->GetValue());
|
|
|
|
|
auto pSEAlt = dynamic_cast<vcl::filter::PDFHexStringElement*>(pStructElem->Lookup("Alt"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OUString("alternativloser text - and some description"),
|
|
|
|
|
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pSEAlt));
|
|
|
|
|
auto pKids = dynamic_cast<vcl::filter::PDFArrayElement*>(pStructElem->Lookup("K"));
|
|
|
|
|
auto nMCID(0);
|
|
|
|
|
auto nRef(0);
|
|
|
|
|
for (size_t i = 0; i < pKids->GetElements().size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
auto pNum = dynamic_cast<vcl::filter::PDFNumberElement*>(pKids->GetElement(i));
|
2023-11-02 16:34:56 +01:00
|
|
|
|
auto pObjR = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pKids->GetElement(i));
|
2023-10-30 16:09:34 +01:00
|
|
|
|
if (pNum)
|
|
|
|
|
{
|
|
|
|
|
++nMCID;
|
|
|
|
|
}
|
2023-11-02 16:34:56 +01:00
|
|
|
|
if (pObjR)
|
2023-10-30 16:09:34 +01:00
|
|
|
|
{
|
|
|
|
|
++nRef;
|
2023-11-02 16:34:56 +01:00
|
|
|
|
auto pOType = dynamic_cast<vcl::filter::PDFNameElement*>(pObjR->LookupElement("Type"));
|
2023-10-30 16:09:34 +01:00
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("OBJR"), pOType->GetValue());
|
2023-11-02 16:34:56 +01:00
|
|
|
|
auto pAnnotRef
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFReferenceElement*>(pObjR->LookupElement("Obj"));
|
2023-10-30 16:09:34 +01:00
|
|
|
|
CPPUNIT_ASSERT_EQUAL(pAnnot, pAnnotRef->LookupObject());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID)>(1), nMCID);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testFlyFrameHyperlinkAnnot)
|
|
|
|
|
{
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
|
|
|
|
|
// Enable PDF/UA
|
|
|
|
|
uno::Sequence<beans::PropertyValue> aFilterData(
|
|
|
|
|
comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
|
|
|
|
|
aMediaDescriptor["FilterData"] <<= aFilterData;
|
|
|
|
|
|
|
|
|
|
saveAsPDF(u"image-hyperlink-alttext.fodt");
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
|
|
|
|
|
CPPUNIT_ASSERT(aDocument.Read(aStream));
|
|
|
|
|
|
|
|
|
|
// The document has one page.
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
|
|
|
|
|
|
|
|
|
|
auto pAnnots = dynamic_cast<vcl::filter::PDFArrayElement*>(aPages[0]->Lookup("Annots"));
|
|
|
|
|
CPPUNIT_ASSERT(pAnnots);
|
|
|
|
|
|
|
|
|
|
// There should be one annotation
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pAnnots->GetElements().size());
|
|
|
|
|
auto pAnnotReference
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFReferenceElement*>(pAnnots->GetElements()[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pAnnotReference);
|
|
|
|
|
// check /Annot - produced by sw
|
|
|
|
|
vcl::filter::PDFObjectElement* pAnnot = pAnnotReference->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pAnnot);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(
|
|
|
|
|
OString("Annot"),
|
|
|
|
|
static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type"))->GetValue());
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(
|
|
|
|
|
OString("Link"),
|
|
|
|
|
static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype"))->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pContents = dynamic_cast<vcl::filter::PDFHexStringElement*>(pAnnot->Lookup("Contents"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OUString("Image2"),
|
|
|
|
|
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pContents));
|
|
|
|
|
|
|
|
|
|
auto pStructParent
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNumberElement*>(pAnnot->Lookup("StructParent"));
|
|
|
|
|
CPPUNIT_ASSERT(pStructParent);
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFReferenceElement* pStructElemRef(nullptr);
|
|
|
|
|
|
|
|
|
|
// check ParentTree to find StructElem
|
|
|
|
|
auto nRoots(0);
|
|
|
|
|
for (const auto& rDocElement : aDocument.GetElements())
|
|
|
|
|
{
|
|
|
|
|
auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
|
|
|
|
|
if (!pObject1)
|
|
|
|
|
continue;
|
|
|
|
|
auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
|
|
|
|
|
if (pType1 && pType1->GetValue() == "StructTreeRoot")
|
|
|
|
|
{
|
|
|
|
|
++nRoots;
|
|
|
|
|
auto pParentTree
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFReferenceElement*>(pObject1->Lookup("ParentTree"));
|
|
|
|
|
CPPUNIT_ASSERT(pParentTree);
|
|
|
|
|
auto pNumTree = pParentTree->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pNumTree);
|
|
|
|
|
auto pNums = dynamic_cast<vcl::filter::PDFArrayElement*>(pNumTree->Lookup("Nums"));
|
|
|
|
|
CPPUNIT_ASSERT(pNums);
|
|
|
|
|
auto nFound(0);
|
|
|
|
|
for (size_t i = 0; i < pNums->GetElements().size(); i += 2)
|
|
|
|
|
{
|
|
|
|
|
auto pI = dynamic_cast<vcl::filter::PDFNumberElement*>(pNums->GetElement(i));
|
|
|
|
|
if (pI->GetValue() == pStructParent->GetValue())
|
|
|
|
|
{
|
|
|
|
|
++nFound;
|
|
|
|
|
CPPUNIT_ASSERT(i < pNums->GetElements().size() - 1);
|
|
|
|
|
pStructElemRef
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFReferenceElement*>(pNums->GetElement(i + 1));
|
|
|
|
|
CPPUNIT_ASSERT(pStructElemRef);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nFound)>(1), nFound);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRoots)>(1), nRoots);
|
|
|
|
|
|
|
|
|
|
// check /StructElem - produced by sw painting code
|
|
|
|
|
CPPUNIT_ASSERT(pStructElemRef);
|
|
|
|
|
auto pStructElem(pStructElemRef->LookupObject());
|
|
|
|
|
CPPUNIT_ASSERT(pStructElem);
|
|
|
|
|
|
|
|
|
|
auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pStructElem->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType->GetValue());
|
|
|
|
|
auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pStructElem->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Link"), pS->GetValue());
|
|
|
|
|
auto pKids = dynamic_cast<vcl::filter::PDFArrayElement*>(pStructElem->Lookup("K"));
|
|
|
|
|
auto nMCID(0);
|
|
|
|
|
auto nRef(0);
|
|
|
|
|
for (size_t i = 0; i < pKids->GetElements().size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
auto pNum = dynamic_cast<vcl::filter::PDFNumberElement*>(pKids->GetElement(i));
|
2023-11-02 16:34:56 +01:00
|
|
|
|
auto pObjR = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pKids->GetElement(i));
|
2023-10-30 16:09:34 +01:00
|
|
|
|
if (pNum)
|
|
|
|
|
{
|
|
|
|
|
++nMCID;
|
|
|
|
|
}
|
2023-11-02 16:34:56 +01:00
|
|
|
|
if (pObjR)
|
2023-10-30 16:09:34 +01:00
|
|
|
|
{
|
|
|
|
|
++nRef;
|
2023-11-02 16:34:56 +01:00
|
|
|
|
auto pOType = dynamic_cast<vcl::filter::PDFNameElement*>(pObjR->LookupElement("Type"));
|
2023-10-30 16:09:34 +01:00
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("OBJR"), pOType->GetValue());
|
2023-11-02 16:34:56 +01:00
|
|
|
|
auto pAnnotRef
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFReferenceElement*>(pObjR->LookupElement("Obj"));
|
2023-10-30 16:09:34 +01:00
|
|
|
|
CPPUNIT_ASSERT_EQUAL(pAnnot, pAnnotRef->LookupObject());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID)>(1), nMCID);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef);
|
|
|
|
|
|
|
|
|
|
// the Link is inside a Figure
|
|
|
|
|
auto pParentRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(pStructElem->Lookup("P"));
|
|
|
|
|
CPPUNIT_ASSERT(pParentRef);
|
|
|
|
|
auto pParent(pParentRef->LookupObject());
|
|
|
|
|
CPPUNIT_ASSERT(pParent);
|
|
|
|
|
auto pParentType = dynamic_cast<vcl::filter::PDFNameElement*>(pParent->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pParentType->GetValue());
|
|
|
|
|
auto pParentS = dynamic_cast<vcl::filter::PDFNameElement*>(pParent->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Figure"), pParentS->GetValue());
|
|
|
|
|
auto pAlt = dynamic_cast<vcl::filter::PDFHexStringElement*>(pParent->Lookup("Alt"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OUString("Ship drawing - Very cute"),
|
|
|
|
|
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAlt));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testFormControlAnnot)
|
|
|
|
|
{
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
|
|
|
|
|
// Enable PDF/UA
|
|
|
|
|
uno::Sequence<beans::PropertyValue> aFilterData(
|
|
|
|
|
comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
|
|
|
|
|
aMediaDescriptor["FilterData"] <<= aFilterData;
|
|
|
|
|
|
|
|
|
|
saveAsPDF(u"formcontrol.fodt");
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
|
|
|
|
|
CPPUNIT_ASSERT(aDocument.Read(aStream));
|
|
|
|
|
|
|
|
|
|
// The document has one page.
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
|
|
|
|
|
|
|
|
|
|
auto pAnnots = dynamic_cast<vcl::filter::PDFArrayElement*>(aPages[0]->Lookup("Annots"));
|
|
|
|
|
CPPUNIT_ASSERT(pAnnots);
|
|
|
|
|
|
|
|
|
|
// There should be one annotation
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pAnnots->GetElements().size());
|
|
|
|
|
auto pAnnotReference
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFReferenceElement*>(pAnnots->GetElements()[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pAnnotReference);
|
|
|
|
|
// check /Annot
|
|
|
|
|
vcl::filter::PDFObjectElement* pAnnot = pAnnotReference->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pAnnot);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(
|
|
|
|
|
OString("Annot"),
|
|
|
|
|
static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type"))->GetValue());
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(
|
|
|
|
|
OString("Widget"),
|
|
|
|
|
static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype"))->GetValue());
|
|
|
|
|
auto pT = dynamic_cast<vcl::filter::PDFLiteralStringElement*>(pAnnot->Lookup("T"));
|
|
|
|
|
CPPUNIT_ASSERT(pT);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Check Box 1"), pT->GetValue());
|
|
|
|
|
auto pTU = dynamic_cast<vcl::filter::PDFHexStringElement*>(pAnnot->Lookup("TU"));
|
|
|
|
|
CPPUNIT_ASSERT(pTU);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OUString("helpful text"),
|
|
|
|
|
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pTU));
|
|
|
|
|
|
|
|
|
|
auto pStructParent
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNumberElement*>(pAnnot->Lookup("StructParent"));
|
|
|
|
|
CPPUNIT_ASSERT(pStructParent);
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFReferenceElement* pStructElemRef(nullptr);
|
|
|
|
|
|
|
|
|
|
// check ParentTree to find StructElem
|
|
|
|
|
auto nRoots(0);
|
|
|
|
|
for (const auto& rDocElement : aDocument.GetElements())
|
|
|
|
|
{
|
|
|
|
|
auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
|
|
|
|
|
if (!pObject1)
|
|
|
|
|
continue;
|
|
|
|
|
auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
|
|
|
|
|
if (pType1 && pType1->GetValue() == "StructTreeRoot")
|
|
|
|
|
{
|
|
|
|
|
++nRoots;
|
|
|
|
|
auto pParentTree
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFReferenceElement*>(pObject1->Lookup("ParentTree"));
|
|
|
|
|
CPPUNIT_ASSERT(pParentTree);
|
|
|
|
|
auto pNumTree = pParentTree->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pNumTree);
|
|
|
|
|
auto pNums = dynamic_cast<vcl::filter::PDFArrayElement*>(pNumTree->Lookup("Nums"));
|
|
|
|
|
CPPUNIT_ASSERT(pNums);
|
|
|
|
|
auto nFound(0);
|
|
|
|
|
for (size_t i = 0; i < pNums->GetElements().size(); i += 2)
|
|
|
|
|
{
|
|
|
|
|
auto pI = dynamic_cast<vcl::filter::PDFNumberElement*>(pNums->GetElement(i));
|
|
|
|
|
if (pI->GetValue() == pStructParent->GetValue())
|
|
|
|
|
{
|
|
|
|
|
++nFound;
|
|
|
|
|
CPPUNIT_ASSERT(i < pNums->GetElements().size() - 1);
|
|
|
|
|
pStructElemRef
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFReferenceElement*>(pNums->GetElement(i + 1));
|
|
|
|
|
CPPUNIT_ASSERT(pStructElemRef);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nFound)>(1), nFound);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRoots)>(1), nRoots);
|
|
|
|
|
|
|
|
|
|
// check /StructElem
|
|
|
|
|
CPPUNIT_ASSERT(pStructElemRef);
|
|
|
|
|
auto pStructElem(pStructElemRef->LookupObject());
|
|
|
|
|
CPPUNIT_ASSERT(pStructElem);
|
|
|
|
|
|
|
|
|
|
auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pStructElem->Lookup("Type"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType->GetValue());
|
|
|
|
|
auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pStructElem->Lookup("S"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Form"), pS->GetValue());
|
|
|
|
|
auto pAlt = dynamic_cast<vcl::filter::PDFHexStringElement*>(pStructElem->Lookup("Alt"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OUString("textuelle alternative - a box to check"),
|
|
|
|
|
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAlt));
|
|
|
|
|
auto pA = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pStructElem->Lookup("A"));
|
|
|
|
|
CPPUNIT_ASSERT(pA);
|
|
|
|
|
auto pO = dynamic_cast<vcl::filter::PDFNameElement*>(pA->LookupElement("O"));
|
|
|
|
|
CPPUNIT_ASSERT(pO);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("PrintField"), pO->GetValue());
|
|
|
|
|
auto pRole = dynamic_cast<vcl::filter::PDFNameElement*>(pA->LookupElement("Role"));
|
|
|
|
|
CPPUNIT_ASSERT(pRole);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("cb"), pRole->GetValue());
|
|
|
|
|
auto pKids = dynamic_cast<vcl::filter::PDFArrayElement*>(pStructElem->Lookup("K"));
|
|
|
|
|
auto nMCID(0);
|
|
|
|
|
auto nRef(0);
|
|
|
|
|
for (size_t i = 0; i < pKids->GetElements().size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
auto pNum = dynamic_cast<vcl::filter::PDFNumberElement*>(pKids->GetElement(i));
|
2023-11-02 16:34:56 +01:00
|
|
|
|
auto pObjR = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pKids->GetElement(i));
|
2023-10-30 16:09:34 +01:00
|
|
|
|
if (pNum)
|
|
|
|
|
{
|
|
|
|
|
++nMCID;
|
|
|
|
|
}
|
2023-11-02 16:34:56 +01:00
|
|
|
|
if (pObjR)
|
2023-10-30 16:09:34 +01:00
|
|
|
|
{
|
|
|
|
|
++nRef;
|
2023-11-02 16:34:56 +01:00
|
|
|
|
auto pOType = dynamic_cast<vcl::filter::PDFNameElement*>(pObjR->LookupElement("Type"));
|
2023-10-30 16:09:34 +01:00
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("OBJR"), pOType->GetValue());
|
2023-11-02 16:34:56 +01:00
|
|
|
|
auto pAnnotRef
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFReferenceElement*>(pObjR->LookupElement("Obj"));
|
2023-10-30 16:09:34 +01:00
|
|
|
|
CPPUNIT_ASSERT_EQUAL(pAnnot, pAnnotRef->LookupObject());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID)>(1), nMCID);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf142129)
|
|
|
|
|
{
|
|
|
|
|
loadFromURL(u"master.odm");
|
|
|
|
|
|
|
|
|
|
// update linked section
|
|
|
|
|
dispatchCommand(mxComponent, ".uno:UpdateAllLinks", {});
|
|
|
|
|
|
|
|
|
|
uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
|
|
|
|
|
// Enable Outlines export
|
|
|
|
|
uno::Sequence<beans::PropertyValue> aFilterData(
|
|
|
|
|
comphelper::InitPropertySequence({ { "ExportBookmarks", uno::Any(true) } }));
|
|
|
|
|
aMediaDescriptor["FilterData"] <<= aFilterData;
|
|
|
|
|
xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
|
|
|
|
|
|
|
|
|
|
// Parse the export result.
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
|
|
|
|
|
CPPUNIT_ASSERT(aDocument.Read(aStream));
|
|
|
|
|
|
|
|
|
|
auto* pCatalog = aDocument.GetCatalog();
|
|
|
|
|
CPPUNIT_ASSERT(pCatalog);
|
|
|
|
|
auto* pCatalogDictionary = pCatalog->GetDictionary();
|
|
|
|
|
CPPUNIT_ASSERT(pCatalogDictionary);
|
|
|
|
|
auto* pOutlinesObject = pCatalogDictionary->LookupObject("Outlines");
|
|
|
|
|
CPPUNIT_ASSERT(pOutlinesObject);
|
|
|
|
|
auto* pOutlinesDictionary = pOutlinesObject->GetDictionary();
|
|
|
|
|
#if 0
|
|
|
|
|
// Type isn't actually written currently
|
|
|
|
|
auto* pType
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFNameElement*>(pOutlinesDictionary->LookupElement("Type"));
|
|
|
|
|
CPPUNIT_ASSERT(pType);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Outlines"), pType->GetValue());
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
auto* pFirst = dynamic_cast<vcl::filter::PDFReferenceElement*>(
|
|
|
|
|
pOutlinesDictionary->LookupElement("First"));
|
|
|
|
|
CPPUNIT_ASSERT(pFirst);
|
|
|
|
|
auto* pFirstD = pFirst->LookupObject()->GetDictionary();
|
|
|
|
|
CPPUNIT_ASSERT(pFirstD);
|
|
|
|
|
//CPPUNIT_ASSERT_EQUAL(OString("Outlines"), dynamic_cast<vcl::filter::PDFNameElement*>(pFirstD->LookupElement("Type"))->GetValue());
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(u"Preface"_ustr, ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
|
|
|
|
|
*dynamic_cast<vcl::filter::PDFHexStringElement*>(
|
|
|
|
|
pFirstD->LookupElement("Title"))));
|
|
|
|
|
|
|
|
|
|
auto* pFirst1
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFReferenceElement*>(pFirstD->LookupElement("First"));
|
|
|
|
|
CPPUNIT_ASSERT(pFirst1);
|
|
|
|
|
auto* pFirst1D = pFirst1->LookupObject()->GetDictionary();
|
|
|
|
|
CPPUNIT_ASSERT(pFirst1D);
|
|
|
|
|
// here is a hidden section with headings "Copyright" etc.; check that
|
|
|
|
|
// there are no outline entries for it
|
|
|
|
|
//CPPUNIT_ASSERT_EQUAL(OString("Outlines"), dynamic_cast<vcl::filter::PDFNameElement*>(pFirst1D->LookupElement("Type"))->GetValue());
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(
|
|
|
|
|
u"Who is this book for?"_ustr,
|
|
|
|
|
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
|
|
|
|
|
*dynamic_cast<vcl::filter::PDFHexStringElement*>(pFirst1D->LookupElement("Title"))));
|
|
|
|
|
|
|
|
|
|
auto* pFirst2
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFReferenceElement*>(pFirst1D->LookupElement("Next"));
|
|
|
|
|
auto* pFirst2D = pFirst2->LookupObject()->GetDictionary();
|
|
|
|
|
CPPUNIT_ASSERT(pFirst2D);
|
|
|
|
|
//CPPUNIT_ASSERT_EQUAL(OString("Outlines"), dynamic_cast<vcl::filter::PDFNameElement*>(pFirst2D->LookupElement("Type"))->GetValue());
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(
|
|
|
|
|
u"What\u2019s in this book?"_ustr,
|
|
|
|
|
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
|
|
|
|
|
*dynamic_cast<vcl::filter::PDFHexStringElement*>(pFirst2D->LookupElement("Title"))));
|
|
|
|
|
|
|
|
|
|
auto* pFirst3
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFReferenceElement*>(pFirst2D->LookupElement("Next"));
|
|
|
|
|
auto* pFirst3D = pFirst3->LookupObject()->GetDictionary();
|
|
|
|
|
CPPUNIT_ASSERT(pFirst3D);
|
|
|
|
|
//CPPUNIT_ASSERT_EQUAL(OString("Outlines"), dynamic_cast<vcl::filter::PDFNameElement*>(pFirst3D->LookupElement("Type"))->GetValue());
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(
|
|
|
|
|
u"Minimum requirements for using LibreOffice"_ustr,
|
|
|
|
|
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
|
|
|
|
|
*dynamic_cast<vcl::filter::PDFHexStringElement*>(pFirst3D->LookupElement("Title"))));
|
|
|
|
|
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<vcl::filter::PDFElement*>(nullptr),
|
|
|
|
|
pFirst3D->LookupElement("Next"));
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<vcl::filter::PDFElement*>(nullptr),
|
|
|
|
|
pFirstD->LookupElement("Next"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testPdfImageRotate180)
|
|
|
|
|
{
|
|
|
|
|
// Create an empty document.
|
|
|
|
|
mxComponent = loadFromDesktop("private:factory/swriter");
|
|
|
|
|
uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
|
|
|
|
|
uno::Reference<text::XText> xText = xTextDocument->getText();
|
|
|
|
|
uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
|
|
|
|
|
|
|
|
|
|
// Insert the PDF image.
|
|
|
|
|
uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
|
|
|
|
|
uno::Reference<beans::XPropertySet> xGraphicObject(
|
|
|
|
|
xFactory->createInstance("com.sun.star.text.TextGraphicObject"), uno::UNO_QUERY);
|
|
|
|
|
OUString aURL = createFileURL(u"pdf-image-rotate-180.pdf");
|
|
|
|
|
xGraphicObject->setPropertyValue("GraphicURL", uno::Any(aURL));
|
|
|
|
|
uno::Reference<drawing::XShape> xShape(xGraphicObject, uno::UNO_QUERY);
|
|
|
|
|
xShape->setSize(awt::Size(1000, 1000));
|
|
|
|
|
uno::Reference<text::XTextContent> xTextContent(xGraphicObject, uno::UNO_QUERY);
|
|
|
|
|
xText->insertTextContent(xCursor->getStart(), xTextContent, /*bAbsorb=*/false);
|
|
|
|
|
|
|
|
|
|
// Save as PDF.
|
|
|
|
|
uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
|
|
|
|
|
|
|
|
|
|
// Parse the export result.
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
|
|
|
|
|
|
|
|
|
|
// Make sure that the page -> form -> form has a child image.
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
|
|
|
|
|
CPPUNIT_ASSERT(pPdfPage);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getObjectCount());
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPdfPage->getObject(0);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form, pPageObject->getType());
|
|
|
|
|
// 2: white background and the actual object.
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(2, pPageObject->getFormObjectCount());
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPageObject> pFormObject = pPageObject->getFormObject(1);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form, pFormObject->getType());
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pFormObject->getFormObjectCount());
|
|
|
|
|
|
|
|
|
|
// Check if the inner form object (original page object in the pdf image) has the correct
|
|
|
|
|
// rotation.
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPageObject> pInnerFormObject = pFormObject->getFormObject(0);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form, pInnerFormObject->getType());
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pInnerFormObject->getFormObjectCount());
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPageObject> pImage = pInnerFormObject->getFormObject(0);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Image, pImage->getType());
|
|
|
|
|
basegfx::B2DHomMatrix aMat = pInnerFormObject->getMatrix();
|
|
|
|
|
basegfx::B2DTuple aScale;
|
|
|
|
|
basegfx::B2DTuple aTranslate;
|
|
|
|
|
double fRotate = 0;
|
|
|
|
|
double fShearX = 0;
|
|
|
|
|
aMat.decompose(aScale, aTranslate, fRotate, fShearX);
|
|
|
|
|
// Without the accompanying fix in place, this test would have failed with:
|
|
|
|
|
// - Expected: -1
|
|
|
|
|
// - Actual : 1
|
|
|
|
|
// i.e. the 180 degrees rotation didn't happen (via a combination of horizontal + vertical
|
|
|
|
|
// flip).
|
|
|
|
|
CPPUNIT_ASSERT_DOUBLES_EQUAL(-1.0, aScale.getX(), 0.01);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf144222)
|
|
|
|
|
{
|
|
|
|
|
// Assume Windows has the font for U+4E2D
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("calc_pdf_Export");
|
|
|
|
|
saveAsPDF(u"tdf144222.ods");
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
|
|
|
|
|
|
|
|
|
|
// The document has one page.
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
|
|
|
|
|
CPPUNIT_ASSERT(pPdfPage);
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumTextPage> pTextPage = pPdfPage->getTextPage();
|
|
|
|
|
CPPUNIT_ASSERT(pTextPage);
|
|
|
|
|
|
|
|
|
|
int nPageObjectCount = pPdfPage->getObjectCount();
|
|
|
|
|
const OUString sChar = u"\u4E2D"_ustr;
|
|
|
|
|
basegfx::B2DRectangle aRect1, aRect2;
|
|
|
|
|
int nCount = 0;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < nPageObjectCount; ++i)
|
|
|
|
|
{
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPageObject> pPdfPageObject = pPdfPage->getObject(i);
|
|
|
|
|
if (pPdfPageObject->getType() == vcl::pdf::PDFPageObjectType::Text)
|
|
|
|
|
{
|
|
|
|
|
++nCount;
|
|
|
|
|
OUString sText = pPdfPageObject->getText(pTextPage);
|
|
|
|
|
if (sText == sChar)
|
|
|
|
|
aRect1 = pPdfPageObject->getBounds();
|
|
|
|
|
else
|
|
|
|
|
aRect2 = pPdfPageObject->getBounds();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(2, nCount);
|
|
|
|
|
CPPUNIT_ASSERT(!aRect1.isEmpty());
|
|
|
|
|
CPPUNIT_ASSERT(!aRect2.isEmpty());
|
|
|
|
|
CPPUNIT_ASSERT(!aRect1.overlaps(aRect2));
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf145873)
|
|
|
|
|
{
|
|
|
|
|
// Import the bugdoc and export as PDF.
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("impress_pdf_Export");
|
|
|
|
|
saveAsPDF(u"tdf145873.pptx");
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
|
|
|
|
|
|
|
|
|
|
// The document has one page.
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
|
|
|
|
|
CPPUNIT_ASSERT(pPdfPage);
|
|
|
|
|
int nPageObjectCount = pPdfPage->getObjectCount();
|
|
|
|
|
|
|
|
|
|
// tdf#145873: Without the fix #1 in place, this test would have failed with
|
|
|
|
|
// - Expected: 66
|
|
|
|
|
// - Actual : 3
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(66, nPageObjectCount);
|
|
|
|
|
|
|
|
|
|
auto pObject = pPdfPage->getObject(4);
|
|
|
|
|
CPPUNIT_ASSERT_MESSAGE("no object", pObject != nullptr);
|
|
|
|
|
|
|
|
|
|
// tdf#145873: Without the fix #2 in place, this test would have failed with
|
|
|
|
|
// - Expected: 13.40
|
|
|
|
|
// - Actual : 3.57...
|
|
|
|
|
// - Delta : 0.1
|
|
|
|
|
CPPUNIT_ASSERT_DOUBLES_EQUAL(13.40, pObject->getBounds().getWidth(), 0.1);
|
|
|
|
|
// - Expected: 13.79
|
|
|
|
|
// - Actual : 3.74...
|
|
|
|
|
// - Delta : 0.1
|
|
|
|
|
CPPUNIT_ASSERT_DOUBLES_EQUAL(13.79, pObject->getBounds().getHeight(), 0.1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testPdfImageHyperlink)
|
|
|
|
|
{
|
|
|
|
|
// Given a Draw file, containing a PDF image, which has a hyperlink in it:
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("draw_pdf_Export");
|
|
|
|
|
|
|
|
|
|
// When saving to PDF:
|
|
|
|
|
saveAsPDF(u"pdf-image-hyperlink.odg");
|
|
|
|
|
|
|
|
|
|
// Then make sure that link is preserved:
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
|
|
|
|
|
CPPUNIT_ASSERT(pPdfPage);
|
|
|
|
|
// Without the accompanying fix in place, this test would have failed, the hyperlink of the PDF
|
|
|
|
|
// image was lost.
|
|
|
|
|
CPPUNIT_ASSERT(pPdfPage->hasLinks());
|
|
|
|
|
|
|
|
|
|
// Also test the precision of the form XObject.
|
|
|
|
|
// Given a full-page form XObject, page height is 27.94 cm (792 points):
|
|
|
|
|
// When writing the reciprocal of the object height to PDF:
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPageObject> pFormObject;
|
|
|
|
|
for (int i = 0; i < pPdfPage->getObjectCount(); ++i)
|
|
|
|
|
{
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPageObject> pObject = pPdfPage->getObject(i);
|
|
|
|
|
if (pObject->getType() == vcl::pdf::PDFPageObjectType::Form)
|
|
|
|
|
{
|
|
|
|
|
pFormObject = std::move(pObject);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT(pFormObject);
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPageObject> pInnerFormObject;
|
|
|
|
|
for (int i = 0; i < pFormObject->getFormObjectCount(); ++i)
|
|
|
|
|
{
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPageObject> pObject = pFormObject->getFormObject(i);
|
|
|
|
|
if (pObject->getType() == vcl::pdf::PDFPageObjectType::Form)
|
|
|
|
|
{
|
|
|
|
|
pInnerFormObject = std::move(pObject);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT(pInnerFormObject);
|
|
|
|
|
// Then make sure that enough digits are used, so the point size is unchanged:
|
|
|
|
|
basegfx::B2DHomMatrix aMatrix = pInnerFormObject->getMatrix();
|
|
|
|
|
basegfx::B2DTuple aScale;
|
|
|
|
|
basegfx::B2DTuple aTranslate;
|
|
|
|
|
double fRotate{};
|
|
|
|
|
double fShearX{};
|
|
|
|
|
aMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
|
|
|
|
|
// Without the accompanying fix in place, this test would have failed with:
|
|
|
|
|
// - Expected: 0.0012626264
|
|
|
|
|
// - Actual : 0.00126
|
|
|
|
|
// i.e. the rounded reciprocal was 794 points, not the original 792.
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(0.0012626264, rtl::math::round(aScale.getY(), 10));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testURIs)
|
|
|
|
|
{
|
|
|
|
|
struct
|
|
|
|
|
{
|
|
|
|
|
OUString in;
|
|
|
|
|
OString out;
|
|
|
|
|
bool relativeFsys;
|
|
|
|
|
} URIs[] = { {
|
|
|
|
|
"http://example.com/",
|
|
|
|
|
"http://example.com/",
|
|
|
|
|
true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"file://localfile.odt/",
|
|
|
|
|
"file://localfile.odt/",
|
|
|
|
|
true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
// tdf 143216
|
|
|
|
|
"http://username:password@example.com",
|
|
|
|
|
"http://username:password@example.com",
|
|
|
|
|
true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"git://git.example.org/project/example",
|
|
|
|
|
"git://git.example.org/project/example",
|
|
|
|
|
true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
// The odt/pdf gets substituted due to 'ConvertOOoTargetToPDFTarget'
|
|
|
|
|
"filebypath.odt",
|
|
|
|
|
"filebypath.pdf",
|
|
|
|
|
true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
// The odt/pdf gets substituted due to 'ConvertOOoTargetToPDFTarget'
|
|
|
|
|
// but this time with ExportLinksRelativeFsys off the path is added
|
|
|
|
|
"filebypath.odt",
|
|
|
|
|
OUStringToOString(utl::GetTempNameBaseDirectory(), RTL_TEXTENCODING_UTF8)
|
|
|
|
|
+ "filebypath.pdf",
|
|
|
|
|
false,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
// This also gets made relative due to 'ExportLinksRelativeFsys'
|
|
|
|
|
utl::GetTempNameBaseDirectory() + "fileintempdir.odt",
|
|
|
|
|
"fileintempdir.pdf",
|
|
|
|
|
true,
|
|
|
|
|
} };
|
|
|
|
|
|
|
|
|
|
// Create an empty document.
|
|
|
|
|
// Note: The test harness gets very upset if we try and create multiple
|
|
|
|
|
// documents, or recreate it; so reuse one instance for all the links
|
|
|
|
|
mxComponent = loadFromDesktop("private:factory/swriter");
|
|
|
|
|
uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
|
|
|
|
|
uno::Reference<text::XText> xText = xTextDocument->getText();
|
|
|
|
|
uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
|
|
|
|
|
xText->insertString(xCursor, "Test pdf", /*bAbsorb=*/false);
|
|
|
|
|
|
|
|
|
|
// Set the name so it can do relative name replacement
|
|
|
|
|
uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
|
|
|
|
|
xModel->attachResource(maTempFile.GetURL(), xModel->getArgs());
|
|
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < (sizeof(URIs) / sizeof(URIs[0])); i++)
|
|
|
|
|
{
|
|
|
|
|
// Test the filename rewriting
|
|
|
|
|
uno::Sequence<beans::PropertyValue> aFilterData(comphelper::InitPropertySequence({
|
|
|
|
|
{ "ExportLinksRelativeFsys", uno::Any(URIs[i].relativeFsys) },
|
|
|
|
|
{ "ConvertOOoTargetToPDFTarget", uno::Any(true) },
|
|
|
|
|
}));
|
|
|
|
|
aMediaDescriptor["FilterData"] <<= aFilterData;
|
|
|
|
|
|
|
|
|
|
// Add a link (based on testNestedHyperlink in rtfexport3)
|
|
|
|
|
xCursor->gotoStart(/*bExpand=*/false);
|
|
|
|
|
xCursor->gotoEnd(/*bExpand=*/true);
|
|
|
|
|
uno::Reference<beans::XPropertySet> xCursorProps(xCursor, uno::UNO_QUERY);
|
|
|
|
|
xCursorProps->setPropertyValue("HyperLinkURL", uno::Any(URIs[i].in));
|
|
|
|
|
|
|
|
|
|
// Save as PDF.
|
|
|
|
|
uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
|
|
|
|
|
|
|
|
|
|
// Use the filter rather than the pdfium route, as per the tdf105093 test, it's
|
|
|
|
|
// easier to parse the annotations
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
|
|
|
|
|
// Parse the export result.
|
|
|
|
|
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
|
|
|
|
|
CPPUNIT_ASSERT(aDocument.Read(aStream));
|
|
|
|
|
|
|
|
|
|
// The document has one page.
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
|
|
|
|
|
auto pAnnots = dynamic_cast<vcl::filter::PDFArrayElement*>(aPages[0]->Lookup("Annots"));
|
|
|
|
|
CPPUNIT_ASSERT(pAnnots);
|
|
|
|
|
|
|
|
|
|
// There should be one annotation
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pAnnots->GetElements().size());
|
|
|
|
|
auto pAnnotReference
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFReferenceElement*>(pAnnots->GetElements()[0]);
|
|
|
|
|
CPPUNIT_ASSERT(pAnnotReference);
|
|
|
|
|
vcl::filter::PDFObjectElement* pAnnot = pAnnotReference->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pAnnot);
|
|
|
|
|
// We're expecting something like /Type /Annot /A << /Type /Action /S /URI /URI (path)
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(
|
|
|
|
|
OString("Annot"),
|
|
|
|
|
static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type"))->GetValue());
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(
|
|
|
|
|
OString("Link"),
|
|
|
|
|
static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype"))->GetValue());
|
|
|
|
|
auto pAction = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pAnnot->Lookup("A"));
|
|
|
|
|
CPPUNIT_ASSERT(pAction);
|
|
|
|
|
auto pURIElem
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFLiteralStringElement*>(pAction->LookupElement("URI"));
|
|
|
|
|
CPPUNIT_ASSERT(pURIElem);
|
|
|
|
|
// Check it matches
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(URIs[i].out, pURIElem->GetValue());
|
|
|
|
|
// tdf#148934 check a11y
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(
|
|
|
|
|
OUString("Test pdf"),
|
|
|
|
|
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
|
|
|
|
|
*dynamic_cast<vcl::filter::PDFHexStringElement*>(pAnnot->Lookup("Contents"))));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testPdfImageAnnots)
|
|
|
|
|
{
|
|
|
|
|
// Given a document with a PDF image that has 2 comments (popup, text) and a hyperlink:
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("draw_pdf_Export");
|
|
|
|
|
|
|
|
|
|
// When saving to PDF:
|
|
|
|
|
saveAsPDF(u"pdf-image-annots.odg");
|
|
|
|
|
|
|
|
|
|
// Then make sure only the hyperlink is kept, since Draw itself has its own comments:
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
|
|
|
|
|
CPPUNIT_ASSERT(pPdfPage);
|
|
|
|
|
// Without the accompanying fix in place, this test would have failed with:
|
|
|
|
|
// - Expected: 1
|
|
|
|
|
// - Actual : 3
|
|
|
|
|
// i.e. not only the hyperlink but also the 2 comments were exported, leading to duplication.
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getAnnotationCount());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testPdfImageEncryption)
|
|
|
|
|
{
|
|
|
|
|
// Given an empty document, with an inserted PDF image:
|
|
|
|
|
mxComponent = loadFromDesktop("private:factory/swriter");
|
|
|
|
|
uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
|
|
|
|
|
uno::Reference<text::XText> xText = xTextDocument->getText();
|
|
|
|
|
uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
|
|
|
|
|
uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
|
|
|
|
|
uno::Reference<beans::XPropertySet> xGraphicObject(
|
|
|
|
|
xFactory->createInstance("com.sun.star.text.TextGraphicObject"), uno::UNO_QUERY);
|
|
|
|
|
OUString aURL = createFileURL(u"rectangles.pdf");
|
|
|
|
|
xGraphicObject->setPropertyValue("GraphicURL", uno::Any(aURL));
|
|
|
|
|
uno::Reference<drawing::XShape> xShape(xGraphicObject, uno::UNO_QUERY);
|
|
|
|
|
xShape->setSize(awt::Size(1000, 1000));
|
|
|
|
|
uno::Reference<text::XTextContent> xTextContent(xGraphicObject, uno::UNO_QUERY);
|
|
|
|
|
xText->insertTextContent(xCursor->getStart(), xTextContent, /*bAbsorb=*/false);
|
|
|
|
|
|
|
|
|
|
// When saving as encrypted PDF:
|
|
|
|
|
uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
uno::Sequence<beans::PropertyValue> aFilterData = {
|
|
|
|
|
comphelper::makePropertyValue("EncryptFile", true),
|
|
|
|
|
comphelper::makePropertyValue("DocumentOpenPassword", OUString("secret")),
|
|
|
|
|
};
|
|
|
|
|
aMediaDescriptor["FilterData"] <<= aFilterData;
|
|
|
|
|
xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
|
|
|
|
|
|
|
|
|
|
// Then make sure that the image is not lost:
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport("secret");
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
|
|
|
|
|
CPPUNIT_ASSERT(pPdfPage);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getObjectCount());
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPdfPage->getObject(0);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form, pPageObject->getType());
|
|
|
|
|
// Without the accompanying fix in place, this test would have failed with:
|
|
|
|
|
// - Expected: 2
|
|
|
|
|
// - Actual : 0
|
|
|
|
|
// i.e. instead of the white background and the actual form child, the image was lost due to
|
|
|
|
|
// missing encryption.
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(2, pPageObject->getFormObjectCount());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testBitmapScaledown)
|
|
|
|
|
{
|
|
|
|
|
// FIXME: the DPI check should be removed when either (1) the test is fixed to work with
|
|
|
|
|
// non-default DPI; or (2) unit tests on Windows are made to use svp VCL plugin.
|
|
|
|
|
if (!IsDefaultDPI())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Given a document with an upscaled and rotated barcode bitmap in it:
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
|
|
|
|
|
// When saving as PDF:
|
|
|
|
|
saveAsPDF(u"bitmap-scaledown.odt");
|
|
|
|
|
|
|
|
|
|
// Then verify that the bitmap is not downscaled:
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
|
|
|
|
|
CPPUNIT_ASSERT(pPdfPage);
|
|
|
|
|
int nPageObjectCount = pPdfPage->getObjectCount();
|
|
|
|
|
for (int i = 0; i < nPageObjectCount; ++i)
|
|
|
|
|
{
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPdfPage->getObject(i);
|
|
|
|
|
if (pPageObject->getType() != vcl::pdf::PDFPageObjectType::Image)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumBitmap> pBitmap = pPageObject->getImageBitmap();
|
|
|
|
|
CPPUNIT_ASSERT(pBitmap);
|
|
|
|
|
// In-file sizes: good is 2631x380, bad is 1565x14.
|
|
|
|
|
int nWidth = pBitmap->getWidth();
|
|
|
|
|
// Without the accompanying fix in place, this test would have failed with:
|
|
|
|
|
// - Expected: 2616
|
|
|
|
|
// - Actual : 1565
|
|
|
|
|
// i.e. the bitmap in the pdf result was small enough to be blurry.
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(2616, nWidth);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf139627)
|
|
|
|
|
{
|
|
|
|
|
#if HAVE_MORE_FONTS
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
saveAsPDF(u"justified-arabic-kashida.odt");
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
|
|
|
|
|
|
|
|
|
|
// The document has one page.
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
|
|
|
|
|
CPPUNIT_ASSERT(pPdfPage);
|
|
|
|
|
|
|
|
|
|
// 7 objects, 3 text, others are path
|
|
|
|
|
int nPageObjectCount = pPdfPage->getObjectCount();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(7, nPageObjectCount);
|
|
|
|
|
|
|
|
|
|
// 3 text objects
|
|
|
|
|
OUString sText[3];
|
|
|
|
|
|
|
|
|
|
/* With "Noto Sans Arabic" font, these are the X ranges on Linux:
|
|
|
|
|
0: ( 61.75 - 415.94)
|
|
|
|
|
1: (479.70 - 422.40)
|
|
|
|
|
2: (209.40 - 453.2)
|
|
|
|
|
*/
|
|
|
|
|
basegfx::B2DRectangle aRect[3];
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumTextPage> pTextPage = pPdfPage->getTextPage();
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject;
|
|
|
|
|
|
|
|
|
|
int nTextObjectCount = 0;
|
|
|
|
|
for (int i = 0; i < nPageObjectCount; ++i)
|
|
|
|
|
{
|
|
|
|
|
pPageObject = pPdfPage->getObject(i);
|
|
|
|
|
CPPUNIT_ASSERT_MESSAGE("no object", pPageObject != nullptr);
|
|
|
|
|
if (pPageObject->getType() == vcl::pdf::PDFPageObjectType::Text)
|
|
|
|
|
{
|
|
|
|
|
sText[nTextObjectCount] = pPageObject->getText(pTextPage);
|
|
|
|
|
aRect[nTextObjectCount] = pPageObject->getBounds();
|
|
|
|
|
++nTextObjectCount;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(3, nTextObjectCount);
|
|
|
|
|
|
|
|
|
|
// Text: جِـرم (which means "mass" in Persian)
|
|
|
|
|
// Rendered as (left to right): "reh + mim" - "kasreh" - "jeh + tatweel"
|
|
|
|
|
int rehmim = 0, kasreh = 1, jehtatweel = 2;
|
|
|
|
|
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(u"رم"_ustr, sText[rehmim].trim());
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(u""_ustr, sText[kasreh].trim());
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(u""_ustr, sText[jehtatweel].trim());
|
|
|
|
|
|
|
|
|
|
// "Kasreh" should be within "jeh" character
|
|
|
|
|
CPPUNIT_ASSERT_GREATER(aRect[jehtatweel].getMinX(), aRect[kasreh].getMinX());
|
|
|
|
|
CPPUNIT_ASSERT_LESS(aRect[jehtatweel].getMaxX(), aRect[kasreh].getMaxX());
|
|
|
|
|
|
|
|
|
|
// "Tatweel" should cover "jeh" and "reh"+"mim" to avoid gap
|
|
|
|
|
// Checking right gap
|
|
|
|
|
//CPPUNIT_ASSERT_GREATER(aRect[jehtatweel].getMinX(), aRect[tatweel].getMaxX());
|
|
|
|
|
// Checking left gap
|
|
|
|
|
// Kashida fails to reach to rehmim before the series of patches starting
|
|
|
|
|
// with 3901e029bd39575f700e69a73818565d62226a23. The visible symptom is
|
|
|
|
|
// a gap in the left of Kashida.
|
|
|
|
|
CPPUNIT_ASSERT_LESS(aRect[rehmim].getMaxX(), aRect[jehtatweel].getMinX());
|
|
|
|
|
|
|
|
|
|
// Overlappings of Kashida and surrounding characters is ~9% of the width
|
|
|
|
|
// of the "jeh" character, while using "Noto Arabic Sans" font in this
|
|
|
|
|
// specific example.
|
|
|
|
|
// We set the hard limit of 10% here.
|
|
|
|
|
CPPUNIT_ASSERT_LESS(0.1, fabs(aRect[rehmim].getMaxX() - aRect[jehtatweel].getMinX())
|
|
|
|
|
/ aRect[jehtatweel].getWidth());
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testRexportRefToKids)
|
|
|
|
|
{
|
|
|
|
|
// We need to enable PDFium import (and make sure to disable after the test)
|
|
|
|
|
bool bResetEnvVar = false;
|
|
|
|
|
if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
|
|
|
|
|
{
|
|
|
|
|
bResetEnvVar = true;
|
|
|
|
|
osl_setEnvironment(OUString("LO_IMPORT_USE_PDFIUM").pData, OUString("1").pData);
|
|
|
|
|
}
|
|
|
|
|
comphelper::ScopeGuard aPDFiumEnvVarGuard([&]() {
|
|
|
|
|
if (bResetEnvVar)
|
|
|
|
|
osl_clearEnvironment(OUString("LO_IMPORT_USE_PDFIUM").pData);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Load the PDF and save as PDF
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
load(u"ref-to-kids.pdf", aDocument);
|
|
|
|
|
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(5), aPages.size());
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFObjectElement* pResources = aPages[0]->LookupObject("Resources");
|
|
|
|
|
CPPUNIT_ASSERT(pResources);
|
|
|
|
|
|
|
|
|
|
auto pXObjects
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFDictionaryElement*>(pResources->Lookup("XObject"));
|
|
|
|
|
CPPUNIT_ASSERT(pXObjects);
|
|
|
|
|
|
|
|
|
|
// Without the fix LookupObject for all /Im's will fail.
|
|
|
|
|
for (auto const& rPair : pXObjects->GetItems())
|
|
|
|
|
{
|
|
|
|
|
if (rPair.first.startsWith("Im"))
|
|
|
|
|
CPPUNIT_ASSERT(pXObjects->LookupObject(rPair.first));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testRexportFilterSingletonArray)
|
|
|
|
|
{
|
|
|
|
|
// We need to enable PDFium import (and make sure to disable after the test)
|
|
|
|
|
bool bResetEnvVar = false;
|
|
|
|
|
if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
|
|
|
|
|
{
|
|
|
|
|
bResetEnvVar = true;
|
|
|
|
|
osl_setEnvironment(OUString("LO_IMPORT_USE_PDFIUM").pData, OUString("1").pData);
|
|
|
|
|
}
|
|
|
|
|
comphelper::ScopeGuard aPDFiumEnvVarGuard([&]() {
|
|
|
|
|
if (bResetEnvVar)
|
|
|
|
|
osl_clearEnvironment(OUString("LO_IMPORT_USE_PDFIUM").pData);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Load the PDF and save as PDF
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
// Loading fails with tagged PDF enabled
|
|
|
|
|
load(u"ref-to-kids.pdf", aDocument, false);
|
|
|
|
|
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(5), aPages.size());
|
|
|
|
|
|
|
|
|
|
// Directly go to the inner XObject Im5 that contains the rectangle drawings.
|
|
|
|
|
auto pInnerIm = aDocument.LookupObject(5);
|
|
|
|
|
CPPUNIT_ASSERT(pInnerIm);
|
|
|
|
|
|
|
|
|
|
auto pFilter = dynamic_cast<vcl::filter::PDFNameElement*>(pInnerIm->Lookup("Filter"));
|
|
|
|
|
CPPUNIT_ASSERT(pFilter);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Filter must be FlateDecode", OString("FlateDecode"),
|
|
|
|
|
pFilter->GetValue());
|
|
|
|
|
|
|
|
|
|
vcl::filter::PDFStreamElement* pStream = pInnerIm->GetStream();
|
|
|
|
|
CPPUNIT_ASSERT(pStream);
|
|
|
|
|
SvMemoryStream& rObjectStream = pStream->GetMemory();
|
|
|
|
|
// Uncompress it.
|
|
|
|
|
SvMemoryStream aUncompressed;
|
|
|
|
|
ZCodec aZCodec;
|
|
|
|
|
aZCodec.BeginCompression();
|
|
|
|
|
rObjectStream.Seek(0);
|
|
|
|
|
aZCodec.Decompress(rObjectStream, aUncompressed);
|
|
|
|
|
CPPUNIT_ASSERT(aZCodec.EndCompression());
|
|
|
|
|
|
|
|
|
|
// Without the fix, the stream is doubly compressed,
|
|
|
|
|
// hence one decompression will not yield the "re" expressions.
|
|
|
|
|
auto pStart = static_cast<const char*>(aUncompressed.GetData());
|
|
|
|
|
const char* pEnd = pStart + aUncompressed.GetSize();
|
|
|
|
|
OString aImage = "100 0 30 50 re B*\n70 67 50 30 re B*\n";
|
|
|
|
|
auto it = std::search(pStart, pEnd, aImage.getStr(), aImage.getStr() + aImage.getLength());
|
|
|
|
|
CPPUNIT_ASSERT(it != pEnd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testRexportMediaBoxOrigin)
|
|
|
|
|
{
|
|
|
|
|
// We need to enable PDFium import (and make sure to disable after the test)
|
|
|
|
|
bool bResetEnvVar = false;
|
|
|
|
|
if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
|
|
|
|
|
{
|
|
|
|
|
bResetEnvVar = true;
|
|
|
|
|
osl_setEnvironment(OUString("LO_IMPORT_USE_PDFIUM").pData, OUString("1").pData);
|
|
|
|
|
}
|
|
|
|
|
comphelper::ScopeGuard aPDFiumEnvVarGuard([&]() {
|
|
|
|
|
if (bResetEnvVar)
|
|
|
|
|
osl_clearEnvironment(OUString("LO_IMPORT_USE_PDFIUM").pData);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Load the PDF and save as PDF
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
load(u"ref-to-kids.pdf", aDocument);
|
|
|
|
|
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(5), aPages.size());
|
|
|
|
|
|
|
|
|
|
// Directly go to the inner XObject Im12 that contains the rectangle drawings in page 2.
|
|
|
|
|
auto pInnerIm = aDocument.LookupObject(12);
|
|
|
|
|
CPPUNIT_ASSERT(pInnerIm);
|
|
|
|
|
|
|
|
|
|
constexpr sal_Int32 aOrigin[2] = { -800, -600 };
|
|
|
|
|
sal_Int32 aSize[2] = { 0, 0 };
|
|
|
|
|
|
|
|
|
|
auto pBBox = dynamic_cast<vcl::filter::PDFArrayElement*>(pInnerIm->Lookup("BBox"));
|
|
|
|
|
CPPUNIT_ASSERT(pBBox);
|
|
|
|
|
const auto& rElements2 = pBBox->GetElements();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), rElements2.size());
|
|
|
|
|
for (sal_Int32 nIdx = 0; nIdx < 4; ++nIdx)
|
|
|
|
|
{
|
|
|
|
|
const auto* pNumElement = dynamic_cast<vcl::filter::PDFNumberElement*>(rElements2[nIdx]);
|
|
|
|
|
CPPUNIT_ASSERT(pNumElement);
|
|
|
|
|
if (nIdx < 2)
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(aOrigin[nIdx], static_cast<sal_Int32>(pNumElement->GetValue()));
|
|
|
|
|
else
|
|
|
|
|
aSize[nIdx - 2] = static_cast<sal_Int32>(pNumElement->GetValue()) - aOrigin[nIdx - 2];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto pMatrix = dynamic_cast<vcl::filter::PDFArrayElement*>(pInnerIm->Lookup("Matrix"));
|
|
|
|
|
CPPUNIT_ASSERT(pMatrix);
|
|
|
|
|
const auto& rElements = pMatrix->GetElements();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(6), rElements.size());
|
|
|
|
|
sal_Int32 aMatTranslate[6]
|
|
|
|
|
= { // Rotation by $\theta$ $cos(\theta), sin(\theta), -sin(\theta), cos(\theta)$
|
|
|
|
|
0, -1, 1, 0,
|
|
|
|
|
// Translate x,y
|
|
|
|
|
-aOrigin[1] - aSize[1] / 2 + aSize[0] / 2, aOrigin[0] + aSize[0] / 2 + aSize[1] / 2
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for (sal_Int32 nIdx = 0; nIdx < 6; ++nIdx)
|
|
|
|
|
{
|
|
|
|
|
const auto* pNumElement = dynamic_cast<vcl::filter::PDFNumberElement*>(rElements[nIdx]);
|
|
|
|
|
CPPUNIT_ASSERT(pNumElement);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(aMatTranslate[nIdx], static_cast<sal_Int32>(pNumElement->GetValue()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testRexportResourceItemReference)
|
|
|
|
|
{
|
|
|
|
|
// We need to enable PDFium import (and make sure to disable after the test)
|
|
|
|
|
bool bResetEnvVar = false;
|
|
|
|
|
if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
|
|
|
|
|
{
|
|
|
|
|
bResetEnvVar = true;
|
|
|
|
|
osl_setEnvironment(OUString("LO_IMPORT_USE_PDFIUM").pData, OUString("1").pData);
|
|
|
|
|
}
|
|
|
|
|
comphelper::ScopeGuard aPDFiumEnvVarGuard([&]() {
|
|
|
|
|
if (bResetEnvVar)
|
|
|
|
|
osl_clearEnvironment(OUString("LO_IMPORT_USE_PDFIUM").pData);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Load the PDF and save as PDF
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
load(u"ref-to-kids.pdf", aDocument);
|
|
|
|
|
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(5), aPages.size());
|
|
|
|
|
|
|
|
|
|
// Directly go to the inner XObject Im12 that has reference to Font in page 2.
|
|
|
|
|
auto pInnerIm = aDocument.LookupObject(12);
|
|
|
|
|
CPPUNIT_ASSERT(pInnerIm);
|
|
|
|
|
|
|
|
|
|
auto pResources
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFDictionaryElement*>(pInnerIm->Lookup("Resources"));
|
|
|
|
|
CPPUNIT_ASSERT(pResources);
|
|
|
|
|
auto pFontsReference
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFReferenceElement*>(pResources->LookupElement("Font"));
|
|
|
|
|
CPPUNIT_ASSERT(pFontsReference);
|
|
|
|
|
|
|
|
|
|
auto pFontsObject = pFontsReference->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pFontsObject);
|
|
|
|
|
|
|
|
|
|
auto pFontDict
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFDictionaryElement*>(pFontsObject->Lookup("FF132"));
|
|
|
|
|
CPPUNIT_ASSERT(pFontDict);
|
|
|
|
|
|
|
|
|
|
auto pFontDescriptor = pFontDict->LookupObject("FontDescriptor");
|
|
|
|
|
CPPUNIT_ASSERT(pFontDescriptor);
|
|
|
|
|
|
|
|
|
|
auto pFontWidths = pFontDict->LookupObject("Widths");
|
|
|
|
|
CPPUNIT_ASSERT(pFontWidths);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf152246)
|
|
|
|
|
{
|
|
|
|
|
// Import the bugdoc and export as PDF.
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
saveAsPDF(u"content-control-rtl.docx");
|
|
|
|
|
|
|
|
|
|
// Parse the export result.
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
|
|
|
|
|
CPPUNIT_ASSERT(aDocument.Read(aStream));
|
|
|
|
|
|
|
|
|
|
// The document has one page.
|
|
|
|
|
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
|
|
|
|
|
|
|
|
|
|
// Position array
|
|
|
|
|
constexpr double aPos[5][4] = { { 56.699, 707.701, 131.401, 721.499 },
|
|
|
|
|
{ 198.499, 707.701, 273.201, 721.499 },
|
|
|
|
|
{ 303.349, 680.101, 378.051, 693.899 },
|
|
|
|
|
{ 480.599, 680.101, 555.301, 693.899 },
|
|
|
|
|
{ 56.699, 652.501, 131.401, 666.299 } };
|
|
|
|
|
|
|
|
|
|
// Get page annotations.
|
|
|
|
|
auto pAnnots = dynamic_cast<vcl::filter::PDFArrayElement*>(aPages[0]->Lookup("Annots"));
|
|
|
|
|
CPPUNIT_ASSERT(pAnnots);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(5), pAnnots->GetElements().size());
|
|
|
|
|
for (sal_Int32 i = 0; i < 5; ++i)
|
|
|
|
|
{
|
|
|
|
|
auto pAnnotReference
|
|
|
|
|
= dynamic_cast<vcl::filter::PDFReferenceElement*>(pAnnots->GetElements()[i]);
|
|
|
|
|
CPPUNIT_ASSERT(pAnnotReference);
|
|
|
|
|
vcl::filter::PDFObjectElement* pAnnot = pAnnotReference->LookupObject();
|
|
|
|
|
CPPUNIT_ASSERT(pAnnot);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(
|
|
|
|
|
OString("Annot"),
|
|
|
|
|
static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type"))->GetValue());
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(
|
|
|
|
|
OString("Widget"),
|
|
|
|
|
static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype"))->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pRect = dynamic_cast<vcl::filter::PDFArrayElement*>(pAnnot->Lookup("Rect"));
|
|
|
|
|
CPPUNIT_ASSERT(pRect);
|
|
|
|
|
const auto& rElements = pRect->GetElements();
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), rElements.size());
|
|
|
|
|
for (sal_Int32 nIdx = 0; nIdx < 4; ++nIdx)
|
|
|
|
|
{
|
|
|
|
|
const auto* pNumElement = dynamic_cast<vcl::filter::PDFNumberElement*>(rElements[nIdx]);
|
|
|
|
|
CPPUNIT_ASSERT(pNumElement);
|
|
|
|
|
CPPUNIT_ASSERT_DOUBLES_EQUAL(aPos[i][nIdx], pNumElement->GetValue(), 1e-6);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf155161)
|
|
|
|
|
{
|
|
|
|
|
// TODO: We seem to get a fallback font on Windows
|
|
|
|
|
#ifndef _WIN32
|
|
|
|
|
vcl::filter::PDFDocument aDocument;
|
|
|
|
|
load(u"tdf155161.odt", aDocument);
|
|
|
|
|
|
|
|
|
|
// Check that all fonts in the document are Type 3 fonts
|
|
|
|
|
int nFonts = 0;
|
|
|
|
|
for (const auto& aElement : aDocument.GetElements())
|
|
|
|
|
{
|
|
|
|
|
auto pObject = dynamic_cast<vcl::filter::PDFObjectElement*>(aElement.get());
|
|
|
|
|
if (!pObject)
|
|
|
|
|
continue;
|
|
|
|
|
auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Type"));
|
|
|
|
|
if (pType && pType->GetValue() == "Font")
|
|
|
|
|
{
|
|
|
|
|
auto pSubtype = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Subtype"));
|
|
|
|
|
CPPUNIT_ASSERT(pSubtype);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Type3"), pSubtype->GetValue());
|
|
|
|
|
|
|
|
|
|
auto pName = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Name"));
|
|
|
|
|
CPPUNIT_ASSERT(pName);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(OString("Cantarell-Regular"), pName->GetValue());
|
|
|
|
|
|
|
|
|
|
nFonts++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef MACOSX
|
|
|
|
|
// There must be two fonts
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(2, nFonts);
|
|
|
|
|
#else
|
|
|
|
|
// But it seems that embedded variable fonts don’t register all supported
|
|
|
|
|
// styles on Linux, so the bold and regular text use the same regular font.
|
|
|
|
|
CPPUNIT_ASSERT(nFonts);
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf48707_1)
|
|
|
|
|
{
|
|
|
|
|
// Import the bugdoc and export as PDF.
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
saveAsPDF(u"tdf48707-1.fodt");
|
|
|
|
|
|
|
|
|
|
// Parse the export result with pdfium.
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
|
|
|
|
|
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
|
|
|
|
|
|
|
|
|
|
auto pPage = pPdfDocument->openPage(0);
|
|
|
|
|
CPPUNIT_ASSERT(pPage);
|
|
|
|
|
|
|
|
|
|
int nPageObjectCount = pPage->getObjectCount();
|
|
|
|
|
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(6, nPageObjectCount);
|
|
|
|
|
|
|
|
|
|
auto pTextPage = pPage->getTextPage();
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < nPageObjectCount; ++i)
|
|
|
|
|
{
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPage->getObject(i);
|
|
|
|
|
// The text and path objects (underline and overline) should all be red.
|
|
|
|
|
if (pPageObject->getType() == vcl::pdf::PDFPageObjectType::Text)
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(COL_LIGHTRED, pPageObject->getFillColor());
|
|
|
|
|
else
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(COL_LIGHTRED, pPageObject->getStrokeColor());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf48707_2)
|
|
|
|
|
{
|
|
|
|
|
// Import the bugdoc and export as PDF.
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
saveAsPDF(u"tdf48707-2.fodt");
|
|
|
|
|
|
|
|
|
|
// Parse the export result with pdfium.
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
|
|
|
|
|
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
|
|
|
|
|
|
|
|
|
|
auto pPage = pPdfDocument->openPage(0);
|
|
|
|
|
CPPUNIT_ASSERT(pPage);
|
|
|
|
|
|
|
|
|
|
int nPageObjectCount = pPage->getObjectCount();
|
|
|
|
|
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(13, nPageObjectCount);
|
|
|
|
|
|
|
|
|
|
auto pTextPage = pPage->getTextPage();
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < nPageObjectCount; ++i)
|
|
|
|
|
{
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPage->getObject(i);
|
|
|
|
|
if (pPageObject->getType() != vcl::pdf::PDFPageObjectType::Path)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// The table-like paths should be red, underline and overline should be black.
|
|
|
|
|
if (i >= 8)
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(COL_BLACK, pPageObject->getStrokeColor());
|
|
|
|
|
else
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(COL_LIGHTRED, pPageObject->getStrokeColor());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf156528)
|
|
|
|
|
{
|
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
|
|
|
|
saveAsPDF(u"wide_page1.fodt");
|
|
|
|
|
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
|
|
|
|
|
|
|
|
|
|
// The document has two pages
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(2, pPdfDocument->getPageCount());
|
|
|
|
|
|
|
|
|
|
// 1st page (5100 mm width x 210 mm high, UserUnit = 2)
|
|
|
|
|
auto pPdfPage = pPdfDocument->openPage(0);
|
|
|
|
|
CPPUNIT_ASSERT(pPdfPage);
|
|
|
|
|
CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(5100.0 / 2, o3tl::Length::mm, o3tl::Length::pt),
|
|
|
|
|
pPdfPage->getWidth(), 1);
|
|
|
|
|
CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(210.0 / 2, o3tl::Length::mm, o3tl::Length::pt),
|
|
|
|
|
pPdfPage->getHeight(), 1);
|
|
|
|
|
|
|
|
|
|
// 1 object (rectangle 5060 mm width x 170 mm high, UserUnit = 2)
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getObjectCount());
|
|
|
|
|
auto pRect = pPdfPage->getObject(0);
|
|
|
|
|
CPPUNIT_ASSERT(pRect);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Path, pRect->getType());
|
|
|
|
|
auto bounds = pRect->getBounds();
|
|
|
|
|
CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(5060.0 / 2, o3tl::Length::mm, o3tl::Length::pt),
|
|
|
|
|
bounds.getWidth(), 1);
|
|
|
|
|
CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(170.0 / 2, o3tl::Length::mm, o3tl::Length::pt),
|
|
|
|
|
bounds.getHeight(), 1);
|
|
|
|
|
|
|
|
|
|
// 2nd page (210 mm width x 297 mm high, UserUnit = 1)
|
|
|
|
|
pPdfPage = pPdfDocument->openPage(1);
|
|
|
|
|
CPPUNIT_ASSERT(pPdfPage);
|
|
|
|
|
CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(210.0, o3tl::Length::mm, o3tl::Length::pt),
|
|
|
|
|
pPdfPage->getWidth(), 1);
|
|
|
|
|
CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(297.0, o3tl::Length::mm, o3tl::Length::pt),
|
|
|
|
|
pPdfPage->getHeight(), 1);
|
|
|
|
|
|
|
|
|
|
// 1 object (rectangle 170 mm width x 257 mm high, UserUnit = 1)
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getObjectCount());
|
|
|
|
|
pRect = pPdfPage->getObject(0);
|
|
|
|
|
CPPUNIT_ASSERT(pRect);
|
|
|
|
|
CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Path, pRect->getType());
|
|
|
|
|
bounds = pRect->getBounds();
|
|
|
|
|
// Without the fix, this would fail with
|
|
|
|
|
// - Expected: 481.889763779528
|
|
|
|
|
// - Actual : 241.925001144409
|
|
|
|
|
// - Delta : 1
|
|
|
|
|
CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(170.0, o3tl::Length::mm, o3tl::Length::pt),
|
|
|
|
|
bounds.getWidth(), 1);
|
|
|
|
|
//
|
|
|
|
|
// - Expected: 728.503937007874
|
|
|
|
|
// - Actual : 365.25
|
|
|
|
|
// - Delta : 1
|
|
|
|
|
CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(257.0, o3tl::Length::mm, o3tl::Length::pt),
|
|
|
|
|
bounds.getHeight(), 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
|
|
CPPUNIT_PLUGIN_IMPLEMENT();
|
|
|
|
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|