[Libreoffice-commits] core.git: 2 commits - xmlsecurity/source

Miklos Vajna vmiklos at collabora.co.uk
Wed Nov 9 19:22:02 UTC 2016


 xmlsecurity/source/pdfio/pdfdocument.cxx |  380 +++++++++++++++++++++++--------
 1 file changed, 289 insertions(+), 91 deletions(-)

New commits:
commit 2a7e39eac2f44ad48455c8a5c04242b1fc92c726
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Wed Nov 9 15:49:35 2016 +0100

    xmlsecurity PDF sign: conditionally write xref stream
    
    In case the input document used a PDF 1.5 xref stream, not an old xref
    table, then write that as part of the incremental update. Acrobat seems
    to require this.
    
    Change-Id: I9f1f73140c26308f8720aa1ffe1b905d0e60ede0
    Reviewed-on: https://gerrit.libreoffice.org/30724
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>

diff --git a/xmlsecurity/source/pdfio/pdfdocument.cxx b/xmlsecurity/source/pdfio/pdfdocument.cxx
index 3b90bf3..8bd942f 100644
--- a/xmlsecurity/source/pdfio/pdfdocument.cxx
+++ b/xmlsecurity/source/pdfio/pdfdocument.cxx
@@ -667,80 +667,197 @@ bool PDFDocument::Sign(const uno::Reference<security::XCertificate>& xCertificat
         m_aEditBuffer.WriteCharPtr(">>\nendobj\n\n");
     }
 
-    // Write the xref table.
     sal_uInt64 nXRefOffset = m_aEditBuffer.Tell();
-    m_aEditBuffer.WriteCharPtr("xref\n");
-    for (const auto& rXRef : m_aXRef)
-    {
-        size_t nObject = rXRef.first;
-        size_t nOffset = rXRef.second.m_nOffset;
-        if (!rXRef.second.m_bDirty)
-            continue;
-
-        m_aEditBuffer.WriteUInt32AsString(nObject);
-        m_aEditBuffer.WriteCharPtr(" 1\n");
-        OStringBuffer aBuffer;
-        aBuffer.append(static_cast<sal_Int32>(nOffset));
-        while (aBuffer.getLength() < 10)
-            aBuffer.insert(0, "0");
-        if (nObject == 0)
-            aBuffer.append(" 65535 f \n");
-        else
-            aBuffer.append(" 00000 n \n");
-        m_aEditBuffer.WriteOString(aBuffer.toString());
-    }
-
-    // Write the trailer.
-    m_aEditBuffer.WriteCharPtr("trailer\n<</Size ");
-    m_aEditBuffer.WriteUInt32AsString(m_aXRef.size());
-    m_aEditBuffer.WriteCharPtr("/Root ");
-    m_aEditBuffer.WriteUInt32AsString(pRoot->GetObjectValue());
-    m_aEditBuffer.WriteCharPtr(" ");
-    m_aEditBuffer.WriteUInt32AsString(pRoot->GetGenerationValue());
-    m_aEditBuffer.WriteCharPtr(" R\n");
-    PDFReferenceElement* pInfo = nullptr;
     if (m_pXRefStream)
-        pInfo = dynamic_cast<PDFReferenceElement*>(m_pXRefStream->Lookup("Info"));
-    else
-        pInfo = dynamic_cast<PDFReferenceElement*>(m_pTrailer->Lookup("Info"));
-    if (pInfo)
     {
-        m_aEditBuffer.WriteCharPtr("/Info ");
-        m_aEditBuffer.WriteUInt32AsString(pInfo->GetObjectValue());
+        // Write the xref stream.
+        // This is a bit meta: the xref stream stores its own offset.
+        sal_Int32 nXRefStreamId = m_aXRef.size();
+        XRefEntry aXRefStreamEntry;
+        aXRefStreamEntry.m_nOffset = nXRefOffset;
+        aXRefStreamEntry.m_bDirty = true;
+        m_aXRef[nXRefStreamId] = aXRefStreamEntry;
+
+        // Write stream data.
+        SvMemoryStream aXRefStream;
+        for (const auto& rXRef : m_aXRef)
+        {
+            const XRefEntry& rEntry = rXRef.second;
+
+            if (!rEntry.m_bDirty)
+                continue;
+
+            // First field.
+            unsigned char nType = 0;
+            switch (rEntry.m_eType)
+            {
+            case XRefEntryType::FREE:
+                nType = 0;
+                break;
+            case XRefEntryType::NOT_COMPRESSED:
+                nType = 1;
+                break;
+            case XRefEntryType::COMPRESSED:
+                nType = 2;
+                break;
+            }
+            aXRefStream.WriteUChar(nType);
+
+            // Second field.
+            const size_t nOffsetLen = 3;
+            for (size_t i = 0; i < nOffsetLen; ++i)
+            {
+                size_t nByte = nOffsetLen - i - 1;
+                // Fields requiring more than one byte are stored with the
+                // high-order byte first.
+                unsigned char nCh = (rEntry.m_nOffset & (0xff << (nByte * 8))) >> (nByte * 8);
+                aXRefStream.WriteUChar(nCh);
+            }
+
+            // Third field.
+            aXRefStream.WriteUChar(0);
+        }
+
+        m_aEditBuffer.WriteUInt32AsString(nXRefStreamId);
+        m_aEditBuffer.WriteCharPtr(" 0 obj\n<<");
+
+        // ID.
+        auto pID = dynamic_cast<PDFArrayElement*>(m_pXRefStream->Lookup("ID"));
+        if (pID)
+        {
+            const std::vector<PDFElement*>& rElements = pID->GetElements();
+            m_aEditBuffer.WriteCharPtr("/ID [ <");
+            for (size_t i = 0; i < rElements.size(); ++i)
+            {
+                auto pIDString = dynamic_cast<PDFHexStringElement*>(rElements[i]);
+                if (!pIDString)
+                    continue;
+
+                m_aEditBuffer.WriteOString(pIDString->GetValue());
+                if ((i + 1) < rElements.size())
+                    m_aEditBuffer.WriteCharPtr("> <");
+            }
+            m_aEditBuffer.WriteCharPtr("> ] ");
+        }
+
+        // Index.
+        m_aEditBuffer.WriteCharPtr("/Index [ ");
+        for (const auto& rXRef : m_aXRef)
+        {
+            if (!rXRef.second.m_bDirty)
+                continue;
+
+            m_aEditBuffer.WriteUInt32AsString(rXRef.first);
+            m_aEditBuffer.WriteCharPtr(" 1 ");
+        }
+        m_aEditBuffer.WriteCharPtr("] ");
+
+        // Info.
+        auto pInfo = dynamic_cast<PDFReferenceElement*>(m_pXRefStream->Lookup("Info"));
+        if (pInfo)
+        {
+            m_aEditBuffer.WriteCharPtr("/Info ");
+            m_aEditBuffer.WriteUInt32AsString(pInfo->GetObjectValue());
+            m_aEditBuffer.WriteCharPtr(" ");
+            m_aEditBuffer.WriteUInt32AsString(pInfo->GetGenerationValue());
+            m_aEditBuffer.WriteCharPtr(" R ");
+        }
+
+        // Length.
+        m_aEditBuffer.WriteCharPtr("/Length ");
+        m_aEditBuffer.WriteUInt32AsString(aXRefStream.GetSize());
+
+        if (!m_aStartXRefs.empty())
+        {
+            // Write location of the previous cross-reference section.
+            m_aEditBuffer.WriteCharPtr("/Prev ");
+            m_aEditBuffer.WriteUInt32AsString(m_aStartXRefs.back());
+        }
+
+        // Root.
+        m_aEditBuffer.WriteCharPtr("/Root ");
+        m_aEditBuffer.WriteUInt32AsString(pRoot->GetObjectValue());
         m_aEditBuffer.WriteCharPtr(" ");
-        m_aEditBuffer.WriteUInt32AsString(pInfo->GetGenerationValue());
-        m_aEditBuffer.WriteCharPtr(" R\n");
+        m_aEditBuffer.WriteUInt32AsString(pRoot->GetGenerationValue());
+        m_aEditBuffer.WriteCharPtr(" R ");
+
+        // Size.
+        m_aEditBuffer.WriteCharPtr("/Size ");
+        m_aEditBuffer.WriteUInt32AsString(m_aXRef.size());
+
+        m_aEditBuffer.WriteCharPtr("/Type/XRef/W[1 3 1]>>\nstream\n");
+        aXRefStream.Seek(0);
+        m_aEditBuffer.WriteStream(aXRefStream);
+        m_aEditBuffer.WriteCharPtr("\nendstream\nendobj\n\n");
     }
-    PDFArrayElement* pID = nullptr;
-    if (m_pXRefStream)
-        pID = dynamic_cast<PDFArrayElement*>(m_pXRefStream->Lookup("ID"));
     else
-        pID = dynamic_cast<PDFArrayElement*>(m_pTrailer->Lookup("ID"));
-    if (pID)
     {
-        const std::vector<PDFElement*>& rElements = pID->GetElements();
-        m_aEditBuffer.WriteCharPtr("/ID [ <");
-        for (size_t i = 0; i < rElements.size(); ++i)
+        // Write the xref table.
+        m_aEditBuffer.WriteCharPtr("xref\n");
+        for (const auto& rXRef : m_aXRef)
         {
-            auto pIDString = dynamic_cast<PDFHexStringElement*>(rElements[i]);
-            if (!pIDString)
+            size_t nObject = rXRef.first;
+            size_t nOffset = rXRef.second.m_nOffset;
+            if (!rXRef.second.m_bDirty)
                 continue;
 
-            m_aEditBuffer.WriteOString(pIDString->GetValue());
-            if ((i + 1) < rElements.size())
-                m_aEditBuffer.WriteCharPtr(">\n<");
+            m_aEditBuffer.WriteUInt32AsString(nObject);
+            m_aEditBuffer.WriteCharPtr(" 1\n");
+            OStringBuffer aBuffer;
+            aBuffer.append(static_cast<sal_Int32>(nOffset));
+            while (aBuffer.getLength() < 10)
+                aBuffer.insert(0, "0");
+            if (nObject == 0)
+                aBuffer.append(" 65535 f \n");
+            else
+                aBuffer.append(" 00000 n \n");
+            m_aEditBuffer.WriteOString(aBuffer.toString());
         }
-        m_aEditBuffer.WriteCharPtr("> ]\n");
-    }
 
-    if (!m_aStartXRefs.empty())
-    {
-        // Write location of the previous cross-reference section.
-        m_aEditBuffer.WriteCharPtr("/Prev ");
-        m_aEditBuffer.WriteUInt32AsString(m_aStartXRefs.back());
-    }
+        // Write the trailer.
+        m_aEditBuffer.WriteCharPtr("trailer\n<</Size ");
+        m_aEditBuffer.WriteUInt32AsString(m_aXRef.size());
+        m_aEditBuffer.WriteCharPtr("/Root ");
+        m_aEditBuffer.WriteUInt32AsString(pRoot->GetObjectValue());
+        m_aEditBuffer.WriteCharPtr(" ");
+        m_aEditBuffer.WriteUInt32AsString(pRoot->GetGenerationValue());
+        m_aEditBuffer.WriteCharPtr(" R\n");
+        auto pInfo = dynamic_cast<PDFReferenceElement*>(m_pTrailer->Lookup("Info"));
+        if (pInfo)
+        {
+            m_aEditBuffer.WriteCharPtr("/Info ");
+            m_aEditBuffer.WriteUInt32AsString(pInfo->GetObjectValue());
+            m_aEditBuffer.WriteCharPtr(" ");
+            m_aEditBuffer.WriteUInt32AsString(pInfo->GetGenerationValue());
+            m_aEditBuffer.WriteCharPtr(" R\n");
+        }
+        auto pID = dynamic_cast<PDFArrayElement*>(m_pTrailer->Lookup("ID"));
+        if (pID)
+        {
+            const std::vector<PDFElement*>& rElements = pID->GetElements();
+            m_aEditBuffer.WriteCharPtr("/ID [ <");
+            for (size_t i = 0; i < rElements.size(); ++i)
+            {
+                auto pIDString = dynamic_cast<PDFHexStringElement*>(rElements[i]);
+                if (!pIDString)
+                    continue;
 
-    m_aEditBuffer.WriteCharPtr(">>\n");
+                m_aEditBuffer.WriteOString(pIDString->GetValue());
+                if ((i + 1) < rElements.size())
+                    m_aEditBuffer.WriteCharPtr(">\n<");
+            }
+            m_aEditBuffer.WriteCharPtr("> ]\n");
+        }
+
+        if (!m_aStartXRefs.empty())
+        {
+            // Write location of the previous cross-reference section.
+            m_aEditBuffer.WriteCharPtr("/Prev ");
+            m_aEditBuffer.WriteUInt32AsString(m_aStartXRefs.back());
+        }
+
+        m_aEditBuffer.WriteCharPtr(">>\n");
+    }
 
     // Write startxref.
     m_aEditBuffer.WriteCharPtr("startxref\n");
commit 05ad6dfd4e7201793a6350b440173e4a6335c776
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Wed Nov 9 14:19:05 2016 +0100

    xmlsecurity PDF sign: handle when Catalog's AcroForm is an indirect dictionary
    
    Normally it's a direct dictionary, but it's OK to have it as a reference, and
    then the referenced object is a dictionary.
    
    Change-Id: If09edaf23501883be68148e430c42e721ec68247
    Reviewed-on: https://gerrit.libreoffice.org/30719
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>

diff --git a/xmlsecurity/source/pdfio/pdfdocument.cxx b/xmlsecurity/source/pdfio/pdfdocument.cxx
index 8c01cd9..3b90bf3 100644
--- a/xmlsecurity/source/pdfio/pdfdocument.cxx
+++ b/xmlsecurity/source/pdfio/pdfdocument.cxx
@@ -107,6 +107,8 @@ class PDFObjectElement : public PDFElement
     std::vector< std::unique_ptr<PDFObjectElement> > m_aStoredElements;
     /// Elements of an object in an object stream.
     std::vector< std::unique_ptr<PDFElement> > m_aElements;
+    /// Uncompressed buffer of an object in an object stream.
+    std::unique_ptr<SvMemoryStream> m_pStreamBuffer;
 
 public:
     PDFObjectElement(PDFDocument& rDoc, double fObjectValue, double fGenerationValue);
@@ -126,6 +128,8 @@ public:
     /// Parse objects stored in this object stream.
     void ParseStoredObjects();
     std::vector< std::unique_ptr<PDFElement> >& GetStoredElements();
+    SvMemoryStream* GetStreamBuffer() const;
+    void SetStreamBuffer(std::unique_ptr<SvMemoryStream>& pStreamBuffer);
 };
 
 /// Dictionary object: a set key-value pairs.
@@ -557,50 +561,111 @@ bool PDFDocument::Sign(const uno::Reference<security::XCertificate>& xCertificat
         SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Sign: invalid catalog obj id");
         return false;
     }
-    m_aXRef[nCatalogId].m_nOffset = m_aEditBuffer.Tell();
-    m_aXRef[nCatalogId].m_bDirty = true;
-    m_aEditBuffer.WriteUInt32AsString(nCatalogId);
-    m_aEditBuffer.WriteCharPtr(" 0 obj\n");
-    m_aEditBuffer.WriteCharPtr("<<");
-    auto pAcroForm = dynamic_cast<PDFDictionaryElement*>(pCatalog->Lookup("AcroForm"));
-    if (!pAcroForm)
+    PDFElement* pAcroForm = pCatalog->Lookup("AcroForm");
+    auto pAcroFormReference = dynamic_cast<PDFReferenceElement*>(pAcroForm);
+    if (pAcroFormReference)
     {
-        // No AcroForm key, assume no signatures.
-        m_aEditBuffer.WriteBytes(static_cast<const char*>(m_aEditBuffer.GetData()) + pCatalog->GetDictionaryOffset(), pCatalog->GetDictionaryLength());
-        m_aEditBuffer.WriteCharPtr("/AcroForm<</Fields[\n");
-        m_aEditBuffer.WriteUInt32AsString(nAnnotId);
-        m_aEditBuffer.WriteCharPtr(" 0 R\n]/SigFlags 3>>\n");
-    }
-    else
-    {
-        // AcroForm key is already there, insert our reference at the Fields end.
-        auto it = pAcroForm->GetItems().find("Fields");
-        if (it == pAcroForm->GetItems().end())
+        // Write the updated AcroForm key of the Catalog object.
+        PDFObjectElement* pAcroFormObject = pAcroFormReference->LookupObject();
+        if (!pAcroFormObject)
         {
-            SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Sign: AcroForm without required Fields key");
+            SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Sign: invalid AcroForm reference");
             return false;
         }
 
-        auto pFields = dynamic_cast<PDFArrayElement*>(it->second);
-        if (!pFields)
+        sal_uInt32 nAcroFormId = pAcroFormObject->GetObjectValue();
+        m_aXRef[nAcroFormId].m_eType = XRefEntryType::NOT_COMPRESSED;
+        m_aXRef[nAcroFormId].m_nOffset = m_aEditBuffer.Tell();
+        m_aXRef[nAcroFormId].m_nGenerationNumber = 0;
+        m_aXRef[nAcroFormId].m_bDirty = true;
+        m_aEditBuffer.WriteUInt32AsString(nAcroFormId);
+        m_aEditBuffer.WriteCharPtr(" 0 obj\n");
+
+        SvMemoryStream* pStreamBuffer = pAcroFormObject->GetStreamBuffer();
+        if (!pStreamBuffer)
         {
-            SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Sign: AcroForm Fields is not an array");
+            SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Sign: AcroForm object is in an object stream");
+            return false;
+        }
+
+        if (!pAcroFormObject->Lookup("Fields"))
+        {
+            SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Sign: AcroForm object without required Fields key");
+            return false;
+        }
+
+        PDFDictionaryElement* pAcroFormDictionary = pAcroFormObject->GetDictionary();
+        if (!pAcroFormDictionary)
+        {
+            SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Sign: AcroForm object has no dictionary");
             return false;
         }
 
         // Offset right before the end of the Fields array.
-        sal_uInt64 nFieldsEndOffset = pAcroForm->GetKeyOffset("Fields") + pAcroForm->GetKeyValueLength("Fields") - 1;
-        // Length of beginning of the Catalog dictionary -> Fields end.
-        sal_uInt64 nFieldsBeforeEndLength = nFieldsEndOffset - pCatalog->GetDictionaryOffset();
-        m_aEditBuffer.WriteBytes(static_cast<const char*>(m_aEditBuffer.GetData()) + pCatalog->GetDictionaryOffset(), nFieldsBeforeEndLength);
+        sal_uInt64 nFieldsEndOffset = pAcroFormDictionary->GetKeyOffset("Fields") + pAcroFormDictionary->GetKeyValueLength("Fields") - strlen("]");
+        // Length of beginning of the object dictionary -> Fields end.
+        sal_uInt64 nFieldsBeforeEndLength = nFieldsEndOffset;
+        m_aEditBuffer.WriteBytes(pStreamBuffer->GetData(), nFieldsBeforeEndLength);
+
+        // Append our reference at the end of the Fields array.
         m_aEditBuffer.WriteCharPtr(" ");
         m_aEditBuffer.WriteUInt32AsString(nAnnotId);
         m_aEditBuffer.WriteCharPtr(" 0 R");
-        // Length of Fields end -> end of the Catalog dictionary.
-        sal_uInt64 nFieldsAfterEndLength = pCatalog->GetDictionaryOffset() + pCatalog->GetDictionaryLength() - nFieldsEndOffset;
-        m_aEditBuffer.WriteBytes(static_cast<const char*>(m_aEditBuffer.GetData()) + nFieldsEndOffset, nFieldsAfterEndLength);
+
+        // Length of Fields end -> end of the object dictionary.
+        sal_uInt64 nFieldsAfterEndLength = pStreamBuffer->GetSize() - nFieldsEndOffset;
+        m_aEditBuffer.WriteBytes(static_cast<const char*>(pStreamBuffer->GetData()) + nFieldsEndOffset, nFieldsAfterEndLength);
+
+        m_aEditBuffer.WriteCharPtr("\nendobj\n\n");
+    }
+    else
+    {
+        // Write the updated Catalog object, references nAnnotId.
+        auto pAcroFormDictionary = dynamic_cast<PDFDictionaryElement*>(pAcroForm);
+        m_aXRef[nCatalogId].m_nOffset = m_aEditBuffer.Tell();
+        m_aXRef[nCatalogId].m_bDirty = true;
+        m_aEditBuffer.WriteUInt32AsString(nCatalogId);
+        m_aEditBuffer.WriteCharPtr(" 0 obj\n");
+        m_aEditBuffer.WriteCharPtr("<<");
+        if (!pAcroFormDictionary)
+        {
+            // No AcroForm key, assume no signatures.
+            m_aEditBuffer.WriteBytes(static_cast<const char*>(m_aEditBuffer.GetData()) + pCatalog->GetDictionaryOffset(), pCatalog->GetDictionaryLength());
+            m_aEditBuffer.WriteCharPtr("/AcroForm<</Fields[\n");
+            m_aEditBuffer.WriteUInt32AsString(nAnnotId);
+            m_aEditBuffer.WriteCharPtr(" 0 R\n]/SigFlags 3>>\n");
+        }
+        else
+        {
+            // AcroForm key is already there, insert our reference at the Fields end.
+            auto it = pAcroFormDictionary->GetItems().find("Fields");
+            if (it == pAcroFormDictionary->GetItems().end())
+            {
+                SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Sign: AcroForm without required Fields key");
+                return false;
+            }
+
+            auto pFields = dynamic_cast<PDFArrayElement*>(it->second);
+            if (!pFields)
+            {
+                SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Sign: AcroForm Fields is not an array");
+                return false;
+            }
+
+            // Offset right before the end of the Fields array.
+            sal_uInt64 nFieldsEndOffset = pAcroFormDictionary->GetKeyOffset("Fields") + pAcroFormDictionary->GetKeyValueLength("Fields") - 1;
+            // Length of beginning of the Catalog dictionary -> Fields end.
+            sal_uInt64 nFieldsBeforeEndLength = nFieldsEndOffset - pCatalog->GetDictionaryOffset();
+            m_aEditBuffer.WriteBytes(static_cast<const char*>(m_aEditBuffer.GetData()) + pCatalog->GetDictionaryOffset(), nFieldsBeforeEndLength);
+            m_aEditBuffer.WriteCharPtr(" ");
+            m_aEditBuffer.WriteUInt32AsString(nAnnotId);
+            m_aEditBuffer.WriteCharPtr(" 0 R");
+            // Length of Fields end -> end of the Catalog dictionary.
+            sal_uInt64 nFieldsAfterEndLength = pCatalog->GetDictionaryOffset() + pCatalog->GetDictionaryLength() - nFieldsEndOffset;
+            m_aEditBuffer.WriteBytes(static_cast<const char*>(m_aEditBuffer.GetData()) + nFieldsEndOffset, nFieldsAfterEndLength);
+        }
+        m_aEditBuffer.WriteCharPtr(">>\nendobj\n\n");
     }
-    m_aEditBuffer.WriteCharPtr(">>\nendobj\n\n");
 
     // Write the xref table.
     sal_uInt64 nXRefOffset = m_aEditBuffer.Tell();
@@ -2736,6 +2801,12 @@ void PDFObjectElement::ParseStoredObjects()
         m_rDoc.Tokenize(aStoredStream, TokenizeMode::STORED_OBJECT, pStored->GetStoredElements(), pStored);
         // This is how references know the object is stored inside this object stream.
         m_rDoc.SetIDObject(nObjNum, pStored);
+
+        // Store the stream of the object in the object stream for later use.
+        std::unique_ptr<SvMemoryStream> pStreamBuffer(new SvMemoryStream());
+        aStoredStream.Seek(0);
+        pStreamBuffer->WriteStream(aStoredStream);
+        pStored->SetStreamBuffer(pStreamBuffer);
     }
 }
 
@@ -2744,6 +2815,16 @@ std::vector< std::unique_ptr<PDFElement> >& PDFObjectElement::GetStoredElements(
     return m_aElements;
 }
 
+SvMemoryStream* PDFObjectElement::GetStreamBuffer() const
+{
+    return m_pStreamBuffer.get();
+}
+
+void PDFObjectElement::SetStreamBuffer(std::unique_ptr<SvMemoryStream>& pStreamBuffer)
+{
+    m_pStreamBuffer = std::move(pStreamBuffer);
+}
+
 PDFReferenceElement::PDFReferenceElement(PDFDocument& rDoc, int fObjectValue, int fGenerationValue)
     : m_rDoc(rDoc),
       m_fObjectValue(fObjectValue),


More information about the Libreoffice-commits mailing list