summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--xmlsecurity/inc/pdfio/pdfdocument.hxx8
-rw-r--r--xmlsecurity/source/pdfio/pdfdocument.cxx175
2 files changed, 174 insertions, 9 deletions
diff --git a/xmlsecurity/inc/pdfio/pdfdocument.hxx b/xmlsecurity/inc/pdfio/pdfdocument.hxx
index b02bf9322088..3886eab8e73a 100644
--- a/xmlsecurity/inc/pdfio/pdfdocument.hxx
+++ b/xmlsecurity/inc/pdfio/pdfdocument.hxx
@@ -37,7 +37,13 @@ public:
virtual ~PDFElement() { }
};
-/// In-memory representation of an on-disk PDF document.
+/**
+ * In-memory representation of an on-disk PDF document.
+ *
+ * The PDF element list is not meant to be saved back to disk, but some
+ * elements remember their source offset / length, and based on that it's
+ * possible to modify the input file.
+ */
class XMLSECURITY_DLLPUBLIC PDFDocument
{
/// This vector owns all elements.
diff --git a/xmlsecurity/source/pdfio/pdfdocument.cxx b/xmlsecurity/source/pdfio/pdfdocument.cxx
index 947bacb78f13..5778f202f835 100644
--- a/xmlsecurity/source/pdfio/pdfdocument.cxx
+++ b/xmlsecurity/source/pdfio/pdfdocument.cxx
@@ -59,11 +59,18 @@ public:
/// Numbering object: an integer or a real.
class PDFNumberElement : public PDFElement
{
+ /// Input file start location.
+ sal_uInt64 m_nOffset;
+ /// Input file token length.
+ sal_uInt64 m_nLength;
double m_fValue;
public:
+ PDFNumberElement();
bool Read(SvStream& rStream) override;
double GetValue() const;
+ sal_uInt64 GetLocation() const;
+ sal_uInt64 GetLength() const;
};
class PDFReferenceElement;
@@ -79,6 +86,10 @@ class PDFObjectElement : public PDFElement
sal_uInt64 m_nDictionaryOffset;
/// Length of the dictionary buffer till (before) the '<<' token.
sal_uInt64 m_nDictionaryLength;
+ /// Position after the '/' token.
+ std::map<OString, sal_uInt64> m_aDictionaryKeyOffset;
+ /// Length of the dictionary key and value, till (before) the next token.
+ std::map<OString, sal_uInt64> m_aDictionaryKeyValueLength;
public:
PDFObjectElement(PDFDocument& rDoc, double fObjectValue, double fGenerationValue);
@@ -91,12 +102,16 @@ public:
sal_uInt64 GetDictionaryOffset();
void SetDictionaryLength(sal_uInt64 nDictionaryLength);
sal_uInt64 GetDictionaryLength();
+ void SetDictionaryKeyOffset(const OString& rKey, sal_uInt64 nOffset);
+ sal_uInt64 GetDictionaryKeyOffset(const OString& rKey) const;
+ void SetDictionaryKeyValueLength(const OString& rKey, sal_uInt64 nLength);
+ sal_uInt64 GetDictionaryKeyValueLength(const OString& rKey) const;
};
/// Dictionary object: a set key-value pairs.
class PDFDictionaryElement : public PDFElement
{
- /// Offset after the '>>' token.
+ /// Offset after the '<<' token.
sal_uInt64 m_nLocation;
public:
@@ -122,9 +137,16 @@ public:
class PDFNameElement : public PDFElement
{
OString m_aValue;
+ /// Offset after the '/' token.
+ sal_uInt64 m_nLocation;
+ /// Length till the next token start.
+ sal_uInt64 m_nLength;
public:
+ PDFNameElement();
bool Read(SvStream& rStream) override;
const OString& GetValue() const;
+ sal_uInt64 GetLocation() const;
+ sal_uInt64 GetLength() const;
};
/// Reference object: something with a unique ID.
@@ -172,8 +194,11 @@ public:
/// Array object: a list.
class PDFArrayElement : public PDFElement
{
+ /// Location after the '[' token.
+ sal_uInt64 m_nOffset;
std::vector<PDFElement*> m_aElements;
public:
+ PDFArrayElement();
bool Read(SvStream& rStream) override;
void PushBack(PDFElement* pElement);
const std::vector<PDFElement*>& GetElements();
@@ -182,8 +207,12 @@ public:
/// End of an array: ']'.
class PDFEndArrayElement : public PDFElement
{
+ /// Location before the ']' token.
+ sal_uInt64 m_nOffset;
public:
+ PDFEndArrayElement();
bool Read(SvStream& rStream) override;
+ sal_uInt64 GetOffset() const;
};
/// Boolean object: a 'true' or a 'false'.
@@ -355,11 +384,33 @@ bool PDFDocument::Sign(const uno::Reference<security::XCertificate>& xCertificat
m_aEditBuffer.WriteUInt32AsString(nFirstPageId);
m_aEditBuffer.WriteCharPtr(" 0 obj\n");
m_aEditBuffer.WriteCharPtr("<<");
- m_aEditBuffer.WriteBytes(static_cast<const char*>(m_aEditBuffer.GetData()) + pFirstPage->GetDictionaryOffset(), pFirstPage->GetDictionaryLength());
- m_aEditBuffer.WriteCharPtr("/Annots[");
- m_aEditBuffer.WriteUInt32AsString(nAnnotId);
- m_aEditBuffer.WriteCharPtr(" 0 R]");
- m_aEditBuffer.WriteCharPtr(">>\nendobj\n\n");
+ auto pAnnots = dynamic_cast<PDFArrayElement*>(pFirstPage->Lookup("Annots"));
+ if (!pAnnots)
+ {
+ // No Annots key, just write the key with a single reference.
+ m_aEditBuffer.WriteBytes(static_cast<const char*>(m_aEditBuffer.GetData()) + pFirstPage->GetDictionaryOffset(), pFirstPage->GetDictionaryLength());
+ m_aEditBuffer.WriteCharPtr("/Annots[");
+ m_aEditBuffer.WriteUInt32AsString(nAnnotId);
+ m_aEditBuffer.WriteCharPtr(" 0 R]");
+ }
+ else
+ {
+ // Annots key is already there, insert our reference at the end.
+
+ // Offset right before the end of the Annots array.
+ sal_uInt64 nAnnotsEndOffset = pFirstPage->GetDictionaryKeyOffset("Annots") + pFirstPage->GetDictionaryKeyValueLength("Annots") - 1;
+ // Length of beginning of the dictionary -> Annots end.
+ sal_uInt64 nAnnotsBeforeEndLength = nAnnotsEndOffset - pFirstPage->GetDictionaryOffset();
+ m_aEditBuffer.WriteBytes(static_cast<const char*>(m_aEditBuffer.GetData()) + pFirstPage->GetDictionaryOffset(), nAnnotsBeforeEndLength);
+ m_aEditBuffer.WriteCharPtr(" ");
+ m_aEditBuffer.WriteUInt32AsString(nAnnotId);
+ m_aEditBuffer.WriteCharPtr(" 0 R");
+ // Length of Annots end -> end of the dictionary.
+ sal_uInt64 nAnnotsAfterEndLength = pFirstPage->GetDictionaryOffset() + pFirstPage->GetDictionaryLength() - nAnnotsEndOffset;
+ m_aEditBuffer.WriteBytes(static_cast<const char*>(m_aEditBuffer.GetData()) + nAnnotsEndOffset, nAnnotsAfterEndLength);
+ }
+ m_aEditBuffer.WriteCharPtr(">>");
+ m_aEditBuffer.WriteCharPtr("\nendobj\n\n");
// Write the updated Catalog object, references nAnnotId.
auto pRoot = dynamic_cast<PDFReferenceElement*>(m_pTrailer->Lookup("Root"));
@@ -1349,9 +1400,17 @@ bool PDFCommentElement::Read(SvStream& rStream)
return false;
}
+PDFNumberElement::PDFNumberElement()
+ : m_nOffset(0),
+ m_nLength(0),
+ m_fValue(0)
+{
+}
+
bool PDFNumberElement::Read(SvStream& rStream)
{
OStringBuffer aBuf;
+ m_nOffset = rStream.Tell();
char ch;
rStream.ReadChar(ch);
while (!rStream.IsEof())
@@ -1359,6 +1418,7 @@ bool PDFNumberElement::Read(SvStream& rStream)
if (!isdigit(ch) && ch != '-')
{
rStream.SeekRel(-1);
+ m_nLength = rStream.Tell() - m_nOffset;
m_fValue = aBuf.makeStringAndClear().toDouble();
SAL_INFO("xmlsecurity.pdfio", "PDFNumberElement::Read: m_fValue is '" << m_fValue << "'");
return true;
@@ -1370,6 +1430,16 @@ bool PDFNumberElement::Read(SvStream& rStream)
return false;
}
+sal_uInt64 PDFNumberElement::GetLocation() const
+{
+ return m_nOffset;
+}
+
+sal_uInt64 PDFNumberElement::GetLength() const
+{
+ return m_nLength;
+}
+
PDFBooleanElement::PDFBooleanElement(bool /*bValue*/)
{
}
@@ -1506,6 +1576,7 @@ void PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement>
}
OString aName;
+ sal_uInt64 nNameOffset = 0;
std::vector<PDFNumberElement*> aNumbers;
// The array value we're in -- if any.
PDFArrayElement* pArray = nullptr;
@@ -1541,7 +1612,13 @@ void PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement>
{
if (!aNumbers.empty())
{
- rDictionary[aName] = aNumbers.back();
+ PDFNumberElement* pNumber = aNumbers.back();
+ rDictionary[aName] = pNumber;
+ if (pThisObject)
+ {
+ pThisObject->SetDictionaryKeyOffset(aName, nNameOffset);
+ pThisObject->SetDictionaryKeyValueLength(aName, pNumber->GetLocation() + pNumber->GetLength() - nNameOffset);
+ }
aName.clear();
aNumbers.clear();
}
@@ -1550,11 +1627,17 @@ void PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement>
{
// Remember key.
aName = pName->GetValue();
+ nNameOffset = pName->GetLocation();
}
else
{
// Name-name key-value.
rDictionary[aName] = pName;
+ if (pThisObject)
+ {
+ pThisObject->SetDictionaryKeyOffset(aName, nNameOffset);
+ pThisObject->SetDictionaryKeyValueLength(aName, pName->GetLocation() + pName->GetLength() - nNameOffset);
+ }
aName.clear();
}
continue;
@@ -1567,7 +1650,8 @@ void PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement>
continue;
}
- if (pArray && dynamic_cast<PDFEndArrayElement*>(rElements[i].get()))
+ auto pEndArr = dynamic_cast<PDFEndArrayElement*>(rElements[i].get());
+ if (pArray && pEndArr)
{
if (!aNumbers.empty())
{
@@ -1576,6 +1660,12 @@ void PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement>
aNumbers.clear();
}
rDictionary[aName] = pArray;
+ if (pThisObject)
+ {
+ pThisObject->SetDictionaryKeyOffset(aName, nNameOffset);
+ // Include the ending ']' in the length of the key - (array)value pair length.
+ pThisObject->SetDictionaryKeyValueLength(aName, pEndArr->GetOffset() - nNameOffset + 1);
+ }
aName.clear();
pArray = nullptr;
continue;
@@ -1587,6 +1677,8 @@ void PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement>
if (!pArray)
{
rDictionary[aName] = pReference;
+ if (pThisObject)
+ pThisObject->SetDictionaryKeyOffset(aName, nNameOffset);
aName.clear();
}
else
@@ -1601,6 +1693,8 @@ void PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement>
if (pLiteralString)
{
rDictionary[aName] = pLiteralString;
+ if (pThisObject)
+ pThisObject->SetDictionaryKeyOffset(aName, nNameOffset);
aName.clear();
continue;
}
@@ -1611,6 +1705,8 @@ void PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement>
if (!pArray)
{
rDictionary[aName] = pHexString;
+ if (pThisObject)
+ pThisObject->SetDictionaryKeyOffset(aName, nNameOffset);
aName.clear();
}
else
@@ -1633,6 +1729,8 @@ void PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement>
if (!aNumbers.empty())
{
rDictionary[aName] = aNumbers.back();
+ if (pThisObject)
+ pThisObject->SetDictionaryKeyOffset(aName, nNameOffset);
aName.clear();
aNumbers.clear();
}
@@ -1690,6 +1788,34 @@ sal_uInt64 PDFObjectElement::GetDictionaryOffset()
return m_nDictionaryOffset;
}
+void PDFObjectElement::SetDictionaryKeyOffset(const OString& rKey, sal_uInt64 nOffset)
+{
+ m_aDictionaryKeyOffset[rKey] = nOffset;
+}
+
+void PDFObjectElement::SetDictionaryKeyValueLength(const OString& rKey, sal_uInt64 nLength)
+{
+ m_aDictionaryKeyValueLength[rKey] = nLength;
+}
+
+sal_uInt64 PDFObjectElement::GetDictionaryKeyOffset(const OString& rKey) const
+{
+ auto it = m_aDictionaryKeyOffset.find(rKey);
+ if (it == m_aDictionaryKeyOffset.end())
+ return 0;
+
+ return it->second;
+}
+
+sal_uInt64 PDFObjectElement::GetDictionaryKeyValueLength(const OString& rKey) const
+{
+ auto it = m_aDictionaryKeyValueLength.find(rKey);
+ if (it == m_aDictionaryKeyValueLength.end())
+ return 0;
+
+ return it->second;
+}
+
void PDFObjectElement::SetDictionaryLength(sal_uInt64 nDictionaryLength)
{
m_nDictionaryLength = nDictionaryLength;
@@ -1878,6 +2004,12 @@ bool PDFEndDictionaryElement::Read(SvStream& rStream)
return true;
}
+PDFNameElement::PDFNameElement()
+ : m_nLocation(0),
+ m_nLength(0)
+{
+}
+
bool PDFNameElement::Read(SvStream& rStream)
{
char ch;
@@ -1887,6 +2019,7 @@ bool PDFNameElement::Read(SvStream& rStream)
SAL_WARN("xmlsecurity.pdfio", "PDFNameElement::Read: unexpected character: " << ch);
return false;
}
+ m_nLocation = rStream.Tell();
if (rStream.IsEof())
{
@@ -1918,6 +2051,16 @@ const OString& PDFNameElement::GetValue() const
return m_aValue;
}
+sal_uInt64 PDFNameElement::GetLocation() const
+{
+ return m_nLocation;
+}
+
+sal_uInt64 PDFNameElement::GetLength() const
+{
+ return m_nLength;
+}
+
PDFStreamElement::PDFStreamElement(size_t nLength)
: m_nLength(nLength)
{
@@ -1941,6 +2084,11 @@ bool PDFEndObjectElement::Read(SvStream& /*rStream*/)
return true;
}
+PDFArrayElement::PDFArrayElement()
+ : m_nOffset(0)
+{
+}
+
bool PDFArrayElement::Read(SvStream& rStream)
{
char ch;
@@ -1950,6 +2098,7 @@ bool PDFArrayElement::Read(SvStream& rStream)
SAL_WARN("xmlsecurity.pdfio", "PDFArrayElement::Read: unexpected character: " << ch);
return false;
}
+ m_nOffset = rStream.Tell();
SAL_INFO("xmlsecurity.pdfio", "PDFArrayElement::Read: '['");
@@ -1966,8 +2115,14 @@ const std::vector<PDFElement*>& PDFArrayElement::GetElements()
return m_aElements;
}
+PDFEndArrayElement::PDFEndArrayElement()
+ : m_nOffset(0)
+{
+}
+
bool PDFEndArrayElement::Read(SvStream& rStream)
{
+ m_nOffset = rStream.Tell();
char ch;
rStream.ReadChar(ch);
if (ch != ']')
@@ -1981,6 +2136,10 @@ bool PDFEndArrayElement::Read(SvStream& rStream)
return true;
}
+sal_uInt64 PDFEndArrayElement::GetOffset() const
+{
+ return m_nOffset;
+}
} // namespace pdfio
} // namespace xmlsecurity