Add postscript path

This patch adds the additional functions required to make postscript
printing functional. The most significant additions are are two added
compression functions and a new API for setting the postscript level.

Not currently called from Chromium, Chromium patch to come.

BUG=

Review-Url: https://codereview.chromium.org/2612243005
diff --git a/BUILD.gn b/BUILD.gn
index ebb821c..f80875f 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1712,7 +1712,9 @@
     "core/fpdfdoc/cpdf_filespec_unittest.cpp",
     "core/fpdfdoc/cpdf_formfield_unittest.cpp",
     "core/fpdftext/fpdf_text_int_unittest.cpp",
+    "core/fxcodec/codec/fx_codec_a85_unittest.cpp",
     "core/fxcodec/codec/fx_codec_jpx_unittest.cpp",
+    "core/fxcodec/codec/fx_codec_rle_unittest.cpp",
     "core/fxcodec/jbig2/JBig2_Image_unittest.cpp",
     "core/fxcrt/cfx_maybe_owned_unittest.cpp",
     "core/fxcrt/cfx_observable_unittest.cpp",
diff --git a/core/fxcodec/codec/fx_codec.cpp b/core/fxcodec/codec/fx_codec.cpp
index 6b6c723..23171cf 100644
--- a/core/fxcodec/codec/fx_codec.cpp
+++ b/core/fxcodec/codec/fx_codec.cpp
@@ -6,6 +6,7 @@
 
 #include "core/fxcodec/fx_codec.h"
 
+#include <algorithm>
 #include <cmath>
 #include <memory>
 #include <utility>
@@ -101,14 +102,151 @@
                                          uint32_t src_size,
                                          uint8_t** dest_buf,
                                          uint32_t* dest_size) {
-  return false;
+  // Check inputs
+  if (!src_buf || !dest_buf || !dest_size || src_size == 0)
+    return false;
+
+  // Edge case
+  if (src_size == 1) {
+    *dest_buf = FX_Alloc(uint8_t, 3);
+    (*dest_buf)[0] = 0;
+    (*dest_buf)[1] = src_buf[0];
+    (*dest_buf)[2] = 128;
+    *dest_size = 3;
+    return true;
+  }
+
+  // Worst case: 1 nonmatch, 2 match, 1 nonmatch, 2 match, etc. This becomes
+  // 4 output chars for every 3 input, plus up to 4 more for the 1-2 chars
+  // rounded off plus the terminating character.
+  uint32_t est_size = 4 * ((src_size + 2) / 3) + 1;
+  *dest_buf = FX_Alloc(uint8_t, est_size);
+
+  // Set up pointers.
+  uint8_t* out = *dest_buf;
+  uint32_t run_start = 0;
+  uint32_t run_end = 1;
+  uint8_t x = src_buf[run_start];
+  uint8_t y = src_buf[run_end];
+  while (run_end < src_size) {
+    uint32_t max_len = std::min((uint32_t)128, src_size - run_start);
+    while (x == y && (run_end - run_start < max_len - 1))
+      y = src_buf[++run_end];
+
+    // Reached end with matched run. Update variables to expected values.
+    if (x == y) {
+      run_end++;
+      if (run_end < src_size)
+        y = src_buf[run_end];
+    }
+    if (run_end - run_start > 1) {  // Matched run but not at end of input.
+      out[0] = 257 - (run_end - run_start);
+      out[1] = x;
+      x = y;
+      run_start = run_end;
+      run_end++;
+      if (run_end < src_size)
+        y = src_buf[run_end];
+      out += 2;
+      continue;
+    }
+    // Mismatched run
+    while (x != y && run_end <= run_start + max_len) {
+      out[run_end - run_start] = x;
+      x = y;
+      run_end++;
+      if (run_end == src_size) {
+        if (run_end <= run_start + max_len) {
+          out[run_end - run_start] = x;
+          run_end++;
+        }
+        break;
+      }
+      y = src_buf[run_end];
+    }
+    out[0] = run_end - run_start - 2;
+    out += run_end - run_start;
+    run_start = run_end - 1;
+  }
+  if (run_start < src_size) {  // 1 leftover character
+    out[0] = 0;
+    out[1] = x;
+    out += 2;
+  }
+  *out = 128;
+  *dest_size = out + 1 - *dest_buf;
+  return true;
 }
 
 bool CCodec_BasicModule::A85Encode(const uint8_t* src_buf,
                                    uint32_t src_size,
                                    uint8_t** dest_buf,
                                    uint32_t* dest_size) {
-  return false;
+  // Check inputs.
+  if (!src_buf || !dest_buf || !dest_size)
+    return false;
+
+  if (src_size == 0) {
+    *dest_size = 0;
+    return false;
+  }
+
+  // Worst case: 5 output for each 4 input (plus up to 4 from leftover), plus
+  // 2 character new lines each 75 output chars plus 2 termination chars. May
+  // have fewer if there are special "z" chars.
+  uint32_t est_size = 5 * (src_size / 4) + 4 + src_size / 30 + 2;
+  *dest_buf = FX_Alloc(uint8_t, est_size);
+
+  // Set up pointers.
+  uint8_t* out = *dest_buf;
+  uint32_t pos = 0;
+  uint32_t line_length = 0;
+  while (src_size >= 4 && pos < src_size - 3) {
+    uint32_t val = ((uint32_t)(src_buf[pos]) << 24) +
+                   ((uint32_t)(src_buf[pos + 1]) << 16) +
+                   ((uint32_t)(src_buf[pos + 2]) << 8) +
+                   (uint32_t)(src_buf[pos + 3]);
+    pos += 4;
+    if (val == 0) {  // All zero special case
+      *out = 'z';
+      out++;
+      line_length++;
+    } else {  // Compute base 85 characters and add 33.
+      for (int i = 4; i >= 0; i--) {
+        out[i] = (uint8_t)(val % 85) + 33;
+        val = val / 85;
+      }
+      out += 5;
+      line_length += 5;
+    }
+    if (line_length >= 75) {  // Add a return.
+      *out++ = '\r';
+      *out++ = '\n';
+      line_length = 0;
+    }
+  }
+  if (pos < src_size) {  // Leftover bytes
+    uint32_t val = 0;
+    int count = 0;
+    while (pos < src_size) {
+      val += (uint32_t)(src_buf[pos] << (8 * (3 - pos)));
+      count++;
+      pos++;
+    }
+    for (int i = 4; i >= 0; i--) {
+      if (i <= count)
+        out[i] = (uint8_t)(val % 85) + 33;
+      val = val / 85;
+    }
+    out += count + 1;
+  }
+
+  // Terminating characters.
+  out[0] = '~';
+  out[1] = '>';
+  out += 2;
+  *dest_size = out - *dest_buf;
+  return true;
 }
 
 #ifdef PDF_ENABLE_XFA
diff --git a/core/fxcodec/codec/fx_codec_a85_unittest.cpp b/core/fxcodec/codec/fx_codec_a85_unittest.cpp
new file mode 100644
index 0000000..7910881
--- /dev/null
+++ b/core/fxcodec/codec/fx_codec_a85_unittest.cpp
@@ -0,0 +1,209 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include <limits>
+
+#include "core/fxcodec/codec/ccodec_basicmodule.h"
+#include "core/fxcodec/fx_codec.h"
+#include "testing/fx_string_testhelpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(fxcodec, A85TestBadInputs) {
+  uint8_t src_buf[4] = {1, 2, 3, 4};
+  uint8_t* dest_buf = nullptr;
+  uint32_t src_size = 4;
+  uint32_t dest_size = 0;
+
+  CCodec_BasicModule* pEncoders = CCodec_ModuleMgr().GetBasicModule();
+  EXPECT_TRUE(pEncoders);
+
+  // Error codes, not segvs, should callers pass us a nullptr pointer.
+  EXPECT_FALSE(pEncoders->A85Encode(src_buf, src_size, &dest_buf, nullptr));
+  EXPECT_FALSE(pEncoders->A85Encode(src_buf, src_size, nullptr, &dest_size));
+  EXPECT_FALSE(pEncoders->A85Encode(src_buf, 0, &dest_buf, &dest_size));
+  EXPECT_FALSE(pEncoders->A85Encode(nullptr, src_size, &dest_buf, &dest_size));
+}
+
+// No leftover bytes, just translate 2 sets of symbols.
+TEST(fxcodec, A85TestBasic) {
+  // Make sure really big values don't break.
+  uint8_t src_buf[8] = {1, 2, 3, 4, 255, 255, 255, 255};
+  uint8_t* dest_buf = nullptr;
+  uint32_t src_size = 8;
+  uint32_t dest_size = 0;
+
+  CCodec_BasicModule* pEncoders = CCodec_ModuleMgr().GetBasicModule();
+  EXPECT_TRUE(pEncoders);
+
+  // Should succeed.
+  EXPECT_TRUE(pEncoders->A85Encode(src_buf, src_size, &dest_buf, &dest_size));
+
+  // Should have 5 chars for each set of 4 and 2 terminators.
+  EXPECT_EQ(12u, dest_size);
+  uint8_t expected_out[12] = {33, 60, 78, 63, 43, 115, 56, 87, 45, 33, 126, 62};
+
+  // Check the output
+  for (uint32_t i = 0; i < 12; i++)
+    EXPECT_EQ(dest_buf[i], expected_out[i]) << " at " << i;
+  FX_Free(dest_buf);
+}
+
+// Leftover bytes.
+TEST(fxcodec, A85TestLeftoverBytes) {
+  // 1 Leftover Byte:
+  uint8_t src_buf_1leftover[5] = {1, 2, 3, 4, 255};
+  uint8_t* dest_buf = nullptr;
+  uint32_t src_size = 5;
+  uint32_t dest_size = 0;
+
+  CCodec_BasicModule* pEncoders = CCodec_ModuleMgr().GetBasicModule();
+  EXPECT_TRUE(pEncoders);
+
+  // Should succeed
+  EXPECT_TRUE(
+      pEncoders->A85Encode(src_buf_1leftover, src_size, &dest_buf, &dest_size));
+  EXPECT_EQ(9u, dest_size);  // 5 chars for first symbol + 2 + 2 terminators.
+  uint8_t expected_out_1leftover[9] = {33, 60, 78, 63, 43, 114, 114, 126, 62};
+
+  // Check the output
+  for (uint32_t i = 0; i < 9; i++)
+    EXPECT_EQ(dest_buf[i], expected_out_1leftover[i]) << " at " << i;
+  FX_Free(dest_buf);
+
+  // 2 Leftover bytes:
+  src_size++;
+  dest_buf = nullptr;
+  dest_size = 0;
+  uint8_t src_buf_2leftover[6] = {1, 2, 3, 4, 255, 254};
+  // Should succeed
+  EXPECT_TRUE(
+      pEncoders->A85Encode(src_buf_2leftover, src_size, &dest_buf, &dest_size));
+  EXPECT_EQ(10u, dest_size);  // 5 chars for first symbol + 3 + 2 terminators.
+  uint8_t expected_out_2leftover[10] = {33,  60, 78, 63,  43,
+                                        115, 56, 68, 126, 62};
+
+  // Check the output
+  for (uint32_t i = 0; i < 10; i++)
+    EXPECT_EQ(dest_buf[i], expected_out_2leftover[i]) << " at " << i;
+  FX_Free(dest_buf);
+
+  // 3 Leftover bytes:
+  src_size++;
+  dest_buf = nullptr;
+  dest_size = 0;
+  uint8_t src_buf_3leftover[7] = {1, 2, 3, 4, 255, 254, 253};
+  // Should succeed
+  EXPECT_TRUE(
+      pEncoders->A85Encode(src_buf_3leftover, src_size, &dest_buf, &dest_size));
+  EXPECT_EQ(11u, dest_size);  // 5 chars for first symbol + 4 + 2 terminators.
+  uint8_t expected_out_3leftover[11] = {33, 60, 78,  63,  43, 115,
+                                        56, 77, 114, 126, 62};
+
+  // Check the output
+  for (uint32_t i = 0; i < 11; i++)
+    EXPECT_EQ(dest_buf[i], expected_out_3leftover[i]) << " at " << i;
+  FX_Free(dest_buf);
+}
+
+// Test all zeros comes through as "z".
+TEST(fxcodec, A85TestZeros) {
+  // Make sure really big values don't break.
+  uint8_t src_buf[8] = {1, 2, 3, 4, 0, 0, 0, 0};
+  uint8_t* dest_buf = nullptr;
+  uint32_t src_size = 8;
+  uint32_t dest_size = 0;
+
+  CCodec_BasicModule* pEncoders = CCodec_ModuleMgr().GetBasicModule();
+  EXPECT_TRUE(pEncoders);
+
+  // Should succeed.
+  EXPECT_TRUE(pEncoders->A85Encode(src_buf, src_size, &dest_buf, &dest_size));
+
+  // Should have 5 chars for first set of 4 + 1 for z + 2 terminators.
+  EXPECT_EQ(8u, dest_size);
+  uint8_t expected_out[8] = {33, 60, 78, 63, 43, 122, 126, 62};
+
+  // Check the output
+  for (uint32_t i = 0; i < 8; i++)
+    EXPECT_EQ(dest_buf[i], expected_out[i]) << " at " << i;
+  FX_Free(dest_buf);
+
+  // Should also work if it is at the start:
+  dest_buf = nullptr;
+  dest_size = 0;
+  uint8_t src_buf_2[8] = {0, 0, 0, 0, 1, 2, 3, 4};
+
+  // Should succeed.
+  EXPECT_TRUE(pEncoders->A85Encode(src_buf_2, src_size, &dest_buf, &dest_size));
+
+  // Should have 5 chars for set of 4 + 1 for z + 2 terminators.
+  EXPECT_EQ(8u, dest_size);
+  uint8_t expected_out_2[8] = {122, 33, 60, 78, 63, 43, 126, 62};
+
+  // Check the output
+  for (uint32_t i = 0; i < 8; i++)
+    EXPECT_EQ(dest_buf[i], expected_out_2[i]) << " at " << i;
+  FX_Free(dest_buf);
+
+  // Try with 2 leftover zero bytes. Make sure we don't get a "z".
+  src_size = 6;  // Cut off the last 2 zeros.
+  dest_buf = nullptr;
+  dest_size = 0;
+
+  // Should succeed.
+  EXPECT_TRUE(pEncoders->A85Encode(src_buf, src_size, &dest_buf, &dest_size));
+
+  // Should have 5 chars for set of 4 + 3 for last 2 + 2 terminators.
+  EXPECT_EQ(10u, dest_size);
+  uint8_t expected_out_leftover[10] = {33, 60, 78, 63, 43, 33, 33, 33, 126, 62};
+
+  // Check the output
+  for (uint32_t i = 0; i < 10; i++)
+    EXPECT_EQ(dest_buf[i], expected_out_leftover[i]) << " at " << i;
+  FX_Free(dest_buf);
+}
+
+// Make sure we get returns in the expected locations.
+TEST(fxcodec, A85TestLineBreaks) {
+  // Make sure really big values don't break.
+  uint8_t src_buf[131] = {0};
+  // 1 full line + most of a line of normal symbols.
+  for (int k = 0; k < 116; k += 4) {
+    src_buf[k] = 1;
+    src_buf[k + 1] = 2;
+    src_buf[k + 2] = 3;
+    src_buf[k + 3] = 4;
+  }
+  // Fill in the end, leaving an all zero gap + 3 extra zeros at the end.
+  for (int k = 120; k < 128; k++) {
+    src_buf[k] = 1;
+    src_buf[k + 1] = 2;
+    src_buf[k + 2] = 3;
+    src_buf[k + 3] = 4;
+  }
+  uint8_t* dest_buf = nullptr;
+  uint32_t src_size = 131;
+  uint32_t dest_size = 0;
+
+  CCodec_BasicModule* pEncoders = CCodec_ModuleMgr().GetBasicModule();
+  EXPECT_TRUE(pEncoders);
+
+  // Should succeed.
+  EXPECT_TRUE(pEncoders->A85Encode(src_buf, src_size, &dest_buf, &dest_size));
+
+  // Should have 75 chars in the first row plus 2 char return,
+  // 76 chars in the second row plus 2 char return,
+  // and 9 chars in the last row with 2 terminators.
+  EXPECT_EQ(166u, dest_size);
+
+  // Check for the returns.
+  EXPECT_EQ(dest_buf[75], 13);
+  EXPECT_EQ(dest_buf[76], 10);
+  EXPECT_EQ(dest_buf[153], 13);
+  EXPECT_EQ(dest_buf[154], 10);
+
+  FX_Free(dest_buf);
+}
diff --git a/core/fxcodec/codec/fx_codec_rle_unittest.cpp b/core/fxcodec/codec/fx_codec_rle_unittest.cpp
new file mode 100644
index 0000000..94d87cd
--- /dev/null
+++ b/core/fxcodec/codec/fx_codec_rle_unittest.cpp
@@ -0,0 +1,195 @@
+// Copyright 2016 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include <limits>
+
+#include "core/fpdfapi/parser/fpdf_parser_decode.h"
+#include "core/fxcodec/codec/ccodec_basicmodule.h"
+#include "core/fxcodec/fx_codec.h"
+#include "testing/fx_string_testhelpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(fxcodec, RLETestBadInputs) {
+  uint8_t src_buf[1] = {1};
+  uint8_t* dest_buf = nullptr;
+  uint32_t src_size = 4;
+  uint32_t dest_size = 0;
+
+  CCodec_BasicModule* pEncoders = CCodec_ModuleMgr().GetBasicModule();
+  EXPECT_TRUE(pEncoders);
+
+  // Error codes, not segvs, should callers pass us a nullptr pointer.
+  EXPECT_FALSE(
+      pEncoders->RunLengthEncode(src_buf, src_size, &dest_buf, nullptr));
+  EXPECT_FALSE(
+      pEncoders->RunLengthEncode(src_buf, src_size, nullptr, &dest_size));
+  EXPECT_FALSE(pEncoders->RunLengthEncode(src_buf, 0, &dest_buf, &dest_size));
+  EXPECT_FALSE(
+      pEncoders->RunLengthEncode(nullptr, src_size, &dest_buf, &dest_size));
+}
+
+// Check length 1 input works. Check terminating character is applied.
+TEST(fxcodec, RLETestShortInput) {
+  uint8_t src_buf[1] = {1};
+  uint8_t* dest_buf = nullptr;
+  uint32_t src_size = 1;
+  uint32_t dest_size = 0;
+
+  CCodec_BasicModule* pEncoders = CCodec_ModuleMgr().GetBasicModule();
+  EXPECT_TRUE(pEncoders);
+
+  EXPECT_TRUE(
+      pEncoders->RunLengthEncode(src_buf, src_size, &dest_buf, &dest_size));
+  EXPECT_EQ(3u, dest_size);
+  EXPECT_EQ(dest_buf[0], 0);
+  EXPECT_EQ(dest_buf[1], 1);
+  EXPECT_EQ(dest_buf[2], 128);
+
+  FX_Free(dest_buf);
+}
+
+// Check a few basic cases (2 matching runs in a row, matching run followed
+// by a nonmatching run, and nonmatching run followed by a matching run).
+TEST(fxcodec, RLETestNormalInputs) {
+  // Match, match
+  uint8_t src_buf_1[10] = {2, 2, 2, 2, 4, 4, 4, 4, 4, 4};
+
+  // Match, nonmatch
+  uint8_t src_buf_2[10] = {2, 2, 2, 2, 1, 2, 3, 4, 5, 6};
+
+  // Nonmatch, match
+  uint8_t src_buf_3[10] = {1, 2, 3, 4, 5, 3, 3, 3, 3, 3};
+
+  uint32_t src_size = 10;
+  uint32_t dest_size = 0;
+  uint8_t* dest_buf = nullptr;
+
+  CCodec_BasicModule* pEncoders = CCodec_ModuleMgr().GetBasicModule();
+  EXPECT_TRUE(pEncoders);
+
+  // Case 1:
+  EXPECT_TRUE(
+      pEncoders->RunLengthEncode(src_buf_1, src_size, &dest_buf, &dest_size));
+  uint8_t* decoded_buf = nullptr;
+  uint32_t decoded_size = 0;
+  RunLengthDecode(dest_buf, dest_size, decoded_buf, decoded_size);
+  EXPECT_EQ(decoded_size, src_size);
+  for (uint32_t i = 0; i < src_size; i++)
+    EXPECT_EQ(decoded_buf[i], src_buf_1[i]) << " at " << i;
+  FX_Free(dest_buf);
+  FX_Free(decoded_buf);
+
+  // Case 2:
+  dest_buf = nullptr;
+  dest_size = 0;
+  EXPECT_TRUE(
+      pEncoders->RunLengthEncode(src_buf_2, src_size, &dest_buf, &dest_size));
+  decoded_buf = nullptr;
+  decoded_size = 0;
+  RunLengthDecode(dest_buf, dest_size, decoded_buf, decoded_size);
+  EXPECT_EQ(decoded_size, src_size);
+  for (uint32_t i = 0; i < src_size; i++)
+    EXPECT_EQ(decoded_buf[i], src_buf_2[i]) << " at " << i;
+  FX_Free(dest_buf);
+  FX_Free(decoded_buf);
+
+  // Case 3:
+  dest_buf = nullptr;
+  dest_size = 0;
+  EXPECT_TRUE(
+      pEncoders->RunLengthEncode(src_buf_3, src_size, &dest_buf, &dest_size));
+  decoded_buf = nullptr;
+  decoded_size = 0;
+  RunLengthDecode(dest_buf, dest_size, decoded_buf, decoded_size);
+  EXPECT_EQ(decoded_size, src_size);
+  for (uint32_t i = 0; i < src_size; i++)
+    EXPECT_EQ(decoded_buf[i], src_buf_3[i]) << " at " << i;
+  FX_Free(dest_buf);
+  FX_Free(decoded_buf);
+}
+
+// Check that runs longer than 128 are broken up properly, both matched and
+// nonmatched.
+TEST(fxcodec, RLETestFullLengthInputs) {
+  // Match, match
+  uint8_t src_buf_1[260] = {1};
+
+  // Match, nonmatch
+  uint8_t src_buf_2[260] = {2};
+  for (uint16_t i = 128; i < 260; i++)
+    src_buf_2[i] = (uint8_t)(i - 125);
+
+  // Nonmatch, match
+  uint8_t src_buf_3[260] = {3};
+  for (uint8_t i = 0; i < 128; i++)
+    src_buf_3[i] = i;
+
+  // Nonmatch, nonmatch
+  uint8_t src_buf_4[260];
+  for (uint16_t i = 0; i < 260; i++)
+    src_buf_4[i] = (uint8_t)(i);
+
+  uint32_t src_size = 260;
+  uint32_t dest_size = 0;
+  uint8_t* dest_buf = nullptr;
+
+  CCodec_BasicModule* pEncoders = CCodec_ModuleMgr().GetBasicModule();
+  EXPECT_TRUE(pEncoders);
+
+  // Case 1:
+  EXPECT_TRUE(
+      pEncoders->RunLengthEncode(src_buf_1, src_size, &dest_buf, &dest_size));
+  uint8_t* decoded_buf = nullptr;
+  uint32_t decoded_size = 0;
+  RunLengthDecode(dest_buf, dest_size, decoded_buf, decoded_size);
+  EXPECT_EQ(decoded_size, src_size);
+  for (uint32_t i = 0; i < src_size; i++)
+    EXPECT_EQ(decoded_buf[i], src_buf_1[i]) << " at " << i;
+  FX_Free(dest_buf);
+  FX_Free(decoded_buf);
+
+  // Case 2:
+  dest_buf = nullptr;
+  dest_size = 0;
+  EXPECT_TRUE(
+      pEncoders->RunLengthEncode(src_buf_2, src_size, &dest_buf, &dest_size));
+  decoded_buf = nullptr;
+  decoded_size = 0;
+  RunLengthDecode(dest_buf, dest_size, decoded_buf, decoded_size);
+  EXPECT_EQ(decoded_size, src_size);
+  for (uint32_t i = 0; i < src_size; i++)
+    EXPECT_EQ(decoded_buf[i], src_buf_2[i]) << " at " << i;
+  FX_Free(dest_buf);
+  FX_Free(decoded_buf);
+
+  // Case 3:
+  dest_buf = nullptr;
+  dest_size = 0;
+  EXPECT_TRUE(
+      pEncoders->RunLengthEncode(src_buf_3, src_size, &dest_buf, &dest_size));
+  decoded_buf = nullptr;
+  decoded_size = 0;
+  RunLengthDecode(dest_buf, dest_size, decoded_buf, decoded_size);
+  EXPECT_EQ(decoded_size, src_size);
+  for (uint32_t i = 0; i < src_size; i++)
+    EXPECT_EQ(decoded_buf[i], src_buf_3[i]) << " at " << i;
+  FX_Free(dest_buf);
+  FX_Free(decoded_buf);
+
+  // Case 4:
+  dest_buf = nullptr;
+  dest_size = 0;
+  EXPECT_TRUE(
+      pEncoders->RunLengthEncode(src_buf_4, src_size, &dest_buf, &dest_size));
+  decoded_buf = nullptr;
+  decoded_size = 0;
+  RunLengthDecode(dest_buf, dest_size, decoded_buf, decoded_size);
+  EXPECT_EQ(decoded_size, src_size);
+  for (uint32_t i = 0; i < src_size; i++)
+    EXPECT_EQ(decoded_buf[i], src_buf_4[i]) << " at " << i;
+  FX_Free(dest_buf);
+  FX_Free(decoded_buf);
+}
diff --git a/core/fxge/cfx_windowsdevice.h b/core/fxge/cfx_windowsdevice.h
index 5a4b901..6240d84 100644
--- a/core/fxge/cfx_windowsdevice.h
+++ b/core/fxge/cfx_windowsdevice.h
@@ -25,6 +25,7 @@
 extern PDFiumEnsureTypefaceCharactersAccessible
     g_pdfium_typeface_accessible_func;
 #endif
+extern int g_pdfium_print_postscript_level;
 
 class CFX_WindowsDevice : public CFX_RenderDevice {
  public:
diff --git a/core/fxge/win32/cfx_psrenderer.cpp b/core/fxge/win32/cfx_psrenderer.cpp
index c0d7557..b62d0cb 100644
--- a/core/fxge/win32/cfx_psrenderer.cpp
+++ b/core/fxge/win32/cfx_psrenderer.cpp
@@ -39,7 +39,7 @@
 
 CFX_PSRenderer::~CFX_PSRenderer() {}
 
-#define OUTPUT_PS(str) m_pOutput->OutputPS(str, sizeof str - 1)
+#define OUTPUT_PS(str) m_pOutput->OutputPS(str, sizeof(str) - 1)
 
 void CFX_PSRenderer::Init(CPSOutput* pOutput,
                           int pslevel,
@@ -125,11 +125,9 @@
         buf << " m ";
         break;
       case FXPT_LINETO:
-        if (flag & FXPT_CLOSEFIGURE) {
-          buf << " l h ";
-        } else {
-          buf << " l ";
-        }
+        buf << " l ";
+        if (flag & FXPT_CLOSEFIGURE)
+          buf << "h ";
         break;
       case FXPT_BEZIERTO: {
         FX_FLOAT x1 = pPathData->GetPointX(i + 1);
@@ -140,12 +138,10 @@
           pObject2Device->Transform(x1, y1);
           pObject2Device->Transform(x2, y2);
         }
-        buf << " " << x1 << " " << y1 << " " << x2 << " " << y2;
-        if (flag & FXPT_CLOSEFIGURE) {
-          buf << " c h\n";
-        } else {
-          buf << " c\n";
-        }
+        buf << " " << x1 << " " << y1 << " " << x2 << " " << y2 << " c";
+        if (flag & FXPT_CLOSEFIGURE)
+          buf << " h";
+        buf << "\n";
         i += 2;
         break;
       }
@@ -160,10 +156,12 @@
   StartRendering();
   OutputPath(pPathData, pObject2Device);
   CFX_FloatRect rect = pPathData->GetBoundingBox();
-  if (pObject2Device) {
+  if (pObject2Device)
     rect.Transform(pObject2Device);
-  }
-  m_ClipBox.Intersect(rect.GetOuterRect());
+  m_ClipBox.left = static_cast<int>(rect.left);
+  m_ClipBox.right = static_cast<int>(rect.left + rect.right);
+  m_ClipBox.top = static_cast<int>(rect.top + rect.bottom);
+  m_ClipBox.bottom = static_cast<int>(rect.bottom);
   if ((fill_mode & 3) == FXFILL_WINDING) {
     OUTPUT_PS("W n\n");
   } else {
@@ -263,7 +261,7 @@
       FXSYS_memcmp(m_CurGraphState.m_DashArray, pGraphState->m_DashArray,
                    sizeof(FX_FLOAT) * m_CurGraphState.m_DashCount)) {
     buf << "[";
-    for (int i = 0; i < pGraphState->m_DashCount; i++) {
+    for (int i = 0; i < pGraphState->m_DashCount; ++i) {
       buf << pGraphState->m_DashArray[i] << " ";
     }
     buf << "]" << pGraphState->m_DashPhase << " d\n";
@@ -281,7 +279,7 @@
     buf << pGraphState->m_MiterLimit << " M\n";
   }
   m_CurGraphState.Copy(*pGraphState);
-  m_bGraphStateSet = TRUE;
+  m_bGraphStateSet = true;
   if (buf.GetSize()) {
     m_pOutput->OutputPS((const FX_CHAR*)buf.GetBuffer(), buf.GetSize());
   }
@@ -297,7 +295,7 @@
                                 dest_buf, dest_size);
     FX_Free(src_buf);
   } else {
-    (*dest_buf).reset(src_buf);
+    dest_buf->reset(src_buf);
     *dest_size = (width + 7) / 8 * height;
   }
 }
@@ -315,7 +313,7 @@
     return;
   }
   CCodec_ModuleMgr* pEncoders = CFX_GEModule::Get()->GetCodecModule();
-  uint8_t* dest_buf = NULL;
+  uint8_t* dest_buf = nullptr;
   uint32_t dest_size = src_size;
   if (PSLevel >= 3) {
     if (pEncoders &&
@@ -334,7 +332,7 @@
     *output_buf = dest_buf;
     *output_size = dest_size;
   } else {
-    *filter = NULL;
+    *filter = nullptr;
     FX_Free(dest_buf);
   }
 }
@@ -407,9 +405,10 @@
     }
     buf << width << " 0 0 -" << height << " 0 " << height
         << "]currentfile/ASCII85Decode filter ";
-    if (output_buf.get() != src_buf)
+    if (output_buf.get() != src_buf) {
       buf << "<</K -1/EndOfBlock false/Columns " << width << "/Rows " << height
           << ">>/CCITTFaxDecode filter ";
+    }
     if (pSource->IsAlphaMask()) {
       buf << "iM\n";
     } else {
@@ -419,23 +418,26 @@
     WritePSBinary(output_buf.get(), output_size);
     output_buf.release();
   } else {
-    CFX_MaybeOwned<CFX_DIBSource> pConverted((CFX_DIBSource*)pSource);
+    CFX_DIBExtractor source_extractor(pSource);
+    CFX_MaybeOwned<CFX_DIBSource> pConverted(source_extractor.GetBitmap());
+    if (!pConverted.Get())
+      return false;
     switch (pSource->GetFormat()) {
       case FXDIB_1bppRgb:
       case FXDIB_Rgb32:
-        pConverted = pSource->CloneConvert(FXDIB_Rgb);
+        pConverted = pConverted->CloneConvert(FXDIB_Rgb).release();
         break;
       case FXDIB_8bppRgb:
         if (pSource->GetPalette()) {
-          pConverted = pSource->CloneConvert(FXDIB_Rgb);
+          pConverted = pConverted->CloneConvert(FXDIB_Rgb).release();
         }
         break;
       case FXDIB_1bppCmyk:
-        pConverted = pSource->CloneConvert(FXDIB_Cmyk);
+        pConverted = pConverted->CloneConvert(FXDIB_Cmyk).release();
         break;
       case FXDIB_8bppCmyk:
         if (pSource->GetPalette()) {
-          pConverted = pSource->CloneConvert(FXDIB_Cmyk);
+          pConverted = pConverted->CloneConvert(FXDIB_Cmyk).release();
         }
         break;
       default:
@@ -445,26 +447,23 @@
       OUTPUT_PS("\nQ\n");
       return false;
     }
-    int Bpp = pConverted->GetBPP() / 8;
+    int bpp = pConverted->GetBPP() / 8;
     uint8_t* output_buf = nullptr;
     FX_STRSIZE output_size = 0;
     const FX_CHAR* filter = nullptr;
-    if (flags & FXRENDER_IMAGE_LOSSY) {
-      CCodec_ModuleMgr* pEncoders = CFX_GEModule::Get()->GetCodecModule();
-      if (pEncoders &&
-          pEncoders->GetJpegModule()->JpegEncode(pConverted.Get(), &output_buf,
-                                                 &output_size)) {
-        filter = "/DCTDecode filter ";
-      }
+    if ((m_PSLevel == 2 || flags & FXRENDER_IMAGE_LOSSY) &&
+        CCodec_JpegModule::JpegEncode(pConverted.Get(), &output_buf,
+                                      &output_size)) {
+      filter = "/DCTDecode filter ";
     }
     if (!filter) {
-      int src_pitch = width * Bpp;
+      int src_pitch = width * bpp;
       output_size = height * src_pitch;
       output_buf = FX_Alloc(uint8_t, output_size);
       for (int row = 0; row < height; row++) {
         const uint8_t* src_scan = pConverted->GetScanline(row);
         uint8_t* dest_scan = output_buf + row * src_pitch;
-        if (Bpp == 3) {
+        if (bpp == 3) {
           for (int col = 0; col < width; col++) {
             *dest_scan++ = src_scan[2];
             *dest_scan++ = src_scan[1];
@@ -485,18 +484,13 @@
       output_buf = compressed_buf;
       output_size = compressed_size;
     }
-    CFX_DIBSource* converted = pConverted.Get();
-    if (converted != pSource) {
-      delete converted;
-      pConverted.Reset();
-    }
     buf << " 8[";
     buf << width << " 0 0 -" << height << " 0 " << height << "]";
     buf << "currentfile/ASCII85Decode filter ";
     if (filter) {
       buf << filter;
     }
-    buf << "false " << Bpp;
+    buf << "false " << bpp;
     buf << " colorimage\n";
     m_pOutput->OutputPS((const FX_CHAR*)buf.GetBuffer(), buf.GetSize());
     WritePSBinary(output_buf, output_size);
diff --git a/core/fxge/win32/fx_win32_device.cpp b/core/fxge/win32/fx_win32_device.cpp
index 1bfb4b7..c673a18 100644
--- a/core/fxge/win32/fx_win32_device.cpp
+++ b/core/fxge/win32/fx_win32_device.cpp
@@ -14,10 +14,6 @@
 #include "core/fxcrt/cfx_maybe_owned.h"
 #include "core/fxcrt/fx_memory.h"
 #include "core/fxcrt/fx_system.h"
-#include "core/fxge/cfx_fontmapper.h"
-#include "core/fxge/cfx_gemodule.h"
-#include "core/fxge/cfx_graphstatedata.h"
-#include "core/fxge/cfx_pathdata.h"
 #include "core/fxge/cfx_windowsdevice.h"
 #include "core/fxge/dib/dib_int.h"
 #include "core/fxge/fx_font.h"
@@ -697,6 +693,8 @@
 
 }  // namespace
 
+int g_pdfium_print_postscript_level = 0;
+
 std::unique_ptr<IFX_SystemFontInfo> IFX_SystemFontInfo::CreateDefault(
     const char** pUnused) {
   if (IsGDIEnabled())
@@ -1387,7 +1385,13 @@
   int obj_type = ::GetObjectType(hDC);
   bool use_printer = device_type == DT_RASPRINTER ||
                      device_type == DT_PLOTTER || obj_type == OBJ_ENHMETADC;
-  if (use_printer)
-    return new CGdiPrinterDriver(hDC);
-  return new CGdiDisplayDriver(hDC);
+
+  if (!use_printer)
+    return new CGdiDisplayDriver(hDC);
+
+  if (g_pdfium_print_postscript_level == 2 ||
+      g_pdfium_print_postscript_level == 3) {
+    return new CPSPrinterDriver(hDC, g_pdfium_print_postscript_level, false);
+  }
+  return new CGdiPrinterDriver(hDC);
 }
diff --git a/core/fxge/win32/fx_win32_print.cpp b/core/fxge/win32/fx_win32_print.cpp
index a8cfb3d..94c415b 100644
--- a/core/fxge/win32/fx_win32_print.cpp
+++ b/core/fxge/win32/fx_win32_print.cpp
@@ -11,11 +11,11 @@
 #include <vector>
 
 #include "core/fxcrt/fx_system.h"
-#include "core/fxge/cfx_renderdevice.h"
 #include "core/fxge/cfx_windowsdevice.h"
 #include "core/fxge/dib/dib_int.h"
 #include "core/fxge/fx_freetype.h"
 #include "core/fxge/ge/fx_text_int.h"
+#include "core/fxge/win32/cpsoutput.h"
 #include "core/fxge/win32/win32_int.h"
 #include "third_party/base/ptr_util.h"
 
@@ -328,8 +328,8 @@
 #endif
 }
 
-CPSPrinterDriver::CPSPrinterDriver(HDC hDC, int pslevel, bool bCmykOutput) {
-  m_hDC = hDC;
+CPSPrinterDriver::CPSPrinterDriver(HDC hDC, int pslevel, bool bCmykOutput)
+    : m_hDC(hDC), m_bCmykOutput(bCmykOutput) {
   m_HorzSize = ::GetDeviceCaps(m_hDC, HORZSIZE);
   m_VertSize = ::GetDeviceCaps(m_hDC, VERTSIZE);
   m_Width = ::GetDeviceCaps(m_hDC, HORZRES);
@@ -337,7 +337,6 @@
   m_nBitsPerPixel = ::GetDeviceCaps(m_hDC, BITSPIXEL);
   m_pPSOutput = pdfium::MakeUnique<CPSOutput>(m_hDC);
   m_PSRenderer.Init(m_pPSOutput.get(), pslevel, m_Width, m_Height, bCmykOutput);
-  m_bCmykOutput = bCmykOutput;
   HRGN hRgn = ::CreateRectRgn(0, 0, 1, 1);
   int ret = ::GetClipRgn(hDC, hRgn);
   if (ret == 1) {
@@ -354,7 +353,7 @@
           path.AppendRect((FX_FLOAT)pRect->left, (FX_FLOAT)pRect->bottom,
                           (FX_FLOAT)pRect->right, (FX_FLOAT)pRect->top);
         }
-        m_PSRenderer.SetClip_PathFill(&path, NULL, FXFILL_WINDING);
+        m_PSRenderer.SetClip_PathFill(&path, nullptr, FXFILL_WINDING);
       }
       FX_Free(pData);
     }
diff --git a/fpdfsdk/fpdfview.cpp b/fpdfsdk/fpdfview.cpp
index c292384..ff4d46e 100644
--- a/fpdfsdk/fpdfview.cpp
+++ b/fpdfsdk/fpdfview.cpp
@@ -429,7 +429,8 @@
   return FSDK_SetSandBoxPolicy(policy, enable);
 }
 
-#if defined(_WIN32) && defined(PDFIUM_PRINT_TEXT_WITH_GDI)
+#if defined(_WIN32)
+#if defined(PDFIUM_PRINT_TEXT_WITH_GDI)
 DLLEXPORT void STDCALL
 FPDF_SetTypefaceAccessibleFunc(PDFiumEnsureTypefaceCharactersAccessible func) {
   g_pdfium_typeface_accessible_func = func;
@@ -438,7 +439,15 @@
 DLLEXPORT void STDCALL FPDF_SetPrintTextWithGDI(FPDF_BOOL use_gdi) {
   g_pdfium_print_text_with_gdi = !!use_gdi;
 }
-#endif
+#endif  // PDFIUM_PRINT_TEXT_WITH_GDI
+
+DLLEXPORT FPDF_BOOL STDCALL FPDF_SetPrintPostscriptLevel(int postscript_level) {
+  if (postscript_level != 0 && postscript_level != 2 && postscript_level != 3)
+    return FALSE;
+  g_pdfium_print_postscript_level = postscript_level;
+  return TRUE;
+}
+#endif  // defined(_WIN32)
 
 DLLEXPORT FPDF_DOCUMENT STDCALL FPDF_LoadDocument(FPDF_STRING file_path,
                                                   FPDF_BYTESTRING password) {
@@ -656,6 +665,9 @@
   pPage->SetRenderContext(pdfium::WrapUnique(pContext));
 
   std::unique_ptr<CFX_DIBitmap> pBitmap;
+  // TODO: This results in unnecessary rasterization of some PDFs due to
+  // HasImageMask() returning true. If any image on the page is a mask, the
+  // entire page gets rasterized and the spool size gets huge.
   const bool bNewBitmap =
       pPage->BackgroundAlphaNeeded() || pPage->HasImageMask();
   if (bNewBitmap) {
diff --git a/public/fpdfview.h b/public/fpdfview.h
index 403f4e4..7378d5f 100644
--- a/public/fpdfview.h
+++ b/public/fpdfview.h
@@ -213,7 +213,8 @@
 DLLEXPORT void STDCALL FPDF_SetSandBoxPolicy(FPDF_DWORD policy,
                                              FPDF_BOOL enable);
 
-#if defined(_WIN32) && defined(PDFIUM_PRINT_TEXT_WITH_GDI)
+#if defined(_WIN32)
+#if defined(PDFIUM_PRINT_TEXT_WITH_GDI)
 // Pointer to a helper function to make |font| with |text| of |text_length|
 // accessible when printing text with GDI. This is useful in sandboxed
 // environments where PDFium's access to GDI may be restricted.
@@ -239,7 +240,20 @@
 // Return value:
 //          None.
 DLLEXPORT void STDCALL FPDF_SetPrintTextWithGDI(FPDF_BOOL use_gdi);
-#endif
+#endif  // PDFIUM_PRINT_TEXT_WITH_GDI
+
+// Function: FPDF_SetPrintPostscriptLevel
+//          Set postscript printing level when printing on Windows.
+//          Experimental API.
+// Parameters:
+//          postscript_level -  0 to disable postscript printing,
+//                              2 to print with postscript level 2,
+//                              3 to print with postscript level 3.
+//                              All other values are invalid.
+// Return value:
+//          True if successful, false if unsucessful (typically invalid input).
+DLLEXPORT FPDF_BOOL STDCALL FPDF_SetPrintPostscriptLevel(FPDF_BOOL use_gdi);
+#endif  // defined(_WIN32)
 
 // Function: FPDF_LoadDocument
 //          Open and load a PDF document.
diff --git a/testing/libfuzzer/BUILD.gn b/testing/libfuzzer/BUILD.gn
index 2b9915d..9ba8e32 100644
--- a/testing/libfuzzer/BUILD.gn
+++ b/testing/libfuzzer/BUILD.gn
@@ -105,6 +105,12 @@
   ]
 }
 
+pdfium_fuzzer("pdf_codec_a85_fuzzer") {
+  sources = [
+    "pdf_codec_a85_fuzzer.cc",
+  ]
+}
+
 pdfium_fuzzer("pdf_codec_fax_fuzzer") {
   sources = [
     "pdf_codec_fax_fuzzer.cc",
@@ -123,6 +129,12 @@
   ]
 }
 
+pdfium_fuzzer("pdf_codec_rle_fuzzer") {
+  sources = [
+    "pdf_codec_rle_fuzzer.cc",
+  ]
+}
+
 pdfium_fuzzer("pdf_hint_table_fuzzer") {
   sources = [
     "pdf_hint_table_fuzzer.cc",
diff --git a/testing/libfuzzer/pdf_codec_a85_fuzzer.cc b/testing/libfuzzer/pdf_codec_a85_fuzzer.cc
new file mode 100644
index 0000000..20bd792
--- /dev/null
+++ b/testing/libfuzzer/pdf_codec_a85_fuzzer.cc
@@ -0,0 +1,18 @@
+// Copyright 2017 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cstdint>
+#include <memory>
+
+#include "core/fxcodec/codec/ccodec_basicmodule.h"
+#include "core/fxcrt/fx_memory.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  uint8_t* dest_buf = nullptr;
+  uint32_t dest_size = 0;
+  CCodec_BasicModule encoder_module;
+  encoder_module.A85Encode(data, size, &dest_buf, &dest_size);
+  FX_Free(dest_buf);
+  return 0;
+}
diff --git a/testing/libfuzzer/pdf_codec_rle_fuzzer.cc b/testing/libfuzzer/pdf_codec_rle_fuzzer.cc
new file mode 100644
index 0000000..c86671e
--- /dev/null
+++ b/testing/libfuzzer/pdf_codec_rle_fuzzer.cc
@@ -0,0 +1,18 @@
+// Copyright 2017 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cstdint>
+#include <memory>
+
+#include "core/fxcodec/codec/ccodec_basicmodule.h"
+#include "core/fxcrt/fx_memory.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  uint8_t* dest_buf = nullptr;
+  uint32_t dest_size = 0;
+  CCodec_BasicModule encoder_module;
+  encoder_module.RunLengthEncode(data, size, &dest_buf, &dest_size);
+  FX_Free(dest_buf);
+  return 0;
+}