summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--BUILD.gn2
-rw-r--r--core/fxcodec/codec/fx_codec.cpp142
-rw-r--r--core/fxcodec/codec/fx_codec_a85_unittest.cpp209
-rw-r--r--core/fxcodec/codec/fx_codec_rle_unittest.cpp195
-rw-r--r--core/fxge/cfx_windowsdevice.h1
-rw-r--r--core/fxge/win32/cfx_psrenderer.cpp78
-rw-r--r--core/fxge/win32/fx_win32_device.cpp18
-rw-r--r--core/fxge/win32/fx_win32_print.cpp9
-rw-r--r--fpdfsdk/fpdfview.cpp16
-rw-r--r--public/fpdfview.h18
-rw-r--r--testing/libfuzzer/BUILD.gn12
-rw-r--r--testing/libfuzzer/pdf_codec_a85_fuzzer.cc18
-rw-r--r--testing/libfuzzer/pdf_codec_rle_fuzzer.cc18
13 files changed, 676 insertions, 60 deletions
diff --git a/BUILD.gn b/BUILD.gn
index ebb821ccff..f80875ff29 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1712,7 +1712,9 @@ test("pdfium_unittests") {
"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 6b6c723388..23171cf3ea 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 @@ bool CCodec_BasicModule::RunLengthEncode(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 || 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 0000000000..7910881772
--- /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 0000000000..94d87cde92
--- /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 5a4b901621..6240d84219 100644
--- a/core/fxge/cfx_windowsdevice.h
+++ b/core/fxge/cfx_windowsdevice.h
@@ -25,6 +25,7 @@ extern bool g_pdfium_print_text_with_gdi;
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 c0d7557541..b62d0cb8f5 100644
--- a/core/fxge/win32/cfx_psrenderer.cpp
+++ b/core/fxge/win32/cfx_psrenderer.cpp
@@ -39,7 +39,7 @@ CFX_PSRenderer::CFX_PSRenderer() {
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 @@ void CFX_PSRenderer::OutputPath(const CFX_PathData* pPathData,
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 @@ void CFX_PSRenderer::OutputPath(const CFX_PathData* pPathData,
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 @@ void CFX_PSRenderer::SetClip_PathFill(const CFX_PathData* pPathData,
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 @@ void CFX_PSRenderer::SetGraphState(const CFX_GraphStateData* pGraphState) {
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 @@ void CFX_PSRenderer::SetGraphState(const CFX_GraphStateData* pGraphState) {
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 @@ static void FaxCompressData(uint8_t* src_buf,
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 @@ static void PSCompressData(int PSLevel,
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 @@ static void PSCompressData(int PSLevel,
*output_buf = dest_buf;
*output_size = dest_size;
} else {
- *filter = NULL;
+ *filter = nullptr;
FX_Free(dest_buf);
}
}
@@ -407,9 +405,10 @@ bool CFX_PSRenderer::DrawDIBits(const CFX_DIBSource* pSource,
}
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 @@ bool CFX_PSRenderer::DrawDIBits(const CFX_DIBSource* pSource,
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 @@ bool CFX_PSRenderer::DrawDIBits(const CFX_DIBSource* pSource,
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 @@ bool CFX_PSRenderer::DrawDIBits(const CFX_DIBSource* pSource,
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 1bfb4b7d6d..c673a18d2d 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 @@ bool CFX_Win32FontInfo::GetFontCharset(void* hFont, int& charset) {
} // 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 @@ IFX_RenderDeviceDriver* CFX_WindowsDevice::CreateDriver(HDC hDC) {
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 a8cfb3d1b9..94c415b721 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 @@ bool CGdiPrinterDriver::DrawDeviceText(int nChars,
#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 @@ CPSPrinterDriver::CPSPrinterDriver(HDC hDC, int pslevel, bool bCmykOutput) {
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 @@ CPSPrinterDriver::CPSPrinterDriver(HDC hDC, int pslevel, bool bCmykOutput) {
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 c292384e88..ff4d46e357 100644
--- a/fpdfsdk/fpdfview.cpp
+++ b/fpdfsdk/fpdfview.cpp
@@ -429,7 +429,8 @@ DLLEXPORT void STDCALL FPDF_SetSandBoxPolicy(FPDF_DWORD policy,
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 @@ FPDF_SetTypefaceAccessibleFunc(PDFiumEnsureTypefaceCharactersAccessible func) {
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 @@ DLLEXPORT void STDCALL FPDF_RenderPage(HDC dc,
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 403f4e46ec..7378d5f9b1 100644
--- a/public/fpdfview.h
+++ b/public/fpdfview.h
@@ -213,7 +213,8 @@ DLLEXPORT void STDCALL FPDF_DestroyLibrary();
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 @@ FPDF_SetTypefaceAccessibleFunc(PDFiumEnsureTypefaceCharactersAccessible func);
// 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 2b9915d322..9ba8e32ba6 100644
--- a/testing/libfuzzer/BUILD.gn
+++ b/testing/libfuzzer/BUILD.gn
@@ -105,6 +105,12 @@ pdfium_fuzzer("pdf_cmap_fuzzer") {
]
}
+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_jbig2_fuzzer") {
]
}
+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 0000000000..20bd792886
--- /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 0000000000..c86671e5f4
--- /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;
+}