tdf#160196 add support for PDF/UA-2 and NS for struct. elem.
This adds support for PDF/UA-2 which is used when using PDF 2.0 (and PDF/UA-1 when using PDF 1.x). PDF/UA-2 requires that the PDF 2.0 introduced namespaces are set for the structure elements, so this also adds support for the namespaces with always setting the PDF 2.0 namespace for all the structured elements (we don't use others anyway). Change-Id: Ifbd8f8126d9bcc0d4dcd06a5f56bacdb0804adbd Reviewed-on: https://gerrit.libreoffice.org/c/core/+/180565 Reviewed-by: Tomaž Vajngerl <quikee@gmail.com> Tested-by: Jenkins
This commit is contained in:
committed by
Tomaž Vajngerl
parent
88906ae23d
commit
d23c5560c1
@@ -117,6 +117,12 @@ public:
|
||||
mrBuffer.append(")");
|
||||
}
|
||||
|
||||
void writeKeyAndLiteral(std::string_view key, std::string_view value)
|
||||
{
|
||||
mrBuffer.append(key);
|
||||
writeLiteral(value);
|
||||
}
|
||||
|
||||
void writeLiteralEncrypt(std::u16string_view value, sal_Int32 nObject,
|
||||
rtl_TextEncoding nEncoding = RTL_TEXTENCODING_ASCII_US);
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ public:
|
||||
OString m_sCreateDate;
|
||||
|
||||
sal_Int32 mnPDF_A = 0;
|
||||
bool mbPDF_UA = false;
|
||||
sal_Int32 mnPDF_UA = 0;
|
||||
|
||||
public:
|
||||
XmpMetadata();
|
||||
|
||||
@@ -835,6 +835,8 @@ private:
|
||||
|
||||
::comphelper::Hash m_DocDigest;
|
||||
|
||||
std::unordered_map<std::string_view, sal_Int32> m_aNamespacesMap;
|
||||
|
||||
// reduce repeated allocations
|
||||
OStringBuffer updateGraphicsStateLine{256};
|
||||
OStringBuffer drawBitmapLine{80};
|
||||
@@ -989,6 +991,8 @@ private:
|
||||
//check if internal dummy container are needed in the structure elements
|
||||
void addInternalStructureContainer( PDFStructureElement& rEle );
|
||||
//<---i94258
|
||||
// writes namespaces
|
||||
void emitNamespaces();
|
||||
// writes document structure
|
||||
sal_Int32 emitStructure( PDFStructureElement& rEle );
|
||||
// writes structure parent tree
|
||||
|
||||
@@ -111,6 +111,8 @@ static bool g_bDebugDisableCompression = getenv("VCL_DEBUG_DISABLE_PDFCOMPRESSIO
|
||||
namespace
|
||||
{
|
||||
|
||||
constexpr std::string_view constNamespacePDF2("http://iso.org/pdf2/ssn");
|
||||
|
||||
constexpr sal_Int32 nLog10Divisor = 3;
|
||||
constexpr double fDivisor = 1000.0;
|
||||
|
||||
@@ -1334,6 +1336,12 @@ PDFWriterImpl::PDFWriterImpl( const PDFWriter::PDFWriterContext& rContext,
|
||||
m_aContext.Tagged = true;
|
||||
}
|
||||
|
||||
// Add common PDF 2.0 namespace when we are using PDF 2.0
|
||||
if (m_aContext.Version == PDFWriter::PDFVersion::PDF_2_0)
|
||||
{
|
||||
m_aNamespacesMap.emplace(constNamespacePDF2, createObject());
|
||||
}
|
||||
|
||||
if( m_aContext.DPIx == 0 || m_aContext.DPIy == 0 )
|
||||
SetReferenceDevice( VirtualDevice::RefDevMode::PDF1 );
|
||||
else
|
||||
@@ -1933,6 +1941,30 @@ OString PDFWriterImpl::emitStructureAttributes( PDFStructureElement& i_rEle )
|
||||
return aRet.makeStringAndClear();
|
||||
}
|
||||
|
||||
// Write the namespace objects to the stream
|
||||
void PDFWriterImpl::emitNamespaces()
|
||||
{
|
||||
if (m_aContext.Version < PDFWriter::PDFVersion::PDF_2_0)
|
||||
return;
|
||||
|
||||
for (auto&[sNamespace, nObject] : m_aNamespacesMap)
|
||||
{
|
||||
if (!updateObject(nObject))
|
||||
return;
|
||||
|
||||
COSWriter aWriter(m_aContext.Encryption.getParams(), m_pPDFEncryptor);
|
||||
aWriter.startObject(nObject);
|
||||
aWriter.startDict();
|
||||
aWriter.write("/Type", "/Namespace");
|
||||
aWriter.writeKeyAndLiteral("/NS", sNamespace);
|
||||
aWriter.endDict();
|
||||
aWriter.endObject();
|
||||
|
||||
if (!writeBuffer(aWriter.getLine()))
|
||||
m_aNamespacesMap[sNamespace] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
sal_Int32 PDFWriterImpl::emitStructure( PDFStructureElement& rEle )
|
||||
{
|
||||
assert(rEle.m_nOwnElement == 0 || rEle.m_oType);
|
||||
@@ -1979,10 +2011,21 @@ sal_Int32 PDFWriterImpl::emitStructure( PDFStructureElement& rEle )
|
||||
nParentTree = createObject();
|
||||
if (!nParentTree)
|
||||
return 0;
|
||||
aLine.append( "/StructTreeRoot\n"
|
||||
"/ParentTree "
|
||||
+ OString::number(nParentTree)
|
||||
+ " 0 R\n" );
|
||||
aLine.append("/StructTreeRoot\n");
|
||||
aWriter.writeKeyAndReference("/ParentTree", nParentTree);
|
||||
|
||||
// Write the reference to the PDF 2.0 namespace
|
||||
if (m_aContext.Version >= PDFWriter::PDFVersion::PDF_2_0)
|
||||
{
|
||||
auto iterator = m_aNamespacesMap.find(constNamespacePDF2);
|
||||
if (iterator != m_aNamespacesMap.end())
|
||||
{
|
||||
aLine.append("/Namespaces [");
|
||||
aWriter.writeReference(iterator->second);
|
||||
aLine.append("]");
|
||||
}
|
||||
}
|
||||
|
||||
if( ! m_aRoleMap.empty() )
|
||||
{
|
||||
aLine.append( "/RoleMap<<" );
|
||||
@@ -2002,8 +2045,16 @@ sal_Int32 PDFWriterImpl::emitStructure( PDFStructureElement& rEle )
|
||||
}
|
||||
else
|
||||
{
|
||||
aLine.append( "/StructElem\n"
|
||||
"/S/" );
|
||||
aLine.append("/StructElem");
|
||||
|
||||
// Write the reference to the PDF 2.0 namespace
|
||||
if (m_aContext.Version >= PDFWriter::PDFVersion::PDF_2_0)
|
||||
{
|
||||
auto iterator = m_aNamespacesMap.find(constNamespacePDF2);
|
||||
if (iterator != m_aNamespacesMap.end())
|
||||
aWriter.writeKeyAndReference("/NS", iterator->second);
|
||||
}
|
||||
aLine.append("/S/");
|
||||
if( !rEle.m_aAlias.isEmpty() )
|
||||
aLine.append( rEle.m_aAlias );
|
||||
else
|
||||
@@ -5149,6 +5200,8 @@ bool PDFWriterImpl::emitCatalog()
|
||||
// emit metadata
|
||||
sal_Int32 nMetadataObject = emitDocumentMetadata();
|
||||
|
||||
emitNamespaces();
|
||||
|
||||
sal_Int32 nStructureDict = 0;
|
||||
if(m_aStructure.size() > 1)
|
||||
{
|
||||
@@ -5341,12 +5394,13 @@ bool PDFWriterImpl::emitCatalog()
|
||||
}
|
||||
|
||||
// viewer preferences, if we had some, then emit
|
||||
if( m_aContext.HideViewerToolbar ||
|
||||
if (m_aContext.HideViewerToolbar ||
|
||||
(!m_aContext.DocumentInfo.Title.isEmpty() && m_aContext.DisplayPDFDocumentTitle) ||
|
||||
m_aContext.HideViewerMenubar ||
|
||||
m_aContext.HideViewerWindowControls || m_aContext.FitWindow ||
|
||||
m_aContext.CenterWindow || (m_aContext.FirstPageLeft && m_aContext.PageLayout == PDFWriter::ContinuousFacing ) ||
|
||||
m_aContext.OpenInFullScreenMode )
|
||||
m_aContext.OpenInFullScreenMode ||
|
||||
m_bIsPDF_UA)
|
||||
{
|
||||
aLine.append( "/ViewerPreferences<<" );
|
||||
if( m_aContext.HideViewerToolbar )
|
||||
@@ -5359,7 +5413,8 @@ bool PDFWriterImpl::emitCatalog()
|
||||
aLine.append( "/FitWindow true\n" );
|
||||
if( m_aContext.CenterWindow )
|
||||
aLine.append( "/CenterWindow true\n" );
|
||||
if (!m_aContext.DocumentInfo.Title.isEmpty() && m_aContext.DisplayPDFDocumentTitle)
|
||||
// PDF/UA-2 requires /DisplayDocTitle set to true
|
||||
if ((!m_aContext.DocumentInfo.Title.isEmpty() && m_aContext.DisplayPDFDocumentTitle) || m_bIsPDF_UA)
|
||||
aLine.append( "/DisplayDocTitle true\n" );
|
||||
if( m_aContext.FirstPageLeft && m_aContext.PageLayout == PDFWriter::ContinuousFacing )
|
||||
aLine.append( "/Direction/R2L\n" );
|
||||
@@ -5877,7 +5932,11 @@ sal_Int32 PDFWriterImpl::emitDocumentMetadata()
|
||||
if (m_nPDFA_Version > 0)
|
||||
aMetadata.mnPDF_A = m_nPDFA_Version;
|
||||
|
||||
aMetadata.mbPDF_UA = m_bIsPDF_UA;
|
||||
if (m_bIsPDF_UA)
|
||||
{
|
||||
// If PDF 2.0+ we need to use PDF/UA-2 otherwise PDF/UA-1
|
||||
aMetadata.mnPDF_UA = (m_aContext.Version >= PDFWriter::PDFVersion::PDF_2_0) ? 2 : 1;
|
||||
}
|
||||
|
||||
lcl_assignMeta(m_aContext.DocumentInfo.Title, aMetadata.msTitle);
|
||||
lcl_assignMeta(m_aContext.DocumentInfo.Author, aMetadata.msAuthor);
|
||||
|
||||
@@ -206,7 +206,7 @@ void XmpMetadata::write()
|
||||
}
|
||||
|
||||
// PDF/UA
|
||||
if (mbPDF_UA)
|
||||
if (mnPDF_UA > 0)
|
||||
{
|
||||
if (mnPDF_A != 0)
|
||||
{ // tdf#157517 PDF/A extension schema is required
|
||||
@@ -289,7 +289,7 @@ void XmpMetadata::write()
|
||||
aXmlWriter.endElement(); // pdfaExtension:schemas
|
||||
aXmlWriter.endElement(); // rdf:Description
|
||||
}
|
||||
OString sPdfUaVersion = OString::number(1);
|
||||
OString sPdfUaVersion = OString::number(mnPDF_UA);
|
||||
aXmlWriter.startElement("rdf:Description");
|
||||
aXmlWriter.attribute("rdf:about", ""_ostr);
|
||||
aXmlWriter.attribute("xmlns:pdfuaid", "http://www.aiim.org/pdfua/ns/id/"_ostr);
|
||||
@@ -298,6 +298,12 @@ void XmpMetadata::write()
|
||||
aXmlWriter.content(sPdfUaVersion);
|
||||
aXmlWriter.endElement();
|
||||
|
||||
if (mnPDF_UA == 2)
|
||||
{
|
||||
aXmlWriter.startElement("pdfuaid:rev");
|
||||
aXmlWriter.content("2024"_ostr);
|
||||
aXmlWriter.endElement();
|
||||
}
|
||||
aXmlWriter.endElement();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user