Rework of CPDF_Object writing.

Move writing logic into implementation of related clases.

Change-Id: If70dc418b352b562ee681ea34fa6595d6f52eee3
Reviewed-on: https://pdfium-review.googlesource.com/36350
Commit-Queue: Art Snake <art-snake@yandex-team.ru>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fpdfapi/edit/cpdf_creator.cpp b/core/fpdfapi/edit/cpdf_creator.cpp
index f5d8c0f..5d4fac4 100644
--- a/core/fpdfapi/edit/cpdf_creator.cpp
+++ b/core/fpdfapi/edit/cpdf_creator.cpp
@@ -151,143 +151,20 @@
 
 CPDF_Creator::~CPDF_Creator() {}
 
-bool CPDF_Creator::WriteStream(const CPDF_Object* pStream, uint32_t objnum) {
-  CPDF_CryptoHandler* pCrypto =
-      pStream != m_pMetadata ? GetCryptoHandler() : nullptr;
-
-  CPDF_FlateEncoder encoder(pStream->AsStream(), pStream != m_pMetadata);
-  CPDF_Encryptor encryptor(pCrypto, objnum, encoder.GetSpan());
-  if (static_cast<uint32_t>(encoder.GetDict()->GetIntegerFor("Length")) !=
-      encryptor.GetSpan().size()) {
-    encoder.CloneDict();
-    encoder.GetClonedDict()->SetNewFor<CPDF_Number>(
-        "Length", static_cast<int>(encryptor.GetSpan().size()));
-  }
-
-  if (!WriteDirectObj(objnum, encoder.GetDict(), true) ||
-      !m_Archive->WriteString("stream\r\n")) {
-    return false;
-  }
-
-  // Allow for empty streams.
-  if (encryptor.GetSpan().size() > 0 &&
-      !m_Archive->WriteBlock(encryptor.GetSpan().data(),
-                             encryptor.GetSpan().size())) {
-    return false;
-  }
-
-  return m_Archive->WriteString("\r\nendstream");
-}
-
 bool CPDF_Creator::WriteIndirectObj(uint32_t objnum, const CPDF_Object* pObj) {
   if (!m_Archive->WriteDWord(objnum) || !m_Archive->WriteString(" 0 obj\r\n"))
     return false;
 
-  if (pObj->IsStream()) {
-    if (!WriteStream(pObj, objnum))
-      return false;
-  } else if (!WriteDirectObj(objnum, pObj, true)) {
+  std::unique_ptr<CPDF_Encryptor> encryptor;
+  if (GetCryptoHandler() && pObj != m_pEncryptDict)
+    encryptor = pdfium::MakeUnique<CPDF_Encryptor>(GetCryptoHandler(), objnum);
+
+  if (!pObj->WriteTo(m_Archive.get(), encryptor.get()))
     return false;
-  }
 
   return m_Archive->WriteString("\r\nendobj\r\n");
 }
 
-bool CPDF_Creator::WriteDirectObj(uint32_t objnum,
-                                  const CPDF_Object* pObj,
-                                  bool bEncrypt) {
-  switch (pObj->GetType()) {
-    case CPDF_Object::BOOLEAN:
-    case CPDF_Object::NAME:
-    case CPDF_Object::NULLOBJ:
-    case CPDF_Object::NUMBER:
-    case CPDF_Object::REFERENCE:
-      if (!pObj->WriteTo(m_Archive.get()))
-        return false;
-      break;
-
-    case CPDF_Object::STRING: {
-      ByteString str = pObj->GetString();
-      bool bHex = pObj->AsString()->IsHex();
-      if (!GetCryptoHandler() || !bEncrypt) {
-        if (!pObj->WriteTo(m_Archive.get()))
-          return false;
-        break;
-      }
-      CPDF_Encryptor encryptor(GetCryptoHandler(), objnum, str.AsRawSpan());
-      ByteString content = PDF_EncodeString(
-          ByteString(encryptor.GetSpan().data(), encryptor.GetSpan().size()),
-          bHex);
-      if (!m_Archive->WriteString(content.AsStringView()))
-        return false;
-      break;
-    }
-    case CPDF_Object::STREAM: {
-      CPDF_FlateEncoder encoder(pObj->AsStream(), true);
-      CPDF_Encryptor encryptor(GetCryptoHandler(), objnum, encoder.GetSpan());
-      if (static_cast<uint32_t>(encoder.GetDict()->GetIntegerFor("Length")) !=
-          encryptor.GetSpan().size()) {
-        encoder.CloneDict();
-        encoder.GetClonedDict()->SetNewFor<CPDF_Number>(
-            "Length", static_cast<int>(encryptor.GetSpan().size()));
-      }
-      if (!WriteDirectObj(objnum, encoder.GetDict(), true) ||
-          !m_Archive->WriteString("stream\r\n") ||
-          !m_Archive->WriteBlock(encryptor.GetSpan().data(),
-                                 encryptor.GetSpan().size()) ||
-          !m_Archive->WriteString("\r\nendstream")) {
-        return false;
-      }
-
-      break;
-    }
-    case CPDF_Object::ARRAY: {
-      if (!m_Archive->WriteString("["))
-        return false;
-
-      const CPDF_Array* p = pObj->AsArray();
-      for (size_t i = 0; i < p->GetCount(); i++) {
-        if (!WriteDirectObj(objnum, p->GetObjectAt(i), true))
-          return false;
-      }
-      if (!m_Archive->WriteString("]"))
-        return false;
-      break;
-    }
-    case CPDF_Object::DICTIONARY: {
-      if (!GetCryptoHandler() || pObj == m_pEncryptDict) {
-        if (!pObj->WriteTo(m_Archive.get()))
-          return false;
-        break;
-      }
-
-      if (!m_Archive->WriteString("<<"))
-        return false;
-
-      const CPDF_Dictionary* p = pObj->AsDictionary();
-      bool bSignDict = p->IsSignatureDict();
-      for (const auto& it : *p) {
-        bool bSignValue = false;
-        const ByteString& key = it.first;
-        CPDF_Object* pValue = it.second.get();
-        if (!m_Archive->WriteString("/") ||
-            !m_Archive->WriteString(PDF_NameEncode(key).AsStringView())) {
-          return false;
-        }
-
-        if (bSignDict && key == "Contents")
-          bSignValue = true;
-        if (!WriteDirectObj(objnum, pValue, !bSignValue))
-          return false;
-      }
-      if (!m_Archive->WriteString(">>"))
-        return false;
-      break;
-    }
-  }
-  return true;
-}
-
 bool CPDF_Creator::WriteOldIndirectObject(uint32_t objnum) {
   if (m_pParser->IsObjectFreeOrNull(objnum))
     return true;
@@ -583,7 +460,7 @@
           !m_Archive->WriteString(PDF_NameEncode(key).AsStringView())) {
         return Stage::kInvalid;
       }
-      if (!pValue->WriteTo(m_Archive.get()))
+      if (!pValue->WriteTo(m_Archive.get(), nullptr))
         return Stage::kInvalid;
     }
   } else {
@@ -632,7 +509,7 @@
   }
   if (m_pIDArray) {
     if (!m_Archive->WriteString(("/ID")) ||
-        !m_pIDArray->WriteTo(m_Archive.get())) {
+        !m_pIDArray->WriteTo(m_Archive.get(), nullptr)) {
       return Stage::kInvalid;
     }
   }
diff --git a/core/fpdfapi/edit/cpdf_creator.h b/core/fpdfapi/edit/cpdf_creator.h
index a726114..39e0950 100644
--- a/core/fpdfapi/edit/cpdf_creator.h
+++ b/core/fpdfapi/edit/cpdf_creator.h
@@ -69,11 +69,8 @@
   bool WriteOldIndirectObject(uint32_t objnum);
   bool WriteOldObjs();
   bool WriteNewObjs();
-  bool WriteDirectObj(uint32_t objnum, const CPDF_Object* pObj, bool bEncrypt);
   bool WriteIndirectObj(uint32_t objnum, const CPDF_Object* pObj);
 
-  bool WriteStream(const CPDF_Object* pStream, uint32_t objnum);
-
   CPDF_CryptoHandler* GetCryptoHandler();
 
   UnownedPtr<CPDF_Document> const m_pDocument;
diff --git a/core/fpdfapi/edit/cpdf_encryptor.cpp b/core/fpdfapi/edit/cpdf_encryptor.cpp
index 8994b55..c74e53e 100644
--- a/core/fpdfapi/edit/cpdf_encryptor.cpp
+++ b/core/fpdfapi/edit/cpdf_encryptor.cpp
@@ -5,25 +5,27 @@
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
 #include "core/fpdfapi/edit/cpdf_encryptor.h"
+
 #include "core/fpdfapi/parser/cpdf_crypto_handler.h"
+#include "third_party/base/ptr_util.h"
 
-CPDF_Encryptor::CPDF_Encryptor(CPDF_CryptoHandler* pHandler,
-                               int objnum,
-                               pdfium::span<const uint8_t> src_data) {
+CPDF_Encryptor::CPDF_Encryptor(CPDF_CryptoHandler* pHandler, int objnum)
+    : m_pHandler(pHandler), m_ObjNum(objnum) {
+  ASSERT(m_pHandler);
+}
+
+std::vector<uint8_t> CPDF_Encryptor::Encrypt(
+    pdfium::span<const uint8_t> src_data) const {
   if (src_data.empty())
-    return;
+    return std::vector<uint8_t>();
 
-  if (!pHandler) {
-    m_Span = src_data;
-    return;
-  }
-
-  uint32_t buf_size = pHandler->EncryptGetSize(src_data);
-  m_NewBuf.resize(buf_size);
-  pHandler->EncryptContent(objnum, 0, src_data, m_NewBuf.data(),
-                           buf_size);  // Updates |buf_size| with actual.
-  m_NewBuf.resize(buf_size);
-  m_Span = m_NewBuf;
+  std::vector<uint8_t> result;
+  uint32_t buf_size = m_pHandler->EncryptGetSize(src_data);
+  result.resize(buf_size);
+  m_pHandler->EncryptContent(m_ObjNum, 0, src_data, result.data(),
+                             buf_size);  // Updates |buf_size| with actual.
+  result.resize(buf_size);
+  return result;
 }
 
 CPDF_Encryptor::~CPDF_Encryptor() {}
diff --git a/core/fpdfapi/edit/cpdf_encryptor.h b/core/fpdfapi/edit/cpdf_encryptor.h
index 67fd5c4..5486c4f 100644
--- a/core/fpdfapi/edit/cpdf_encryptor.h
+++ b/core/fpdfapi/edit/cpdf_encryptor.h
@@ -9,25 +9,25 @@
 
 #include <stdint.h>
 
+#include <memory>
 #include <vector>
 
 #include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/unowned_ptr.h"
 #include "third_party/base/span.h"
 
 class CPDF_CryptoHandler;
 
 class CPDF_Encryptor {
  public:
-  CPDF_Encryptor(CPDF_CryptoHandler* pHandler,
-                 int objnum,
-                 pdfium::span<const uint8_t> src_data);
+  CPDF_Encryptor(CPDF_CryptoHandler* pHandler, int objnum);
   ~CPDF_Encryptor();
 
-  pdfium::span<const uint8_t> GetSpan() const { return m_Span; }
+  std::vector<uint8_t> Encrypt(pdfium::span<const uint8_t> src_data) const;
 
  private:
-  std::vector<uint8_t> m_NewBuf;
-  pdfium::span<const uint8_t> m_Span;
+  UnownedPtr<CPDF_CryptoHandler> const m_pHandler;
+  const int m_ObjNum;
 };
 
 #endif  // CORE_FPDFAPI_EDIT_CPDF_ENCRYPTOR_H_
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
index f6a941d..9693bc4 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
@@ -266,7 +266,7 @@
     // If there are parameters, write properties, direct or indirect.
     if (item->GetParamType() == CPDF_ContentMarkItem::DirectDict) {
       CPDF_StringArchiveStream archive_stream(buf);
-      item->GetParam()->WriteTo(&archive_stream);
+      item->GetParam()->WriteTo(&archive_stream, nullptr);
       *buf << " ";
     } else {
       ASSERT(item->GetParamType() == CPDF_ContentMarkItem::PropertiesDict);
diff --git a/core/fpdfapi/parser/cpdf_array.cpp b/core/fpdfapi/parser/cpdf_array.cpp
index d53476d..68b301e 100644
--- a/core/fpdfapi/parser/cpdf_array.cpp
+++ b/core/fpdfapi/parser/cpdf_array.cpp
@@ -230,12 +230,13 @@
   return pRet;
 }
 
-bool CPDF_Array::WriteTo(IFX_ArchiveStream* archive) const {
+bool CPDF_Array::WriteTo(IFX_ArchiveStream* archive,
+                         const CPDF_Encryptor* encryptor) const {
   if (!archive->WriteString("["))
     return false;
 
   for (size_t i = 0; i < GetCount(); ++i) {
-    if (!GetObjectAt(i)->WriteTo(archive))
+    if (!GetObjectAt(i)->WriteTo(archive, encryptor))
       return false;
   }
   return archive->WriteString("]");
diff --git a/core/fpdfapi/parser/cpdf_array.h b/core/fpdfapi/parser/cpdf_array.h
index d980b14..1e63983 100644
--- a/core/fpdfapi/parser/cpdf_array.h
+++ b/core/fpdfapi/parser/cpdf_array.h
@@ -33,7 +33,8 @@
   bool IsArray() const override;
   CPDF_Array* AsArray() override;
   const CPDF_Array* AsArray() const override;
-  bool WriteTo(IFX_ArchiveStream* archive) const override;
+  bool WriteTo(IFX_ArchiveStream* archive,
+               const CPDF_Encryptor* encryptor) const override;
 
   bool IsEmpty() const { return m_Objects.empty(); }
   size_t GetCount() const { return m_Objects.size(); }
diff --git a/core/fpdfapi/parser/cpdf_boolean.cpp b/core/fpdfapi/parser/cpdf_boolean.cpp
index a1dc450..a261101 100644
--- a/core/fpdfapi/parser/cpdf_boolean.cpp
+++ b/core/fpdfapi/parser/cpdf_boolean.cpp
@@ -46,7 +46,8 @@
   return this;
 }
 
-bool CPDF_Boolean::WriteTo(IFX_ArchiveStream* archive) const {
+bool CPDF_Boolean::WriteTo(IFX_ArchiveStream* archive,
+                           const CPDF_Encryptor* encryptor) const {
   return archive->WriteString(" ") &&
          archive->WriteString(GetString().AsStringView());
 }
diff --git a/core/fpdfapi/parser/cpdf_boolean.h b/core/fpdfapi/parser/cpdf_boolean.h
index c0a69d8..61c213b 100644
--- a/core/fpdfapi/parser/cpdf_boolean.h
+++ b/core/fpdfapi/parser/cpdf_boolean.h
@@ -28,7 +28,8 @@
   bool IsBoolean() const override;
   CPDF_Boolean* AsBoolean() override;
   const CPDF_Boolean* AsBoolean() const override;
-  bool WriteTo(IFX_ArchiveStream* archive) const override;
+  bool WriteTo(IFX_ArchiveStream* archive,
+               const CPDF_Encryptor* encryptor) const override;
 
  protected:
   bool m_bValue;
diff --git a/core/fpdfapi/parser/cpdf_dictionary.cpp b/core/fpdfapi/parser/cpdf_dictionary.cpp
index 78227ae..5859304 100644
--- a/core/fpdfapi/parser/cpdf_dictionary.cpp
+++ b/core/fpdfapi/parser/cpdf_dictionary.cpp
@@ -269,10 +269,13 @@
   return m_pPool ? m_pPool->Intern(str) : str;
 }
 
-bool CPDF_Dictionary::WriteTo(IFX_ArchiveStream* archive) const {
+bool CPDF_Dictionary::WriteTo(IFX_ArchiveStream* archive,
+                              const CPDF_Encryptor* encryptor) const {
   if (!archive->WriteString("<<"))
     return false;
 
+  const bool is_signature = CPDF_CryptoHandler::IsSignatureDictionary(this);
+
   for (const auto& it : *this) {
     const ByteString& key = it.first;
     CPDF_Object* pValue = it.second.get();
@@ -280,9 +283,11 @@
         !archive->WriteString(PDF_NameEncode(key).AsStringView())) {
       return false;
     }
-
-    if (!pValue->WriteTo(archive))
+    if (!pValue->WriteTo(archive, !is_signature || key != "Contents"
+                                      ? encryptor
+                                      : nullptr)) {
       return false;
+    }
   }
   return archive->WriteString(">>");
 }
diff --git a/core/fpdfapi/parser/cpdf_dictionary.h b/core/fpdfapi/parser/cpdf_dictionary.h
index 569baed..5cd0e8b 100644
--- a/core/fpdfapi/parser/cpdf_dictionary.h
+++ b/core/fpdfapi/parser/cpdf_dictionary.h
@@ -38,7 +38,8 @@
   bool IsDictionary() const override;
   CPDF_Dictionary* AsDictionary() override;
   const CPDF_Dictionary* AsDictionary() const override;
-  bool WriteTo(IFX_ArchiveStream* archive) const override;
+  bool WriteTo(IFX_ArchiveStream* archive,
+               const CPDF_Encryptor* encryptor) const override;
 
   size_t GetCount() const { return m_Map.size(); }
   const CPDF_Object* GetObjectFor(const ByteString& key) const;
diff --git a/core/fpdfapi/parser/cpdf_name.cpp b/core/fpdfapi/parser/cpdf_name.cpp
index 59a0142..d35a76c 100644
--- a/core/fpdfapi/parser/cpdf_name.cpp
+++ b/core/fpdfapi/parser/cpdf_name.cpp
@@ -51,7 +51,8 @@
   return PDF_DecodeText(m_Name);
 }
 
-bool CPDF_Name::WriteTo(IFX_ArchiveStream* archive) const {
+bool CPDF_Name::WriteTo(IFX_ArchiveStream* archive,
+                        const CPDF_Encryptor* encryptor) const {
   return archive->WriteString("/") &&
          archive->WriteString(PDF_NameEncode(GetString()).AsStringView());
 }
diff --git a/core/fpdfapi/parser/cpdf_name.h b/core/fpdfapi/parser/cpdf_name.h
index 880c10a..eef6bca 100644
--- a/core/fpdfapi/parser/cpdf_name.h
+++ b/core/fpdfapi/parser/cpdf_name.h
@@ -27,7 +27,8 @@
   bool IsName() const override;
   CPDF_Name* AsName() override;
   const CPDF_Name* AsName() const override;
-  bool WriteTo(IFX_ArchiveStream* archive) const override;
+  bool WriteTo(IFX_ArchiveStream* archive,
+               const CPDF_Encryptor* encryptor) const override;
 
  protected:
   ByteString m_Name;
diff --git a/core/fpdfapi/parser/cpdf_null.cpp b/core/fpdfapi/parser/cpdf_null.cpp
index 1074efd..f94b467 100644
--- a/core/fpdfapi/parser/cpdf_null.cpp
+++ b/core/fpdfapi/parser/cpdf_null.cpp
@@ -18,7 +18,8 @@
   return pdfium::MakeUnique<CPDF_Null>();
 }
 
-bool CPDF_Null::WriteTo(IFX_ArchiveStream* archive) const {
+bool CPDF_Null::WriteTo(IFX_ArchiveStream* archive,
+                        const CPDF_Encryptor* encryptor) const {
   return archive->WriteString(" null");
 }
 
diff --git a/core/fpdfapi/parser/cpdf_null.h b/core/fpdfapi/parser/cpdf_null.h
index 2ec05ec..003ad2f 100644
--- a/core/fpdfapi/parser/cpdf_null.h
+++ b/core/fpdfapi/parser/cpdf_null.h
@@ -18,7 +18,8 @@
   // CPDF_Object.
   Type GetType() const override;
   std::unique_ptr<CPDF_Object> Clone() const override;
-  bool WriteTo(IFX_ArchiveStream* archive) const override;
+  bool WriteTo(IFX_ArchiveStream* archive,
+               const CPDF_Encryptor* encryptor) const override;
   bool IsNull() const override;
 };
 
diff --git a/core/fpdfapi/parser/cpdf_number.cpp b/core/fpdfapi/parser/cpdf_number.cpp
index dac1f40..b75ce07 100644
--- a/core/fpdfapi/parser/cpdf_number.cpp
+++ b/core/fpdfapi/parser/cpdf_number.cpp
@@ -57,7 +57,8 @@
                     : ByteString::FormatFloat(m_Float);
 }
 
-bool CPDF_Number::WriteTo(IFX_ArchiveStream* archive) const {
+bool CPDF_Number::WriteTo(IFX_ArchiveStream* archive,
+                          const CPDF_Encryptor* encryptor) const {
   return archive->WriteString(" ") &&
          archive->WriteString(GetString().AsStringView());
 }
diff --git a/core/fpdfapi/parser/cpdf_number.h b/core/fpdfapi/parser/cpdf_number.h
index 6611773..4681bb2 100644
--- a/core/fpdfapi/parser/cpdf_number.h
+++ b/core/fpdfapi/parser/cpdf_number.h
@@ -31,7 +31,8 @@
   bool IsNumber() const override;
   CPDF_Number* AsNumber() override;
   const CPDF_Number* AsNumber() const override;
-  bool WriteTo(IFX_ArchiveStream* archive) const override;
+  bool WriteTo(IFX_ArchiveStream* archive,
+               const CPDF_Encryptor* encryptor) const override;
 
   bool IsInteger() const { return m_bInteger; }
 
diff --git a/core/fpdfapi/parser/cpdf_object.h b/core/fpdfapi/parser/cpdf_object.h
index 5ff028e..6c6b583 100644
--- a/core/fpdfapi/parser/cpdf_object.h
+++ b/core/fpdfapi/parser/cpdf_object.h
@@ -17,6 +17,7 @@
 class CPDF_Array;
 class CPDF_Boolean;
 class CPDF_Dictionary;
+class CPDF_Encryptor;
 class CPDF_IndirectObjectHolder;
 class CPDF_Name;
 class CPDF_Null;
@@ -95,7 +96,8 @@
   virtual CPDF_String* AsString();
   virtual const CPDF_String* AsString() const;
 
-  virtual bool WriteTo(IFX_ArchiveStream* archive) const = 0;
+  virtual bool WriteTo(IFX_ArchiveStream* archive,
+                       const CPDF_Encryptor* encryptor) const = 0;
 
   // Create a deep copy of the object with the option to either
   // copy a reference object or directly copy the object it refers to
diff --git a/core/fpdfapi/parser/cpdf_reference.cpp b/core/fpdfapi/parser/cpdf_reference.cpp
index 0f3b762..5a4d58a 100644
--- a/core/fpdfapi/parser/cpdf_reference.cpp
+++ b/core/fpdfapi/parser/cpdf_reference.cpp
@@ -99,7 +99,8 @@
                     : nullptr;
 }
 
-bool CPDF_Reference::WriteTo(IFX_ArchiveStream* archive) const {
+bool CPDF_Reference::WriteTo(IFX_ArchiveStream* archive,
+                             const CPDF_Encryptor* encryptor) const {
   return archive->WriteString(" ") && archive->WriteDWord(GetRefObjNum()) &&
          archive->WriteString(" 0 R ");
 }
diff --git a/core/fpdfapi/parser/cpdf_reference.h b/core/fpdfapi/parser/cpdf_reference.h
index d1e0a07..cdb5d5e 100644
--- a/core/fpdfapi/parser/cpdf_reference.h
+++ b/core/fpdfapi/parser/cpdf_reference.h
@@ -33,7 +33,8 @@
   bool IsReference() const override;
   CPDF_Reference* AsReference() override;
   const CPDF_Reference* AsReference() const override;
-  bool WriteTo(IFX_ArchiveStream* archive) const override;
+  bool WriteTo(IFX_ArchiveStream* archive,
+               const CPDF_Encryptor* encryptor) const override;
   std::unique_ptr<CPDF_Object> MakeReference(
       CPDF_IndirectObjectHolder* holder) const override;
 
diff --git a/core/fpdfapi/parser/cpdf_stream.cpp b/core/fpdfapi/parser/cpdf_stream.cpp
index fdbe308..6447899 100644
--- a/core/fpdfapi/parser/cpdf_stream.cpp
+++ b/core/fpdfapi/parser/cpdf_stream.cpp
@@ -7,8 +7,11 @@
 #include "core/fpdfapi/parser/cpdf_stream.h"
 
 #include <utility>
+#include <vector>
 
 #include "constants/stream_dict_common.h"
+#include "core/fpdfapi/edit/cpdf_encryptor.h"
+#include "core/fpdfapi/edit/cpdf_flateencoder.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
@@ -18,6 +21,15 @@
 #include "third_party/base/ptr_util.h"
 #include "third_party/base/stl_util.h"
 
+namespace {
+
+bool IsMetaDataStreamDictionary(const CPDF_Dictionary* dict) {
+  return dict && dict->GetStringFor("Type") == "Metadata" &&
+         dict->GetStringFor("Subtype") == "XML";
+}
+
+}  // namespace
+
 CPDF_Stream::CPDF_Stream() {}
 
 CPDF_Stream::CPDF_Stream(std::unique_ptr<uint8_t, FxFreeDeleter> pData,
@@ -167,12 +179,33 @@
   return PDF_DecodeText(pAcc->GetData(), pAcc->GetSize());
 }
 
-bool CPDF_Stream::WriteTo(IFX_ArchiveStream* archive) const {
-  if (!GetDict()->WriteTo(archive) || !archive->WriteString("stream\r\n"))
+bool CPDF_Stream::WriteTo(IFX_ArchiveStream* archive,
+                          const CPDF_Encryptor* encryptor) const {
+  const bool is_metadata = IsMetaDataStreamDictionary(GetDict());
+  CPDF_FlateEncoder encoder(this, !is_metadata);
+
+  std::vector<uint8_t> encrypted_data;
+  pdfium::span<const uint8_t> data = encoder.GetSpan();
+
+  if (encryptor && !is_metadata) {
+    encrypted_data = encryptor->Encrypt(data);
+    data = encrypted_data;
+  }
+
+  if (static_cast<uint32_t>(encoder.GetDict()->GetIntegerFor("Length")) !=
+      data.size()) {
+    encoder.CloneDict();
+    encoder.GetClonedDict()->SetNewFor<CPDF_Number>(
+        "Length", static_cast<int>(data.size()));
+  }
+
+  if (!encoder.GetDict()->WriteTo(archive, encryptor))
     return false;
 
-  auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(this);
-  pAcc->LoadAllDataRaw();
-  return archive->WriteBlock(pAcc->GetData(), pAcc->GetSize()) &&
-         archive->WriteString("\r\nendstream");
+  if (!archive->WriteString("stream\r\n") ||
+      !archive->WriteBlock(data.data(), data.size()) ||
+      !archive->WriteString("\r\nendstream")) {
+    return false;
+  }
+  return true;
 }
diff --git a/core/fpdfapi/parser/cpdf_stream.h b/core/fpdfapi/parser/cpdf_stream.h
index 7e98f30..4a5ac61 100644
--- a/core/fpdfapi/parser/cpdf_stream.h
+++ b/core/fpdfapi/parser/cpdf_stream.h
@@ -34,7 +34,8 @@
   bool IsStream() const override;
   CPDF_Stream* AsStream() override;
   const CPDF_Stream* AsStream() const override;
-  bool WriteTo(IFX_ArchiveStream* archive) const override;
+  bool WriteTo(IFX_ArchiveStream* archive,
+               const CPDF_Encryptor* encryptor) const override;
 
   uint32_t GetRawSize() const { return m_dwSize; }
   // Will be null in case when stream is not memory based.
diff --git a/core/fpdfapi/parser/cpdf_string.cpp b/core/fpdfapi/parser/cpdf_string.cpp
index 851442e..e7f2a9d 100644
--- a/core/fpdfapi/parser/cpdf_string.cpp
+++ b/core/fpdfapi/parser/cpdf_string.cpp
@@ -7,7 +7,9 @@
 #include "core/fpdfapi/parser/cpdf_string.h"
 
 #include <utility>
+#include <vector>
 
+#include "core/fpdfapi/edit/cpdf_encryptor.h"
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
 #include "core/fxcrt/fx_stream.h"
 #include "third_party/base/ptr_util.h"
@@ -65,7 +67,15 @@
   return PDF_DecodeText(m_String);
 }
 
-bool CPDF_String::WriteTo(IFX_ArchiveStream* archive) const {
-  return archive->WriteString(
-      PDF_EncodeString(GetString(), IsHex()).AsStringView());
+bool CPDF_String::WriteTo(IFX_ArchiveStream* archive,
+                          const CPDF_Encryptor* encryptor) const {
+  std::vector<uint8_t> encrypted_data;
+  pdfium::span<const uint8_t> data = m_String.AsRawSpan();
+  if (encryptor) {
+    encrypted_data = encryptor->Encrypt(data);
+    data = encrypted_data;
+  }
+  const ByteString content =
+      PDF_EncodeString(ByteString(data.data(), data.size()), IsHex());
+  return archive->WriteString(content.AsStringView());
 }
diff --git a/core/fpdfapi/parser/cpdf_string.h b/core/fpdfapi/parser/cpdf_string.h
index b6aaacb..144fe64 100644
--- a/core/fpdfapi/parser/cpdf_string.h
+++ b/core/fpdfapi/parser/cpdf_string.h
@@ -31,7 +31,8 @@
   bool IsString() const override;
   CPDF_String* AsString() override;
   const CPDF_String* AsString() const override;
-  bool WriteTo(IFX_ArchiveStream* archive) const override;
+  bool WriteTo(IFX_ArchiveStream* archive,
+               const CPDF_Encryptor* encryptor) const override;
 
   bool IsHex() const { return m_bHex; }