Avoid writing out empty streams in CPDF_Stream::WriteTo().

Do not call CFX_FileBufferArchive::WriteBlock() with no data to write
out to avoid an ASSERT() failure. Although
CFX_FileBufferArchive::WriteBlock() can handle no data just fine, it is
an IFX_WriteStream::WriteBlock() implementation and not all
IFX_WriteStream::WriteBlock() implementations can handle no data.

BUG=chromium:905142

Change-Id: Icef8460443a9363526679261aac0c1ef74bccec4
Reviewed-on: https://pdfium-review.googlesource.com/c/45531
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/core/fpdfapi/parser/cpdf_stream.cpp b/core/fpdfapi/parser/cpdf_stream.cpp
index 11ea67e..db38c41 100644
--- a/core/fpdfapi/parser/cpdf_stream.cpp
+++ b/core/fpdfapi/parser/cpdf_stream.cpp
@@ -192,20 +192,24 @@
     data = encrypted_data;
   }
 
-  if (static_cast<uint32_t>(encoder.GetDict()->GetIntegerFor("Length")) !=
-      data.size()) {
+  size_t size = data.size();
+  if (static_cast<size_t>(encoder.GetDict()->GetIntegerFor("Length")) != size) {
     encoder.CloneDict();
-    encoder.GetClonedDict()->SetNewFor<CPDF_Number>(
-        "Length", static_cast<int>(data.size()));
+    encoder.GetClonedDict()->SetNewFor<CPDF_Number>("Length",
+                                                    static_cast<int>(size));
   }
 
   if (!encoder.GetDict()->WriteTo(archive, encryptor))
     return false;
 
-  if (!archive->WriteString("stream\r\n") ||
-      !archive->WriteBlock(data.data(), data.size()) ||
-      !archive->WriteString("\r\nendstream")) {
+  if (!archive->WriteString("stream\r\n"))
     return false;
-  }
+
+  if (size && !archive->WriteBlock(data.data(), size))
+    return false;
+
+  if (!archive->WriteString("\r\nendstream"))
+    return false;
+
   return true;
 }
diff --git a/fpdfsdk/fpdf_save_embeddertest.cpp b/fpdfsdk/fpdf_save_embeddertest.cpp
index 510660e..46d165c 100644
--- a/fpdfsdk/fpdf_save_embeddertest.cpp
+++ b/fpdfsdk/fpdf_save_embeddertest.cpp
@@ -100,3 +100,9 @@
   EXPECT_THAT(GetString(),
               testing::Not(testing::HasSubstr("0000000000 65536 f\r\n")));
 }
+
+TEST_F(FPDFSaveEmbedderTest, BUG_905142) {
+  EXPECT_TRUE(OpenDocument("bug_905142.pdf"));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  EXPECT_THAT(GetString(), testing::HasSubstr("/Length 0"));
+}
diff --git a/testing/resources/bug_905142.in b/testing/resources/bug_905142.in
new file mode 100644
index 0000000..d37f68f
--- /dev/null
+++ b/testing/resources/bug_905142.in
@@ -0,0 +1,31 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Kids [ 3 0 R ]
+  /Count 1
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 1 0 R
+  /Contents [ 4 0 R ]
+  /MediaBox [ 0 0 612 792 ]
+>>
+endobj
+{{object 4 0}} <<
+  /Filter /FlateDecode
+  /Length 0
+>>
+stream
+
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_905142.pdf b/testing/resources/bug_905142.pdf
new file mode 100644
index 0000000..9f2898f
--- /dev/null
+++ b/testing/resources/bug_905142.pdf
@@ -0,0 +1,42 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Kids [ 3 0 R ]
+  /Count 1
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 1 0 R
+  /Contents [ 4 0 R ]
+  /MediaBox [ 0 0 612 792 ]
+>>
+endobj
+4 0 obj <<
+  /Filter /FlateDecode
+  /Length 0
+>>
+stream
+
+endstream
+endobj
+xref
+0 5
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000133 00000 n 
+0000000234 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 5
+>>
+startxref
+308
+%%EOF