/* -*- 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/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
/* * Convert a string before using it. * * This string conversion function is needed because the destination name * in a PDF file seen through an Internet browser should be * specially crafted, in order to be used directly by the browser. * In this way the fragment part of a hyperlink to a PDF file (e.g. something * as 'test1/test2/a-file.pdf\#thefragment) will be (hopefully) interpreted by the * PDF reader (currently only Adobe Reader plug-in seems to be working that way) called * from inside the Internet browser as: 'open the file test1/test2/a-file.pdf * and go to named destination thefragment using default zoom'. * The conversion is needed because in case of a fragment in the form: Slide%201 * (meaning Slide 1) as it is converted obeying the Inet rules, it will become Slide25201 * using this conversion, in both the generated named destinations, fragment and GoToR * destination. * * The names for destinations are name objects and so they don't need to be encrypted * even though they expose the content of PDF file (e.g. guessing the PDF content from the * destination name). * * Further limitation: it is advisable to use standard ASCII characters for * OOo bookmarks.
*/ void appendDestinationName( const OUString& rString, OStringBuffer& rBuffer )
{ const sal_Unicode* pStr = rString.getStr();
sal_Int32 nLen = rString.getLength(); for( int i = 0; i < nLen; i++ )
{
sal_Unicode aChar = pStr[i]; if( (aChar >= '0' && aChar <= '9' ) ||
(aChar >= 'a' && aChar <= 'z' ) ||
(aChar >= 'A' && aChar <= 'Z' ) ||
aChar == '-' )
{
rBuffer.append(static_cast<char>(aChar));
} else
{
sal_Int8 aValueHigh = sal_Int8(aChar >> 8); if(aValueHigh > 0)
vcl::COSWriter::appendHex(aValueHigh, rBuffer);
vcl::COSWriter::appendHex(static_cast<sal_Int8>(aChar & 255 ), rBuffer);
}
}
}
} // end anonymous namespace
void PDFWriterImpl::createWidgetFieldName( sal_Int32 i_nWidgetIndex, const PDFWriter::AnyWidget& i_rControl )
{ /* #i80258# previously we use appendName here however we need a slightly different coding scheme than the normal name encoding for field names
*/ const OUString& rName = i_rControl.Name;
OString aStr( OUStringToOString( rName, RTL_TEXTENCODING_UTF8 ) ); int nLen = aStr.getLength();
OStringBuffer aBuffer( rName.getLength()+64 ); for( int i = 0; i < nLen; i++ )
{ /* #i16920# PDF recommendation: output UTF8, any byte * outside the interval [32(=ASCII' ');126(=ASCII'~')] * should be escaped hexadecimal
*/ if( aStr[i] >= 32 && aStr[i] <= 126 )
aBuffer.append( aStr[i] ); else
{
aBuffer.append( '#' );
COSWriter::appendHex(static_cast<sal_Int8>(aStr[i]), aBuffer);
}
}
/* #i82785# create hierarchical fields down to the for each dot in i_rName */
sal_Int32 nTokenIndex = 0, nLastTokenIndex = 0;
OString aPartialName;
OString aDomain; do
{
nLastTokenIndex = nTokenIndex;
aPartialName = aFullName.getToken( 0, '.', nTokenIndex ); if( nTokenIndex != -1 )
{ // find or create a hierarchical field // first find the fully qualified name up to this field
aDomain = aFullName.copy( 0, nTokenIndex-1 );
std::unordered_map< OString, sal_Int32 >::const_iterator it = m_aFieldNameMap.find( aDomain ); if( it == m_aFieldNameMap.end() )
{ // create new hierarchy field
sal_Int32 nNewWidget = m_aWidgets.size();
m_aWidgets.emplace_back( );
m_aWidgets[nNewWidget].m_nObject = createObject();
m_aWidgets[nNewWidget].m_eType = PDFWriter::Hierarchy;
m_aWidgets[nNewWidget].m_aName = aPartialName;
m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[nNewWidget].m_nObject;
m_aFieldNameMap[aDomain] = nNewWidget;
m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[nNewWidget].m_nObject; if( nLastTokenIndex > 0 )
{ // this field is not a root field and // needs to be inserted to its parent
OString aParentDomain( aDomain.copy( 0, nLastTokenIndex-1 ) );
it = m_aFieldNameMap.find( aParentDomain );
OSL_ENSURE( it != m_aFieldNameMap.end(), "field name not found" ); if( it != m_aFieldNameMap.end() )
{
OSL_ENSURE( it->second < sal_Int32(m_aWidgets.size()), "invalid field number entry" ); if( it->second < sal_Int32(m_aWidgets.size()) )
{
PDFWidget& rParentField( m_aWidgets[it->second] );
rParentField.m_aKids.push_back( m_aWidgets[nNewWidget].m_nObject );
rParentField.m_aKidsIndex.push_back( nNewWidget );
m_aWidgets[nNewWidget].m_nParent = rParentField.m_nObject;
}
}
}
} elseif( m_aWidgets[it->second].m_eType != PDFWriter::Hierarchy )
{ // this is invalid, someone tries to have a terminal field as parent // example: a button with the name foo.bar exists and // another button is named foo.bar.no // workaround: put the second terminal field as much up in the hierarchy as // necessary to have a non-terminal field as parent (or none at all) // since it->second already is terminal, we just need to use its parent
aDomain.clear();
aPartialName = aFullName.copy( aFullName.lastIndexOf( '.' )+1 ); if( nLastTokenIndex > 0 )
{
aDomain = aFullName.copy( 0, nLastTokenIndex-1 );
aFullName = aDomain + "." + aPartialName;
} else
aFullName = aPartialName; break;
}
}
} while( nTokenIndex != -1 );
// insert widget into its hierarchy field if( !aDomain.isEmpty() )
{
std::unordered_map< OString, sal_Int32 >::const_iterator it = m_aFieldNameMap.find( aDomain ); if( it != m_aFieldNameMap.end() )
{
OSL_ENSURE( it->second >= 0 && o3tl::make_unsigned(it->second) < m_aWidgets.size(), "invalid field index" ); if( it->second >= 0 && o3tl::make_unsigned(it->second) < m_aWidgets.size() )
{
m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[it->second].m_nObject;
m_aWidgets[it->second].m_aKids.push_back( m_aWidgets[i_nWidgetIndex].m_nObject);
m_aWidgets[it->second].m_aKidsIndex.push_back( i_nWidgetIndex );
}
}
}
if( aPartialName.isEmpty() )
{ // how funny, an empty field name if( i_rControl.getType() == PDFWriter::RadioButton )
{
aPartialName = "RadioGroup" +
OString::number( static_cast<const PDFWriter::RadioButtonWidget&>(i_rControl).RadioGroup );
} else
aPartialName = "Widget"_ostr;
}
constchar* getPDFVersionStr(PDFWriter::PDFVersion ePDFVersion)
{ switch (ePDFVersion)
{ case PDFWriter::PDFVersion::PDF_A_1: case PDFWriter::PDFVersion::PDF_1_4: return"1.4"; case PDFWriter::PDFVersion::PDF_1_5: return"1.5"; case PDFWriter::PDFVersion::PDF_1_6: return"1.6"; default: case PDFWriter::PDFVersion::PDF_A_2: case PDFWriter::PDFVersion::PDF_A_3: case PDFWriter::PDFVersion::PDF_1_7: return"1.7"; // PDF 2.0 case PDFWriter::PDFVersion::PDF_A_4: case PDFWriter::PDFVersion::PDF_2_0: return"2.0";
}
}
//build the document id
OString aInfoValuesOut;
OStringBuffer aID(1024); if (!i_rDocInfo.Title.isEmpty())
COSWriter::appendUnicodeTextString(i_rDocInfo.Title, aID); if (!i_rDocInfo.Author.isEmpty())
COSWriter::appendUnicodeTextString(i_rDocInfo.Author, aID); if (!i_rDocInfo.Subject.isEmpty())
COSWriter::appendUnicodeTextString(i_rDocInfo.Subject, aID); if (!i_rDocInfo.Keywords.isEmpty())
COSWriter::appendUnicodeTextString(i_rDocInfo.Keywords, aID); if (!i_rDocInfo.Creator.isEmpty())
COSWriter::appendUnicodeTextString(i_rDocInfo.Creator, aID); if (!i_rDocInfo.Producer.isEmpty())
COSWriter::appendUnicodeTextString(i_rDocInfo.Producer, aID);
::comphelper::Hash aDigest(::comphelper::HashType::MD5);
aDigest.update(reinterpret_cast<unsignedcharconst*>(&aGMT), sizeof(aGMT));
aDigest.update(reinterpret_cast<unsignedcharconst*>(aInfoValuesOut.getStr()),
aInfoValuesOut.getLength()); //the binary form of the doc id is needed for encryption stuff
o_rIdentifier = aDigest.finalize();
}
switch (m_pWriter->m_aContext.Version)
{ // 1.6 or later default:
m_nUserUnit = std::ceil(std::max(nPageWidth, nPageHeight) / 14400.0); break; case PDFWriter::PDFVersion::PDF_1_4: case PDFWriter::PDFVersion::PDF_1_5: case PDFWriter::PDFVersion::PDF_A_1: break;
}
}
double PDFPage::getHeight() const
{ double fRet = m_nPageHeight ? m_nPageHeight : 842; // default A4 height in inch/72, OK to use hardcoded value here?
switch (m_aContext.Version)
{ case PDFWriter::PDFVersion::PDF_A_1:
m_nPDFA_Version = 1;
m_bIsPDF_A1 = true;
m_aContext.Version = PDFWriter::PDFVersion::PDF_1_4; //meaning we need PDF 1.4, PDF/A flavour break; case PDFWriter::PDFVersion::PDF_A_2:
m_nPDFA_Version = 2;
m_bIsPDF_A2 = true;
m_aContext.Version = PDFWriter::PDFVersion::PDF_1_7; break; case PDFWriter::PDFVersion::PDF_A_3:
m_nPDFA_Version = 3;
m_bIsPDF_A3 = true;
m_aContext.Version = PDFWriter::PDFVersion::PDF_1_7; break; case PDFWriter::PDFVersion::PDF_A_4:
m_nPDFA_Version = 4;
m_bIsPDF_A4 = true;
m_aContext.Version = PDFWriter::PDFVersion::PDF_2_0; break; default: break;
}
// PDF/UA can only be enabled if PDF version is 1.7 (PDF/UA-1) and 2.0 (PDF/UA-2) if (m_aContext.UniversalAccessibilityCompliance && m_aContext.Version >= PDFWriter::PDFVersion::PDF_1_7)
{
m_bIsPDF_UA = true;
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());
}
// every structure element already has a unique object id - just use it for ID static OString GenerateID(sal_Int32 const nObjectId)
{ return"id" + OString::number(nObjectId);
}
sal_Int32 PDFWriterImpl::emitStructIDTree(sal_Int32 const nObject)
{ // loosely following PDF 1.7, 10.6.5 Example of Logical Structure, Example 10.15 if (nObject < 0)
{ return nObject;
} // the name tree entries must be sorted lexicographically.
std::map<OString, sal_Int32> ids; for (auto n : m_StructElemObjsWithID)
{
ids.emplace(GenerateID(n), n);
}
OStringBuffer buf;
COSWriter aWriter(buf, m_aContext.Encryption.getParams(), m_pPDFEncryptor);
appendObjectID(nObject, buf);
buf.append("<); for (autoconst& it : ids)
{
aWriter.writeLiteralEncrypt(it.first, nObject);
buf.append(" ");
appendObjectReference(it.second, buf);
buf.append("\n");
}
buf.append("] >>\nendobj\n\n");
if (!updateObject(nObject)) return 0; if (!writeBuffer(buf)) return 0;
OString PDFWriterImpl::emitStructureAttributes( PDFStructureElement& i_rEle )
{ // create layout, list and table attribute sets
OStringBuffer aLayout(256), aList(64), aTable(64);
OStringBuffer aPrintField; for (autoconst& attribute : i_rEle.m_aAttributes)
{ if( attribute.first == PDFWriter::ListNumbering )
appendStructureAttributeLine( attribute.first, attribute.second, aList, true ); elseif (attribute.first == PDFWriter::Role)
{
appendStructureAttributeLine(attribute.first, attribute.second, aPrintField, true);
} elseif( attribute.first == PDFWriter::RowSpan ||
attribute.first == PDFWriter::ColSpan ||
attribute.first == PDFWriter::Scope)
{
appendStructureAttributeLine( attribute.first, attribute.second, aTable, false );
} elseif( attribute.first == PDFWriter::LinkAnnotation )
{
sal_Int32 nLink = attribute.second.nValue;
std::map< sal_Int32, sal_Int32 >::const_iterator link_it =
m_aLinkPropertyMap.find( nLink ); if( link_it != m_aLinkPropertyMap.end() )
nLink = link_it->second; if( nLink >= 0 && o3tl::make_unsigned(nLink) < m_aLinks.size() )
{
AppendAnnotKid(i_rEle, m_aLinks[nLink]);
} else
{
OSL_FAIL( "unresolved link id for Link structure" );
SAL_INFO("vcl.pdfwriter", "unresolved link id " << nLink << " for Link structure"); if (g_bDebugDisableCompression)
{
OString aLine = "unresolved link id " +
OString::number( nLink ) + " for Link structure";
emitComment( aLine.getStr() );
}
}
} elseif (attribute.first == PDFWriter::NoteAnnotation)
{
sal_Int32 nNote = attribute.second.nValue;
std::map<sal_Int32, sal_Int32>::const_iterator link_it = m_aLinkPropertyMap.find(nNote); if (link_it != m_aLinkPropertyMap.end())
nNote = link_it->second; if (nNote >= 0 && o3tl::make_unsigned(nNote) < m_aNotes.size())
{
AppendAnnotKid(i_rEle, m_aNotes[nNote]);
} else
{
OSL_FAIL("unresolved note id for Note structure");
SAL_INFO("vcl.pdfwriter", "unresolved note id " << nNote << " for Note structure"); if (g_bDebugDisableCompression)
{
OString aLine
= "unresolved note id " + OString::number(nNote) + " for Note structure";
emitComment(aLine.getStr());
}
}
} else
appendStructureAttributeLine( attribute.first, attribute.second, aLayout, true );
} if( ! i_rEle.m_aBBox.IsEmpty() )
{
aLayout.append( "/BBox[" );
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.30Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.